diff --git a/ext/cIGraph.c b/ext/cIGraph.c index ecd25e6..ab02281 100644 --- a/ext/cIGraph.c +++ b/ext/cIGraph.c @@ -244,6 +244,8 @@ void Init_igraph(){ rb_define_alias (cIGraph, "neighborhood", "neighbourhood"); rb_define_alias (cIGraph, "neighborhood_graphs", "neighbourhood_graphs"); + rb_define_method(cIGraph, "subcomponent", cIGraph_subcomponent, 2); /* in cIGraph_components.c */ + rb_define_method(cIGraph, "topological_sorting", cIGraph_topological_sorting, 1); /* in cIGraph_topological_sort.c */ rb_define_singleton_method(cIGraph, "read_graph_edgelist", cIGraph_read_graph_edgelist, 2); /* in cIGraph_file.c */ diff --git a/ext/cIGraph_attribute_handler.c b/ext/cIGraph_attribute_handler.c index 999258b..3917a66 100644 --- a/ext/cIGraph_attribute_handler.c +++ b/ext/cIGraph_attribute_handler.c @@ -181,12 +181,12 @@ int cIGraph_attribute_add_vertices(igraph_t *graph, long int nv, igraph_vector_p VALUE values; if(attr){ - if(((igraph_i_attribute_record_t*)VECTOR(*attr)[0])->type == IGRAPH_ATTRIBUTE_PY_OBJECT){ + if(igraph_vector_ptr_size(attr) > 0 && ((igraph_i_attribute_record_t*)VECTOR(*attr)[0])->type == IGRAPH_ATTRIBUTE_PY_OBJECT){ values = (VALUE)((igraph_i_attribute_record_t*)VECTOR(*attr)[0])->value; Check_Type(values, T_ARRAY); for(i=0;ilen;i++){ - rb_ary_push(vertex_array, RARRAY(values)->ptr[i]); + rb_ary_push(vertex_array, RARRAY(values)->ptr[i]); } //Otherwise read each attriute into hashes and use those } else { @@ -196,6 +196,12 @@ int cIGraph_attribute_add_vertices(igraph_t *graph, long int nv, igraph_vector_p igraph_i_attribute_record_t *attr_rec; char *s; record = rb_hash_new(); + + //For when no attributes are given + if(igraph_vector_ptr_size(attr) == 0){ + record = INT2NUM(i+1); + } + for (j=0; j Array + * + * Returns an Array of vertices that are in the same component as the vertex v. + * mode defines the type of the component for directed graphs, possible + * values: IGraph::OUT: the set of vertices reachable from the vertex, + * IGraph::IN the set of vertices from which the vertex is reachable, + * IGraph::ALL the graph is considered as an undirected graph. Note that + * this is not the same as the union of the previous two. + */ +VALUE cIgraph_subcomponent(VALUE self, VALUE v, VALUE mode){ + + igraph_t *graph; + igraph_neimode_t pmode = NUM2INT(mode); + igraph_vector_t neis; + int i; + VALUE component = rb_ary_new(); + + igraph_vector_init_int(&neis,0); + + Data_Get_Struct(self, igraph_t, graph); + + igraph_subcomponent(graph, &neis, cIGraph_get_vertex_id(self,from), pmode); + + for(i=0;i IGraph + * + * Reads an edge list from a File (or any IO) 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. + */ +VALUE cIGraph_read_graph_edgelist(VALUE self, VALUE file, VALUE directed){ + + VALUE string; + FILE *stream; + VALUE new_graph; + VALUE v_ary; + igraph_t *graph; + igraph_bool_t directed_b = 0; + + igraph_vs_t vs; + igraph_vit_t vit; + + int vid; + + if(directed) + directed_b = 1; + + new_graph = cIGraph_alloc(cIGraph); + Data_Get_Struct(new_graph, igraph_t, graph); + + string = rb_funcall(file, rb_intern("read"), 0); + stream = fmemopen(RSTRING(string)->ptr,RSTRING(string)->len, "r"); + + igraph_read_graph_edgelist(graph, stream, 0, directed_b); + + fclose(stream); + + igraph_vs_all(&vs); + igraph_vit_create(graph, vs, &vit); + + v_ary = ((VALUE*)graph->attr)[0]; + + while (!IGRAPH_VIT_END(vit)) { + vid = IGRAPH_VIT_GET(vit); + rb_ary_push(v_ary,INT2NUM(vid)); + IGRAPH_VIT_NEXT(vit); + } + + igraph_vit_destroy(&vit); + igraph_vs_destroy(&vs); + + return new_graph; + +} + +/* call-seq: + * graph.write_graph_edgelist(file) -> Integer + * + * Writes an edge list to an IO + * + * 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. + */ +VALUE cIGraph_write_graph_edgelist(VALUE self, VALUE file){ + + char *buf; + size_t size; + FILE *stream; + igraph_t *graph; + int e; + + Data_Get_Struct(self, igraph_t, graph); + + stream = open_memstream(&buf,&size); + e = igraph_write_graph_edgelist(graph, stream); + fflush(stream); + + rb_funcall(file, rb_intern("write"), 1, rb_str_new(buf,size)); + + fclose(stream); + + return e; + +} + +VALUE cIGraph_read_graph_graphml(VALUE self, VALUE file, VALUE index){ + + VALUE string; + FILE *stream; + VALUE new_graph; + igraph_t *graph; + + new_graph = cIGraph_alloc(cIGraph); + Data_Get_Struct(new_graph, igraph_t, graph); + + string = rb_funcall(file, rb_intern("read"), 0); + stream = fmemopen(RSTRING(string)->ptr,RSTRING(string)->len, "r"); + + igraph_read_graph_graphml(graph, stream, NUM2INT(index)); + + fclose(stream); + + return new_graph; + +} + +VALUE cIGraph_write_graph_graphml(VALUE self, VALUE file){ + + char *buf; + size_t size; + FILE *stream; + igraph_t *graph; + int e; + + Data_Get_Struct(self, igraph_t, graph); + + stream = open_memstream(&buf,&size); + e = igraph_write_graph_graphml(graph, stream); + fflush(stream); + + rb_funcall(file, rb_intern("write"), 1, rb_str_new(buf,size)); + + fclose(stream); + + return e; + +} + +VALUE cIGraph_read_graph_pajek(VALUE self, VALUE file){ + + VALUE string; + FILE *stream; + VALUE new_graph; + igraph_t *graph; + + new_graph = cIGraph_alloc(cIGraph); + Data_Get_Struct(new_graph, igraph_t, graph); + + string = rb_funcall(file, rb_intern("read"), 0); + stream = fmemopen(RSTRING(string)->ptr,RSTRING(string)->len, "r"); + + igraph_read_graph_pajek(graph, stream); + + fclose(stream); + + return new_graph; + +} + +VALUE cIGraph_write_graph_pajek(VALUE self, VALUE file){ + + char *buf; + size_t size; + FILE *stream; + igraph_t *graph; + int e; + + Data_Get_Struct(self, igraph_t, graph); + + stream = open_memstream(&buf,&size); + e = igraph_write_graph_pajek(graph, stream); + fflush(stream); + + rb_funcall(file, rb_intern("write"), 1, rb_str_new(buf,size)); + + fclose(stream); + + return e; + +} diff --git a/ext/cIGraph_layout.c b/ext/cIGraph_layout.c new file mode 100644 index 0000000..070cb33 --- /dev/null +++ b/ext/cIGraph_layout.c @@ -0,0 +1,62 @@ +#include "igraph.h" +#include "ruby.h" +#include "cIGraph.h" + +/* call-seq: + * graph.layout_random -> IGraphMatrix + * + * Returns a random layout + */ +VALUE cIGraph_layout_random(VALUE self){ + + igraph_t *graph; + igraph_matrix_t *res = malloc(sizeof(igraph_matrix_t)); + + Data_Get_Struct(self, igraph_t, graph); + + igraph_matrix_init(res,0,0); + igraph_layout_random(graph,res); + + return Data_Wrap_Struct(cIGraphMatrix, 0, cIGraph_matrix_free, res); + +} + +VALUE cIGraph_layout_circle(VALUE self){ + + igraph_t *graph; + igraph_matrix_t *res = malloc(sizeof(igraph_matrix_t)); + + Data_Get_Struct(self, igraph_t, graph); + + igraph_matrix_init(res,0,0); + igraph_layout_circle(graph,res); + + return Data_Wrap_Struct(cIGraphMatrix, 0, cIGraph_matrix_free, res); + +} + +VALUE cIGraph_layout_fruchterman_reingold(VALUE self, + VALUE niter, + VALUE maxdelta, + VALUE area, + VALUE coolexp, + VALUE repulserad, + VALUE use_seed){ + + igraph_t *graph; + igraph_matrix_t *res = malloc(sizeof(igraph_matrix_t)); + + Data_Get_Struct(self, igraph_t, graph); + + igraph_matrix_init(res,0,0); + igraph_layout_fruchterman_reingold(graph,res, + NUM2INT(niter), + NUM2DBL(maxdelta), + NUM2DBL(area), + NUM2DBL(coolexp), + NUM2DBL(repulserad), + use_seed == Qtrue ? 1: 0); + + return Data_Wrap_Struct(cIGraphMatrix, 0, cIGraph_matrix_free, res); + +} diff --git a/ext/cIGraph_matrix.c b/ext/cIGraph_matrix.c new file mode 100644 index 0000000..d73db46 --- /dev/null +++ b/ext/cIGraph_matrix.c @@ -0,0 +1,199 @@ +#include "igraph.h" +#include "ruby.h" +#include "cIGraph.h" + +//Classes +VALUE cIGraphMatrix; + +void cIGraph_matrix_free(void *p){ + igraph_matrix_destroy(p); +} + +VALUE cIGraph_matrix_alloc(VALUE klass){ + + igraph_matrix_t *m = malloc(sizeof(igraph_matrix_t)); + VALUE obj; + + igraph_matrix_init(m, 0, 0); + + obj = Data_Wrap_Struct(klass, 0, cIGraph_matrix_free, m); + + return obj; + +} + +/* Document-method: initialize_copy + * + * Internal method for copying IGraph objects. + */ +VALUE cIGraph_matrix_init_copy(VALUE copy, VALUE orig){ + + igraph_matrix_t *orig_m; + igraph_matrix_t *copy_m; + + if (copy == orig) + return copy; + + if(TYPE(orig) != T_DATA || RDATA(orig)->dfree != (RUBY_DATA_FUNC)cIGraph_free){ + rb_raise(rb_eTypeError, "Wrong argument type."); + } + + Data_Get_Struct(copy, igraph_matrix_t, copy_m); + Data_Get_Struct(orig, igraph_matrix_t, orig_m); + + igraph_matrix_copy(copy_m,orig_m); + + return copy; + +} + +/* call-seq: + * IGraphMatrix[[x,y,...],...] -> IGraphMatrix + * + * IGraphMatrix[[1,2],[3,4]] + * + * Creates a graph with four vertices. Vertex 1 is connected to vertex 2. + * Vertex 3 is connected to vertex 4. + */ + +VALUE cIGraph_matrix_initialize(int argc, VALUE *argv, VALUE self){ + + igraph_matrix_t *m; + VALUE rows; + int nrows; + int ncols; + int i; + int j; + + rb_scan_args(argc,argv,"0*", &rows); + + Data_Get_Struct(self, igraph_matrix_t, m); + + nrows = RARRAY(rows)->len; + ncols = RARRAY(RARRAY(rows)->ptr[0])->len; + + igraph_matrix_resize(m, nrows, ncols); + + //Loop through rows + for (i=0; iptr[i])->ptr[j]); + } + } + + return self; + +} + +VALUE cIGraph_matrix_get(VALUE self, VALUE i, VALUE j){ + + igraph_matrix_t *m; + + Data_Get_Struct(self, igraph_matrix_t, m); + return rb_float_new(MATRIX(*m,NUM2INT(i),NUM2INT(j))); + +} + +VALUE cIGraph_matrix_set(VALUE self, VALUE i, VALUE j, VALUE x){ + + igraph_matrix_t *m; + + Data_Get_Struct(self, igraph_matrix_t, m); + MATRIX(*m,NUM2INT(i),NUM2INT(j)) = NUM2DBL(x); + return x; + +} + +VALUE cIGraph_matrix_each(VALUE self){ + + igraph_matrix_t *m; + int i; + int j; + + Data_Get_Struct(self, igraph_matrix_t, m); + + for(i=0;i < m->nrow;i++){ + for(j=0;j < m->ncol;j++){ + rb_yield(rb_float_new(MATRIX(*m,i,j))); + } + } + + return Qnil; + +} + +VALUE cIGraph_matrix_size(VALUE self){ + + igraph_matrix_t *m; + + Data_Get_Struct(self, igraph_matrix_t, m); + return LONG2FIX(igraph_matrix_size(m)); + +} + +VALUE cIGraph_matrix_nrow(VALUE self){ + + igraph_matrix_t *m; + + Data_Get_Struct(self, igraph_matrix_t, m); + return LONG2FIX(igraph_matrix_nrow(m)); + +} + +VALUE cIGraph_matrix_ncol(VALUE self){ + + igraph_matrix_t *m; + + Data_Get_Struct(self, igraph_matrix_t, m); + return LONG2FIX(igraph_matrix_ncol(m)); + +} + +VALUE cIGraph_matrix_max(VALUE self){ + + igraph_matrix_t *m; + + Data_Get_Struct(self, igraph_matrix_t, m); + return rb_float_new(igraph_matrix_max(m)); + +} + +VALUE cIGraph_matrix_multiply(VALUE self, VALUE x){ + + igraph_matrix_t *m; + igraph_matrix_t *n = malloc(sizeof(igraph_matrix_t)); + VALUE nobj; + + Data_Get_Struct(self, igraph_matrix_t, m); + + igraph_matrix_copy(n,m); + igraph_matrix_multiply(n, NUM2DBL(x)); + + nobj = Data_Wrap_Struct(cIGraphMatrix, 0, cIGraph_matrix_free, n); + + return nobj; + +} + +VALUE cIGraph_matrix_toa(VALUE self){ + + igraph_matrix_t *m; + int i; + int j; + VALUE a = rb_ary_new(); + VALUE row; + + Data_Get_Struct(self, igraph_matrix_t, m); + + for(i=0;i < m->nrow;i++){ + row = rb_ary_new(); + for(j=0;j < m->ncol;j++){ + rb_ary_push(row,rb_float_new(MATRIX(*m,i,j))); + } + rb_ary_push(a,row); + } + + return a; + +} + diff --git a/ext/cIGraph_topological_sort.c b/ext/cIGraph_topological_sort.c new file mode 100644 index 0000000..c780641 --- /dev/null +++ b/ext/cIGraph_topological_sort.c @@ -0,0 +1,43 @@ +#include "igraph.h" +#include "ruby.h" +#include "cIGraph.h" + +/* call-seq: + * graph.topological_sorting(mode) -> Array + * + * Calculate a possible topological sorting of the graph. 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. mode specifies how to use the direction + * of the edges. For 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 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. + */ +VALUE cIGraph_topological_sorting(VALUE self, VALUE mode){ + + igraph_t *graph; + igraph_vector_t res; + igraph_neimode_t pmode = NUM2INT(mode); + VALUE result = rb_ary_new(); + int i; + + igraph_vector_init_int(&res,0); + + Data_Get_Struct(self, igraph_t, graph); + + igraph_topological_sorting(graph, &res, pmode); + + for(i=0;i0,'name'=>'a','type' => 4.0}, + {'id'=>1,'name'=>'b','type' => 5}, + {'id'=>2,'type' => 6}, + {'id'=>3,'name'=>'d'}], + true, + [{'eid'=>'e1'}, + {'eid'=>'e2'}]) + g.attributes['date'] = 'Friday' + s = StringIO.new("") + str = g.write_graph_graphml(s) + s.rewind + assert_equal Graphml_out, s.read + end + + def test_pajek_read_write + g = nil + g = IGraph.read_graph_pajek(StringIO.new(Pajek),0) + assert_instance_of IGraph, g + assert_equal 4, g.vcount + assert_equal 1, g[4,1]['weight'] + h = g.dup + s = StringIO.new('') + h.write_graph_pajek(s) + s.rewind + str = s.read + str.gsub!(/\r/,'') + assert_equal Pajek, str + end + + Graphml = %q{ + + + yellow + + + + 2006-11-12 + + green + incorrect + + + + + blue + + + red + + + + turquoise + + + 1.0 + + + 1.0 + + + 2.0 + + + + + + 1.1 + + + +} + + + Graphml_out = %q{ + + + + + + + + + Friday + + a + 4 + 0 + + + b + 5 + 1 + + + + 6 + 2 + + + d + 3 + + + e1 + + + e2 + + + +} + + Pajek = %q{*Vertices 4 +*Edges +4 1 1 +1 2 4 +1 3 2 +2 3 2 +} + +end diff --git a/test/tc_layout.rb b/test/tc_layout.rb new file mode 100644 index 0000000..e8dde79 --- /dev/null +++ b/test/tc_layout.rb @@ -0,0 +1,26 @@ +require 'test/unit' +require 'igraph' + +class TestGraph < Test::Unit::TestCase + def test_random + g = IGraph.new([1,2,3,4],true) + l = g.layout_random + assert_instance_of IGraphMatrix, l + assert_equal g.vcount, l.nrow + assert_equal 2, l.ncol + end + def test_circle + g = IGraph.new([1,2,3,4],true) + l = g.layout_circle + assert_instance_of IGraphMatrix, l + assert_equal g.vcount, l.nrow + assert_equal 2, l.ncol + end + def test_fruchterman_reingold + g = IGraph.new([1,2,3,4],true) + l = g.layout_fruchterman_reingold(10,1,1,2,1,false) + assert_instance_of IGraphMatrix, l + assert_equal g.vcount, l.nrow + assert_equal 2, l.ncol + end +end diff --git a/test/tc_matrix.rb b/test/tc_matrix.rb new file mode 100644 index 0000000..372a858 --- /dev/null +++ b/test/tc_matrix.rb @@ -0,0 +1,32 @@ +require 'test/unit' +require 'igraph' + +class TestGraph < Test::Unit::TestCase + def test_matrix + m = IGraphMatrix.new([1,2],[3,4]) + assert_equal 1, m[0,0] + assert_equal 3, m[1,0] + end + def test_set + m = IGraphMatrix.new([1,2],[3,4]) + m[0,0] = 6 + assert_equal 6, m[0,0] + end + def test_prop + m = IGraphMatrix.new([1,2],[3,4]) + assert_equal 4, m.size + assert_equal 2, m.nrow + assert_equal 2, m.ncol + assert_equal 4, m.max + end + def test_op + m = IGraphMatrix.new([1,2],[3,4]) + n = m * 2 + assert_equal 1, m[0,0] + assert_equal 2, n[0,0] + end + def test_to_a + m = IGraphMatrix.new([1,2],[3,4]) + assert_equal [[1,2],[3,4]], m.to_a + end +end diff --git a/test/tc_topological_sort.rb b/test/tc_topological_sort.rb new file mode 100644 index 0000000..b2d1fde --- /dev/null +++ b/test/tc_topological_sort.rb @@ -0,0 +1,10 @@ +require 'test/unit' +require 'igraph' + +class TestGraph < Test::Unit::TestCase + def test_top_sort + g = IGraph.new([1,2,2,3,3,4]) + assert_equal [1,2,3,4], g.topological_sorting(IGraph::OUT) + end + +end