File: SSSR.pm

package info (click to toggle)
libgraph-moreutils-perl 0.3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 152 kB
  • sloc: perl: 385; makefile: 2
file content (128 lines) | stat: -rw-r--r-- 3,887 bytes parent folder | download
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package Graph::MoreUtils::SSSR;

# ABSTRACT: Find the Smallest Set of Smallest Rings in graph
our $VERSION = '0.3.0'; # VERSION

use strict;
use warnings;

sub SSSR
{
    my( $graph, $max_depth ) = @_;

    return
        map { detect_rings( $graph, $_, undef, undef, $max_depth ) }
            $graph->vertices;
}

# This subroutine will return cycle base not containing 1-vertex-connected graphs.
# TODO: Finish
sub get_cycle_base
{
    my( $graph, $max_depth ) = @_;

    my @SSSR = SSSR( $graph, $max_depth );
    my %edge_participation;
    for my $cycle (@SSSR) {
        for my $i (0..$#$cycle) {
            my $edge = join '', $cycle->[$i     % @$cycle],
                                $cycle->[($i+1) % @$cycle];
            $edge_participation{$edge} = [] unless $edge_participation{$edge};
            push @{$edge_participation{$edge}}, $cycle;
        }
    }

    # TODO: Cycle through all mutual edges and perform cycle addition
}

sub detect_rings
{
    my ( $graph, $atom, $original_atom, $previous_atom,
         $level, $seen_atoms ) = @_;

    return () if defined $level && !$level;

    $seen_atoms = {} unless defined $seen_atoms;
    $original_atom = $atom unless defined $original_atom;

    my %seen_atoms = ( %$seen_atoms,
                       $atom => { atom     => $atom,
                                  position => scalar keys %$seen_atoms } );

    my @rings;

    # First, look if we have Nachbarpunkte of the current path
    # _different_ from the original atom. If yes, we will discard this
    # cycle since it could be closed in a shorter way:

    for my $neighbour_atom ( $graph->neighbours( $atom ) ) {
        next if $neighbour_atom eq $original_atom;
        next if defined $previous_atom && $previous_atom eq $neighbour_atom;
        next if !exists $seen_atoms->{$neighbour_atom};

        return @rings;
    }

    # If no Nachbarpunkte are found in the previous search, let's look
    # if we can close the ring. If we do so, we set the
    # $Nachbarpunkte_detected flag, so that the search for rings does
    # not go on (the current atom and the original atom would be
    # Nachbarpunkte in any larger cycle containing the current path:

    if( scalar keys %seen_atoms > 2 ) {
        for my $neighbour_atom ( $graph->neighbours( $atom ) ) {
            next if $neighbour_atom ne $original_atom;

            # Detect a ring:

            my @sorted_ring =
                sort_ring_elements( map  { $seen_atoms{$_}->{atom} }
                                    sort { $seen_atoms{$a}->{position} <=>
                                           $seen_atoms{$b}->{position} }
                                         keys %seen_atoms );
            return @rings, \@sorted_ring;
        }
    }

    # Descend the new path in the neighbourhood graph:
    for my $neighbour_atom ( $graph->neighbours( $atom ) ) {
        next if exists $seen_atoms->{$neighbour_atom};
            
        push @rings,
             detect_rings( $graph,
                           $neighbour_atom,
                           $original_atom,
                           $atom,
                           defined $level ? $level - 1 : undef,
                           \%seen_atoms );
    }

    return @rings;
}

sub sort_ring_elements
{
    my( @elements ) = @_;

    return @elements if scalar @elements <= 1;

    my $min_index;
    my $reverse;
    for my $i (0..$#elements) {
        next if defined $min_index && $elements[$i] ge
                                      $elements[$min_index];
        $min_index = $i;
        $reverse = $elements[($i-1) % scalar @elements] lt
                   $elements[($i+1) % scalar @elements];
    }

    if( $reverse ) {
        @elements = reverse @elements;
        $min_index = $#elements - $min_index;
    }

    return @elements[$min_index..$#elements],
           @elements[0..$min_index-1];
}

1;