Home > functions > internal > dsPropagateNamespaces.m

dsPropagateNamespaces

PURPOSE ^

PROPAGATENAMESPACES - namespace-establishing namespace substitutions.

SYNOPSIS ^

function model = dsPropagateNamespaces(model,map, varargin)

DESCRIPTION ^

PROPAGATENAMESPACES - namespace-establishing namespace substitutions.

 Usage:
   model = dsPropagateNamespaces(model,name_map)

 Inputs:
   - model: DynaSim model structure (see dsGenerateModel)
   - name_map: cell matrix mapping parameter, variable, and function names
     between the user-created specification (population equations and mechanism
     files) and the full model with automatically generated namespaces. It has
     four columns with: user-specified name, name with namespace prefix,
     namespace, and type ('parameters', 'fixed_variables', 'state_variables',
     'functions', or 'monitors').

 Outputs:
   - model: DynaSim model structure with namespace added as namespace-delineating prefix

 Example 1: TODO

 See also: dsGenerateModel, dsPropagateFunctions, dsParseModelEquations, dsGetParentNamespace

 Author: Jason Sherfey, PhD <jssherfey@gmail.com>
 Copyright (C) 2016 Jason Sherfey, Boston University, USA

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 function model = dsPropagateNamespaces(model,map, varargin)
0002 %PROPAGATENAMESPACES - namespace-establishing namespace substitutions.
0003 %
0004 % Usage:
0005 %   model = dsPropagateNamespaces(model,name_map)
0006 %
0007 % Inputs:
0008 %   - model: DynaSim model structure (see dsGenerateModel)
0009 %   - name_map: cell matrix mapping parameter, variable, and function names
0010 %     between the user-created specification (population equations and mechanism
0011 %     files) and the full model with automatically generated namespaces. It has
0012 %     four columns with: user-specified name, name with namespace prefix,
0013 %     namespace, and type ('parameters', 'fixed_variables', 'state_variables',
0014 %     'functions', or 'monitors').
0015 %
0016 % Outputs:
0017 %   - model: DynaSim model structure with namespace added as namespace-delineating prefix
0018 %
0019 % Example 1: TODO
0020 %
0021 % See also: dsGenerateModel, dsPropagateFunctions, dsParseModelEquations, dsGetParentNamespace
0022 %
0023 % Author: Jason Sherfey, PhD <jssherfey@gmail.com>
0024 % Copyright (C) 2016 Jason Sherfey, Boston University, USA
0025 
0026 %% auto_gen_test_data_flag argin
0027 options = dsCheckOptions(varargin,{'auto_gen_test_data_flag',0,{0,1}},false);
0028 if options.auto_gen_test_data_flag
0029   varargs = varargin;
0030   varargs{find(strcmp(varargs, 'auto_gen_test_data_flag'))+1} = 0;
0031   varargs(end+1:end+2) = {'unit_test_flag',1};
0032   argin = [{model}, {map}, varargs]; % specific to this function
0033 end
0034 
0035 % Check model
0036 model=dsCheckModel(model, varargin{:});
0037 % Check map
0038 if ~iscell(map) || size(map,2)~=4
0039   error('map must be a cell array with four columns for (name, namespace_name, namespace, type)');
0040 end
0041 names_in_namespace=cellfun(@(x,y)strncmp(y,x,length(y)),map(:,2),map(:,3));
0042 [name_,name__]=dsGetNamespaces(model);
0043 % Purpose: add double underscores between model objects (i.e., separate
0044 % populations and mechanisms in namespace). This enables retrieving model
0045 % object names from the namespace in dsGetParentNamespace for segregating
0046 % model elements in dsPropagateNamespaces. This is necessary if object
0047 % names contain underscores. Limitation: object names with double
0048 % underscores are not permitted.
0049 % Note: this approach to tracking objects with namespaces was chosen
0050 % because of its efficiency given code generation based on string
0051 % substitution.
0052 
0053 % namespace propagation pattern:
0054 allowed_insert_types.fixed_variables=...
0055   {'parameters','fixed_variables','reserved'}; % into fixed_variables
0056 allowed_insert_types.functions=...
0057   {'parameters','fixed_variables','functions','state_variables','reserved'}; % into functions
0058 allowed_insert_types.monitors=...
0059   {'parameters','fixed_variables','functions','state_variables','reserved'}; % into monitors
0060 allowed_insert_types.ODEs=...
0061   {'parameters','fixed_variables','functions','state_variables','reserved'}; % into ODEs
0062 allowed_insert_types.ICs=...
0063   {'parameters','fixed_variables','functions','state_variables','reserved'}; % into ICs
0064 allowed_insert_types.linkers=...
0065   {'parameters','fixed_variables','functions','state_variables','reserved'};
0066 allowed_insert_types.conditionals=...
0067   {'parameters','fixed_variables','functions','state_variables','reserved'};
0068 
0069 %% 1.0 Propagate through structure arrays (linkers, conditionals)
0070 % 1.1 linkers (expression)
0071 if ~isempty(model.linkers)
0072   namespaces={model.linkers.namespace};
0073   expressions=propagate_namespaces({model.linkers.expression},namespaces,map,allowed_insert_types.linkers);
0074   [model.linkers(1:length(model.linkers)).expression]=deal(expressions{:});
0075 end
0076 
0077 % 1.2 conditionals (condition, action, else)
0078 if ~isempty(model.conditionals)
0079   namespaces={model.conditionals.namespace};
0080   target_types={'condition','action','else'};
0081   for type_index=1:length(target_types)
0082     type=target_types{type_index};
0083     tmp=propagate_namespaces({model.conditionals.(type)},namespaces,map,allowed_insert_types.conditionals);
0084     [model.conditionals(1:length(model.conditionals)).(type)]=deal(tmp{:});
0085   end
0086 end
0087 
0088 %% 2.0 Propagate through sub-structures
0089 target_types={'fixed_variables','functions','monitors','ODEs','ICs'};
0090 
0091 % loop over types of model data
0092 for type_index=1:length(target_types)
0093   type=target_types{type_index};%'fixed_variables';
0094   % info for this type
0095   s=model.(type);
0096   if isstruct(s)
0097     fields=fieldnames(s); % namespaced-names of this type (ie, [namespace_name])
0098     expressions=struct2cell(s); % raw-expressions of this type (ie, without namespace prefixes)
0099     namespaces={};
0100     for i=1:length(expressions)
0101       idx=strcmp(fields{i},map(:,2));
0102       if numel(find(idx))>1
0103         % constrain to namespace-conserving entries
0104         tmp=map(idx&names_in_namespace,3);
0105         
0106         % use the lowest level namespace (i.e., longest namespace name)
0107         l=cellfun(@length,tmp);
0108         tmp=tmp{l==max(l)};
0109       else
0110         tmp=map{idx,3};
0111       end
0112       namespaces{end+1}=tmp;
0113     end
0114     % update expressions for names of this type
0115     expressions=propagate_namespaces(expressions,namespaces,map,allowed_insert_types.(type));
0116     
0117     % update model with expressions including namespaces
0118     model.(type)=cell2struct(expressions,fields,1);
0119   end
0120 end
0121 
0122 %% NESTED FUNCTIONS
0123 % function expressions=propagate_namespaces(expressions,names_full,map,insert_types)
0124 function expressions=propagate_namespaces(expressions,namespaces,map,insert_types)
0125   % loop over and update expressions for names of this type
0126   for i=1:length(expressions)
0127     if isempty(expressions{i})
0128       continue;
0129     end
0130     % get namespace for this expression
0131     this_namespace=namespaces{i};
0132     
0133     % convert to double underscore version for segregating objects
0134     this_namespace__ = name__{strcmp(this_namespace,name_)};
0135     
0136     % find parent namespaces (by segregating model objects delimited by underscores)
0137     parent_namespace = dsGetParentNamespace(this_namespace__, varargin{:});
0138     
0139     % find where this and parent namespaces are in map array
0140     insert_type_constraint = ismember(map(:,4),insert_types);
0141     this_namespace_map_inds = find(strcmp(this_namespace,map(:,3)) & insert_type_constraint);
0142     parent_namespace_map_inds = find(strcmp(parent_namespace,map(:,3)) & insert_type_constraint);
0143     
0144     % get list of words in this expression
0145     words=unique(regexp(expressions{i},'[a-zA-Z]+\w*','match'));
0146     
0147     % loop over words
0148     for j=1:length(words)
0149       % search for words in parent namespace of map.names
0150       if any(strcmp(words{j},map(parent_namespace_map_inds,1))) % search parent namespace
0151         % word found in parent namespace of map
0152         ind=parent_namespace_map_inds(strcmp(words{j},map(parent_namespace_map_inds,1)));
0153         new_word=map{ind,2};
0154         
0155         %if IC, need to take just first time index
0156         if exist('type','var') && strcmp(type, 'ICs') && strcmp(words{j}, 'X')
0157           new_word = [new_word '_last']; % HACK
0158         end
0159         % TODO: move this to dsWriteDynaSimSolver()
0160         
0161         % replace found word in expression by map(names_bar|parent_namespace)
0162         expressions{i}=dsStrrep(expressions{i},words{j},new_word, '', '', varargin{:});
0163         % check whether new word is defined in model
0164         % NOTE: this is necessary to account for namespace differences between
0165         %   user-supplied population parameters that should replace default mechanism-level parameters
0166         new_word_type=map{ind,4};
0167 % %{
0168         if ~isfield(model.(new_word_type),new_word)
0169           % if not, define it from (word without namespace/namespace)
0170           if isfield(model.(new_word_type),words{j})
0171             model.(new_word_type).(new_word)=model.(new_word_type).(words{j});
0172             model.(new_word_type) = rmfield(model.(new_word_type),words{j});
0173           else
0174             tmpi=find(strcmp(words{j},map(:,1))&strcmp(new_word_type,map(:,4))&strcmp(this_namespace,map(:,3)));
0175             if ~isempty(tmpi)
0176               old_field = map{tmpi,2};
0177               if ~isempty(tmpi) && isfield(model.(new_word_type),old_field)
0178                 model.(new_word_type).(new_word)=model.(new_word_type).(old_field);
0179                 %model.(new_word_type) = rmfield(model.(new_word_type),old_field);
0180               end
0181             end
0182           end
0183         end
0184 % %}
0185       elseif any(strcmp(words{j},map(this_namespace_map_inds,1))) % search this namespace
0186         % word found in this namespace of map
0187         ind=this_namespace_map_inds(strcmp(words{j},map(this_namespace_map_inds,1)));
0188         new_word=map{ind,2};
0189         
0190         % replace found word in expression by map(names_bar|this_namespace)
0191         expressions{i}=dsStrrep(expressions{i},words{j},new_word, '', '', varargin{:});
0192       end
0193     end
0194   end
0195 end
0196 
0197 %% auto_gen_test_data_flag argout
0198 if options.auto_gen_test_data_flag
0199   argout = {model}; % specific to this function
0200   
0201   dsUnitSaveAutoGenTestData(argin, argout);
0202 end
0203 
0204 end
0205 
0206 % SUBFUNCTIONS
0207 
0208 function parent = dsGetParentNamespace(namespace)
0209 %GETPARENTNAMESPACE - determine parent namespace from namespace specified in namespace
0210 % Usage:
0211 %   parent = dsGetParentNamespace(namespace)
0212 % Input:
0213 %   - namespace: current namespace of object
0214 % Output:
0215 %   - parent: parent namespace containing the current namespace
0216 % Examples:
0217 %   parent=dsGetParentNamespace('pop')
0218 %   parent=dsGetParentNamespace('pop__mech')
0219 %   parent=dsGetParentNamespace('pop__pop')
0220 %   parent=dsGetParentNamespace('pop__pop__mech')
0221 %   parent=dsGetParentNamespace('mech')
0222 %   parent=dsGetParentNamespace('')
0223 
0224 if isempty(namespace) && isnumeric(namespace)
0225   namespace='';
0226 end
0227 if ~isempty(namespace) && namespace(end)=='_'
0228   namespace=namespace(1:end-1);
0229 end
0230 if ~isempty(namespace)
0231   parts=regexp(namespace,'__','split');
0232 else
0233   parts=[];
0234 end
0235 
0236 switch length(parts)
0237   case 0                          % ''
0238     parent='global';
0239   case 1                          % pop or mech
0240     parent='';
0241   case 2
0242     if isequal(parts{1},parts{2}) % pop_pop
0243       parent='global';
0244     else                          % pop_mech
0245       parent=[parts{1} '_'];
0246     end
0247   case 3                          % pop_pop_mech
0248     parent=[parts{1} '_' parts{2} '_'];
0249   otherwise                       % a_b_c_d_...
0250     parent='';
0251     for i=1:length(parts)-1
0252       parent=[parent parts{i} '_'];
0253     end
0254 end
0255 
0256 end

Generated on Tue 12-Dec-2017 11:32:10 by m2html © 2005