Given allocation space parameters (A, alpha), test the lower-bounded continuous optimal allocation line by line without function. This demonstrates relative optimality conditions, inverse optimal allocation, and optimal allocation.

Solves the linear optimal allocation problem. Each individual receives input \(N_i\), and we are interested in individual expected outcomes \(H_i\). Production functions or reduced causal form relationships are linear with individual specific slopes and intercepts. Solve for different planners.

rm(list = ls(all.names = TRUE))
options(knitr.duplicate.label = 'allow')

Load Data

Load data that is generated by regression ffy_opt_dtgch_cbem4 (vignette).

Alternatively, provide hand input data.

bl_save_rda = FALSE

it_data_which = 2

if (it_data_which == 1) {
  # the data here has very little heterogeneity in alpha
  # Load Library
  ls_opti_alpha_A <- ffy_opt_dtgch_cbem4()
  df_raw <- ls_opti_alpha_A$df_raw
  df_hw_cebu_m24 <- df_raw
  df_esti <- ls_opti_alpha_A$df_esti

  # Review dataframes
  # raw file
  head(df_raw, 10)
  head(df_esti, 10)
  ar_prot_data = df_hw_cebu_m24$prot
  fl_N_agg = sum(ar_prot_data)

  # Attach
  attach(df_raw)
}

if (it_data_which == 2) {
  # generate 50 individuals with arbitrary A, alpha and beta
  set.seed(123)
  it_N <- 50
  ar_alpha <- runif(it_N, min=0.1, max=1)
  ar_A <- runif(it_N, min=1, max=2)
  ar_beta <- runif(it_N, min=0, max=1)
  ar_beta <- ar_beta/sum(ar_beta)

  mt_cts <- cbind(seq(1, it_N), ar_A, ar_alpha, ar_beta)
  colnames(mt_cts) <- c('indi.id', 'A_lin', 'alpha_lin', 'beta')
  df_esti <- as_tibble(mt_cts)
  fl_N_agg <- 10*mean(ar_alpha)
}

Solve

I would like to be able to directly call the dataset generated here in various functions. Save the datafile we just created in the project folder.

Prep Inputs

df <- df_esti
svr_A_i <- 'A_lin'
svr_alpha_i <- 'alpha_lin'
svr_beta_i <- 'beta'
fl_N_agg <- fl_N_agg
fl_rho <- -10

Relative Allocations

Rank

# a. select only relevant data
df_opti <- df %>% rename(A = !!sym(svr_A_i), alpha = !!sym(svr_alpha_i), beta = !!sym(svr_beta_i))

# b. Generate V4, Rank Index Value, rho specific
# df_opti <- df_opti %>% mutate(!!paste0('rv_', it_rho_ctr) := A/((alpha*beta))^(1/(1-fl_rho)))
df_opti <- df_opti %>% mutate(rank_val = A/((alpha*beta))^(1/(1-fl_rho)))

# c. Generate Rank Index based on y-intercept
df_opti <- df_opti %>% arrange(rank_val) %>% mutate(rank_y_inter_idx = row_number())

# d. Populate lowest index alpha, beta, and A to all rows
df_opti <- df_opti %>% mutate(lowest_rank_A = A[rank_y_inter_idx==1]) %>%
              mutate(lowest_rank_alpha = alpha[rank_y_inter_idx==1]) %>%
              mutate(lowest_rank_beta = beta[rank_y_inter_idx==1])

# Print
head(df_opti, 10)
## # A tibble: 10 x 9
##    indi.id     A alpha    beta rank_val rank_y_inter_idx lowest_rank_A
##      <dbl> <dbl> <dbl>   <dbl>    <dbl>            <int>         <dbl>
##  1       4  1.12 0.895 0.0370      1.53                1          1.12
##  2      24  1.00 0.995 0.00853     1.54                2          1.12
##  3       7  1.13 0.575 0.0355      1.61                3          1.12
##  4       1  1.05 0.359 0.0233      1.62                4          1.12
##  5      26  1.22 0.738 0.0382      1.69                5          1.12
##  6      12  1.09 0.508 0.0117      1.74                6          1.12
##  7      31  1.24 0.967 0.0240      1.75                7          1.12
##  8      30  1.11 0.232 0.0268      1.76                8          1.12
##  9      14  1.27 0.615 0.0368      1.80                9          1.12
## 10      40  1.18 0.308 0.0171      1.89               10          1.12
## # ... with 2 more variables: lowest_rank_alpha <dbl>, lowest_rank_beta <dbl>
summary(df_opti)
##     indi.id            A             alpha             beta          
##  Min.   : 1.00   Min.   :1.001   Min.   :0.1222   Min.   :0.0004062  
##  1st Qu.:13.25   1st Qu.:1.226   1st Qu.:0.3442   1st Qu.:0.0100739  
##  Median :25.50   Median :1.445   Median :0.5527   Median :0.0195919  
##  Mean   :25.50   Mean   :1.477   Mean   :0.5681   Mean   :0.0200000  
##  3rd Qu.:37.75   3rd Qu.:1.743   3rd Qu.:0.8028   3rd Qu.:0.0285548  
##  Max.   :50.00   Max.   :1.985   Max.   :0.9948   Max.   :0.0381932  
##     rank_val     rank_y_inter_idx lowest_rank_A   lowest_rank_alpha
##  Min.   :1.529   Min.   : 1.00    Min.   :1.122   Min.   :0.8947   
##  1st Qu.:1.961   1st Qu.:13.25    1st Qu.:1.122   1st Qu.:0.8947   
##  Median :2.295   Median :25.50    Median :1.122   Median :0.8947   
##  Mean   :2.302   Mean   :25.50    Mean   :1.122   Mean   :0.8947   
##  3rd Qu.:2.728   3rd Qu.:37.75    3rd Qu.:1.122   3rd Qu.:0.8947   
##  Max.   :3.034   Max.   :50.00    Max.   :1.122   Max.   :0.8947   
##  lowest_rank_beta 
##  Min.   :0.03704  
##  1st Qu.:0.03704  
##  Median :0.03704  
##  Mean   :0.03704  
##  3rd Qu.:0.03704  
##  Max.   :0.03704
summary(df_opti %>% select(lowest_rank_A, lowest_rank_alpha, lowest_rank_beta))
##  lowest_rank_A   lowest_rank_alpha lowest_rank_beta 
##  Min.   :1.122   Min.   :0.8947    Min.   :0.03704  
##  1st Qu.:1.122   1st Qu.:0.8947    1st Qu.:0.03704  
##  Median :1.122   Median :0.8947    Median :0.03704  
##  Mean   :1.122   Mean   :0.8947    Mean   :0.03704  
##  3rd Qu.:1.122   3rd Qu.:0.8947    3rd Qu.:0.03704  
##  Max.   :1.122   Max.   :0.8947    Max.   :0.03704

Relative Slopes, Intercepts

# e. relative slope and relative intercept with respect to lowest index
df_opti <- df_opti %>%
  mutate(rela_slope_to_lowest =
           ((
             (lowest_rank_alpha*lowest_rank_beta)/(alpha*beta)
           )^(1/(fl_rho-1))
           *(lowest_rank_alpha/alpha))
  ) %>%
  mutate(rela_intercept_to_lowest =
           (((
             (lowest_rank_alpha*lowest_rank_beta)/(alpha*beta)
           )^(1/(fl_rho-1))
           *(lowest_rank_A/alpha)) - (A/alpha))
  )

# h. Relative x-intercept points and ranking based on x-intercepts
df_opti <- df_opti %>%
  mutate(rela_x_intercept =
           (-1)*(rela_intercept_to_lowest/rela_slope_to_lowest)
  ) %>% arrange(rela_x_intercept) %>%
  mutate(rank_x_inter_idx = row_number())

# In this example, is x-intercept rank and y-intercept rank the same?
sum(df_opti$rank_y_inter_idx-df_opti$rank_x_inter_idx)
## [1] 0
# Print
summary(df_opti %>%
          select(rela_slope_to_lowest, rela_intercept_to_lowest))
##  rela_slope_to_lowest rela_intercept_to_lowest
##  Min.   :0.7946       Min.   :-6.2816         
##  1st Qu.:1.0155       1st Qu.:-1.4794         
##  Median :1.3717       Median :-0.8238         
##  Mean   :1.8444       Mean   :-1.1871         
##  3rd Qu.:2.1876       3rd Qu.:-0.5305         
##  Max.   :5.7846       Max.   : 0.0000
df_opti %>%
  select(A, alpha, beta,
         rank_val, rela_intercept_to_lowest, rela_x_intercept,
         rank_y_inter_idx, rank_x_inter_idx)
## # A tibble: 50 x 8
##        A alpha    beta rank_val rela_intercept_to_lowest rela_x_intercept
##    <dbl> <dbl>   <dbl>    <dbl>                    <dbl>            <dbl>
##  1  1.12 0.895 0.0370      1.53                  0                 0     
##  2  1.00 0.995 0.00853     1.54                 -0.00948           0.0119
##  3  1.13 0.575 0.0355      1.61                 -0.0938            0.0630
##  4  1.05 0.359 0.0233      1.62                 -0.156             0.0710
##  5  1.22 0.738 0.0382      1.69                 -0.155             0.130 
##  6  1.09 0.508 0.0117      1.74                 -0.266             0.177 
##  7  1.24 0.967 0.0240      1.75                 -0.163             0.182 
##  8  1.11 0.232 0.0268      1.76                 -0.635             0.192 
##  9  1.27 0.615 0.0368      1.80                 -0.310             0.221 
## 10  1.18 0.308 0.0171      1.89                 -0.733             0.299 
## # ... with 40 more rows, and 2 more variables: rank_y_inter_idx <int>,
## #   rank_x_inter_idx <int>

Spline and Inversion

Summing up for Splines

# f. cumulative sums
df_opti <- df_opti %>%
              mutate(rela_slope_to_lowest_cumsum =
                        cumsum(rela_slope_to_lowest)
                    ) %>%
              mutate(rela_intercept_to_lowest_cumsum =
                        cumsum(rela_intercept_to_lowest)
                    )
# Print
summary(df_opti %>% select(rela_slope_to_lowest_cumsum, rela_intercept_to_lowest_cumsum))
##  rela_slope_to_lowest_cumsum rela_intercept_to_lowest_cumsum
##  Min.   : 1.00               Min.   :-59.356                
##  1st Qu.:30.81               1st Qu.:-28.787                
##  Median :48.36               Median :-15.944                
##  Mean   :46.42               Mean   :-19.426                
##  3rd Qu.:64.62               3rd Qu.: -7.318                
##  Max.   :92.22               Max.   :  0.000

Inverting Splines (Cumulative)

# g. inverting cumulative slopes and intercepts
df_opti <- df_opti %>%
              mutate(rela_slope_to_lowest_cumsum_invert =
                        (1/rela_slope_to_lowest_cumsum)
                    ) %>%
              mutate(rela_intercept_to_lowest_cumsum_invert =
                        ((-1)*(rela_intercept_to_lowest_cumsum)/(rela_slope_to_lowest_cumsum))
                    )
# Print
summary(df_opti %>% select(rela_slope_to_lowest_cumsum_invert, rela_intercept_to_lowest_cumsum_invert))
##  rela_slope_to_lowest_cumsum_invert rela_intercept_to_lowest_cumsum_invert
##  Min.   :0.01084                    Min.   :0.0000                        
##  1st Qu.:0.01548                    1st Qu.:0.2375                        
##  Median :0.02068                    Median :0.3296                        
##  Mean   :0.06883                    Mean   :0.3284                        
##  3rd Qu.:0.03247                    3rd Qu.:0.4453                        
##  Max.   :1.00000                    Max.   :0.6436

X intercepts as Spline Knots and inverted Spline Knots

# # h. Relative x-intercept points
# df_opti <- df_opti %>%
#               mutate(rela_x_intercept =
#                         (-1)*(rela_intercept_to_lowest/rela_slope_to_lowest)
#                     )

# i. Inverted relative x-intercepts
df_opti <- df_opti %>%
              mutate(opti_lowest_spline_knots =
                        (rela_intercept_to_lowest_cumsum + rela_slope_to_lowest_cumsum*rela_x_intercept)
                    )
# Print
summary(df_opti %>% select(rela_x_intercept, opti_lowest_spline_knots))
##  rela_x_intercept opti_lowest_spline_knots
##  Min.   :0.0000   Min.   : 0.000          
##  1st Qu.:0.3538   1st Qu.: 3.586          
##  Median :0.6277   Median :14.415          
##  Mean   :0.6334   Mean   :18.657          
##  3rd Qu.:0.9831   3rd Qu.:34.778          
##  Max.   :1.2340   Max.   :54.438

Optimal Allocations

Compute Optimal Allocation for the Lowest

# j. Sort by order of receiving transfers/subsidies
df_opti <- df_opti %>% arrange(rela_x_intercept)

# k. Find position of subsidy
df_opti <- df_opti %>% arrange(opti_lowest_spline_knots) %>%
              mutate(tot_devi = opti_lowest_spline_knots - fl_N_agg) %>%
              arrange((-1)*case_when(tot_devi < 0 ~ tot_devi)) %>%
              mutate(allocate_lowest =
                        case_when(row_number() == 1 ~
                                    rela_intercept_to_lowest_cumsum_invert +
                                    rela_slope_to_lowest_cumsum_invert*fl_N_agg)) %>%
              mutate(allocate_lowest = allocate_lowest[row_number() == 1])
# Print
summary(df_opti %>% select(tot_devi, allocate_lowest))
##     tot_devi      allocate_lowest 
##  Min.   :-5.681   Min.   :0.4198  
##  1st Qu.:-2.095   1st Qu.:0.4198  
##  Median : 8.735   Median :0.4198  
##  Mean   :12.976   Mean   :0.4198  
##  3rd Qu.:29.097   3rd Qu.:0.4198  
##  Max.   :48.758   Max.   :0.4198

Compute Optimal Allocation for All Others given Lowest

# l. Find position of subsidy
df_opti <- df_opti %>%
              mutate(opti_allocate =
                        rela_intercept_to_lowest +
                        rela_slope_to_lowest*allocate_lowest) %>%
              mutate(opti_allocate =
                        case_when(opti_allocate >= 0 ~ opti_allocate, TRUE ~ 0)) %>%
              mutate(allocate_total = sum(opti_allocate, na.rm=TRUE))

# if want 0 to be 0
# case_when(opti_allocate >= 0 ~ opti_allocate, TRUE ~ 0)) %>%
# if want 0 to be NA, makes it easier to see average change for non zeros
# case_when(opti_allocate >= 0 ~ opti_allocate)) %>%

# Print
summary(df_opti %>% select(opti_allocate, allocate_total))
##  opti_allocate    allocate_total 
##  Min.   :0.0000   Min.   :5.681  
##  1st Qu.:0.0000   1st Qu.:5.681  
##  Median :0.0000   Median :5.681  
##  Mean   :0.1136   Mean   :5.681  
##  3rd Qu.:0.1792   3rd Qu.:5.681  
##  Max.   :0.7674   Max.   :5.681

Expected Outcomes Given Optimal Allocations

# l. Predictes Expected choice: A + alpha*opti_allocate
df_opti <- df_opti %>% mutate(opti_exp_outcome = A + alpha*opti_allocate)

# m. Drop some variables that I do not want to keep even in full df to export
# inpalc = input allocation optimal
# expout = expected outcome given input allocation
df_opti <- df_opti %>% select(-one_of(c('lowest_rank_alpha', 'lowest_rank_beta')))
ar_opti_inpalc <- df_opti %>% pull(opti_allocate)
ar_opti_expout <- df_opti %>% pull(opti_exp_outcome)

# Print
head(df_opti, 10)
## # A tibble: 10 x 21
##    indi.id     A alpha    beta rank_val rank_y_inter_idx lowest_rank_A
##      <dbl> <dbl> <dbl>   <dbl>    <dbl>            <int>         <dbl>
##  1      33  1.42 0.722 0.0261      2.03               15          1.12
##  2      48  1.09 0.519 0.00298     1.97               14          1.12
##  3       6  1.21 0.141 0.0346      1.96               13          1.12
##  4      41  1.13 0.229 0.0121      1.93               12          1.12
##  5      35  1.10 0.122 0.0202      1.90               11          1.12
##  6      40  1.18 0.308 0.0171      1.89               10          1.12
##  7      14  1.27 0.615 0.0368      1.80                9          1.12
##  8      30  1.11 0.232 0.0268      1.76                8          1.12
##  9      31  1.24 0.967 0.0240      1.75                7          1.12
## 10      12  1.09 0.508 0.0117      1.74                6          1.12
## # ... with 14 more variables: rela_slope_to_lowest <dbl>,
## #   rela_intercept_to_lowest <dbl>, rela_x_intercept <dbl>,
## #   rank_x_inter_idx <int>, rela_slope_to_lowest_cumsum <dbl>,
## #   rela_intercept_to_lowest_cumsum <dbl>,
## #   rela_slope_to_lowest_cumsum_invert <dbl>,
## #   rela_intercept_to_lowest_cumsum_invert <dbl>,
## #   opti_lowest_spline_knots <dbl>, tot_devi <dbl>, allocate_lowest <dbl>, ...
summary(df_opti)
##     indi.id            A             alpha             beta          
##  Min.   : 1.00   Min.   :1.001   Min.   :0.1222   Min.   :0.0004062  
##  1st Qu.:13.25   1st Qu.:1.226   1st Qu.:0.3442   1st Qu.:0.0100739  
##  Median :25.50   Median :1.445   Median :0.5527   Median :0.0195919  
##  Mean   :25.50   Mean   :1.477   Mean   :0.5681   Mean   :0.0200000  
##  3rd Qu.:37.75   3rd Qu.:1.743   3rd Qu.:0.8028   3rd Qu.:0.0285548  
##  Max.   :50.00   Max.   :1.985   Max.   :0.9948   Max.   :0.0381932  
##     rank_val     rank_y_inter_idx lowest_rank_A   rela_slope_to_lowest
##  Min.   :1.529   Min.   : 1.00    Min.   :1.122   Min.   :0.7946      
##  1st Qu.:1.961   1st Qu.:13.25    1st Qu.:1.122   1st Qu.:1.0155      
##  Median :2.295   Median :25.50    Median :1.122   Median :1.3717      
##  Mean   :2.302   Mean   :25.50    Mean   :1.122   Mean   :1.8444      
##  3rd Qu.:2.728   3rd Qu.:37.75    3rd Qu.:1.122   3rd Qu.:2.1876      
##  Max.   :3.034   Max.   :50.00    Max.   :1.122   Max.   :5.7846      
##  rela_intercept_to_lowest rela_x_intercept rank_x_inter_idx
##  Min.   :-6.2816          Min.   :0.0000   Min.   : 1.00   
##  1st Qu.:-1.4794          1st Qu.:0.3538   1st Qu.:13.25   
##  Median :-0.8238          Median :0.6277   Median :25.50   
##  Mean   :-1.1871          Mean   :0.6334   Mean   :25.50   
##  3rd Qu.:-0.5305          3rd Qu.:0.9831   3rd Qu.:37.75   
##  Max.   : 0.0000          Max.   :1.2340   Max.   :50.00   
##  rela_slope_to_lowest_cumsum rela_intercept_to_lowest_cumsum
##  Min.   : 1.00               Min.   :-59.356                
##  1st Qu.:30.81               1st Qu.:-28.787                
##  Median :48.36               Median :-15.944                
##  Mean   :46.42               Mean   :-19.426                
##  3rd Qu.:64.62               3rd Qu.: -7.318                
##  Max.   :92.22               Max.   :  0.000                
##  rela_slope_to_lowest_cumsum_invert rela_intercept_to_lowest_cumsum_invert
##  Min.   :0.01084                    Min.   :0.0000                        
##  1st Qu.:0.01548                    1st Qu.:0.2375                        
##  Median :0.02068                    Median :0.3296                        
##  Mean   :0.06883                    Mean   :0.3284                        
##  3rd Qu.:0.03247                    3rd Qu.:0.4453                        
##  Max.   :1.00000                    Max.   :0.6436                        
##  opti_lowest_spline_knots    tot_devi      allocate_lowest  opti_allocate   
##  Min.   : 0.000           Min.   :-5.681   Min.   :0.4198   Min.   :0.0000  
##  1st Qu.: 3.586           1st Qu.:-2.095   1st Qu.:0.4198   1st Qu.:0.0000  
##  Median :14.415           Median : 8.735   Median :0.4198   Median :0.0000  
##  Mean   :18.657           Mean   :12.976   Mean   :0.4198   Mean   :0.1136  
##  3rd Qu.:34.778           3rd Qu.:29.097   3rd Qu.:0.4198   3rd Qu.:0.1792  
##  Max.   :54.438           Max.   :48.758   Max.   :0.4198   Max.   :0.7674  
##  allocate_total  opti_exp_outcome
##  Min.   :5.681   Min.   :1.133   
##  1st Qu.:5.681   1st Qu.:1.357   
##  Median :5.681   Median :1.471   
##  Mean   :5.681   Mean   :1.529   
##  3rd Qu.:5.681   3rd Qu.:1.743   
##  Max.   :5.681   Max.   :1.985
print(cbind(ar_opti_expout,ar_opti_inpalc))
##       ar_opti_expout ar_opti_inpalc
##  [1,]       1.422612    0.006880531
##  [2,]       1.133311    0.076469304
##  [3,]       1.257978    0.364864308
##  [4,]       1.194809    0.280558877
##  [5,]       1.182663    0.653270709
##  [6,]       1.266780    0.297369431
##  [7,]       1.446481    0.279664218
##  [8,]       1.286286    0.753652841
##  [9,]       1.449922    0.213404330
## [10,]       1.280823    0.366106406
## [11,]       1.475576    0.346299292
## [12,]       1.321196    0.767417565
## [13,]       1.432987    0.530954043
## [14,]       1.323050    0.324096274
## [15,]       1.497510    0.419810625
## [16,]       1.320373    0.000000000
## [17,]       1.187691    0.000000000
## [18,]       1.439832    0.000000000
## [19,]       1.511505    0.000000000
## [20,]       1.434893    0.000000000
## [21,]       1.442200    0.000000000
## [22,]       1.475317    0.000000000
## [23,]       1.560948    0.000000000
## [24,]       1.665115    0.000000000
## [25,]       1.668056    0.000000000
## [26,]       1.379817    0.000000000
## [27,]       1.374463    0.000000000
## [28,]       1.448516    0.000000000
## [29,]       1.351798    0.000000000
## [30,]       1.466779    0.000000000
## [31,]       1.383970    0.000000000
## [32,]       1.754475    0.000000000
## [33,]       1.753308    0.000000000
## [34,]       1.629221    0.000000000
## [35,]       1.788196    0.000000000
## [36,]       1.653102    0.000000000
## [37,]       1.710182    0.000000000
## [38,]       1.794342    0.000000000
## [39,]       1.798925    0.000000000
## [40,]       1.886469    0.000000000
## [41,]       1.984957    0.000000000
## [42,]       1.656758    0.000000000
## [43,]       1.612771    0.000000000
## [44,]       1.810064    0.000000000
## [45,]       1.895045    0.000000000
## [46,]       1.893051    0.000000000
## [47,]       1.814640    0.000000000
## [48,]       1.343516    0.000000000
## [49,]       1.812390    0.000000000
## [50,]       1.782294    0.000000000

Save Outputs

if (bl_save_rda) {
  # df_opt_dtgch_ropti: dataframe, opt project, data guat cebu height, cebu edu mother, results relative linear optimal
  df_opt_dtgch_cbem4_rrlop <- df_opti
  usethis::use_data(df_opt_dtgch_cbem4_rrlop, df_opt_dtgch_cbem4_rrlop, overwrite = TRUE)  
}