0001 function spec = dsCheckSpecification(specification, varargin)
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114 if ~nargin
0115 spec = localfunctions;
0116 return
0117 end
0118
0119
0120 options = dsCheckOptions(varargin,{'auto_gen_test_data_flag',0,{0,1}},false);
0121 if options.auto_gen_test_data_flag
0122 varargs = varargin;
0123 varargs{find(strcmp(varargs, 'auto_gen_test_data_flag'))+1} = 0;
0124 varargs(end+1:end+2) = {'unit_test_flag',1};
0125 argin = [{specification}, varargs];
0126 end
0127
0128
0129 if ischar(specification) || iscell(specification)
0130 spec.populations.equations=specification;
0131 elseif isstruct(specification)
0132 spec=specification;
0133 elseif isempty(specification)
0134 spec=struct;
0135 else
0136 error('specification must be a DynaSim specification structure or a string with equations or sub-model filename.');
0137 end
0138
0139 spec=backward_compatibility(spec, varargin{:});
0140 pop_field_order={'name','size','equations','mechanism_list','mechanisms','parameters',...
0141 'conditionals','monitors','model'};
0142 con_field_order={'source','target','mechanism_list','mechanisms','parameters'};
0143
0144 if ~isfield(spec,'populations')
0145 spec.populations.name='pop1';
0146 end
0147
0148 if ~isfield(spec,'connections')
0149 spec.connections=[];
0150 end
0151
0152 if ~isfield(spec,'mechanisms')
0153 spec.mechanisms=[];
0154 end
0155
0156
0157 if ~isfield(spec.populations,'name')
0158 spec.populations(1).name='pop1';
0159 end
0160
0161 if ~isfield(spec.populations,'size')
0162 spec.populations(1).size=1;
0163 end
0164
0165 if ~isfield(spec.populations,'equations')
0166 spec.populations(1).equations=[];
0167 end
0168
0169 if ~isfield(spec.populations,'mechanism_list')
0170 spec.populations(1).mechanism_list=[];
0171 end
0172
0173 if ~isfield(spec.populations,'mechanisms')
0174 spec.populations(1).mechanisms=[];
0175 end
0176
0177 if ~isfield(spec.populations,'parameters')
0178 spec.populations(1).parameters={};
0179 end
0180
0181 if ~isfield(spec.populations,'conditionals')
0182 spec.populations(1).conditionals=[];
0183 end
0184
0185 if ~isfield(spec.populations,'monitors')
0186 spec.populations(1).monitors=[];
0187 end
0188
0189 if ~isfield(spec.populations,'model')
0190 spec.populations(1).model=[];
0191 end
0192
0193
0194 if isfield(spec,'compartments')
0195 npops=length(spec.populations);
0196 fields=fieldnames(spec.compartments);
0197 for i=1:length(spec.compartments)
0198 for f=1:length(fields)
0199 spec.populations(npops+i).(fields{f})=spec.compartments(i).(fields{f});
0200 end
0201 end
0202 spec=rmfield(spec,'compartments');
0203 end
0204
0205
0206 for i=1:length(spec.populations)
0207 eqn=spec.populations(i).equations;
0208 if ~isempty(eqn) && ischar(eqn)
0209
0210 if exist([eqn '.eqns'],'file')
0211 eqn=[eqn '.eqns'];
0212 elseif exist([eqn '.pop'],'file')
0213 eqn=[eqn '.pop'];
0214 end
0215 if exist(eqn,'file')
0216
0217 spec.populations(i).equations=dsReadText(eqn);
0218 elseif ~isempty(regexp(eqn,'\[[a-z_A-Z].*\]','match','once'))
0219
0220
0221 tmp=regexp(eqn(2:end-1),'\],?\s*\[','split');
0222 spec.populations(i).equations=tmp{1};
0223 for j=2:length(tmp)
0224 spec.populations(end+1)=spec.populations(i);
0225 spec.populations(end).equations=tmp{j};
0226 spec.populations(end).name=sprintf('pop%g',length(spec.populations));
0227 end
0228 end
0229 end
0230 end
0231
0232
0233 for i=1:length(spec.populations)
0234
0235 if isempty(spec.populations(i).name)
0236 spec.populations(i).name=sprintf('pop%g',i);
0237 end
0238
0239
0240 if isempty(spec.populations(i).size)
0241 spec.populations(i).size=1;
0242 end
0243
0244
0245 if ischar(spec.populations(i).mechanism_list)
0246 spec.populations(i).mechanism_list={spec.populations(i).mechanism_list};
0247 end
0248
0249
0250 if ~iscell(spec.populations(i).parameters)
0251 spec.populations(i).parameters={};
0252 end
0253
0254
0255 if ~isempty(spec.populations(i).equations)
0256
0257 if iscell(spec.populations(i).equations)
0258 eqns=spec.populations(i).equations;
0259 for k=1:length(eqns)
0260 eqn=eqns{k};
0261 if ~isempty(eqn) && ~strcmp(eqn(end),';')
0262 eqns{k}(end+1)=';';
0263 end
0264 end
0265 spec.populations(i).equations=[eqns{:}];
0266 end
0267
0268
0269 eqn=spec.populations(i).equations;
0270 name=regexp(eqn,'^\w+:','match','once');
0271 if ~isempty(name)
0272
0273 eqn=strrep(eqn,name,'');
0274
0275
0276 name=regexp(name,'^(\w+):','tokens','once');
0277 spec.populations(i).equations=eqn;
0278 if strcmp(spec.populations(i).name,sprintf('pop%g',i))
0279
0280 spec.populations(i).name=name{1};
0281 end
0282 end
0283
0284
0285 eqn=spec.populations(i).equations;
0286
0287 pattern='((\w+(\[[\d,]+\])'')|(d\w+(\[[\d,]+\])/dt))\s*=';
0288
0289
0290 LHSs=regexp(eqn,pattern,'match');
0291 if ~isempty(LHSs)
0292
0293 for k=1:length(LHSs)
0294 tmp=regexp(LHSs{k},'\w+\[([\d,]+)\]''','tokens','once');
0295 if isempty(tmp)
0296 tmp=regexp(LHSs{k},'d\w+\[([\d,]+)\]/dt','tokens','once');
0297 end
0298 sz=cellfun(@str2double,regexp(tmp{1},',','split'));
0299
0300 if k==1
0301 sz_first=sz;
0302 elseif sz~=sz_first
0303 error('all variables in same population must have same size. split ODEs with different sizes into different populations.');
0304 end
0305
0306 old=LHSs{k};
0307 new=strrep(LHSs{k},['[' tmp{1} ']'],'');
0308 eqn=strrep(eqn,old,new);
0309 end
0310 spec.populations(i).equations=eqn;
0311 spec.populations(i).size=sz;
0312 end
0313
0314
0315
0316
0317
0318
0319
0320 mech_lists=regexp(spec.populations(i).equations,'\s*(\w+:)?{[\w\d@:,]*}\s*(@\w+)?;?\s*','match');
0321
0322 if ~isempty(mech_lists)
0323 for k=1:length(mech_lists)
0324 mech_list=strtrim(mech_lists{k});
0325
0326
0327 spec.populations(i).equations=strtrim(strrep(spec.populations(i).equations,mech_list,''));
0328
0329
0330 external_link=regexp(mech_list,'}(@[\w\d]+;?)','tokens');
0331 if ~isempty(external_link)
0332
0333 external_link=[external_link{:}];
0334
0335
0336 mech_list=strrep(mech_list,external_link{1},'');
0337
0338
0339 external_link=strrep(external_link{1},';','');
0340
0341
0342 words=regexp(mech_list(2:end-1),',','split');
0343
0344
0345 for w=1:length(words)
0346 mech_list=strrep(mech_list,words{w},[words{w} external_link]);
0347 end
0348 end
0349
0350
0351 host_name=regexp(mech_list,';?\s*([\w\d]+):{','tokens','once');
0352 if ~isempty(host_name)
0353
0354 host_name=[host_name{:}];
0355
0356
0357 mech_list=strrep(mech_list,[host_name ':'],'');
0358
0359
0360 words=regexp(mech_list(2:end-1),',','split');
0361
0362
0363 for w=1:length(words)
0364 mech_list=strrep(mech_list,words{w},[host_name ':' words{w}]);
0365 end
0366 end
0367
0368 mechanisms=regexp(mech_list,'[\w:@]+','match');
0369
0370
0371 if iscell(spec.populations(i).mechanism_list)
0372 spec.populations(i).mechanism_list=cat(2,mechanisms,spec.populations(i).mechanism_list);
0373 else
0374 spec.populations(i).mechanism_list=mechanisms;
0375 end
0376 end
0377 end
0378
0379 param_name={};
0380 param_value={};
0381 eqn=spec.populations(i).equations;
0382 p=getfield(dsParseModelEquations(eqn, varargin{:}),'parameters');
0383 if ~isempty(p)
0384 param_name=cat(1,param_name,fieldnames(p));
0385 param_value=cat(1,param_value,struct2cell(p));
0386 end
0387
0388
0389 o=regexp(eqn,';\s*[a-zA-Z]+\w*\.[a-zA-Z]+\w*\s*=[a-z_A-Z0-9\.]+','match');
0390
0391
0392 if ~isempty(o)
0393
0394 oo=regexprep(o,';','');
0395 for l=1:length(oo)
0396 tmp=strtrim(regexp(oo{l},'=','split'));
0397 param_name{end+1}=tmp{1};
0398 param_value{end+1}=tmp{2};
0399
0400 eqn=strrep(eqn,o{l},'');
0401 end
0402 spec.populations(i).equations=eqn;
0403 end
0404
0405
0406
0407
0408 if ~isempty(param_name)
0409 for l=1:length(param_name)
0410 try
0411 value=eval(param_value{l});
0412 catch
0413 error('Values of this type are not supported for parameters set in equations.');
0414 end
0415 if isempty(spec.populations(i).parameters)
0416 spec.populations(i).parameters={param_name{l},value};
0417 elseif ~ismember(param_name{l},spec.populations(i).parameters(1:2:end))
0418 spec.populations(i).parameters{end+1}=param_name{l};
0419 spec.populations(i).parameters{end+1}=value;
0420 end
0421 end
0422 end
0423
0424
0425
0426
0427
0428
0429 if ~isempty(spec.populations(i).parameters)
0430 keys=spec.populations(i).parameters(1:2:end);
0431 vals=spec.populations(i).parameters(2:2:end);
0432
0433
0434
0435
0436 eqn=spec.populations(i).equations;
0437
0438
0439 words=unique(regexp(eqn,'[a-zA-Z]+\w*','match'));
0440
0441
0442 found_words=words(ismember(words,keys));
0443 if ~isempty(found_words)
0444
0445 for ff=1:length(found_words)
0446 found_word=found_words{ff};
0447 precision=8;
0448 found_value = toString(vals{strcmp(found_word,keys)},precision);
0449
0450
0451 if isempty(regexp(eqn,[';\s*' found_word '\s*='],'once')) && ...
0452 isempty(regexp(eqn,['^' found_word '\s*='],'once'))
0453
0454 if eqn(end)~=';', eqn(end+1)=';'; end
0455 eqn=[eqn sprintf(' %s=%s;',found_word,found_value)];
0456 else
0457
0458 old=regexp(eqn,[';\s*' found_word '\s*=\s*[\w\.'']+'],'match','once');
0459 if ~isempty(old)
0460
0461
0462
0463 new=['; ' found_word '=' found_value];
0464
0465 else
0466
0467 old=regexp(eqn,['^' found_word '\s*=\s*[\w\.'']+'],'match','once');
0468 new=[found_word '=' found_value];
0469 end
0470 eqn=strrep(eqn,old,new);
0471
0472 end
0473 end
0474 spec.populations(i).equations=eqn;
0475 end
0476 end
0477 end
0478
0479 spec.populations(i).mechanism_list=expand_list(spec.populations(i).mechanism_list, varargin{:});
0480 end
0481
0482
0483 if ~isempty(spec.connections)
0484
0485 if ~isfield(spec.connections,'source')
0486 spec.connections(1).source=[];
0487 end
0488
0489 if ~isfield(spec.connections,'target')
0490 spec.connections(1).target=[];
0491 end
0492
0493 if ~isfield(spec.connections,'mechanism_list')
0494 spec.connections(1).mechanism_list=[];
0495 end
0496
0497 if ~isfield(spec.connections,'mechanisms')
0498 spec.connections(1).mechanisms=[];
0499 end
0500
0501 if ~isfield(spec.connections,'parameters')
0502 spec.connections(1).parameters={};
0503 end
0504 end
0505 for i=1:length(spec.connections)
0506 if isempty(spec.connections(i).source) && length(spec.populations)==1
0507 spec.connections(i).source=spec.populations(1).name;
0508 spec.connections(i).target=spec.populations(1).name;
0509 elseif isempty(spec.connections(i).source) && length(spec.populations)>1
0510 error('connection source and target populations must be specified in specification.connections when the model contains more than one population.');
0511 end
0512
0513
0514 if ischar(spec.connections(i).mechanism_list)
0515 spec.connections(i).mechanism_list={spec.connections(i).mechanism_list};
0516 end
0517
0518
0519 spec.connections(i).mechanism_list=expand_list(spec.connections(i).mechanism_list, varargin{:});
0520
0521
0522 if ~iscell(spec.connections(i).parameters)
0523 spec.connections(i).parameters={};
0524 end
0525 end
0526
0527
0528 sizes=[spec.populations.size];
0529 if any(sizes==0)
0530
0531 null_pops=find(sizes==0);
0532 null_names={spec.populations(null_pops).name};
0533
0534
0535 spec.populations(null_pops)=[];
0536
0537
0538 if ~isempty(spec.connections)
0539 sources={spec.connections.source};
0540 targets={spec.connections.target};
0541 null_conns=ismember(sources,null_names) | ismember(targets,null_names);
0542 spec.connections(null_conns)=[];
0543 end
0544 end
0545
0546
0547
0548 otherfields=setdiff(fieldnames(spec.populations),pop_field_order);
0549 spec.populations=rmfield(spec.populations,otherfields);
0550
0551
0552 spec.populations=orderfields(spec.populations,pop_field_order);
0553 if isstruct(spec.connections)
0554 otherfields=setdiff(fieldnames(spec.connections),con_field_order);
0555 spec.connections=rmfield(spec.connections,otherfields);
0556 spec.connections=orderfields(spec.connections,con_field_order);
0557 end
0558 spec=orderfields(spec,{'populations','connections','mechanisms'});
0559
0560
0561 [~,files]=dsLocateModelFiles(spec);
0562
0563 for f=1:length(files)
0564 [~,name]=fileparts(files{f});
0565 if isempty(spec.mechanisms) || ~ismember(name,{spec.mechanisms.name})
0566 spec.mechanisms(end+1).name=name;
0567 spec.mechanisms(end).equations=read_mechanism_file(files{f});
0568 end
0569 end
0570
0571 for m=1:length(spec.mechanisms)
0572 if iscellstr(spec.mechanisms(m).equations)
0573
0574 eqn=spec.mechanisms(m).equations;
0575 idx=cellfun(@isempty,regexp(eqn,';$'));
0576 eqn(idx)=cellfun(@(x)[x ';'],eqn(idx),'uni',0);
0577 idx=cellfun(@isempty,regexp(eqn,'\s$'));
0578 eqn(idx)=cellfun(@(x)[x ' '],eqn(idx),'uni',0);
0579
0580 spec.mechanisms(m).equations=[eqn{:}];
0581 end
0582 end
0583
0584
0585
0586
0587
0588
0589
0590
0591 fnames={};
0592 if ~isempty(files)
0593 for f=1:length(files)
0594 [~,name]=fileparts2(files{f});
0595 fnames{f}=name;
0596 end
0597 end
0598 if ~isempty(spec.mechanisms)
0599 mnames={spec.mechanisms.name};
0600 else
0601 mnames={};
0602 end
0603 fields={'populations','connections'};
0604
0605 for f=1:length(fields)
0606 object=fields{f};
0607 for i=1:length(spec.(object))
0608 for j=1:length(spec.(object)(i).mechanism_list)
0609 mech=spec.(object)(i).mechanism_list{j};
0610 if ismember(mech,fnames)
0611
0612
0613
0614
0615 index=find(ismember(fnames,mech),1,'first');
0616 spec.(object)(i).mechanism_list{j}=files{index};
0617 end
0618 if ismember('@',mech)
0619 mech=regexp(mech,'@','split');
0620 mech=mech{1};
0621 end
0622 if isfield(spec.(object),'mechanisms') && ~isempty(spec.(object)(i).mechanisms) && ismember(mech,{spec.(object)(i).mechanisms.name})
0623
0624 continue;
0625 end
0626 if ismember(mech,mnames)
0627
0628 index=find(ismember(mnames,mech),1,'first');
0629 if isempty(spec.(object)(i).mechanisms)
0630 spec.(object)(i).mechanisms=spec.mechanisms(index);
0631 elseif ~ismember(mech,{spec.(object)(i).mechanisms.name})
0632
0633 spec.(object)(i).mechanisms(j)=spec.mechanisms(index);
0634 end
0635
0636 if ~isempty(spec.(object)(i).parameters)
0637
0638 eqns=spec.(object)(i).mechanisms(j).equations;
0639 keys=spec.(object)(i).parameters(1:2:end);
0640 vals=spec.(object)(i).parameters(2:2:end);
0641
0642 words=unique(regexp(eqns,'[a-zA-Z]+\w*','match'));
0643
0644 found_words=words(ismember(words,keys));
0645 if ~isempty(found_words)
0646 for ff=1:length(found_words)
0647 found_word=found_words{ff};
0648
0649 precision=8;
0650 found_value=toString(vals{strcmp(found_word,keys)},precision);
0651 rep=sprintf(' %s=%s;',found_word,found_value);
0652
0653 pat=['([^\w]{1})' found_word '\s*=\s*\w+;'];
0654 eqns=regexprep(eqns,pat,['$1' rep]);
0655
0656 pat=['^' found_word '\s*=\s*\w+;'];
0657 eqns=regexprep(eqns,pat,rep);
0658 end
0659 end
0660 spec.(object)(i).mechanisms(j).equations=eqns;
0661 end
0662 end
0663 end
0664 end
0665 end
0666
0667
0668 if options.auto_gen_test_data_flag
0669 argout = {spec};
0670
0671 dsUnitSaveAutoGenTestData(argin, argout);
0672 end
0673
0674 end
0675
0676
0677
0678 function txt=read_mechanism_file(file)
0679 fid=fopen(file,'rt');
0680
0681 txt=textscan(fid,'%s','Delimiter','\n');
0682 if ~isempty(txt)
0683
0684 txt=txt{1}(~cellfun(@isempty,txt{1}));
0685
0686 txt=txt(~cellfun(@isempty,regexp(txt,'^[^#%]')));
0687 txt=regexp(txt,'^[^%#]*','match');
0688 txt=[txt{:}];
0689
0690 txt=strtrim(txt);
0691
0692 idx=cellfun(@isempty,regexp(txt,';$'));
0693 txt(idx)=cellfun(@(x)[x ';'],txt(idx),'uni',0);
0694 idx=cellfun(@isempty,regexp(txt,'\s$'));
0695 txt(idx)=cellfun(@(x)[x ' '],txt(idx),'uni',0);
0696
0697 txt=[txt{:}];
0698 end
0699
0700 fclose(fid);
0701 end
0702
0703 function list = expand_list(list, varargin)
0704
0705 options = dsCheckOptions(varargin,{'auto_gen_test_data_flag',0,{0,1}},false);
0706 if options.auto_gen_test_data_flag
0707 varargs = varargin;
0708 varargs{find(strcmp(varargs, 'auto_gen_test_data_flag'))+1} = 0;
0709 varargs(end+1:end+2) = {'unit_test_flag',1};
0710 argin = [{list}, varargs];
0711 end
0712
0713
0714 if isempty(list)
0715 return;
0716 end
0717
0718 if any(~cellfun(@isempty,regexp(list,'[{,}]+')))
0719 mechs={};
0720 for k=1:length(list)
0721 tmp=regexp(list{k},'\w+','match');
0722 mechs=cat(2,mechs,tmp{:});
0723 end
0724 list=mechs;
0725 end
0726
0727
0728 if options.auto_gen_test_data_flag
0729 argout = {list};
0730
0731 dsUnitSaveAutoGenTestDataLocalFn(argin, argout);
0732 end
0733
0734 end
0735
0736
0737 function spec = backward_compatibility(spec, varargin)
0738
0739
0740
0741
0742 options = dsCheckOptions(varargin,{'auto_gen_test_data_flag',0,{0,1}},false);
0743 if options.auto_gen_test_data_flag
0744 varargs = varargin;
0745 varargs{find(strcmp(varargs, 'auto_gen_test_data_flag'))+1} = 0;
0746 varargs(end+1:end+2) = {'unit_test_flag',1};
0747 argin = [{spec}, varargs];
0748 end
0749
0750 if isfield(spec,'nodes')
0751 spec.populations=spec.nodes;
0752 spec=rmfield(spec,'nodes');
0753 end
0754
0755 if isfield(spec,'cells')
0756 spec.populations=spec.cells;
0757 spec=rmfield(spec,'cells');
0758 end
0759
0760 if isfield(spec,'entities')
0761 spec.populations=spec.entities;
0762 spec=rmfield(spec,'entities');
0763 end
0764
0765 if isfield(spec,'pops')
0766 spec.populations=spec.pops;
0767 spec=rmfield(spec,'pops');
0768 end
0769
0770 if isfield(spec,'cons')
0771 spec.connections=spec.cons;
0772 spec=rmfield(spec,'cons');
0773 end
0774 if isfield(spec,'links')
0775 spec.connections=spec.links;
0776 spec=rmfield(spec,'links');
0777 end
0778 if isfield(spec,'edges')
0779 spec.connections=spec.edges;
0780 spec=rmfield(spec,'edges');
0781 end
0782
0783 if isfield(spec,'edges')
0784 spec.connections=spec.edges;
0785 spec=rmfield(spec,'edges');
0786 end
0787
0788 if isfield(spec,'links')
0789 spec.connections=spec.links;
0790 spec=rmfield(spec,'links');
0791 end
0792
0793 if isfield(spec,'comps')
0794 spec.compartments=spec.comps;
0795 spec=rmfield(spec,'comps');
0796 end
0797
0798 if isfield(spec,'populations')
0799
0800 if isfield(spec.populations,'label')
0801 for i=1:length(spec.populations)
0802 spec.populations(i).name=spec.populations(i).label;
0803 end
0804 spec.populations=rmfield(spec.populations,'label');
0805 end
0806
0807
0808 if isfield(spec.populations,'multiplicity')
0809 for i=1:length(spec.populations)
0810 spec.populations(i).size=spec.populations(i).multiplicity;
0811 end
0812 spec.populations=rmfield(spec.populations,'multiplicity');
0813 end
0814
0815
0816 if isfield(spec.populations,'dynamics')
0817 for i=1:length(spec.populations)
0818 spec.populations(i).equations=spec.populations(i).dynamics;
0819 end
0820 spec.populations=rmfield(spec.populations,'dynamics');
0821 end
0822
0823 end
0824
0825 if isfield(spec,'connections') && isfield(spec.connections,'direction')
0826 for i=1:length(spec.connections)
0827 if ischar(spec.connections(i).direction)
0828 str=spec.connections(i).direction;
0829 if any(regexp(str,'->','once'))
0830 pops=regexp(str,'->','split');
0831 spec.connections(i).source=pops{1};
0832 spec.connections(i).target=pops{2};
0833 elseif any(regexp(str,'<-','once'))
0834 pops=regexp(str,'<-','split');
0835 spec.connections(i).source=pops{2};
0836 spec.connections(i).target=pops{1};
0837 end
0838 end
0839 end
0840 spec.connections=rmfield(spec.connections,'direction');
0841 end
0842
0843
0844 if options.auto_gen_test_data_flag
0845 argout = {spec};
0846
0847 dsUnitSaveAutoGenTestDataLocalFn(argin, argout);
0848 end
0849
0850 end