Chapter 2 Functions and Programming

2.1 Development and Debugging

2.1.1 Matlab Error and Warning Handling

Go back to fan’s MEconTools Package, Matlab Code Examples Repository (bookdown site), or Math for Econ with Matlab Repository (bookdown site).

2.1.1.1 Turn off Warning Messages

During estimation, at some parameter values, estimation routine generates warnings messages. These warning messages fill up the screen and makes it hard to track estimation outputs. In the example below, we run an OLS regression that generates a warning message. We turn the message off, and then we turn the message on again.

First run a regression that generates a warning message.

% OLS Regression
fci_ols_lin = @(y, x) (x'*x)^(-1)*(x'*y);
% Regression inputs
ar_y = [1;1];
mt_x = [0 0; 0 0];
% Estimates
ar_esti = fci_ols_lin(ar_y, mt_x);

Warning: Matrix is singular to working precision.

Second, identify and turn off the warning message.

% Check warning message and warning ID of the last message.
[st_msg, st_warnID] = lastwarn;
% Turn off earning message
warning('off', st_warnID);
% Estimates no longer generates warning
ar_esti = fci_ols_lin(ar_y, mt_x);

Third, turn warning message back on.

% Warning message back on
warning('on', st_warnID);
% Estimates generates warning again
ar_esti = fci_ols_lin(ar_y, mt_x);

Warning: Matrix is singular to working precision.

2.1.2 Matlab Profiler Save to HTML

Go back to fan’s MEconTools Package, Matlab Code Examples Repository (bookdown site), or Math for Econ with Matlab Repository (bookdown site).

2.1.2.1 Profiling Lines of Code

There is a program that we have written. We want to know which lines of code is taking more or less time, and identify opportunities for speed improvement. Use Matlab Profiler to run through code and generate performance report line by line. This can be achieved by clicking on the Run and Time button under the Editor Pane. Below, we start the profiler inline, run a function, and save profiling results to a HTML file.

The function below finds the file to the current m file where the profiler is running at, and generates a subfolder which will contain a subfolder that contains all HTML results from a particular profiler timing run.

% Define profiling folder and file names
% Find current path to m file and generates profiling subfolder
srn_profile_folder = '_profile';
% Name of the profling output folder (including HTML files)
srn_mprofile_subfolder = 'fs_profiler_tester';

% Turn profiler on
profile on

% OLS Regression
fci_ols_lin = @(y, x) (x'*x)^(-1)*(x'*y);
% Regression inputs
it_obs_n = 10000;
it_k_n = 5;
rng(123);
ar_y = rand([it_obs_n,1]);
mt_x = rand([it_obs_n, it_k_n]);
% Regression
ar_esti = fci_ols_lin(ar_y, mt_x);

% Turn Profiler off
profile off;

% Save profiling results to file
% Find current file folder and generate a profiling subfolder
spn_path2file = matlab.desktop.editor.getActiveFilename;
[spt_path_folder_root, ~, ~] = fileparts(spn_path2file);
spn_profiler = fullfile(spt_path_folder_root, srn_profile_folder);
if ~exist(spn_profiler, 'dir')
    mkdir(spn_profiler);
end

% Store results to file
spn_profiler_results = fullfile(spn_profiler, srn_mprofile_subfolder);
profsave(profile('info'), spn_profiler_results);

2.2 varargin Default Parameters

2.2.1 varargin as a Function Parameter

Go back to fan’s MEconTools Package, Matlab Code Examples Repository (bookdown site), or Math for Econ with Matlab Repository (bookdown site).

2.2.1.1 Call Function with Two Parameters and Defaults

Call function below without overriding

ff_varargin(1.1, 2)

fl_a = 1.1000
it_b = 2
mt_data = 3x4    
    0.6965    0.5513    0.9808    0.3921
    0.2861    0.7195    0.6848    0.3432
    0.2269    0.4231    0.4809    0.7290

ar_st_colnames = 1x4 string    
"col1"       "col2"       "col3"       "col4"       

ar_st_rownames = 1x4 string    
"row1"       "row2"       "row3"       "row4"       

st_table_name = "Table Name"
it_table_ctr = 1021

2.2.1.2 Override Subset of Varargin

rng(789);
mt_data_ext = rand(5,2);
ar_st_colnames = ["col1", "col2"];
ar_st_rownames = ["row1", "row2", "row3", "row4", "row5"];
ff_varargin(param_map, support_map, mt_data_ext, ar_st_colnames, ar_st_rownames);

fl_a = 
  Map with properties:

        Count: 2
      KeyType: char
    ValueType: any

it_b = 
  Map with properties:

        Count: 1
      KeyType: char
    ValueType: any

mt_data = 5x2    
    0.3233    0.7589
    0.2302    0.0106
    0.7938    0.0247
    0.6244    0.1110
    0.9754    0.5381

ar_st_colnames = 1x2 string    
"col1"       "col2"       

ar_st_rownames = 1x5 string    
"row1"       "row2"       "row3"       "row4"       "row5"       

st_table_name = "Table Name"
it_table_ctr = 1021

2.2.1.3 Function with varargin as Inputs

Basic default structure with varargin.

function ff_varargin(fl_a, it_b, varargin)
% This is an example of how to use varargin:
% 1. includes array matrix
% 2. includes array
% 3. includes scalar
% 4. includes string
% 5. includes cell array

%% Catch Error
cl_params_len = length(varargin);
if cl_params_len > 5
    error('ff_mat2tab:TooManyOptionalParameters', ...
          'allows at most 5 optional parameters');
end

%% Default Folder Parameters
% by default all go to Sandbox folder with sub folders by dates
rng(123);
mt_data = rand(3,4);
% String array requires double quotes
ar_st_colnames = ["col1", "col2", "col3", "col4"];
ar_st_rownames = ["row1", "row2", "row3", "row4"];
% Others
st_table_name = "Table Name";
it_table_ctr = 1021;
cl_params = {mt_data ar_st_colnames ar_st_rownames ...
                   st_table_name it_table_ctr};

%% Parse Parameters
% numvarargs is the number of varagin inputted
[cl_params{1:cl_params_len}] = varargin{:};
% cell2mat(cl_params(1)) works with array
mt_data = cell2mat(cl_params(1));
% The structure below works with cell array
ar_st_colnames = cl_params{2};
ar_st_rownames = cl_params{3};
% Others
st_table_name = cl_params{4};
it_table_ctr = cl_params{5};

% Build Basic Matlab Table
% Suppose we want to store matrix results in a table,
% there are Q columns and N rows, The Q columns each is a different variable.
fl_a
it_b
mt_data
ar_st_colnames
ar_st_rownames
st_table_name
it_table_ctr

end

2.2.2 Container Default Parameter with varargin

Go back to fan’s MEconTools Package, Matlab Code Examples Repository (bookdown site), or Math for Econ with Matlab Repository (bookdown site).

2.2.2.1 Call Function with Default Parameters

Call function below without overriding

ff_defaultmap()

    {'c_gap'}    {'c_max'}    {'c_min'}    {'c_min_for_util'}    {'fl_crra'}    {'it_rown'}    {'st_single_double'}

    {[1.0000e-03]}    {[60]}    {[1.0000e-03]}    {[1.0000e-03]}    {[1.5000]}    {[100]}    {'double'}

Elapsed time is 0.048585 seconds.

2.2.2.2 Call Function overriding some Parameters

param_map = containers.Map('KeyType','char', 'ValueType','any');
param_map('fl_w_max') = 1.11;
param_map('it_w_i') = 2.22;

support_map = containers.Map('KeyType','char', 'ValueType','any');
support_map('bl_display') = true;
ff_defaultmap(param_map, support_map)

  Columns 1 through 8

    {'c_gap'}    {'c_max'}    {'c_min'}    {'c_min_for_util'}    {'fl_crra'}    {'fl_w_max'}    {'it_rown'}    {'it_w_i'}

  Column 9

    {'st_single_double'}

    {[1.0000e-03]}    {[60]}    {[1.0000e-03]}    {[1.0000e-03]}    {[1.5000]}    {[1.1100]}    {[100]}    {[2.2200]}    {'double'}

Elapsed time is 0.016033 seconds.

2.2.2.3 Function with Map Defaults and Overriding

This default parameter style is fairly succinct, allows for program testability, and easy adjustments/addition of additional parameters to models.

function ff_defaultmap(varargin)

% Parameters
params_len = length(varargin);
if params_len > 3
    error('ff_defaultmap:Can only have 3 container map parameters');
end
bl_input_override = 0;
if (params_len == 3)
    bl_input_override = varargin{3};
end

% Defaults
if (bl_input_override)
    % this relies on externally generated parameters, defaults do not have to be generated
    % if this file has to be invoked many times, then this saves time by avoiding
    % regenerating defaults over and over again
    [param_map, support_map, ~] = varargin{:};
else    
    param_map = containers.Map('KeyType','char', 'ValueType','any');
    param_map('fl_crra') = 1.5;
    param_map('c_min') = 0.001;
    param_map('c_min_for_util') = 0.001;
    param_map('c_gap') = 10^-3;
    param_map('c_max') = 60;
    param_map('it_rown') = 100;
    param_map('st_single_double') = 'double';
    
    support_map = containers.Map('KeyType','char', 'ValueType','any');
    support_map('bl_display') = true;
    support_map('bl_graph') = true;
    support_map('bl_graph_onebyones') = true;
    support_map('bl_time') = true;
    support_map('bl_profile') = false;
    support_map('st_profile_path') = [pwd '/profile'];
    default_maps = {param_map, support_map};
end

% Parse Parameters
% see: C:\Users\fan\M4Econ\support\dtype\map_override.m
[default_maps{1:params_len}] = varargin{:};
param_map = [param_map; default_maps{1}];
support_map = [support_map; default_maps{2}];

params_group = values(param_map, {'fl_crra', 'c_min', 'c_min_for_util', 'c_gap', 'c_max'});
[fl_crra, c_min, c_min_for_util, c_gap, c_max] = params_group{:};
params_group = values(param_map, {'it_rown'});
[it_rown] = params_group{:};
params_group = values(param_map, {'st_single_double'});
[st_single_double] = params_group{:};

% support
params_group = values(support_map, {'bl_display', 'bl_graph', 'bl_graph_onebyones'});
[bl_display, bl_graph, bl_graph_onebyones] = params_group{:};
params_group = values(support_map, {'bl_time', 'bl_profile', 'st_profile_path'});
[bl_time, bl_profile, st_profile_path] = params_group{:};

% Tic toc starts
if (bl_time); tic; end

% Print Parameters
if (bl_display)
    disp(param_map.keys);
    disp(param_map.values);
end

% Profile On
if (bl_profile)
    close all;
    profile off;
    profile on;
end

%% Profiling
if (bl_profile)
    profile off
    profile viewer
    profsave(profile('info'), st_profile_path);
end

if (bl_time); toc; end

end

2.3 Dynamic Functions

2.3.1 Anonymous Function in Matlab

Go back to fan’s MEconTools Package, Matlab Code Examples Repository (bookdown site), or Math for Econ with Matlab Repository (bookdown site).

2.3.1.1 Define a Wage Equation with Partial Income for Part-time Workss

Individuals might work part or full time. Define a mincer wage equation that is a function of experienc, education and other individual characteristics.

First define parameters.

% Experience
COEF_beta_psi_0 = 1.8884;
COEF_beta_psi_k = 0.0767;
COEF_beta_psi_kk = -0.2683;
% Education
COEF_beta_psi_e0 = 0.0465;
COEF_beta_w_e_1 = 0.1432;
COEF_beta_w_e_2 = 0.1435;
COEF_beta_w_e_3 = 0.2806;
% for part time
COEF_beta_w_part_0 = -0.0082;
COEF_beta_w_part_1 = -0.4863;

Second define the log wage equation. This wage equation is a function of the parameters defined above, and also Education (ED), experience (EX) and the wage shcok (EPS_w).

% Log of wage
f_log_psi = @(ED, EX) ...
    COEF_beta_psi_0 ...
    + COEF_beta_psi_k.*EX ...
    + COEF_beta_psi_kk.*(EX.^2/100) ...
    + COEF_beta_psi_e0.*(ED - 12) ...
    + COEF_beta_w_e_1.*(ED == 12) ...
    + COEF_beta_w_e_2.*(ED > 12).*(ED < 16) ...
    + COEF_beta_w_e_3.*(ED >= 16);

Third, define wage, which might differ depending on work status as well as schooling status. D_e is schooling or not, which can take values of 0 or 1. D_k is work status, which can take values or 0, 0.5 (part-time work) and 1 (full-time work).

% Per hour wage considering part time, part time wage differ if also schooling
f_hr_wage = @(D_e, D_k, ED, EX) ...
    exp(f_log_psi(ED, EX)).*exp((D_k==0.5).*(COEF_beta_w_part_0 + COEF_beta_w_part_1.*D_e));
% Total wage
f_wage = @(D_e, D_k, ED, EX) ...
    f_hr_wage(D_e, D_k, ED, EX).*(2080.*(D_k == 1) + 1040.*(D_k == 0.5) + 0.*(D_k == 0));

Fourth, test the wage equation by calling it with different work and schooling choices, along with different education, experience, and shock levels.

% no experience, education, not school, not work
disp(['f_wage(0,0,0,0,0)=' num2str(f_wage(0,0,0,0))]);

f_wage(0,0,0,0,0)=0

% no experience, education, part-time
disp(['f_wage(0,0.5,0,0,0)=' num2str(f_wage(0,0.5,0,0))]);

f_wage(0,0.5,0,0,0)=3901.7326

% no experience, education, full-time
disp(['f_wage(0,1,0,0,0)=' num2str(f_wage(0,1,0,0))]);

f_wage(0,1,0,0,0)=7867.7167

2.3.2 Dynamically Generate M Files

Go back to fan’s MEconTools Package, Matlab Code Examples Repository (bookdown site), or Math for Econ with Matlab Repository (bookdown site).

2.3.2.1 Dynamically Generate and Run M Files

Use-case: There is a wage equation that provides wage for college or non-college educated indivdiuals. The parameter for college and non-college educated distinguishes them with values 2 and 1. Initially, model was designed to be solved jointly for college and non-college educated, so the education grid as both values 1 and 2. In modified code version, only one education group solved at once. The education grid is always length 1. This wage education is called by many functions, so do not want to change the parameter structure. Instead, depending on whether low or high education group is been solved for, regenerate this function with hard-coded values for education groups.

see this and this.

First cell array to store function code lines.

cl_code = {'function F=fsi_test_wage(j,educ)', ...
     '% both', ...
     '%it_edu_grid_type = 1;', ...
     '% non-college only', ...
     '%it_edu_grid_type = 2;', ...
     '% college only', ...
     'it_edu_grid_type = 3;', ...
     'if (it_edu_grid_type == 1 || it_edu_grid_type == 2)', ...
     '    % no changes needed when both, or low education type', ...
     '    % low type = 1, educ = 1', ...
     '    F = fsi_test_wage_equa(j,educ);', ...
     'elseif (it_edu_grid_type == 3)', ...
     '    % problem high type, educ should equal 2, but solving for high edu only, educ=1', ...
     '    F = fsi_test_wage_equa(j,1);', ...
     'end', ...
     'end'};

Second, get current path.

% Check folder to use
spn_current_mlx_path = matlab.desktop.editor.getActiveFilename;
[spt_filepath, snm_name, st_ext] = fileparts(spn_current_mlx_path);
disp(['spt_filepath=' spt_filepath]);

spt_filepath=G:\repos\M4Econ\function\anonymous

spt_filepath = fullfile(spt_filepath, '_file');
if ~exist(spt_filepath, 'dir')
    mkdir(spt_filepath);
end
disp(['spt_filepath:' spt_filepath]);

spt_filepath:G:\repos\M4Econ\function\anonymous\_file

Third, generate file.

% Start file
spn_path_file = fullfile(spt_filepath, 'fsi_test_wage.m');
f = fopen(spn_path_file, 'w');
fprintf(f, '%s\n', cl_code{:});
fclose(f);
tmpfile;

Third, add functions in subfolder to path.

% Add to path to matlab path
addpath(spt_filepath);

Call and use function

wage = fsi_test_wage(19, 1);
disp(['wage:' num2str(wage)]);

wage:0.54154