.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/mdcev_no_outside_good/plot_non_monotonic_forecasting.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_mdcev_no_outside_good_plot_non_monotonic_forecasting.py: File non_monotonic_forecasting.py Michel Bierlaire, EPFL Fri Jul 25 2025, 17:27:35 Forecasting with a MDCEV model and the "non-monotonic utility" specification. .. GENERATED FROM PYTHON SOURCE LINES 8-131 .. rst-class:: sphx-glr-script-out .. code-block:: none Example: non monotonic utility Forecasting observation 0 / 2 [10 draws] ============ Comparison =================== Brute force: {1: '8.52', 2: '163', 3: '322', 4: '5.73'} objective 210, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '8.52', 2: '163', 3: '322', 4: '5.73'} objective 210, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '6.16', 2: '223', 3: '265', 4: '5.34'} objective 249, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '6.16', 2: '223', 3: '265', 4: '5.35'} objective 249, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '17.9', 2: '299', 3: '173', 4: '10.2'} objective 188, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '17.9', 2: '299', 3: '173', 4: '10.2'} objective 188, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '46.1', 2: '320', 3: '128', 4: '6.18'} objective 171, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '46.1', 2: '320', 3: '128', 4: '6.17'} objective 171, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '16.7', 2: '212', 3: '253', 4: '18.4'} objective 168, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '16.7', 2: '212', 3: '253', 4: '18.4'} objective 168, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '14.3', 2: '372', 3: '109', 4: '5.37'} objective 187, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '14.3', 2: '372', 3: '109', 4: '5.37'} objective 187, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '52.5', 2: '124', 3: '30.1', 4: '294'} objective 195, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '52.5', 2: '123', 3: '30.1', 4: '294'} objective 195, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '18', 2: '324', 3: '152', 4: '5.9'} objective 221, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '18', 2: '324', 3: '152', 4: '5.9'} objective 221, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '35.7', 2: '291', 3: '169', 4: '5.1'} objective 227, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '35.7', 2: '291', 3: '169', 4: '5.1'} objective 227, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '1.38', 2: '12', 3: '484', 4: '3.1'} objective 420, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '1.38', 2: '12', 3: '484', 4: '3.1'} objective 420, constraint 500, choice set {1, 2, 3, 4} Forecasting observation 1 / 2 [10 draws] ============ Comparison =================== Brute force: {1: '1.82', 2: '121', 3: '17', 4: '360'} objective 255, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '1.83', 2: '121', 3: '17', 4: '360'} objective 255, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '59.8', 2: '348', 3: '83.7', 4: '8.72'} objective 166, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '59.4', 2: '348', 3: '84.3', 4: '8.71'} objective 166, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '14.5', 2: '57.9', 3: '423', 4: '4.11'} objective 257, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '14.5', 2: '57.9', 3: '423', 4: '4.11'} objective 257, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '2.63', 2: '470', 3: '22.5', 4: '4.52'} objective 270, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '2.63', 2: '470', 3: '22.5', 4: '4.51'} objective 270, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '18.8', 2: '43.5', 3: '429', 4: '8.41'} objective 227, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '18.8', 2: '43.5', 3: '429', 4: '8.41'} objective 227, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '16.4', 2: '437', 3: '44.1', 4: '2.93'} objective 254, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '16.4', 2: '437', 3: '44.1', 4: '2.93'} objective 254, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '11.6', 2: '324', 3: '156', 4: '8.26'} objective 220, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '11.6', 2: '324', 3: '156', 4: '8.26'} objective 220, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '14.3', 2: '389', 3: '93.4', 4: '3.42'} objective 258, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '14.3', 2: '389', 3: '93.4', 4: '3.41'} objective 258, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '9.09', 2: '401', 3: '84.1', 4: '5.79'} objective 218, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '9.09', 2: '401', 3: '84.1', 4: '5.79'} objective 218, constraint 500, choice set {1, 2, 3, 4} ============ Comparison =================== Brute force: {1: '19.9', 2: '352', 3: '119', 4: '9.42'} objective 163, constraint 500, choice set {1, 2, 3, 4} Analytical: {1: '19.9', 2: '352', 3: '119', 4: '9.41'} objective 163, constraint 500, choice set {1, 2, 3, 4} Forecasting observation 0 / 2 [2000 draws] Forecasting observation 1 / 2 [2000 draws] Execution time for 2000 draws with brute force algorithm: 20.2 seconds Forecasting observation 0 / 2 [2000 draws] Forecasting observation 1 / 2 [2000 draws] Execution time for 2000 draws with analytical algorithm: 1.03 seconds 1 2 3 4 count 2000.000000 2.000000e+03 2000.000000 2000.000000 mean 39.598668 3.267214e+02 122.819661 10.860305 std 79.603989 1.411482e+02 125.865887 31.240736 min 0.000000 3.640836e-15 0.000000 0.000000 25% 6.887531 2.312174e+02 31.750637 4.066402 50% 13.874335 3.747594e+02 70.409042 5.882995 75% 30.098289 4.426312e+02 172.528683 8.805091 max 497.780408 5.000000e+02 500.000000 482.881548 1 2 3 4 count 2000.000000 2000.000000 2000.000000 2000.000000 mean 39.602338 326.719112 122.818632 10.859918 std 79.609622 141.150621 125.866995 31.243921 min 0.000000 0.000000 0.000000 0.000000 25% 6.886657 231.826845 31.749006 4.065986 50% 13.873640 374.762290 70.406599 5.882881 75% 30.103048 442.632262 172.489746 8.807932 max 497.783463 500.000000 500.000000 482.850818 1 2 3 4 count 2000.000000 2.000000e+03 2000.000000 2000.000000 mean 38.561604 3.233693e+02 127.299435 10.769694 std 78.690955 1.438931e+02 128.870804 31.042549 min 0.000000 1.370070e-15 0.000000 0.000000 25% 6.448176 2.145525e+02 31.190340 3.906875 50% 12.450833 3.725790e+02 73.637691 5.842609 75% 27.639689 4.437999e+02 187.630390 8.913430 max 487.783138 5.000000e+02 499.616097 486.839276 1 2 3 4 count 2000.000000 2000.000000 2000.000000 2000.000000 mean 38.561587 323.361076 127.305445 10.771892 std 78.693250 143.900485 128.877816 31.050409 min 0.000000 0.000000 0.000000 0.000000 25% 6.449612 214.741469 31.189704 3.908586 50% 12.449893 372.572041 73.635679 5.842149 75% 27.649573 443.800831 187.630687 8.919349 max 487.784147 500.000000 499.616172 486.818113 | .. code-block:: Python import sys import time import numpy as np import pandas as pd from IPython.display import display import biogeme.biogeme_logging as blog from biogeme.database import Database from biogeme.results_processing import EstimationResults from non_monotonic_specification import the_non_monotonic from process_data import database logger = blog.get_screen_logger(level=blog.INFO) logger.info('Example: non monotonic utility') result_file = 'saved_results/non_monotonic.yaml' try: results = EstimationResults.from_yaml_file(filename=result_file) except FileNotFoundError as e: print(e) print(f'File {result_file} is missing.') sys.exit() the_non_monotonic.estimation_results = results # % # We apply the model only on the first two rows of the database. two_rows_of_database: Database = database.extract_rows([10, 11]) # % budget_in_hours = 500 # % # # Validation # % # As the implementation is still experimental, we compare the result obtained by the bruteforce algorithm and # the analytical algorithm for a few draws. # Note that minor discrepancies between the outcome of the two algorithms are likely to occur, due to numerical # imprecision, inevitable in finite arithmetic. # However, if there are major differences, it should be reported. # % number_of_draws = 10 # % # We generate the draws epsilons = [ np.random.gumbel( loc=0, scale=1, size=(number_of_draws, the_non_monotonic.number_of_alternatives) ) for _ in range(two_rows_of_database.num_rows()) ] # % # We first compare the results obtained from the brute force and the analytical algorithms, for each draw. the_non_monotonic.validate_forecast( database=two_rows_of_database, total_budget=budget_in_hours, epsilons=epsilons ) # % # # Forecasting # We use a larger number of draws to obtain the forecast. # % number_of_draws = 2000 # % # We generate the draws epsilons = the_non_monotonic.generate_epsilons( number_of_observations=two_rows_of_database.num_rows(), number_of_draws=number_of_draws, ) # % # First, the brute force algorithm. start_time = time.time() optimal_consumptions_brute_force: list[pd.DataFrame] = the_non_monotonic.forecast( database=two_rows_of_database, total_budget=budget_in_hours, epsilons=epsilons, brute_force=True, ) end_time = time.time() # % print( f'Execution time for {number_of_draws} draws with brute force algorithm: {end_time-start_time:.3g} seconds' ) # % # Then, the analytical algorithm. start_time = time.time() optimal_consumptions_analytical: list[pd.DataFrame] = the_non_monotonic.forecast( database=two_rows_of_database, total_budget=budget_in_hours, epsilons=epsilons, brute_force=False, ) end_time = time.time() # % print( f'Execution time for {number_of_draws} draws with analytical algorithm: {end_time-start_time:.3g} seconds' ) # % # Results for the first observation, brute force method display(optimal_consumptions_brute_force[0].describe()) # % # Results for the first observation, analytical method display(optimal_consumptions_analytical[0].describe()) # % # Results for the second observation, brute force method display(optimal_consumptions_brute_force[1].describe()) # % # Results for the second observation, analytical method display(optimal_consumptions_analytical[1].describe()) .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 22.740 seconds) .. _sphx_glr_download_auto_examples_mdcev_no_outside_good_plot_non_monotonic_forecasting.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_non_monotonic_forecasting.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_non_monotonic_forecasting.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_non_monotonic_forecasting.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_