Removed/changed some print statements
[PsN.git] / lib / output_subs.pm
blob2966d31ebd8a40fd98b343ca7fc7e24c99555f76
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 if ( defined $this -> {'filename'} and $this -> {'filename'} ne '' ) {
81 ( $this -> {'directory'}, $this -> {'filename'} ) =
82 OSspecific::absolute_path( $this -> {'directory'},$this->{'filename'} );
83 if( -e $this -> full_name ){
84 if($this -> {'target'} eq 'mem'){
85 $this -> _read_problems;
87 } else {
88 debug -> die( message => "The NONMEM output file ".
89 $this -> full_name." does not exist" )
90 unless $this -> {'ignore_missing_files'};
92 } else {
93 debug -> die( message => "No filename specified or filename equals empty string!" );
95 if( defined $this -> problems() ) {
96 my $mes = $this -> parsing_error_message();
97 foreach my $prob ( @{$this -> problems()} ) {
98 $mes .= $prob -> parsing_error_message();
99 $this -> parsed_successfully($this -> parsed_successfully() *
100 $prob -> parsed_successfully());
102 $this -> parsing_error_message( $mes );
104 if( defined $this -> parsing_error_message() and
105 $this -> parsing_error_message() ne '' ) {
106 print $this -> full_name,":\n",$this -> parsing_error_message();
108 if( defined $this -> problems() ) {
109 my $mes = $this -> parsing_error_message();
110 foreach my $prob ( @{$this -> problems()} ) {
111 $mes .= $prob -> parsing_error_message();
112 $this -> parsed_successfully($this -> parsed_successfully() *
113 $prob -> parsed_successfully());
115 $this -> parsing_error_message( $mes );
117 if( defined $this -> parsing_error_message() and
118 $this -> parsing_error_message() ne '' ) {
119 print $this -> full_name,":\n",$this -> parsing_error_message();
122 end new
124 # }}} new
126 # {{{ register_in_database
128 start register_in_database
129 if ( $PsN::config -> {'_'} -> {'use_database'} ) {
130 my $md5sum;
131 if( -e $self -> full_name ){
132 # md5sum
133 $md5sum = md5_hex(OSspecific::slurp_file($self-> full_name ));
135 # Backslashes messes up the sql syntax
136 my $file_str = $self->{'filename'};
137 my $dir_str = $self->{'directory'};
138 $file_str =~ s/\\/\//g;
139 $dir_str =~ s/\\/\//g;
141 my $dbh = DBI -> connect("DBI:mysql:host=".$PsN::config -> {'_'} -> {'database_server'}.
142 ";databse=".$PsN::config -> {'_'} -> {'project'},
143 $PsN::config -> {'_'} -> {'user'},
144 $PsN::config -> {'_'} -> {'password'},
145 {'RaiseError' => 1});
147 my $sth;
148 my $select_arr = [];
150 if ( not $force ) {
151 my $sth = $dbh -> prepare( "SELECT output_id FROM ".$PsN::config -> {'_'} -> {'project'}.
152 ".output ".
153 "WHERE filename = '$file_str' AND ".
154 "directory = '$dir_str' AND ".
155 "md5sum = '".$md5sum."'" );
156 $sth -> execute or debug -> die( message => $sth->errstr ) ;
158 $select_arr = $sth -> fetchall_arrayref;
161 if ( scalar @{$select_arr} > 0 ) {
162 debug -> warn( level => 1,
163 message => "Found an old entry in the database matching the ".
164 "current output file" );
165 if ( scalar @{$select_arr} > 1 ) {
166 debug -> warn( level => 1,
167 message => "Found more than one matching entry in database".
168 ", using the first" );
170 $self -> {'output_id'} = $select_arr->[0][0];
171 # Maybe we should update the table with a new model_id if such is supplied to us?
172 $self -> {'model_id'} = $select_arr->[0][1];
173 } else {
174 my ( $date_str, $time_str );
175 if ( $Config{osname} eq 'MSWin32' ) {
176 $date_str = `date /T`;
177 $time_str = ' '.`time /T`;
178 } else {
179 # Assuming UNIX
180 $date_str = `date`;
182 chomp($date_str);
183 chomp($time_str);
184 my $date_time = $date_str.$time_str;
185 my @mod_str = ('','');
186 if ( defined $model_id ) {
187 @mod_str = ('model_id, ',"$model_id, ");
189 $sth = $dbh -> prepare("INSERT INTO ".$PsN::config -> {'_'} -> {'project'}.
190 ".output ".
191 "( ".$mod_str[0].
192 "filename, date, directory, md5sum ) ".
193 "VALUES (".$mod_str[1].
194 "'$file_str', '$date_time', ".
195 "'$dir_str', '".$md5sum."' )");
197 $sth -> execute;
198 $self -> {'output_id'} = $sth->{'mysql_insertid'};
199 $self -> {'model_id'} = $model_id;
201 $sth -> finish;
202 $dbh -> disconnect;
203 if ( defined $self -> {'output_id'} ) {
204 foreach my $problem ( @{$self -> {'problems'}} ) {
205 $problem -> register_in_database( output_id => $self -> {'output_id'},
206 model_id => $model_id );
210 end register_in_database
212 # }}} register_in_database
214 # {{{ full_name
215 start full_name
217 $full_name = $self -> {'directory'} . $self -> {'filename'};
219 end full_name
221 # }}} full_name
223 # {{{ copy
224 start copy
226 $new_output = Storable::dclone( $self );
228 end copy
229 # }}} copy
231 # {{{ Definitions and help text for all accessors
233 start comegas
234 # Since PsN output objects are read-only, once they are
235 # initialized (probably through parsing a NONMEM output file) the
236 # methods of the output class are only used to extract
237 # information, not to set any.
239 # The general structure of the values returned by the methods
240 # reflect the level where the attributes belong (problems or sub
241 # problems) and of course also the structure of the attribute
242 # itself (scalar (ofv), array (thetas) or matrix
243 # (raw_cormatrix)). Taking ofv as example, this means that the
244 # returned variable will be a (reference to a) two-dimensional
245 # array, with the indexes problem and subproblem since ofv is a
246 # scalar on the sub problem level.
248 # Most methods take two optional arguments, I<problems> and
249 # I<subproblems>. These can be used to specify which problem or sub
250 # problem that the method should extract the required information
251 # from. problems and subproblems should be references to arrays of
252 # numbers. Some methods that return information related to model
253 # parameters also take I<parameter_numbers> as another optional
254 # argument and this can be used to specify a subset of parameters.
256 # Example:
258 # Return the standard errors for omega 1 and 3 (in all problems
259 # and sub problems)
261 # @seomega = @{$output_object -> seomegas( parameter_numbers => [1,3] )};
264 # comegas returns the standard deviation for elements on the
265 # diagonal and correlation coefficients for off-diagonal elements.
266 end comegas
268 start condition_number
269 # condition_number returns the 2-norm condition number for the correlation matrix, i.e.
270 # the largest eigen value divided by the smallest.
271 # See L</comegas> for details of the method arguments.
273 # Level: Sub problem
274 end condition_number
276 start covariance_step_run
277 # Returns 1 if the covariance step was run, 0 otherwise. See
278 # L</comegas> for details.
280 # Level: Problem
281 end covariance_step_run
283 start covariance_step_successful
284 # Returns 1 if the covariance step was successful, 0
285 # otherwise. See L</comegas> for details on the method arguments.
287 # Level: Sub problem
288 end covariance_step_successful
290 start covariance_step_warnings
291 # Returns 0 if there were no warnings or errors printed during the
292 # covariance step, 1 otherwise. See L</comegas> for details on the
293 # method arguments.
295 # Level: Sub problem
296 end covariance_step_warnings
298 start csigmas
299 # csigmas returns the standard deviation for elements on the
300 # diagonal and correlation coefficients for off-diagonal elements.
301 # See L</comegas> for details on the method arguments.
303 # Level: Sub problem
304 end csigmas
306 start cvseomegas
307 # cvseomegas returns the relative standard error for the omegas, i.e. SE/estimate.
308 # See L</comegas> for details on the method arguments.
310 # Level: Sub problem
311 end cvseomegas
313 start cvsesigmas
314 # cvsesigmas returns the relative standard error for the sigmas, i.e. SE/estimate.
315 # See L</comegas> for details on the method arguments.
317 # Level: Sub problem
318 end cvsesigmas
320 start cvsethetas
321 # cvsethetas returns the relative standard error for the thetas, i.e. SE/estimate.
322 # See L</comegas> for details on the method arguments.
324 # Level: Sub problem
325 end cvsethetas
327 start eigens
328 # eigens returns the eigen values.
329 # See L</comegas> for details of the method arguments.
331 # Level: Sub problem
332 end eigens
334 start etabar
335 # etabar returns the ETABAR estimates.
336 # See L</comegas> for details of the method arguments.
338 # Level: Sub problem
339 end etabar
341 start feval
342 # feval returns the number of function evaluations.
343 # See L</comegas> for details of the method arguments.
345 # Level: Sub problem
346 end feval
348 start finalparam
349 # finalparam returns the final parameter vector as it appears in the monitoring of search section.
350 # See L</comegas> for details of the method arguments.
352 # Level: Sub problem
353 end finalparam
355 start final_gradients
356 # final_gradients returns the final gradient vector as it appears in the monitoring of search section.
357 # See L</comegas> for details of the method arguments.
359 # Level: Sub problem
360 end final_gradients
362 start fixedomegas
363 # fixedomegas returns the a vector of booleans; 1's if
364 # the parameters were fixed during the model fit, 0's
365 # if they were not.
366 # See L</comegas> for details of the method arguments.
368 # Level: Sub problem
369 end fixedomegas
371 start fixedsigmas
372 # fixedsigmas returns the a vector of booleans; 1's if
373 # the parameters were fixed during the model fit, 0's
374 # if they were not.
375 # See L</comegas> for details of the method arguments.
377 # Level: Sub problem
378 end fixedsigmas
380 start fixedthetas
381 # fixedthetas returns the a vector of booleans; 1's if
382 # the parameters were fixed during the model fit, 0's
383 # if they were not.
384 # See L</comegas> for details of the method arguments.
386 # Level: Sub problem
387 end fixedthetas
389 start funcevalpath
390 # funcevalpath returns the number of function evaluations for each printed iteration in the monitoring of search section.
391 # See L</comegas> for details of the method arguments.
393 # Level: Sub problem
394 end funcevalpath
396 start gradient_path
397 # gradient_path returns the gradients for each printed iteration in the monitoring of search section (returns a matrix for each sub problem).
398 # See L</comegas> for details of the method arguments.
400 # Level: Sub problem
401 end gradient_path
403 start have_output
404 # Returns 1 if the output object is initialized, i.e. if the I<problems>
405 # or I<filename> attributes are set. Returns 0 otherwise.
406 end have_output
408 start initgrad
409 # initgrad returns the initial gradient vector in the monitoring of search section.
410 # See L</comegas> for details of the method arguments.
412 # Level: Sub problem
413 end initgrad
415 start initomegas
416 # initomegas returns the initial omega values.
417 # See L</comegas> for details of the method arguments.
419 # Level: Sub problem
420 end initomegas
422 start initsigmas
423 # initsigmas returns the initial sigma values.
424 # See L</comegas> for details of the method arguments.
426 # Level: Sub problem
427 end initsigmas
429 start initthetas
430 # initthetas returns the initial theta values.
431 # See L</comegas> for details of the method arguments.
433 # Level: Sub problem
434 end initthetas
436 start iternum
437 # iternum returns a vector of the iteration numbers in the monitoring of search section.
438 # See L</comegas> for details of the method arguments.
440 # Level: Sub problem
441 end iternum
443 start nind
444 # nind returns the number of individuals.
445 # See L</comegas> for details of the method arguments.
447 # Level: Problem
448 end nind
450 start nobs
451 # nobs returns the number of observations.
452 # See L</comegas> for details of the method arguments.
454 # Level: Problem
455 end nobs
457 start npofv
458 # npofv returns the non-parametric objective function value.
459 # See L</comegas> for details of the method arguments.
461 # Level: Sub problem
462 end npofv
464 start nrecs
465 # nrecs returns the number of records.
466 # See L</comegas> for details of the method arguments.
468 # Level: Problem
469 end nrecs
471 start npomegas
472 # npomegas returns the non-parametric omega estimates.
473 # See L</comegas> for details of the method arguments.
475 # Level: Sub problem
476 end npomegas
478 start npthetas
479 # npthetas returns the non-parametric theta estimates.
480 # See L</comegas> for details of the method arguments.
482 # Level: Sub problem
483 end npthetas
485 start nth
486 # nth returns the number of thetas.
487 # See L</comegas> for details of the method arguments.
489 # Level: Sub problem
490 end nth
492 start ofvpath
493 # ofvpath returns the objective [function] values in the monitoring of search section.
494 # See L</comegas> for details of the method arguments.
496 # Level: Sub problem
497 end ofvpath
499 start ofv
500 # ofv returns the objective function value(s).
501 # See L</comegas> for details of the method arguments.
503 # Level: Sub problem
504 end ofv
506 start omega_block_structure
507 # omega_block_structure returns the block structure for
508 # the omega parameters in a lower triangular matrix form
509 # as in the OMEGA HAS BLOCK FORM section in the NONMEM output file.
510 # See L</comegas> for details of the method arguments.
512 # Level: Sub problem
513 end omega_block_structure
515 start omeganameval
516 # omeganameval returns (at the sub problem level) a hash
517 # with default parameter names , i.e. OM1, OM1_2 etc as keys
518 # and parameter estimates as values.
519 # See L</comegas> for details of the method arguments.
521 # Level: Sub problem
522 end omeganameval
524 start omeganames
525 # omeganames returns the default parameter names, e.g. OM1, OM1_2, OM2, etc
526 # See L</comegas> for details of the method arguments.
528 # Level: Sub problem
529 end omeganames
531 start omegas
532 # omegas returns the omega parameter estimates.
533 # See L</comegas> for details of the method arguments.
535 # Level: Sub problem
536 end omegas
538 start parameter_path
539 # parameter_path returns the (normalized) parameter estimates for each iteration in the monitoring of search section (Matrix returned).
540 # See L</comegas> for details of the method arguments.
542 # Level: Sub problem
543 end parameter_path
545 start pval
546 # pval returns the P VAL (reflects the probability that the etas are not centered around zero).
547 # See L</comegas> for details of the method arguments.
549 # Level: Sub problem
550 end pval
552 start raw_covmatrix
553 # raw_covmatrix returns the (raw) covariance matrix including empty matrix elements marked as '.........'.
554 # See L</comegas> for details of the method arguments.
556 # Level: Sub problem
557 end raw_covmatrix
559 start raw_invcovmatrix
560 # raw_invcovmatrix returns the (raw) inverse covariance matrix including empty matrix elements marked as '.........'.
561 # See L</comegas> for details of the method arguments.
563 # Level: Sub problem
564 end raw_invcovmatrix
566 start raw_cormatrix
567 # raw_cormatrix returns the (raw) correlation matrix including empty matrix elements marked as '.........'.
568 # See L</comegas> for details of the method arguments.
570 # Level: Sub problem
571 end raw_cormatrix
573 start raw_omegas
574 # raw_omegas returns the (raw) omegas.
575 # See L</comegas> for details of the method arguments.
577 # Level: Sub problem
578 end raw_omegas
580 start raw_seomegas
581 # raw_seomegas returns the (raw) omega standard error estimates.
582 # See L</comegas> for details of the method arguments.
584 # Level: Sub problem
585 end raw_seomegas
587 start raw_sesigmas
588 # raw_sesigmas returns the (raw) sigma standard error estimates.
589 # See L</comegas> for details of the method arguments.
591 # Level: Sub problem
592 end raw_sesigmas
594 start raw_sigmas
595 # raw_sigmas returns the (raw) sigmas.
596 # See L</comegas> for details of the method arguments.
598 # Level: Sub problem
599 end raw_sigmas
601 start raw_tmatrix
602 # raw_tmatrix returns the (raw) T-matrix.
603 # See L</comegas> for details of the method arguments.
605 # Level: Sub problem
606 end raw_tmatrix
608 start seomegas
609 # seomegas returns the omega standard error estimates.
610 # See L</comegas> for details of the method arguments.
612 # Level: Sub problem
613 end seomegas
615 start sesigmas
616 # sesigmas returns the sigma standard error estimates.
617 # See L</comegas> for details of the method arguments.
619 # Level: Sub problem
620 end sesigmas
622 start sethetas
623 # sethetas returns the theta standard error estimates.
624 # See L</comegas> for details of the method arguments.
626 # Level: Sub problem
627 end sethetas
629 start significant_digits
630 # significant_digits returns the number of significant digits for the model fit.
631 # See L</comegas> for details of the method arguments.
633 # Level: Sub problem
634 end significant_digits
636 start sigma_block_structure
637 # sigma_block_structure returns the block structure for
638 # the sigma parameters in a lower triangular matrix form
639 # as in the sigma HAS BLOCK FORM section in the NONMEM output file.
640 # See L</csigmas> for details of the method arguments.
642 # Level: Sub problem
643 end sigma_block_structure
645 start sigmanameval
646 # sigmanameval returns (at the sub problem level) a hash
647 # with default parameter names , i.e. SI1, SI1_2 etc as keys
648 # and parameter estimates as values.
649 # See L</comegas> for details of the method arguments.
651 # Level: Sub problem
652 end sigmanameval
654 start sigmanames
655 # sigmanames returns the default parameter names, i.e. SI1, SI1_2, SI2 etc.
656 # See L</comegas> for details of the method arguments.
658 # Level: Sub problem
659 end sigmanames
661 start sigmas
662 # sigmas returns the sigma parameter estimates.
663 # See L</comegas> for details of the method arguments.
665 # Level: Sub problem
666 end sigmas
668 start simulationstep
669 # simulationstep returns a boolean value 1 or 0, reflecting
670 # whether a simulation was performed or not. See L</comegas> for
671 # Details of the method arguments.
673 # Level: Sub Problem
674 end simulationstep
676 start minimization_successful
677 # minimization_successful returns a boolean value 1 or 0,
678 # reflecting whether the minimization was successful or not. See
679 # L</comegas> for details of the method arguments.
681 # Level: Sub Problem
682 end minimization_successful
684 start minimization_message
685 # minimization_message returns the minimization message, i.e
686 # MINIMIZATION SUCCESSFUL...
687 # See L</comegas> for details of the method arguments.
689 # Level: Sub problem
690 end minimization_message
692 start thetanameval
693 # thetanameval returns (at the sub problem level) a hash
694 # with default parameter names , i.e. TH1, TH2 etc as keys
695 # and parameter estimates as values.
696 # See L</comegas> for details of the method arguments.
698 # Level: Sub problem
699 end thetanameval
701 start thetanames
702 # thetanames returns the default theta parameter names, TH1, TH2 etc.
703 # See L</comegas> for details of the method arguments.
705 # Level: Sub problem
706 end thetanames
708 start thetas
709 # thetas returns the theta parameter estimates.
710 # See L</comegas> for details of the method arguments.
712 # Level: Sub problem
713 end thetas
715 # }}} Definitions and help text for all accessors
717 # {{{ have_output
718 start have_output
720 # have_output returns true if the output files exits or if there
721 # is output data in memory.
722 if( -e $self -> full_name || defined @{$self -> {'problems'}}){
723 return 1;
724 } else {
725 return 0;
728 end have_output
729 # }}} have_output
731 # {{{ _read_problems
733 start _read_problems
735 # This is a private method, and should not be used outside
736 # this file.
738 @{$self -> {'lstfile'}} = OSspecific::slurp_file($self-> full_name ) ;
740 $self -> {'lstfile_pos'} = 0;
742 # {{{ Old db code. Keep for now
743 # if ( $PsN::config -> {'_'} -> {'use_database'} and
744 # $self -> {'register_in_database'} and
745 # defined $self -> {'output_id'} ) {
746 # my $md5sum = md5_hex(@{$self -> {'lstfile'}});
747 # my $dbh = DBI -> connect("DBI:mysql:host=".$PsN::config -> {'_'} -> {'database_server'}.
748 # ";databse=".$PsN::config -> {'_'} -> {'project'},
749 # $PsN::config -> {'_'} -> {'user'},
750 # $PsN::config -> {'_'} -> {'password'},
751 # {'RaiseError' => 1});
752 # my $sth;
753 # my $sth = $dbh -> prepare( "UPDATE ".$PsN::config -> {'_'} -> {'project'}.
754 # ".output SET md5sum='$md5sum' ".
755 # "WHERE output_id = ".$self -> {'output_id'});
756 # $sth -> execute or debug -> die( message => $sth->errstr ) ;
757 # $sth -> finish;
759 # $dbh -> disconnect;
761 # }}}
763 my $problem_start;
764 my $success = 0;
765 while ( $_ = @{$self -> {'lstfile'}}[ $self -> {'lstfile_pos'} ++ ] ) {
766 if ( /^ PROBLEM NO\.:\s+\d+\s+$/ or $self -> {'lstfile_pos'} >
767 $#{$self -> {'lstfile'}} ) {
768 if ( defined $problem_start ) {
769 my $adj = ($self -> {'lstfile_pos'} > $#{$self -> {'lstfile'}}) ? 1 : 2;
770 my @problem_lstfile =
771 @{$self -> {'lstfile'} } [$problem_start .. ($self ->
772 {'lstfile_pos'} - $adj)];
773 $self -> add_problem ( init_data =>
774 { lstfile => \@problem_lstfile,
775 output_id => $self -> {'output_id'},
776 model_id => $self -> {'model_id'} } );
777 @problem_lstfile = undef;
778 $success = 1;
780 $problem_start = $self -> {'lstfile_pos' };
783 $self -> {'lstfile'} = undef;
784 unless( $success ){
785 debug -> warn( level => 1,
786 message => 'Could not find a PROBLEM NO statement in "' .
787 $self -> full_name . '"' . "\n" );
788 $self -> parsing_error( message => 'Could not find a PROBLEM NO statement in "' .
789 $self -> full_name . '"' . "\n" );
790 return 0;
791 } else {
792 $self -> {'parsed_successfully'} = 1;
794 $self -> {'parsed'} = 1;
796 end _read_problems
798 # }}} _read_problems
800 # {{{ parsing_error
801 start parsing_error
802 $self -> parsed_successfully( 0 );
803 $self -> parsing_error_message( $message );
804 end parsing_error
805 # }}} parsing_error
807 # {{{ access_any
809 start access_any
811 # You should not really use access_any but instead the
812 # specific selector for the information you want, such as
813 # L</sigmas>, L</raw_tmatrix> or similar.
816 # TODO: Add sanity checking of parameter values (more than
817 # the automatic). e.g check that parameter_numbers is a two-
818 # dimensional array.
820 if ( $self -> have_output ) {
821 unless ( defined $self -> {'problems'} and
822 scalar @{$self -> {'problems'}} > 0) {
823 $self -> _read_problems;
825 } else {
826 debug -> die( message => "Trying to access output object, that have no data on file(".
827 $self->full_name.") or in memory" );
830 my @own_problems;
831 if( defined $self -> {'problems'} ) {
832 unless( $#problems > 0 ){
833 debug -> warn(level => 2,
834 message => "Problems undefined, using all" );
835 @problems = (1 .. scalar @{$self -> {'problems'}});
837 @own_problems = @{$self -> {'problems'}};
838 } else {
839 return \@return_value; #Return the empty array
842 foreach my $i ( @problems ) {
843 if ( defined $own_problems[$i-1] ) {
844 if ( defined( $own_problems[$i-1] -> can( $attribute ) ) ) {
845 debug -> warn(level => 2,
846 message => "method $attribute defined on the problem level" );
847 my $meth_ret = $own_problems[$i-1] -> $attribute;
848 if ( ref ($meth_ret) ) {
849 my @prob_attr = @{$meth_ret};
850 if ( scalar @parameter_numbers > 0 ) {
851 my @tmp_arr = ();
852 foreach my $num ( @parameter_numbers ) {
853 if ( $num > 0 and $num <= scalar @prob_attr ) {
854 push( @tmp_arr, $prob_attr[$num-1] );
855 } else {
856 debug -> die( message => "( $attribute ): no such parameter number $num!".
857 "(".scalar @prob_attr." exists)" );
860 @prob_attr = @tmp_arr;
862 push( @return_value, \@prob_attr );
863 } else {
864 push( @return_value, $meth_ret ) if defined $meth_ret;
866 } else {
867 debug -> warn(level => 2,
868 message => "method $attribute defined on the subproblem level" );
869 my $problem_ret =
870 $own_problems[$i-1] ->
871 access_any( attribute => $attribute,
872 subproblems => \@subproblems,
873 parameter_numbers => \@parameter_numbers );
874 push( @return_value, $problem_ret ) if defined $problem_ret;
876 } else {
877 debug -> die( message => "No such problem ".($i-1) );
880 # Check the return_value to see if we have empty arrays
881 # if ( $#return_value == 0 and ref $return_value[0] and scalar @{$return_value[0]} < 1 ) {
882 # @return_value = ();
885 end access_any
887 # }}} access_any
889 # {{{ high_correlations
890 start high_correlations
892 my $correlation_matrix = $self -> correlation_matrix( problems => \@problems,
893 subproblems => \@subproblems );
894 my @thetanames = @{$self -> thetanames( problems => \@problems,
895 subproblems => \@subproblems )};
896 my @omeganames = @{$self -> omeganames( problems => \@problems,
897 subproblems => \@subproblems )};
898 my @sigmanames = @{$self -> sigmanames( problems => \@problems,
899 subproblems => \@subproblems )};
900 my @estimated_thetas = @{$self -> estimated_thetas( problems => \@problems,
901 subproblems => \@subproblems )};
902 my @estimated_omegas = @{$self -> estimated_omegas( problems => \@problems,
903 subproblems => \@subproblems )};
904 my @estimated_sigmas = @{$self -> estimated_sigmas( problems => \@problems,
905 subproblems => \@subproblems )};
907 for ( my $i = 0; $i < scalar @{$correlation_matrix}; $i++ ) {
908 my ( @prob_corr, @pf_corr );
909 my @names = ( @{$thetanames[$i]}, @{$omeganames[$i]}, @{$sigmanames[$i]} );
910 my @estimated = ( @{$estimated_thetas[$i]}, @{$estimated_omegas[$i]}, @{$estimated_sigmas[$i]} );
911 for ( my $j = 0; $j < scalar @{$correlation_matrix -> [$i]}; $j++ ) {
912 my ( @sp_corr, @spf_corr );;
913 my $idx = 0;
914 for ( my $row = 1; $row <= scalar @names; $row++ ) {
915 for ( my $col = 1; $col <= $row; $col++ ) {
916 if ( ( $estimated[$row-1] and $estimated[$col-1] ) ) {
917 if ( not ( $row == $col ) and
918 $correlation_matrix -> [$i][$j][$idx] > $limit or
919 $correlation_matrix -> [$i][$j][$idx] < -$limit ) {
920 push( @sp_corr, $names[$row-1]."-".$names[$col-1] );
921 push( @spf_corr, $correlation_matrix -> [$i][$j][$idx] );
923 $idx++;
928 # my @names = ( @{$thetanames[$i]}, @{$omeganames[$i]}, @{$sigmanames[$i]} );
929 # my ( @sp_corr, @spf_corr );;
930 # my ( $row, $col ) = ( 1, 1 );
931 # foreach my $element ( @{$correlation_matrix -> [$i][$j]} ) {
932 # if ( $col == $row ) {
933 # $row++;
934 # $col = 1;
935 # } else {
936 # if ( $element > $limit or $element < -$limit ) {
937 # push( @sp_corr, $names[$row-1]."-".$names[$col-1] );
938 # push( @spf_corr, $element );
940 # $col++;
944 push( @prob_corr, \@sp_corr );
945 push( @pf_corr, \@spf_corr );
947 push( @high_correlations, \@prob_corr );
948 push( @found_correlations, \@pf_corr );
951 end high_correlations
952 # }}} high_correlations
954 # {{{ large_standard_errors
955 start large_standard_errors
957 foreach my $param ( 'theta', 'omega', 'sigma' ) {
958 my @names = eval( '@{$self -> '.$param.'names( problems => \@problems,'.
959 'subproblems => \@subproblems )}' );
960 my @cvs = eval( '@{$self -> cvse'.$param.'s( problems => \@problems,'.
961 'subproblems => \@subproblems )}' );
962 for ( my $i = 0; $i <= $#cvs; $i++ ) {
963 if ( $param eq 'theta' ) {
964 $large_standard_errors[$i] = [];
965 $found_cv[$i] = [];
967 next unless( defined $cvs[$i] );
968 for ( my $j = 0; $j < scalar @{$cvs[$i]}; $j++ ) {
969 if ( $param eq 'theta' ) {
970 $large_standard_errors[$i][$j] = [];
971 $found_cv[$i][$j] = [];
973 next unless( defined $cvs[$i][$j] );
974 for ( my $k = 0; $k < scalar @{$cvs[$i][$j]}; $k++ ) {
975 if ( abs($cvs[$i][$j][$k]) > eval('$'.$param.'_cv_limit') ) {
976 push( @{$large_standard_errors[$i][$j]}, $names[$i][$k] );
977 push( @{$found_cv[$i][$j]}, $cvs[$i][$j][$k] );
984 end large_standard_errors
985 # }}} large_standard_errors
987 # {{{ near_bounds
989 start near_bounds
991 sub test_sigdig {
992 my ( $number, $goal, $sigdig, $zerolim ) = @_;
993 $number = &FormatSigFigs($number, $sigdig );
994 my $test;
995 if ( $goal == 0 ) {
996 $test = abs($number) < $zerolim ? 1 : 0;
997 } else {
998 $goal = &FormatSigFigs($goal, $sigdig );
999 $test = $number eq $goal ? 1 : 0;
1001 return $test;
1004 my @thetanames = @{$self -> thetanames};
1005 my @omeganames = @{$self -> omeganames};
1006 my @sigmanames = @{$self -> sigmanames};
1009 my @indexes;
1010 foreach my $param ( 'theta', 'omega', 'sigma' ) {
1011 my $setm = eval( '$self -> '.$param.'s' );
1012 next unless( defined $setm );
1013 my @estimates = @{$setm};
1014 my @bounds = eval( '@{$self -> '.$param.'s}' );
1015 @indexes = eval( '@{$self -> '.$param.'_indexes}' ) unless ( $param eq 'theta' );
1016 for ( my $i = 0; $i <= $#estimates; $i++ ) {
1017 if ( $param eq 'theta' ) {
1018 $near_bounds[$i] = [];
1019 $found_bounds[$i] = [];
1020 $found_estimates[$i] = [];
1022 next unless( defined $estimates[$i] );
1023 for ( my $j = 0; $j < scalar @{$estimates[$i]}; $j++ ) {
1024 if ( $param eq 'theta' ) {
1025 $near_bounds[$i][$j] = [];
1026 $found_bounds[$i][$j] = [];
1027 $found_estimates[$i][$j] = [];
1029 next unless( defined $estimates[$i][$j] );
1030 for ( my $k = 0; $k < scalar @{$estimates[$i][$j]}; $k++ ) {
1031 # Unless the parameter is fixed:
1032 if ( not eval( '$self -> fixed'.$param.'s->[$i][$k]' ) ) {
1033 if ( $param eq 'theta' ) {
1034 if ( test_sigdig( $estimates[$i][$j][$k],
1035 $self -> lower_theta_bounds -> [$i][$k],
1036 $significant_digits, $zero_limit ) ) {
1037 push( @{$near_bounds[$i][$j]}, eval('$'.$param."names[$i][$k]") );
1038 push( @{$found_bounds[$i][$j]}, $self -> lower_theta_bounds -> [$i][$k] );
1039 push( @{$found_estimates[$i][$j]}, $estimates[$i][$j][$k] );
1041 if ( test_sigdig( $estimates[$i][$j][$k],
1042 $self -> upper_theta_bounds -> [$i][$k],
1043 $significant_digits, $zero_limit ) ) {
1044 push( @{$near_bounds[$i][$j]}, eval('$'.$param."names[$i][$k]") );
1045 push( @{$found_bounds[$i][$j]}, $self -> upper_theta_bounds -> [$i][$k] );
1046 push( @{$found_estimates[$i][$j]}, $estimates[$i][$j][$k] );
1048 } else {
1049 my ( $upper, $lower, $sigdig );
1050 if ( $indexes[$i][$k][0] == $indexes[$i][$k][1] ) { # on diagonal
1051 ( $lower, $upper, $sigdig ) = ( 0, 1000000, $significant_digits );
1052 } else {
1053 ( $lower, $upper, $sigdig ) = ( -1, 1, $off_diagonal_sign_digits );
1055 if ( test_sigdig( $estimates[$i][$j][$k], $lower, $sigdig, $zero_limit ) ) {
1056 push( @{$near_bounds[$i][$j]}, eval('$'.$param."names[$i][$k]" ) );
1057 push( @{$found_bounds[$i][$j]}, $lower );
1058 push( @{$found_estimates[$i][$j]}, $estimates[$i][$j][$k] );
1060 if ( test_sigdig( $estimates[$i][$j][$k], $upper, $sigdig, $zero_limit ) ) {
1061 push( @{$near_bounds[$i][$j]}, eval('$'.$param."names[$i][$k]" ) );
1062 push( @{$found_bounds[$i][$j]}, $upper );
1063 push( @{$found_estimates[$i][$j]}, $estimates[$i][$j][$k] );
1072 end near_bounds
1074 # }}} near_bounds
1076 # {{{ problem_structure
1077 start problem_structure
1079 my $flush = 0;
1080 unless( defined $self -> {'problems'} ) {
1081 # Try to read from disk
1082 $self -> _read_problems;
1083 $flush = 1;
1085 if( defined $self -> {'problems'} ) {
1086 for(my $problem = 0; $problem < @{$self -> {'problems'}}; $problem++ ){
1087 if( defined $self -> {'problems'} -> [$problem] -> {'subproblems'} ) {
1088 $structure[$problem] = scalar @{$self -> {'problems'} -> [$problem] -> {'subproblems'}};
1089 } else {
1090 # This is a case when the subproblem(s) could not be read.
1091 $structure[$problem] = 0;
1094 $self -> flush if( $flush );
1097 end problem_structure
1098 # }}}
1100 # {{{ labels
1101 start labels
1102 # labels is this far only a wrap-around for L</thetanames>,
1103 # L</omeganames> and L</sigmanames>
1104 # The functionality of these could be moved here later on.
1106 if ( not defined $parameter_type or
1107 $parameter_type eq '' ) {
1108 my @thetanames = @{$self -> thetanames};
1109 my @omeganames = @{$self -> omeganames};
1110 my @sigmanames = @{$self -> sigmanames};
1111 for ( my $i = 0; $i <= $#thetanames; $i++ ) {
1112 if( defined $thetanames[$i] ){
1113 push( @{$labels[$i]}, @{$thetanames[$i]} );
1115 if( defined $omeganames[$i] ){
1116 push( @{$labels[$i]}, @{$omeganames[$i]} );
1118 if( defined $sigmanames[$i] ){
1119 push( @{$labels[$i]}, @{$sigmanames[$i]} );
1122 } else {
1123 my $accessor = $parameter_type.'names';
1124 @labels = @{$self -> $accessor};
1127 end labels
1128 # }}} labels
1130 # {{{ flush
1131 start flush
1133 # flush is not an accessor method. As its name implies it flushes the
1134 # output objects memory by setting the I<problems> attribute to undef.
1135 # This method can be useful when many output objects are handled and
1136 # the memory is limited.
1138 # Flushes the object to save memory. There is no need to
1139 # synchronize the ouptut object before this since they are read-
1140 # only.
1143 $self -> {'problems'} = undef;
1145 end flush
1146 # }}} flush