moved nonpb.pm
[PsN.git] / lib / output_subs.pm
blobf0d17e7881df699d66e6590890608ff6101a65aa
1 # {{{ include
3 start include statements
4 # A Perl module for parsing NONMEM output files
5 use Digest::MD5 'md5_hex';
6 use OSspecific;
7 use Storable;
8 use Config;
9 use ext::Math::SigFigs;
10 use Data::Dumper;
11 end include statements
13 # }}} include statements
15 # {{{ description
17 # No method, just documentation
18 start description
19 # The PsN output class is built to ease the (often trivial,
20 # but still time consuming) task of gathering and structuring the
21 # information contained in NONMEM output files. The major parts of a
22 # NONMEM output file are parsed and in the L</methods> section
23 # you can find a listing of the routines that are available.
24 end description
26 # }}} description
28 # {{{ synopsis
30 start synopsis
31 # use output;
33 # my $out_obj = output -> new ( filename => 'run1.lst' );
35 # my @thetas = @{$out_obj -> thetas};
36 # my @omegas = @{$out_obj -> omegas};
37 # my @ofvs = @{$out_obj -> ofvs};
38 end synopsis
40 # }}} synopsis
42 # {{{ see_also
44 start see_also
45 # =begin html
47 # <a HREF="data.html">data</a>, <a HREF="model.html">model</a>
48 # <a HREF="tool/modelfit.html">tool::modelfit</a>,
49 # <a HREF="tool.html">tool</a>
51 # =end html
53 # =begin man
55 # data, model, tool::modelfit, tool
57 # =end man
58 end see_also
60 # }}} see_also
62 # {{{ new
64 start new
66 # Usage:
68 # $outputObject -> new( filename => 'run1.lst' );
70 # The basic usage above creates a output object with the data
71 # in file.out parsed into memory.
73 # $outputObject -> new( filename => 'run1.lst',
74 # target => 'disk' );
76 # If I<target> is set to 'disk', the data in "run1.lst" will
77 # be left on disk in an effort to preserve memory. The file
78 # will be read if needed.
80 debug -> warn( level => 2,
81 message => "Initiating new\tNM::output object from file $parm{'filename'}" );
82 if ( defined $this -> {'filename'} and $this -> {'filename'} ne '' ) {
83 ( $this -> {'directory'}, $this -> {'filename'} ) =
84 OSspecific::absolute_path( $this -> {'directory'},$this->{'filename'} );
85 if( -e $this -> full_name ){
86 if($this -> {'target'} eq 'mem'){
87 $this -> _read_problems;
89 } else {
90 debug -> die( message => "The NONMEM output file ".
91 $this -> full_name." does not exist" )
92 unless $this -> {'ignore_missing_files'};
94 } else {
95 debug -> die( message => "No filename specified or filename equals empty string!" );
96 $this->{'filename'} = 'tempfile';
99 end new
101 # }}} new
103 # {{{ register_in_database
105 start register_in_database
106 if ( $PsN::config -> {'_'} -> {'use_database'} ) {
107 my $md5sum;
108 if( -e $self -> full_name ){
109 # md5sum
110 $md5sum = md5_hex(OSspecific::slurp_file($self-> full_name ));
112 # Backslashes messes up the sql syntax
113 my $file_str = $self->{'filename'};
114 my $dir_str = $self->{'directory'};
115 $file_str =~ s/\\/\//g;
116 $dir_str =~ s/\\/\//g;
118 my $dbh = DBI -> connect("DBI:mysql:host=".$PsN::config -> {'_'} -> {'database_server'}.
119 ";databse=".$PsN::config -> {'_'} -> {'project'},
120 $PsN::config -> {'_'} -> {'user'},
121 $PsN::config -> {'_'} -> {'password'},
122 {'RaiseError' => 1});
124 my $sth;
125 my $select_arr = [];
127 if ( not $force ) {
128 my $sth = $dbh -> prepare( "SELECT output_id FROM ".$PsN::config -> {'_'} -> {'project'}.
129 ".output ".
130 "WHERE filename = '$file_str' AND ".
131 "directory = '$dir_str' AND ".
132 "md5sum = '".$md5sum."'" );
133 $sth -> execute or debug -> die( message => $sth->errstr ) ;
135 $select_arr = $sth -> fetchall_arrayref;
138 if ( scalar @{$select_arr} > 0 ) {
139 debug -> warn( level => 1,
140 message => "Found an old entry in the database matching the ".
141 "current output file" );
142 if ( scalar @{$select_arr} > 1 ) {
143 debug -> warn( level => 1,
144 message => "Found more than one matching entry in database".
145 ", using the first" );
147 $self -> {'output_id'} = $select_arr->[0][0];
148 # Maybe we should update the table with a new model_id if such is supplied to us?
149 $self -> {'model_id'} = $select_arr->[0][1];
150 } else {
151 my ( $date_str, $time_str );
152 if ( $Config{osname} eq 'MSWin32' ) {
153 $date_str = `date /T`;
154 $time_str = ' '.`time /T`;
155 } else {
156 # Assuming UNIX
157 $date_str = `date`;
159 chomp($date_str);
160 chomp($time_str);
161 my $date_time = $date_str.$time_str;
162 my @mod_str = ('','');
163 if ( defined $model_id ) {
164 @mod_str = ('model_id, ',"$model_id, ");
166 $sth = $dbh -> prepare("INSERT INTO ".$PsN::config -> {'_'} -> {'project'}.
167 ".output ".
168 "( ".$mod_str[0].
169 "filename, date, directory, md5sum ) ".
170 "VALUES (".$mod_str[1].
171 "'$file_str', '$date_time', ".
172 "'$dir_str', '".$md5sum."' )");
174 $sth -> execute;
175 $self -> {'output_id'} = $sth->{'mysql_insertid'};
176 $self -> {'model_id'} = $model_id;
178 $sth -> finish;
179 $dbh -> disconnect;
180 if ( defined $self -> {'output_id'} ) {
181 foreach my $problem ( @{$self -> {'problems'}} ) {
182 $problem -> register_in_database( output_id => $self -> {'output_id'},
183 model_id => $model_id );
187 end register_in_database
189 # }}} register_in_database
191 # {{{ full_name
192 start full_name
194 $full_name = $self -> {'directory'} . $self -> {'filename'};
196 end full_name
198 # }}} full_name
200 # {{{ copy
201 start copy
203 $new_output = Storable::dclone( $self );
205 end copy
206 # }}} copy
208 # {{{ Definitions and help text for all accessors
210 start comegas
211 # Since PsN output objects are read-only, once they are
212 # initialized (probably through parsing a NONMEM output file) the
213 # methods of the output class are only used to extract
214 # information, not to set any.
216 # The general structure of the values returned by the methods
217 # reflect the level where the attributes belong (problems or sub
218 # problems) and of course also the structure of the attribute
219 # itself (scalar (ofv), array (thetas) or matrix
220 # (raw_cormatrix)). Taking ofv as example, this means that the
221 # returned variable will be a (reference to a) two-dimensional
222 # array, with the indexes problem and subproblem since ofv is a
223 # scalar on the sub problem level.
225 # Most methods take two optional arguments, I<problems> and
226 # I<subproblems>. These can be used to specify which problem or sub
227 # problem that the method should extract the required information
228 # from. problems and subproblems should be references to arrays of
229 # numbers. Some methods that return information related to model
230 # parameters also take I<parameter_numbers> as another optional
231 # argument and this can be used to specify a subset of parameters.
233 # Example:
235 # Return the standard errors for omega 1 and 3 (in all problems
236 # and sub problems)
238 # @seomega = @{$output_object -> seomegas( parameter_numbers => [1,3] )};
241 # comegas returns the standard deviation for elements on the
242 # diagonal and correlation coefficients for off-diagonal elements.
243 end comegas
245 start condition_number
246 # condition_number returns the 2-norm condition number for the correlation matrix, i.e.
247 # the largest eigen value divided by the smallest.
248 # See L</comegas> for details of the method arguments.
250 # Level: Sub problem
251 end condition_number
253 start covariance_step_run
254 # Returns 1 if the covariance step was run, 0 otherwise. See
255 # L</comegas> for details.
257 # Level: Problem
258 end covariance_step_run
260 start covariance_step_successful
261 # Returns 1 if the covariance step was successful, 0
262 # otherwise. See L</comegas> for details on the method arguments.
264 # Level: Sub problem
265 end covariance_step_successful
267 start covariance_step_warnings
268 # Returns 0 if there were no warnings or errors printed during the
269 # covariance step, 1 otherwise. See L</comegas> for details on the
270 # method arguments.
272 # Level: Sub problem
273 end covariance_step_warnings
275 start csigmas
276 # csigmas returns the standard deviation for elements on the
277 # diagonal and correlation coefficients for off-diagonal elements.
278 # See L</comegas> for details on the method arguments.
280 # Level: Sub problem
281 end csigmas
283 start cvseomegas
284 # cvseomegas returns the relative standard error for the omegas, i.e. SE/estimate.
285 # See L</comegas> for details on the method arguments.
287 # Level: Sub problem
288 end cvseomegas
290 start cvsesigmas
291 # cvsesigmas returns the relative standard error for the sigmas, i.e. SE/estimate.
292 # See L</comegas> for details on the method arguments.
294 # Level: Sub problem
295 end cvsesigmas
297 start cvsethetas
298 # cvsethetas returns the relative standard error for the thetas, i.e. SE/estimate.
299 # See L</comegas> for details on the method arguments.
301 # Level: Sub problem
302 end cvsethetas
304 start eigens
305 # eigens returns the eigen values.
306 # See L</comegas> for details of the method arguments.
308 # Level: Sub problem
309 end eigens
311 start etabar
312 # etabar returns the ETABAR estimates.
313 # See L</comegas> for details of the method arguments.
315 # Level: Sub problem
316 end etabar
318 start feval
319 # feval returns the number of function evaluations.
320 # See L</comegas> for details of the method arguments.
322 # Level: Sub problem
323 end feval
325 start finalparam
326 # finalparam returns the final parameter vector as it appears in the monitoring of search section.
327 # See L</comegas> for details of the method arguments.
329 # Level: Sub problem
330 end finalparam
332 start final_gradients
333 # final_gradients returns the final gradient vector as it appears in the monitoring of search section.
334 # See L</comegas> for details of the method arguments.
336 # Level: Sub problem
337 end final_gradients
339 start fixedomegas
340 # fixedomegas returns the a vector of booleans; 1's if
341 # the parameters were fixed during the model fit, 0's
342 # if they were not.
343 # See L</comegas> for details of the method arguments.
345 # Level: Sub problem
346 end fixedomegas
348 start fixedsigmas
349 # fixedsigmas returns the a vector of booleans; 1's if
350 # the parameters were fixed during the model fit, 0's
351 # if they were not.
352 # See L</comegas> for details of the method arguments.
354 # Level: Sub problem
355 end fixedsigmas
357 start fixedthetas
358 # fixedthetas returns the a vector of booleans; 1's if
359 # the parameters were fixed during the model fit, 0's
360 # if they were not.
361 # See L</comegas> for details of the method arguments.
363 # Level: Sub problem
364 end fixedthetas
366 start funcevalpath
367 # funcevalpath returns the number of function evaluations for each printed iteration in the monitoring of search section.
368 # See L</comegas> for details of the method arguments.
370 # Level: Sub problem
371 end funcevalpath
373 start gradient_path
374 # gradient_path returns the gradients for each printed iteration in the monitoring of search section (returns a matrix for each sub problem).
375 # See L</comegas> for details of the method arguments.
377 # Level: Sub problem
378 end gradient_path
380 start initgrad
381 # initgrad returns the initial gradient vector in the monitoring of search section.
382 # See L</comegas> for details of the method arguments.
384 # Level: Sub problem
385 end initgrad
387 start initomegas
388 # initomegas returns the initial omega values.
389 # See L</comegas> for details of the method arguments.
391 # Level: Sub problem
392 end initomegas
394 start initsigmas
395 # initsigmas returns the initial sigma values.
396 # See L</comegas> for details of the method arguments.
398 # Level: Sub problem
399 end initsigmas
401 start initthetas
402 # initthetas returns the initial theta values.
403 # See L</comegas> for details of the method arguments.
405 # Level: Sub problem
406 end initthetas
408 start iternum
409 # iternum returns a vector of the iteration numbers in the monitoring of search section.
410 # See L</comegas> for details of the method arguments.
412 # Level: Sub problem
413 end iternum
415 start nind
416 # nind returns the number of individuals.
417 # See L</comegas> for details of the method arguments.
419 # Level: Problem
420 end nind
422 start nobs
423 # nobs returns the number of observations.
424 # See L</comegas> for details of the method arguments.
426 # Level: Problem
427 end nobs
429 start npofv
430 # npofv returns the non-parametric objective function value.
431 # See L</comegas> for details of the method arguments.
433 # Level: Sub problem
434 end npofv
436 start nrecs
437 # nrecs returns the number of records.
438 # See L</comegas> for details of the method arguments.
440 # Level: Problem
441 end nrecs
443 start npomegas
444 # npomegas returns the non-parametric omega estimates.
445 # See L</comegas> for details of the method arguments.
447 # Level: Sub problem
448 end npomegas
450 start npthetas
451 # npthetas returns the non-parametric theta estimates.
452 # See L</comegas> for details of the method arguments.
454 # Level: Sub problem
455 end npthetas
457 start nth
458 # nth returns the number of thetas.
459 # See L</comegas> for details of the method arguments.
461 # Level: Sub problem
462 end nth
464 start ofvpath
465 # ofvpath returns the objective [function] values in the monitoring of search section.
466 # See L</comegas> for details of the method arguments.
468 # Level: Sub problem
469 end ofvpath
471 start ofv
472 # ofv returns the objective function value(s).
473 # See L</comegas> for details of the method arguments.
475 # Level: Sub problem
476 end ofv
478 start omega_block_structure
479 # omega_block_structure returns the block structure for
480 # the omega parameters in a lower triangular matrix form
481 # as in the OMEGA HAS BLOCK FORM section in the NONMEM output file.
482 # See L</comegas> for details of the method arguments.
484 # Level: Sub problem
485 end omega_block_structure
487 start omeganameval
488 # omeganameval returns (at the sub problem level) a hash
489 # with default parameter names , i.e. OM1, OM1_2 etc as keys
490 # and parameter estimates as values.
491 # See L</comegas> for details of the method arguments.
493 # Level: Sub problem
494 end omeganameval
496 start omeganames
497 # omeganames returns the default parameter names, e.g. OM1, OM1_2, OM2, etc
498 # See L</comegas> for details of the method arguments.
500 # Level: Sub problem
501 end omeganames
503 start omegas
504 # omegas returns the omega parameter estimates.
505 # See L</comegas> for details of the method arguments.
507 # Level: Sub problem
508 end omegas
510 start parameter_path
511 # parameter_path returns the (normalized) parameter estimates for each iteration in the monitoring of search section (Matrix returned).
512 # See L</comegas> for details of the method arguments.
514 # Level: Sub problem
515 end parameter_path
517 start pval
518 # pval returns the P VAL (reflects the probability that the etas are not centered around zero).
519 # See L</comegas> for details of the method arguments.
521 # Level: Sub problem
522 end pval
524 start raw_covmatrix
525 # raw_covmatrix returns the (raw) covariance matrix including empty matrix elements marked as '.........'.
526 # See L</comegas> for details of the method arguments.
528 # Level: Sub problem
529 end raw_covmatrix
531 start raw_invcovmatrix
532 # raw_invcovmatrix returns the (raw) inverse covariance matrix including empty matrix elements marked as '.........'.
533 # See L</comegas> for details of the method arguments.
535 # Level: Sub problem
536 end raw_invcovmatrix
538 start raw_cormatrix
539 # raw_cormatrix returns the (raw) correlation matrix including empty matrix elements marked as '.........'.
540 # See L</comegas> for details of the method arguments.
542 # Level: Sub problem
543 end raw_cormatrix
545 start raw_omegas
546 # raw_omegas returns the (raw) omegas.
547 # See L</comegas> for details of the method arguments.
549 # Level: Sub problem
550 end raw_omegas
552 start raw_seomegas
553 # raw_seomegas returns the (raw) omega standard error estimates.
554 # See L</comegas> for details of the method arguments.
556 # Level: Sub problem
557 end raw_seomegas
559 start raw_sesigmas
560 # raw_sesigmas returns the (raw) sigma standard error estimates.
561 # See L</comegas> for details of the method arguments.
563 # Level: Sub problem
564 end raw_sesigmas
566 start raw_sigmas
567 # raw_sigmas returns the (raw) sigmas.
568 # See L</comegas> for details of the method arguments.
570 # Level: Sub problem
571 end raw_sigmas
573 start raw_tmatrix
574 # raw_tmatrix returns the (raw) T-matrix.
575 # See L</comegas> for details of the method arguments.
577 # Level: Sub problem
578 end raw_tmatrix
580 start seomegas
581 # seomegas returns the omega standard error estimates.
582 # See L</comegas> for details of the method arguments.
584 # Level: Sub problem
585 end seomegas
587 start sesigmas
588 # sesigmas returns the sigma standard error estimates.
589 # See L</comegas> for details of the method arguments.
591 # Level: Sub problem
592 end sesigmas
594 start sethetas
595 # sethetas returns the theta standard error estimates.
596 # See L</comegas> for details of the method arguments.
598 # Level: Sub problem
599 end sethetas
601 start significant_digits
602 # significant_digits returns the number of significant digits for the model fit.
603 # See L</comegas> for details of the method arguments.
605 # Level: Sub problem
606 end significant_digits
608 start sigma_block_structure
609 # sigma_block_structure returns the block structure for
610 # the sigma parameters in a lower triangular matrix form
611 # as in the sigma HAS BLOCK FORM section in the NONMEM output file.
612 # See L</csigmas> for details of the method arguments.
614 # Level: Sub problem
615 end sigma_block_structure
617 start sigmanameval
618 # sigmanameval returns (at the sub problem level) a hash
619 # with default parameter names , i.e. SI1, SI1_2 etc as keys
620 # and parameter estimates as values.
621 # See L</comegas> for details of the method arguments.
623 # Level: Sub problem
624 end sigmanameval
626 start sigmanames
627 # sigmanames returns the default parameter names, i.e. SI1, SI1_2, SI2 etc.
628 # See L</comegas> for details of the method arguments.
630 # Level: Sub problem
631 end sigmanames
633 start sigmas
634 # sigmas returns the sigma parameter estimates.
635 # See L</comegas> for details of the method arguments.
637 # Level: Sub problem
638 end sigmas
640 start simulationstep
641 # simulationstep returns a boolean value 1 or 0, reflecting
642 # whether a simulation was performed or not. See L</comegas> for
643 # Details of the method arguments.
645 # Level: Sub Problem
646 end simulationstep
648 start minimization_successful
649 # minimization_successful returns a boolean value 1 or 0,
650 # reflecting whether the minimization was successful or not. See
651 # L</comegas> for details of the method arguments.
653 # Level: Sub Problem
654 end minimization_successful
656 start minimization_message
657 # minimization_message returns the minimization message, i.e
658 # MINIMIZATION SUCCESSFUL...
659 # See L</comegas> for details of the method arguments.
661 # Level: Sub problem
662 end minimization_message
664 start thetanameval
665 # thetanameval returns (at the sub problem level) a hash
666 # with default parameter names , i.e. TH1, TH2 etc as keys
667 # and parameter estimates as values.
668 # See L</comegas> for details of the method arguments.
670 # Level: Sub problem
671 end thetanameval
673 start thetanames
674 # thetanames returns the default theta parameter names, TH1, TH2 etc.
675 # See L</comegas> for details of the method arguments.
677 # Level: Sub problem
678 end thetanames
680 start thetas
681 # thetas returns the theta parameter estimates.
682 # See L</comegas> for details of the method arguments.
684 # Level: Sub problem
685 end thetas
687 # }}} Definitions and help text for all accessors
689 # {{{ have_output
691 start have_output
693 # have_output returns true if the output files exits or if there
694 # is output data in memory.
695 if( -e $self -> full_name || defined @{$self -> {'problems'}}){
696 return 1;
697 } else {
698 return 0;
701 end have_output
703 # }}} have_output
705 # {{{ _read_problems
707 start _read_problems
709 # This is a private method, and should not be used outside
710 # this file.
712 @{$self -> {'lstfile'}} = OSspecific::slurp_file($self-> full_name ) ;
714 $self -> {'lstfile_pos'} = 0;
716 # Old db code. Keep for now
717 # if ( $PsN::config -> {'_'} -> {'use_database'} and
718 # $self -> {'register_in_database'} and
719 # defined $self -> {'output_id'} ) {
720 # my $md5sum = md5_hex(@{$self -> {'lstfile'}});
721 # my $dbh = DBI -> connect("DBI:mysql:host=".$PsN::config -> {'_'} -> {'database_server'}.
722 # ";databse=".$PsN::config -> {'_'} -> {'project'},
723 # $PsN::config -> {'_'} -> {'user'},
724 # $PsN::config -> {'_'} -> {'password'},
725 # {'RaiseError' => 1});
726 # my $sth;
727 # my $sth = $dbh -> prepare( "UPDATE ".$PsN::config -> {'_'} -> {'project'}.
728 # ".output SET md5sum='$md5sum' ".
729 # "WHERE output_id = ".$self -> {'output_id'});
730 # $sth -> execute or debug -> die( message => $sth->errstr ) ;
731 # $sth -> finish;
733 # $dbh -> disconnect;
736 $self -> {'parsed_successfully'} = 1;
738 my $problem_start;
739 my $success = 0;
740 while ( $_ = @{$self -> {'lstfile'}}[ $self -> {'lstfile_pos'} ++ ] ) {
741 if ( /^ PROBLEM NO\.:\s+\d+\s+$/ or $self -> {'lstfile_pos'} >
742 $#{$self -> {'lstfile'}} ) {
743 if ( defined $problem_start ) {
744 my $adj = ($self -> {'lstfile_pos'} > $#{$self -> {'lstfile'}}) ? 1 : 2;
745 my @problem_lstfile =
746 @{$self -> {'lstfile'} } [$problem_start .. ($self ->
747 {'lstfile_pos'} - $adj)];
748 $self -> add_problem ( init_data =>
749 { lstfile => \@problem_lstfile,
750 output_id => $self -> {'output_id'},
751 model_id => $self -> {'model_id'} } );
752 @problem_lstfile = undef;
753 $success = 1;
755 my @problems = @{$self -> {'problems'}};
757 my $mes = $self -> parsing_error_message();
758 $mes .= $problems[$#problems] -> parsing_error_message();
759 $self -> parsing_error_message( $mes );
760 $self -> parsed_successfully($self -> parsed_successfully() *
761 $problems[$#problems] -> parsed_successfully());
763 $self -> msfo_has_terminated($self -> msfo_has_terminated() +
764 $problems[$#problems] -> msfo_has_terminated());
767 $problem_start = $self -> {'lstfile_pos' };
770 $self -> {'lstfile'} = undef;
771 unless( $success ){
772 debug -> warn( level => 1,
773 message => 'Could not find a PROBLEM NO statement in "' .
774 $self -> full_name . '"' . "\n" );
776 $self -> parsing_error( message => 'Could not find a PROBLEM NO statement in "' .
777 $self -> full_name . '"' . "\n" );
778 $self -> {'parsed_successfully'} = 0;
779 return 0;
782 $self -> {'parsed'} = 1;
784 end _read_problems
786 # }}} _read_problems
788 # {{{ parsing_error
789 start parsing_error
790 $self -> parsed_successfully( 0 );
791 $self -> parsing_error_message( $message );
792 end parsing_error
793 # }}} parsing_error
795 # {{{ access_any
797 start access_any
799 # You should not really use access_any but instead the
800 # specific selector for the information you want, such as
801 # L</sigmas>, L</raw_tmatrix> or similar.
804 # TODO: Add sanity checking of parameter values (more than
805 # the automatic). e.g check that parameter_numbers is a two-
806 # dimensional array.
808 if ( $self -> have_output ) {
809 unless ( defined $self -> {'problems'} and
810 scalar @{$self -> {'problems'}} > 0) {
811 $self -> _read_problems;
813 } else {
814 debug -> die( message => "Trying to access output object, that have no data on file(".
815 $self->full_name.") or in memory" );
818 my @own_problems;
819 if( defined $self -> {'problems'} ) {
820 unless( $#problems > 0 ){
821 debug -> warn(level => 2,
822 message => "Problems undefined, using all" );
823 @problems = (1 .. scalar @{$self -> {'problems'}});
825 @own_problems = @{$self -> {'problems'}};
826 } else {
827 return \@return_value; #Return the empty array
830 foreach my $i ( @problems ) {
831 if ( defined $own_problems[$i-1] ) {
832 if ( defined( $own_problems[$i-1] -> can( $attribute ) ) ) {
833 debug -> warn(level => 2,
834 message => "method $attribute defined on the problem level" );
835 my $meth_ret = $own_problems[$i-1] -> $attribute;
836 if ( ref ($meth_ret) ) {
837 my @prob_attr = @{$meth_ret};
838 if ( scalar @parameter_numbers > 0 ) {
839 my @tmp_arr = ();
840 foreach my $num ( @parameter_numbers ) {
841 if ( $num > 0 and $num <= scalar @prob_attr ) {
842 push( @tmp_arr, $prob_attr[$num-1] );
843 } else {
844 debug -> die( message => "( $attribute ): no such parameter number $num!".
845 "(".scalar @prob_attr." exists)" );
848 @prob_attr = @tmp_arr;
850 push( @return_value, \@prob_attr );
851 } else {
852 push( @return_value, $meth_ret ) if defined $meth_ret;
854 } else {
855 debug -> warn(level => 2,
856 message => "method $attribute defined on the subproblem level" );
857 my $problem_ret =
858 $own_problems[$i-1] ->
859 access_any( attribute => $attribute,
860 subproblems => \@subproblems,
861 parameter_numbers => \@parameter_numbers );
862 push( @return_value, $problem_ret ) if defined $problem_ret;
864 } else {
865 debug -> die( message => "No such problem ".($i-1) );
868 # Check the return_value to see if we have empty arrays
869 # if ( $#return_value == 0 and ref $return_value[0] and scalar @{$return_value[0]} < 1 ) {
870 # @return_value = ();
873 end access_any
875 # }}} access_any
877 # {{{ high_correlations
878 start high_correlations
880 my $correlation_matrix = $self -> correlation_matrix( problems => \@problems,
881 subproblems => \@subproblems );
882 my @thetanames = @{$self -> thetanames( problems => \@problems,
883 subproblems => \@subproblems )};
884 my @omeganames = @{$self -> omeganames( problems => \@problems,
885 subproblems => \@subproblems )};
886 my @sigmanames = @{$self -> sigmanames( problems => \@problems,
887 subproblems => \@subproblems )};
888 my @estimated_thetas = @{$self -> estimated_thetas( problems => \@problems,
889 subproblems => \@subproblems )};
890 my @estimated_omegas = @{$self -> estimated_omegas( problems => \@problems,
891 subproblems => \@subproblems )};
892 my @estimated_sigmas = @{$self -> estimated_sigmas( problems => \@problems,
893 subproblems => \@subproblems )};
895 for ( my $i = 0; $i < scalar @{$correlation_matrix}; $i++ ) {
896 my ( @prob_corr, @pf_corr );
897 my @names = ( @{$thetanames[$i]}, @{$omeganames[$i]}, @{$sigmanames[$i]} );
898 my @estimated = ( @{$estimated_thetas[$i]}, @{$estimated_omegas[$i]}, @{$estimated_sigmas[$i]} );
899 for ( my $j = 0; $j < scalar @{$correlation_matrix -> [$i]}; $j++ ) {
900 my ( @sp_corr, @spf_corr );;
901 my $idx = 0;
902 for ( my $row = 1; $row <= scalar @names; $row++ ) {
903 for ( my $col = 1; $col <= $row; $col++ ) {
904 if ( ( $estimated[$row-1] and $estimated[$col-1] ) ) {
905 if ( not ( $row == $col ) and
906 $correlation_matrix -> [$i][$j][$idx] > $limit or
907 $correlation_matrix -> [$i][$j][$idx] < -$limit ) {
908 push( @sp_corr, $names[$row-1]."-".$names[$col-1] );
909 push( @spf_corr, $correlation_matrix -> [$i][$j][$idx] );
911 $idx++;
916 # my @names = ( @{$thetanames[$i]}, @{$omeganames[$i]}, @{$sigmanames[$i]} );
917 # my ( @sp_corr, @spf_corr );;
918 # my ( $row, $col ) = ( 1, 1 );
919 # foreach my $element ( @{$correlation_matrix -> [$i][$j]} ) {
920 # if ( $col == $row ) {
921 # $row++;
922 # $col = 1;
923 # } else {
924 # if ( $element > $limit or $element < -$limit ) {
925 # push( @sp_corr, $names[$row-1]."-".$names[$col-1] );
926 # push( @spf_corr, $element );
928 # $col++;
932 push( @prob_corr, \@sp_corr );
933 push( @pf_corr, \@spf_corr );
935 push( @high_correlations, \@prob_corr );
936 push( @found_correlations, \@pf_corr );
939 end high_correlations
940 # }}} high_correlations
942 # {{{ large_standard_errors
943 start large_standard_errors
945 foreach my $param ( 'theta', 'omega', 'sigma' ) {
946 my @names = eval( '@{$self -> '.$param.'names( problems => \@problems,'.
947 'subproblems => \@subproblems )}' );
948 my @cvs = eval( '@{$self -> cvse'.$param.'s( problems => \@problems,'.
949 'subproblems => \@subproblems )}' );
950 for ( my $i = 0; $i <= $#cvs; $i++ ) {
951 if ( $param eq 'theta' ) {
952 $large_standard_errors[$i] = [];
953 $found_cv[$i] = [];
955 next unless( defined $cvs[$i] );
956 for ( my $j = 0; $j < scalar @{$cvs[$i]}; $j++ ) {
957 if ( $param eq 'theta' ) {
958 $large_standard_errors[$i][$j] = [];
959 $found_cv[$i][$j] = [];
961 next unless( defined $cvs[$i][$j] );
962 for ( my $k = 0; $k < scalar @{$cvs[$i][$j]}; $k++ ) {
963 if ( abs($cvs[$i][$j][$k]) > eval('$'.$param.'_cv_limit') ) {
964 push( @{$large_standard_errors[$i][$j]}, $names[$i][$k] );
965 push( @{$found_cv[$i][$j]}, $cvs[$i][$j][$k] );
972 end large_standard_errors
973 # }}} large_standard_errors
975 # {{{ near_bounds
977 start near_bounds
979 sub test_sigdig {
980 my ( $number, $goal, $sigdig, $zerolim ) = @_;
981 $number = &FormatSigFigs($number, $sigdig );
982 my $test;
983 if ( $goal == 0 ) {
984 $test = abs($number) < $zerolim ? 1 : 0;
985 } else {
986 $goal = &FormatSigFigs($goal, $sigdig );
987 $test = $number eq $goal ? 1 : 0;
989 return $test;
992 my @thetanames = @{$self -> thetanames};
993 my @omeganames = @{$self -> omeganames};
994 my @sigmanames = @{$self -> sigmanames};
997 my @indexes;
998 foreach my $param ( 'theta', 'omega', 'sigma' ) {
999 my $setm = eval( '$self -> '.$param.'s' );
1000 next unless( defined $setm );
1001 my @estimates = @{$setm};
1002 my @bounds = eval( '@{$self -> '.$param.'s}' );
1003 @indexes = eval( '@{$self -> '.$param.'_indexes}' ) unless ( $param eq 'theta' );
1004 for ( my $i = 0; $i <= $#estimates; $i++ ) {
1005 if ( $param eq 'theta' ) {
1006 $near_bounds[$i] = [];
1007 $found_bounds[$i] = [];
1008 $found_estimates[$i] = [];
1010 next unless( defined $estimates[$i] );
1011 for ( my $j = 0; $j < scalar @{$estimates[$i]}; $j++ ) {
1012 if ( $param eq 'theta' ) {
1013 $near_bounds[$i][$j] = [];
1014 $found_bounds[$i][$j] = [];
1015 $found_estimates[$i][$j] = [];
1017 next unless( defined $estimates[$i][$j] );
1018 for ( my $k = 0; $k < scalar @{$estimates[$i][$j]}; $k++ ) {
1019 # Unless the parameter is fixed:
1020 if ( not eval( '$self -> fixed'.$param.'s->[$i][$k]' ) ) {
1021 if ( $param eq 'theta' ) {
1022 if ( test_sigdig( $estimates[$i][$j][$k],
1023 $self -> lower_theta_bounds -> [$i][$k],
1024 $significant_digits, $zero_limit ) ) {
1025 push( @{$near_bounds[$i][$j]}, eval('$'.$param."names[$i][$k]") );
1026 push( @{$found_bounds[$i][$j]}, $self -> lower_theta_bounds -> [$i][$k] );
1027 push( @{$found_estimates[$i][$j]}, $estimates[$i][$j][$k] );
1029 if ( test_sigdig( $estimates[$i][$j][$k],
1030 $self -> upper_theta_bounds -> [$i][$k],
1031 $significant_digits, $zero_limit ) ) {
1032 push( @{$near_bounds[$i][$j]}, eval('$'.$param."names[$i][$k]") );
1033 push( @{$found_bounds[$i][$j]}, $self -> upper_theta_bounds -> [$i][$k] );
1034 push( @{$found_estimates[$i][$j]}, $estimates[$i][$j][$k] );
1036 } else {
1037 my ( $upper, $lower, $sigdig );
1038 if ( $indexes[$i][$k][0] == $indexes[$i][$k][1] ) { # on diagonal
1039 ( $lower, $upper, $sigdig ) = ( 0, 1000000, $significant_digits );
1040 } else {
1041 ( $lower, $upper, $sigdig ) = ( -1, 1, $off_diagonal_sign_digits );
1043 if ( test_sigdig( $estimates[$i][$j][$k], $lower, $sigdig, $zero_limit ) ) {
1044 push( @{$near_bounds[$i][$j]}, eval('$'.$param."names[$i][$k]" ) );
1045 push( @{$found_bounds[$i][$j]}, $lower );
1046 push( @{$found_estimates[$i][$j]}, $estimates[$i][$j][$k] );
1048 if ( test_sigdig( $estimates[$i][$j][$k], $upper, $sigdig, $zero_limit ) ) {
1049 push( @{$near_bounds[$i][$j]}, eval('$'.$param."names[$i][$k]" ) );
1050 push( @{$found_bounds[$i][$j]}, $upper );
1051 push( @{$found_estimates[$i][$j]}, $estimates[$i][$j][$k] );
1060 end near_bounds
1062 # }}} near_bounds
1064 # {{{ problem_structure
1066 start problem_structure
1068 my $flush = 0;
1069 unless( defined $self -> {'problems'} ) {
1070 # Try to read from disk
1071 $self -> _read_problems;
1072 $flush = 1;
1074 if( defined $self -> {'problems'} ) {
1075 for(my $problem = 0; $problem < @{$self -> {'problems'}}; $problem++ ){
1076 if( defined $self -> {'problems'} -> [$problem] -> {'subproblems'} ) {
1077 $structure[$problem] = scalar @{$self -> {'problems'} -> [$problem] -> {'subproblems'}};
1078 } else {
1079 # This is a case when the subproblem(s) could not be read.
1080 $structure[$problem] = 0;
1083 $self -> flush if( $flush );
1086 end problem_structure
1088 # }}}
1090 # {{{ labels
1091 start labels
1092 # labels is this far only a wrap-around for L</thetanames>,
1093 # L</omeganames> and L</sigmanames>
1094 # The functionality of these could be moved here later on.
1096 if ( not defined $parameter_type or
1097 $parameter_type eq '' ) {
1098 my @thetanames = @{$self -> thetanames};
1099 my @omeganames = @{$self -> omeganames};
1100 my @sigmanames = @{$self -> sigmanames};
1101 for ( my $i = 0; $i <= $#thetanames; $i++ ) {
1102 if( defined $thetanames[$i] ){
1103 push( @{$labels[$i]}, @{$thetanames[$i]} );
1105 if( defined $omeganames[$i] ){
1106 push( @{$labels[$i]}, @{$omeganames[$i]} );
1108 if( defined $sigmanames[$i] ){
1109 push( @{$labels[$i]}, @{$sigmanames[$i]} );
1112 } else {
1113 my $accessor = $parameter_type.'names';
1114 @labels = @{$self -> $accessor};
1117 end labels
1118 # }}} labels
1120 # {{{ flush
1121 start flush
1123 # flush is not an accessor method. As its name implies it flushes the
1124 # output objects memory by setting the I<problems> attribute to undef.
1125 # This method can be useful when many output objects are handled and
1126 # the memory is limited.
1128 # Flushes the object to save memory. There is no need to
1129 # synchronize the ouptut object before this since they are read-
1130 # only.
1133 $self -> {'problems'} = undef;
1134 $self -> {'synced'} = 0;
1136 end flush
1137 # }}} flush