Generate files from latent-variable measurement specifications.

This script illustrates the latent-variables specification API. It does not estimate any model and does not use data. Instead, it starts from pure specification objects, resolves them into semantic ResolvedModel objects, and uses the resolved specifications to generate files that can be inspected or used elsewhere.

The script shows how to:

  • define pure latent-variable and measurement specifications,

  • define normalization plans in expert mode, using explicit parameter fixings,

  • resolve a specification into a semantic ResolvedModel,

  • build live Biogeme expressions from the resolved specification,

  • generate files from the resolved specification:
    • pedagogical runnable Python code,

    • a LaTeX scientific report,

    • a static HTML report.

The same specification is resolved under several build contexts:

  • maximum likelihood and Bayesian estimation modes,

  • log/exp and lower-bound parameterizations for positive parameters,

  • automatic normalization and an explicit normalization plan.

Only specification files and reports are generated. No estimation algorithm is run in this example.

Michel Bierlaire Sat Jun 06 2026, 15:20:15

====================
SPEC loaded
====================
- #latent_variables:  2
- #likert_indicators: 14
- #likert_types:      1

====================
Normalization plans ready
====================
- plan_empty:   (no fixings)
- plan_example: (some intercept/loadings/sigma fixed)

============================================================
CASE 1: ML + log/exp positivity + EMPTY plan
============================================================
- estimation mode: maximum_likelihood
- positivity mode: log_exp
- latent variables: 2
- indicators: 14
- ordinal threshold systems: 1
- measurement models present: ['ordered_probit']
- resolved parameters: 66
- structural expressions: ['car_centric_attitude', 'environmental_attitude']
- threshold systems: ['likert']
- measurement terms: 14 indicators
  example term (Envir01): OrderedProbit(((((<Numeric value=0.0> + <Beta name=measurement_intercept_Envir01 value=0.0 status=0>) + (<Beta name=measurement_coefficient_car_centric_attitude_Envir01 value=0.0 status=0> * (((((((((((<Numeric value=0.0> + <Beta name=struct_car_centric_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_top_manager value=0.0 status=0> * <Variable name=top_manager>)) + (<Beta name=struct_car_centric_attitude_age_30_less value=0.0 status=0> * <Variable name=age_30_less>)) + (<Beta name=struct_car_centric_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_car_centric_attitude_car_oriented_parents value=0.0 status=0> * <Variable name=car_oriented_parents>)) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_car_centric_attitude_male value=0.0 status=0> * <Variable name=male>)) + (<Beta name=struct_car_centric_attitude_used_to_go_to_school_by_car value=0.0 status=0> * <Variable name=used_to_go_to_school_by_car>)) + (exp(<Beta name=struct_car_centric_attitude_sigma_log value=2.302585092994046 status=0>) * <Draws name=struct_car_centric_attitude_draws>)))) + (<Beta name=measurement_coefficient_environmental_attitude_Envir01 value=0.0 status=0> * (((((((((<Numeric value=0.0> + <Beta name=struct_environmental_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_environmental_attitude_childSuburb value=0.0 status=0> * <Variable name=childSuburb>)) + (<Beta name=struct_environmental_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_environmental_attitude_city_center_as_kid value=0.0 status=0> * <Variable name=city_center_as_kid>)) + (<Beta name=struct_environmental_attitude_artisans value=0.0 status=0> * <Variable name=artisans>)) + (<Beta name=struct_environmental_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_environmental_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_environmental_attitude_single value=0.0 status=0> * <Variable name=single>)) + (exp(<Beta name=struct_environmental_attitude_sigma_log value=2.302585092994046 status=0>) * <Draws name=struct_environmental_attitude_draws>)))) / exp(<Beta name=measurement_Envir01_sigma_log value=2.302585092994046 status=0>)), [(UnaryMinus((exp(<Beta name=likert_delta_0_log value=-0.86 status=0>) + exp(<Beta name=likert_delta_1_log value=-0.43 status=0>))) / exp(<Beta name=measurement_Envir01_sigma_log value=2.302585092994046 status=0>)), (UnaryMinus(exp(<Beta name=likert_delta_0_log value=-0.86 status=0>)) / exp(<Beta name=measurement_Envir01_sigma_log value=2.302585092994046 status=0>)), (exp(<Beta name=likert_delta_0_log value=-0.86 status=0>) / exp(<Beta name=measurement_Envir01_sigma_log value=2.302585092994046 status=0>)), ((exp(<Beta name=likert_delta_0_log value=-0.86 status=0>) + exp(<Beta name=likert_delta_1_log value=-0.43 status=0>)) / exp(<Beta name=measurement_Envir01_sigma_log value=2.302585092994046 status=0>))])
- generated Python file: b01_generated_files/case_1_ml_plus_log_exp_positivity_plus_empty_plan.py
- generated LaTeX file: b01_generated_files/case_1_ml_plus_log_exp_positivity_plus_empty_plan.tex
- generated HTML file: b01_generated_files/case_1_ml_plus_log_exp_positivity_plus_empty_plan.html
- normalization rules: none
- warnings:
  * No obvious reference indicator could be inferred for latent variable 'car_centric_attitude'.
  * No obvious reference indicator could be inferred for latent variable 'environmental_attitude'.

============================================================
CASE 2: ML + log/exp positivity + EXAMPLE plan
============================================================
- estimation mode: maximum_likelihood
- positivity mode: log_exp
- latent variables: 2
- indicators: 14
- ordinal threshold systems: 1
- measurement models present: ['ordered_probit']
- resolved parameters: 66
- structural expressions: ['car_centric_attitude', 'environmental_attitude']
- threshold systems: ['likert']
- measurement terms: 14 indicators
  example term (Envir01): OrderedProbit(((((<Numeric value=0.0> + <Numeric value=0.0>) + (<Numeric value=-1.0> * (((((((((((<Numeric value=0.0> + <Beta name=struct_car_centric_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_top_manager value=0.0 status=0> * <Variable name=top_manager>)) + (<Beta name=struct_car_centric_attitude_age_30_less value=0.0 status=0> * <Variable name=age_30_less>)) + (<Beta name=struct_car_centric_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_car_centric_attitude_car_oriented_parents value=0.0 status=0> * <Variable name=car_oriented_parents>)) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_car_centric_attitude_male value=0.0 status=0> * <Variable name=male>)) + (<Beta name=struct_car_centric_attitude_used_to_go_to_school_by_car value=0.0 status=0> * <Variable name=used_to_go_to_school_by_car>)) + (exp(<Beta name=struct_car_centric_attitude_sigma_log value=2.302585092994046 status=0>) * <Draws name=struct_car_centric_attitude_draws>)))) + (<Beta name=measurement_coefficient_environmental_attitude_Envir01 value=0.0 status=0> * (((((((((<Numeric value=0.0> + <Beta name=struct_environmental_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_environmental_attitude_childSuburb value=0.0 status=0> * <Variable name=childSuburb>)) + (<Beta name=struct_environmental_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_environmental_attitude_city_center_as_kid value=0.0 status=0> * <Variable name=city_center_as_kid>)) + (<Beta name=struct_environmental_attitude_artisans value=0.0 status=0> * <Variable name=artisans>)) + (<Beta name=struct_environmental_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_environmental_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_environmental_attitude_single value=0.0 status=0> * <Variable name=single>)) + (exp(<Beta name=struct_environmental_attitude_sigma_log value=2.302585092994046 status=0>) * <Draws name=struct_environmental_attitude_draws>)))) / <Numeric value=1.0>), [(UnaryMinus((exp(<Beta name=likert_delta_0_log value=-0.86 status=0>) + exp(<Beta name=likert_delta_1_log value=-0.43 status=0>))) / <Numeric value=1.0>), (UnaryMinus(exp(<Beta name=likert_delta_0_log value=-0.86 status=0>)) / <Numeric value=1.0>), (exp(<Beta name=likert_delta_0_log value=-0.86 status=0>) / <Numeric value=1.0>), ((exp(<Beta name=likert_delta_0_log value=-0.86 status=0>) + exp(<Beta name=likert_delta_1_log value=-0.43 status=0>)) / <Numeric value=1.0>)])
- generated Python file: b01_generated_files/case_2_ml_plus_log_exp_positivity_plus_example_plan.py
- generated LaTeX file: b01_generated_files/case_2_ml_plus_log_exp_positivity_plus_example_plan.tex
- generated HTML file: b01_generated_files/case_2_ml_plus_log_exp_positivity_plus_example_plan.html
- normalization rules:
  * MeasurementIntercept(indicator_name='Envir01') = 0.0 (reference indicator (location))
  * MeasurementIntercept(indicator_name='Envir02') = 0.0 (reference indicator (location))
  * MeasurementLoading(latent_name='car_centric_attitude', indicator_name='Envir01') = -1.0 (reference indicator (scale))
  * MeasurementLoading(latent_name='environmental_attitude', indicator_name='Envir02') = 1.0 (reference indicator (scale))
  * MeasurementSigma(indicator_name='Envir01') = 1.0 (ordinal threshold scale)
- warnings: none

============================================================
CASE 3: ML + BOUNDED positivity + EMPTY plan (non-standard but illustrative)
============================================================
- estimation mode: maximum_likelihood
- positivity mode: lower_bound
- latent variables: 2
- indicators: 14
- ordinal threshold systems: 1
- measurement models present: ['ordered_probit']
- resolved parameters: 66
- structural expressions: ['car_centric_attitude', 'environmental_attitude']
- threshold systems: ['likert']
- measurement terms: 14 indicators
  example term (Envir01): OrderedProbit(((((<Numeric value=0.0> + <Beta name=measurement_intercept_Envir01 value=0.0 status=0>) + (<Beta name=measurement_coefficient_car_centric_attitude_Envir01 value=0.0 status=0> * (((((((((((<Numeric value=0.0> + <Beta name=struct_car_centric_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_top_manager value=0.0 status=0> * <Variable name=top_manager>)) + (<Beta name=struct_car_centric_attitude_age_30_less value=0.0 status=0> * <Variable name=age_30_less>)) + (<Beta name=struct_car_centric_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_car_centric_attitude_car_oriented_parents value=0.0 status=0> * <Variable name=car_oriented_parents>)) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_car_centric_attitude_male value=0.0 status=0> * <Variable name=male>)) + (<Beta name=struct_car_centric_attitude_used_to_go_to_school_by_car value=0.0 status=0> * <Variable name=used_to_go_to_school_by_car>)) + (<Beta name=struct_car_centric_attitude_sigma value=10.0 status=0> * <Draws name=struct_car_centric_attitude_draws>)))) + (<Beta name=measurement_coefficient_environmental_attitude_Envir01 value=0.0 status=0> * (((((((((<Numeric value=0.0> + <Beta name=struct_environmental_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_environmental_attitude_childSuburb value=0.0 status=0> * <Variable name=childSuburb>)) + (<Beta name=struct_environmental_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_environmental_attitude_city_center_as_kid value=0.0 status=0> * <Variable name=city_center_as_kid>)) + (<Beta name=struct_environmental_attitude_artisans value=0.0 status=0> * <Variable name=artisans>)) + (<Beta name=struct_environmental_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_environmental_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_environmental_attitude_single value=0.0 status=0> * <Variable name=single>)) + (<Beta name=struct_environmental_attitude_sigma value=10.0 status=0> * <Draws name=struct_environmental_attitude_draws>)))) / <Beta name=measurement_Envir01_sigma value=10.0 status=0>), [(UnaryMinus((<Beta name=likert_delta_0 value=1.0 status=0> + <Beta name=likert_delta_1 value=1.0 status=0>)) / <Beta name=measurement_Envir01_sigma value=10.0 status=0>), (UnaryMinus(<Beta name=likert_delta_0 value=1.0 status=0>) / <Beta name=measurement_Envir01_sigma value=10.0 status=0>), (<Beta name=likert_delta_0 value=1.0 status=0> / <Beta name=measurement_Envir01_sigma value=10.0 status=0>), ((<Beta name=likert_delta_0 value=1.0 status=0> + <Beta name=likert_delta_1 value=1.0 status=0>) / <Beta name=measurement_Envir01_sigma value=10.0 status=0>)])
- generated Python file: b01_generated_files/case_3_ml_plus_bounded_positivity_plus_empty_plan_non_standard_but_illustrative.py
- generated LaTeX file: b01_generated_files/case_3_ml_plus_bounded_positivity_plus_empty_plan_non_standard_but_illustrative.tex
- generated HTML file: b01_generated_files/case_3_ml_plus_bounded_positivity_plus_empty_plan_non_standard_but_illustrative.html
- normalization rules: none
- warnings:
  * No obvious reference indicator could be inferred for latent variable 'car_centric_attitude'.
  * No obvious reference indicator could be inferred for latent variable 'environmental_attitude'.

============================================================
CASE 4: ML + BOUNDED positivity + EXAMPLE plan (non-standard but illustrative)
============================================================
- estimation mode: maximum_likelihood
- positivity mode: lower_bound
- latent variables: 2
- indicators: 14
- ordinal threshold systems: 1
- measurement models present: ['ordered_probit']
- resolved parameters: 66
- structural expressions: ['car_centric_attitude', 'environmental_attitude']
- threshold systems: ['likert']
- measurement terms: 14 indicators
  example term (Envir01): OrderedProbit(((((<Numeric value=0.0> + <Numeric value=0.0>) + (<Numeric value=-1.0> * (((((((((((<Numeric value=0.0> + <Beta name=struct_car_centric_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_top_manager value=0.0 status=0> * <Variable name=top_manager>)) + (<Beta name=struct_car_centric_attitude_age_30_less value=0.0 status=0> * <Variable name=age_30_less>)) + (<Beta name=struct_car_centric_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_car_centric_attitude_car_oriented_parents value=0.0 status=0> * <Variable name=car_oriented_parents>)) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_car_centric_attitude_male value=0.0 status=0> * <Variable name=male>)) + (<Beta name=struct_car_centric_attitude_used_to_go_to_school_by_car value=0.0 status=0> * <Variable name=used_to_go_to_school_by_car>)) + (<Beta name=struct_car_centric_attitude_sigma value=10.0 status=0> * <Draws name=struct_car_centric_attitude_draws>)))) + (<Beta name=measurement_coefficient_environmental_attitude_Envir01 value=0.0 status=0> * (((((((((<Numeric value=0.0> + <Beta name=struct_environmental_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_environmental_attitude_childSuburb value=0.0 status=0> * <Variable name=childSuburb>)) + (<Beta name=struct_environmental_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_environmental_attitude_city_center_as_kid value=0.0 status=0> * <Variable name=city_center_as_kid>)) + (<Beta name=struct_environmental_attitude_artisans value=0.0 status=0> * <Variable name=artisans>)) + (<Beta name=struct_environmental_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_environmental_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_environmental_attitude_single value=0.0 status=0> * <Variable name=single>)) + (<Beta name=struct_environmental_attitude_sigma value=10.0 status=0> * <Draws name=struct_environmental_attitude_draws>)))) / <Numeric value=1.0>), [(UnaryMinus((<Beta name=likert_delta_0 value=1.0 status=0> + <Beta name=likert_delta_1 value=1.0 status=0>)) / <Numeric value=1.0>), (UnaryMinus(<Beta name=likert_delta_0 value=1.0 status=0>) / <Numeric value=1.0>), (<Beta name=likert_delta_0 value=1.0 status=0> / <Numeric value=1.0>), ((<Beta name=likert_delta_0 value=1.0 status=0> + <Beta name=likert_delta_1 value=1.0 status=0>) / <Numeric value=1.0>)])
- generated Python file: b01_generated_files/case_4_ml_plus_bounded_positivity_plus_example_plan_non_standard_but_illustrative.py
- generated LaTeX file: b01_generated_files/case_4_ml_plus_bounded_positivity_plus_example_plan_non_standard_but_illustrative.tex
- generated HTML file: b01_generated_files/case_4_ml_plus_bounded_positivity_plus_example_plan_non_standard_but_illustrative.html
- normalization rules:
  * MeasurementIntercept(indicator_name='Envir01') = 0.0 (reference indicator (location))
  * MeasurementIntercept(indicator_name='Envir02') = 0.0 (reference indicator (location))
  * MeasurementLoading(latent_name='car_centric_attitude', indicator_name='Envir01') = -1.0 (reference indicator (scale))
  * MeasurementLoading(latent_name='environmental_attitude', indicator_name='Envir02') = 1.0 (reference indicator (scale))
  * MeasurementSigma(indicator_name='Envir01') = 1.0 (ordinal threshold scale)
- warnings: none

============================================================
CASE 5: BAYESIAN + BOUNDED positivity + EMPTY plan
============================================================
- estimation mode: bayesian
- positivity mode: lower_bound
- latent variables: 2
- indicators: 14
- ordinal threshold systems: 1
- measurement models present: ['ordered_probit']
- resolved parameters: 66
- structural expressions: ['car_centric_attitude', 'environmental_attitude']
- threshold systems: ['likert']
- measurement terms: 14 indicators
  example term (Envir01): OrderedProbit(((((<Numeric value=0.0> + <Beta name=measurement_intercept_Envir01 value=0.0 status=0>) + (<Beta name=measurement_coefficient_car_centric_attitude_Envir01 value=0.0 status=0> * DistributedParameter(car_centric_attitude, (((((((((((`0.0` + Beta('struct_car_centric_attitude_intercept', 0.0, None, None, 0)) + (Beta('struct_car_centric_attitude_high_education', 0.0, None, None, 0) * high_education)) + (Beta('struct_car_centric_attitude_top_manager', 0.0, None, None, 0) * top_manager)) + (Beta('struct_car_centric_attitude_age_30_less', 0.0, None, None, 0) * age_30_less)) + (Beta('struct_car_centric_attitude_ScaledIncome', 0.0, None, None, 0) * ScaledIncome)) + (Beta('struct_car_centric_attitude_car_oriented_parents', 0.0, None, None, 0) * car_oriented_parents)) + (Beta('struct_car_centric_attitude_high_education', 0.0, None, None, 0) * high_education)) + (Beta('struct_car_centric_attitude_low_education', 0.0, None, None, 0) * low_education)) + (Beta('struct_car_centric_attitude_male', 0.0, None, None, 0) * male)) + (Beta('struct_car_centric_attitude_used_to_go_to_school_by_car', 0.0, None, None, 0) * used_to_go_to_school_by_car)) + (Beta('struct_car_centric_attitude_sigma', 10.0, 1e-15, None, 0) * Draws("struct_car_centric_attitude_draws", "Normal"))), (((((((((((<Numeric value=0.0> + <Beta name=struct_car_centric_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_top_manager value=0.0 status=0> * <Variable name=top_manager>)) + (<Beta name=struct_car_centric_attitude_age_30_less value=0.0 status=0> * <Variable name=age_30_less>)) + (<Beta name=struct_car_centric_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_car_centric_attitude_car_oriented_parents value=0.0 status=0> * <Variable name=car_oriented_parents>)) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_car_centric_attitude_male value=0.0 status=0> * <Variable name=male>)) + (<Beta name=struct_car_centric_attitude_used_to_go_to_school_by_car value=0.0 status=0> * <Variable name=used_to_go_to_school_by_car>)) + (<Beta name=struct_car_centric_attitude_sigma value=10.0 status=0> * <Draws name=struct_car_centric_attitude_draws>))))) + (<Beta name=measurement_coefficient_environmental_attitude_Envir01 value=0.0 status=0> * DistributedParameter(environmental_attitude, (((((((((`0.0` + Beta('struct_environmental_attitude_intercept', 0.0, None, None, 0)) + (Beta('struct_environmental_attitude_childSuburb', 0.0, None, None, 0) * childSuburb)) + (Beta('struct_environmental_attitude_ScaledIncome', 0.0, None, None, 0) * ScaledIncome)) + (Beta('struct_environmental_attitude_city_center_as_kid', 0.0, None, None, 0) * city_center_as_kid)) + (Beta('struct_environmental_attitude_artisans', 0.0, None, None, 0) * artisans)) + (Beta('struct_environmental_attitude_high_education', 0.0, None, None, 0) * high_education)) + (Beta('struct_environmental_attitude_low_education', 0.0, None, None, 0) * low_education)) + (Beta('struct_environmental_attitude_single', 0.0, None, None, 0) * single)) + (Beta('struct_environmental_attitude_sigma', 10.0, 1e-15, None, 0) * Draws("struct_environmental_attitude_draws", "Normal"))), (((((((((<Numeric value=0.0> + <Beta name=struct_environmental_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_environmental_attitude_childSuburb value=0.0 status=0> * <Variable name=childSuburb>)) + (<Beta name=struct_environmental_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_environmental_attitude_city_center_as_kid value=0.0 status=0> * <Variable name=city_center_as_kid>)) + (<Beta name=struct_environmental_attitude_artisans value=0.0 status=0> * <Variable name=artisans>)) + (<Beta name=struct_environmental_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_environmental_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_environmental_attitude_single value=0.0 status=0> * <Variable name=single>)) + (<Beta name=struct_environmental_attitude_sigma value=10.0 status=0> * <Draws name=struct_environmental_attitude_draws>))))) / <Beta name=measurement_Envir01_sigma value=10.0 status=0>), [(UnaryMinus((<Beta name=likert_delta_0 value=1.0 status=0> + <Beta name=likert_delta_1 value=1.0 status=0>)) / <Beta name=measurement_Envir01_sigma value=10.0 status=0>), (UnaryMinus(<Beta name=likert_delta_0 value=1.0 status=0>) / <Beta name=measurement_Envir01_sigma value=10.0 status=0>), (<Beta name=likert_delta_0 value=1.0 status=0> / <Beta name=measurement_Envir01_sigma value=10.0 status=0>), ((<Beta name=likert_delta_0 value=1.0 status=0> + <Beta name=likert_delta_1 value=1.0 status=0>) / <Beta name=measurement_Envir01_sigma value=10.0 status=0>)])
- generated Python file: b01_generated_files/case_5_bayesian_plus_bounded_positivity_plus_empty_plan.py
- generated LaTeX file: b01_generated_files/case_5_bayesian_plus_bounded_positivity_plus_empty_plan.tex
- generated HTML file: b01_generated_files/case_5_bayesian_plus_bounded_positivity_plus_empty_plan.html
- normalization rules: none
- warnings:
  * No obvious reference indicator could be inferred for latent variable 'car_centric_attitude'.
  * No obvious reference indicator could be inferred for latent variable 'environmental_attitude'.

============================================================
CASE 6: BAYESIAN + BOUNDED positivity + EXAMPLE plan
============================================================
- estimation mode: bayesian
- positivity mode: lower_bound
- latent variables: 2
- indicators: 14
- ordinal threshold systems: 1
- measurement models present: ['ordered_probit']
- resolved parameters: 66
- structural expressions: ['car_centric_attitude', 'environmental_attitude']
- threshold systems: ['likert']
- measurement terms: 14 indicators
  example term (Envir01): OrderedProbit(((((<Numeric value=0.0> + <Numeric value=0.0>) + (<Numeric value=-1.0> * DistributedParameter(car_centric_attitude, (((((((((((`0.0` + Beta('struct_car_centric_attitude_intercept', 0.0, None, None, 0)) + (Beta('struct_car_centric_attitude_high_education', 0.0, None, None, 0) * high_education)) + (Beta('struct_car_centric_attitude_top_manager', 0.0, None, None, 0) * top_manager)) + (Beta('struct_car_centric_attitude_age_30_less', 0.0, None, None, 0) * age_30_less)) + (Beta('struct_car_centric_attitude_ScaledIncome', 0.0, None, None, 0) * ScaledIncome)) + (Beta('struct_car_centric_attitude_car_oriented_parents', 0.0, None, None, 0) * car_oriented_parents)) + (Beta('struct_car_centric_attitude_high_education', 0.0, None, None, 0) * high_education)) + (Beta('struct_car_centric_attitude_low_education', 0.0, None, None, 0) * low_education)) + (Beta('struct_car_centric_attitude_male', 0.0, None, None, 0) * male)) + (Beta('struct_car_centric_attitude_used_to_go_to_school_by_car', 0.0, None, None, 0) * used_to_go_to_school_by_car)) + (Beta('struct_car_centric_attitude_sigma', 10.0, 1e-15, None, 0) * Draws("struct_car_centric_attitude_draws", "Normal"))), (((((((((((<Numeric value=0.0> + <Beta name=struct_car_centric_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_top_manager value=0.0 status=0> * <Variable name=top_manager>)) + (<Beta name=struct_car_centric_attitude_age_30_less value=0.0 status=0> * <Variable name=age_30_less>)) + (<Beta name=struct_car_centric_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_car_centric_attitude_car_oriented_parents value=0.0 status=0> * <Variable name=car_oriented_parents>)) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_car_centric_attitude_male value=0.0 status=0> * <Variable name=male>)) + (<Beta name=struct_car_centric_attitude_used_to_go_to_school_by_car value=0.0 status=0> * <Variable name=used_to_go_to_school_by_car>)) + (<Beta name=struct_car_centric_attitude_sigma value=10.0 status=0> * <Draws name=struct_car_centric_attitude_draws>))))) + (<Beta name=measurement_coefficient_environmental_attitude_Envir01 value=0.0 status=0> * DistributedParameter(environmental_attitude, (((((((((`0.0` + Beta('struct_environmental_attitude_intercept', 0.0, None, None, 0)) + (Beta('struct_environmental_attitude_childSuburb', 0.0, None, None, 0) * childSuburb)) + (Beta('struct_environmental_attitude_ScaledIncome', 0.0, None, None, 0) * ScaledIncome)) + (Beta('struct_environmental_attitude_city_center_as_kid', 0.0, None, None, 0) * city_center_as_kid)) + (Beta('struct_environmental_attitude_artisans', 0.0, None, None, 0) * artisans)) + (Beta('struct_environmental_attitude_high_education', 0.0, None, None, 0) * high_education)) + (Beta('struct_environmental_attitude_low_education', 0.0, None, None, 0) * low_education)) + (Beta('struct_environmental_attitude_single', 0.0, None, None, 0) * single)) + (Beta('struct_environmental_attitude_sigma', 10.0, 1e-15, None, 0) * Draws("struct_environmental_attitude_draws", "Normal"))), (((((((((<Numeric value=0.0> + <Beta name=struct_environmental_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_environmental_attitude_childSuburb value=0.0 status=0> * <Variable name=childSuburb>)) + (<Beta name=struct_environmental_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_environmental_attitude_city_center_as_kid value=0.0 status=0> * <Variable name=city_center_as_kid>)) + (<Beta name=struct_environmental_attitude_artisans value=0.0 status=0> * <Variable name=artisans>)) + (<Beta name=struct_environmental_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_environmental_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_environmental_attitude_single value=0.0 status=0> * <Variable name=single>)) + (<Beta name=struct_environmental_attitude_sigma value=10.0 status=0> * <Draws name=struct_environmental_attitude_draws>))))) / <Numeric value=1.0>), [(UnaryMinus((<Beta name=likert_delta_0 value=1.0 status=0> + <Beta name=likert_delta_1 value=1.0 status=0>)) / <Numeric value=1.0>), (UnaryMinus(<Beta name=likert_delta_0 value=1.0 status=0>) / <Numeric value=1.0>), (<Beta name=likert_delta_0 value=1.0 status=0> / <Numeric value=1.0>), ((<Beta name=likert_delta_0 value=1.0 status=0> + <Beta name=likert_delta_1 value=1.0 status=0>) / <Numeric value=1.0>)])
- generated Python file: b01_generated_files/case_6_bayesian_plus_bounded_positivity_plus_example_plan.py
- generated LaTeX file: b01_generated_files/case_6_bayesian_plus_bounded_positivity_plus_example_plan.tex
- generated HTML file: b01_generated_files/case_6_bayesian_plus_bounded_positivity_plus_example_plan.html
- normalization rules:
  * MeasurementIntercept(indicator_name='Envir01') = 0.0 (reference indicator (location))
  * MeasurementIntercept(indicator_name='Envir02') = 0.0 (reference indicator (location))
  * MeasurementLoading(latent_name='car_centric_attitude', indicator_name='Envir01') = -1.0 (reference indicator (scale))
  * MeasurementLoading(latent_name='environmental_attitude', indicator_name='Envir02') = 1.0 (reference indicator (scale))
  * MeasurementSigma(indicator_name='Envir01') = 1.0 (ordinal threshold scale)
- warnings: none

============================================================
CASE 7: BAYESIAN + log/exp positivity + EMPTY plan (non-standard but illustrative)
============================================================
- estimation mode: bayesian
- positivity mode: log_exp
- latent variables: 2
- indicators: 14
- ordinal threshold systems: 1
- measurement models present: ['ordered_probit']
- resolved parameters: 66
- structural expressions: ['car_centric_attitude', 'environmental_attitude']
- threshold systems: ['likert']
- measurement terms: 14 indicators
  example term (Envir01): OrderedProbit(((((<Numeric value=0.0> + <Beta name=measurement_intercept_Envir01 value=0.0 status=0>) + (<Beta name=measurement_coefficient_car_centric_attitude_Envir01 value=0.0 status=0> * DistributedParameter(car_centric_attitude, (((((((((((`0.0` + Beta('struct_car_centric_attitude_intercept', 0.0, None, None, 0)) + (Beta('struct_car_centric_attitude_high_education', 0.0, None, None, 0) * high_education)) + (Beta('struct_car_centric_attitude_top_manager', 0.0, None, None, 0) * top_manager)) + (Beta('struct_car_centric_attitude_age_30_less', 0.0, None, None, 0) * age_30_less)) + (Beta('struct_car_centric_attitude_ScaledIncome', 0.0, None, None, 0) * ScaledIncome)) + (Beta('struct_car_centric_attitude_car_oriented_parents', 0.0, None, None, 0) * car_oriented_parents)) + (Beta('struct_car_centric_attitude_high_education', 0.0, None, None, 0) * high_education)) + (Beta('struct_car_centric_attitude_low_education', 0.0, None, None, 0) * low_education)) + (Beta('struct_car_centric_attitude_male', 0.0, None, None, 0) * male)) + (Beta('struct_car_centric_attitude_used_to_go_to_school_by_car', 0.0, None, None, 0) * used_to_go_to_school_by_car)) + (exp(Beta('struct_car_centric_attitude_sigma_log', 2.302585092994046, None, None, 0)) * Draws("struct_car_centric_attitude_draws", "Normal"))), (((((((((((<Numeric value=0.0> + <Beta name=struct_car_centric_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_top_manager value=0.0 status=0> * <Variable name=top_manager>)) + (<Beta name=struct_car_centric_attitude_age_30_less value=0.0 status=0> * <Variable name=age_30_less>)) + (<Beta name=struct_car_centric_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_car_centric_attitude_car_oriented_parents value=0.0 status=0> * <Variable name=car_oriented_parents>)) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_car_centric_attitude_male value=0.0 status=0> * <Variable name=male>)) + (<Beta name=struct_car_centric_attitude_used_to_go_to_school_by_car value=0.0 status=0> * <Variable name=used_to_go_to_school_by_car>)) + (exp(<Beta name=struct_car_centric_attitude_sigma_log value=2.302585092994046 status=0>) * <Draws name=struct_car_centric_attitude_draws>))))) + (<Beta name=measurement_coefficient_environmental_attitude_Envir01 value=0.0 status=0> * DistributedParameter(environmental_attitude, (((((((((`0.0` + Beta('struct_environmental_attitude_intercept', 0.0, None, None, 0)) + (Beta('struct_environmental_attitude_childSuburb', 0.0, None, None, 0) * childSuburb)) + (Beta('struct_environmental_attitude_ScaledIncome', 0.0, None, None, 0) * ScaledIncome)) + (Beta('struct_environmental_attitude_city_center_as_kid', 0.0, None, None, 0) * city_center_as_kid)) + (Beta('struct_environmental_attitude_artisans', 0.0, None, None, 0) * artisans)) + (Beta('struct_environmental_attitude_high_education', 0.0, None, None, 0) * high_education)) + (Beta('struct_environmental_attitude_low_education', 0.0, None, None, 0) * low_education)) + (Beta('struct_environmental_attitude_single', 0.0, None, None, 0) * single)) + (exp(Beta('struct_environmental_attitude_sigma_log', 2.302585092994046, None, None, 0)) * Draws("struct_environmental_attitude_draws", "Normal"))), (((((((((<Numeric value=0.0> + <Beta name=struct_environmental_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_environmental_attitude_childSuburb value=0.0 status=0> * <Variable name=childSuburb>)) + (<Beta name=struct_environmental_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_environmental_attitude_city_center_as_kid value=0.0 status=0> * <Variable name=city_center_as_kid>)) + (<Beta name=struct_environmental_attitude_artisans value=0.0 status=0> * <Variable name=artisans>)) + (<Beta name=struct_environmental_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_environmental_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_environmental_attitude_single value=0.0 status=0> * <Variable name=single>)) + (exp(<Beta name=struct_environmental_attitude_sigma_log value=2.302585092994046 status=0>) * <Draws name=struct_environmental_attitude_draws>))))) / exp(<Beta name=measurement_Envir01_sigma_log value=2.302585092994046 status=0>)), [(UnaryMinus((exp(<Beta name=likert_delta_0_log value=-0.86 status=0>) + exp(<Beta name=likert_delta_1_log value=-0.43 status=0>))) / exp(<Beta name=measurement_Envir01_sigma_log value=2.302585092994046 status=0>)), (UnaryMinus(exp(<Beta name=likert_delta_0_log value=-0.86 status=0>)) / exp(<Beta name=measurement_Envir01_sigma_log value=2.302585092994046 status=0>)), (exp(<Beta name=likert_delta_0_log value=-0.86 status=0>) / exp(<Beta name=measurement_Envir01_sigma_log value=2.302585092994046 status=0>)), ((exp(<Beta name=likert_delta_0_log value=-0.86 status=0>) + exp(<Beta name=likert_delta_1_log value=-0.43 status=0>)) / exp(<Beta name=measurement_Envir01_sigma_log value=2.302585092994046 status=0>))])
- generated Python file: b01_generated_files/case_7_bayesian_plus_log_exp_positivity_plus_empty_plan_non_standard_but_illustrative.py
- generated LaTeX file: b01_generated_files/case_7_bayesian_plus_log_exp_positivity_plus_empty_plan_non_standard_but_illustrative.tex
- generated HTML file: b01_generated_files/case_7_bayesian_plus_log_exp_positivity_plus_empty_plan_non_standard_but_illustrative.html
- normalization rules: none
- warnings:
  * No obvious reference indicator could be inferred for latent variable 'car_centric_attitude'.
  * No obvious reference indicator could be inferred for latent variable 'environmental_attitude'.

============================================================
CASE 8: BAYESIAN + log/exp positivity + EXAMPLE plan (non-standard but illustrative)
============================================================
- estimation mode: bayesian
- positivity mode: log_exp
- latent variables: 2
- indicators: 14
- ordinal threshold systems: 1
- measurement models present: ['ordered_probit']
- resolved parameters: 66
- structural expressions: ['car_centric_attitude', 'environmental_attitude']
- threshold systems: ['likert']
- measurement terms: 14 indicators
  example term (Envir01): OrderedProbit(((((<Numeric value=0.0> + <Numeric value=0.0>) + (<Numeric value=-1.0> * DistributedParameter(car_centric_attitude, (((((((((((`0.0` + Beta('struct_car_centric_attitude_intercept', 0.0, None, None, 0)) + (Beta('struct_car_centric_attitude_high_education', 0.0, None, None, 0) * high_education)) + (Beta('struct_car_centric_attitude_top_manager', 0.0, None, None, 0) * top_manager)) + (Beta('struct_car_centric_attitude_age_30_less', 0.0, None, None, 0) * age_30_less)) + (Beta('struct_car_centric_attitude_ScaledIncome', 0.0, None, None, 0) * ScaledIncome)) + (Beta('struct_car_centric_attitude_car_oriented_parents', 0.0, None, None, 0) * car_oriented_parents)) + (Beta('struct_car_centric_attitude_high_education', 0.0, None, None, 0) * high_education)) + (Beta('struct_car_centric_attitude_low_education', 0.0, None, None, 0) * low_education)) + (Beta('struct_car_centric_attitude_male', 0.0, None, None, 0) * male)) + (Beta('struct_car_centric_attitude_used_to_go_to_school_by_car', 0.0, None, None, 0) * used_to_go_to_school_by_car)) + (exp(Beta('struct_car_centric_attitude_sigma_log', 2.302585092994046, None, None, 0)) * Draws("struct_car_centric_attitude_draws", "Normal"))), (((((((((((<Numeric value=0.0> + <Beta name=struct_car_centric_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_top_manager value=0.0 status=0> * <Variable name=top_manager>)) + (<Beta name=struct_car_centric_attitude_age_30_less value=0.0 status=0> * <Variable name=age_30_less>)) + (<Beta name=struct_car_centric_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_car_centric_attitude_car_oriented_parents value=0.0 status=0> * <Variable name=car_oriented_parents>)) + (<Beta name=struct_car_centric_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_car_centric_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_car_centric_attitude_male value=0.0 status=0> * <Variable name=male>)) + (<Beta name=struct_car_centric_attitude_used_to_go_to_school_by_car value=0.0 status=0> * <Variable name=used_to_go_to_school_by_car>)) + (exp(<Beta name=struct_car_centric_attitude_sigma_log value=2.302585092994046 status=0>) * <Draws name=struct_car_centric_attitude_draws>))))) + (<Beta name=measurement_coefficient_environmental_attitude_Envir01 value=0.0 status=0> * DistributedParameter(environmental_attitude, (((((((((`0.0` + Beta('struct_environmental_attitude_intercept', 0.0, None, None, 0)) + (Beta('struct_environmental_attitude_childSuburb', 0.0, None, None, 0) * childSuburb)) + (Beta('struct_environmental_attitude_ScaledIncome', 0.0, None, None, 0) * ScaledIncome)) + (Beta('struct_environmental_attitude_city_center_as_kid', 0.0, None, None, 0) * city_center_as_kid)) + (Beta('struct_environmental_attitude_artisans', 0.0, None, None, 0) * artisans)) + (Beta('struct_environmental_attitude_high_education', 0.0, None, None, 0) * high_education)) + (Beta('struct_environmental_attitude_low_education', 0.0, None, None, 0) * low_education)) + (Beta('struct_environmental_attitude_single', 0.0, None, None, 0) * single)) + (exp(Beta('struct_environmental_attitude_sigma_log', 2.302585092994046, None, None, 0)) * Draws("struct_environmental_attitude_draws", "Normal"))), (((((((((<Numeric value=0.0> + <Beta name=struct_environmental_attitude_intercept value=0.0 status=0>) + (<Beta name=struct_environmental_attitude_childSuburb value=0.0 status=0> * <Variable name=childSuburb>)) + (<Beta name=struct_environmental_attitude_ScaledIncome value=0.0 status=0> * <Variable name=ScaledIncome>)) + (<Beta name=struct_environmental_attitude_city_center_as_kid value=0.0 status=0> * <Variable name=city_center_as_kid>)) + (<Beta name=struct_environmental_attitude_artisans value=0.0 status=0> * <Variable name=artisans>)) + (<Beta name=struct_environmental_attitude_high_education value=0.0 status=0> * <Variable name=high_education>)) + (<Beta name=struct_environmental_attitude_low_education value=0.0 status=0> * <Variable name=low_education>)) + (<Beta name=struct_environmental_attitude_single value=0.0 status=0> * <Variable name=single>)) + (exp(<Beta name=struct_environmental_attitude_sigma_log value=2.302585092994046 status=0>) * <Draws name=struct_environmental_attitude_draws>))))) / <Numeric value=1.0>), [(UnaryMinus((exp(<Beta name=likert_delta_0_log value=-0.86 status=0>) + exp(<Beta name=likert_delta_1_log value=-0.43 status=0>))) / <Numeric value=1.0>), (UnaryMinus(exp(<Beta name=likert_delta_0_log value=-0.86 status=0>)) / <Numeric value=1.0>), (exp(<Beta name=likert_delta_0_log value=-0.86 status=0>) / <Numeric value=1.0>), ((exp(<Beta name=likert_delta_0_log value=-0.86 status=0>) + exp(<Beta name=likert_delta_1_log value=-0.43 status=0>)) / <Numeric value=1.0>)])
- generated Python file: b01_generated_files/case_8_bayesian_plus_log_exp_positivity_plus_example_plan_non_standard_but_illustrative.py
- generated LaTeX file: b01_generated_files/case_8_bayesian_plus_log_exp_positivity_plus_example_plan_non_standard_but_illustrative.tex
- generated HTML file: b01_generated_files/case_8_bayesian_plus_log_exp_positivity_plus_example_plan_non_standard_but_illustrative.html
- normalization rules:
  * MeasurementIntercept(indicator_name='Envir01') = 0.0 (reference indicator (location))
  * MeasurementIntercept(indicator_name='Envir02') = 0.0 (reference indicator (location))
  * MeasurementLoading(latent_name='car_centric_attitude', indicator_name='Envir01') = -1.0 (reference indicator (scale))
  * MeasurementLoading(latent_name='environmental_attitude', indicator_name='Envir02') = 1.0 (reference indicator (scale))
  * MeasurementSigma(indicator_name='Envir01') = 1.0 (ordinal threshold scale)
- warnings: none

====================
END OF SCRIPT
====================

from __future__ import annotations

from pathlib import Path

from likert_spec import likert_indicators, likert_types
from two_latent_variables_spec import latent_variables

from biogeme.latent_variables import (
    BuildContext,
    EstimationMode,
    Fixing,
    IndicatorMeasurementSpec,
    MeasurementConfiguration,
    MeasurementIntercept,
    MeasurementLoading,
    MeasurementModel,
    MeasurementSigma,
    NormalizationPlan,
    PositiveParameterSpec,
    PositivityMode,
    build_biogeme_model,
    generate_html_report,
    generate_latex_report,
    generate_python_code,
    resolve_model,
    save_html_report,
    save_latex_report,
    save_python_code,
)

OUTPUT_DIR = Path('b01_generated_files')
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

print('\n====================')
print('SPEC loaded')
print('====================')

print(f'- #latent_variables:  {len(latent_variables)}')
print(f'- #likert_indicators: {len(likert_indicators)}')
print(f'- #likert_types:      {len(likert_types)}')

# =============================================================================
# Homogeneous ordinal measurement configuration used by the resolver
# =============================================================================
DEFAULT_SIGMA_START = 10.0

measurement_configuration = MeasurementConfiguration(
    specifications=[
        IndicatorMeasurementSpec(
            indicator_name=indicator.name,
            measurement_model=MeasurementModel.ORDERED_PROBIT,
            measurement_sigma=PositiveParameterSpec(start=DEFAULT_SIGMA_START),
        )
        for indicator in likert_indicators
    ]
)

# =============================================================================
# Normalization plans used when resolving the specifications
# =============================================================================
# Plan A: empty plan. The resolver determines the required normalizations.
plan_empty = NormalizationPlan()

# Plan B: explicit plan. These fixings illustrate how expert-specified
# normalizations are passed to the resolver.
plan_example = NormalizationPlan()

# --- LV normalizations by reference-indicator strategy (examples)
# car_centric_attitude anchored on Envir01
plan_example.add(
    Fixing(MeasurementIntercept('Envir01'), 0.0, note='reference indicator (location)')
)
plan_example.add(
    Fixing(
        MeasurementLoading('car_centric_attitude', 'Envir01'),
        -1.0,
        note='reference indicator (scale)',
    )
)

# environmental_attitude anchored on Envir02
plan_example.add(
    Fixing(MeasurementIntercept('Envir02'), 0.0, note='reference indicator (location)')
)
plan_example.add(
    Fixing(
        MeasurementLoading('environmental_attitude', 'Envir02'),
        1.0,
        note='reference indicator (scale)',
    )
)

# --- Ordinal scale normalization (one per ordinal threshold system)
# With a single ordinal threshold system ("likert"), fix one measurement sigma.
plan_example.add(
    Fixing(MeasurementSigma('Envir01'), 1.0, note='ordinal threshold scale')
)

print('\n====================')
print('Normalization plans ready')
print('====================')
print('- plan_empty:   (no fixings)')
print('- plan_example: (some intercept/loadings/sigma fixed)')


# =============================================================================
# Helpers
# =============================================================================
def make_context(
    estimation_mode: EstimationMode,
    positivity_mode: PositivityMode,
) -> BuildContext:
    """Create a build context used to resolve and generate a specification."""
    base = BuildContext.default(estimation_mode)
    return BuildContext(
        estimation_mode=base.estimation_mode,
        draw_type=base.draw_type,
        positivity_mode=positivity_mode,
        naming=base.naming,
        ordinal_eps=base.ordinal_eps,
        ordinal_enforce_order=base.ordinal_enforce_order,
    )


def title_to_file_stem(title: str) -> str:
    """Convert a case title into a filesystem-friendly stem."""
    stem = title.lower()
    replacements = {
        ' ': '_',
        '/': '_',
        '+': 'plus',
        ':': '',
        ',': '',
        '(': '',
        ')': '',
        '-': '_',
    }
    for source, target in replacements.items():
        stem = stem.replace(source, target)
    while '__' in stem:
        stem = stem.replace('__', '_')
    return stem.strip('_')


def summarize_case(
    *, title: str, context: BuildContext, plan: NormalizationPlan
) -> None:
    """Resolve one specification, generate files, and print a compact summary.

    No estimation is performed. The generated Python, LaTeX, and HTML files are
    derived from the resolved specification.
    """
    print('\n' + '=' * 60)
    print(title)
    print('=' * 60)

    resolved = resolve_model(
        latent_variables=latent_variables,
        likert_indicators=likert_indicators,
        likert_types=likert_types,
        measurement_configuration=measurement_configuration,
        context=context,
        normalization_plan=plan,
    )
    built = build_biogeme_model(resolved)
    python_code = generate_python_code(resolved)
    latex_report = generate_latex_report(resolved)
    html_report = generate_html_report(resolved)

    stem = title_to_file_stem(title)
    python_path = OUTPUT_DIR / f'{stem}.py'
    latex_path = OUTPUT_DIR / f'{stem}.tex'
    html_path = OUTPUT_DIR / f'{stem}.html'

    save_python_code(python_code, python_path)
    save_latex_report(latex_report, latex_path)
    save_html_report(html_report, html_path)

    measurement_models = [
        model.value for model in resolved.metadata.measurement_models_present
    ]

    print(f'- estimation mode: {context.estimation_mode.value}')
    print(f'- positivity mode: {context.positivity_mode.value}')
    print(f'- latent variables: {resolved.metadata.n_latent_variables}')
    print(f'- indicators: {resolved.metadata.n_indicators}')
    print(f'- ordinal threshold systems: {resolved.metadata.n_threshold_systems}')
    print(f'- measurement models present: {measurement_models}')
    print(f'- resolved parameters: {len(resolved.parameters)}')
    print(f'- structural expressions: {list(built.latent_expressions.keys())}')
    print(f'- threshold systems: {list(built.threshold_expressions.keys())}')
    print(f'- measurement terms: {len(built.measurement_terms)} indicators')
    print(f'  example term (Envir01): {built.measurement_terms.get("Envir01")}')
    print(f'- generated Python file: {python_path.as_posix()}')
    print(f'- generated LaTeX file: {latex_path.as_posix()}')
    print(f'- generated HTML file: {html_path.as_posix()}')

    if resolved.normalization.rules:
        print('- normalization rules:')
        for rule in resolved.normalization.rules:
            print(f'  * {rule.target_name} = {rule.value} ({rule.reason})')
    else:
        print('- normalization rules: none')

    if resolved.normalization.warnings:
        print('- warnings:')
        for warning in resolved.normalization.warnings:
            print(f'  * {warning}')
    else:
        print('- warnings: none')


# =============================================================================
# Enumerate build contexts and normalization plans explicitly
# =============================================================================
# We generate files for 8 cases:
#   modes: maximum likelihood vs Bayesian,
#   positivity: log/exp vs lower-bound parameterization,
#   normalization plan: automatic vs explicit.
#
# Some combinations are not intended as recommended estimation settings. They
# are included to exercise the specification resolver and code generators.

# -------------------------------------------------------------------------
# CASE 1: ML + log/exp positivity + empty plan
# -------------------------------------------------------------------------
context_1 = make_context(
    EstimationMode.MAXIMUM_LIKELIHOOD,
    PositivityMode.LOG_EXP,
)
summarize_case(
    title='CASE 1: ML + log/exp positivity + EMPTY plan',
    context=context_1,
    plan=plan_empty,
)

# -------------------------------------------------------------------------
# CASE 2: ML + log/exp positivity + example plan
# -------------------------------------------------------------------------
context_2 = make_context(
    EstimationMode.MAXIMUM_LIKELIHOOD,
    PositivityMode.LOG_EXP,
)
summarize_case(
    title='CASE 2: ML + log/exp positivity + EXAMPLE plan',
    context=context_2,
    plan=plan_example,
)

# -------------------------------------------------------------------------
# CASE 3: ML + bounded positivity + empty plan
# -------------------------------------------------------------------------
context_3 = make_context(
    EstimationMode.MAXIMUM_LIKELIHOOD,
    PositivityMode.LOWER_BOUND,
)
summarize_case(
    title='CASE 3: ML + BOUNDED positivity + EMPTY plan (non-standard but illustrative)',
    context=context_3,
    plan=plan_empty,
)

# -------------------------------------------------------------------------
# CASE 4: ML + bounded positivity + example plan
# -------------------------------------------------------------------------
context_4 = make_context(
    EstimationMode.MAXIMUM_LIKELIHOOD,
    PositivityMode.LOWER_BOUND,
)
summarize_case(
    title='CASE 4: ML + BOUNDED positivity + EXAMPLE plan (non-standard but illustrative)',
    context=context_4,
    plan=plan_example,
)

# -------------------------------------------------------------------------
# CASE 5: Bayesian + bounded positivity + empty plan
# -------------------------------------------------------------------------
context_5 = make_context(
    EstimationMode.BAYESIAN,
    PositivityMode.LOWER_BOUND,
)
summarize_case(
    title='CASE 5: BAYESIAN + BOUNDED positivity + EMPTY plan',
    context=context_5,
    plan=plan_empty,
)

# -------------------------------------------------------------------------
# CASE 6: Bayesian + bounded positivity + example plan
# -------------------------------------------------------------------------
context_6 = make_context(
    EstimationMode.BAYESIAN,
    PositivityMode.LOWER_BOUND,
)
summarize_case(
    title='CASE 6: BAYESIAN + BOUNDED positivity + EXAMPLE plan',
    context=context_6,
    plan=plan_example,
)

# -------------------------------------------------------------------------
# CASE 7: Bayesian + log/exp positivity + empty plan
# -------------------------------------------------------------------------
context_7 = make_context(
    EstimationMode.BAYESIAN,
    PositivityMode.LOG_EXP,
)
summarize_case(
    title='CASE 7: BAYESIAN + log/exp positivity + EMPTY plan (non-standard but illustrative)',
    context=context_7,
    plan=plan_empty,
)

# -------------------------------------------------------------------------
# CASE 8: Bayesian + log/exp positivity + example plan
# -------------------------------------------------------------------------
context_8 = make_context(
    EstimationMode.BAYESIAN,
    PositivityMode.LOG_EXP,
)
summarize_case(
    title='CASE 8: BAYESIAN + log/exp positivity + EXAMPLE plan (non-standard but illustrative)',
    context=context_8,
    plan=plan_example,
)

print('\n====================')
print('END OF SCRIPT')
print('====================')

Total running time of the script: (0 minutes 0.058 seconds)

Gallery generated by Sphinx-Gallery