def test_weights_and_formatting(dimensions: int, specification: str, size: int) -> None: """Test that weights sum to one and that the configurations can be formatted.""" integration = Integration(specification, size) assert str(integration) weights = integration._build(dimensions)[1] np.testing.assert_allclose(weights.sum(), 1, rtol=0, atol=1e-12)
def test_hermite_integral(dimensions, specification, size, naive_specification): """Test if approximations of a simple Gauss-Hermite integral (product of squared variables of integration with respect to the standard normal density) are reasonably correct. Then, if a naive specification is given, tests that it performs worse, even with an order of magnitude more nodes. """ integral = lambda n, w: w.T @ (n ** 2).prod(axis=1) nodes, weights = Integration(specification, size, seed=0)._build(dimensions) simulated = integral(nodes, weights) np.testing.assert_allclose(simulated, 1, rtol=0, atol=0.01) if naive_specification: naive = integral(*Integration(naive_specification, 10 * weights.size, seed=0)._build(dimensions)) np.testing.assert_array_less(np.linalg.norm(simulated - 1), np.linalg.norm(naive - 1))
def test_nwspgr(dimensions: int, level: int, nested: bool) -> None: """Compare with output from the Matlab function nwspgr by Florian Heiss and Viktor Winschel.""" if shutil.which('matlab') is None: return pytest.skip( "Failed to find a MATLAB executable in this environment.") with tempfile.NamedTemporaryFile() as handle: command = ';'.join([ f"[nodes, weights] = nwspgr('{'KPN' if nested else 'GQN'}', {dimensions}, {level})", f"save('{handle.name}', 'nodes', 'weights')", 'exit' ]) subprocess.run([ 'matlab', '-nodesktop', '-nosplash', '-minimize', '-wait', '-r', f'"{command}"' ]) nwspgr = scipy.io.loadmat(handle.name) nodes, weights = Integration('nested_grid' if nested else 'grid', level)._build(dimensions) np.testing.assert_allclose(nwspgr['nodes'], np.c_[nodes], rtol=0, atol=1e-14) np.testing.assert_allclose(nwspgr['weights'], np.c_[weights], rtol=0, atol=1e-14)
def medium_blp_simulation() -> SimulationFixture: """Solve a simulation with four markets, linear/nonlinear/cost constants, two linear characteristics, two cost characteristics, a demographic interacted with second-degree prices, and an alternative ownership structure. """ id_data = build_id_data(T=4, J=25, F=6) simulation = Simulation( product_formulations=(Formulation('1 + x + y'), Formulation('1 + I(prices ** 2)'), Formulation('1 + a + b')), beta=[1, 2, 1], sigma=[ [0.5, 0], [0.0, 0], ], gamma=[1, 1, 2], product_data={ 'market_ids': id_data.market_ids, 'firm_ids': id_data.firm_ids, 'clustering_ids': np.random.RandomState(1).choice(range(20), id_data.size), 'ownership': build_ownership( id_data, lambda f, g: 1 if f == g else (0.1 if f > 3 and g > 3 else 0)) }, agent_formulation=Formulation('0 + f'), pi=[[+0], [-3]], integration=Integration('product', 4), xi_variance=0.0001, omega_variance=0.0001, correlation=0.8, seed=1) return simulation, simulation.solve()
def small_blp_simulation() -> SimulationFixture: """Solve a simulation with three markets, linear prices, a linear/nonlinear characteristic, two cost characteristics, and an acquisition. """ id_data = build_id_data(T=3, J=18, F=3, mergers=[{1: 0}]) simulation = Simulation( product_formulations=( Formulation('0 + prices + x'), Formulation('0 + x'), Formulation('0 + a + b') ), beta=[-5, 1], sigma=2, gamma=[2, 1], product_data={ 'market_ids': id_data.market_ids, 'firm_ids': id_data.firm_ids, 'clustering_ids': np.random.RandomState(0).choice(range(10), id_data.size) }, integration=Integration('product', 3), xi_variance=0.001, omega_variance=0.001, correlation=0.7, seed=0 ) return simulation, simulation.solve()
def medium_simulation(): """Solve a simulation with four markets, a nonlinear/cost constant, two linear characteristics, two cost characteristics, a demographic interacted with prices, a double acquisition, and a non-standard ownership structure. """ id_data = build_id_data(T=4, J=25, F=6, mergers=[{f: 2 for f in range(2)}]) simulation = Simulation( product_formulations=( Formulation('0 + x + y'), Formulation('1 + prices'), Formulation('1 + a + b') ), beta=[2, 1], sigma=[ [0.5, 0], [0, 0], ], gamma=[1, 1, 2], product_data={ 'market_ids': id_data.market_ids, 'firm_ids': id_data.firm_ids, 'ownership': build_ownership(id_data, lambda f, g: 1 if f == g else (0.1 if f > 3 and g > 3 else 0)) }, agent_formulation=Formulation('0 + f'), pi=[ [ 0], [-3] ], integration=Integration('product', 4), xi_variance=0.0001, omega_variance=0.0001, correlation=0.8, seed=1 ) return simulation, simulation.solve()
def large_nested_blp_simulation() -> SimulationFixture: """Solve a simulation with 20 markets, varying numbers of products per market, a linear constant, log-normal coefficients on prices, a linear/nonlinear/cost characteristic, another three linear characteristics, another two cost characteristics, demographics interacted with prices and the linear/nonlinear/cost characteristic, three nesting groups with the same nesting parameter, and a log-linear cost specification. """ id_data = build_id_data(T=20, J=20, F=9) keep = np.arange(id_data.size) np.random.RandomState(0).shuffle(keep) id_data = id_data[keep[:int(0.5 * id_data.size)]] simulation = Simulation( product_formulations=( Formulation('1 + x + y + z + q'), Formulation('0 + I(-prices) + x'), Formulation('0 + log(x) + log(a) + log(b)') ), product_data={ 'market_ids': id_data.market_ids, 'firm_ids': id_data.firm_ids, 'nesting_ids': np.random.RandomState(2).choice(['f', 'g', 'h'], id_data.size), 'clustering_ids': np.random.RandomState(2).choice(range(30), id_data.size) }, beta=[1, 1, 2, 3, 1], sigma=[ [0.5, 0], [0.0, 2] ], pi=[ [2, 1, 0], [0, 0, 2] ], gamma=[0.1, 0.2, 0.3], rho=0.1, agent_formulation=Formulation('1 + f + g'), integration=Integration('product', 4), xi_variance=0.00001, omega_variance=0.00001, correlation=0.9, distributions=['lognormal', 'normal'], costs_type='log', seed=2, ) simulation_results = simulation.replace_endogenous() simulated_micro_moments = [DemographicExpectationMoment( product_id=None, demographics_index=1, value=0, market_ids=simulation.unique_market_ids[3:5] )] return simulation, simulation_results, {}, simulated_micro_moments
def large_blp_simulation() -> SimulationFixture: """Solve a simulation with 20 markets, varying numbers of products per market, a linear constant, linear/nonlinear prices, a linear/nonlinear/cost characteristic, another three linear characteristics, another two cost characteristics, demographics interacted with prices and the linear/nonlinear/cost characteristic, dense parameter matrices, a log-linear cost specification, and local differentiation instruments on the demand side. """ id_data = build_id_data(T=20, J=20, F=9) keep = np.arange(id_data.size) np.random.RandomState(0).shuffle(keep) id_data = id_data[keep[:int(0.5 * id_data.size)]] simulation = Simulation( product_formulations=(Formulation('1 + prices + x + y + z + q'), Formulation('0 + prices + x'), Formulation('0 + log(x) + log(a) + log(b)')), beta=[1, -10, 1, 2, 3, 1], sigma=[[1, -0.1], [0, +2.0]], gamma=[0.1, 0.2, 0.3], product_data={ 'market_ids': id_data.market_ids, 'firm_ids': id_data.firm_ids, 'clustering_ids': np.random.RandomState(2).choice(range(30), id_data.size) }, agent_formulation=Formulation('0 + f + g'), pi=[[1, 0], [0, 2]], integration=Integration('product', 4), xi_variance=0.00001, omega_variance=0.00001, correlation=0.9, costs_type='log', seed=2) simulation_results = simulation.solve() differentiation_instruments = np.c_[ build_differentiation_instruments(Formulation('0 + x + y + z + q'), simulation_results.product_data), build_matrix(Formulation('0 + a + b'), simulation_results.product_data )] simulation_results.product_data = update_matrices( simulation_results.product_data, { 'demand_instruments': (differentiation_instruments, simulation_results.product_data.demand_instruments.dtype) }) return simulation, simulation_results
def small_simulation(): """Solve a simulation with two markets, linear prices, a nonlinear characteristic, a cost characteristic, and an acquisition. """ simulation = Simulation( product_formulations=( Formulation('0 + prices'), Formulation('0 + x'), Formulation('0 + a') ), beta=-5, sigma=1, gamma=2, product_data=build_id_data(T=2, J=18, F=3, mergers=[{1: 0}]), integration=Integration('product', 3), xi_variance=0.001, omega_variance=0.001, correlation=0.7, seed=0 ) return simulation, simulation.solve()
def large_nested_blp_simulation() -> SimulationFixture: """Solve a simulation with ten markets, a linear constant, linear/nonlinear prices, a linear/nonlinear/cost characteristic, another three linear characteristics, another four cost characteristics, demographics interacted with prices and the linear/nonlinear/cost characteristic, three nesting groups with the same nesting parameter, an acquisition, a triple acquisition, and a log-linear cost specification. """ id_data = build_id_data(T=10, J=20, F=9, mergers=[{f: 4 + int(f > 0) for f in range(4)}]) simulation = Simulation( product_formulations=( Formulation('1 + prices + x + y + z + q'), Formulation('0 + prices + x'), Formulation('0 + log(x) + a + b + c + d') ), beta=[1, -10, 1, 2, 3, 1], sigma=[ [1, 0], [0, 2] ], gamma=[0.1, 0.2, 0.3, 0.1, 0.3], product_data={ 'market_ids': id_data.market_ids, 'firm_ids': id_data.firm_ids, 'nesting_ids': np.random.RandomState(2).choice(['f', 'g', 'h'], id_data.size), 'clustering_ids': np.random.RandomState(2).choice(range(30), id_data.size) }, agent_formulation=Formulation('0 + f + g'), pi=[ [1, 0], [0, 2] ], integration=Integration('product', 4), rho=0.05, xi_variance=0.00001, omega_variance=0.00001, correlation=0.9, costs_type='log', seed=2 ) return simulation, simulation.solve()