예제 #1
0
def table_1(n_states=3, quadratic=False):
    f, log_Rw, z0, z1, Rf, Rm, SMB, HML = preprocess_data(n_states)
    g = log_Rw  # Set g to be log return on wealth

    result_min = solve(f,
                       g,
                       z0,
                       z1,
                       ξ=100.,
                       quadratic=quadratic,
                       tol=1e-9,
                       max_iter=1000)

    # Table 1: transition matrix and stationary probability
    print('Table 1: Empirical and distorted transition probabilities')
    print('-------------------------------------------------------')
    print('               empirical             min divergence')
    print('-------------------------------------------------------')
    print('Transition Matrix:')
    for state in np.arange(1, n_states + 1):
        print(
            f"           {np.round(result_min['P'][state-1],2)}         {np.round(result_min['P_tilde'][state-1],2)}"
        )
    print('')
    print('Stationary Probability:')
    print(
        f"           {np.round(result_min['π'],2)}         {np.round(result_min['π_tilde'],2)}"
    )
    print('-------------------------------------------------------')
예제 #2
0
def figure_1():
    # Figure 1: bounds on expected log market return
    # Below we load presolved ξs but users can use find_ξ to resolve them.
    f, log_Rw, z0, z1, Rf, Rm, SMB, HML = preprocess_data(n_states=3)
    g = log_Rw

    result_min = solve(f,
                       g,
                       z0,
                       z1,
                       ξ=10.,
                       quadratic=False,
                       tol=1e-9,
                       max_iter=1000)

    ξs_lower = [100, 0.29255, 0.20511, 0.16625, 0.14302, 0.12713, 0.11539]
    ξs_upper = [100, 0.29898, 0.21142, 0.17248, 0.14918, 0.13323, 0.12141]
    result_lower_list = []
    result_upper_list = []

    for i in range(0, 7):
        temp = solve(f,
                     g,
                     z0,
                     z1,
                     ξ=ξs_lower[i],
                     quadratic=False,
                     tol=1e-9,
                     max_iter=1000)
        result_lower_list.append(temp)
        temp = solve(f,
                     -g,
                     z0,
                     z1,
                     ξ=ξs_upper[i],
                     quadratic=False,
                     tol=1e-9,
                     max_iter=1000)
        result_upper_list.append(temp)

    def f1(percent):
        box_chart(result_min, result_lower_list[int(percent / 5)],
                  result_upper_list[int(percent / 5)])

    res = interact(f1,
                   percent=widgets.IntSlider(min=0, max=30, step=5, value=20))
    return res
예제 #3
0
def table_5(n_states=3):
    # Table 5: Comparison of transition matrices
    f, log_Rw, z0, z1, Rf, Rm, SMB, HML = preprocess_data(n_states)
    g = log_Rw  # Set g to be log return on wealth

    result_re = solve(f,
                      g,
                      z0,
                      z1,
                      ξ=10.,
                      quadratic=False,
                      tol=1e-9,
                      max_iter=1000)
    result_qd = solve(f,
                      g,
                      z0,
                      z1,
                      ξ=10.,
                      quadratic=True,
                      tol=1e-9,
                      max_iter=1000)

    print('Table 5: Transition probabilities and stationary probabilities')
    print('-------------------------------------------------------')
    print('          relative entropy        quadratic divergence')
    print('-------------------------------------------------------')
    print('Transition Matrix:')
    for state in np.arange(1, n_states + 1):
        print(
            f"           {np.round(result_re['P'][state-1],2)}         {np.round(result_qd['P_tilde'][state-1],2)}"
        )
    print('')
    print('Stationary Probability:')
    print(
        f"           {np.round(result_re['π'],2)}         {np.round(result_qd['π_tilde'],2)}"
    )
    print('-------------------------------------------------------')
예제 #4
0
def objective_vs_ξ(n_states):
    """
    An illustration of how the optimized μ and ϵ change with ξ.

    """
    f, log_Rw, z0, z1, Rf, Rm, SMB, HML = preprocess_data(n_states)
    ξ_grid = np.arange(.01, 1.01, .005)
    results_lower = [None] * len(ξ_grid)

    for i in range(len(ξ_grid)):
        ξ = ξ_grid[i]
        temp = solve(f=f,
                     g=log_Rw,
                     z0=z0,
                     z1=z1,
                     ξ=ξ,
                     quadratic=False,
                     tol=1e-9,
                     max_iter=1000)
        results_lower[i] = temp

    μs_lower = np.array([result['μ'] for result in results_lower])
    ϵs_lower = np.array([result['ϵ'] for result in results_lower])

    fig = make_subplots(rows=1, cols=2)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=μs_lower,
                             name='μ',
                             line=dict(color='blue')),
                  row=1,
                  col=1)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=ϵs_lower,
                             name='ϵ',
                             line=dict(color='green')),
                  row=1,
                  col=2)
    fig.update_layout(height=400,
                      width=1000,
                      title_text="Minimized μ (left) and ϵ (right)",
                      showlegend=False)
    fig.update_xaxes(rangemode="tozero", title_text='ξ')
    fig.update_yaxes(rangemode="tozero")

    fig['layout']['xaxis' + str(int(1))].update(range=(0., 1.))
    fig['layout']['xaxis' + str(int(2))].update(range=(0., 1.))

    fig.show()
예제 #5
0
def entropy_moment_bounds(n_states):
    f, log_Rw, z0, z1, Rf, Rm, SMB, HML = preprocess_data(n_states)
    ξ_grid = np.arange(.01, 1.01, .01)
    results_lower = [None] * len(ξ_grid)
    results_upper = [None] * len(ξ_grid)

    for i in range(len(ξ_grid)):
        ξ = ξ_grid[i]
        temp = solve(f=f,
                     g=log_Rw,
                     z0=z0,
                     z1=z1,
                     ξ=ξ,
                     quadratic=False,
                     tol=1e-9,
                     max_iter=1000)
        results_lower[i] = temp
        temp = solve(f=f,
                     g=-log_Rw,
                     z0=z0,
                     z1=z1,
                     ξ=ξ,
                     quadratic=False,
                     tol=1e-9,
                     max_iter=1000)
        results_upper[i] = temp

    REs_lower = np.array([result['RE'] for result in results_lower])
    moment_bounds_cond_lower = np.array(
        [result['moment_bound_cond'] for result in results_lower])
    moment_bounds_cond_upper = np.array(
        [-result['moment_bound_cond'] for result in results_upper])
    moment_bounds_lower = np.array(
        [result['moment_bound'] for result in results_lower])
    moment_bounds_upper = np.array(
        [-result['moment_bound'] for result in results_upper])
    moment_cond = np.array(
        [result['moment_empirical_cond'] for result in results_lower])
    moment = np.array([result['moment_empirical'] for result in results_lower])

    # Plots for RE and E[Mg(X)]
    fig = make_subplots(rows=1, cols=2)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=np.ones_like(ξ_grid) * REs_lower[-1] * 1.2,
                             name='1.2x min RE',
                             line=dict(color='black', dash='dash')),
                  row=1,
                  col=1)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=REs_lower,
                             name='lower bound',
                             line=dict(color='blue')),
                  row=1,
                  col=1)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=moment_bounds_lower,
                             name='lower bound',
                             line=dict(color='green')),
                  row=1,
                  col=2)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=moment_bounds_upper,
                             name='upper bound',
                             line=dict(color='red')),
                  row=1,
                  col=2)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=moment,
                             name='E[g(X)]',
                             line=dict(dash='dash', color='orange')),
                  row=1,
                  col=2)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=moment_bounds_cond_lower[:, 0],
                             name='lower bound',
                             visible=False,
                             line=dict(color='green')),
                  row=1,
                  col=2)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=moment_bounds_cond_upper[:, 0],
                             name='upper bound',
                             visible=False,
                             line=dict(color='red')),
                  row=1,
                  col=2)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=moment_cond[:, 0],
                             name='E[g(X)|1]',
                             visible=False,
                             line=dict(dash='dash', color='orange')),
                  row=1,
                  col=2)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=moment_bounds_cond_lower[:, 1],
                             name='lower bound',
                             visible=False,
                             line=dict(color='green')),
                  row=1,
                  col=2)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=moment_bounds_cond_upper[:, 1],
                             name='upper bound',
                             visible=False,
                             line=dict(color='red')),
                  row=1,
                  col=2)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=moment_cond[:, 1],
                             name='E[g(X)|2]',
                             visible=False,
                             line=dict(dash='dash', color='orange')),
                  row=1,
                  col=2)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=moment_bounds_cond_lower[:, 2],
                             name='lower bound',
                             visible=False,
                             line=dict(color='green')),
                  row=1,
                  col=2)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=moment_bounds_cond_upper[:, 2],
                             name='upper bound',
                             visible=False,
                             line=dict(color='red')),
                  row=1,
                  col=2)
    fig.add_trace(go.Scatter(x=ξ_grid,
                             y=moment_cond[:, 2],
                             name='E[g(X)|3]',
                             visible=False,
                             line=dict(dash='dash', color='orange')),
                  row=1,
                  col=2)
    fig.update_layout(
        height=400,
        width=1000,
        title_text="Relative entropy (left) and moment bounds (right)",
        showlegend=False)
    fig.update_xaxes(rangemode="tozero", title_text='ξ')
    fig.update_yaxes(rangemode="tozero")

    fig['layout']['xaxis' + str(int(1))].update(range=(0., 1.))
    fig['layout']['yaxis' + str(int(1))].update(range=(0., 0.06))
    fig['layout']['xaxis' + str(int(2))].update(range=(0., 1.))
    fig['layout']['yaxis' + str(int(2))].update(range=(-0.01, 0.04))

    # Add button
    fig.update_layout(updatemenus=[
        dict(
            type="buttons",
            direction="up",
            active=0,
            x=1.2,
            y=0.9,
            buttons=list([
                dict(label="Unconditional",
                     method="update",
                     args=[{
                         "visible": [True] * 5 + [False] * 15
                     }]),
                dict(label="State 1",
                     method="update",
                     args=[{
                         "visible": [True] * 2 + [False] * 3 + [True] * 3 +
                         [False] * 6
                     }]),
                dict(label="State 2",
                     method="update",
                     args=[{
                         "visible": [True] * 2 + [False] * 6 + [True] * 3 +
                         [False] * 3
                     }]),
                dict(label="State 3",
                     method="update",
                     args=[{
                         "visible": [True] * 2 + [False] * 9 + [True] * 3
                     }]),
            ]),
        )
    ])

    fig.show()
예제 #6
0
def table_3():
    # Table 3: Expected log market return bounds
    f, log_Rw, z0, z1, Rf, Rm, SMB, HML = preprocess_data(n_states=3)
    g = log_Rw  # Set g to be log return on wealth

    # 1) Relative entropy specification
    result_min_RE = solve(f,
                          g,
                          z0,
                          z1,
                          ξ=100.,
                          quadratic=False,
                          tol=1e-9,
                          max_iter=1000)
    ξ_20_lower_RE = find_ξ(solver_args=(f, g, z0, z1, False, 1e-9, 1000),
                           min_div=result_min_RE['RE'],
                           pct=0.2,
                           initial_guess=1.,
                           interval=(0, 10.),
                           tol=1e-5,
                           max_iter=100)
    result_lower_RE = solve(f,
                            g,
                            z0,
                            z1,
                            ξ=ξ_20_lower_RE,
                            quadratic=False,
                            tol=1e-9,
                            max_iter=1000)
    ξ_20_upper_RE = find_ξ(solver_args=(f, -g, z0, z1, False, 1e-9, 1000),
                           min_div=result_min_RE['RE'],
                           pct=0.2,
                           initial_guess=1.,
                           interval=(0, 10.),
                           tol=1e-5,
                           max_iter=100)
    result_upper_RE = solve(f,
                            -g,
                            z0,
                            z1,
                            ξ=ξ_20_upper_RE,
                            quadratic=False,
                            tol=1e-9,
                            max_iter=1000)

    # 2) Quadratic specification
    result_min_QD = solve(f,
                          g,
                          z0,
                          z1,
                          ξ=10.,
                          quadratic=True,
                          tol=1e-9,
                          max_iter=1000)
    ξ_20_lower_QD = find_ξ(solver_args=(f, g, z0, z1, True, 1e-9, 1000),
                           min_div=result_min_QD['QD'],
                           pct=0.2,
                           initial_guess=1.,
                           interval=(0, 10.),
                           tol=1e-4,
                           max_iter=100)
    result_lower_QD = solve(f,
                            g,
                            z0,
                            z1,
                            ξ=ξ_20_lower_QD,
                            quadratic=True,
                            tol=1e-9,
                            max_iter=1000)
    ξ_20_upper_QD = find_ξ(solver_args=(f, -g, z0, z1, True, 1e-9, 1000),
                           min_div=result_min_QD['QD'],
                           pct=0.2,
                           initial_guess=1.,
                           interval=(0, 10.),
                           tol=1e-4,
                           max_iter=100)
    result_upper_QD = solve(f,
                            -g,
                            z0,
                            z1,
                            ξ=ξ_20_upper_QD,
                            quadratic=True,
                            tol=1e-9,
                            max_iter=1000)

    print('Table 3: Expected log market return bounds')
    print(
        '-----------------------------------------------------------------------'
    )
    print(
        'conditioning    empirical   relative entropy     quadratic divergence'
    )
    print('                             (lower, upper)         (lower,upper)')
    print(
        '-----------------------------------------------------------------------'
    )
    print('low D/P           %s         (%s,%s)           (%s,%s) ' \
          % (np.round(result_min_RE['moment_empirical_cond'][0]*400,2),
             np.round(result_lower_RE['moment_bound_cond'][0]*400,2),
             np.round(-result_upper_RE['moment_bound_cond'][0]*400,2),
             np.round(result_lower_QD['moment_bound_cond'][0]*400,2),
             np.round(-result_upper_QD['moment_bound_cond'][0]*400,2)))
    print('mid D/P           %s         (%s,%s)           (%s,%s)    ' \
          % (np.round(result_min_RE['moment_empirical_cond'][1]*400,2),
             np.round(result_lower_RE['moment_bound_cond'][1]*400,2),
             np.round(-result_upper_RE['moment_bound_cond'][1]*400,2),
             np.round(result_lower_QD['moment_bound_cond'][1]*400,2),
             np.round(-result_upper_QD['moment_bound_cond'][1]*400,2)))
    print('high D/P          %s        (%s,%s)           (%s,%s)  ' \
          % (np.round(result_min_RE['moment_empirical_cond'][2]*400,2),
             np.round(result_lower_RE['moment_bound_cond'][2]*400,2),
             np.round(-result_upper_RE['moment_bound_cond'][2]*400,2),
             np.round(result_lower_QD['moment_bound_cond'][2]*400,2),
             np.round(-result_upper_QD['moment_bound_cond'][2]*400,2)))
    print('unconditional     %s         (%s,%s)           (%s,%s)   ' \
          % (np.round(result_min_RE['moment_empirical']*400,2),
             np.round(result_lower_RE['moment_bound']*400,2),
             np.round(-result_upper_RE['moment_bound']*400,2),
             np.round(result_lower_QD['moment_bound']*400,2),
             np.round(-result_upper_QD['moment_bound']*400,2)))
    print(
        '-----------------------------------------------------------------------'
    )
    print('Note 1: here we use n_states = 3.')
    print(
        'Note 2: the numbers in the parentheses impose a divergence constraint'
    )
    print('        that is 20 percent higher than the minimum.')
예제 #7
0
def table_2():
    # Table 2: bounds on log expected return and generalized volatility
    f, log_Rw, z0, z1, Rf, Rm, SMB, HML = preprocess_data(n_states=3)

    # 1) Calculate bounds on expected return
    # Minimum divergence case
    result_min = solve(f,
                       Rm,
                       z0,
                       z1,
                       ξ=100.,
                       quadratic=False,
                       tol=1e-9,
                       max_iter=1000)

    # 20% higher divergence case, lower bound problem
    ξ_20_lower = find_ξ(solver_args=(f, Rm, z0, z1, False, 1e-9, 1000),
                        min_div=result_min['RE'],
                        pct=0.2,
                        initial_guess=1.,
                        interval=(0, 100.),
                        tol=1e-5,
                        max_iter=100)
    result_lower = solve(f=f,
                         g=Rm,
                         z0=z0,
                         z1=z1,
                         ξ=ξ_20_lower,
                         quadratic=False,
                         tol=1e-9,
                         max_iter=1000)

    # 20% higher divergence case, upper bound problem
    ξ_20_upper = find_ξ(solver_args=(f, -Rm, z0, z1, False, 1e-9, 1000),
                        min_div=result_min['RE'],
                        pct=0.2,
                        initial_guess=1.,
                        interval=(0, 100.),
                        tol=1e-5,
                        max_iter=100)
    result_upper = solve(f=f,
                         g=-Rm,
                         z0=z0,
                         z1=z1,
                         ξ=ξ_20_upper,
                         quadratic=False,
                         tol=1e-9,
                         max_iter=1000)

    # 2) Calculate bounds on generalized volatility
    # Below we load presolved ζs but users can plot the objective function over ζ to find the optimal ζs.
    g1 = Rm
    g2 = log_Rw
    solver_args = (f, z0, z1, False, 1e-9, 1000)
    vol_min, vol_cond_min, vol_empirical, vol_cond_empirical\
        = bound_ratio(find_ξ_args=(solver_args, 0., None, None, None, None),
                      g1=g1, g2=g2, ζ=1., lower=True, result_type=2)
    vol_lower, vol_cond_lower, _, _\
        = bound_ratio(find_ξ_args=(solver_args, 0.2, 1., (0., 10.), 1e-5, 100),
                         g1=g1, g2=g2, ζ=1.008, lower=True, result_type=2)
    vol_upper, vol_cond_upper, _, _\
        = bound_ratio(find_ξ_args=(solver_args, 0.2, 1., (0., 10.), 1e-5, 100),
                         g1=g1, g2=g2, ζ=1.009, lower=False, result_type=2)

    print('Table 2: Log expected market return and generalized volatility')
    print(
        '-------------------------------------------------------------------------------'
    )
    print(
        'conditioning      logE           logE          logE - Elog     logE - Elog'
    )
    print(
        '                empirical       imputed         empirical        imputed'
    )
    print(
        '                             (lower, upper)                   (lower,upper)'
    )
    print(
        '-------------------------------------------------------------------------------'
    )
    print('low D/P           %s           %s             %s            %s' \
          % (np.round(np.log(result_min['moment_empirical_cond'][0])*400,2),
             np.round(np.log(result_min['moment_bound_cond'][0])*400,2),
             np.round(vol_cond_empirical[0]*400,2),
             np.round(vol_cond_min[0]*400,2)))
    print('                              (%s,%s)                      (%s,%s)' \
          % (np.round(np.log(result_lower['moment_bound_cond'][0])*400,2),
             np.round(np.log(-result_upper['moment_bound_cond'][0])*400,2),
             np.round(vol_cond_lower[0]*400,2),np.round(vol_cond_upper[0]*400,2)))
    print('mid D/P           %s            %s             %s            %s' \
          % (np.round(np.log(result_min['moment_empirical_cond'][1])*400,2),
             np.round(np.log(result_min['moment_bound_cond'][1])*400,2),
             np.round(vol_cond_empirical[1]*400,2),np.round(vol_cond_min[1]*400,2)))
    print('                              (%s,%s)                      (%s,%s)' \
          % (np.round(np.log(result_lower['moment_bound_cond'][1])*400,2),
             np.round(np.log(-result_upper['moment_bound_cond'][1])*400,2),
             np.round(vol_cond_lower[1]*400,2),np.round(vol_cond_upper[1]*400,2)))
    print('high D/P          %s          %s             %s            %s' \
          % (np.round(np.log(result_min['moment_empirical_cond'][2])*400,2),
             np.round(np.log(result_min['moment_bound_cond'][2])*400,2),
             np.round(vol_cond_empirical[2]*400,2),np.round(vol_cond_min[2]*400,2)))
    print('                              (%s,%s)                      (%s,%s)' \
          % (np.round(np.log(result_lower['moment_bound_cond'][2])*400,2),
             np.round(np.log(-result_upper['moment_bound_cond'][2])*400,2),
             np.round(vol_cond_lower[2]*400,2),np.round(vol_cond_upper[2]*400,2)))
    print('unconditional     %s           %s             %s            %s' \
          % (np.round(np.log(result_min['moment_empirical'])*400,2),
             np.round(np.log(result_min['moment_bound'])*400,2),
             np.round(vol_empirical*400,2),np.round(vol_min*400,2)))
    print('                              (%s,%s)                      (%s,%s)' \
          % (np.round(np.log(result_lower['moment_bound'])*400,2),
             np.round(np.log(-result_upper['moment_bound'])*400,2),
             np.round(vol_lower*400,2),np.round(vol_upper*400,2)))
    print(
        '-------------------------------------------------------------------------------'
    )
    print(
        'Note 1: here we use n_states = 3 and a relative entropy divergence.')
    print(
        'Note 2: the numbers in the parentheses impose a divergence constraint'
    )
    print('        that is 20 percent higher than the minimum.')
예제 #8
0
def bound_ratio(find_ξ_args, g1, g2, ζ, lower=True, result_type=0):
    """
    This function computes the bound on ratios of two gs at a given ζ.
    
    Parameters
    ----------
    find_ξ_args : tuple
        Arguments (except for g in solver_args and min_div) to be passed into find_ξ,
        including (solver_args, pct, initial_guess, interval, tol, max_iter).
        If pct == 0., then the function will use ξ = 100.
    g1 : (n,) ndarray
        The g1 in g = g1 - ζ*g2.
    g2 : (n,) ndarray
        The g2 in g = g1 - ζ*g2.
    ζ : float
        The ζ in g = g1 - ζ*g2.
    lower : bool
        If True, it will compute the lower bound of the ratio.
        If False, it will compute the upper bound of the ratio.
    result_type : int
        If 0, the function will compute the log difference of the two moments.
        If 1, the function will compute the ratio of the two moments.
        If 2, the function will subtract the log of the first moment by the second moment.

    Returns
    -------
    ratio : float
        Unconditional ratio of two moments. 
    ratio_cond : (n_states,) ndarray
        Conditional ratios of two moments.
    ratio_empirical : float
        Empirical unconditional ratio of two moments.
    ratio_cond_empirical : (n_states,) ndarray
        Empirical conditional ratios of two moments.

    """
    if lower:
        g = g1 - ζ * g2
    else:
        g = -(g1 - ζ * g2)
    # f, z0, z1, quadratic, tol, max_iter
    f = find_ξ_args[0][0]
    z0 = find_ξ_args[0][1]
    z1 = find_ξ_args[0][2]
    n_states = z1.shape[1]
    quadratic = find_ξ_args[0][3]
    solver_tol = find_ξ_args[0][4]
    solver_max_iter = find_ξ_args[0][5]
    solver_args = (f, g, z0, z1, quadratic, solver_tol, solver_max_iter)
    result = solve(f, g, z0, z1, 100., quadratic, solver_tol, solver_max_iter)
    if find_ξ_args[1] != 0:
        if quadratic:
            min_div = result['QD']
        else:
            min_div = result['RE']
        ξ = find_ξ(solver_args, min_div, find_ξ_args[1], find_ξ_args[2],
                   find_ξ_args[3], find_ξ_args[4], find_ξ_args[5])
        result = solve(f, g, z0, z1, ξ, quadratic, solver_tol, solver_max_iter)

    # Calculate ratio, empirical
    # Term 1
    moment_cond_g1 = np.zeros(n_states)
    for state in range(n_states):
        moment_cond_g1[state] = np.mean(g1[z0[:, state]])
    moment_g1 = moment_cond_g1 @ result['π']

    # Term 2
    moment_cond_g2 = np.zeros(n_states)
    for state in range(n_states):
        moment_cond_g2[state] = np.mean(g2[z0[:, state]])
    moment_g2 = moment_cond_g2 @ result['π']

    # Calculate ratio, distorted
    # Term 1
    moment_bound_cond_g1 = np.zeros(n_states)
    for state in range(n_states):
        moment_bound_cond_g1[state] = np.mean(result['N'][z0[:, state]] *
                                              g1[z0[:, state]])
    moment_bound_g1 = moment_bound_cond_g1 @ result['π_tilde']

    # Term 2
    moment_bound_cond_g2 = np.zeros(n_states)
    for state in range(n_states):
        moment_bound_cond_g2[state] = np.mean(result['N'][z0[:, state]] *
                                              g2[z0[:, state]])
    moment_bound_g2 = moment_bound_cond_g2 @ result['π_tilde']

    # Combine term 1 and term 2
    if result_type == 0:
        ratio_empirical = np.log(moment_g1) - np.log(moment_g2)
        ratio_empirical_cond = np.log(moment_cond_g1) - np.log(moment_cond_g2)
        ratio_bound = np.log(moment_bound_g1) - np.log(moment_bound_g2)
        ratio_bound_cond = np.log(moment_bound_cond_g1) - np.log(
            moment_bound_cond_g2)
    elif result_type == 1:
        ratio_empirical = moment_g1 / moment_g2
        ratio_empirical_cond = moment_cond_g1 / moment_cond_g2
        ratio_bound = moment_bound_g1 / moment_bound_g2
        ratio_bound_cond = moment_bound_cond_g1 / moment_bound_cond_g2
    elif result_type == 2:
        ratio_empirical = np.log(moment_g1) - moment_g2
        ratio_empirical_cond = np.log(moment_cond_g1) - moment_cond_g2
        ratio_bound = np.log(moment_bound_g1) - moment_bound_g2
        ratio_bound_cond = np.log(moment_bound_cond_g1) - moment_bound_cond_g2
    else:
        raise ValueError('Invalid result_typle. It should be 0, 1 or 2.')

    return ratio_bound, ratio_bound_cond, ratio_empirical, ratio_empirical_cond