1 # TODO: All: 2004-09-06 Fix absolute paths for data and output files. (under both
6 start include statements
7 # A Perl module for parsing and manipulating NONMEM model files
10 use Digest
::MD5
'md5_hex';
17 use POSIX
qw(ceil floor);
18 end include statements
20 # }}} include statements
22 # {{{ description, synopsis and see_also
24 # No method, just documentation
26 # The model class is built around the NONMEM model file. This is an
27 # ordinary ASCII text file that, except for the data, holds all
28 # information needed for fitting a non-linear mixed effect model
29 # using NONMEM. Typically, a model file contains specifications
30 # for a pharmacokinetic and/or a pharmacodynamic model, initial
31 # estimates of model parameters, boundaries for model parameters
32 # as well as details about the data location and format.
36 # Note that the functionality for actually running NONMEM has been
37 # moved to <a href="tool/modelfit.html">tool::modelfit</a>.
43 # Note that the functionality for actually running NONMEM has been
44 # moved to I<tool::modelfit>.
52 # my $mod_obj = model -> new ( filename => 'run1.mod' );
54 # $mod_obj -> datafiles ( new_names => ['test040314.dta'] );
56 # $mod_obj -> initial_values ( parameter_type => 'theta',
57 # parameter_numbers => [[1,3]],
58 # new_values => [[1.2,34]] );
64 # <a HREF="data.html">data</a>, <a HREF="output.html">output</a>
65 # <a HREF="tool/modelfit.html">tool::modelfit</a>,
66 # <a HREF="tool.html">tool</a>
72 # data, output, tool::modelfit, tool
94 # $model = model -> new( filename => 'run1.mod' )
96 # This is the simplest and most common way to create a model
97 # object and it requires a file on disk.
99 # $model = model -> new( filename => 'run1.mod',
102 # If the target parameter is set to anything other than I<mem>
103 # the output object (with file name given by the model
104 # attribute I<outputfile>) and the data objects (identified by
105 # the data file names in the $DATA NONMEM model file section)
106 # will be initialized but will contain no information from
107 # their files. If information from them are requiered later
108 # on, they are read and parsed and the appropriate attributes
109 # of the data and output objects are set.
113 # See I<data> and I<output> for details.
119 # See <a href="data.html">data</a> and <a href="output.html">
120 # output</a> for details.
124 if ( defined $parm{'problems'} ) {
125 $this -> {'problems'} = $parm{'problems'};
127 ($this -> {'directory'}, $this -> {'filename'}) =
128 OSspecific
::absolute_path
( $this -> {'directory'}, $this -> {'filename'} );
129 $this -> _read_problems
;
130 $this -> {'synced'} = 1;
133 if ( defined $parm{'active_problems'} ) {
134 $this -> {'active_problems'} = $parm{'active_problems'};
135 } elsif ( defined $this -> {'problems'} ) {
137 for ( @
{$this -> {'problems'}} ) {
140 $this -> {'active_problems'} = \
@active;
143 if ( defined $this -> {'extra_data_files'} ){
144 for( my $i; $i < scalar @
{$this -> {'extra_data_files'}}; $i++ ){
145 my ( $dir, $file ) = OSspecific
::absolute_path
( $this -> {'directory'}, $this -> {'extra_data_files'} -> [$i]);
146 $this -> {'extra_data_files'} -> [$i] = $dir . $file;
150 my $subroutine_files = $this -> subroutine_files
;
151 if( defined $subroutine_files and scalar @
{$subroutine_files} > 0 ){
152 push( @
{$this -> {'extra_files'}}, @
{$subroutine_files} );
155 if ( defined $this -> {'extra_files'} ){
156 for( my $i; $i < scalar @
{$this -> {'extra_files'}}; $i++ ){
157 my ( $dir, $file ) = OSspecific
::absolute_path
( $this -> {'directory'}, $this -> {'extra_files'} -> [$i]);
158 $this -> {'extra_files'} -> [$i] = $dir . $file;
162 # Read datafiles, if any.
163 unless( defined $this -> {'datas'} and not $this -> {'quick_reload'} ){
164 my @idcolumns = @
{$this -> idcolumns
};
165 my @datafiles = @
{$this -> datafiles
('absolute_path' => 1)};
166 # my @datafiles = @{$this -> datafiles('absolute_path' => 0)};
167 for ( my $i = 0; $i <= $#datafiles; $i++ ) {
168 my $datafile = $datafiles[$i];
169 my $idcolumn = $idcolumns[$i];
170 my ( $cont_column, $wrap_column ) = $this -> {'problems'} -> [$i] -> cont_wrap_columns
;
171 my $ignoresign = defined $this -> ignoresigns ?
$this -> ignoresigns
-> [$i] : undef;
172 my @model_header = @
{$this -> {'problems'} -> [$i] -> header
};
173 if ( defined $idcolumn ) {
174 push ( @
{$this -> {'datas'}}, data
->
175 new
( idcolumn
=> $idcolumn,
176 filename
=> $datafile,
177 cont_column
=> $cont_column,
178 wrap_column
=> $wrap_column,
179 #model_header => \@model_header,
180 ignoresign
=> $ignoresign,
181 directory
=> $this -> {'directory'},
182 ignore_missing_files
=> $this -> {'ignore_missing_files'} ||
183 $this -> {'ignore_missing_data'},
184 target
=> $this -> {'target'}) );
186 'debug' -> die( message
=> "Model -> new: Both idcolumn and datafile must ".
187 "be specified to create a model object." );
192 # Read outputfile, if any.
193 if( ! defined $this -> {'outputs'} ) {
194 unless( defined $this -> {'outputfile'} ){
195 ($this -> {'outputfile'} = $this -> {'filename'}) =~ s/\.mod$/.lst/;
197 push ( @
{$this -> {'outputs'}}, output
->
198 new
( filename
=> $this -> {'outputfile'},
199 directory
=> $this -> {'directory'},
200 ignore_missing_files
=>
201 $this -> {'ignore_missing_files'} || $this -> {'ignore_missing_output_files'},
202 target
=> $this -> {'target'},
203 model_id
=> $this -> {'model_id'} ) );
210 # {{{ register_in_database
212 start register_in_database
214 if ( $PsN::config
-> {'_'} -> {'use_database'} ) {
215 # Backslashes messes up the sql syntax
216 my $file_str = $self->{'filename'};
217 my $dir_str = $self->{'directory'};
218 $file_str =~ s/\\/\//g
;
219 $dir_str =~ s/\\/\//g
;
222 my $md5sum = md5_hex
(OSspecific
::slurp_file
($self-> full_name
));
224 my $dbh = DBI
-> connect("DBI:mysql:host=".$PsN::config
-> {'_'} -> {'database_server'}.
225 ";databse=".$PsN::config
-> {'_'} -> {'project'},
226 $PsN::config
-> {'_'} -> {'user'},
227 $PsN::config
-> {'_'} -> {'password'},
228 {'RaiseError' => 1});
235 my $sth = $dbh -> prepare
( "SELECT model_id FROM ".$PsN::config
-> {'_'} -> {'project'}.
237 "WHERE filename = '$file_str' AND ".
238 "directory = '$dir_str' AND ".
239 "md5sum = '".$md5sum."'" );
240 $sth -> execute
or 'debug' -> die( message
=> $sth->errstr ) ;
242 $select_arr = $sth -> fetchall_arrayref
;
245 if ( scalar @
{$select_arr} > 0 ) {
246 'debug' -> warn( level
=> 1,
247 message
=> "Found an old entry in the database matching the ".
248 "current model file" );
249 if ( scalar @
{$select_arr} > 1 ) {
250 'debug' -> warn( level
=> 1,
251 message
=> "Found more than one matching entry in database".
252 ", using the first" );
254 $self -> {'model_id'} = $select_arr->[0][0];
256 my ( $date_str, $time_str );
257 if( $Config{osname
} eq 'MSWin32' ){
258 $date_str = `date /T`;
259 $time_str = ' '.`time /T`;
266 my $date_time = $date_str.$time_str;
267 $sth = $dbh -> prepare
("INSERT INTO ".$PsN::config
-> {'_'} -> {'project'}.
268 ".model (filename,date,directory,md5sum) ".
269 "VALUES ('$file_str', '$date_time', '$dir_str','".
272 $self -> {'model_id'} = $sth->{'mysql_insertid'};
274 $sth -> finish
if ( defined $sth );
277 $model_id = $self -> {'model_id'} # return the model_id;
279 end register_in_database
281 # }}} register_in_database
283 # {{{ shrinkage_stats
285 start shrinkage_stats
287 if ( $#problem_numbers > 0 and ref $enabled eq 'ARRAY' ){
288 if ( $#problem_numbers != ( scalar @
{$enabled} - 1 ) ) {
289 'debug' -> die( message
=> "The number of problem_numbers ".($#problem_numbers+1).
290 "and enabled/disabled shrinkage_stats ".scalar @
{$enabled}.
294 unless( $#problem_numbers > 0 ){
295 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
298 if( ref \
$enabled eq 'SCALAR' ) {
299 for ( @problem_numbers ) {
300 push( @en_arr, $enabled );
302 } elsif ( not ref $enabled eq 'ARRAY' ) {
303 debug
-> die( message
=> 'enabled must be a scalar or a reference to an array, '.
304 'not a reference to a '.ref($enabled).'.' );
307 my @problems = @
{$self -> {'problems'}};
309 foreach my $i ( @problem_numbers ) {
310 if ( defined $problems[ $i-1 ] ) {
311 if ( defined $en_arr[ $j ] ) {
312 if( $en_arr[ $j ] ) {
313 $problems[ $i-1 ] -> shrinkage_module
-> enable
;
315 $problems[ $i-1 ] -> shrinkage_module
-> disable
;
317 # my $eta_file = $self -> filename.'_'.$i.'.etas';
318 # my $eps_file = $self -> filename.'_'.$i.'.wres';
319 # $problems[ $i-1 ] -> {'eta_shrinkage_table'} = $eta_file;
320 # $problems[ $i-1 ] -> {'wres_shrinkage_table'} = $eps_file;
322 push( @indicators, $problems[ $i-1 ] -> shrinkage_module
-> status
);
325 'debug' -> die( message
=> "Problem number $i does not exist!" );
332 # }}} shrinkage_stats
338 my @problems = @
{$self -> {'problems'}};
339 foreach my $problem ( @problems ) {
340 push( @wres_shrinkage, $problem -> wres_shrinkage
);
351 my @problems = @
{$self -> {'problems'}};
352 foreach my $problem ( @problems ) {
353 push( @eta_shrinkage, $problem -> eta_shrinkage
);
360 # {{{ nonparametric_code
362 start nonparametric_code
364 if ( $#problem_numbers > 0 and $#enabled > 0 ){
365 if ( $#problem_numbers != $#enabled ) {
366 'debug' -> die( message
=> "The number of problem_numbers ".($#problem_numbers+1).
367 "and enabled/disabled nonparametric_code ".($#enabled+1).
371 unless( $#problem_numbers > 0 ){
372 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
374 my @problems = @
{$self -> {'problems'}};
376 foreach my $i ( @problem_numbers ) {
377 if ( defined $problems[ $i-1 ] ) {
378 if ( defined $enabled[ $j ] ) {
379 $problems[ $i-1 ] -> nonparametric_code
( $enabled[ $j ] );
381 push( @indicators, $problems[ $i-1 ] -> nonparametric_code
);
384 'debug' -> die( message
=> "Problem number $i does not exist!" );
389 end nonparametric_code
391 # }}} nonparametric_code
393 # {{{ add_nonparametric_code
395 start add_nonparametric_code
397 $self -> set_records
( type
=> 'nonparametric',
398 record_strings
=> [ 'MARGINALS UNCONDITIONAL' ] );
399 $self -> set_option
( record_name
=> 'estimation',
400 option_name
=> 'POSTHOC' );
401 my ( $msfo_ref, $junk ) = $self ->
402 _get_option_val_pos
( name
=> 'MSFO',
403 record_name
=> 'estimation' );
404 my @nomegas = @
{$self -> nomegas
};
406 for( my $i = 0; $i <= $#nomegas; $i++ ) { # loop the problems
408 for( my $j = 0; $j <= $nomegas[$i]; $j++ ) {
409 $marg_str = $marg_str.' COM('.($j+1).')=MG'.($j+1);
411 $marg_str = $marg_str.' FILE='.$self->filename.'.marginals'.
412 ' NOAPPEND ONEHEADER NOPRINT';
413 $self -> add_records
( problem_numbers
=> [($i+1)],
415 record_strings
=> [ $marg_str ] );
416 $self -> remove_option
( record_name
=> 'abbreviated',
417 option_name
=> 'COMRES' );
418 $self -> add_option
( record_name
=> 'abbreviated',
419 option_name
=> 'COMRES',
420 option_value
=> ($nomegas[$i]+1),
421 add_record
=> 1 ); #Add $ABB if not existing
423 $self -> add_marginals_code
( problem_numbers
=> [($i+1)],
424 nomegas
=> [ $nomegas[$i] ] );
427 if( not defined $msfo_ref ) {
428 for( my $i = 0; $i < $self -> nproblems
; $i++ ) {
429 $self -> add_option
( record_name
=> 'estimation',
430 option_name
=> 'MSFO',
431 option_value
=> $self -> filename
.'.msfo'.($i+1) );
434 for( my $i = 0; $i < scalar @
{$msfo_ref}; $i++ ) {
435 if( not defined $msfo_ref->[$i] or not defined $msfo_ref->[$i][0] ) {
436 $self -> add_option
( record_name
=> 'estimation',
437 option_name
=> 'MSFO',
438 option_value
=> $self -> filename
.'.msfo'.($i+1) );
443 end add_nonparametric_code
445 # }}} add_nonparametric_code
451 if ( defined $self -> {'datas'} ) {
452 foreach my $data ( @
{$self -> {'datas'}} ) {
465 $full_name = $self -> {'directory'} . $self -> {'filename'};
475 unless( defined $self -> {'outputfile'} ){
476 'debug' -> die( message
=> "No output file is set, cannot synchronize output" );
478 @
{$self -> {'outputs'}} = ();
479 push ( @
{$self -> {'outputs'}}, output
->
480 new
( filename
=> $self -> {'outputfile'},
481 ignore_missing_files
=> $self -> {'ignore_missing_files'},
482 target
=> $self -> {'target'},
483 model_id
=> $self -> {'model_id'} ) );
489 # {{{ add_marginals_code
491 start add_marginals_code
493 # add_marginals_code takes two arguments.
495 # - problem_numbers is an array holding the numbers of the problems in
496 # which code should be added.
498 # - nomegas which is an array holding the number of (diagonal-element)
499 # omegas of each problem given by problem_numbers.
501 # For each omega in each problem, verbatim code is added to make the
502 # marginals available for printing (e.g. to a table file). COM(1) will
503 # hold the nonparametric density, COM(2) the marginal cumulative value
504 # for the first eta, COM(2) the marginal cumulative density for the
505 # second eta and so on.
507 unless( $#problem_numbers >= 0 ){
508 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
511 my @problems = @
{$self -> {'problems'}};
513 foreach my $i ( @problem_numbers ) {
514 if ( defined $problems[ $i-1 ] ) {
515 $problems[$i-1] -> add_marginals_code
( nomegas
=> $nomegas[ $j ] );
517 'debug' -> die( message
=> "Problem number $i does not exist.");
522 end add_marginals_code
524 # }}} add_marginals_code
530 # add_records is be used to add a record to a problem. The
531 # I<record_strings> argument should be a NONMEM-formatted
532 # record block. The type string is any of the record classes
533 # found in the problem directory. I<problem_number> is the
534 # index to the subproblem in the modelobject that is
537 unless( $#problem_numbers >= 0 ){
538 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
541 my @problems = @
{$self -> {'problems'}};
542 foreach my $i ( @problem_numbers ) {
543 if ( defined $problems[ $i-1 ] ) {
544 # if( defined $self -> {'problems'} ){
545 # if( defined @{$self -> {'problems'}}[$problem_number - 1] ){
546 # my $problem = @{$self -> {'problems'}}[$problem_number - 1];
547 # $problem -> add_records( 'type' => $type,
548 # 'record_strings' => \@record_strings );
549 $problems[$i-1] -> add_records
( 'type' => $type,
550 'record_strings' => \
@record_strings );
552 'debug' -> die( message
=> "Problem number $i does not exist.");
556 # 'debug' -> die( message => "Model -> add_records: No Problems in model object.") ;
567 # set_records is be used to set a record in a problem. The
568 # new record replaces all existing record of the same type
569 # in the specified problem. The I<record_strings> argument
570 # should be a NONMEM-formatted record block. The type
571 # string is any of the record classes found in the problem
572 # directory. I<problem_number> is the index to the subproblem
573 # in the modelobject that is modified.
575 unless( $#problem_numbers >= 0 ){
576 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
579 my @problems = @
{$self -> {'problems'}};
580 foreach my $i ( @problem_numbers ) {
581 if ( defined $problems[ $i-1 ] ) {
582 # if( defined $self -> {'problems'} ){
583 # if( defined @{$self -> {'problems'}}[$problem_number - 1] ){
584 # my $problem = @{$self -> {'problems'}}[$problem_number - 1];
585 # $problem -> set_records( 'type' => $type,
586 # 'record_strings' => \@record_strings );
587 $problems[$i-1] -> set_records
( 'type' => $type,
588 'record_strings' => \
@record_strings );
590 'debug' -> die( "Problem number $i does not exist." );
594 # 'debug' -> die( "No Problems in model object.") ;
605 # set_records is be used to set a record in a problem. The
606 # new record replaces all existing record of the same type
607 # in the specified problem. The I<record_strings> argument
608 # should be a NONMEM-formatted record block. The type
609 # string is any of the record classes found in the problem
610 # directory. I<problem_number> is the index to the subproblem
611 # in the modelobject that is modified.
613 unless( $#problem_numbers >= 0 ){
614 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
617 my @problems = @
{$self -> {'problems'}};
618 foreach my $i ( @problem_numbers ) {
619 if ( defined $problems[ $i-1 ] ) {
620 # if( defined $self -> {'problems'} ){
621 # if( defined @{$self -> {'problems'}}[$problem_number - 1] ){
622 # my $problem = @{$self -> {'problems'}}[$problem_number - 1];
623 # $problem -> remove_records( 'type' => $type );
624 $problems[$i-1] -> remove_records
( 'type' => $type );
626 'debug' -> die( message
=> "Problem number $i, does not exist" );
630 # 'debug' -> die( message => "No Problems in model object." );
641 # Copy produces a new modelfile object where the I<filename>
642 # argument is the name of the file which will be used if
643 # model::write is called on the new object.
644 # The default behaviour of model -> copy will be to also copy
645 # the data and output objects that are connected to the model
646 # object. To prevent this the I<copy_data> and I<copy_output> options
648 # The values of data_filenames, unless given,
649 # will be the modelfilename but with '.mod' exchanged for
650 # '_$i.dta', where $i is the problem number.
651 # Since output objects are meant to be read-only, no
652 # output_filename can be specified and the output object copy
653 # will reside in memory only.
656 # PP_TODO fix a nice copying of modelfile data
657 # preferably in memory copy. Perhaps flush data ?
659 # Check sanity of the length of data file names argument
660 if ( scalar @data_file_names > 0 ) {
661 'debug' -> die( message
=> "model -> copy: The number of specified new data file " .
662 "names ". scalar @data_file_names. "must\n match the number".
663 " of data objects connected to the model object".
664 scalar @
{$self -> {'datas'}} )
665 unless ( scalar @data_file_names == scalar @
{$self -> {'datas'}} );
668 ($d_filename = $filename) =~ s/\.mod$//;
669 for ( my $i = 1; $i <= scalar @
{$self -> {'datas'}}; $i++ ) {
670 # Data filename is created in this directory (no directory needed).
671 push( @data_file_names, $d_filename."_data_".$i."_copy.dta" );
675 # Check sanity of the length of extra_data file names argument
676 if ( scalar @extra_data_file_names > 0 ) {
677 'debug' -> die( message
=> "The number of specified new extra_data file ".
678 "names ". scalar @extra_data_file_names, "must\n match the number".
679 " of problems (one extra_data file per prolem)".
680 scalar @
{$self -> {'extra_data_files'}} )
681 unless( scalar @extra_data_file_names == scalar @
{$self -> {'extra_data_files'}} );
683 if ( defined $self -> {'extra_data_files'} ) {
685 ($d_filename = $filename) =~ s/\.mod$//;
686 for ( my $i = 1; $i <= scalar @
{$self -> {'extra_data_files'}}; $i++ ) {
687 # Extra_Data filename is created in this directory (no directory needed).
688 push( @extra_data_file_names, $d_filename."_extra_data_".$i."_copy.dta" );
693 ($directory, $filename) = OSspecific
::absolute_path
( $directory, $filename );
697 # save references to own data and output objects
698 my $datas = $self -> {'datas'};
699 # $Data::Dumper::Maxdepth = 2;
700 # die "MC1: ", Dumper $datas -> [0] -> {'individuals'};
701 my $outputs = $self -> {'outputs'};
703 my @problems = @
{$self -> {'problems'}};
704 for ( my $i = 0; $i <= $#problems; $i++ ) {
705 if ( defined $problems[$i] -> {'extra_data'} ) {
706 $extra_datas{$i} = $problems[$i] -> {'extra_data'};
710 my ( @new_datas, @new_extra_datas, @new_outputs );
712 $self -> synchronize
if not $self -> {'synced'};
714 # remove ref to data and output object to speed up the
716 $self -> {'datas'} = undef;
717 $self -> {'outputs'} = undef;
718 for ( my $i = 0; $i <= $#problems; $i++ ) {
719 $problems[$i] -> {'extra_data'} = undef;
722 # Copy the data objects if so is requested
723 if ( defined $datas ) {
725 foreach my $data ( @
{$datas} ) {
726 if ( $copy_data == 1 ) {
727 push( @new_datas, $data ->
728 copy
( filename
=> $data_file_names[$i]) );
730 # This line assumes one data per problem! May be a source of error.
731 my ( $cont_column, $wrap_column ) = $self -> problems
-> [$i] -> cont_wrap_columns
;
732 my $ignoresign = defined $self -> ignoresigns ?
$self -> ignoresigns
-> [$i] : undef;
733 my @model_header = @
{$self -> problems
-> [$i] -> header
};
734 push @new_datas, data
->
735 new
( filename
=> $data -> filename
,
736 directory
=> $data -> directory
,
737 cont_column
=> $cont_column,
738 wrap_column
=> $wrap_column,
739 #model_header => \@model_header,
741 ignoresign
=> $ignoresign,
742 idcolumn
=> $data -> idcolumn
);
748 # Copy the extra_data objects if so is requested
749 for ( my $i = 0; $i <= $#problems; $i++ ) {
750 my $extra_data = $extra_datas{$i};
751 if ( defined $extra_data ) {
752 if ( $copy_data == 1 ) {
753 push( @new_extra_datas, $extra_data ->
754 copy
( filename
=> $extra_data_file_names[$i]) );
756 push( @new_extra_datas, extra_data
->
757 new
( filename
=> $extra_data -> filename
,
758 directory
=> $extra_data -> directory
,
760 idcolumn
=> $extra_data -> idcolumn
) );
766 # Clone self into new model object and set synced to 0 for
768 $new_model = Storable
::dclone
( $self );
769 $new_model -> {'synced'} = 0;
771 # $Data::Dumper::Maxdepth = 3;
772 # die Dumper $new_datas[0] -> {'individuals'};
774 # Restore the data and output objects for self
775 $self -> {'datas'} = $datas;
776 $self -> {'outputs'} = $outputs;
777 for ( my $i = 0; $i <= $#problems; $i++ ) {
778 if( defined $extra_datas{$i} ){
779 $problems[$i] -> {'extra_data'} = $extra_datas{$i};
783 # Set the new file name for the copy
784 $new_model -> directory
( $directory );
785 $new_model -> filename
( $filename );
787 # {{{ update the shrinkage modules
789 my @problems = @
{$new_model -> problems
};
790 for( my $i = 1; $i <= scalar @problems; $i++ ) {
791 $problems[ $i-1 ] -> shrinkage_module
-> model
( $new_model );
794 # }}} update the shrinkage modules
796 # Copy the output object if so is requested (only one output
797 # object defined per model object)
798 if ( defined $outputs ) {
799 foreach my $output ( @
{$outputs} ) {
800 if ( $copy_output == 1 ) {
801 push( @new_outputs, $output -> copy
);
803 my $new_out = $filename;
804 $new_out =~ s/\.mod$/\.lst/;
805 push( @new_outputs, output
->
806 new
( filename
=> $new_out,
807 directory
=> $directory,
809 ignore_missing_files
=> 1,
810 model_id
=> $new_model -> {'model_id'} ) );
815 # Add the copied data and output objects to the model copy
816 $new_model -> datas
( \
@new_datas );
818 if ( $#new_extra_datas >= 0 ) {
819 my @new_problems = @
{$new_model -> problems
};
820 for ( my $i = 0; $i <= $#new_problems; $i++ ) {
821 $new_problems[$i] -> {'extra_data'} = $new_extra_datas[$i];
822 if ( $copy_data == 1 ){
823 $new_problems[$i] -> {'extra_data_file_name'} = $extra_data_file_names[$i];
828 $new_model -> {'outputs'} = \
@new_outputs;
830 $new_model -> _write
;
832 $new_model -> synchronize
if $target eq 'disk';
842 if ( $#problem_numbers > 0 ){
843 if ( $#problem_numbers != $#enabled ) {
844 'debug' -> die( message
=> "The number of problem_numbers ".($#problem_numbers+1).
845 "and enabled/disabled covariance records ".($#enabled+1).
849 unless( $#problem_numbers > 0 ){
850 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
852 my @problems = @
{$self -> {'problems'}};
854 foreach my $i ( @problem_numbers ) {
855 if ( defined $problems[ $i-1 ] ) {
856 if ( defined $enabled[ $j ] ) {
857 $problems[ $i-1 ] -> covariance
( enabled
=> $enabled[ $j ] );
859 push( @indicators, $problems[ $i-1 ] -> covariance
);
862 'debug' -> die( message
=> "Problem number $i does not exist!" );
875 my $nprobs = scalar @
{$self -> {'problems'}};
876 if ( defined $parm ) {
877 if ( ref($parm) eq 'ARRAY' ) {
878 my @new_datas = @
{$parm};
879 # Check that new_headers and problems match
880 'debug' -> die( message
=> "The number of problems $nprobs and".
881 " new data ". $#new_datas+1 ." don't match in ".
882 $self -> full_name
) unless ( $#new_datas + 1 == $nprobs );
883 if ( defined $self -> {'problems'} ) {
884 for( my $i = 0; $i < $nprobs; $i++ ) {
885 $self -> _option_name
( position
=> 0,
887 problem_number
=> $i+1,
888 new_name
=> $new_datas[$i] -> filename
);
891 'debug' -> die( message
=> "No problems defined in ".
892 $self -> full_name
);
895 'debug' -> die( message
=> "Supplied new value is not an array" );
907 # datafile either retrieves or sets a new name for the datafile in the first problem of the
908 # model. This method is only here for compatibility reasons. Don't use it. Use L</datafiles> instead.
910 if( defined $new_name ){
911 $self -> _option_name
( position
=> 0,
913 problem_number
=> $problem_number,
914 new_name
=> $new_name);
915 my ( $cont_column, $wrap_column ) = $self -> problems
-> [$problem_number-1] ->
917 my $ignoresign = defined $self -> ignoresigns ?
918 $self -> ignoresigns
-> [$problem_number-1] : undef;
919 my @model_header = @
{$self -> problems
-> [$problem_number-1] -> header
};
920 $self -> {'datas'} -> [$problem_number-1] = data
->
921 new
( idcolumn
=> $self -> idcolumn
( problem_number
=> $problem_number ),
922 ignoresign
=> $ignoresign,
923 filename
=> $new_name,
924 cont_column
=> $cont_column,
925 wrap_column
=> $wrap_column,
926 #model_header => \@model_header,
927 ignore_missing_files
=> $self -> {'ignore_missing_files'},
928 target
=> $self -> {'target'} );
930 $name = $self -> _option_name
( position
=> 0, record
=> 'data', problem_number
=> $problem_number );
941 # The datafiles method retrieves or sets the names of the
942 # datafiles specified in the $DATA record of each problem. The
943 # problem_numbers argument can be used to control which
944 # problem that is affected. If absolute_path is set to 1, the
945 # returned file names are given with absolute paths.
947 unless( $#problem_numbers > 0 ){
948 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
950 if ( scalar @new_names > 0 ) {
952 my @idcolumns = @
{$self ->
953 idcolumns
( problem_numbers
=> \
@problem_numbers )};
954 foreach my $new_name ( @new_names ) {
955 if ( $absolute_path ) {
957 ($tmp, $new_name) = OSspecific
::absolute_path
('', $new_name );
958 $new_name = $tmp . $new_name;
961 $self -> _option_name
( position
=> 0,
963 problem_number
=> $problem_numbers[$i],
964 new_name
=> $new_name);
965 my ( $cont_column, $wrap_column ) = $self -> problems
->
966 [$problem_numbers[$i]-1] -> cont_wrap_columns
;
967 my $ignoresign = defined $self -> ignoresigns ?
$self -> ignoresigns
-> [$i] : undef;
968 my @model_header = @
{$self -> problems
-> [$i] -> header
};
969 $self -> {'datas'} -> [$problem_numbers[$i]-1] = data
->
970 new
( idcolumn
=> $idcolumns[$i],
971 ignoresign
=> $ignoresign,
972 filename
=> $new_name,
973 cont_column
=> $cont_column,
974 wrap_column
=> $wrap_column,
975 #model_header => \@model_header,
976 ignore_missing_files
=> $self -> {'ignore_missing_files'},
977 target
=> $self -> {'target'} );
981 foreach my $prob_num ( @problem_numbers ) {
982 if ( $absolute_path ) {
983 my ($d_dir, $d_name);
985 OSspecific
::absolute_path
($self -> {'directory'}, $self ->_option_name( position
=> 0,
987 problem_number
=> $prob_num ) );
988 push( @names, $d_dir . $d_name );
990 my $name = $self -> _option_name
( position
=> 0,
992 problem_number
=> $prob_num );
993 $name =~ s/.*[\/\\]//;
994 push( @names, $name );
1007 # Returns the des part specified subproblem.
1008 # TODO: Even though new_des can be specified, they wont be set
1011 my @prob = @
{$self -> problems
};
1012 my @des = @
{$prob[$problem_number - 1] -> get_record
('des') -> code
}
1013 if ( defined $prob[$problem_number - 1] -> get_record
('des') );
1022 $self -> {'problems'} -> [0] -> eigen
;
1033 # @error = $modelObject -> error;
1035 # Returns the error part specified subproblem.
1036 # TODO: Even though new_error can be specified, they wont be set
1038 my @prob = @
{$self -> problems
};
1039 my @error = @
{$prob[0] -> get_record
('error') -> code
}
1040 if ( defined $prob[0] -> get_record
('error') );
1046 # {{{ extra_data_files
1048 start extra_data_files
1051 # Sets or retrieves extra_data_file_name on problem level
1052 my $nprobs = scalar @
{$self -> {'problems'}};
1053 if ( defined $parm ) {
1054 if ( ref($parm) eq 'ARRAY' ) {
1055 my @new_file_names = @
{$parm};
1056 # Check that new_file_names and problems match
1057 'debug' -> die( message
=> "model -> extra_data_files: The number of problems $nprobs and" .
1058 " new_file_names " . $#new_file_names+1 . " don't match in ".
1059 $self -> full_name
) unless ( $#new_file_names + 1 == $nprobs );
1060 if ( defined $self -> {'problems'} ) {
1061 for( my $i = 0; $i < $nprobs; $i++ ) {
1062 $self -> {'problems'} -> [$i] -> extra_data_file_name
( $new_file_names[$i] );
1065 'debug' -> die( message
=> "No problems defined in " .
1066 $self -> full_name
);
1069 'debug' -> die(message
=> "Supplied new value is not an array.");
1072 if ( defined $self -> {'problems'} ) {
1073 for( my $i = 0; $i < $nprobs; $i++ ) {
1074 if( defined $self -> {'problems'} -> [$i] -> extra_data_file_name
) {
1075 push ( @file_names ,$self -> {'problems'} -> [$i] -> extra_data_file_name
);
1080 return \
@file_names;
1082 end extra_data_files
1086 # {{{ extra_data_headers
1088 start extra_data_headers
1091 # Sets or retrieves extra_data_header on problem level
1092 my $nprobs = scalar @
{$self -> {'problems'}};
1093 if ( defined $parm ) {
1094 if ( ref($parm) eq 'ARRAY' ) {
1095 my @new_headers = @
{$parm};
1096 # Check that new_headers and problems match
1097 'debug' -> die( message
=> "The number of problems $nprobs and".
1098 " new_headers " . $#new_headers+1 . " don't match in ".
1099 $self -> full_name
) unless ( $#new_headers + 1 == $nprobs );
1100 if ( defined $self -> {'problems'} ) {
1101 for( my $i = 0; $i < $nprobs; $i++ ) {
1102 $self -> {'problems'} -> [$i] -> extra_data_header
( $new_headers[$i] );
1105 'debug' -> die( message
=> "No problems defined in " . $self -> full_name
);
1108 'debug' -> die( message
=> "Supplied new value is not an array" );
1111 if ( defined $self -> {'problems'} ) {
1112 for( my $i = 0; $i < $nprobs; $i++ ) {
1113 push ( @headers, $self -> {'problems'} -> [$i] -> extra_data_header
);
1119 end extra_data_headers
1121 # }}} extra_data_headers
1127 # Calls <I>factors</I> on the data object of a specified
1128 # problem. See <I>data -> factors</I> for details.
1130 my $extra_data_column;
1131 if ( defined $column_head ) {
1132 # Check normal data object first
1133 my ( $values_ref, $positions_ref ) = $self ->
1134 _get_option_val_pos
( problem_numbers
=> [$problem_number],
1135 name
=> $column_head,
1136 record_name
=> 'input',
1137 global_position
=> 1 );
1138 $column_number = $positions_ref -> [0];
1139 # Next, check extra_data
1140 my $extra_data_headers = $self -> extra_data_headers
;
1141 if ( defined $extra_data_headers and defined $extra_data_headers -> [0] ) {
1142 for ( my $i = 1; $i <= scalar @
{$extra_data_headers->[0]}; $i++ ) {
1143 $extra_data_column = $i if ( $column_head eq $extra_data_headers->[0][$i-1] );
1146 'debug' -> die( message
=> "Unknown column \"$column_head\"" )
1147 unless ( defined $column_number or defined $extra_data_column );
1149 $column_number = $column;
1151 if ( defined $column_number) {
1152 %factors = %{$self -> {'datas'} -> [$problem_number-1] ->
1153 factors
( column
=> $column_number,
1154 unique_in_individual
=> $unique_in_individual,
1155 return_occurences
=> $return_occurences )};
1157 %factors = %{$self -> {'problems'} -> [$problem_number-1] -> extra_data
1158 -> factors
( column
=> $extra_data_column,
1159 unique_in_individual
=> $unique_in_individual,
1160 return_occurences
=> $return_occurences )};
1171 # Calls <I>fractions</I> on the data object of a specified
1172 # problem. See <I>data -> fractions</I> for details.
1174 my $extra_data_column;
1175 if ( defined $column_head ) {
1176 # Check normal data object first
1177 my ( $values_ref, $positions_ref ) = $self ->
1178 _get_option_val_pos
( problem_numbers
=> [$problem_number],
1179 name
=> $column_head,
1180 record_name
=> 'input',
1181 global_position
=> 1 );
1182 $column_number = $positions_ref -> [0];
1183 # Next, check extra_data
1184 my $extra_data_headers = $self -> extra_data_headers
;
1185 if ( defined $extra_data_headers and defined $extra_data_headers -> [0] ) {
1186 for ( my $i = 1; $i <= scalar @
{$extra_data_headers->[0]}; $i++ ) {
1187 $extra_data_column = $i if ( $column_head eq $extra_data_headers->[0][$i-1] );
1190 'debug' -> die( "Unknown column \"$column_head\"" )
1191 unless ( defined $column_number or defined $extra_data_column );
1193 $column_number = $column;
1195 if ( defined $column_number) {
1196 %fractions = %{$self -> {'datas'} -> [$problem_number-1] ->
1197 fractions
( column
=> $column_number,
1198 unique_in_individual
=> $unique_in_individual,
1199 ignore_missing
=> $ignore_missing )};
1201 %fractions = %{$self -> {'problems'} -> [$problem_number-1] -> extra_data
1202 -> fractions
( column
=> $extra_data_column,
1203 unique_in_individual
=> $unique_in_individual,
1204 ignore_missing
=> $ignore_missing )};
1215 # Sets or gets the 'fixed' status of a (number of)
1216 # parameter(s). 1 correspond to a parameter being fixed and
1217 # 0 not fixed. The returned parameter is a reference to a
1218 # two-dimensional array, indexed by problems and parameter
1220 # Valid parameter types are 'theta', 'omega' and 'sigma'.
1222 @fixed = @
{ $self -> _init_attr
1223 ( parameter_type
=> $parameter_type,
1224 parameter_numbers
=> \
@parameter_numbers,
1225 problem_numbers
=> \
@problem_numbers,
1226 new_values
=> \
@new_values,
1227 attribute
=> 'fix')};
1233 # {{{ have_missing_data
1235 start have_missing_data
1237 # Calls <I>have_missing_data</I> on the data object of a specified
1238 # problem. See <I>data -> have_missing_data</I> for details.
1240 my $extra_data_column;
1241 if ( defined $column_head ) {
1242 # Check normal data object first
1243 my ( $values_ref, $positions_ref ) = $self ->
1244 _get_option_val_pos
( problem_numbers
=> [$problem_number],
1245 name
=> $column_head,
1246 record_name
=> 'input',
1247 global_position
=> 1 );
1248 $column_number = $positions_ref -> [0];
1249 # Next, check extra_data
1250 my $extra_data_headers = $self -> extra_data_headers
;
1251 if ( defined $extra_data_headers and defined $extra_data_headers -> [0] ) {
1252 for ( my $i = 1; $i <= scalar @
{$extra_data_headers->[0]}; $i++ ) {
1253 $extra_data_column = $i if ( $column_head eq $extra_data_headers->[0][$i-1] );
1256 'debug' -> die( message
=> "Unknown column \"$column_head\"" )
1257 unless ( defined $column_number or defined $extra_data_column );
1259 $column_number = $column;
1261 if ( defined $column_number) {
1262 $return_value = $self -> {'datas'} -> [$problem_number-1] ->
1263 have_missing_data
( column
=> $column_number );
1265 $return_value = $self -> {'problems'} -> [$problem_number-1] ->
1266 extra_data
-> have_missing_data
( column
=> $extra_data_column );
1269 end have_missing_data
1279 # @idcolumns = @{$modelObject -> idcolumns( problem_numbers => [2,3] );
1281 # idcolumns returns the idcolumn index in the datafile for the
1282 # specified problem.
1285 ( $junk_ref, $col ) = $self ->
1286 _get_option_val_pos
( name
=> 'ID',
1287 record_name
=> 'input',
1288 problem_numbers
=> [$problem_number] );
1290 if ( $problem_number ne 'all' ) {
1304 # @column_numbers = @{$modelObject -> idcolumns( problem_numbers => [2] )};
1306 # idcolumns returns the idcolumn indexes in the datafile for the
1307 # specified problems.
1309 my ( $junk_ref, $col_ref ) = $self ->
1310 _get_option_val_pos
( name
=> 'ID',
1311 record_name
=> 'input',
1312 problem_numbers
=> \
@problem_numbers );
1313 # There should only be one instance of $INPUT and hence we collapse
1314 # the two-dim return from _get_option_pos_val to a one-dim array:
1316 foreach my $prob ( @
{$col_ref} ) {
1317 foreach my $inst ( @
{$prob} ) {
1318 push( @column_numbers, $inst );
1332 # @ignore_signs = @{$modelObject -> ignoresigns( problem_numbers => [2,4] )};
1334 # ignoresigns returns the ignore signs in the datafile for the
1335 # specified problems
1337 my ( $ignore_opt_ref, $junk_ref ) = $self ->
1338 _get_option_val_pos
( name
=> 'IGNORE',
1339 record_name
=> 'data',
1340 problem_numbers
=> \
@problem_numbers );
1342 # There should only be one instance of $DATA and hence we collapse
1343 # the two-dim return from _get_option_pos_val to a one-dim array:
1344 foreach my $prob ( @
{$ignore_opt_ref} ) {
1345 foreach my $inst ( @
{$prob} ) {
1346 $inst = '#' unless defined $inst;
1347 push( @ignore, $inst );
1361 # @indexArray = @{$modelObject -> indexes( 'parameter_type' => 'omega' )};
1363 # A call to I<indexes> returns the indexes of all parameters
1364 # specified in I<parameter_numbers> from the subproblems
1365 # specified in I<problem_numbers>. The method returns a reference to an array that has
1366 # the same structure as parameter_numbers but for each
1367 # array of numbers is instead an array of indices. The method
1368 # uses a method from the model::problem class to format the
1369 # indices, so here are a few lines from the code comments in
1370 # model/problem.pm that describes the returned value:
1373 # The Indexes method calculates the index for a
1374 # parameter. Off-diagonal elements will get a index 'i_j', where i
1375 # is the row number and j is the column number
1378 unless( $#problem_numbers > 0 ){
1379 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
1381 my @problems = @
{$self -> {'problems'}};
1382 foreach my $i ( @problem_numbers ) {
1383 if ( defined $problems[ $i-1 ] ) {
1385 $problems[ $i-1 ] ->
1386 indexes
( parameter_type
=> $parameter_type,
1387 parameter_numbers
=> $parameter_numbers[ $i-1 ] ) );
1389 'debug' -> die( message
=> "Problem number $i does not exist!" );
1397 # {{{ initial_values
1399 start initial_values
1401 # initial_values either sets or gets the initial values of
1402 # the parameter specified in "parameter_type" for each
1403 # problem specified in problem_numbers. For each element
1404 # in problem_numbers there must be a reference in
1405 # parameter_numbers to an array that specify the indices
1406 # of the parameters in the subproblem for which the initial
1407 # values are set, replaced or retrieved.
1409 # The add_if_absent argument tells the method to add an init
1410 # (theta,omega,sigma) if the parameter number points to a
1411 # non-existing parameter with parameter number one higher
1412 # than the highest presently included. Only applicable if
1413 # new_values are set. Valid parameter types are 'theta',
1414 # 'omega' and 'sigma'.
1416 @initial_values = @
{ $self -> _init_attr
1417 ( parameter_type
=> $parameter_type,
1418 parameter_numbers
=> \
@parameter_numbers,
1419 problem_numbers
=> \
@problem_numbers,
1420 new_values
=> \
@new_values,
1421 attribute
=> 'init',
1422 add_if_absent
=> $add_if_absent )};
1426 # }}} initial_values
1434 # if( $modelObject -> is_option_set( record => 'recordName', name => 'optionName' ) ){
1435 # print "problem_number 1 has option optionName set in record recordName";
1438 # is_option_set checks if an option is set in a given record in given problem.
1440 my ( @problems, @records, @options );
1441 my $accessor = $record.'s';
1442 if ( defined $self -> {'problems'} ) {
1443 @problems = @
{$self -> {'problems'}};
1445 'debug' -> die( message
=> "No problems defined in model" );
1447 unless( defined $problems[$problem_number - 1] ){
1448 'debug' -> warn( level
=> 2,
1449 message
=> "model -> is_option_set: No problem number $problem_number defined in model" );
1450 return 0; # No option can be set if no problem exists.
1453 if ( defined $problems[$problem_number - 1] -> $accessor ) {
1454 @records = @
{$problems[$problem_number - 1] -> $accessor};
1456 'debug' -> warn( level
=> 2,
1457 message
=> "model -> is_option_set: No record $record defined" .
1458 " in problem number $problem_number." );
1462 unless(defined $records[$instance - 1] ){
1463 'debug' -> warn( level
=> 2,
1464 message
=> "model -> is_option_set: No record instance number $instance defined in model." );
1468 if ( defined $records[$instance - 1] -> options
) {
1469 @options = @
{$records[$instance - 1] -> options
};
1471 'debug' -> warn( level
=> 2,
1472 message
=> "No option defined in record: $record in problem number $problem_number." );
1475 foreach my $option ( @options ) {
1476 $found = 1 if ( defined $option and $option -> name
eq $name );
1488 # is_run returns true if the outputobject owned by the
1489 # modelobject has valid outpudata either in memory or on disc.
1490 if( defined $self -> {'outputs'} ){
1491 if( @
{$self -> {'outputs'}}[0] -> have_output
){
1505 my $problems = $self -> {'problems'};
1506 if( defined $problems -> [$problem_number - 1] ) {
1507 my $problem = $problems -> [$problem_number - 1];
1508 # If we don't have an ESTIMATION record we are simulating.
1509 $is_sim = 1 unless( defined $problem -> {'estimations'} and
1510 scalar( @
{$problem-> {'estimations'}} ) > 0 );
1512 # If we have a ONLYSIM option in the simulation record.
1513 $is_sim = 1 if( $self -> is_option_set
( name
=> 'ONLYSIM',
1514 record
=> 'simulation',
1515 problem_number
=> $problem_number ));
1517 # If max evaluations is zero we are simulating
1518 $is_sim = 1 if( defined $self -> maxeval
(problem_numbers
=> [$problem_number]) and
1519 defined $self -> maxeval
(problem_numbers
=> [$problem_number])->[0][0] and
1520 $self -> maxeval
(problem_numbers
=> [$problem_number])->[0][0] == 0 );
1524 # If non of the above is true, we are estimating.
1526 'debug' -> warn( level
=> 1,
1527 message
=> 'Problem nr. $problem_number not defined. Assuming no simulation' );
1539 # lower_bounds either sets or gets the initial values of the
1540 # parameter specified in the argument parameter_type for
1541 # each problem specified in problem_numbers. See L</fixed>.
1543 @lower_bounds = @
{ $self -> _init_attr
1544 ( parameter_type
=> $parameter_type,
1545 parameter_numbers
=> \
@parameter_numbers,
1546 problem_numbers
=> \
@problem_numbers,
1547 new_values
=> \
@new_values,
1548 attribute
=> 'lobnd')};
1560 # @labels = @{$modobj -> labels( parameter_type => 'theta' )};
1562 # This basic usage takes one arguments and returns matched names and
1563 # estimated values of the specified parameter. The parameter_type argument
1564 # is mandatory. It returns the labels of all parameters of type given by
1566 # @labels will be a two-dimensional array:
1567 # [[label1][label2][label3]...]
1569 # $labels -> labels( parameter_type => 'theta',
1570 # problem_numbers => [2,4] );
1572 # To get labels of specific problems, the problem_numbers argument can be used.
1573 # It should be a reference to an array containing the numbers
1574 # of all problems whos labels should be retrieved.
1576 # $modobj -> labels( parameter_type => 'theta',
1577 # problem_numbers => [2,4],
1578 # parameter_numbers => [[1,3][4,6]]);
1580 # The retrieval can be even more specific by using the parameter_numbers
1581 # argument. It should be a reference to a two-dimensional array, where
1582 # the inner arrays holds the numbers of the parameters that should be
1583 # fetched. In the example above, parameters one and three from problem two
1584 # plus parameters four and six from problem four are retrieved.
1586 # $modobj -> labels( parameter_type => 'theta',
1587 # problem_numbers => [2,4],
1588 # parameter_numbers => [[1,3][4,6]],
1591 # To get generic labels for the parameters - e.g. OM1, OM2_1, OM2 etc -
1592 # set the generic argument to 1.
1594 # $modobj -> labels( parameter_type => 'theta',
1595 # problem_numbers => [2],
1596 # parameter_numbers => [[1,3]],
1597 # new_values => [['Volume','Clearance']] );
1599 # The new_values argument can be used to give parameters new labels. In
1600 # the above example, parameters one and three in problem two are renamed
1601 # Volume and Clearance.
1604 my ( @index, $idx );
1605 @labels = @
{ $self -> _init_attr
1606 ( parameter_type
=> $parameter_type,
1607 parameter_numbers
=> \
@parameter_numbers,
1608 problem_numbers
=> \
@problem_numbers,
1609 new_values
=> \
@new_values,
1610 attribute
=> 'label' )};
1612 # foreach my $prl ( @labels ) {
1613 # foreach my $label ( @{$prl} ) {
1614 # print "Label: $label\n";
1619 @index = @
{$self -> indexes
( parameter_type
=> $parameter_type,
1620 parameter_numbers
=> \
@parameter_numbers,
1621 problem_numbers
=> \
@problem_numbers )};
1622 for ( my $i = 0; $i <= $#labels; $i++ ) {
1623 for ( my $j = 0; $j < scalar @
{$labels[$i]}; $j++ ) {
1624 $idx = $index[$i][$j];
1625 $labels[$i][$j] = uc(substr($parameter_type,0,2)).$idx
1626 unless ( defined $labels[$i][$j] and not $generic );
1640 # @maxev = @{$modobj -> maxeval};
1642 # This basic usage takes no arguments and returns the value of the
1643 # MAXEVAL option in the $ESTIMATION record of each problem.
1644 # @maxev will be a two dimensional array:
1645 # [[maxeval_prob1][maxeval_prob2][maxeval_prob3]...]
1647 # $modobj -> maxeval( new_values => [[0],[999]];
1649 # If the new_values argument of maxeval is given, the values of the
1650 # MAXEVAL options will be changed. In this example, MAXEVAL will be
1651 # set to 0 in the first problem and to 999 in the second.
1652 # The number of elements in new_values must match the number of problems
1653 # in the model object $modobj.
1655 # $modobj -> maxeval( new_values => [[0],[999]],
1656 # problem_numbers => [2,4] );
1658 # To set the MAXEVAL of specific problems, the problem_numbers argument can
1659 # be used. It should be a reference to an array containing the numbers
1660 # of all problems where the MAXEVAL should be changed or retrieved.
1661 # If specified, the size of new_values must be the same as the size
1662 # of problem_numbers.
1667 my ( $val_ref, $junk ) = $self ->
1668 _option_val_pos
( name
=> 'MAX',
1669 record_name
=> 'estimation',
1670 problem_numbers
=> \
@problem_numbers,
1671 new_values
=> \
@new_values,
1672 exact_match
=> $exact_match );
1673 @values = @
{$val_ref};
1683 # Calls <I>median</I> on the data object of a specified
1684 # problem. See <I>data -> median</I> for details.
1686 my $extra_data_column;
1687 if ( defined $column_head ) {
1688 # Check normal data object first
1689 my ( $values_ref, $positions_ref ) = $self ->
1690 _get_option_val_pos
( problem_numbers
=> [$problem_number],
1691 name
=> $column_head,
1692 record_name
=> 'input',
1693 global_position
=> 1 );
1694 $column_number = $positions_ref -> [0];
1695 if ( not defined $column_number ) {
1696 # Next, check extra_data
1697 my $extra_data_headers = $self -> extra_data_headers
;
1698 if ( defined $extra_data_headers and defined $extra_data_headers -> [0] ) {
1699 for ( my $i = 1; $i <= scalar @
{$extra_data_headers->[0]}; $i++ ) {
1700 $extra_data_column = $i if ( $column_head eq $extra_data_headers->[0][$i-1] );
1704 'debug' -> die( message
=> "Unknown column \"$column_head\"" )
1705 unless ( defined $column_number or defined $extra_data_column );
1707 $column_number = $column;
1710 if ( defined $column_number) {
1711 $median = $self -> {'datas'} -> [$problem_number-1] ->
1712 median
( column
=> $column_number,
1713 unique_in_individual
=> $unique_in_individual );
1715 $median = $self -> {'problems'} -> [$problem_number-1] -> extra_data
->
1716 median
( column
=> $extra_data_column,
1717 unique_in_individual
=> $unique_in_individual );
1728 # Calls <I>max</I> on the data object of a specified
1729 # problem. See <I>data -> max</I> for details.
1731 my $extra_data_column;
1732 if ( defined $column_head ) {
1733 # Check normal data object first
1734 my ( $values_ref, $positions_ref ) = $self ->
1735 _get_option_val_pos
( problem_numbers
=> [$problem_number],
1736 name
=> $column_head,
1737 record_name
=> 'input',
1738 global_position
=> 1 );
1739 $column_number = $positions_ref -> [0];
1740 if ( not defined $column_number ) {
1741 # Next, check extra_data
1742 my $extra_data_headers = $self -> extra_data_headers
;
1743 if ( defined $extra_data_headers and defined $extra_data_headers -> [0] ) {
1744 for ( my $i = 1; $i <= scalar @
{$extra_data_headers->[0]}; $i++ ) {
1745 $extra_data_column = $i if ( $column_head eq $extra_data_headers->[0][$i-1] );
1749 'debug' -> die( message
=> "Unknown column \"$column_head\"" )
1750 unless ( defined $column_number or defined $extra_data_column );
1752 $column_number = $column;
1755 if ( defined $column_number) {
1756 $max = $self -> {'datas'} -> [$problem_number-1] ->
1757 max
( column
=> $column_number );
1759 $max = $self -> {'problems'} -> [$problem_number-1] -> extra_data
->
1760 max
( column
=> $extra_data_column );
1771 # Calls <I>min</I> on the data object of a specified
1772 # problem. See <I>data -> min</I> for details.
1774 my $extra_data_column;
1775 if ( defined $column_head ) {
1776 # Check normal data object first
1777 my ( $values_ref, $positions_ref ) = $self ->
1778 _get_option_val_pos
( problem_numbers
=> [$problem_number],
1779 name
=> $column_head,
1780 record_name
=> 'input',
1781 global_position
=> 1 );
1782 $column_number = $positions_ref -> [0];
1783 if ( not defined $column_number ) {
1784 # Next, check extra_data
1785 my $extra_data_headers = $self -> extra_data_headers
;
1786 if ( defined $extra_data_headers and defined $extra_data_headers -> [0] ) {
1787 for ( my $i = 1; $i <= scalar @
{$extra_data_headers->[0]}; $i++ ) {
1788 $extra_data_column = $i if ( $column_head eq $extra_data_headers->[0][$i-1] );
1792 'debug' -> die( message
=> "Unknown column \"$column_head\"" )
1793 unless ( defined $column_number or defined $extra_data_column );
1795 $column_number = $column;
1798 if ( defined $column_number) {
1799 $min = $self -> {'datas'} -> [$problem_number-1] ->
1800 min
( column
=> $column_number );
1802 $min = $self -> {'problems'} -> [$problem_number-1] -> extra_data
->
1803 min
( column
=> $extra_data_column );
1816 # @name_val = @{$modobj -> name_val( parameter_type => 'theta' )};
1818 # This basic usage takes one arguments and returns matched names and
1819 # estimated values of the specified parameter. The parameter_type argument
1821 # The names are taken from
1822 # the labels of the parameters (se the labels method for specifications of
1823 # default labels) and the values are aquired from the output object bound
1824 # to the model object. If no output exists, the name_val method returns
1826 # @name_val will be a two-dimensional array of references to hashes using
1827 # the names from each problem as keys:
1828 # [[ref to hash1 ][ref to hash2][ref to hash3]...]
1830 # $modobj -> name_val( parameter_type => 'theta',
1831 # problem_numbers => [2,4] );
1833 # To get matched names and values of specific problems, the problem_numbers argument
1834 # can be used. It should be a reference to an array containing the numbers
1835 # of all problems whos names and values should be retrieved.
1837 # $modobj -> name_val( parameter_type => 'theta',
1838 # problem_numbers => [2,4],
1839 # parameter_numbers => [[1,3][4,6]]);
1841 # The retrieval can be even more specific by using the parameter_numbers
1842 # argument. It should be a reference to a two-dimensional array, where
1843 # the inner arrays holds the numbers of the parameters that should be
1844 # fetched. In the example above, parameters one and three from problem two
1845 # plus parameters four and six from problem four are retrieved.
1848 unless( $#problem_numbers > 0 ){
1849 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
1851 my @names = @
{$self -> labels
( parameter_type
=> $parameter_type,
1852 parameter_numbers
=> \
@parameter_numbers,
1853 problem_numbers
=> \
@problem_numbers )};
1855 if ( defined $self -> outputs
-> [0] ) {
1856 my $accessor = $parameter_type.'s';
1857 @values = @
{$self -> outputs
-> [0] ->
1858 $accessor( problems
=> \
@problem_numbers,
1859 parameter_numbers
=> \
@parameter_numbers )};
1860 # my @problems = @{$self -> {'problems'}};
1861 # foreach my $i ( @problem_numbers ) {
1862 # if ( defined $problems[ $i-1 ] ) {
1863 # my $pn_ref = $#parameter_numbers >= 0 ? \@{$parameter_numbers[ $i-1 ]} : [];
1864 # push( @names_values,
1865 # $problems[ $i-1 ] ->
1866 # name_val( parameter_type => $parameter_type,
1867 # parameter_numbers => $pn_ref ) );
1869 # die "Model -> name_val: Problem number $i does not exist!\n";
1873 # if ( scalar @{$self -> {'outputs'}} > 0 ) {
1874 # my $outobj = $self -> {'outputs'} -> [0];
1877 'debug' -> die( message
=> "The number of problems retrieved from the model" .
1878 " do not match the ones retrived from the output" ) unless( $#names == $#values );
1879 for( my $i = 0; $i <= $#names; $i++ ) {
1880 'debug' -> die( message
=> "Problem " . $i+1 .
1881 " The number of parameters retrieved from the model (".scalar @
{$names[$i]}.
1882 ") do not match the ones retrived from the output (".
1883 scalar @
{$values[$i][0]}.")" )
1884 unless( scalar @
{$names[$i]} == scalar @
{$values[$i][0]} );
1886 for( my $j = 0; $j < scalar @
{$values[$i]}; $j++ ){
1888 for( my $k = 0; $k < scalar @
{$names[$i]}; $k++ ){
1889 $nv{$names[$i][$k]} = $values[$i][$j][$k];
1891 push( @prob_nv, \
%nv );
1893 push( @names_values, \
@prob_nv );
1904 # nproblems returns the number of problems in the modelobject.
1906 $number_of_problem = scalar @
{$self -> {'problems'}};
1916 # returns the number of thetas in the model for the given
1918 $nthetas = $self -> _parameter_count
( 'record' => 'theta', 'problem_number' => $problem_number );
1928 # returns the number of omegas in the model for the given
1930 # $nomegas = $self -> _parameter_count( 'record' => 'omega', 'problem_number' => $problem_number );
1931 unless( $#problem_numbers >= 0 ){
1932 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
1935 my @problems = @
{$self -> {'problems'}};
1936 foreach my $i ( @problem_numbers ) {
1937 if ( defined $problems[ $i-1 ] ) {
1938 push( @nomegas, $problems[ $i-1 ] -> nomegas
);
1940 'debug' -> die( "Problem number $i does not exist." );
1952 # returns the number of sigmas in the model for the given problem number.
1954 # $nsigmas = $self -> _parameter_count( 'record' => 'sigma', 'problem_number' => $problem_number );
1956 unless( $#problem_numbers >= 0 ){
1957 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
1960 my @problems = @
{$self -> {'problems'}};
1961 foreach my $i ( @problem_numbers ) {
1962 if ( defined $problems[ $i-1 ] ) {
1963 push( @nsigmas, $problems[ $i-1 ] -> nsigmas
);
1965 'debug' -> die( "Problem number $i does not exist." );
1979 # This method is a (partially) automatically generated accessor for the
1980 # outputfile attribute of the model class. Since no named argument is needed
1981 # for accessors, the two possible ways of calling outputfile are:
1983 # $modelObject -> outputfile( 'newfilename.lst' );
1985 # $outputfilename = $modelObject -> outputfile;
1987 # The first alternative sets a new name for the output file, and the second
1988 # retrieves the value.
1990 # The extra feature for this accessor, compared to other accessors, is that
1991 # if a new name is given, the accessor tries to create a new output object
1994 if( defined $parm ) {
1995 $self -> {'outputs'} =
1997 new
( filename
=> $parm,
1998 ignore_missing_files
=> $self -> {'ignore_missing_files'},
1999 target
=> $self -> {'target'},
2000 model_id
=> $self -> {'model_id'} ) ];
2011 # sets or gets the pk code for a given problem in the
2012 # model object. The new_pk argument should be an array where
2013 # each element contains a row of a valid NONMEM $PK block,
2015 my @prob = @
{$self -> problems
};
2017 unless( defined $prob[$problem_number - 1] ){
2018 'debug' -> die( message
=> "Problem number $problem_number does not exist" );
2021 my $pks = $prob[$problem_number - 1] -> pks
;
2022 if( scalar @new_pk > 0 ) {
2023 if( defined $pks and scalar @
{$pks} > 0 ){
2024 $prob[$problem_number - 1] -> pks
-> [0] -> code
(\
@new_pk);
2026 'debug' -> die( message
=> "No \$PK record" );
2029 if ( defined $pks and scalar @
{$pks} > 0 ) {
2030 @pk = @
{$prob[$problem_number - 1] -> pks
-> [0] -> code
};
2042 # Sets or gets the pred code for a given problem in the model
2043 # object. See L</pk> for details.
2044 my @prob = @
{$self -> problems
};
2046 unless( defined $prob[$problem_number - 1] ){
2047 'debug' -> die( message
=> "problem number $problem_number does not exist" );
2050 if( scalar @new_pred > 0 ) {
2051 if( defined $prob[$problem_number - 1] -> preds
){
2052 $prob[$problem_number - 1] -> preds
-> [0] -> code
(\
@new_pred);
2054 'debug' -> die( message
=> "No \$PRED record" );
2057 if ( defined $prob[$problem_number - 1] -> preds
) {
2058 @pred = @
{$prob[$problem_number - 1] -> preds
-> [0] -> code
};
2060 'debug' -> die( message
=> "No \$PRED record" );
2072 # Prints the formatted model to standard out.
2075 foreach my $problem ( @
{$self -> {'problems'}} ) {
2076 push( @formatted, $problem -> format_problem
);
2078 for ( @formatted ) {
2086 # {{{ problem_structure
2088 start problem_structure
2090 my ( $val, $pos ) = $self -> _option_val_pos
( record_name
=> 'simulation',
2091 name
=> 'SUBPROBLEMS' );
2092 if( defined $val ) {
2094 for( my $i = 0; $i <= $#vals; $i++ ) {
2095 if( defined $vals[$i] ) {
2096 if( scalar @
{$vals[$i]} > 0 ) {
2097 $subproblems[$i] = $vals[$i][0];
2099 $subproblems[$i] = 1;
2102 $subproblems[$i] = 1;
2107 end problem_structure
2109 # }}} problem_structure
2111 # {{{ randomize_inits
2113 start randomize_inits
2115 foreach my $prob ( @
{$self -> {'problems'}} ) {
2116 $prob -> set_random_inits
( degree
=> $degree );
2128 # If the argument new_data is given, record sets new_data in
2129 # the model objects member specified with record_name. The
2130 # format of new_data is an array of strings, where each
2131 # element corresponds to a line of code as it would have
2132 # looked like in a valid NONMEM modelfile. If new_data is left
2133 # undefined, record returns lines of code belonging to the
2134 # record specified by record_name in a format that is valid in
2135 # a NONMEM modelfile.
2137 my @problems = @
{$self -> {'problems'}};
2140 if ( defined $problems[ $problem_number - 1 ] ) {
2141 if ( scalar(@new_data) > 0 ){
2142 my $rec_class = "model::problem::$record_name";
2143 my $record = $rec_class -> new
('record_arr' => \
@new_data );
2145 $record_name .= 's';
2146 $records = $problems[ $problem_number - 1 ] -> {$record_name};
2147 foreach my $record( @
{$records} ){
2148 push(@data, $record -> _format_record
);
2163 # $model -> remove_inits( type => 'theta',
2164 # indexes => [1,2,5,6] )
2167 # In all cases the type must be set to theta. Removing Omegas in
2168 # Sigmas is not allowed, (If need that feature, send us a
2169 # mail). In the above example the thetas 1, 2, 5 and 6 will be
2170 # removed from the modelfile. Notice that this alters the theta
2171 # numbering, so if you later decide that theta number 7 must be
2172 # removed as well, you must calculate its new position in the
2173 # file. In this case the new number would be 3. Also notice that
2174 # numbering starts with 1.
2176 # $model -> remove_inits( type => 'theta',
2177 # labels => ['V', 'CL'] )
2180 # If you have specified labels in you modelfiles(a label is
2181 # string inside a comment on the same row as the theta) you can
2182 # specify an array with labels, and the corresponding theta, if
2183 # it exists, will be removed. This is a much better approach
2184 # since you don't need to know where in order the theta you wish
2185 # to remove appears. If you specify both labels and indexes, the
2186 # indexes will be ignored.
2188 'debug' -> die( message
=> 'does not have the functionality for removing $OMEGA or $SIGMA options yet' )
2189 if ( $type eq 'omega' or $type eq 'sigma' );
2190 my $accessor = $type.'s';
2192 # First pick out a referens to the theta records array.
2193 my $inits_ref = $self -> problems
-> [$problem_number -1] -> $accessor;
2195 # If we have any thetas at all:
2196 if ( defined $inits_ref ) {
2197 my @inits = @
{$inits_ref};
2199 # If labels are specified, we translate the labels into
2201 if ( scalar @labels > 0 ) {
2204 # Loop over theta records
2205 foreach my $init ( @inits ) {
2206 # Loop over the individual thetas inside
2207 foreach my $option ( @
{$init -> options
} ) {
2208 # Loop over all given labels.
2209 foreach my $label ( @labels ) {
2210 # Push the index number if a given label match the
2212 push( @indexes, $i ) if ( $option -> label
eq $label);
2214 # $i is the count of thetas so far
2220 # We don't really remove thetas, we do a loop over all thetas
2221 # and recording which we like to keep. We do that by selecting
2222 # an index, from @indexes, that shall be removed and loop over
2223 # the thetas, all thetas that doesn't match the index are
2224 # stored in @keep_options. When we find a theta that matches,
2225 # we pick a new index and continue the loop. So by makeing
2226 # sure that @indexes is sorted, we only need to loop over the
2229 @indexes = sort {$a <=> $b} @indexes;
2235 # Loop over all records
2236 RECORD_LOOP
: foreach my $record ( @inits ){
2237 my @keep_options = ();
2238 # Loop over all thetas
2239 foreach my $option ( @
{$record -> options
} ) {
2240 if( $indexes[ $index ] == $nr_options ){
2241 # If a theta matches an index, we take the next index
2242 # and forget the theta.
2243 unless( $index > $#indexes ){
2247 # Otherwise we rember it.
2248 push(@keep_options,$option);
2252 if( scalar(@keep_options) > 0 ){
2253 # If we remember some thetas, we must also remember the
2254 # record which they are in.
2255 $record -> options
( \
@keep_options );
2256 push( @keep_records, $record );
2260 # Set the all kept thetas back into the modelobject.
2261 @
{$inits_ref} = @keep_records;
2264 'debug' -> die( message
=> "No init of type $type defined" );
2275 # restore_inits brings back initial values previously stored
2276 # using store_inits. This method pair allows a user to store
2277 # the currents initial values in a backup, replace them with
2278 # temporary values and later restore them.
2280 if ( defined $self -> {'problems'} ) {
2281 foreach my $problem ( @
{$self -> {'problems'}} ){
2282 $problem -> restore_inits
;
2294 # store_inits stores initial values that can later be
2295 # brought back using restore_inits. See L</restore_inits>.
2297 if ( defined $self -> {'problems'} ) {
2298 foreach my $problem ( @
{$self -> {'problems'}} ){
2299 $problem -> store_inits
;
2311 # Synchronize checks the I<synced> object attribute to see
2312 # if the model is in sync with its corresponding file, given
2313 # by the objetc attribute I<filename>. If not, it checks if
2314 # the model contains any defined problems and if it does, it
2315 # writes the formatted model to disk, overwriting any
2316 # existing file of name I<filename>. If no problem is
2317 # defined, synchronize tries to parse the file I<filename>
2318 # and set the object internals to match it.
2319 unless( $self -> {'synced'} ){
2320 if( defined $self -> {'problems'} and
2321 scalar @
{$self -> {'problems'}} > 0 ){
2324 if( -e
$self -> full_name
){
2325 $self -> _read_problems
;
2331 $self -> {'synced'} = 1;
2339 # synchronizes the object with the file on disk and empties
2340 # most of the objects attributes to save memory.
2341 if( defined $self -> {'problems'} and
2342 ( !$self -> {'synced'} or $force ) ) {
2345 $self -> {'problems'} = undef;
2346 $self -> {'synced'} = 0;
2354 if ( $parm eq 'disk' ) {
2355 $self -> {'target'} = 'disk';
2357 } elsif ( $parm eq 'mem' and $self -> {'target'} eq 'disk' ) {
2358 $self -> {'target'} = 'mem';
2359 $self -> synchronize
;
2371 # @tableNames = @{$modobj -> table_names};
2373 # This basic usage takes no arguments and returns the value of
2374 # the FILE option in the $TABLE NONMEM record of each
2375 # problem. @tableNames will be a two dimensional array:
2377 # [[tableName_prob1][tableName_prob2][tableName_prob3]...]
2380 # If the I<new_names> argument of table_names is given, the
2381 # values of the FILE options will be changed.
2383 # To set the FILE of specific problems, the I<problem_numbers>
2384 # argument can be used. It should be a reference to an array
2385 # containing the numbers of all problems where the FILE should
2386 # be changed or retrieved. If specified, the size of
2387 # I<new_names> must be the same as the size of
2388 # I<problem_numbers>.
2390 # The I<ignore_missing_files> boolean argument can be used to
2391 # set names of table that does not exist yet (e.g. before a
2392 # run has been performed).
2394 my ( $name_ref, $junk ) = $self ->
2395 _option_val_pos
( name
=> 'FILE',
2396 record_name
=> 'table',
2397 problem_numbers
=> \
@problem_numbers,
2398 new_values
=> \
@new_names );
2399 if ( $#new_names >= 0 ) {
2400 my @problems = @
{$self -> {'problems'}};
2401 unless( $#problem_numbers > 0 ){
2402 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
2404 foreach my $i ( @problem_numbers ) {
2405 $problems[$i-1] -> _read_table_files
( ignore_missing_files
=> $ignore_missing_files || $self -> {'ignore_missing_output_files'});
2408 @names = @
{$name_ref};
2420 # @table_files = @{$modobj -> table_files};
2422 # This basic usage takes no arguments and returns the table
2423 # files objects for all problems. @table_files will be a
2424 # two dimensional array:
2426 # [[table_file_object_prob1][table_file_object_prob2]...]
2429 # To retrieve the table file objects from specific problems,
2430 # the I<problem_numbers> argument can be used. It should be
2431 # a reference to an array containing the numbers of all
2432 # problems from which the table file objects should be
2435 unless( $#problem_numbers > 0 ){
2436 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
2438 my @problems = @
{$self -> {'problems'}};
2439 foreach my $i ( @problem_numbers ) {
2440 if ( defined $problems[ $i-1 ] ) {
2441 push( @table_files, $problems[$i-1] -> table_files
);
2443 'debug' -> die( message
=> "Problem number $i does not exist!" );
2455 # Sets or gets the units of a (number of) parameter(s). The
2456 # unit is not a proper NONMEM syntax but is recognized by
2457 # the PsN model class. A unit (and a label) can be specified
2458 # as a comments after a parameter definition. e.g.:
2460 # $THETA (0,13.2,100) ; MTT; h
2462 # which will give this theta the label I<MTT> and unit I<h>.
2463 @units = @
{ $self -> _init_attr
( parameter_type
=> $parameter_type,
2464 parameter_numbers
=> \
@parameter_numbers,
2465 problem_numbers
=> \
@problem_numbers,
2466 new_values
=> \
@new_values,
2479 # $modobj -> update_inits ( from_output => $outobj );
2483 # $modobj -> update_inits ( from_output_file => $outfile );
2485 # This basic usage takes the parameter estimates from the
2486 # output object I<$outobj> or from the output file I<$outfile>
2487 # and updates the initial estimates in the model object
2488 # I<$modobj>. The number of problems and parameters must be
2489 # the same in the model and output objects. If there exist
2490 # more than one subproblem per problem in the output object,
2491 # only the estimates from the first subproblem will be
2494 # $modobj -> update_inits ( from_output => $outobj,
2495 # ignore_missing_parameters => 1 );
2497 # If the ignore_missing_parameters argument is set to 1, the number of
2498 # parameters in the model and output objects do not need to match. The
2499 # parameters that exist in both objects are used for the update of the
2502 # $modobj -> update_inits ( from_output => $outobj,
2503 # from_model => $from_modobj );
2505 # If the from_model argument is given, update_inits tries to match the
2506 # parameter names (labels) given in $from_modobj and $modobj and
2507 # and thereafter updating the $modobj object. See L</units> and L</labels>.
2510 my ( %labels, @own_labels, @from_labels );
2511 'debug' -> die( message
=> "No output object defined and" .
2512 " no output object found through the model object specified." )
2513 unless ( ( defined $from_model and
2514 ( defined $from_model -> outputs
and
2515 defined @
{$from_model -> outputs
}[0] ) ) or
2516 defined $from_output or
2517 defined $from_output_file );
2518 if ( defined $from_output ) {
2519 'debug' -> warn( level
=> 2,
2520 message
=> "using output object ".
2521 "specified as argument\n" );
2522 } elsif ( defined $from_output_file ) {
2523 $from_output = output
-> new
( filename
=> $from_output_file );
2525 $from_output = @
{$from_model -> outputs
}[0];
2529 if( $update_thetas ){
2530 push( @params, 'theta' );
2532 if( $update_omegas ) {
2533 push( @params, 'omega' );
2535 if( $update_sigmas ) {
2536 push( @params, 'sigma' );
2539 foreach my $param ( @params ) {
2540 # Get own labels and from labels
2541 if ( defined $from_model ) {
2542 @own_labels = @
{$self -> labels
( parameter_type
=> $param )};
2544 @from_labels = @
{$from_model -> labels
( parameter_type
=> $param )};
2545 'debug' -> die( message
=> "The number of problems are not the same in from-model ".
2546 $from_model -> full_name
." (".
2547 ($#from_labels+1).")".
2548 " and the model to be updated ".
2549 $self -> full_name
." (".
2550 ($#own_labels+1).")" ) unless ( $#own_labels == $#from_labels );
2552 @own_labels = @
{$self -> labels
( parameter_type
=> $param,
2554 @from_labels = @
{$from_output -> labels
( parameter_type
=> $param )};
2555 'debug' -> die( message
=> "The number of problems are not the same in from-output ".
2556 $from_output -> full_name
." (".
2557 ($#from_labels+1).")".
2558 " and the model to be updated ".
2559 $self -> full_name
." (".
2560 ($#own_labels+1).")" ) unless ( $#own_labels == $#from_labels );
2563 # Loop over the problems:
2564 my $accessor = $param.'s';
2565 # Since initial estimates are specified on the problem level and not on
2566 # the subproblem level we use the estimates from the outputs first subproblem
2567 my @from_values = @
{$from_output -> $accessor ( subproblems
=> [1] )};
2568 # {{{ Omega and Sigma update section
2570 # The functionality that has been commented out because it
2571 # fails when omegas are zero. This functionality should be
2572 # moved to output::problem::subproblem (2005-02-09) TODO
2574 # if ($param eq 'omega' or $param eq 'sigma')
2576 # #print "FL: ", Dumper @from_labels;
2577 # #print "OL: ", Dumper @own_labels;
2578 # print "FV: $param Before " . Dumper(@from_values) . "\n";
2579 # #Fix omegas and sigmas so that the correlation between elements <=1
2580 # my $raw_accessor = "raw_" . $accessor;
2581 # my @raw_from_values = @{$from_output -> $raw_accessor(subproblems => [1])};
2583 # for (my $a=0; $a<scalar(@from_values); $a++)
2585 # my $prob_values = $from_values[$a];
2586 # my $raw_prob_values = $raw_from_values[$a];
2587 # for (my $b=0; $b<scalar(@{$prob_values}); $b++)
2589 # my $values = $prob_values->[$b];
2590 # my $raw_values = $raw_prob_values->[$b];
2592 # #Find out the n*n-matrix size (pq-formula)
2593 # my $n = int(sqrt(1/4+scalar(@{$raw_values})*2)-1/2);
2594 # for ($i=0; $i<$n; $i++)
2596 # for ($j=0; $j<$n; $j++)
2598 # if ( $j<=$i && $raw_values->[$i*($i+1)/2+$j]!=0 )
2600 # #print "Omega value = " . @other_val[$counter] . "\n";
2603 # #Only check the low-triangular off-diagonals of the omega matrix
2604 # #omega(i,j)>=sqrt(omega(i,i)*omega(j,j))
2605 # if ($j<=$i && $j!=$i &&
2606 # ($raw_values->[$i*($i+1)/2+$j]>=sqrt(
2607 # $raw_values->[$i*($i+1)/2+$i]*$raw_values->[$j*($j+1)/2+$j])))
2609 # print "Changing raw value at $i, $j: " . $raw_values->[$i*($i+1)/2+$j] ." to ".
2610 # (int(10000*sqrt($raw_values->[$i*($i+1)/2+$i]*$raw_values->[$j*($j+1)/2+$j]))/10000) ."\n";
2611 # #print "At index ($i,$j)\n" if ($self->{'debug'});
2612 # $raw_values->[$i*($i+1)/2+$j]=int(10000*sqrt(
2613 # $raw_values->[$i*($i+1)/2+$i]*$raw_values->[$j*($j+1)/2+$j]))/10000;
2614 # print "Changing omega value " . $values->[$counter-1] . " to " . $raw_values->[$i*($i+1)/2+$j] ."\n";
2615 # $values->[$counter-1] = $raw_values->[$i*($i+1)/2+$j];
2621 # #print "FL: ", Dumper @from_labels;
2622 # #print "OL: ", Dumper @own_labels;
2623 # print "FV: $param After ", Dumper(@from_values), "\n";
2629 for ( my $i = 0; $i <= $#own_labels; $i++ ) {
2630 unless ( $ignore_missing_parameters ) {
2631 my $from_name = defined $from_model ?
$from_model -> filename
:
2632 $from_output -> filename
;
2633 'debug' -> die( message
=> "Model -> update_inits: The number of ".$param.
2634 "s are not the same in from-model (" . $from_name .
2635 "): " . scalar @
{$from_labels[$i]} .
2636 ", and the model to be updated (" . $self -> {'filename'} .
2637 "): " . scalar @
{$own_labels[$i]} )
2638 unless ( scalar @
{$own_labels[$i]} ==
2639 scalar @
{$from_labels[$i]} );
2642 for ( my $j = 0; $j < scalar @
{$from_labels[$i]}; $j++ ) {
2643 for ( my $k = 0; $k < scalar @
{$own_labels[$i]}; $k++ ) {
2644 if ( $from_labels[$i][$j] eq $own_labels[$i][$k] ){
2645 $labels{$k+1} = $from_values[$i][0][$j];
2650 my @own_idxs = keys( %labels );
2652 for(my $i=0; $i <= $#own_idxs; $i++){
2653 @from_vals[$i] = $labels{ $own_idxs[$i] };
2656 $self -> initial_values
( problem_numbers
=> [$i+1],
2657 parameter_type
=> $param,
2658 parameter_numbers
=> [\
@own_idxs],
2659 new_values
=> [\
@from_vals] );
2671 # upper_bounds either sets or gets the initial values of the
2672 # parameter specified in I<parameter_type> for each
2673 # subproblem specified in I<problem_numbers>. For each
2674 # element in I<problem_numbers> there must be an array in
2675 # I<parameter_numbers> that specify the indices of the
2676 # parameters in the subproblem for which the upper bounds
2677 # are set, replaced or retrieved.
2679 @upper_bounds = @
{ $self -> _init_attr
2680 ( parameter_type
=> $parameter_type,
2681 parameter_numbers
=> \
@parameter_numbers,
2682 problem_numbers
=> \
@problem_numbers,
2683 new_values
=> \
@new_values,
2684 attribute
=> 'upbnd')};
2690 # {{{ clean_extra_data_code
2691 start clean_extra_data_code
2694 # This method cleans out old code for extra data. It searches
2695 # all subroutine statements in all problems for external
2696 # subroutines named "get_sub" and "reader" which are added by
2697 # "add_extra_data_code".
2699 foreach my $problem( @
{$self -> {'problems'}} ){
2700 if ( defined $problem -> subroutines
and defined $problem -> subroutines
-> [0] -> options
) {
2701 foreach my $option ( @
{$problem -> subroutines
-> [0] -> options
} ){
2702 if( lc($option -> name
) eq 'other'){
2703 if( lc($option -> value
) =~ /get_sub|reader/ ){
2705 # If we find "get_sub" or "reader" we remove
2706 # everything between "IMPORTING COVARIATE DATA" and
2707 # "IMPORTING COVARIATE DATA END" by finding the
2708 # indexes in the code array and and splicing it out.
2711 if( $problem -> pks
){
2712 # If the code is in a pk block:
2713 $code = $problem -> pks
-> [0] -> code
;
2715 $code = $problem -> preds
-> [0] -> code
;
2720 for( my $i = 0; $i <= $#{$code}; $i++ ){
2721 if( $code -> [$i] eq ";***IMPORTING COVARIATE DATA*******\n" ){
2724 if( $code -> [$i] eq ";***IMPORTING COVARIATE DATA END***\n" ){
2728 @
{$code} = ( @
{$code}[0..$start_idx] , @
{$code}[$end_idx..$#{$code}] );
2730 if( $problem -> pks
){
2731 # Put the cut down code back in the right place:
2732 $problem -> pks
-> [0] -> code
( $code );
2734 $problem -> preds
-> [0] -> code
( $code );
2744 end clean_extra_data_code
2745 # }}} clean_extra_data_code
2747 # {{{ add_extra_data_code
2749 start add_extra_data_code
2751 # This method adds fortran code that will handle wide datasets
2752 # (that is data sets with more than 20 columns). It adds code to
2753 # each problems pk or pred.
2757 # Get the headers of the columns that have been moved to another
2760 # unless( defined $self -> extra_data_headers ){
2761 # die "ERROR model::add_extra_data_code: No headers for the extra data file\n";
2764 # extra_data_headers is a two dimensional array. One array of
2765 # headers for each problem in the modelfile.
2766 for( my $i = 0; $i <= $#{$self -> {'problems'}}; $i++ ){
2767 if ( defined $self -> {'problems'} -> [$i] -> {'extra_data_header'} ) {
2768 my $problem_headers = $self -> {'problems'} -> [$i] -> {'extra_data_header'};
2773 # Loop over the problem specific headers and make a string
2774 # that will go into the fortran code. Assume that the
2775 # first column holds the ID, hence the $i=1
2776 for (my $i = 1; $i <= $#{$problem_headers}; $i++ ) {
2777 my $header = $problem_headers -> [$i];
2778 push( @headers, $header );
2779 # Chopp the string at 40 characters, to be nice to g77 :)
2780 if ( $length + length($header) > 40 ) {
2781 $header_string .= "\n\"& ";
2784 if ( $i < $#{$problem_headers} ) {
2785 $header_string .= 'I' . $header . ', ';
2786 $length += length( 'I' . $header . ', ' );
2788 $header_string .= 'I' . $header;
2789 $length += length( 'I' . $header );
2793 my @code_lines = ('',
2794 ';***IMPORTING COVARIATE DATA*******',
2796 '" REAL CURID, MID,',
2797 '"& '.$header_string,
2800 '" IF (.NOT.READ) THEN',
2806 '" IF (NEWIND.LT.2) THEN',
2807 '" CALL GET_SUB (NEWIND,ID,CURID,MID,',
2808 '"& '.$header_string. ')',
2811 ' IF (CID.NE.ID) THEN',
2812 ' PRINT *, \'ERROR CHECKING FAILED, CID = \', CID ,\' ID = \', ID',
2816 foreach my $header ( @headers ) {
2817 push( @code_lines, " $header = I$header" );
2820 push( @code_lines, (';***IMPORTING COVARIATE DATA END***','','') );
2822 my $problem = $self -> {'problems'} -> [$i];
2823 if ( defined $problem -> {'subroutines'} ) {
2824 $problem -> subroutines
-> [0] -> _add_option
( option_string
=> 'OTHER=get_sub' . $i );
2825 $problem -> subroutines
-> [0] -> _add_option
( option_string
=> 'OTHER=reader' . $i );
2827 $problem -> add_records
( type
=> 'subroutines', record_strings
=> ['OTHER=get_sub', 'OTHER=reader'] );
2830 if ( defined $problem -> pks
) {
2831 unshift( @
{$problem -> pks
-> [0] -> code
}, join("\n", @code_lines ));
2833 unshift( @
{$problem -> preds
-> [0] -> code
},join("\n", @code_lines ));
2838 end add_extra_data_code
2846 for( my $i = 0; $i < scalar @
{$self -> {'problems'}}; $i++ ) {
2847 $self -> {'datas'}[$i] -> drop_dropped
( model_header
=> $self -> {'problems'}[$i] -> header
);
2848 $self -> {'problems'}[$i] -> drop_dropped
( );
2849 #$self -> {'datas'}[$i] -> model_header( $self -> {'problems'}[$i] -> header );
2860 my $default_wrap = 18;
2862 my ( @wrap_columns, @cont_columns );
2863 if ( not defined $wrap_column ) {
2864 for( my $i = 0; $i < scalar @
{$self -> {'problems'}}; $i++ ) {
2865 my $columns = scalar @
{$self -> {'problems'}[$i] -> dropped_columns
}-1; #skip ID
2866 my $modulus = $columns%($default_wrap-2); # default_wrap-2 to account for ID and CONT
2867 my $rows = (($columns-$modulus)/($default_wrap-2))+1;
2869 push( @wrap_columns, undef );
2871 push( @wrap_columns, (ceil
( $columns/$rows )+2) ); #Must use #cols + ID and CONT
2875 for( my $i = 0; $i < scalar @
{$self -> {'problems'}}; $i++ ) {
2876 push( @wrap_columns, $wrap_column );
2880 for( my $i = 0; $i < scalar @
{$self -> {'problems'}}; $i++ ) {
2881 next if ( not defined $wrap_columns[$i] );
2882 $wrap_column = $wrap_columns[$i];
2883 $cont_column = $wrap_columns[$i] if( not defined $cont_column );
2884 my ( $prim, $sec ) =
2885 $self -> {'datas'}[$i] -> wrap
( cont_column
=> $cont_column,
2886 wrap_column
=> $wrap_column,
2887 model_header
=> $self -> {'problems'}[$i] -> header
);
2888 $self -> {'problems'}[$i] -> primary_columns
( $prim );
2889 $self -> {'problems'}[$i] -> secondary_columns
( $sec );
2890 $self -> {'data_wrapped'}++;
2900 for( my $i = 0; $i < scalar @
{$self -> {'problems'}}; $i++ ) {
2901 $self -> {'datas'}[$i] -> unwrap
;
2902 $self -> {'problems'}[$i] -> primary_columns
( [] );
2903 $self -> {'problems'}[$i] -> secondary_columns
( [] );
2905 $self -> {'data_wrapped'} = 0;
2910 # {{{ write_get_subs
2912 start write_get_subs
2914 for( my $i = 0; $i <= $#{$self -> {'problems'}}; $i++ ){
2915 if ( defined $self -> {'problems'} -> [$i] -> {'extra_data_header'} and
2916 defined $self -> problems
-> [$i] -> extra_data
) {
2917 my @problem_header = @
{$self -> {'problems'} -> [$i] -> {'extra_data_header'}};
2922 my $rows = $self -> problems
-> [$i] -> extra_data
-> count_ind
;
2924 # Assume that first column holds the ID. Get rid of it.
2925 shift( @problem_header );
2926 for ( my $i = 0; $i <= $#problem_header; $i++ ) {
2927 my $header = $problem_header[$i];
2928 push( @headers, $header );
2929 # Chop the string at 40 characters, to be nice to g77 :)
2930 if ( $length + length($header) > 40 ) {
2931 $header_string .= "\n & ";
2934 if ( $i < $#problem_header ) {
2935 $header_string .= $header . ', ';
2936 $length += length( $header . ', ' );
2938 $header_string .= $header;
2939 $length += length( $header );
2943 open( FILE
, '>', 'get_sub' . $i . '.f' );
2944 print FILE
(" SUBROUTINE GET_SUB (NEWIND,ID,CURID,MID,\n",
2945 " & $header_string)\n",
2946 " COMMON /READ/ TID,TCOV\n",
2948 " REAL ID,CURID,MID,\n",
2949 " & $header_string\n",
2951 " INTEGER NEWIND\n",
2953 " REAL TID($rows), TCOV($rows,",scalar @problem_header,")\n",
2956 "C START AT TOP EVERY TIME\n",
2957 " IF (NEWIND.EQ.1) THEN \n",
2959 " IF (CURID.GT.$rows) THEN \n",
2960 " PRINT *, \"Covariate data not found for\", ID\n",
2965 " IF (ID.GT.TID (CURID)) THEN\n",
2966 " CURID = CURID + 1\n",
2969 " ELSEIF (NEWIND.EQ.0) THEN\n",
2974 for ( my $i = 1; $i <= $#headers+1; $i++ ) {
2975 $length += length("TCOV(I,$i),");
2976 if ( $length > 40 ) {
2980 print FILE
" ".$headers[$i-1] . " = TCOV(CURID,$i)\n";
2983 print FILE
(" MID = TID(CURID)\n",
3000 for( my $i = 0; $i <= $#{$self -> {'problems'}}; $i++ ){
3001 if ( defined $self -> {'problems'} -> [$i] -> {'extra_data_header'} and
3002 defined $self -> {'problems'} -> [$i] -> {'extra_data'} ) {
3003 my @problem_header = @
{$self -> {'problems'} -> [$i] -> {'extra_data_header'}};
3007 my $rows = $self -> problems
-> [$i] -> extra_data
-> count_ind
;
3008 my $filename = $self -> problems
-> [$i] -> extra_data
-> filename
;
3009 # Assume that first column holds the ID. Get rid of it.
3010 shift( @problem_header );
3012 'debug' -> warn( level
=> 2,
3013 message
=> "Writing reader".$i.".f to directory".cwd
);
3014 open( FILE
, '>', 'reader' . $i . '.f' );
3015 print FILE
(" SUBROUTINE READER()\n",
3017 " COMMON /READ/ TID,TCOV\n",
3019 " REAL TID ($rows), TCOV ($rows, ",scalar @problem_header,")\n",
3021 " OPEN (UNIT = 77,FILE = '$filename')\n",
3023 " DO 11,I = 1,$rows\n",
3024 " READ (77,*) TID(I)," );
3027 for ( my $i = 1; $i <= $#problem_header+1; $i++ ) {
3028 $length += length("TCOV(I,$i),");
3029 if ( $length > 40 ) {
3033 if ( $i <= $#problem_header ) {
3034 print FILE
"TCOV(I,$i),";
3036 print FILE
"TCOV(I,$i)\n";
3040 print FILE
( "11 CONTINUE\n",
3054 # $model -> _write( filename => 'model.mod' );
3056 # Writes the content of the modelobject to disk. Either to the
3057 # filename given, or to the string returned by model::full_name.
3061 # An element in the active_problems array is a boolean that
3062 # corresponds to the element with the same index in the problems
3063 # array. If the boolean is true, the problem will be run. All
3064 # other will be commented out.
3065 my @active = @
{$self -> {'active_problems'}};
3067 # loop over all problems.
3068 for ( my $i = 0; $i < scalar @
{$self -> {'problems'}}; $i++ ) {
3069 # Call on the problem object to format it as text. The
3070 # filename and problem numbers are needed to make some
3071 # autogenerated files (msfi, tabels etc...) unique to the
3073 my @preformatted = @
{$self -> {'problems'} -> [$i] ->
3074 # _format_problem };
3075 _format_problem
( filename
=> $self -> filename
,
3076 problem_number
=> ($i+1) ) };
3077 # Check if the problem is NOT active, if so comment it out.
3078 unless ( $active[$i] ) {
3079 for ( my $j = 0; $j <= $#preformatted; $j++ ) {
3080 $preformatted[$j] = '; '.$preformatted[$j];
3083 # Add extra line to avoid problems with execution of NONMEM
3084 push(@preformatted,"\n");
3085 push( @formatted, @preformatted );
3088 # Open a file and print the formatted problems.
3089 # TODO Add some errorchecking.
3090 open( FILE
, '>'. $filename );
3091 for ( @formatted ) {
3098 if ( $write_data ) {
3099 foreach my $data ( @
{$self -> {'datas'}} ) {
3111 if ( defined $parm and $parm ne $self -> {'filename'} ) {
3112 $self -> {'filename'} = $parm;
3113 $self -> {'model_id'} = undef;
3120 # {{{ _get_option_val_pos
3122 start _get_option_val_pos
3126 # ( $values_ref, $positions_ref ) ->
3127 # _get_option_val_pos ( name => 'ID',
3128 # record_name => 'input' );
3129 # my @values = @{$values_ref};
3130 # my @positions = @{$positions_ref};
3132 # This basic usage returns the name of the third option in the first
3133 # instance of the record specified by I<record_name> for all problems
3135 # If global_position is set to 1, only one value and position
3136 # pair is returned per problem. If there are more than one
3137 # match in the model; the first will be returned for each
3140 # Private method, should preferably not be used outside model.pm
3142 # my ( @records, @instances );
3143 my $accessor = $record_name.'s';
3144 my @problems = @
{$self -> {'problems'}};
3145 unless( $#problem_numbers > 0 ){
3146 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
3148 foreach my $i ( @problem_numbers ) {
3149 my $rec_ref = $problems[ $i-1 ] -> $accessor;
3150 if ( defined $problems[ $i-1 ] and defined $rec_ref ) {
3151 my @records = @
{$rec_ref};
3152 unless( $#instances > 0 ){
3153 @instances = (1 .. $#records+1);
3156 my @inst_values = ();
3157 my @inst_positions = ();
3159 my ( $glob_value, $glob_position );
3160 INSTANCES
: foreach my $j ( @instances ) {
3161 if ( defined $records[ $j-1 ] ) {
3163 my ( $value, $position );
3164 foreach my $option ( @
{$records[$j-1] -> {'options'}} ) {
3165 if ( defined $option and $option -> name
eq $name) {
3166 if ( $global_position ) {
3167 $glob_value = $option -> value
;
3168 $glob_position = $glob_pos;
3171 $value = $option -> value
;
3178 push( @inst_values, $value );
3179 push( @inst_positions, $position );
3181 'debug' -> die( message
=> "Instance $j in problem number $i does not exist!" )
3184 if ( $global_position ) {
3185 push( @values, $glob_value );
3186 push( @positions, $glob_position );
3188 push( @values, \
@inst_values );
3189 push( @positions, \
@inst_positions );
3192 'debug' -> die( message
=> "Problem number $i does not exist!" );
3195 # if( defined $problem_number ) {
3196 # if( $problem_number < 1 or $problem_number > scalar @problems ) {
3197 # die "model -> _get_option_val_pos: No such problem number, ",
3198 # $problem_number,", in this model!\n";
3202 # die "modelfile -> _get_option_val_pos: No problem $problem_number exists\n"
3203 # if( (scalar @problems < 1) and ($problem_number ne 'all') );
3205 # foreach my $problem ( @problems ) {
3206 # @records = @{$problem -> $accessor};
3207 # @records = ($records[$instance-1]) if ( $instance ne 'all' );
3208 # die "modelfile -> _get_option_val_pos: No record instance $instance ".
3209 # "of record $record_name in problem $problem_number exists\n"
3210 # if( (scalar @records < 1) and ($instance ne 'all') );
3211 # foreach my $record ( @records ) {
3213 # foreach my $option ( @{$record -> {'options'}} ) {
3214 # if ( defined $option and $option -> name eq $name) {
3215 # print "Found $name at $i\n" if ( $self -> {'debug'} );
3216 # push( @values, $option -> value );
3217 # push( @positions, $i );
3224 end _get_option_val_pos
3226 # }}} _get_option_val_pos
3232 # The I<add_if_absent> argument tells the method to add an init (theta,omega,sigma)
3233 # if the parameter number points to a non-existing parameter with parameter number
3234 # one higher than the highest presently included. Only applicatble if
3235 # I<new_values> are set. Default value = 0;
3237 unless( scalar @problem_numbers > 0 ){
3238 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
3240 my @problems = @
{$self -> {'problems'}};
3241 if ( $#new_values >= 0 ) {
3242 'debug' -> die( message
=> "The number of new value sets " .
3243 $#new_values+1 . " do not" .
3244 " match the number of problems " . $#problem_numbers+1 . " specified" )
3245 unless(($#new_values == $#problem_numbers) );
3246 if ( $#parameter_numbers > 0 ) {
3247 'debug' -> die( message
=> "The number of parameter number sets do not" .
3248 " match the number of problems specified" )
3249 unless(($#parameter_numbers == $#problem_numbers) );
3253 my $new_val_idx = 0;
3254 foreach my $i ( @problem_numbers ) {
3255 if ( defined $problems[ $i-1 ] ) {
3256 if ( scalar @new_values > 0) {
3258 # Use attribute parameter_values to collect diagnostic outputs
3259 push( @parameter_values,
3260 $problems[ $i-1 ] ->
3261 _init_attr
( parameter_type
=> $parameter_type,
3262 parameter_numbers
=> $parameter_numbers[ $new_val_idx ],
3263 new_values
=> \@
{$new_values[ $new_val_idx ]},
3264 attribute
=> $attribute,
3265 add_if_absent
=> $add_if_absent ) );
3268 # {{{ Retrieve values
3269 push( @parameter_values,
3270 $problems[ $i-1 ] ->
3271 _init_attr
( parameter_type
=> $parameter_type,
3272 parameter_numbers
=> $parameter_numbers[ $i-1 ],
3273 attribute
=> $attribute ) );
3274 # }}} Retrieve values
3277 'debug' -> die( message
=> "Problem number $i does not exist!" );
3292 # $modobj -> _option_name ( record => $record_name,
3295 # This basic usage returns the name of the third option in the first
3296 # instance of the record specified by I<record>.
3299 my ( @problems, @records, @options, $i );
3300 my $accessor = $record.'s';
3301 if ( defined $self -> {'problems'} ) {
3302 @problems = @
{$self -> {'problems'}};
3304 'debug' -> die( message
=> "No problems defined in model" );
3306 if ( defined $problems[$problem_number - 1] -> $accessor ) {
3307 @records = @
{$problems[$problem_number - 1] -> $accessor};
3309 'debug' -> die( message
=> "No record $record defined in ".
3310 "problem number $problem_number." );
3312 if ( defined $records[$instance - 1] -> options
) {
3313 @options = @
{$records[$instance - 1] -> options
};
3315 'debug' -> die( message
=> "model -> _option_name: No option defined in record ".
3316 "$record in problem number $problem_number." );
3319 foreach my $option ( @options ) {
3320 if ( $i == $position ) {
3321 if ( defined $new_name ){
3322 $option -> name
($new_name) if ( defined $option );
3324 $name = $option -> name
if ( defined $option );
3334 # {{{ _parameter_count
3335 start _parameter_count
3337 if( defined $self -> {'problems'} ){
3338 my $problems = $self -> {'problems'};
3339 if( defined @
{$problems}[$problem_number - 1] ){
3340 $count = @
{$problems}[$problem_number - 1] -> record_count
( 'record_name' => $record );
3344 end _parameter_count
3345 # }}} _parameter_count
3347 # {{{ _read_problems
3349 start _read_problems
3352 # To read problems from a modelfile we need its full name
3353 # (meaning filename and path). And we need an array for the
3354 # modelfile lines and an array with indexes telling where
3355 # problems start in the modelfile array.
3358 my $file = $self -> full_name
;
3359 my ( @modelfile, @problems );
3360 my ( @problem_start_index );
3362 # Check if the file is missing, and if that is ok.
3363 # TODO Check accessor what happens if the file is missing.
3365 return if( not (-e
$file) && $self -> {'ignore_missing_files'} );
3367 # Open the file, slurp it and close it
3368 open( FILE
, "$file" ) ||
3369 'debug' -> die( message
=> "Model -> _read_problems: Could not open $file".
3371 @modelfile = <FILE
>;
3374 my @extra_data_files = defined $self ->{'extra_data_files'} ?
3375 @
{$self -> {'extra_data_files'}} : ();
3376 my @extra_data_headers = defined $self ->{'extra_data_headers'} ?
3377 @
{$self -> {'extra_data_headers'}} : ();
3380 # # Find the indexes where the problems start
3381 # for ( my $i = 0; $i <= $#modelfile; $i++ ) {
3382 # push( @problem_start_index, $i )if ( $modelfile[$i] =~ /\$PROB/ );
3385 # # Loop over the number of problems. Copy the each problems lines
3386 # # and create a problem object.
3388 # for( my $i = 0; $i <= $#problem_start_index; $i++ ) {
3389 # my $start_index = $problem_start_index[$i];
3390 # my $end_index = defined $problem_start_index[$i+1] ? $problem_start_index[$i+1] - 1: $#modelfile ;
3392 # my @problem_lines = @modelfile[$start_index .. $end_index];
3394 # # Problem object creation.
3395 # push( @problems, model::problem -> new ( debug => $self -> {'debug'},
3396 # ignore_missing_files => $self -> {'ignore_missing_files'},
3397 # prob_arr => \@problem_lines,
3398 # extra_data_file_name => $extra_data_files[$i],
3399 # extra_data_header => $extra_data_headers[$i]) );
3401 my $start_index = 0;
3406 # It may look like the loop takes one step to much, but its a
3407 # trick that helps parsing the last problem.
3408 for ( my $i = 0; $i <= @modelfile; $i++ ) {
3409 if( $i <= $#modelfile ){
3410 $_ = $modelfile[$i];
3413 # In this if statement we use the lazy evaluation of logical
3414 # or to make sure we only execute search pattern when we have
3415 # a line to search. Which is all cases but the very last loop
3418 if( $i > $#modelfile or /\$PROB/ ){
3421 # The if statement here is only necessary in the first loop
3422 # iteration. When start_index == end_index == 0 we want to
3423 # skip to the next iteration looking for the actual end of
3424 # the first problem.
3426 if( $end_index > $start_index and not $first ){
3427 # extract lines of code:
3428 my @problem_lines = @modelfile[$start_index .. $end_index-1];
3429 # reset the search for problems by moving the problem start
3433 my $sh_mod = new model
::shrinkage_module
( model
=> $self,
3434 temp_problem_number
=> ($#problems+2));
3435 push( @problems, model
::problem
->
3436 new
( directory
=> $self -> {'directory'},
3437 ignore_missing_files
=> $self -> {'ignore_missing_files'},
3438 ignore_missing_output_files
=> $self -> {'ignore_missing_output_files'},
3439 sde
=> $self -> {'sde'},
3440 prob_arr
=> \
@problem_lines,
3441 extra_data_file_name
=> $extra_data_files[$prob_num],
3442 extra_data_header
=> $extra_data_headers[$prob_num],
3443 shrinkage_module
=> $sh_mod ) );
3445 $sh_mod -> problem
( $problems[$#problems] );
3452 # Set the problems in the modelobject.
3453 $self -> problems
(\
@problems);
3457 # }}} _read_problems
3463 unless( $#problem_numbers >= 0 ){
3464 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
3467 my @problems = @
{$self -> {'problems'}};
3468 foreach my $i ( @problem_numbers ) {
3469 if ( defined $problems[ $i-1 ] ) {
3470 my $found = $self -> is_option_set
( 'problem_number' => $i,
3471 'record' => $record_name,
3472 'name' => $option_name );
3473 $problems[$i-1] -> remove_option
( record_name
=> $record_name,
3474 option_name
=> $option_name ) if ( $found );
3475 $problems[$i-1] -> add_option
( record_name
=> $record_name,
3476 option_name
=> $option_name,
3477 option_value
=> $option_value );
3489 unless( $#problem_numbers >= 0 ){
3490 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
3493 my @problems = @
{$self -> {'problems'}};
3494 foreach my $i ( @problem_numbers ) {
3495 if ( defined $problems[ $i-1 ] ) {
3496 $problems[$i-1] -> add_option
( record_name
=> $record_name,
3497 option_name
=> $option_name,
3498 option_value
=> $option_value,
3499 add_record
=> $add_record );
3511 unless( $#problem_numbers >= 0 ){
3512 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
3515 my @problems = @
{$self -> {'problems'}};
3516 foreach my $i ( @problem_numbers ) {
3517 if ( defined $problems[ $i-1 ] ) {
3518 $problems[$i-1] -> remove_option
( record_name
=> $record_name,
3519 option_name
=> $option_name );
3527 # {{{ _option_val_pos
3529 start _option_val_pos
3531 unless( $#problem_numbers >= 0 ){
3532 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
3534 my @problems = @
{$self -> {'problems'}};
3535 if ( $#new_values >= 0 ) {
3536 'debug' -> die( message
=> "Trying to set option $name in record $record_name but the ".
3537 "number of new value sets (".
3539 "), do not match the number of problems specified (".
3540 ($#problem_numbers+1).")" )
3541 unless(($#new_values == $#problem_numbers) );
3542 if ( $#instance_numbers > 0 ) {
3543 'debug' -> die( message
=> "The number of instance number sets (".
3544 ($#instance_numbers+1).
3545 "),do not match the number of problems specified (".
3546 ($#problem_numbers+1).")" )
3547 unless(($#instance_numbers == $#problem_numbers) );
3551 foreach my $i ( @problem_numbers ) {
3552 if ( defined $problems[ $i-1 ] ) {
3553 my $rn_ref = $#instance_numbers >= 0 ? \@
{$instance_numbers[ $i-1 ]} : [];
3554 if ( scalar @new_values > 0) {
3557 if( not defined $new_values[ $i-1 ] ) {
3558 debug
-> die( message
=> " The specified new_values was undefined for problem $i" );
3561 if( not ref( $new_values[ $i-1 ] ) eq 'ARRAY' ) {
3562 debug
-> die( message
=> " The specified new_values for problem $i is not an array as it should be but a ".
3563 ( defined ref( $new_values[ $i-1 ] ) ?
3564 ref( $new_values[ $i-1 ] ) : 'undef' ) );
3567 $problems[ $i-1 ] ->
3568 _option_val_pos
( record_name
=> $record_name,
3569 instance_numbers
=> $rn_ref,
3570 new_values
=> \@
{$new_values[ $i-1 ]},
3572 exact_match
=> $exact_match );
3576 # {{{ Retrieve values
3577 my ( $val_ref, $pos_ref ) =
3578 $problems[ $i-1 ] ->
3579 _option_val_pos
( record_name
=> $record_name,
3580 instance_numbers
=> $rn_ref,
3582 exact_match
=> $exact_match );
3583 push( @values, $val_ref );
3584 push( @positions, $pos_ref );
3585 # }}} Retrieve values
3588 'debug' -> die( message
=> "Problem number $i does not exist!" );
3594 # }}} _option_val_pos
3596 # {{{ subroutine_files
3598 start subroutine_files
3601 foreach my $subr( 'PRED','CRIT', 'CONTR', 'CCONTR', 'MIX', 'CONPAR', 'OTHER', 'PRIOR' ){
3602 my ( $model_fsubs, $junk ) = $self -> _option_val_pos
( record_name
=> 'subroutine',
3604 if( @
{$model_fsubs} > 0 ){
3605 foreach my $prob_fsubs ( @
{$model_fsubs} ){
3606 foreach my $fsub( @
{$prob_fsubs} ){
3613 @fsubs = keys %fsubs;
3615 for( my $i = 0; $i <= $#fsubs; $i ++ ){
3616 unless( $fsubs[$i] =~ /\.f$/ ){
3622 end subroutine_files