use Benchmark;
use Encode qw/ encode_utf8 /;
use Exporter 'import';
+use Graph;
use JSON qw/ encode_json decode_json /;
use LWP::UserAgent;
use Text::Tradition;
is( $row->{'genealogical'}, $expected_genealogical{$row->{'id'}},
"Got correct genealogical flag for row " . $row->{'id'} );
}
-is( $data->{'conflict_count'}, 34, "Got right conflict count" );
is( $data->{'variant_count'}, 58, "Got right total variant number" );
+# TODO Make something meaningful of conflict count, maybe test other bits
=end testing
# Get the stemma
my $stemma = $tradition->stemma( $stemma_id );
+
# Figure out which witnesses we are working with
my @lacunose = $stemma->hypotheticals;
my @tradition_wits = map { $_->sigil } $tradition->witnesses;
- map { push( @tradition_wits, $_->sigil."_ac" ) if $_->is_layered }
+ map { push( @tradition_wits, $_->sigil.$c->ac_label ) if $_->is_layered }
$tradition->witnesses;
push( @lacunose, _symmdiff( [ $stemma->witnesses ], \@tradition_wits ) );
push( @groups, group_variants( $tradition, $rank, $missing, \@collapse ) );
$lacunae{$rank} = $missing;
}
-
+ $DB::single = 1;
# Parse the answer
my $answer = solve_variants( $stemma, @groups );
sub group_variants {
my( $tradition, $rank, $lacunose, $collapse ) = @_;
my $c = $tradition->collation;
- my $aclabel = $c->ac_label;
+ my $aclabel = $c->ac_label;
# Get the alignment table readings
my %readings_at_rank;
my @gap_wits;
foreach my $tablewit ( @{$c->alignment_table->{'alignment'}} ) {
my $rdg = $tablewit->{'tokens'}->[$rank-1];
my $wit = $tablewit->{'witness'};
- $wit =~ s/^(.*)\Q$aclabel\E$/${1}_ac/;
if( $rdg && $rdg->{'t'}->is_lacuna ) {
- _add_to_witlist( $wit, $lacunose, '_ac' );
+ _add_to_witlist( $wit, $lacunose, $aclabel );
} elsif( $rdg ) {
$readings_at_rank{$rdg->{'t'}->text} = $rdg->{'t'};
} else {
- _add_to_witlist( $wit, \@gap_wits, '_ac' );
+ _add_to_witlist( $wit, \@gap_wits, $aclabel );
}
}
# Group the readings, collapsing groups by relationship if needed
my %grouped_readings;
- foreach my $rdg ( sort { $b->witnesses <=> $a->witnesses } values %readings_at_rank ) {
+ foreach my $rdg ( sort { $b->witnesses <=> $a->witnesses }
+ values %readings_at_rank ) {
# Skip readings that have been collapsed into others.
next if exists $grouped_readings{$rdg->id} && !$grouped_readings{$rdg->id};
my @wits = $rdg->witnesses;
- map { s/\Q$aclabel\E$/_ac/ } @wits;
if( $collapse ) {
my $filter = sub { my $r = $_[0]; grep { $_ eq $r->type } @$collapse; };
foreach my $other ( $rdg->related_readings( $filter ) ) {
my @otherwits = $other->witnesses;
- map { s/\Q$aclabel\E$/_ac/ } @otherwits;
push( @wits, @otherwits );
$grouped_readings{$other->id} = 0;
}
my( $stemma, @groups ) = @_;
# Make the json with stemma + groups
- my $jsonstruct = { 'graph' => $stemma->editable( ' ' ), 'groupings' => [] };
+ my $groupings = [];
foreach my $ghash ( @groups ) {
my @grouping;
foreach my $k ( sort keys %$ghash ) {
push( @grouping, $ghash->{$k} );
}
- push( @{$jsonstruct->{'groupings'}}, \@grouping );
+ push( @$groupings, \@grouping );
}
- my $json = encode_json( $jsonstruct );
+ ## Witness map is a HACK to get around limitations in node names from IDP
+ my $witness_map = {};
+ my $json = encode_json( _safe_wit_strings( $stemma, $groupings, $witness_map ) );
# Send it off and get the result
my $solver_url = 'http://byzantini.st/cgi-bin/graphcalc.cgi';
my $answer;
if( $resp->is_success ) {
- $answer = decode_json( $resp->content );
+ $answer = _desanitize_names( decode_json( $resp->content ), $witness_map );
} else {
# Fall back to the old method.
warn "IDP solver returned " . $resp->status_line . " / " . $resp->content
. "; falling back to perl method";
- $answer = perl_solver( $stemma, @{$jsonstruct->{'groupings'}} );
+ $answer = perl_solver( $stemma, @$groupings );
}
# Fold the result back into what we know about the groups.
'genealogical_count' => $genealogical };
}
+#### HACKERY to cope with IDP's limited idea of what a node name looks like ###
+
+sub _safe_wit_strings {
+ my( $stemma, $groupings, $witness_map ) = @_;
+ my $safegraph = Graph->new();
+ # Convert the graph to a safe representation and store the conversion.
+ foreach my $n ( $stemma->graph->vertices ) {
+ my $sn = _safe_witstr( $n );
+ warn "Ambiguous stringification $sn for $n and " . $witness_map->{$sn}
+ if exists $witness_map->{$sn};
+ $witness_map->{$sn} = $n;
+ $safegraph->add_vertex( $sn );
+ $safegraph->set_vertex_attributes( $sn,
+ $stemma->graph->get_vertex_attributes( $n ) );
+ }
+ foreach my $e ( $stemma->graph->edges ) {
+ my @safe_e = ( _safe_witstr( $e->[0] ), _safe_witstr( $e->[1] ) );
+ $safegraph->add_edge( @safe_e );
+ }
+ my $safe_stemma = Text::Tradition::Stemma->new(
+ 'collation' => $stemma->collation, 'graph' => $safegraph );
+
+ # Now convert the witness groupings to a safe representation.
+ my $safe_groupings = [];
+ foreach my $grouping ( @$groupings ) {
+ my $safe_grouping = [];
+ foreach my $group ( @$grouping ) {
+ my $safe_group = [];
+ foreach my $n ( @$group ) {
+ my $sn = _safe_witstr( $n );
+ warn "Ambiguous stringification $sn for $n and " . $witness_map->{$sn}
+ if exists $witness_map->{$sn} && $witness_map->{$sn} ne $n;
+ $witness_map->{$sn} = $n;
+ push( @$safe_group, $sn );
+ }
+ push( @$safe_grouping, $safe_group );
+ }
+ push( @$safe_groupings, $safe_grouping );
+ }
+
+ # Return it all in the struct we expect. We have stored the reductions
+ # in the $witness_map that we were passed.
+ return { 'graph' => $safe_stemma->editable( ' ' ), 'groupings' => $safe_groupings };
+}
+
+sub _safe_witstr {
+ my $witstr = shift;
+ $witstr =~ s/\s+/_/g;
+ $witstr =~ s/[^\w\d-]//g;
+ return $witstr;
+}
+
+sub _desanitize_names {
+ my( $jsonstruct, $witness_map ) = @_;
+ my $result = [];
+ foreach my $grouping ( @$jsonstruct ) {
+ my $real_grouping = [];
+ foreach my $element ( @$grouping ) {
+ if( ref( $element ) eq 'ARRAY' ) {
+ # it's the groupset.
+ my $real_groupset = [];
+ foreach my $group ( @$element ) {
+ my $real_group = [];
+ foreach my $n ( @$group ) {
+ my $rn = $witness_map->{$n};
+ push( @$real_group, $rn );
+ }
+ push( @$real_groupset, $real_group );
+ }
+ push( @$real_grouping, $real_groupset );
+ } else {
+ # It is the boolean, not actually a group.
+ push( @$real_grouping, $element );
+ }
+ }
+ push( @$result, $real_grouping );
+ }
+ return $result;
+}
+
+### END HACKERY ###
+
=head2 analyze_location ( $tradition, $graph, $location_hash )
Given the tradition, its stemma graph, and the solution from the graph solver,