filipo has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I am fairly new to perl and can you help!. I'm trying to create a graph (undirected) below are the input, the output i want and my script thus far. I'm using split and access each element with 'for' and want to connect each compound i.e. C01=C03, C01=C04, C02=C03 and so on with R1 as the name of the edges. But i can seem to get the edges connected. I'm getting the following error message; "Graph::add_vertex: undef vertex". Thanks

Input file

R1#C01+ C02 <=> C03 + C04

R2#C01 + C04 + C05 <=> C07 + C08 + C09

input file ends

The output i want for each of the reactants to be connected to the product

C01=C03, C01=C04,C02=C03,C02=C04,C04=C07,C04=C08,C04=C09,C01=C07,C01=C08,C01=C09,C05=C07,C05=C08,C05=C09

#!/usr/bin/perl use Graph::Undirected; use warnings; use strict; package Graph::Base; # Instantiate my $G = new Graph::Undirected; open (IN, "Input.txt") || die $!; while (<IN>){ my @rxn=split/\#/,$_; # "$rxn[1]\n"; #debug my @cmp= split/\<=>/,$rxn[1]; #print"$cmp[0]\n"; # debug first element for reactants. my @rt=split/ \+ /,$cmp[0]; # split by space and '+'. if i don't p +ut space b/n \+ i get bad formated output. my $x; #declaring $x b/c the use of strict wouldn't allow me to $x or +my ($x)! for ($x=0; $x<=$#rt; $x++){ #print "$rt[$x]\n"; # debug prints the reactant compounds. } my @pd=split/\+/,$cmp[1]; my $y; for ($y=0; $y<=$#pd; $y++){ #print "$pd[$y]\n"; } $G = $G->add_edge($rt[$x], $pd[$y]); print "$G\n"; }

Replies are listed 'Best First'.
Re: creating an undirected graph
by toolic (Bishop) on Aug 16, 2013 at 15:15 UTC

    Tips from the Basic debugging checklist: print and Data::Dumper. This will show you that $x and $y attempt to access elements beyond the end of your @rt and @pd arrays.

    What should x and y be?

    I ran your code thru perltidy, then fixed some split's and cluttered it up with ugly debug code to show you how I came to those conclusions:

    use Graph::Undirected; use warnings; use strict; package Graph::Base; # Instantiate my $G = new Graph::Undirected; open( IN, "Input.txt" ) || die $!; while (<IN>) { chomp; my @rxn = split /\#/, $_; # "$rxn[1]\n"; #debug my @cmp = split /\s*\<=>\s*/, $rxn[1]; #use Data::Dumper; $Data::Dumper::Sortkeys=1; print Dumper(\@cmp); #print"$cmp[0]\n"; # debug first element for reactants. # my @rt = split / \+ /, $cmp[0]; # split by space and '+'. if i + don't put space b/n \+ i get bad formated output. my @rt = split /[+\s]+/, $cmp[0]; # split by space and '+'. if +i don't put space b/n \+ i get bad formated output. my $x; #declaring $x b/c the use of st +rict wouldn't allow me to $x or my ($x)! for ( $x = 0 ; $x <= $#rt ; $x++ ) { #print "$rt[$x]\n"; # debug prints the reactant compounds. } # my @pd = split /\+/, $cmp[1]; my @pd = split /[+\s]+/, $cmp[1]; #use Data::Dumper; $Data::Dumper::Sortkeys=1; print Dumper(\@pd); my $y; for ( $y = 0 ; $y <= $#pd ; $y++ ) { #print "$pd[$y]\n"; } #use Data::Dumper; $Data::Dumper::Sortkeys=1; print Dumper(\@rt); #use Data::Dumper; $Data::Dumper::Sortkeys=1; print Dumper(\@pd); #print "$x $y\n"; $x=1; $y = 1; # <----------------------------- ???????????? $G = $G->add_edge( $rt[$x], $pd[$y] ); print "$G\n"; } __END__ My output: C02=C04 C02=C04,C04=C08

    Things I fixed:

Re: creating an undirected graph
by McA (Priest) on Aug 16, 2013 at 15:27 UTC

    toolic has been faster with posting. But as I also looked at the problem, I post my part of solution:

    use Data::Dumper; open (my $infile, "<", "Input.txt") || die $!; while (defined(my $line = <$infile>)) { chomp $line; my @rxn = split /#/, $line; next if length $rxn[1] == 0; my ($left_side, $right_side) = split /\s*<=>\s*/, $rxn[1], 2; my @left_elements = split /\s*\+\s*/, $left_side; my @right_elements = split /\s*\+\s*/, $right_side; my @combinations; for my $left_element (@left_elements) { for my $right_element (@right_elements) { push @combinations, [$left_element, $right_element]; } } print join(", ", map { "$_->[0]=$_->[1]" } @combinations), "\n"; # print Dumper(\@combinations); }

    In @combinations you now have a list of combinations, you can create edges from.

    McA

Re: creating an undirected graph
by kcott (Archbishop) on Aug 17, 2013 at 09:39 UTC

    G'day filipo,

    I see ++toolic has pointed out issues in you code. Here's my take on a solution (pm_graph_undirected.pl) which uses glob to generate the combinations.

    #!/usr/bin/env perl -l use strict; use warnings; use autodie; use Graph::Undirected; my $graph = Graph::Undirected::->new(); open my $input_fh, '<', './pm_graph_undirected.txt'; while (<$input_fh>) { my ($x, $y) = /^\w+[#]([^<]+)\s+<=>\s+(.*)$/; $graph = $graph->add_edge(split /,/) for glob join ',' => map { '{' . join(',' => split /[+ ]+/ => $_) . '}' } ($x, $y); } print $graph;

    Input:

    $ cat pm_graph_undirected.txt R1#C01+ C02 <=> C03 + C04 R2#C01 + C04 + C05 <=> C07 + C08 + C09

    Output:

    $ pm_graph_undirected.pl C01=C03,C01=C04,C01=C07,C01=C08,C01=C09,C02=C03,C02=C04,C04=C07,C04=C0 +8,C04=C09,C05=C07,C05=C08,C05=C09

    Update: ++choroba has pointed out a limitation with glob (below). I've tracked down details: see GLOB_LIMIT in File::Glob - POSIX FLAGS.

    [Update2: very minor code change to remove an unnecessary comma s['}',]['}'] in the map. Retested and got the same output.]

    -- Ken

      Note that glob only generates strings of a limited length (4096 on my machine). Probably not an issue with a dozen of nodes with two letter names.
      لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

      That great, thanks for the help.