From ca4ffba84490a362623f5c11b2cd4c699b0b60ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Sass?= Date: Wed, 12 Oct 2022 12:30:30 +0200 Subject: [PATCH] Version 2.0.0a1 (#875) ## Big Changes * We redesigned the scenario class completely. The scenario is implemented as a dataclass now and holds only environment variables (like limitations or save directory). Everything else was moved to the components directly. * We removed runtime optimization completely (no adaptive capping or imputing anymore). * We removed the command-line interface and restructured everything alongside. Since SMAC was building upon the command-line interface (especially in combination with the scenario), it was complicated to understand the behavior or find specific implementations. With the removal, we re-wrote everything in python and re-implemented the feature of using scripts as target functions. * Introducing trials: Each config/seed/budget/instance calculation is a trial. * The configuration chooser is integrated into the SMBO object now. Therefore, SMBO finally implements an ask-tell interface now. * Facades are redesigned so that they accept instantiated components directly. If a component is not passed, a default component is used, which is specified for each facade individually in the form of static methods. You can use those static methods directly to adapt a component to your choice. * A lot of API changes and renamings (e.g., RandomConfigurationChooser -> RandomDesign, Runhistory2EPM -> RunHistoryEncoder). * Ambiguous variables are renamed and unified across files. * Dependencies of modules are reduced drastically. * We incorporated Pynisher 1.0, which ensures limitations cross-platform. * We incorporated ConfigSpace 0.6, which simplified our examples. * Examples and documentation are completely reworked. Examples use the new ConfigSpace, and the documentation is adapted to version 2.0. * Transparent target function signatures: SMAC checks now explicitly if an argument is available (the required arguments are now specified in the intensifier). If there are more arguments that are not passed by SMAC, a warning is raised. * Components implement a ``meta`` property now, all of which describe the initial state of SMAC. The facade collects all metadata and saves the initial state of the scenario. * Improved multi-objective in general: RunHistory (in addition to RunHistoryEncoder) both incorporates the multi-objective algorithm. In other words, if the multi-objective algorithm changes the output, it directly affects the optimization process. * Configspace is saved in json only * StatusType is saved as integer and not as dict anymore * We changed the behavior of continuing a run: * SMAC automatically checks if a scenario was saved earlier. If there exists a scenario and the initial state is the same, SMAC automatically loads the previous data. However, continuing from that run is not possible yet. * If there was a scenario earlier, but the initial state is different, then the user is asked to overwrite the run or to still continue the run although the state is different (Note that this only can happen if the name specified in the scenario is the same). Alternatively, an `old` to the old run is added (e.g., the name was test, it becomes test-old). * The initial state of the SMAC run also specifies the name (if no name in the scenario is specified). If the user changes something in the code base or in the scenario, the name and, therefore, the save location automatically changes. ## New Features * Added a new termination feature: Use `terminate_cost_threshold` in the scenario to stop the optimization after a configuration was evaluated with a cost lower than the threshold. * Callbacks are completely redesigned. Added callbacks to the facade are called in different positions in the Bayesian optimization loop. * The multi-objective algorithm `MeanAggregationStrategy` supports objective weights now. * RunHistory got more methods like ``get_incumbent`` or ``get_pareto_front``. ## Fixes * You ever noticed that the third configuration has no origin? It's fixed now. * We fixed ParEGO (it updates every time training is performed now). ## Optimization Changes * Changed initial design behavior * You can add additional configurations now. * ``max_ratio`` will limit both ``n_configs`` and ``n_configs_per_hyperparameter`` but not additional configurations * Reduced default ``max_ratio`` to 0.1. ## Code Related * Converted all unittests to pytests. * Instances, seeds, and budgets can be set to none now. However, mixing none and non-none will throw an exception. Co-authored-by: Carolin Benjamins Co-authored-by: dengdifan Co-authored-by: eddiebergman Co-authored-by: timruhkopf Co-authored-by: Katharina Eggensperger --- .github/workflows/dist.yml | 4 +- .github/workflows/docs.yml | 4 +- .github/workflows/examples.yml | 67 - .github/workflows/pre-commit.yml | 2 + .github/workflows/pytest.yml | 11 +- .gitignore | 6 +- MANIFEST.in | 8 +- README.md | 90 +- changelog.md | 52 +- docs/.DS_Store | Bin 6148 -> 6148 bytes .../installation.rst => 1_installation.rst} | 25 +- docs/2_package_overview.rst | 66 + docs/3_getting_started.rst | 140 + docs/4_minimal_example.rst | 33 + docs/5_api.rst | 21 + docs/6_references.rst | 22 + docs/7_glossary.rst | 98 + docs/{faq.rst => 8_faq.rst} | 34 +- docs/{license.rst => 9_license.rst} | 0 docs/Makefile | 15 +- docs/advanced_usage/10_continue.rst | 15 + docs/advanced_usage/11_reproducability.rst | 16 + docs/advanced_usage/1_components.rst | 242 + docs/advanced_usage/2_multi_fidelity.rst | 18 + docs/advanced_usage/3_multi_objective.rst | 45 + docs/advanced_usage/4_instances.rst | 45 + docs/advanced_usage/5_ask_and_tell.rst | 41 + docs/advanced_usage/6_commandline.rst | 92 + docs/advanced_usage/7_stopping_criteria.rst | 35 + docs/advanced_usage/8_logging.rst | 73 + docs/advanced_usage/9_parallelism.rst | 5 + docs/advanced_usage/index.rst | 27 + docs/api.rst | 19 - docs/conf.py | 18 +- docs/details/arguments.rst | 22 - docs/details/callbacks.rst | 23 - docs/details/cuda.rst | 5 - docs/details/facades.rst | 44 - docs/details/index.rst | 22 - docs/details/instances.rst | 47 - docs/details/multi_objective.rst | 31 - docs/details/parallelism.rst | 70 - docs/details/restoring.rst | 29 - docs/details/run_history.rst | 16 - docs/details/scenario.rst | 49 - docs/details/target_algorithm_evaluator.rst | 91 - docs/details/validation.rst | 70 - docs/getting_started/basic_usage.rst | 53 - docs/getting_started/index.rst | 13 - docs/getting_started/minimal_example.rst | 52 - docs/getting_started/package_overview.rst | 83 - docs/glossary.rst | 72 - docs/images/components.png | Bin 196579 -> 0 bytes docs/images/smac_facades_all_classes.png | Bin 326126 -> 0 bytes .../images/smac_facades_all_classes_withf.png | Bin 476451 -> 0 bytes docs/index.rst | 25 +- examples/1_basics/1_synthetic_function.py | 67 + examples/1_basics/2_svm_cv.py | 103 + examples/1_basics/3_ask_and_tell.py | 81 + examples/1_basics/4_callback.py | 84 + examples/1_basics/5_priors.py | 172 + examples/1_basics/README.rst | 2 + examples/2_multi_fidelity/1_mlp_epochs.py | 141 + examples/2_multi_fidelity/2_sgd_datasets.py | 139 + examples/2_multi_fidelity/README.rst | 3 + examples/3_multi_objective/1_schaffer.py | 88 + examples/3_multi_objective/2_parego.py | 176 + examples/3_multi_objective/README.rst | 3 + .../4_advanced_optimizer/1_turbo_optimizer.py | 110 + .../4_advanced_optimizer/2_boing_optimizer.py | 72 + .../1_call_target_function_script.py | 59 + examples/5_commandline/README.rst | 4 + examples/5_commandline/script.sh | 19 + examples/README.rst | 2 +- examples/commandline/README.rst | 2 - examples/commandline/branin.py | 65 - examples/commandline/branin/configspace.pcs | 2 - examples/commandline/branin/scenario.txt | 5 - examples/commandline/restore_branin.py | 95 - examples/commandline/spear_qcp.py | 22 - examples/commandline/spear_qcp/features.txt | 5 - examples/commandline/spear_qcp/instances.txt | 4 - .../spear_qcp/instances/qcplin2006.10218.cnf | 7673 --- .../spear_qcp/instances/qcplin2006.1031.cnf | 47943 ---------------- .../spear_qcp/instances/qcplin2006.10556.cnf | 5265 -- .../spear_qcp/instances/qcplin2006.10641.cnf | 23451 -------- examples/commandline/spear_qcp/scenario.txt | 10 - .../target_algorithm/runsolver/runsolver | Bin 124213 -> 0 bytes .../runsolver-3.3.4-patched/src/Changelog | 130 - .../src/CircularBufferFilter.hh | 218 - .../runsolver-3.3.4-patched/src/Cores.hh | 203 - .../src/CreateSyscallsNames.cc | 94 - .../src/LICENSE-GPL-3.0.txt | 674 - .../runsolver-3.3.4-patched/src/Makefile | 69 - .../runsolver-3.3.4-patched/src/Makefile.back | 69 - .../runsolver-3.3.4-patched/src/Observer.hh | 105 - .../src/ProcessData.hh | 550 - .../src/ProcessHistory.hh | 131 - .../src/ProcessList.hh | 66 - .../src/ProcessTree.hh | 711 - .../runsolver-3.3.4-patched/src/README | 25 - .../src/SignalNames.cc | 65 - .../runsolver-3.3.4-patched/src/SignalNames.d | 1 - .../src/SignalNames.hh | 27 - .../runsolver-3.3.4-patched/src/SignalNames.o | Bin 2856 -> 0 bytes .../src/SyscallNames.cc | 326 - .../src/SyscallNames.d | 1 - .../src/SyscallNames.hh | 8 - .../src/SyscallNames.o | Bin 14688 -> 0 bytes .../src/SyscallsTracer.hh | 1439 - .../src/TimeStamper.hh | 442 - .../runsolver-3.3.4-patched/src/aeatk.c | 157 - .../runsolver-3.3.4-patched/src/runsolver | Bin 124213 -> 0 bytes .../runsolver-3.3.4-patched/src/runsolver.cc | 2254 - .../runsolver-3.3.4-patched/src/runsolver.d | 3 - .../runsolver-3.3.4-patched/src/runsolver.o | Bin 176416 -> 0 bytes .../src/runsolver.spec | 53 - .../src/vlineSplitter.cc | 114 - .../spear_qcp/target_algorithm/scripts/SAT | Bin 1492405 -> 0 bytes .../scripts/SATCSSCWrapper.py | 344 - .../scripts/genericWrapper.py | 778 - .../scripts/generic_solver_wrapper.rb | 183 - .../target_algorithm/spear-python/README.md | 10 - .../spear-python/Spear-32_1.2.1 | Bin 1456308 -> 0 bytes .../spear-python/spear-params-mixed.pcs | 38 - .../spear-python/spear-params.pcs | 38 - .../spear-python/spearCSSCWrapper.py | 27 - examples/commandline/spear_qcp_roar.sh | 4 - examples/commandline/spear_qcp_smac.sh | 4 - examples/python/README.rst | 2 - examples/python/plot_gb_non_deterministic.py | 130 - examples/python/plot_mlp_mf.py | 182 - .../python/plot_scalarized_multi_objective.py | 204 - examples/python/plot_sgd_instances.py | 176 - .../python/plot_simple_multi_objective.py | 80 - examples/python/plot_svm_cv.py | 131 - examples/python/plot_svm_eips.py | 124 - examples/python/plot_synthetic_function.py | 84 - .../python/plot_synthetic_function_boing.py | 74 - .../plot_synthetic_function_parallel.py | 109 - .../python/plot_synthetic_function_turbo.py | 80 - examples/python/plot_user_prior_mlp.py | 166 - examples/python/spear_mf_instances.py | 48 - pyproject.toml | 5 +- scripts/check-deterministic.sh | 35 - scripts/plot_traj_perf.py | 145 - scripts/smac-validate.py | 141 - scripts/smac.py | 19 - scripts/test_no_files_left.sh | 10 - scripts/update_files.py | 76 + setup.py | 25 +- smac/__init__.py | 42 +- smac/{epm => acquisition}/__init__.py | 0 smac/acquisition/function/__init__.py | 22 + .../function/abstract_acquisition_function.py | 110 + smac/acquisition/function/confidence_bound.py | 77 + .../function/expected_improvement.py | 206 + .../integrated_acquisition_function.py | 84 + .../function/prior_acqusition_function.py | 226 + .../function/probability_improvement.py | 80 + smac/acquisition/function/thompson.py | 65 + smac/acquisition/maximizer/__init__.py | 19 + .../abstract_acqusition_maximizer.py | 160 + .../maximizer/differential_evolution.py | 55 + smac/acquisition/maximizer/helpers.py | 112 + .../maximizer/local_and_random_search.py | 251 + smac/acquisition/maximizer/local_search.py | 370 + smac/acquisition/maximizer/random_search.py | 40 + smac/callback.py | 64 + smac/callbacks.py | 26 - smac/configspace/__init__.py | 38 - smac/configspace/util.py | 25 - smac/constants.py | 9 + smac/epm/base_epm.py | 313 - smac/epm/base_imputor.py | 45 - smac/epm/gaussian_process/augmented.py | 496 - smac/epm/gaussian_process/gp.py | 316 - smac/epm/gaussian_process/gpytorch.py | 365 - smac/epm/gaussian_process/kernels/__init__.py | 763 - smac/epm/gaussian_process/kernels/boing.py | 560 - smac/epm/gaussian_process/utils/prior.py | 545 - smac/epm/multi_objective_epm.py | 183 - smac/epm/random_epm.py | 101 - smac/epm/random_forest/__init__.py | 72 - smac/epm/random_forest/rf_mo.py | 49 - smac/epm/random_forest/rf_with_instances.py | 333 - smac/epm/random_forest/rfr_imputator.py | 192 - smac/epm/utils.py | 255 - smac/facade/__init__.py | 19 + smac/facade/abstract_facade.py | 465 + smac/facade/algorithm_configuration_facade.py | 191 + smac/facade/blackbox_facade.py | 319 + smac/facade/boing_facade.py | 264 + smac/facade/experimental/hydra_facade.py | 305 - smac/facade/func_facade.py | 115 - smac/facade/hyperband_facade.py | 70 +- .../hyperparameter_optimization_facade.py | 220 + smac/facade/multi_fidelity_facade.py | 103 + smac/facade/psmac_facade.py | 348 - smac/facade/random_facade.py | 161 + smac/facade/roar_facade.py | 155 - smac/facade/smac_ac_facade.py | 833 - smac/facade/smac_bb_facade.py | 195 - smac/facade/smac_boing_facade.py | 111 - smac/facade/smac_hpo_facade.py | 93 - smac/facade/smac_mf_facade.py | 66 - smac/initial_design/__init__.py | 15 + .../initial_design/abstract_initial_design.py | 197 + .../default_configuration_design.py | 25 - smac/initial_design/default_design.py | 17 + smac/initial_design/factorial_design.py | 45 +- smac/initial_design/initial_design.py | 159 - smac/initial_design/latin_hypercube_design.py | 36 +- .../random_configuration_design.py | 28 - smac/initial_design/random_design.py | 20 + smac/initial_design/sobol_design.py | 49 +- smac/intensification/abstract_racer.py | 416 - smac/intensification/hyperband.py | 480 - smac/intensification/intensification.py | 1014 - smac/intensification/parallel_scheduling.py | 337 - smac/intensification/simple_intensifier.py | 199 - smac/intensification/successive_halving.py | 1326 - smac/intensifier/__init__.py | 13 + smac/intensifier/abstract_intensifier.py | 369 + .../abstract_parallel_intensifier.py | 276 + smac/intensifier/hyperband.py | 73 + smac/intensifier/hyperband_worker.py | 179 + smac/intensifier/intensifier.py | 809 + smac/intensifier/stages.py | 16 + smac/intensifier/successive_halving.py | 316 + smac/intensifier/successive_halving_worker.py | 820 + smac/logging.yml | 21 + smac/main/__init__.py | 7 + smac/main/base_smbo.py | 544 + smac/main/boing.py | 627 + smac/main/smbo.py | 313 + smac/main/turbo.py | 105 + smac/model/__init__.py | 9 + smac/model/abstract_model.py | 324 + smac/model/gaussian_process/__init__.py | 11 + .../abstract_gaussian_process.py} | 117 +- .../gaussian_process/gaussian_process.py | 301 + .../gpytorch_gaussian_process.py | 874 + .../gaussian_process/kernels/__init__.py | 21 + smac/model/gaussian_process/kernels/_boing.py | 567 + .../gaussian_process/kernels/base_kernels.py | 465 + .../kernels/hamming_kernel.py | 120 + .../gaussian_process/kernels/matern_kernel.py | 110 + .../gaussian_process/kernels/rbf_kernel.py | 74 + .../gaussian_process/kernels/white_kernel.py | 59 + .../mcmc_gaussian_process.py} | 335 +- .../model/gaussian_process/priors/__init__.py | 12 + .../gaussian_process/priors/abstract_prior.py | 155 + .../gaussian_process/priors/gamma_prior.py | 74 + .../priors/horseshoe_prior.py | 67 + .../priors/log_normal_prior.py | 68 + .../gaussian_process/priors/tophat_prior.py | 164 + smac/model/multi_objective_model.py | 98 + smac/model/random_forest/__init__.py | 7 + .../random_forest/abstract_random_forest.py | 50 + smac/model/random_forest/random_forest.py | 312 + smac/model/random_model.py | 37 + smac/multi_objective/__init__.py | 11 + .../abstract_multi_objective_algorithm.py | 46 +- smac/multi_objective/aggregation_strategy.py | 68 +- smac/multi_objective/parego.py | 72 +- smac/optimizer/acquisition/__init__.py | 731 - smac/optimizer/acquisition/maximizer.py | 902 - .../configuration_chooser/boing_chooser.py | 658 - .../configuration_chooser/epm_chooser.py | 235 - .../configuration_chooser/random_chooser.py | 217 - .../configuration_chooser/turbo_chooser.py | 131 - smac/optimizer/pSMAC.py | 92 - smac/optimizer/smbo.py | 544 - smac/optimizer/subspaces/__init__.py | 656 - smac/optimizer/subspaces/boing_subspace.py | 175 - smac/optimizer/subspaces/turbo_subspace.py | 319 - smac/random_design/__init__.py | 19 + smac/random_design/abstract_random_design.py | 55 + smac/random_design/annealing_design.py | 84 + smac/random_design/modulus_design.py | 110 + smac/random_design/probability_design.py | 88 + smac/runhistory/__init__.py | 21 + smac/runhistory/dataclasses.py | 131 + smac/runhistory/encoder/__init__.py | 27 + smac/runhistory/encoder/abstract_encoder.py | 282 + smac/runhistory/encoder/boing_encoder.py | 66 + smac/runhistory/encoder/eips_encoder.py | 86 + smac/runhistory/encoder/encoder.py | 75 + .../encoder/inverse_scaled_encoder.py | 39 + smac/runhistory/encoder/log_encoder.py | 27 + smac/runhistory/encoder/log_scaled_encoder.py | 36 + smac/runhistory/encoder/scaled_encoder.py | 28 + .../runhistory/encoder/sqrt_scaled_encoder.py | 40 + smac/runhistory/enumerations.py | 58 + smac/runhistory/runhistory.py | 1193 +- smac/runhistory/runhistory2epm.py | 729 - smac/runhistory/runhistory2epm_boing.py | 92 - smac/runner/__init__.py | 17 + smac/runner/abstract_runner.py | 235 + smac/runner/abstract_serial_runner.py | 42 + smac/runner/dask_runner.py | 188 + smac/runner/exceptions.py | 14 + smac/runner/target_function_runner.py | 231 + smac/runner/target_function_script_runner.py | 217 + smac/scenario.py | 249 + smac/scenario/scenario.py | 168 - smac/smac_cli.py | 236 - smac/stats.py | 230 + smac/stats/stats.py | 210 - smac/tae/__init__.py | 59 - smac/tae/base.py | 323 - smac/tae/dask_runner.py | 305 - smac/tae/execute_func.py | 356 - smac/tae/execute_ta_run_aclib.py | 168 - smac/tae/execute_ta_run_hydra.py | 80 - smac/tae/execute_ta_run_old.py | 178 - smac/tae/serial_runner.py | 178 - smac/utils/configspace.py | 225 + smac/utils/constants.py | 7 - smac/utils/data_structures.py | 66 + smac/utils/dependencies.py | 138 - smac/utils/io/__init__.py | 0 smac/utils/io/cmd_reader.py | 1371 - smac/utils/io/input_reader.py | 216 - smac/utils/io/output_directory.py | 48 - smac/utils/io/output_writer.py | 213 - smac/utils/io/result_merging.py | 207 - smac/utils/io/traj_logging.py | 384 - smac/utils/logging.py | 91 +- smac/utils/merge_foreign_data.py | 107 - .../utils.py => utils/multi_objective.py} | 5 +- smac/utils/subspaces/__init__.py | 666 + smac/utils/subspaces/boing_subspace.py | 178 + smac/utils/subspaces/turbo_subspace.py | 305 + smac/utils/test_helpers.py | 13 - smac/utils/validate.py | 701 - tests/conftest.py | 199 + .../utils => tests/fixtures}/__init__.py | 0 tests/fixtures/configspace.py | 62 + tests/fixtures/datasets.py | 57 + tests/fixtures/models.py | 101 + tests/fixtures/runhistory.py | 8 + tests/fixtures/scenario.py | 52 + tests/fixtures/stats.py | 14 + tests/test_acquisition/test_functions.py | 760 + tests/test_acquisition/test_maximizers.py | 432 + tests/test_ask_and_tell/test_hyperband.py | 231 + tests/test_ask_and_tell/test_intensifier.py | 233 + tests/test_callback.py | 124 + tests/test_cli/__init__.py | 0 .../random_configuration_chooser_impl.py | 24 - tests/test_cli/test_deterministic_smac.py | 202 - tests/test_cli/test_restore_state.py | 133 - tests/test_configspace/__init__.py | 0 tests/test_configspace/test_configspace.py | 22 - tests/test_continue.py | 57 + tests/test_epm/__init__.py | 0 tests/test_epm/test_base_epm.py | 69 - tests/test_epm/test_gp.py | 404 - tests/test_epm/test_gp_mcmc.py | 211 - tests/test_epm/test_gp_priors.py | 249 - tests/test_epm/test_rf_with_instances.py | 335 - .../test_uncorrelated_mo_rf_with_instances.py | 103 - tests/test_epm/test_util_funcs.py | 115 - tests/test_facade/__init__.py | 0 tests/test_facade/test_boing_facade.py | 61 - tests/test_facade/test_func_facade.py | 55 - tests/test_facade/test_hb4ac_facade.py | 45 - tests/test_facade/test_hydra_facade.py | 75 - tests/test_facade/test_psmac_facade.py | 67 - tests/test_facade/test_roar_facade.py | 68 - tests/test_facade/test_smac4mf_facade.py | 43 - tests/test_facade/test_smac_facade.py | 577 - tests/test_files/example_run/configspace.json | 24 - tests/test_files/example_run/configspace.pcs | 2 - tests/test_files/example_run/runhistory.json | 467 - tests/test_files/example_run/scenario.txt | 14 - tests/test_files/example_run/stats.json | 1 - tests/test_files/example_run/traj.json | 8 - tests/test_files/example_run/traj_aclib2.json | 8 - tests/test_files/example_run/traj_old.csv | 9 - tests/test_files/example_ta.py | 19 - tests/test_files/features_example.csv | 4 - tests/test_files/params_branin.pcs | 2 - tests/test_files/restore_scenario_one.txt | 6 - tests/test_files/restore_scenario_two.txt | 6 - tests/test_files/scenario_test/features.txt | 4 - .../scenario_test/features_multiple.txt | 4 - tests/test_files/scenario_test/param.pcs | 38 - tests/test_files/scenario_test/scenario.txt | 12 - tests/test_files/scenario_test/test.txt | 3 - tests/test_files/scenario_test/training.txt | 3 - tests/test_files/spear-params.pcs | 38 - .../test_files/spear_hydra_test_scenario.txt | 10 - tests/test_files/test_deterministic.pcs | 2 - tests/test_files/test_deterministic_func.py | 18 - .../test_deterministic_scenario.txt | 5 - tests/test_files/test_local_search.pcs | 10 - tests/test_files/train_insts_example.txt | 3 - tests/test_files/validation/scenario.txt | 2 - .../validation/test_validation_pcs.pcs | 2 - .../validation/test_validation_traj.json | 11 - .../test_factorical_design.py | 78 +- .../test_initial_design.py | 242 +- .../test_latin_hypercube_design.py | 66 +- .../test_random_config_design.py | 58 - .../test_initial_design/test_random_design.py | 17 + .../test_initial_design/test_sobol_design.py | 57 +- .../test_intensifier}/__init__.py | 0 .../test_abstract_intensifier.py | 168 + tests/test_intensifier/test_hyperband.py | 648 + tests/test_intensifier/test_intensify.py | 743 + .../test_parallel_scheduler.py | 171 + .../test_successive_halving.py | 1651 + tests/test_intensify/__init__.py | 0 tests/test_intensify/test_abstract_racer.py | 254 - tests/test_intensify/test_eval_utils.py | 40 - tests/test_intensify/test_hyperband.py | 643 - tests/test_intensify/test_intensify.py | 1244 - .../test_intensify/test_parallel_scheduler.py | 188 - .../test_intensify/test_simple_intensifier.py | 186 - .../test_intensify/test_successive_halving.py | 2204 - tests/test_local_bo/__init__.py | 0 tests/test_local_bo/test_abstract_subspace.py | 470 - tests/test_local_bo/test_epm_chooser_boing.py | 283 - tests/test_local_bo/test_epm_chooser_turbo.py | 41 - tests/test_local_bo/test_rh2epm_boing.py | 62 - tests/test_local_bo/test_subspace_boing.py | 68 - tests/test_local_bo/test_turbo_subspace.py | 185 - .../test_main}/__init__.py | 0 tests/test_main/_test_boing.py | 202 + tests/test_main/_test_turbo.py | 65 + tests/test_main/test_smbo.py | 57 + .../test_model}/__init__.py | 0 .../_test_boing_kernel.py} | 4 +- .../_test_gp_gpytorch.py} | 20 +- .../_test_lgpga.py} | 16 +- tests/test_model/_test_util_funcs.py | 67 + tests/test_model/test_abstract_model.py | 90 + tests/test_model/test_gp.py | 444 + tests/test_model/test_gp_mcmc.py | 242 + tests/test_model/test_gp_priors.py | 262 + tests/test_model/test_mo.py | 39 + tests/test_model/test_rf.py | 280 + .../test_multi_objective}/__init__.py | 0 .../test_multi_objective.py | 43 + tests/test_multi_objective/test_schaffer.py | 161 +- .../test_schaffer_upscaled.py | 127 +- .../test_random_design}/__init__.py | 0 .../test_annealing_design.py | 17 + .../test_random_design/test_modulus_design.py | 53 + .../test_probability_design.py | 31 + .../test_random_design/test_random_design.py | 38 + tests/test_runhistory/test_rfr_imputor.py | 251 - tests/test_runhistory/test_runhistory.py | 889 +- tests/test_runhistory/test_runhistory2epm.py | 611 - .../test_runhistory_encoder.py | 275 + .../test_runhistory_multi_objective.py | 1179 +- {smac/stats => tests/test_runner}/__init__.py | 0 tests/test_runner/files/crashed.sh | 19 + tests/test_runner/files/exit.sh | 19 + tests/test_runner/files/python.py | 12 + tests/test_runner/files/success.sh | 19 + .../files/success_multi_objective.sh | 19 + tests/test_runner/test_dask_runner.py | 198 + .../test_script_target_algorithm_runner.py | 73 + .../test_target_algorithm_runner.py | 170 + tests/test_scenario/__init__.py | 0 tests/test_scenario/test_scenario.py | 506 +- tests/test_smbo/__init__.py | 0 tests/test_smbo/test/traj_old.csv | 1 - tests/test_smbo/test_acquisition.py | 609 - tests/test_smbo/test_ei_optimization.py | 375 - .../test_epm_configuration_chooser.py | 277 - tests/test_smbo/test_pSMAC.py | 164 - .../test_random_configuration_chooser.py | 85 - tests/test_smbo/test_smbo.py | 543 - tests/test_tae/__init__.py | 0 tests/test_tae/dummy_ta_wrapper.py | 13 - tests/test_tae/dummy_ta_wrapper_aclib.py | 16 - tests/test_tae/test_exec_func.py | 203 - tests/test_tae/test_exec_tae_run.py | 251 - tests/test_tae/test_hydra_tae.py | 80 - tests/test_tae/test_parallel_runner.py | 218 - tests/test_tae/test_serial_runner.py | 103 - tests/test_tae/test_tae_aclib.py | 99 - tests/test_tae/test_tae_old.py | 104 - tests/test_utils/__init__.py | 0 tests/test_utils/io/__init__.py | 0 tests/test_utils/io/test_CMDReader.py | 62 - tests/test_utils/io/test_inputreader.py | 85 - tests/test_utils/io/test_result_merging.py | 48 - tests/test_utils/io/test_traj_logging.py | 229 - tests/test_utils/test_multi_objective.py | 48 - tests/test_utils/test_validate.py | 459 - 496 files changed, 30199 insertions(+), 136293 deletions(-) delete mode 100644 .github/workflows/examples.yml rename docs/{getting_started/installation.rst => 1_installation.rst} (73%) create mode 100644 docs/2_package_overview.rst create mode 100644 docs/3_getting_started.rst create mode 100644 docs/4_minimal_example.rst create mode 100644 docs/5_api.rst create mode 100644 docs/6_references.rst create mode 100644 docs/7_glossary.rst rename docs/{faq.rst => 8_faq.rst} (64%) rename docs/{license.rst => 9_license.rst} (100%) create mode 100644 docs/advanced_usage/10_continue.rst create mode 100644 docs/advanced_usage/11_reproducability.rst create mode 100644 docs/advanced_usage/1_components.rst create mode 100644 docs/advanced_usage/2_multi_fidelity.rst create mode 100644 docs/advanced_usage/3_multi_objective.rst create mode 100644 docs/advanced_usage/4_instances.rst create mode 100644 docs/advanced_usage/5_ask_and_tell.rst create mode 100644 docs/advanced_usage/6_commandline.rst create mode 100644 docs/advanced_usage/7_stopping_criteria.rst create mode 100644 docs/advanced_usage/8_logging.rst create mode 100644 docs/advanced_usage/9_parallelism.rst create mode 100644 docs/advanced_usage/index.rst delete mode 100644 docs/api.rst delete mode 100644 docs/details/arguments.rst delete mode 100644 docs/details/callbacks.rst delete mode 100644 docs/details/cuda.rst delete mode 100644 docs/details/facades.rst delete mode 100644 docs/details/index.rst delete mode 100644 docs/details/instances.rst delete mode 100644 docs/details/multi_objective.rst delete mode 100644 docs/details/parallelism.rst delete mode 100644 docs/details/restoring.rst delete mode 100644 docs/details/run_history.rst delete mode 100644 docs/details/scenario.rst delete mode 100644 docs/details/target_algorithm_evaluator.rst delete mode 100644 docs/details/validation.rst delete mode 100644 docs/getting_started/basic_usage.rst delete mode 100644 docs/getting_started/index.rst delete mode 100644 docs/getting_started/minimal_example.rst delete mode 100644 docs/getting_started/package_overview.rst delete mode 100644 docs/glossary.rst delete mode 100644 docs/images/components.png delete mode 100644 docs/images/smac_facades_all_classes.png delete mode 100644 docs/images/smac_facades_all_classes_withf.png create mode 100644 examples/1_basics/1_synthetic_function.py create mode 100644 examples/1_basics/2_svm_cv.py create mode 100644 examples/1_basics/3_ask_and_tell.py create mode 100644 examples/1_basics/4_callback.py create mode 100644 examples/1_basics/5_priors.py create mode 100644 examples/1_basics/README.rst create mode 100644 examples/2_multi_fidelity/1_mlp_epochs.py create mode 100644 examples/2_multi_fidelity/2_sgd_datasets.py create mode 100644 examples/2_multi_fidelity/README.rst create mode 100644 examples/3_multi_objective/1_schaffer.py create mode 100644 examples/3_multi_objective/2_parego.py create mode 100644 examples/3_multi_objective/README.rst create mode 100644 examples/4_advanced_optimizer/1_turbo_optimizer.py create mode 100644 examples/4_advanced_optimizer/2_boing_optimizer.py create mode 100644 examples/5_commandline/1_call_target_function_script.py create mode 100644 examples/5_commandline/README.rst create mode 100755 examples/5_commandline/script.sh delete mode 100644 examples/commandline/README.rst delete mode 100644 examples/commandline/branin.py delete mode 100644 examples/commandline/branin/configspace.pcs delete mode 100644 examples/commandline/branin/scenario.txt delete mode 100644 examples/commandline/restore_branin.py delete mode 100644 examples/commandline/spear_qcp.py delete mode 100644 examples/commandline/spear_qcp/features.txt delete mode 100644 examples/commandline/spear_qcp/instances.txt delete mode 100755 examples/commandline/spear_qcp/instances/qcplin2006.10218.cnf delete mode 100755 examples/commandline/spear_qcp/instances/qcplin2006.1031.cnf delete mode 100755 examples/commandline/spear_qcp/instances/qcplin2006.10556.cnf delete mode 100755 examples/commandline/spear_qcp/instances/qcplin2006.10641.cnf delete mode 100644 examples/commandline/spear_qcp/scenario.txt delete mode 100755 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Changelog delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/CircularBufferFilter.hh delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Cores.hh delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/CreateSyscallsNames.cc delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/LICENSE-GPL-3.0.txt delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Makefile delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Makefile.back delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Observer.hh delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/ProcessData.hh delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/ProcessHistory.hh delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/ProcessList.hh delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/ProcessTree.hh delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/README delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SignalNames.cc delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SignalNames.d delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SignalNames.hh delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SignalNames.o delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.cc delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.d delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.hh delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.o delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallsTracer.hh delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/TimeStamper.hh delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/aeatk.c delete mode 100755 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/runsolver delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/runsolver.cc delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/runsolver.d delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/runsolver.o delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/runsolver.spec delete mode 100644 examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/vlineSplitter.cc delete mode 100755 examples/commandline/spear_qcp/target_algorithm/scripts/SAT delete mode 100755 examples/commandline/spear_qcp/target_algorithm/scripts/SATCSSCWrapper.py delete mode 100755 examples/commandline/spear_qcp/target_algorithm/scripts/genericWrapper.py delete mode 100755 examples/commandline/spear_qcp/target_algorithm/scripts/generic_solver_wrapper.rb delete mode 100755 examples/commandline/spear_qcp/target_algorithm/spear-python/README.md delete mode 100755 examples/commandline/spear_qcp/target_algorithm/spear-python/Spear-32_1.2.1 delete mode 100755 examples/commandline/spear_qcp/target_algorithm/spear-python/spear-params-mixed.pcs delete mode 100755 examples/commandline/spear_qcp/target_algorithm/spear-python/spear-params.pcs delete mode 100644 examples/commandline/spear_qcp/target_algorithm/spear-python/spearCSSCWrapper.py delete mode 100644 examples/commandline/spear_qcp_roar.sh delete mode 100755 examples/commandline/spear_qcp_smac.sh delete mode 100644 examples/python/README.rst delete mode 100644 examples/python/plot_gb_non_deterministic.py delete mode 100644 examples/python/plot_mlp_mf.py delete mode 100644 examples/python/plot_scalarized_multi_objective.py delete mode 100644 examples/python/plot_sgd_instances.py delete mode 100644 examples/python/plot_simple_multi_objective.py delete mode 100644 examples/python/plot_svm_cv.py delete mode 100644 examples/python/plot_svm_eips.py delete mode 100644 examples/python/plot_synthetic_function.py delete mode 100644 examples/python/plot_synthetic_function_boing.py delete mode 100644 examples/python/plot_synthetic_function_parallel.py delete mode 100644 examples/python/plot_synthetic_function_turbo.py delete mode 100644 examples/python/plot_user_prior_mlp.py delete mode 100644 examples/python/spear_mf_instances.py delete mode 100755 scripts/check-deterministic.sh delete mode 100644 scripts/plot_traj_perf.py delete mode 100644 scripts/smac-validate.py delete mode 100644 scripts/smac.py delete mode 100644 scripts/test_no_files_left.sh create mode 100644 scripts/update_files.py rename smac/{epm => acquisition}/__init__.py (100%) create mode 100644 smac/acquisition/function/__init__.py create mode 100644 smac/acquisition/function/abstract_acquisition_function.py create mode 100644 smac/acquisition/function/confidence_bound.py create mode 100644 smac/acquisition/function/expected_improvement.py create mode 100644 smac/acquisition/function/integrated_acquisition_function.py create mode 100644 smac/acquisition/function/prior_acqusition_function.py create mode 100644 smac/acquisition/function/probability_improvement.py create mode 100644 smac/acquisition/function/thompson.py create mode 100644 smac/acquisition/maximizer/__init__.py create mode 100644 smac/acquisition/maximizer/abstract_acqusition_maximizer.py create mode 100644 smac/acquisition/maximizer/differential_evolution.py create mode 100644 smac/acquisition/maximizer/helpers.py create mode 100644 smac/acquisition/maximizer/local_and_random_search.py create mode 100644 smac/acquisition/maximizer/local_search.py create mode 100644 smac/acquisition/maximizer/random_search.py create mode 100644 smac/callback.py delete mode 100644 smac/callbacks.py delete mode 100644 smac/configspace/__init__.py delete mode 100644 smac/configspace/util.py create mode 100644 smac/constants.py delete mode 100644 smac/epm/base_epm.py delete mode 100644 smac/epm/base_imputor.py delete mode 100644 smac/epm/gaussian_process/augmented.py delete mode 100644 smac/epm/gaussian_process/gp.py delete mode 100644 smac/epm/gaussian_process/gpytorch.py delete mode 100644 smac/epm/gaussian_process/kernels/__init__.py delete mode 100644 smac/epm/gaussian_process/kernels/boing.py delete mode 100644 smac/epm/gaussian_process/utils/prior.py delete mode 100644 smac/epm/multi_objective_epm.py delete mode 100644 smac/epm/random_epm.py delete mode 100644 smac/epm/random_forest/__init__.py delete mode 100644 smac/epm/random_forest/rf_mo.py delete mode 100644 smac/epm/random_forest/rf_with_instances.py delete mode 100644 smac/epm/random_forest/rfr_imputator.py delete mode 100644 smac/epm/utils.py create mode 100644 smac/facade/abstract_facade.py create mode 100644 smac/facade/algorithm_configuration_facade.py create mode 100644 smac/facade/blackbox_facade.py create mode 100644 smac/facade/boing_facade.py delete mode 100644 smac/facade/experimental/hydra_facade.py delete mode 100644 smac/facade/func_facade.py create mode 100644 smac/facade/hyperparameter_optimization_facade.py create mode 100644 smac/facade/multi_fidelity_facade.py delete mode 100644 smac/facade/psmac_facade.py create mode 100644 smac/facade/random_facade.py delete mode 100644 smac/facade/roar_facade.py delete mode 100644 smac/facade/smac_ac_facade.py delete mode 100644 smac/facade/smac_bb_facade.py delete mode 100644 smac/facade/smac_boing_facade.py delete mode 100644 smac/facade/smac_hpo_facade.py delete mode 100644 smac/facade/smac_mf_facade.py create mode 100644 smac/initial_design/abstract_initial_design.py delete mode 100644 smac/initial_design/default_configuration_design.py create mode 100644 smac/initial_design/default_design.py delete mode 100644 smac/initial_design/initial_design.py delete mode 100644 smac/initial_design/random_configuration_design.py create mode 100644 smac/initial_design/random_design.py delete mode 100644 smac/intensification/abstract_racer.py delete mode 100644 smac/intensification/hyperband.py delete mode 100644 smac/intensification/intensification.py delete mode 100644 smac/intensification/parallel_scheduling.py delete mode 100644 smac/intensification/simple_intensifier.py delete mode 100644 smac/intensification/successive_halving.py create mode 100644 smac/intensifier/__init__.py create mode 100644 smac/intensifier/abstract_intensifier.py create mode 100644 smac/intensifier/abstract_parallel_intensifier.py create mode 100644 smac/intensifier/hyperband.py create mode 100644 smac/intensifier/hyperband_worker.py create mode 100644 smac/intensifier/intensifier.py create mode 100644 smac/intensifier/stages.py create mode 100644 smac/intensifier/successive_halving.py create mode 100644 smac/intensifier/successive_halving_worker.py create mode 100644 smac/logging.yml create mode 100644 smac/main/__init__.py create mode 100644 smac/main/base_smbo.py create mode 100644 smac/main/boing.py create mode 100644 smac/main/smbo.py create mode 100644 smac/main/turbo.py create mode 100644 smac/model/__init__.py create mode 100644 smac/model/abstract_model.py create mode 100644 smac/model/gaussian_process/__init__.py rename smac/{epm/gaussian_process/__init__.py => model/gaussian_process/abstract_gaussian_process.py} (60%) create mode 100644 smac/model/gaussian_process/gaussian_process.py create mode 100644 smac/model/gaussian_process/gpytorch_gaussian_process.py create mode 100644 smac/model/gaussian_process/kernels/__init__.py create mode 100644 smac/model/gaussian_process/kernels/_boing.py create mode 100644 smac/model/gaussian_process/kernels/base_kernels.py create mode 100644 smac/model/gaussian_process/kernels/hamming_kernel.py create mode 100644 smac/model/gaussian_process/kernels/matern_kernel.py create mode 100644 smac/model/gaussian_process/kernels/rbf_kernel.py create mode 100644 smac/model/gaussian_process/kernels/white_kernel.py rename smac/{epm/gaussian_process/mcmc.py => model/gaussian_process/mcmc_gaussian_process.py} (56%) create mode 100644 smac/model/gaussian_process/priors/__init__.py create mode 100644 smac/model/gaussian_process/priors/abstract_prior.py create mode 100644 smac/model/gaussian_process/priors/gamma_prior.py create mode 100644 smac/model/gaussian_process/priors/horseshoe_prior.py create mode 100644 smac/model/gaussian_process/priors/log_normal_prior.py create mode 100644 smac/model/gaussian_process/priors/tophat_prior.py create mode 100644 smac/model/multi_objective_model.py create mode 100644 smac/model/random_forest/__init__.py create mode 100644 smac/model/random_forest/abstract_random_forest.py create mode 100644 smac/model/random_forest/random_forest.py create mode 100644 smac/model/random_model.py delete mode 100644 smac/optimizer/acquisition/__init__.py delete mode 100644 smac/optimizer/acquisition/maximizer.py delete mode 100644 smac/optimizer/configuration_chooser/boing_chooser.py delete mode 100644 smac/optimizer/configuration_chooser/epm_chooser.py delete mode 100644 smac/optimizer/configuration_chooser/random_chooser.py delete mode 100644 smac/optimizer/configuration_chooser/turbo_chooser.py delete mode 100644 smac/optimizer/pSMAC.py delete mode 100644 smac/optimizer/smbo.py delete mode 100644 smac/optimizer/subspaces/__init__.py delete mode 100644 smac/optimizer/subspaces/boing_subspace.py delete mode 100644 smac/optimizer/subspaces/turbo_subspace.py create mode 100644 smac/random_design/__init__.py create mode 100644 smac/random_design/abstract_random_design.py create mode 100644 smac/random_design/annealing_design.py create mode 100644 smac/random_design/modulus_design.py create mode 100644 smac/random_design/probability_design.py create mode 100644 smac/runhistory/dataclasses.py create mode 100644 smac/runhistory/encoder/__init__.py create mode 100644 smac/runhistory/encoder/abstract_encoder.py create mode 100644 smac/runhistory/encoder/boing_encoder.py create mode 100644 smac/runhistory/encoder/eips_encoder.py create mode 100644 smac/runhistory/encoder/encoder.py create mode 100644 smac/runhistory/encoder/inverse_scaled_encoder.py create mode 100644 smac/runhistory/encoder/log_encoder.py create mode 100644 smac/runhistory/encoder/log_scaled_encoder.py create mode 100644 smac/runhistory/encoder/scaled_encoder.py create mode 100644 smac/runhistory/encoder/sqrt_scaled_encoder.py create mode 100644 smac/runhistory/enumerations.py delete mode 100644 smac/runhistory/runhistory2epm.py delete mode 100644 smac/runhistory/runhistory2epm_boing.py create mode 100644 smac/runner/__init__.py create mode 100644 smac/runner/abstract_runner.py create mode 100644 smac/runner/abstract_serial_runner.py create mode 100644 smac/runner/dask_runner.py create mode 100644 smac/runner/exceptions.py create mode 100644 smac/runner/target_function_runner.py create mode 100644 smac/runner/target_function_script_runner.py create mode 100644 smac/scenario.py delete mode 100644 smac/scenario/scenario.py delete mode 100644 smac/smac_cli.py create mode 100644 smac/stats.py delete mode 100644 smac/stats/stats.py delete mode 100644 smac/tae/__init__.py delete mode 100644 smac/tae/base.py delete mode 100644 smac/tae/dask_runner.py delete mode 100644 smac/tae/execute_func.py delete mode 100644 smac/tae/execute_ta_run_aclib.py delete mode 100644 smac/tae/execute_ta_run_hydra.py delete mode 100644 smac/tae/execute_ta_run_old.py delete mode 100644 smac/tae/serial_runner.py create mode 100644 smac/utils/configspace.py delete mode 100644 smac/utils/constants.py create mode 100644 smac/utils/data_structures.py delete mode 100644 smac/utils/dependencies.py delete mode 100644 smac/utils/io/__init__.py delete mode 100644 smac/utils/io/cmd_reader.py delete mode 100644 smac/utils/io/input_reader.py delete mode 100644 smac/utils/io/output_directory.py delete mode 100644 smac/utils/io/output_writer.py delete mode 100644 smac/utils/io/result_merging.py delete mode 100644 smac/utils/io/traj_logging.py delete mode 100644 smac/utils/merge_foreign_data.py rename smac/{multi_objective/utils.py => utils/multi_objective.py} (90%) create mode 100644 smac/utils/subspaces/__init__.py create mode 100644 smac/utils/subspaces/boing_subspace.py create mode 100644 smac/utils/subspaces/turbo_subspace.py delete mode 100644 smac/utils/test_helpers.py delete mode 100644 smac/utils/validate.py create mode 100644 tests/conftest.py rename {smac/epm/gaussian_process/utils => tests/fixtures}/__init__.py (100%) create mode 100644 tests/fixtures/configspace.py create mode 100644 tests/fixtures/datasets.py create mode 100644 tests/fixtures/models.py create mode 100644 tests/fixtures/runhistory.py create mode 100644 tests/fixtures/scenario.py create mode 100644 tests/fixtures/stats.py create mode 100644 tests/test_acquisition/test_functions.py create mode 100644 tests/test_acquisition/test_maximizers.py create mode 100644 tests/test_ask_and_tell/test_hyperband.py create mode 100644 tests/test_ask_and_tell/test_intensifier.py create mode 100644 tests/test_callback.py delete mode 100644 tests/test_cli/__init__.py delete mode 100644 tests/test_cli/random_configuration_chooser_impl.py delete mode 100644 tests/test_cli/test_deterministic_smac.py delete mode 100644 tests/test_cli/test_restore_state.py delete mode 100644 tests/test_configspace/__init__.py delete mode 100644 tests/test_configspace/test_configspace.py create mode 100644 tests/test_continue.py delete mode 100644 tests/test_epm/__init__.py delete mode 100644 tests/test_epm/test_base_epm.py delete mode 100644 tests/test_epm/test_gp.py delete mode 100644 tests/test_epm/test_gp_mcmc.py delete mode 100644 tests/test_epm/test_gp_priors.py delete mode 100644 tests/test_epm/test_rf_with_instances.py delete mode 100644 tests/test_epm/test_uncorrelated_mo_rf_with_instances.py delete mode 100644 tests/test_epm/test_util_funcs.py delete mode 100644 tests/test_facade/__init__.py delete mode 100644 tests/test_facade/test_boing_facade.py delete mode 100644 tests/test_facade/test_func_facade.py delete mode 100644 tests/test_facade/test_hb4ac_facade.py delete mode 100644 tests/test_facade/test_hydra_facade.py delete mode 100644 tests/test_facade/test_psmac_facade.py delete mode 100644 tests/test_facade/test_roar_facade.py delete mode 100644 tests/test_facade/test_smac4mf_facade.py delete mode 100644 tests/test_facade/test_smac_facade.py delete mode 100644 tests/test_files/example_run/configspace.json delete mode 100644 tests/test_files/example_run/configspace.pcs delete mode 100644 tests/test_files/example_run/runhistory.json delete mode 100644 tests/test_files/example_run/scenario.txt delete mode 100644 tests/test_files/example_run/stats.json delete mode 100644 tests/test_files/example_run/traj.json delete mode 100644 tests/test_files/example_run/traj_aclib2.json delete mode 100644 tests/test_files/example_run/traj_old.csv delete mode 100644 tests/test_files/example_ta.py delete mode 100644 tests/test_files/features_example.csv delete mode 100644 tests/test_files/params_branin.pcs delete mode 100644 tests/test_files/restore_scenario_one.txt delete mode 100644 tests/test_files/restore_scenario_two.txt delete mode 100644 tests/test_files/scenario_test/features.txt delete mode 100644 tests/test_files/scenario_test/features_multiple.txt delete mode 100644 tests/test_files/scenario_test/param.pcs delete mode 100644 tests/test_files/scenario_test/scenario.txt delete mode 100644 tests/test_files/scenario_test/test.txt delete mode 100644 tests/test_files/scenario_test/training.txt delete mode 100755 tests/test_files/spear-params.pcs delete mode 100644 tests/test_files/spear_hydra_test_scenario.txt delete mode 100644 tests/test_files/test_deterministic.pcs delete mode 100644 tests/test_files/test_deterministic_func.py delete mode 100644 tests/test_files/test_deterministic_scenario.txt delete mode 100644 tests/test_files/test_local_search.pcs delete mode 100644 tests/test_files/train_insts_example.txt delete mode 100644 tests/test_files/validation/scenario.txt delete mode 100644 tests/test_files/validation/test_validation_pcs.pcs delete mode 100644 tests/test_files/validation/test_validation_traj.json delete mode 100644 tests/test_initial_design/test_random_config_design.py create mode 100644 tests/test_initial_design/test_random_design.py rename {smac/facade/experimental => tests/test_intensifier}/__init__.py (100%) create mode 100644 tests/test_intensifier/test_abstract_intensifier.py create mode 100644 tests/test_intensifier/test_hyperband.py create mode 100644 tests/test_intensifier/test_intensify.py create mode 100644 tests/test_intensifier/test_parallel_scheduler.py create mode 100644 tests/test_intensifier/test_successive_halving.py delete mode 100644 tests/test_intensify/__init__.py delete mode 100644 tests/test_intensify/test_abstract_racer.py delete mode 100644 tests/test_intensify/test_eval_utils.py delete mode 100644 tests/test_intensify/test_hyperband.py delete mode 100644 tests/test_intensify/test_intensify.py delete mode 100644 tests/test_intensify/test_parallel_scheduler.py delete mode 100644 tests/test_intensify/test_simple_intensifier.py delete mode 100644 tests/test_intensify/test_successive_halving.py delete mode 100644 tests/test_local_bo/__init__.py delete mode 100644 tests/test_local_bo/test_abstract_subspace.py delete mode 100644 tests/test_local_bo/test_epm_chooser_boing.py delete mode 100644 tests/test_local_bo/test_epm_chooser_turbo.py delete mode 100644 tests/test_local_bo/test_rh2epm_boing.py delete mode 100644 tests/test_local_bo/test_subspace_boing.py delete mode 100644 tests/test_local_bo/test_turbo_subspace.py rename {smac/intensification => tests/test_main}/__init__.py (100%) create mode 100644 tests/test_main/_test_boing.py create mode 100644 tests/test_main/_test_turbo.py create mode 100644 tests/test_main/test_smbo.py rename {smac/optimizer => tests/test_model}/__init__.py (100%) rename tests/{test_epm/test_boing_kernel.py => test_model/_test_boing_kernel.py} (98%) rename tests/{test_epm/test_gp_gpytorch.py => test_model/_test_gp_gpytorch.py} (96%) rename tests/{test_epm/test_lgpga.py => test_model/_test_lgpga.py} (94%) create mode 100644 tests/test_model/_test_util_funcs.py create mode 100644 tests/test_model/test_abstract_model.py create mode 100644 tests/test_model/test_gp.py create mode 100644 tests/test_model/test_gp_mcmc.py create mode 100644 tests/test_model/test_gp_priors.py create mode 100644 tests/test_model/test_mo.py create mode 100644 tests/test_model/test_rf.py rename {smac/optimizer/configuration_chooser => tests/test_multi_objective}/__init__.py (100%) create mode 100644 tests/test_multi_objective/test_multi_objective.py rename {smac/scenario => tests/test_random_design}/__init__.py (100%) create mode 100644 tests/test_random_design/test_annealing_design.py create mode 100644 tests/test_random_design/test_modulus_design.py create mode 100644 tests/test_random_design/test_probability_design.py create mode 100644 tests/test_random_design/test_random_design.py delete mode 100644 tests/test_runhistory/test_rfr_imputor.py delete mode 100644 tests/test_runhistory/test_runhistory2epm.py create mode 100644 tests/test_runhistory/test_runhistory_encoder.py rename {smac/stats => tests/test_runner}/__init__.py (100%) create mode 100755 tests/test_runner/files/crashed.sh create mode 100755 tests/test_runner/files/exit.sh create mode 100755 tests/test_runner/files/python.py create mode 100755 tests/test_runner/files/success.sh create mode 100755 tests/test_runner/files/success_multi_objective.sh create mode 100644 tests/test_runner/test_dask_runner.py create mode 100644 tests/test_runner/test_script_target_algorithm_runner.py create mode 100644 tests/test_runner/test_target_algorithm_runner.py delete mode 100644 tests/test_scenario/__init__.py delete mode 100644 tests/test_smbo/__init__.py delete mode 100644 tests/test_smbo/test/traj_old.csv delete mode 100644 tests/test_smbo/test_acquisition.py delete mode 100644 tests/test_smbo/test_ei_optimization.py delete mode 100644 tests/test_smbo/test_epm_configuration_chooser.py delete mode 100644 tests/test_smbo/test_pSMAC.py delete mode 100644 tests/test_smbo/test_random_configuration_chooser.py delete mode 100644 tests/test_smbo/test_smbo.py delete mode 100644 tests/test_tae/__init__.py delete mode 100644 tests/test_tae/dummy_ta_wrapper.py delete mode 100644 tests/test_tae/dummy_ta_wrapper_aclib.py delete mode 100644 tests/test_tae/test_exec_func.py delete mode 100644 tests/test_tae/test_exec_tae_run.py delete mode 100644 tests/test_tae/test_hydra_tae.py delete mode 100644 tests/test_tae/test_parallel_runner.py delete mode 100644 tests/test_tae/test_serial_runner.py delete mode 100644 tests/test_tae/test_tae_aclib.py delete mode 100644 tests/test_tae/test_tae_old.py delete mode 100644 tests/test_utils/__init__.py delete mode 100644 tests/test_utils/io/__init__.py delete mode 100644 tests/test_utils/io/test_CMDReader.py delete mode 100644 tests/test_utils/io/test_inputreader.py delete mode 100644 tests/test_utils/io/test_result_merging.py delete mode 100644 tests/test_utils/io/test_traj_logging.py delete mode 100644 tests/test_utils/test_multi_objective.py delete mode 100644 tests/test_utils/test_validate.py diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index e43d6bcfb..3755ba596 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -9,12 +9,14 @@ on: branches: - main - development + - development-2.0 # Trigger on a open/push to a PR targeting one of these branches pull_request: branches: - main - development + - development-2.0 jobs: dist: @@ -27,7 +29,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 with: - python-version: "3.9" + python-version: "3.10" - name: Build dist run: | diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 4f8f0f174..78ed9a191 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -11,12 +11,14 @@ on: branches: - main - development + - development-2.0 # Trigger on a open/push to a PR targeting one of these branches pull_request: branches: - main - development + - development-2.0 env: name: SMAC3 @@ -31,7 +33,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 with: - python-version: "3.9" + python-version: "3.10" - name: Install dependencies run: | diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml deleted file mode 100644 index 001e6be3e..000000000 --- a/.github/workflows/examples.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: examples - -on: - # Manual trigger option in github - workflow_dispatch: - - # Trigger on push to these branches - push: - branches: - - main - - development - - # Trigger on a open/push to a PR targeting one of these branches - pull_request: - branches: - - main - - development - -jobs: - ubuntu: - - runs-on: ubuntu-18.04 - strategy: - matrix: - python-version: [3.9] - fail-fast: false - - steps: - - uses: actions/checkout@v3 - - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Conda Install test dependencies - run: | - # Miniconda is available in $CONDA env var - $CONDA/bin/conda create -n testenv --yes pip wheel gxx_linux-64 gcc_linux-64 swig python=${{ matrix.python-version }} - $CONDA/envs/testenv/bin/python3 -m pip install --upgrade pip - $CONDA/envs/testenv/bin/pip3 install .[all] - - - name: Spear-QCP ROAR (Commandline) - timeout-minutes: 20 - run: | - # Activate anaconda so default python is from conda - export PATH="$CONDA/envs/testenv/bin:$PATH" - - # cd examples/commandline - bash examples/commandline/spear_qcp_roar.sh - - - name: Spear-QCP SMAC (Commandline) - timeout-minutes: 20 - run: | - # Activate anaconda so default python is from conda - export PATH="$CONDA/envs/testenv/bin:$PATH" - - # cd examples/commandline - bash examples/commandline/spear_qcp_smac.sh - - - name: Branin (Commandline) - timeout-minutes: 20 - run: | - # Activate anaconda so default python is from conda - export PATH="$CONDA/envs/testenv/bin:$PATH" - - # cd examples/commandline - python ./scripts/smac.py --scenario examples/commandline/branin/scenario.txt diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 5e5815016..e86c722b2 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -9,12 +9,14 @@ on: branches: - main - development + - development-2.0 # When a push occurs on a PR that targets these branches pull_request: branches: - main - development + - development-2.0 jobs: run-all-files: diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 620da251b..98addbe87 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -9,12 +9,14 @@ on: branches: - main - development + - development-2.0 # Triggers with push to a pr aimed at main pull_request: branches: - main - development + - development-2.0 schedule: # Every day at 7AM UTC @@ -43,7 +45,8 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9"] + python-version: ["3.8", "3.9", "3.10"] + # python-version: ["3.8"] os: ["ubuntu-latest"] steps: @@ -96,7 +99,8 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10"] + # python-version: ["3.8"] os: ["ubuntu-latest"] steps: @@ -132,7 +136,8 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10"] + # python-version: ["3.8"] os: ["ubuntu-latest"] steps: diff --git a/.gitignore b/.gitignore index 8b391fae8..b7dc6899c 100644 --- a/.gitignore +++ b/.gitignore @@ -134,9 +134,13 @@ dmypy.json .pyre/ *smac3-output_* +*smac3_output* # macOS files .DS_Store # Remove docker files -docker \ No newline at end of file +docker + +# Others +src \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in index 2073f018d..492229af8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,12 +1,6 @@ -# MANIFEST.in defines what files are included/excluded from a built pacakage -# that gets installed through PyPI or through `pip install` -# To modify, check out -# https://packaging.python.org/en/latest/guides/using-manifest-in/ - -# Include individual files include LICENSE.txt -include requirements.txt include smac/py.typed # This file is to export types +include smac/logging.yml prune tests prune examples diff --git a/README.md b/README.md index 9db198007..ba8b4e4d8 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,11 @@ [![codecov Status](https://codecov.io/gh/automl/SMAC3/branch/master/graph/badge.svg)](https://codecov.io/gh/automl/SMAC3) -SMAC is a tool for algorithm configuration to optimize the parameters of -arbitrary algorithms, including hyperparameter optimization of Machine Learning algorithms. The main core consists of -Bayesian Optimization in combination with an aggressive racing mechanism to -efficiently decide which of two configurations performs better. +SMAC is a tool for algorithm configuration to optimize the parameters of arbitrary algorithms, including hyperparameter +optimization of Machine Learning algorithms. The main core consists of Bayesian Optimization in combination with an +aggressive racing mechanism to efficiently decide which of two configurations performs better. -SMAC3 is written in Python3 and continuously tested with Python 3.7, 3.8, 3.9, and 3.10. Its Random +SMAC3 is written in Python3 and continuously tested with Python 3.8, 3.9, and 3.10. Its Random Forest is written in C++. In further texts, SMAC is representatively mentioned for SMAC3. > [Documention](https://automl.github.io/SMAC3) @@ -20,14 +19,27 @@ Forest is written in C++. In further texts, SMAC is representatively mentioned f > [Roadmap](https://github.com/orgs/automl/projects/5/views/2) +## Important: Changes in v2.0 + +With the next big major release of SMAC, we drastically boosted the user experience by improving the APIs and how the +pipelining is done (see [changelog](CHANGELOG.md)). However, cleaning the code base, we removed the command-line +interface (calling a target function from a script is still supported), runtime optimization, and pSMAC. Also, + python 3.7 is not supported anymore. If you depend on those functionalities, we kindly ask you to keep using v1.4. + +We are excited to introduce the new major release and look forward to developing new features on the new code base. +We hope you enjoy this new user experience as much as we do. 🚀 + + ## Installation -Create a new environment with python 3.9 and make sure swig is installed either on your system or +This instruction is for the installation on a Linux system, for Windows and Mac and further information see the [documention](https://automl.github.io/SMAC3/main/installation.html). + +Create a new environment with python 3.10 and make sure swig is installed either on your system or inside the environment. We demonstrate the installation via anaconda in the following: Create and activate environment: ``` -conda create -n SMAC python=3.9 +conda create -n SMAC python=3.10 conda activate SMAC ``` @@ -44,56 +56,41 @@ pip install smac Or alternatively, clone the environment: ``` git clone https://github.com/automl/SMAC3.git && cd SMAC3 -pip install -r requirements.txt -pip install . +pip install -e .[dev] ``` -We refer to the [documention](https://automl.github.io/SMAC3) for further installation options. - ## Minimal Example ```py -import numpy as np - -from sklearn.ensemble import RandomForestClassifier -from ConfigSpace import ConfigurationSpace -from ConfigSpace.hyperparameters import UniformIntegerHyperparameter -from smac.facade.smac_bb_facade import SMAC4BB -from smac.scenario.scenario import Scenario - - -X_train, y_train = np.random.randint(2, size=(20, 2)), np.random.randint(2, size=20) -X_val, y_val = np.random.randint(2, size=(5, 2)), np.random.randint(2, size=5) +from ConfigSpace import Configuration, ConfigurationSpace +import numpy as np +from smac import HyperparameterOptimizationFacade, Scenario +from sklearn import datasets +from sklearn.svm import SVC +from sklearn.model_selection import cross_val_score -def train_random_forest(config): - model = RandomForestClassifier(max_depth=config["depth"]) - model.fit(X_train, y_train) +iris = datasets.load_iris() - # Define the evaluation metric as return - return 1 - model.score(X_val, y_val) +def train(config: Configuration, seed: int = 0) -> float: + classifier = SVC(C=config["C"], random_state=seed) + scores = cross_val_score(classifier, iris.data, iris.target, cv=5) + return 1 - np.mean(scores) -if __name__ == "__main__": - # Define your hyperparameters - configspace = ConfigurationSpace() - configspace.add_hyperparameter(UniformIntegerHyperparameter("depth", 2, 100)) - # Provide meta data for the optimization - scenario = Scenario({ - "run_obj": "quality", # Optimize quality (alternatively runtime) - "runcount-limit": 10, # Max number of function evaluations (the more the better) - "cs": configspace, - }) +configspace = ConfigurationSpace({"C": (0.100, 1000.0)}) - smac = SMAC4BB(scenario=scenario, tae_runner=train_random_forest) - best_found_config = smac.optimize() +# Scenario object specifying the optimization environment +scenario = Scenario(configspace, deterministic=True, n_trials=200) +# Use SMAC to find the best configuration/hyperparameters +smac = HyperparameterOptimizationFacade(scenario, train) +incumbent = smac.optimize() ``` -More examples can be found in the [documention](https://automl.github.io/SMAC3). - +More examples can be found in the [documention](https://automl.github.io/SMAC3/main/examples/). ## License @@ -109,15 +106,18 @@ You should have received a copy of the 3-clause BSD license along with this program (see LICENSE file). If not, see [here](https://opensource.org/licenses/BSD-3-Clause). + ## Miscellaneous SMAC3 is developed by the [AutoML Groups of the Universities of Hannover and Freiburg](http://www.automl.org/). -If you have found a bug, please report to [issues](https://github.com/automl/SMAC3/issues). Moreover, we are appreciating any kind of help. -Find our guidlines for contributing to this package [here](https://github.com/automl/SMAC3/blob/master/.github/CONTRIBUTING.md). +If you have found a bug, please report to [issues](https://github.com/automl/SMAC3/issues). Moreover, we are +appreciating any kind of help. Find our guidlines for contributing to this package +[here](https://github.com/automl/SMAC3/blob/master/.github/CONTRIBUTING.md). -If you use SMAC in one of your research projects, please cite our [JMLR paper](https://jmlr.org/papers/v23/21-0888.html): +If you use SMAC in one of your research projects, please cite our +[JMLR paper](https://jmlr.org/papers/v23/21-0888.html): ``` @article{JMLR:v23:21-0888, author = {Marius Lindauer and Katharina Eggensperger and Matthias Feurer and André Biedenkapp and Difan Deng and Carolin Benjamins and Tim Ruhkopf and René Sass and Frank Hutter}, @@ -131,4 +131,4 @@ If you use SMAC in one of your research projects, please cite our [JMLR paper](h } ``` -Copyright (C) 2016-2022 [AutoML Group](http://www.automl.org/). +Copyright (C) 2016-2022 [AutoML Group](http://www.automl.org). diff --git a/changelog.md b/changelog.md index 109d06023..9efc44b47 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,53 @@ +# 2.0.0a1 + +## Big Changes +* We redesigned the scenario class completely. The scenario is implemented as a dataclass now and holds only environment variables (like limitations or save directory). Everything else was moved to the components directly. +* We removed runtime optimization completely (no adaptive capping or imputing anymore). +* We removed the command-line interface and restructured everything alongside. Since SMAC was building upon the command-line interface (especially in combination with the scenario), it was complicated to understand the behavior or find specific implementations. With the removal, we re-wrote everything in python and re-implemented the feature of using scripts as target functions. +* Introducing trials: Each config/seed/budget/instance calculation is a trial. +* The configuration chooser is integrated into the SMBO object now. Therefore, SMBO finally implements an ask-tell interface now. +* Facades are redesigned so that they accept instantiated components directly. If a component is not passed, a default component is used, which is specified for each facade individually in the form of static methods. You can use those static methods directly to adapt a component to your choice. +* A lot of API changes and renamings (e.g., RandomConfigurationChooser -> RandomDesign, Runhistory2EPM -> RunHistoryEncoder). +* Ambiguous variables are renamed and unified across files. +* Dependencies of modules are reduced drastically. +* We incorporated Pynisher 1.0, which ensures limitations cross-platform. +* We incorporated ConfigSpace 0.6, which simplified our examples. +* Examples and documentation are completely reworked. Examples use the new ConfigSpace, and the documentation is adapted to version 2.0. +* Transparent target function signatures: SMAC checks now explicitly if an argument is available (the required arguments are now specified in the intensifier). If there are more arguments that are not passed by SMAC, a warning is raised. +* Components implement a ``meta`` property now, all of which describe the initial state of SMAC. The facade collects all metadata and saves the initial state of the scenario. +* Improved multi-objective in general: RunHistory (in addition to RunHistoryEncoder) both incorporates the multi-objective algorithm. In other words, if the multi-objective algorithm changes the output, it directly affects the optimization process. +* Configspace is saved in json only +* StatusType is saved as integer and not as dict anymore +* We changed the behavior of continuing a run: + * SMAC automatically checks if a scenario was saved earlier. If there exists a scenario and the initial state is the same, SMAC automatically loads the previous data. However, continuing from that run is not possible yet. + * If there was a scenario earlier, but the initial state is different, then the user is asked to overwrite the run or to still continue the run although the state is different (Note that this only can happen if the name specified in the scenario is the same). Alternatively, an `old` to the old run is added (e.g., the name was test, it becomes test-old). + * The initial state of the SMAC run also specifies the name (if no name in the scenario is specified). If the user changes something in the code base or in the scenario, the name and, therefore, the save location automatically changes. + +## New Features +* Added a new termination feature: Use `terminate_cost_threshold` in the scenario to stop the optimization after a configuration was evaluated with a cost lower than the threshold. +* Callbacks are completely redesigned. Added callbacks to the facade are called in different positions in the Bayesian optimization loop. +* The multi-objective algorithm `MeanAggregationStrategy` supports objective weights now. +* RunHistory got more methods like ``get_incumbent`` or ``get_pareto_front``. + +## Fixes +* You ever noticed that the third configuration has no origin? It's fixed now. +* We fixed ParEGO (it updates every time training is performed now). + +## Optimization Changes +* Changed initial design behavior + * You can add additional configurations now. + * ``max_ratio`` will limit both ``n_configs`` and ``n_configs_per_hyperparameter`` but not additional configurations + * Reduced default ``max_ratio`` to 0.1. + +## Code Related +* Converted all unittests to pytests. +* Instances, seeds, and budgets can be set to none now. However, mixing none and non-none will throw an exception. + + # 1.4.0 ## Features -* [BOinG](https://arxiv.org/abs/2111.05834): A two-stage Bayesian optimization approach to allow the +* [BOinG](https://arxiv.org/abs/2111.05834): A two-stage bayesian optimization approach to allow the optimizer to focus on the most promising regions. * [TurBO](https://arxiv.org/abs/1910.01739): Reimplementaion of TurBO-1 algorithm. * Updated pSMAC: Can pass arbitrary SMAC facades now. Added example and fixed tests. @@ -12,8 +58,8 @@ or optionally in `average_cost`/`sum_cost`/`min_cost` to receive a single float the cached cost values do not need to be updated everytime a new entry to the runhistory was added. ## Interface changes -* We changed the location of Gaussian processes and random forests. They are in the folders -`epm/gaussian_process` and `epm/random_forest` now. +* We changed the location of gaussian processes and random forests. They are in the folders +epm/gaussian_process and epm/random_forest now. * Also, we restructured the optimizer folder and therefore the location of the acquisition functions and configuration chooser. * Multi-objective functions are located in the folder `multi_objective`. diff --git a/docs/.DS_Store b/docs/.DS_Store index a1d75c5fbf7adb3d7b5cba12d72b5b2f41124757..a8404af241c0042701a153208c79722d89478031 100644 GIT binary patch delta 76 zcmZoMXfc=|#>B)qu~2NHo+2aj#DLw41(+BanJ4owKITj*PR>cn&(C4p{E(5Kbu&8$ eKL=3lW=5v(%#-;=EIAk%7=Va@VRL}U7G?kkM-o#2 delta 68 zcmZoMXfc=|#>B`mu~2NHo+2aD#DLwC4MbQb^D{lz%*M>fy0Jl=aWgvyKL=3FW<}=j W%#-;=EIAl}fRTZLX>)+c7G?lfIS-fs diff --git a/docs/getting_started/installation.rst b/docs/1_installation.rst similarity index 73% rename from docs/getting_started/installation.rst rename to docs/1_installation.rst index b74afdcd4..2442904de 100644 --- a/docs/getting_started/installation.rst +++ b/docs/1_installation.rst @@ -4,7 +4,7 @@ Installation Requirements ~~~~~~~~~~~~ -SMAC is written in python3 and therefore requires an environment with python>=3.7. +SMAC is written in python3 and therefore requires an environment with python>=3.8. Furthermore, the random forest used in SMAC requires SWIG as a build dependency. Install it either in your environment or on your system directly. The command to install swig on linux machines is the following: @@ -12,24 +12,22 @@ environment or on your system directly. The command to install swig on linux mac apt-get install swig -SMAC is tested on Linux and Mac (Intel) machines with python 3.7, 3.8, and 3.9. -.. warning:: - When using Mac, make sure ``smac.optimize`` is - wrapped inside ``if __name__ == "__main__"``. +SMAC is tested on Linux and Mac machines with python 3.8, 3.9 and 3.10. + Anaconda ~~~~~~~~ -Create and activate environment: +We recommend using Anaconda to create and activate an environment: .. code-block:: - conda create -n SMAC python=3.9 + conda create -n SMAC python=3.10 conda activate SMAC -If you haven't installed swig yet, you can install it directly inside the environment: +If you haven't installed swig yet, you can install it directly inside the Anaconda environment: .. code-block:: @@ -48,15 +46,7 @@ Or alternatively, clone the environment from GitHub directly: .. code-block:: git clone https://github.com/automl/SMAC3.git && cd SMAC3 - pip install -r requirements.txt - pip install . - - -.. warning:: - - Please note that calling SMAC via :term:`CLI` is only available when installing from GitHub. We - refer to :ref:`Branin` for more details. - + pip install -e .[dev] Conda-forge @@ -78,4 +68,3 @@ You must have `conda >= 4.9` installed. To update conda or check your current co Read `SMAC feedstock `_ for more details. - \ No newline at end of file diff --git a/docs/2_package_overview.rst b/docs/2_package_overview.rst new file mode 100644 index 000000000..2cbe72507 --- /dev/null +++ b/docs/2_package_overview.rst @@ -0,0 +1,66 @@ +Package Overview +================ + +SMAC supports you in determining well-performing hyperparameter configurations for your algorithms. By being a robust +and flexible framework for :term:`Bayesian Optimization`, SMAC can improve performance within few function +evaluations. It offers several entry points and pre-sets for typical use cases, such as optimizing +hyperparameters, solving low dimensional continuous (artificial) global optimization problems and configuring algorithms +to perform well across multiple problem :term:`instances`. + + +Features +-------- + +SMAC has following characteristics and capabilities: + +Global Optimizer + :term:`Bayesian Optimization` is used for sample-efficient optimization. + +Optimize :term:`Black-Box` Functions + Optimization is only aware of input and output. It is agnostic to internals of the function. + +Flexible Hyperparameters + Use categorical, continuous, hierarchical and/or conditional hyperparameters with the well-integrated + `ConfigurationSpace `_. SMAC can optimize *up to 100 hyperparameters* + efficiently. + +Any Objectives + Optimization with any :term:`objective` (e.g., accuracy, runtime, cross-validation, ...) is possible. + +:ref:`Multi-Objective` + Optimize arbitrary number of objectives using scalarized multi-ojective algorithms. Both ParEGO [Know06]_ and + mean aggregation strategies are supported. + +:ref:`Multi-Fidelity` Optimization + Judge configurations on multiple :term:`budgets` to discard unsuitable configurations + early on. This will result in a massive speed-up, depending on the budgets. + +:ref:`Instances` + Find well-performing hyperparameter configurations not only for one instance (e.g. dataset) of + an algorithm, but for many. + +Command-Line Interface + SMAC can not only be executed within a python file but also from the commandline. Consequently, + not only algorithms in python can be optimized but in other languages as well. + + .. note :: + + Command-line interface has been temporarely disabled in v2.0. Please fall back to v1.4 if you need it. + + +Comparison +---------- + +The following table provides an overview of SMAC's capabilities in comparison with other optimization tools. + +.. csv-table:: + :header: "Package", "Complex Hyperparameter Space", ":term:`Multi-Objective` ", ":term:`Multi-Fidelity`", ":term:`Instances`", "Command-Line Interface", "Parallelism" + :widths: 10, 10, 10, 10, 10, 10, 10 + + HyperMapper, ✅, ✅, ❌, ❌, ❌, ❌ + Optuna, ✅, ✅, ✅, ❌, ✅, ✅ + Hyperopt, ✅, ❌, ❌, ❌, ✅, ✅ + BoTorch, ❌, ✅, ✅, ❌, ❌, ✅ + OpenBox, ✅, ✅, ❌, ❌, ❌, ✅ + HpBandSter, ✅, ❌, ✅, ❌, ❌, ✅ + SMAC, ✅, ✅, ✅, ✅, ✅, ✅ diff --git a/docs/3_getting_started.rst b/docs/3_getting_started.rst new file mode 100644 index 000000000..0632b222b --- /dev/null +++ b/docs/3_getting_started.rst @@ -0,0 +1,140 @@ +Getting Started +=============== + +In the core, SMAC needs four components (configuration space, target function, scenario and a facade) to run an +optimization process, all of which are explained on this page. + + + +Configuration Space +------------------- + +The configuration space defines the search space of the hyperparameters and, therefore, the tunable parameters' legal +ranges and default values. + +.. code-block:: python + + from ConfigSpace import ConfigSpace + + cs = ConfigurationSpace({ + "myfloat": (0.1, 1.5), # Uniform Float + "myint": (2, 10), # Uniform Integer + "species": ["mouse", "cat", "dog"], # Categorical + }) + +Please see the documentation of `ConfigSpace `_ for more details. + + +Target Function +--------------- + +The target function takes a configuration from the configuration space and returns a performance value. +For example, you could use a Neural Network and predict the performance based on the learning rate. Every configuration +would (most likely) return a different value. However, SMAC tries to find the best learning rate by trying +different and potentially improving configurations. + +.. code-block:: python + + def train(self, config: Configuration, seed: int) -> float: + model = MultiLayerPerceptron(learning_rate=config["learning_rate"]) + model.fit(...) + accuracy = model.validate(...) + + return 1 - accuracy # SMAC always minimizes (the smaller the better) + +.. warning:: + + SMAC *always* minimizes the value returned from the target function. + + +.. note:: + + In general, the arguments of the target function depend on the intensifier. However, + in all cases, the first argument must be the configuration (arbitrary argument name is possible here) and a seed. + If you specified instances in the scenario, SMAC requires ``instance`` as argument additionally. If you use + ``SuccessiveHalving`` or ``Hyperband`` as intensifier but you did not specify instances, SMAC passes `budget` as + argument to the target function. But don't worry: SMAC will tell you if something is missing or if something is not + used. + + +.. warning:: + + SMAC passes either `instance` or `budget` to the target function but never both. + + +Scenario +-------- + +The :ref:`Scenario` is used to provide environment variables. For example, +you want to limit the optimization process by a time limit or want to specify where to save the results. + +.. code-block:: python + + from smac import Scenario + + scenario = Scenario( + configspace=cs, + output_directory=Path("your_output_directory") + walltime_limit=120, # Limit to two minutes + n_trials=500, # Evaluated max 500 trials + n_workers=8, # Use eight workers + ... + ) + + +Facade +------ + +A :ref:`facade` is the entry point to SMAC, which constructs a default optimization +pipeline for you. SMAC offers various facades, which satisfy many use cases and are crucial to +achieving peak performance. The idea behind the facades is to provide a simple interface to SMAC, +which is easy to use and understand without diving deep into the material. However, experts are +invited to change the components as they please to achieve even better performance potentially. The following +table (horizontal scrollable) shows you what is supported and reveals the default :ref:`components`: + + +.. csv-table:: + :header: "", ":ref:`Black-Box`", ":ref:`Hyperparameter Optimization`", ":ref:`Multi-Fidelity`", ":ref:`Algorithm Configuration`", ":ref:`Random`", ":ref:`Hyperband`" + + "#Parameters", "low", "low/medium/high", "low/medium/high", "low/medium/high", "low/medium/high", "low/medium/high" + "Supports Instances", "❌", "✅", "✅", "✅", "❌", "✅" + "Supports Multi-Fidelity", "❌", "❌", "✅", "✅", "❌", "✅" + "Initial Design", ":ref:`Sobol`", ":ref:`Sobol`", ":ref:`Random`", ":ref:`Default`", ":ref:`Default`", ":ref:`Default`" + "Surrogate Model", ":ref:`Gaussian Process`", ":ref:`Random Forest`", ":ref:`Random Forest`", ":ref:`Random Forest`", "Not used", "Not used" + "Acquisition Function", ":ref:`Expected Improvement`", ":ref:`Log Expected Improvement`", ":ref:`Log Expected Improvement`", ":ref:`Expected Improvement`", "Not used", "Not used" + "Acquisition Maximier", ":ref:`Local and Sorted Random Search`", ":ref:`Local and Sorted Random Search`", ":ref:`Local and Sorted Random Search`", ":ref:`Local and Sorted Random Search`", ":ref:`Local and Sorted Random Search`", ":ref:`Local and Sorted Random Search`" + "Intensifier", ":ref:`Default`", ":ref:`Default`", ":ref:`Hyperband`", ":ref:`Hyperband`", ":ref:`Default`", ":ref:`Hyperband`", + "Runhistory Encoder", ":ref:`Default`", ":ref:`Log`", ":ref:`Log`", ":ref:`Default`", ":ref:`Default`", ":ref:`Default`" + "Random Design Probability", "8.5%", "20%", "20%", "50%", "Not used", "Not used" + + +.. note:: + + The multi-fidelity facade is the closest implementation to `BOHB `_. + + +.. note:: + + We want to emphasize that SMAC is a highly modular optimization framework. + The facade accepts many arguments to specify components of the pipeline. Please also note, that in contrast + to previous versions, instantiated objects are passed instead of *kwargs*. + + +The facades can be imported directely from the ``smac`` module. + +.. code-block:: python + + from smac import BlackBoxFacade as BBFacade + from smac import HyperparameterOptimizationFacade as HPOFacade + from smac import MultiFidelityFacade as MFFacade + from smac import AlgorithmConfigurationFacade as ACFacade + from smac import RandomFacade as RFacade + from smac import HyperbandFacade as HBFacade + + smac = HPOFacade(scenario=scenario, target_function=train) + smac = MFFacade(scenario=scenario, target_function=train) + smac = ACFacade(scenario=scenario, target_function=train) + smac = RFacade(scenario=scenario, target_function=train) + smac = HBFacade(scenario=scenario, target_function=train) + + diff --git a/docs/4_minimal_example.rst b/docs/4_minimal_example.rst new file mode 100644 index 000000000..c4eb2eb9a --- /dev/null +++ b/docs/4_minimal_example.rst @@ -0,0 +1,33 @@ +Minimal Example +=============== + +The following code optimizes a support vector machine on the iris dataset. + + +.. code-block:: python + + from ConfigSpace import Configuration, ConfigurationSpace + + import numpy as np + from smac import HyperparameterOptimizationFacade, Scenario + from sklearn import datasets + from sklearn.svm import SVC + from sklearn.model_selection import cross_val_score + + iris = datasets.load_iris() + + + def train(config: Configuration, seed: int = 0) -> float: + classifier = SVC(C=config["C"], random_state=seed) + scores = cross_val_score(classifier, iris.data, iris.target, cv=5) + return 1 - np.mean(scores) + + + configspace = ConfigurationSpace({"C": (0.100, 1000.0)}) + + # Scenario object specifying the optimization environment + scenario = Scenario(configspace, deterministic=True, n_trials=200) + + # Use SMAC to find the best configuration/hyperparameters + smac = HyperparameterOptimizationFacade(scenario, train) + incumbent = smac.optimize() \ No newline at end of file diff --git a/docs/5_api.rst b/docs/5_api.rst new file mode 100644 index 000000000..97be932a6 --- /dev/null +++ b/docs/5_api.rst @@ -0,0 +1,21 @@ +API References +============== + +.. autosummary:: + :template: module.rst + :toctree: api + :recursive: + + smac.facade + smac.main + smac.model + smac.acquisition + smac.intensifier + smac.initial_design + smac.random_design + smac.runner + smac.runhistory + smac.multi_objective + smac.utils + smac.scenario + smac.callback diff --git a/docs/6_references.rst b/docs/6_references.rst new file mode 100644 index 000000000..a5dfd1987 --- /dev/null +++ b/docs/6_references.rst @@ -0,0 +1,22 @@ +References +========== + + +.. [LJDR18] L. Li, K. Jamieson, G. DeSalvo, A. Rostamizadeh, A. Talwalkar; + Hyperband: A Novel Bandit-Based Approach to Hyperparameter Optimization; + https://jmlr.org/papers/v18/16-558.html + + +.. [HSSL22] Carl Hvarfner, Danny Stoll, Artur Souza, Marius Lindauer, Frank Hutter, Luigi Nardi; + πBO: Augmenting Acquisition Functions with User Beliefs for Bayesian Optimization; + https://arxiv.org/pdf/2204.11051.pdf + + +.. [Know06] J. Knowles; + ParEGO: A Hybrid Algorithm with on-Line Landscape Approximation for Expensive Multiobjective Optimization Problems; + https://ieeexplore.ieee.org/document/1583627 + + +.. [SKKS10] N. Srinivas, S. M. Kakade, A. Krause, M. Seeger; + Gaussian Process Optimization in the Bandit Setting: No Regret and Experimental Design; + https://arxiv.org/pdf/0912.3995.pdf \ No newline at end of file diff --git a/docs/7_glossary.rst b/docs/7_glossary.rst new file mode 100644 index 000000000..1d493e17b --- /dev/null +++ b/docs/7_glossary.rst @@ -0,0 +1,98 @@ +Glossary +======== + +.. glossary:: + + SMAC + Sequential Model-Based Algorithm Configuration. + + BO + See :term:`Bayesian Optimization`. + + HB + See :term:`Hyperband`. + + BOHB + `Bayesian optimization and Hyperband `_. + + ROAR + See :term:`Random Online Adaptive Racing`. + + BB + See :term:`Black-Box`. + + MF + See :term:`Multi-Fidelity`. + + RF + Random Forest. + + GP + Gaussian Process. + + GP-MCMC + Gaussian Process with Markov-Chain Monte-Carlo. + + CV + Cross-Validation. + + CLI + Command-Line Interface. + + HP + Hyperparameter. + + Bayesian Optimization + Bayesian optimization is a sequential design strategy for global optimization of black-box functions that does + not assume any functional forms. It is usually employed to optimize expensive-to-evaluate functions. + A Bayesian optimization weights exploration and exploitation to find the minimum of its objective. + + Hyperband + `Hyperband `_. A novel bandit-based algorithm for hyperparameter + optimization. Hyperband is an extension of successive halving and therefore works with + multi-fidelities. + + Random Online Adaptive Racing + Random Online Adaptive Racing. A simple model-free instantiation of the general SMBO framework. + It selects configurations uniformly random and iteratively compares them against the current incumbent + using the intensification mechanism. See `SMAC extended `_ + chapter 3.2 for details. + + Black-Box + Refers to an algorithm being optimized, where only input and output are observable. + + Target Function + Your model, which returns a cost based on the given config, seed, budget, and/or instance. + + Trial + Trial is a single run of a target function on a combination of configuration, seed, budget and/or instance. + + Objective + An objective is a metric to evaluate the quality or performance of an algorithm. + + Multi-Objective + A multi-objective optimization problem is a problem with more than one objective. + The goal is to find a solution that is optimal or at least a good compromise in all objectives. + + Budget + Budget is another word for fidelity. Examples are the number of training epochs or the size of + the data subset the algorithm is trained on. However, budget can also be used in the context of + instances. For example, if you have 100 instances (let's say we optimize across datasets) and you want to run + your algorithm on 10 of them, then the budget is 10. + + Multi-Fidelity + Multi-fidelity refers to running an algorithm on multiple budgets (such as number of epochs or + subsets of data) and thereby evaluating the performance prematurely. + + Instances + Often you want to optimize across different datasets, subsets, or even different transformations (e.g. + augmentation). In general, each of these is called an instance. Configurations are evaluated on multiple + instances so that a configuration found which performs superior on all instances instead of only + a few. + + Intensification + A mechanism, that governs how many evaluations to perform with each configuration and when to trust a + configuration enough to make it the new current best known configuration (the incumbent). + + Incumbent + The incumbent is the current best known configuration. diff --git a/docs/faq.rst b/docs/8_faq.rst similarity index 64% rename from docs/faq.rst rename to docs/8_faq.rst index 286d24ede..9822611a8 100644 --- a/docs/faq.rst +++ b/docs/8_faq.rst @@ -11,8 +11,7 @@ Should I use SMAC2 or SMAC3? SMAC cannot be imported. - Try to either run SMAC from SMAC's root directory - or try to run the installation first. + Try to either run SMAC from SMAC's root directory or try to run the installation first. pyrfr raises cryptic import errors. @@ -21,20 +20,8 @@ pyrfr raises cryptic import errors. :ref:`Installation ` for a solution. -My target algorithm is not accepted when using the scenario-file. - Make sure that your algorithm accepts commandline options as provided by - SMAC. Refer to :ref:`commandline execution ` for - details on how to wrap your algorithm. - - You can also run SMAC with ``--verbose DEBUG`` to see how SMAC tried to call your algorithm. - - -Can I restore SMAC from a previous state? - Yes. Have a look :ref:`here`. - - How can I use :term:`BOHB` and/or `HpBandSter `_ with SMAC? - The facade SMAC4HPO is the closes implementation to :term:`BOHB` and/or `HpBandSter `_. + The facade MultiFidelityFacade is the closes implementation to :term:`BOHB` and/or `HpBandSter `_. I discovered a bug or SMAC does not behave as expected. Where should I report to? @@ -54,25 +41,18 @@ I discovered a bug or SMAC does not behave as expected. Where should I report to I want to contribute code or discuss a new idea. Where should I report to? SMAC uses the `GitHub issue-tracker `_ to also take care of questions and feedback and is the preferred location for discussing new features and ongoing work. Please also have a look at our - `contribution guide `_. + `contribution guide `_. What is the meaning of *deterministic*? - If the ``deterministic`` flag is set to `False` the target algorithm is assumed to be non-deterministic. + If the ``deterministic`` flag is set to `False` the target function is assumed to be non-deterministic. To evaluate a configuration of a non-deterministic algorithm, multiple runs with different seeds will be evaluated to determine the performance of that configuration on one instance. Deterministic algorithms don't depend on seeds, thus requiring only one evaluation of a configuration on an instance to evaluate the performance on that instance. Nevertheless the default seed 0 is still passed to the - target algorithm. - - -I want my algorithm to be optimized across different datasets. How should I realize that? - Generally, you have two options: Validate all datasets within your :ref:`TAE` or use instances. - The significant advantage of instances is that not all datasets necessarily have to be processed. - If the first instances already perform worse, the configuration might be discarded early. This - will lead to a speed-up. + target function. Why does SMAC not run on Colab/Mac and crashes with the error "Child process not yet created"? - SMAC uses pynisher to enforce time and memory limits on the target algorithm runner. However, pynisher may not always - work on specific setups. To overcome this error, it is recommended to set `limit_resources` to false to make SMAC run. + SMAC uses pynisher to enforce time and memory limits on the target function runner. However, pynisher may not always + work on specific setups. To overcome this error, it is recommended to remove limitations to make SMAC run. diff --git a/docs/license.rst b/docs/9_license.rst similarity index 100% rename from docs/license.rst rename to docs/9_license.rst diff --git a/docs/Makefile b/docs/Makefile index 24201379f..d0d8f6e2b 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -3,29 +3,28 @@ BUILDDIR = build SPHINXOPTS = ALLSPHINXOPTS = $(SPHINXOPTS) . -.PHONY: clean html linkcheck examples docs +.PHONY: clean buildapi linkcheck html docs html-noexamples clean: rm -rf $(BUILDDIR)/* rm -rf api rm -rf examples -html: - SPHINX_GALLERY_PLOT=False $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - linkcheck: SPHINX_GALLERY_PLOT=False $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." -examples: +html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." +html-noexamples: + SPHINX_GALLERY_PLOT=False $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." -docs: examples linkcheck +docs: html linkcheck diff --git a/docs/advanced_usage/10_continue.rst b/docs/advanced_usage/10_continue.rst new file mode 100644 index 000000000..976f4f16d --- /dev/null +++ b/docs/advanced_usage/10_continue.rst @@ -0,0 +1,15 @@ +Continue a Run +============== + +SMAC automatically restores states where it left off if a run was interrupted or finished. To do so, it reads in +files (derivided from scenario's name, output_directory and seed) and sets the components. + +.. warning:: + + If you changed any code and specified a name, SMAC will ask you whether you still want to resume or + delete the old run completely. If you not specified a name, SMAC generates a new name and the old run is + not affected. + +Unfortunately, since many components of SMAC have internal states (especially the intensifier), it is not possible to +continue a run from a previous state yet. + diff --git a/docs/advanced_usage/11_reproducability.rst b/docs/advanced_usage/11_reproducability.rst new file mode 100644 index 000000000..3940677a5 --- /dev/null +++ b/docs/advanced_usage/11_reproducability.rst @@ -0,0 +1,16 @@ +Reproducability +=============== + + +The intensifier ``Intensifier`` does not ensure reproducability because it is depending on computation time. +The intensification of configurations are depending on the ``intensifier_percentage`` and how long the optimization +run took so far. Based on that, a ``time_bound`` for the intensification procedure is computed (in ``base_smbo``), +which is further used to determine whether the next iteration should start (``intensifier.process_results``). + +.. warning :: + + The intensifier ``Intensifier`` (used by HyperparameterOptimizationFacade) is *not* reproducable! + + +However, since ``time_bound`` is only used in ``Intensifier``, all other intensifiers guarantee reproducability when +using one worker. \ No newline at end of file diff --git a/docs/advanced_usage/1_components.rst b/docs/advanced_usage/1_components.rst new file mode 100644 index 000000000..cf82a4bdc --- /dev/null +++ b/docs/advanced_usage/1_components.rst @@ -0,0 +1,242 @@ +Components +========== + +Additionally to the basic components mentioned in :ref:`Getting Started`, all other components are +explained in the following to get a better picture of SMAC. These components are all used to guide +the optimization process and simple changes can influence the results drastically. + +Before diving into the components, we shortly want to explain the main Bayesian optimization loop in SMAC. +The :ref:`SMBO` receives all instantiated components from the facade and the logic happens here. +In general, a while loop is used to ask for the next trial, submit it to the runner, and wait for the runner to +finish the evaluation. Since the runner and the SMBO object are decoupled, the while loop continues and asks for even +more trials, which also can be submitted to the runner. If the ask method (which is, by the way, processed by +the intensifier) returns a wait flag, no further trials will be passed to the runner, and SMAC needs to wait until +trials have been evaluated and told to the intensifier. + +Also, the limitations like wallclock time and remaining trials are checked, and callbacks are called in the SMBO class. + + +:ref:`Surrogate Model` +--------------------------------------------------- + +The surrogate model is used to approximate the objective function of configurations. In previous versions, the model was +referred to Empirical Performance Model (EPM). Mostly, Bayesian optimization is used/associated with Gaussian +processes. However, SMAC incorporates random forests as surrogate models, which makes it possible to optimize for higher +dimensional and complex spaces. + +The data used to train the surrogate model is collected by the runhistory encoder from the runhistory. If budgets are +involved, the highest budget which satisfies ``self._min_samples`` (defaults to 1) in :ref:`smac.main.smbo` is +used. If no budgets are used, all observations are used. + +If you are using instances, it is recommended to use instance features. The model is trained on each instance +associated with its features. Imagine you have two hyperparameters, two instances and no instance features, the model +would be trained on: + +.. csv-table:: + :header: "HP 1", "HP 2", "Objective Value" + + "0.1", "0.8", "0.5" + "0.1", "0.8", "0.75" + "505", "7", "2.4" + "505", "7", "1.3" + +You can see that the same inputs lead to different objective values because of two instances. If you associate +each instance with a feature, you would end-up with the following data points: + +.. csv-table:: + :header: "HP 1", "HP 2", "Instance Feature", "Objective Value" + + "0.1", "0.8", "0", "0.5" + "0.1", "0.8", "1", "0.75" + "505", "7", "0", "2.4" + "505", "7", "1", "1.3" + + +Let me explain how the data are received in detail: + +- Intensifier requests new configurations via ``get_next_configurations`` by :ref:`smac.main.smbo`. +- SMBO collects the data via the runhistory encoder which iterates over the runhistory trials. +- The runhistory encoder only collects trials which are in ``considered_states`` and timeout trials. Also, only the + highest budget is considered if budgets are used. In this step, multi-objective values are scalarized using the + ``normalize_costs`` function (uses ``objective_bounds`` from the runhistory) and the multi-objective algorithm. +- In the next step, the selected trial objectives are transformed (e.g., log-transformed, depending on the selected + encoder). +- The hyperparameters might still have inactive values. The model takes care of that after the collected data + are passed from the SMBO object to the model. + + + +:ref:`Acquisition Function` +------------------------------------------------------ + +Acquisition functions are mathematical techniques that guide how the parameter space should be explored during Bayesian +optimization. They use the predicted mean and predicted variance generated by the surrogate model. + +The acquisition function is used by the acquisition maximizer (see next section). Otherwise, SMAC provides +a bunch of different acquisition functions (Lower Confidence Bound, Expected Improvement, Probability Improvement, +Thompson, integrated acquisition functions and prior acquisition functions). We refer to literature +for more information about acquisition functions. + +.. note :: + + The acquisition function calculates the acquisition value for each configuration. However, the configurations + are provided by the acquisition maximizer. Therefore, the acquisition maximizer is responsible for receiving + the next configurations. + + +:ref:`Acquisition Maximizer` +------------------------------------------------------- + +The acquisition maximizer is a wrapper upon the acquisition function and returns the next configurations. SMAC +supports local search, (sorted) random search, local and (sorted) random search, and differential evolution. +While local search checks neighbours of the best configurations, random search makes sure to explore the configuration +space. When using sorted random search, random configurations are sorted by the value of the acquisition function. + +.. warning :: + + Pay attention to the number of challengers: If you experience RAM issues or long computational times in the + acquisition function, you might lower the number of challengers. + +The acquisition maximizer also incorporates the random design. Please see the +:ref:`ChallengerList` for more information. + + +:ref:`Initial Design` +------------------------------------------ + +The surrogate model needs data to be trained. Therefore, the initial design is used to generate the initial data points. +We provide random, latin hypercube, sobol, factorial and default initial designs. The default initial design uses +the default configuration from the configuration space and with the factorial initial design, we generate corner +points of the configuration space. The sobol sequences are an example of quasi-random low-disrepancy sequences and +the latin hypercube design is a statistical method for generating a near-random sample of parameter values from +a multidimensional distribution. + + +:ref:`Random Design` +------------------------------------------ + +The random design is used in the acquisition maximizer to tell whether the next configuration should be +random or sampled from the acquisition function. For example, if we use a random design with a probability of +50%, we have a 50% chance to sample a random configuration and a 50% chance to sample a configuration from the +acquisition function (although the acquisition function includes exploration and exploitation trade-off already). +This design makes sure that the optimization process is not stuck in a local optimum and we +are *guaranteed* to find the best configuration over time. + +In addition to simple probability random design, we also provide annealing and modulus random design. + + +:ref:`Intensifier` +------------------------------------ + +The intensifier compares different configurations based on evaluated :term:`trial` so far. It decides +which configuration should be `intensified`` or in other words if a configuration is worth to spend more time on (e.g., +evaluate another seed pair, evaluate on another instance, or evaluate on a higher budget). + +.. warning :: + + Always pay attention to ``max_config_calls``: If this argument is set high, the intensifier might spend a lot of + time on a single configuration. Also, since the default ``Intensifier`` is depending on runtime, reproducibility + is not given unless you set ``intensify_percentage`` to 0. + + +Depending on the components and arguments, the intensifier tells you which seeds, budgets, and/or instances +are used throughout the optimization process. You can use the methods ``uses_seeds``, ``uses_budgets``, and +``uses_instances`` (directly callable via the facade) to (sanity-)check whether the intensifier uses these arguments. +If you want to know the exact values, use ``get_target_function_seeds``, ``get_target_function_budgets``, and +``get_target_function_instances``. + + + +:ref:`Multi-Objective Algorithm` +-------------------------------------------------------- + +The multi-objective algorithm is used to scalarize multi-objective values. The multi-objective algorithm +gets normalized objective values passed and returns a single value. The resulting value (called by the +runhistory encoder) is then used to train the surrogate model. +The runhistory has access to the multi-objective algorithm as well which plays a role in the method ``get_cost``. +The method ``get_cost`` is used to compare configurations in the intensifier and therefore to determine the +incumbent. + +.. warning :: + + Depending on the multi-objective algorithm, the incumbent might be ambiguous because there might be multiple + incumbents on the Pareto front. Let's take ParEGO for example: + Everytime a new configuration is sampled, the objective weights are updated (see runhistory encoder). Therefore, + calling the ``get_incumbent`` method in the runhistory might return a different configuration based on the internal state + of the multi-objective algorithm. + + +:ref:`RunHistory` +--------------------------------------------- + +The runhistory holds all (un-)evaluated trials of the optimization run. You can use the runhistory to +get configs, the :term:`incumbent`, (min/sum/average) cost of configs, trials of a config, and more. +The runhistory encoder iterates over the runhistory to receive data for the surrogate model. The following +code shows how to iterate over the runhistory: + +.. code-block:: python + + smac = HPOFacade(...) + + # Iterate over all trials + for trial_info, trial_value in smac.runhistory.items(): + # Trial info + config = trial_info.config + instance = trial_info.instance + seed = trial_info.seed + + # Trial value + cost = trial_value.cost + time = trial_value.time + status = trial_value.status + starttime = trial_value.starttime + endtime = trial_value.endtime + additional_info = trial_value.additional_info + + # Iterate over all configs + for config in smac.runhistory.get_configs(): + # Get the cost of all trials of this config + average_cost = smac.runhistory.average_cost(config) + + +:ref:`RunHistory Encoder` +-------------------------------------------------- + +The runhistory encoder is used to encode the runhistory data into a format that can be used by the surrogate model. +Only trials with the status ``considered_states`` and timeout trials are considered. Multi-objective values are +scalarized using the ``normalize_costs`` function (uses ``objective_bounds`` from the runhistory). Afterwards, the +normalized value is processed by the multi-objective algorithm. + + +:ref:`Callback` +------------------------------ + +Callbacks provide the ability to easily execute code before, inside, and after the Bayesian optimization loop. +To add a callback, you have to inherit from ``smac.Callback`` and overwrite the methods (if needed). +Afterwards, you can pass the callbacks to any facade. + +.. code-block:: python + + from smac import MultiFidelityFacade, Callback + + + class CustomCallback(Callback): + def on_start(self, smbo: SMBO) -> None: + pass + + def on_end(self, smbo: SMBO) -> None: + pass + + def on_iteration_start(self, smbo: SMBO) -> None: + pass + + def on_iteration_end(self, smbo: SMBO, info: RunInfo, value: RunValue) -> bool | None: + # We just do a simple printing here + print(info, value) + + + smac = MultiFidelityFacade( + ... + callbacks=[CustomCallback()] + ) + smac.optimize() \ No newline at end of file diff --git a/docs/advanced_usage/2_multi_fidelity.rst b/docs/advanced_usage/2_multi_fidelity.rst new file mode 100644 index 000000000..6519f3666 --- /dev/null +++ b/docs/advanced_usage/2_multi_fidelity.rst @@ -0,0 +1,18 @@ +Multi-Fidelity Optimization +=========================== + +Multi-fidelity refers to running an algorithm on multiple budgets (such as number of epochs or +subsets of data) and thereby evaluating the performance prematurely. You can run a multi-fidelity optimization +when using :ref:`Successive Halving` or +:ref:`Hyperband`. `Hyperband` is the default intensifier in the +:ref:`multi-fidelity facade` and requires the arguments +``min_budget`` and ``max_budget`` in the scenario if no instances are used. + +In general, multi-fidelity works for both real-valued and instance budgets. In the real-valued case, +the budget is directly passed to the target function. In the instance case, the budget is not passed to the +target function but ``min_budget`` and ``max_budget`` are used internally to determine the number of instances of +each stage. That's also the reason why ``min_budget`` and ``max_budget`` are *not* required when using instances: +The ``max_budget`` is simply the max number of instances, whereas the ``min_budget`` is simply 1. + +Please have a look into our :ref:`multi-fidelity examples` to see how to use +multi-fidelity optimization in real-world applications. \ No newline at end of file diff --git a/docs/advanced_usage/3_multi_objective.rst b/docs/advanced_usage/3_multi_objective.rst new file mode 100644 index 000000000..afab1ccd6 --- /dev/null +++ b/docs/advanced_usage/3_multi_objective.rst @@ -0,0 +1,45 @@ +Multi-Objective Optimization +============================ + +Often we do not only want to optimize just a single objective, but multiple instead. SMAC offers a multi-objective +optimization interface to do exactly that. Right now, the algorithm used for this is a mean aggregation strategy or +ParEGO [Know06]_. In both cases, multiple objectives are aggregated into a single scalar objective, which is then +optimized by SMAC. However, the run history still keeps the original objectives. + + +The basic recipe is as follows: + +#. Make sure that your target function returns a cost *dictionary* containing the objective names as keys + and the objective values as values, e.g. ``{'myobj1': 0.3, 'myobj2': 200}``. Alternatively, you can simply + return a list, e.g., ``[0.3, 200]``. +#. When instantiating SMAC pass the names of your objectives to the scenario object via the ``objectives`` + argument, e.g., ``multi_objectives: ["myobj1", "myobj2"]``. +#. Now you can optionally pass a custom multi-objective algorithm class to the SMAC + facade (via ``multi_objective_algorithm``). In all facades, a mean aggregation strategy is used as the + multi-objective algorithm default. + + +.. warning :: + + Depending on the multi-objective algorithm, the incumbent might be ambiguous because there might be multiple + incumbents on the Pareto front. Let's take ParEGO for example: + Everytime a new configuration is sampled, the weights are updated (see runhistory encoder). Therefore, calling + the ``get_incumbent`` method in the runhistory might return a different configuration based on the internal state + of the multi-objective algorithm. + + +You can use ``get_pareto_front`` in the run history to get the configurations on the Pareto front. + + +.. code-block:: python + + smac = ... + smac.optimize() + + for incumbent, costs in smac.get_pareto_front(): + print(incumbent, costs) + + + + +We show an example of how to use multi-objective with plots in our :ref:`examples`. diff --git a/docs/advanced_usage/4_instances.rst b/docs/advanced_usage/4_instances.rst new file mode 100644 index 000000000..9df90b4b8 --- /dev/null +++ b/docs/advanced_usage/4_instances.rst @@ -0,0 +1,45 @@ +Optimization across Instances +============================= + +Often you want to optimize the cost across different datasets, subsets, or even different +augmentations. For this purpose, you can use instances. + +To work with instances, you need to add your pre-defined instance names to the scenario object. +In the following example, we want to use five different subsets, identified by its id: + +.. code-block:: python + + instances = ["d0", "d1", "d2", "d3", "d4"] + scenario = Scenario( + ... + "instances": instances, + ... + ) + + +Additionally to the instances, there is the option to define ``instance_features``. Those instance features are +used to expand the internal X matrix and thus play a role in training the underlying surrogate model. For example, if I +want to add the number of samples and the mean of each subset, I can do as follows: + +.. code-block:: bash + + instance_features = { + "d0": [121, 0.6], + "d1": [140, 0.65], + "d2": [99, 0.45], + "d3": [102, 0.59], + "d4": [132, 0.48], + } + + scenario = Scenario( + ... + "instances": instances, + "instance_features": instance_features + ... + ) + + +.. warning :: + + If you use the multi-fidelity facade, instances are used as budget type. Therefore, the target function is + called with an instance instead of a budget. \ No newline at end of file diff --git a/docs/advanced_usage/5_ask_and_tell.rst b/docs/advanced_usage/5_ask_and_tell.rst new file mode 100644 index 000000000..329c048df --- /dev/null +++ b/docs/advanced_usage/5_ask_and_tell.rst @@ -0,0 +1,41 @@ +Ask-and-Tell Interface +====================== + +SMAC provides an ask-and-tell interface in v2.0, giving the user the opportunity to ask for the next trial +and report the results of the trial. However, the ask-and-tell interfaces comes in v2.0 with some +limitations which you should be aware of. + +.. note :: + + The initial design is already part of the ask and tell. Therefore, if you ask for the first trials, + you will receive configurations from the initial design. + + +.. warning :: + + Using ask-and-tell instead of the optimize method might result in different results because + some callbacks are ignored and skipped trials are handled differently. In fact, skipped trials + are ignored completely and can results in being stuck when only skipped trials are found. + +Please have a look at our :ref:`ask-and-tell example`. + + +Calling Tell without Ask +------------------------ + +Sometimes you want to report pre-evaluated trials to the optimization. You can realize this by calling the +``tell`` method without calling ``ask`` before. But be aware that this *only* works with the ``Intensifier`` and not +with ``Successive Halving`` or ``Hyperband``. The ``Intensifier`` checks the run history and detects pre-evaluated +trials and incorporates it into the optimization. Since ``Successive Halving`` and ``Hyperband`` are more +complicated (needs specific budgets and pre-defined number of configurations in each stage), it is not supported yet. + +.. warning :: + + Calling ``tell`` without ``ask`` does only work for specific intensifiers. + + +Calling Multiple Tells +---------------------- + +Calling multiple times ``tell`` before ``ask`` is not supported yet. + diff --git a/docs/advanced_usage/6_commandline.rst b/docs/advanced_usage/6_commandline.rst new file mode 100644 index 000000000..4d6516357 --- /dev/null +++ b/docs/advanced_usage/6_commandline.rst @@ -0,0 +1,92 @@ +Command-Line Interface +====================== + +The command-line interface enables the user to run target functions which are non-python code. +The passed and further called script (using `Popen`) needs to return a standard output which is then interpreted +to perform the optimization process. + +.. note :: + + In SMAC v2.0, SMAC can not be called from the command-line directly. Instead, the user should use the python + interface to call SMAC. The command-line interface is still available in SMAC v1.4. + + + +Call of the Target Function +--------------------------- + +The following example shows how the script is called: + +.. code-block:: bash + + filename --instance=test --instance_features=test --seed=0 --hyperparameter1=5323 + +However, as for the python target function, the arguments like instance or budget are depending on which +components are used. The hyperparameters are depending on the configuration space. The variable ``filename`` could be +something like ``./path/to/your/script.sh``. + +We recommend using following code to receive the arguments in a bash script. Please note, that the user is not limited +to bash scripts but can also use executables, python scripts or anything else. + +.. note :: + + Since the script is called wih the filename only, make sure to mark the type of the file (e.g., ``#!/bin/bash`` + or ``#!/usr/bin/env python``). + +.. warning :: + + Everytime an instance is passed, also an instance feature in form of a comma-separated list (no spaces) of floats is + passed. If no instance feature for the instance is given, an empty list is passed. + + +.. code-block:: bash + + #!/bin/bash + + # Set arguments first + for argument in "$@" + do + key=$(echo $argument | cut -f1 -d=) + value=$(echo $argument | cut -f2 -d=) + + if [[ $key == *"--"* ]]; then + v="${key/--/}" + declare $v="${value}" + fi + done + + echo $instance + echo $hyperparameter1 + + +Return of the Target Function +----------------------------- + +The script must return an stdout (echo or print) in the following form (white-spaces are ignored): + +.. code-block:: + + cost=0.5; runtime=0.01; status=SUCCESS; additional_info=test (single-objective) + cost=0.5, 0.4; runtime=0.01; status=SUCCESS; additional_info=test (multi-objective) + +All arguments are optional except cost and are separated by a semicolon. The string of the status must match +one of the values from :ref:`StatusType`. + + +Start the Optimization +---------------------- + +The optimization will be started by the normal python interface. The only difference is that you need to pass +a string as target function instead of a python function. + +.. warning :: + + Your script needs to have rights to be executed (e.g., update the rights with ``chmod``). + +.. code-block:: python + + ... + smac = BlackBoxFacade(scenario, target_function="./path/to/your/script.sh") + incumbent = smac.optimize() + ... + diff --git a/docs/advanced_usage/7_stopping_criteria.rst b/docs/advanced_usage/7_stopping_criteria.rst new file mode 100644 index 000000000..f232b5374 --- /dev/null +++ b/docs/advanced_usage/7_stopping_criteria.rst @@ -0,0 +1,35 @@ +Stopping Criteria +================= + +In addition to the standard stopping criteria like number of trials or wallclock time, SMAC also provides +more advanced criteria. + + +Termination Cost Threshold +-------------------------- + +SMAC can stop the optimization process after a user-defined cost was reached. In each iteration, the average cost +(using ``average_cost`` from the run history) from the incumbent is compared to the termination cost threshold. If one +of the objective cost is below its associated termination cost threshold, the optimization process is stopped. +Note, since the ``average_cost`` method is used, all instance-seed-budget trials of the incumbent are considered so far. +In other words, the process can be stopped even if the incumbent has not been evaluated on all instances, on the +highest fidelity, or on all seeds. + + +.. code-block:: python + + scenario = Scenario( + ... + objectives=["accuracy", "runtime"], + termination_cost_threshold=[0.1, np.inf] + ... + ) + +In the code above, the optimization process is stopped if the average accuracy of the incumbent is below 0.1. The +runtime is ignored completely as it is set to infinity. Note here again that SMAC minimizes the objective values. + + +Automatically Stopping +---------------------- + +Coming in the next version of SMAC. \ No newline at end of file diff --git a/docs/advanced_usage/8_logging.rst b/docs/advanced_usage/8_logging.rst new file mode 100644 index 000000000..688d61a85 --- /dev/null +++ b/docs/advanced_usage/8_logging.rst @@ -0,0 +1,73 @@ +Logging +======= + +Logging is a crucical part of the optimization, which should be customizable by the user. This page gives you the +overview how to customize the logging experience with SMAC. + +Level +----- + +The easiest way to change the logging behaviour is to change the level of the global logger. SMAC does this for you +if you specify the ``logging_level`` in any facade. + +.. code-block:: python + + smac = Facade( + ... + logging_level=20, + ... + ) + + +The table shows you the specific levels: + +.. csv-table:: + :header: "Name", "Level" + + 0, SHOW ALL + 10, DEBUG + 20, INFO + 30, WARNING + 40, ERROR + 50, CRITICAL + + +Custom File +----------- + +Sometimes, the user wants to disable or highlight specifiy modules. You can do that by passing a custom yaml +file to the facade instead. + +.. code-block:: python + + smac = Facade( + ... + logging_level="path/to/your/logging.yaml", + ... + ) + + +The following file shows you how to display only error messages from the intensifier +but keep the level of everything else on INFO: + +.. code-block:: yaml + + version: 1 + disable_existing_loggers: false + formatters: + simple: + format: '[%(levelname)s][%(filename)s:%(lineno)d] %(message)s' + handlers: + console: + class: logging.StreamHandler + level: INFO + formatter: simple + stream: ext://sys.stdout + loggers: + smac.intensifier: + level: ERROR + handlers: [console] + root: + level: INFO + handlers: [console] + diff --git a/docs/advanced_usage/9_parallelism.rst b/docs/advanced_usage/9_parallelism.rst new file mode 100644 index 000000000..efcb95291 --- /dev/null +++ b/docs/advanced_usage/9_parallelism.rst @@ -0,0 +1,5 @@ +Parallelism +=========== + +SMAC supports multiple workers when using ``Successive Halving`` or ``Hyperband`` natively. +Just specify ``n_workers`` in the scenario and you are ready to go. \ No newline at end of file diff --git a/docs/advanced_usage/index.rst b/docs/advanced_usage/index.rst new file mode 100644 index 000000000..bdc389d9f --- /dev/null +++ b/docs/advanced_usage/index.rst @@ -0,0 +1,27 @@ +Advanced Usage +============== + +In this chapter, we will discuss some more advanced usage of the library. If you want to customize +SMAC to your needs, we strongly recommend reading all pages. Since SMAC holds a lot of complex dependencies, +we can not guarantee that your customization will work. However, we can give you some hints on how SMAC +reacts to certain things. + + +Navigation +---------- + +.. toctree:: + :maxdepth: 2 + + 1_components + 2_multi_fidelity + 3_multi_objective + 4_instances + 5_ask_and_tell + 6_commandline + 7_stopping_criteria + 8_logging + 9_parallelism + 10_continue + 11_reproducability + diff --git a/docs/api.rst b/docs/api.rst deleted file mode 100644 index f22ec8950..000000000 --- a/docs/api.rst +++ /dev/null @@ -1,19 +0,0 @@ -API References -============== - -.. autosummary:: - :toctree: api - :recursive: - - smac.epm - smac.facade - smac.initial_design - smac.intensification - smac.optimizer - smac.runhistory - smac.scenario - smac.smac_cli - smac.stats - smac.tae - smac.callbacks - smac.utils diff --git a/docs/conf.py b/docs/conf.py index 9b16fe2de..88a45a819 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,7 +1,9 @@ import automl_sphinx_theme from smac import copyright, author, version, name -from smac.utils.io.cmd_reader import CMDReader +from sphinx_gallery.sorting import FileNameSortKey + +# from smac.cli.cmd_reader import CMDReader options = { @@ -10,19 +12,21 @@ "version": version, "name": name, "html_theme_options": { - "github_url": "https://github.com/https://github.com/automl/SMAC3", + "github_url": "https://github.com/automl/SMAC3", "twitter_url": "https://twitter.com/automl_org?lang=de", }, - "ignore_pattern": ".*pcs$|.*scenario.txt$|.*spear_qcp$", + # "ignore_pattern": ".*pcs$|.*scenario.txt$|.*spear_qcp$", "sphinx_gallery_conf": { "plot_gallery": True, + "within_subsection_order": FileNameSortKey, + "filename_pattern": "/", # We want to execute all files in `examples` }, } automl_sphinx_theme.set_options(globals(), options) # Write outputs -cmd_reader = CMDReader() -cmd_reader.write_main_options_to_doc() -cmd_reader.write_smac_options_to_doc() -cmd_reader.write_scenario_options_to_doc() +# cmd_reader = CMDReader() +# cmd_reader.write_main_options_to_doc() +# cmd_reader.write_smac_options_to_doc() +# cmd_reader.write_scenario_options_to_doc() diff --git a/docs/details/arguments.rst b/docs/details/arguments.rst deleted file mode 100644 index c9d9c3c8a..000000000 --- a/docs/details/arguments.rst +++ /dev/null @@ -1,22 +0,0 @@ -Arguments -========= - -If you are using Python, have a look :ref:`here` for a detailed API reference. When -using the commandline, view the basic command options via - -.. code-block:: - - python scripts/smac.py --help - -or view all other options in the following: - - -Main Options -^^^^^^^^^^^^ - -.. include:: ../main_options.rst - - -SMAC Options -^^^^^^^^^^^^ -.. include:: ../smac_options.rst \ No newline at end of file diff --git a/docs/details/callbacks.rst b/docs/details/callbacks.rst deleted file mode 100644 index b885784f5..000000000 --- a/docs/details/callbacks.rst +++ /dev/null @@ -1,23 +0,0 @@ -Callbacks -========= - -Callbacks allow customizing the behavior of SMAC to ones needs. Currently, the list of -implemented callbacks is very limited, but they can easily be added. - - -How to add a new callback -^^^^^^^^^^^^^^^^^^^^^^^^^ - -* Implement a callback class in ``smac/callbacks.py``. There are no restrictions on how such a - callback must look like, but it is recommended to implement the main logic inside the `__call__` - function, such as for example in ``IncorporateRunResultCallback``. - -* Add your callback to ``smac.smbo.optimizer.SMBO._callbacks``, using the name of your callback - as the key, and an empty list as the value. - -* Add your callback to ``smac.smbo.optimizer.SMBO._callback_to_key``, using the callback class as - the key, and the name as value (the name used in 2.). - -* Implement calling all registered callbacks at the correct place. This is as simple as - ``for callback in self._callbacks['your_callback']: callback(*args, **kwargs)``, where you - obviously need to change the callback name and signature. \ No newline at end of file diff --git a/docs/details/cuda.rst b/docs/details/cuda.rst deleted file mode 100644 index ebe08f5cd..000000000 --- a/docs/details/cuda.rst +++ /dev/null @@ -1,5 +0,0 @@ -CUDA -==== - -.. note:: - This page is under construction. \ No newline at end of file diff --git a/docs/details/facades.rst b/docs/details/facades.rst deleted file mode 100644 index 584ddabb9..000000000 --- a/docs/details/facades.rst +++ /dev/null @@ -1,44 +0,0 @@ -Facades -------- - -SMAC of course itself offers a lot of design choices, some of which are crucial to achieve peak performance. -Luckily, often it is sufficient to distinguish between a few problem classes. -To make the usage of SMAC as easy as possible, we provide several facades designed for these different use cases. -Here we give some general recommendations on when to use which facade. -These recommendations are based on our experience and technical limitations and is by far not intended to be complete: - -.. csv-table:: - :header: "", "SMAC4BB", "SMAC4HPO", "SMAC4MF", "SMAC4AC" - :widths: 15, 10, 10, 10, 10 - - "# parameter", "low", "low/medium/high", "low/medium/high", "low/medium/high" - "Categorical hyperparameters", "Supported", "Supported", "Supported", "Supported" - "Conditional hyperparameters", "Supported", "Supported", "Supported", "Supported" - "Instances", "No", "None or :term:`CV`", "None or :term:`CV`", "Yes" - "Stochasticity", "No", "Supported", "Supported", "Supported" - "Objective", "Any (except runtime)", "e.g. validation loss", "e.g. validation loss", "Any" - "Multi-Fidelity", "No", "No", "Yes", "Yes" - "Search Strategy", ":term:`Gaussian Process` or :term:`GP-MCMC`", ":term:`Random Forest`", ":term:`Random - Forest`", ":term:`Random Forest`, :term:`Gaussian Process`, :term:`GP-MCMC` or Random" - - -.. note:: - - The ``SMAC4MF`` facade is the closest implementation to - `BOHB `_. - - -Inheritance -~~~~~~~~~~~ - -Here we show the class inheritance of the different facades. -Because SMAC4AC is the facade every other facade is inherited from, we recommend using SMAC4AC if a lot of flexibility is needed. - -.. figure:: ../images/smac_facades_all_classes.png - :width: 100% - :align: center - :alt: Class diagram of the SMAC facades. - :figclass: align-center - - Class inheritance of the SMAC facades. - diff --git a/docs/details/index.rst b/docs/details/index.rst deleted file mode 100644 index 32b3959af..000000000 --- a/docs/details/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -Details -======= - -This chapter gives more details of single components of SMAC. - -.. toctree:: - :maxdepth: 2 - :hidden: - - facades - target_algorithm_evaluator - scenario - arguments - multi_objective - run_history - instances - validation - callbacks - parallelism - cuda - restoring - diff --git a/docs/details/instances.rst b/docs/details/instances.rst deleted file mode 100644 index b61399106..000000000 --- a/docs/details/instances.rst +++ /dev/null @@ -1,47 +0,0 @@ -Instances and Features -====================== - -Often you want to optimize the cost across different datasets, subsets, or even different -transformations. For this purpose, you can use instances. A randomly selected instance is passed to -the target algorithm evaluator, in which you can access it. - -To work with instances, you need to add your predefined instances (list) to the scenario object. The -items of the instances can be chosen individually. In the following example, -I want to use five different subsets, identified by its id: - -.. code-block:: bash - - instances = [0, 1, 2, 3, 4] - scenario = Scenario({ - ... - 'instances': instances, - ... - }) - - -Alternatively, you can also pass ``instance_file`` to the scenario object. -Additionally to the instances, there is the option to define ``features``. Those instance features are -used to expand the internal X matrix and thus play a role in training the underlying optimizer. -See `here `_ for -the exact implementation. - -For example, if I want to add the number of samples and the mean of each subset, I can do as -follows: - -.. code-block:: bash - - instance_features = { - 0: [121, 0.6], - 1: [140, 0.65], - 2: [99, 0.45], - 3: [102, 0.59], - 4: [132, 0.48], - } - scenario = Scenario({ - ... - 'instances': instances, - 'features': instance_features - ... - }) - - diff --git a/docs/details/multi_objective.rst b/docs/details/multi_objective.rst deleted file mode 100644 index 85df0ef8b..000000000 --- a/docs/details/multi_objective.rst +++ /dev/null @@ -1,31 +0,0 @@ -Multi-Objective Optimization -============================ - -Often we do not only want to optimize just cost or runtime, but both or other objectives instead. -SMAC offers a multi-objective optimization interface to do exactly that. -Right now, the algorithm used for this is `ParEgo`_ [Christescu & Knowles, 2015]. -`ParEgo`_ weights and sums the individual objectives so that we can optimize a single scalar. - -The costs returned by your target algorithm are stored as usual in the runhistory object, such that -you can recover the Pareto front later on. - - -The basic recipe is as follows: - -#. Make sure that your target algorithm returns a cost *dictionary* containing the objective names as keys - and the objective values as values, e.g. ``{'myobj1': 0.3, 'myobj2': 200}``. Alternatively, you can simply - return a list, e.g ``[0.3, 200]``. -#. When instantiating SMAC pass the names of your objectives to the scenario object via the ``multi_objectives`` - argument, e.g. ``multi_objectives = "myobj1, myobj2"`` or ``multi_objectives = ["myobj1", "myobj2"]``. - Please set ``run_obj = 'quality'``. -#. Now you can optionally pass a custom multi-objective algorithm class or further kwargs to the SMAC - facade (via ``multi_objective_algorithm`` and/or ``multi_objective_kwargs``). - Per default, a mean aggregation strategy is used as the multi-objective algorithm. - - -We show an example of how to use multi-objective with a nice Pareto front plot in our examples: -:ref:`Scalarized Multi-Objective Using ParEGO`. - - -.. _ParEgo: https://www.cs.bham.ac.uk/~jdk/UKCI-2015.pdf -.. _example: https://github.com/automl/SMAC3/blob/master/examples/python/scalarized_multi_objective.py diff --git a/docs/details/parallelism.rst b/docs/details/parallelism.rst deleted file mode 100644 index 28e100733..000000000 --- a/docs/details/parallelism.rst +++ /dev/null @@ -1,70 +0,0 @@ -Parallelism -=========== - -SMAC also provides a parallel mode to use several parallel computational resources (such as CPU cores). -This variant of SMAC is called pSMAC (parallel SMAC) [1]_. -The general idea is that all target algorithm run evaluations are shared between the individual SMAC runs -such that all SMAC runs are better informed and can work together. - -.. warning:: - - To use pSMAC, please note that it communicates via the file space, - i.e., all pSMAC runs write from time to time its runhistory (all target algorithm evaluations) - to disk and read the runhistories of all other pSMAC runs. - So, a requirement for pSMAC is that it can write to a shared file space. - - -.. note:: - - SMAC also supports DASH. The documentation is in progress. - - -.. [1] Ramage, S. E. A. (2015). Advances in meta-algorithmic software libraries for - distributed automated algorithm configuration (T). University of British - Columbia. Retrieved from - https://open.library.ubc.ca/collections/ubctheses/24/items/1.0167184. - - -Commandline -~~~~~~~~~~~ -To use pSMAC via the commandline interface, please specify the following two arguments: - -.. code-block:: bash - - --shared_model True --input_psmac_dirs - -``shared_model`` will activate the information sharing between SMAC runs and -``input_psmac_dirs`` specifies the output directories. - -.. note:: - - pSMAC has no option to specify the number of parallel runs. You have to start as many pSMAC runs as you want to run. - -On the command line an exemplary call could be: - -.. code-block:: bash - - python3 smac --scenario SCENARIO --seed INT --shared_model True --input_psmac_dirs smac3-output* - -If you want to verify that all arguments are correct and pSMAC finds all files on the file space, -please set the ``verbose`` level to DEBUG and grep in the following way: - -.. code-block:: bash - - python3 smac --verbose DEBUG [...] | grep -E "Loaded [0-9]+ new runs" - -.. warning:: - We recommend that each pSMAC uses a different random seed. - -Usage in Python -~~~~~~~~~~~~~~~ - -The same arguments used on the commandline can also be passed to the *Scenario* constructor. -See above for a detailed description. - -.. code-block:: python - - scenario = Scenario({"shared_model": True, "input_psmac_dirs": }) - - - diff --git a/docs/details/restoring.rst b/docs/details/restoring.rst deleted file mode 100644 index a7be6636f..000000000 --- a/docs/details/restoring.rst +++ /dev/null @@ -1,29 +0,0 @@ -Restoring -========= - -Python -~~~~~~ - -.. note:: - This chapter is under construction. - - -Commandline -~~~~~~~~~~~ - -If a SMAC run was interrupted or you want to extend its computation- or -time-limits, it can be restored and continued. -To restore or continue a previous SMAC run, use the -``--restore_state FOLDER`` option in the commandline. If you want to increase -computation- or time-limits, change the scenario-file specified with the -``--scenario SCENARIOFILE`` option (not the one in the folder to be restored). -Restarting a SMAC run that quit due to budget-exhaustion will do nothing, -because the budget is still exhausted. - -.. warning:: - Changing any other options than ``output_dir``, ``wallclock_limit``, ``runcount_limit`` or - ``tuner-timeout`` in the scenario-file is NOT intended and will likely lead - to unexpected behaviour! - -For an example of restoring states from within your Python code, there is an -implementation with the Branin-example in "examples/quickstart/branin/restore_state.py". \ No newline at end of file diff --git a/docs/details/run_history.rst b/docs/details/run_history.rst deleted file mode 100644 index fcbd9e256..000000000 --- a/docs/details/run_history.rst +++ /dev/null @@ -1,16 +0,0 @@ -Run-History -=========== - - -Iterating over Run-History -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: - - smac = SMAC4AC(...) - smac.optimize(...) - rh = smac.get_runhistory() - for (config_id, instance_id, seed, budget), (cost, time, status, starttime, endtime, additional_info) in rh.data.items(): - config = rh.ids_config[config_id] - ... - diff --git a/docs/details/scenario.rst b/docs/details/scenario.rst deleted file mode 100644 index cfe901a24..000000000 --- a/docs/details/scenario.rst +++ /dev/null @@ -1,49 +0,0 @@ -Scenario -======== - -The scenario-object is used to configure SMAC and can be constructed either by providing an acutal -scenario-object or by specifing the options in a scenario file. - -Create Scenario -~~~~~~~~~~~~~~~ - -Specify the options directly: - -.. code-block:: python - - from smac.scenario.scenario import Scenario - - scenario = Scenario({ - 'option': 'value', - ... - }) - -or specify the options in a scenario.txt file: - -.. code-block:: - - OPTION1 = VALUE1 - OPTION2 = VALUE2 - -Use ``"true"`` or ``"false"`` for boolean values, respectively. - -Finally, create the scenario object by handing over the filename: - -.. code-block:: python - - from smac.scenario.scenario import Scenario - - scenario = Scenario("/path/to/your/scenario.txt") - - -Options -~~~~~~~ - -Following options can be defined within the scenario object: - -.. include:: ../scenario_options.rst - -Additionally, you can also specify SMAC options with the scenario object: - -.. include:: ../smac_options.rst - diff --git a/docs/details/target_algorithm_evaluator.rst b/docs/details/target_algorithm_evaluator.rst deleted file mode 100644 index ee4952ed2..000000000 --- a/docs/details/target_algorithm_evaluator.rst +++ /dev/null @@ -1,91 +0,0 @@ -Target Algorithm Evaluator -========================== - -SMAC evaluates the algorithm to be optimized by invoking it through a Target -Algorithm Evaluator (TAE). There are different TAEs implemented in SMAC which -provide slightly different interfaces. - - -Python ------- - -The target algorithm is called with a dict- or array-like configuration and optionally -with seed and instance, returning either the loss as a float or a tuple (loss, -additional information). This is very handy when used within Python to -optimize any blackbox-function. Using this TAE, there is no need to print a -result string; the error will be interpreted from the return object. With this TAE, `Pynisher `_ is used to -enforce time- and memory-limits. - -.. warning:: - - SMAC is always minimizing, meaning that smaller values are better. For example, if you want to - optimize accuracy, you have to use ``1 - accuracy`` for correct usage. - -The following example shows the interface for all possible arguments: - -.. code-block:: python - - train_and_validate_your_model(config, budget, instance, seed): - # ... - return 1 - accuracy - - -If you want to return additional information, which will be written into the RunHistory, you can -follow the next example: - -.. code-block:: python - - train_and_validate_your_model(config, budget, instance, seed): - # ... - additional_info = { - "loss": loss, - "something_else": ... - } - return (1 - accuracy, additional_info) - -Finally, pass your function to your SMAC facade: - -.. code-block:: python - - # Create your SMAC object - smac = SMAC4BB( - ... - tae_runner=train_and_validate_your_model - ... - ) - - - -Commandline ------------ - -When using the commandline, SMAC takes the algorithm call from the ``algo`` parameter of the -:ref:`scenario`. The parameters will be appended to the algorithm call, which in total -looks like: - -.. code-block:: bash - - - python3 algo.py 123 0 10 25 12345 -param1 value1 -param2 value2 [...] - -The first two parameters after the ``algo.py`` are the instance name, on which the target algorithm is evaluated, and extra information about the instance (rarely used). -The third parameter is the cutoff time, which is the maximal time the target algorithm is allowed to run. -The fourth parameter is the runlength, which is the maximal number of steps an algorithm is allowed to run. -The fifth parameter is the random seed which is followed by the target algorithm parameters. - -It expects the target algorithm to print a string during execution with on of the following formats: - -.. code-block:: bash - - Result of this algorithm run: , , , , , - Result for SMAC: , , , , , - Result for ParamILS: , , , , , - -The example :ref:`Branin` reflect the usage. - -* **STATUS** can be one of [SAT, UNSAT, SUCCESS, TIMEOUT, MEMOUT, CRASHED, ABORT]. SAT and UNSAT are mainly supported for backcompatibility and are treated as SUCCESS. The difference between CRASHED and ABORT is that ABORT is called when all future calls are assumed to crash and will abort the whole optimization, whereas CRASHED only indicates a single failed run. -* **running time** indicates the time that the execution took. -* **runlength** indicates the number of steps needed for execution. -* **quality** is the solution quality. -* **seed** is the seed used for the algorithm call. -* **instance-specifics** is additional information. diff --git a/docs/details/validation.rst b/docs/details/validation.rst deleted file mode 100644 index f4e81eae4..000000000 --- a/docs/details/validation.rst +++ /dev/null @@ -1,70 +0,0 @@ -Validation -========== - -During optimization, SMAC does not automatically evaluate all configurations on all instances. -The :mod:`validate-module ` provides a convenient -way to do this after a configuration run is finished. You can specify a set of configurations and a set of instances, -whereupon the validator will run every configuration on every instance. This is -either done by calling the target algorithm or it is estimated using an EPM. -This functionality can be important when comparing default and incumbent, giving -a best estimate of the actual quality of the incumbent or investigating the -quality of the incumbents founds over the time of the optimization. -Validation supports parallel computing, non-deterministic target algorithms and -reusing results from runhistories. - -.. warning:: - - When evaluating the cost via target-algorithm runs (no EPM), runs should be - reused from a given runhistory only on comparable hardware! - - -Python ------- - -To validate directly in Python (e.g. to perform a validation immediately after an -optimization), the :class:`Validator ` can be used. It provides two different -methods, :meth:`validate ` and -:meth:`validate_epm `. Both return runhistories -containing results for all desired config/instance-pairs. -To validate an actual :class:`SMAC-object `, -it has its own method :meth:`validate ` within SMAC. - - -Commandline ------------ - -.. code-block:: bash - - python3 smac-validate.py --scenario SCENARIO --trajectory TRAJECTORY --output OUTPUT [--configs CONFIG_MODE] [--instances INSTANCE_MODE] [--[no-]epm] [--runhistory RUNHISTORY] [--seed SEED] [--repetitions REPETITIONS] [--n_jobs N_JOBS] [--tae TAE] - - -Required: - :scenario: Path to the file that specifies the :ref:`scenario ` that is used for the validation. - :trajectory: Path to the trajectory of the SMAC run. - :output: Path to save output-runhistory to. - -Optional: - :instances: Which instances to validate on, train or test are defined in scenario. From: [train, test, train+test]. **Default**: test - - :configs: What configurations to validate. From: [def, inc, def+inc, wallclock_time, cpu_time, all]. - `def` and `inc` validate on default and/or incumbent. - `all` validates on all configurations in the specified trajectory. - `wallclock_time`/`cpu_time` validate at cpu- or wallclock-time steps of [max_time/2^0, max_time/2^1, max_time/2^3, ..., default]. - **Default**: def+inc - - :[no-]epm: `epm` uses an EPM which is built upon the given runhistory to estimate the costs of the config/instance-pairs. - `no-epm` evaluates the config/instance-pairs by actually running the target algorithm. *Default*: no-epm - :runhistory: Path to a runhistory. If specified, runs from the runhistory will not be re-evaluated. *Default*: None - - :repetitions: For non-deterministic target algorithms, this option specifies the number of different seeds that are evaluated per config/instance-pair. *Default*: 1. - - :n_jobs: if `no-epm` is used, this is the number of cores to use for evaluation in parallel. *Default*: 1. - - :tae: From [old, aclib]. If `no-epm` is used, this specifies the format of the (see :ref:`TAE`). *Default*: old. - - :verbose_level: In [INFO, DEBUG]. Specifies the logging-verbosity. *Default*: INFO. - - :seed: Seed to be used for validation - - - diff --git a/docs/getting_started/basic_usage.rst b/docs/getting_started/basic_usage.rst deleted file mode 100644 index e02e7f028..000000000 --- a/docs/getting_started/basic_usage.rst +++ /dev/null @@ -1,53 +0,0 @@ -Basic Usage -=========== - -There are two ways to use SMAC, either within your Python-code or over the commandline. - -Either way, you need to provide a :ref:`target algorithm` you want to -optimize and the `configuration space `_, which -specifies the legal ranges and default values of the tunable parameters. -In addition, you can configure the optimization process with the :ref:`scenario` object. - - -Python -~~~~~~ - -The usage of SMAC from your Python-code is described in the :ref:`minimal example`. -Scenario and configuration space are both build within the code. The target algorithm needs to be -registered with a :ref:`target algorithm`, which communicates between -SMAC and the target algorithm. To optimize a function, you can instantiate ``ExecuteTAFuncDict`` or -``ExecuteTAFuncArray``. In both cases, the algorithm needs to return a cost, representing the -quality of the solution. Time- and memory-limits, on the other hand, are measured and enforced by -Pynisher. - - -Commandline -~~~~~~~~~~~ - -To use SMAC via the commandline, you need a :ref:`scenario-file ` and a :term:`PCS-file `. -The script to invoke SMAC is located in *scripts/smac*. Please see the -:ref:`Branin`-example to see how to use it. - -SMAC is called via the commandline with the following arguments: - -.. code-block:: bash - - python smac.py --scenario SCENARIO --seed INT --verbose_level LEVEL --mode MODE - -Please refer to :ref:`arguments` for more options. - -In the scenario file, there are two mandatory parameters: The ``algo`` parameter -defines how SMAC will call the target algorithm. Parameters will be appended to the call -with ``-PARAMETER VALUE``. So make sure your algorithm will accept the parameters in this -form. Read more in the section :ref:`here`. -The ``paramfile`` parameter defines the path to the :term:`PCS` file, -which describes the ranges and default values of the tunable parameters. -Both will interpret paths from the execution-directory. - -Currently, running SMAC via the commandline will register the algorithm with a :ref:`Target -Algorithm Evaluator`, that requires the target algorithm to print the -results to the console in the following format (see :ref:`Branin`): - -.. code-block:: bash - - Result for SMAC: , , , , , diff --git a/docs/getting_started/index.rst b/docs/getting_started/index.rst deleted file mode 100644 index 2926faaf7..000000000 --- a/docs/getting_started/index.rst +++ /dev/null @@ -1,13 +0,0 @@ -Getting Started -=============== - -.. toctree:: - :maxdepth: 2 - - installation - minimal_example - package_overview - basic_usage - - - diff --git a/docs/getting_started/minimal_example.rst b/docs/getting_started/minimal_example.rst deleted file mode 100644 index bb08f5864..000000000 --- a/docs/getting_started/minimal_example.rst +++ /dev/null @@ -1,52 +0,0 @@ -Minimal Example -=============== - -The following code optimizes the depth of a random forest: - -.. code-block:: python - - import numpy as np - - from sklearn.ensemble import RandomForestClassifier - from ConfigSpace import ConfigurationSpace - from ConfigSpace.hyperparameters import UniformIntegerHyperparameter - from smac.facade.smac_bb_facade import SMAC4BB - from smac.scenario.scenario import Scenario - - - X_train, y_train = np.random.randint(2, size=(20, 2)), np.random.randint(2, size=20) - X_val, y_val = np.random.randint(2, size=(5, 2)), np.random.randint(2, size=5) - - - def train_random_forest(config): - """ - Train a random forest model on a single given hyperparameter configuration, - defined by config and return the accuracy on the validation data. - - Input: - config (Configuration): Configuration object derived from ConfigurationSpace. - - Return: - cost (float): Performance measure on the validation data. - """ - model = RandomForestClassifier(max_depth=config["depth"]) - model.fit(X_train, y_train) - - # define the evaluation metric as return - return 1 - model.score(X_val, y_val) - - - if __name__ == "__main__": - # Define your hyperparameters - configspace = ConfigurationSpace() - configspace.add_hyperparameter(UniformIntegerHyperparameter("depth", 2, 100)) - - # Provide meta data for the optimization - scenario = Scenario({ - "run_obj": "quality", # Optimize quality (alternatively runtime) - "runcount-limit": 10, # Max number of function evaluations (the more the better) - "cs": configspace, - }) - - smac = SMAC4BB(scenario=scenario, tae_runner=train_random_forest) - best_found_config = smac.optimize() \ No newline at end of file diff --git a/docs/getting_started/package_overview.rst b/docs/getting_started/package_overview.rst deleted file mode 100644 index d3103af0b..000000000 --- a/docs/getting_started/package_overview.rst +++ /dev/null @@ -1,83 +0,0 @@ -Package Overview -================ - -SMAC supports you in determining well-performing hyperparameter configurations for your algorithms. -By being a robust and flexible framework for :term:`BO`, SMAC can improve performance within few function evaluations. -It offers several :ref:`Facades` and pre-sets for typical use cases, such as optimizing -hyperparameters, solving low dimensional continuous (artificial) global optimization problems and configuring algorithms to perform well across multiple problem :ref:`Instances`. - - -Features -~~~~~~~~~ - -SMAC has following characteristics and capabilities: - -Global optimizer - :term:`Bayesian Optimization` is used for sample-efficient optimization. - -Optimize :term:`Black-Box` functions - Optimization is only aware of input and output. It is agnostic to internals of the function. - -Flexible hyperparameters - Use categorical, continuous or hierarchical hyperparameters with the well-integrated - `ConfigurationSpace `_. SMAC can - optimize up to 100 - hyperparameters efficiently. - -Any objectives - Optimization with any :term:`objective` (e.g., quality or runtime) is possible. - -:ref:`Multi-Objective` - Optimize any number of objectives using scalarized multi-ojective algorithms. - -:term:`Multi-Fidelity` Optimization - Judge configurations on multiple :term:`budgets` to discard unsuitable configurations - early on. This will result in a massive speed-up, depending on the budgets. - -:ref:`Instances` - Find well-performing hyperparameter configurations not only for one instance (e.g. dataset) of - an algorithm, but for many. - -Commandline (:term:`CLI`) - SMAC can not only be executed within a python file but also from the commandline. Consequently, - not only algorithms in python can be optimized but in other languages as well. - - -Components -~~~~~~~~~~ - -Surrogate Models - - Gaussian Process - - Random Forest (with instances and without) - -Acquisition Functions - - Probability of Improvement (PI) - - Expected Improvement (EI) - - Lower Confidence Bound (LCB) - - Thompson Sampling (TS) - -Intensification - - Aggressive Racing - - Successive Halving - - Hyperband - -Please see the following figure for a more detailed overview. - -.. figure:: ../images/components.png - - -Comparison -~~~~~~~~~~ -The following table provides an overview of SMAC's capabilities in comparison with other optimization tools. - -.. csv-table:: - :header: "Package", "Complex Hyperparameter Spaces", "Multi-:term:`Objective` ", ":term:`Multi-Fidelity`", ":ref:`Instances`", ":term:`CLI`", "Parallelism" - :widths: 14, 14, 14, 14, 14, 14, 14 - - HyperMapper, ✅, ✅, ❌, ❌, ❌, ❌ - Optuna, ✅, ✅, ❌, ❌, ✅, ✅ - Hyperopt, ✅, ❌, ❌, ❌, ✅, ✅ - BoTorch, ❌, ✅, ✅, ❌, ❌, ✅ - OpenBox, ✅, ✅, ❌, ❌, ❌, ✅ - HpBandSter, ✅, ❌, ✅, ❌, ❌, ✅ - SMAC, ✅, ✅, ✅, ✅, ✅, ✅ diff --git a/docs/glossary.rst b/docs/glossary.rst deleted file mode 100644 index d69801905..000000000 --- a/docs/glossary.rst +++ /dev/null @@ -1,72 +0,0 @@ -Glossary -======== - -.. glossary:: - - BO - Bayesian Optimization. A Black-Box optimization algorithm weighing exploration & exploitation - to find the minimum of its objective. - - HB - `Hyperband `_. A novel bandit-based algorithm for hyperparameter - optimization. Hyperband is an extension of successive halving and therefore works with - multi-fidelities. - - BOHB - `Bayesian optimization and Hyperband `_. - - SMAC - Sequential Model-Based Algorithm Configuration. - - ROAR - Random Online Adaptive Racing. A simple model-free instantiation of the general SMBO framework. - It selects configurations uniformly random and iteratively compares them against the current incumbent - using the intensification mechanism. See `SMAC extended `_ - chapter 3.2 for details. - - BB - Black-Box. Refers to an algorithm being optimized, where only input and output are observable. - - MF - Multi-Fidelity. Refers to running an algorithm on multiple budgets (such as number of epochs or - subsets of data) and thereby evaluating the performance prematurely. - - TAE - Target Algorithm Evaluator. Your model, which returns a cost based on the given config, - budget and instance. - - RF - Random Forest. - - GP - Gaussian Process. - - GP-MCMC - Gaussian Process with Markov-Chain Monte-Carlo. - - Objective - An objective is a metric to evaluate the quality or performance of an Algorithm. - - Budget - Budget is another word for fidelity. Examples are the number of training epochs or the size of - the data subset the algorithm is trained on. - - PCS - `ConfigurationSpace `_ can be written/read from a PCS file. - - EPM - Empirical Performance Models. Empirical performance models are regression models that characterize a given - algorithm’s performance across problem instances and/or parameter settings. These models can predict the - performance of algorithms on previously unseen input, including previously unseen problem instances and or - previously untested parameter settings and are useful for analyzing of how an algorithm performs under different - conditions, select promising configurations for a new problem instance, or surrogate benchmarks. - - Intensification - A mechanism, that governs how many evaluations to perform with each configuration and when to trust a configuration - enough to make it the new current best known configuration (the incumbent). - - CV - Cross-Validation. - - CLI - Command-Line Interface. \ No newline at end of file diff --git a/docs/images/components.png b/docs/images/components.png deleted file mode 100644 index fa20aef035e25230dd6524f187f3b9a36fdfd69f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196579 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

Q`e36@U5P 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≫~5t*Ss^GQ_tn3=?Z!nL_kX-@(I18?M#^iCJ9JWU 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

  • +$1z(`6(#y{0akJepQqWGU2pb4 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 z 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

    kW1yN zOUFQzrl_B-Sp|MUu#RV$3y&ThS-ZBd>*75T$OFvhL8l@*16&XJ3X=9Yf%G7o>wpl~ zwRWG1xa_^I$jh~QcONgea@Vq}xijSs3+8N>x5veX>cDlZu|vwfm!9oNzSrGC@Lyeh zc?1Ixa9vdTq6eYgg1Wo0U|_bX#23;~bTL8zDqcu|J7T6$C-e`USE|w4?>%I(t9^UhpN2b2RdW6FmPgQ8Q>zP|oo7I283qw349Ylp#nF9uIUV;R5k8srzim3e~)I~!zw z?|9kj3L?@IG%ObviYAa11t8J{x6YNti9{5``}y-VAK$@jv%P7L*{8Jq_3GlWpQ>7x zHcfe?NQQ?UFSt`b3JMQ$d{OTKxr@0bUx){ym{He-mjdbx-Z+MV&-Ra41C6cf6x z5^1C3-8z^J8C+xhi1#?xxkuBFq5Y-)RLw9XsWB|q?#zoCgu206*+{|iGkPxu4?Txq zBMy8PzWkLy$1CQ~zFWsXact+p$%^2es^^!x;nEGLZ2K1rfID^u58Hq?r9yikUC8;} zO$4w_w1Vr%jao(U&TMRIl0Ls&LHqLuk%8Qr1Db%3Nj}AOEMFB2a-8^79JI^!r~0SN zhN+0kCi!cSN%oq0Oj$cWWN2M$0S1B}JJ?+Ght3L>}N3r}@*bv&5AkH`Dp0GRU5-M@0-3-bg6}`tGP?WF`RGD;Y3gPZkpZGIfm2V*Wm{s&EiM?wUps*IINzj}LOP zppUOW-{jmAFWC;{$AWF%-2d@Br#a}@g{Q&?=pI*gLmFccL$cY?lfx5_)e(?9c)?ap zDXw*}wXFeIuIhzl*a!x&3n)eNcBp%pb-;TLplxs{yT4dKmI|QDn=EBJgP@>e^K9Yd zgqU$J91A++m$NgDubHU{#a+t6Tq~r_ zQ`06(2Us$^3PZPxryYJD7#LUr#niLOtz6G>RWOFLm|?(>KVC!M_*f_)Dyfl{uVr`o zMa{`Oe88(|2Q2Q0!YW|Aw&nT&6QRjfrMwj~BY68fhh;feZ4@bSjje5i=h*>V(4(U$ zUn&Vis^Z!nAO?=Kc;$nzkVG~3tMaqB3#DT(I1Wj>RDY_ie?*nQZ^Ui#+V;$R9i4{T zJn?samYRidTwLD+8mZ_PdUW*I*9tGT0YO9?bRmpa2SL&ASj$e(Zz-;|8UxYGN))v{ zItmOr_p^ib@rp%R%jz#t-cc=^YY2E0mADR*+{U;h#vU7J7%cpuG9BF3KMqkZ(o z-D8%T2I++KaRMcUej%nUM+R)Uuiu6ID7bn@?6*#kA@YYS7pg%C)#O}LfZBTvw*m*eG)-Ml}46YSx*IW|%SK%c|Tx!$LvM5au2p0h0tE5$%}kyx*Sd^-uXg z>#E{qH2P|KF^VQz^ph)Mi3VB}4L(j{m@)f!$V}O^qSd zAB_kx9l-EwuAKP( zU!u6wQeJHq+lG51{S$*+o^j%Rm8O zTfPe1+=}aV5{`Yn?Owm^mg%Y2*EY4d`mnAAefsVA#Z|$Z9HCn9s2)*NoSCl+jWR3P zG|6%uviip4=!1$j@sobHB9j^fu%ODH?*>(aR__&1^__wn^hQb#JlW{O@)P&rQpnHx zD-8zZ(%JF@*izdy3O7ZsE~{Yroqq$k3OJ

    c&g6)f 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

    5oU^%NwC8)cp%Wt-@( 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�Ta$aRqTN^{LW4(7wO2UD4?anjj zzUqL-D*%2>j->o4#17B&$B8N_cqVs_WXR4ZeNyQS}|K+Zwj>6j$t(R}rOqI+my<;lM$>*J2;s z)QZr?t4E0ub$>NLJy2a-ob76gGL7XMM# z9ie`INx^w#$&RQWFvx0)Jud`t74!A-6RS*=ZZkM+NK{d7`hJ=gt5)rm9TF$^@`_!* z=Z30GmbDRJ4RFp~eaY*;e_TesAq$$526TZ$dltv2T>Mb3@RswFBE+zd;MFFV)t;9@ z{L?bB#~q6JJdX=Fj(-z%WCt~&i|7>ed*lC1X!EXE%iF(kLH9%G9=wd#C+k^(t+&6O z^jl=}dAk>$Bls@2K!z%VC}wb~mYrrz05!3vcYwPQ#LzK-);_}oMNZ!6}n?K4$MZ2r-N551! zIljXL7`ysnJ8V}_J7`_i4n1=G_&eJaCLpc2(+FkWI*7##$pdyT%gfO|u`{+s{Aa3% z%-sqRigiYsjmPFovrpNGrmI(%D~h7d506iW{ct|~z+0H%P|6RgA4Xu$4EX4)&+ETV zw>h67%70(jxlxWv(}_~@^YlS;CXzr5Qg0tW|29a*EV9Z~9}uTHf!y zTF}V}$m@3U7QX)<`Cnu5OrUAd!WJkyuCg7DIbo-;>*Tw`f8tWSRP01mqC2Ep9HV>T z@5s05GmcO5x&$yYA1}=rYEJ zpRLmzz9ewG(UDElMpW|8`cDA-dJUw*hU@u;cNy-{7jspk(vk?hlbtT+mE9*>%$TcI zeEGA*0e!U$R$=Bnu(z*g6)a!Pp_bvsUYuW`-&y!ad%gD3YAM49z7` z>DJcPe|Lio51SwWruFjUrZ4d!RTporGJyD~0gw*^0PX7=t=vbpKYSl`HpTHejc+Lx z?mGa~BP4kcPkZR^ZBC`Xvu<8DmghLgGbZ;p&Y{!vA%R<0F20zH=e^L+H>;AXZH|mm z#FExyMM~_)hgeO87z^jt*6X-F7%z&kgg|{5pk;nmR#w-m5+j^;$b>RV|MlxM%1HAd zkZ9b;JoU^N#ysQl>uuw9rCA_{hs@T;LG7k1Rq#}D8gE38O>te!zcw@+`xna|XbtV` zon-&J)cbDtqgdvWbK{oH*g$c#myJ7ft9t4HZN}1&(%jZ8s@#X$H=NGTzTu}oL0Nf| zg6Cv6o7R&nAw1^|YKmkoW%N86aXKJr6Pj3R9CU$#CxgOBBIEfeR?T4cIRHy2gq6kR z&t!m`5$plZI|dS_hw{u8ndxHvnDr(ZNb$4#BBZ<&(QdcAXQ+GkIPOKyD~|vvBB*#_ zKff-Y>5R*Z)o$BkqnP^uC`plprY|n8A8)?$)6Jp(eSgEt@PicO_A@&0jGB%Q{IjGf9+ayosuv=-8P-Wg-FmolPLrb9>7U6|S<;0d90FGMWR#fe zYo!FYMHDYLk@>@F{L-64dHy8kkQhPJbtDa~_uk`NdKxOUZvf~^?e2`-=IHQAdEXKKDHI(bjf4xZ;@EI7RuWQJdK1JSSyhYN{5amK?eHk$KN>m+7G86A?fWT9710t6 zO40-b3|S?NOp>p!IN|^U1NlMBF*97w(W|C+D$j3J_<|UXd>oaE#lHVT*LBBp-L`E) zMkz{mLn6BG$jnY=G8%|bq9U6jTaqHOS5~EzJyKSRvPYDVa0~Bo^*rx?_j7;x zeBS51@9^{cUEk|G&*MCXNr8qhOpb5AHuurV7?O}{yqIE~EmnAdp_gX)^yZxiDPlyK ztN}_dq&BDrN02U2se|wqz$J@7FR=;Nb?;P?L`FSZ{z-B_xj#8@z{Wp3d?Txfp%3c3 zgr-0bUJAgt2I+khnDyz=>a$us<}8sJ$Kc1hU5GfSgDTN9D6NZ*2ZwKTa)0dk?tPlTpJDu+y!KN<3J#vVY`MnbI)*I|)%hb&Pb?#e zA`t$tsyT5kEYjcZM;T<*s@mSa^1j?~-U)~q%;Bj~2#Sx!pEx0yVK6>8_~ty9$b&b% zzrhI#Pr=#!s}sC>=ziGJbi(k#kt~%xG9_wrLi84GAPtc`MhP*DqNoIxocn+xkm;0t zAKDa*JTZ1MSFA6dW|uw<3uE-aQk9iJD%G9Z$EwTHb)Ir*2~gbPtdm+-Crr^KG1tjN zP1Alzb75`AO}QOReh|2L$A%@+3cBBm#tJb`psDdjSsqhbiejt=KOFhoKDt-&$2ga(3T&(>;+qKc5zE;GWB~_ zViN$MmY_I#xn^5&)bO2t&dy$a?)~lZp|Ls7|3H>U#H^FBs=#w}Xl76*Du(6Crz3Ds z631Jcu)Te0VIdiMD)N#5hf*IoQu$;>Q2GLN%!(O$a;}Ar;3g4M!$)#h9dBdLce{0E ze-h2S(QnPFW5jnC#&EtyE~@;uv_10x`fWIpJsXy?hw01HzLcnn!;^ zsGV zoqDhG27Ut)#8F2LihtBNUv~t4Cu76kLRK-xp=TZyV z`Bc#C3{qPBb~e4}(3r6P?Uq`fszZ!gcb1vTHtO6-h`#weWIxOBa8)eghAb$X_<1QP z8KM|eLs{+b)9M}49J{vM*Ka`iPtXlK9m(&n9!-6(_%Pz4S?L2+CFM|x)HMR(Hpc+I z?sXbaA}d-v=T-PvomT#MB-HNX3?(abge2SoTj~+$xa5h@U{MADvmN#rc^)BPw!;=~ zBT9tF^XtXdy`g5g{&3cS83jI0_`p7RN-F_)2=LCq0em>F&Cd@vyY30c<+c||X2?$a z`0Kg?(~6sy>xu8oxHb`{Q9F?-D(9)q(g$mk zyzyoGVov;A1;qPmK|}Dw{>{CtUSXrv9R1Ax;o-T`Km{Oduw(gwCs5B!2Un?Vlq!v% zqQ$Arbc$K;Ar65qY&%T6MI}dYHcH`a2(zGu{bym@GV~7#q4?ap&}s|^os)tue~hL4 z{PEL7X(g%9o^ohpM70J6G6~-AU|{qUa=wE_U%Delw?}`wzdZY3^)0r(j-SL9uV&5p zCZ3uIP4!@PdP#=I_H=D5feOc$58dBDUOFU2+5nMjUG>yfN`|iAj*00SOp7F<@*5bb z5C1*R^Ph~mc0KzDgG179CnWSQ{_~HV+;v)NY8ltnj3Lg|`tm)R+bUylrJ-Sv#@AFw zTu^&5tD(wdcgzh3n%Og)CzOALESmC6U=4Y;7T>Uzx92Q5lTFDrtvM$P) zs`w;r=o7Z0FV}zO=+V^NlwsMzBELV*T#ADyzFA9vMN8lm)4hh(iwh}!s-yJ9PuMPh zk<)rwtW{@}Xu$Gti;t@PD}!u`9l9~xY(xkAXK?%KD|{rx2!ssFSi}^0KyDQF5XwV% zE^JBi7~!Bjt2z{c_LrOooYfA+JlhYu=y33tO`6n)~54mRwYe5+bA4Ras5TsPt$ zOx*r4T86<)Y7G+Xoi067+JGevp+Uevm|8&|h9B>dS3uI;u|)Mh`D8FQ^}ONr%{K-H zzejE?rQ>;f3UGoYFjb)L+h(ug0VCH3NlC?SS|OBE(sZUgewh7x)~toYRv4^9bF2i? z(d<>Us;&7}$H?ClH72AwM?^k0 zS}sV2lXucxs}>5=cw`_Iyl+H1K!&qmaU9*kLq}|JVuA~G^Ng{61b4`;?JDfw8;a`0 zp)*q7?-_^GFz5o*yk20h2y(>ZYxGe(@XPY4tgNgKOhw(XvH!KRLx#9BO(^d9*S!xN z&+qrbH-wCLyb_PO)|2eN9zr4pXiMRrx z9y`}dMvEvr+%I-}7hfFFxrv$*%+`q7S`rn54fUE5nd9A3m-^%pZ=BxV)qHjzQKTa% zR3fwlTDLPtBFAU;AdToRT<%po_3>dq%%KtCrXHbj7sH)Xp#`pqC-+;%lHfX_|UJewjpU z6JIpA)DSlzzKK%>riaVbUNj&st0_^s?dk0ZG&7idubbvd?AYOBb^Do>-7g*1iI;ha zy!x5?%_Ac(eDCoA3IavTe<;5Xse2Jt%RmPKgr3XD zvgPvRpF%(oieS}WAAvkVfu&kvxwk^1tD-_}}}HRbookb9jh9 zv(u3tgU@t@smG#ru4(r%(|(!oT;>EiIY5Yj(`g~!3qxJwk&xJgDNjE<3Y-jL`U2Qe z3BAohYc=1!FO*<_6E~pBdAF<(zuF-BKUOP9LHH85>WAS0ViR-~1TtbD-qL+sH$!g> z5YTn7Zgs54rwAs^LtN9TH6ZAiIDX2~5=bmC^!s;=nC`HqUw|qnrT0*9odkoxWyQty zeyWVw`16J9Qe=F(DqSmv5{A9$)B-e559^S~*#)>a?LGf#Kk4HDX}$$0mbDFUZa+|M zKY<`}xeadSGsUrH#4KnNbV?{{4A}!J@bdudbE&22I_?_PRGk8fqKYlP!kQ!y4z3dt z)%N>2vB)Jr5N)xr(hE;@@syu-z^HQGn)^8eeie zYiq@dM_S|0Q$gujTatBKTg>8dfmmV(LBf{-jo9lJMnka>vi(TX;@Uk|2Nx+( zjwkw=`H%u2wF(lI%a?)^&QipNU1=$Is&QF0Upy=kr!FNK!22u{-2^iHe4zIyI~c+* z^9;eHuYUhfM_S;sw;qH~=e;~}Mm?YSXk!4m0od-<-ye8jg0kyDz;Qak2PB>X)}?75 zaW*FZ6FK1$%)f`?Dlel!$)$cpShA?rWc>9q$-`@9G+uS*Qpg= z;Tnvy6qVF<;?FoshBlCElB?M>{2Tt3_&v5xwX{R>)AL1GLMD7MLG{ziGSrs%fooez z(#4hUvUk9d=(Qrm&pg*8e(28!wLK3(A=V#N`FAi!NsfVnlafx0YOSQCu*Gl&^;0_~ zcR>XWGu3{?cM!53SOjG3@ccDq(Pm6?!ikZ9^R=743HYo~8v+EMftm#zoC@2*Ay$0l zy{Mb%!`f)~^>a+?Sw3SULh`7fy1GQ(jipZl249cBx`>dz(>`{YU1h}MQ3I+=Z%X~T zzE1>Njj{2v!~E98eY|@FF9U;yNeJx8pIK9}QD7#zDPSzuHP!YcT(^_a#NPT1wvi0F ziLcGgw?&50|7bLV^6i;Jvz(zNti9CdY|)*O02b8k?rfkfwj!gab9zbx4!(mN_m!!O zIodc>3R)=Y#_;le3bf~yF;pcS2p{KE$?UL%8#1uxhuxc9P3%wwr)K?D;40? z^T>GX4T@Jsc~YM0E9Dr;=g{x{zf+mLS`NnmzfSHcDIoAR5t(WCdG!AFt7yW^QNF1EHH^~f%-s& zbhY#8`AyYPP zz%b0I^DRQsEG&&+BJwk z(4X(TS2knh+ZiL{{>9reV`(7EDpt5XQ(aS&z;C6)3IP@7va7ChYt33%0m^gibN^{h z?1*nWu8aarUVt5kXbq+_1|Wy&r!u2E2Gx4G1W6Sn7Q)}A1D9D^PWt4v3Hk^bQ(hNh zHl|6YaP<@nRi$(npwZJ?m!7`qpw;gOhua!!(e-to{9@{Mqb){MbA%H_sr{dZ{A@n>XG8hyMF zGw7uD>GojJTPEevzbKpEBVP_0d&zf`s+j|pncxGYsW5{MJ{nE+H)V#fgvh;3=J%0# z48ru_`3%N#INbwN>TOMpY7JVC(5sPbX0;}8W==~8H^?#VFYr>N%kRJggC~lIqYFmi z$nv4>*%qLtG4zZU9va<_r1vNtHqXWXk*ID?l#Xfl_Ryht{O156X_O2F_j^jU*S&?;kn44TJ*#${9vdGIMTq&`!v6Wum zK?+5{wOhv2-0$Bt!nw~a4X*#|)5)Yb^pJ(ZTaqc6wLgPek3s0MHYcTzKIK9Njb6u^ zZ4B|bcA>a(h*v(DjD~u~cH1E56c8`>KHjjIvG=_25IG@1;t#Mg8tELOxpe(B?NjLi z&xpuKLO-f6#Ru|3!_2*-T3aYeUHRl`kJXT%AWM5C5~)CPKtFS;*>-V@$ElypV0`29 zeH%sE&B!9R@Q;~9jszQo>R1eU?4dO}XJ85|CT3b{?i2#Z^C2u`q=wJsVxF&m*^KV^ zc#XS<2h<3+Jeq(h?5lq7^m_2gA3Fd=(>SzxarWsOw#gX&=9B`B!}qm$UTlm&kb0>7%1NMwvA-YU$R!?h zs!BW0w50Wj?ZD1t_W@;_P@f&5$tluosWVy>#g}*fY4`b{d|0i#JmU5qK#^||r53v_ zIxr;wP=WI8$$%Gz<*gH!4~ZZW#NkRODzp>V=h|9YY|A2?^!E46dYvmaPMdF)PPqCJ0k2JG3$1%Fyfzir0PT`3%` zS*V8&)Bg~^kLoh4xuz#CtNAK8KIWcks=$Sxo6-ey+ES9sd8IN=WwMf)inNPBKh&oP zmI;{opsmUzObW%VZEe+RG!hTqwfi0yWCLZIh_)onsM{dv{^;%hxQwtKbVif<0HAc3roC9 zaK+R)gL<+@>HUoB-F;nye0+R(9Kh;StpW5QTaRl7i;M`ToIe$(lB6H?GpUJRb@=@T z#1^?_A#&BD?=}16HEykBsV6xo-quM z%JICA1VjnS@A^^$GY=Zn5W78Z3aQYm^OKn0RBIq3{jqt+N|UvO(}S_s65|6XK&CD`I)MQH zl?p(On^s?dRbwgk(Cg!vujVBdN;-aIg7M5?{4slbp)P-Fk_X6QtH zDbxge@6Ug}HR>7+M`{%4U4y1`>?nP9X@NQbg9bhgvGy&==I=kuto&XuvfAUo9-FcY z(%vgFUEFj>L?W-BUX;5G{`9V?)5s}U8U6|vP-?(9sV{5I_a)Nw6L$b}?|p9I#jL4aWyax&GV0A#=omUbInyfEzi%fU(Q+I{aB34REH_(>5| zo3fF+@Js+QbYZZuZngncb#0D;81wL%UaQ&CwxQ8Bmtt8*S26=$6@z9Gh;jNOM zPDefYP{Fm7d8>{o>oTk~Y0^;JZ%I#Id%4K}usRq$#OSrY6c2s(x-Xk2&~EIMl|3zS zGK`jGb$QCWEbN^#Z_Kt0rO_y(dwaE}fKkqyLZjL2`UhJMwVKjzq4d$TE2t`@Wt%s3 zzpjY~^sLZA(~M8sUGf*b3XTGE7n=|8cE%;QRMbz|}7A{6^>X3eRf% zEf{5$Pf!|grMmRiKvg8n;WF@+KCRMVz|-*M0fXzvq{@5`f@H8b+PB3o4f{!XKJ&q?OCOXb4>UR1Mk(LUr^!ZO~1&{n~q%J@qi*x#9 zr58iFsMJe`)?~^yFZq8z^E^4qnP6ur>FHCgjiRH8+UgM$qa(jXR7;z$%4B@fu^|l5 zgawY3`fRD)^3w?`qsmV(2t%%Gjt9UJ=hNO*#^>5jtxJhE0H_oZGL9sbHbesdG^-#Z zP8zp_ghc6xMKA=F`?8m!t+rnCIBiwGR9ao&mJ25Z3~(s)f=h(m-DQQXTi?a5THujt zog1zqX0Vj)N(sqVE0DTZflaus?#AeH1MixGwd24d^uyH_tfWhvAERJMNYfeB&vuJrM)57KPm+9LKtLwN%e=kq|P#{9#wc zWWv8@RoXdsWy0`^f8Iqq+T|@qZml*E#aCa7TDrPoT=;*CqoIyqtditpgi8mnu8Q}m zTr*i{2oxp6keZM-{khnI7C8g;2eC27Q6qiQXKk+a2U2wz(sMRzoe-_kr4+CmpwUyD z-W6g#kKPv@WfeY5h;`-}892WT@zlR#P zH7va8afCH@SlQi-fB^!YiX4xRH-`^?K&Vcd<(uEYHq1Tgtb~yccX?KXpZ~VW25LmxnYC;L zI)E(0Z3XLYYF!9>j|w3sZ3OR!;r|#o_Q(4pw-&4Jvd~IW8A1uo?albyY3G8TG z5(k}!=;loW22&qDK3=o1uuyj=@cZL<2?}{@>!HEb2dKp$D;tJP!M(lLqaKKa++4Tn zn-Aywo`B^b-fP=~ZJEX(M1FA|Jb$e+5qF;O>}Ug~utUdBmM_eHz28o~@rrcX|A4I* zg%y~zPEb+X(V*HE%lmzxM*Uqw*NRrvXZIFMr&7Wr!!jVkspxvgTz*!C;5x};B=I9H zo)$l2AfDF3Ku^yXU6^W3y*9P}Qx6|p?>_)F|ADKi>1-a2!;PMTOWYQA?whu6-{WvT z7MaUX(chYo!uBAJL3QeLNjR?VYp`3%tL}0^cLJJc<)0;V*vDBz+oZ9R1 zMLIospIMaU_J9B0eEFoq9)olP4|H!>KDac$VHK&JLSyN2aVS1A^35D)1Ij9fetgd8 z9<=9&5%L%PSC9sSlhRSe0AV#vP1~ml2y==H+dZs}@@gGfg1zsP0}U1#k2Z601!L1| zDEkc69!PeqC4yiL>}!Pfz3eEVN2yez+4A$a@QyB#0fmP?GKF+^XX&2`Z&GGfrEC?Z zp-xw(PFGqGpb=>J>do-up6Xv*7-z>M#7gkEM&j4>YL{=zV|)4&Se8+UAR>-I*kVE+jU<~xzWQDm zuJ zPwKgEY7~z-xN%~hw{|*uUfKlROyB6}ZjY_`;9*nK(z-NbQ(~CM{b>2mpJdQxMxeX( z#R~aS@7hLdCzN(Lm8AI{a(0fIh$iaD0|#Ozr0~{5;wEMoA`NLZEDNk1LU?K)SHka- z1ny$ED`JP}U15~zLV(SOlQ6JcANMf_ik!cbr2ls^PhsHzPqABIZ>b$goyWKGkZz zlsZuXMice(}%W4LUy5$!GHA(PUt zFjD9diLQW1%rJz=?ghClg`-^$zzSv{iFRr(6I>}0_=A!tS_6Y)E@Gh9WlEQTZmK?I z*+*ugDs20a7S}&`iys13B}%($a&CBGqvIgfX(@dh=-Cg+gV;V|K~{EQ5Qar}LIg!b zlpGyJ&{oz<#%u!S(c_R!QLVA{@waRxWn}^sBDG~GX`^68{DF&MxRUze!;?zduZcb0 zR_LvY4_?4ajGYLdM&h=G5$8S1YVSxLh}i&Ao}DAnO1F^(RJvB@FVsk~z|fyAkQh(+ z)&=5DZY+3#kxH!w@8nIp`)BB*|JmT&ak%i;0eMFx|7wBkR__vyYHk7NZV4Mc??=0; zT26fnB*R(fKY0e^_yadK3i)pa35XUObIJ@GPyIN8Q2@R%PQ5kA*Yd&9F${shi82(I zpmTJYs@7@e7T{>e`V^&aZ}04S28IH|i7NBGrRrYcWLF)x zuhy~&8A|VA5|0=IhDOa_>inA_aoJUCVOLOP0v#KnWe3mI;9Vxp0qDMsWv{~>nt&I- zDx|Q8#5S;by!m3oI#03hH7sjzH}C(&(wE8dQ0&Ql4QdK|vggqFwPwA&{Y=$nHCk(f z$&nK){=*9QV`A9mTiW3$TZ>P%?7|~BOsg+VqQ^Af&sjF2_z}PXK!l!IvZ%syUV7mX zc91`R|Bep9c7v)*#_{!xY`v32C7{3`pUuau?>~63$E2hI8>IS?tzjQvIZ+>r_2K|p zHwo*bS=Ap-Bwcy!WcdL1dD_7*mBA>FJ-Zu(SQye&)_@3bTvKy2ejSf*p1HJ2qmvvr z8(ZxQClly@pnu)NQ9?5K{7*PrmL1`%owK=odH?+RJatyE_|f4|%$uq{tCpaD#|*_F z>Xz{o$diZKXKBM(g&DT-JMV`v;?-(b0`mTE1&Oqri(~M3ls8)wL08*PFpfh zJLCe*-MBGW95i&ol-3vwN9K}ocC_GcvW!Rcx5j5D)?#Bu;BPSt{x&*X6NWlVzJ+=F zsbStly~9hLIKT_(rT5)={_mS7--G@XuS{t6f1rwzTvN2B;e0k@)Q(psf+W89w`<*h-MI4GKZc#hrp>NcNhR>DD>}SJji(HAOCOF*j|}wJf`TXJJjoR(aRJn# z%d&kphn|K;As4mW@MO|faVs2QQnr<&XKaHJWCs=u>q)a{j&*^OH@c1P(kcR zd5%gcF*L5!SKJZb@ooi6PY}UGIM8cJ|DgGuZIjVxOCK%P@Eg~|D?Ais7pN#cm0W!H zs{&8!K;7*K4;uUT&YUPmWnE-`+7Y5>Q$?1=gr zAJ|5ns6kpM@u(b4!lQHCf;>9CGvEU_eZ9SdPQ9yxj1bv8Ji-PGNhpt)ds6k^CCL7Y;{;q~Xy`ZRNj8e4`=LZ@O zrROUW_Zx~plCh4=m`81c_<)q1KZK)+fsn+ep-uvfBqB|VaC)}w9iqwtz&P4h9S=G| z{C;}8qyjsW$CG4=!Wzj^oFBPE1mbTF?8)$8`FG^)&Idsr#a!x}Me^r2QYNX&-*DE; zJI3;GlaDGVv#TtPp4PfnhT*Z|-^aQ{?G`+wX7!3?7XzL1Vbl?WIB^x0_mDaCM9ILCZzngkoT&pJl zbHH(l3z*yLP)vDvb)x5x4`zAyBeE% zfsM;JYl^G<{Fk+0L1BSBva?1>4)}h7qyKOP8}*Gv?iaqo?Wx%6k)0f_;^$TwwYE|T z`C#vH6bQcL-;vX}<%dH+N&x0;JndS4jEv&W^`GB$8l->RY z132r`^ztvu_kc^#v$|Fl$R8`PReab_@W~E%!W%Fg(zE-5om)7gN_R%jl0>mGr9uOr z@1;=klXZSVjW>vavG$5o)r^l~M2sxk+yY!pt)_UTbQye3QtHh!X=(6QB?E#}qGM{j zT(V1>g(qO=Wd`PzZv!fOZWvSA(@m$}vAg$3m_Pr%-jhSgrI)6`O^b~AEF_zM0&5;AFx1kcWPF?3Is;lLU_)ml1+lM`esSC3*GjW>eIxmv}-3Laq(OrXt;e#4n zcZWHDZKHGotUA}nF`49ZJ5au?{S5X{cl<2cG)55aBb-n?aNGakIjZ8zq;n|R>KBMW zWl3*ptE-9nxu?ck2lZFC2LdZTHJ82mIhT58&@j}`^qD+qdk9ikgcF836is|EiR)-N zr5po5ML!_{T*M2RX?J;eEpst~rqv4){@ISwl{Y@%@e^zgOrwE;z1}O=g2iD4e!+#Y z1WL{NujH;E>B|x}yUzaweh8D3n1XPwelPi|0&0$~qnn!);pc$%(_JL0^BWl$#@Ak0 zZ&%gS+#{KW6?a4}|Nm+x6?o{h=;eLVN`I&*yPhayyEh)sOR<9j?4vVL0rtkpX&Zj( ztx**^R23x@p8*s|$liGQgZ5Il|Va@Y!&jC_X+8 zMr&MW@bRtwlkjlig9-)3`9I?lTxd(3hlAF0w#T&q6S9$nOu%CX1TnE>5al;a8!*z@x{H&=tSMK0~~(A_WK z-$JGl>mH1i=P%gH2UMuy9su?jXG!7&fPc4els)+M>oP(3EJG9@%ER8sy5YDnR!FGe zRQ070dVk!19nHM>wS`L~3s?$o%hb=t9qS7)P>_mc4W3fH_YCI-1XEHtwPL0{Xc->5 zS1F&$_Tl$@bw>2w;=4(qzm=YhIV*uo38%7+qXd6|>1X=v0krOjWQSv~6UXYizY};g z+QoOG+c+U9Gk>3=%noRr3>pyjS_1v=sd(Q2PkEsTaex)2m-^Sle7D7l5Tq2-nw7(3 z;`oc!U4ieZ(ZyI)tS~&dmzEGiJK*bhx`T4>nIbQQwweZeTkwer>Cg%A32 zPH?swrMX^illAn)M`|%MR8Q>O_Bsz8zeWOXkbz`!@+=_qB5QU0Ae%bhj|~8t#&2xO z<<|u(7Ic+@`&L-^r)_u(Ng<_eik-_V>+8JINI#z6nE$pNec|a z1kXjJ2H zDV>^m?Uq)hJ%3hhRpM5ga{34u?jW8%5&*#R${UAeA&P5o#4BK3Ju4b;$hL)>Ah9hq za^ScVc03WQ5lSI|8#ELgQ1N}6Z0A;d^!^o+<|M*wEm|?QLm?RYc|lW0D2kZt@eN|e zA^HNNptb^YBP;NWN~Q#N@1`YbpV=Ff@V>{z$08~0uEWF8rTM8r4DNk68{=OqkLWhv z!b3nTDkZGnc41^A{5QmSY`XY-dM&?ZQV4p@hECHb&$<4BMr!egIYL$HT@7;d`nC|? z%9igXpB$yE2y;sEAoLm=&YU@uP?CyB9#&R<4g*+T;&kuay}NegJaer{fi-=}*OR~` z(E`S1eEH}@NtLzj!EnzY%*1=X-QSA34CH)BLTYh4ID$Bj$7^hCY%H*kGId$}mYI78 zK#MEaTRE6-!SJL^vgx_^svLM^Z*3+kNvbf(8rAhpiQ4pLGk;QhOS;Q{Pr>yO;((Q-__0hU$D8$w|KLb^jt@8uYZk8 z@h236eV~1{oD40E#h^x14)V^<61sm7@zHR3W7_lXa@WkExgQ8ic-yXSa#EyAmdxaf z9&aJMayUiRj0|=Q6lT53NM8G2Iw}G*XvUHwROB@_Ql2XId7>i_8Ofx_)c6(S$_DA$oe-<>kn^HwdcjZA z`mI6(cIxzR7XbZKDq%A z&ETm)Ukg6`nM?Kkm8;8pCLZAz{Dc$hEiCLb++a0ld1#ti& z<1%x&Xq&b^MOK18u?2p3=VdmIpqc;0!-#eO0QMHp)KGa{3KIM9v)oP>*`DK1(tXy|86CX zq`#ot5??ux<~KuifX;OdupDw|p3k-%2BPKD9Gn;tp($@P{Qq2lh&+$kvrkUOi(*qn zxOGK7py1Qt$42o8*N$QQUBC8L^As`Nf^K2(RmehMcsOaNEX30QLyom)M9R>MZ=$|2 z&a;S`dEP8`8exokHTH_$1(Hra;4@xxTe{Yse!IM|xJZ(U+(P^9gHQdBtLvjC4+WS~rvHG^9Z(FC37b!mQOuMW+?qn%QvHYl~ou;hS>2_(OPg>6M#jVm0RH@~s@-idJ{AK%kI+@T&fL*MuBq8u^MPoj!S4}V;>g=Lw) zYHCEtV17*jXk~Cp+#D0qCIHAuEUHvn9u{D0iWRzB|WRjvEQKhWA29S{A^wvY|_08bJ2$+xRh>xSV1}iwsf2uZx?` zW_ii!F&KaYQ3htOv5H~>s~QIFgZNK`;0plK52d*Lyav~RA(~L)B9Hg6ngzxW#Aep1 zr9n2oUGMwX%F6W!a>2^-Z&8Imgkw&}$oP05t|AtEnv1gNPpaU$WZ~i^WXXZH>*&2T zeHqmD_R8(kOy*jFKebrA>vMkk)LclROQmqnpr%g0vDP#BDQl}BjQ|Jd!F!Tde^0)~ zCEJ)9aNRCi#As*tavnNa&?+Ttl>Xwxk56o_F9m8y(B}=GFOXov>2U8$W&J#aEssf@ zO?MJidOFv8IoL!D4Gw{9PfekT{>w-}iPf1|w5b@7n37kE||+ALUZ* zf$0Di(iSh7oF7ASO5Eq~gzQUbCZ-FdK#CH8CQYmaeU%mGMU=7)Uh+<$CBW031+ttC zT##5D{|7Tw7>;I9Q;Sp$CUQ$~KxFT#?MIthe!G*WWR6(*6OA z>a$nj4EPi7qxY}eiH|q7Z-RG|I58J0mp}8ua|Iakq zT_i7lF_jnrh#b+(o=sK;;E9p#DqhhkI2%p39G==+y#g|W>0zn_czBk(1~-26WtxZW zR4C4=S`9i1?ShXlB!PetlVCXKHCb2{Fd&2v@-x0cCGEE;GjXinKMLW#1(7f�uRW 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 diff --git a/docs/index.rst b/docs/index.rst index c972796eb..2fada0f56 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,21 +5,24 @@ Home :hidden: :maxdepth: 2 - getting_started/index + 1_installation + 2_package_overview + 3_getting_started + 4_minimal_example examples/index - details/index - api - glossary - faq - license + advanced_usage/index + 5_api + 6_references + 7_glossary + 8_faq + 9_license -SMAC is a tool for algorithm configuration to optimize the parameters of -arbitrary algorithms, including hyperparameter optimization of Machine Learning algorithms. The main core consists of -Bayesian Optimization in combination with an aggressive racing mechanism to -efficiently decide which of two configurations performs better. +SMAC is a tool for algorithm configuration to optimize the parameters of arbitrary algorithms, including hyperparameter +optimization of Machine Learning algorithms. The main core consists of Bayesian Optimization in combination with an +aggressive racing mechanism to efficiently decide which of two configurations performs better. -SMAC3 is written in Python3 and continuously tested with Python 3.7, 3.8 and 3.9. Its Random +SMAC3 is written in Python3 and continuously tested with Python 3.8, 3.9, and 3.10. Its Random Forest is written in C++. In further texts, SMAC is representatively mentioned for SMAC3. If you use SMAC, please cite our `JMLR paper `_: diff --git a/examples/1_basics/1_synthetic_function.py b/examples/1_basics/1_synthetic_function.py new file mode 100644 index 000000000..93182732f --- /dev/null +++ b/examples/1_basics/1_synthetic_function.py @@ -0,0 +1,67 @@ +""" +Synthetic Function +^^^^^^^^^^^^^^^^^^ + +An example of applying SMAC to optimize a synthetic function (2D Rosenbrock function). + +We use the black-box facade because it is designed for black-box function optimization. +The black-box facade uses a :term:`Gaussian Process` as its surrogate model. +The facade works best on a numerical hyperparameter configuration space and should not +be applied to problems with large evaluation budgets (up to 1000 evaluations). +""" + +from ConfigSpace import Configuration, ConfigurationSpace, Float + +from smac import BlackBoxFacade, Scenario + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +class Rosenbrock2D: + @property + def configspace(self) -> ConfigurationSpace: + cs = ConfigurationSpace(seed=0) + x0 = Float("x0", (-5, 10), default=-3) + x1 = Float("x1", (-5, 10), default=-4) + cs.add_hyperparameters([x0, x1]) + + return cs + + def train(self, config: Configuration, seed: int = 0) -> float: + """The 2-dimensional Rosenbrock function as a toy model. + The Rosenbrock function is well-known in the optimization community and + often serves as a toy problem. It can be defined for arbitrary + dimensions. The minimum is always at x_i = 1 with a function value of + zero. All input parameters are continuous. The search domain for + all x's is the interval [-5, 10]. + """ + x1 = config["x0"] + x2 = config["x1"] + + cost = 100.0 * (x2 - x1**2.0) ** 2.0 + (1 - x1) ** 2.0 + return cost + + +if __name__ == "__main__": + model = Rosenbrock2D() + + # Scenario object specifying the optimization "environment" + scenario = Scenario(model.configspace, name="synthetic_function", n_trials=300) + + # Now we use SMAC to find the best hyperparameters + smac = BlackBoxFacade( + scenario, + model.train, # We pass the target function here + overwrite=True, # Overrides any previous results that are found that are inconsistent with the meta-data. + ) + + incumbent = smac.optimize() + + # Get cost of default configuration + default_cost = smac.validate(model.configspace.get_default_configuration()) + print(f"Default cost: {default_cost}") + + # Let's calculate the cost of the incumbent + incumbent_cost = smac.validate(incumbent) + print(f"Default cost: {incumbent_cost}") diff --git a/examples/1_basics/2_svm_cv.py b/examples/1_basics/2_svm_cv.py new file mode 100644 index 000000000..9882ff011 --- /dev/null +++ b/examples/1_basics/2_svm_cv.py @@ -0,0 +1,103 @@ +""" +Support Vector Machine with Cross-Validation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +An example of optimizing a simple support vector machine on the IRIS dataset. We use the +hyperparameter optimization facade, which uses a random forest as its surrogate model. It is able to +scale to higher evaluation budgets and a higher number of dimensions. Also, you can use mixed data +types as well as conditional hyperparameters. +""" + +import numpy as np +from ConfigSpace import Categorical, Configuration, ConfigurationSpace, Float, Integer +from ConfigSpace.conditions import InCondition +from sklearn import datasets, svm +from sklearn.model_selection import cross_val_score + +from smac import HyperparameterOptimizationFacade, Scenario +from smac.initial_design import SobolInitialDesign + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +# We load the iris-dataset (a widely used benchmark) +iris = datasets.load_iris() + + +class SVM: + @property + def configspace(self) -> ConfigurationSpace: + # Build Configuration Space which defines all parameters and their ranges + cs = ConfigurationSpace(seed=0) + + # First we create our hyperparameters + kernel = Categorical("kernel", ["linear", "poly", "rbf", "sigmoid"], default="poly") + C = Float("C", (0.001, 1000.0), default=1.0, log=True) + shrinking = Categorical("shrinking", [True, False], default=True) + degree = Integer("degree", (1, 5), default=3) + coef = Float("coef0", (0.0, 10.0), default=0.0) + gamma = Categorical("gamma", ["auto", "value"], default="auto") + gamma_value = Float("gamma_value", (0.0001, 8.0), default=1.0, log=True) + + # Then we create dependencies + use_degree = InCondition(child=degree, parent=kernel, values=["poly"]) + use_coef = InCondition(child=coef, parent=kernel, values=["poly", "sigmoid"]) + use_gamma = InCondition(child=gamma, parent=kernel, values=["rbf", "poly", "sigmoid"]) + use_gamma_value = InCondition(child=gamma_value, parent=gamma, values=["value"]) + + # Add hyperparameters and conditions to our configspace + cs.add_hyperparameters([kernel, C, shrinking, degree, coef, gamma, gamma_value]) + cs.add_conditions([use_degree, use_coef, use_gamma, use_gamma_value]) + + return cs + + def train(self, config: Configuration, seed: int = 0) -> float: + """Creates a SVM based on a configuration and evaluates it on the + iris-dataset using cross-validation.""" + config_dict = config.get_dictionary() + if "gamma" in config: + config_dict["gamma"] = config_dict["gamma_value"] if config_dict["gamma"] == "value" else "auto" + config_dict.pop("gamma_value", None) + + classifier = svm.SVC(**config_dict, random_state=seed) + scores = cross_val_score(classifier, iris.data, iris.target, cv=5) + cost = 1 - np.mean(scores) + + return cost + + +if __name__ == "__main__": + classifier = SVM() + + # Next, we create an object, holding general information about the run + scenario = Scenario( + classifier.configspace, + n_trials=50, # We want to run max 50 trials (combination of config and seed) + ) + + # We want to run the facade's default initial design, but we want to alter the number + # of initial configs to be 5. + initial_design = HyperparameterOptimizationFacade.get_initial_design(scenario, n_configs=10) + + # You can also override the initial design completely, by providing another + # initial design class. + initial_design = SobolInitialDesign(scenario, n_configs=10) + + # Now we use SMAC to find the best hyperparameters + smac = HyperparameterOptimizationFacade( + scenario, + classifier.train, + initial_design=initial_design, + overwrite=True, # If the run exists, we overwrite it; alternatively, we can continue from last state + ) + + incumbent = smac.optimize() + + # Get cost of default configuration + default_cost = smac.validate(classifier.configspace.get_default_configuration()) + print(f"Default cost: {default_cost}") + + # Let's calculate the cost of the incumbent + incumbent_cost = smac.validate(incumbent) + print(f"Default cost: {incumbent_cost}") diff --git a/examples/1_basics/3_ask_and_tell.py b/examples/1_basics/3_ask_and_tell.py new file mode 100644 index 000000000..e22361e1d --- /dev/null +++ b/examples/1_basics/3_ask_and_tell.py @@ -0,0 +1,81 @@ +""" +Ask-and-Tell +^^^^^^^^^^^^ + +This examples show how to use the Ask-and-Tell interface. +""" + +from ConfigSpace import Configuration, ConfigurationSpace, Float + +from smac import HyperparameterOptimizationFacade, Scenario +from smac.runhistory.dataclasses import TrialValue + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +class Rosenbrock2D: + @property + def configspace(self) -> ConfigurationSpace: + cs = ConfigurationSpace(seed=0) + x0 = Float("x0", (-5, 10), default=-3) + x1 = Float("x1", (-5, 10), default=-4) + cs.add_hyperparameters([x0, x1]) + + return cs + + def train(self, config: Configuration, seed: int = 0) -> float: + """The 2-dimensional Rosenbrock function as a toy model. + The Rosenbrock function is well know in the optimization community and + often serves as a toy problem. It can be defined for arbitrary + dimensions. The minimium is always at x_i = 1 with a function value of + zero. All input parameters are continuous. The search domain for + all x's is the interval [-5, 10]. + """ + x1 = config["x0"] + x2 = config["x1"] + + cost = 100.0 * (x2 - x1**2.0) ** 2.0 + (1 - x1) ** 2.0 + return cost + + +if __name__ == "__main__": + model = Rosenbrock2D() + + # Scenario object + scenario = Scenario(model.configspace, deterministic=False, n_trials=100) + + intensifier = HyperparameterOptimizationFacade.get_intensifier( + scenario, + max_config_calls=1, # We basically use one seed only + ) + + # Now we use SMAC to find the best hyperparameters + smac = HyperparameterOptimizationFacade( + scenario, + model.train, + intensifier=intensifier, + overwrite=True, + ) + + # We can ask SMAC which trials should be evaluated next + for _ in range(10): + info = smac.ask() + assert info.seed is not None + + cost = model.train(info.config, seed=info.seed) + value = TrialValue(cost=cost, time=0.5) + + smac.tell(info, value) + + # After calling ask+tell, we can still optimize + incumbent = smac.optimize() + assert smac.stats.finished == 100 + + # Get cost of default configuration + default_cost = smac.validate(model.configspace.get_default_configuration()) + print(f"Default cost: {default_cost}") + + # Let's calculate the cost of the incumbent + incumbent_cost = smac.validate(incumbent) + print(f"Default cost: {incumbent_cost}") diff --git a/examples/1_basics/4_callback.py b/examples/1_basics/4_callback.py new file mode 100644 index 000000000..06f1d60be --- /dev/null +++ b/examples/1_basics/4_callback.py @@ -0,0 +1,84 @@ +""" +Custom Callback +^^^^^^^^^^^^^^^ + +Using callbacks is the easieast way to integrate custom code inside the Bayesian optimization loop. +In this example, we disable SMAC's default logging option and use the custom callback to log the evaluated trials. +Furthermore, we print some stages of the optimization process. +""" + +from __future__ import annotations + +from ConfigSpace import Configuration, ConfigurationSpace, Float + +import smac +from smac import Callback +from smac import HyperparameterOptimizationFacade as HPOFacade +from smac import Scenario +from smac.runhistory import TrialInfo, TrialValue + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +class Rosenbrock2D: + @property + def configspace(self) -> ConfigurationSpace: + cs = ConfigurationSpace(seed=0) + x0 = Float("x0", (-5, 10), default=-3) + x1 = Float("x1", (-5, 10), default=-4) + cs.add_hyperparameters([x0, x1]) + + return cs + + def train(self, config: Configuration, seed: int = 0) -> float: + x1 = config["x0"] + x2 = config["x1"] + + cost = 100.0 * (x2 - x1**2.0) ** 2.0 + (1 - x1) ** 2.0 + return cost + + +class CustomCallback(Callback): + def __init__(self) -> None: + self.trials_counter = 0 + + def on_start(self, smbo: smac.main.BaseSMBO) -> None: + print("Let's start!") + print("") + + def on_tell_end(self, smbo: smac.main.BaseSMBO, info: TrialInfo, value: TrialValue) -> bool | None: + self.trials_counter += 1 + if self.trials_counter % 10 == 0: + print(f"Evaluated {self.trials_counter} trials so far.") + + incumbent = smbo.incumbent + assert incumbent is not None + print(f"Current incumbent: {incumbent.get_dictionary()}") + print(f"Current incumbent value: {smbo.runhistory.get_cost(incumbent)}") + print("") + + if self.trials_counter == 50: + print(f"We just triggered to stop the optimization after {smbo.stats.finished} finished trials.") + return False + + return None + + +if __name__ == "__main__": + model = Rosenbrock2D() + + # Scenario object specifying the optimization "environment" + scenario = Scenario(model.configspace, n_trials=200) + + # Now we use SMAC to find the best hyperparameters + smac = HPOFacade( + scenario, + model.train, + overwrite=True, + callbacks=[CustomCallback()], + logging_level=999999, + ) + + # Let's optimize + smac.optimize() diff --git a/examples/1_basics/5_priors.py b/examples/1_basics/5_priors.py new file mode 100644 index 000000000..218bd8f46 --- /dev/null +++ b/examples/1_basics/5_priors.py @@ -0,0 +1,172 @@ +""" +User Priors over the Optimum +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Example for optimizing a Multi-Layer Perceptron (MLP) setting priors over the optimum on the +hyperparameters. These priors are derived from user knowledge (from previous runs on similar +tasks, common knowledge or intuition gained from manual tuning). To create the priors, we make +use of the Normal and Beta Hyperparameters, as well as the "weights" property of the +``CategoricalHyperparameter``. This can be integrated into the optimiztion for any SMAC facade, +but we stick with the hyperparameter optimization facade here. To incorporate user priors into the +optimization, you have to change the acquisition function to ``PriorAcquisitionFunction``. +""" + +import warnings + +import numpy as np +from ConfigSpace import ( + BetaIntegerHyperparameter, + CategoricalHyperparameter, + Configuration, + ConfigurationSpace, + NormalFloatHyperparameter, + UniformIntegerHyperparameter, +) +from sklearn.datasets import load_digits +from sklearn.exceptions import ConvergenceWarning +from sklearn.model_selection import StratifiedKFold, cross_val_score +from sklearn.neural_network import MLPClassifier + +from smac import HyperparameterOptimizationFacade, Scenario +from smac.acquisition.function import PriorAcquisitionFunction + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +digits = load_digits() + + +class MLP: + @property + def configspace(self) -> ConfigurationSpace: + # Build Configuration Space which defines all parameters and their ranges. + # To illustrate different parameter types, + # we use continuous, integer and categorical parameters. + cs = ConfigurationSpace() + + # We do not have an educated belief on the number of layers beforehand + # As such, the prior on the HP is uniform + n_layer = UniformIntegerHyperparameter( + "n_layer", + lower=1, + upper=5, + ) + + # We believe the optimal network is likely going to be relatively wide, + # And place a Beta Prior skewed towards wider networks in log space + n_neurons = BetaIntegerHyperparameter( + "n_neurons", + lower=8, + upper=256, + alpha=4, + beta=2, + log=True, + ) + + # We believe that ReLU is likely going to be the optimal activation function about + # 60% of the time, and thus place weight on that accordingly + activation = CategoricalHyperparameter( + "activation", + ["logistic", "tanh", "relu"], + weights=[1, 1, 3], + default_value="relu", + ) + + # Moreover, we believe ADAM is the most likely optimizer + optimizer = CategoricalHyperparameter( + "optimizer", + ["sgd", "adam"], + weights=[1, 2], + default_value="adam", + ) + + # We do not have an educated opinion on the batch size, and thus leave it as-is + batch_size = UniformIntegerHyperparameter( + "batch_size", + 16, + 512, + default_value=128, + ) + + # We place a log-normal prior on the learning rate, so that it is centered on 10^-3, + # with one unit of standard deviation per multiple of 10 (in log space) + learning_rate_init = NormalFloatHyperparameter( + "learning_rate_init", + lower=1e-5, + upper=1.0, + mu=np.log(1e-3), + sigma=np.log(10), + log=True, + ) + + # Add all hyperparameters at once: + cs.add_hyperparameters([n_layer, n_neurons, activation, optimizer, batch_size, learning_rate_init]) + + return cs + + def train(self, config: Configuration, seed: int = 0) -> float: + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=ConvergenceWarning) + + classifier = MLPClassifier( + hidden_layer_sizes=[config["n_neurons"]] * config["n_layer"], + solver=config["optimizer"], + batch_size=config["batch_size"], + activation=config["activation"], + learning_rate_init=config["learning_rate_init"], + random_state=seed, + max_iter=5, + ) + + # Returns the 5-fold cross validation accuracy + cv = StratifiedKFold(n_splits=5, random_state=seed, shuffle=True) # to make CV splits consistent + score = cross_val_score(classifier, digits.data, digits.target, cv=cv, error_score="raise") + + return 1 - np.mean(score) + + +if __name__ == "__main__": + mlp = MLP() + default_config = mlp.configspace.get_default_configuration() + + # Define our environment variables + scenario = Scenario(mlp.configspace, n_trials=40) + + # We also want to include our default configuration in the initial design + initial_design = HyperparameterOptimizationFacade.get_initial_design( + scenario, + additional_configs=[default_config], + ) + + # We define the prior acquisition function, which conduct the optimization using priors over the optimum + acquisition_function = PriorAcquisitionFunction( + acquisition_function=HyperparameterOptimizationFacade.get_acquisition_function(scenario), + decay_beta=scenario.n_trials / 10, # Proven solid value + ) + + # We only want one config call (use only one seed in this example) + intensifier = HyperparameterOptimizationFacade.get_intensifier( + scenario, + max_config_calls=1, + ) + + # Create our SMAC object and pass the scenario and the train method + smac = HyperparameterOptimizationFacade( + scenario, + mlp.train, + initial_design=initial_design, + acquisition_function=acquisition_function, + intensifier=intensifier, + overwrite=True, + ) + + incumbent = smac.optimize() + + # Get cost of default configuration + default_cost = smac.validate(default_config) + print(f"Default cost: {default_cost}") + + # Let's calculate the cost of the incumbent + incumbent_cost = smac.validate(incumbent) + print(f"Default cost: {incumbent_cost}") diff --git a/examples/1_basics/README.rst b/examples/1_basics/README.rst new file mode 100644 index 000000000..ab81e73c5 --- /dev/null +++ b/examples/1_basics/README.rst @@ -0,0 +1,2 @@ +Basics +------ \ No newline at end of file diff --git a/examples/2_multi_fidelity/1_mlp_epochs.py b/examples/2_multi_fidelity/1_mlp_epochs.py new file mode 100644 index 000000000..d187d16d5 --- /dev/null +++ b/examples/2_multi_fidelity/1_mlp_epochs.py @@ -0,0 +1,141 @@ +""" +Multi-Layer Perceptron Using Multiple Epochs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Example for optimizing a Multi-Layer Perceptron (MLP) using multiple budgets. +Since we want to take advantage of Multi-Fidelity, the ``MultiFidelityFacade`` is a good choice. By default, +``MultiFidelityFacade`` internally runs with `hyperband `_ as +intensification, which is a combination of an +aggressive racing mechanism and successive halving. Crucially, the target function function +must accept a budget variable, detailing how much fidelity smac wants to allocate to this +configuration. + +MLP is a deep neural network, and therefore, we choose epochs as fidelity type. This implies, +that ``budget`` specifies the number of epochs smac wants to allocate. The digits dataset +is chosen to optimize the average accuracy on 5-fold cross validation. + +.. note:: + + This example uses the ``MultiFidelityFacade`` facade, which is the closest implementation to + `BOHB `_. +""" + +import warnings + +import numpy as np +from ConfigSpace import ( + Categorical, + Configuration, + ConfigurationSpace, + EqualsCondition, + Float, + InCondition, + Integer, +) +from sklearn.datasets import load_digits +from sklearn.model_selection import StratifiedKFold, cross_val_score +from sklearn.neural_network import MLPClassifier + +from smac import MultiFidelityFacade, Scenario + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +digits = load_digits() + + +class MLP: + @property + def configspace(self) -> ConfigurationSpace: + # Build Configuration Space which defines all parameters and their ranges. + # To illustrate different parameter types, we use continuous, integer and categorical parameters. + cs = ConfigurationSpace() + + n_layer = Integer("n_layer", (1, 5), default=1) + n_neurons = Integer("n_neurons", (8, 256), log=True, default=10) + activation = Categorical("activation", ["logistic", "tanh", "relu"], default="tanh") + solver = Categorical("solver", ["lbfgs", "sgd", "adam"], default="adam") + batch_size = Integer("batch_size", (30, 300), default=200) + learning_rate = Categorical("learning_rate", ["constant", "invscaling", "adaptive"], default="constant") + learning_rate_init = Float("learning_rate_init", (0.0001, 1.0), default=0.001, log=True) + + # Add all hyperparameters at once: + cs.add_hyperparameters([n_layer, n_neurons, activation, solver, batch_size, learning_rate, learning_rate_init]) + + # Adding conditions to restrict the hyperparameter space... + # ... since learning rate is used when solver is 'sgd'. + use_lr = EqualsCondition(child=learning_rate, parent=solver, value="sgd") + # ... since learning rate initialization will only be accounted for when using 'sgd' or 'adam'. + use_lr_init = InCondition(child=learning_rate_init, parent=solver, values=["sgd", "adam"]) + # ... since batch size will not be considered when optimizer is 'lbfgs'. + use_batch_size = InCondition(child=batch_size, parent=solver, values=["sgd", "adam"]) + + # We can also add multiple conditions on hyperparameters at once: + cs.add_conditions([use_lr, use_batch_size, use_lr_init]) + + return cs + + def train(self, config: Configuration, seed: int = 0, budget: int = 25) -> float: + # For deactivated parameters (by virtue of the conditions), + # the configuration stores None-values. + # This is not accepted by the MLP, so we replace them with placeholder values. + lr = config["learning_rate"] if config["learning_rate"] else "constant" + lr_init = config["learning_rate_init"] if config["learning_rate_init"] else 0.001 + batch_size = config["batch_size"] if config["batch_size"] else 200 + + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + + classifier = MLPClassifier( + hidden_layer_sizes=[config["n_neurons"]] * config["n_layer"], + solver=config["solver"], + batch_size=batch_size, + activation=config["activation"], + learning_rate=lr, + learning_rate_init=lr_init, + max_iter=int(np.ceil(budget)), + random_state=seed, + ) + + # Returns the 5-fold cross validation accuracy + cv = StratifiedKFold(n_splits=5, random_state=seed, shuffle=True) # to make CV splits consistent + score = cross_val_score(classifier, digits.data, digits.target, cv=cv, error_score="raise") + + return 1 - np.mean(score) + + +if __name__ == "__main__": + mlp = MLP() + + # Define our environment variables + scenario = Scenario( + mlp.configspace, + walltime_limit=40, # After 40 seconds, we stop the hyperparameter optimization + n_trials=200, # Evaluate max 200 different trials + min_budget=5, # Train the MLP using a hyperparameter configuration for at least 5 epochs + max_budget=25, # Train the MLP using a hyperparameter configuration for at most 25 epochs + n_workers=1, + ) + + # We want to run five random configurations before starting the optimization. + initial_design = MultiFidelityFacade.get_initial_design(scenario, n_configs=5) + + # Create our SMAC object and pass the scenario and the train method + smac = MultiFidelityFacade( + scenario, + mlp.train, + initial_design=initial_design, + overwrite=True, + ) + + # Let's optimize + incumbent = smac.optimize() + + # Get cost of default configuration + default_cost = smac.validate(mlp.configspace.get_default_configuration()) + print(f"Default cost: {default_cost}") + + # Let's calculate the cost of the incumbent + incumbent_cost = smac.validate(incumbent) + print(f"Incumbent cost: {incumbent_cost}") diff --git a/examples/2_multi_fidelity/2_sgd_datasets.py b/examples/2_multi_fidelity/2_sgd_datasets.py new file mode 100644 index 000000000..4d344224e --- /dev/null +++ b/examples/2_multi_fidelity/2_sgd_datasets.py @@ -0,0 +1,139 @@ +""" +Stochastic Gradient Descent On Multiple Datasets +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Example for optimizing a Multi-Layer Perceptron (MLP) across multiple (dataset) instances. + +Alternative to budgets, here wlog. we consider instances as a fidelity type. An instance represents a specific +scenario/condition (e.g. different datasets, subsets, transformations) for the algorithm to run. SMAC then returns the +algorithm that had the best performance across all the instances. In this case, an instance is a binary dataset i.e., +digit-2 vs digit-3. + +If we use instance as our fidelity, we need to initialize scenario with argument instance. In this case the argument +budget is no longer required by the target function. But due to the scenario instance argument, +the target function now is required to have an instance argument. +""" + +from __future__ import annotations + +import itertools +import warnings + +import numpy as np +from ConfigSpace import Categorical, Configuration, ConfigurationSpace, Float +from sklearn import datasets +from sklearn.linear_model import SGDClassifier +from sklearn.model_selection import StratifiedKFold, cross_val_score + +from smac import MultiFidelityFacade, Scenario + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +class DigitsDataset: + def __init__(self) -> None: + self._data = datasets.load_digits() + + def get_instances(self) -> list[str]: + """Create instances from the dataset which include two classes only.""" + return [f"{classA}-{classB}" for classA, classB in itertools.combinations(self._data.target_names, 2)] + + def get_instance_features(self) -> dict[str, list[int | float]]: + """Returns the mean and variance of all instances as features.""" + features = {} + for instance in self.get_instances(): + data, _ = self.get_instance_data(instance) + features[instance] = [np.mean(data), np.var(data)] + + return features + + def get_instance_data(self, instance: str) -> tuple[np.ndarray, np.ndarray]: + """Retrieve data from the passed instance.""" + # We split the dataset into two classes + classA, classB = instance.split("-") + indices = np.where(np.logical_or(int(classA) == self._data.target, int(classB) == self._data.target)) + + data = self._data.data[indices] + target = self._data.target[indices] + + return data, target + + +class SGD: + def __init__(self, dataset: DigitsDataset) -> None: + self.dataset = dataset + + @property + def configspace(self) -> ConfigurationSpace: + """Build the configuration space which defines all parameters and their ranges for the SGD classifier.""" + cs = ConfigurationSpace() + + # We define a few possible parameters for the SGD classifier + alpha = Float("alpha", (0, 1), default=1.0) + l1_ratio = Float("l1_ratio", (0, 1), default=0.5) + learning_rate = Categorical("learning_rate", ["constant", "invscaling", "adaptive"], default="constant") + eta0 = Float("eta0", (0.00001, 1), default=0.1, log=True) + # Add the parameters to configuration space + cs.add_hyperparameters([alpha, l1_ratio, learning_rate, eta0]) + + return cs + + def train(self, config: Configuration, instance: str, seed: int = 0) -> float: + """Creates a SGD classifier based on a configuration and evaluates it on the + digits dataset using cross-validation.""" + + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + + # SGD classifier using given configuration + clf = SGDClassifier( + loss="log", + penalty="elasticnet", + alpha=config["alpha"], + l1_ratio=config["l1_ratio"], + learning_rate=config["learning_rate"], + eta0=config["eta0"], + max_iter=30, + early_stopping=True, + random_state=seed, + ) + + # get instance + data, target = self.dataset.get_instance_data(instance) + + cv = StratifiedKFold(n_splits=4, random_state=seed, shuffle=True) # to make CV splits consistent + scores = cross_val_score(clf, data, target, cv=cv) + + return 1 - np.mean(scores) + + +if __name__ == "__main__": + dataset = DigitsDataset() + model = SGD(dataset) + + scenario = Scenario( + model.configspace, + walltime_limit=30, # We want to optimize for 30 seconds + n_trials=5000, # We want to try max 5000 different trials + min_budget=1, # Use min one instance + max_budget=45, # Use max 45 instances (if we have a lot of instances we could constraint it here) + instances=dataset.get_instances(), + instance_features=dataset.get_instance_features(), + ) + + # Create our SMAC object and pass the scenario and the train method + smac = MultiFidelityFacade( + scenario, + model.train, + overwrite=True, + ) + + # Now we start the optimization process + incumbent = smac.optimize() + + default_cost = smac.validate(model.configspace.get_default_configuration()) + print(f"Default cost: {default_cost}") + + incumbent_cost = smac.validate(incumbent) + print(f"Incumbent cost: {incumbent_cost}") diff --git a/examples/2_multi_fidelity/README.rst b/examples/2_multi_fidelity/README.rst new file mode 100644 index 000000000..77811397b --- /dev/null +++ b/examples/2_multi_fidelity/README.rst @@ -0,0 +1,3 @@ +Multi-Fidelity and Multi-Instances +---------------------------------- + diff --git a/examples/3_multi_objective/1_schaffer.py b/examples/3_multi_objective/1_schaffer.py new file mode 100644 index 000000000..ad5053d68 --- /dev/null +++ b/examples/3_multi_objective/1_schaffer.py @@ -0,0 +1,88 @@ +""" +2D Schaffer Function with Objective Weights +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A simple example on how to use multi-objective optimization is shown. The `schaffer` function is used. +""" + +from __future__ import annotations + +from typing import Dict, Tuple + +import numpy as np +from ConfigSpace import Configuration, ConfigurationSpace +from matplotlib import pyplot as plt + +from smac import HyperparameterOptimizationFacade as HPOFacade +from smac import Scenario +from smac.facade import AbstractFacade + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def schaffer(x: float) -> Tuple[float, float]: + f1 = np.square(x) + f2 = np.square(np.sqrt(f1) - 2) + + return f1, f2 + + +def target_function(config: Configuration, seed: int = 0) -> Dict[str, float]: + f1, f2 = schaffer(config["x"]) + return {"metric1": f1, "metric2": f2} + + +def plot(all_x: list[float]) -> None: + plt.figure() + for x in all_x: + f1, f2 = schaffer(x) + plt.scatter(f1, f2, c="blue", alpha=0.1, zorder=3000) + + # plt.vlines([1], 0, 4, linestyles="dashed", colors=["red"]) + # plt.hlines([1], 0, 4, linestyles="dashed", colors=["red"]) + + plt.show() + + +def plot_from_smac(smac: AbstractFacade) -> None: + rh = smac.runhistory + all_x = [] + for trial_key in rh: + config = rh.ids_config[trial_key.config_id] + all_x.append(config["x"]) + + plot(all_x) + + +if __name__ == "__main__": + # Simple configspace + cs = ConfigurationSpace({"x": (-2.0, 2.0)}) + + # Scenario object + scenario = Scenario( + configspace=cs, + deterministic=True, # Only one seed + n_trials=150, + objectives=["metric1", "metric2"], + ) + + smac = HPOFacade( + scenario=scenario, + target_function=target_function, + multi_objective_algorithm=HPOFacade.get_multi_objective_algorithm( + scenario, + objective_weights=[1, 2], # Weight metric2 twice as much as metric1 + ), + overwrite=True, + ) + incumbent = smac.optimize() + + default_cost = smac.validate(cs.get_default_configuration()) + print(f"Default costs: {default_cost}") + + incumbent_cost = smac.validate(incumbent) + print(f"Incumbent costs: {incumbent_cost}") + + # Plot the evaluated points + plot_from_smac(smac) diff --git a/examples/3_multi_objective/2_parego.py b/examples/3_multi_objective/2_parego.py new file mode 100644 index 000000000..d691afc29 --- /dev/null +++ b/examples/3_multi_objective/2_parego.py @@ -0,0 +1,176 @@ +""" +ParEGO +^^^^^^ + +An example of how to use multi-objective optimization with ParEGO. Both accuracy and run-time are going to be +optimized, and the configurations are shown in a plot, highlighting the best ones in a Pareto front. The red cross +indicates the best configuration selected by SMAC. + +In the optimization, SMAC evaluates the configurations on three different seeds. Therefore, the plot shows the +mean accuracy and run-time of each configuration. +""" +from __future__ import annotations + +import time +import warnings + +import matplotlib.pyplot as plt +import numpy as np +from ConfigSpace import ( + Categorical, + Configuration, + ConfigurationSpace, + EqualsCondition, + Float, + InCondition, + Integer, +) +from sklearn.datasets import load_digits +from sklearn.model_selection import StratifiedKFold, cross_val_score +from sklearn.neural_network import MLPClassifier + +from smac import HyperparameterOptimizationFacade as HPOFacade +from smac import Scenario +from smac.facade.abstract_facade import AbstractFacade +from smac.multi_objective.parego import ParEGO + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +digits = load_digits() + + +class MLP: + @property + def configspace(self) -> ConfigurationSpace: + cs = ConfigurationSpace() + + n_layer = Integer("n_layer", (1, 5), default=1) + n_neurons = Integer("n_neurons", (8, 256), log=True, default=10) + activation = Categorical("activation", ["logistic", "tanh", "relu"], default="tanh") + solver = Categorical("solver", ["lbfgs", "sgd", "adam"], default="adam") + batch_size = Integer("batch_size", (30, 300), default=200) + learning_rate = Categorical("learning_rate", ["constant", "invscaling", "adaptive"], default="constant") + learning_rate_init = Float("learning_rate_init", (0.0001, 1.0), default=0.001, log=True) + + cs.add_hyperparameters([n_layer, n_neurons, activation, solver, batch_size, learning_rate, learning_rate_init]) + + use_lr = EqualsCondition(child=learning_rate, parent=solver, value="sgd") + use_lr_init = InCondition(child=learning_rate_init, parent=solver, values=["sgd", "adam"]) + use_batch_size = InCondition(child=batch_size, parent=solver, values=["sgd", "adam"]) + + # We can also add multiple conditions on hyperparameters at once: + cs.add_conditions([use_lr, use_batch_size, use_lr_init]) + + return cs + + def train(self, config: Configuration, seed: int = 0, budget: int = 10) -> dict[str, float]: + lr = config["learning_rate"] if config["learning_rate"] else "constant" + lr_init = config["learning_rate_init"] if config["learning_rate_init"] else 0.001 + batch_size = config["batch_size"] if config["batch_size"] else 200 + + start_time = time.time() + + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + + classifier = MLPClassifier( + hidden_layer_sizes=[config["n_neurons"]] * config["n_layer"], + solver=config["solver"], + batch_size=batch_size, + activation=config["activation"], + learning_rate=lr, + learning_rate_init=lr_init, + max_iter=int(np.ceil(budget)), + random_state=seed, + ) + + # Returns the 5-fold cross validation accuracy + cv = StratifiedKFold(n_splits=5, random_state=seed, shuffle=True) # to make CV splits consistent + score = cross_val_score(classifier, digits.data, digits.target, cv=cv, error_score="raise") + + return { + "1 - accuracy": 1 - np.mean(score), + "time": time.time() - start_time, + } + + +def plot_pareto(smac: AbstractFacade) -> None: + """Plots configurations from SMAC and highlights the best configurations in a Pareto front.""" + # Get Pareto costs + _, c = smac.runhistory.get_pareto_front() + pareto_costs = np.array(c) + + # Sort them a bit + pareto_costs = pareto_costs[pareto_costs[:, 0].argsort()] + + # Get all other costs from runhistory + average_costs = [] + for config in smac.runhistory.get_configs(): + # Since we use multiple seeds, we have to average them to get only one cost value pair for each configuration + average_cost = smac.runhistory.average_cost(config) + + if average_cost not in c: + average_costs += [average_cost] + + # Let's work with a numpy array + costs = np.vstack(average_costs) + costs_x, costs_y = costs[:, 0], costs[:, 1] + pareto_costs_x, pareto_costs_y = pareto_costs[:, 0], pareto_costs[:, 1] + + plt.scatter(costs_x, costs_y, marker="x") + plt.scatter(pareto_costs_x, pareto_costs_y, marker="x", c="r") + plt.step( + [pareto_costs_x[0]] + pareto_costs_x.tolist() + [np.max(costs_x)], # We add bounds + [np.max(costs_y)] + pareto_costs_y.tolist() + [np.min(pareto_costs_y)], # We add bounds + where="post", + linestyle=":", + ) + + plt.title("Pareto-Front") + plt.xlabel(smac.scenario.objectives[0]) + plt.ylabel(smac.scenario.objectives[1]) + plt.show() + + +if __name__ == "__main__": + mlp = MLP() + + # Define our environment variables + scenario = Scenario( + mlp.configspace, + objectives=["1 - accuracy", "time"], + walltime_limit=40, # After 40 seconds, we stop the hyperparameter optimization + n_trials=200, # Evaluate max 200 different trials + n_workers=1, + ) + + # We want to run five random configurations before starting the optimization. + initial_design = HPOFacade.get_initial_design(scenario, n_configs=5) + multi_objective_algorithm = ParEGO(scenario) + + # Create our SMAC object and pass the scenario and the train method + smac = HPOFacade( + scenario, + mlp.train, + initial_design=initial_design, + multi_objective_algorithm=multi_objective_algorithm, + overwrite=True, + ) + + # Let's optimize + # Keep in mind: The incumbent is ambiguous here because of ParEGO + smac.optimize() + + # Get cost of default configuration + default_cost = smac.validate(mlp.configspace.get_default_configuration()) + print(f"Default costs: {default_cost}\n") + + print("Validated costs from the Pareto front:") + for i, config in enumerate(smac.runhistory.get_pareto_front()[0]): + cost = smac.validate(config) + print(cost) + + # Let's plot a pareto front + plot_pareto(smac) diff --git a/examples/3_multi_objective/README.rst b/examples/3_multi_objective/README.rst new file mode 100644 index 000000000..995ad9408 --- /dev/null +++ b/examples/3_multi_objective/README.rst @@ -0,0 +1,3 @@ +Multi-Objective +--------------- + diff --git a/examples/4_advanced_optimizer/1_turbo_optimizer.py b/examples/4_advanced_optimizer/1_turbo_optimizer.py new file mode 100644 index 000000000..860243c02 --- /dev/null +++ b/examples/4_advanced_optimizer/1_turbo_optimizer.py @@ -0,0 +1,110 @@ +# """ +# Synthetic Function with TuRBO as optimizer +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +# An example of applying SMAC with trust region BO (TuRBO) to optimize a +# synthetic function (2d rosenbrock function). + +# Eriksson et al. Scalable Global Optimization via Local {Bayesian} Optimization, +# http://papers.nips.cc/paper/8788-scalable-global-optimization-via-local-bayesian-optimization.pdf + +# TurBO gradually shrinks its search space to the vicinity of the optimum configuration that is ever optimized. +# TuRBO optimizer requires EPMChooserTurBO to suggest the next configuration. Currently, it only supports pure numerical +# hyperparameters. +# """ +# from ConfigSpace import Configuration, ConfigurationSpace, Float + +# # Import SMAC-utilities +# from smac.scenario import Scenario + +# # Import components required by TuRBO optimizers +# from smac.facade.blackbox_facade import BlackBoxFacade +# from smac.main.turbo import TuRBOSMBO + + +# class Rosenbrock2D: +# @property +# def configspace(self) -> ConfigurationSpace: +# cs = ConfigurationSpace(seed=0) +# x0 = Float("x0", (-5, 10), default=-3) +# x1 = Float("x1", (-5, 10), default=-4) +# cs.add_hyperparameters([x0, x1]) + +# return cs + +# def train(self, config: Configuration) -> float: +# """The 2-dimensional Rosenbrock function as a toy model. +# The Rosenbrock function is well know in the optimization community and +# often serves as a toy problem. It can be defined for arbitrary +# dimensions. The minimium is always at x_i = 1 with a function value of +# zero. All input parameters are continuous. The search domain for +# all x's is the interval [-5, 10]. +# """ +# x1 = config["x0"] +# x2 = config["x1"] + +# cost = 100.0 * (x2 - x1**2.0) ** 2.0 + (1 - x1) ** 2.0 +# return cost + + +# class TuRBOFacade(BlackBoxFacade): +# """A wrapper that allows to run TuRBO optimizer. Its arguments are described under smac.main.turbo.TuRBOSMBO""" + +# def _init_optimizer( +# self, +# length_init=0.8, +# length_min=0.5**8, +# length_max=1.6, +# success_tol=3, +# failure_tol_min=4, +# n_init_x_params=2, +# n_candidate_max=5000, +# ) -> None: +# self.optimizer = TuRBOSMBO( +# length_init=length_init, +# length_min=length_min, +# length_max=length_max, +# success_tol=success_tol, +# failure_tol_min=failure_tol_min, +# n_init_x_params=n_init_x_params, +# n_candidate_max=n_candidate_max, +# scenario=self._scenario, +# stats=self.stats, +# runner=self.runner, +# initial_design=self.initial_design, +# runhistory=self.runhistory, +# runhistory_encoder=self.runhistory_encoder, +# intensifier=self.intensifier, +# model=self.model, +# acquisition_function=self.acquisition_function, +# acquisition_optimizer=self.acquisition_optimizer, +# random_design=self.random_design, +# seed=self.seed, +# ) + + +# if __name__ == "__main__": +# exit() + +# model = Rosenbrock2D() + +# # Scenario object +# scenario = Scenario(model.configspace, n_trials=100) + +# # Example call of the target function +# default_value = model.train(model.configspace.get_default_configuration()) +# print(f"Default value: {round(default_value, 2)}") + +# # Optimize, using a SMAC-object +# print("Optimizing! Depending on your machine, this might take a few minutes.") +# smac = TuRBOFacade( +# scenario=scenario, +# target_function=model.train, +# overwrite=True, +# ) +# smac.optimize() + +# incumbent = smac.optimize() + +# incumbent_value = model.train(incumbent) +# print(f"Incumbent value: {round(incumbent_value, 2)}") diff --git a/examples/4_advanced_optimizer/2_boing_optimizer.py b/examples/4_advanced_optimizer/2_boing_optimizer.py new file mode 100644 index 000000000..3eb1a1869 --- /dev/null +++ b/examples/4_advanced_optimizer/2_boing_optimizer.py @@ -0,0 +1,72 @@ +# """ +# Synthetic Function with BOinG as optimizer +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +# An example of applying SMAC with BO inside Grove (BOinG) to optimize a +# synthetic function (2d rosenbrock function). + +# BOinG optimizer requires a SMAC4BOING wrapper to optimize the target function. It is a two stage BO algorithm. +# In the first stage, BOinG constructs an RF to capture the global loss landscape. Then in the second stage, it only +# optimizes inside a subregion near the candidate suggested by the RF model with a GP model to focus only on the most +# promising region. +# """ + +# # Import SMAC-utilities +# from smac.scenario import Scenario + +# from smac.facade.boing_facade import BOinGFacade + +# # from .syn_func import Rosenbrock2D + +# from ConfigSpace import Configuration, ConfigurationSpace, Float + + +# class Rosenbrock2D: +# @property +# def configspace(self) -> ConfigurationSpace: +# cs = ConfigurationSpace(seed=0) +# x0 = Float("x0", (-5, 10), default=-3) +# x1 = Float("x1", (-5, 10), default=-4) +# cs.add_hyperparameters([x0, x1]) + +# return cs + +# def train(self, config: Configuration) -> float: +# """The 2-dimensional Rosenbrock function as a toy model. +# The Rosenbrock function is well know in the optimization community and +# often serves as a toy problem. It can be defined for arbitrary +# dimensions. The minimium is always at x_i = 1 with a function value of +# zero. All input parameters are continuous. The search domain for +# all x's is the interval [-5, 10]. +# """ +# x1 = config["x0"] +# x2 = config["x1"] + +# cost = 100.0 * (x2 - x1**2.0) ** 2.0 + (1 - x1) ** 2.0 +# return cost + + +# if __name__ == "__main__": +# exit() + +# model = Rosenbrock2D() + +# # Scenario object +# scenario = Scenario(model.configspace, n_trials=100) + +# # Example call of the target function +# default_value = model.train(model.configspace.get_default_configuration()) +# print(f"Default value: {round(default_value, 2)}") + +# # Optimize, using a SMAC-object +# print("Optimizing! Depending on your machine, this might take a few minutes.") +# smac = BOinGFacade( +# scenario=scenario, +# target_function=model.train, +# overwrite=True, +# ) + +# incumbent = smac.optimize() + +# incumbent_value = model.train(incumbent) +# print(f"Incumbent value: {round(incumbent_value, 2)}") diff --git a/examples/5_commandline/1_call_target_function_script.py b/examples/5_commandline/1_call_target_function_script.py new file mode 100644 index 000000000..8c28ae0a3 --- /dev/null +++ b/examples/5_commandline/1_call_target_function_script.py @@ -0,0 +1,59 @@ +""" +Call Target Function From Script +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This simple example shows how to call a script with the following content: + +.. code-block:: bash + + #!/bin/bash + + # Set arguments first + for argument in "$@" + do + key=$(echo $argument | cut -f1 -d=) + value=$(echo $argument | cut -f2 -d=) + + if [[ $key == *"--"* ]]; then + v="${key/--/}" + declare $v="${value}" + fi + done + + # We simply set the cost to our parameter + cost=$x0 + + # Return everything + echo "cost=$cost" + +""" + +from ConfigSpace import ConfigurationSpace + +from smac import BlackBoxFacade, Scenario + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +if __name__ == "__main__": + cs = ConfigurationSpace({"x0": (0, 1000)}, seed=0) + + # Scenario object specifying the optimization "environment" + scenario = Scenario(cs, deterministic=True, n_trials=30) + + # Now we use SMAC to find the best hyperparameters + smac = BlackBoxFacade( + scenario, + "./script.sh", # We pass the filename of our script here + overwrite=True, # Overrides any previous results that are found that are inconsistent with the meta-data + ) + incumbent = smac.optimize() + + # Get cost of default configuration + default_cost = smac.validate(cs.get_default_configuration()) + print(f"Default cost: {default_cost}") + + # Let's calculate the cost of the incumbent + incumbent_cost = smac.validate(incumbent) + print(f"Incumbent cost: {incumbent_cost}") diff --git a/examples/5_commandline/README.rst b/examples/5_commandline/README.rst new file mode 100644 index 000000000..7614fd90d --- /dev/null +++ b/examples/5_commandline/README.rst @@ -0,0 +1,4 @@ +Command-Line Interface +---------------------- + +SMAC can call a target function from a script. This is useful if you want to optimize non-python code. \ No newline at end of file diff --git a/examples/5_commandline/script.sh b/examples/5_commandline/script.sh new file mode 100755 index 000000000..0a2d09118 --- /dev/null +++ b/examples/5_commandline/script.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Set arguments first +for argument in "$@" +do + key=$(echo $argument | cut -f1 -d=) + value=$(echo $argument | cut -f2 -d=) + + if [[ $key == *"--"* ]]; then + v="${key/--/}" + declare $v="${value}" + fi +done + +# We simply set the cost to our parameter +cost=$x0 + +# Return everything +echo "cost=$cost" \ No newline at end of file diff --git a/examples/README.rst b/examples/README.rst index 7c04aa758..deaa8fbac 100644 --- a/examples/README.rst +++ b/examples/README.rst @@ -1,5 +1,5 @@ Examples ======== -We provide several examples for both python and commandline. Practical use-cases were chosen to show the +We provide several examples of how to use SMAC with Python. Practical use-cases were chosen to show the variety of SMAC. \ No newline at end of file diff --git a/examples/commandline/README.rst b/examples/commandline/README.rst deleted file mode 100644 index eec160f9f..000000000 --- a/examples/commandline/README.rst +++ /dev/null @@ -1,2 +0,0 @@ -Commandline -~~~~~~~~~~~ \ No newline at end of file diff --git a/examples/commandline/branin.py b/examples/commandline/branin.py deleted file mode 100644 index de8329b87..000000000 --- a/examples/commandline/branin.py +++ /dev/null @@ -1,65 +0,0 @@ -""" -Branin -^^^^^^ - -This file is a wrapper used by SMAC to optimize parameters on the branin function. -To run this example in the terminal, execute: - -.. code-block:: bash - - python ./scripts/smac.py --scenario examples/commandline/branin/scenario.txt - - -Inside the scenario, this file and also ``configspace.pcs`` is referenced and therefore used -for the optimization. A full call by SMAC looks like this: - -.. code-block:: bash - - - python branin.py 0 0 9999999 0 12345 -x1 0 -x2 0 - - -Since SMAC processes results from the commandline, print-statements are -crucial. The format of the results must be the following to ensure correct usage: - -.. code-block:: bash - - Result for SMAC: , , , , , - -""" - -import sys - -import numpy as np - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def branin(x): - x1 = x[0] - x2 = x[1] - a = 1.0 - b = 5.1 / (4.0 * np.pi**2) - c = 5.0 / np.pi - r = 6.0 - s = 10.0 - t = 1.0 / (8.0 * np.pi) - ret = a * (x2 - b * x1**2 + c * x1 - r) ** 2 + s * (1 - t) * np.cos(x1) + s - - return ret - - -if __name__ == "__main__": - # Unused in this example: - # instance, instance_specific, cutoff, runlength = sys.argv[1:5] - seed = sys.argv[5] - - # sys.argv[6] and sys.argv[8] are the names of the target algorithm - # parameters (here: "-x1", "-x2") - x = float(sys.argv[7]) - y = float(sys.argv[9]) - result = branin((x, y)) - - # This line is important so the result can be processed by SMAC: - print("Result for SMAC: SUCCESS, -1, -1, %f, %s" % (result, seed)) diff --git a/examples/commandline/branin/configspace.pcs b/examples/commandline/branin/configspace.pcs deleted file mode 100644 index 233c070d8..000000000 --- a/examples/commandline/branin/configspace.pcs +++ /dev/null @@ -1,2 +0,0 @@ -x1 [-5,10] [0] -x2 [0,15] [0] \ No newline at end of file diff --git a/examples/commandline/branin/scenario.txt b/examples/commandline/branin/scenario.txt deleted file mode 100644 index af0585735..000000000 --- a/examples/commandline/branin/scenario.txt +++ /dev/null @@ -1,5 +0,0 @@ -algo = python examples/commandline/branin.py -paramfile = examples/commandline/branin/configspace.pcs -run_obj = quality -runcount_limit = 10 -deterministic = 1 diff --git a/examples/commandline/restore_branin.py b/examples/commandline/restore_branin.py deleted file mode 100644 index 0a20f67d7..000000000 --- a/examples/commandline/restore_branin.py +++ /dev/null @@ -1,95 +0,0 @@ -""" -Restore Branin -^^^^^^^^^^^^^^ -This file runs SMAC and then restores the branin run with an extended computation -budget. This will also work for SMAC runs that have crashed and are continued. - -""" - -import logging -import os - -from smac.facade.smac_ac_facade import SMAC4AC -from smac.runhistory.runhistory import RunHistory -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.utils.io.traj_logging import TrajLogger - -logging.basicConfig(level=logging.INFO) - - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -if "__main__" == __name__: - - # Initialize scenario, using runcount_limit as budget. - original_scenario_dict = { - "algo": "python examples/commandline/branin.py", - "paramfile": "examples/commandline/branin/configspace.pcs", - "run_obj": "quality", - "runcount_limit": 25, - "deterministic": True, - "output_dir": "restore_me", - } - original_scenario = Scenario(original_scenario_dict) - - smac = SMAC4AC(scenario=original_scenario, run_id=1) - smac.optimize() - - print("\nBudget exhausted! Starting restoring optimization ...\n") - - # Now the output is in the folder 'restore_me/run_1' (or whatever run_id has - # been passed to the SMAC-object above) - old_output_dir = os.path.join(original_scenario.output_dir, "run_1") - - # We could simply modify the scenario-object, stored in - # 'smac.solver.scenario' and start optimization again: - - # smac.solver.scenario.ta_run_limit = 50 - # smac.optimize() - - # Or, to show the whole process of recovering a SMAC-run from the output - # directory, create a new scenario with an extended budget: - new_scenario = Scenario( - original_scenario_dict, - cmd_options={ - "runcount_limit": 50, # overwrite these args - "output_dir": "restored", - }, - ) - - # We load the runhistory - rh_path = os.path.join(old_output_dir, "runhistory.json") - runhistory = RunHistory() - runhistory.load_json(rh_path, new_scenario.cs) - - # And the stats - stats_path = os.path.join(old_output_dir, "stats.json") - stats = Stats(new_scenario) - stats.load(stats_path) - - # And the trajectory - traj_path = os.path.join(old_output_dir, "traj_aclib2.json") - trajectory = TrajLogger.read_traj_aclib_format(fn=traj_path, cs=new_scenario.cs) - incumbent = trajectory[-1]["incumbent"] - - # Now we can initialize SMAC with the recovered objects and restore the - # state where we left off. By providing stats and a restore_incumbent, SMAC - # automatically detects the intention of restoring a state. - smac = SMAC4AC( - scenario=new_scenario, - runhistory=runhistory, - stats=stats, - restore_incumbent=incumbent, - run_id=1, - ) - - # Because we changed the output_dir, we might want to copy the old - # trajectory-file (runhistory and stats will be complete, but trajectory is - # written sequentially) - # new_traj_path = os.path.join(new_scenario.output_dir, "run_1", "traj_aclib2.json") - # shutil.copy(traj_path, new_traj_path) - - smac.optimize() diff --git a/examples/commandline/spear_qcp.py b/examples/commandline/spear_qcp.py deleted file mode 100644 index 5cf408042..000000000 --- a/examples/commandline/spear_qcp.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -SPEAR-QCP -^^^^^^^^^ - -We optimize the SPEAR algorithm on QCP to demonstrate the powerful ROAR and SMAC4AC facade using -commandline. Algorithm and instance definition is done inside scenario file. - - -ROAR: - -.. code-block:: bash - - $ python3 ./scripts/smac.py --scenario examples/commandline/spear_qcp/scenario.txt --verbose DEBUG --mode ROAR - - -SMAC4AC: - -.. code-block:: bash - - $ python3 ./scripts/smac.py --scenario examples/commandline/spear_qcp/scenario.txt --verbose DEBUG --mode SMAC4AC - -""" diff --git a/examples/commandline/spear_qcp/features.txt b/examples/commandline/spear_qcp/features.txt deleted file mode 100644 index f2a1a3c0d..000000000 --- a/examples/commandline/spear_qcp/features.txt +++ /dev/null @@ -1,5 +0,0 @@ -instance,nvarsOrig,nclausesOrig,nvars,nclauses,reducedVars,reducedClauses,Pre-featuretime,vars-clauses-ratio,POSNEG-RATIO-CLAUSE-mean,POSNEG-RATIO-CLAUSE-coeff-variation,POSNEG-RATIO-CLAUSE-min,POSNEG-RATIO-CLAUSE-max,POSNEG-RATIO-CLAUSE-entropy,VCG-CLAUSE-mean,VCG-CLAUSE-coeff-variation,VCG-CLAUSE-min,VCG-CLAUSE-max,VCG-CLAUSE-entropy,UNARY,BINARY+,TRINARY+,Basic-featuretime,VCG-VAR-mean,VCG-VAR-coeff-variation,VCG-VAR-min,VCG-VAR-max,VCG-VAR-entropy,POSNEG-RATIO-VAR-mean,POSNEG-RATIO-VAR-stdev,POSNEG-RATIO-VAR-min,POSNEG-RATIO-VAR-max,POSNEG-RATIO-VAR-entropy,HORNY-VAR-mean,HORNY-VAR-coeff-variation,HORNY-VAR-min,HORNY-VAR-max,HORNY-VAR-entropy,horn-clauses-fraction,VG-mean,VG-coeff-variation,VG-min,VG-max,KLB-featuretime,CG-mean,CG-coeff-variation,CG-min,CG-max,CG-entropy,cluster-coeff-mean,cluster-coeff-coeff-variation,cluster-coeff-min,cluster-coeff-max,cluster-coeff-entropy,CG-featuretime -examples/commandline/spear_qcp/instances/qcplin2006.10218.cnf,1066.000000000,7672.000000000,1066.000000000,7672.000000000,0.000000000,0.000000000,0.000000000,0.138946820,1.000000000,0.000000000,1.000000000,1.000000000,-0.000000000,0.002104580,0.414210982,0.001876173,0.009380863,0.419454484,0.000000000,0.919968717,0.934176225,0.000000000,0.002104580,0.211989361,0.000912409,0.003388947,2.637733740,0.609452265,0.093700913,0.142857143,0.769230769,2.634151109,0.001713548,0.260365412,0.000521376,0.002997914,2.637733740,0.913321168,0.001713548,0.260365412,0.000521376,0.002997914,0.000000000,0.001190460,1.697700286,0.000651721,0.018899896,0.685808369,0.308779748,0.259989170,0.013698630,0.333333333,0.489480586,0.040000000 -examples/commandline/spear_qcp/instances/qcplin2006.1031.cnf,4414.000000000,47942.000000000,4414.000000000,47942.000000000,0.000000000,0.000000000,0.080000000,0.092069584,1.000000000,0.000000000,1.000000000,1.000000000,-0.000000000,0.000498469,0.499375269,0.000453104,0.003624830,0.242904063,0.000000000,0.962308623,0.963539277,-0.000000000,0.000498469,0.199437031,0.000187727,0.000855200,2.973299748,0.738104759,0.056760402,0.333333333,0.853658537,2.786049416,0.000435893,0.228067646,0.000125151,0.000792624,2.973299748,0.962016603,0.000435893,0.228067646,0.000125151,0.000792624,0.020000000,0.000200663,2.678805722,0.000104293,0.008614576,0.362796522,0.321423138,0.186619164,0.004830918,0.333333333,0.212842478,0.480000000 -examples/commandline/spear_qcp/instances/qcplin2006.10641.cnf,2601.000000000,23450.000000000,2601.000000000,23450.000000000,0.000000000,0.000000000,0.020000000,0.110916844,1.000000000,0.000000000,1.000000000,1.000000000,-0.000000000,0.000853616,0.464965421,0.000768935,0.004998078,0.322691705,0.000000000,0.945714286,0.950831557,-0.000000000,0.000853616,0.227710399,0.000341151,0.001620469,2.923828161,0.683156865,0.079194513,0.250000000,0.842105263,2.850751479,0.000725684,0.267853732,0.000213220,0.001492537,2.923828161,0.943752665,0.000725684,0.267853732,0.000213220,0.001492537,0.020000000,0.000402453,2.225203063,0.000213220,0.011684435,0.497530439,0.316389245,0.219928533,0.007272727,0.333333333,0.320483938,0.160000000 -examples/commandline/spear_qcp/instances/qcplin2006.10556.cnf,700.000000000,5264.000000000,700.000000000,5264.000000000,0.000000000,0.000000000,0.000000000,0.132978723,1.000000000,0.000000000,1.000000000,1.000000000,-0.000000000,0.003206144,0.422271582,0.002857143,0.014285714,0.397357103,0.000000000,0.925151976,0.934080547,0.000000000,0.003206144,0.194120455,0.001709726,0.005129179,2.592682102,0.629580435,0.080031728,0.333333333,0.777777778,2.587874876,0.002636235,0.236085964,0.001139818,0.004559271,2.592682102,0.922682371,0.002636235,0.236085964,0.001139818,0.004559271,0.000000000,0.001752816,1.774079901,0.000949848,0.030205167,0.613406321,0.310773447,0.251687289,0.012500000,0.333333333,0.423983707,0.020000000 diff --git a/examples/commandline/spear_qcp/instances.txt b/examples/commandline/spear_qcp/instances.txt deleted file mode 100644 index a7cdd2d98..000000000 --- a/examples/commandline/spear_qcp/instances.txt +++ /dev/null @@ -1,4 +0,0 @@ -examples/commandline/spear_qcp/instances/qcplin2006.1031.cnf -examples/commandline/spear_qcp/instances/qcplin2006.10641.cnf -examples/commandline/spear_qcp/instances/qcplin2006.10218.cnf -examples/commandline/spear_qcp/instances/qcplin2006.10556.cnf diff --git a/examples/commandline/spear_qcp/instances/qcplin2006.10218.cnf b/examples/commandline/spear_qcp/instances/qcplin2006.10218.cnf deleted file mode 100755 index 921a93a78..000000000 --- a/examples/commandline/spear_qcp/instances/qcplin2006.10218.cnf +++ /dev/null @@ -1,7673 +0,0 @@ -p cnf 1066 7672 - -1 -2 0 - -1 -3 0 - -1 -4 0 - -2 -3 0 - -2 -4 0 - -3 -4 0 - -5 -6 0 - -5 -7 0 - -5 -8 0 - -5 -9 0 - -5 -10 0 - -5 -11 0 - -6 -7 0 - -6 -8 0 - -6 -9 0 - -6 -10 0 - -6 -11 0 - -7 -8 0 - -7 -9 0 - -7 -10 0 - -7 -11 0 - -8 -9 0 - -8 -10 0 - -8 -11 0 - -9 -10 0 - -9 -11 0 - -10 -11 0 - -12 -13 0 - -12 -14 0 - -12 -15 0 - -13 -14 0 - -13 -15 0 - -14 -15 0 - -16 -17 0 - -16 -18 0 - -17 -18 0 - -19 -20 0 - -19 -21 0 - -19 -22 0 - -19 -23 0 - -20 -21 0 - -20 -22 0 - -20 -23 0 - -21 -22 0 - -21 -23 0 - -22 -23 0 - -24 -25 0 - -24 -26 0 - -24 -27 0 - -24 -28 0 - -24 -29 0 - -25 -26 0 - -25 -27 0 - -25 -28 0 - -25 -29 0 - -26 -27 0 - -26 -28 0 - -26 -29 0 - -27 -28 0 - -27 -29 0 - -28 -29 0 - -30 -31 0 - -30 -32 0 - -31 -32 0 - -33 -34 0 - -33 -35 0 - -33 -36 0 - -33 -37 0 - -33 -38 0 - -33 -39 0 - -34 -35 0 - -34 -36 0 - -34 -37 0 - -34 -38 0 - -34 -39 0 - -35 -36 0 - -35 -37 0 - -35 -38 0 - -35 -39 0 - -36 -37 0 - -36 -38 0 - -36 -39 0 - -37 -38 0 - -37 -39 0 - -38 -39 0 - -40 -41 0 - -40 -42 0 - -40 -43 0 - -40 -44 0 - -41 -42 0 - -41 -43 0 - -41 -44 0 - -42 -43 0 - -42 -44 0 - -43 -44 0 - -45 -46 0 - -45 -47 0 - -45 -48 0 - -45 -49 0 - -46 -47 0 - -46 -48 0 - -46 -49 0 - -47 -48 0 - -47 -49 0 - -48 -49 0 - -50 -51 0 - -50 -52 0 - -50 -53 0 - -50 -54 0 - -50 -55 0 - -51 -52 0 - -51 -53 0 - -51 -54 0 - -51 -55 0 - -52 -53 0 - -52 -54 0 - -52 -55 0 - -53 -54 0 - -53 -55 0 - -54 -55 0 - -56 -57 0 - -56 -58 0 - -56 -59 0 - -56 -60 0 - -56 -61 0 - -56 -62 0 - -57 -58 0 - -57 -59 0 - -57 -60 0 - -57 -61 0 - -57 -62 0 - -58 -59 0 - -58 -60 0 - -58 -61 0 - -58 -62 0 - -59 -60 0 - -59 -61 0 - -59 -62 0 - -60 -61 0 - -60 -62 0 - -61 -62 0 - -63 -64 0 - -63 -65 0 - -63 -66 0 - -63 -67 0 - -64 -65 0 - -64 -66 0 - -64 -67 0 - -65 -66 0 - -65 -67 0 - -66 -67 0 - -68 -69 0 - -68 -70 0 - -68 -71 0 - -68 -72 0 - -68 -73 0 - -68 -74 0 - -69 -70 0 - -69 -71 0 - -69 -72 0 - -69 -73 0 - -69 -74 0 - -70 -71 0 - -70 -72 0 - -70 -73 0 - -70 -74 0 - -71 -72 0 - -71 -73 0 - -71 -74 0 - -72 -73 0 - -72 -74 0 - -73 -74 0 - -75 -76 0 - -75 -77 0 - -75 -78 0 - -76 -77 0 - -76 -78 0 - -77 -78 0 - -79 -80 0 - -79 -81 0 - -79 -82 0 - -79 -83 0 - -79 -84 0 - -80 -81 0 - -80 -82 0 - -80 -83 0 - -80 -84 0 - -81 -82 0 - -81 -83 0 - -81 -84 0 - -82 -83 0 - -82 -84 0 - -83 -84 0 - -85 -86 0 - -85 -87 0 - -85 -88 0 - -85 -89 0 - -85 -90 0 - -85 -91 0 - -86 -87 0 - -86 -88 0 - -86 -89 0 - -86 -90 0 - -86 -91 0 - -87 -88 0 - -87 -89 0 - -87 -90 0 - -87 -91 0 - -88 -89 0 - -88 -90 0 - -88 -91 0 - -89 -90 0 - -89 -91 0 - -90 -91 0 - -92 -93 0 - -92 -94 0 - -92 -95 0 - -92 -96 0 - -92 -97 0 - -92 -98 0 - -93 -94 0 - -93 -95 0 - -93 -96 0 - -93 -97 0 - -93 -98 0 - -94 -95 0 - -94 -96 0 - -94 -97 0 - -94 -98 0 - -95 -96 0 - -95 -97 0 - -95 -98 0 - -96 -97 0 - -96 -98 0 - -97 -98 0 - -99 -100 0 - -99 -101 0 - -99 -102 0 - -99 -103 0 - -100 -101 0 - -100 -102 0 - -100 -103 0 - -101 -102 0 - -101 -103 0 - -102 -103 0 - -104 -105 0 - -104 -106 0 - -104 -107 0 - -104 -108 0 - -105 -106 0 - -105 -107 0 - -105 -108 0 - -106 -107 0 - -106 -108 0 - -107 -108 0 - -109 -110 0 - -109 -111 0 - -109 -112 0 - -109 -113 0 - -110 -111 0 - -110 -112 0 - -110 -113 0 - -111 -112 0 - -111 -113 0 - -112 -113 0 - -114 -115 0 - -114 -116 0 - -114 -117 0 - -115 -116 0 - -115 -117 0 - -116 -117 0 - -118 -119 0 - -118 -120 0 - -119 -120 0 - -121 -122 0 - -123 -124 0 - -123 -125 0 - -123 -126 0 - -124 -125 0 - -124 -126 0 - -125 -126 0 - -127 -128 0 - -127 -129 0 - -127 -130 0 - -127 -131 0 - -127 -132 0 - -128 -129 0 - -128 -130 0 - -128 -131 0 - -128 -132 0 - -129 -130 0 - -129 -131 0 - -129 -132 0 - -130 -131 0 - -130 -132 0 - -131 -132 0 - -133 -134 0 - -133 -135 0 - -133 -136 0 - -133 -137 0 - -133 -138 0 - -133 -139 0 - -134 -135 0 - -134 -136 0 - -134 -137 0 - -134 -138 0 - -134 -139 0 - -135 -136 0 - -135 -137 0 - -135 -138 0 - -135 -139 0 - -136 -137 0 - -136 -138 0 - -136 -139 0 - -137 -138 0 - -137 -139 0 - -138 -139 0 - -140 -141 0 - -140 -142 0 - -141 -142 0 - -143 -144 0 - -143 -145 0 - -144 -145 0 - -146 -147 0 - -146 -148 0 - -146 -149 0 - -146 -150 0 - -147 -148 0 - -147 -149 0 - -147 -150 0 - -148 -149 0 - -148 -150 0 - -149 -150 0 - -151 -152 0 - -151 -153 0 - -151 -154 0 - -151 -155 0 - -152 -153 0 - -152 -154 0 - -152 -155 0 - -153 -154 0 - -153 -155 0 - -154 -155 0 - -156 -157 0 - -156 -158 0 - -156 -159 0 - -157 -158 0 - -157 -159 0 - -158 -159 0 - -160 -161 0 - -160 -162 0 - -160 -163 0 - -160 -164 0 - -160 -165 0 - -161 -162 0 - -161 -163 0 - -161 -164 0 - -161 -165 0 - -162 -163 0 - -162 -164 0 - -162 -165 0 - -163 -164 0 - -163 -165 0 - -164 -165 0 - -166 -167 0 - -166 -168 0 - -167 -168 0 - -169 -170 0 - -171 -172 0 - -171 -173 0 - -171 -174 0 - -172 -173 0 - -172 -174 0 - -173 -174 0 - -175 -176 0 - -175 -177 0 - -175 -178 0 - -175 -179 0 - -175 -180 0 - -176 -177 0 - -176 -178 0 - -176 -179 0 - -176 -180 0 - -177 -178 0 - -177 -179 0 - -177 -180 0 - -178 -179 0 - -178 -180 0 - -179 -180 0 - -181 -182 0 - -181 -183 0 - -181 -184 0 - -181 -185 0 - -182 -183 0 - -182 -184 0 - -182 -185 0 - -183 -184 0 - -183 -185 0 - -184 -185 0 - -186 -187 0 - -186 -188 0 - -186 -189 0 - -187 -188 0 - -187 -189 0 - -188 -189 0 - -190 -191 0 - -190 -192 0 - -191 -192 0 - -193 -194 0 - -195 -196 0 - -195 -197 0 - -196 -197 0 - -198 -199 0 - -200 -201 0 - -202 -203 0 - -202 -204 0 - -202 -205 0 - -202 -206 0 - -203 -204 0 - -203 -205 0 - -203 -206 0 - -204 -205 0 - -204 -206 0 - -205 -206 0 - -207 -208 0 - -207 -209 0 - -207 -210 0 - -207 -211 0 - -207 -212 0 - -208 -209 0 - -208 -210 0 - -208 -211 0 - -208 -212 0 - -209 -210 0 - -209 -211 0 - -209 -212 0 - -210 -211 0 - -210 -212 0 - -211 -212 0 - -213 -214 0 - -213 -215 0 - -213 -216 0 - -213 -217 0 - -214 -215 0 - -214 -216 0 - -214 -217 0 - -215 -216 0 - -215 -217 0 - -216 -217 0 - -218 -219 0 - -218 -220 0 - -219 -220 0 - -221 -222 0 - -221 -223 0 - -222 -223 0 - -224 -225 0 - -224 -226 0 - -224 -227 0 - -225 -226 0 - -225 -227 0 - -226 -227 0 - -228 -229 0 - -228 -230 0 - -228 -231 0 - -228 -232 0 - -228 -233 0 - -228 -234 0 - -229 -230 0 - -229 -231 0 - -229 -232 0 - -229 -233 0 - -229 -234 0 - -230 -231 0 - -230 -232 0 - -230 -233 0 - -230 -234 0 - -231 -232 0 - -231 -233 0 - -231 -234 0 - -232 -233 0 - -232 -234 0 - -233 -234 0 - -235 -236 0 - -235 -237 0 - -236 -237 0 - -238 -239 0 - -238 -240 0 - -238 -241 0 - -239 -240 0 - -239 -241 0 - -240 -241 0 - -242 -243 0 - -242 -244 0 - -242 -245 0 - -243 -244 0 - -243 -245 0 - -244 -245 0 - -246 -247 0 - -248 -249 0 - -248 -250 0 - -249 -250 0 - -251 -252 0 - -251 -253 0 - -252 -253 0 - -254 -255 0 - -256 -257 0 - -256 -258 0 - -256 -259 0 - -256 -260 0 - -257 -258 0 - -257 -259 0 - -257 -260 0 - -258 -259 0 - -258 -260 0 - -259 -260 0 - -261 -262 0 - -261 -263 0 - -261 -264 0 - -262 -263 0 - -262 -264 0 - -263 -264 0 - -265 -266 0 - -265 -267 0 - -265 -268 0 - -265 -269 0 - -266 -267 0 - -266 -268 0 - -266 -269 0 - -267 -268 0 - -267 -269 0 - -268 -269 0 - -270 -271 0 - -270 -272 0 - -270 -273 0 - -270 -274 0 - -270 -275 0 - -271 -272 0 - -271 -273 0 - -271 -274 0 - -271 -275 0 - -272 -273 0 - -272 -274 0 - -272 -275 0 - -273 -274 0 - -273 -275 0 - -274 -275 0 - -276 -277 0 - -276 -278 0 - -276 -279 0 - -276 -280 0 - -276 -281 0 - -276 -282 0 - -277 -278 0 - -277 -279 0 - -277 -280 0 - -277 -281 0 - -277 -282 0 - -278 -279 0 - -278 -280 0 - -278 -281 0 - -278 -282 0 - -279 -280 0 - -279 -281 0 - -279 -282 0 - -280 -281 0 - -280 -282 0 - -281 -282 0 - -283 -284 0 - -283 -285 0 - -283 -286 0 - -283 -287 0 - -283 -288 0 - -283 -289 0 - -284 -285 0 - -284 -286 0 - -284 -287 0 - -284 -288 0 - -284 -289 0 - -285 -286 0 - -285 -287 0 - -285 -288 0 - -285 -289 0 - -286 -287 0 - -286 -288 0 - -286 -289 0 - -287 -288 0 - -287 -289 0 - -288 -289 0 - -290 -291 0 - -290 -292 0 - -290 -293 0 - -290 -294 0 - -291 -292 0 - -291 -293 0 - -291 -294 0 - -292 -293 0 - -292 -294 0 - -293 -294 0 - -295 -296 0 - -297 -298 0 - -297 -299 0 - -297 -300 0 - -297 -301 0 - -298 -299 0 - -298 -300 0 - -298 -301 0 - -299 -300 0 - -299 -301 0 - -300 -301 0 - -302 -303 0 - -302 -304 0 - -303 -304 0 - -305 -306 0 - -305 -307 0 - -305 -308 0 - -306 -307 0 - -306 -308 0 - -307 -308 0 - -309 -310 0 - -309 -311 0 - -309 -312 0 - -309 -313 0 - -309 -314 0 - -310 -311 0 - -310 -312 0 - -310 -313 0 - -310 -314 0 - -311 -312 0 - -311 -313 0 - -311 -314 0 - -312 -313 0 - -312 -314 0 - -313 -314 0 - -315 -316 0 - -315 -317 0 - -315 -318 0 - -315 -319 0 - -315 -320 0 - -315 -321 0 - -315 -322 0 - -315 -323 0 - -316 -317 0 - -316 -318 0 - -316 -319 0 - -316 -320 0 - -316 -321 0 - -316 -322 0 - -316 -323 0 - -317 -318 0 - -317 -319 0 - -317 -320 0 - -317 -321 0 - -317 -322 0 - -317 -323 0 - -318 -319 0 - -318 -320 0 - -318 -321 0 - -318 -322 0 - -318 -323 0 - -319 -320 0 - -319 -321 0 - -319 -322 0 - -319 -323 0 - -320 -321 0 - -320 -322 0 - -320 -323 0 - -321 -322 0 - -321 -323 0 - -322 -323 0 - -324 -325 0 - -324 -326 0 - -324 -327 0 - -325 -326 0 - -325 -327 0 - -326 -327 0 - -328 -329 0 - -328 -330 0 - -328 -331 0 - -328 -332 0 - -329 -330 0 - -329 -331 0 - -329 -332 0 - -330 -331 0 - -330 -332 0 - -331 -332 0 - -333 -334 0 - -333 -335 0 - -333 -336 0 - -333 -337 0 - -333 -338 0 - -333 -339 0 - -333 -340 0 - -334 -335 0 - -334 -336 0 - -334 -337 0 - -334 -338 0 - -334 -339 0 - -334 -340 0 - -335 -336 0 - -335 -337 0 - -335 -338 0 - -335 -339 0 - -335 -340 0 - -336 -337 0 - -336 -338 0 - -336 -339 0 - -336 -340 0 - -337 -338 0 - -337 -339 0 - -337 -340 0 - -338 -339 0 - -338 -340 0 - -339 -340 0 - -341 -342 0 - -341 -343 0 - -341 -344 0 - -342 -343 0 - -342 -344 0 - -343 -344 0 - -345 -346 0 - -345 -347 0 - -345 -348 0 - -345 -349 0 - -345 -350 0 - -346 -347 0 - -346 -348 0 - -346 -349 0 - -346 -350 0 - -347 -348 0 - -347 -349 0 - -347 -350 0 - -348 -349 0 - -348 -350 0 - -349 -350 0 - -351 -352 0 - -351 -353 0 - -351 -354 0 - -351 -355 0 - -351 -356 0 - -351 -357 0 - -352 -353 0 - -352 -354 0 - -352 -355 0 - -352 -356 0 - -352 -357 0 - -353 -354 0 - -353 -355 0 - -353 -356 0 - -353 -357 0 - -354 -355 0 - -354 -356 0 - -354 -357 0 - -355 -356 0 - -355 -357 0 - -356 -357 0 - -358 -359 0 - -358 -360 0 - -358 -361 0 - -358 -362 0 - -358 -363 0 - -359 -360 0 - -359 -361 0 - -359 -362 0 - -359 -363 0 - -360 -361 0 - -360 -362 0 - -360 -363 0 - -361 -362 0 - -361 -363 0 - -362 -363 0 - -364 -365 0 - -364 -366 0 - -364 -367 0 - -364 -368 0 - -364 -369 0 - -365 -366 0 - -365 -367 0 - -365 -368 0 - -365 -369 0 - -366 -367 0 - -366 -368 0 - -366 -369 0 - -367 -368 0 - -367 -369 0 - -368 -369 0 - -370 -371 0 - -370 -372 0 - -370 -373 0 - -371 -372 0 - -371 -373 0 - -372 -373 0 - -374 -375 0 - -374 -376 0 - -374 -377 0 - -374 -378 0 - -374 -379 0 - -374 -380 0 - -374 -381 0 - -375 -376 0 - -375 -377 0 - -375 -378 0 - -375 -379 0 - -375 -380 0 - -375 -381 0 - -376 -377 0 - -376 -378 0 - -376 -379 0 - -376 -380 0 - -376 -381 0 - -377 -378 0 - -377 -379 0 - -377 -380 0 - -377 -381 0 - -378 -379 0 - -378 -380 0 - -378 -381 0 - -379 -380 0 - -379 -381 0 - -380 -381 0 - -382 -383 0 - -382 -384 0 - -382 -385 0 - -382 -386 0 - -383 -384 0 - -383 -385 0 - -383 -386 0 - -384 -385 0 - -384 -386 0 - -385 -386 0 - -387 -388 0 - -387 -389 0 - -387 -390 0 - -388 -389 0 - -388 -390 0 - -389 -390 0 - -391 -392 0 - -391 -393 0 - -391 -394 0 - -391 -395 0 - -392 -393 0 - -392 -394 0 - -392 -395 0 - -393 -394 0 - -393 -395 0 - -394 -395 0 - -396 -397 0 - -396 -398 0 - -396 -399 0 - -397 -398 0 - -397 -399 0 - -398 -399 0 - -400 -401 0 - -402 -403 0 - -402 -404 0 - -402 -405 0 - -402 -406 0 - -402 -407 0 - -403 -404 0 - -403 -405 0 - -403 -406 0 - -403 -407 0 - -404 -405 0 - -404 -406 0 - -404 -407 0 - -405 -406 0 - -405 -407 0 - -406 -407 0 - -408 -409 0 - -408 -410 0 - -408 -411 0 - -408 -412 0 - -409 -410 0 - -409 -411 0 - -409 -412 0 - -410 -411 0 - -410 -412 0 - -411 -412 0 - -413 -414 0 - -413 -415 0 - -413 -416 0 - -413 -417 0 - -413 -418 0 - -414 -415 0 - -414 -416 0 - -414 -417 0 - -414 -418 0 - -415 -416 0 - -415 -417 0 - -415 -418 0 - -416 -417 0 - -416 -418 0 - -417 -418 0 - -419 -420 0 - -419 -421 0 - -420 -421 0 - -422 -423 0 - -422 -424 0 - -423 -424 0 - -425 -426 0 - -427 -428 0 - -427 -429 0 - -427 -430 0 - -428 -429 0 - -428 -430 0 - -429 -430 0 - -431 -432 0 - -431 -433 0 - -432 -433 0 - -434 -435 0 - -436 -437 0 - -436 -438 0 - -436 -439 0 - -437 -438 0 - -437 -439 0 - -438 -439 0 - -440 -441 0 - -440 -442 0 - -441 -442 0 - -443 -444 0 - -445 -446 0 - -447 -448 0 - -449 -450 0 - -449 -451 0 - -450 -451 0 - -452 -453 0 - -452 -454 0 - -452 -455 0 - -452 -456 0 - -452 -457 0 - -453 -454 0 - -453 -455 0 - -453 -456 0 - -453 -457 0 - -454 -455 0 - -454 -456 0 - -454 -457 0 - -455 -456 0 - -455 -457 0 - -456 -457 0 - -458 -459 0 - -458 -460 0 - -458 -461 0 - -459 -460 0 - -459 -461 0 - -460 -461 0 - -462 -463 0 - -462 -464 0 - -462 -465 0 - -463 -464 0 - -463 -465 0 - -464 -465 0 - -466 -467 0 - -466 -468 0 - -467 -468 0 - -469 -470 0 - -469 -471 0 - -469 -472 0 - -470 -471 0 - -470 -472 0 - -471 -472 0 - -473 -474 0 - -473 -475 0 - -473 -476 0 - -473 -477 0 - -473 -478 0 - -473 -479 0 - -474 -475 0 - -474 -476 0 - -474 -477 0 - -474 -478 0 - -474 -479 0 - -475 -476 0 - -475 -477 0 - -475 -478 0 - -475 -479 0 - -476 -477 0 - -476 -478 0 - -476 -479 0 - -477 -478 0 - -477 -479 0 - -478 -479 0 - -480 -481 0 - -480 -482 0 - -481 -482 0 - -483 -484 0 - -483 -485 0 - -484 -485 0 - -486 -487 0 - -486 -488 0 - -486 -489 0 - -487 -488 0 - -487 -489 0 - -488 -489 0 - -490 -491 0 - -490 -492 0 - -490 -493 0 - -490 -494 0 - -490 -495 0 - -490 -496 0 - -491 -492 0 - -491 -493 0 - -491 -494 0 - -491 -495 0 - -491 -496 0 - -492 -493 0 - -492 -494 0 - -492 -495 0 - -492 -496 0 - -493 -494 0 - -493 -495 0 - -493 -496 0 - -494 -495 0 - -494 -496 0 - -495 -496 0 - -497 -498 0 - -497 -499 0 - -497 -500 0 - -497 -501 0 - -497 -502 0 - -497 -503 0 - -497 -504 0 - -497 -505 0 - -498 -499 0 - -498 -500 0 - -498 -501 0 - -498 -502 0 - -498 -503 0 - -498 -504 0 - -498 -505 0 - -499 -500 0 - -499 -501 0 - -499 -502 0 - -499 -503 0 - -499 -504 0 - -499 -505 0 - -500 -501 0 - -500 -502 0 - -500 -503 0 - -500 -504 0 - -500 -505 0 - -501 -502 0 - -501 -503 0 - -501 -504 0 - -501 -505 0 - -502 -503 0 - -502 -504 0 - -502 -505 0 - -503 -504 0 - -503 -505 0 - -504 -505 0 - -506 -507 0 - -506 -508 0 - -506 -509 0 - -506 -510 0 - -506 -511 0 - -507 -508 0 - -507 -509 0 - -507 -510 0 - -507 -511 0 - -508 -509 0 - -508 -510 0 - -508 -511 0 - -509 -510 0 - -509 -511 0 - -510 -511 0 - -512 -513 0 - -514 -515 0 - -514 -516 0 - -514 -517 0 - -514 -518 0 - -514 -519 0 - -515 -516 0 - -515 -517 0 - -515 -518 0 - -515 -519 0 - -516 -517 0 - -516 -518 0 - -516 -519 0 - -517 -518 0 - -517 -519 0 - -518 -519 0 - -520 -521 0 - -520 -522 0 - -520 -523 0 - -520 -524 0 - -520 -525 0 - -520 -526 0 - -521 -522 0 - -521 -523 0 - -521 -524 0 - -521 -525 0 - -521 -526 0 - -522 -523 0 - -522 -524 0 - -522 -525 0 - -522 -526 0 - -523 -524 0 - -523 -525 0 - -523 -526 0 - -524 -525 0 - -524 -526 0 - -525 -526 0 - -527 -528 0 - -527 -529 0 - -527 -530 0 - -527 -531 0 - -527 -532 0 - -527 -533 0 - -528 -529 0 - -528 -530 0 - -528 -531 0 - -528 -532 0 - -528 -533 0 - -529 -530 0 - -529 -531 0 - -529 -532 0 - -529 -533 0 - -530 -531 0 - -530 -532 0 - -530 -533 0 - -531 -532 0 - -531 -533 0 - -532 -533 0 - -534 -535 0 - -534 -536 0 - -534 -537 0 - -534 -538 0 - -534 -539 0 - -535 -536 0 - -535 -537 0 - -535 -538 0 - -535 -539 0 - -536 -537 0 - -536 -538 0 - -536 -539 0 - -537 -538 0 - -537 -539 0 - -538 -539 0 - -540 -541 0 - -540 -542 0 - -540 -543 0 - -540 -544 0 - -541 -542 0 - -541 -543 0 - -541 -544 0 - -542 -543 0 - -542 -544 0 - -543 -544 0 - -545 -546 0 - -545 -547 0 - -545 -548 0 - -545 -549 0 - -545 -550 0 - -545 -551 0 - -546 -547 0 - -546 -548 0 - -546 -549 0 - -546 -550 0 - -546 -551 0 - -547 -548 0 - -547 -549 0 - -547 -550 0 - -547 -551 0 - -548 -549 0 - -548 -550 0 - -548 -551 0 - -549 -550 0 - -549 -551 0 - -550 -551 0 - -552 -553 0 - -552 -554 0 - -552 -555 0 - -552 -556 0 - -552 -557 0 - -553 -554 0 - -553 -555 0 - -553 -556 0 - -553 -557 0 - -554 -555 0 - -554 -556 0 - -554 -557 0 - -555 -556 0 - -555 -557 0 - -556 -557 0 - -558 -559 0 - -558 -560 0 - -558 -561 0 - -558 -562 0 - -558 -563 0 - -558 -564 0 - -559 -560 0 - -559 -561 0 - -559 -562 0 - -559 -563 0 - -559 -564 0 - -560 -561 0 - -560 -562 0 - -560 -563 0 - -560 -564 0 - -561 -562 0 - -561 -563 0 - -561 -564 0 - -562 -563 0 - -562 -564 0 - -563 -564 0 - -565 -566 0 - -565 -567 0 - -565 -568 0 - -566 -567 0 - -566 -568 0 - -567 -568 0 - -569 -570 0 - -571 -572 0 - -571 -573 0 - -571 -574 0 - -572 -573 0 - -572 -574 0 - -573 -574 0 - -575 -576 0 - -575 -577 0 - -576 -577 0 - -578 -579 0 - -578 -580 0 - -578 -581 0 - -579 -580 0 - -579 -581 0 - -580 -581 0 - -582 -583 0 - -582 -584 0 - -583 -584 0 - -585 -586 0 - -585 -587 0 - -585 -588 0 - -586 -587 0 - -586 -588 0 - -587 -588 0 - -589 -590 0 - -589 -591 0 - -589 -592 0 - -590 -591 0 - -590 -592 0 - -591 -592 0 - -593 -594 0 - -593 -595 0 - -594 -595 0 - -596 -597 0 - -596 -598 0 - -596 -599 0 - -596 -600 0 - -597 -598 0 - -597 -599 0 - -597 -600 0 - -598 -599 0 - -598 -600 0 - -599 -600 0 - -601 -602 0 - -601 -603 0 - -602 -603 0 - -604 -605 0 - -604 -606 0 - -605 -606 0 - -607 -608 0 - -607 -609 0 - -607 -610 0 - -608 -609 0 - -608 -610 0 - -609 -610 0 - -611 -612 0 - -611 -613 0 - -611 -614 0 - -611 -615 0 - -612 -613 0 - -612 -614 0 - -612 -615 0 - -613 -614 0 - -613 -615 0 - -614 -615 0 - -616 -617 0 - -616 -618 0 - -616 -619 0 - -616 -620 0 - -617 -618 0 - -617 -619 0 - -617 -620 0 - -618 -619 0 - -618 -620 0 - -619 -620 0 - -621 -622 0 - -621 -623 0 - -621 -624 0 - -621 -625 0 - -622 -623 0 - -622 -624 0 - -622 -625 0 - -623 -624 0 - -623 -625 0 - -624 -625 0 - -626 -627 0 - -626 -628 0 - -627 -628 0 - -629 -630 0 - -629 -631 0 - -629 -632 0 - -630 -631 0 - -630 -632 0 - -631 -632 0 - -633 -634 0 - -633 -635 0 - -634 -635 0 - -636 -637 0 - -636 -638 0 - -636 -639 0 - -637 -638 0 - -637 -639 0 - -638 -639 0 - -640 -641 0 - -640 -642 0 - -640 -643 0 - -641 -642 0 - -641 -643 0 - -642 -643 0 - -644 -645 0 - -644 -646 0 - -644 -647 0 - -645 -646 0 - -645 -647 0 - -646 -647 0 - -648 -649 0 - -648 -650 0 - -649 -650 0 - -651 -652 0 - -651 -653 0 - -652 -653 0 - -654 -655 0 - -654 -656 0 - -654 -657 0 - -654 -658 0 - -655 -656 0 - -655 -657 0 - -655 -658 0 - -656 -657 0 - -656 -658 0 - -657 -658 0 - -659 -660 0 - -659 -661 0 - -659 -662 0 - -659 -663 0 - -660 -661 0 - -660 -662 0 - -660 -663 0 - -661 -662 0 - -661 -663 0 - -662 -663 0 - -664 -665 0 - -664 -666 0 - -664 -667 0 - -664 -668 0 - -665 -666 0 - -665 -667 0 - -665 -668 0 - -666 -667 0 - -666 -668 0 - -667 -668 0 - -669 -670 0 - -671 -672 0 - -671 -673 0 - -671 -674 0 - -671 -675 0 - -671 -676 0 - -671 -677 0 - -671 -678 0 - -671 -679 0 - -672 -673 0 - -672 -674 0 - -672 -675 0 - -672 -676 0 - -672 -677 0 - -672 -678 0 - -672 -679 0 - -673 -674 0 - -673 -675 0 - -673 -676 0 - -673 -677 0 - -673 -678 0 - -673 -679 0 - -674 -675 0 - -674 -676 0 - -674 -677 0 - -674 -678 0 - -674 -679 0 - -675 -676 0 - -675 -677 0 - -675 -678 0 - -675 -679 0 - -676 -677 0 - -676 -678 0 - -676 -679 0 - -677 -678 0 - -677 -679 0 - -678 -679 0 - -680 -681 0 - -680 -682 0 - -680 -683 0 - -680 -684 0 - -681 -682 0 - -681 -683 0 - -681 -684 0 - -682 -683 0 - -682 -684 0 - -683 -684 0 - -685 -686 0 - -685 -687 0 - -685 -688 0 - -685 -689 0 - -685 -690 0 - -685 -691 0 - -685 -692 0 - -686 -687 0 - -686 -688 0 - -686 -689 0 - -686 -690 0 - -686 -691 0 - -686 -692 0 - -687 -688 0 - -687 -689 0 - -687 -690 0 - -687 -691 0 - -687 -692 0 - -688 -689 0 - -688 -690 0 - -688 -691 0 - -688 -692 0 - -689 -690 0 - -689 -691 0 - -689 -692 0 - -690 -691 0 - -690 -692 0 - -691 -692 0 - -693 -694 0 - -693 -695 0 - -693 -696 0 - -693 -697 0 - -693 -698 0 - -694 -695 0 - -694 -696 0 - -694 -697 0 - -694 -698 0 - -695 -696 0 - -695 -697 0 - -695 -698 0 - -696 -697 0 - -696 -698 0 - -697 -698 0 - -699 -700 0 - -699 -701 0 - -699 -702 0 - -699 -703 0 - -699 -704 0 - -699 -705 0 - -700 -701 0 - -700 -702 0 - -700 -703 0 - -700 -704 0 - -700 -705 0 - -701 -702 0 - -701 -703 0 - -701 -704 0 - -701 -705 0 - -702 -703 0 - -702 -704 0 - -702 -705 0 - -703 -704 0 - -703 -705 0 - -704 -705 0 - -706 -707 0 - -706 -708 0 - -706 -709 0 - -707 -708 0 - -707 -709 0 - -708 -709 0 - -710 -711 0 - -710 -712 0 - -710 -713 0 - -710 -714 0 - -711 -712 0 - -711 -713 0 - -711 -714 0 - -712 -713 0 - -712 -714 0 - -713 -714 0 - -715 -716 0 - -717 -718 0 - -717 -719 0 - -717 -720 0 - -717 -721 0 - -718 -719 0 - -718 -720 0 - -718 -721 0 - -719 -720 0 - -719 -721 0 - -720 -721 0 - -722 -723 0 - -722 -724 0 - -722 -725 0 - -722 -726 0 - -722 -727 0 - -722 -728 0 - -723 -724 0 - -723 -725 0 - -723 -726 0 - -723 -727 0 - -723 -728 0 - -724 -725 0 - -724 -726 0 - -724 -727 0 - -724 -728 0 - -725 -726 0 - -725 -727 0 - -725 -728 0 - -726 -727 0 - -726 -728 0 - -727 -728 0 - -729 -730 0 - -729 -731 0 - -729 -732 0 - -730 -731 0 - -730 -732 0 - -731 -732 0 - -733 -734 0 - -733 -735 0 - -733 -736 0 - -733 -737 0 - -733 -738 0 - -733 -739 0 - -734 -735 0 - -734 -736 0 - -734 -737 0 - -734 -738 0 - -734 -739 0 - -735 -736 0 - -735 -737 0 - -735 -738 0 - -735 -739 0 - -736 -737 0 - -736 -738 0 - -736 -739 0 - -737 -738 0 - -737 -739 0 - -738 -739 0 - -740 -741 0 - -740 -742 0 - -740 -743 0 - -740 -744 0 - -741 -742 0 - -741 -743 0 - -741 -744 0 - -742 -743 0 - -742 -744 0 - -743 -744 0 - -745 -746 0 - -745 -747 0 - -745 -748 0 - -745 -749 0 - -745 -750 0 - -746 -747 0 - -746 -748 0 - -746 -749 0 - -746 -750 0 - -747 -748 0 - -747 -749 0 - -747 -750 0 - -748 -749 0 - -748 -750 0 - -749 -750 0 - -751 -752 0 - -751 -753 0 - -751 -754 0 - -751 -755 0 - -751 -756 0 - -751 -757 0 - -752 -753 0 - -752 -754 0 - -752 -755 0 - -752 -756 0 - -752 -757 0 - -753 -754 0 - -753 -755 0 - -753 -756 0 - -753 -757 0 - -754 -755 0 - -754 -756 0 - -754 -757 0 - -755 -756 0 - -755 -757 0 - -756 -757 0 - -758 -759 0 - -758 -760 0 - -758 -761 0 - -758 -762 0 - -758 -763 0 - -758 -764 0 - -759 -760 0 - -759 -761 0 - -759 -762 0 - -759 -763 0 - -759 -764 0 - -760 -761 0 - -760 -762 0 - -760 -763 0 - -760 -764 0 - -761 -762 0 - -761 -763 0 - -761 -764 0 - -762 -763 0 - -762 -764 0 - -763 -764 0 - -765 -766 0 - -765 -767 0 - -765 -768 0 - -765 -769 0 - -765 -770 0 - -765 -771 0 - -766 -767 0 - -766 -768 0 - -766 -769 0 - -766 -770 0 - -766 -771 0 - -767 -768 0 - -767 -769 0 - -767 -770 0 - -767 -771 0 - -768 -769 0 - -768 -770 0 - -768 -771 0 - -769 -770 0 - -769 -771 0 - -770 -771 0 - -772 -773 0 - -772 -774 0 - -772 -775 0 - -772 -776 0 - -773 -774 0 - -773 -775 0 - -773 -776 0 - -774 -775 0 - -774 -776 0 - -775 -776 0 - -777 -778 0 - -777 -779 0 - -777 -780 0 - -778 -779 0 - -778 -780 0 - -779 -780 0 - -781 -782 0 - -781 -783 0 - -781 -784 0 - -781 -785 0 - -782 -783 0 - -782 -784 0 - -782 -785 0 - -783 -784 0 - -783 -785 0 - -784 -785 0 - -786 -787 0 - -786 -788 0 - -786 -789 0 - -786 -790 0 - -786 -791 0 - -787 -788 0 - -787 -789 0 - -787 -790 0 - -787 -791 0 - -788 -789 0 - -788 -790 0 - -788 -791 0 - -789 -790 0 - -789 -791 0 - -790 -791 0 - -792 -793 0 - -792 -794 0 - -792 -795 0 - -792 -796 0 - -792 -797 0 - -792 -798 0 - -792 -799 0 - -793 -794 0 - -793 -795 0 - -793 -796 0 - -793 -797 0 - -793 -798 0 - -793 -799 0 - -794 -795 0 - -794 -796 0 - -794 -797 0 - -794 -798 0 - -794 -799 0 - -795 -796 0 - -795 -797 0 - -795 -798 0 - -795 -799 0 - -796 -797 0 - -796 -798 0 - -796 -799 0 - -797 -798 0 - -797 -799 0 - -798 -799 0 - -800 -801 0 - -800 -802 0 - -800 -803 0 - -800 -804 0 - -801 -802 0 - -801 -803 0 - -801 -804 0 - -802 -803 0 - -802 -804 0 - -803 -804 0 - -805 -806 0 - -805 -807 0 - -805 -808 0 - -805 -809 0 - -805 -810 0 - -805 -811 0 - -805 -812 0 - -806 -807 0 - -806 -808 0 - -806 -809 0 - -806 -810 0 - -806 -811 0 - -806 -812 0 - -807 -808 0 - -807 -809 0 - -807 -810 0 - -807 -811 0 - -807 -812 0 - -808 -809 0 - -808 -810 0 - -808 -811 0 - -808 -812 0 - -809 -810 0 - -809 -811 0 - -809 -812 0 - -810 -811 0 - -810 -812 0 - -811 -812 0 - -813 -814 0 - -813 -815 0 - -813 -816 0 - -813 -817 0 - -814 -815 0 - -814 -816 0 - -814 -817 0 - -815 -816 0 - -815 -817 0 - -816 -817 0 - -818 -819 0 - -818 -820 0 - -818 -821 0 - -819 -820 0 - -819 -821 0 - -820 -821 0 - -822 -823 0 - -822 -824 0 - -822 -825 0 - -822 -826 0 - -822 -827 0 - -823 -824 0 - -823 -825 0 - -823 -826 0 - -823 -827 0 - -824 -825 0 - -824 -826 0 - -824 -827 0 - -825 -826 0 - -825 -827 0 - -826 -827 0 - -828 -829 0 - -828 -830 0 - -828 -831 0 - -829 -830 0 - -829 -831 0 - -830 -831 0 - -832 -833 0 - -832 -834 0 - -832 -835 0 - -832 -836 0 - -833 -834 0 - -833 -835 0 - -833 -836 0 - -834 -835 0 - -834 -836 0 - -835 -836 0 - -837 -838 0 - -837 -839 0 - -837 -840 0 - -837 -841 0 - -838 -839 0 - -838 -840 0 - -838 -841 0 - -839 -840 0 - -839 -841 0 - -840 -841 0 - -842 -843 0 - -842 -844 0 - -842 -845 0 - -842 -846 0 - -843 -844 0 - -843 -845 0 - -843 -846 0 - -844 -845 0 - -844 -846 0 - -845 -846 0 - -847 -848 0 - -847 -849 0 - -847 -850 0 - -848 -849 0 - -848 -850 0 - -849 -850 0 - -851 -852 0 - -851 -853 0 - -851 -854 0 - -852 -853 0 - -852 -854 0 - -853 -854 0 - -855 -856 0 - -855 -857 0 - -855 -858 0 - -855 -859 0 - -856 -857 0 - -856 -858 0 - -856 -859 0 - -857 -858 0 - -857 -859 0 - -858 -859 0 - -860 -861 0 - -860 -862 0 - -860 -863 0 - -860 -864 0 - -860 -865 0 - -860 -866 0 - -861 -862 0 - -861 -863 0 - -861 -864 0 - -861 -865 0 - -861 -866 0 - -862 -863 0 - -862 -864 0 - -862 -865 0 - -862 -866 0 - -863 -864 0 - -863 -865 0 - -863 -866 0 - -864 -865 0 - -864 -866 0 - -865 -866 0 - -867 -868 0 - -867 -869 0 - -867 -870 0 - -867 -871 0 - -867 -872 0 - -868 -869 0 - -868 -870 0 - -868 -871 0 - -868 -872 0 - -869 -870 0 - -869 -871 0 - -869 -872 0 - -870 -871 0 - -870 -872 0 - -871 -872 0 - -873 -874 0 - -873 -875 0 - -873 -876 0 - -873 -877 0 - -874 -875 0 - -874 -876 0 - -874 -877 0 - -875 -876 0 - -875 -877 0 - -876 -877 0 - -878 -879 0 - -878 -880 0 - -879 -880 0 - -881 -882 0 - -881 -883 0 - -881 -884 0 - -881 -885 0 - -881 -886 0 - -881 -887 0 - -882 -883 0 - -882 -884 0 - -882 -885 0 - -882 -886 0 - -882 -887 0 - -883 -884 0 - -883 -885 0 - -883 -886 0 - -883 -887 0 - -884 -885 0 - -884 -886 0 - -884 -887 0 - -885 -886 0 - -885 -887 0 - -886 -887 0 - -888 -889 0 - -888 -890 0 - -889 -890 0 - -891 -892 0 - -891 -893 0 - -891 -894 0 - -891 -895 0 - -891 -896 0 - -891 -897 0 - -891 -898 0 - -892 -893 0 - -892 -894 0 - -892 -895 0 - -892 -896 0 - -892 -897 0 - -892 -898 0 - -893 -894 0 - -893 -895 0 - -893 -896 0 - -893 -897 0 - -893 -898 0 - -894 -895 0 - -894 -896 0 - -894 -897 0 - -894 -898 0 - -895 -896 0 - -895 -897 0 - -895 -898 0 - -896 -897 0 - -896 -898 0 - -897 -898 0 - -899 -900 0 - -901 -902 0 - -901 -903 0 - -901 -904 0 - -902 -903 0 - -902 -904 0 - -903 -904 0 - -905 -906 0 - -905 -907 0 - -905 -908 0 - -905 -909 0 - -905 -910 0 - -906 -907 0 - -906 -908 0 - -906 -909 0 - -906 -910 0 - -907 -908 0 - -907 -909 0 - -907 -910 0 - -908 -909 0 - -908 -910 0 - -909 -910 0 - -911 -912 0 - -911 -913 0 - -911 -914 0 - -911 -915 0 - -911 -916 0 - -912 -913 0 - -912 -914 0 - -912 -915 0 - -912 -916 0 - -913 -914 0 - -913 -915 0 - -913 -916 0 - -914 -915 0 - -914 -916 0 - -915 -916 0 - -917 -918 0 - -917 -919 0 - -917 -920 0 - -917 -921 0 - -918 -919 0 - -918 -920 0 - -918 -921 0 - -919 -920 0 - -919 -921 0 - -920 -921 0 - -922 -923 0 - -922 -924 0 - -922 -925 0 - -922 -926 0 - -922 -927 0 - -922 -928 0 - -923 -924 0 - -923 -925 0 - -923 -926 0 - -923 -927 0 - -923 -928 0 - -924 -925 0 - -924 -926 0 - -924 -927 0 - -924 -928 0 - -925 -926 0 - -925 -927 0 - -925 -928 0 - -926 -927 0 - -926 -928 0 - -927 -928 0 - -929 -930 0 - -929 -931 0 - -929 -932 0 - -929 -933 0 - -929 -934 0 - -929 -935 0 - -929 -936 0 - -930 -931 0 - -930 -932 0 - -930 -933 0 - -930 -934 0 - -930 -935 0 - -930 -936 0 - -931 -932 0 - -931 -933 0 - -931 -934 0 - -931 -935 0 - -931 -936 0 - -932 -933 0 - -932 -934 0 - -932 -935 0 - -932 -936 0 - -933 -934 0 - -933 -935 0 - -933 -936 0 - -934 -935 0 - -934 -936 0 - -935 -936 0 - -937 -938 0 - -937 -939 0 - -937 -940 0 - -937 -941 0 - -937 -942 0 - -938 -939 0 - -938 -940 0 - -938 -941 0 - -938 -942 0 - -939 -940 0 - -939 -941 0 - -939 -942 0 - -940 -941 0 - -940 -942 0 - -941 -942 0 - -943 -944 0 - -943 -945 0 - -943 -946 0 - -943 -947 0 - -943 -948 0 - -944 -945 0 - -944 -946 0 - -944 -947 0 - -944 -948 0 - -945 -946 0 - -945 -947 0 - -945 -948 0 - -946 -947 0 - -946 -948 0 - -947 -948 0 - -949 -950 0 - -949 -951 0 - -949 -952 0 - -949 -953 0 - -950 -951 0 - -950 -952 0 - -950 -953 0 - -951 -952 0 - -951 -953 0 - -952 -953 0 - -954 -955 0 - -954 -956 0 - -954 -957 0 - -954 -958 0 - -955 -956 0 - -955 -957 0 - -955 -958 0 - -956 -957 0 - -956 -958 0 - -957 -958 0 - -959 -960 0 - -959 -961 0 - -959 -962 0 - -959 -963 0 - -959 -964 0 - -960 -961 0 - -960 -962 0 - -960 -963 0 - -960 -964 0 - -961 -962 0 - -961 -963 0 - -961 -964 0 - -962 -963 0 - -962 -964 0 - -963 -964 0 - -965 -966 0 - -965 -967 0 - -966 -967 0 - -968 -969 0 - -968 -970 0 - -968 -971 0 - -969 -970 0 - -969 -971 0 - -970 -971 0 - -972 -973 0 - -972 -974 0 - -972 -975 0 - -972 -976 0 - -972 -977 0 - -973 -974 0 - -973 -975 0 - -973 -976 0 - -973 -977 0 - -974 -975 0 - -974 -976 0 - -974 -977 0 - -975 -976 0 - -975 -977 0 - -976 -977 0 - -978 -979 0 - -978 -980 0 - -978 -981 0 - -978 -982 0 - -979 -980 0 - -979 -981 0 - -979 -982 0 - -980 -981 0 - -980 -982 0 - -981 -982 0 - -983 -984 0 - -983 -985 0 - -983 -986 0 - -983 -987 0 - -983 -988 0 - -984 -985 0 - -984 -986 0 - -984 -987 0 - -984 -988 0 - -985 -986 0 - -985 -987 0 - -985 -988 0 - -986 -987 0 - -986 -988 0 - -987 -988 0 - -989 -990 0 - -989 -991 0 - -989 -992 0 - -989 -993 0 - -989 -994 0 - -990 -991 0 - -990 -992 0 - -990 -993 0 - -990 -994 0 - -991 -992 0 - -991 -993 0 - -991 -994 0 - -992 -993 0 - -992 -994 0 - -993 -994 0 - -995 -996 0 - -995 -997 0 - -995 -998 0 - -995 -999 0 - -995 -1000 0 - -995 -1001 0 - -995 -1002 0 - -996 -997 0 - -996 -998 0 - -996 -999 0 - -996 -1000 0 - -996 -1001 0 - -996 -1002 0 - -997 -998 0 - -997 -999 0 - -997 -1000 0 - -997 -1001 0 - -997 -1002 0 - -998 -999 0 - -998 -1000 0 - -998 -1001 0 - -998 -1002 0 - -999 -1000 0 - -999 -1001 0 - -999 -1002 0 - -1000 -1001 0 - -1000 -1002 0 - -1001 -1002 0 - -1003 -1004 0 - -1003 -1005 0 - -1004 -1005 0 - -1006 -1007 0 - -1006 -1008 0 - -1006 -1009 0 - -1006 -1010 0 - -1006 -1011 0 - -1006 -1012 0 - -1006 -1013 0 - -1007 -1008 0 - -1007 -1009 0 - -1007 -1010 0 - -1007 -1011 0 - -1007 -1012 0 - -1007 -1013 0 - -1008 -1009 0 - -1008 -1010 0 - -1008 -1011 0 - -1008 -1012 0 - -1008 -1013 0 - -1009 -1010 0 - -1009 -1011 0 - -1009 -1012 0 - -1009 -1013 0 - -1010 -1011 0 - -1010 -1012 0 - -1010 -1013 0 - -1011 -1012 0 - -1011 -1013 0 - -1012 -1013 0 - -1014 -1015 0 - -1014 -1016 0 - -1014 -1017 0 - -1014 -1018 0 - -1014 -1019 0 - -1015 -1016 0 - -1015 -1017 0 - -1015 -1018 0 - -1015 -1019 0 - -1016 -1017 0 - -1016 -1018 0 - -1016 -1019 0 - -1017 -1018 0 - -1017 -1019 0 - -1018 -1019 0 - -1020 -1021 0 - -1020 -1022 0 - -1020 -1023 0 - -1020 -1024 0 - -1021 -1022 0 - -1021 -1023 0 - -1021 -1024 0 - -1022 -1023 0 - -1022 -1024 0 - -1023 -1024 0 - -1025 -1026 0 - -1025 -1027 0 - -1025 -1028 0 - -1025 -1029 0 - -1025 -1030 0 - -1026 -1027 0 - -1026 -1028 0 - -1026 -1029 0 - -1026 -1030 0 - -1027 -1028 0 - -1027 -1029 0 - -1027 -1030 0 - -1028 -1029 0 - -1028 -1030 0 - -1029 -1030 0 - -1031 -1032 0 - -1031 -1033 0 - -1032 -1033 0 - -1034 -1035 0 - -1034 -1036 0 - -1034 -1037 0 - -1034 -1038 0 - -1035 -1036 0 - -1035 -1037 0 - -1035 -1038 0 - -1036 -1037 0 - -1036 -1038 0 - -1037 -1038 0 - -1039 -1040 0 - -1039 -1041 0 - -1039 -1042 0 - -1039 -1043 0 - -1039 -1044 0 - -1040 -1041 0 - -1040 -1042 0 - -1040 -1043 0 - -1040 -1044 0 - -1041 -1042 0 - -1041 -1043 0 - -1041 -1044 0 - -1042 -1043 0 - -1042 -1044 0 - -1043 -1044 0 - -1045 -1046 0 - -1045 -1047 0 - -1045 -1048 0 - -1045 -1049 0 - -1045 -1050 0 - -1045 -1051 0 - -1045 -1052 0 - -1046 -1047 0 - -1046 -1048 0 - -1046 -1049 0 - -1046 -1050 0 - -1046 -1051 0 - -1046 -1052 0 - -1047 -1048 0 - -1047 -1049 0 - -1047 -1050 0 - -1047 -1051 0 - -1047 -1052 0 - -1048 -1049 0 - -1048 -1050 0 - -1048 -1051 0 - -1048 -1052 0 - -1049 -1050 0 - -1049 -1051 0 - -1049 -1052 0 - -1050 -1051 0 - -1050 -1052 0 - -1051 -1052 0 - -1053 -1054 0 - -1053 -1055 0 - -1053 -1056 0 - -1054 -1055 0 - -1054 -1056 0 - -1055 -1056 0 - -1057 -1058 0 - -1057 -1059 0 - -1057 -1060 0 - -1057 -1061 0 - -1058 -1059 0 - -1058 -1060 0 - -1058 -1061 0 - -1059 -1060 0 - -1059 -1061 0 - -1060 -1061 0 - -1062 -1063 0 - -1062 -1064 0 - -1062 -1065 0 - -1062 -1066 0 - -1063 -1064 0 - -1063 -1065 0 - -1063 -1066 0 - -1064 -1065 0 - -1064 -1066 0 - -1065 -1066 0 - -45 -302 0 - -45 -818 0 - -302 -818 0 - -46 -202 0 - -46 -596 0 - -202 -596 0 - -449 -597 0 - -449 -819 0 - -597 -819 0 - -203 -598 0 - -203 -1003 0 - -598 -1003 0 - -47 -204 0 - -47 -450 0 - -47 -599 0 - -47 -636 0 - -47 -820 0 - -204 -450 0 - -204 -599 0 - -204 -636 0 - -204 -820 0 - -450 -599 0 - -450 -636 0 - -450 -820 0 - -599 -636 0 - -599 -820 0 - -636 -820 0 - -48 -205 0 - -48 -303 0 - -48 -637 0 - -48 -1004 0 - -205 -303 0 - -205 -637 0 - -205 -1004 0 - -303 -637 0 - -303 -1004 0 - -637 -1004 0 - -49 -206 0 - -49 -304 0 - -49 -638 0 - -206 -304 0 - -206 -638 0 - -304 -638 0 - -451 -600 0 - -451 -639 0 - -451 -821 0 - -451 -1005 0 - -600 -639 0 - -600 -821 0 - -600 -1005 0 - -639 -821 0 - -639 -1005 0 - -821 -1005 0 - -256 -305 0 - -256 -382 0 - -256 -452 0 - -256 -822 0 - -256 -860 0 - -256 -1006 0 - -305 -382 0 - -305 -452 0 - -305 -822 0 - -305 -860 0 - -305 -1006 0 - -382 -452 0 - -382 -822 0 - -382 -860 0 - -382 -1006 0 - -452 -822 0 - -452 -860 0 - -452 -1006 0 - -822 -860 0 - -822 -1006 0 - -860 -1006 0 - -50 -306 0 - -50 -671 0 - -50 -861 0 - -50 -1007 0 - -306 -671 0 - -306 -861 0 - -306 -1007 0 - -671 -861 0 - -671 -1007 0 - -861 -1007 0 - -51 -672 0 - -51 -745 0 - -51 -862 0 - -51 -1008 0 - -672 -745 0 - -672 -862 0 - -672 -1008 0 - -745 -862 0 - -745 -1008 0 - -862 -1008 0 - -52 -257 0 - -52 -383 0 - -52 -453 0 - -52 -746 0 - -52 -863 0 - -52 -1009 0 - -257 -383 0 - -257 -453 0 - -257 -746 0 - -257 -863 0 - -257 -1009 0 - -383 -453 0 - -383 -746 0 - -383 -863 0 - -383 -1009 0 - -453 -746 0 - -453 -863 0 - -453 -1009 0 - -746 -863 0 - -746 -1009 0 - -863 -1009 0 - -258 -307 0 - -258 -384 0 - -258 -490 0 - -258 -673 0 - -258 -823 0 - -258 -864 0 - -307 -384 0 - -307 -490 0 - -307 -673 0 - -307 -823 0 - -307 -864 0 - -384 -490 0 - -384 -673 0 - -384 -823 0 - -384 -864 0 - -490 -673 0 - -490 -823 0 - -490 -864 0 - -673 -823 0 - -673 -864 0 - -823 -864 0 - -491 -674 0 - -491 -747 0 - -674 -747 0 - -114 -454 0 - -114 -492 0 - -114 -675 0 - -114 -824 0 - -114 -865 0 - -454 -492 0 - -454 -675 0 - -454 -824 0 - -454 -865 0 - -492 -675 0 - -492 -824 0 - -492 -865 0 - -675 -824 0 - -675 -865 0 - -824 -865 0 - -676 -866 0 - -676 -1010 0 - -866 -1010 0 - -53 -115 0 - -53 -259 0 - -53 -455 0 - -53 -493 0 - -53 -748 0 - -53 -825 0 - -115 -259 0 - -115 -455 0 - -115 -493 0 - -115 -748 0 - -115 -825 0 - -259 -455 0 - -259 -493 0 - -259 -748 0 - -259 -825 0 - -455 -493 0 - -455 -748 0 - -455 -825 0 - -493 -748 0 - -493 -825 0 - -748 -825 0 - -54 -116 0 - -54 -260 0 - -54 -308 0 - -54 -494 0 - -54 -677 0 - -54 -749 0 - -54 -1011 0 - -116 -260 0 - -116 -308 0 - -116 -494 0 - -116 -677 0 - -116 -749 0 - -116 -1011 0 - -260 -308 0 - -260 -494 0 - -260 -677 0 - -260 -749 0 - -260 -1011 0 - -308 -494 0 - -308 -677 0 - -308 -749 0 - -308 -1011 0 - -494 -677 0 - -494 -749 0 - -494 -1011 0 - -677 -749 0 - -677 -1011 0 - -749 -1011 0 - -55 -385 0 - -55 -456 0 - -55 -495 0 - -55 -678 0 - -55 -826 0 - -55 -1012 0 - -385 -456 0 - -385 -495 0 - -385 -678 0 - -385 -826 0 - -385 -1012 0 - -456 -495 0 - -456 -678 0 - -456 -826 0 - -456 -1012 0 - -495 -678 0 - -495 -826 0 - -495 -1012 0 - -678 -826 0 - -678 -1012 0 - -826 -1012 0 - -117 -386 0 - -117 -457 0 - -117 -496 0 - -117 -679 0 - -117 -750 0 - -117 -827 0 - -117 -1013 0 - -386 -457 0 - -386 -496 0 - -386 -679 0 - -386 -750 0 - -386 -827 0 - -386 -1013 0 - -457 -496 0 - -457 -679 0 - -457 -750 0 - -457 -827 0 - -457 -1013 0 - -496 -679 0 - -496 -750 0 - -496 -827 0 - -496 -1013 0 - -679 -750 0 - -679 -827 0 - -679 -1013 0 - -750 -827 0 - -750 -1013 0 - -827 -1013 0 - -207 -309 0 - -207 -640 0 - -207 -680 0 - -207 -1014 0 - -309 -640 0 - -309 -680 0 - -309 -1014 0 - -640 -680 0 - -640 -1014 0 - -680 -1014 0 - -156 -208 0 - -156 -641 0 - -156 -681 0 - -156 -929 0 - -156 -1015 0 - -208 -641 0 - -208 -681 0 - -208 -929 0 - -208 -1015 0 - -641 -681 0 - -641 -929 0 - -641 -1015 0 - -681 -929 0 - -681 -1015 0 - -929 -1015 0 - -209 -310 0 - -209 -930 0 - -310 -930 0 - -246 -425 0 - -246 -601 0 - -246 -682 0 - -246 -931 0 - -425 -601 0 - -425 -682 0 - -425 -931 0 - -601 -682 0 - -601 -931 0 - -682 -931 0 - -210 -247 0 - -210 -311 0 - -210 -426 0 - -210 -932 0 - -210 -1016 0 - -247 -311 0 - -247 -426 0 - -247 -932 0 - -247 -1016 0 - -311 -426 0 - -311 -932 0 - -311 -1016 0 - -426 -932 0 - -426 -1016 0 - -932 -1016 0 - -157 -312 0 - -157 -933 0 - -312 -933 0 - -158 -313 0 - -158 -934 0 - -158 -1017 0 - -313 -934 0 - -313 -1017 0 - -934 -1017 0 - -159 -211 0 - -159 -602 0 - -159 -683 0 - -159 -1018 0 - -211 -602 0 - -211 -683 0 - -211 -1018 0 - -602 -683 0 - -602 -1018 0 - -683 -1018 0 - -212 -314 0 - -212 -642 0 - -212 -935 0 - -314 -642 0 - -314 -935 0 - -642 -935 0 - -603 -643 0 - -603 -684 0 - -603 -936 0 - -603 -1019 0 - -643 -684 0 - -643 -936 0 - -643 -1019 0 - -684 -936 0 - -684 -1019 0 - -936 -1019 0 - -56 -315 0 - -56 -685 0 - -56 -867 0 - -315 -685 0 - -315 -867 0 - -685 -867 0 - -1 -57 0 - -1 -261 0 - -1 -316 0 - -1 -686 0 - -1 -751 0 - -1 -828 0 - -1 -868 0 - -57 -261 0 - -57 -316 0 - -57 -686 0 - -57 -751 0 - -57 -828 0 - -57 -868 0 - -261 -316 0 - -261 -686 0 - -261 -751 0 - -261 -828 0 - -261 -868 0 - -316 -686 0 - -316 -751 0 - -316 -828 0 - -316 -868 0 - -686 -751 0 - -686 -828 0 - -686 -868 0 - -751 -828 0 - -751 -868 0 - -828 -868 0 - -58 -687 0 - -58 -752 0 - -58 -869 0 - -687 -752 0 - -687 -869 0 - -752 -869 0 - -317 -753 0 - -317 -829 0 - -753 -829 0 - -59 -262 0 - -59 -318 0 - -59 -458 0 - -59 -688 0 - -59 -870 0 - -262 -318 0 - -262 -458 0 - -262 -688 0 - -262 -870 0 - -318 -458 0 - -318 -688 0 - -318 -870 0 - -458 -688 0 - -458 -870 0 - -688 -870 0 - -319 -459 0 - -319 -604 0 - -319 -830 0 - -319 -871 0 - -459 -604 0 - -459 -830 0 - -459 -871 0 - -604 -830 0 - -604 -871 0 - -830 -871 0 - -320 -387 0 - -320 -460 0 - -320 -689 0 - -320 -754 0 - -387 -460 0 - -387 -689 0 - -387 -754 0 - -460 -689 0 - -460 -754 0 - -689 -754 0 - -60 -321 0 - -60 -388 0 - -60 -690 0 - -60 -755 0 - -321 -388 0 - -321 -690 0 - -321 -755 0 - -388 -690 0 - -388 -755 0 - -690 -755 0 - -2 -61 0 - -2 -263 0 - -2 -322 0 - -2 -389 0 - -2 -872 0 - -61 -263 0 - -61 -322 0 - -61 -389 0 - -61 -872 0 - -263 -322 0 - -263 -389 0 - -263 -872 0 - -322 -389 0 - -322 -872 0 - -389 -872 0 - -3 -62 0 - -3 -264 0 - -3 -323 0 - -3 -605 0 - -3 -691 0 - -3 -756 0 - -62 -264 0 - -62 -323 0 - -62 -605 0 - -62 -691 0 - -62 -756 0 - -264 -323 0 - -264 -605 0 - -264 -691 0 - -264 -756 0 - -323 -605 0 - -323 -691 0 - -323 -756 0 - -605 -691 0 - -605 -756 0 - -691 -756 0 - -4 -390 0 - -4 -461 0 - -4 -606 0 - -4 -692 0 - -4 -757 0 - -4 -831 0 - -390 -461 0 - -390 -606 0 - -390 -692 0 - -390 -757 0 - -390 -831 0 - -461 -606 0 - -461 -692 0 - -461 -757 0 - -461 -831 0 - -606 -692 0 - -606 -757 0 - -606 -831 0 - -692 -757 0 - -692 -831 0 - -757 -831 0 - -5 -265 0 - -5 -427 0 - -5 -693 0 - -5 -758 0 - -5 -873 0 - -265 -427 0 - -265 -693 0 - -265 -758 0 - -265 -873 0 - -427 -693 0 - -427 -758 0 - -427 -873 0 - -693 -758 0 - -693 -873 0 - -758 -873 0 - -6 -497 0 - -6 -759 0 - -6 -937 0 - -497 -759 0 - -497 -937 0 - -759 -937 0 - -160 -428 0 - -160 -498 0 - -160 -694 0 - -160 -760 0 - -428 -498 0 - -428 -694 0 - -428 -760 0 - -498 -694 0 - -498 -760 0 - -694 -760 0 - -248 -429 0 - -248 -462 0 - -248 -499 0 - -248 -695 0 - -248 -874 0 - -248 -938 0 - -429 -462 0 - -429 -499 0 - -429 -695 0 - -429 -874 0 - -429 -938 0 - -462 -499 0 - -462 -695 0 - -462 -874 0 - -462 -938 0 - -499 -695 0 - -499 -874 0 - -499 -938 0 - -695 -874 0 - -695 -938 0 - -874 -938 0 - -7 -249 0 - -7 -266 0 - -7 -463 0 - -7 -500 0 - -7 -761 0 - -249 -266 0 - -249 -463 0 - -249 -500 0 - -249 -761 0 - -266 -463 0 - -266 -500 0 - -266 -761 0 - -463 -500 0 - -463 -761 0 - -500 -761 0 - -161 -391 0 - -161 -501 0 - -161 -696 0 - -161 -762 0 - -391 -501 0 - -391 -696 0 - -391 -762 0 - -501 -696 0 - -501 -762 0 - -696 -762 0 - -250 -430 0 - -250 -939 0 - -430 -939 0 - -162 -267 0 - -162 -392 0 - -162 -464 0 - -162 -502 0 - -162 -875 0 - -162 -940 0 - -267 -392 0 - -267 -464 0 - -267 -502 0 - -267 -875 0 - -267 -940 0 - -392 -464 0 - -392 -502 0 - -392 -875 0 - -392 -940 0 - -464 -502 0 - -464 -875 0 - -464 -940 0 - -502 -875 0 - -502 -940 0 - -875 -940 0 - -8 -163 0 - -8 -268 0 - -8 -393 0 - -8 -503 0 - -8 -876 0 - -8 -941 0 - -163 -268 0 - -163 -393 0 - -163 -503 0 - -163 -876 0 - -163 -941 0 - -268 -393 0 - -268 -503 0 - -268 -876 0 - -268 -941 0 - -393 -503 0 - -393 -876 0 - -393 -941 0 - -503 -876 0 - -503 -941 0 - -876 -941 0 - -9 -164 0 - -9 -269 0 - -9 -504 0 - -9 -697 0 - -9 -763 0 - -164 -269 0 - -164 -504 0 - -164 -697 0 - -164 -763 0 - -269 -504 0 - -269 -697 0 - -269 -763 0 - -504 -697 0 - -504 -763 0 - -697 -763 0 - -10 -165 0 - -10 -394 0 - -10 -465 0 - -10 -505 0 - -10 -698 0 - -165 -394 0 - -165 -465 0 - -165 -505 0 - -165 -698 0 - -394 -465 0 - -394 -505 0 - -394 -698 0 - -465 -505 0 - -465 -698 0 - -505 -698 0 - -11 -395 0 - -11 -764 0 - -11 -877 0 - -11 -942 0 - -395 -764 0 - -395 -877 0 - -395 -942 0 - -764 -877 0 - -764 -942 0 - -877 -942 0 - -699 -878 0 - -699 -1020 0 - -878 -1020 0 - -506 -765 0 - -506 -943 0 - -765 -943 0 - -190 -507 0 - -190 -607 0 - -190 -700 0 - -190 -766 0 - -507 -607 0 - -507 -700 0 - -507 -766 0 - -607 -700 0 - -607 -766 0 - -700 -766 0 - -466 -508 0 - -466 -608 0 - -466 -701 0 - -466 -879 0 - -466 -944 0 - -508 -608 0 - -508 -701 0 - -508 -879 0 - -508 -944 0 - -608 -701 0 - -608 -879 0 - -608 -944 0 - -701 -879 0 - -701 -944 0 - -879 -944 0 - -191 -396 0 - -191 -467 0 - -191 -702 0 - -191 -767 0 - -191 -945 0 - -396 -467 0 - -396 -702 0 - -396 -767 0 - -396 -945 0 - -467 -702 0 - -467 -767 0 - -467 -945 0 - -702 -767 0 - -702 -945 0 - -767 -945 0 - -397 -509 0 - -397 -703 0 - -397 -768 0 - -397 -1021 0 - -509 -703 0 - -509 -768 0 - -509 -1021 0 - -703 -768 0 - -703 -1021 0 - -768 -1021 0 - -946 -1022 0 - -192 -510 0 - -192 -609 0 - -192 -704 0 - -192 -769 0 - -192 -1023 0 - -510 -609 0 - -510 -704 0 - -510 -769 0 - -510 -1023 0 - -609 -704 0 - -609 -769 0 - -609 -1023 0 - -704 -769 0 - -704 -1023 0 - -769 -1023 0 - -398 -770 0 - -398 -880 0 - -398 -947 0 - -770 -880 0 - -770 -947 0 - -880 -947 0 - -399 -468 0 - -399 -511 0 - -399 -610 0 - -399 -705 0 - -399 -771 0 - -399 -948 0 - -399 -1024 0 - -468 -511 0 - -468 -610 0 - -468 -705 0 - -468 -771 0 - -468 -948 0 - -468 -1024 0 - -511 -610 0 - -511 -705 0 - -511 -771 0 - -511 -948 0 - -511 -1024 0 - -610 -705 0 - -610 -771 0 - -610 -948 0 - -610 -1024 0 - -705 -771 0 - -705 -948 0 - -705 -1024 0 - -771 -948 0 - -771 -1024 0 - -948 -1024 0 - -270 -443 0 - -270 -881 0 - -270 -1025 0 - -443 -881 0 - -443 -1025 0 - -881 -1025 0 - -63 -444 0 - -63 -882 0 - -63 -1026 0 - -444 -882 0 - -444 -1026 0 - -882 -1026 0 - -64 -271 0 - -64 -431 0 - -64 -883 0 - -64 -1027 0 - -271 -431 0 - -271 -883 0 - -271 -1027 0 - -431 -883 0 - -431 -1027 0 - -883 -1027 0 - -65 -272 0 - -65 -884 0 - -65 -1028 0 - -272 -884 0 - -272 -1028 0 - -884 -1028 0 - -273 -885 0 - -118 -251 0 - -118 -432 0 - -118 -569 0 - -118 -886 0 - -251 -432 0 - -251 -569 0 - -251 -886 0 - -432 -569 0 - -432 -886 0 - -569 -886 0 - -252 -433 0 - -252 -887 0 - -252 -1029 0 - -433 -887 0 - -433 -1029 0 - -887 -1029 0 - -66 -119 0 - -66 -253 0 - -66 -274 0 - -66 -570 0 - -119 -253 0 - -119 -274 0 - -119 -570 0 - -253 -274 0 - -253 -570 0 - -274 -570 0 - -67 -120 0 - -67 -275 0 - -67 -1030 0 - -120 -275 0 - -120 -1030 0 - -275 -1030 0 - -121 -400 0 - -121 -512 0 - -400 -512 0 - -122 -401 0 - -122 -513 0 - -401 -513 0 - -611 -949 0 - -324 -612 0 - -324 -888 0 - -324 -950 0 - -612 -888 0 - -612 -950 0 - -888 -950 0 - -166 -325 0 - -166 -644 0 - -166 -889 0 - -166 -951 0 - -325 -644 0 - -325 -889 0 - -325 -951 0 - -644 -889 0 - -644 -951 0 - -889 -951 0 - -167 -326 0 - -167 -613 0 - -167 -645 0 - -167 -1031 0 - -326 -613 0 - -326 -645 0 - -326 -1031 0 - -613 -645 0 - -613 -1031 0 - -645 -1031 0 - -168 -614 0 - -168 -1032 0 - -614 -1032 0 - -327 -646 0 - -327 -890 0 - -327 -952 0 - -646 -890 0 - -646 -952 0 - -890 -952 0 - -615 -647 0 - -615 -953 0 - -615 -1033 0 - -647 -953 0 - -647 -1033 0 - -953 -1033 0 - -68 -213 0 - -68 -891 0 - -68 -1034 0 - -213 -891 0 - -213 -1034 0 - -891 -1034 0 - -69 -123 0 - -69 -772 0 - -69 -892 0 - -123 -772 0 - -123 -892 0 - -772 -892 0 - -70 -214 0 - -70 -445 0 - -70 -773 0 - -70 -893 0 - -70 -954 0 - -70 -1035 0 - -214 -445 0 - -214 -773 0 - -214 -893 0 - -214 -954 0 - -214 -1035 0 - -445 -773 0 - -445 -893 0 - -445 -954 0 - -445 -1035 0 - -773 -893 0 - -773 -954 0 - -773 -1035 0 - -893 -954 0 - -893 -1035 0 - -954 -1035 0 - -71 -774 0 - -71 -894 0 - -71 -1036 0 - -774 -894 0 - -774 -1036 0 - -894 -1036 0 - -124 -215 0 - -124 -446 0 - -124 -514 0 - -124 -571 0 - -124 -775 0 - -124 -955 0 - -215 -446 0 - -215 -514 0 - -215 -571 0 - -215 -775 0 - -215 -955 0 - -446 -514 0 - -446 -571 0 - -446 -775 0 - -446 -955 0 - -514 -571 0 - -514 -775 0 - -514 -955 0 - -571 -775 0 - -571 -955 0 - -775 -955 0 - -72 -193 0 - -72 -895 0 - -72 -1037 0 - -193 -895 0 - -193 -1037 0 - -895 -1037 0 - -515 -572 0 - -515 -896 0 - -515 -956 0 - -572 -896 0 - -572 -956 0 - -896 -956 0 - -516 -897 0 - -516 -957 0 - -897 -957 0 - -73 -125 0 - -73 -194 0 - -73 -216 0 - -73 -517 0 - -73 -573 0 - -73 -776 0 - -125 -194 0 - -125 -216 0 - -125 -517 0 - -125 -573 0 - -125 -776 0 - -194 -216 0 - -194 -517 0 - -194 -573 0 - -194 -776 0 - -216 -517 0 - -216 -573 0 - -216 -776 0 - -517 -573 0 - -517 -776 0 - -573 -776 0 - -126 -518 0 - -126 -574 0 - -126 -898 0 - -126 -958 0 - -518 -574 0 - -518 -898 0 - -518 -958 0 - -574 -898 0 - -574 -958 0 - -898 -958 0 - -74 -217 0 - -74 -519 0 - -74 -1038 0 - -217 -519 0 - -217 -1038 0 - -519 -1038 0 - -75 -328 0 - -75 -777 0 - -75 -832 0 - -328 -777 0 - -328 -832 0 - -777 -832 0 - -76 -218 0 - -76 -778 0 - -218 -778 0 - -329 -469 0 - -329 -833 0 - -469 -833 0 - -169 -330 0 - -169 -648 0 - -169 -834 0 - -330 -648 0 - -330 -834 0 - -648 -834 0 - -470 -835 0 - -77 -219 0 - -77 -471 0 - -77 -649 0 - -77 -779 0 - -77 -836 0 - -219 -471 0 - -219 -649 0 - -219 -779 0 - -219 -836 0 - -471 -649 0 - -471 -779 0 - -471 -836 0 - -649 -779 0 - -649 -836 0 - -779 -836 0 - -170 -331 0 - -170 -472 0 - -331 -472 0 - -78 -220 0 - -78 -332 0 - -78 -650 0 - -78 -780 0 - -220 -332 0 - -220 -650 0 - -220 -780 0 - -332 -650 0 - -332 -780 0 - -650 -780 0 - -12 -333 0 - -12 -402 0 - -12 -473 0 - -12 -651 0 - -333 -402 0 - -333 -473 0 - -333 -651 0 - -402 -473 0 - -402 -651 0 - -473 -651 0 - -79 -403 0 - -79 -474 0 - -79 -616 0 - -403 -474 0 - -403 -616 0 - -474 -616 0 - -127 -334 0 - -127 -520 0 - -127 -959 0 - -334 -520 0 - -334 -959 0 - -520 -959 0 - -80 -335 0 - -80 -475 0 - -80 -706 0 - -335 -475 0 - -335 -706 0 - -475 -706 0 - -336 -476 0 - -336 -521 0 - -336 -617 0 - -336 -960 0 - -476 -521 0 - -476 -617 0 - -476 -960 0 - -521 -617 0 - -521 -960 0 - -617 -960 0 - -618 -707 0 - -618 -961 0 - -707 -961 0 - -13 -81 0 - -13 -128 0 - -13 -477 0 - -13 -522 0 - -13 -619 0 - -13 -652 0 - -81 -128 0 - -81 -477 0 - -81 -522 0 - -81 -619 0 - -81 -652 0 - -128 -477 0 - -128 -522 0 - -128 -619 0 - -128 -652 0 - -477 -522 0 - -477 -619 0 - -477 -652 0 - -522 -619 0 - -522 -652 0 - -619 -652 0 - -82 -129 0 - -82 -171 0 - -82 -337 0 - -82 -404 0 - -82 -523 0 - -82 -653 0 - -82 -708 0 - -129 -171 0 - -129 -337 0 - -129 -404 0 - -129 -523 0 - -129 -653 0 - -129 -708 0 - -171 -337 0 - -171 -404 0 - -171 -523 0 - -171 -653 0 - -171 -708 0 - -337 -404 0 - -337 -523 0 - -337 -653 0 - -337 -708 0 - -404 -523 0 - -404 -653 0 - -404 -708 0 - -523 -653 0 - -523 -708 0 - -653 -708 0 - -130 -338 0 - -130 -962 0 - -338 -962 0 - -131 -172 0 - -131 -339 0 - -131 -405 0 - -131 -478 0 - -131 -524 0 - -131 -963 0 - -172 -339 0 - -172 -405 0 - -172 -478 0 - -172 -524 0 - -172 -963 0 - -339 -405 0 - -339 -478 0 - -339 -524 0 - -339 -963 0 - -405 -478 0 - -405 -524 0 - -405 -963 0 - -478 -524 0 - -478 -963 0 - -524 -963 0 - -14 -83 0 - -14 -132 0 - -14 -173 0 - -14 -340 0 - -14 -406 0 - -14 -525 0 - -14 -964 0 - -83 -132 0 - -83 -173 0 - -83 -340 0 - -83 -406 0 - -83 -525 0 - -83 -964 0 - -132 -173 0 - -132 -340 0 - -132 -406 0 - -132 -525 0 - -132 -964 0 - -173 -340 0 - -173 -406 0 - -173 -525 0 - -173 -964 0 - -340 -406 0 - -340 -525 0 - -340 -964 0 - -406 -525 0 - -406 -964 0 - -525 -964 0 - -15 -84 0 - -15 -174 0 - -15 -407 0 - -15 -479 0 - -15 -526 0 - -15 -620 0 - -15 -709 0 - -84 -174 0 - -84 -407 0 - -84 -479 0 - -84 -526 0 - -84 -620 0 - -84 -709 0 - -174 -407 0 - -174 -479 0 - -174 -526 0 - -174 -620 0 - -174 -709 0 - -407 -479 0 - -407 -526 0 - -407 -620 0 - -407 -709 0 - -479 -526 0 - -479 -620 0 - -479 -709 0 - -526 -620 0 - -526 -709 0 - -620 -709 0 - -654 -710 0 - -654 -899 0 - -654 -965 0 - -710 -899 0 - -710 -965 0 - -899 -965 0 - -195 -341 0 - -195 -711 0 - -195 -900 0 - -341 -711 0 - -341 -900 0 - -711 -900 0 - -16 -196 0 - -16 -655 0 - -196 -655 0 - -342 -656 0 - -342 -712 0 - -656 -712 0 - -343 -966 0 - -17 -197 0 - -17 -344 0 - -17 -657 0 - -17 -713 0 - -197 -344 0 - -197 -657 0 - -197 -713 0 - -344 -657 0 - -344 -713 0 - -657 -713 0 - -18 -658 0 - -18 -714 0 - -18 -967 0 - -658 -714 0 - -658 -967 0 - -714 -967 0 - -19 -276 0 - -19 -659 0 - -19 -837 0 - -276 -659 0 - -276 -837 0 - -659 -837 0 - -85 -277 0 - -85 -621 0 - -85 -781 0 - -277 -621 0 - -277 -781 0 - -621 -781 0 - -86 -278 0 - -279 -527 0 - -279 -660 0 - -279 -838 0 - -279 -968 0 - -527 -660 0 - -527 -838 0 - -527 -968 0 - -660 -838 0 - -660 -968 0 - -838 -968 0 - -528 -575 0 - -528 -622 0 - -528 -839 0 - -528 -969 0 - -575 -622 0 - -575 -839 0 - -575 -969 0 - -622 -839 0 - -622 -969 0 - -839 -969 0 - -20 -87 0 - -20 -221 0 - -20 -280 0 - -20 -529 0 - -20 -576 0 - -20 -623 0 - -20 -661 0 - -20 -782 0 - -20 -840 0 - -87 -221 0 - -87 -280 0 - -87 -529 0 - -87 -576 0 - -87 -623 0 - -87 -661 0 - -87 -782 0 - -87 -840 0 - -221 -280 0 - -221 -529 0 - -221 -576 0 - -221 -623 0 - -221 -661 0 - -221 -782 0 - -221 -840 0 - -280 -529 0 - -280 -576 0 - -280 -623 0 - -280 -661 0 - -280 -782 0 - -280 -840 0 - -529 -576 0 - -529 -623 0 - -529 -661 0 - -529 -782 0 - -529 -840 0 - -576 -623 0 - -576 -661 0 - -576 -782 0 - -576 -840 0 - -623 -661 0 - -623 -782 0 - -623 -840 0 - -661 -782 0 - -661 -840 0 - -782 -840 0 - -783 -970 0 - -88 -222 0 - -88 -530 0 - -88 -662 0 - -88 -784 0 - -222 -530 0 - -222 -662 0 - -222 -784 0 - -530 -662 0 - -530 -784 0 - -662 -784 0 - -21 -89 0 - -21 -281 0 - -21 -531 0 - -21 -577 0 - -21 -971 0 - -89 -281 0 - -89 -531 0 - -89 -577 0 - -89 -971 0 - -281 -531 0 - -281 -577 0 - -281 -971 0 - -531 -577 0 - -531 -971 0 - -577 -971 0 - -22 -90 0 - -22 -282 0 - -22 -532 0 - -22 -624 0 - -22 -663 0 - -22 -785 0 - -90 -282 0 - -90 -532 0 - -90 -624 0 - -90 -663 0 - -90 -785 0 - -282 -532 0 - -282 -624 0 - -282 -663 0 - -282 -785 0 - -532 -624 0 - -532 -663 0 - -532 -785 0 - -624 -663 0 - -624 -785 0 - -663 -785 0 - -23 -91 0 - -23 -223 0 - -23 -533 0 - -23 -625 0 - -23 -841 0 - -91 -223 0 - -91 -533 0 - -91 -625 0 - -91 -841 0 - -223 -533 0 - -223 -625 0 - -223 -841 0 - -533 -625 0 - -533 -841 0 - -625 -841 0 - -345 -408 0 - -345 -447 0 - -345 -901 0 - -345 -1039 0 - -408 -447 0 - -408 -901 0 - -408 -1039 0 - -447 -901 0 - -447 -1039 0 - -901 -1039 0 - -409 -434 0 - -409 -786 0 - -409 -902 0 - -409 -1040 0 - -434 -786 0 - -434 -902 0 - -434 -1040 0 - -786 -902 0 - -786 -1040 0 - -902 -1040 0 - -346 -448 0 - -346 -534 0 - -346 -787 0 - -448 -534 0 - -448 -787 0 - -534 -787 0 - -535 -788 0 - -198 -536 0 - -198 -789 0 - -536 -789 0 - -347 -435 0 - -347 -1041 0 - -435 -1041 0 - -348 -410 0 - -348 -537 0 - -348 -903 0 - -348 -1042 0 - -410 -537 0 - -410 -903 0 - -410 -1042 0 - -537 -903 0 - -537 -1042 0 - -903 -1042 0 - -199 -349 0 - -199 -538 0 - -199 -715 0 - -199 -790 0 - -199 -1043 0 - -349 -538 0 - -349 -715 0 - -349 -790 0 - -349 -1043 0 - -538 -715 0 - -538 -790 0 - -538 -1043 0 - -715 -790 0 - -715 -1043 0 - -790 -1043 0 - -411 -539 0 - -411 -716 0 - -411 -1044 0 - -539 -716 0 - -539 -1044 0 - -716 -1044 0 - -350 -412 0 - -350 -791 0 - -350 -904 0 - -412 -791 0 - -412 -904 0 - -791 -904 0 - -224 -351 0 - -224 -664 0 - -224 -1045 0 - -351 -664 0 - -351 -1045 0 - -664 -1045 0 - -24 -133 0 - -24 -283 0 - -24 -352 0 - -24 -436 0 - -24 -792 0 - -24 -842 0 - -133 -283 0 - -133 -352 0 - -133 -436 0 - -133 -792 0 - -133 -842 0 - -283 -352 0 - -283 -436 0 - -283 -792 0 - -283 -842 0 - -352 -436 0 - -352 -792 0 - -352 -842 0 - -436 -792 0 - -436 -842 0 - -792 -842 0 - -175 -225 0 - -175 -665 0 - -175 -793 0 - -175 -972 0 - -175 -1046 0 - -225 -665 0 - -225 -793 0 - -225 -972 0 - -225 -1046 0 - -665 -793 0 - -665 -972 0 - -665 -1046 0 - -793 -972 0 - -793 -1046 0 - -972 -1046 0 - -284 -413 0 - -284 -437 0 - -284 -794 0 - -284 -1047 0 - -413 -437 0 - -413 -794 0 - -413 -1047 0 - -437 -794 0 - -437 -1047 0 - -794 -1047 0 - -285 -353 0 - -285 -1048 0 - -353 -1048 0 - -176 -438 0 - -176 -795 0 - -438 -795 0 - -134 -439 0 - -134 -578 0 - -134 -843 0 - -134 -973 0 - -439 -578 0 - -439 -843 0 - -439 -973 0 - -578 -843 0 - -578 -973 0 - -843 -973 0 - -25 -135 0 - -25 -226 0 - -25 -286 0 - -25 -579 0 - -25 -666 0 - -25 -796 0 - -25 -844 0 - -135 -226 0 - -135 -286 0 - -135 -579 0 - -135 -666 0 - -135 -796 0 - -135 -844 0 - -226 -286 0 - -226 -579 0 - -226 -666 0 - -226 -796 0 - -226 -844 0 - -286 -579 0 - -286 -666 0 - -286 -796 0 - -286 -844 0 - -579 -666 0 - -579 -796 0 - -579 -844 0 - -666 -796 0 - -666 -844 0 - -796 -844 0 - -354 -414 0 - -354 -797 0 - -354 -974 0 - -414 -797 0 - -414 -974 0 - -797 -974 0 - -136 -177 0 - -136 -287 0 - -136 -355 0 - -136 -415 0 - -136 -580 0 - -136 -975 0 - -177 -287 0 - -177 -355 0 - -177 -415 0 - -177 -580 0 - -177 -975 0 - -287 -355 0 - -287 -415 0 - -287 -580 0 - -287 -975 0 - -355 -415 0 - -355 -580 0 - -355 -975 0 - -415 -580 0 - -415 -975 0 - -580 -975 0 - -26 -137 0 - -26 -178 0 - -26 -288 0 - -26 -356 0 - -26 -416 0 - -26 -581 0 - -26 -976 0 - -26 -1049 0 - -137 -178 0 - -137 -288 0 - -137 -356 0 - -137 -416 0 - -137 -581 0 - -137 -976 0 - -137 -1049 0 - -178 -288 0 - -178 -356 0 - -178 -416 0 - -178 -581 0 - -178 -976 0 - -178 -1049 0 - -288 -356 0 - -288 -416 0 - -288 -581 0 - -288 -976 0 - -288 -1049 0 - -356 -416 0 - -356 -581 0 - -356 -976 0 - -356 -1049 0 - -416 -581 0 - -416 -976 0 - -416 -1049 0 - -581 -976 0 - -581 -1049 0 - -976 -1049 0 - -27 -138 0 - -27 -179 0 - -27 -289 0 - -27 -357 0 - -27 -667 0 - -27 -798 0 - -27 -1050 0 - -138 -179 0 - -138 -289 0 - -138 -357 0 - -138 -667 0 - -138 -798 0 - -138 -1050 0 - -179 -289 0 - -179 -357 0 - -179 -667 0 - -179 -798 0 - -179 -1050 0 - -289 -357 0 - -289 -667 0 - -289 -798 0 - -289 -1050 0 - -357 -667 0 - -357 -798 0 - -357 -1050 0 - -667 -798 0 - -667 -1050 0 - -798 -1050 0 - -28 -180 0 - -28 -227 0 - -28 -417 0 - -28 -845 0 - -28 -1051 0 - -180 -227 0 - -180 -417 0 - -180 -845 0 - -180 -1051 0 - -227 -417 0 - -227 -845 0 - -227 -1051 0 - -417 -845 0 - -417 -1051 0 - -845 -1051 0 - -29 -139 0 - -29 -418 0 - -29 -668 0 - -29 -799 0 - -29 -846 0 - -29 -977 0 - -29 -1052 0 - -139 -418 0 - -139 -668 0 - -139 -799 0 - -139 -846 0 - -139 -977 0 - -139 -1052 0 - -418 -668 0 - -418 -799 0 - -418 -846 0 - -418 -977 0 - -418 -1052 0 - -668 -799 0 - -668 -846 0 - -668 -977 0 - -668 -1052 0 - -799 -846 0 - -799 -977 0 - -799 -1052 0 - -846 -977 0 - -846 -1052 0 - -977 -1052 0 - -419 -905 0 - -419 -1053 0 - -905 -1053 0 - -140 -717 0 - -140 -800 0 - -140 -906 0 - -717 -800 0 - -717 -906 0 - -800 -906 0 - -718 -801 0 - -718 -907 0 - -718 -978 0 - -718 -1054 0 - -801 -907 0 - -801 -978 0 - -801 -1054 0 - -907 -978 0 - -907 -1054 0 - -978 -1054 0 - -141 -540 0 - -141 -582 0 - -141 -802 0 - -141 -979 0 - -540 -582 0 - -540 -802 0 - -540 -979 0 - -582 -802 0 - -582 -979 0 - -802 -979 0 - -719 -908 0 - -719 -1055 0 - -908 -1055 0 - -541 -583 0 - -541 -803 0 - -541 -980 0 - -583 -803 0 - -583 -980 0 - -803 -980 0 - -542 -584 0 - -542 -909 0 - -542 -981 0 - -584 -909 0 - -584 -981 0 - -909 -981 0 - -420 -543 0 - -420 -720 0 - -420 -910 0 - -420 -982 0 - -543 -720 0 - -543 -910 0 - -543 -982 0 - -720 -910 0 - -720 -982 0 - -910 -982 0 - -142 -421 0 - -142 -544 0 - -142 -721 0 - -142 -804 0 - -142 -1056 0 - -421 -544 0 - -421 -721 0 - -421 -804 0 - -421 -1056 0 - -544 -721 0 - -544 -804 0 - -544 -1056 0 - -721 -804 0 - -721 -1056 0 - -804 -1056 0 - -290 -847 0 - -290 -911 0 - -847 -911 0 - -92 -228 0 - -92 -722 0 - -92 -912 0 - -228 -722 0 - -228 -912 0 - -722 -912 0 - -93 -181 0 - -93 -229 0 - -93 -723 0 - -93 -805 0 - -93 -913 0 - -93 -983 0 - -181 -229 0 - -181 -723 0 - -181 -805 0 - -181 -913 0 - -181 -983 0 - -229 -723 0 - -229 -805 0 - -229 -913 0 - -229 -983 0 - -723 -805 0 - -723 -913 0 - -723 -983 0 - -805 -913 0 - -805 -983 0 - -913 -983 0 - -94 -291 0 - -94 -440 0 - -94 -806 0 - -94 -914 0 - -291 -440 0 - -291 -806 0 - -291 -914 0 - -440 -806 0 - -440 -914 0 - -806 -914 0 - -230 -545 0 - -230 -585 0 - -230 -807 0 - -230 -848 0 - -230 -984 0 - -545 -585 0 - -545 -807 0 - -545 -848 0 - -545 -984 0 - -585 -807 0 - -585 -848 0 - -585 -984 0 - -807 -848 0 - -807 -984 0 - -848 -984 0 - -95 -231 0 - -95 -546 0 - -95 -586 0 - -95 -808 0 - -95 -985 0 - -231 -546 0 - -231 -586 0 - -231 -808 0 - -231 -985 0 - -546 -586 0 - -546 -808 0 - -546 -985 0 - -586 -808 0 - -586 -985 0 - -808 -985 0 - -182 -441 0 - -182 -547 0 - -182 -724 0 - -182 -809 0 - -441 -547 0 - -441 -724 0 - -441 -809 0 - -547 -724 0 - -547 -809 0 - -724 -809 0 - -232 -254 0 - -232 -442 0 - -232 -725 0 - -232 -915 0 - -232 -986 0 - -254 -442 0 - -254 -725 0 - -254 -915 0 - -254 -986 0 - -442 -725 0 - -442 -915 0 - -442 -986 0 - -725 -915 0 - -725 -986 0 - -915 -986 0 - -96 -233 0 - -96 -255 0 - -96 -292 0 - -96 -548 0 - -96 -587 0 - -96 -810 0 - -96 -849 0 - -233 -255 0 - -233 -292 0 - -233 -548 0 - -233 -587 0 - -233 -810 0 - -233 -849 0 - -255 -292 0 - -255 -548 0 - -255 -587 0 - -255 -810 0 - -255 -849 0 - -292 -548 0 - -292 -587 0 - -292 -810 0 - -292 -849 0 - -548 -587 0 - -548 -810 0 - -548 -849 0 - -587 -810 0 - -587 -849 0 - -810 -849 0 - -726 -811 0 - -726 -987 0 - -811 -987 0 - -183 -293 0 - -183 -549 0 - -183 -588 0 - -183 -916 0 - -183 -988 0 - -293 -549 0 - -293 -588 0 - -293 -916 0 - -293 -988 0 - -549 -588 0 - -549 -916 0 - -549 -988 0 - -588 -916 0 - -588 -988 0 - -916 -988 0 - -97 -184 0 - -97 -294 0 - -97 -550 0 - -97 -727 0 - -97 -812 0 - -184 -294 0 - -184 -550 0 - -184 -727 0 - -184 -812 0 - -294 -550 0 - -294 -727 0 - -294 -812 0 - -550 -727 0 - -550 -812 0 - -727 -812 0 - -98 -185 0 - -98 -234 0 - -98 -551 0 - -98 -728 0 - -98 -850 0 - -185 -234 0 - -185 -551 0 - -185 -728 0 - -185 -850 0 - -234 -551 0 - -234 -728 0 - -234 -850 0 - -551 -728 0 - -551 -850 0 - -728 -850 0 - -295 -358 0 - -235 -359 0 - -235 -552 0 - -235 -589 0 - -359 -552 0 - -359 -589 0 - -552 -589 0 - -200 -553 0 - -200 -626 0 - -553 -626 0 - -480 -554 0 - -480 -590 0 - -480 -627 0 - -554 -590 0 - -554 -627 0 - -590 -627 0 - -201 -360 0 - -201 -481 0 - -360 -481 0 - -236 -361 0 - -236 -555 0 - -236 -669 0 - -361 -555 0 - -361 -669 0 - -555 -669 0 - -237 -362 0 - -237 -591 0 - -362 -591 0 - -296 -363 0 - -296 -556 0 - -296 -592 0 - -363 -556 0 - -363 -592 0 - -556 -592 0 - -482 -557 0 - -482 -628 0 - -482 -670 0 - -557 -628 0 - -557 -670 0 - -628 -670 0 - -99 -629 0 - -99 -1057 0 - -629 -1057 0 - -364 -630 0 - -186 -365 0 - -186 -729 0 - -365 -729 0 - -631 -730 0 - -631 -1058 0 - -730 -1058 0 - -100 -143 0 - -100 -187 0 - -100 -366 0 - -100 -731 0 - -100 -1059 0 - -143 -187 0 - -143 -366 0 - -143 -731 0 - -143 -1059 0 - -187 -366 0 - -187 -731 0 - -187 -1059 0 - -366 -731 0 - -366 -1059 0 - -731 -1059 0 - -30 -101 0 - -30 -144 0 - -30 -188 0 - -30 -367 0 - -30 -1060 0 - -101 -144 0 - -101 -188 0 - -101 -367 0 - -101 -1060 0 - -144 -188 0 - -144 -367 0 - -144 -1060 0 - -188 -367 0 - -188 -1060 0 - -367 -1060 0 - -31 -102 0 - -31 -145 0 - -31 -189 0 - -31 -368 0 - -31 -632 0 - -31 -732 0 - -31 -1061 0 - -102 -145 0 - -102 -189 0 - -102 -368 0 - -102 -632 0 - -102 -732 0 - -102 -1061 0 - -145 -189 0 - -145 -368 0 - -145 -632 0 - -145 -732 0 - -145 -1061 0 - -189 -368 0 - -189 -632 0 - -189 -732 0 - -189 -1061 0 - -368 -632 0 - -368 -732 0 - -368 -1061 0 - -632 -732 0 - -632 -1061 0 - -732 -1061 0 - -32 -103 0 - -32 -369 0 - -103 -369 0 - -33 -483 0 - -33 -917 0 - -483 -917 0 - -34 -104 0 - -34 -146 0 - -34 -733 0 - -34 -813 0 - -34 -918 0 - -104 -146 0 - -104 -733 0 - -104 -813 0 - -104 -918 0 - -146 -733 0 - -146 -813 0 - -146 -918 0 - -733 -813 0 - -733 -918 0 - -813 -918 0 - -35 -105 0 - -35 -238 0 - -35 -558 0 - -35 -593 0 - -35 -814 0 - -35 -989 0 - -105 -238 0 - -105 -558 0 - -105 -593 0 - -105 -814 0 - -105 -989 0 - -238 -558 0 - -238 -593 0 - -238 -814 0 - -238 -989 0 - -558 -593 0 - -558 -814 0 - -558 -989 0 - -593 -814 0 - -593 -989 0 - -814 -989 0 - -559 -734 0 - -559 -919 0 - -559 -990 0 - -734 -919 0 - -734 -990 0 - -919 -990 0 - -560 -735 0 - -560 -815 0 - -735 -815 0 - -239 -736 0 - -239 -920 0 - -239 -991 0 - -736 -920 0 - -736 -991 0 - -920 -991 0 - -147 -240 0 - -147 -594 0 - -147 -992 0 - -240 -594 0 - -240 -992 0 - -594 -992 0 - -36 -106 0 - -36 -148 0 - -36 -561 0 - -36 -595 0 - -36 -921 0 - -36 -993 0 - -106 -148 0 - -106 -561 0 - -106 -595 0 - -106 -921 0 - -106 -993 0 - -148 -561 0 - -148 -595 0 - -148 -921 0 - -148 -993 0 - -561 -595 0 - -561 -921 0 - -561 -993 0 - -595 -921 0 - -595 -993 0 - -921 -993 0 - -37 -107 0 - -37 -149 0 - -37 -562 0 - -37 -737 0 - -37 -816 0 - -107 -149 0 - -107 -562 0 - -107 -737 0 - -107 -816 0 - -149 -562 0 - -149 -737 0 - -149 -816 0 - -562 -737 0 - -562 -816 0 - -737 -816 0 - -38 -108 0 - -38 -241 0 - -38 -484 0 - -38 -563 0 - -38 -738 0 - -108 -241 0 - -108 -484 0 - -108 -563 0 - -108 -738 0 - -241 -484 0 - -241 -563 0 - -241 -738 0 - -484 -563 0 - -484 -738 0 - -563 -738 0 - -39 -150 0 - -39 -485 0 - -39 -564 0 - -39 -739 0 - -39 -817 0 - -39 -994 0 - -150 -485 0 - -150 -564 0 - -150 -739 0 - -150 -817 0 - -150 -994 0 - -485 -564 0 - -485 -739 0 - -485 -817 0 - -485 -994 0 - -564 -739 0 - -564 -817 0 - -564 -994 0 - -739 -817 0 - -739 -994 0 - -817 -994 0 - -297 -370 0 - -297 -422 0 - -297 -486 0 - -297 -851 0 - -370 -422 0 - -370 -486 0 - -370 -851 0 - -422 -486 0 - -422 -851 0 - -486 -851 0 - -298 -371 0 - -298 -852 0 - -371 -852 0 - -487 -565 0 - -487 -853 0 - -565 -853 0 - -299 -488 0 - -299 -566 0 - -299 -854 0 - -488 -566 0 - -488 -854 0 - -566 -854 0 - -300 -372 0 - -300 -423 0 - -300 -489 0 - -300 -567 0 - -372 -423 0 - -372 -489 0 - -372 -567 0 - -423 -489 0 - -423 -567 0 - -489 -567 0 - -301 -373 0 - -301 -424 0 - -301 -568 0 - -373 -424 0 - -373 -568 0 - -424 -568 0 - -40 -374 0 - -40 -855 0 - -40 -922 0 - -40 -1062 0 - -374 -855 0 - -374 -922 0 - -374 -1062 0 - -855 -922 0 - -855 -1062 0 - -922 -1062 0 - -109 -242 0 - -109 -740 0 - -109 -923 0 - -109 -995 0 - -109 -1063 0 - -242 -740 0 - -242 -923 0 - -242 -995 0 - -242 -1063 0 - -740 -923 0 - -740 -995 0 - -740 -1063 0 - -923 -995 0 - -923 -1063 0 - -995 -1063 0 - -375 -633 0 - -375 -856 0 - -375 -924 0 - -375 -996 0 - -633 -856 0 - -633 -924 0 - -633 -996 0 - -856 -924 0 - -856 -996 0 - -924 -996 0 - -376 -741 0 - -376 -857 0 - -376 -925 0 - -376 -997 0 - -741 -857 0 - -741 -925 0 - -741 -997 0 - -857 -925 0 - -857 -997 0 - -925 -997 0 - -41 -110 0 - -41 -151 0 - -41 -243 0 - -41 -634 0 - -41 -858 0 - -110 -151 0 - -110 -243 0 - -110 -634 0 - -110 -858 0 - -151 -243 0 - -151 -634 0 - -151 -858 0 - -243 -634 0 - -243 -858 0 - -634 -858 0 - -377 -742 0 - -377 -998 0 - -742 -998 0 - -111 -152 0 - -111 -244 0 - -111 -378 0 - -111 -743 0 - -111 -1064 0 - -152 -244 0 - -152 -378 0 - -152 -743 0 - -152 -1064 0 - -244 -378 0 - -244 -743 0 - -244 -1064 0 - -378 -743 0 - -378 -1064 0 - -743 -1064 0 - -153 -379 0 - -153 -926 0 - -153 -999 0 - -379 -926 0 - -379 -999 0 - -926 -999 0 - -42 -112 0 - -42 -154 0 - -42 -380 0 - -42 -927 0 - -42 -1000 0 - -42 -1065 0 - -112 -154 0 - -112 -380 0 - -112 -927 0 - -112 -1000 0 - -112 -1065 0 - -154 -380 0 - -154 -927 0 - -154 -1000 0 - -154 -1065 0 - -380 -927 0 - -380 -1000 0 - -380 -1065 0 - -927 -1000 0 - -927 -1065 0 - -1000 -1065 0 - -43 -113 0 - -43 -245 0 - -43 -381 0 - -43 -928 0 - -43 -1001 0 - -113 -245 0 - -113 -381 0 - -113 -928 0 - -113 -1001 0 - -245 -381 0 - -245 -928 0 - -245 -1001 0 - -381 -928 0 - -381 -1001 0 - -928 -1001 0 - -44 -155 0 - -44 -635 0 - -44 -744 0 - -44 -859 0 - -44 -1002 0 - -44 -1066 0 - -155 -635 0 - -155 -744 0 - -155 -859 0 - -155 -1002 0 - -155 -1066 0 - -635 -744 0 - -635 -859 0 - -635 -1002 0 - -635 -1066 0 - -744 -859 0 - -744 -1002 0 - -744 -1066 0 - -859 -1002 0 - -859 -1066 0 - -1002 -1066 0 - -12 -19 0 - -12 -33 0 - -12 -40 0 - -19 -33 0 - -19 -40 0 - -33 -40 0 - -1 -5 0 - -1 -24 0 - -1 -34 0 - -5 -24 0 - -5 -34 0 - -24 -34 0 - -6 -35 0 - -7 -13 0 - -7 -16 0 - -7 -20 0 - -7 -25 0 - -7 -41 0 - -13 -16 0 - -13 -20 0 - -13 -25 0 - -13 -41 0 - -16 -20 0 - -16 -25 0 - -16 -41 0 - -20 -25 0 - -20 -41 0 - -25 -41 0 - -2 -8 0 - -2 -14 0 - -2 -21 0 - -2 -26 0 - -2 -30 0 - -2 -36 0 - -2 -42 0 - -8 -14 0 - -8 -21 0 - -8 -26 0 - -8 -30 0 - -8 -36 0 - -8 -42 0 - -14 -21 0 - -14 -26 0 - -14 -30 0 - -14 -36 0 - -14 -42 0 - -21 -26 0 - -21 -30 0 - -21 -36 0 - -21 -42 0 - -26 -30 0 - -26 -36 0 - -26 -42 0 - -30 -36 0 - -30 -42 0 - -36 -42 0 - -3 -9 0 - -3 -17 0 - -3 -22 0 - -3 -27 0 - -3 -31 0 - -3 -37 0 - -9 -17 0 - -9 -22 0 - -9 -27 0 - -9 -31 0 - -9 -37 0 - -17 -22 0 - -17 -27 0 - -17 -31 0 - -17 -37 0 - -22 -27 0 - -22 -31 0 - -22 -37 0 - -27 -31 0 - -27 -37 0 - -31 -37 0 - -10 -15 0 - -10 -23 0 - -10 -28 0 - -10 -38 0 - -15 -23 0 - -15 -28 0 - -15 -38 0 - -23 -28 0 - -23 -38 0 - -28 -38 0 - -11 -32 0 - -11 -43 0 - -32 -43 0 - -4 -18 0 - -4 -29 0 - -4 -39 0 - -4 -44 0 - -18 -29 0 - -18 -39 0 - -18 -44 0 - -29 -39 0 - -29 -44 0 - -39 -44 0 - -50 -56 0 - -50 -68 0 - -50 -92 0 - -56 -68 0 - -56 -92 0 - -68 -92 0 - -45 -57 0 - -45 -69 0 - -45 -75 0 - -45 -104 0 - -57 -69 0 - -57 -75 0 - -57 -104 0 - -69 -75 0 - -69 -104 0 - -75 -104 0 - -51 -58 0 - -51 -63 0 - -51 -70 0 - -51 -93 0 - -51 -109 0 - -58 -63 0 - -58 -70 0 - -58 -93 0 - -58 -109 0 - -63 -70 0 - -63 -93 0 - -63 -109 0 - -70 -93 0 - -70 -109 0 - -93 -109 0 - -52 -64 0 - -52 -71 0 - -52 -79 0 - -52 -85 0 - -52 -94 0 - -52 -99 0 - -64 -71 0 - -64 -79 0 - -64 -85 0 - -64 -94 0 - -64 -99 0 - -71 -79 0 - -71 -85 0 - -71 -94 0 - -71 -99 0 - -79 -85 0 - -79 -94 0 - -79 -99 0 - -85 -94 0 - -85 -99 0 - -94 -99 0 - -59 -65 0 - -59 -72 0 - -59 -80 0 - -59 -86 0 - -65 -72 0 - -65 -80 0 - -65 -86 0 - -72 -80 0 - -72 -86 0 - -80 -86 0 - -46 -76 0 - -46 -95 0 - -46 -105 0 - -76 -95 0 - -76 -105 0 - -95 -105 0 - -47 -53 0 - -47 -66 0 - -47 -73 0 - -47 -77 0 - -47 -81 0 - -47 -87 0 - -47 -96 0 - -47 -110 0 - -53 -66 0 - -53 -73 0 - -53 -77 0 - -53 -81 0 - -53 -87 0 - -53 -96 0 - -53 -110 0 - -66 -73 0 - -66 -77 0 - -66 -81 0 - -66 -87 0 - -66 -96 0 - -66 -110 0 - -73 -77 0 - -73 -81 0 - -73 -87 0 - -73 -96 0 - -73 -110 0 - -77 -81 0 - -77 -87 0 - -77 -96 0 - -77 -110 0 - -81 -87 0 - -81 -96 0 - -81 -110 0 - -87 -96 0 - -87 -110 0 - -96 -110 0 - -48 -60 0 - -48 -82 0 - -48 -88 0 - -48 -100 0 - -48 -111 0 - -60 -82 0 - -60 -88 0 - -60 -100 0 - -60 -111 0 - -82 -88 0 - -82 -100 0 - -82 -111 0 - -88 -100 0 - -88 -111 0 - -100 -111 0 - -61 -83 0 - -61 -89 0 - -61 -101 0 - -61 -106 0 - -61 -112 0 - -83 -89 0 - -83 -101 0 - -83 -106 0 - -83 -112 0 - -89 -101 0 - -89 -106 0 - -89 -112 0 - -101 -106 0 - -101 -112 0 - -106 -112 0 - -54 -62 0 - -54 -67 0 - -54 -90 0 - -54 -97 0 - -54 -102 0 - -54 -107 0 - -62 -67 0 - -62 -90 0 - -62 -97 0 - -62 -102 0 - -62 -107 0 - -67 -90 0 - -67 -97 0 - -67 -102 0 - -67 -107 0 - -90 -97 0 - -90 -102 0 - -90 -107 0 - -97 -102 0 - -97 -107 0 - -102 -107 0 - -55 -74 0 - -55 -84 0 - -55 -91 0 - -55 -98 0 - -55 -108 0 - -74 -84 0 - -74 -91 0 - -74 -98 0 - -74 -108 0 - -84 -91 0 - -84 -98 0 - -84 -108 0 - -91 -98 0 - -91 -108 0 - -98 -108 0 - -49 -78 0 - -49 -103 0 - -49 -113 0 - -78 -103 0 - -78 -113 0 - -103 -113 0 - -123 -133 0 - -123 -140 0 - -123 -146 0 - -133 -140 0 - -133 -146 0 - -140 -146 0 - -124 -127 0 - -124 -141 0 - -127 -141 0 - -114 -118 0 - -114 -134 0 - -118 -134 0 - -115 -119 0 - -115 -125 0 - -115 -128 0 - -115 -135 0 - -115 -151 0 - -119 -125 0 - -119 -128 0 - -119 -135 0 - -119 -151 0 - -125 -128 0 - -125 -135 0 - -125 -151 0 - -128 -135 0 - -128 -151 0 - -135 -151 0 - -129 -142 0 - -129 -143 0 - -129 -152 0 - -142 -143 0 - -142 -152 0 - -143 -152 0 - -130 -147 0 - -121 -126 0 - -121 -131 0 - -121 -136 0 - -121 -153 0 - -126 -131 0 - -126 -136 0 - -126 -153 0 - -131 -136 0 - -131 -153 0 - -136 -153 0 - -132 -137 0 - -132 -144 0 - -132 -148 0 - -132 -154 0 - -137 -144 0 - -137 -148 0 - -137 -154 0 - -144 -148 0 - -144 -154 0 - -148 -154 0 - -116 -120 0 - -116 -138 0 - -116 -145 0 - -116 -149 0 - -120 -138 0 - -120 -145 0 - -120 -149 0 - -138 -145 0 - -138 -149 0 - -145 -149 0 - -117 -122 0 - -117 -139 0 - -117 -150 0 - -117 -155 0 - -122 -139 0 - -122 -150 0 - -122 -155 0 - -139 -150 0 - -139 -155 0 - -150 -155 0 - -156 -175 0 - -156 -181 0 - -175 -181 0 - -166 -169 0 - -166 -186 0 - -169 -186 0 - -160 -176 0 - -160 -182 0 - -176 -182 0 - -161 -171 0 - -161 -187 0 - -171 -187 0 - -157 -162 0 - -157 -170 0 - -157 -172 0 - -157 -177 0 - -157 -183 0 - -162 -170 0 - -162 -172 0 - -162 -177 0 - -162 -183 0 - -170 -172 0 - -170 -177 0 - -170 -183 0 - -172 -177 0 - -172 -183 0 - -177 -183 0 - -158 -163 0 - -158 -173 0 - -158 -178 0 - -158 -188 0 - -163 -173 0 - -163 -178 0 - -163 -188 0 - -173 -178 0 - -173 -188 0 - -178 -188 0 - -164 -167 0 - -164 -179 0 - -164 -184 0 - -164 -189 0 - -167 -179 0 - -167 -184 0 - -167 -189 0 - -179 -184 0 - -179 -189 0 - -184 -189 0 - -159 -165 0 - -159 -168 0 - -159 -174 0 - -159 -180 0 - -159 -185 0 - -165 -168 0 - -165 -174 0 - -165 -180 0 - -165 -185 0 - -168 -174 0 - -168 -180 0 - -168 -185 0 - -174 -180 0 - -174 -185 0 - -180 -185 0 - -193 -195 0 - -190 -200 0 - -194 -196 0 - -194 -198 0 - -196 -198 0 - -191 -201 0 - -192 -197 0 - -192 -199 0 - -197 -199 0 - -207 -213 0 - -207 -224 0 - -207 -228 0 - -213 -224 0 - -213 -228 0 - -224 -228 0 - -208 -214 0 - -208 -225 0 - -208 -229 0 - -208 -242 0 - -214 -225 0 - -214 -229 0 - -214 -242 0 - -225 -229 0 - -225 -242 0 - -229 -242 0 - -209 -215 0 - -209 -230 0 - -209 -235 0 - -215 -230 0 - -215 -235 0 - -230 -235 0 - -202 -218 0 - -202 -231 0 - -202 -238 0 - -218 -231 0 - -218 -238 0 - -231 -238 0 - -203 -232 0 - -203 -239 0 - -232 -239 0 - -204 -216 0 - -204 -219 0 - -204 -221 0 - -204 -226 0 - -204 -233 0 - -204 -243 0 - -216 -219 0 - -216 -221 0 - -216 -226 0 - -216 -233 0 - -216 -243 0 - -219 -221 0 - -219 -226 0 - -219 -233 0 - -219 -243 0 - -221 -226 0 - -221 -233 0 - -221 -243 0 - -226 -233 0 - -226 -243 0 - -233 -243 0 - -205 -222 0 - -205 -236 0 - -205 -244 0 - -222 -236 0 - -222 -244 0 - -236 -244 0 - -210 -237 0 - -210 -240 0 - -237 -240 0 - -211 -217 0 - -211 -223 0 - -211 -227 0 - -211 -234 0 - -211 -241 0 - -217 -223 0 - -217 -227 0 - -217 -234 0 - -217 -241 0 - -223 -227 0 - -223 -234 0 - -223 -241 0 - -227 -234 0 - -227 -241 0 - -234 -241 0 - -206 -212 0 - -206 -220 0 - -206 -245 0 - -212 -220 0 - -212 -245 0 - -220 -245 0 - -246 -248 0 - -246 -251 0 - -248 -251 0 - -252 -254 0 - -249 -253 0 - -249 -255 0 - -253 -255 0 - -247 -250 0 - -256 -270 0 - -256 -276 0 - -256 -290 0 - -256 -297 0 - -270 -276 0 - -270 -290 0 - -270 -297 0 - -276 -290 0 - -276 -297 0 - -290 -297 0 - -261 -265 0 - -261 -283 0 - -261 -295 0 - -261 -298 0 - -265 -283 0 - -265 -295 0 - -265 -298 0 - -283 -295 0 - -283 -298 0 - -295 -298 0 - -257 -271 0 - -257 -277 0 - -257 -284 0 - -257 -291 0 - -271 -277 0 - -271 -284 0 - -271 -291 0 - -277 -284 0 - -277 -291 0 - -284 -291 0 - -262 -272 0 - -262 -278 0 - -262 -285 0 - -272 -278 0 - -272 -285 0 - -278 -285 0 - -258 -273 0 - -258 -279 0 - -273 -279 0 - -259 -266 0 - -259 -274 0 - -259 -280 0 - -259 -286 0 - -259 -292 0 - -259 -299 0 - -266 -274 0 - -266 -280 0 - -266 -286 0 - -266 -292 0 - -266 -299 0 - -274 -280 0 - -274 -286 0 - -274 -292 0 - -274 -299 0 - -280 -286 0 - -280 -292 0 - -280 -299 0 - -286 -292 0 - -286 -299 0 - -292 -299 0 - -267 -287 0 - -267 -293 0 - -267 -300 0 - -287 -293 0 - -287 -300 0 - -293 -300 0 - -263 -268 0 - -263 -281 0 - -263 -288 0 - -263 -296 0 - -263 -301 0 - -268 -281 0 - -268 -288 0 - -268 -296 0 - -268 -301 0 - -281 -288 0 - -281 -296 0 - -281 -301 0 - -288 -296 0 - -288 -301 0 - -296 -301 0 - -260 -264 0 - -260 -269 0 - -260 -275 0 - -260 -282 0 - -260 -289 0 - -260 -294 0 - -264 -269 0 - -264 -275 0 - -264 -282 0 - -264 -289 0 - -264 -294 0 - -269 -275 0 - -269 -282 0 - -269 -289 0 - -269 -294 0 - -275 -282 0 - -275 -289 0 - -275 -294 0 - -282 -289 0 - -282 -294 0 - -289 -294 0 - -305 -333 0 - -305 -345 0 - -305 -370 0 - -305 -374 0 - -333 -345 0 - -333 -370 0 - -333 -374 0 - -345 -370 0 - -345 -374 0 - -370 -374 0 - -306 -309 0 - -306 -315 0 - -306 -351 0 - -309 -315 0 - -309 -351 0 - -315 -351 0 - -302 -316 0 - -302 -328 0 - -302 -352 0 - -302 -358 0 - -302 -371 0 - -316 -328 0 - -316 -352 0 - -316 -358 0 - -316 -371 0 - -328 -352 0 - -328 -358 0 - -328 -371 0 - -352 -358 0 - -352 -371 0 - -358 -371 0 - -310 -317 0 - -310 -334 0 - -310 -346 0 - -310 -359 0 - -317 -334 0 - -317 -346 0 - -317 -359 0 - -334 -346 0 - -334 -359 0 - -346 -359 0 - -318 -335 0 - -318 -341 0 - -318 -353 0 - -335 -341 0 - -335 -353 0 - -341 -353 0 - -319 -324 0 - -319 -329 0 - -319 -336 0 - -319 -364 0 - -319 -375 0 - -324 -329 0 - -324 -336 0 - -324 -364 0 - -324 -375 0 - -329 -336 0 - -329 -364 0 - -329 -375 0 - -336 -364 0 - -336 -375 0 - -364 -375 0 - -307 -325 0 - -307 -330 0 - -307 -365 0 - -307 -376 0 - -325 -330 0 - -325 -365 0 - -325 -376 0 - -330 -365 0 - -330 -376 0 - -365 -376 0 - -320 -354 0 - -320 -360 0 - -320 -377 0 - -354 -360 0 - -354 -377 0 - -360 -377 0 - -303 -321 0 - -303 -337 0 - -303 -342 0 - -303 -361 0 - -303 -366 0 - -303 -378 0 - -321 -337 0 - -321 -342 0 - -321 -361 0 - -321 -366 0 - -321 -378 0 - -337 -342 0 - -337 -361 0 - -337 -366 0 - -337 -378 0 - -342 -361 0 - -342 -366 0 - -342 -378 0 - -361 -366 0 - -361 -378 0 - -366 -378 0 - -311 -338 0 - -311 -343 0 - -311 -347 0 - -311 -362 0 - -338 -343 0 - -338 -347 0 - -338 -362 0 - -343 -347 0 - -343 -362 0 - -347 -362 0 - -312 -331 0 - -312 -339 0 - -312 -355 0 - -312 -372 0 - -312 -379 0 - -331 -339 0 - -331 -355 0 - -331 -372 0 - -331 -379 0 - -339 -355 0 - -339 -372 0 - -339 -379 0 - -355 -372 0 - -355 -379 0 - -372 -379 0 - -313 -322 0 - -313 -340 0 - -313 -348 0 - -313 -356 0 - -313 -363 0 - -313 -367 0 - -313 -373 0 - -313 -380 0 - -322 -340 0 - -322 -348 0 - -322 -356 0 - -322 -363 0 - -322 -367 0 - -322 -373 0 - -322 -380 0 - -340 -348 0 - -340 -356 0 - -340 -363 0 - -340 -367 0 - -340 -373 0 - -340 -380 0 - -348 -356 0 - -348 -363 0 - -348 -367 0 - -348 -373 0 - -348 -380 0 - -356 -363 0 - -356 -367 0 - -356 -373 0 - -356 -380 0 - -363 -367 0 - -363 -373 0 - -363 -380 0 - -367 -373 0 - -367 -380 0 - -373 -380 0 - -308 -323 0 - -308 -326 0 - -308 -344 0 - -308 -349 0 - -308 -357 0 - -308 -368 0 - -323 -326 0 - -323 -344 0 - -323 -349 0 - -323 -357 0 - -323 -368 0 - -326 -344 0 - -326 -349 0 - -326 -357 0 - -326 -368 0 - -344 -349 0 - -344 -357 0 - -344 -368 0 - -349 -357 0 - -349 -368 0 - -357 -368 0 - -304 -314 0 - -304 -327 0 - -304 -332 0 - -304 -350 0 - -304 -369 0 - -304 -381 0 - -314 -327 0 - -314 -332 0 - -314 -350 0 - -314 -369 0 - -314 -381 0 - -327 -332 0 - -327 -350 0 - -327 -369 0 - -327 -381 0 - -332 -350 0 - -332 -369 0 - -332 -381 0 - -350 -369 0 - -350 -381 0 - -369 -381 0 - -382 -402 0 - -382 -408 0 - -382 -419 0 - -382 -422 0 - -402 -408 0 - -402 -419 0 - -402 -422 0 - -408 -419 0 - -408 -422 0 - -419 -422 0 - -383 -403 0 - -383 -409 0 - -383 -413 0 - -403 -409 0 - -403 -413 0 - -409 -413 0 - -384 -420 0 - -387 -396 0 - -387 -414 0 - -396 -414 0 - -388 -391 0 - -388 -397 0 - -388 -404 0 - -388 -421 0 - -391 -397 0 - -391 -404 0 - -391 -421 0 - -397 -404 0 - -397 -421 0 - -404 -421 0 - -392 -400 0 - -392 -405 0 - -392 -415 0 - -392 -423 0 - -400 -405 0 - -400 -415 0 - -400 -423 0 - -405 -415 0 - -405 -423 0 - -415 -423 0 - -389 -393 0 - -389 -406 0 - -389 -410 0 - -389 -416 0 - -389 -424 0 - -393 -406 0 - -393 -410 0 - -393 -416 0 - -393 -424 0 - -406 -410 0 - -406 -416 0 - -406 -424 0 - -410 -416 0 - -410 -424 0 - -416 -424 0 - -385 -394 0 - -385 -407 0 - -385 -411 0 - -385 -417 0 - -394 -407 0 - -394 -411 0 - -394 -417 0 - -407 -411 0 - -407 -417 0 - -411 -417 0 - -395 -398 0 - -395 -412 0 - -398 -412 0 - -386 -390 0 - -386 -399 0 - -386 -401 0 - -386 -418 0 - -390 -399 0 - -390 -401 0 - -390 -418 0 - -399 -401 0 - -399 -418 0 - -401 -418 0 - -427 -436 0 - -431 -434 0 - -431 -437 0 - -431 -440 0 - -434 -437 0 - -434 -440 0 - -437 -440 0 - -428 -438 0 - -428 -441 0 - -438 -441 0 - -425 -429 0 - -425 -432 0 - -425 -439 0 - -429 -432 0 - -429 -439 0 - -432 -439 0 - -433 -442 0 - -426 -430 0 - -426 -435 0 - -430 -435 0 - -443 -447 0 - -444 -445 0 - -446 -448 0 - -452 -473 0 - -452 -483 0 - -452 -486 0 - -473 -483 0 - -473 -486 0 - -483 -486 0 - -453 -474 0 - -458 -475 0 - -459 -469 0 - -459 -476 0 - -469 -476 0 - -449 -454 0 - -449 -462 0 - -449 -466 0 - -449 -470 0 - -449 -480 0 - -449 -487 0 - -454 -462 0 - -454 -466 0 - -454 -470 0 - -454 -480 0 - -454 -487 0 - -462 -466 0 - -462 -470 0 - -462 -480 0 - -462 -487 0 - -466 -470 0 - -466 -480 0 - -466 -487 0 - -470 -480 0 - -470 -487 0 - -480 -487 0 - -450 -455 0 - -450 -463 0 - -450 -471 0 - -450 -477 0 - -450 -488 0 - -455 -463 0 - -455 -471 0 - -455 -477 0 - -455 -488 0 - -463 -471 0 - -463 -477 0 - -463 -488 0 - -471 -477 0 - -471 -488 0 - -477 -488 0 - -460 -467 0 - -460 -481 0 - -467 -481 0 - -464 -472 0 - -464 -478 0 - -464 -489 0 - -472 -478 0 - -472 -489 0 - -478 -489 0 - -456 -465 0 - -456 -479 0 - -456 -484 0 - -465 -479 0 - -465 -484 0 - -479 -484 0 - -451 -457 0 - -451 -461 0 - -451 -468 0 - -451 -482 0 - -451 -485 0 - -457 -461 0 - -457 -468 0 - -457 -482 0 - -457 -485 0 - -461 -468 0 - -461 -482 0 - -461 -485 0 - -468 -482 0 - -468 -485 0 - -482 -485 0 - -506 -514 0 - -506 -520 0 - -506 -534 0 - -506 -540 0 - -506 -545 0 - -506 -552 0 - -514 -520 0 - -514 -534 0 - -514 -540 0 - -514 -545 0 - -514 -552 0 - -520 -534 0 - -520 -540 0 - -520 -545 0 - -520 -552 0 - -534 -540 0 - -534 -545 0 - -534 -552 0 - -540 -545 0 - -540 -552 0 - -545 -552 0 - -497 -535 0 - -497 -541 0 - -497 -546 0 - -497 -558 0 - -535 -541 0 - -535 -546 0 - -535 -558 0 - -541 -546 0 - -541 -558 0 - -546 -558 0 - -515 -521 0 - -515 -542 0 - -521 -542 0 - -490 -516 0 - -490 -527 0 - -490 -543 0 - -490 -559 0 - -516 -527 0 - -516 -543 0 - -516 -559 0 - -527 -543 0 - -527 -559 0 - -543 -559 0 - -491 -498 0 - -491 -507 0 - -491 -547 0 - -491 -553 0 - -491 -560 0 - -498 -507 0 - -498 -547 0 - -498 -553 0 - -498 -560 0 - -507 -547 0 - -507 -553 0 - -507 -560 0 - -547 -553 0 - -547 -560 0 - -553 -560 0 - -492 -499 0 - -492 -508 0 - -492 -528 0 - -492 -554 0 - -492 -565 0 - -499 -508 0 - -499 -528 0 - -499 -554 0 - -499 -565 0 - -508 -528 0 - -508 -554 0 - -508 -565 0 - -528 -554 0 - -528 -565 0 - -554 -565 0 - -493 -500 0 - -493 -517 0 - -493 -522 0 - -493 -529 0 - -493 -536 0 - -493 -548 0 - -493 -566 0 - -500 -517 0 - -500 -522 0 - -500 -529 0 - -500 -536 0 - -500 -548 0 - -500 -566 0 - -517 -522 0 - -517 -529 0 - -517 -536 0 - -517 -548 0 - -517 -566 0 - -522 -529 0 - -522 -536 0 - -522 -548 0 - -522 -566 0 - -529 -536 0 - -529 -548 0 - -529 -566 0 - -536 -548 0 - -536 -566 0 - -548 -566 0 - -501 -509 0 - -501 -523 0 - -501 -530 0 - -501 -544 0 - -501 -555 0 - -509 -523 0 - -509 -530 0 - -509 -544 0 - -509 -555 0 - -523 -530 0 - -523 -544 0 - -523 -555 0 - -530 -544 0 - -530 -555 0 - -544 -555 0 - -502 -512 0 - -502 -518 0 - -502 -524 0 - -502 -549 0 - -502 -567 0 - -512 -518 0 - -512 -524 0 - -512 -549 0 - -512 -567 0 - -518 -524 0 - -518 -549 0 - -518 -567 0 - -524 -549 0 - -524 -567 0 - -549 -567 0 - -503 -525 0 - -503 -531 0 - -503 -537 0 - -503 -556 0 - -503 -561 0 - -503 -568 0 - -525 -531 0 - -525 -537 0 - -525 -556 0 - -525 -561 0 - -525 -568 0 - -531 -537 0 - -531 -556 0 - -531 -561 0 - -531 -568 0 - -537 -556 0 - -537 -561 0 - -537 -568 0 - -556 -561 0 - -556 -568 0 - -561 -568 0 - -494 -504 0 - -494 -510 0 - -494 -532 0 - -494 -538 0 - -494 -550 0 - -494 -562 0 - -504 -510 0 - -504 -532 0 - -504 -538 0 - -504 -550 0 - -504 -562 0 - -510 -532 0 - -510 -538 0 - -510 -550 0 - -510 -562 0 - -532 -538 0 - -532 -550 0 - -532 -562 0 - -538 -550 0 - -538 -562 0 - -550 -562 0 - -495 -505 0 - -495 -519 0 - -495 -526 0 - -495 -533 0 - -495 -539 0 - -495 -551 0 - -495 -563 0 - -505 -519 0 - -505 -526 0 - -505 -533 0 - -505 -539 0 - -505 -551 0 - -505 -563 0 - -519 -526 0 - -519 -533 0 - -519 -539 0 - -519 -551 0 - -519 -563 0 - -526 -533 0 - -526 -539 0 - -526 -551 0 - -526 -563 0 - -533 -539 0 - -533 -551 0 - -533 -563 0 - -539 -551 0 - -539 -563 0 - -551 -563 0 - -496 -511 0 - -496 -513 0 - -496 -557 0 - -496 -564 0 - -511 -513 0 - -511 -557 0 - -511 -564 0 - -513 -557 0 - -513 -564 0 - -557 -564 0 - -571 -582 0 - -571 -585 0 - -571 -589 0 - -582 -585 0 - -582 -589 0 - -585 -589 0 - -583 -586 0 - -583 -593 0 - -586 -593 0 - -572 -584 0 - -569 -575 0 - -569 -578 0 - -569 -590 0 - -575 -578 0 - -575 -590 0 - -578 -590 0 - -570 -573 0 - -570 -576 0 - -570 -579 0 - -570 -587 0 - -573 -576 0 - -573 -579 0 - -573 -587 0 - -576 -579 0 - -576 -587 0 - -579 -587 0 - -591 -594 0 - -574 -580 0 - -574 -588 0 - -580 -588 0 - -577 -581 0 - -577 -592 0 - -577 -595 0 - -581 -592 0 - -581 -595 0 - -592 -595 0 - -616 -621 0 - -616 -629 0 - -621 -629 0 - -596 -611 0 - -604 -612 0 - -604 -617 0 - -604 -630 0 - -604 -633 0 - -612 -617 0 - -612 -630 0 - -612 -633 0 - -617 -630 0 - -617 -633 0 - -630 -633 0 - -607 -626 0 - -597 -601 0 - -597 -608 0 - -597 -622 0 - -597 -627 0 - -601 -608 0 - -601 -622 0 - -601 -627 0 - -608 -622 0 - -608 -627 0 - -622 -627 0 - -598 -618 0 - -598 -631 0 - -618 -631 0 - -599 -619 0 - -599 -623 0 - -599 -634 0 - -619 -623 0 - -619 -634 0 - -623 -634 0 - -605 -609 0 - -605 -613 0 - -605 -624 0 - -605 -632 0 - -609 -613 0 - -609 -624 0 - -609 -632 0 - -613 -624 0 - -613 -632 0 - -624 -632 0 - -602 -614 0 - -602 -620 0 - -602 -625 0 - -614 -620 0 - -614 -625 0 - -620 -625 0 - -600 -603 0 - -600 -606 0 - -600 -610 0 - -600 -615 0 - -600 -628 0 - -600 -635 0 - -603 -606 0 - -603 -610 0 - -603 -615 0 - -603 -628 0 - -603 -635 0 - -606 -610 0 - -606 -615 0 - -606 -628 0 - -606 -635 0 - -610 -615 0 - -610 -628 0 - -610 -635 0 - -615 -628 0 - -615 -635 0 - -628 -635 0 - -651 -659 0 - -640 -664 0 - -641 -654 0 - -641 -665 0 - -654 -665 0 - -644 -648 0 - -644 -660 0 - -648 -660 0 - -636 -649 0 - -636 -652 0 - -636 -655 0 - -636 -661 0 - -636 -666 0 - -649 -652 0 - -649 -655 0 - -649 -661 0 - -649 -666 0 - -652 -655 0 - -652 -661 0 - -652 -666 0 - -655 -661 0 - -655 -666 0 - -661 -666 0 - -637 -653 0 - -637 -656 0 - -637 -662 0 - -637 -669 0 - -653 -656 0 - -653 -662 0 - -653 -669 0 - -656 -662 0 - -656 -669 0 - -662 -669 0 - -645 -657 0 - -645 -663 0 - -645 -667 0 - -657 -663 0 - -657 -667 0 - -663 -667 0 - -638 -642 0 - -638 -646 0 - -638 -650 0 - -642 -646 0 - -642 -650 0 - -646 -650 0 - -639 -643 0 - -639 -647 0 - -639 -658 0 - -639 -668 0 - -639 -670 0 - -643 -647 0 - -643 -658 0 - -643 -668 0 - -643 -670 0 - -647 -658 0 - -647 -668 0 - -647 -670 0 - -658 -668 0 - -658 -670 0 - -668 -670 0 - -671 -680 0 - -671 -685 0 - -671 -699 0 - -671 -722 0 - -680 -685 0 - -680 -699 0 - -680 -722 0 - -685 -699 0 - -685 -722 0 - -699 -722 0 - -686 -693 0 - -686 -717 0 - -686 -733 0 - -693 -717 0 - -693 -733 0 - -717 -733 0 - -672 -681 0 - -672 -687 0 - -672 -710 0 - -672 -718 0 - -672 -723 0 - -672 -740 0 - -681 -687 0 - -681 -710 0 - -681 -718 0 - -681 -723 0 - -681 -740 0 - -687 -710 0 - -687 -718 0 - -687 -723 0 - -687 -740 0 - -710 -718 0 - -710 -723 0 - -710 -740 0 - -718 -723 0 - -718 -740 0 - -723 -740 0 - -688 -706 0 - -688 -711 0 - -688 -719 0 - -706 -711 0 - -706 -719 0 - -711 -719 0 - -673 -720 0 - -673 -729 0 - -673 -734 0 - -673 -741 0 - -720 -729 0 - -720 -734 0 - -720 -741 0 - -729 -734 0 - -729 -741 0 - -734 -741 0 - -674 -694 0 - -674 -700 0 - -674 -724 0 - -674 -735 0 - -694 -700 0 - -694 -724 0 - -694 -735 0 - -700 -724 0 - -700 -735 0 - -724 -735 0 - -675 -682 0 - -675 -695 0 - -675 -701 0 - -682 -695 0 - -682 -701 0 - -695 -701 0 - -676 -707 0 - -676 -725 0 - -676 -730 0 - -676 -736 0 - -707 -725 0 - -707 -730 0 - -707 -736 0 - -725 -730 0 - -725 -736 0 - -730 -736 0 - -689 -702 0 - -689 -726 0 - -689 -742 0 - -702 -726 0 - -702 -742 0 - -726 -742 0 - -690 -696 0 - -690 -703 0 - -690 -708 0 - -690 -712 0 - -690 -721 0 - -690 -731 0 - -690 -743 0 - -696 -703 0 - -696 -708 0 - -696 -712 0 - -696 -721 0 - -696 -731 0 - -696 -743 0 - -703 -708 0 - -703 -712 0 - -703 -721 0 - -703 -731 0 - -703 -743 0 - -708 -712 0 - -708 -721 0 - -708 -731 0 - -708 -743 0 - -712 -721 0 - -712 -731 0 - -712 -743 0 - -721 -731 0 - -721 -743 0 - -731 -743 0 - -677 -691 0 - -677 -697 0 - -677 -704 0 - -677 -713 0 - -677 -715 0 - -677 -727 0 - -677 -732 0 - -677 -737 0 - -691 -697 0 - -691 -704 0 - -691 -713 0 - -691 -715 0 - -691 -727 0 - -691 -732 0 - -691 -737 0 - -697 -704 0 - -697 -713 0 - -697 -715 0 - -697 -727 0 - -697 -732 0 - -697 -737 0 - -704 -713 0 - -704 -715 0 - -704 -727 0 - -704 -732 0 - -704 -737 0 - -713 -715 0 - -713 -727 0 - -713 -732 0 - -713 -737 0 - -715 -727 0 - -715 -732 0 - -715 -737 0 - -727 -732 0 - -727 -737 0 - -732 -737 0 - -678 -683 0 - -678 -698 0 - -678 -709 0 - -678 -716 0 - -678 -728 0 - -678 -738 0 - -683 -698 0 - -683 -709 0 - -683 -716 0 - -683 -728 0 - -683 -738 0 - -698 -709 0 - -698 -716 0 - -698 -728 0 - -698 -738 0 - -709 -716 0 - -709 -728 0 - -709 -738 0 - -716 -728 0 - -716 -738 0 - -728 -738 0 - -679 -684 0 - -679 -692 0 - -679 -705 0 - -679 -714 0 - -679 -739 0 - -679 -744 0 - -684 -692 0 - -684 -705 0 - -684 -714 0 - -684 -739 0 - -684 -744 0 - -692 -705 0 - -692 -714 0 - -692 -739 0 - -692 -744 0 - -705 -714 0 - -705 -739 0 - -705 -744 0 - -714 -739 0 - -714 -744 0 - -739 -744 0 - -751 -758 0 - -751 -772 0 - -751 -777 0 - -751 -792 0 - -751 -800 0 - -751 -813 0 - -758 -772 0 - -758 -777 0 - -758 -792 0 - -758 -800 0 - -758 -813 0 - -772 -777 0 - -772 -792 0 - -772 -800 0 - -772 -813 0 - -777 -792 0 - -777 -800 0 - -777 -813 0 - -792 -800 0 - -792 -813 0 - -800 -813 0 - -745 -752 0 - -745 -773 0 - -745 -793 0 - -745 -801 0 - -745 -805 0 - -752 -773 0 - -752 -793 0 - -752 -801 0 - -752 -805 0 - -773 -793 0 - -773 -801 0 - -773 -805 0 - -793 -801 0 - -793 -805 0 - -801 -805 0 - -746 -774 0 - -746 -781 0 - -746 -786 0 - -746 -794 0 - -746 -806 0 - -774 -781 0 - -774 -786 0 - -774 -794 0 - -774 -806 0 - -781 -786 0 - -781 -794 0 - -781 -806 0 - -786 -794 0 - -786 -806 0 - -794 -806 0 - -753 -765 0 - -753 -775 0 - -753 -787 0 - -753 -802 0 - -753 -807 0 - -765 -775 0 - -765 -787 0 - -765 -802 0 - -765 -807 0 - -775 -787 0 - -775 -802 0 - -775 -807 0 - -787 -802 0 - -787 -807 0 - -802 -807 0 - -759 -778 0 - -759 -788 0 - -759 -803 0 - -759 -808 0 - -759 -814 0 - -778 -788 0 - -778 -803 0 - -778 -808 0 - -778 -814 0 - -788 -803 0 - -788 -808 0 - -788 -814 0 - -803 -808 0 - -803 -814 0 - -808 -814 0 - -747 -760 0 - -747 -766 0 - -747 -795 0 - -747 -809 0 - -747 -815 0 - -760 -766 0 - -760 -795 0 - -760 -809 0 - -760 -815 0 - -766 -795 0 - -766 -809 0 - -766 -815 0 - -795 -809 0 - -795 -815 0 - -809 -815 0 - -748 -761 0 - -748 -776 0 - -748 -779 0 - -748 -782 0 - -748 -789 0 - -748 -796 0 - -748 -810 0 - -761 -776 0 - -761 -779 0 - -761 -782 0 - -761 -789 0 - -761 -796 0 - -761 -810 0 - -776 -779 0 - -776 -782 0 - -776 -789 0 - -776 -796 0 - -776 -810 0 - -779 -782 0 - -779 -789 0 - -779 -796 0 - -779 -810 0 - -782 -789 0 - -782 -796 0 - -782 -810 0 - -789 -796 0 - -789 -810 0 - -796 -810 0 - -754 -767 0 - -754 -783 0 - -754 -797 0 - -754 -811 0 - -767 -783 0 - -767 -797 0 - -767 -811 0 - -783 -797 0 - -783 -811 0 - -797 -811 0 - -755 -762 0 - -755 -768 0 - -755 -784 0 - -755 -804 0 - -762 -768 0 - -762 -784 0 - -762 -804 0 - -768 -784 0 - -768 -804 0 - -784 -804 0 - -749 -756 0 - -749 -763 0 - -749 -769 0 - -749 -785 0 - -749 -790 0 - -749 -798 0 - -749 -812 0 - -749 -816 0 - -756 -763 0 - -756 -769 0 - -756 -785 0 - -756 -790 0 - -756 -798 0 - -756 -812 0 - -756 -816 0 - -763 -769 0 - -763 -785 0 - -763 -790 0 - -763 -798 0 - -763 -812 0 - -763 -816 0 - -769 -785 0 - -769 -790 0 - -769 -798 0 - -769 -812 0 - -769 -816 0 - -785 -790 0 - -785 -798 0 - -785 -812 0 - -785 -816 0 - -790 -798 0 - -790 -812 0 - -790 -816 0 - -798 -812 0 - -798 -816 0 - -812 -816 0 - -764 -770 0 - -764 -780 0 - -764 -791 0 - -770 -780 0 - -770 -791 0 - -780 -791 0 - -750 -757 0 - -750 -771 0 - -750 -799 0 - -750 -817 0 - -757 -771 0 - -757 -799 0 - -757 -817 0 - -771 -799 0 - -771 -817 0 - -799 -817 0 - -822 -837 0 - -822 -847 0 - -822 -851 0 - -822 -855 0 - -837 -847 0 - -837 -851 0 - -837 -855 0 - -847 -851 0 - -847 -855 0 - -851 -855 0 - -818 -828 0 - -818 -832 0 - -818 -842 0 - -818 -852 0 - -828 -832 0 - -828 -842 0 - -828 -852 0 - -832 -842 0 - -832 -852 0 - -842 -852 0 - -829 -848 0 - -830 -833 0 - -830 -856 0 - -833 -856 0 - -823 -834 0 - -823 -838 0 - -823 -857 0 - -834 -838 0 - -834 -857 0 - -838 -857 0 - -819 -824 0 - -819 -835 0 - -819 -839 0 - -819 -843 0 - -819 -853 0 - -824 -835 0 - -824 -839 0 - -824 -843 0 - -824 -853 0 - -835 -839 0 - -835 -843 0 - -835 -853 0 - -839 -843 0 - -839 -853 0 - -843 -853 0 - -820 -825 0 - -820 -836 0 - -820 -840 0 - -820 -844 0 - -820 -849 0 - -820 -854 0 - -820 -858 0 - -825 -836 0 - -825 -840 0 - -825 -844 0 - -825 -849 0 - -825 -854 0 - -825 -858 0 - -836 -840 0 - -836 -844 0 - -836 -849 0 - -836 -854 0 - -836 -858 0 - -840 -844 0 - -840 -849 0 - -840 -854 0 - -840 -858 0 - -844 -849 0 - -844 -854 0 - -844 -858 0 - -849 -854 0 - -849 -858 0 - -854 -858 0 - -826 -841 0 - -826 -845 0 - -826 -850 0 - -841 -845 0 - -841 -850 0 - -845 -850 0 - -821 -827 0 - -821 -831 0 - -821 -846 0 - -821 -859 0 - -827 -831 0 - -827 -846 0 - -827 -859 0 - -831 -846 0 - -831 -859 0 - -846 -859 0 - -860 -881 0 - -860 -901 0 - -860 -905 0 - -860 -911 0 - -860 -917 0 - -860 -922 0 - -881 -901 0 - -881 -905 0 - -881 -911 0 - -881 -917 0 - -881 -922 0 - -901 -905 0 - -901 -911 0 - -901 -917 0 - -901 -922 0 - -905 -911 0 - -905 -917 0 - -905 -922 0 - -911 -917 0 - -911 -922 0 - -917 -922 0 - -861 -867 0 - -861 -878 0 - -861 -891 0 - -861 -912 0 - -867 -878 0 - -867 -891 0 - -867 -912 0 - -878 -891 0 - -878 -912 0 - -891 -912 0 - -868 -873 0 - -868 -892 0 - -868 -906 0 - -868 -918 0 - -873 -892 0 - -873 -906 0 - -873 -918 0 - -892 -906 0 - -892 -918 0 - -906 -918 0 - -862 -869 0 - -862 -882 0 - -862 -893 0 - -862 -899 0 - -862 -907 0 - -862 -913 0 - -862 -923 0 - -869 -882 0 - -869 -893 0 - -869 -899 0 - -869 -907 0 - -869 -913 0 - -869 -923 0 - -882 -893 0 - -882 -899 0 - -882 -907 0 - -882 -913 0 - -882 -923 0 - -893 -899 0 - -893 -907 0 - -893 -913 0 - -893 -923 0 - -899 -907 0 - -899 -913 0 - -899 -923 0 - -907 -913 0 - -907 -923 0 - -913 -923 0 - -863 -883 0 - -863 -894 0 - -863 -902 0 - -863 -914 0 - -883 -894 0 - -883 -902 0 - -883 -914 0 - -894 -902 0 - -894 -914 0 - -902 -914 0 - -870 -884 0 - -870 -895 0 - -870 -900 0 - -870 -908 0 - -884 -895 0 - -884 -900 0 - -884 -908 0 - -895 -900 0 - -895 -908 0 - -900 -908 0 - -871 -888 0 - -871 -896 0 - -871 -909 0 - -871 -924 0 - -888 -896 0 - -888 -909 0 - -888 -924 0 - -896 -909 0 - -896 -924 0 - -909 -924 0 - -864 -885 0 - -864 -889 0 - -864 -897 0 - -864 -910 0 - -864 -919 0 - -864 -925 0 - -885 -889 0 - -885 -897 0 - -885 -910 0 - -885 -919 0 - -885 -925 0 - -889 -897 0 - -889 -910 0 - -889 -919 0 - -889 -925 0 - -897 -910 0 - -897 -919 0 - -897 -925 0 - -910 -919 0 - -910 -925 0 - -919 -925 0 - -865 -874 0 - -865 -879 0 - -865 -886 0 - -874 -879 0 - -874 -886 0 - -879 -886 0 - -866 -887 0 - -866 -915 0 - -866 -920 0 - -887 -915 0 - -887 -920 0 - -915 -920 0 - -875 -898 0 - -875 -916 0 - -875 -926 0 - -898 -916 0 - -898 -926 0 - -916 -926 0 - -872 -876 0 - -872 -903 0 - -872 -921 0 - -872 -927 0 - -876 -903 0 - -876 -921 0 - -876 -927 0 - -903 -921 0 - -903 -927 0 - -921 -927 0 - -877 -880 0 - -877 -890 0 - -877 -904 0 - -877 -928 0 - -880 -890 0 - -880 -904 0 - -880 -928 0 - -890 -904 0 - -890 -928 0 - -904 -928 0 - -929 -954 0 - -929 -965 0 - -929 -972 0 - -929 -978 0 - -929 -983 0 - -929 -995 0 - -954 -965 0 - -954 -972 0 - -954 -978 0 - -954 -983 0 - -954 -995 0 - -965 -972 0 - -965 -978 0 - -965 -983 0 - -965 -995 0 - -972 -978 0 - -972 -983 0 - -972 -995 0 - -978 -983 0 - -978 -995 0 - -983 -995 0 - -930 -943 0 - -930 -955 0 - -930 -959 0 - -930 -979 0 - -930 -984 0 - -943 -955 0 - -943 -959 0 - -943 -979 0 - -943 -984 0 - -955 -959 0 - -955 -979 0 - -955 -984 0 - -959 -979 0 - -959 -984 0 - -979 -984 0 - -937 -949 0 - -937 -980 0 - -937 -985 0 - -937 -989 0 - -949 -980 0 - -949 -985 0 - -949 -989 0 - -980 -985 0 - -980 -989 0 - -985 -989 0 - -950 -956 0 - -950 -960 0 - -950 -981 0 - -950 -996 0 - -956 -960 0 - -956 -981 0 - -956 -996 0 - -960 -981 0 - -960 -996 0 - -981 -996 0 - -951 -957 0 - -951 -968 0 - -951 -982 0 - -951 -990 0 - -951 -997 0 - -957 -968 0 - -957 -982 0 - -957 -990 0 - -957 -997 0 - -968 -982 0 - -968 -990 0 - -968 -997 0 - -982 -990 0 - -982 -997 0 - -990 -997 0 - -931 -938 0 - -931 -944 0 - -931 -969 0 - -931 -973 0 - -938 -944 0 - -938 -969 0 - -938 -973 0 - -944 -969 0 - -944 -973 0 - -969 -973 0 - -961 -986 0 - -961 -991 0 - -986 -991 0 - -945 -970 0 - -945 -974 0 - -945 -987 0 - -945 -998 0 - -970 -974 0 - -970 -987 0 - -970 -998 0 - -974 -987 0 - -974 -998 0 - -987 -998 0 - -932 -939 0 - -932 -946 0 - -932 -962 0 - -932 -966 0 - -932 -992 0 - -939 -946 0 - -939 -962 0 - -939 -966 0 - -939 -992 0 - -946 -962 0 - -946 -966 0 - -946 -992 0 - -962 -966 0 - -962 -992 0 - -966 -992 0 - -933 -940 0 - -933 -958 0 - -933 -963 0 - -933 -975 0 - -933 -988 0 - -933 -999 0 - -940 -958 0 - -940 -963 0 - -940 -975 0 - -940 -988 0 - -940 -999 0 - -958 -963 0 - -958 -975 0 - -958 -988 0 - -958 -999 0 - -963 -975 0 - -963 -988 0 - -963 -999 0 - -975 -988 0 - -975 -999 0 - -988 -999 0 - -934 -941 0 - -934 -964 0 - -934 -971 0 - -934 -976 0 - -934 -993 0 - -934 -1000 0 - -941 -964 0 - -941 -971 0 - -941 -976 0 - -941 -993 0 - -941 -1000 0 - -964 -971 0 - -964 -976 0 - -964 -993 0 - -964 -1000 0 - -971 -976 0 - -971 -993 0 - -971 -1000 0 - -976 -993 0 - -976 -1000 0 - -993 -1000 0 - -935 -942 0 - -935 -947 0 - -935 -952 0 - -935 -1001 0 - -942 -947 0 - -942 -952 0 - -942 -1001 0 - -947 -952 0 - -947 -1001 0 - -952 -1001 0 - -936 -948 0 - -936 -953 0 - -936 -967 0 - -936 -977 0 - -936 -994 0 - -936 -1002 0 - -948 -953 0 - -948 -967 0 - -948 -977 0 - -948 -994 0 - -948 -1002 0 - -953 -967 0 - -953 -977 0 - -953 -994 0 - -953 -1002 0 - -967 -977 0 - -967 -994 0 - -967 -1002 0 - -977 -994 0 - -977 -1002 0 - -994 -1002 0 - -1006 -1025 0 - -1006 -1039 0 - -1006 -1053 0 - -1006 -1062 0 - -1025 -1039 0 - -1025 -1053 0 - -1025 -1062 0 - -1039 -1053 0 - -1039 -1062 0 - -1053 -1062 0 - -1007 -1014 0 - -1007 -1020 0 - -1007 -1034 0 - -1007 -1045 0 - -1014 -1020 0 - -1014 -1034 0 - -1014 -1045 0 - -1020 -1034 0 - -1020 -1045 0 - -1034 -1045 0 - -1008 -1015 0 - -1008 -1026 0 - -1008 -1035 0 - -1008 -1046 0 - -1008 -1054 0 - -1008 -1063 0 - -1015 -1026 0 - -1015 -1035 0 - -1015 -1046 0 - -1015 -1054 0 - -1015 -1063 0 - -1026 -1035 0 - -1026 -1046 0 - -1026 -1054 0 - -1026 -1063 0 - -1035 -1046 0 - -1035 -1054 0 - -1035 -1063 0 - -1046 -1054 0 - -1046 -1063 0 - -1054 -1063 0 - -1009 -1027 0 - -1009 -1036 0 - -1009 -1040 0 - -1009 -1047 0 - -1009 -1057 0 - -1027 -1036 0 - -1027 -1040 0 - -1027 -1047 0 - -1027 -1057 0 - -1036 -1040 0 - -1036 -1047 0 - -1036 -1057 0 - -1040 -1047 0 - -1040 -1057 0 - -1047 -1057 0 - -1028 -1037 0 - -1028 -1048 0 - -1028 -1055 0 - -1037 -1048 0 - -1037 -1055 0 - -1048 -1055 0 - -1003 -1010 0 - -1003 -1029 0 - -1003 -1058 0 - -1010 -1029 0 - -1010 -1058 0 - -1029 -1058 0 - -1004 -1021 0 - -1004 -1056 0 - -1004 -1059 0 - -1004 -1064 0 - -1021 -1056 0 - -1021 -1059 0 - -1021 -1064 0 - -1056 -1059 0 - -1056 -1064 0 - -1059 -1064 0 - -1016 -1022 0 - -1016 -1041 0 - -1022 -1041 0 - -1017 -1042 0 - -1017 -1049 0 - -1017 -1060 0 - -1017 -1065 0 - -1042 -1049 0 - -1042 -1060 0 - -1042 -1065 0 - -1049 -1060 0 - -1049 -1065 0 - -1060 -1065 0 - -1011 -1023 0 - -1011 -1030 0 - -1011 -1031 0 - -1011 -1043 0 - -1011 -1050 0 - -1011 -1061 0 - -1023 -1030 0 - -1023 -1031 0 - -1023 -1043 0 - -1023 -1050 0 - -1023 -1061 0 - -1030 -1031 0 - -1030 -1043 0 - -1030 -1050 0 - -1030 -1061 0 - -1031 -1043 0 - -1031 -1050 0 - -1031 -1061 0 - -1043 -1050 0 - -1043 -1061 0 - -1050 -1061 0 - -1012 -1018 0 - -1012 -1032 0 - -1012 -1038 0 - -1012 -1044 0 - -1012 -1051 0 - -1018 -1032 0 - -1018 -1038 0 - -1018 -1044 0 - -1018 -1051 0 - -1032 -1038 0 - -1032 -1044 0 - -1032 -1051 0 - -1038 -1044 0 - -1038 -1051 0 - -1044 -1051 0 - -1005 -1013 0 - -1005 -1019 0 - -1005 -1024 0 - -1005 -1033 0 - -1005 -1052 0 - -1005 -1066 0 - -1013 -1019 0 - -1013 -1024 0 - -1013 -1033 0 - -1013 -1052 0 - -1013 -1066 0 - -1019 -1024 0 - -1019 -1033 0 - -1019 -1052 0 - -1019 -1066 0 - -1024 -1033 0 - -1024 -1052 0 - -1024 -1066 0 - -1033 -1052 0 - -1033 -1066 0 - -1052 -1066 0 - 1 2 3 4 0 - 5 6 7 8 9 10 11 0 - 12 13 14 15 0 - 16 17 18 0 - 19 20 21 22 23 0 - 24 25 26 27 28 29 0 - 30 31 32 0 - 33 34 35 36 37 38 39 0 - 40 41 42 43 44 0 - 45 46 47 48 49 0 - 50 51 52 53 54 55 0 - 56 57 58 59 60 61 62 0 - 63 64 65 66 67 0 - 68 69 70 71 72 73 74 0 - 75 76 77 78 0 - 79 80 81 82 83 84 0 - 85 86 87 88 89 90 91 0 - 92 93 94 95 96 97 98 0 - 99 100 101 102 103 0 - 104 105 106 107 108 0 - 109 110 111 112 113 0 - 114 115 116 117 0 - 118 119 120 0 - 121 122 0 - 123 124 125 126 0 - 127 128 129 130 131 132 0 - 133 134 135 136 137 138 139 0 - 140 141 142 0 - 143 144 145 0 - 146 147 148 149 150 0 - 151 152 153 154 155 0 - 156 157 158 159 0 - 160 161 162 163 164 165 0 - 166 167 168 0 - 169 170 0 - 171 172 173 174 0 - 175 176 177 178 179 180 0 - 181 182 183 184 185 0 - 186 187 188 189 0 - 190 191 192 0 - 193 194 0 - 195 196 197 0 - 198 199 0 - 200 201 0 - 202 203 204 205 206 0 - 207 208 209 210 211 212 0 - 213 214 215 216 217 0 - 218 219 220 0 - 221 222 223 0 - 224 225 226 227 0 - 228 229 230 231 232 233 234 0 - 235 236 237 0 - 238 239 240 241 0 - 242 243 244 245 0 - 246 247 0 - 248 249 250 0 - 251 252 253 0 - 254 255 0 - 256 257 258 259 260 0 - 261 262 263 264 0 - 265 266 267 268 269 0 - 270 271 272 273 274 275 0 - 276 277 278 279 280 281 282 0 - 283 284 285 286 287 288 289 0 - 290 291 292 293 294 0 - 295 296 0 - 297 298 299 300 301 0 - 302 303 304 0 - 305 306 307 308 0 - 309 310 311 312 313 314 0 - 315 316 317 318 319 320 321 322 323 0 - 324 325 326 327 0 - 328 329 330 331 332 0 - 333 334 335 336 337 338 339 340 0 - 341 342 343 344 0 - 345 346 347 348 349 350 0 - 351 352 353 354 355 356 357 0 - 358 359 360 361 362 363 0 - 364 365 366 367 368 369 0 - 370 371 372 373 0 - 374 375 376 377 378 379 380 381 0 - 382 383 384 385 386 0 - 387 388 389 390 0 - 391 392 393 394 395 0 - 396 397 398 399 0 - 400 401 0 - 402 403 404 405 406 407 0 - 408 409 410 411 412 0 - 413 414 415 416 417 418 0 - 419 420 421 0 - 422 423 424 0 - 425 426 0 - 427 428 429 430 0 - 431 432 433 0 - 434 435 0 - 436 437 438 439 0 - 440 441 442 0 - 443 444 0 - 445 446 0 - 447 448 0 - 449 450 451 0 - 452 453 454 455 456 457 0 - 458 459 460 461 0 - 462 463 464 465 0 - 466 467 468 0 - 469 470 471 472 0 - 473 474 475 476 477 478 479 0 - 480 481 482 0 - 483 484 485 0 - 486 487 488 489 0 - 490 491 492 493 494 495 496 0 - 497 498 499 500 501 502 503 504 505 0 - 506 507 508 509 510 511 0 - 512 513 0 - 514 515 516 517 518 519 0 - 520 521 522 523 524 525 526 0 - 527 528 529 530 531 532 533 0 - 534 535 536 537 538 539 0 - 540 541 542 543 544 0 - 545 546 547 548 549 550 551 0 - 552 553 554 555 556 557 0 - 558 559 560 561 562 563 564 0 - 565 566 567 568 0 - 569 570 0 - 571 572 573 574 0 - 575 576 577 0 - 578 579 580 581 0 - 582 583 584 0 - 585 586 587 588 0 - 589 590 591 592 0 - 593 594 595 0 - 596 597 598 599 600 0 - 601 602 603 0 - 604 605 606 0 - 607 608 609 610 0 - 611 612 613 614 615 0 - 616 617 618 619 620 0 - 621 622 623 624 625 0 - 626 627 628 0 - 629 630 631 632 0 - 633 634 635 0 - 636 637 638 639 0 - 640 641 642 643 0 - 644 645 646 647 0 - 648 649 650 0 - 651 652 653 0 - 654 655 656 657 658 0 - 659 660 661 662 663 0 - 664 665 666 667 668 0 - 669 670 0 - 671 672 673 674 675 676 677 678 679 0 - 680 681 682 683 684 0 - 685 686 687 688 689 690 691 692 0 - 693 694 695 696 697 698 0 - 699 700 701 702 703 704 705 0 - 706 707 708 709 0 - 710 711 712 713 714 0 - 715 716 0 - 717 718 719 720 721 0 - 722 723 724 725 726 727 728 0 - 729 730 731 732 0 - 733 734 735 736 737 738 739 0 - 740 741 742 743 744 0 - 745 746 747 748 749 750 0 - 751 752 753 754 755 756 757 0 - 758 759 760 761 762 763 764 0 - 765 766 767 768 769 770 771 0 - 772 773 774 775 776 0 - 777 778 779 780 0 - 781 782 783 784 785 0 - 786 787 788 789 790 791 0 - 792 793 794 795 796 797 798 799 0 - 800 801 802 803 804 0 - 805 806 807 808 809 810 811 812 0 - 813 814 815 816 817 0 - 818 819 820 821 0 - 822 823 824 825 826 827 0 - 828 829 830 831 0 - 832 833 834 835 836 0 - 837 838 839 840 841 0 - 842 843 844 845 846 0 - 847 848 849 850 0 - 851 852 853 854 0 - 855 856 857 858 859 0 - 860 861 862 863 864 865 866 0 - 867 868 869 870 871 872 0 - 873 874 875 876 877 0 - 878 879 880 0 - 881 882 883 884 885 886 887 0 - 888 889 890 0 - 891 892 893 894 895 896 897 898 0 - 899 900 0 - 901 902 903 904 0 - 905 906 907 908 909 910 0 - 911 912 913 914 915 916 0 - 917 918 919 920 921 0 - 922 923 924 925 926 927 928 0 - 929 930 931 932 933 934 935 936 0 - 937 938 939 940 941 942 0 - 943 944 945 946 947 948 0 - 949 950 951 952 953 0 - 954 955 956 957 958 0 - 959 960 961 962 963 964 0 - 965 966 967 0 - 968 969 970 971 0 - 972 973 974 975 976 977 0 - 978 979 980 981 982 0 - 983 984 985 986 987 988 0 - 989 990 991 992 993 994 0 - 995 996 997 998 999 1000 1001 1002 0 - 1003 1004 1005 0 - 1006 1007 1008 1009 1010 1011 1012 1013 0 - 1014 1015 1016 1017 1018 1019 0 - 1020 1021 1022 1023 1024 0 - 1025 1026 1027 1028 1029 1030 0 - 1031 1032 1033 0 - 1034 1035 1036 1037 1038 0 - 1039 1040 1041 1042 1043 1044 0 - 1045 1046 1047 1048 1049 1050 1051 1052 0 - 1053 1054 1055 1056 0 - 1057 1058 1059 1060 1061 0 - 1062 1063 1064 1065 1066 0 - 45 302 818 0 - 46 202 596 0 - 449 597 819 0 - 203 598 1003 0 - 47 204 450 599 636 820 0 - 48 205 303 637 1004 0 - 49 206 304 638 0 - 451 600 639 821 1005 0 - 256 305 382 452 822 860 1006 0 - 50 306 671 861 1007 0 - 51 672 745 862 1008 0 - 52 257 383 453 746 863 1009 0 - 258 307 384 490 673 823 864 0 - 491 674 747 0 - 114 454 492 675 824 865 0 - 676 866 1010 0 - 53 115 259 455 493 748 825 0 - 54 116 260 308 494 677 749 1011 0 - 55 385 456 495 678 826 1012 0 - 117 386 457 496 679 750 827 1013 0 - 207 309 640 680 1014 0 - 156 208 641 681 929 1015 0 - 209 310 930 0 - 246 425 601 682 931 0 - 210 247 311 426 932 1016 0 - 157 312 933 0 - 158 313 934 1017 0 - 159 211 602 683 1018 0 - 212 314 642 935 0 - 603 643 684 936 1019 0 - 56 315 685 867 0 - 1 57 261 316 686 751 828 868 0 - 58 687 752 869 0 - 317 753 829 0 - 59 262 318 458 688 870 0 - 319 459 604 830 871 0 - 320 387 460 689 754 0 - 60 321 388 690 755 0 - 2 61 263 322 389 872 0 - 3 62 264 323 605 691 756 0 - 4 390 461 606 692 757 831 0 - 5 265 427 693 758 873 0 - 6 497 759 937 0 - 160 428 498 694 760 0 - 248 429 462 499 695 874 938 0 - 7 249 266 463 500 761 0 - 161 391 501 696 762 0 - 250 430 939 0 - 162 267 392 464 502 875 940 0 - 8 163 268 393 503 876 941 0 - 9 164 269 504 697 763 0 - 10 165 394 465 505 698 0 - 11 395 764 877 942 0 - 699 878 1020 0 - 506 765 943 0 - 190 507 607 700 766 0 - 466 508 608 701 879 944 0 - 191 396 467 702 767 945 0 - 397 509 703 768 1021 0 - 946 1022 0 - 192 510 609 704 769 1023 0 - 398 770 880 947 0 - 399 468 511 610 705 771 948 1024 0 - 270 443 881 1025 0 - 63 444 882 1026 0 - 64 271 431 883 1027 0 - 65 272 884 1028 0 - 273 885 0 - 118 251 432 569 886 0 - 252 433 887 1029 0 - 66 119 253 274 570 0 - 67 120 275 1030 0 - 121 400 512 0 - 122 401 513 0 - 611 949 0 - 324 612 888 950 0 - 166 325 644 889 951 0 - 167 326 613 645 1031 0 - 168 614 1032 0 - 327 646 890 952 0 - 615 647 953 1033 0 - 68 213 891 1034 0 - 69 123 772 892 0 - 70 214 445 773 893 954 1035 0 - 71 774 894 1036 0 - 124 215 446 514 571 775 955 0 - 72 193 895 1037 0 - 515 572 896 956 0 - 516 897 957 0 - 73 125 194 216 517 573 776 0 - 126 518 574 898 958 0 - 74 217 519 1038 0 - 75 328 777 832 0 - 76 218 778 0 - 329 469 833 0 - 169 330 648 834 0 - 470 835 0 - 77 219 471 649 779 836 0 - 170 331 472 0 - 78 220 332 650 780 0 - 12 333 402 473 651 0 - 79 403 474 616 0 - 127 334 520 959 0 - 80 335 475 706 0 - 336 476 521 617 960 0 - 618 707 961 0 - 13 81 128 477 522 619 652 0 - 82 129 171 337 404 523 653 708 0 - 130 338 962 0 - 131 172 339 405 478 524 963 0 - 14 83 132 173 340 406 525 964 0 - 15 84 174 407 479 526 620 709 0 - 654 710 899 965 0 - 195 341 711 900 0 - 16 196 655 0 - 342 656 712 0 - 343 966 0 - 17 197 344 657 713 0 - 18 658 714 967 0 - 19 276 659 837 0 - 85 277 621 781 0 - 86 278 0 - 279 527 660 838 968 0 - 528 575 622 839 969 0 - 20 87 221 280 529 576 623 661 782 840 0 - 783 970 0 - 88 222 530 662 784 0 - 21 89 281 531 577 971 0 - 22 90 282 532 624 663 785 0 - 23 91 223 533 625 841 0 - 345 408 447 901 1039 0 - 409 434 786 902 1040 0 - 346 448 534 787 0 - 535 788 0 - 198 536 789 0 - 347 435 1041 0 - 348 410 537 903 1042 0 - 199 349 538 715 790 1043 0 - 411 539 716 1044 0 - 350 412 791 904 0 - 224 351 664 1045 0 - 24 133 283 352 436 792 842 0 - 175 225 665 793 972 1046 0 - 284 413 437 794 1047 0 - 285 353 1048 0 - 176 438 795 0 - 134 439 578 843 973 0 - 25 135 226 286 579 666 796 844 0 - 354 414 797 974 0 - 136 177 287 355 415 580 975 0 - 26 137 178 288 356 416 581 976 1049 0 - 27 138 179 289 357 667 798 1050 0 - 28 180 227 417 845 1051 0 - 29 139 418 668 799 846 977 1052 0 - 419 905 1053 0 - 140 717 800 906 0 - 718 801 907 978 1054 0 - 141 540 582 802 979 0 - 719 908 1055 0 - 541 583 803 980 0 - 542 584 909 981 0 - 420 543 720 910 982 0 - 142 421 544 721 804 1056 0 - 290 847 911 0 - 92 228 722 912 0 - 93 181 229 723 805 913 983 0 - 94 291 440 806 914 0 - 230 545 585 807 848 984 0 - 95 231 546 586 808 985 0 - 182 441 547 724 809 0 - 232 254 442 725 915 986 0 - 96 233 255 292 548 587 810 849 0 - 726 811 987 0 - 183 293 549 588 916 988 0 - 97 184 294 550 727 812 0 - 98 185 234 551 728 850 0 - 295 358 0 - 235 359 552 589 0 - 200 553 626 0 - 480 554 590 627 0 - 201 360 481 0 - 236 361 555 669 0 - 237 362 591 0 - 296 363 556 592 0 - 482 557 628 670 0 - 99 629 1057 0 - 364 630 0 - 186 365 729 0 - 631 730 1058 0 - 100 143 187 366 731 1059 0 - 30 101 144 188 367 1060 0 - 31 102 145 189 368 632 732 1061 0 - 32 103 369 0 - 33 483 917 0 - 34 104 146 733 813 918 0 - 35 105 238 558 593 814 989 0 - 559 734 919 990 0 - 560 735 815 0 - 239 736 920 991 0 - 147 240 594 992 0 - 36 106 148 561 595 921 993 0 - 37 107 149 562 737 816 0 - 38 108 241 484 563 738 0 - 39 150 485 564 739 817 994 0 - 297 370 422 486 851 0 - 298 371 852 0 - 487 565 853 0 - 299 488 566 854 0 - 300 372 423 489 567 0 - 301 373 424 568 0 - 40 374 855 922 1062 0 - 109 242 740 923 995 1063 0 - 375 633 856 924 996 0 - 376 741 857 925 997 0 - 41 110 151 243 634 858 0 - 377 742 998 0 - 111 152 244 378 743 1064 0 - 153 379 926 999 0 - 42 112 154 380 927 1000 1065 0 - 43 113 245 381 928 1001 0 - 44 155 635 744 859 1002 1066 0 - 12 19 33 40 0 - 1 5 24 34 0 - 6 35 0 - 7 13 16 20 25 41 0 - 2 8 14 21 26 30 36 42 0 - 3 9 17 22 27 31 37 0 - 10 15 23 28 38 0 - 11 32 43 0 - 4 18 29 39 44 0 - 50 56 68 92 0 - 45 57 69 75 104 0 - 51 58 63 70 93 109 0 - 52 64 71 79 85 94 99 0 - 59 65 72 80 86 0 - 46 76 95 105 0 - 47 53 66 73 77 81 87 96 110 0 - 48 60 82 88 100 111 0 - 61 83 89 101 106 112 0 - 54 62 67 90 97 102 107 0 - 55 74 84 91 98 108 0 - 49 78 103 113 0 - 123 133 140 146 0 - 124 127 141 0 - 114 118 134 0 - 115 119 125 128 135 151 0 - 129 142 143 152 0 - 130 147 0 - 121 126 131 136 153 0 - 132 137 144 148 154 0 - 116 120 138 145 149 0 - 117 122 139 150 155 0 - 156 175 181 0 - 166 169 186 0 - 160 176 182 0 - 161 171 187 0 - 157 162 170 172 177 183 0 - 158 163 173 178 188 0 - 164 167 179 184 189 0 - 159 165 168 174 180 185 0 - 193 195 0 - 190 200 0 - 194 196 198 0 - 191 201 0 - 192 197 199 0 - 207 213 224 228 0 - 208 214 225 229 242 0 - 209 215 230 235 0 - 202 218 231 238 0 - 203 232 239 0 - 204 216 219 221 226 233 243 0 - 205 222 236 244 0 - 210 237 240 0 - 211 217 223 227 234 241 0 - 206 212 220 245 0 - 246 248 251 0 - 252 254 0 - 249 253 255 0 - 247 250 0 - 256 270 276 290 297 0 - 261 265 283 295 298 0 - 257 271 277 284 291 0 - 262 272 278 285 0 - 258 273 279 0 - 259 266 274 280 286 292 299 0 - 267 287 293 300 0 - 263 268 281 288 296 301 0 - 260 264 269 275 282 289 294 0 - 305 333 345 370 374 0 - 306 309 315 351 0 - 302 316 328 352 358 371 0 - 310 317 334 346 359 0 - 318 335 341 353 0 - 319 324 329 336 364 375 0 - 307 325 330 365 376 0 - 320 354 360 377 0 - 303 321 337 342 361 366 378 0 - 311 338 343 347 362 0 - 312 331 339 355 372 379 0 - 313 322 340 348 356 363 367 373 380 0 - 308 323 326 344 349 357 368 0 - 304 314 327 332 350 369 381 0 - 382 402 408 419 422 0 - 383 403 409 413 0 - 384 420 0 - 387 396 414 0 - 388 391 397 404 421 0 - 392 400 405 415 423 0 - 389 393 406 410 416 424 0 - 385 394 407 411 417 0 - 395 398 412 0 - 386 390 399 401 418 0 - 427 436 0 - 431 434 437 440 0 - 428 438 441 0 - 425 429 432 439 0 - 433 442 0 - 426 430 435 0 - 443 447 0 - 444 445 0 - 446 448 0 - 452 473 483 486 0 - 453 474 0 - 458 475 0 - 459 469 476 0 - 449 454 462 466 470 480 487 0 - 450 455 463 471 477 488 0 - 460 467 481 0 - 464 472 478 489 0 - 456 465 479 484 0 - 451 457 461 468 482 485 0 - 506 514 520 534 540 545 552 0 - 497 535 541 546 558 0 - 515 521 542 0 - 490 516 527 543 559 0 - 491 498 507 547 553 560 0 - 492 499 508 528 554 565 0 - 493 500 517 522 529 536 548 566 0 - 501 509 523 530 544 555 0 - 502 512 518 524 549 567 0 - 503 525 531 537 556 561 568 0 - 494 504 510 532 538 550 562 0 - 495 505 519 526 533 539 551 563 0 - 496 511 513 557 564 0 - 571 582 585 589 0 - 583 586 593 0 - 572 584 0 - 569 575 578 590 0 - 570 573 576 579 587 0 - 591 594 0 - 574 580 588 0 - 577 581 592 595 0 - 616 621 629 0 - 596 611 0 - 604 612 617 630 633 0 - 607 626 0 - 597 601 608 622 627 0 - 598 618 631 0 - 599 619 623 634 0 - 605 609 613 624 632 0 - 602 614 620 625 0 - 600 603 606 610 615 628 635 0 - 651 659 0 - 640 664 0 - 641 654 665 0 - 644 648 660 0 - 636 649 652 655 661 666 0 - 637 653 656 662 669 0 - 645 657 663 667 0 - 638 642 646 650 0 - 639 643 647 658 668 670 0 - 671 680 685 699 722 0 - 686 693 717 733 0 - 672 681 687 710 718 723 740 0 - 688 706 711 719 0 - 673 720 729 734 741 0 - 674 694 700 724 735 0 - 675 682 695 701 0 - 676 707 725 730 736 0 - 689 702 726 742 0 - 690 696 703 708 712 721 731 743 0 - 677 691 697 704 713 715 727 732 737 0 - 678 683 698 709 716 728 738 0 - 679 684 692 705 714 739 744 0 - 751 758 772 777 792 800 813 0 - 745 752 773 793 801 805 0 - 746 774 781 786 794 806 0 - 753 765 775 787 802 807 0 - 759 778 788 803 808 814 0 - 747 760 766 795 809 815 0 - 748 761 776 779 782 789 796 810 0 - 754 767 783 797 811 0 - 755 762 768 784 804 0 - 749 756 763 769 785 790 798 812 816 0 - 764 770 780 791 0 - 750 757 771 799 817 0 - 822 837 847 851 855 0 - 818 828 832 842 852 0 - 829 848 0 - 830 833 856 0 - 823 834 838 857 0 - 819 824 835 839 843 853 0 - 820 825 836 840 844 849 854 858 0 - 826 841 845 850 0 - 821 827 831 846 859 0 - 860 881 901 905 911 917 922 0 - 861 867 878 891 912 0 - 868 873 892 906 918 0 - 862 869 882 893 899 907 913 923 0 - 863 883 894 902 914 0 - 870 884 895 900 908 0 - 871 888 896 909 924 0 - 864 885 889 897 910 919 925 0 - 865 874 879 886 0 - 866 887 915 920 0 - 875 898 916 926 0 - 872 876 903 921 927 0 - 877 880 890 904 928 0 - 929 954 965 972 978 983 995 0 - 930 943 955 959 979 984 0 - 937 949 980 985 989 0 - 950 956 960 981 996 0 - 951 957 968 982 990 997 0 - 931 938 944 969 973 0 - 961 986 991 0 - 945 970 974 987 998 0 - 932 939 946 962 966 992 0 - 933 940 958 963 975 988 999 0 - 934 941 964 971 976 993 1000 0 - 935 942 947 952 1001 0 - 936 948 953 967 977 994 1002 0 - 1006 1025 1039 1053 1062 0 - 1007 1014 1020 1034 1045 0 - 1008 1015 1026 1035 1046 1054 1063 0 - 1009 1027 1036 1040 1047 1057 0 - 1028 1037 1048 1055 0 - 1003 1010 1029 1058 0 - 1004 1021 1056 1059 1064 0 - 1016 1022 1041 0 - 1017 1042 1049 1060 1065 0 - 1011 1023 1030 1031 1043 1050 1061 0 - 1012 1018 1032 1038 1044 1051 0 - 1005 1013 1019 1024 1033 1052 1066 0 diff --git a/examples/commandline/spear_qcp/instances/qcplin2006.1031.cnf b/examples/commandline/spear_qcp/instances/qcplin2006.1031.cnf deleted file mode 100755 index c0e4d83a7..000000000 --- a/examples/commandline/spear_qcp/instances/qcplin2006.1031.cnf +++ /dev/null @@ -1,47943 +0,0 @@ -p cnf 4414 47942 - -1 -2 0 - -1 -3 0 - -1 -4 0 - -1 -5 0 - -1 -6 0 - -1 -7 0 - -1 -8 0 - -1 -9 0 - -1 -10 0 - -1 -11 0 - -2 -3 0 - -2 -4 0 - -2 -5 0 - -2 -6 0 - -2 -7 0 - -2 -8 0 - -2 -9 0 - -2 -10 0 - -2 -11 0 - -3 -4 0 - -3 -5 0 - -3 -6 0 - -3 -7 0 - -3 -8 0 - -3 -9 0 - -3 -10 0 - -3 -11 0 - -4 -5 0 - -4 -6 0 - -4 -7 0 - -4 -8 0 - -4 -9 0 - -4 -10 0 - -4 -11 0 - -5 -6 0 - -5 -7 0 - -5 -8 0 - -5 -9 0 - -5 -10 0 - -5 -11 0 - -6 -7 0 - -6 -8 0 - -6 -9 0 - -6 -10 0 - -6 -11 0 - -7 -8 0 - -7 -9 0 - -7 -10 0 - -7 -11 0 - -8 -9 0 - -8 -10 0 - -8 -11 0 - -9 -10 0 - -9 -11 0 - -10 -11 0 - -12 -13 0 - -12 -14 0 - -12 -15 0 - -12 -16 0 - -12 -17 0 - -13 -14 0 - -13 -15 0 - -13 -16 0 - -13 -17 0 - -14 -15 0 - -14 -16 0 - -14 -17 0 - -15 -16 0 - -15 -17 0 - -16 -17 0 - -18 -19 0 - -18 -20 0 - -18 -21 0 - -18 -22 0 - -18 -23 0 - -19 -20 0 - -19 -21 0 - -19 -22 0 - -19 -23 0 - -20 -21 0 - -20 -22 0 - -20 -23 0 - -21 -22 0 - -21 -23 0 - -22 -23 0 - -24 -25 0 - -24 -26 0 - -24 -27 0 - -24 -28 0 - -24 -29 0 - -24 -30 0 - -24 -31 0 - -24 -32 0 - -25 -26 0 - -25 -27 0 - -25 -28 0 - -25 -29 0 - -25 -30 0 - -25 -31 0 - -25 -32 0 - -26 -27 0 - -26 -28 0 - -26 -29 0 - -26 -30 0 - -26 -31 0 - -26 -32 0 - -27 -28 0 - -27 -29 0 - -27 -30 0 - -27 -31 0 - -27 -32 0 - -28 -29 0 - -28 -30 0 - -28 -31 0 - -28 -32 0 - -29 -30 0 - -29 -31 0 - -29 -32 0 - -30 -31 0 - -30 -32 0 - -31 -32 0 - -33 -34 0 - -33 -35 0 - -33 -36 0 - -33 -37 0 - -33 -38 0 - -34 -35 0 - -34 -36 0 - -34 -37 0 - -34 -38 0 - -35 -36 0 - -35 -37 0 - -35 -38 0 - -36 -37 0 - -36 -38 0 - -37 -38 0 - -39 -40 0 - -39 -41 0 - -39 -42 0 - -39 -43 0 - -39 -44 0 - -39 -45 0 - -40 -41 0 - -40 -42 0 - -40 -43 0 - -40 -44 0 - -40 -45 0 - -41 -42 0 - -41 -43 0 - -41 -44 0 - -41 -45 0 - -42 -43 0 - -42 -44 0 - -42 -45 0 - -43 -44 0 - -43 -45 0 - -44 -45 0 - -46 -47 0 - -46 -48 0 - -46 -49 0 - -46 -50 0 - -46 -51 0 - -46 -52 0 - -46 -53 0 - -47 -48 0 - -47 -49 0 - -47 -50 0 - -47 -51 0 - -47 -52 0 - -47 -53 0 - -48 -49 0 - -48 -50 0 - -48 -51 0 - -48 -52 0 - -48 -53 0 - -49 -50 0 - -49 -51 0 - -49 -52 0 - -49 -53 0 - -50 -51 0 - -50 -52 0 - -50 -53 0 - -51 -52 0 - -51 -53 0 - -52 -53 0 - -54 -55 0 - -54 -56 0 - -54 -57 0 - -54 -58 0 - -55 -56 0 - -55 -57 0 - -55 -58 0 - -56 -57 0 - -56 -58 0 - -57 -58 0 - -59 -60 0 - -59 -61 0 - -59 -62 0 - -59 -63 0 - -59 -64 0 - -60 -61 0 - -60 -62 0 - -60 -63 0 - -60 -64 0 - -61 -62 0 - -61 -63 0 - -61 -64 0 - -62 -63 0 - -62 -64 0 - -63 -64 0 - -65 -66 0 - -65 -67 0 - -65 -68 0 - -66 -67 0 - -66 -68 0 - -67 -68 0 - -69 -70 0 - -69 -71 0 - -69 -72 0 - -69 -73 0 - -69 -74 0 - -69 -75 0 - -69 -76 0 - -70 -71 0 - -70 -72 0 - -70 -73 0 - -70 -74 0 - -70 -75 0 - -70 -76 0 - -71 -72 0 - -71 -73 0 - -71 -74 0 - -71 -75 0 - -71 -76 0 - -72 -73 0 - -72 -74 0 - -72 -75 0 - -72 -76 0 - -73 -74 0 - -73 -75 0 - -73 -76 0 - -74 -75 0 - -74 -76 0 - -75 -76 0 - -77 -78 0 - -77 -79 0 - -77 -80 0 - -78 -79 0 - -78 -80 0 - -79 -80 0 - -81 -82 0 - -81 -83 0 - -81 -84 0 - -81 -85 0 - -81 -86 0 - -81 -87 0 - -81 -88 0 - -81 -89 0 - -82 -83 0 - -82 -84 0 - -82 -85 0 - -82 -86 0 - -82 -87 0 - -82 -88 0 - -82 -89 0 - -83 -84 0 - -83 -85 0 - -83 -86 0 - -83 -87 0 - -83 -88 0 - -83 -89 0 - -84 -85 0 - -84 -86 0 - -84 -87 0 - -84 -88 0 - -84 -89 0 - -85 -86 0 - -85 -87 0 - -85 -88 0 - -85 -89 0 - -86 -87 0 - -86 -88 0 - -86 -89 0 - -87 -88 0 - -87 -89 0 - -88 -89 0 - -90 -91 0 - -90 -92 0 - -90 -93 0 - -90 -94 0 - -91 -92 0 - -91 -93 0 - -91 -94 0 - -92 -93 0 - -92 -94 0 - -93 -94 0 - -95 -96 0 - -95 -97 0 - -95 -98 0 - -95 -99 0 - -95 -100 0 - -95 -101 0 - -95 -102 0 - -95 -103 0 - -95 -104 0 - -95 -105 0 - -95 -106 0 - -96 -97 0 - -96 -98 0 - -96 -99 0 - -96 -100 0 - -96 -101 0 - -96 -102 0 - -96 -103 0 - -96 -104 0 - -96 -105 0 - -96 -106 0 - -97 -98 0 - -97 -99 0 - -97 -100 0 - -97 -101 0 - -97 -102 0 - -97 -103 0 - -97 -104 0 - -97 -105 0 - -97 -106 0 - -98 -99 0 - -98 -100 0 - -98 -101 0 - -98 -102 0 - -98 -103 0 - -98 -104 0 - -98 -105 0 - -98 -106 0 - -99 -100 0 - -99 -101 0 - -99 -102 0 - -99 -103 0 - -99 -104 0 - -99 -105 0 - -99 -106 0 - -100 -101 0 - -100 -102 0 - -100 -103 0 - -100 -104 0 - -100 -105 0 - -100 -106 0 - -101 -102 0 - -101 -103 0 - -101 -104 0 - -101 -105 0 - -101 -106 0 - -102 -103 0 - -102 -104 0 - -102 -105 0 - -102 -106 0 - -103 -104 0 - -103 -105 0 - -103 -106 0 - -104 -105 0 - -104 -106 0 - -105 -106 0 - -107 -108 0 - -107 -109 0 - -107 -110 0 - -107 -111 0 - -107 -112 0 - -107 -113 0 - -107 -114 0 - -107 -115 0 - -107 -116 0 - -107 -117 0 - -108 -109 0 - -108 -110 0 - -108 -111 0 - -108 -112 0 - -108 -113 0 - -108 -114 0 - -108 -115 0 - -108 -116 0 - -108 -117 0 - -109 -110 0 - -109 -111 0 - -109 -112 0 - -109 -113 0 - -109 -114 0 - -109 -115 0 - -109 -116 0 - -109 -117 0 - -110 -111 0 - -110 -112 0 - -110 -113 0 - -110 -114 0 - -110 -115 0 - -110 -116 0 - -110 -117 0 - -111 -112 0 - -111 -113 0 - -111 -114 0 - -111 -115 0 - -111 -116 0 - -111 -117 0 - -112 -113 0 - -112 -114 0 - -112 -115 0 - -112 -116 0 - -112 -117 0 - -113 -114 0 - -113 -115 0 - -113 -116 0 - -113 -117 0 - -114 -115 0 - -114 -116 0 - -114 -117 0 - -115 -116 0 - -115 -117 0 - -116 -117 0 - -118 -119 0 - -120 -121 0 - -120 -122 0 - -120 -123 0 - -120 -124 0 - -120 -125 0 - -120 -126 0 - -120 -127 0 - -121 -122 0 - -121 -123 0 - -121 -124 0 - -121 -125 0 - -121 -126 0 - -121 -127 0 - -122 -123 0 - -122 -124 0 - -122 -125 0 - -122 -126 0 - -122 -127 0 - -123 -124 0 - -123 -125 0 - -123 -126 0 - -123 -127 0 - -124 -125 0 - -124 -126 0 - -124 -127 0 - -125 -126 0 - -125 -127 0 - -126 -127 0 - -128 -129 0 - -128 -130 0 - -128 -131 0 - -128 -132 0 - -128 -133 0 - -128 -134 0 - -128 -135 0 - -128 -136 0 - -129 -130 0 - -129 -131 0 - -129 -132 0 - -129 -133 0 - -129 -134 0 - -129 -135 0 - -129 -136 0 - -130 -131 0 - -130 -132 0 - -130 -133 0 - -130 -134 0 - -130 -135 0 - -130 -136 0 - -131 -132 0 - -131 -133 0 - -131 -134 0 - -131 -135 0 - -131 -136 0 - -132 -133 0 - -132 -134 0 - -132 -135 0 - -132 -136 0 - -133 -134 0 - -133 -135 0 - -133 -136 0 - -134 -135 0 - -134 -136 0 - -135 -136 0 - -137 -138 0 - -137 -139 0 - -137 -140 0 - -137 -141 0 - -137 -142 0 - -138 -139 0 - -138 -140 0 - -138 -141 0 - -138 -142 0 - -139 -140 0 - -139 -141 0 - -139 -142 0 - -140 -141 0 - -140 -142 0 - -141 -142 0 - -143 -144 0 - -143 -145 0 - -143 -146 0 - -143 -147 0 - -143 -148 0 - -143 -149 0 - -143 -150 0 - -143 -151 0 - -143 -152 0 - -144 -145 0 - -144 -146 0 - -144 -147 0 - -144 -148 0 - -144 -149 0 - -144 -150 0 - -144 -151 0 - -144 -152 0 - -145 -146 0 - -145 -147 0 - -145 -148 0 - -145 -149 0 - -145 -150 0 - -145 -151 0 - -145 -152 0 - -146 -147 0 - -146 -148 0 - -146 -149 0 - -146 -150 0 - -146 -151 0 - -146 -152 0 - -147 -148 0 - -147 -149 0 - -147 -150 0 - -147 -151 0 - -147 -152 0 - -148 -149 0 - -148 -150 0 - -148 -151 0 - -148 -152 0 - -149 -150 0 - -149 -151 0 - -149 -152 0 - -150 -151 0 - -150 -152 0 - -151 -152 0 - -153 -154 0 - -153 -155 0 - -153 -156 0 - -153 -157 0 - -153 -158 0 - -153 -159 0 - -154 -155 0 - -154 -156 0 - -154 -157 0 - -154 -158 0 - -154 -159 0 - -155 -156 0 - -155 -157 0 - -155 -158 0 - -155 -159 0 - -156 -157 0 - -156 -158 0 - -156 -159 0 - -157 -158 0 - -157 -159 0 - -158 -159 0 - -160 -161 0 - -160 -162 0 - -160 -163 0 - -160 -164 0 - -160 -165 0 - -160 -166 0 - -161 -162 0 - -161 -163 0 - -161 -164 0 - -161 -165 0 - -161 -166 0 - -162 -163 0 - -162 -164 0 - -162 -165 0 - -162 -166 0 - -163 -164 0 - -163 -165 0 - -163 -166 0 - -164 -165 0 - -164 -166 0 - -165 -166 0 - -167 -168 0 - -167 -169 0 - -167 -170 0 - -167 -171 0 - -167 -172 0 - -167 -173 0 - -167 -174 0 - -168 -169 0 - -168 -170 0 - -168 -171 0 - -168 -172 0 - -168 -173 0 - -168 -174 0 - -169 -170 0 - -169 -171 0 - -169 -172 0 - -169 -173 0 - -169 -174 0 - -170 -171 0 - -170 -172 0 - -170 -173 0 - -170 -174 0 - -171 -172 0 - -171 -173 0 - -171 -174 0 - -172 -173 0 - -172 -174 0 - -173 -174 0 - -175 -176 0 - -175 -177 0 - -175 -178 0 - -175 -179 0 - -176 -177 0 - -176 -178 0 - -176 -179 0 - -177 -178 0 - -177 -179 0 - -178 -179 0 - -180 -181 0 - -180 -182 0 - -180 -183 0 - -180 -184 0 - -180 -185 0 - -180 -186 0 - -180 -187 0 - -180 -188 0 - -180 -189 0 - -181 -182 0 - -181 -183 0 - -181 -184 0 - -181 -185 0 - -181 -186 0 - -181 -187 0 - -181 -188 0 - -181 -189 0 - -182 -183 0 - -182 -184 0 - -182 -185 0 - -182 -186 0 - -182 -187 0 - -182 -188 0 - -182 -189 0 - -183 -184 0 - -183 -185 0 - -183 -186 0 - -183 -187 0 - -183 -188 0 - -183 -189 0 - -184 -185 0 - -184 -186 0 - -184 -187 0 - -184 -188 0 - -184 -189 0 - -185 -186 0 - -185 -187 0 - -185 -188 0 - -185 -189 0 - -186 -187 0 - -186 -188 0 - -186 -189 0 - -187 -188 0 - -187 -189 0 - -188 -189 0 - -190 -191 0 - -190 -192 0 - -190 -193 0 - -190 -194 0 - -190 -195 0 - -190 -196 0 - -190 -197 0 - -191 -192 0 - -191 -193 0 - -191 -194 0 - -191 -195 0 - -191 -196 0 - -191 -197 0 - -192 -193 0 - -192 -194 0 - -192 -195 0 - -192 -196 0 - -192 -197 0 - -193 -194 0 - -193 -195 0 - -193 -196 0 - -193 -197 0 - -194 -195 0 - -194 -196 0 - -194 -197 0 - -195 -196 0 - -195 -197 0 - -196 -197 0 - -198 -199 0 - -198 -200 0 - -198 -201 0 - -198 -202 0 - -198 -203 0 - -198 -204 0 - -199 -200 0 - -199 -201 0 - -199 -202 0 - -199 -203 0 - -199 -204 0 - -200 -201 0 - -200 -202 0 - -200 -203 0 - -200 -204 0 - -201 -202 0 - -201 -203 0 - -201 -204 0 - -202 -203 0 - -202 -204 0 - -203 -204 0 - -205 -206 0 - -205 -207 0 - -205 -208 0 - -205 -209 0 - -205 -210 0 - -206 -207 0 - -206 -208 0 - -206 -209 0 - -206 -210 0 - -207 -208 0 - -207 -209 0 - -207 -210 0 - -208 -209 0 - -208 -210 0 - -209 -210 0 - -211 -212 0 - -211 -213 0 - -211 -214 0 - -211 -215 0 - -211 -216 0 - -211 -217 0 - -211 -218 0 - -212 -213 0 - -212 -214 0 - -212 -215 0 - -212 -216 0 - -212 -217 0 - -212 -218 0 - -213 -214 0 - -213 -215 0 - -213 -216 0 - -213 -217 0 - -213 -218 0 - -214 -215 0 - -214 -216 0 - -214 -217 0 - -214 -218 0 - -215 -216 0 - -215 -217 0 - -215 -218 0 - -216 -217 0 - -216 -218 0 - -217 -218 0 - -219 -220 0 - -219 -221 0 - -219 -222 0 - -219 -223 0 - -219 -224 0 - -219 -225 0 - -219 -226 0 - -219 -227 0 - -220 -221 0 - -220 -222 0 - -220 -223 0 - -220 -224 0 - -220 -225 0 - -220 -226 0 - -220 -227 0 - -221 -222 0 - -221 -223 0 - -221 -224 0 - -221 -225 0 - -221 -226 0 - -221 -227 0 - -222 -223 0 - -222 -224 0 - -222 -225 0 - -222 -226 0 - -222 -227 0 - -223 -224 0 - -223 -225 0 - -223 -226 0 - -223 -227 0 - -224 -225 0 - -224 -226 0 - -224 -227 0 - -225 -226 0 - -225 -227 0 - -226 -227 0 - -228 -229 0 - -228 -230 0 - -228 -231 0 - -228 -232 0 - -228 -233 0 - -228 -234 0 - -228 -235 0 - -228 -236 0 - -229 -230 0 - -229 -231 0 - -229 -232 0 - -229 -233 0 - -229 -234 0 - -229 -235 0 - -229 -236 0 - -230 -231 0 - -230 -232 0 - -230 -233 0 - -230 -234 0 - -230 -235 0 - -230 -236 0 - -231 -232 0 - -231 -233 0 - -231 -234 0 - -231 -235 0 - -231 -236 0 - -232 -233 0 - -232 -234 0 - -232 -235 0 - -232 -236 0 - -233 -234 0 - -233 -235 0 - -233 -236 0 - -234 -235 0 - -234 -236 0 - -235 -236 0 - -237 -238 0 - -237 -239 0 - -237 -240 0 - -237 -241 0 - -237 -242 0 - -237 -243 0 - -237 -244 0 - -237 -245 0 - -237 -246 0 - -238 -239 0 - -238 -240 0 - -238 -241 0 - -238 -242 0 - -238 -243 0 - -238 -244 0 - -238 -245 0 - -238 -246 0 - -239 -240 0 - -239 -241 0 - -239 -242 0 - -239 -243 0 - -239 -244 0 - -239 -245 0 - -239 -246 0 - -240 -241 0 - -240 -242 0 - -240 -243 0 - -240 -244 0 - -240 -245 0 - -240 -246 0 - -241 -242 0 - -241 -243 0 - -241 -244 0 - -241 -245 0 - -241 -246 0 - -242 -243 0 - -242 -244 0 - -242 -245 0 - -242 -246 0 - -243 -244 0 - -243 -245 0 - -243 -246 0 - -244 -245 0 - -244 -246 0 - -245 -246 0 - -247 -248 0 - -247 -249 0 - -247 -250 0 - -247 -251 0 - -247 -252 0 - -248 -249 0 - -248 -250 0 - -248 -251 0 - -248 -252 0 - -249 -250 0 - -249 -251 0 - -249 -252 0 - -250 -251 0 - -250 -252 0 - -251 -252 0 - -253 -254 0 - -253 -255 0 - -253 -256 0 - -253 -257 0 - -253 -258 0 - -254 -255 0 - -254 -256 0 - -254 -257 0 - -254 -258 0 - -255 -256 0 - -255 -257 0 - -255 -258 0 - -256 -257 0 - -256 -258 0 - -257 -258 0 - -259 -260 0 - -259 -261 0 - -259 -262 0 - -259 -263 0 - -259 -264 0 - -259 -265 0 - -260 -261 0 - -260 -262 0 - -260 -263 0 - -260 -264 0 - -260 -265 0 - -261 -262 0 - -261 -263 0 - -261 -264 0 - -261 -265 0 - -262 -263 0 - -262 -264 0 - -262 -265 0 - -263 -264 0 - -263 -265 0 - -264 -265 0 - -266 -267 0 - -266 -268 0 - -266 -269 0 - -266 -270 0 - -266 -271 0 - -266 -272 0 - -266 -273 0 - -267 -268 0 - -267 -269 0 - -267 -270 0 - -267 -271 0 - -267 -272 0 - -267 -273 0 - -268 -269 0 - -268 -270 0 - -268 -271 0 - -268 -272 0 - -268 -273 0 - -269 -270 0 - -269 -271 0 - -269 -272 0 - -269 -273 0 - -270 -271 0 - -270 -272 0 - -270 -273 0 - -271 -272 0 - -271 -273 0 - -272 -273 0 - -274 -275 0 - -274 -276 0 - -274 -277 0 - -274 -278 0 - -274 -279 0 - -274 -280 0 - -274 -281 0 - -274 -282 0 - -275 -276 0 - -275 -277 0 - -275 -278 0 - -275 -279 0 - -275 -280 0 - -275 -281 0 - -275 -282 0 - -276 -277 0 - -276 -278 0 - -276 -279 0 - -276 -280 0 - -276 -281 0 - -276 -282 0 - -277 -278 0 - -277 -279 0 - -277 -280 0 - -277 -281 0 - -277 -282 0 - -278 -279 0 - -278 -280 0 - -278 -281 0 - -278 -282 0 - -279 -280 0 - -279 -281 0 - -279 -282 0 - -280 -281 0 - -280 -282 0 - -281 -282 0 - -283 -284 0 - -283 -285 0 - -283 -286 0 - -283 -287 0 - -284 -285 0 - -284 -286 0 - -284 -287 0 - -285 -286 0 - -285 -287 0 - -286 -287 0 - -288 -289 0 - -288 -290 0 - -288 -291 0 - -289 -290 0 - -289 -291 0 - -290 -291 0 - -292 -293 0 - -292 -294 0 - -292 -295 0 - -292 -296 0 - -292 -297 0 - -292 -298 0 - -292 -299 0 - -293 -294 0 - -293 -295 0 - -293 -296 0 - -293 -297 0 - -293 -298 0 - -293 -299 0 - -294 -295 0 - -294 -296 0 - -294 -297 0 - -294 -298 0 - -294 -299 0 - -295 -296 0 - -295 -297 0 - -295 -298 0 - -295 -299 0 - -296 -297 0 - -296 -298 0 - -296 -299 0 - -297 -298 0 - -297 -299 0 - -298 -299 0 - -300 -301 0 - -300 -302 0 - -300 -303 0 - -300 -304 0 - -300 -305 0 - -300 -306 0 - -301 -302 0 - -301 -303 0 - -301 -304 0 - -301 -305 0 - -301 -306 0 - -302 -303 0 - -302 -304 0 - -302 -305 0 - -302 -306 0 - -303 -304 0 - -303 -305 0 - -303 -306 0 - -304 -305 0 - -304 -306 0 - -305 -306 0 - -307 -308 0 - -307 -309 0 - -307 -310 0 - -307 -311 0 - -307 -312 0 - -308 -309 0 - -308 -310 0 - -308 -311 0 - -308 -312 0 - -309 -310 0 - -309 -311 0 - -309 -312 0 - -310 -311 0 - -310 -312 0 - -311 -312 0 - -313 -314 0 - -313 -315 0 - -313 -316 0 - -313 -317 0 - -313 -318 0 - -313 -319 0 - -313 -320 0 - -313 -321 0 - -313 -322 0 - -314 -315 0 - -314 -316 0 - -314 -317 0 - -314 -318 0 - -314 -319 0 - -314 -320 0 - -314 -321 0 - -314 -322 0 - -315 -316 0 - -315 -317 0 - -315 -318 0 - -315 -319 0 - -315 -320 0 - -315 -321 0 - -315 -322 0 - -316 -317 0 - -316 -318 0 - -316 -319 0 - -316 -320 0 - -316 -321 0 - -316 -322 0 - -317 -318 0 - -317 -319 0 - -317 -320 0 - -317 -321 0 - -317 -322 0 - -318 -319 0 - -318 -320 0 - -318 -321 0 - -318 -322 0 - -319 -320 0 - -319 -321 0 - -319 -322 0 - -320 -321 0 - -320 -322 0 - -321 -322 0 - -323 -324 0 - -323 -325 0 - -323 -326 0 - -323 -327 0 - -324 -325 0 - -324 -326 0 - -324 -327 0 - -325 -326 0 - -325 -327 0 - -326 -327 0 - -328 -329 0 - -328 -330 0 - -328 -331 0 - -328 -332 0 - -328 -333 0 - -328 -334 0 - -328 -335 0 - -329 -330 0 - -329 -331 0 - -329 -332 0 - -329 -333 0 - -329 -334 0 - -329 -335 0 - -330 -331 0 - -330 -332 0 - -330 -333 0 - -330 -334 0 - -330 -335 0 - -331 -332 0 - -331 -333 0 - -331 -334 0 - -331 -335 0 - -332 -333 0 - -332 -334 0 - -332 -335 0 - -333 -334 0 - -333 -335 0 - -334 -335 0 - -336 -337 0 - -336 -338 0 - -336 -339 0 - -336 -340 0 - -336 -341 0 - -336 -342 0 - -336 -343 0 - -337 -338 0 - -337 -339 0 - -337 -340 0 - -337 -341 0 - -337 -342 0 - -337 -343 0 - -338 -339 0 - -338 -340 0 - -338 -341 0 - -338 -342 0 - -338 -343 0 - -339 -340 0 - -339 -341 0 - -339 -342 0 - -339 -343 0 - -340 -341 0 - -340 -342 0 - -340 -343 0 - -341 -342 0 - -341 -343 0 - -342 -343 0 - -344 -345 0 - -344 -346 0 - -344 -347 0 - -344 -348 0 - -344 -349 0 - -344 -350 0 - -344 -351 0 - -344 -352 0 - -345 -346 0 - -345 -347 0 - -345 -348 0 - -345 -349 0 - -345 -350 0 - -345 -351 0 - -345 -352 0 - -346 -347 0 - -346 -348 0 - -346 -349 0 - -346 -350 0 - -346 -351 0 - -346 -352 0 - -347 -348 0 - -347 -349 0 - -347 -350 0 - -347 -351 0 - -347 -352 0 - -348 -349 0 - -348 -350 0 - -348 -351 0 - -348 -352 0 - -349 -350 0 - -349 -351 0 - -349 -352 0 - -350 -351 0 - -350 -352 0 - -351 -352 0 - -353 -354 0 - -353 -355 0 - -353 -356 0 - -353 -357 0 - -353 -358 0 - -353 -359 0 - -354 -355 0 - -354 -356 0 - -354 -357 0 - -354 -358 0 - -354 -359 0 - -355 -356 0 - -355 -357 0 - -355 -358 0 - -355 -359 0 - -356 -357 0 - -356 -358 0 - -356 -359 0 - -357 -358 0 - -357 -359 0 - -358 -359 0 - -360 -361 0 - -360 -362 0 - -360 -363 0 - -360 -364 0 - -360 -365 0 - -361 -362 0 - -361 -363 0 - -361 -364 0 - -361 -365 0 - -362 -363 0 - -362 -364 0 - -362 -365 0 - -363 -364 0 - -363 -365 0 - -364 -365 0 - -366 -367 0 - -366 -368 0 - -366 -369 0 - -366 -370 0 - -367 -368 0 - -367 -369 0 - -367 -370 0 - -368 -369 0 - -368 -370 0 - -369 -370 0 - -371 -372 0 - -371 -373 0 - -371 -374 0 - -371 -375 0 - -371 -376 0 - -371 -377 0 - -371 -378 0 - -371 -379 0 - -371 -380 0 - -371 -381 0 - -372 -373 0 - -372 -374 0 - -372 -375 0 - -372 -376 0 - -372 -377 0 - -372 -378 0 - -372 -379 0 - -372 -380 0 - -372 -381 0 - -373 -374 0 - -373 -375 0 - -373 -376 0 - -373 -377 0 - -373 -378 0 - -373 -379 0 - -373 -380 0 - -373 -381 0 - -374 -375 0 - -374 -376 0 - -374 -377 0 - -374 -378 0 - -374 -379 0 - -374 -380 0 - -374 -381 0 - -375 -376 0 - -375 -377 0 - -375 -378 0 - -375 -379 0 - -375 -380 0 - -375 -381 0 - -376 -377 0 - -376 -378 0 - -376 -379 0 - -376 -380 0 - -376 -381 0 - -377 -378 0 - -377 -379 0 - -377 -380 0 - -377 -381 0 - -378 -379 0 - -378 -380 0 - -378 -381 0 - -379 -380 0 - -379 -381 0 - -380 -381 0 - -382 -383 0 - -382 -384 0 - -382 -385 0 - -383 -384 0 - -383 -385 0 - -384 -385 0 - -386 -387 0 - -386 -388 0 - -386 -389 0 - -386 -390 0 - -386 -391 0 - -387 -388 0 - -387 -389 0 - -387 -390 0 - -387 -391 0 - -388 -389 0 - -388 -390 0 - -388 -391 0 - -389 -390 0 - -389 -391 0 - -390 -391 0 - -392 -393 0 - -392 -394 0 - -392 -395 0 - -392 -396 0 - -393 -394 0 - -393 -395 0 - -393 -396 0 - -394 -395 0 - -394 -396 0 - -395 -396 0 - -397 -398 0 - -397 -399 0 - -397 -400 0 - -397 -401 0 - -398 -399 0 - -398 -400 0 - -398 -401 0 - -399 -400 0 - -399 -401 0 - -400 -401 0 - -402 -403 0 - -402 -404 0 - -402 -405 0 - -402 -406 0 - -402 -407 0 - -402 -408 0 - -403 -404 0 - -403 -405 0 - -403 -406 0 - -403 -407 0 - -403 -408 0 - -404 -405 0 - -404 -406 0 - -404 -407 0 - -404 -408 0 - -405 -406 0 - -405 -407 0 - -405 -408 0 - -406 -407 0 - -406 -408 0 - -407 -408 0 - -409 -410 0 - -409 -411 0 - -409 -412 0 - -409 -413 0 - -409 -414 0 - -409 -415 0 - -410 -411 0 - -410 -412 0 - -410 -413 0 - -410 -414 0 - -410 -415 0 - -411 -412 0 - -411 -413 0 - -411 -414 0 - -411 -415 0 - -412 -413 0 - -412 -414 0 - -412 -415 0 - -413 -414 0 - -413 -415 0 - -414 -415 0 - -416 -417 0 - -416 -418 0 - -416 -419 0 - -416 -420 0 - -416 -421 0 - -416 -422 0 - -417 -418 0 - -417 -419 0 - -417 -420 0 - -417 -421 0 - -417 -422 0 - -418 -419 0 - -418 -420 0 - -418 -421 0 - -418 -422 0 - -419 -420 0 - -419 -421 0 - -419 -422 0 - -420 -421 0 - -420 -422 0 - -421 -422 0 - -423 -424 0 - -423 -425 0 - -423 -426 0 - -423 -427 0 - -423 -428 0 - -423 -429 0 - -424 -425 0 - -424 -426 0 - -424 -427 0 - -424 -428 0 - -424 -429 0 - -425 -426 0 - -425 -427 0 - -425 -428 0 - -425 -429 0 - -426 -427 0 - -426 -428 0 - -426 -429 0 - -427 -428 0 - -427 -429 0 - -428 -429 0 - -430 -431 0 - -430 -432 0 - -430 -433 0 - -430 -434 0 - -430 -435 0 - -431 -432 0 - -431 -433 0 - -431 -434 0 - -431 -435 0 - -432 -433 0 - -432 -434 0 - -432 -435 0 - -433 -434 0 - -433 -435 0 - -434 -435 0 - -436 -437 0 - -436 -438 0 - -436 -439 0 - -436 -440 0 - -437 -438 0 - -437 -439 0 - -437 -440 0 - -438 -439 0 - -438 -440 0 - -439 -440 0 - -441 -442 0 - -441 -443 0 - -441 -444 0 - -441 -445 0 - -441 -446 0 - -442 -443 0 - -442 -444 0 - -442 -445 0 - -442 -446 0 - -443 -444 0 - -443 -445 0 - -443 -446 0 - -444 -445 0 - -444 -446 0 - -445 -446 0 - -447 -448 0 - -447 -449 0 - -447 -450 0 - -447 -451 0 - -447 -452 0 - -447 -453 0 - -447 -454 0 - -447 -455 0 - -447 -456 0 - -447 -457 0 - -448 -449 0 - -448 -450 0 - -448 -451 0 - -448 -452 0 - -448 -453 0 - -448 -454 0 - -448 -455 0 - -448 -456 0 - -448 -457 0 - -449 -450 0 - -449 -451 0 - -449 -452 0 - -449 -453 0 - -449 -454 0 - -449 -455 0 - -449 -456 0 - -449 -457 0 - -450 -451 0 - -450 -452 0 - -450 -453 0 - -450 -454 0 - -450 -455 0 - -450 -456 0 - -450 -457 0 - -451 -452 0 - -451 -453 0 - -451 -454 0 - -451 -455 0 - -451 -456 0 - -451 -457 0 - -452 -453 0 - -452 -454 0 - -452 -455 0 - -452 -456 0 - -452 -457 0 - -453 -454 0 - -453 -455 0 - -453 -456 0 - -453 -457 0 - -454 -455 0 - -454 -456 0 - -454 -457 0 - -455 -456 0 - -455 -457 0 - -456 -457 0 - -458 -459 0 - -458 -460 0 - -458 -461 0 - -459 -460 0 - -459 -461 0 - -460 -461 0 - -462 -463 0 - -462 -464 0 - -462 -465 0 - -462 -466 0 - -462 -467 0 - -462 -468 0 - -462 -469 0 - -462 -470 0 - -462 -471 0 - -463 -464 0 - -463 -465 0 - -463 -466 0 - -463 -467 0 - -463 -468 0 - -463 -469 0 - -463 -470 0 - -463 -471 0 - -464 -465 0 - -464 -466 0 - -464 -467 0 - -464 -468 0 - -464 -469 0 - -464 -470 0 - -464 -471 0 - -465 -466 0 - -465 -467 0 - -465 -468 0 - -465 -469 0 - -465 -470 0 - -465 -471 0 - -466 -467 0 - -466 -468 0 - -466 -469 0 - -466 -470 0 - -466 -471 0 - -467 -468 0 - -467 -469 0 - -467 -470 0 - -467 -471 0 - -468 -469 0 - -468 -470 0 - -468 -471 0 - -469 -470 0 - -469 -471 0 - -470 -471 0 - -472 -473 0 - -472 -474 0 - -472 -475 0 - -472 -476 0 - -472 -477 0 - -473 -474 0 - -473 -475 0 - -473 -476 0 - -473 -477 0 - -474 -475 0 - -474 -476 0 - -474 -477 0 - -475 -476 0 - -475 -477 0 - -476 -477 0 - -478 -479 0 - -480 -481 0 - -480 -482 0 - -480 -483 0 - -480 -484 0 - -480 -485 0 - -480 -486 0 - -480 -487 0 - -480 -488 0 - -480 -489 0 - -481 -482 0 - -481 -483 0 - -481 -484 0 - -481 -485 0 - -481 -486 0 - -481 -487 0 - -481 -488 0 - -481 -489 0 - -482 -483 0 - -482 -484 0 - -482 -485 0 - -482 -486 0 - -482 -487 0 - -482 -488 0 - -482 -489 0 - -483 -484 0 - -483 -485 0 - -483 -486 0 - -483 -487 0 - -483 -488 0 - -483 -489 0 - -484 -485 0 - -484 -486 0 - -484 -487 0 - -484 -488 0 - -484 -489 0 - -485 -486 0 - -485 -487 0 - -485 -488 0 - -485 -489 0 - -486 -487 0 - -486 -488 0 - -486 -489 0 - -487 -488 0 - -487 -489 0 - -488 -489 0 - -490 -491 0 - -490 -492 0 - -490 -493 0 - -490 -494 0 - -490 -495 0 - -490 -496 0 - -491 -492 0 - -491 -493 0 - -491 -494 0 - -491 -495 0 - -491 -496 0 - -492 -493 0 - -492 -494 0 - -492 -495 0 - -492 -496 0 - -493 -494 0 - -493 -495 0 - -493 -496 0 - -494 -495 0 - -494 -496 0 - -495 -496 0 - -497 -498 0 - -497 -499 0 - -497 -500 0 - -497 -501 0 - -497 -502 0 - -497 -503 0 - -497 -504 0 - -498 -499 0 - -498 -500 0 - -498 -501 0 - -498 -502 0 - -498 -503 0 - -498 -504 0 - -499 -500 0 - -499 -501 0 - -499 -502 0 - -499 -503 0 - -499 -504 0 - -500 -501 0 - -500 -502 0 - -500 -503 0 - -500 -504 0 - -501 -502 0 - -501 -503 0 - -501 -504 0 - -502 -503 0 - -502 -504 0 - -503 -504 0 - -505 -506 0 - -505 -507 0 - -505 -508 0 - -505 -509 0 - -505 -510 0 - -505 -511 0 - -505 -512 0 - -506 -507 0 - -506 -508 0 - -506 -509 0 - -506 -510 0 - -506 -511 0 - -506 -512 0 - -507 -508 0 - -507 -509 0 - -507 -510 0 - -507 -511 0 - -507 -512 0 - -508 -509 0 - -508 -510 0 - -508 -511 0 - -508 -512 0 - -509 -510 0 - -509 -511 0 - -509 -512 0 - -510 -511 0 - -510 -512 0 - -511 -512 0 - -513 -514 0 - -513 -515 0 - -513 -516 0 - -513 -517 0 - -513 -518 0 - -514 -515 0 - -514 -516 0 - -514 -517 0 - -514 -518 0 - -515 -516 0 - -515 -517 0 - -515 -518 0 - -516 -517 0 - -516 -518 0 - -517 -518 0 - -519 -520 0 - -519 -521 0 - -519 -522 0 - -519 -523 0 - -519 -524 0 - -519 -525 0 - -519 -526 0 - -519 -527 0 - -519 -528 0 - -519 -529 0 - -520 -521 0 - -520 -522 0 - -520 -523 0 - -520 -524 0 - -520 -525 0 - -520 -526 0 - -520 -527 0 - -520 -528 0 - -520 -529 0 - -521 -522 0 - -521 -523 0 - -521 -524 0 - -521 -525 0 - -521 -526 0 - -521 -527 0 - -521 -528 0 - -521 -529 0 - -522 -523 0 - -522 -524 0 - -522 -525 0 - -522 -526 0 - -522 -527 0 - -522 -528 0 - -522 -529 0 - -523 -524 0 - -523 -525 0 - -523 -526 0 - -523 -527 0 - -523 -528 0 - -523 -529 0 - -524 -525 0 - -524 -526 0 - -524 -527 0 - -524 -528 0 - -524 -529 0 - -525 -526 0 - -525 -527 0 - -525 -528 0 - -525 -529 0 - -526 -527 0 - -526 -528 0 - -526 -529 0 - -527 -528 0 - -527 -529 0 - -528 -529 0 - -530 -531 0 - -530 -532 0 - -530 -533 0 - -530 -534 0 - -530 -535 0 - -530 -536 0 - -530 -537 0 - -530 -538 0 - -530 -539 0 - -530 -540 0 - -531 -532 0 - -531 -533 0 - -531 -534 0 - -531 -535 0 - -531 -536 0 - -531 -537 0 - -531 -538 0 - -531 -539 0 - -531 -540 0 - -532 -533 0 - -532 -534 0 - -532 -535 0 - -532 -536 0 - -532 -537 0 - -532 -538 0 - -532 -539 0 - -532 -540 0 - -533 -534 0 - -533 -535 0 - -533 -536 0 - -533 -537 0 - -533 -538 0 - -533 -539 0 - -533 -540 0 - -534 -535 0 - -534 -536 0 - -534 -537 0 - -534 -538 0 - -534 -539 0 - -534 -540 0 - -535 -536 0 - -535 -537 0 - -535 -538 0 - -535 -539 0 - -535 -540 0 - -536 -537 0 - -536 -538 0 - -536 -539 0 - -536 -540 0 - -537 -538 0 - -537 -539 0 - -537 -540 0 - -538 -539 0 - -538 -540 0 - -539 -540 0 - -541 -542 0 - -541 -543 0 - -541 -544 0 - -541 -545 0 - -541 -546 0 - -541 -547 0 - -541 -548 0 - -541 -549 0 - -541 -550 0 - -541 -551 0 - -542 -543 0 - -542 -544 0 - -542 -545 0 - -542 -546 0 - -542 -547 0 - -542 -548 0 - -542 -549 0 - -542 -550 0 - -542 -551 0 - -543 -544 0 - -543 -545 0 - -543 -546 0 - -543 -547 0 - -543 -548 0 - -543 -549 0 - -543 -550 0 - -543 -551 0 - -544 -545 0 - -544 -546 0 - -544 -547 0 - -544 -548 0 - -544 -549 0 - -544 -550 0 - -544 -551 0 - -545 -546 0 - -545 -547 0 - -545 -548 0 - -545 -549 0 - -545 -550 0 - -545 -551 0 - -546 -547 0 - -546 -548 0 - -546 -549 0 - -546 -550 0 - -546 -551 0 - -547 -548 0 - -547 -549 0 - -547 -550 0 - -547 -551 0 - -548 -549 0 - -548 -550 0 - -548 -551 0 - -549 -550 0 - -549 -551 0 - -550 -551 0 - -552 -553 0 - -552 -554 0 - -552 -555 0 - -552 -556 0 - -552 -557 0 - -553 -554 0 - -553 -555 0 - -553 -556 0 - -553 -557 0 - -554 -555 0 - -554 -556 0 - -554 -557 0 - -555 -556 0 - -555 -557 0 - -556 -557 0 - -558 -559 0 - -558 -560 0 - -558 -561 0 - -558 -562 0 - -558 -563 0 - -558 -564 0 - -558 -565 0 - -558 -566 0 - -559 -560 0 - -559 -561 0 - -559 -562 0 - -559 -563 0 - -559 -564 0 - -559 -565 0 - -559 -566 0 - -560 -561 0 - -560 -562 0 - -560 -563 0 - -560 -564 0 - -560 -565 0 - -560 -566 0 - -561 -562 0 - -561 -563 0 - -561 -564 0 - -561 -565 0 - -561 -566 0 - -562 -563 0 - -562 -564 0 - -562 -565 0 - -562 -566 0 - -563 -564 0 - -563 -565 0 - -563 -566 0 - -564 -565 0 - -564 -566 0 - -565 -566 0 - -567 -568 0 - -567 -569 0 - -567 -570 0 - -567 -571 0 - -568 -569 0 - -568 -570 0 - -568 -571 0 - -569 -570 0 - -569 -571 0 - -570 -571 0 - -572 -573 0 - -572 -574 0 - -572 -575 0 - -572 -576 0 - -572 -577 0 - -572 -578 0 - -572 -579 0 - -572 -580 0 - -573 -574 0 - -573 -575 0 - -573 -576 0 - -573 -577 0 - -573 -578 0 - -573 -579 0 - -573 -580 0 - -574 -575 0 - -574 -576 0 - -574 -577 0 - -574 -578 0 - -574 -579 0 - -574 -580 0 - -575 -576 0 - -575 -577 0 - -575 -578 0 - -575 -579 0 - -575 -580 0 - -576 -577 0 - -576 -578 0 - -576 -579 0 - -576 -580 0 - -577 -578 0 - -577 -579 0 - -577 -580 0 - -578 -579 0 - -578 -580 0 - -579 -580 0 - -581 -582 0 - -581 -583 0 - -582 -583 0 - -584 -585 0 - -584 -586 0 - -584 -587 0 - -584 -588 0 - -584 -589 0 - -584 -590 0 - -584 -591 0 - -585 -586 0 - -585 -587 0 - -585 -588 0 - -585 -589 0 - -585 -590 0 - -585 -591 0 - -586 -587 0 - -586 -588 0 - -586 -589 0 - -586 -590 0 - -586 -591 0 - -587 -588 0 - -587 -589 0 - -587 -590 0 - -587 -591 0 - -588 -589 0 - -588 -590 0 - -588 -591 0 - -589 -590 0 - -589 -591 0 - -590 -591 0 - -592 -593 0 - -592 -594 0 - -592 -595 0 - -592 -596 0 - -592 -597 0 - -592 -598 0 - -593 -594 0 - -593 -595 0 - -593 -596 0 - -593 -597 0 - -593 -598 0 - -594 -595 0 - -594 -596 0 - -594 -597 0 - -594 -598 0 - -595 -596 0 - -595 -597 0 - -595 -598 0 - -596 -597 0 - -596 -598 0 - -597 -598 0 - -599 -600 0 - -599 -601 0 - -599 -602 0 - -599 -603 0 - -599 -604 0 - -600 -601 0 - -600 -602 0 - -600 -603 0 - -600 -604 0 - -601 -602 0 - -601 -603 0 - -601 -604 0 - -602 -603 0 - -602 -604 0 - -603 -604 0 - -605 -606 0 - -605 -607 0 - -605 -608 0 - -605 -609 0 - -605 -610 0 - -605 -611 0 - -605 -612 0 - -606 -607 0 - -606 -608 0 - -606 -609 0 - -606 -610 0 - -606 -611 0 - -606 -612 0 - -607 -608 0 - -607 -609 0 - -607 -610 0 - -607 -611 0 - -607 -612 0 - -608 -609 0 - -608 -610 0 - -608 -611 0 - -608 -612 0 - -609 -610 0 - -609 -611 0 - -609 -612 0 - -610 -611 0 - -610 -612 0 - -611 -612 0 - -613 -614 0 - -613 -615 0 - -613 -616 0 - -613 -617 0 - -613 -618 0 - -613 -619 0 - -613 -620 0 - -613 -621 0 - -614 -615 0 - -614 -616 0 - -614 -617 0 - -614 -618 0 - -614 -619 0 - -614 -620 0 - -614 -621 0 - -615 -616 0 - -615 -617 0 - -615 -618 0 - -615 -619 0 - -615 -620 0 - -615 -621 0 - -616 -617 0 - -616 -618 0 - -616 -619 0 - -616 -620 0 - -616 -621 0 - -617 -618 0 - -617 -619 0 - -617 -620 0 - -617 -621 0 - -618 -619 0 - -618 -620 0 - -618 -621 0 - -619 -620 0 - -619 -621 0 - -620 -621 0 - -622 -623 0 - -622 -624 0 - -622 -625 0 - -622 -626 0 - -622 -627 0 - -622 -628 0 - -622 -629 0 - -623 -624 0 - -623 -625 0 - -623 -626 0 - -623 -627 0 - -623 -628 0 - -623 -629 0 - -624 -625 0 - -624 -626 0 - -624 -627 0 - -624 -628 0 - -624 -629 0 - -625 -626 0 - -625 -627 0 - -625 -628 0 - -625 -629 0 - -626 -627 0 - -626 -628 0 - -626 -629 0 - -627 -628 0 - -627 -629 0 - -628 -629 0 - -630 -631 0 - -630 -632 0 - -630 -633 0 - -630 -634 0 - -631 -632 0 - -631 -633 0 - -631 -634 0 - -632 -633 0 - -632 -634 0 - -633 -634 0 - -635 -636 0 - -635 -637 0 - -635 -638 0 - -635 -639 0 - -635 -640 0 - -635 -641 0 - -635 -642 0 - -635 -643 0 - -635 -644 0 - -636 -637 0 - -636 -638 0 - -636 -639 0 - -636 -640 0 - -636 -641 0 - -636 -642 0 - -636 -643 0 - -636 -644 0 - -637 -638 0 - -637 -639 0 - -637 -640 0 - -637 -641 0 - -637 -642 0 - -637 -643 0 - -637 -644 0 - -638 -639 0 - -638 -640 0 - -638 -641 0 - -638 -642 0 - -638 -643 0 - -638 -644 0 - -639 -640 0 - -639 -641 0 - -639 -642 0 - -639 -643 0 - -639 -644 0 - -640 -641 0 - -640 -642 0 - -640 -643 0 - -640 -644 0 - -641 -642 0 - -641 -643 0 - -641 -644 0 - -642 -643 0 - -642 -644 0 - -643 -644 0 - -645 -646 0 - -645 -647 0 - -645 -648 0 - -645 -649 0 - -645 -650 0 - -646 -647 0 - -646 -648 0 - -646 -649 0 - -646 -650 0 - -647 -648 0 - -647 -649 0 - -647 -650 0 - -648 -649 0 - -648 -650 0 - -649 -650 0 - -651 -652 0 - -651 -653 0 - -651 -654 0 - -651 -655 0 - -651 -656 0 - -651 -657 0 - -651 -658 0 - -652 -653 0 - -652 -654 0 - -652 -655 0 - -652 -656 0 - -652 -657 0 - -652 -658 0 - -653 -654 0 - -653 -655 0 - -653 -656 0 - -653 -657 0 - -653 -658 0 - -654 -655 0 - -654 -656 0 - -654 -657 0 - -654 -658 0 - -655 -656 0 - -655 -657 0 - -655 -658 0 - -656 -657 0 - -656 -658 0 - -657 -658 0 - -659 -660 0 - -659 -661 0 - -659 -662 0 - -659 -663 0 - -659 -664 0 - -659 -665 0 - -659 -666 0 - -659 -667 0 - -660 -661 0 - -660 -662 0 - -660 -663 0 - -660 -664 0 - -660 -665 0 - -660 -666 0 - -660 -667 0 - -661 -662 0 - -661 -663 0 - -661 -664 0 - -661 -665 0 - -661 -666 0 - -661 -667 0 - -662 -663 0 - -662 -664 0 - -662 -665 0 - -662 -666 0 - -662 -667 0 - -663 -664 0 - -663 -665 0 - -663 -666 0 - -663 -667 0 - -664 -665 0 - -664 -666 0 - -664 -667 0 - -665 -666 0 - -665 -667 0 - -666 -667 0 - -668 -669 0 - -668 -670 0 - -668 -671 0 - -668 -672 0 - -668 -673 0 - -668 -674 0 - -668 -675 0 - -669 -670 0 - -669 -671 0 - -669 -672 0 - -669 -673 0 - -669 -674 0 - -669 -675 0 - -670 -671 0 - -670 -672 0 - -670 -673 0 - -670 -674 0 - -670 -675 0 - -671 -672 0 - -671 -673 0 - -671 -674 0 - -671 -675 0 - -672 -673 0 - -672 -674 0 - -672 -675 0 - -673 -674 0 - -673 -675 0 - -674 -675 0 - -676 -677 0 - -676 -678 0 - -676 -679 0 - -676 -680 0 - -676 -681 0 - -676 -682 0 - -676 -683 0 - -676 -684 0 - -676 -685 0 - -677 -678 0 - -677 -679 0 - -677 -680 0 - -677 -681 0 - -677 -682 0 - -677 -683 0 - -677 -684 0 - -677 -685 0 - -678 -679 0 - -678 -680 0 - -678 -681 0 - -678 -682 0 - -678 -683 0 - -678 -684 0 - -678 -685 0 - -679 -680 0 - -679 -681 0 - -679 -682 0 - -679 -683 0 - -679 -684 0 - -679 -685 0 - -680 -681 0 - -680 -682 0 - -680 -683 0 - -680 -684 0 - -680 -685 0 - -681 -682 0 - -681 -683 0 - -681 -684 0 - -681 -685 0 - -682 -683 0 - -682 -684 0 - -682 -685 0 - -683 -684 0 - -683 -685 0 - -684 -685 0 - -686 -687 0 - -686 -688 0 - -686 -689 0 - -686 -690 0 - -687 -688 0 - -687 -689 0 - -687 -690 0 - -688 -689 0 - -688 -690 0 - -689 -690 0 - -691 -692 0 - -691 -693 0 - -691 -694 0 - -692 -693 0 - -692 -694 0 - -693 -694 0 - -695 -696 0 - -695 -697 0 - -695 -698 0 - -695 -699 0 - -695 -700 0 - -695 -701 0 - -695 -702 0 - -695 -703 0 - -695 -704 0 - -695 -705 0 - -696 -697 0 - -696 -698 0 - -696 -699 0 - -696 -700 0 - -696 -701 0 - -696 -702 0 - -696 -703 0 - -696 -704 0 - -696 -705 0 - -697 -698 0 - -697 -699 0 - -697 -700 0 - -697 -701 0 - -697 -702 0 - -697 -703 0 - -697 -704 0 - -697 -705 0 - -698 -699 0 - -698 -700 0 - -698 -701 0 - -698 -702 0 - -698 -703 0 - -698 -704 0 - -698 -705 0 - -699 -700 0 - -699 -701 0 - -699 -702 0 - -699 -703 0 - -699 -704 0 - -699 -705 0 - -700 -701 0 - -700 -702 0 - -700 -703 0 - -700 -704 0 - -700 -705 0 - -701 -702 0 - -701 -703 0 - -701 -704 0 - -701 -705 0 - -702 -703 0 - -702 -704 0 - -702 -705 0 - -703 -704 0 - -703 -705 0 - -704 -705 0 - -706 -707 0 - -706 -708 0 - -706 -709 0 - -706 -710 0 - -706 -711 0 - -707 -708 0 - -707 -709 0 - -707 -710 0 - -707 -711 0 - -708 -709 0 - -708 -710 0 - -708 -711 0 - -709 -710 0 - -709 -711 0 - -710 -711 0 - -712 -713 0 - -712 -714 0 - -712 -715 0 - -712 -716 0 - -712 -717 0 - -712 -718 0 - -713 -714 0 - -713 -715 0 - -713 -716 0 - -713 -717 0 - -713 -718 0 - -714 -715 0 - -714 -716 0 - -714 -717 0 - -714 -718 0 - -715 -716 0 - -715 -717 0 - -715 -718 0 - -716 -717 0 - -716 -718 0 - -717 -718 0 - -719 -720 0 - -719 -721 0 - -719 -722 0 - -719 -723 0 - -720 -721 0 - -720 -722 0 - -720 -723 0 - -721 -722 0 - -721 -723 0 - -722 -723 0 - -724 -725 0 - -724 -726 0 - -724 -727 0 - -724 -728 0 - -724 -729 0 - -725 -726 0 - -725 -727 0 - -725 -728 0 - -725 -729 0 - -726 -727 0 - -726 -728 0 - -726 -729 0 - -727 -728 0 - -727 -729 0 - -728 -729 0 - -730 -731 0 - -730 -732 0 - -730 -733 0 - -730 -734 0 - -730 -735 0 - -730 -736 0 - -730 -737 0 - -730 -738 0 - -730 -739 0 - -731 -732 0 - -731 -733 0 - -731 -734 0 - -731 -735 0 - -731 -736 0 - -731 -737 0 - -731 -738 0 - -731 -739 0 - -732 -733 0 - -732 -734 0 - -732 -735 0 - -732 -736 0 - -732 -737 0 - -732 -738 0 - -732 -739 0 - -733 -734 0 - -733 -735 0 - -733 -736 0 - -733 -737 0 - -733 -738 0 - -733 -739 0 - -734 -735 0 - -734 -736 0 - -734 -737 0 - -734 -738 0 - -734 -739 0 - -735 -736 0 - -735 -737 0 - -735 -738 0 - -735 -739 0 - -736 -737 0 - -736 -738 0 - -736 -739 0 - -737 -738 0 - -737 -739 0 - -738 -739 0 - -740 -741 0 - -740 -742 0 - -740 -743 0 - -740 -744 0 - -740 -745 0 - -740 -746 0 - -740 -747 0 - -741 -742 0 - -741 -743 0 - -741 -744 0 - -741 -745 0 - -741 -746 0 - -741 -747 0 - -742 -743 0 - -742 -744 0 - -742 -745 0 - -742 -746 0 - -742 -747 0 - -743 -744 0 - -743 -745 0 - -743 -746 0 - -743 -747 0 - -744 -745 0 - -744 -746 0 - -744 -747 0 - -745 -746 0 - -745 -747 0 - -746 -747 0 - -748 -749 0 - -748 -750 0 - -748 -751 0 - -748 -752 0 - -748 -753 0 - -748 -754 0 - -749 -750 0 - -749 -751 0 - -749 -752 0 - -749 -753 0 - -749 -754 0 - -750 -751 0 - -750 -752 0 - -750 -753 0 - -750 -754 0 - -751 -752 0 - -751 -753 0 - -751 -754 0 - -752 -753 0 - -752 -754 0 - -753 -754 0 - -755 -756 0 - -755 -757 0 - -755 -758 0 - -755 -759 0 - -756 -757 0 - -756 -758 0 - -756 -759 0 - -757 -758 0 - -757 -759 0 - -758 -759 0 - -760 -761 0 - -760 -762 0 - -760 -763 0 - -760 -764 0 - -760 -765 0 - -760 -766 0 - -760 -767 0 - -761 -762 0 - -761 -763 0 - -761 -764 0 - -761 -765 0 - -761 -766 0 - -761 -767 0 - -762 -763 0 - -762 -764 0 - -762 -765 0 - -762 -766 0 - -762 -767 0 - -763 -764 0 - -763 -765 0 - -763 -766 0 - -763 -767 0 - -764 -765 0 - -764 -766 0 - -764 -767 0 - -765 -766 0 - -765 -767 0 - -766 -767 0 - -768 -769 0 - -768 -770 0 - -768 -771 0 - -768 -772 0 - -768 -773 0 - -768 -774 0 - -768 -775 0 - -768 -776 0 - -768 -777 0 - -769 -770 0 - -769 -771 0 - -769 -772 0 - -769 -773 0 - -769 -774 0 - -769 -775 0 - -769 -776 0 - -769 -777 0 - -770 -771 0 - -770 -772 0 - -770 -773 0 - -770 -774 0 - -770 -775 0 - -770 -776 0 - -770 -777 0 - -771 -772 0 - -771 -773 0 - -771 -774 0 - -771 -775 0 - -771 -776 0 - -771 -777 0 - -772 -773 0 - -772 -774 0 - -772 -775 0 - -772 -776 0 - -772 -777 0 - -773 -774 0 - -773 -775 0 - -773 -776 0 - -773 -777 0 - -774 -775 0 - -774 -776 0 - -774 -777 0 - -775 -776 0 - -775 -777 0 - -776 -777 0 - -778 -779 0 - -778 -780 0 - -778 -781 0 - -778 -782 0 - -778 -783 0 - -779 -780 0 - -779 -781 0 - -779 -782 0 - -779 -783 0 - -780 -781 0 - -780 -782 0 - -780 -783 0 - -781 -782 0 - -781 -783 0 - -782 -783 0 - -784 -785 0 - -784 -786 0 - -784 -787 0 - -784 -788 0 - -784 -789 0 - -784 -790 0 - -784 -791 0 - -784 -792 0 - -785 -786 0 - -785 -787 0 - -785 -788 0 - -785 -789 0 - -785 -790 0 - -785 -791 0 - -785 -792 0 - -786 -787 0 - -786 -788 0 - -786 -789 0 - -786 -790 0 - -786 -791 0 - -786 -792 0 - -787 -788 0 - -787 -789 0 - -787 -790 0 - -787 -791 0 - -787 -792 0 - -788 -789 0 - -788 -790 0 - -788 -791 0 - -788 -792 0 - -789 -790 0 - -789 -791 0 - -789 -792 0 - -790 -791 0 - -790 -792 0 - -791 -792 0 - -793 -794 0 - -793 -795 0 - -793 -796 0 - -793 -797 0 - -793 -798 0 - -793 -799 0 - -793 -800 0 - -793 -801 0 - -793 -802 0 - -793 -803 0 - -794 -795 0 - -794 -796 0 - -794 -797 0 - -794 -798 0 - -794 -799 0 - -794 -800 0 - -794 -801 0 - -794 -802 0 - -794 -803 0 - -795 -796 0 - -795 -797 0 - -795 -798 0 - -795 -799 0 - -795 -800 0 - -795 -801 0 - -795 -802 0 - -795 -803 0 - -796 -797 0 - -796 -798 0 - -796 -799 0 - -796 -800 0 - -796 -801 0 - -796 -802 0 - -796 -803 0 - -797 -798 0 - -797 -799 0 - -797 -800 0 - -797 -801 0 - -797 -802 0 - -797 -803 0 - -798 -799 0 - -798 -800 0 - -798 -801 0 - -798 -802 0 - -798 -803 0 - -799 -800 0 - -799 -801 0 - -799 -802 0 - -799 -803 0 - -800 -801 0 - -800 -802 0 - -800 -803 0 - -801 -802 0 - -801 -803 0 - -802 -803 0 - -804 -805 0 - -804 -806 0 - -804 -807 0 - -804 -808 0 - -804 -809 0 - -804 -810 0 - -804 -811 0 - -804 -812 0 - -804 -813 0 - -805 -806 0 - -805 -807 0 - -805 -808 0 - -805 -809 0 - -805 -810 0 - -805 -811 0 - -805 -812 0 - -805 -813 0 - -806 -807 0 - -806 -808 0 - -806 -809 0 - -806 -810 0 - -806 -811 0 - -806 -812 0 - -806 -813 0 - -807 -808 0 - -807 -809 0 - -807 -810 0 - -807 -811 0 - -807 -812 0 - -807 -813 0 - -808 -809 0 - -808 -810 0 - -808 -811 0 - -808 -812 0 - -808 -813 0 - -809 -810 0 - -809 -811 0 - -809 -812 0 - -809 -813 0 - -810 -811 0 - -810 -812 0 - -810 -813 0 - -811 -812 0 - -811 -813 0 - -812 -813 0 - -814 -815 0 - -814 -816 0 - -814 -817 0 - -814 -818 0 - -814 -819 0 - -814 -820 0 - -814 -821 0 - -814 -822 0 - -815 -816 0 - -815 -817 0 - -815 -818 0 - -815 -819 0 - -815 -820 0 - -815 -821 0 - -815 -822 0 - -816 -817 0 - -816 -818 0 - -816 -819 0 - -816 -820 0 - -816 -821 0 - -816 -822 0 - -817 -818 0 - -817 -819 0 - -817 -820 0 - -817 -821 0 - -817 -822 0 - -818 -819 0 - -818 -820 0 - -818 -821 0 - -818 -822 0 - -819 -820 0 - -819 -821 0 - -819 -822 0 - -820 -821 0 - -820 -822 0 - -821 -822 0 - -823 -824 0 - -823 -825 0 - -823 -826 0 - -823 -827 0 - -823 -828 0 - -823 -829 0 - -823 -830 0 - -824 -825 0 - -824 -826 0 - -824 -827 0 - -824 -828 0 - -824 -829 0 - -824 -830 0 - -825 -826 0 - -825 -827 0 - -825 -828 0 - -825 -829 0 - -825 -830 0 - -826 -827 0 - -826 -828 0 - -826 -829 0 - -826 -830 0 - -827 -828 0 - -827 -829 0 - -827 -830 0 - -828 -829 0 - -828 -830 0 - -829 -830 0 - -831 -832 0 - -831 -833 0 - -831 -834 0 - -831 -835 0 - -831 -836 0 - -832 -833 0 - -832 -834 0 - -832 -835 0 - -832 -836 0 - -833 -834 0 - -833 -835 0 - -833 -836 0 - -834 -835 0 - -834 -836 0 - -835 -836 0 - -837 -838 0 - -837 -839 0 - -837 -840 0 - -837 -841 0 - -838 -839 0 - -838 -840 0 - -838 -841 0 - -839 -840 0 - -839 -841 0 - -840 -841 0 - -842 -843 0 - -842 -844 0 - -842 -845 0 - -842 -846 0 - -842 -847 0 - -842 -848 0 - -842 -849 0 - -843 -844 0 - -843 -845 0 - -843 -846 0 - -843 -847 0 - -843 -848 0 - -843 -849 0 - -844 -845 0 - -844 -846 0 - -844 -847 0 - -844 -848 0 - -844 -849 0 - -845 -846 0 - -845 -847 0 - -845 -848 0 - -845 -849 0 - -846 -847 0 - -846 -848 0 - -846 -849 0 - -847 -848 0 - -847 -849 0 - -848 -849 0 - -850 -851 0 - -850 -852 0 - -850 -853 0 - -850 -854 0 - -850 -855 0 - -850 -856 0 - -850 -857 0 - -850 -858 0 - -851 -852 0 - -851 -853 0 - -851 -854 0 - -851 -855 0 - -851 -856 0 - -851 -857 0 - -851 -858 0 - -852 -853 0 - -852 -854 0 - -852 -855 0 - -852 -856 0 - -852 -857 0 - -852 -858 0 - -853 -854 0 - -853 -855 0 - -853 -856 0 - -853 -857 0 - -853 -858 0 - -854 -855 0 - -854 -856 0 - -854 -857 0 - -854 -858 0 - -855 -856 0 - -855 -857 0 - -855 -858 0 - -856 -857 0 - -856 -858 0 - -857 -858 0 - -859 -860 0 - -859 -861 0 - -859 -862 0 - -859 -863 0 - -859 -864 0 - -860 -861 0 - -860 -862 0 - -860 -863 0 - -860 -864 0 - -861 -862 0 - -861 -863 0 - -861 -864 0 - -862 -863 0 - -862 -864 0 - -863 -864 0 - -865 -866 0 - -865 -867 0 - -865 -868 0 - -865 -869 0 - -865 -870 0 - -865 -871 0 - -865 -872 0 - -865 -873 0 - -866 -867 0 - -866 -868 0 - -866 -869 0 - -866 -870 0 - -866 -871 0 - -866 -872 0 - -866 -873 0 - -867 -868 0 - -867 -869 0 - -867 -870 0 - -867 -871 0 - -867 -872 0 - -867 -873 0 - -868 -869 0 - -868 -870 0 - -868 -871 0 - -868 -872 0 - -868 -873 0 - -869 -870 0 - -869 -871 0 - -869 -872 0 - -869 -873 0 - -870 -871 0 - -870 -872 0 - -870 -873 0 - -871 -872 0 - -871 -873 0 - -872 -873 0 - -874 -875 0 - -874 -876 0 - -875 -876 0 - -877 -878 0 - -877 -879 0 - -877 -880 0 - -877 -881 0 - -877 -882 0 - -878 -879 0 - -878 -880 0 - -878 -881 0 - -878 -882 0 - -879 -880 0 - -879 -881 0 - -879 -882 0 - -880 -881 0 - -880 -882 0 - -881 -882 0 - -883 -884 0 - -883 -885 0 - -883 -886 0 - -883 -887 0 - -883 -888 0 - -883 -889 0 - -884 -885 0 - -884 -886 0 - -884 -887 0 - -884 -888 0 - -884 -889 0 - -885 -886 0 - -885 -887 0 - -885 -888 0 - -885 -889 0 - -886 -887 0 - -886 -888 0 - -886 -889 0 - -887 -888 0 - -887 -889 0 - -888 -889 0 - -890 -891 0 - -890 -892 0 - -890 -893 0 - -890 -894 0 - -891 -892 0 - -891 -893 0 - -891 -894 0 - -892 -893 0 - -892 -894 0 - -893 -894 0 - -895 -896 0 - -895 -897 0 - -895 -898 0 - -895 -899 0 - -896 -897 0 - -896 -898 0 - -896 -899 0 - -897 -898 0 - -897 -899 0 - -898 -899 0 - -900 -901 0 - -900 -902 0 - -901 -902 0 - -903 -904 0 - -903 -905 0 - -903 -906 0 - -903 -907 0 - -903 -908 0 - -903 -909 0 - -903 -910 0 - -903 -911 0 - -904 -905 0 - -904 -906 0 - -904 -907 0 - -904 -908 0 - -904 -909 0 - -904 -910 0 - -904 -911 0 - -905 -906 0 - -905 -907 0 - -905 -908 0 - -905 -909 0 - -905 -910 0 - -905 -911 0 - -906 -907 0 - -906 -908 0 - -906 -909 0 - -906 -910 0 - -906 -911 0 - -907 -908 0 - -907 -909 0 - -907 -910 0 - -907 -911 0 - -908 -909 0 - -908 -910 0 - -908 -911 0 - -909 -910 0 - -909 -911 0 - -910 -911 0 - -912 -913 0 - -912 -914 0 - -912 -915 0 - -912 -916 0 - -912 -917 0 - -912 -918 0 - -913 -914 0 - -913 -915 0 - -913 -916 0 - -913 -917 0 - -913 -918 0 - -914 -915 0 - -914 -916 0 - -914 -917 0 - -914 -918 0 - -915 -916 0 - -915 -917 0 - -915 -918 0 - -916 -917 0 - -916 -918 0 - -917 -918 0 - -919 -920 0 - -919 -921 0 - -919 -922 0 - -919 -923 0 - -920 -921 0 - -920 -922 0 - -920 -923 0 - -921 -922 0 - -921 -923 0 - -922 -923 0 - -924 -925 0 - -924 -926 0 - -924 -927 0 - -924 -928 0 - -924 -929 0 - -924 -930 0 - -924 -931 0 - -924 -932 0 - -924 -933 0 - -924 -934 0 - -925 -926 0 - -925 -927 0 - -925 -928 0 - -925 -929 0 - -925 -930 0 - -925 -931 0 - -925 -932 0 - -925 -933 0 - -925 -934 0 - -926 -927 0 - -926 -928 0 - -926 -929 0 - -926 -930 0 - -926 -931 0 - -926 -932 0 - -926 -933 0 - -926 -934 0 - -927 -928 0 - -927 -929 0 - -927 -930 0 - -927 -931 0 - -927 -932 0 - -927 -933 0 - -927 -934 0 - -928 -929 0 - -928 -930 0 - -928 -931 0 - -928 -932 0 - -928 -933 0 - -928 -934 0 - -929 -930 0 - -929 -931 0 - -929 -932 0 - -929 -933 0 - -929 -934 0 - -930 -931 0 - -930 -932 0 - -930 -933 0 - -930 -934 0 - -931 -932 0 - -931 -933 0 - -931 -934 0 - -932 -933 0 - -932 -934 0 - -933 -934 0 - -935 -936 0 - -935 -937 0 - -935 -938 0 - -935 -939 0 - -935 -940 0 - -935 -941 0 - -935 -942 0 - -936 -937 0 - -936 -938 0 - -936 -939 0 - -936 -940 0 - -936 -941 0 - -936 -942 0 - -937 -938 0 - -937 -939 0 - -937 -940 0 - -937 -941 0 - -937 -942 0 - -938 -939 0 - -938 -940 0 - -938 -941 0 - -938 -942 0 - -939 -940 0 - -939 -941 0 - -939 -942 0 - -940 -941 0 - -940 -942 0 - -941 -942 0 - -943 -944 0 - -943 -945 0 - -943 -946 0 - -943 -947 0 - -943 -948 0 - -943 -949 0 - -943 -950 0 - -944 -945 0 - -944 -946 0 - -944 -947 0 - -944 -948 0 - -944 -949 0 - -944 -950 0 - -945 -946 0 - -945 -947 0 - -945 -948 0 - -945 -949 0 - -945 -950 0 - -946 -947 0 - -946 -948 0 - -946 -949 0 - -946 -950 0 - -947 -948 0 - -947 -949 0 - -947 -950 0 - -948 -949 0 - -948 -950 0 - -949 -950 0 - -951 -952 0 - -951 -953 0 - -951 -954 0 - -951 -955 0 - -951 -956 0 - -951 -957 0 - -952 -953 0 - -952 -954 0 - -952 -955 0 - -952 -956 0 - -952 -957 0 - -953 -954 0 - -953 -955 0 - -953 -956 0 - -953 -957 0 - -954 -955 0 - -954 -956 0 - -954 -957 0 - -955 -956 0 - -955 -957 0 - -956 -957 0 - -958 -959 0 - -958 -960 0 - -958 -961 0 - -958 -962 0 - -958 -963 0 - -958 -964 0 - -958 -965 0 - -959 -960 0 - -959 -961 0 - -959 -962 0 - -959 -963 0 - -959 -964 0 - -959 -965 0 - -960 -961 0 - -960 -962 0 - -960 -963 0 - -960 -964 0 - -960 -965 0 - -961 -962 0 - -961 -963 0 - -961 -964 0 - -961 -965 0 - -962 -963 0 - -962 -964 0 - -962 -965 0 - -963 -964 0 - -963 -965 0 - -964 -965 0 - -966 -967 0 - -966 -968 0 - -966 -969 0 - -966 -970 0 - -966 -971 0 - -966 -972 0 - -966 -973 0 - -967 -968 0 - -967 -969 0 - -967 -970 0 - -967 -971 0 - -967 -972 0 - -967 -973 0 - -968 -969 0 - -968 -970 0 - -968 -971 0 - -968 -972 0 - -968 -973 0 - -969 -970 0 - -969 -971 0 - -969 -972 0 - -969 -973 0 - -970 -971 0 - -970 -972 0 - -970 -973 0 - -971 -972 0 - -971 -973 0 - -972 -973 0 - -974 -975 0 - -974 -976 0 - -974 -977 0 - -974 -978 0 - -974 -979 0 - -974 -980 0 - -974 -981 0 - -974 -982 0 - -975 -976 0 - -975 -977 0 - -975 -978 0 - -975 -979 0 - -975 -980 0 - -975 -981 0 - -975 -982 0 - -976 -977 0 - -976 -978 0 - -976 -979 0 - -976 -980 0 - -976 -981 0 - -976 -982 0 - -977 -978 0 - -977 -979 0 - -977 -980 0 - -977 -981 0 - -977 -982 0 - -978 -979 0 - -978 -980 0 - -978 -981 0 - -978 -982 0 - -979 -980 0 - -979 -981 0 - -979 -982 0 - -980 -981 0 - -980 -982 0 - -981 -982 0 - -983 -984 0 - -983 -985 0 - -983 -986 0 - -983 -987 0 - -983 -988 0 - -983 -989 0 - -983 -990 0 - -984 -985 0 - -984 -986 0 - -984 -987 0 - -984 -988 0 - -984 -989 0 - -984 -990 0 - -985 -986 0 - -985 -987 0 - -985 -988 0 - -985 -989 0 - -985 -990 0 - -986 -987 0 - -986 -988 0 - -986 -989 0 - -986 -990 0 - -987 -988 0 - -987 -989 0 - -987 -990 0 - -988 -989 0 - -988 -990 0 - -989 -990 0 - -991 -992 0 - -991 -993 0 - -991 -994 0 - -991 -995 0 - -991 -996 0 - -991 -997 0 - -992 -993 0 - -992 -994 0 - -992 -995 0 - -992 -996 0 - -992 -997 0 - -993 -994 0 - -993 -995 0 - -993 -996 0 - -993 -997 0 - -994 -995 0 - -994 -996 0 - -994 -997 0 - -995 -996 0 - -995 -997 0 - -996 -997 0 - -998 -999 0 - -998 -1000 0 - -998 -1001 0 - -998 -1002 0 - -998 -1003 0 - -999 -1000 0 - -999 -1001 0 - -999 -1002 0 - -999 -1003 0 - -1000 -1001 0 - -1000 -1002 0 - -1000 -1003 0 - -1001 -1002 0 - -1001 -1003 0 - -1002 -1003 0 - -1004 -1005 0 - -1004 -1006 0 - -1004 -1007 0 - -1004 -1008 0 - -1004 -1009 0 - -1004 -1010 0 - -1004 -1011 0 - -1004 -1012 0 - -1004 -1013 0 - -1004 -1014 0 - -1005 -1006 0 - -1005 -1007 0 - -1005 -1008 0 - -1005 -1009 0 - -1005 -1010 0 - -1005 -1011 0 - -1005 -1012 0 - -1005 -1013 0 - -1005 -1014 0 - -1006 -1007 0 - -1006 -1008 0 - -1006 -1009 0 - -1006 -1010 0 - -1006 -1011 0 - -1006 -1012 0 - -1006 -1013 0 - -1006 -1014 0 - -1007 -1008 0 - -1007 -1009 0 - -1007 -1010 0 - -1007 -1011 0 - -1007 -1012 0 - -1007 -1013 0 - -1007 -1014 0 - -1008 -1009 0 - -1008 -1010 0 - -1008 -1011 0 - -1008 -1012 0 - -1008 -1013 0 - -1008 -1014 0 - -1009 -1010 0 - -1009 -1011 0 - -1009 -1012 0 - -1009 -1013 0 - -1009 -1014 0 - -1010 -1011 0 - -1010 -1012 0 - -1010 -1013 0 - -1010 -1014 0 - -1011 -1012 0 - -1011 -1013 0 - -1011 -1014 0 - -1012 -1013 0 - -1012 -1014 0 - -1013 -1014 0 - -1015 -1016 0 - -1015 -1017 0 - -1015 -1018 0 - -1015 -1019 0 - -1015 -1020 0 - -1015 -1021 0 - -1015 -1022 0 - -1015 -1023 0 - -1015 -1024 0 - -1016 -1017 0 - -1016 -1018 0 - -1016 -1019 0 - -1016 -1020 0 - -1016 -1021 0 - -1016 -1022 0 - -1016 -1023 0 - -1016 -1024 0 - -1017 -1018 0 - -1017 -1019 0 - -1017 -1020 0 - -1017 -1021 0 - -1017 -1022 0 - -1017 -1023 0 - -1017 -1024 0 - -1018 -1019 0 - -1018 -1020 0 - -1018 -1021 0 - -1018 -1022 0 - -1018 -1023 0 - -1018 -1024 0 - -1019 -1020 0 - -1019 -1021 0 - -1019 -1022 0 - -1019 -1023 0 - -1019 -1024 0 - -1020 -1021 0 - -1020 -1022 0 - -1020 -1023 0 - -1020 -1024 0 - -1021 -1022 0 - -1021 -1023 0 - -1021 -1024 0 - -1022 -1023 0 - -1022 -1024 0 - -1023 -1024 0 - -1025 -1026 0 - -1025 -1027 0 - -1025 -1028 0 - -1025 -1029 0 - -1025 -1030 0 - -1025 -1031 0 - -1026 -1027 0 - -1026 -1028 0 - -1026 -1029 0 - -1026 -1030 0 - -1026 -1031 0 - -1027 -1028 0 - -1027 -1029 0 - -1027 -1030 0 - -1027 -1031 0 - -1028 -1029 0 - -1028 -1030 0 - -1028 -1031 0 - -1029 -1030 0 - -1029 -1031 0 - -1030 -1031 0 - -1032 -1033 0 - -1032 -1034 0 - -1032 -1035 0 - -1032 -1036 0 - -1032 -1037 0 - -1032 -1038 0 - -1032 -1039 0 - -1033 -1034 0 - -1033 -1035 0 - -1033 -1036 0 - -1033 -1037 0 - -1033 -1038 0 - -1033 -1039 0 - -1034 -1035 0 - -1034 -1036 0 - -1034 -1037 0 - -1034 -1038 0 - -1034 -1039 0 - -1035 -1036 0 - -1035 -1037 0 - -1035 -1038 0 - -1035 -1039 0 - -1036 -1037 0 - -1036 -1038 0 - -1036 -1039 0 - -1037 -1038 0 - -1037 -1039 0 - -1038 -1039 0 - -1040 -1041 0 - -1040 -1042 0 - -1040 -1043 0 - -1040 -1044 0 - -1040 -1045 0 - -1041 -1042 0 - -1041 -1043 0 - -1041 -1044 0 - -1041 -1045 0 - -1042 -1043 0 - -1042 -1044 0 - -1042 -1045 0 - -1043 -1044 0 - -1043 -1045 0 - -1044 -1045 0 - -1046 -1047 0 - -1046 -1048 0 - -1046 -1049 0 - -1046 -1050 0 - -1046 -1051 0 - -1046 -1052 0 - -1046 -1053 0 - -1046 -1054 0 - -1046 -1055 0 - -1046 -1056 0 - -1046 -1057 0 - -1046 -1058 0 - -1047 -1048 0 - -1047 -1049 0 - -1047 -1050 0 - -1047 -1051 0 - -1047 -1052 0 - -1047 -1053 0 - -1047 -1054 0 - -1047 -1055 0 - -1047 -1056 0 - -1047 -1057 0 - -1047 -1058 0 - -1048 -1049 0 - -1048 -1050 0 - -1048 -1051 0 - -1048 -1052 0 - -1048 -1053 0 - -1048 -1054 0 - -1048 -1055 0 - -1048 -1056 0 - -1048 -1057 0 - -1048 -1058 0 - -1049 -1050 0 - -1049 -1051 0 - -1049 -1052 0 - -1049 -1053 0 - -1049 -1054 0 - -1049 -1055 0 - -1049 -1056 0 - -1049 -1057 0 - -1049 -1058 0 - -1050 -1051 0 - -1050 -1052 0 - -1050 -1053 0 - -1050 -1054 0 - -1050 -1055 0 - -1050 -1056 0 - -1050 -1057 0 - -1050 -1058 0 - -1051 -1052 0 - -1051 -1053 0 - -1051 -1054 0 - -1051 -1055 0 - -1051 -1056 0 - -1051 -1057 0 - -1051 -1058 0 - -1052 -1053 0 - -1052 -1054 0 - -1052 -1055 0 - -1052 -1056 0 - -1052 -1057 0 - -1052 -1058 0 - -1053 -1054 0 - -1053 -1055 0 - -1053 -1056 0 - -1053 -1057 0 - -1053 -1058 0 - -1054 -1055 0 - -1054 -1056 0 - -1054 -1057 0 - -1054 -1058 0 - -1055 -1056 0 - -1055 -1057 0 - -1055 -1058 0 - -1056 -1057 0 - -1056 -1058 0 - -1057 -1058 0 - -1059 -1060 0 - -1059 -1061 0 - -1059 -1062 0 - -1059 -1063 0 - -1059 -1064 0 - -1059 -1065 0 - -1060 -1061 0 - -1060 -1062 0 - -1060 -1063 0 - -1060 -1064 0 - -1060 -1065 0 - -1061 -1062 0 - -1061 -1063 0 - -1061 -1064 0 - -1061 -1065 0 - -1062 -1063 0 - -1062 -1064 0 - -1062 -1065 0 - -1063 -1064 0 - -1063 -1065 0 - -1064 -1065 0 - -1066 -1067 0 - -1066 -1068 0 - -1066 -1069 0 - -1066 -1070 0 - -1066 -1071 0 - -1066 -1072 0 - -1066 -1073 0 - -1066 -1074 0 - -1066 -1075 0 - -1066 -1076 0 - -1066 -1077 0 - -1066 -1078 0 - -1066 -1079 0 - -1067 -1068 0 - -1067 -1069 0 - -1067 -1070 0 - -1067 -1071 0 - -1067 -1072 0 - -1067 -1073 0 - -1067 -1074 0 - -1067 -1075 0 - -1067 -1076 0 - -1067 -1077 0 - -1067 -1078 0 - -1067 -1079 0 - -1068 -1069 0 - -1068 -1070 0 - -1068 -1071 0 - -1068 -1072 0 - -1068 -1073 0 - -1068 -1074 0 - -1068 -1075 0 - -1068 -1076 0 - -1068 -1077 0 - -1068 -1078 0 - -1068 -1079 0 - -1069 -1070 0 - -1069 -1071 0 - -1069 -1072 0 - -1069 -1073 0 - -1069 -1074 0 - -1069 -1075 0 - -1069 -1076 0 - -1069 -1077 0 - -1069 -1078 0 - -1069 -1079 0 - -1070 -1071 0 - -1070 -1072 0 - -1070 -1073 0 - -1070 -1074 0 - -1070 -1075 0 - -1070 -1076 0 - -1070 -1077 0 - -1070 -1078 0 - -1070 -1079 0 - -1071 -1072 0 - -1071 -1073 0 - -1071 -1074 0 - -1071 -1075 0 - -1071 -1076 0 - -1071 -1077 0 - -1071 -1078 0 - -1071 -1079 0 - -1072 -1073 0 - -1072 -1074 0 - -1072 -1075 0 - -1072 -1076 0 - -1072 -1077 0 - -1072 -1078 0 - -1072 -1079 0 - -1073 -1074 0 - -1073 -1075 0 - -1073 -1076 0 - -1073 -1077 0 - -1073 -1078 0 - -1073 -1079 0 - -1074 -1075 0 - -1074 -1076 0 - -1074 -1077 0 - -1074 -1078 0 - -1074 -1079 0 - -1075 -1076 0 - -1075 -1077 0 - -1075 -1078 0 - -1075 -1079 0 - -1076 -1077 0 - -1076 -1078 0 - -1076 -1079 0 - -1077 -1078 0 - -1077 -1079 0 - -1078 -1079 0 - -1080 -1081 0 - -1080 -1082 0 - -1080 -1083 0 - -1080 -1084 0 - -1080 -1085 0 - -1080 -1086 0 - -1080 -1087 0 - -1080 -1088 0 - -1081 -1082 0 - -1081 -1083 0 - -1081 -1084 0 - -1081 -1085 0 - -1081 -1086 0 - -1081 -1087 0 - -1081 -1088 0 - -1082 -1083 0 - -1082 -1084 0 - -1082 -1085 0 - -1082 -1086 0 - -1082 -1087 0 - -1082 -1088 0 - -1083 -1084 0 - -1083 -1085 0 - -1083 -1086 0 - -1083 -1087 0 - -1083 -1088 0 - -1084 -1085 0 - -1084 -1086 0 - -1084 -1087 0 - -1084 -1088 0 - -1085 -1086 0 - -1085 -1087 0 - -1085 -1088 0 - -1086 -1087 0 - -1086 -1088 0 - -1087 -1088 0 - -1089 -1090 0 - -1089 -1091 0 - -1089 -1092 0 - -1089 -1093 0 - -1089 -1094 0 - -1090 -1091 0 - -1090 -1092 0 - -1090 -1093 0 - -1090 -1094 0 - -1091 -1092 0 - -1091 -1093 0 - -1091 -1094 0 - -1092 -1093 0 - -1092 -1094 0 - -1093 -1094 0 - -1095 -1096 0 - -1095 -1097 0 - -1095 -1098 0 - -1095 -1099 0 - -1095 -1100 0 - -1095 -1101 0 - -1095 -1102 0 - -1095 -1103 0 - -1096 -1097 0 - -1096 -1098 0 - -1096 -1099 0 - -1096 -1100 0 - -1096 -1101 0 - -1096 -1102 0 - -1096 -1103 0 - -1097 -1098 0 - -1097 -1099 0 - -1097 -1100 0 - -1097 -1101 0 - -1097 -1102 0 - -1097 -1103 0 - -1098 -1099 0 - -1098 -1100 0 - -1098 -1101 0 - -1098 -1102 0 - -1098 -1103 0 - -1099 -1100 0 - -1099 -1101 0 - -1099 -1102 0 - -1099 -1103 0 - -1100 -1101 0 - -1100 -1102 0 - -1100 -1103 0 - -1101 -1102 0 - -1101 -1103 0 - -1102 -1103 0 - -1104 -1105 0 - -1104 -1106 0 - -1104 -1107 0 - -1104 -1108 0 - -1104 -1109 0 - -1105 -1106 0 - -1105 -1107 0 - -1105 -1108 0 - -1105 -1109 0 - -1106 -1107 0 - -1106 -1108 0 - -1106 -1109 0 - -1107 -1108 0 - -1107 -1109 0 - -1108 -1109 0 - -1110 -1111 0 - -1110 -1112 0 - -1110 -1113 0 - -1110 -1114 0 - -1110 -1115 0 - -1110 -1116 0 - -1110 -1117 0 - -1110 -1118 0 - -1111 -1112 0 - -1111 -1113 0 - -1111 -1114 0 - -1111 -1115 0 - -1111 -1116 0 - -1111 -1117 0 - -1111 -1118 0 - -1112 -1113 0 - -1112 -1114 0 - -1112 -1115 0 - -1112 -1116 0 - -1112 -1117 0 - -1112 -1118 0 - -1113 -1114 0 - -1113 -1115 0 - -1113 -1116 0 - -1113 -1117 0 - -1113 -1118 0 - -1114 -1115 0 - -1114 -1116 0 - -1114 -1117 0 - -1114 -1118 0 - -1115 -1116 0 - -1115 -1117 0 - -1115 -1118 0 - -1116 -1117 0 - -1116 -1118 0 - -1117 -1118 0 - -1119 -1120 0 - -1119 -1121 0 - -1119 -1122 0 - -1119 -1123 0 - -1119 -1124 0 - -1120 -1121 0 - -1120 -1122 0 - -1120 -1123 0 - -1120 -1124 0 - -1121 -1122 0 - -1121 -1123 0 - -1121 -1124 0 - -1122 -1123 0 - -1122 -1124 0 - -1123 -1124 0 - -1125 -1126 0 - -1125 -1127 0 - -1125 -1128 0 - -1125 -1129 0 - -1125 -1130 0 - -1125 -1131 0 - -1125 -1132 0 - -1125 -1133 0 - -1125 -1134 0 - -1125 -1135 0 - -1126 -1127 0 - -1126 -1128 0 - -1126 -1129 0 - -1126 -1130 0 - -1126 -1131 0 - -1126 -1132 0 - -1126 -1133 0 - -1126 -1134 0 - -1126 -1135 0 - -1127 -1128 0 - -1127 -1129 0 - -1127 -1130 0 - -1127 -1131 0 - -1127 -1132 0 - -1127 -1133 0 - -1127 -1134 0 - -1127 -1135 0 - -1128 -1129 0 - -1128 -1130 0 - -1128 -1131 0 - -1128 -1132 0 - -1128 -1133 0 - -1128 -1134 0 - -1128 -1135 0 - -1129 -1130 0 - -1129 -1131 0 - -1129 -1132 0 - -1129 -1133 0 - -1129 -1134 0 - -1129 -1135 0 - -1130 -1131 0 - -1130 -1132 0 - -1130 -1133 0 - -1130 -1134 0 - -1130 -1135 0 - -1131 -1132 0 - -1131 -1133 0 - -1131 -1134 0 - -1131 -1135 0 - -1132 -1133 0 - -1132 -1134 0 - -1132 -1135 0 - -1133 -1134 0 - -1133 -1135 0 - -1134 -1135 0 - -1136 -1137 0 - -1136 -1138 0 - -1136 -1139 0 - -1136 -1140 0 - -1136 -1141 0 - -1136 -1142 0 - -1137 -1138 0 - -1137 -1139 0 - -1137 -1140 0 - -1137 -1141 0 - -1137 -1142 0 - -1138 -1139 0 - -1138 -1140 0 - -1138 -1141 0 - -1138 -1142 0 - -1139 -1140 0 - -1139 -1141 0 - -1139 -1142 0 - -1140 -1141 0 - -1140 -1142 0 - -1141 -1142 0 - -1143 -1144 0 - -1143 -1145 0 - -1143 -1146 0 - -1143 -1147 0 - -1143 -1148 0 - -1143 -1149 0 - -1143 -1150 0 - -1144 -1145 0 - -1144 -1146 0 - -1144 -1147 0 - -1144 -1148 0 - -1144 -1149 0 - -1144 -1150 0 - -1145 -1146 0 - -1145 -1147 0 - -1145 -1148 0 - -1145 -1149 0 - -1145 -1150 0 - -1146 -1147 0 - -1146 -1148 0 - -1146 -1149 0 - -1146 -1150 0 - -1147 -1148 0 - -1147 -1149 0 - -1147 -1150 0 - -1148 -1149 0 - -1148 -1150 0 - -1149 -1150 0 - -1151 -1152 0 - -1151 -1153 0 - -1151 -1154 0 - -1151 -1155 0 - -1151 -1156 0 - -1152 -1153 0 - -1152 -1154 0 - -1152 -1155 0 - -1152 -1156 0 - -1153 -1154 0 - -1153 -1155 0 - -1153 -1156 0 - -1154 -1155 0 - -1154 -1156 0 - -1155 -1156 0 - -1157 -1158 0 - -1157 -1159 0 - -1157 -1160 0 - -1157 -1161 0 - -1157 -1162 0 - -1157 -1163 0 - -1157 -1164 0 - -1157 -1165 0 - -1158 -1159 0 - -1158 -1160 0 - -1158 -1161 0 - -1158 -1162 0 - -1158 -1163 0 - -1158 -1164 0 - -1158 -1165 0 - -1159 -1160 0 - -1159 -1161 0 - -1159 -1162 0 - -1159 -1163 0 - -1159 -1164 0 - -1159 -1165 0 - -1160 -1161 0 - -1160 -1162 0 - -1160 -1163 0 - -1160 -1164 0 - -1160 -1165 0 - -1161 -1162 0 - -1161 -1163 0 - -1161 -1164 0 - -1161 -1165 0 - -1162 -1163 0 - -1162 -1164 0 - -1162 -1165 0 - -1163 -1164 0 - -1163 -1165 0 - -1164 -1165 0 - -1166 -1167 0 - -1166 -1168 0 - -1166 -1169 0 - -1166 -1170 0 - -1166 -1171 0 - -1166 -1172 0 - -1166 -1173 0 - -1166 -1174 0 - -1166 -1175 0 - -1167 -1168 0 - -1167 -1169 0 - -1167 -1170 0 - -1167 -1171 0 - -1167 -1172 0 - -1167 -1173 0 - -1167 -1174 0 - -1167 -1175 0 - -1168 -1169 0 - -1168 -1170 0 - -1168 -1171 0 - -1168 -1172 0 - -1168 -1173 0 - -1168 -1174 0 - -1168 -1175 0 - -1169 -1170 0 - -1169 -1171 0 - -1169 -1172 0 - -1169 -1173 0 - -1169 -1174 0 - -1169 -1175 0 - -1170 -1171 0 - -1170 -1172 0 - -1170 -1173 0 - -1170 -1174 0 - -1170 -1175 0 - -1171 -1172 0 - -1171 -1173 0 - -1171 -1174 0 - -1171 -1175 0 - -1172 -1173 0 - -1172 -1174 0 - -1172 -1175 0 - -1173 -1174 0 - -1173 -1175 0 - -1174 -1175 0 - -1176 -1177 0 - -1176 -1178 0 - -1176 -1179 0 - -1176 -1180 0 - -1176 -1181 0 - -1176 -1182 0 - -1176 -1183 0 - -1176 -1184 0 - -1176 -1185 0 - -1177 -1178 0 - -1177 -1179 0 - -1177 -1180 0 - -1177 -1181 0 - -1177 -1182 0 - -1177 -1183 0 - -1177 -1184 0 - -1177 -1185 0 - -1178 -1179 0 - -1178 -1180 0 - -1178 -1181 0 - -1178 -1182 0 - -1178 -1183 0 - -1178 -1184 0 - -1178 -1185 0 - -1179 -1180 0 - -1179 -1181 0 - -1179 -1182 0 - -1179 -1183 0 - -1179 -1184 0 - -1179 -1185 0 - -1180 -1181 0 - -1180 -1182 0 - -1180 -1183 0 - -1180 -1184 0 - -1180 -1185 0 - -1181 -1182 0 - -1181 -1183 0 - -1181 -1184 0 - -1181 -1185 0 - -1182 -1183 0 - -1182 -1184 0 - -1182 -1185 0 - -1183 -1184 0 - -1183 -1185 0 - -1184 -1185 0 - -1186 -1187 0 - -1186 -1188 0 - -1186 -1189 0 - -1186 -1190 0 - -1186 -1191 0 - -1186 -1192 0 - -1186 -1193 0 - -1186 -1194 0 - -1186 -1195 0 - -1186 -1196 0 - -1187 -1188 0 - -1187 -1189 0 - -1187 -1190 0 - -1187 -1191 0 - -1187 -1192 0 - -1187 -1193 0 - -1187 -1194 0 - -1187 -1195 0 - -1187 -1196 0 - -1188 -1189 0 - -1188 -1190 0 - -1188 -1191 0 - -1188 -1192 0 - -1188 -1193 0 - -1188 -1194 0 - -1188 -1195 0 - -1188 -1196 0 - -1189 -1190 0 - -1189 -1191 0 - -1189 -1192 0 - -1189 -1193 0 - -1189 -1194 0 - -1189 -1195 0 - -1189 -1196 0 - -1190 -1191 0 - -1190 -1192 0 - -1190 -1193 0 - -1190 -1194 0 - -1190 -1195 0 - -1190 -1196 0 - -1191 -1192 0 - -1191 -1193 0 - -1191 -1194 0 - -1191 -1195 0 - -1191 -1196 0 - -1192 -1193 0 - -1192 -1194 0 - -1192 -1195 0 - -1192 -1196 0 - -1193 -1194 0 - -1193 -1195 0 - -1193 -1196 0 - -1194 -1195 0 - -1194 -1196 0 - -1195 -1196 0 - -1197 -1198 0 - -1197 -1199 0 - -1197 -1200 0 - -1197 -1201 0 - -1198 -1199 0 - -1198 -1200 0 - -1198 -1201 0 - -1199 -1200 0 - -1199 -1201 0 - -1200 -1201 0 - -1202 -1203 0 - -1202 -1204 0 - -1202 -1205 0 - -1202 -1206 0 - -1203 -1204 0 - -1203 -1205 0 - -1203 -1206 0 - -1204 -1205 0 - -1204 -1206 0 - -1205 -1206 0 - -1207 -1208 0 - -1207 -1209 0 - -1207 -1210 0 - -1207 -1211 0 - -1208 -1209 0 - -1208 -1210 0 - -1208 -1211 0 - -1209 -1210 0 - -1209 -1211 0 - -1210 -1211 0 - -1212 -1213 0 - -1212 -1214 0 - -1212 -1215 0 - -1213 -1214 0 - -1213 -1215 0 - -1214 -1215 0 - -1216 -1217 0 - -1216 -1218 0 - -1216 -1219 0 - -1216 -1220 0 - -1216 -1221 0 - -1217 -1218 0 - -1217 -1219 0 - -1217 -1220 0 - -1217 -1221 0 - -1218 -1219 0 - -1218 -1220 0 - -1218 -1221 0 - -1219 -1220 0 - -1219 -1221 0 - -1220 -1221 0 - -1222 -1223 0 - -1222 -1224 0 - -1222 -1225 0 - -1222 -1226 0 - -1222 -1227 0 - -1223 -1224 0 - -1223 -1225 0 - -1223 -1226 0 - -1223 -1227 0 - -1224 -1225 0 - -1224 -1226 0 - -1224 -1227 0 - -1225 -1226 0 - -1225 -1227 0 - -1226 -1227 0 - -1228 -1229 0 - -1228 -1230 0 - -1228 -1231 0 - -1228 -1232 0 - -1228 -1233 0 - -1228 -1234 0 - -1228 -1235 0 - -1229 -1230 0 - -1229 -1231 0 - -1229 -1232 0 - -1229 -1233 0 - -1229 -1234 0 - -1229 -1235 0 - -1230 -1231 0 - -1230 -1232 0 - -1230 -1233 0 - -1230 -1234 0 - -1230 -1235 0 - -1231 -1232 0 - -1231 -1233 0 - -1231 -1234 0 - -1231 -1235 0 - -1232 -1233 0 - -1232 -1234 0 - -1232 -1235 0 - -1233 -1234 0 - -1233 -1235 0 - -1234 -1235 0 - -1236 -1237 0 - -1236 -1238 0 - -1236 -1239 0 - -1236 -1240 0 - -1236 -1241 0 - -1236 -1242 0 - -1236 -1243 0 - -1237 -1238 0 - -1237 -1239 0 - -1237 -1240 0 - -1237 -1241 0 - -1237 -1242 0 - -1237 -1243 0 - -1238 -1239 0 - -1238 -1240 0 - -1238 -1241 0 - -1238 -1242 0 - -1238 -1243 0 - -1239 -1240 0 - -1239 -1241 0 - -1239 -1242 0 - -1239 -1243 0 - -1240 -1241 0 - -1240 -1242 0 - -1240 -1243 0 - -1241 -1242 0 - -1241 -1243 0 - -1242 -1243 0 - -1244 -1245 0 - -1244 -1246 0 - -1244 -1247 0 - -1244 -1248 0 - -1245 -1246 0 - -1245 -1247 0 - -1245 -1248 0 - -1246 -1247 0 - -1246 -1248 0 - -1247 -1248 0 - -1249 -1250 0 - -1249 -1251 0 - -1249 -1252 0 - -1249 -1253 0 - -1250 -1251 0 - -1250 -1252 0 - -1250 -1253 0 - -1251 -1252 0 - -1251 -1253 0 - -1252 -1253 0 - -1254 -1255 0 - -1254 -1256 0 - -1254 -1257 0 - -1254 -1258 0 - -1254 -1259 0 - -1254 -1260 0 - -1254 -1261 0 - -1255 -1256 0 - -1255 -1257 0 - -1255 -1258 0 - -1255 -1259 0 - -1255 -1260 0 - -1255 -1261 0 - -1256 -1257 0 - -1256 -1258 0 - -1256 -1259 0 - -1256 -1260 0 - -1256 -1261 0 - -1257 -1258 0 - -1257 -1259 0 - -1257 -1260 0 - -1257 -1261 0 - -1258 -1259 0 - -1258 -1260 0 - -1258 -1261 0 - -1259 -1260 0 - -1259 -1261 0 - -1260 -1261 0 - -1262 -1263 0 - -1262 -1264 0 - -1262 -1265 0 - -1262 -1266 0 - -1262 -1267 0 - -1262 -1268 0 - -1263 -1264 0 - -1263 -1265 0 - -1263 -1266 0 - -1263 -1267 0 - -1263 -1268 0 - -1264 -1265 0 - -1264 -1266 0 - -1264 -1267 0 - -1264 -1268 0 - -1265 -1266 0 - -1265 -1267 0 - -1265 -1268 0 - -1266 -1267 0 - -1266 -1268 0 - -1267 -1268 0 - -1269 -1270 0 - -1269 -1271 0 - -1269 -1272 0 - -1269 -1273 0 - -1270 -1271 0 - -1270 -1272 0 - -1270 -1273 0 - -1271 -1272 0 - -1271 -1273 0 - -1272 -1273 0 - -1274 -1275 0 - -1274 -1276 0 - -1274 -1277 0 - -1274 -1278 0 - -1274 -1279 0 - -1274 -1280 0 - -1274 -1281 0 - -1275 -1276 0 - -1275 -1277 0 - -1275 -1278 0 - -1275 -1279 0 - -1275 -1280 0 - -1275 -1281 0 - -1276 -1277 0 - -1276 -1278 0 - -1276 -1279 0 - -1276 -1280 0 - -1276 -1281 0 - -1277 -1278 0 - -1277 -1279 0 - -1277 -1280 0 - -1277 -1281 0 - -1278 -1279 0 - -1278 -1280 0 - -1278 -1281 0 - -1279 -1280 0 - -1279 -1281 0 - -1280 -1281 0 - -1282 -1283 0 - -1282 -1284 0 - -1282 -1285 0 - -1282 -1286 0 - -1282 -1287 0 - -1282 -1288 0 - -1283 -1284 0 - -1283 -1285 0 - -1283 -1286 0 - -1283 -1287 0 - -1283 -1288 0 - -1284 -1285 0 - -1284 -1286 0 - -1284 -1287 0 - -1284 -1288 0 - -1285 -1286 0 - -1285 -1287 0 - -1285 -1288 0 - -1286 -1287 0 - -1286 -1288 0 - -1287 -1288 0 - -1289 -1290 0 - -1289 -1291 0 - -1289 -1292 0 - -1289 -1293 0 - -1289 -1294 0 - -1289 -1295 0 - -1290 -1291 0 - -1290 -1292 0 - -1290 -1293 0 - -1290 -1294 0 - -1290 -1295 0 - -1291 -1292 0 - -1291 -1293 0 - -1291 -1294 0 - -1291 -1295 0 - -1292 -1293 0 - -1292 -1294 0 - -1292 -1295 0 - -1293 -1294 0 - -1293 -1295 0 - -1294 -1295 0 - -1296 -1297 0 - -1296 -1298 0 - -1296 -1299 0 - -1296 -1300 0 - -1296 -1301 0 - -1297 -1298 0 - -1297 -1299 0 - -1297 -1300 0 - -1297 -1301 0 - -1298 -1299 0 - -1298 -1300 0 - -1298 -1301 0 - -1299 -1300 0 - -1299 -1301 0 - -1300 -1301 0 - -1302 -1303 0 - -1304 -1305 0 - -1304 -1306 0 - -1304 -1307 0 - -1304 -1308 0 - -1304 -1309 0 - -1304 -1310 0 - -1305 -1306 0 - -1305 -1307 0 - -1305 -1308 0 - -1305 -1309 0 - -1305 -1310 0 - -1306 -1307 0 - -1306 -1308 0 - -1306 -1309 0 - -1306 -1310 0 - -1307 -1308 0 - -1307 -1309 0 - -1307 -1310 0 - -1308 -1309 0 - -1308 -1310 0 - -1309 -1310 0 - -1311 -1312 0 - -1311 -1313 0 - -1312 -1313 0 - -1314 -1315 0 - -1314 -1316 0 - -1314 -1317 0 - -1314 -1318 0 - -1314 -1319 0 - -1315 -1316 0 - -1315 -1317 0 - -1315 -1318 0 - -1315 -1319 0 - -1316 -1317 0 - -1316 -1318 0 - -1316 -1319 0 - -1317 -1318 0 - -1317 -1319 0 - -1318 -1319 0 - -1320 -1321 0 - -1320 -1322 0 - -1320 -1323 0 - -1320 -1324 0 - -1320 -1325 0 - -1321 -1322 0 - -1321 -1323 0 - -1321 -1324 0 - -1321 -1325 0 - -1322 -1323 0 - -1322 -1324 0 - -1322 -1325 0 - -1323 -1324 0 - -1323 -1325 0 - -1324 -1325 0 - -1326 -1327 0 - -1326 -1328 0 - -1326 -1329 0 - -1326 -1330 0 - -1327 -1328 0 - -1327 -1329 0 - -1327 -1330 0 - -1328 -1329 0 - -1328 -1330 0 - -1329 -1330 0 - -1331 -1332 0 - -1331 -1333 0 - -1331 -1334 0 - -1331 -1335 0 - -1331 -1336 0 - -1331 -1337 0 - -1331 -1338 0 - -1331 -1339 0 - -1331 -1340 0 - -1332 -1333 0 - -1332 -1334 0 - -1332 -1335 0 - -1332 -1336 0 - -1332 -1337 0 - -1332 -1338 0 - -1332 -1339 0 - -1332 -1340 0 - -1333 -1334 0 - -1333 -1335 0 - -1333 -1336 0 - -1333 -1337 0 - -1333 -1338 0 - -1333 -1339 0 - -1333 -1340 0 - -1334 -1335 0 - -1334 -1336 0 - -1334 -1337 0 - -1334 -1338 0 - -1334 -1339 0 - -1334 -1340 0 - -1335 -1336 0 - -1335 -1337 0 - -1335 -1338 0 - -1335 -1339 0 - -1335 -1340 0 - -1336 -1337 0 - -1336 -1338 0 - -1336 -1339 0 - -1336 -1340 0 - -1337 -1338 0 - -1337 -1339 0 - -1337 -1340 0 - -1338 -1339 0 - -1338 -1340 0 - -1339 -1340 0 - -1341 -1342 0 - -1341 -1343 0 - -1342 -1343 0 - -1344 -1345 0 - -1344 -1346 0 - -1344 -1347 0 - -1344 -1348 0 - -1345 -1346 0 - -1345 -1347 0 - -1345 -1348 0 - -1346 -1347 0 - -1346 -1348 0 - -1347 -1348 0 - -1349 -1350 0 - -1349 -1351 0 - -1349 -1352 0 - -1349 -1353 0 - -1349 -1354 0 - -1350 -1351 0 - -1350 -1352 0 - -1350 -1353 0 - -1350 -1354 0 - -1351 -1352 0 - -1351 -1353 0 - -1351 -1354 0 - -1352 -1353 0 - -1352 -1354 0 - -1353 -1354 0 - -1355 -1356 0 - -1355 -1357 0 - -1355 -1358 0 - -1355 -1359 0 - -1356 -1357 0 - -1356 -1358 0 - -1356 -1359 0 - -1357 -1358 0 - -1357 -1359 0 - -1358 -1359 0 - -1360 -1361 0 - -1360 -1362 0 - -1361 -1362 0 - -1363 -1364 0 - -1363 -1365 0 - -1363 -1366 0 - -1363 -1367 0 - -1363 -1368 0 - -1363 -1369 0 - -1364 -1365 0 - -1364 -1366 0 - -1364 -1367 0 - -1364 -1368 0 - -1364 -1369 0 - -1365 -1366 0 - -1365 -1367 0 - -1365 -1368 0 - -1365 -1369 0 - -1366 -1367 0 - -1366 -1368 0 - -1366 -1369 0 - -1367 -1368 0 - -1367 -1369 0 - -1368 -1369 0 - -1370 -1371 0 - -1370 -1372 0 - -1370 -1373 0 - -1370 -1374 0 - -1370 -1375 0 - -1370 -1376 0 - -1370 -1377 0 - -1370 -1378 0 - -1370 -1379 0 - -1370 -1380 0 - -1370 -1381 0 - -1370 -1382 0 - -1371 -1372 0 - -1371 -1373 0 - -1371 -1374 0 - -1371 -1375 0 - -1371 -1376 0 - -1371 -1377 0 - -1371 -1378 0 - -1371 -1379 0 - -1371 -1380 0 - -1371 -1381 0 - -1371 -1382 0 - -1372 -1373 0 - -1372 -1374 0 - -1372 -1375 0 - -1372 -1376 0 - -1372 -1377 0 - -1372 -1378 0 - -1372 -1379 0 - -1372 -1380 0 - -1372 -1381 0 - -1372 -1382 0 - -1373 -1374 0 - -1373 -1375 0 - -1373 -1376 0 - -1373 -1377 0 - -1373 -1378 0 - -1373 -1379 0 - -1373 -1380 0 - -1373 -1381 0 - -1373 -1382 0 - -1374 -1375 0 - -1374 -1376 0 - -1374 -1377 0 - -1374 -1378 0 - -1374 -1379 0 - -1374 -1380 0 - -1374 -1381 0 - -1374 -1382 0 - -1375 -1376 0 - -1375 -1377 0 - -1375 -1378 0 - -1375 -1379 0 - -1375 -1380 0 - -1375 -1381 0 - -1375 -1382 0 - -1376 -1377 0 - -1376 -1378 0 - -1376 -1379 0 - -1376 -1380 0 - -1376 -1381 0 - -1376 -1382 0 - -1377 -1378 0 - -1377 -1379 0 - -1377 -1380 0 - -1377 -1381 0 - -1377 -1382 0 - -1378 -1379 0 - -1378 -1380 0 - -1378 -1381 0 - -1378 -1382 0 - -1379 -1380 0 - -1379 -1381 0 - -1379 -1382 0 - -1380 -1381 0 - -1380 -1382 0 - -1381 -1382 0 - -1383 -1384 0 - -1383 -1385 0 - -1383 -1386 0 - -1383 -1387 0 - -1383 -1388 0 - -1383 -1389 0 - -1383 -1390 0 - -1384 -1385 0 - -1384 -1386 0 - -1384 -1387 0 - -1384 -1388 0 - -1384 -1389 0 - -1384 -1390 0 - -1385 -1386 0 - -1385 -1387 0 - -1385 -1388 0 - -1385 -1389 0 - -1385 -1390 0 - -1386 -1387 0 - -1386 -1388 0 - -1386 -1389 0 - -1386 -1390 0 - -1387 -1388 0 - -1387 -1389 0 - -1387 -1390 0 - -1388 -1389 0 - -1388 -1390 0 - -1389 -1390 0 - -1391 -1392 0 - -1391 -1393 0 - -1391 -1394 0 - -1391 -1395 0 - -1391 -1396 0 - -1391 -1397 0 - -1391 -1398 0 - -1391 -1399 0 - -1391 -1400 0 - -1391 -1401 0 - -1392 -1393 0 - -1392 -1394 0 - -1392 -1395 0 - -1392 -1396 0 - -1392 -1397 0 - -1392 -1398 0 - -1392 -1399 0 - -1392 -1400 0 - -1392 -1401 0 - -1393 -1394 0 - -1393 -1395 0 - -1393 -1396 0 - -1393 -1397 0 - -1393 -1398 0 - -1393 -1399 0 - -1393 -1400 0 - -1393 -1401 0 - -1394 -1395 0 - -1394 -1396 0 - -1394 -1397 0 - -1394 -1398 0 - -1394 -1399 0 - -1394 -1400 0 - -1394 -1401 0 - -1395 -1396 0 - -1395 -1397 0 - -1395 -1398 0 - -1395 -1399 0 - -1395 -1400 0 - -1395 -1401 0 - -1396 -1397 0 - -1396 -1398 0 - -1396 -1399 0 - -1396 -1400 0 - -1396 -1401 0 - -1397 -1398 0 - -1397 -1399 0 - -1397 -1400 0 - -1397 -1401 0 - -1398 -1399 0 - -1398 -1400 0 - -1398 -1401 0 - -1399 -1400 0 - -1399 -1401 0 - -1400 -1401 0 - -1402 -1403 0 - -1402 -1404 0 - -1402 -1405 0 - -1402 -1406 0 - -1402 -1407 0 - -1402 -1408 0 - -1402 -1409 0 - -1403 -1404 0 - -1403 -1405 0 - -1403 -1406 0 - -1403 -1407 0 - -1403 -1408 0 - -1403 -1409 0 - -1404 -1405 0 - -1404 -1406 0 - -1404 -1407 0 - -1404 -1408 0 - -1404 -1409 0 - -1405 -1406 0 - -1405 -1407 0 - -1405 -1408 0 - -1405 -1409 0 - -1406 -1407 0 - -1406 -1408 0 - -1406 -1409 0 - -1407 -1408 0 - -1407 -1409 0 - -1408 -1409 0 - -1410 -1411 0 - -1410 -1412 0 - -1411 -1412 0 - -1413 -1414 0 - -1413 -1415 0 - -1413 -1416 0 - -1413 -1417 0 - -1413 -1418 0 - -1413 -1419 0 - -1413 -1420 0 - -1413 -1421 0 - -1414 -1415 0 - -1414 -1416 0 - -1414 -1417 0 - -1414 -1418 0 - -1414 -1419 0 - -1414 -1420 0 - -1414 -1421 0 - -1415 -1416 0 - -1415 -1417 0 - -1415 -1418 0 - -1415 -1419 0 - -1415 -1420 0 - -1415 -1421 0 - -1416 -1417 0 - -1416 -1418 0 - -1416 -1419 0 - -1416 -1420 0 - -1416 -1421 0 - -1417 -1418 0 - -1417 -1419 0 - -1417 -1420 0 - -1417 -1421 0 - -1418 -1419 0 - -1418 -1420 0 - -1418 -1421 0 - -1419 -1420 0 - -1419 -1421 0 - -1420 -1421 0 - -1422 -1423 0 - -1422 -1424 0 - -1422 -1425 0 - -1423 -1424 0 - -1423 -1425 0 - -1424 -1425 0 - -1426 -1427 0 - -1426 -1428 0 - -1426 -1429 0 - -1426 -1430 0 - -1426 -1431 0 - -1426 -1432 0 - -1426 -1433 0 - -1426 -1434 0 - -1426 -1435 0 - -1426 -1436 0 - -1427 -1428 0 - -1427 -1429 0 - -1427 -1430 0 - -1427 -1431 0 - -1427 -1432 0 - -1427 -1433 0 - -1427 -1434 0 - -1427 -1435 0 - -1427 -1436 0 - -1428 -1429 0 - -1428 -1430 0 - -1428 -1431 0 - -1428 -1432 0 - -1428 -1433 0 - -1428 -1434 0 - -1428 -1435 0 - -1428 -1436 0 - -1429 -1430 0 - -1429 -1431 0 - -1429 -1432 0 - -1429 -1433 0 - -1429 -1434 0 - -1429 -1435 0 - -1429 -1436 0 - -1430 -1431 0 - -1430 -1432 0 - -1430 -1433 0 - -1430 -1434 0 - -1430 -1435 0 - -1430 -1436 0 - -1431 -1432 0 - -1431 -1433 0 - -1431 -1434 0 - -1431 -1435 0 - -1431 -1436 0 - -1432 -1433 0 - -1432 -1434 0 - -1432 -1435 0 - -1432 -1436 0 - -1433 -1434 0 - -1433 -1435 0 - -1433 -1436 0 - -1434 -1435 0 - -1434 -1436 0 - -1435 -1436 0 - -1437 -1438 0 - -1437 -1439 0 - -1437 -1440 0 - -1437 -1441 0 - -1437 -1442 0 - -1438 -1439 0 - -1438 -1440 0 - -1438 -1441 0 - -1438 -1442 0 - -1439 -1440 0 - -1439 -1441 0 - -1439 -1442 0 - -1440 -1441 0 - -1440 -1442 0 - -1441 -1442 0 - -1443 -1444 0 - -1443 -1445 0 - -1443 -1446 0 - -1443 -1447 0 - -1443 -1448 0 - -1443 -1449 0 - -1443 -1450 0 - -1443 -1451 0 - -1444 -1445 0 - -1444 -1446 0 - -1444 -1447 0 - -1444 -1448 0 - -1444 -1449 0 - -1444 -1450 0 - -1444 -1451 0 - -1445 -1446 0 - -1445 -1447 0 - -1445 -1448 0 - -1445 -1449 0 - -1445 -1450 0 - -1445 -1451 0 - -1446 -1447 0 - -1446 -1448 0 - -1446 -1449 0 - -1446 -1450 0 - -1446 -1451 0 - -1447 -1448 0 - -1447 -1449 0 - -1447 -1450 0 - -1447 -1451 0 - -1448 -1449 0 - -1448 -1450 0 - -1448 -1451 0 - -1449 -1450 0 - -1449 -1451 0 - -1450 -1451 0 - -1452 -1453 0 - -1452 -1454 0 - -1452 -1455 0 - -1452 -1456 0 - -1452 -1457 0 - -1452 -1458 0 - -1452 -1459 0 - -1452 -1460 0 - -1452 -1461 0 - -1452 -1462 0 - -1452 -1463 0 - -1452 -1464 0 - -1453 -1454 0 - -1453 -1455 0 - -1453 -1456 0 - -1453 -1457 0 - -1453 -1458 0 - -1453 -1459 0 - -1453 -1460 0 - -1453 -1461 0 - -1453 -1462 0 - -1453 -1463 0 - -1453 -1464 0 - -1454 -1455 0 - -1454 -1456 0 - -1454 -1457 0 - -1454 -1458 0 - -1454 -1459 0 - -1454 -1460 0 - -1454 -1461 0 - -1454 -1462 0 - -1454 -1463 0 - -1454 -1464 0 - -1455 -1456 0 - -1455 -1457 0 - -1455 -1458 0 - -1455 -1459 0 - -1455 -1460 0 - -1455 -1461 0 - -1455 -1462 0 - -1455 -1463 0 - -1455 -1464 0 - -1456 -1457 0 - -1456 -1458 0 - -1456 -1459 0 - -1456 -1460 0 - -1456 -1461 0 - -1456 -1462 0 - -1456 -1463 0 - -1456 -1464 0 - -1457 -1458 0 - -1457 -1459 0 - -1457 -1460 0 - -1457 -1461 0 - -1457 -1462 0 - -1457 -1463 0 - -1457 -1464 0 - -1458 -1459 0 - -1458 -1460 0 - -1458 -1461 0 - -1458 -1462 0 - -1458 -1463 0 - -1458 -1464 0 - -1459 -1460 0 - -1459 -1461 0 - -1459 -1462 0 - -1459 -1463 0 - -1459 -1464 0 - -1460 -1461 0 - -1460 -1462 0 - -1460 -1463 0 - -1460 -1464 0 - -1461 -1462 0 - -1461 -1463 0 - -1461 -1464 0 - -1462 -1463 0 - -1462 -1464 0 - -1463 -1464 0 - -1465 -1466 0 - -1465 -1467 0 - -1465 -1468 0 - -1465 -1469 0 - -1465 -1470 0 - -1465 -1471 0 - -1465 -1472 0 - -1465 -1473 0 - -1466 -1467 0 - -1466 -1468 0 - -1466 -1469 0 - -1466 -1470 0 - -1466 -1471 0 - -1466 -1472 0 - -1466 -1473 0 - -1467 -1468 0 - -1467 -1469 0 - -1467 -1470 0 - -1467 -1471 0 - -1467 -1472 0 - -1467 -1473 0 - -1468 -1469 0 - -1468 -1470 0 - -1468 -1471 0 - -1468 -1472 0 - -1468 -1473 0 - -1469 -1470 0 - -1469 -1471 0 - -1469 -1472 0 - -1469 -1473 0 - -1470 -1471 0 - -1470 -1472 0 - -1470 -1473 0 - -1471 -1472 0 - -1471 -1473 0 - -1472 -1473 0 - -1474 -1475 0 - -1474 -1476 0 - -1474 -1477 0 - -1474 -1478 0 - -1474 -1479 0 - -1474 -1480 0 - -1474 -1481 0 - -1474 -1482 0 - -1474 -1483 0 - -1475 -1476 0 - -1475 -1477 0 - -1475 -1478 0 - -1475 -1479 0 - -1475 -1480 0 - -1475 -1481 0 - -1475 -1482 0 - -1475 -1483 0 - -1476 -1477 0 - -1476 -1478 0 - -1476 -1479 0 - -1476 -1480 0 - -1476 -1481 0 - -1476 -1482 0 - -1476 -1483 0 - -1477 -1478 0 - -1477 -1479 0 - -1477 -1480 0 - -1477 -1481 0 - -1477 -1482 0 - -1477 -1483 0 - -1478 -1479 0 - -1478 -1480 0 - -1478 -1481 0 - -1478 -1482 0 - -1478 -1483 0 - -1479 -1480 0 - -1479 -1481 0 - -1479 -1482 0 - -1479 -1483 0 - -1480 -1481 0 - -1480 -1482 0 - -1480 -1483 0 - -1481 -1482 0 - -1481 -1483 0 - -1482 -1483 0 - -1484 -1485 0 - -1484 -1486 0 - -1484 -1487 0 - -1484 -1488 0 - -1484 -1489 0 - -1484 -1490 0 - -1484 -1491 0 - -1484 -1492 0 - -1485 -1486 0 - -1485 -1487 0 - -1485 -1488 0 - -1485 -1489 0 - -1485 -1490 0 - -1485 -1491 0 - -1485 -1492 0 - -1486 -1487 0 - -1486 -1488 0 - -1486 -1489 0 - -1486 -1490 0 - -1486 -1491 0 - -1486 -1492 0 - -1487 -1488 0 - -1487 -1489 0 - -1487 -1490 0 - -1487 -1491 0 - -1487 -1492 0 - -1488 -1489 0 - -1488 -1490 0 - -1488 -1491 0 - -1488 -1492 0 - -1489 -1490 0 - -1489 -1491 0 - -1489 -1492 0 - -1490 -1491 0 - -1490 -1492 0 - -1491 -1492 0 - -1493 -1494 0 - -1493 -1495 0 - -1493 -1496 0 - -1493 -1497 0 - -1493 -1498 0 - -1493 -1499 0 - -1493 -1500 0 - -1493 -1501 0 - -1493 -1502 0 - -1493 -1503 0 - -1494 -1495 0 - -1494 -1496 0 - -1494 -1497 0 - -1494 -1498 0 - -1494 -1499 0 - -1494 -1500 0 - -1494 -1501 0 - -1494 -1502 0 - -1494 -1503 0 - -1495 -1496 0 - -1495 -1497 0 - -1495 -1498 0 - -1495 -1499 0 - -1495 -1500 0 - -1495 -1501 0 - -1495 -1502 0 - -1495 -1503 0 - -1496 -1497 0 - -1496 -1498 0 - -1496 -1499 0 - -1496 -1500 0 - -1496 -1501 0 - -1496 -1502 0 - -1496 -1503 0 - -1497 -1498 0 - -1497 -1499 0 - -1497 -1500 0 - -1497 -1501 0 - -1497 -1502 0 - -1497 -1503 0 - -1498 -1499 0 - -1498 -1500 0 - -1498 -1501 0 - -1498 -1502 0 - -1498 -1503 0 - -1499 -1500 0 - -1499 -1501 0 - -1499 -1502 0 - -1499 -1503 0 - -1500 -1501 0 - -1500 -1502 0 - -1500 -1503 0 - -1501 -1502 0 - -1501 -1503 0 - -1502 -1503 0 - -1504 -1505 0 - -1504 -1506 0 - -1504 -1507 0 - -1504 -1508 0 - -1504 -1509 0 - -1504 -1510 0 - -1504 -1511 0 - -1504 -1512 0 - -1505 -1506 0 - -1505 -1507 0 - -1505 -1508 0 - -1505 -1509 0 - -1505 -1510 0 - -1505 -1511 0 - -1505 -1512 0 - -1506 -1507 0 - -1506 -1508 0 - -1506 -1509 0 - -1506 -1510 0 - -1506 -1511 0 - -1506 -1512 0 - -1507 -1508 0 - -1507 -1509 0 - -1507 -1510 0 - -1507 -1511 0 - -1507 -1512 0 - -1508 -1509 0 - -1508 -1510 0 - -1508 -1511 0 - -1508 -1512 0 - -1509 -1510 0 - -1509 -1511 0 - -1509 -1512 0 - -1510 -1511 0 - -1510 -1512 0 - -1511 -1512 0 - -1513 -1514 0 - -1513 -1515 0 - -1513 -1516 0 - -1513 -1517 0 - -1513 -1518 0 - -1513 -1519 0 - -1513 -1520 0 - -1513 -1521 0 - -1513 -1522 0 - -1514 -1515 0 - -1514 -1516 0 - -1514 -1517 0 - -1514 -1518 0 - -1514 -1519 0 - -1514 -1520 0 - -1514 -1521 0 - -1514 -1522 0 - -1515 -1516 0 - -1515 -1517 0 - -1515 -1518 0 - -1515 -1519 0 - -1515 -1520 0 - -1515 -1521 0 - -1515 -1522 0 - -1516 -1517 0 - -1516 -1518 0 - -1516 -1519 0 - -1516 -1520 0 - -1516 -1521 0 - -1516 -1522 0 - -1517 -1518 0 - -1517 -1519 0 - -1517 -1520 0 - -1517 -1521 0 - -1517 -1522 0 - -1518 -1519 0 - -1518 -1520 0 - -1518 -1521 0 - -1518 -1522 0 - -1519 -1520 0 - -1519 -1521 0 - -1519 -1522 0 - -1520 -1521 0 - -1520 -1522 0 - -1521 -1522 0 - -1523 -1524 0 - -1523 -1525 0 - -1523 -1526 0 - -1523 -1527 0 - -1523 -1528 0 - -1523 -1529 0 - -1523 -1530 0 - -1523 -1531 0 - -1524 -1525 0 - -1524 -1526 0 - -1524 -1527 0 - -1524 -1528 0 - -1524 -1529 0 - -1524 -1530 0 - -1524 -1531 0 - -1525 -1526 0 - -1525 -1527 0 - -1525 -1528 0 - -1525 -1529 0 - -1525 -1530 0 - -1525 -1531 0 - -1526 -1527 0 - -1526 -1528 0 - -1526 -1529 0 - -1526 -1530 0 - -1526 -1531 0 - -1527 -1528 0 - -1527 -1529 0 - -1527 -1530 0 - -1527 -1531 0 - -1528 -1529 0 - -1528 -1530 0 - -1528 -1531 0 - -1529 -1530 0 - -1529 -1531 0 - -1530 -1531 0 - -1532 -1533 0 - -1532 -1534 0 - -1532 -1535 0 - -1532 -1536 0 - -1532 -1537 0 - -1532 -1538 0 - -1532 -1539 0 - -1532 -1540 0 - -1532 -1541 0 - -1532 -1542 0 - -1533 -1534 0 - -1533 -1535 0 - -1533 -1536 0 - -1533 -1537 0 - -1533 -1538 0 - -1533 -1539 0 - -1533 -1540 0 - -1533 -1541 0 - -1533 -1542 0 - -1534 -1535 0 - -1534 -1536 0 - -1534 -1537 0 - -1534 -1538 0 - -1534 -1539 0 - -1534 -1540 0 - -1534 -1541 0 - -1534 -1542 0 - -1535 -1536 0 - -1535 -1537 0 - -1535 -1538 0 - -1535 -1539 0 - -1535 -1540 0 - -1535 -1541 0 - -1535 -1542 0 - -1536 -1537 0 - -1536 -1538 0 - -1536 -1539 0 - -1536 -1540 0 - -1536 -1541 0 - -1536 -1542 0 - -1537 -1538 0 - -1537 -1539 0 - -1537 -1540 0 - -1537 -1541 0 - -1537 -1542 0 - -1538 -1539 0 - -1538 -1540 0 - -1538 -1541 0 - -1538 -1542 0 - -1539 -1540 0 - -1539 -1541 0 - -1539 -1542 0 - -1540 -1541 0 - -1540 -1542 0 - -1541 -1542 0 - -1543 -1544 0 - -1543 -1545 0 - -1543 -1546 0 - -1543 -1547 0 - -1543 -1548 0 - -1543 -1549 0 - -1543 -1550 0 - -1543 -1551 0 - -1543 -1552 0 - -1543 -1553 0 - -1543 -1554 0 - -1544 -1545 0 - -1544 -1546 0 - -1544 -1547 0 - -1544 -1548 0 - -1544 -1549 0 - -1544 -1550 0 - -1544 -1551 0 - -1544 -1552 0 - -1544 -1553 0 - -1544 -1554 0 - -1545 -1546 0 - -1545 -1547 0 - -1545 -1548 0 - -1545 -1549 0 - -1545 -1550 0 - -1545 -1551 0 - -1545 -1552 0 - -1545 -1553 0 - -1545 -1554 0 - -1546 -1547 0 - -1546 -1548 0 - -1546 -1549 0 - -1546 -1550 0 - -1546 -1551 0 - -1546 -1552 0 - -1546 -1553 0 - -1546 -1554 0 - -1547 -1548 0 - -1547 -1549 0 - -1547 -1550 0 - -1547 -1551 0 - -1547 -1552 0 - -1547 -1553 0 - -1547 -1554 0 - -1548 -1549 0 - -1548 -1550 0 - -1548 -1551 0 - -1548 -1552 0 - -1548 -1553 0 - -1548 -1554 0 - -1549 -1550 0 - -1549 -1551 0 - -1549 -1552 0 - -1549 -1553 0 - -1549 -1554 0 - -1550 -1551 0 - -1550 -1552 0 - -1550 -1553 0 - -1550 -1554 0 - -1551 -1552 0 - -1551 -1553 0 - -1551 -1554 0 - -1552 -1553 0 - -1552 -1554 0 - -1553 -1554 0 - -1555 -1556 0 - -1555 -1557 0 - -1555 -1558 0 - -1555 -1559 0 - -1555 -1560 0 - -1555 -1561 0 - -1555 -1562 0 - -1556 -1557 0 - -1556 -1558 0 - -1556 -1559 0 - -1556 -1560 0 - -1556 -1561 0 - -1556 -1562 0 - -1557 -1558 0 - -1557 -1559 0 - -1557 -1560 0 - -1557 -1561 0 - -1557 -1562 0 - -1558 -1559 0 - -1558 -1560 0 - -1558 -1561 0 - -1558 -1562 0 - -1559 -1560 0 - -1559 -1561 0 - -1559 -1562 0 - -1560 -1561 0 - -1560 -1562 0 - -1561 -1562 0 - -1563 -1564 0 - -1563 -1565 0 - -1563 -1566 0 - -1563 -1567 0 - -1563 -1568 0 - -1564 -1565 0 - -1564 -1566 0 - -1564 -1567 0 - -1564 -1568 0 - -1565 -1566 0 - -1565 -1567 0 - -1565 -1568 0 - -1566 -1567 0 - -1566 -1568 0 - -1567 -1568 0 - -1569 -1570 0 - -1569 -1571 0 - -1569 -1572 0 - -1569 -1573 0 - -1569 -1574 0 - -1570 -1571 0 - -1570 -1572 0 - -1570 -1573 0 - -1570 -1574 0 - -1571 -1572 0 - -1571 -1573 0 - -1571 -1574 0 - -1572 -1573 0 - -1572 -1574 0 - -1573 -1574 0 - -1575 -1576 0 - -1577 -1578 0 - -1577 -1579 0 - -1577 -1580 0 - -1577 -1581 0 - -1578 -1579 0 - -1578 -1580 0 - -1578 -1581 0 - -1579 -1580 0 - -1579 -1581 0 - -1580 -1581 0 - -1582 -1583 0 - -1582 -1584 0 - -1582 -1585 0 - -1582 -1586 0 - -1582 -1587 0 - -1583 -1584 0 - -1583 -1585 0 - -1583 -1586 0 - -1583 -1587 0 - -1584 -1585 0 - -1584 -1586 0 - -1584 -1587 0 - -1585 -1586 0 - -1585 -1587 0 - -1586 -1587 0 - -1588 -1589 0 - -1590 -1591 0 - -1590 -1592 0 - -1590 -1593 0 - -1590 -1594 0 - -1591 -1592 0 - -1591 -1593 0 - -1591 -1594 0 - -1592 -1593 0 - -1592 -1594 0 - -1593 -1594 0 - -1595 -1596 0 - -1595 -1597 0 - -1595 -1598 0 - -1595 -1599 0 - -1595 -1600 0 - -1596 -1597 0 - -1596 -1598 0 - -1596 -1599 0 - -1596 -1600 0 - -1597 -1598 0 - -1597 -1599 0 - -1597 -1600 0 - -1598 -1599 0 - -1598 -1600 0 - -1599 -1600 0 - -1601 -1602 0 - -1601 -1603 0 - -1601 -1604 0 - -1601 -1605 0 - -1602 -1603 0 - -1602 -1604 0 - -1602 -1605 0 - -1603 -1604 0 - -1603 -1605 0 - -1604 -1605 0 - -1606 -1607 0 - -1606 -1608 0 - -1606 -1609 0 - -1606 -1610 0 - -1606 -1611 0 - -1606 -1612 0 - -1607 -1608 0 - -1607 -1609 0 - -1607 -1610 0 - -1607 -1611 0 - -1607 -1612 0 - -1608 -1609 0 - -1608 -1610 0 - -1608 -1611 0 - -1608 -1612 0 - -1609 -1610 0 - -1609 -1611 0 - -1609 -1612 0 - -1610 -1611 0 - -1610 -1612 0 - -1611 -1612 0 - -1613 -1614 0 - -1613 -1615 0 - -1613 -1616 0 - -1613 -1617 0 - -1613 -1618 0 - -1613 -1619 0 - -1613 -1620 0 - -1613 -1621 0 - -1613 -1622 0 - -1613 -1623 0 - -1614 -1615 0 - -1614 -1616 0 - -1614 -1617 0 - -1614 -1618 0 - -1614 -1619 0 - -1614 -1620 0 - -1614 -1621 0 - -1614 -1622 0 - -1614 -1623 0 - -1615 -1616 0 - -1615 -1617 0 - -1615 -1618 0 - -1615 -1619 0 - -1615 -1620 0 - -1615 -1621 0 - -1615 -1622 0 - -1615 -1623 0 - -1616 -1617 0 - -1616 -1618 0 - -1616 -1619 0 - -1616 -1620 0 - -1616 -1621 0 - -1616 -1622 0 - -1616 -1623 0 - -1617 -1618 0 - -1617 -1619 0 - -1617 -1620 0 - -1617 -1621 0 - -1617 -1622 0 - -1617 -1623 0 - -1618 -1619 0 - -1618 -1620 0 - -1618 -1621 0 - -1618 -1622 0 - -1618 -1623 0 - -1619 -1620 0 - -1619 -1621 0 - -1619 -1622 0 - -1619 -1623 0 - -1620 -1621 0 - -1620 -1622 0 - -1620 -1623 0 - -1621 -1622 0 - -1621 -1623 0 - -1622 -1623 0 - -1624 -1625 0 - -1624 -1626 0 - -1624 -1627 0 - -1624 -1628 0 - -1624 -1629 0 - -1624 -1630 0 - -1624 -1631 0 - -1624 -1632 0 - -1624 -1633 0 - -1624 -1634 0 - -1625 -1626 0 - -1625 -1627 0 - -1625 -1628 0 - -1625 -1629 0 - -1625 -1630 0 - -1625 -1631 0 - -1625 -1632 0 - -1625 -1633 0 - -1625 -1634 0 - -1626 -1627 0 - -1626 -1628 0 - -1626 -1629 0 - -1626 -1630 0 - -1626 -1631 0 - -1626 -1632 0 - -1626 -1633 0 - -1626 -1634 0 - -1627 -1628 0 - -1627 -1629 0 - -1627 -1630 0 - -1627 -1631 0 - -1627 -1632 0 - -1627 -1633 0 - -1627 -1634 0 - -1628 -1629 0 - -1628 -1630 0 - -1628 -1631 0 - -1628 -1632 0 - -1628 -1633 0 - -1628 -1634 0 - -1629 -1630 0 - -1629 -1631 0 - -1629 -1632 0 - -1629 -1633 0 - -1629 -1634 0 - -1630 -1631 0 - -1630 -1632 0 - -1630 -1633 0 - -1630 -1634 0 - -1631 -1632 0 - -1631 -1633 0 - -1631 -1634 0 - -1632 -1633 0 - -1632 -1634 0 - -1633 -1634 0 - -1635 -1636 0 - -1635 -1637 0 - -1635 -1638 0 - -1635 -1639 0 - -1635 -1640 0 - -1635 -1641 0 - -1636 -1637 0 - -1636 -1638 0 - -1636 -1639 0 - -1636 -1640 0 - -1636 -1641 0 - -1637 -1638 0 - -1637 -1639 0 - -1637 -1640 0 - -1637 -1641 0 - -1638 -1639 0 - -1638 -1640 0 - -1638 -1641 0 - -1639 -1640 0 - -1639 -1641 0 - -1640 -1641 0 - -1642 -1643 0 - -1642 -1644 0 - -1642 -1645 0 - -1642 -1646 0 - -1642 -1647 0 - -1643 -1644 0 - -1643 -1645 0 - -1643 -1646 0 - -1643 -1647 0 - -1644 -1645 0 - -1644 -1646 0 - -1644 -1647 0 - -1645 -1646 0 - -1645 -1647 0 - -1646 -1647 0 - -1648 -1649 0 - -1648 -1650 0 - -1648 -1651 0 - -1649 -1650 0 - -1649 -1651 0 - -1650 -1651 0 - -1652 -1653 0 - -1652 -1654 0 - -1652 -1655 0 - -1652 -1656 0 - -1652 -1657 0 - -1652 -1658 0 - -1652 -1659 0 - -1653 -1654 0 - -1653 -1655 0 - -1653 -1656 0 - -1653 -1657 0 - -1653 -1658 0 - -1653 -1659 0 - -1654 -1655 0 - -1654 -1656 0 - -1654 -1657 0 - -1654 -1658 0 - -1654 -1659 0 - -1655 -1656 0 - -1655 -1657 0 - -1655 -1658 0 - -1655 -1659 0 - -1656 -1657 0 - -1656 -1658 0 - -1656 -1659 0 - -1657 -1658 0 - -1657 -1659 0 - -1658 -1659 0 - -1660 -1661 0 - -1660 -1662 0 - -1660 -1663 0 - -1660 -1664 0 - -1660 -1665 0 - -1661 -1662 0 - -1661 -1663 0 - -1661 -1664 0 - -1661 -1665 0 - -1662 -1663 0 - -1662 -1664 0 - -1662 -1665 0 - -1663 -1664 0 - -1663 -1665 0 - -1664 -1665 0 - -1666 -1667 0 - -1666 -1668 0 - -1666 -1669 0 - -1666 -1670 0 - -1666 -1671 0 - -1666 -1672 0 - -1666 -1673 0 - -1667 -1668 0 - -1667 -1669 0 - -1667 -1670 0 - -1667 -1671 0 - -1667 -1672 0 - -1667 -1673 0 - -1668 -1669 0 - -1668 -1670 0 - -1668 -1671 0 - -1668 -1672 0 - -1668 -1673 0 - -1669 -1670 0 - -1669 -1671 0 - -1669 -1672 0 - -1669 -1673 0 - -1670 -1671 0 - -1670 -1672 0 - -1670 -1673 0 - -1671 -1672 0 - -1671 -1673 0 - -1672 -1673 0 - -1674 -1675 0 - -1674 -1676 0 - -1674 -1677 0 - -1674 -1678 0 - -1674 -1679 0 - -1674 -1680 0 - -1674 -1681 0 - -1675 -1676 0 - -1675 -1677 0 - -1675 -1678 0 - -1675 -1679 0 - -1675 -1680 0 - -1675 -1681 0 - -1676 -1677 0 - -1676 -1678 0 - -1676 -1679 0 - -1676 -1680 0 - -1676 -1681 0 - -1677 -1678 0 - -1677 -1679 0 - -1677 -1680 0 - -1677 -1681 0 - -1678 -1679 0 - -1678 -1680 0 - -1678 -1681 0 - -1679 -1680 0 - -1679 -1681 0 - -1680 -1681 0 - -1682 -1683 0 - -1682 -1684 0 - -1682 -1685 0 - -1682 -1686 0 - -1682 -1687 0 - -1682 -1688 0 - -1682 -1689 0 - -1682 -1690 0 - -1682 -1691 0 - -1682 -1692 0 - -1683 -1684 0 - -1683 -1685 0 - -1683 -1686 0 - -1683 -1687 0 - -1683 -1688 0 - -1683 -1689 0 - -1683 -1690 0 - -1683 -1691 0 - -1683 -1692 0 - -1684 -1685 0 - -1684 -1686 0 - -1684 -1687 0 - -1684 -1688 0 - -1684 -1689 0 - -1684 -1690 0 - -1684 -1691 0 - -1684 -1692 0 - -1685 -1686 0 - -1685 -1687 0 - -1685 -1688 0 - -1685 -1689 0 - -1685 -1690 0 - -1685 -1691 0 - -1685 -1692 0 - -1686 -1687 0 - -1686 -1688 0 - -1686 -1689 0 - -1686 -1690 0 - -1686 -1691 0 - -1686 -1692 0 - -1687 -1688 0 - -1687 -1689 0 - -1687 -1690 0 - -1687 -1691 0 - -1687 -1692 0 - -1688 -1689 0 - -1688 -1690 0 - -1688 -1691 0 - -1688 -1692 0 - -1689 -1690 0 - -1689 -1691 0 - -1689 -1692 0 - -1690 -1691 0 - -1690 -1692 0 - -1691 -1692 0 - -1693 -1694 0 - -1693 -1695 0 - -1693 -1696 0 - -1693 -1697 0 - -1693 -1698 0 - -1694 -1695 0 - -1694 -1696 0 - -1694 -1697 0 - -1694 -1698 0 - -1695 -1696 0 - -1695 -1697 0 - -1695 -1698 0 - -1696 -1697 0 - -1696 -1698 0 - -1697 -1698 0 - -1699 -1700 0 - -1699 -1701 0 - -1699 -1702 0 - -1699 -1703 0 - -1699 -1704 0 - -1699 -1705 0 - -1699 -1706 0 - -1699 -1707 0 - -1700 -1701 0 - -1700 -1702 0 - -1700 -1703 0 - -1700 -1704 0 - -1700 -1705 0 - -1700 -1706 0 - -1700 -1707 0 - -1701 -1702 0 - -1701 -1703 0 - -1701 -1704 0 - -1701 -1705 0 - -1701 -1706 0 - -1701 -1707 0 - -1702 -1703 0 - -1702 -1704 0 - -1702 -1705 0 - -1702 -1706 0 - -1702 -1707 0 - -1703 -1704 0 - -1703 -1705 0 - -1703 -1706 0 - -1703 -1707 0 - -1704 -1705 0 - -1704 -1706 0 - -1704 -1707 0 - -1705 -1706 0 - -1705 -1707 0 - -1706 -1707 0 - -1708 -1709 0 - -1708 -1710 0 - -1708 -1711 0 - -1708 -1712 0 - -1708 -1713 0 - -1708 -1714 0 - -1708 -1715 0 - -1708 -1716 0 - -1708 -1717 0 - -1708 -1718 0 - -1708 -1719 0 - -1708 -1720 0 - -1708 -1721 0 - -1709 -1710 0 - -1709 -1711 0 - -1709 -1712 0 - -1709 -1713 0 - -1709 -1714 0 - -1709 -1715 0 - -1709 -1716 0 - -1709 -1717 0 - -1709 -1718 0 - -1709 -1719 0 - -1709 -1720 0 - -1709 -1721 0 - -1710 -1711 0 - -1710 -1712 0 - -1710 -1713 0 - -1710 -1714 0 - -1710 -1715 0 - -1710 -1716 0 - -1710 -1717 0 - -1710 -1718 0 - -1710 -1719 0 - -1710 -1720 0 - -1710 -1721 0 - -1711 -1712 0 - -1711 -1713 0 - -1711 -1714 0 - -1711 -1715 0 - -1711 -1716 0 - -1711 -1717 0 - -1711 -1718 0 - -1711 -1719 0 - -1711 -1720 0 - -1711 -1721 0 - -1712 -1713 0 - -1712 -1714 0 - -1712 -1715 0 - -1712 -1716 0 - -1712 -1717 0 - -1712 -1718 0 - -1712 -1719 0 - -1712 -1720 0 - -1712 -1721 0 - -1713 -1714 0 - -1713 -1715 0 - -1713 -1716 0 - -1713 -1717 0 - -1713 -1718 0 - -1713 -1719 0 - -1713 -1720 0 - -1713 -1721 0 - -1714 -1715 0 - -1714 -1716 0 - -1714 -1717 0 - -1714 -1718 0 - -1714 -1719 0 - -1714 -1720 0 - -1714 -1721 0 - -1715 -1716 0 - -1715 -1717 0 - -1715 -1718 0 - -1715 -1719 0 - -1715 -1720 0 - -1715 -1721 0 - -1716 -1717 0 - -1716 -1718 0 - -1716 -1719 0 - -1716 -1720 0 - -1716 -1721 0 - -1717 -1718 0 - -1717 -1719 0 - -1717 -1720 0 - -1717 -1721 0 - -1718 -1719 0 - -1718 -1720 0 - -1718 -1721 0 - -1719 -1720 0 - -1719 -1721 0 - -1720 -1721 0 - -1722 -1723 0 - -1722 -1724 0 - -1722 -1725 0 - -1722 -1726 0 - -1722 -1727 0 - -1723 -1724 0 - -1723 -1725 0 - -1723 -1726 0 - -1723 -1727 0 - -1724 -1725 0 - -1724 -1726 0 - -1724 -1727 0 - -1725 -1726 0 - -1725 -1727 0 - -1726 -1727 0 - -1728 -1729 0 - -1728 -1730 0 - -1728 -1731 0 - -1728 -1732 0 - -1728 -1733 0 - -1729 -1730 0 - -1729 -1731 0 - -1729 -1732 0 - -1729 -1733 0 - -1730 -1731 0 - -1730 -1732 0 - -1730 -1733 0 - -1731 -1732 0 - -1731 -1733 0 - -1732 -1733 0 - -1734 -1735 0 - -1734 -1736 0 - -1734 -1737 0 - -1734 -1738 0 - -1734 -1739 0 - -1735 -1736 0 - -1735 -1737 0 - -1735 -1738 0 - -1735 -1739 0 - -1736 -1737 0 - -1736 -1738 0 - -1736 -1739 0 - -1737 -1738 0 - -1737 -1739 0 - -1738 -1739 0 - -1740 -1741 0 - -1740 -1742 0 - -1740 -1743 0 - -1740 -1744 0 - -1740 -1745 0 - -1740 -1746 0 - -1740 -1747 0 - -1741 -1742 0 - -1741 -1743 0 - -1741 -1744 0 - -1741 -1745 0 - -1741 -1746 0 - -1741 -1747 0 - -1742 -1743 0 - -1742 -1744 0 - -1742 -1745 0 - -1742 -1746 0 - -1742 -1747 0 - -1743 -1744 0 - -1743 -1745 0 - -1743 -1746 0 - -1743 -1747 0 - -1744 -1745 0 - -1744 -1746 0 - -1744 -1747 0 - -1745 -1746 0 - -1745 -1747 0 - -1746 -1747 0 - -1748 -1749 0 - -1748 -1750 0 - -1748 -1751 0 - -1748 -1752 0 - -1749 -1750 0 - -1749 -1751 0 - -1749 -1752 0 - -1750 -1751 0 - -1750 -1752 0 - -1751 -1752 0 - -1753 -1754 0 - -1753 -1755 0 - -1753 -1756 0 - -1753 -1757 0 - -1753 -1758 0 - -1753 -1759 0 - -1753 -1760 0 - -1753 -1761 0 - -1753 -1762 0 - -1753 -1763 0 - -1754 -1755 0 - -1754 -1756 0 - -1754 -1757 0 - -1754 -1758 0 - -1754 -1759 0 - -1754 -1760 0 - -1754 -1761 0 - -1754 -1762 0 - -1754 -1763 0 - -1755 -1756 0 - -1755 -1757 0 - -1755 -1758 0 - -1755 -1759 0 - -1755 -1760 0 - -1755 -1761 0 - -1755 -1762 0 - -1755 -1763 0 - -1756 -1757 0 - -1756 -1758 0 - -1756 -1759 0 - -1756 -1760 0 - -1756 -1761 0 - -1756 -1762 0 - -1756 -1763 0 - -1757 -1758 0 - -1757 -1759 0 - -1757 -1760 0 - -1757 -1761 0 - -1757 -1762 0 - -1757 -1763 0 - -1758 -1759 0 - -1758 -1760 0 - -1758 -1761 0 - -1758 -1762 0 - -1758 -1763 0 - -1759 -1760 0 - -1759 -1761 0 - -1759 -1762 0 - -1759 -1763 0 - -1760 -1761 0 - -1760 -1762 0 - -1760 -1763 0 - -1761 -1762 0 - -1761 -1763 0 - -1762 -1763 0 - -1764 -1765 0 - -1764 -1766 0 - -1764 -1767 0 - -1764 -1768 0 - -1764 -1769 0 - -1764 -1770 0 - -1764 -1771 0 - -1765 -1766 0 - -1765 -1767 0 - -1765 -1768 0 - -1765 -1769 0 - -1765 -1770 0 - -1765 -1771 0 - -1766 -1767 0 - -1766 -1768 0 - -1766 -1769 0 - -1766 -1770 0 - -1766 -1771 0 - -1767 -1768 0 - -1767 -1769 0 - -1767 -1770 0 - -1767 -1771 0 - -1768 -1769 0 - -1768 -1770 0 - -1768 -1771 0 - -1769 -1770 0 - -1769 -1771 0 - -1770 -1771 0 - -1772 -1773 0 - -1772 -1774 0 - -1772 -1775 0 - -1772 -1776 0 - -1772 -1777 0 - -1772 -1778 0 - -1772 -1779 0 - -1772 -1780 0 - -1773 -1774 0 - -1773 -1775 0 - -1773 -1776 0 - -1773 -1777 0 - -1773 -1778 0 - -1773 -1779 0 - -1773 -1780 0 - -1774 -1775 0 - -1774 -1776 0 - -1774 -1777 0 - -1774 -1778 0 - -1774 -1779 0 - -1774 -1780 0 - -1775 -1776 0 - -1775 -1777 0 - -1775 -1778 0 - -1775 -1779 0 - -1775 -1780 0 - -1776 -1777 0 - -1776 -1778 0 - -1776 -1779 0 - -1776 -1780 0 - -1777 -1778 0 - -1777 -1779 0 - -1777 -1780 0 - -1778 -1779 0 - -1778 -1780 0 - -1779 -1780 0 - -1781 -1782 0 - -1781 -1783 0 - -1781 -1784 0 - -1781 -1785 0 - -1781 -1786 0 - -1781 -1787 0 - -1781 -1788 0 - -1782 -1783 0 - -1782 -1784 0 - -1782 -1785 0 - -1782 -1786 0 - -1782 -1787 0 - -1782 -1788 0 - -1783 -1784 0 - -1783 -1785 0 - -1783 -1786 0 - -1783 -1787 0 - -1783 -1788 0 - -1784 -1785 0 - -1784 -1786 0 - -1784 -1787 0 - -1784 -1788 0 - -1785 -1786 0 - -1785 -1787 0 - -1785 -1788 0 - -1786 -1787 0 - -1786 -1788 0 - -1787 -1788 0 - -1789 -1790 0 - -1791 -1792 0 - -1791 -1793 0 - -1791 -1794 0 - -1791 -1795 0 - -1791 -1796 0 - -1791 -1797 0 - -1791 -1798 0 - -1792 -1793 0 - -1792 -1794 0 - -1792 -1795 0 - -1792 -1796 0 - -1792 -1797 0 - -1792 -1798 0 - -1793 -1794 0 - -1793 -1795 0 - -1793 -1796 0 - -1793 -1797 0 - -1793 -1798 0 - -1794 -1795 0 - -1794 -1796 0 - -1794 -1797 0 - -1794 -1798 0 - -1795 -1796 0 - -1795 -1797 0 - -1795 -1798 0 - -1796 -1797 0 - -1796 -1798 0 - -1797 -1798 0 - -1799 -1800 0 - -1799 -1801 0 - -1799 -1802 0 - -1799 -1803 0 - -1799 -1804 0 - -1799 -1805 0 - -1799 -1806 0 - -1799 -1807 0 - -1800 -1801 0 - -1800 -1802 0 - -1800 -1803 0 - -1800 -1804 0 - -1800 -1805 0 - -1800 -1806 0 - -1800 -1807 0 - -1801 -1802 0 - -1801 -1803 0 - -1801 -1804 0 - -1801 -1805 0 - -1801 -1806 0 - -1801 -1807 0 - -1802 -1803 0 - -1802 -1804 0 - -1802 -1805 0 - -1802 -1806 0 - -1802 -1807 0 - -1803 -1804 0 - -1803 -1805 0 - -1803 -1806 0 - -1803 -1807 0 - -1804 -1805 0 - -1804 -1806 0 - -1804 -1807 0 - -1805 -1806 0 - -1805 -1807 0 - -1806 -1807 0 - -1808 -1809 0 - -1808 -1810 0 - -1808 -1811 0 - -1808 -1812 0 - -1808 -1813 0 - -1808 -1814 0 - -1808 -1815 0 - -1808 -1816 0 - -1808 -1817 0 - -1809 -1810 0 - -1809 -1811 0 - -1809 -1812 0 - -1809 -1813 0 - -1809 -1814 0 - -1809 -1815 0 - -1809 -1816 0 - -1809 -1817 0 - -1810 -1811 0 - -1810 -1812 0 - -1810 -1813 0 - -1810 -1814 0 - -1810 -1815 0 - -1810 -1816 0 - -1810 -1817 0 - -1811 -1812 0 - -1811 -1813 0 - -1811 -1814 0 - -1811 -1815 0 - -1811 -1816 0 - -1811 -1817 0 - -1812 -1813 0 - -1812 -1814 0 - -1812 -1815 0 - -1812 -1816 0 - -1812 -1817 0 - -1813 -1814 0 - -1813 -1815 0 - -1813 -1816 0 - -1813 -1817 0 - -1814 -1815 0 - -1814 -1816 0 - -1814 -1817 0 - -1815 -1816 0 - -1815 -1817 0 - -1816 -1817 0 - -1818 -1819 0 - -1818 -1820 0 - -1818 -1821 0 - -1818 -1822 0 - -1818 -1823 0 - -1818 -1824 0 - -1818 -1825 0 - -1818 -1826 0 - -1819 -1820 0 - -1819 -1821 0 - -1819 -1822 0 - -1819 -1823 0 - -1819 -1824 0 - -1819 -1825 0 - -1819 -1826 0 - -1820 -1821 0 - -1820 -1822 0 - -1820 -1823 0 - -1820 -1824 0 - -1820 -1825 0 - -1820 -1826 0 - -1821 -1822 0 - -1821 -1823 0 - -1821 -1824 0 - -1821 -1825 0 - -1821 -1826 0 - -1822 -1823 0 - -1822 -1824 0 - -1822 -1825 0 - -1822 -1826 0 - -1823 -1824 0 - -1823 -1825 0 - -1823 -1826 0 - -1824 -1825 0 - -1824 -1826 0 - -1825 -1826 0 - -1827 -1828 0 - -1827 -1829 0 - -1827 -1830 0 - -1828 -1829 0 - -1828 -1830 0 - -1829 -1830 0 - -1831 -1832 0 - -1831 -1833 0 - -1831 -1834 0 - -1831 -1835 0 - -1831 -1836 0 - -1831 -1837 0 - -1831 -1838 0 - -1832 -1833 0 - -1832 -1834 0 - -1832 -1835 0 - -1832 -1836 0 - -1832 -1837 0 - -1832 -1838 0 - -1833 -1834 0 - -1833 -1835 0 - -1833 -1836 0 - -1833 -1837 0 - -1833 -1838 0 - -1834 -1835 0 - -1834 -1836 0 - -1834 -1837 0 - -1834 -1838 0 - -1835 -1836 0 - -1835 -1837 0 - -1835 -1838 0 - -1836 -1837 0 - -1836 -1838 0 - -1837 -1838 0 - -1839 -1840 0 - -1839 -1841 0 - -1839 -1842 0 - -1839 -1843 0 - -1839 -1844 0 - -1840 -1841 0 - -1840 -1842 0 - -1840 -1843 0 - -1840 -1844 0 - -1841 -1842 0 - -1841 -1843 0 - -1841 -1844 0 - -1842 -1843 0 - -1842 -1844 0 - -1843 -1844 0 - -1845 -1846 0 - -1845 -1847 0 - -1845 -1848 0 - -1845 -1849 0 - -1845 -1850 0 - -1845 -1851 0 - -1846 -1847 0 - -1846 -1848 0 - -1846 -1849 0 - -1846 -1850 0 - -1846 -1851 0 - -1847 -1848 0 - -1847 -1849 0 - -1847 -1850 0 - -1847 -1851 0 - -1848 -1849 0 - -1848 -1850 0 - -1848 -1851 0 - -1849 -1850 0 - -1849 -1851 0 - -1850 -1851 0 - -1852 -1853 0 - -1852 -1854 0 - -1852 -1855 0 - -1852 -1856 0 - -1853 -1854 0 - -1853 -1855 0 - -1853 -1856 0 - -1854 -1855 0 - -1854 -1856 0 - -1855 -1856 0 - -1857 -1858 0 - -1857 -1859 0 - -1857 -1860 0 - -1858 -1859 0 - -1858 -1860 0 - -1859 -1860 0 - -1861 -1862 0 - -1861 -1863 0 - -1861 -1864 0 - -1861 -1865 0 - -1861 -1866 0 - -1861 -1867 0 - -1861 -1868 0 - -1861 -1869 0 - -1861 -1870 0 - -1862 -1863 0 - -1862 -1864 0 - -1862 -1865 0 - -1862 -1866 0 - -1862 -1867 0 - -1862 -1868 0 - -1862 -1869 0 - -1862 -1870 0 - -1863 -1864 0 - -1863 -1865 0 - -1863 -1866 0 - -1863 -1867 0 - -1863 -1868 0 - -1863 -1869 0 - -1863 -1870 0 - -1864 -1865 0 - -1864 -1866 0 - -1864 -1867 0 - -1864 -1868 0 - -1864 -1869 0 - -1864 -1870 0 - -1865 -1866 0 - -1865 -1867 0 - -1865 -1868 0 - -1865 -1869 0 - -1865 -1870 0 - -1866 -1867 0 - -1866 -1868 0 - -1866 -1869 0 - -1866 -1870 0 - -1867 -1868 0 - -1867 -1869 0 - -1867 -1870 0 - -1868 -1869 0 - -1868 -1870 0 - -1869 -1870 0 - -1871 -1872 0 - -1871 -1873 0 - -1871 -1874 0 - -1871 -1875 0 - -1871 -1876 0 - -1871 -1877 0 - -1871 -1878 0 - -1871 -1879 0 - -1871 -1880 0 - -1871 -1881 0 - -1872 -1873 0 - -1872 -1874 0 - -1872 -1875 0 - -1872 -1876 0 - -1872 -1877 0 - -1872 -1878 0 - -1872 -1879 0 - -1872 -1880 0 - -1872 -1881 0 - -1873 -1874 0 - -1873 -1875 0 - -1873 -1876 0 - -1873 -1877 0 - -1873 -1878 0 - -1873 -1879 0 - -1873 -1880 0 - -1873 -1881 0 - -1874 -1875 0 - -1874 -1876 0 - -1874 -1877 0 - -1874 -1878 0 - -1874 -1879 0 - -1874 -1880 0 - -1874 -1881 0 - -1875 -1876 0 - -1875 -1877 0 - -1875 -1878 0 - -1875 -1879 0 - -1875 -1880 0 - -1875 -1881 0 - -1876 -1877 0 - -1876 -1878 0 - -1876 -1879 0 - -1876 -1880 0 - -1876 -1881 0 - -1877 -1878 0 - -1877 -1879 0 - -1877 -1880 0 - -1877 -1881 0 - -1878 -1879 0 - -1878 -1880 0 - -1878 -1881 0 - -1879 -1880 0 - -1879 -1881 0 - -1880 -1881 0 - -1882 -1883 0 - -1882 -1884 0 - -1882 -1885 0 - -1882 -1886 0 - -1882 -1887 0 - -1882 -1888 0 - -1883 -1884 0 - -1883 -1885 0 - -1883 -1886 0 - -1883 -1887 0 - -1883 -1888 0 - -1884 -1885 0 - -1884 -1886 0 - -1884 -1887 0 - -1884 -1888 0 - -1885 -1886 0 - -1885 -1887 0 - -1885 -1888 0 - -1886 -1887 0 - -1886 -1888 0 - -1887 -1888 0 - -1889 -1890 0 - -1889 -1891 0 - -1889 -1892 0 - -1889 -1893 0 - -1889 -1894 0 - -1889 -1895 0 - -1889 -1896 0 - -1890 -1891 0 - -1890 -1892 0 - -1890 -1893 0 - -1890 -1894 0 - -1890 -1895 0 - -1890 -1896 0 - -1891 -1892 0 - -1891 -1893 0 - -1891 -1894 0 - -1891 -1895 0 - -1891 -1896 0 - -1892 -1893 0 - -1892 -1894 0 - -1892 -1895 0 - -1892 -1896 0 - -1893 -1894 0 - -1893 -1895 0 - -1893 -1896 0 - -1894 -1895 0 - -1894 -1896 0 - -1895 -1896 0 - -1897 -1898 0 - -1897 -1899 0 - -1897 -1900 0 - -1897 -1901 0 - -1898 -1899 0 - -1898 -1900 0 - -1898 -1901 0 - -1899 -1900 0 - -1899 -1901 0 - -1900 -1901 0 - -1902 -1903 0 - -1902 -1904 0 - -1902 -1905 0 - -1902 -1906 0 - -1902 -1907 0 - -1902 -1908 0 - -1902 -1909 0 - -1903 -1904 0 - -1903 -1905 0 - -1903 -1906 0 - -1903 -1907 0 - -1903 -1908 0 - -1903 -1909 0 - -1904 -1905 0 - -1904 -1906 0 - -1904 -1907 0 - -1904 -1908 0 - -1904 -1909 0 - -1905 -1906 0 - -1905 -1907 0 - -1905 -1908 0 - -1905 -1909 0 - -1906 -1907 0 - -1906 -1908 0 - -1906 -1909 0 - -1907 -1908 0 - -1907 -1909 0 - -1908 -1909 0 - -1910 -1911 0 - -1910 -1912 0 - -1910 -1913 0 - -1910 -1914 0 - -1910 -1915 0 - -1910 -1916 0 - -1910 -1917 0 - -1910 -1918 0 - -1910 -1919 0 - -1910 -1920 0 - -1911 -1912 0 - -1911 -1913 0 - -1911 -1914 0 - -1911 -1915 0 - -1911 -1916 0 - -1911 -1917 0 - -1911 -1918 0 - -1911 -1919 0 - -1911 -1920 0 - -1912 -1913 0 - -1912 -1914 0 - -1912 -1915 0 - -1912 -1916 0 - -1912 -1917 0 - -1912 -1918 0 - -1912 -1919 0 - -1912 -1920 0 - -1913 -1914 0 - -1913 -1915 0 - -1913 -1916 0 - -1913 -1917 0 - -1913 -1918 0 - -1913 -1919 0 - -1913 -1920 0 - -1914 -1915 0 - -1914 -1916 0 - -1914 -1917 0 - -1914 -1918 0 - -1914 -1919 0 - -1914 -1920 0 - -1915 -1916 0 - -1915 -1917 0 - -1915 -1918 0 - -1915 -1919 0 - -1915 -1920 0 - -1916 -1917 0 - -1916 -1918 0 - -1916 -1919 0 - -1916 -1920 0 - -1917 -1918 0 - -1917 -1919 0 - -1917 -1920 0 - -1918 -1919 0 - -1918 -1920 0 - -1919 -1920 0 - -1921 -1922 0 - -1921 -1923 0 - -1921 -1924 0 - -1921 -1925 0 - -1921 -1926 0 - -1921 -1927 0 - -1921 -1928 0 - -1922 -1923 0 - -1922 -1924 0 - -1922 -1925 0 - -1922 -1926 0 - -1922 -1927 0 - -1922 -1928 0 - -1923 -1924 0 - -1923 -1925 0 - -1923 -1926 0 - -1923 -1927 0 - -1923 -1928 0 - -1924 -1925 0 - -1924 -1926 0 - -1924 -1927 0 - -1924 -1928 0 - -1925 -1926 0 - -1925 -1927 0 - -1925 -1928 0 - -1926 -1927 0 - -1926 -1928 0 - -1927 -1928 0 - -1929 -1930 0 - -1929 -1931 0 - -1929 -1932 0 - -1929 -1933 0 - -1929 -1934 0 - -1929 -1935 0 - -1930 -1931 0 - -1930 -1932 0 - -1930 -1933 0 - -1930 -1934 0 - -1930 -1935 0 - -1931 -1932 0 - -1931 -1933 0 - -1931 -1934 0 - -1931 -1935 0 - -1932 -1933 0 - -1932 -1934 0 - -1932 -1935 0 - -1933 -1934 0 - -1933 -1935 0 - -1934 -1935 0 - -1936 -1937 0 - -1936 -1938 0 - -1936 -1939 0 - -1936 -1940 0 - -1936 -1941 0 - -1936 -1942 0 - -1937 -1938 0 - -1937 -1939 0 - -1937 -1940 0 - -1937 -1941 0 - -1937 -1942 0 - -1938 -1939 0 - -1938 -1940 0 - -1938 -1941 0 - -1938 -1942 0 - -1939 -1940 0 - -1939 -1941 0 - -1939 -1942 0 - -1940 -1941 0 - -1940 -1942 0 - -1941 -1942 0 - -1943 -1944 0 - -1943 -1945 0 - -1943 -1946 0 - -1943 -1947 0 - -1943 -1948 0 - -1943 -1949 0 - -1943 -1950 0 - -1943 -1951 0 - -1944 -1945 0 - -1944 -1946 0 - -1944 -1947 0 - -1944 -1948 0 - -1944 -1949 0 - -1944 -1950 0 - -1944 -1951 0 - -1945 -1946 0 - -1945 -1947 0 - -1945 -1948 0 - -1945 -1949 0 - -1945 -1950 0 - -1945 -1951 0 - -1946 -1947 0 - -1946 -1948 0 - -1946 -1949 0 - -1946 -1950 0 - -1946 -1951 0 - -1947 -1948 0 - -1947 -1949 0 - -1947 -1950 0 - -1947 -1951 0 - -1948 -1949 0 - -1948 -1950 0 - -1948 -1951 0 - -1949 -1950 0 - -1949 -1951 0 - -1950 -1951 0 - -1952 -1953 0 - -1952 -1954 0 - -1952 -1955 0 - -1952 -1956 0 - -1952 -1957 0 - -1952 -1958 0 - -1952 -1959 0 - -1952 -1960 0 - -1953 -1954 0 - -1953 -1955 0 - -1953 -1956 0 - -1953 -1957 0 - -1953 -1958 0 - -1953 -1959 0 - -1953 -1960 0 - -1954 -1955 0 - -1954 -1956 0 - -1954 -1957 0 - -1954 -1958 0 - -1954 -1959 0 - -1954 -1960 0 - -1955 -1956 0 - -1955 -1957 0 - -1955 -1958 0 - -1955 -1959 0 - -1955 -1960 0 - -1956 -1957 0 - -1956 -1958 0 - -1956 -1959 0 - -1956 -1960 0 - -1957 -1958 0 - -1957 -1959 0 - -1957 -1960 0 - -1958 -1959 0 - -1958 -1960 0 - -1959 -1960 0 - -1961 -1962 0 - -1961 -1963 0 - -1961 -1964 0 - -1961 -1965 0 - -1961 -1966 0 - -1961 -1967 0 - -1961 -1968 0 - -1961 -1969 0 - -1962 -1963 0 - -1962 -1964 0 - -1962 -1965 0 - -1962 -1966 0 - -1962 -1967 0 - -1962 -1968 0 - -1962 -1969 0 - -1963 -1964 0 - -1963 -1965 0 - -1963 -1966 0 - -1963 -1967 0 - -1963 -1968 0 - -1963 -1969 0 - -1964 -1965 0 - -1964 -1966 0 - -1964 -1967 0 - -1964 -1968 0 - -1964 -1969 0 - -1965 -1966 0 - -1965 -1967 0 - -1965 -1968 0 - -1965 -1969 0 - -1966 -1967 0 - -1966 -1968 0 - -1966 -1969 0 - -1967 -1968 0 - -1967 -1969 0 - -1968 -1969 0 - -1970 -1971 0 - -1970 -1972 0 - -1970 -1973 0 - -1970 -1974 0 - -1970 -1975 0 - -1970 -1976 0 - -1971 -1972 0 - -1971 -1973 0 - -1971 -1974 0 - -1971 -1975 0 - -1971 -1976 0 - -1972 -1973 0 - -1972 -1974 0 - -1972 -1975 0 - -1972 -1976 0 - -1973 -1974 0 - -1973 -1975 0 - -1973 -1976 0 - -1974 -1975 0 - -1974 -1976 0 - -1975 -1976 0 - -1977 -1978 0 - -1977 -1979 0 - -1977 -1980 0 - -1977 -1981 0 - -1977 -1982 0 - -1978 -1979 0 - -1978 -1980 0 - -1978 -1981 0 - -1978 -1982 0 - -1979 -1980 0 - -1979 -1981 0 - -1979 -1982 0 - -1980 -1981 0 - -1980 -1982 0 - -1981 -1982 0 - -1983 -1984 0 - -1983 -1985 0 - -1983 -1986 0 - -1983 -1987 0 - -1983 -1988 0 - -1983 -1989 0 - -1983 -1990 0 - -1984 -1985 0 - -1984 -1986 0 - -1984 -1987 0 - -1984 -1988 0 - -1984 -1989 0 - -1984 -1990 0 - -1985 -1986 0 - -1985 -1987 0 - -1985 -1988 0 - -1985 -1989 0 - -1985 -1990 0 - -1986 -1987 0 - -1986 -1988 0 - -1986 -1989 0 - -1986 -1990 0 - -1987 -1988 0 - -1987 -1989 0 - -1987 -1990 0 - -1988 -1989 0 - -1988 -1990 0 - -1989 -1990 0 - -1991 -1992 0 - -1991 -1993 0 - -1991 -1994 0 - -1991 -1995 0 - -1991 -1996 0 - -1992 -1993 0 - -1992 -1994 0 - -1992 -1995 0 - -1992 -1996 0 - -1993 -1994 0 - -1993 -1995 0 - -1993 -1996 0 - -1994 -1995 0 - -1994 -1996 0 - -1995 -1996 0 - -1997 -1998 0 - -1997 -1999 0 - -1997 -2000 0 - -1997 -2001 0 - -1997 -2002 0 - -1997 -2003 0 - -1997 -2004 0 - -1997 -2005 0 - -1997 -2006 0 - -1998 -1999 0 - -1998 -2000 0 - -1998 -2001 0 - -1998 -2002 0 - -1998 -2003 0 - -1998 -2004 0 - -1998 -2005 0 - -1998 -2006 0 - -1999 -2000 0 - -1999 -2001 0 - -1999 -2002 0 - -1999 -2003 0 - -1999 -2004 0 - -1999 -2005 0 - -1999 -2006 0 - -2000 -2001 0 - -2000 -2002 0 - -2000 -2003 0 - -2000 -2004 0 - -2000 -2005 0 - -2000 -2006 0 - -2001 -2002 0 - -2001 -2003 0 - -2001 -2004 0 - -2001 -2005 0 - -2001 -2006 0 - -2002 -2003 0 - -2002 -2004 0 - -2002 -2005 0 - -2002 -2006 0 - -2003 -2004 0 - -2003 -2005 0 - -2003 -2006 0 - -2004 -2005 0 - -2004 -2006 0 - -2005 -2006 0 - -2007 -2008 0 - -2007 -2009 0 - -2007 -2010 0 - -2007 -2011 0 - -2007 -2012 0 - -2007 -2013 0 - -2007 -2014 0 - -2008 -2009 0 - -2008 -2010 0 - -2008 -2011 0 - -2008 -2012 0 - -2008 -2013 0 - -2008 -2014 0 - -2009 -2010 0 - -2009 -2011 0 - -2009 -2012 0 - -2009 -2013 0 - -2009 -2014 0 - -2010 -2011 0 - -2010 -2012 0 - -2010 -2013 0 - -2010 -2014 0 - -2011 -2012 0 - -2011 -2013 0 - -2011 -2014 0 - -2012 -2013 0 - -2012 -2014 0 - -2013 -2014 0 - -2015 -2016 0 - -2015 -2017 0 - -2015 -2018 0 - -2015 -2019 0 - -2015 -2020 0 - -2015 -2021 0 - -2015 -2022 0 - -2015 -2023 0 - -2015 -2024 0 - -2015 -2025 0 - -2015 -2026 0 - -2016 -2017 0 - -2016 -2018 0 - -2016 -2019 0 - -2016 -2020 0 - -2016 -2021 0 - -2016 -2022 0 - -2016 -2023 0 - -2016 -2024 0 - -2016 -2025 0 - -2016 -2026 0 - -2017 -2018 0 - -2017 -2019 0 - -2017 -2020 0 - -2017 -2021 0 - -2017 -2022 0 - -2017 -2023 0 - -2017 -2024 0 - -2017 -2025 0 - -2017 -2026 0 - -2018 -2019 0 - -2018 -2020 0 - -2018 -2021 0 - -2018 -2022 0 - -2018 -2023 0 - -2018 -2024 0 - -2018 -2025 0 - -2018 -2026 0 - -2019 -2020 0 - -2019 -2021 0 - -2019 -2022 0 - -2019 -2023 0 - -2019 -2024 0 - -2019 -2025 0 - -2019 -2026 0 - -2020 -2021 0 - -2020 -2022 0 - -2020 -2023 0 - -2020 -2024 0 - -2020 -2025 0 - -2020 -2026 0 - -2021 -2022 0 - -2021 -2023 0 - -2021 -2024 0 - -2021 -2025 0 - -2021 -2026 0 - -2022 -2023 0 - -2022 -2024 0 - -2022 -2025 0 - -2022 -2026 0 - -2023 -2024 0 - -2023 -2025 0 - -2023 -2026 0 - -2024 -2025 0 - -2024 -2026 0 - -2025 -2026 0 - -2027 -2028 0 - -2027 -2029 0 - -2027 -2030 0 - -2027 -2031 0 - -2027 -2032 0 - -2027 -2033 0 - -2027 -2034 0 - -2027 -2035 0 - -2027 -2036 0 - -2027 -2037 0 - -2027 -2038 0 - -2028 -2029 0 - -2028 -2030 0 - -2028 -2031 0 - -2028 -2032 0 - -2028 -2033 0 - -2028 -2034 0 - -2028 -2035 0 - -2028 -2036 0 - -2028 -2037 0 - -2028 -2038 0 - -2029 -2030 0 - -2029 -2031 0 - -2029 -2032 0 - -2029 -2033 0 - -2029 -2034 0 - -2029 -2035 0 - -2029 -2036 0 - -2029 -2037 0 - -2029 -2038 0 - -2030 -2031 0 - -2030 -2032 0 - -2030 -2033 0 - -2030 -2034 0 - -2030 -2035 0 - -2030 -2036 0 - -2030 -2037 0 - -2030 -2038 0 - -2031 -2032 0 - -2031 -2033 0 - -2031 -2034 0 - -2031 -2035 0 - -2031 -2036 0 - -2031 -2037 0 - -2031 -2038 0 - -2032 -2033 0 - -2032 -2034 0 - -2032 -2035 0 - -2032 -2036 0 - -2032 -2037 0 - -2032 -2038 0 - -2033 -2034 0 - -2033 -2035 0 - -2033 -2036 0 - -2033 -2037 0 - -2033 -2038 0 - -2034 -2035 0 - -2034 -2036 0 - -2034 -2037 0 - -2034 -2038 0 - -2035 -2036 0 - -2035 -2037 0 - -2035 -2038 0 - -2036 -2037 0 - -2036 -2038 0 - -2037 -2038 0 - -2039 -2040 0 - -2039 -2041 0 - -2039 -2042 0 - -2039 -2043 0 - -2040 -2041 0 - -2040 -2042 0 - -2040 -2043 0 - -2041 -2042 0 - -2041 -2043 0 - -2042 -2043 0 - -2044 -2045 0 - -2044 -2046 0 - -2044 -2047 0 - -2044 -2048 0 - -2044 -2049 0 - -2044 -2050 0 - -2044 -2051 0 - -2044 -2052 0 - -2044 -2053 0 - -2044 -2054 0 - -2045 -2046 0 - -2045 -2047 0 - -2045 -2048 0 - -2045 -2049 0 - -2045 -2050 0 - -2045 -2051 0 - -2045 -2052 0 - -2045 -2053 0 - -2045 -2054 0 - -2046 -2047 0 - -2046 -2048 0 - -2046 -2049 0 - -2046 -2050 0 - -2046 -2051 0 - -2046 -2052 0 - -2046 -2053 0 - -2046 -2054 0 - -2047 -2048 0 - -2047 -2049 0 - -2047 -2050 0 - -2047 -2051 0 - -2047 -2052 0 - -2047 -2053 0 - -2047 -2054 0 - -2048 -2049 0 - -2048 -2050 0 - -2048 -2051 0 - -2048 -2052 0 - -2048 -2053 0 - -2048 -2054 0 - -2049 -2050 0 - -2049 -2051 0 - -2049 -2052 0 - -2049 -2053 0 - -2049 -2054 0 - -2050 -2051 0 - -2050 -2052 0 - -2050 -2053 0 - -2050 -2054 0 - -2051 -2052 0 - -2051 -2053 0 - -2051 -2054 0 - -2052 -2053 0 - -2052 -2054 0 - -2053 -2054 0 - -2055 -2056 0 - -2055 -2057 0 - -2055 -2058 0 - -2055 -2059 0 - -2055 -2060 0 - -2055 -2061 0 - -2056 -2057 0 - -2056 -2058 0 - -2056 -2059 0 - -2056 -2060 0 - -2056 -2061 0 - -2057 -2058 0 - -2057 -2059 0 - -2057 -2060 0 - -2057 -2061 0 - -2058 -2059 0 - -2058 -2060 0 - -2058 -2061 0 - -2059 -2060 0 - -2059 -2061 0 - -2060 -2061 0 - -2062 -2063 0 - -2062 -2064 0 - -2062 -2065 0 - -2062 -2066 0 - -2062 -2067 0 - -2062 -2068 0 - -2062 -2069 0 - -2062 -2070 0 - -2062 -2071 0 - -2062 -2072 0 - -2062 -2073 0 - -2063 -2064 0 - -2063 -2065 0 - -2063 -2066 0 - -2063 -2067 0 - -2063 -2068 0 - -2063 -2069 0 - -2063 -2070 0 - -2063 -2071 0 - -2063 -2072 0 - -2063 -2073 0 - -2064 -2065 0 - -2064 -2066 0 - -2064 -2067 0 - -2064 -2068 0 - -2064 -2069 0 - -2064 -2070 0 - -2064 -2071 0 - -2064 -2072 0 - -2064 -2073 0 - -2065 -2066 0 - -2065 -2067 0 - -2065 -2068 0 - -2065 -2069 0 - -2065 -2070 0 - -2065 -2071 0 - -2065 -2072 0 - -2065 -2073 0 - -2066 -2067 0 - -2066 -2068 0 - -2066 -2069 0 - -2066 -2070 0 - -2066 -2071 0 - -2066 -2072 0 - -2066 -2073 0 - -2067 -2068 0 - -2067 -2069 0 - -2067 -2070 0 - -2067 -2071 0 - -2067 -2072 0 - -2067 -2073 0 - -2068 -2069 0 - -2068 -2070 0 - -2068 -2071 0 - -2068 -2072 0 - -2068 -2073 0 - -2069 -2070 0 - -2069 -2071 0 - -2069 -2072 0 - -2069 -2073 0 - -2070 -2071 0 - -2070 -2072 0 - -2070 -2073 0 - -2071 -2072 0 - -2071 -2073 0 - -2072 -2073 0 - -2074 -2075 0 - -2074 -2076 0 - -2074 -2077 0 - -2074 -2078 0 - -2074 -2079 0 - -2074 -2080 0 - -2074 -2081 0 - -2074 -2082 0 - -2075 -2076 0 - -2075 -2077 0 - -2075 -2078 0 - -2075 -2079 0 - -2075 -2080 0 - -2075 -2081 0 - -2075 -2082 0 - -2076 -2077 0 - -2076 -2078 0 - -2076 -2079 0 - -2076 -2080 0 - -2076 -2081 0 - -2076 -2082 0 - -2077 -2078 0 - -2077 -2079 0 - -2077 -2080 0 - -2077 -2081 0 - -2077 -2082 0 - -2078 -2079 0 - -2078 -2080 0 - -2078 -2081 0 - -2078 -2082 0 - -2079 -2080 0 - -2079 -2081 0 - -2079 -2082 0 - -2080 -2081 0 - -2080 -2082 0 - -2081 -2082 0 - -2083 -2084 0 - -2083 -2085 0 - -2083 -2086 0 - -2083 -2087 0 - -2084 -2085 0 - -2084 -2086 0 - -2084 -2087 0 - -2085 -2086 0 - -2085 -2087 0 - -2086 -2087 0 - -2088 -2089 0 - -2088 -2090 0 - -2088 -2091 0 - -2088 -2092 0 - -2088 -2093 0 - -2088 -2094 0 - -2088 -2095 0 - -2088 -2096 0 - -2088 -2097 0 - -2089 -2090 0 - -2089 -2091 0 - -2089 -2092 0 - -2089 -2093 0 - -2089 -2094 0 - -2089 -2095 0 - -2089 -2096 0 - -2089 -2097 0 - -2090 -2091 0 - -2090 -2092 0 - -2090 -2093 0 - -2090 -2094 0 - -2090 -2095 0 - -2090 -2096 0 - -2090 -2097 0 - -2091 -2092 0 - -2091 -2093 0 - -2091 -2094 0 - -2091 -2095 0 - -2091 -2096 0 - -2091 -2097 0 - -2092 -2093 0 - -2092 -2094 0 - -2092 -2095 0 - -2092 -2096 0 - -2092 -2097 0 - -2093 -2094 0 - -2093 -2095 0 - -2093 -2096 0 - -2093 -2097 0 - -2094 -2095 0 - -2094 -2096 0 - -2094 -2097 0 - -2095 -2096 0 - -2095 -2097 0 - -2096 -2097 0 - -2098 -2099 0 - -2098 -2100 0 - -2098 -2101 0 - -2098 -2102 0 - -2098 -2103 0 - -2098 -2104 0 - -2098 -2105 0 - -2098 -2106 0 - -2099 -2100 0 - -2099 -2101 0 - -2099 -2102 0 - -2099 -2103 0 - -2099 -2104 0 - -2099 -2105 0 - -2099 -2106 0 - -2100 -2101 0 - -2100 -2102 0 - -2100 -2103 0 - -2100 -2104 0 - -2100 -2105 0 - -2100 -2106 0 - -2101 -2102 0 - -2101 -2103 0 - -2101 -2104 0 - -2101 -2105 0 - -2101 -2106 0 - -2102 -2103 0 - -2102 -2104 0 - -2102 -2105 0 - -2102 -2106 0 - -2103 -2104 0 - -2103 -2105 0 - -2103 -2106 0 - -2104 -2105 0 - -2104 -2106 0 - -2105 -2106 0 - -2107 -2108 0 - -2107 -2109 0 - -2107 -2110 0 - -2107 -2111 0 - -2107 -2112 0 - -2107 -2113 0 - -2107 -2114 0 - -2107 -2115 0 - -2107 -2116 0 - -2107 -2117 0 - -2108 -2109 0 - -2108 -2110 0 - -2108 -2111 0 - -2108 -2112 0 - -2108 -2113 0 - -2108 -2114 0 - -2108 -2115 0 - -2108 -2116 0 - -2108 -2117 0 - -2109 -2110 0 - -2109 -2111 0 - -2109 -2112 0 - -2109 -2113 0 - -2109 -2114 0 - -2109 -2115 0 - -2109 -2116 0 - -2109 -2117 0 - -2110 -2111 0 - -2110 -2112 0 - -2110 -2113 0 - -2110 -2114 0 - -2110 -2115 0 - -2110 -2116 0 - -2110 -2117 0 - -2111 -2112 0 - -2111 -2113 0 - -2111 -2114 0 - -2111 -2115 0 - -2111 -2116 0 - -2111 -2117 0 - -2112 -2113 0 - -2112 -2114 0 - -2112 -2115 0 - -2112 -2116 0 - -2112 -2117 0 - -2113 -2114 0 - -2113 -2115 0 - -2113 -2116 0 - -2113 -2117 0 - -2114 -2115 0 - -2114 -2116 0 - -2114 -2117 0 - -2115 -2116 0 - -2115 -2117 0 - -2116 -2117 0 - -2118 -2119 0 - -2118 -2120 0 - -2118 -2121 0 - -2118 -2122 0 - -2118 -2123 0 - -2118 -2124 0 - -2119 -2120 0 - -2119 -2121 0 - -2119 -2122 0 - -2119 -2123 0 - -2119 -2124 0 - -2120 -2121 0 - -2120 -2122 0 - -2120 -2123 0 - -2120 -2124 0 - -2121 -2122 0 - -2121 -2123 0 - -2121 -2124 0 - -2122 -2123 0 - -2122 -2124 0 - -2123 -2124 0 - -2125 -2126 0 - -2125 -2127 0 - -2125 -2128 0 - -2125 -2129 0 - -2125 -2130 0 - -2125 -2131 0 - -2126 -2127 0 - -2126 -2128 0 - -2126 -2129 0 - -2126 -2130 0 - -2126 -2131 0 - -2127 -2128 0 - -2127 -2129 0 - -2127 -2130 0 - -2127 -2131 0 - -2128 -2129 0 - -2128 -2130 0 - -2128 -2131 0 - -2129 -2130 0 - -2129 -2131 0 - -2130 -2131 0 - -2132 -2133 0 - -2132 -2134 0 - -2132 -2135 0 - -2132 -2136 0 - -2132 -2137 0 - -2132 -2138 0 - -2132 -2139 0 - -2133 -2134 0 - -2133 -2135 0 - -2133 -2136 0 - -2133 -2137 0 - -2133 -2138 0 - -2133 -2139 0 - -2134 -2135 0 - -2134 -2136 0 - -2134 -2137 0 - -2134 -2138 0 - -2134 -2139 0 - -2135 -2136 0 - -2135 -2137 0 - -2135 -2138 0 - -2135 -2139 0 - -2136 -2137 0 - -2136 -2138 0 - -2136 -2139 0 - -2137 -2138 0 - -2137 -2139 0 - -2138 -2139 0 - -2140 -2141 0 - -2140 -2142 0 - -2140 -2143 0 - -2140 -2144 0 - -2140 -2145 0 - -2141 -2142 0 - -2141 -2143 0 - -2141 -2144 0 - -2141 -2145 0 - -2142 -2143 0 - -2142 -2144 0 - -2142 -2145 0 - -2143 -2144 0 - -2143 -2145 0 - -2144 -2145 0 - -2146 -2147 0 - -2146 -2148 0 - -2146 -2149 0 - -2146 -2150 0 - -2146 -2151 0 - -2146 -2152 0 - -2146 -2153 0 - -2147 -2148 0 - -2147 -2149 0 - -2147 -2150 0 - -2147 -2151 0 - -2147 -2152 0 - -2147 -2153 0 - -2148 -2149 0 - -2148 -2150 0 - -2148 -2151 0 - -2148 -2152 0 - -2148 -2153 0 - -2149 -2150 0 - -2149 -2151 0 - -2149 -2152 0 - -2149 -2153 0 - -2150 -2151 0 - -2150 -2152 0 - -2150 -2153 0 - -2151 -2152 0 - -2151 -2153 0 - -2152 -2153 0 - -2154 -2155 0 - -2154 -2156 0 - -2154 -2157 0 - -2154 -2158 0 - -2154 -2159 0 - -2154 -2160 0 - -2154 -2161 0 - -2155 -2156 0 - -2155 -2157 0 - -2155 -2158 0 - -2155 -2159 0 - -2155 -2160 0 - -2155 -2161 0 - -2156 -2157 0 - -2156 -2158 0 - -2156 -2159 0 - -2156 -2160 0 - -2156 -2161 0 - -2157 -2158 0 - -2157 -2159 0 - -2157 -2160 0 - -2157 -2161 0 - -2158 -2159 0 - -2158 -2160 0 - -2158 -2161 0 - -2159 -2160 0 - -2159 -2161 0 - -2160 -2161 0 - -2162 -2163 0 - -2162 -2164 0 - -2162 -2165 0 - -2162 -2166 0 - -2162 -2167 0 - -2163 -2164 0 - -2163 -2165 0 - -2163 -2166 0 - -2163 -2167 0 - -2164 -2165 0 - -2164 -2166 0 - -2164 -2167 0 - -2165 -2166 0 - -2165 -2167 0 - -2166 -2167 0 - -2168 -2169 0 - -2168 -2170 0 - -2168 -2171 0 - -2168 -2172 0 - -2168 -2173 0 - -2168 -2174 0 - -2168 -2175 0 - -2168 -2176 0 - -2169 -2170 0 - -2169 -2171 0 - -2169 -2172 0 - -2169 -2173 0 - -2169 -2174 0 - -2169 -2175 0 - -2169 -2176 0 - -2170 -2171 0 - -2170 -2172 0 - -2170 -2173 0 - -2170 -2174 0 - -2170 -2175 0 - -2170 -2176 0 - -2171 -2172 0 - -2171 -2173 0 - -2171 -2174 0 - -2171 -2175 0 - -2171 -2176 0 - -2172 -2173 0 - -2172 -2174 0 - -2172 -2175 0 - -2172 -2176 0 - -2173 -2174 0 - -2173 -2175 0 - -2173 -2176 0 - -2174 -2175 0 - -2174 -2176 0 - -2175 -2176 0 - -2177 -2178 0 - -2177 -2179 0 - -2177 -2180 0 - -2177 -2181 0 - -2177 -2182 0 - -2177 -2183 0 - -2177 -2184 0 - -2177 -2185 0 - -2178 -2179 0 - -2178 -2180 0 - -2178 -2181 0 - -2178 -2182 0 - -2178 -2183 0 - -2178 -2184 0 - -2178 -2185 0 - -2179 -2180 0 - -2179 -2181 0 - -2179 -2182 0 - -2179 -2183 0 - -2179 -2184 0 - -2179 -2185 0 - -2180 -2181 0 - -2180 -2182 0 - -2180 -2183 0 - -2180 -2184 0 - -2180 -2185 0 - -2181 -2182 0 - -2181 -2183 0 - -2181 -2184 0 - -2181 -2185 0 - -2182 -2183 0 - -2182 -2184 0 - -2182 -2185 0 - -2183 -2184 0 - -2183 -2185 0 - -2184 -2185 0 - -2186 -2187 0 - -2186 -2188 0 - -2186 -2189 0 - -2186 -2190 0 - -2186 -2191 0 - -2186 -2192 0 - -2186 -2193 0 - -2186 -2194 0 - -2186 -2195 0 - -2186 -2196 0 - -2187 -2188 0 - -2187 -2189 0 - -2187 -2190 0 - -2187 -2191 0 - -2187 -2192 0 - -2187 -2193 0 - -2187 -2194 0 - -2187 -2195 0 - -2187 -2196 0 - -2188 -2189 0 - -2188 -2190 0 - -2188 -2191 0 - -2188 -2192 0 - -2188 -2193 0 - -2188 -2194 0 - -2188 -2195 0 - -2188 -2196 0 - -2189 -2190 0 - -2189 -2191 0 - -2189 -2192 0 - -2189 -2193 0 - -2189 -2194 0 - -2189 -2195 0 - -2189 -2196 0 - -2190 -2191 0 - -2190 -2192 0 - -2190 -2193 0 - -2190 -2194 0 - -2190 -2195 0 - -2190 -2196 0 - -2191 -2192 0 - -2191 -2193 0 - -2191 -2194 0 - -2191 -2195 0 - -2191 -2196 0 - -2192 -2193 0 - -2192 -2194 0 - -2192 -2195 0 - -2192 -2196 0 - -2193 -2194 0 - -2193 -2195 0 - -2193 -2196 0 - -2194 -2195 0 - -2194 -2196 0 - -2195 -2196 0 - -2197 -2198 0 - -2197 -2199 0 - -2197 -2200 0 - -2197 -2201 0 - -2197 -2202 0 - -2198 -2199 0 - -2198 -2200 0 - -2198 -2201 0 - -2198 -2202 0 - -2199 -2200 0 - -2199 -2201 0 - -2199 -2202 0 - -2200 -2201 0 - -2200 -2202 0 - -2201 -2202 0 - -2203 -2204 0 - -2203 -2205 0 - -2203 -2206 0 - -2203 -2207 0 - -2203 -2208 0 - -2204 -2205 0 - -2204 -2206 0 - -2204 -2207 0 - -2204 -2208 0 - -2205 -2206 0 - -2205 -2207 0 - -2205 -2208 0 - -2206 -2207 0 - -2206 -2208 0 - -2207 -2208 0 - -2209 -2210 0 - -2209 -2211 0 - -2209 -2212 0 - -2210 -2211 0 - -2210 -2212 0 - -2211 -2212 0 - -2213 -2214 0 - -2213 -2215 0 - -2213 -2216 0 - -2213 -2217 0 - -2213 -2218 0 - -2213 -2219 0 - -2214 -2215 0 - -2214 -2216 0 - -2214 -2217 0 - -2214 -2218 0 - -2214 -2219 0 - -2215 -2216 0 - -2215 -2217 0 - -2215 -2218 0 - -2215 -2219 0 - -2216 -2217 0 - -2216 -2218 0 - -2216 -2219 0 - -2217 -2218 0 - -2217 -2219 0 - -2218 -2219 0 - -2220 -2221 0 - -2220 -2222 0 - -2220 -2223 0 - -2220 -2224 0 - -2220 -2225 0 - -2221 -2222 0 - -2221 -2223 0 - -2221 -2224 0 - -2221 -2225 0 - -2222 -2223 0 - -2222 -2224 0 - -2222 -2225 0 - -2223 -2224 0 - -2223 -2225 0 - -2224 -2225 0 - -2226 -2227 0 - -2226 -2228 0 - -2227 -2228 0 - -2229 -2230 0 - -2229 -2231 0 - -2229 -2232 0 - -2229 -2233 0 - -2229 -2234 0 - -2229 -2235 0 - -2229 -2236 0 - -2230 -2231 0 - -2230 -2232 0 - -2230 -2233 0 - -2230 -2234 0 - -2230 -2235 0 - -2230 -2236 0 - -2231 -2232 0 - -2231 -2233 0 - -2231 -2234 0 - -2231 -2235 0 - -2231 -2236 0 - -2232 -2233 0 - -2232 -2234 0 - -2232 -2235 0 - -2232 -2236 0 - -2233 -2234 0 - -2233 -2235 0 - -2233 -2236 0 - -2234 -2235 0 - -2234 -2236 0 - -2235 -2236 0 - -2237 -2238 0 - -2237 -2239 0 - -2237 -2240 0 - -2237 -2241 0 - -2238 -2239 0 - -2238 -2240 0 - -2238 -2241 0 - -2239 -2240 0 - -2239 -2241 0 - -2240 -2241 0 - -2242 -2243 0 - -2242 -2244 0 - -2242 -2245 0 - -2242 -2246 0 - -2242 -2247 0 - -2242 -2248 0 - -2242 -2249 0 - -2243 -2244 0 - -2243 -2245 0 - -2243 -2246 0 - -2243 -2247 0 - -2243 -2248 0 - -2243 -2249 0 - -2244 -2245 0 - -2244 -2246 0 - -2244 -2247 0 - -2244 -2248 0 - -2244 -2249 0 - -2245 -2246 0 - -2245 -2247 0 - -2245 -2248 0 - -2245 -2249 0 - -2246 -2247 0 - -2246 -2248 0 - -2246 -2249 0 - -2247 -2248 0 - -2247 -2249 0 - -2248 -2249 0 - -2250 -2251 0 - -2250 -2252 0 - -2250 -2253 0 - -2250 -2254 0 - -2250 -2255 0 - -2250 -2256 0 - -2250 -2257 0 - -2250 -2258 0 - -2250 -2259 0 - -2251 -2252 0 - -2251 -2253 0 - -2251 -2254 0 - -2251 -2255 0 - -2251 -2256 0 - -2251 -2257 0 - -2251 -2258 0 - -2251 -2259 0 - -2252 -2253 0 - -2252 -2254 0 - -2252 -2255 0 - -2252 -2256 0 - -2252 -2257 0 - -2252 -2258 0 - -2252 -2259 0 - -2253 -2254 0 - -2253 -2255 0 - -2253 -2256 0 - -2253 -2257 0 - -2253 -2258 0 - -2253 -2259 0 - -2254 -2255 0 - -2254 -2256 0 - -2254 -2257 0 - -2254 -2258 0 - -2254 -2259 0 - -2255 -2256 0 - -2255 -2257 0 - -2255 -2258 0 - -2255 -2259 0 - -2256 -2257 0 - -2256 -2258 0 - -2256 -2259 0 - -2257 -2258 0 - -2257 -2259 0 - -2258 -2259 0 - -2260 -2261 0 - -2260 -2262 0 - -2260 -2263 0 - -2260 -2264 0 - -2260 -2265 0 - -2261 -2262 0 - -2261 -2263 0 - -2261 -2264 0 - -2261 -2265 0 - -2262 -2263 0 - -2262 -2264 0 - -2262 -2265 0 - -2263 -2264 0 - -2263 -2265 0 - -2264 -2265 0 - -2266 -2267 0 - -2266 -2268 0 - -2266 -2269 0 - -2266 -2270 0 - -2266 -2271 0 - -2266 -2272 0 - -2267 -2268 0 - -2267 -2269 0 - -2267 -2270 0 - -2267 -2271 0 - -2267 -2272 0 - -2268 -2269 0 - -2268 -2270 0 - -2268 -2271 0 - -2268 -2272 0 - -2269 -2270 0 - -2269 -2271 0 - -2269 -2272 0 - -2270 -2271 0 - -2270 -2272 0 - -2271 -2272 0 - -2273 -2274 0 - -2273 -2275 0 - -2273 -2276 0 - -2273 -2277 0 - -2274 -2275 0 - -2274 -2276 0 - -2274 -2277 0 - -2275 -2276 0 - -2275 -2277 0 - -2276 -2277 0 - -2278 -2279 0 - -2278 -2280 0 - -2278 -2281 0 - -2278 -2282 0 - -2279 -2280 0 - -2279 -2281 0 - -2279 -2282 0 - -2280 -2281 0 - -2280 -2282 0 - -2281 -2282 0 - -2283 -2284 0 - -2283 -2285 0 - -2283 -2286 0 - -2283 -2287 0 - -2283 -2288 0 - -2284 -2285 0 - -2284 -2286 0 - -2284 -2287 0 - -2284 -2288 0 - -2285 -2286 0 - -2285 -2287 0 - -2285 -2288 0 - -2286 -2287 0 - -2286 -2288 0 - -2287 -2288 0 - -2289 -2290 0 - -2289 -2291 0 - -2289 -2292 0 - -2289 -2293 0 - -2290 -2291 0 - -2290 -2292 0 - -2290 -2293 0 - -2291 -2292 0 - -2291 -2293 0 - -2292 -2293 0 - -2294 -2295 0 - -2294 -2296 0 - -2294 -2297 0 - -2294 -2298 0 - -2295 -2296 0 - -2295 -2297 0 - -2295 -2298 0 - -2296 -2297 0 - -2296 -2298 0 - -2297 -2298 0 - -2299 -2300 0 - -2299 -2301 0 - -2300 -2301 0 - -2302 -2303 0 - -2302 -2304 0 - -2302 -2305 0 - -2302 -2306 0 - -2303 -2304 0 - -2303 -2305 0 - -2303 -2306 0 - -2304 -2305 0 - -2304 -2306 0 - -2305 -2306 0 - -2307 -2308 0 - -2307 -2309 0 - -2307 -2310 0 - -2307 -2311 0 - -2307 -2312 0 - -2307 -2313 0 - -2308 -2309 0 - -2308 -2310 0 - -2308 -2311 0 - -2308 -2312 0 - -2308 -2313 0 - -2309 -2310 0 - -2309 -2311 0 - -2309 -2312 0 - -2309 -2313 0 - -2310 -2311 0 - -2310 -2312 0 - -2310 -2313 0 - -2311 -2312 0 - -2311 -2313 0 - -2312 -2313 0 - -2314 -2315 0 - -2314 -2316 0 - -2314 -2317 0 - -2314 -2318 0 - -2315 -2316 0 - -2315 -2317 0 - -2315 -2318 0 - -2316 -2317 0 - -2316 -2318 0 - -2317 -2318 0 - -2319 -2320 0 - -2319 -2321 0 - -2319 -2322 0 - -2320 -2321 0 - -2320 -2322 0 - -2321 -2322 0 - -2323 -2324 0 - -2323 -2325 0 - -2323 -2326 0 - -2323 -2327 0 - -2323 -2328 0 - -2323 -2329 0 - -2324 -2325 0 - -2324 -2326 0 - -2324 -2327 0 - -2324 -2328 0 - -2324 -2329 0 - -2325 -2326 0 - -2325 -2327 0 - -2325 -2328 0 - -2325 -2329 0 - -2326 -2327 0 - -2326 -2328 0 - -2326 -2329 0 - -2327 -2328 0 - -2327 -2329 0 - -2328 -2329 0 - -2330 -2331 0 - -2330 -2332 0 - -2330 -2333 0 - -2331 -2332 0 - -2331 -2333 0 - -2332 -2333 0 - -2334 -2335 0 - -2334 -2336 0 - -2334 -2337 0 - -2334 -2338 0 - -2334 -2339 0 - -2334 -2340 0 - -2334 -2341 0 - -2334 -2342 0 - -2334 -2343 0 - -2335 -2336 0 - -2335 -2337 0 - -2335 -2338 0 - -2335 -2339 0 - -2335 -2340 0 - -2335 -2341 0 - -2335 -2342 0 - -2335 -2343 0 - -2336 -2337 0 - -2336 -2338 0 - -2336 -2339 0 - -2336 -2340 0 - -2336 -2341 0 - -2336 -2342 0 - -2336 -2343 0 - -2337 -2338 0 - -2337 -2339 0 - -2337 -2340 0 - -2337 -2341 0 - -2337 -2342 0 - -2337 -2343 0 - -2338 -2339 0 - -2338 -2340 0 - -2338 -2341 0 - -2338 -2342 0 - -2338 -2343 0 - -2339 -2340 0 - -2339 -2341 0 - -2339 -2342 0 - -2339 -2343 0 - -2340 -2341 0 - -2340 -2342 0 - -2340 -2343 0 - -2341 -2342 0 - -2341 -2343 0 - -2342 -2343 0 - -2344 -2345 0 - -2344 -2346 0 - -2344 -2347 0 - -2344 -2348 0 - -2344 -2349 0 - -2344 -2350 0 - -2345 -2346 0 - -2345 -2347 0 - -2345 -2348 0 - -2345 -2349 0 - -2345 -2350 0 - -2346 -2347 0 - -2346 -2348 0 - -2346 -2349 0 - -2346 -2350 0 - -2347 -2348 0 - -2347 -2349 0 - -2347 -2350 0 - -2348 -2349 0 - -2348 -2350 0 - -2349 -2350 0 - -2351 -2352 0 - -2351 -2353 0 - -2351 -2354 0 - -2351 -2355 0 - -2352 -2353 0 - -2352 -2354 0 - -2352 -2355 0 - -2353 -2354 0 - -2353 -2355 0 - -2354 -2355 0 - -2356 -2357 0 - -2356 -2358 0 - -2356 -2359 0 - -2357 -2358 0 - -2357 -2359 0 - -2358 -2359 0 - -2360 -2361 0 - -2360 -2362 0 - -2360 -2363 0 - -2360 -2364 0 - -2360 -2365 0 - -2361 -2362 0 - -2361 -2363 0 - -2361 -2364 0 - -2361 -2365 0 - -2362 -2363 0 - -2362 -2364 0 - -2362 -2365 0 - -2363 -2364 0 - -2363 -2365 0 - -2364 -2365 0 - -2366 -2367 0 - -2366 -2368 0 - -2367 -2368 0 - -2369 -2370 0 - -2369 -2371 0 - -2369 -2372 0 - -2369 -2373 0 - -2369 -2374 0 - -2369 -2375 0 - -2369 -2376 0 - -2370 -2371 0 - -2370 -2372 0 - -2370 -2373 0 - -2370 -2374 0 - -2370 -2375 0 - -2370 -2376 0 - -2371 -2372 0 - -2371 -2373 0 - -2371 -2374 0 - -2371 -2375 0 - -2371 -2376 0 - -2372 -2373 0 - -2372 -2374 0 - -2372 -2375 0 - -2372 -2376 0 - -2373 -2374 0 - -2373 -2375 0 - -2373 -2376 0 - -2374 -2375 0 - -2374 -2376 0 - -2375 -2376 0 - -2377 -2378 0 - -2377 -2379 0 - -2377 -2380 0 - -2378 -2379 0 - -2378 -2380 0 - -2379 -2380 0 - -2381 -2382 0 - -2381 -2383 0 - -2381 -2384 0 - -2381 -2385 0 - -2381 -2386 0 - -2381 -2387 0 - -2382 -2383 0 - -2382 -2384 0 - -2382 -2385 0 - -2382 -2386 0 - -2382 -2387 0 - -2383 -2384 0 - -2383 -2385 0 - -2383 -2386 0 - -2383 -2387 0 - -2384 -2385 0 - -2384 -2386 0 - -2384 -2387 0 - -2385 -2386 0 - -2385 -2387 0 - -2386 -2387 0 - -2388 -2389 0 - -2388 -2390 0 - -2388 -2391 0 - -2389 -2390 0 - -2389 -2391 0 - -2390 -2391 0 - -2392 -2393 0 - -2392 -2394 0 - -2392 -2395 0 - -2392 -2396 0 - -2392 -2397 0 - -2392 -2398 0 - -2393 -2394 0 - -2393 -2395 0 - -2393 -2396 0 - -2393 -2397 0 - -2393 -2398 0 - -2394 -2395 0 - -2394 -2396 0 - -2394 -2397 0 - -2394 -2398 0 - -2395 -2396 0 - -2395 -2397 0 - -2395 -2398 0 - -2396 -2397 0 - -2396 -2398 0 - -2397 -2398 0 - -2399 -2400 0 - -2399 -2401 0 - -2399 -2402 0 - -2399 -2403 0 - -2399 -2404 0 - -2400 -2401 0 - -2400 -2402 0 - -2400 -2403 0 - -2400 -2404 0 - -2401 -2402 0 - -2401 -2403 0 - -2401 -2404 0 - -2402 -2403 0 - -2402 -2404 0 - -2403 -2404 0 - -2405 -2406 0 - -2405 -2407 0 - -2405 -2408 0 - -2405 -2409 0 - -2405 -2410 0 - -2406 -2407 0 - -2406 -2408 0 - -2406 -2409 0 - -2406 -2410 0 - -2407 -2408 0 - -2407 -2409 0 - -2407 -2410 0 - -2408 -2409 0 - -2408 -2410 0 - -2409 -2410 0 - -2411 -2412 0 - -2411 -2413 0 - -2411 -2414 0 - -2411 -2415 0 - -2411 -2416 0 - -2411 -2417 0 - -2412 -2413 0 - -2412 -2414 0 - -2412 -2415 0 - -2412 -2416 0 - -2412 -2417 0 - -2413 -2414 0 - -2413 -2415 0 - -2413 -2416 0 - -2413 -2417 0 - -2414 -2415 0 - -2414 -2416 0 - -2414 -2417 0 - -2415 -2416 0 - -2415 -2417 0 - -2416 -2417 0 - -2418 -2419 0 - -2418 -2420 0 - -2418 -2421 0 - -2418 -2422 0 - -2418 -2423 0 - -2419 -2420 0 - -2419 -2421 0 - -2419 -2422 0 - -2419 -2423 0 - -2420 -2421 0 - -2420 -2422 0 - -2420 -2423 0 - -2421 -2422 0 - -2421 -2423 0 - -2422 -2423 0 - -2424 -2425 0 - -2424 -2426 0 - -2424 -2427 0 - -2424 -2428 0 - -2424 -2429 0 - -2425 -2426 0 - -2425 -2427 0 - -2425 -2428 0 - -2425 -2429 0 - -2426 -2427 0 - -2426 -2428 0 - -2426 -2429 0 - -2427 -2428 0 - -2427 -2429 0 - -2428 -2429 0 - -2430 -2431 0 - -2430 -2432 0 - -2430 -2433 0 - -2430 -2434 0 - -2430 -2435 0 - -2430 -2436 0 - -2430 -2437 0 - -2430 -2438 0 - -2430 -2439 0 - -2430 -2440 0 - -2431 -2432 0 - -2431 -2433 0 - -2431 -2434 0 - -2431 -2435 0 - -2431 -2436 0 - -2431 -2437 0 - -2431 -2438 0 - -2431 -2439 0 - -2431 -2440 0 - -2432 -2433 0 - -2432 -2434 0 - -2432 -2435 0 - -2432 -2436 0 - -2432 -2437 0 - -2432 -2438 0 - -2432 -2439 0 - -2432 -2440 0 - -2433 -2434 0 - -2433 -2435 0 - -2433 -2436 0 - -2433 -2437 0 - -2433 -2438 0 - -2433 -2439 0 - -2433 -2440 0 - -2434 -2435 0 - -2434 -2436 0 - -2434 -2437 0 - -2434 -2438 0 - -2434 -2439 0 - -2434 -2440 0 - -2435 -2436 0 - -2435 -2437 0 - -2435 -2438 0 - -2435 -2439 0 - -2435 -2440 0 - -2436 -2437 0 - -2436 -2438 0 - -2436 -2439 0 - -2436 -2440 0 - -2437 -2438 0 - -2437 -2439 0 - -2437 -2440 0 - -2438 -2439 0 - -2438 -2440 0 - -2439 -2440 0 - -2441 -2442 0 - -2441 -2443 0 - -2441 -2444 0 - -2441 -2445 0 - -2441 -2446 0 - -2442 -2443 0 - -2442 -2444 0 - -2442 -2445 0 - -2442 -2446 0 - -2443 -2444 0 - -2443 -2445 0 - -2443 -2446 0 - -2444 -2445 0 - -2444 -2446 0 - -2445 -2446 0 - -2447 -2448 0 - -2447 -2449 0 - -2447 -2450 0 - -2447 -2451 0 - -2448 -2449 0 - -2448 -2450 0 - -2448 -2451 0 - -2449 -2450 0 - -2449 -2451 0 - -2450 -2451 0 - -2452 -2453 0 - -2452 -2454 0 - -2452 -2455 0 - -2452 -2456 0 - -2452 -2457 0 - -2452 -2458 0 - -2452 -2459 0 - -2452 -2460 0 - -2453 -2454 0 - -2453 -2455 0 - -2453 -2456 0 - -2453 -2457 0 - -2453 -2458 0 - -2453 -2459 0 - -2453 -2460 0 - -2454 -2455 0 - -2454 -2456 0 - -2454 -2457 0 - -2454 -2458 0 - -2454 -2459 0 - -2454 -2460 0 - -2455 -2456 0 - -2455 -2457 0 - -2455 -2458 0 - -2455 -2459 0 - -2455 -2460 0 - -2456 -2457 0 - -2456 -2458 0 - -2456 -2459 0 - -2456 -2460 0 - -2457 -2458 0 - -2457 -2459 0 - -2457 -2460 0 - -2458 -2459 0 - -2458 -2460 0 - -2459 -2460 0 - -2461 -2462 0 - -2461 -2463 0 - -2461 -2464 0 - -2461 -2465 0 - -2461 -2466 0 - -2461 -2467 0 - -2461 -2468 0 - -2461 -2469 0 - -2461 -2470 0 - -2461 -2471 0 - -2462 -2463 0 - -2462 -2464 0 - -2462 -2465 0 - -2462 -2466 0 - -2462 -2467 0 - -2462 -2468 0 - -2462 -2469 0 - -2462 -2470 0 - -2462 -2471 0 - -2463 -2464 0 - -2463 -2465 0 - -2463 -2466 0 - -2463 -2467 0 - -2463 -2468 0 - -2463 -2469 0 - -2463 -2470 0 - -2463 -2471 0 - -2464 -2465 0 - -2464 -2466 0 - -2464 -2467 0 - -2464 -2468 0 - -2464 -2469 0 - -2464 -2470 0 - -2464 -2471 0 - -2465 -2466 0 - -2465 -2467 0 - -2465 -2468 0 - -2465 -2469 0 - -2465 -2470 0 - -2465 -2471 0 - -2466 -2467 0 - -2466 -2468 0 - -2466 -2469 0 - -2466 -2470 0 - -2466 -2471 0 - -2467 -2468 0 - -2467 -2469 0 - -2467 -2470 0 - -2467 -2471 0 - -2468 -2469 0 - -2468 -2470 0 - -2468 -2471 0 - -2469 -2470 0 - -2469 -2471 0 - -2470 -2471 0 - -2472 -2473 0 - -2472 -2474 0 - -2472 -2475 0 - -2472 -2476 0 - -2472 -2477 0 - -2472 -2478 0 - -2472 -2479 0 - -2473 -2474 0 - -2473 -2475 0 - -2473 -2476 0 - -2473 -2477 0 - -2473 -2478 0 - -2473 -2479 0 - -2474 -2475 0 - -2474 -2476 0 - -2474 -2477 0 - -2474 -2478 0 - -2474 -2479 0 - -2475 -2476 0 - -2475 -2477 0 - -2475 -2478 0 - -2475 -2479 0 - -2476 -2477 0 - -2476 -2478 0 - -2476 -2479 0 - -2477 -2478 0 - -2477 -2479 0 - -2478 -2479 0 - -2480 -2481 0 - -2480 -2482 0 - -2480 -2483 0 - -2480 -2484 0 - -2480 -2485 0 - -2480 -2486 0 - -2481 -2482 0 - -2481 -2483 0 - -2481 -2484 0 - -2481 -2485 0 - -2481 -2486 0 - -2482 -2483 0 - -2482 -2484 0 - -2482 -2485 0 - -2482 -2486 0 - -2483 -2484 0 - -2483 -2485 0 - -2483 -2486 0 - -2484 -2485 0 - -2484 -2486 0 - -2485 -2486 0 - -2487 -2488 0 - -2487 -2489 0 - -2487 -2490 0 - -2487 -2491 0 - -2487 -2492 0 - -2487 -2493 0 - -2488 -2489 0 - -2488 -2490 0 - -2488 -2491 0 - -2488 -2492 0 - -2488 -2493 0 - -2489 -2490 0 - -2489 -2491 0 - -2489 -2492 0 - -2489 -2493 0 - -2490 -2491 0 - -2490 -2492 0 - -2490 -2493 0 - -2491 -2492 0 - -2491 -2493 0 - -2492 -2493 0 - -2494 -2495 0 - -2494 -2496 0 - -2494 -2497 0 - -2494 -2498 0 - -2494 -2499 0 - -2495 -2496 0 - -2495 -2497 0 - -2495 -2498 0 - -2495 -2499 0 - -2496 -2497 0 - -2496 -2498 0 - -2496 -2499 0 - -2497 -2498 0 - -2497 -2499 0 - -2498 -2499 0 - -2500 -2501 0 - -2500 -2502 0 - -2500 -2503 0 - -2501 -2502 0 - -2501 -2503 0 - -2502 -2503 0 - -2504 -2505 0 - -2504 -2506 0 - -2504 -2507 0 - -2504 -2508 0 - -2504 -2509 0 - -2505 -2506 0 - -2505 -2507 0 - -2505 -2508 0 - -2505 -2509 0 - -2506 -2507 0 - -2506 -2508 0 - -2506 -2509 0 - -2507 -2508 0 - -2507 -2509 0 - -2508 -2509 0 - -2510 -2511 0 - -2510 -2512 0 - -2510 -2513 0 - -2510 -2514 0 - -2510 -2515 0 - -2510 -2516 0 - -2510 -2517 0 - -2511 -2512 0 - -2511 -2513 0 - -2511 -2514 0 - -2511 -2515 0 - -2511 -2516 0 - -2511 -2517 0 - -2512 -2513 0 - -2512 -2514 0 - -2512 -2515 0 - -2512 -2516 0 - -2512 -2517 0 - -2513 -2514 0 - -2513 -2515 0 - -2513 -2516 0 - -2513 -2517 0 - -2514 -2515 0 - -2514 -2516 0 - -2514 -2517 0 - -2515 -2516 0 - -2515 -2517 0 - -2516 -2517 0 - -2518 -2519 0 - -2518 -2520 0 - -2518 -2521 0 - -2518 -2522 0 - -2518 -2523 0 - -2519 -2520 0 - -2519 -2521 0 - -2519 -2522 0 - -2519 -2523 0 - -2520 -2521 0 - -2520 -2522 0 - -2520 -2523 0 - -2521 -2522 0 - -2521 -2523 0 - -2522 -2523 0 - -2524 -2525 0 - -2524 -2526 0 - -2524 -2527 0 - -2524 -2528 0 - -2524 -2529 0 - -2524 -2530 0 - -2524 -2531 0 - -2525 -2526 0 - -2525 -2527 0 - -2525 -2528 0 - -2525 -2529 0 - -2525 -2530 0 - -2525 -2531 0 - -2526 -2527 0 - -2526 -2528 0 - -2526 -2529 0 - -2526 -2530 0 - -2526 -2531 0 - -2527 -2528 0 - -2527 -2529 0 - -2527 -2530 0 - -2527 -2531 0 - -2528 -2529 0 - -2528 -2530 0 - -2528 -2531 0 - -2529 -2530 0 - -2529 -2531 0 - -2530 -2531 0 - -2532 -2533 0 - -2532 -2534 0 - -2532 -2535 0 - -2532 -2536 0 - -2532 -2537 0 - -2532 -2538 0 - -2533 -2534 0 - -2533 -2535 0 - -2533 -2536 0 - -2533 -2537 0 - -2533 -2538 0 - -2534 -2535 0 - -2534 -2536 0 - -2534 -2537 0 - -2534 -2538 0 - -2535 -2536 0 - -2535 -2537 0 - -2535 -2538 0 - -2536 -2537 0 - -2536 -2538 0 - -2537 -2538 0 - -2539 -2540 0 - -2539 -2541 0 - -2539 -2542 0 - -2539 -2543 0 - -2539 -2544 0 - -2540 -2541 0 - -2540 -2542 0 - -2540 -2543 0 - -2540 -2544 0 - -2541 -2542 0 - -2541 -2543 0 - -2541 -2544 0 - -2542 -2543 0 - -2542 -2544 0 - -2543 -2544 0 - -2545 -2546 0 - -2545 -2547 0 - -2545 -2548 0 - -2545 -2549 0 - -2545 -2550 0 - -2545 -2551 0 - -2545 -2552 0 - -2545 -2553 0 - -2545 -2554 0 - -2546 -2547 0 - -2546 -2548 0 - -2546 -2549 0 - -2546 -2550 0 - -2546 -2551 0 - -2546 -2552 0 - -2546 -2553 0 - -2546 -2554 0 - -2547 -2548 0 - -2547 -2549 0 - -2547 -2550 0 - -2547 -2551 0 - -2547 -2552 0 - -2547 -2553 0 - -2547 -2554 0 - -2548 -2549 0 - -2548 -2550 0 - -2548 -2551 0 - -2548 -2552 0 - -2548 -2553 0 - -2548 -2554 0 - -2549 -2550 0 - -2549 -2551 0 - -2549 -2552 0 - -2549 -2553 0 - -2549 -2554 0 - -2550 -2551 0 - -2550 -2552 0 - -2550 -2553 0 - -2550 -2554 0 - -2551 -2552 0 - -2551 -2553 0 - -2551 -2554 0 - -2552 -2553 0 - -2552 -2554 0 - -2553 -2554 0 - -2555 -2556 0 - -2555 -2557 0 - -2555 -2558 0 - -2555 -2559 0 - -2555 -2560 0 - -2556 -2557 0 - -2556 -2558 0 - -2556 -2559 0 - -2556 -2560 0 - -2557 -2558 0 - -2557 -2559 0 - -2557 -2560 0 - -2558 -2559 0 - -2558 -2560 0 - -2559 -2560 0 - -2561 -2562 0 - -2561 -2563 0 - -2561 -2564 0 - -2562 -2563 0 - -2562 -2564 0 - -2563 -2564 0 - -2565 -2566 0 - -2565 -2567 0 - -2565 -2568 0 - -2565 -2569 0 - -2566 -2567 0 - -2566 -2568 0 - -2566 -2569 0 - -2567 -2568 0 - -2567 -2569 0 - -2568 -2569 0 - -2570 -2571 0 - -2572 -2573 0 - -2572 -2574 0 - -2572 -2575 0 - -2572 -2576 0 - -2572 -2577 0 - -2572 -2578 0 - -2572 -2579 0 - -2572 -2580 0 - -2573 -2574 0 - -2573 -2575 0 - -2573 -2576 0 - -2573 -2577 0 - -2573 -2578 0 - -2573 -2579 0 - -2573 -2580 0 - -2574 -2575 0 - -2574 -2576 0 - -2574 -2577 0 - -2574 -2578 0 - -2574 -2579 0 - -2574 -2580 0 - -2575 -2576 0 - -2575 -2577 0 - -2575 -2578 0 - -2575 -2579 0 - -2575 -2580 0 - -2576 -2577 0 - -2576 -2578 0 - -2576 -2579 0 - -2576 -2580 0 - -2577 -2578 0 - -2577 -2579 0 - -2577 -2580 0 - -2578 -2579 0 - -2578 -2580 0 - -2579 -2580 0 - -2581 -2582 0 - -2581 -2583 0 - -2581 -2584 0 - -2581 -2585 0 - -2581 -2586 0 - -2582 -2583 0 - -2582 -2584 0 - -2582 -2585 0 - -2582 -2586 0 - -2583 -2584 0 - -2583 -2585 0 - -2583 -2586 0 - -2584 -2585 0 - -2584 -2586 0 - -2585 -2586 0 - -2587 -2588 0 - -2587 -2589 0 - -2587 -2590 0 - -2587 -2591 0 - -2587 -2592 0 - -2587 -2593 0 - -2588 -2589 0 - -2588 -2590 0 - -2588 -2591 0 - -2588 -2592 0 - -2588 -2593 0 - -2589 -2590 0 - -2589 -2591 0 - -2589 -2592 0 - -2589 -2593 0 - -2590 -2591 0 - -2590 -2592 0 - -2590 -2593 0 - -2591 -2592 0 - -2591 -2593 0 - -2592 -2593 0 - -2594 -2595 0 - -2594 -2596 0 - -2594 -2597 0 - -2594 -2598 0 - -2594 -2599 0 - -2595 -2596 0 - -2595 -2597 0 - -2595 -2598 0 - -2595 -2599 0 - -2596 -2597 0 - -2596 -2598 0 - -2596 -2599 0 - -2597 -2598 0 - -2597 -2599 0 - -2598 -2599 0 - -2600 -2601 0 - -2600 -2602 0 - -2600 -2603 0 - -2600 -2604 0 - -2600 -2605 0 - -2601 -2602 0 - -2601 -2603 0 - -2601 -2604 0 - -2601 -2605 0 - -2602 -2603 0 - -2602 -2604 0 - -2602 -2605 0 - -2603 -2604 0 - -2603 -2605 0 - -2604 -2605 0 - -2606 -2607 0 - -2606 -2608 0 - -2606 -2609 0 - -2606 -2610 0 - -2606 -2611 0 - -2607 -2608 0 - -2607 -2609 0 - -2607 -2610 0 - -2607 -2611 0 - -2608 -2609 0 - -2608 -2610 0 - -2608 -2611 0 - -2609 -2610 0 - -2609 -2611 0 - -2610 -2611 0 - -2612 -2613 0 - -2612 -2614 0 - -2612 -2615 0 - -2612 -2616 0 - -2612 -2617 0 - -2612 -2618 0 - -2613 -2614 0 - -2613 -2615 0 - -2613 -2616 0 - -2613 -2617 0 - -2613 -2618 0 - -2614 -2615 0 - -2614 -2616 0 - -2614 -2617 0 - -2614 -2618 0 - -2615 -2616 0 - -2615 -2617 0 - -2615 -2618 0 - -2616 -2617 0 - -2616 -2618 0 - -2617 -2618 0 - -2619 -2620 0 - -2619 -2621 0 - -2619 -2622 0 - -2620 -2621 0 - -2620 -2622 0 - -2621 -2622 0 - -2623 -2624 0 - -2623 -2625 0 - -2624 -2625 0 - -2626 -2627 0 - -2626 -2628 0 - -2626 -2629 0 - -2626 -2630 0 - -2626 -2631 0 - -2627 -2628 0 - -2627 -2629 0 - -2627 -2630 0 - -2627 -2631 0 - -2628 -2629 0 - -2628 -2630 0 - -2628 -2631 0 - -2629 -2630 0 - -2629 -2631 0 - -2630 -2631 0 - -2632 -2633 0 - -2632 -2634 0 - -2632 -2635 0 - -2633 -2634 0 - -2633 -2635 0 - -2634 -2635 0 - -2636 -2637 0 - -2636 -2638 0 - -2636 -2639 0 - -2636 -2640 0 - -2636 -2641 0 - -2637 -2638 0 - -2637 -2639 0 - -2637 -2640 0 - -2637 -2641 0 - -2638 -2639 0 - -2638 -2640 0 - -2638 -2641 0 - -2639 -2640 0 - -2639 -2641 0 - -2640 -2641 0 - -2642 -2643 0 - -2642 -2644 0 - -2642 -2645 0 - -2642 -2646 0 - -2642 -2647 0 - -2642 -2648 0 - -2642 -2649 0 - -2643 -2644 0 - -2643 -2645 0 - -2643 -2646 0 - -2643 -2647 0 - -2643 -2648 0 - -2643 -2649 0 - -2644 -2645 0 - -2644 -2646 0 - -2644 -2647 0 - -2644 -2648 0 - -2644 -2649 0 - -2645 -2646 0 - -2645 -2647 0 - -2645 -2648 0 - -2645 -2649 0 - -2646 -2647 0 - -2646 -2648 0 - -2646 -2649 0 - -2647 -2648 0 - -2647 -2649 0 - -2648 -2649 0 - -2650 -2651 0 - -2650 -2652 0 - -2650 -2653 0 - -2650 -2654 0 - -2650 -2655 0 - -2650 -2656 0 - -2651 -2652 0 - -2651 -2653 0 - -2651 -2654 0 - -2651 -2655 0 - -2651 -2656 0 - -2652 -2653 0 - -2652 -2654 0 - -2652 -2655 0 - -2652 -2656 0 - -2653 -2654 0 - -2653 -2655 0 - -2653 -2656 0 - -2654 -2655 0 - -2654 -2656 0 - -2655 -2656 0 - -2657 -2658 0 - -2657 -2659 0 - -2657 -2660 0 - -2658 -2659 0 - -2658 -2660 0 - -2659 -2660 0 - -2661 -2662 0 - -2661 -2663 0 - -2661 -2664 0 - -2662 -2663 0 - -2662 -2664 0 - -2663 -2664 0 - -2665 -2666 0 - -2665 -2667 0 - -2665 -2668 0 - -2665 -2669 0 - -2665 -2670 0 - -2666 -2667 0 - -2666 -2668 0 - -2666 -2669 0 - -2666 -2670 0 - -2667 -2668 0 - -2667 -2669 0 - -2667 -2670 0 - -2668 -2669 0 - -2668 -2670 0 - -2669 -2670 0 - -2671 -2672 0 - -2671 -2673 0 - -2672 -2673 0 - -2674 -2675 0 - -2674 -2676 0 - -2674 -2677 0 - -2674 -2678 0 - -2674 -2679 0 - -2674 -2680 0 - -2675 -2676 0 - -2675 -2677 0 - -2675 -2678 0 - -2675 -2679 0 - -2675 -2680 0 - -2676 -2677 0 - -2676 -2678 0 - -2676 -2679 0 - -2676 -2680 0 - -2677 -2678 0 - -2677 -2679 0 - -2677 -2680 0 - -2678 -2679 0 - -2678 -2680 0 - -2679 -2680 0 - -2681 -2682 0 - -2681 -2683 0 - -2681 -2684 0 - -2682 -2683 0 - -2682 -2684 0 - -2683 -2684 0 - -2685 -2686 0 - -2685 -2687 0 - -2685 -2688 0 - -2685 -2689 0 - -2685 -2690 0 - -2685 -2691 0 - -2685 -2692 0 - -2686 -2687 0 - -2686 -2688 0 - -2686 -2689 0 - -2686 -2690 0 - -2686 -2691 0 - -2686 -2692 0 - -2687 -2688 0 - -2687 -2689 0 - -2687 -2690 0 - -2687 -2691 0 - -2687 -2692 0 - -2688 -2689 0 - -2688 -2690 0 - -2688 -2691 0 - -2688 -2692 0 - -2689 -2690 0 - -2689 -2691 0 - -2689 -2692 0 - -2690 -2691 0 - -2690 -2692 0 - -2691 -2692 0 - -2693 -2694 0 - -2693 -2695 0 - -2693 -2696 0 - -2693 -2697 0 - -2693 -2698 0 - -2693 -2699 0 - -2693 -2700 0 - -2693 -2701 0 - -2693 -2702 0 - -2694 -2695 0 - -2694 -2696 0 - -2694 -2697 0 - -2694 -2698 0 - -2694 -2699 0 - -2694 -2700 0 - -2694 -2701 0 - -2694 -2702 0 - -2695 -2696 0 - -2695 -2697 0 - -2695 -2698 0 - -2695 -2699 0 - -2695 -2700 0 - -2695 -2701 0 - -2695 -2702 0 - -2696 -2697 0 - -2696 -2698 0 - -2696 -2699 0 - -2696 -2700 0 - -2696 -2701 0 - -2696 -2702 0 - -2697 -2698 0 - -2697 -2699 0 - -2697 -2700 0 - -2697 -2701 0 - -2697 -2702 0 - -2698 -2699 0 - -2698 -2700 0 - -2698 -2701 0 - -2698 -2702 0 - -2699 -2700 0 - -2699 -2701 0 - -2699 -2702 0 - -2700 -2701 0 - -2700 -2702 0 - -2701 -2702 0 - -2703 -2704 0 - -2703 -2705 0 - -2703 -2706 0 - -2703 -2707 0 - -2703 -2708 0 - -2703 -2709 0 - -2703 -2710 0 - -2704 -2705 0 - -2704 -2706 0 - -2704 -2707 0 - -2704 -2708 0 - -2704 -2709 0 - -2704 -2710 0 - -2705 -2706 0 - -2705 -2707 0 - -2705 -2708 0 - -2705 -2709 0 - -2705 -2710 0 - -2706 -2707 0 - -2706 -2708 0 - -2706 -2709 0 - -2706 -2710 0 - -2707 -2708 0 - -2707 -2709 0 - -2707 -2710 0 - -2708 -2709 0 - -2708 -2710 0 - -2709 -2710 0 - -2711 -2712 0 - -2711 -2713 0 - -2711 -2714 0 - -2711 -2715 0 - -2711 -2716 0 - -2711 -2717 0 - -2711 -2718 0 - -2711 -2719 0 - -2712 -2713 0 - -2712 -2714 0 - -2712 -2715 0 - -2712 -2716 0 - -2712 -2717 0 - -2712 -2718 0 - -2712 -2719 0 - -2713 -2714 0 - -2713 -2715 0 - -2713 -2716 0 - -2713 -2717 0 - -2713 -2718 0 - -2713 -2719 0 - -2714 -2715 0 - -2714 -2716 0 - -2714 -2717 0 - -2714 -2718 0 - -2714 -2719 0 - -2715 -2716 0 - -2715 -2717 0 - -2715 -2718 0 - -2715 -2719 0 - -2716 -2717 0 - -2716 -2718 0 - -2716 -2719 0 - -2717 -2718 0 - -2717 -2719 0 - -2718 -2719 0 - -2720 -2721 0 - -2720 -2722 0 - -2720 -2723 0 - -2720 -2724 0 - -2721 -2722 0 - -2721 -2723 0 - -2721 -2724 0 - -2722 -2723 0 - -2722 -2724 0 - -2723 -2724 0 - -2725 -2726 0 - -2725 -2727 0 - -2725 -2728 0 - -2726 -2727 0 - -2726 -2728 0 - -2727 -2728 0 - -2729 -2730 0 - -2729 -2731 0 - -2729 -2732 0 - -2729 -2733 0 - -2729 -2734 0 - -2729 -2735 0 - -2729 -2736 0 - -2729 -2737 0 - -2729 -2738 0 - -2730 -2731 0 - -2730 -2732 0 - -2730 -2733 0 - -2730 -2734 0 - -2730 -2735 0 - -2730 -2736 0 - -2730 -2737 0 - -2730 -2738 0 - -2731 -2732 0 - -2731 -2733 0 - -2731 -2734 0 - -2731 -2735 0 - -2731 -2736 0 - -2731 -2737 0 - -2731 -2738 0 - -2732 -2733 0 - -2732 -2734 0 - -2732 -2735 0 - -2732 -2736 0 - -2732 -2737 0 - -2732 -2738 0 - -2733 -2734 0 - -2733 -2735 0 - -2733 -2736 0 - -2733 -2737 0 - -2733 -2738 0 - -2734 -2735 0 - -2734 -2736 0 - -2734 -2737 0 - -2734 -2738 0 - -2735 -2736 0 - -2735 -2737 0 - -2735 -2738 0 - -2736 -2737 0 - -2736 -2738 0 - -2737 -2738 0 - -2739 -2740 0 - -2739 -2741 0 - -2739 -2742 0 - -2739 -2743 0 - -2739 -2744 0 - -2740 -2741 0 - -2740 -2742 0 - -2740 -2743 0 - -2740 -2744 0 - -2741 -2742 0 - -2741 -2743 0 - -2741 -2744 0 - -2742 -2743 0 - -2742 -2744 0 - -2743 -2744 0 - -2745 -2746 0 - -2745 -2747 0 - -2745 -2748 0 - -2745 -2749 0 - -2745 -2750 0 - -2746 -2747 0 - -2746 -2748 0 - -2746 -2749 0 - -2746 -2750 0 - -2747 -2748 0 - -2747 -2749 0 - -2747 -2750 0 - -2748 -2749 0 - -2748 -2750 0 - -2749 -2750 0 - -2751 -2752 0 - -2751 -2753 0 - -2751 -2754 0 - -2751 -2755 0 - -2752 -2753 0 - -2752 -2754 0 - -2752 -2755 0 - -2753 -2754 0 - -2753 -2755 0 - -2754 -2755 0 - -2756 -2757 0 - -2756 -2758 0 - -2756 -2759 0 - -2756 -2760 0 - -2756 -2761 0 - -2756 -2762 0 - -2757 -2758 0 - -2757 -2759 0 - -2757 -2760 0 - -2757 -2761 0 - -2757 -2762 0 - -2758 -2759 0 - -2758 -2760 0 - -2758 -2761 0 - -2758 -2762 0 - -2759 -2760 0 - -2759 -2761 0 - -2759 -2762 0 - -2760 -2761 0 - -2760 -2762 0 - -2761 -2762 0 - -2763 -2764 0 - -2763 -2765 0 - -2763 -2766 0 - -2763 -2767 0 - -2763 -2768 0 - -2764 -2765 0 - -2764 -2766 0 - -2764 -2767 0 - -2764 -2768 0 - -2765 -2766 0 - -2765 -2767 0 - -2765 -2768 0 - -2766 -2767 0 - -2766 -2768 0 - -2767 -2768 0 - -2769 -2770 0 - -2769 -2771 0 - -2769 -2772 0 - -2769 -2773 0 - -2769 -2774 0 - -2770 -2771 0 - -2770 -2772 0 - -2770 -2773 0 - -2770 -2774 0 - -2771 -2772 0 - -2771 -2773 0 - -2771 -2774 0 - -2772 -2773 0 - -2772 -2774 0 - -2773 -2774 0 - -2775 -2776 0 - -2775 -2777 0 - -2775 -2778 0 - -2775 -2779 0 - -2775 -2780 0 - -2775 -2781 0 - -2775 -2782 0 - -2775 -2783 0 - -2775 -2784 0 - -2776 -2777 0 - -2776 -2778 0 - -2776 -2779 0 - -2776 -2780 0 - -2776 -2781 0 - -2776 -2782 0 - -2776 -2783 0 - -2776 -2784 0 - -2777 -2778 0 - -2777 -2779 0 - -2777 -2780 0 - -2777 -2781 0 - -2777 -2782 0 - -2777 -2783 0 - -2777 -2784 0 - -2778 -2779 0 - -2778 -2780 0 - -2778 -2781 0 - -2778 -2782 0 - -2778 -2783 0 - -2778 -2784 0 - -2779 -2780 0 - -2779 -2781 0 - -2779 -2782 0 - -2779 -2783 0 - -2779 -2784 0 - -2780 -2781 0 - -2780 -2782 0 - -2780 -2783 0 - -2780 -2784 0 - -2781 -2782 0 - -2781 -2783 0 - -2781 -2784 0 - -2782 -2783 0 - -2782 -2784 0 - -2783 -2784 0 - -2785 -2786 0 - -2785 -2787 0 - -2785 -2788 0 - -2785 -2789 0 - -2785 -2790 0 - -2785 -2791 0 - -2785 -2792 0 - -2786 -2787 0 - -2786 -2788 0 - -2786 -2789 0 - -2786 -2790 0 - -2786 -2791 0 - -2786 -2792 0 - -2787 -2788 0 - -2787 -2789 0 - -2787 -2790 0 - -2787 -2791 0 - -2787 -2792 0 - -2788 -2789 0 - -2788 -2790 0 - -2788 -2791 0 - -2788 -2792 0 - -2789 -2790 0 - -2789 -2791 0 - -2789 -2792 0 - -2790 -2791 0 - -2790 -2792 0 - -2791 -2792 0 - -2793 -2794 0 - -2793 -2795 0 - -2793 -2796 0 - -2793 -2797 0 - -2793 -2798 0 - -2794 -2795 0 - -2794 -2796 0 - -2794 -2797 0 - -2794 -2798 0 - -2795 -2796 0 - -2795 -2797 0 - -2795 -2798 0 - -2796 -2797 0 - -2796 -2798 0 - -2797 -2798 0 - -2799 -2800 0 - -2799 -2801 0 - -2799 -2802 0 - -2799 -2803 0 - -2799 -2804 0 - -2800 -2801 0 - -2800 -2802 0 - -2800 -2803 0 - -2800 -2804 0 - -2801 -2802 0 - -2801 -2803 0 - -2801 -2804 0 - -2802 -2803 0 - -2802 -2804 0 - -2803 -2804 0 - -2805 -2806 0 - -2805 -2807 0 - -2805 -2808 0 - -2805 -2809 0 - -2806 -2807 0 - -2806 -2808 0 - -2806 -2809 0 - -2807 -2808 0 - -2807 -2809 0 - -2808 -2809 0 - -2810 -2811 0 - -2810 -2812 0 - -2810 -2813 0 - -2810 -2814 0 - -2810 -2815 0 - -2810 -2816 0 - -2810 -2817 0 - -2810 -2818 0 - -2810 -2819 0 - -2811 -2812 0 - -2811 -2813 0 - -2811 -2814 0 - -2811 -2815 0 - -2811 -2816 0 - -2811 -2817 0 - -2811 -2818 0 - -2811 -2819 0 - -2812 -2813 0 - -2812 -2814 0 - -2812 -2815 0 - -2812 -2816 0 - -2812 -2817 0 - -2812 -2818 0 - -2812 -2819 0 - -2813 -2814 0 - -2813 -2815 0 - -2813 -2816 0 - -2813 -2817 0 - -2813 -2818 0 - -2813 -2819 0 - -2814 -2815 0 - -2814 -2816 0 - -2814 -2817 0 - -2814 -2818 0 - -2814 -2819 0 - -2815 -2816 0 - -2815 -2817 0 - -2815 -2818 0 - -2815 -2819 0 - -2816 -2817 0 - -2816 -2818 0 - -2816 -2819 0 - -2817 -2818 0 - -2817 -2819 0 - -2818 -2819 0 - -2820 -2821 0 - -2820 -2822 0 - -2820 -2823 0 - -2820 -2824 0 - -2820 -2825 0 - -2820 -2826 0 - -2820 -2827 0 - -2821 -2822 0 - -2821 -2823 0 - -2821 -2824 0 - -2821 -2825 0 - -2821 -2826 0 - -2821 -2827 0 - -2822 -2823 0 - -2822 -2824 0 - -2822 -2825 0 - -2822 -2826 0 - -2822 -2827 0 - -2823 -2824 0 - -2823 -2825 0 - -2823 -2826 0 - -2823 -2827 0 - -2824 -2825 0 - -2824 -2826 0 - -2824 -2827 0 - -2825 -2826 0 - -2825 -2827 0 - -2826 -2827 0 - -2828 -2829 0 - -2828 -2830 0 - -2828 -2831 0 - -2828 -2832 0 - -2828 -2833 0 - -2828 -2834 0 - -2829 -2830 0 - -2829 -2831 0 - -2829 -2832 0 - -2829 -2833 0 - -2829 -2834 0 - -2830 -2831 0 - -2830 -2832 0 - -2830 -2833 0 - -2830 -2834 0 - -2831 -2832 0 - -2831 -2833 0 - -2831 -2834 0 - -2832 -2833 0 - -2832 -2834 0 - -2833 -2834 0 - -2835 -2836 0 - -2835 -2837 0 - -2835 -2838 0 - -2835 -2839 0 - -2835 -2840 0 - -2835 -2841 0 - -2835 -2842 0 - -2835 -2843 0 - -2835 -2844 0 - -2835 -2845 0 - -2836 -2837 0 - -2836 -2838 0 - -2836 -2839 0 - -2836 -2840 0 - -2836 -2841 0 - -2836 -2842 0 - -2836 -2843 0 - -2836 -2844 0 - -2836 -2845 0 - -2837 -2838 0 - -2837 -2839 0 - -2837 -2840 0 - -2837 -2841 0 - -2837 -2842 0 - -2837 -2843 0 - -2837 -2844 0 - -2837 -2845 0 - -2838 -2839 0 - -2838 -2840 0 - -2838 -2841 0 - -2838 -2842 0 - -2838 -2843 0 - -2838 -2844 0 - -2838 -2845 0 - -2839 -2840 0 - -2839 -2841 0 - -2839 -2842 0 - -2839 -2843 0 - -2839 -2844 0 - -2839 -2845 0 - -2840 -2841 0 - -2840 -2842 0 - -2840 -2843 0 - -2840 -2844 0 - -2840 -2845 0 - -2841 -2842 0 - -2841 -2843 0 - -2841 -2844 0 - -2841 -2845 0 - -2842 -2843 0 - -2842 -2844 0 - -2842 -2845 0 - -2843 -2844 0 - -2843 -2845 0 - -2844 -2845 0 - -2846 -2847 0 - -2846 -2848 0 - -2846 -2849 0 - -2846 -2850 0 - -2846 -2851 0 - -2846 -2852 0 - -2847 -2848 0 - -2847 -2849 0 - -2847 -2850 0 - -2847 -2851 0 - -2847 -2852 0 - -2848 -2849 0 - -2848 -2850 0 - -2848 -2851 0 - -2848 -2852 0 - -2849 -2850 0 - -2849 -2851 0 - -2849 -2852 0 - -2850 -2851 0 - -2850 -2852 0 - -2851 -2852 0 - -2853 -2854 0 - -2853 -2855 0 - -2853 -2856 0 - -2854 -2855 0 - -2854 -2856 0 - -2855 -2856 0 - -2857 -2858 0 - -2857 -2859 0 - -2857 -2860 0 - -2857 -2861 0 - -2857 -2862 0 - -2857 -2863 0 - -2858 -2859 0 - -2858 -2860 0 - -2858 -2861 0 - -2858 -2862 0 - -2858 -2863 0 - -2859 -2860 0 - -2859 -2861 0 - -2859 -2862 0 - -2859 -2863 0 - -2860 -2861 0 - -2860 -2862 0 - -2860 -2863 0 - -2861 -2862 0 - -2861 -2863 0 - -2862 -2863 0 - -2864 -2865 0 - -2864 -2866 0 - -2864 -2867 0 - -2864 -2868 0 - -2864 -2869 0 - -2865 -2866 0 - -2865 -2867 0 - -2865 -2868 0 - -2865 -2869 0 - -2866 -2867 0 - -2866 -2868 0 - -2866 -2869 0 - -2867 -2868 0 - -2867 -2869 0 - -2868 -2869 0 - -2870 -2871 0 - -2870 -2872 0 - -2870 -2873 0 - -2870 -2874 0 - -2870 -2875 0 - -2870 -2876 0 - -2870 -2877 0 - -2870 -2878 0 - -2870 -2879 0 - -2871 -2872 0 - -2871 -2873 0 - -2871 -2874 0 - -2871 -2875 0 - -2871 -2876 0 - -2871 -2877 0 - -2871 -2878 0 - -2871 -2879 0 - -2872 -2873 0 - -2872 -2874 0 - -2872 -2875 0 - -2872 -2876 0 - -2872 -2877 0 - -2872 -2878 0 - -2872 -2879 0 - -2873 -2874 0 - -2873 -2875 0 - -2873 -2876 0 - -2873 -2877 0 - -2873 -2878 0 - -2873 -2879 0 - -2874 -2875 0 - -2874 -2876 0 - -2874 -2877 0 - -2874 -2878 0 - -2874 -2879 0 - -2875 -2876 0 - -2875 -2877 0 - -2875 -2878 0 - -2875 -2879 0 - -2876 -2877 0 - -2876 -2878 0 - -2876 -2879 0 - -2877 -2878 0 - -2877 -2879 0 - -2878 -2879 0 - -2880 -2881 0 - -2880 -2882 0 - -2880 -2883 0 - -2880 -2884 0 - -2880 -2885 0 - -2880 -2886 0 - -2880 -2887 0 - -2881 -2882 0 - -2881 -2883 0 - -2881 -2884 0 - -2881 -2885 0 - -2881 -2886 0 - -2881 -2887 0 - -2882 -2883 0 - -2882 -2884 0 - -2882 -2885 0 - -2882 -2886 0 - -2882 -2887 0 - -2883 -2884 0 - -2883 -2885 0 - -2883 -2886 0 - -2883 -2887 0 - -2884 -2885 0 - -2884 -2886 0 - -2884 -2887 0 - -2885 -2886 0 - -2885 -2887 0 - -2886 -2887 0 - -2888 -2889 0 - -2888 -2890 0 - -2888 -2891 0 - -2888 -2892 0 - -2889 -2890 0 - -2889 -2891 0 - -2889 -2892 0 - -2890 -2891 0 - -2890 -2892 0 - -2891 -2892 0 - -2893 -2894 0 - -2893 -2895 0 - -2893 -2896 0 - -2893 -2897 0 - -2893 -2898 0 - -2893 -2899 0 - -2894 -2895 0 - -2894 -2896 0 - -2894 -2897 0 - -2894 -2898 0 - -2894 -2899 0 - -2895 -2896 0 - -2895 -2897 0 - -2895 -2898 0 - -2895 -2899 0 - -2896 -2897 0 - -2896 -2898 0 - -2896 -2899 0 - -2897 -2898 0 - -2897 -2899 0 - -2898 -2899 0 - -2900 -2901 0 - -2900 -2902 0 - -2900 -2903 0 - -2900 -2904 0 - -2900 -2905 0 - -2900 -2906 0 - -2900 -2907 0 - -2900 -2908 0 - -2900 -2909 0 - -2901 -2902 0 - -2901 -2903 0 - -2901 -2904 0 - -2901 -2905 0 - -2901 -2906 0 - -2901 -2907 0 - -2901 -2908 0 - -2901 -2909 0 - -2902 -2903 0 - -2902 -2904 0 - -2902 -2905 0 - -2902 -2906 0 - -2902 -2907 0 - -2902 -2908 0 - -2902 -2909 0 - -2903 -2904 0 - -2903 -2905 0 - -2903 -2906 0 - -2903 -2907 0 - -2903 -2908 0 - -2903 -2909 0 - -2904 -2905 0 - -2904 -2906 0 - -2904 -2907 0 - -2904 -2908 0 - -2904 -2909 0 - -2905 -2906 0 - -2905 -2907 0 - -2905 -2908 0 - -2905 -2909 0 - -2906 -2907 0 - -2906 -2908 0 - -2906 -2909 0 - -2907 -2908 0 - -2907 -2909 0 - -2908 -2909 0 - -2910 -2911 0 - -2910 -2912 0 - -2910 -2913 0 - -2910 -2914 0 - -2910 -2915 0 - -2910 -2916 0 - -2911 -2912 0 - -2911 -2913 0 - -2911 -2914 0 - -2911 -2915 0 - -2911 -2916 0 - -2912 -2913 0 - -2912 -2914 0 - -2912 -2915 0 - -2912 -2916 0 - -2913 -2914 0 - -2913 -2915 0 - -2913 -2916 0 - -2914 -2915 0 - -2914 -2916 0 - -2915 -2916 0 - -2917 -2918 0 - -2917 -2919 0 - -2917 -2920 0 - -2917 -2921 0 - -2917 -2922 0 - -2917 -2923 0 - -2917 -2924 0 - -2918 -2919 0 - -2918 -2920 0 - -2918 -2921 0 - -2918 -2922 0 - -2918 -2923 0 - -2918 -2924 0 - -2919 -2920 0 - -2919 -2921 0 - -2919 -2922 0 - -2919 -2923 0 - -2919 -2924 0 - -2920 -2921 0 - -2920 -2922 0 - -2920 -2923 0 - -2920 -2924 0 - -2921 -2922 0 - -2921 -2923 0 - -2921 -2924 0 - -2922 -2923 0 - -2922 -2924 0 - -2923 -2924 0 - -2925 -2926 0 - -2925 -2927 0 - -2925 -2928 0 - -2925 -2929 0 - -2925 -2930 0 - -2925 -2931 0 - -2925 -2932 0 - -2925 -2933 0 - -2926 -2927 0 - -2926 -2928 0 - -2926 -2929 0 - -2926 -2930 0 - -2926 -2931 0 - -2926 -2932 0 - -2926 -2933 0 - -2927 -2928 0 - -2927 -2929 0 - -2927 -2930 0 - -2927 -2931 0 - -2927 -2932 0 - -2927 -2933 0 - -2928 -2929 0 - -2928 -2930 0 - -2928 -2931 0 - -2928 -2932 0 - -2928 -2933 0 - -2929 -2930 0 - -2929 -2931 0 - -2929 -2932 0 - -2929 -2933 0 - -2930 -2931 0 - -2930 -2932 0 - -2930 -2933 0 - -2931 -2932 0 - -2931 -2933 0 - -2932 -2933 0 - -2934 -2935 0 - -2934 -2936 0 - -2935 -2936 0 - -2937 -2938 0 - -2937 -2939 0 - -2937 -2940 0 - -2937 -2941 0 - -2937 -2942 0 - -2938 -2939 0 - -2938 -2940 0 - -2938 -2941 0 - -2938 -2942 0 - -2939 -2940 0 - -2939 -2941 0 - -2939 -2942 0 - -2940 -2941 0 - -2940 -2942 0 - -2941 -2942 0 - -2943 -2944 0 - -2943 -2945 0 - -2943 -2946 0 - -2943 -2947 0 - -2944 -2945 0 - -2944 -2946 0 - -2944 -2947 0 - -2945 -2946 0 - -2945 -2947 0 - -2946 -2947 0 - -2948 -2949 0 - -2948 -2950 0 - -2948 -2951 0 - -2948 -2952 0 - -2948 -2953 0 - -2948 -2954 0 - -2949 -2950 0 - -2949 -2951 0 - -2949 -2952 0 - -2949 -2953 0 - -2949 -2954 0 - -2950 -2951 0 - -2950 -2952 0 - -2950 -2953 0 - -2950 -2954 0 - -2951 -2952 0 - -2951 -2953 0 - -2951 -2954 0 - -2952 -2953 0 - -2952 -2954 0 - -2953 -2954 0 - -2955 -2956 0 - -2955 -2957 0 - -2955 -2958 0 - -2955 -2959 0 - -2955 -2960 0 - -2955 -2961 0 - -2956 -2957 0 - -2956 -2958 0 - -2956 -2959 0 - -2956 -2960 0 - -2956 -2961 0 - -2957 -2958 0 - -2957 -2959 0 - -2957 -2960 0 - -2957 -2961 0 - -2958 -2959 0 - -2958 -2960 0 - -2958 -2961 0 - -2959 -2960 0 - -2959 -2961 0 - -2960 -2961 0 - -2962 -2963 0 - -2962 -2964 0 - -2962 -2965 0 - -2962 -2966 0 - -2963 -2964 0 - -2963 -2965 0 - -2963 -2966 0 - -2964 -2965 0 - -2964 -2966 0 - -2965 -2966 0 - -2967 -2968 0 - -2967 -2969 0 - -2967 -2970 0 - -2967 -2971 0 - -2968 -2969 0 - -2968 -2970 0 - -2968 -2971 0 - -2969 -2970 0 - -2969 -2971 0 - -2970 -2971 0 - -2972 -2973 0 - -2972 -2974 0 - -2972 -2975 0 - -2972 -2976 0 - -2972 -2977 0 - -2972 -2978 0 - -2972 -2979 0 - -2973 -2974 0 - -2973 -2975 0 - -2973 -2976 0 - -2973 -2977 0 - -2973 -2978 0 - -2973 -2979 0 - -2974 -2975 0 - -2974 -2976 0 - -2974 -2977 0 - -2974 -2978 0 - -2974 -2979 0 - -2975 -2976 0 - -2975 -2977 0 - -2975 -2978 0 - -2975 -2979 0 - -2976 -2977 0 - -2976 -2978 0 - -2976 -2979 0 - -2977 -2978 0 - -2977 -2979 0 - -2978 -2979 0 - -2980 -2981 0 - -2980 -2982 0 - -2980 -2983 0 - -2980 -2984 0 - -2980 -2985 0 - -2981 -2982 0 - -2981 -2983 0 - -2981 -2984 0 - -2981 -2985 0 - -2982 -2983 0 - -2982 -2984 0 - -2982 -2985 0 - -2983 -2984 0 - -2983 -2985 0 - -2984 -2985 0 - -2986 -2987 0 - -2986 -2988 0 - -2986 -2989 0 - -2986 -2990 0 - -2986 -2991 0 - -2987 -2988 0 - -2987 -2989 0 - -2987 -2990 0 - -2987 -2991 0 - -2988 -2989 0 - -2988 -2990 0 - -2988 -2991 0 - -2989 -2990 0 - -2989 -2991 0 - -2990 -2991 0 - -2992 -2993 0 - -2992 -2994 0 - -2992 -2995 0 - -2992 -2996 0 - -2992 -2997 0 - -2993 -2994 0 - -2993 -2995 0 - -2993 -2996 0 - -2993 -2997 0 - -2994 -2995 0 - -2994 -2996 0 - -2994 -2997 0 - -2995 -2996 0 - -2995 -2997 0 - -2996 -2997 0 - -2998 -2999 0 - -2998 -3000 0 - -2998 -3001 0 - -2998 -3002 0 - -2998 -3003 0 - -2998 -3004 0 - -2998 -3005 0 - -2998 -3006 0 - -2998 -3007 0 - -2999 -3000 0 - -2999 -3001 0 - -2999 -3002 0 - -2999 -3003 0 - -2999 -3004 0 - -2999 -3005 0 - -2999 -3006 0 - -2999 -3007 0 - -3000 -3001 0 - -3000 -3002 0 - -3000 -3003 0 - -3000 -3004 0 - -3000 -3005 0 - -3000 -3006 0 - -3000 -3007 0 - -3001 -3002 0 - -3001 -3003 0 - -3001 -3004 0 - -3001 -3005 0 - -3001 -3006 0 - -3001 -3007 0 - -3002 -3003 0 - -3002 -3004 0 - -3002 -3005 0 - -3002 -3006 0 - -3002 -3007 0 - -3003 -3004 0 - -3003 -3005 0 - -3003 -3006 0 - -3003 -3007 0 - -3004 -3005 0 - -3004 -3006 0 - -3004 -3007 0 - -3005 -3006 0 - -3005 -3007 0 - -3006 -3007 0 - -3008 -3009 0 - -3008 -3010 0 - -3008 -3011 0 - -3008 -3012 0 - -3008 -3013 0 - -3008 -3014 0 - -3008 -3015 0 - -3008 -3016 0 - -3009 -3010 0 - -3009 -3011 0 - -3009 -3012 0 - -3009 -3013 0 - -3009 -3014 0 - -3009 -3015 0 - -3009 -3016 0 - -3010 -3011 0 - -3010 -3012 0 - -3010 -3013 0 - -3010 -3014 0 - -3010 -3015 0 - -3010 -3016 0 - -3011 -3012 0 - -3011 -3013 0 - -3011 -3014 0 - -3011 -3015 0 - -3011 -3016 0 - -3012 -3013 0 - -3012 -3014 0 - -3012 -3015 0 - -3012 -3016 0 - -3013 -3014 0 - -3013 -3015 0 - -3013 -3016 0 - -3014 -3015 0 - -3014 -3016 0 - -3015 -3016 0 - -3017 -3018 0 - -3017 -3019 0 - -3017 -3020 0 - -3017 -3021 0 - -3017 -3022 0 - -3017 -3023 0 - -3017 -3024 0 - -3017 -3025 0 - -3017 -3026 0 - -3017 -3027 0 - -3017 -3028 0 - -3017 -3029 0 - -3017 -3030 0 - -3017 -3031 0 - -3017 -3032 0 - -3018 -3019 0 - -3018 -3020 0 - -3018 -3021 0 - -3018 -3022 0 - -3018 -3023 0 - -3018 -3024 0 - -3018 -3025 0 - -3018 -3026 0 - -3018 -3027 0 - -3018 -3028 0 - -3018 -3029 0 - -3018 -3030 0 - -3018 -3031 0 - -3018 -3032 0 - -3019 -3020 0 - -3019 -3021 0 - -3019 -3022 0 - -3019 -3023 0 - -3019 -3024 0 - -3019 -3025 0 - -3019 -3026 0 - -3019 -3027 0 - -3019 -3028 0 - -3019 -3029 0 - -3019 -3030 0 - -3019 -3031 0 - -3019 -3032 0 - -3020 -3021 0 - -3020 -3022 0 - -3020 -3023 0 - -3020 -3024 0 - -3020 -3025 0 - -3020 -3026 0 - -3020 -3027 0 - -3020 -3028 0 - -3020 -3029 0 - -3020 -3030 0 - -3020 -3031 0 - -3020 -3032 0 - -3021 -3022 0 - -3021 -3023 0 - -3021 -3024 0 - -3021 -3025 0 - -3021 -3026 0 - -3021 -3027 0 - -3021 -3028 0 - -3021 -3029 0 - -3021 -3030 0 - -3021 -3031 0 - -3021 -3032 0 - -3022 -3023 0 - -3022 -3024 0 - -3022 -3025 0 - -3022 -3026 0 - -3022 -3027 0 - -3022 -3028 0 - -3022 -3029 0 - -3022 -3030 0 - -3022 -3031 0 - -3022 -3032 0 - -3023 -3024 0 - -3023 -3025 0 - -3023 -3026 0 - -3023 -3027 0 - -3023 -3028 0 - -3023 -3029 0 - -3023 -3030 0 - -3023 -3031 0 - -3023 -3032 0 - -3024 -3025 0 - -3024 -3026 0 - -3024 -3027 0 - -3024 -3028 0 - -3024 -3029 0 - -3024 -3030 0 - -3024 -3031 0 - -3024 -3032 0 - -3025 -3026 0 - -3025 -3027 0 - -3025 -3028 0 - -3025 -3029 0 - -3025 -3030 0 - -3025 -3031 0 - -3025 -3032 0 - -3026 -3027 0 - -3026 -3028 0 - -3026 -3029 0 - -3026 -3030 0 - -3026 -3031 0 - -3026 -3032 0 - -3027 -3028 0 - -3027 -3029 0 - -3027 -3030 0 - -3027 -3031 0 - -3027 -3032 0 - -3028 -3029 0 - -3028 -3030 0 - -3028 -3031 0 - -3028 -3032 0 - -3029 -3030 0 - -3029 -3031 0 - -3029 -3032 0 - -3030 -3031 0 - -3030 -3032 0 - -3031 -3032 0 - -3033 -3034 0 - -3033 -3035 0 - -3033 -3036 0 - -3033 -3037 0 - -3033 -3038 0 - -3033 -3039 0 - -3033 -3040 0 - -3034 -3035 0 - -3034 -3036 0 - -3034 -3037 0 - -3034 -3038 0 - -3034 -3039 0 - -3034 -3040 0 - -3035 -3036 0 - -3035 -3037 0 - -3035 -3038 0 - -3035 -3039 0 - -3035 -3040 0 - -3036 -3037 0 - -3036 -3038 0 - -3036 -3039 0 - -3036 -3040 0 - -3037 -3038 0 - -3037 -3039 0 - -3037 -3040 0 - -3038 -3039 0 - -3038 -3040 0 - -3039 -3040 0 - -3041 -3042 0 - -3041 -3043 0 - -3041 -3044 0 - -3041 -3045 0 - -3041 -3046 0 - -3041 -3047 0 - -3041 -3048 0 - -3041 -3049 0 - -3041 -3050 0 - -3041 -3051 0 - -3041 -3052 0 - -3041 -3053 0 - -3042 -3043 0 - -3042 -3044 0 - -3042 -3045 0 - -3042 -3046 0 - -3042 -3047 0 - -3042 -3048 0 - -3042 -3049 0 - -3042 -3050 0 - -3042 -3051 0 - -3042 -3052 0 - -3042 -3053 0 - -3043 -3044 0 - -3043 -3045 0 - -3043 -3046 0 - -3043 -3047 0 - -3043 -3048 0 - -3043 -3049 0 - -3043 -3050 0 - -3043 -3051 0 - -3043 -3052 0 - -3043 -3053 0 - -3044 -3045 0 - -3044 -3046 0 - -3044 -3047 0 - -3044 -3048 0 - -3044 -3049 0 - -3044 -3050 0 - -3044 -3051 0 - -3044 -3052 0 - -3044 -3053 0 - -3045 -3046 0 - -3045 -3047 0 - -3045 -3048 0 - -3045 -3049 0 - -3045 -3050 0 - -3045 -3051 0 - -3045 -3052 0 - -3045 -3053 0 - -3046 -3047 0 - -3046 -3048 0 - -3046 -3049 0 - -3046 -3050 0 - -3046 -3051 0 - -3046 -3052 0 - -3046 -3053 0 - -3047 -3048 0 - -3047 -3049 0 - -3047 -3050 0 - -3047 -3051 0 - -3047 -3052 0 - -3047 -3053 0 - -3048 -3049 0 - -3048 -3050 0 - -3048 -3051 0 - -3048 -3052 0 - -3048 -3053 0 - -3049 -3050 0 - -3049 -3051 0 - -3049 -3052 0 - -3049 -3053 0 - -3050 -3051 0 - -3050 -3052 0 - -3050 -3053 0 - -3051 -3052 0 - -3051 -3053 0 - -3052 -3053 0 - -3054 -3055 0 - -3054 -3056 0 - -3054 -3057 0 - -3054 -3058 0 - -3054 -3059 0 - -3054 -3060 0 - -3054 -3061 0 - -3054 -3062 0 - -3054 -3063 0 - -3055 -3056 0 - -3055 -3057 0 - -3055 -3058 0 - -3055 -3059 0 - -3055 -3060 0 - -3055 -3061 0 - -3055 -3062 0 - -3055 -3063 0 - -3056 -3057 0 - -3056 -3058 0 - -3056 -3059 0 - -3056 -3060 0 - -3056 -3061 0 - -3056 -3062 0 - -3056 -3063 0 - -3057 -3058 0 - -3057 -3059 0 - -3057 -3060 0 - -3057 -3061 0 - -3057 -3062 0 - -3057 -3063 0 - -3058 -3059 0 - -3058 -3060 0 - -3058 -3061 0 - -3058 -3062 0 - -3058 -3063 0 - -3059 -3060 0 - -3059 -3061 0 - -3059 -3062 0 - -3059 -3063 0 - -3060 -3061 0 - -3060 -3062 0 - -3060 -3063 0 - -3061 -3062 0 - -3061 -3063 0 - -3062 -3063 0 - -3064 -3065 0 - -3064 -3066 0 - -3064 -3067 0 - -3064 -3068 0 - -3064 -3069 0 - -3065 -3066 0 - -3065 -3067 0 - -3065 -3068 0 - -3065 -3069 0 - -3066 -3067 0 - -3066 -3068 0 - -3066 -3069 0 - -3067 -3068 0 - -3067 -3069 0 - -3068 -3069 0 - -3070 -3071 0 - -3070 -3072 0 - -3070 -3073 0 - -3070 -3074 0 - -3070 -3075 0 - -3070 -3076 0 - -3070 -3077 0 - -3070 -3078 0 - -3070 -3079 0 - -3070 -3080 0 - -3071 -3072 0 - -3071 -3073 0 - -3071 -3074 0 - -3071 -3075 0 - -3071 -3076 0 - -3071 -3077 0 - -3071 -3078 0 - -3071 -3079 0 - -3071 -3080 0 - -3072 -3073 0 - -3072 -3074 0 - -3072 -3075 0 - -3072 -3076 0 - -3072 -3077 0 - -3072 -3078 0 - -3072 -3079 0 - -3072 -3080 0 - -3073 -3074 0 - -3073 -3075 0 - -3073 -3076 0 - -3073 -3077 0 - -3073 -3078 0 - -3073 -3079 0 - -3073 -3080 0 - -3074 -3075 0 - -3074 -3076 0 - -3074 -3077 0 - -3074 -3078 0 - -3074 -3079 0 - -3074 -3080 0 - -3075 -3076 0 - -3075 -3077 0 - -3075 -3078 0 - -3075 -3079 0 - -3075 -3080 0 - -3076 -3077 0 - -3076 -3078 0 - -3076 -3079 0 - -3076 -3080 0 - -3077 -3078 0 - -3077 -3079 0 - -3077 -3080 0 - -3078 -3079 0 - -3078 -3080 0 - -3079 -3080 0 - -3081 -3082 0 - -3081 -3083 0 - -3081 -3084 0 - -3081 -3085 0 - -3081 -3086 0 - -3081 -3087 0 - -3081 -3088 0 - -3081 -3089 0 - -3081 -3090 0 - -3081 -3091 0 - -3081 -3092 0 - -3082 -3083 0 - -3082 -3084 0 - -3082 -3085 0 - -3082 -3086 0 - -3082 -3087 0 - -3082 -3088 0 - -3082 -3089 0 - -3082 -3090 0 - -3082 -3091 0 - -3082 -3092 0 - -3083 -3084 0 - -3083 -3085 0 - -3083 -3086 0 - -3083 -3087 0 - -3083 -3088 0 - -3083 -3089 0 - -3083 -3090 0 - -3083 -3091 0 - -3083 -3092 0 - -3084 -3085 0 - -3084 -3086 0 - -3084 -3087 0 - -3084 -3088 0 - -3084 -3089 0 - -3084 -3090 0 - -3084 -3091 0 - -3084 -3092 0 - -3085 -3086 0 - -3085 -3087 0 - -3085 -3088 0 - -3085 -3089 0 - -3085 -3090 0 - -3085 -3091 0 - -3085 -3092 0 - -3086 -3087 0 - -3086 -3088 0 - -3086 -3089 0 - -3086 -3090 0 - -3086 -3091 0 - -3086 -3092 0 - -3087 -3088 0 - -3087 -3089 0 - -3087 -3090 0 - -3087 -3091 0 - -3087 -3092 0 - -3088 -3089 0 - -3088 -3090 0 - -3088 -3091 0 - -3088 -3092 0 - -3089 -3090 0 - -3089 -3091 0 - -3089 -3092 0 - -3090 -3091 0 - -3090 -3092 0 - -3091 -3092 0 - -3093 -3094 0 - -3093 -3095 0 - -3093 -3096 0 - -3093 -3097 0 - -3093 -3098 0 - -3093 -3099 0 - -3093 -3100 0 - -3094 -3095 0 - -3094 -3096 0 - -3094 -3097 0 - -3094 -3098 0 - -3094 -3099 0 - -3094 -3100 0 - -3095 -3096 0 - -3095 -3097 0 - -3095 -3098 0 - -3095 -3099 0 - -3095 -3100 0 - -3096 -3097 0 - -3096 -3098 0 - -3096 -3099 0 - -3096 -3100 0 - -3097 -3098 0 - -3097 -3099 0 - -3097 -3100 0 - -3098 -3099 0 - -3098 -3100 0 - -3099 -3100 0 - -3101 -3102 0 - -3101 -3103 0 - -3101 -3104 0 - -3101 -3105 0 - -3101 -3106 0 - -3101 -3107 0 - -3101 -3108 0 - -3101 -3109 0 - -3101 -3110 0 - -3101 -3111 0 - -3102 -3103 0 - -3102 -3104 0 - -3102 -3105 0 - -3102 -3106 0 - -3102 -3107 0 - -3102 -3108 0 - -3102 -3109 0 - -3102 -3110 0 - -3102 -3111 0 - -3103 -3104 0 - -3103 -3105 0 - -3103 -3106 0 - -3103 -3107 0 - -3103 -3108 0 - -3103 -3109 0 - -3103 -3110 0 - -3103 -3111 0 - -3104 -3105 0 - -3104 -3106 0 - -3104 -3107 0 - -3104 -3108 0 - -3104 -3109 0 - -3104 -3110 0 - -3104 -3111 0 - -3105 -3106 0 - -3105 -3107 0 - -3105 -3108 0 - -3105 -3109 0 - -3105 -3110 0 - -3105 -3111 0 - -3106 -3107 0 - -3106 -3108 0 - -3106 -3109 0 - -3106 -3110 0 - -3106 -3111 0 - -3107 -3108 0 - -3107 -3109 0 - -3107 -3110 0 - -3107 -3111 0 - -3108 -3109 0 - -3108 -3110 0 - -3108 -3111 0 - -3109 -3110 0 - -3109 -3111 0 - -3110 -3111 0 - -3112 -3113 0 - -3112 -3114 0 - -3112 -3115 0 - -3112 -3116 0 - -3112 -3117 0 - -3112 -3118 0 - -3112 -3119 0 - -3113 -3114 0 - -3113 -3115 0 - -3113 -3116 0 - -3113 -3117 0 - -3113 -3118 0 - -3113 -3119 0 - -3114 -3115 0 - -3114 -3116 0 - -3114 -3117 0 - -3114 -3118 0 - -3114 -3119 0 - -3115 -3116 0 - -3115 -3117 0 - -3115 -3118 0 - -3115 -3119 0 - -3116 -3117 0 - -3116 -3118 0 - -3116 -3119 0 - -3117 -3118 0 - -3117 -3119 0 - -3118 -3119 0 - -3120 -3121 0 - -3120 -3122 0 - -3120 -3123 0 - -3120 -3124 0 - -3120 -3125 0 - -3120 -3126 0 - -3120 -3127 0 - -3120 -3128 0 - -3120 -3129 0 - -3120 -3130 0 - -3120 -3131 0 - -3120 -3132 0 - -3121 -3122 0 - -3121 -3123 0 - -3121 -3124 0 - -3121 -3125 0 - -3121 -3126 0 - -3121 -3127 0 - -3121 -3128 0 - -3121 -3129 0 - -3121 -3130 0 - -3121 -3131 0 - -3121 -3132 0 - -3122 -3123 0 - -3122 -3124 0 - -3122 -3125 0 - -3122 -3126 0 - -3122 -3127 0 - -3122 -3128 0 - -3122 -3129 0 - -3122 -3130 0 - -3122 -3131 0 - -3122 -3132 0 - -3123 -3124 0 - -3123 -3125 0 - -3123 -3126 0 - -3123 -3127 0 - -3123 -3128 0 - -3123 -3129 0 - -3123 -3130 0 - -3123 -3131 0 - -3123 -3132 0 - -3124 -3125 0 - -3124 -3126 0 - -3124 -3127 0 - -3124 -3128 0 - -3124 -3129 0 - -3124 -3130 0 - -3124 -3131 0 - -3124 -3132 0 - -3125 -3126 0 - -3125 -3127 0 - -3125 -3128 0 - -3125 -3129 0 - -3125 -3130 0 - -3125 -3131 0 - -3125 -3132 0 - -3126 -3127 0 - -3126 -3128 0 - -3126 -3129 0 - -3126 -3130 0 - -3126 -3131 0 - -3126 -3132 0 - -3127 -3128 0 - -3127 -3129 0 - -3127 -3130 0 - -3127 -3131 0 - -3127 -3132 0 - -3128 -3129 0 - -3128 -3130 0 - -3128 -3131 0 - -3128 -3132 0 - -3129 -3130 0 - -3129 -3131 0 - -3129 -3132 0 - -3130 -3131 0 - -3130 -3132 0 - -3131 -3132 0 - -3133 -3134 0 - -3133 -3135 0 - -3133 -3136 0 - -3133 -3137 0 - -3133 -3138 0 - -3133 -3139 0 - -3133 -3140 0 - -3133 -3141 0 - -3133 -3142 0 - -3133 -3143 0 - -3134 -3135 0 - -3134 -3136 0 - -3134 -3137 0 - -3134 -3138 0 - -3134 -3139 0 - -3134 -3140 0 - -3134 -3141 0 - -3134 -3142 0 - -3134 -3143 0 - -3135 -3136 0 - -3135 -3137 0 - -3135 -3138 0 - -3135 -3139 0 - -3135 -3140 0 - -3135 -3141 0 - -3135 -3142 0 - -3135 -3143 0 - -3136 -3137 0 - -3136 -3138 0 - -3136 -3139 0 - -3136 -3140 0 - -3136 -3141 0 - -3136 -3142 0 - -3136 -3143 0 - -3137 -3138 0 - -3137 -3139 0 - -3137 -3140 0 - -3137 -3141 0 - -3137 -3142 0 - -3137 -3143 0 - -3138 -3139 0 - -3138 -3140 0 - -3138 -3141 0 - -3138 -3142 0 - -3138 -3143 0 - -3139 -3140 0 - -3139 -3141 0 - -3139 -3142 0 - -3139 -3143 0 - -3140 -3141 0 - -3140 -3142 0 - -3140 -3143 0 - -3141 -3142 0 - -3141 -3143 0 - -3142 -3143 0 - -3144 -3145 0 - -3144 -3146 0 - -3144 -3147 0 - -3144 -3148 0 - -3144 -3149 0 - -3144 -3150 0 - -3144 -3151 0 - -3145 -3146 0 - -3145 -3147 0 - -3145 -3148 0 - -3145 -3149 0 - -3145 -3150 0 - -3145 -3151 0 - -3146 -3147 0 - -3146 -3148 0 - -3146 -3149 0 - -3146 -3150 0 - -3146 -3151 0 - -3147 -3148 0 - -3147 -3149 0 - -3147 -3150 0 - -3147 -3151 0 - -3148 -3149 0 - -3148 -3150 0 - -3148 -3151 0 - -3149 -3150 0 - -3149 -3151 0 - -3150 -3151 0 - -3152 -3153 0 - -3152 -3154 0 - -3152 -3155 0 - -3152 -3156 0 - -3152 -3157 0 - -3152 -3158 0 - -3153 -3154 0 - -3153 -3155 0 - -3153 -3156 0 - -3153 -3157 0 - -3153 -3158 0 - -3154 -3155 0 - -3154 -3156 0 - -3154 -3157 0 - -3154 -3158 0 - -3155 -3156 0 - -3155 -3157 0 - -3155 -3158 0 - -3156 -3157 0 - -3156 -3158 0 - -3157 -3158 0 - -3159 -3160 0 - -3159 -3161 0 - -3159 -3162 0 - -3159 -3163 0 - -3159 -3164 0 - -3159 -3165 0 - -3159 -3166 0 - -3159 -3167 0 - -3159 -3168 0 - -3160 -3161 0 - -3160 -3162 0 - -3160 -3163 0 - -3160 -3164 0 - -3160 -3165 0 - -3160 -3166 0 - -3160 -3167 0 - -3160 -3168 0 - -3161 -3162 0 - -3161 -3163 0 - -3161 -3164 0 - -3161 -3165 0 - -3161 -3166 0 - -3161 -3167 0 - -3161 -3168 0 - -3162 -3163 0 - -3162 -3164 0 - -3162 -3165 0 - -3162 -3166 0 - -3162 -3167 0 - -3162 -3168 0 - -3163 -3164 0 - -3163 -3165 0 - -3163 -3166 0 - -3163 -3167 0 - -3163 -3168 0 - -3164 -3165 0 - -3164 -3166 0 - -3164 -3167 0 - -3164 -3168 0 - -3165 -3166 0 - -3165 -3167 0 - -3165 -3168 0 - -3166 -3167 0 - -3166 -3168 0 - -3167 -3168 0 - -3169 -3170 0 - -3169 -3171 0 - -3169 -3172 0 - -3169 -3173 0 - -3169 -3174 0 - -3169 -3175 0 - -3170 -3171 0 - -3170 -3172 0 - -3170 -3173 0 - -3170 -3174 0 - -3170 -3175 0 - -3171 -3172 0 - -3171 -3173 0 - -3171 -3174 0 - -3171 -3175 0 - -3172 -3173 0 - -3172 -3174 0 - -3172 -3175 0 - -3173 -3174 0 - -3173 -3175 0 - -3174 -3175 0 - -3176 -3177 0 - -3176 -3178 0 - -3176 -3179 0 - -3176 -3180 0 - -3176 -3181 0 - -3176 -3182 0 - -3176 -3183 0 - -3176 -3184 0 - -3177 -3178 0 - -3177 -3179 0 - -3177 -3180 0 - -3177 -3181 0 - -3177 -3182 0 - -3177 -3183 0 - -3177 -3184 0 - -3178 -3179 0 - -3178 -3180 0 - -3178 -3181 0 - -3178 -3182 0 - -3178 -3183 0 - -3178 -3184 0 - -3179 -3180 0 - -3179 -3181 0 - -3179 -3182 0 - -3179 -3183 0 - -3179 -3184 0 - -3180 -3181 0 - -3180 -3182 0 - -3180 -3183 0 - -3180 -3184 0 - -3181 -3182 0 - -3181 -3183 0 - -3181 -3184 0 - -3182 -3183 0 - -3182 -3184 0 - -3183 -3184 0 - -3185 -3186 0 - -3185 -3187 0 - -3185 -3188 0 - -3185 -3189 0 - -3185 -3190 0 - -3185 -3191 0 - -3185 -3192 0 - -3185 -3193 0 - -3185 -3194 0 - -3185 -3195 0 - -3185 -3196 0 - -3186 -3187 0 - -3186 -3188 0 - -3186 -3189 0 - -3186 -3190 0 - -3186 -3191 0 - -3186 -3192 0 - -3186 -3193 0 - -3186 -3194 0 - -3186 -3195 0 - -3186 -3196 0 - -3187 -3188 0 - -3187 -3189 0 - -3187 -3190 0 - -3187 -3191 0 - -3187 -3192 0 - -3187 -3193 0 - -3187 -3194 0 - -3187 -3195 0 - -3187 -3196 0 - -3188 -3189 0 - -3188 -3190 0 - -3188 -3191 0 - -3188 -3192 0 - -3188 -3193 0 - -3188 -3194 0 - -3188 -3195 0 - -3188 -3196 0 - -3189 -3190 0 - -3189 -3191 0 - -3189 -3192 0 - -3189 -3193 0 - -3189 -3194 0 - -3189 -3195 0 - -3189 -3196 0 - -3190 -3191 0 - -3190 -3192 0 - -3190 -3193 0 - -3190 -3194 0 - -3190 -3195 0 - -3190 -3196 0 - -3191 -3192 0 - -3191 -3193 0 - -3191 -3194 0 - -3191 -3195 0 - -3191 -3196 0 - -3192 -3193 0 - -3192 -3194 0 - -3192 -3195 0 - -3192 -3196 0 - -3193 -3194 0 - -3193 -3195 0 - -3193 -3196 0 - -3194 -3195 0 - -3194 -3196 0 - -3195 -3196 0 - -3197 -3198 0 - -3197 -3199 0 - -3197 -3200 0 - -3197 -3201 0 - -3197 -3202 0 - -3197 -3203 0 - -3197 -3204 0 - -3197 -3205 0 - -3198 -3199 0 - -3198 -3200 0 - -3198 -3201 0 - -3198 -3202 0 - -3198 -3203 0 - -3198 -3204 0 - -3198 -3205 0 - -3199 -3200 0 - -3199 -3201 0 - -3199 -3202 0 - -3199 -3203 0 - -3199 -3204 0 - -3199 -3205 0 - -3200 -3201 0 - -3200 -3202 0 - -3200 -3203 0 - -3200 -3204 0 - -3200 -3205 0 - -3201 -3202 0 - -3201 -3203 0 - -3201 -3204 0 - -3201 -3205 0 - -3202 -3203 0 - -3202 -3204 0 - -3202 -3205 0 - -3203 -3204 0 - -3203 -3205 0 - -3204 -3205 0 - -3206 -3207 0 - -3206 -3208 0 - -3206 -3209 0 - -3206 -3210 0 - -3206 -3211 0 - -3207 -3208 0 - -3207 -3209 0 - -3207 -3210 0 - -3207 -3211 0 - -3208 -3209 0 - -3208 -3210 0 - -3208 -3211 0 - -3209 -3210 0 - -3209 -3211 0 - -3210 -3211 0 - -3212 -3213 0 - -3212 -3214 0 - -3212 -3215 0 - -3212 -3216 0 - -3212 -3217 0 - -3212 -3218 0 - -3212 -3219 0 - -3212 -3220 0 - -3213 -3214 0 - -3213 -3215 0 - -3213 -3216 0 - -3213 -3217 0 - -3213 -3218 0 - -3213 -3219 0 - -3213 -3220 0 - -3214 -3215 0 - -3214 -3216 0 - -3214 -3217 0 - -3214 -3218 0 - -3214 -3219 0 - -3214 -3220 0 - -3215 -3216 0 - -3215 -3217 0 - -3215 -3218 0 - -3215 -3219 0 - -3215 -3220 0 - -3216 -3217 0 - -3216 -3218 0 - -3216 -3219 0 - -3216 -3220 0 - -3217 -3218 0 - -3217 -3219 0 - -3217 -3220 0 - -3218 -3219 0 - -3218 -3220 0 - -3219 -3220 0 - -3221 -3222 0 - -3221 -3223 0 - -3221 -3224 0 - -3221 -3225 0 - -3221 -3226 0 - -3221 -3227 0 - -3222 -3223 0 - -3222 -3224 0 - -3222 -3225 0 - -3222 -3226 0 - -3222 -3227 0 - -3223 -3224 0 - -3223 -3225 0 - -3223 -3226 0 - -3223 -3227 0 - -3224 -3225 0 - -3224 -3226 0 - -3224 -3227 0 - -3225 -3226 0 - -3225 -3227 0 - -3226 -3227 0 - -3228 -3229 0 - -3228 -3230 0 - -3228 -3231 0 - -3229 -3230 0 - -3229 -3231 0 - -3230 -3231 0 - -3232 -3233 0 - -3232 -3234 0 - -3232 -3235 0 - -3232 -3236 0 - -3233 -3234 0 - -3233 -3235 0 - -3233 -3236 0 - -3234 -3235 0 - -3234 -3236 0 - -3235 -3236 0 - -3237 -3238 0 - -3237 -3239 0 - -3237 -3240 0 - -3237 -3241 0 - -3237 -3242 0 - -3238 -3239 0 - -3238 -3240 0 - -3238 -3241 0 - -3238 -3242 0 - -3239 -3240 0 - -3239 -3241 0 - -3239 -3242 0 - -3240 -3241 0 - -3240 -3242 0 - -3241 -3242 0 - -3243 -3244 0 - -3243 -3245 0 - -3243 -3246 0 - -3243 -3247 0 - -3243 -3248 0 - -3243 -3249 0 - -3244 -3245 0 - -3244 -3246 0 - -3244 -3247 0 - -3244 -3248 0 - -3244 -3249 0 - -3245 -3246 0 - -3245 -3247 0 - -3245 -3248 0 - -3245 -3249 0 - -3246 -3247 0 - -3246 -3248 0 - -3246 -3249 0 - -3247 -3248 0 - -3247 -3249 0 - -3248 -3249 0 - -3250 -3251 0 - -3250 -3252 0 - -3250 -3253 0 - -3250 -3254 0 - -3250 -3255 0 - -3251 -3252 0 - -3251 -3253 0 - -3251 -3254 0 - -3251 -3255 0 - -3252 -3253 0 - -3252 -3254 0 - -3252 -3255 0 - -3253 -3254 0 - -3253 -3255 0 - -3254 -3255 0 - -3256 -3257 0 - -3256 -3258 0 - -3256 -3259 0 - -3256 -3260 0 - -3257 -3258 0 - -3257 -3259 0 - -3257 -3260 0 - -3258 -3259 0 - -3258 -3260 0 - -3259 -3260 0 - -3261 -3262 0 - -3261 -3263 0 - -3261 -3264 0 - -3261 -3265 0 - -3261 -3266 0 - -3261 -3267 0 - -3261 -3268 0 - -3262 -3263 0 - -3262 -3264 0 - -3262 -3265 0 - -3262 -3266 0 - -3262 -3267 0 - -3262 -3268 0 - -3263 -3264 0 - -3263 -3265 0 - -3263 -3266 0 - -3263 -3267 0 - -3263 -3268 0 - -3264 -3265 0 - -3264 -3266 0 - -3264 -3267 0 - -3264 -3268 0 - -3265 -3266 0 - -3265 -3267 0 - -3265 -3268 0 - -3266 -3267 0 - -3266 -3268 0 - -3267 -3268 0 - -3269 -3270 0 - -3269 -3271 0 - -3269 -3272 0 - -3269 -3273 0 - -3270 -3271 0 - -3270 -3272 0 - -3270 -3273 0 - -3271 -3272 0 - -3271 -3273 0 - -3272 -3273 0 - -3274 -3275 0 - -3274 -3276 0 - -3274 -3277 0 - -3274 -3278 0 - -3274 -3279 0 - -3275 -3276 0 - -3275 -3277 0 - -3275 -3278 0 - -3275 -3279 0 - -3276 -3277 0 - -3276 -3278 0 - -3276 -3279 0 - -3277 -3278 0 - -3277 -3279 0 - -3278 -3279 0 - -3280 -3281 0 - -3280 -3282 0 - -3280 -3283 0 - -3280 -3284 0 - -3280 -3285 0 - -3280 -3286 0 - -3280 -3287 0 - -3281 -3282 0 - -3281 -3283 0 - -3281 -3284 0 - -3281 -3285 0 - -3281 -3286 0 - -3281 -3287 0 - -3282 -3283 0 - -3282 -3284 0 - -3282 -3285 0 - -3282 -3286 0 - -3282 -3287 0 - -3283 -3284 0 - -3283 -3285 0 - -3283 -3286 0 - -3283 -3287 0 - -3284 -3285 0 - -3284 -3286 0 - -3284 -3287 0 - -3285 -3286 0 - -3285 -3287 0 - -3286 -3287 0 - -3288 -3289 0 - -3288 -3290 0 - -3288 -3291 0 - -3288 -3292 0 - -3288 -3293 0 - -3288 -3294 0 - -3288 -3295 0 - -3288 -3296 0 - -3289 -3290 0 - -3289 -3291 0 - -3289 -3292 0 - -3289 -3293 0 - -3289 -3294 0 - -3289 -3295 0 - -3289 -3296 0 - -3290 -3291 0 - -3290 -3292 0 - -3290 -3293 0 - -3290 -3294 0 - -3290 -3295 0 - -3290 -3296 0 - -3291 -3292 0 - -3291 -3293 0 - -3291 -3294 0 - -3291 -3295 0 - -3291 -3296 0 - -3292 -3293 0 - -3292 -3294 0 - -3292 -3295 0 - -3292 -3296 0 - -3293 -3294 0 - -3293 -3295 0 - -3293 -3296 0 - -3294 -3295 0 - -3294 -3296 0 - -3295 -3296 0 - -3297 -3298 0 - -3297 -3299 0 - -3298 -3299 0 - -3300 -3301 0 - -3300 -3302 0 - -3300 -3303 0 - -3300 -3304 0 - -3300 -3305 0 - -3300 -3306 0 - -3301 -3302 0 - -3301 -3303 0 - -3301 -3304 0 - -3301 -3305 0 - -3301 -3306 0 - -3302 -3303 0 - -3302 -3304 0 - -3302 -3305 0 - -3302 -3306 0 - -3303 -3304 0 - -3303 -3305 0 - -3303 -3306 0 - -3304 -3305 0 - -3304 -3306 0 - -3305 -3306 0 - -3307 -3308 0 - -3307 -3309 0 - -3307 -3310 0 - -3307 -3311 0 - -3307 -3312 0 - -3307 -3313 0 - -3308 -3309 0 - -3308 -3310 0 - -3308 -3311 0 - -3308 -3312 0 - -3308 -3313 0 - -3309 -3310 0 - -3309 -3311 0 - -3309 -3312 0 - -3309 -3313 0 - -3310 -3311 0 - -3310 -3312 0 - -3310 -3313 0 - -3311 -3312 0 - -3311 -3313 0 - -3312 -3313 0 - -3314 -3315 0 - -3314 -3316 0 - -3314 -3317 0 - -3314 -3318 0 - -3314 -3319 0 - -3314 -3320 0 - -3314 -3321 0 - -3314 -3322 0 - -3314 -3323 0 - -3314 -3324 0 - -3314 -3325 0 - -3314 -3326 0 - -3315 -3316 0 - -3315 -3317 0 - -3315 -3318 0 - -3315 -3319 0 - -3315 -3320 0 - -3315 -3321 0 - -3315 -3322 0 - -3315 -3323 0 - -3315 -3324 0 - -3315 -3325 0 - -3315 -3326 0 - -3316 -3317 0 - -3316 -3318 0 - -3316 -3319 0 - -3316 -3320 0 - -3316 -3321 0 - -3316 -3322 0 - -3316 -3323 0 - -3316 -3324 0 - -3316 -3325 0 - -3316 -3326 0 - -3317 -3318 0 - -3317 -3319 0 - -3317 -3320 0 - -3317 -3321 0 - -3317 -3322 0 - -3317 -3323 0 - -3317 -3324 0 - -3317 -3325 0 - -3317 -3326 0 - -3318 -3319 0 - -3318 -3320 0 - -3318 -3321 0 - -3318 -3322 0 - -3318 -3323 0 - -3318 -3324 0 - -3318 -3325 0 - -3318 -3326 0 - -3319 -3320 0 - -3319 -3321 0 - -3319 -3322 0 - -3319 -3323 0 - -3319 -3324 0 - -3319 -3325 0 - -3319 -3326 0 - -3320 -3321 0 - -3320 -3322 0 - -3320 -3323 0 - -3320 -3324 0 - -3320 -3325 0 - -3320 -3326 0 - -3321 -3322 0 - -3321 -3323 0 - -3321 -3324 0 - -3321 -3325 0 - -3321 -3326 0 - -3322 -3323 0 - -3322 -3324 0 - -3322 -3325 0 - -3322 -3326 0 - -3323 -3324 0 - -3323 -3325 0 - -3323 -3326 0 - -3324 -3325 0 - -3324 -3326 0 - -3325 -3326 0 - -3327 -3328 0 - -3327 -3329 0 - -3327 -3330 0 - -3327 -3331 0 - -3327 -3332 0 - -3327 -3333 0 - -3327 -3334 0 - -3327 -3335 0 - -3328 -3329 0 - -3328 -3330 0 - -3328 -3331 0 - -3328 -3332 0 - -3328 -3333 0 - -3328 -3334 0 - -3328 -3335 0 - -3329 -3330 0 - -3329 -3331 0 - -3329 -3332 0 - -3329 -3333 0 - -3329 -3334 0 - -3329 -3335 0 - -3330 -3331 0 - -3330 -3332 0 - -3330 -3333 0 - -3330 -3334 0 - -3330 -3335 0 - -3331 -3332 0 - -3331 -3333 0 - -3331 -3334 0 - -3331 -3335 0 - -3332 -3333 0 - -3332 -3334 0 - -3332 -3335 0 - -3333 -3334 0 - -3333 -3335 0 - -3334 -3335 0 - -3336 -3337 0 - -3336 -3338 0 - -3336 -3339 0 - -3336 -3340 0 - -3336 -3341 0 - -3337 -3338 0 - -3337 -3339 0 - -3337 -3340 0 - -3337 -3341 0 - -3338 -3339 0 - -3338 -3340 0 - -3338 -3341 0 - -3339 -3340 0 - -3339 -3341 0 - -3340 -3341 0 - -3342 -3343 0 - -3342 -3344 0 - -3343 -3344 0 - -3345 -3346 0 - -3345 -3347 0 - -3345 -3348 0 - -3345 -3349 0 - -3345 -3350 0 - -3345 -3351 0 - -3345 -3352 0 - -3345 -3353 0 - -3346 -3347 0 - -3346 -3348 0 - -3346 -3349 0 - -3346 -3350 0 - -3346 -3351 0 - -3346 -3352 0 - -3346 -3353 0 - -3347 -3348 0 - -3347 -3349 0 - -3347 -3350 0 - -3347 -3351 0 - -3347 -3352 0 - -3347 -3353 0 - -3348 -3349 0 - -3348 -3350 0 - -3348 -3351 0 - -3348 -3352 0 - -3348 -3353 0 - -3349 -3350 0 - -3349 -3351 0 - -3349 -3352 0 - -3349 -3353 0 - -3350 -3351 0 - -3350 -3352 0 - -3350 -3353 0 - -3351 -3352 0 - -3351 -3353 0 - -3352 -3353 0 - -3354 -3355 0 - -3354 -3356 0 - -3354 -3357 0 - -3354 -3358 0 - -3354 -3359 0 - -3354 -3360 0 - -3354 -3361 0 - -3354 -3362 0 - -3354 -3363 0 - -3354 -3364 0 - -3355 -3356 0 - -3355 -3357 0 - -3355 -3358 0 - -3355 -3359 0 - -3355 -3360 0 - -3355 -3361 0 - -3355 -3362 0 - -3355 -3363 0 - -3355 -3364 0 - -3356 -3357 0 - -3356 -3358 0 - -3356 -3359 0 - -3356 -3360 0 - -3356 -3361 0 - -3356 -3362 0 - -3356 -3363 0 - -3356 -3364 0 - -3357 -3358 0 - -3357 -3359 0 - -3357 -3360 0 - -3357 -3361 0 - -3357 -3362 0 - -3357 -3363 0 - -3357 -3364 0 - -3358 -3359 0 - -3358 -3360 0 - -3358 -3361 0 - -3358 -3362 0 - -3358 -3363 0 - -3358 -3364 0 - -3359 -3360 0 - -3359 -3361 0 - -3359 -3362 0 - -3359 -3363 0 - -3359 -3364 0 - -3360 -3361 0 - -3360 -3362 0 - -3360 -3363 0 - -3360 -3364 0 - -3361 -3362 0 - -3361 -3363 0 - -3361 -3364 0 - -3362 -3363 0 - -3362 -3364 0 - -3363 -3364 0 - -3365 -3366 0 - -3365 -3367 0 - -3365 -3368 0 - -3365 -3369 0 - -3365 -3370 0 - -3365 -3371 0 - -3365 -3372 0 - -3366 -3367 0 - -3366 -3368 0 - -3366 -3369 0 - -3366 -3370 0 - -3366 -3371 0 - -3366 -3372 0 - -3367 -3368 0 - -3367 -3369 0 - -3367 -3370 0 - -3367 -3371 0 - -3367 -3372 0 - -3368 -3369 0 - -3368 -3370 0 - -3368 -3371 0 - -3368 -3372 0 - -3369 -3370 0 - -3369 -3371 0 - -3369 -3372 0 - -3370 -3371 0 - -3370 -3372 0 - -3371 -3372 0 - -3373 -3374 0 - -3373 -3375 0 - -3373 -3376 0 - -3373 -3377 0 - -3373 -3378 0 - -3373 -3379 0 - -3373 -3380 0 - -3374 -3375 0 - -3374 -3376 0 - -3374 -3377 0 - -3374 -3378 0 - -3374 -3379 0 - -3374 -3380 0 - -3375 -3376 0 - -3375 -3377 0 - -3375 -3378 0 - -3375 -3379 0 - -3375 -3380 0 - -3376 -3377 0 - -3376 -3378 0 - -3376 -3379 0 - -3376 -3380 0 - -3377 -3378 0 - -3377 -3379 0 - -3377 -3380 0 - -3378 -3379 0 - -3378 -3380 0 - -3379 -3380 0 - -3381 -3382 0 - -3381 -3383 0 - -3381 -3384 0 - -3381 -3385 0 - -3381 -3386 0 - -3381 -3387 0 - -3381 -3388 0 - -3381 -3389 0 - -3381 -3390 0 - -3381 -3391 0 - -3382 -3383 0 - -3382 -3384 0 - -3382 -3385 0 - -3382 -3386 0 - -3382 -3387 0 - -3382 -3388 0 - -3382 -3389 0 - -3382 -3390 0 - -3382 -3391 0 - -3383 -3384 0 - -3383 -3385 0 - -3383 -3386 0 - -3383 -3387 0 - -3383 -3388 0 - -3383 -3389 0 - -3383 -3390 0 - -3383 -3391 0 - -3384 -3385 0 - -3384 -3386 0 - -3384 -3387 0 - -3384 -3388 0 - -3384 -3389 0 - -3384 -3390 0 - -3384 -3391 0 - -3385 -3386 0 - -3385 -3387 0 - -3385 -3388 0 - -3385 -3389 0 - -3385 -3390 0 - -3385 -3391 0 - -3386 -3387 0 - -3386 -3388 0 - -3386 -3389 0 - -3386 -3390 0 - -3386 -3391 0 - -3387 -3388 0 - -3387 -3389 0 - -3387 -3390 0 - -3387 -3391 0 - -3388 -3389 0 - -3388 -3390 0 - -3388 -3391 0 - -3389 -3390 0 - -3389 -3391 0 - -3390 -3391 0 - -3392 -3393 0 - -3392 -3394 0 - -3392 -3395 0 - -3392 -3396 0 - -3392 -3397 0 - -3392 -3398 0 - -3392 -3399 0 - -3393 -3394 0 - -3393 -3395 0 - -3393 -3396 0 - -3393 -3397 0 - -3393 -3398 0 - -3393 -3399 0 - -3394 -3395 0 - -3394 -3396 0 - -3394 -3397 0 - -3394 -3398 0 - -3394 -3399 0 - -3395 -3396 0 - -3395 -3397 0 - -3395 -3398 0 - -3395 -3399 0 - -3396 -3397 0 - -3396 -3398 0 - -3396 -3399 0 - -3397 -3398 0 - -3397 -3399 0 - -3398 -3399 0 - -3400 -3401 0 - -3400 -3402 0 - -3400 -3403 0 - -3401 -3402 0 - -3401 -3403 0 - -3402 -3403 0 - -3404 -3405 0 - -3404 -3406 0 - -3404 -3407 0 - -3404 -3408 0 - -3404 -3409 0 - -3404 -3410 0 - -3404 -3411 0 - -3405 -3406 0 - -3405 -3407 0 - -3405 -3408 0 - -3405 -3409 0 - -3405 -3410 0 - -3405 -3411 0 - -3406 -3407 0 - -3406 -3408 0 - -3406 -3409 0 - -3406 -3410 0 - -3406 -3411 0 - -3407 -3408 0 - -3407 -3409 0 - -3407 -3410 0 - -3407 -3411 0 - -3408 -3409 0 - -3408 -3410 0 - -3408 -3411 0 - -3409 -3410 0 - -3409 -3411 0 - -3410 -3411 0 - -3412 -3413 0 - -3412 -3414 0 - -3412 -3415 0 - -3412 -3416 0 - -3412 -3417 0 - -3412 -3418 0 - -3412 -3419 0 - -3412 -3420 0 - -3412 -3421 0 - -3413 -3414 0 - -3413 -3415 0 - -3413 -3416 0 - -3413 -3417 0 - -3413 -3418 0 - -3413 -3419 0 - -3413 -3420 0 - -3413 -3421 0 - -3414 -3415 0 - -3414 -3416 0 - -3414 -3417 0 - -3414 -3418 0 - -3414 -3419 0 - -3414 -3420 0 - -3414 -3421 0 - -3415 -3416 0 - -3415 -3417 0 - -3415 -3418 0 - -3415 -3419 0 - -3415 -3420 0 - -3415 -3421 0 - -3416 -3417 0 - -3416 -3418 0 - -3416 -3419 0 - -3416 -3420 0 - -3416 -3421 0 - -3417 -3418 0 - -3417 -3419 0 - -3417 -3420 0 - -3417 -3421 0 - -3418 -3419 0 - -3418 -3420 0 - -3418 -3421 0 - -3419 -3420 0 - -3419 -3421 0 - -3420 -3421 0 - -3422 -3423 0 - -3422 -3424 0 - -3422 -3425 0 - -3422 -3426 0 - -3422 -3427 0 - -3422 -3428 0 - -3422 -3429 0 - -3422 -3430 0 - -3423 -3424 0 - -3423 -3425 0 - -3423 -3426 0 - -3423 -3427 0 - -3423 -3428 0 - -3423 -3429 0 - -3423 -3430 0 - -3424 -3425 0 - -3424 -3426 0 - -3424 -3427 0 - -3424 -3428 0 - -3424 -3429 0 - -3424 -3430 0 - -3425 -3426 0 - -3425 -3427 0 - -3425 -3428 0 - -3425 -3429 0 - -3425 -3430 0 - -3426 -3427 0 - -3426 -3428 0 - -3426 -3429 0 - -3426 -3430 0 - -3427 -3428 0 - -3427 -3429 0 - -3427 -3430 0 - -3428 -3429 0 - -3428 -3430 0 - -3429 -3430 0 - -3431 -3432 0 - -3431 -3433 0 - -3431 -3434 0 - -3431 -3435 0 - -3431 -3436 0 - -3431 -3437 0 - -3431 -3438 0 - -3431 -3439 0 - -3432 -3433 0 - -3432 -3434 0 - -3432 -3435 0 - -3432 -3436 0 - -3432 -3437 0 - -3432 -3438 0 - -3432 -3439 0 - -3433 -3434 0 - -3433 -3435 0 - -3433 -3436 0 - -3433 -3437 0 - -3433 -3438 0 - -3433 -3439 0 - -3434 -3435 0 - -3434 -3436 0 - -3434 -3437 0 - -3434 -3438 0 - -3434 -3439 0 - -3435 -3436 0 - -3435 -3437 0 - -3435 -3438 0 - -3435 -3439 0 - -3436 -3437 0 - -3436 -3438 0 - -3436 -3439 0 - -3437 -3438 0 - -3437 -3439 0 - -3438 -3439 0 - -3440 -3441 0 - -3440 -3442 0 - -3440 -3443 0 - -3440 -3444 0 - -3440 -3445 0 - -3440 -3446 0 - -3441 -3442 0 - -3441 -3443 0 - -3441 -3444 0 - -3441 -3445 0 - -3441 -3446 0 - -3442 -3443 0 - -3442 -3444 0 - -3442 -3445 0 - -3442 -3446 0 - -3443 -3444 0 - -3443 -3445 0 - -3443 -3446 0 - -3444 -3445 0 - -3444 -3446 0 - -3445 -3446 0 - -3447 -3448 0 - -3447 -3449 0 - -3447 -3450 0 - -3447 -3451 0 - -3447 -3452 0 - -3447 -3453 0 - -3448 -3449 0 - -3448 -3450 0 - -3448 -3451 0 - -3448 -3452 0 - -3448 -3453 0 - -3449 -3450 0 - -3449 -3451 0 - -3449 -3452 0 - -3449 -3453 0 - -3450 -3451 0 - -3450 -3452 0 - -3450 -3453 0 - -3451 -3452 0 - -3451 -3453 0 - -3452 -3453 0 - -3454 -3455 0 - -3454 -3456 0 - -3454 -3457 0 - -3454 -3458 0 - -3454 -3459 0 - -3454 -3460 0 - -3454 -3461 0 - -3454 -3462 0 - -3454 -3463 0 - -3455 -3456 0 - -3455 -3457 0 - -3455 -3458 0 - -3455 -3459 0 - -3455 -3460 0 - -3455 -3461 0 - -3455 -3462 0 - -3455 -3463 0 - -3456 -3457 0 - -3456 -3458 0 - -3456 -3459 0 - -3456 -3460 0 - -3456 -3461 0 - -3456 -3462 0 - -3456 -3463 0 - -3457 -3458 0 - -3457 -3459 0 - -3457 -3460 0 - -3457 -3461 0 - -3457 -3462 0 - -3457 -3463 0 - -3458 -3459 0 - -3458 -3460 0 - -3458 -3461 0 - -3458 -3462 0 - -3458 -3463 0 - -3459 -3460 0 - -3459 -3461 0 - -3459 -3462 0 - -3459 -3463 0 - -3460 -3461 0 - -3460 -3462 0 - -3460 -3463 0 - -3461 -3462 0 - -3461 -3463 0 - -3462 -3463 0 - -3464 -3465 0 - -3464 -3466 0 - -3464 -3467 0 - -3464 -3468 0 - -3465 -3466 0 - -3465 -3467 0 - -3465 -3468 0 - -3466 -3467 0 - -3466 -3468 0 - -3467 -3468 0 - -3469 -3470 0 - -3469 -3471 0 - -3469 -3472 0 - -3469 -3473 0 - -3469 -3474 0 - -3469 -3475 0 - -3469 -3476 0 - -3469 -3477 0 - -3470 -3471 0 - -3470 -3472 0 - -3470 -3473 0 - -3470 -3474 0 - -3470 -3475 0 - -3470 -3476 0 - -3470 -3477 0 - -3471 -3472 0 - -3471 -3473 0 - -3471 -3474 0 - -3471 -3475 0 - -3471 -3476 0 - -3471 -3477 0 - -3472 -3473 0 - -3472 -3474 0 - -3472 -3475 0 - -3472 -3476 0 - -3472 -3477 0 - -3473 -3474 0 - -3473 -3475 0 - -3473 -3476 0 - -3473 -3477 0 - -3474 -3475 0 - -3474 -3476 0 - -3474 -3477 0 - -3475 -3476 0 - -3475 -3477 0 - -3476 -3477 0 - -3478 -3479 0 - -3478 -3480 0 - -3478 -3481 0 - -3478 -3482 0 - -3478 -3483 0 - -3478 -3484 0 - -3478 -3485 0 - -3478 -3486 0 - -3478 -3487 0 - -3478 -3488 0 - -3479 -3480 0 - -3479 -3481 0 - -3479 -3482 0 - -3479 -3483 0 - -3479 -3484 0 - -3479 -3485 0 - -3479 -3486 0 - -3479 -3487 0 - -3479 -3488 0 - -3480 -3481 0 - -3480 -3482 0 - -3480 -3483 0 - -3480 -3484 0 - -3480 -3485 0 - -3480 -3486 0 - -3480 -3487 0 - -3480 -3488 0 - -3481 -3482 0 - -3481 -3483 0 - -3481 -3484 0 - -3481 -3485 0 - -3481 -3486 0 - -3481 -3487 0 - -3481 -3488 0 - -3482 -3483 0 - -3482 -3484 0 - -3482 -3485 0 - -3482 -3486 0 - -3482 -3487 0 - -3482 -3488 0 - -3483 -3484 0 - -3483 -3485 0 - -3483 -3486 0 - -3483 -3487 0 - -3483 -3488 0 - -3484 -3485 0 - -3484 -3486 0 - -3484 -3487 0 - -3484 -3488 0 - -3485 -3486 0 - -3485 -3487 0 - -3485 -3488 0 - -3486 -3487 0 - -3486 -3488 0 - -3487 -3488 0 - -3489 -3490 0 - -3489 -3491 0 - -3489 -3492 0 - -3489 -3493 0 - -3489 -3494 0 - -3489 -3495 0 - -3489 -3496 0 - -3489 -3497 0 - -3489 -3498 0 - -3489 -3499 0 - -3490 -3491 0 - -3490 -3492 0 - -3490 -3493 0 - -3490 -3494 0 - -3490 -3495 0 - -3490 -3496 0 - -3490 -3497 0 - -3490 -3498 0 - -3490 -3499 0 - -3491 -3492 0 - -3491 -3493 0 - -3491 -3494 0 - -3491 -3495 0 - -3491 -3496 0 - -3491 -3497 0 - -3491 -3498 0 - -3491 -3499 0 - -3492 -3493 0 - -3492 -3494 0 - -3492 -3495 0 - -3492 -3496 0 - -3492 -3497 0 - -3492 -3498 0 - -3492 -3499 0 - -3493 -3494 0 - -3493 -3495 0 - -3493 -3496 0 - -3493 -3497 0 - -3493 -3498 0 - -3493 -3499 0 - -3494 -3495 0 - -3494 -3496 0 - -3494 -3497 0 - -3494 -3498 0 - -3494 -3499 0 - -3495 -3496 0 - -3495 -3497 0 - -3495 -3498 0 - -3495 -3499 0 - -3496 -3497 0 - -3496 -3498 0 - -3496 -3499 0 - -3497 -3498 0 - -3497 -3499 0 - -3498 -3499 0 - -3500 -3501 0 - -3500 -3502 0 - -3500 -3503 0 - -3500 -3504 0 - -3500 -3505 0 - -3501 -3502 0 - -3501 -3503 0 - -3501 -3504 0 - -3501 -3505 0 - -3502 -3503 0 - -3502 -3504 0 - -3502 -3505 0 - -3503 -3504 0 - -3503 -3505 0 - -3504 -3505 0 - -3506 -3507 0 - -3506 -3508 0 - -3506 -3509 0 - -3506 -3510 0 - -3506 -3511 0 - -3506 -3512 0 - -3506 -3513 0 - -3506 -3514 0 - -3506 -3515 0 - -3506 -3516 0 - -3507 -3508 0 - -3507 -3509 0 - -3507 -3510 0 - -3507 -3511 0 - -3507 -3512 0 - -3507 -3513 0 - -3507 -3514 0 - -3507 -3515 0 - -3507 -3516 0 - -3508 -3509 0 - -3508 -3510 0 - -3508 -3511 0 - -3508 -3512 0 - -3508 -3513 0 - -3508 -3514 0 - -3508 -3515 0 - -3508 -3516 0 - -3509 -3510 0 - -3509 -3511 0 - -3509 -3512 0 - -3509 -3513 0 - -3509 -3514 0 - -3509 -3515 0 - -3509 -3516 0 - -3510 -3511 0 - -3510 -3512 0 - -3510 -3513 0 - -3510 -3514 0 - -3510 -3515 0 - -3510 -3516 0 - -3511 -3512 0 - -3511 -3513 0 - -3511 -3514 0 - -3511 -3515 0 - -3511 -3516 0 - -3512 -3513 0 - -3512 -3514 0 - -3512 -3515 0 - -3512 -3516 0 - -3513 -3514 0 - -3513 -3515 0 - -3513 -3516 0 - -3514 -3515 0 - -3514 -3516 0 - -3515 -3516 0 - -3517 -3518 0 - -3517 -3519 0 - -3517 -3520 0 - -3517 -3521 0 - -3517 -3522 0 - -3517 -3523 0 - -3517 -3524 0 - -3517 -3525 0 - -3517 -3526 0 - -3517 -3527 0 - -3518 -3519 0 - -3518 -3520 0 - -3518 -3521 0 - -3518 -3522 0 - -3518 -3523 0 - -3518 -3524 0 - -3518 -3525 0 - -3518 -3526 0 - -3518 -3527 0 - -3519 -3520 0 - -3519 -3521 0 - -3519 -3522 0 - -3519 -3523 0 - -3519 -3524 0 - -3519 -3525 0 - -3519 -3526 0 - -3519 -3527 0 - -3520 -3521 0 - -3520 -3522 0 - -3520 -3523 0 - -3520 -3524 0 - -3520 -3525 0 - -3520 -3526 0 - -3520 -3527 0 - -3521 -3522 0 - -3521 -3523 0 - -3521 -3524 0 - -3521 -3525 0 - -3521 -3526 0 - -3521 -3527 0 - -3522 -3523 0 - -3522 -3524 0 - -3522 -3525 0 - -3522 -3526 0 - -3522 -3527 0 - -3523 -3524 0 - -3523 -3525 0 - -3523 -3526 0 - -3523 -3527 0 - -3524 -3525 0 - -3524 -3526 0 - -3524 -3527 0 - -3525 -3526 0 - -3525 -3527 0 - -3526 -3527 0 - -3528 -3529 0 - -3528 -3530 0 - -3528 -3531 0 - -3528 -3532 0 - -3528 -3533 0 - -3529 -3530 0 - -3529 -3531 0 - -3529 -3532 0 - -3529 -3533 0 - -3530 -3531 0 - -3530 -3532 0 - -3530 -3533 0 - -3531 -3532 0 - -3531 -3533 0 - -3532 -3533 0 - -3534 -3535 0 - -3534 -3536 0 - -3534 -3537 0 - -3534 -3538 0 - -3534 -3539 0 - -3534 -3540 0 - -3535 -3536 0 - -3535 -3537 0 - -3535 -3538 0 - -3535 -3539 0 - -3535 -3540 0 - -3536 -3537 0 - -3536 -3538 0 - -3536 -3539 0 - -3536 -3540 0 - -3537 -3538 0 - -3537 -3539 0 - -3537 -3540 0 - -3538 -3539 0 - -3538 -3540 0 - -3539 -3540 0 - -3541 -3542 0 - -3541 -3543 0 - -3541 -3544 0 - -3541 -3545 0 - -3541 -3546 0 - -3542 -3543 0 - -3542 -3544 0 - -3542 -3545 0 - -3542 -3546 0 - -3543 -3544 0 - -3543 -3545 0 - -3543 -3546 0 - -3544 -3545 0 - -3544 -3546 0 - -3545 -3546 0 - -3547 -3548 0 - -3547 -3549 0 - -3547 -3550 0 - -3547 -3551 0 - -3547 -3552 0 - -3547 -3553 0 - -3547 -3554 0 - -3547 -3555 0 - -3548 -3549 0 - -3548 -3550 0 - -3548 -3551 0 - -3548 -3552 0 - -3548 -3553 0 - -3548 -3554 0 - -3548 -3555 0 - -3549 -3550 0 - -3549 -3551 0 - -3549 -3552 0 - -3549 -3553 0 - -3549 -3554 0 - -3549 -3555 0 - -3550 -3551 0 - -3550 -3552 0 - -3550 -3553 0 - -3550 -3554 0 - -3550 -3555 0 - -3551 -3552 0 - -3551 -3553 0 - -3551 -3554 0 - -3551 -3555 0 - -3552 -3553 0 - -3552 -3554 0 - -3552 -3555 0 - -3553 -3554 0 - -3553 -3555 0 - -3554 -3555 0 - -3556 -3557 0 - -3556 -3558 0 - -3556 -3559 0 - -3556 -3560 0 - -3557 -3558 0 - -3557 -3559 0 - -3557 -3560 0 - -3558 -3559 0 - -3558 -3560 0 - -3559 -3560 0 - -3561 -3562 0 - -3561 -3563 0 - -3561 -3564 0 - -3561 -3565 0 - -3562 -3563 0 - -3562 -3564 0 - -3562 -3565 0 - -3563 -3564 0 - -3563 -3565 0 - -3564 -3565 0 - -3566 -3567 0 - -3566 -3568 0 - -3566 -3569 0 - -3566 -3570 0 - -3566 -3571 0 - -3566 -3572 0 - -3566 -3573 0 - -3566 -3574 0 - -3567 -3568 0 - -3567 -3569 0 - -3567 -3570 0 - -3567 -3571 0 - -3567 -3572 0 - -3567 -3573 0 - -3567 -3574 0 - -3568 -3569 0 - -3568 -3570 0 - -3568 -3571 0 - -3568 -3572 0 - -3568 -3573 0 - -3568 -3574 0 - -3569 -3570 0 - -3569 -3571 0 - -3569 -3572 0 - -3569 -3573 0 - -3569 -3574 0 - -3570 -3571 0 - -3570 -3572 0 - -3570 -3573 0 - -3570 -3574 0 - -3571 -3572 0 - -3571 -3573 0 - -3571 -3574 0 - -3572 -3573 0 - -3572 -3574 0 - -3573 -3574 0 - -3575 -3576 0 - -3575 -3577 0 - -3575 -3578 0 - -3575 -3579 0 - -3575 -3580 0 - -3575 -3581 0 - -3575 -3582 0 - -3576 -3577 0 - -3576 -3578 0 - -3576 -3579 0 - -3576 -3580 0 - -3576 -3581 0 - -3576 -3582 0 - -3577 -3578 0 - -3577 -3579 0 - -3577 -3580 0 - -3577 -3581 0 - -3577 -3582 0 - -3578 -3579 0 - -3578 -3580 0 - -3578 -3581 0 - -3578 -3582 0 - -3579 -3580 0 - -3579 -3581 0 - -3579 -3582 0 - -3580 -3581 0 - -3580 -3582 0 - -3581 -3582 0 - -3583 -3584 0 - -3583 -3585 0 - -3583 -3586 0 - -3583 -3587 0 - -3583 -3588 0 - -3583 -3589 0 - -3583 -3590 0 - -3583 -3591 0 - -3583 -3592 0 - -3584 -3585 0 - -3584 -3586 0 - -3584 -3587 0 - -3584 -3588 0 - -3584 -3589 0 - -3584 -3590 0 - -3584 -3591 0 - -3584 -3592 0 - -3585 -3586 0 - -3585 -3587 0 - -3585 -3588 0 - -3585 -3589 0 - -3585 -3590 0 - -3585 -3591 0 - -3585 -3592 0 - -3586 -3587 0 - -3586 -3588 0 - -3586 -3589 0 - -3586 -3590 0 - -3586 -3591 0 - -3586 -3592 0 - -3587 -3588 0 - -3587 -3589 0 - -3587 -3590 0 - -3587 -3591 0 - -3587 -3592 0 - -3588 -3589 0 - -3588 -3590 0 - -3588 -3591 0 - -3588 -3592 0 - -3589 -3590 0 - -3589 -3591 0 - -3589 -3592 0 - -3590 -3591 0 - -3590 -3592 0 - -3591 -3592 0 - -3593 -3594 0 - -3593 -3595 0 - -3593 -3596 0 - -3593 -3597 0 - -3593 -3598 0 - -3593 -3599 0 - -3593 -3600 0 - -3593 -3601 0 - -3593 -3602 0 - -3594 -3595 0 - -3594 -3596 0 - -3594 -3597 0 - -3594 -3598 0 - -3594 -3599 0 - -3594 -3600 0 - -3594 -3601 0 - -3594 -3602 0 - -3595 -3596 0 - -3595 -3597 0 - -3595 -3598 0 - -3595 -3599 0 - -3595 -3600 0 - -3595 -3601 0 - -3595 -3602 0 - -3596 -3597 0 - -3596 -3598 0 - -3596 -3599 0 - -3596 -3600 0 - -3596 -3601 0 - -3596 -3602 0 - -3597 -3598 0 - -3597 -3599 0 - -3597 -3600 0 - -3597 -3601 0 - -3597 -3602 0 - -3598 -3599 0 - -3598 -3600 0 - -3598 -3601 0 - -3598 -3602 0 - -3599 -3600 0 - -3599 -3601 0 - -3599 -3602 0 - -3600 -3601 0 - -3600 -3602 0 - -3601 -3602 0 - -3603 -3604 0 - -3603 -3605 0 - -3603 -3606 0 - -3603 -3607 0 - -3603 -3608 0 - -3603 -3609 0 - -3603 -3610 0 - -3603 -3611 0 - -3603 -3612 0 - -3604 -3605 0 - -3604 -3606 0 - -3604 -3607 0 - -3604 -3608 0 - -3604 -3609 0 - -3604 -3610 0 - -3604 -3611 0 - -3604 -3612 0 - -3605 -3606 0 - -3605 -3607 0 - -3605 -3608 0 - -3605 -3609 0 - -3605 -3610 0 - -3605 -3611 0 - -3605 -3612 0 - -3606 -3607 0 - -3606 -3608 0 - -3606 -3609 0 - -3606 -3610 0 - -3606 -3611 0 - -3606 -3612 0 - -3607 -3608 0 - -3607 -3609 0 - -3607 -3610 0 - -3607 -3611 0 - -3607 -3612 0 - -3608 -3609 0 - -3608 -3610 0 - -3608 -3611 0 - -3608 -3612 0 - -3609 -3610 0 - -3609 -3611 0 - -3609 -3612 0 - -3610 -3611 0 - -3610 -3612 0 - -3611 -3612 0 - -3613 -3614 0 - -3613 -3615 0 - -3613 -3616 0 - -3613 -3617 0 - -3613 -3618 0 - -3613 -3619 0 - -3614 -3615 0 - -3614 -3616 0 - -3614 -3617 0 - -3614 -3618 0 - -3614 -3619 0 - -3615 -3616 0 - -3615 -3617 0 - -3615 -3618 0 - -3615 -3619 0 - -3616 -3617 0 - -3616 -3618 0 - -3616 -3619 0 - -3617 -3618 0 - -3617 -3619 0 - -3618 -3619 0 - -3620 -3621 0 - -3620 -3622 0 - -3620 -3623 0 - -3620 -3624 0 - -3620 -3625 0 - -3620 -3626 0 - -3620 -3627 0 - -3620 -3628 0 - -3620 -3629 0 - -3620 -3630 0 - -3621 -3622 0 - -3621 -3623 0 - -3621 -3624 0 - -3621 -3625 0 - -3621 -3626 0 - -3621 -3627 0 - -3621 -3628 0 - -3621 -3629 0 - -3621 -3630 0 - -3622 -3623 0 - -3622 -3624 0 - -3622 -3625 0 - -3622 -3626 0 - -3622 -3627 0 - -3622 -3628 0 - -3622 -3629 0 - -3622 -3630 0 - -3623 -3624 0 - -3623 -3625 0 - -3623 -3626 0 - -3623 -3627 0 - -3623 -3628 0 - -3623 -3629 0 - -3623 -3630 0 - -3624 -3625 0 - -3624 -3626 0 - -3624 -3627 0 - -3624 -3628 0 - -3624 -3629 0 - -3624 -3630 0 - -3625 -3626 0 - -3625 -3627 0 - -3625 -3628 0 - -3625 -3629 0 - -3625 -3630 0 - -3626 -3627 0 - -3626 -3628 0 - -3626 -3629 0 - -3626 -3630 0 - -3627 -3628 0 - -3627 -3629 0 - -3627 -3630 0 - -3628 -3629 0 - -3628 -3630 0 - -3629 -3630 0 - -3631 -3632 0 - -3631 -3633 0 - -3631 -3634 0 - -3631 -3635 0 - -3631 -3636 0 - -3631 -3637 0 - -3631 -3638 0 - -3632 -3633 0 - -3632 -3634 0 - -3632 -3635 0 - -3632 -3636 0 - -3632 -3637 0 - -3632 -3638 0 - -3633 -3634 0 - -3633 -3635 0 - -3633 -3636 0 - -3633 -3637 0 - -3633 -3638 0 - -3634 -3635 0 - -3634 -3636 0 - -3634 -3637 0 - -3634 -3638 0 - -3635 -3636 0 - -3635 -3637 0 - -3635 -3638 0 - -3636 -3637 0 - -3636 -3638 0 - -3637 -3638 0 - -3639 -3640 0 - -3639 -3641 0 - -3639 -3642 0 - -3639 -3643 0 - -3639 -3644 0 - -3639 -3645 0 - -3639 -3646 0 - -3639 -3647 0 - -3639 -3648 0 - -3639 -3649 0 - -3639 -3650 0 - -3640 -3641 0 - -3640 -3642 0 - -3640 -3643 0 - -3640 -3644 0 - -3640 -3645 0 - -3640 -3646 0 - -3640 -3647 0 - -3640 -3648 0 - -3640 -3649 0 - -3640 -3650 0 - -3641 -3642 0 - -3641 -3643 0 - -3641 -3644 0 - -3641 -3645 0 - -3641 -3646 0 - -3641 -3647 0 - -3641 -3648 0 - -3641 -3649 0 - -3641 -3650 0 - -3642 -3643 0 - -3642 -3644 0 - -3642 -3645 0 - -3642 -3646 0 - -3642 -3647 0 - -3642 -3648 0 - -3642 -3649 0 - -3642 -3650 0 - -3643 -3644 0 - -3643 -3645 0 - -3643 -3646 0 - -3643 -3647 0 - -3643 -3648 0 - -3643 -3649 0 - -3643 -3650 0 - -3644 -3645 0 - -3644 -3646 0 - -3644 -3647 0 - -3644 -3648 0 - -3644 -3649 0 - -3644 -3650 0 - -3645 -3646 0 - -3645 -3647 0 - -3645 -3648 0 - -3645 -3649 0 - -3645 -3650 0 - -3646 -3647 0 - -3646 -3648 0 - -3646 -3649 0 - -3646 -3650 0 - -3647 -3648 0 - -3647 -3649 0 - -3647 -3650 0 - -3648 -3649 0 - -3648 -3650 0 - -3649 -3650 0 - -3651 -3652 0 - -3651 -3653 0 - -3651 -3654 0 - -3652 -3653 0 - -3652 -3654 0 - -3653 -3654 0 - -3655 -3656 0 - -3655 -3657 0 - -3655 -3658 0 - -3655 -3659 0 - -3655 -3660 0 - -3655 -3661 0 - -3655 -3662 0 - -3655 -3663 0 - -3655 -3664 0 - -3655 -3665 0 - -3655 -3666 0 - -3656 -3657 0 - -3656 -3658 0 - -3656 -3659 0 - -3656 -3660 0 - -3656 -3661 0 - -3656 -3662 0 - -3656 -3663 0 - -3656 -3664 0 - -3656 -3665 0 - -3656 -3666 0 - -3657 -3658 0 - -3657 -3659 0 - -3657 -3660 0 - -3657 -3661 0 - -3657 -3662 0 - -3657 -3663 0 - -3657 -3664 0 - -3657 -3665 0 - -3657 -3666 0 - -3658 -3659 0 - -3658 -3660 0 - -3658 -3661 0 - -3658 -3662 0 - -3658 -3663 0 - -3658 -3664 0 - -3658 -3665 0 - -3658 -3666 0 - -3659 -3660 0 - -3659 -3661 0 - -3659 -3662 0 - -3659 -3663 0 - -3659 -3664 0 - -3659 -3665 0 - -3659 -3666 0 - -3660 -3661 0 - -3660 -3662 0 - -3660 -3663 0 - -3660 -3664 0 - -3660 -3665 0 - -3660 -3666 0 - -3661 -3662 0 - -3661 -3663 0 - -3661 -3664 0 - -3661 -3665 0 - -3661 -3666 0 - -3662 -3663 0 - -3662 -3664 0 - -3662 -3665 0 - -3662 -3666 0 - -3663 -3664 0 - -3663 -3665 0 - -3663 -3666 0 - -3664 -3665 0 - -3664 -3666 0 - -3665 -3666 0 - -3667 -3668 0 - -3667 -3669 0 - -3667 -3670 0 - -3667 -3671 0 - -3667 -3672 0 - -3667 -3673 0 - -3667 -3674 0 - -3667 -3675 0 - -3667 -3676 0 - -3667 -3677 0 - -3667 -3678 0 - -3668 -3669 0 - -3668 -3670 0 - -3668 -3671 0 - -3668 -3672 0 - -3668 -3673 0 - -3668 -3674 0 - -3668 -3675 0 - -3668 -3676 0 - -3668 -3677 0 - -3668 -3678 0 - -3669 -3670 0 - -3669 -3671 0 - -3669 -3672 0 - -3669 -3673 0 - -3669 -3674 0 - -3669 -3675 0 - -3669 -3676 0 - -3669 -3677 0 - -3669 -3678 0 - -3670 -3671 0 - -3670 -3672 0 - -3670 -3673 0 - -3670 -3674 0 - -3670 -3675 0 - -3670 -3676 0 - -3670 -3677 0 - -3670 -3678 0 - -3671 -3672 0 - -3671 -3673 0 - -3671 -3674 0 - -3671 -3675 0 - -3671 -3676 0 - -3671 -3677 0 - -3671 -3678 0 - -3672 -3673 0 - -3672 -3674 0 - -3672 -3675 0 - -3672 -3676 0 - -3672 -3677 0 - -3672 -3678 0 - -3673 -3674 0 - -3673 -3675 0 - -3673 -3676 0 - -3673 -3677 0 - -3673 -3678 0 - -3674 -3675 0 - -3674 -3676 0 - -3674 -3677 0 - -3674 -3678 0 - -3675 -3676 0 - -3675 -3677 0 - -3675 -3678 0 - -3676 -3677 0 - -3676 -3678 0 - -3677 -3678 0 - -3679 -3680 0 - -3679 -3681 0 - -3679 -3682 0 - -3679 -3683 0 - -3679 -3684 0 - -3680 -3681 0 - -3680 -3682 0 - -3680 -3683 0 - -3680 -3684 0 - -3681 -3682 0 - -3681 -3683 0 - -3681 -3684 0 - -3682 -3683 0 - -3682 -3684 0 - -3683 -3684 0 - -3685 -3686 0 - -3685 -3687 0 - -3685 -3688 0 - -3685 -3689 0 - -3685 -3690 0 - -3685 -3691 0 - -3686 -3687 0 - -3686 -3688 0 - -3686 -3689 0 - -3686 -3690 0 - -3686 -3691 0 - -3687 -3688 0 - -3687 -3689 0 - -3687 -3690 0 - -3687 -3691 0 - -3688 -3689 0 - -3688 -3690 0 - -3688 -3691 0 - -3689 -3690 0 - -3689 -3691 0 - -3690 -3691 0 - -3692 -3693 0 - -3692 -3694 0 - -3692 -3695 0 - -3692 -3696 0 - -3692 -3697 0 - -3692 -3698 0 - -3692 -3699 0 - -3692 -3700 0 - -3692 -3701 0 - -3692 -3702 0 - -3693 -3694 0 - -3693 -3695 0 - -3693 -3696 0 - -3693 -3697 0 - -3693 -3698 0 - -3693 -3699 0 - -3693 -3700 0 - -3693 -3701 0 - -3693 -3702 0 - -3694 -3695 0 - -3694 -3696 0 - -3694 -3697 0 - -3694 -3698 0 - -3694 -3699 0 - -3694 -3700 0 - -3694 -3701 0 - -3694 -3702 0 - -3695 -3696 0 - -3695 -3697 0 - -3695 -3698 0 - -3695 -3699 0 - -3695 -3700 0 - -3695 -3701 0 - -3695 -3702 0 - -3696 -3697 0 - -3696 -3698 0 - -3696 -3699 0 - -3696 -3700 0 - -3696 -3701 0 - -3696 -3702 0 - -3697 -3698 0 - -3697 -3699 0 - -3697 -3700 0 - -3697 -3701 0 - -3697 -3702 0 - -3698 -3699 0 - -3698 -3700 0 - -3698 -3701 0 - -3698 -3702 0 - -3699 -3700 0 - -3699 -3701 0 - -3699 -3702 0 - -3700 -3701 0 - -3700 -3702 0 - -3701 -3702 0 - -3703 -3704 0 - -3703 -3705 0 - -3703 -3706 0 - -3703 -3707 0 - -3703 -3708 0 - -3703 -3709 0 - -3703 -3710 0 - -3703 -3711 0 - -3703 -3712 0 - -3704 -3705 0 - -3704 -3706 0 - -3704 -3707 0 - -3704 -3708 0 - -3704 -3709 0 - -3704 -3710 0 - -3704 -3711 0 - -3704 -3712 0 - -3705 -3706 0 - -3705 -3707 0 - -3705 -3708 0 - -3705 -3709 0 - -3705 -3710 0 - -3705 -3711 0 - -3705 -3712 0 - -3706 -3707 0 - -3706 -3708 0 - -3706 -3709 0 - -3706 -3710 0 - -3706 -3711 0 - -3706 -3712 0 - -3707 -3708 0 - -3707 -3709 0 - -3707 -3710 0 - -3707 -3711 0 - -3707 -3712 0 - -3708 -3709 0 - -3708 -3710 0 - -3708 -3711 0 - -3708 -3712 0 - -3709 -3710 0 - -3709 -3711 0 - -3709 -3712 0 - -3710 -3711 0 - -3710 -3712 0 - -3711 -3712 0 - -3713 -3714 0 - -3713 -3715 0 - -3713 -3716 0 - -3713 -3717 0 - -3713 -3718 0 - -3713 -3719 0 - -3713 -3720 0 - -3713 -3721 0 - -3713 -3722 0 - -3714 -3715 0 - -3714 -3716 0 - -3714 -3717 0 - -3714 -3718 0 - -3714 -3719 0 - -3714 -3720 0 - -3714 -3721 0 - -3714 -3722 0 - -3715 -3716 0 - -3715 -3717 0 - -3715 -3718 0 - -3715 -3719 0 - -3715 -3720 0 - -3715 -3721 0 - -3715 -3722 0 - -3716 -3717 0 - -3716 -3718 0 - -3716 -3719 0 - -3716 -3720 0 - -3716 -3721 0 - -3716 -3722 0 - -3717 -3718 0 - -3717 -3719 0 - -3717 -3720 0 - -3717 -3721 0 - -3717 -3722 0 - -3718 -3719 0 - -3718 -3720 0 - -3718 -3721 0 - -3718 -3722 0 - -3719 -3720 0 - -3719 -3721 0 - -3719 -3722 0 - -3720 -3721 0 - -3720 -3722 0 - -3721 -3722 0 - -3723 -3724 0 - -3723 -3725 0 - -3723 -3726 0 - -3723 -3727 0 - -3723 -3728 0 - -3723 -3729 0 - -3723 -3730 0 - -3724 -3725 0 - -3724 -3726 0 - -3724 -3727 0 - -3724 -3728 0 - -3724 -3729 0 - -3724 -3730 0 - -3725 -3726 0 - -3725 -3727 0 - -3725 -3728 0 - -3725 -3729 0 - -3725 -3730 0 - -3726 -3727 0 - -3726 -3728 0 - -3726 -3729 0 - -3726 -3730 0 - -3727 -3728 0 - -3727 -3729 0 - -3727 -3730 0 - -3728 -3729 0 - -3728 -3730 0 - -3729 -3730 0 - -3731 -3732 0 - -3731 -3733 0 - -3731 -3734 0 - -3731 -3735 0 - -3731 -3736 0 - -3731 -3737 0 - -3732 -3733 0 - -3732 -3734 0 - -3732 -3735 0 - -3732 -3736 0 - -3732 -3737 0 - -3733 -3734 0 - -3733 -3735 0 - -3733 -3736 0 - -3733 -3737 0 - -3734 -3735 0 - -3734 -3736 0 - -3734 -3737 0 - -3735 -3736 0 - -3735 -3737 0 - -3736 -3737 0 - -3738 -3739 0 - -3738 -3740 0 - -3738 -3741 0 - -3738 -3742 0 - -3738 -3743 0 - -3738 -3744 0 - -3738 -3745 0 - -3738 -3746 0 - -3738 -3747 0 - -3738 -3748 0 - -3739 -3740 0 - -3739 -3741 0 - -3739 -3742 0 - -3739 -3743 0 - -3739 -3744 0 - -3739 -3745 0 - -3739 -3746 0 - -3739 -3747 0 - -3739 -3748 0 - -3740 -3741 0 - -3740 -3742 0 - -3740 -3743 0 - -3740 -3744 0 - -3740 -3745 0 - -3740 -3746 0 - -3740 -3747 0 - -3740 -3748 0 - -3741 -3742 0 - -3741 -3743 0 - -3741 -3744 0 - -3741 -3745 0 - -3741 -3746 0 - -3741 -3747 0 - -3741 -3748 0 - -3742 -3743 0 - -3742 -3744 0 - -3742 -3745 0 - -3742 -3746 0 - -3742 -3747 0 - -3742 -3748 0 - -3743 -3744 0 - -3743 -3745 0 - -3743 -3746 0 - -3743 -3747 0 - -3743 -3748 0 - -3744 -3745 0 - -3744 -3746 0 - -3744 -3747 0 - -3744 -3748 0 - -3745 -3746 0 - -3745 -3747 0 - -3745 -3748 0 - -3746 -3747 0 - -3746 -3748 0 - -3747 -3748 0 - -3749 -3750 0 - -3749 -3751 0 - -3749 -3752 0 - -3749 -3753 0 - -3749 -3754 0 - -3749 -3755 0 - -3749 -3756 0 - -3749 -3757 0 - -3750 -3751 0 - -3750 -3752 0 - -3750 -3753 0 - -3750 -3754 0 - -3750 -3755 0 - -3750 -3756 0 - -3750 -3757 0 - -3751 -3752 0 - -3751 -3753 0 - -3751 -3754 0 - -3751 -3755 0 - -3751 -3756 0 - -3751 -3757 0 - -3752 -3753 0 - -3752 -3754 0 - -3752 -3755 0 - -3752 -3756 0 - -3752 -3757 0 - -3753 -3754 0 - -3753 -3755 0 - -3753 -3756 0 - -3753 -3757 0 - -3754 -3755 0 - -3754 -3756 0 - -3754 -3757 0 - -3755 -3756 0 - -3755 -3757 0 - -3756 -3757 0 - -3758 -3759 0 - -3758 -3760 0 - -3758 -3761 0 - -3758 -3762 0 - -3758 -3763 0 - -3758 -3764 0 - -3758 -3765 0 - -3758 -3766 0 - -3758 -3767 0 - -3758 -3768 0 - -3759 -3760 0 - -3759 -3761 0 - -3759 -3762 0 - -3759 -3763 0 - -3759 -3764 0 - -3759 -3765 0 - -3759 -3766 0 - -3759 -3767 0 - -3759 -3768 0 - -3760 -3761 0 - -3760 -3762 0 - -3760 -3763 0 - -3760 -3764 0 - -3760 -3765 0 - -3760 -3766 0 - -3760 -3767 0 - -3760 -3768 0 - -3761 -3762 0 - -3761 -3763 0 - -3761 -3764 0 - -3761 -3765 0 - -3761 -3766 0 - -3761 -3767 0 - -3761 -3768 0 - -3762 -3763 0 - -3762 -3764 0 - -3762 -3765 0 - -3762 -3766 0 - -3762 -3767 0 - -3762 -3768 0 - -3763 -3764 0 - -3763 -3765 0 - -3763 -3766 0 - -3763 -3767 0 - -3763 -3768 0 - -3764 -3765 0 - -3764 -3766 0 - -3764 -3767 0 - -3764 -3768 0 - -3765 -3766 0 - -3765 -3767 0 - -3765 -3768 0 - -3766 -3767 0 - -3766 -3768 0 - -3767 -3768 0 - -3769 -3770 0 - -3769 -3771 0 - -3769 -3772 0 - -3769 -3773 0 - -3769 -3774 0 - -3769 -3775 0 - -3769 -3776 0 - -3769 -3777 0 - -3769 -3778 0 - -3770 -3771 0 - -3770 -3772 0 - -3770 -3773 0 - -3770 -3774 0 - -3770 -3775 0 - -3770 -3776 0 - -3770 -3777 0 - -3770 -3778 0 - -3771 -3772 0 - -3771 -3773 0 - -3771 -3774 0 - -3771 -3775 0 - -3771 -3776 0 - -3771 -3777 0 - -3771 -3778 0 - -3772 -3773 0 - -3772 -3774 0 - -3772 -3775 0 - -3772 -3776 0 - -3772 -3777 0 - -3772 -3778 0 - -3773 -3774 0 - -3773 -3775 0 - -3773 -3776 0 - -3773 -3777 0 - -3773 -3778 0 - -3774 -3775 0 - -3774 -3776 0 - -3774 -3777 0 - -3774 -3778 0 - -3775 -3776 0 - -3775 -3777 0 - -3775 -3778 0 - -3776 -3777 0 - -3776 -3778 0 - -3777 -3778 0 - -3779 -3780 0 - -3779 -3781 0 - -3779 -3782 0 - -3779 -3783 0 - -3779 -3784 0 - -3779 -3785 0 - -3780 -3781 0 - -3780 -3782 0 - -3780 -3783 0 - -3780 -3784 0 - -3780 -3785 0 - -3781 -3782 0 - -3781 -3783 0 - -3781 -3784 0 - -3781 -3785 0 - -3782 -3783 0 - -3782 -3784 0 - -3782 -3785 0 - -3783 -3784 0 - -3783 -3785 0 - -3784 -3785 0 - -3786 -3787 0 - -3786 -3788 0 - -3786 -3789 0 - -3786 -3790 0 - -3786 -3791 0 - -3786 -3792 0 - -3786 -3793 0 - -3787 -3788 0 - -3787 -3789 0 - -3787 -3790 0 - -3787 -3791 0 - -3787 -3792 0 - -3787 -3793 0 - -3788 -3789 0 - -3788 -3790 0 - -3788 -3791 0 - -3788 -3792 0 - -3788 -3793 0 - -3789 -3790 0 - -3789 -3791 0 - -3789 -3792 0 - -3789 -3793 0 - -3790 -3791 0 - -3790 -3792 0 - -3790 -3793 0 - -3791 -3792 0 - -3791 -3793 0 - -3792 -3793 0 - -3794 -3795 0 - -3794 -3796 0 - -3794 -3797 0 - -3794 -3798 0 - -3794 -3799 0 - -3794 -3800 0 - -3794 -3801 0 - -3795 -3796 0 - -3795 -3797 0 - -3795 -3798 0 - -3795 -3799 0 - -3795 -3800 0 - -3795 -3801 0 - -3796 -3797 0 - -3796 -3798 0 - -3796 -3799 0 - -3796 -3800 0 - -3796 -3801 0 - -3797 -3798 0 - -3797 -3799 0 - -3797 -3800 0 - -3797 -3801 0 - -3798 -3799 0 - -3798 -3800 0 - -3798 -3801 0 - -3799 -3800 0 - -3799 -3801 0 - -3800 -3801 0 - -3802 -3803 0 - -3802 -3804 0 - -3802 -3805 0 - -3802 -3806 0 - -3802 -3807 0 - -3802 -3808 0 - -3803 -3804 0 - -3803 -3805 0 - -3803 -3806 0 - -3803 -3807 0 - -3803 -3808 0 - -3804 -3805 0 - -3804 -3806 0 - -3804 -3807 0 - -3804 -3808 0 - -3805 -3806 0 - -3805 -3807 0 - -3805 -3808 0 - -3806 -3807 0 - -3806 -3808 0 - -3807 -3808 0 - -3809 -3810 0 - -3809 -3811 0 - -3809 -3812 0 - -3809 -3813 0 - -3809 -3814 0 - -3809 -3815 0 - -3809 -3816 0 - -3809 -3817 0 - -3810 -3811 0 - -3810 -3812 0 - -3810 -3813 0 - -3810 -3814 0 - -3810 -3815 0 - -3810 -3816 0 - -3810 -3817 0 - -3811 -3812 0 - -3811 -3813 0 - -3811 -3814 0 - -3811 -3815 0 - -3811 -3816 0 - -3811 -3817 0 - -3812 -3813 0 - -3812 -3814 0 - -3812 -3815 0 - -3812 -3816 0 - -3812 -3817 0 - -3813 -3814 0 - -3813 -3815 0 - -3813 -3816 0 - -3813 -3817 0 - -3814 -3815 0 - -3814 -3816 0 - -3814 -3817 0 - -3815 -3816 0 - -3815 -3817 0 - -3816 -3817 0 - -3818 -3819 0 - -3818 -3820 0 - -3818 -3821 0 - -3818 -3822 0 - -3818 -3823 0 - -3819 -3820 0 - -3819 -3821 0 - -3819 -3822 0 - -3819 -3823 0 - -3820 -3821 0 - -3820 -3822 0 - -3820 -3823 0 - -3821 -3822 0 - -3821 -3823 0 - -3822 -3823 0 - -3824 -3825 0 - -3824 -3826 0 - -3824 -3827 0 - -3824 -3828 0 - -3824 -3829 0 - -3825 -3826 0 - -3825 -3827 0 - -3825 -3828 0 - -3825 -3829 0 - -3826 -3827 0 - -3826 -3828 0 - -3826 -3829 0 - -3827 -3828 0 - -3827 -3829 0 - -3828 -3829 0 - -3830 -3831 0 - -3830 -3832 0 - -3830 -3833 0 - -3830 -3834 0 - -3830 -3835 0 - -3830 -3836 0 - -3830 -3837 0 - -3830 -3838 0 - -3831 -3832 0 - -3831 -3833 0 - -3831 -3834 0 - -3831 -3835 0 - -3831 -3836 0 - -3831 -3837 0 - -3831 -3838 0 - -3832 -3833 0 - -3832 -3834 0 - -3832 -3835 0 - -3832 -3836 0 - -3832 -3837 0 - -3832 -3838 0 - -3833 -3834 0 - -3833 -3835 0 - -3833 -3836 0 - -3833 -3837 0 - -3833 -3838 0 - -3834 -3835 0 - -3834 -3836 0 - -3834 -3837 0 - -3834 -3838 0 - -3835 -3836 0 - -3835 -3837 0 - -3835 -3838 0 - -3836 -3837 0 - -3836 -3838 0 - -3837 -3838 0 - -3839 -3840 0 - -3839 -3841 0 - -3839 -3842 0 - -3839 -3843 0 - -3839 -3844 0 - -3840 -3841 0 - -3840 -3842 0 - -3840 -3843 0 - -3840 -3844 0 - -3841 -3842 0 - -3841 -3843 0 - -3841 -3844 0 - -3842 -3843 0 - -3842 -3844 0 - -3843 -3844 0 - -3845 -3846 0 - -3845 -3847 0 - -3845 -3848 0 - -3845 -3849 0 - -3845 -3850 0 - -3845 -3851 0 - -3845 -3852 0 - -3846 -3847 0 - -3846 -3848 0 - -3846 -3849 0 - -3846 -3850 0 - -3846 -3851 0 - -3846 -3852 0 - -3847 -3848 0 - -3847 -3849 0 - -3847 -3850 0 - -3847 -3851 0 - -3847 -3852 0 - -3848 -3849 0 - -3848 -3850 0 - -3848 -3851 0 - -3848 -3852 0 - -3849 -3850 0 - -3849 -3851 0 - -3849 -3852 0 - -3850 -3851 0 - -3850 -3852 0 - -3851 -3852 0 - -3853 -3854 0 - -3853 -3855 0 - -3853 -3856 0 - -3853 -3857 0 - -3853 -3858 0 - -3854 -3855 0 - -3854 -3856 0 - -3854 -3857 0 - -3854 -3858 0 - -3855 -3856 0 - -3855 -3857 0 - -3855 -3858 0 - -3856 -3857 0 - -3856 -3858 0 - -3857 -3858 0 - -3859 -3860 0 - -3859 -3861 0 - -3860 -3861 0 - -3862 -3863 0 - -3862 -3864 0 - -3862 -3865 0 - -3862 -3866 0 - -3862 -3867 0 - -3862 -3868 0 - -3862 -3869 0 - -3862 -3870 0 - -3863 -3864 0 - -3863 -3865 0 - -3863 -3866 0 - -3863 -3867 0 - -3863 -3868 0 - -3863 -3869 0 - -3863 -3870 0 - -3864 -3865 0 - -3864 -3866 0 - -3864 -3867 0 - -3864 -3868 0 - -3864 -3869 0 - -3864 -3870 0 - -3865 -3866 0 - -3865 -3867 0 - -3865 -3868 0 - -3865 -3869 0 - -3865 -3870 0 - -3866 -3867 0 - -3866 -3868 0 - -3866 -3869 0 - -3866 -3870 0 - -3867 -3868 0 - -3867 -3869 0 - -3867 -3870 0 - -3868 -3869 0 - -3868 -3870 0 - -3869 -3870 0 - -3871 -3872 0 - -3871 -3873 0 - -3871 -3874 0 - -3871 -3875 0 - -3871 -3876 0 - -3871 -3877 0 - -3872 -3873 0 - -3872 -3874 0 - -3872 -3875 0 - -3872 -3876 0 - -3872 -3877 0 - -3873 -3874 0 - -3873 -3875 0 - -3873 -3876 0 - -3873 -3877 0 - -3874 -3875 0 - -3874 -3876 0 - -3874 -3877 0 - -3875 -3876 0 - -3875 -3877 0 - -3876 -3877 0 - -3878 -3879 0 - -3878 -3880 0 - -3878 -3881 0 - -3878 -3882 0 - -3878 -3883 0 - -3878 -3884 0 - -3879 -3880 0 - -3879 -3881 0 - -3879 -3882 0 - -3879 -3883 0 - -3879 -3884 0 - -3880 -3881 0 - -3880 -3882 0 - -3880 -3883 0 - -3880 -3884 0 - -3881 -3882 0 - -3881 -3883 0 - -3881 -3884 0 - -3882 -3883 0 - -3882 -3884 0 - -3883 -3884 0 - -3885 -3886 0 - -3885 -3887 0 - -3885 -3888 0 - -3885 -3889 0 - -3885 -3890 0 - -3886 -3887 0 - -3886 -3888 0 - -3886 -3889 0 - -3886 -3890 0 - -3887 -3888 0 - -3887 -3889 0 - -3887 -3890 0 - -3888 -3889 0 - -3888 -3890 0 - -3889 -3890 0 - -3891 -3892 0 - -3891 -3893 0 - -3891 -3894 0 - -3891 -3895 0 - -3891 -3896 0 - -3891 -3897 0 - -3891 -3898 0 - -3892 -3893 0 - -3892 -3894 0 - -3892 -3895 0 - -3892 -3896 0 - -3892 -3897 0 - -3892 -3898 0 - -3893 -3894 0 - -3893 -3895 0 - -3893 -3896 0 - -3893 -3897 0 - -3893 -3898 0 - -3894 -3895 0 - -3894 -3896 0 - -3894 -3897 0 - -3894 -3898 0 - -3895 -3896 0 - -3895 -3897 0 - -3895 -3898 0 - -3896 -3897 0 - -3896 -3898 0 - -3897 -3898 0 - -3899 -3900 0 - -3899 -3901 0 - -3899 -3902 0 - -3899 -3903 0 - -3899 -3904 0 - -3900 -3901 0 - -3900 -3902 0 - -3900 -3903 0 - -3900 -3904 0 - -3901 -3902 0 - -3901 -3903 0 - -3901 -3904 0 - -3902 -3903 0 - -3902 -3904 0 - -3903 -3904 0 - -3905 -3906 0 - -3905 -3907 0 - -3905 -3908 0 - -3905 -3909 0 - -3905 -3910 0 - -3906 -3907 0 - -3906 -3908 0 - -3906 -3909 0 - -3906 -3910 0 - -3907 -3908 0 - -3907 -3909 0 - -3907 -3910 0 - -3908 -3909 0 - -3908 -3910 0 - -3909 -3910 0 - -3911 -3912 0 - -3911 -3913 0 - -3911 -3914 0 - -3911 -3915 0 - -3911 -3916 0 - -3911 -3917 0 - -3911 -3918 0 - -3911 -3919 0 - -3911 -3920 0 - -3912 -3913 0 - -3912 -3914 0 - -3912 -3915 0 - -3912 -3916 0 - -3912 -3917 0 - -3912 -3918 0 - -3912 -3919 0 - -3912 -3920 0 - -3913 -3914 0 - -3913 -3915 0 - -3913 -3916 0 - -3913 -3917 0 - -3913 -3918 0 - -3913 -3919 0 - -3913 -3920 0 - -3914 -3915 0 - -3914 -3916 0 - -3914 -3917 0 - -3914 -3918 0 - -3914 -3919 0 - -3914 -3920 0 - -3915 -3916 0 - -3915 -3917 0 - -3915 -3918 0 - -3915 -3919 0 - -3915 -3920 0 - -3916 -3917 0 - -3916 -3918 0 - -3916 -3919 0 - -3916 -3920 0 - -3917 -3918 0 - -3917 -3919 0 - -3917 -3920 0 - -3918 -3919 0 - -3918 -3920 0 - -3919 -3920 0 - -3921 -3922 0 - -3921 -3923 0 - -3921 -3924 0 - -3922 -3923 0 - -3922 -3924 0 - -3923 -3924 0 - -3925 -3926 0 - -3925 -3927 0 - -3925 -3928 0 - -3925 -3929 0 - -3925 -3930 0 - -3925 -3931 0 - -3925 -3932 0 - -3925 -3933 0 - -3926 -3927 0 - -3926 -3928 0 - -3926 -3929 0 - -3926 -3930 0 - -3926 -3931 0 - -3926 -3932 0 - -3926 -3933 0 - -3927 -3928 0 - -3927 -3929 0 - -3927 -3930 0 - -3927 -3931 0 - -3927 -3932 0 - -3927 -3933 0 - -3928 -3929 0 - -3928 -3930 0 - -3928 -3931 0 - -3928 -3932 0 - -3928 -3933 0 - -3929 -3930 0 - -3929 -3931 0 - -3929 -3932 0 - -3929 -3933 0 - -3930 -3931 0 - -3930 -3932 0 - -3930 -3933 0 - -3931 -3932 0 - -3931 -3933 0 - -3932 -3933 0 - -3934 -3935 0 - -3934 -3936 0 - -3934 -3937 0 - -3934 -3938 0 - -3934 -3939 0 - -3934 -3940 0 - -3934 -3941 0 - -3934 -3942 0 - -3934 -3943 0 - -3935 -3936 0 - -3935 -3937 0 - -3935 -3938 0 - -3935 -3939 0 - -3935 -3940 0 - -3935 -3941 0 - -3935 -3942 0 - -3935 -3943 0 - -3936 -3937 0 - -3936 -3938 0 - -3936 -3939 0 - -3936 -3940 0 - -3936 -3941 0 - -3936 -3942 0 - -3936 -3943 0 - -3937 -3938 0 - -3937 -3939 0 - -3937 -3940 0 - -3937 -3941 0 - -3937 -3942 0 - -3937 -3943 0 - -3938 -3939 0 - -3938 -3940 0 - -3938 -3941 0 - -3938 -3942 0 - -3938 -3943 0 - -3939 -3940 0 - -3939 -3941 0 - -3939 -3942 0 - -3939 -3943 0 - -3940 -3941 0 - -3940 -3942 0 - -3940 -3943 0 - -3941 -3942 0 - -3941 -3943 0 - -3942 -3943 0 - -3944 -3945 0 - -3944 -3946 0 - -3944 -3947 0 - -3945 -3946 0 - -3945 -3947 0 - -3946 -3947 0 - -3948 -3949 0 - -3948 -3950 0 - -3948 -3951 0 - -3948 -3952 0 - -3948 -3953 0 - -3948 -3954 0 - -3949 -3950 0 - -3949 -3951 0 - -3949 -3952 0 - -3949 -3953 0 - -3949 -3954 0 - -3950 -3951 0 - -3950 -3952 0 - -3950 -3953 0 - -3950 -3954 0 - -3951 -3952 0 - -3951 -3953 0 - -3951 -3954 0 - -3952 -3953 0 - -3952 -3954 0 - -3953 -3954 0 - -3955 -3956 0 - -3955 -3957 0 - -3955 -3958 0 - -3955 -3959 0 - -3955 -3960 0 - -3955 -3961 0 - -3955 -3962 0 - -3955 -3963 0 - -3956 -3957 0 - -3956 -3958 0 - -3956 -3959 0 - -3956 -3960 0 - -3956 -3961 0 - -3956 -3962 0 - -3956 -3963 0 - -3957 -3958 0 - -3957 -3959 0 - -3957 -3960 0 - -3957 -3961 0 - -3957 -3962 0 - -3957 -3963 0 - -3958 -3959 0 - -3958 -3960 0 - -3958 -3961 0 - -3958 -3962 0 - -3958 -3963 0 - -3959 -3960 0 - -3959 -3961 0 - -3959 -3962 0 - -3959 -3963 0 - -3960 -3961 0 - -3960 -3962 0 - -3960 -3963 0 - -3961 -3962 0 - -3961 -3963 0 - -3962 -3963 0 - -3964 -3965 0 - -3964 -3966 0 - -3964 -3967 0 - -3964 -3968 0 - -3964 -3969 0 - -3965 -3966 0 - -3965 -3967 0 - -3965 -3968 0 - -3965 -3969 0 - -3966 -3967 0 - -3966 -3968 0 - -3966 -3969 0 - -3967 -3968 0 - -3967 -3969 0 - -3968 -3969 0 - -3970 -3971 0 - -3970 -3972 0 - -3970 -3973 0 - -3970 -3974 0 - -3970 -3975 0 - -3970 -3976 0 - -3971 -3972 0 - -3971 -3973 0 - -3971 -3974 0 - -3971 -3975 0 - -3971 -3976 0 - -3972 -3973 0 - -3972 -3974 0 - -3972 -3975 0 - -3972 -3976 0 - -3973 -3974 0 - -3973 -3975 0 - -3973 -3976 0 - -3974 -3975 0 - -3974 -3976 0 - -3975 -3976 0 - -3977 -3978 0 - -3977 -3979 0 - -3977 -3980 0 - -3977 -3981 0 - -3977 -3982 0 - -3977 -3983 0 - -3977 -3984 0 - -3977 -3985 0 - -3978 -3979 0 - -3978 -3980 0 - -3978 -3981 0 - -3978 -3982 0 - -3978 -3983 0 - -3978 -3984 0 - -3978 -3985 0 - -3979 -3980 0 - -3979 -3981 0 - -3979 -3982 0 - -3979 -3983 0 - -3979 -3984 0 - -3979 -3985 0 - -3980 -3981 0 - -3980 -3982 0 - -3980 -3983 0 - -3980 -3984 0 - -3980 -3985 0 - -3981 -3982 0 - -3981 -3983 0 - -3981 -3984 0 - -3981 -3985 0 - -3982 -3983 0 - -3982 -3984 0 - -3982 -3985 0 - -3983 -3984 0 - -3983 -3985 0 - -3984 -3985 0 - -3986 -3987 0 - -3986 -3988 0 - -3986 -3989 0 - -3986 -3990 0 - -3986 -3991 0 - -3986 -3992 0 - -3986 -3993 0 - -3987 -3988 0 - -3987 -3989 0 - -3987 -3990 0 - -3987 -3991 0 - -3987 -3992 0 - -3987 -3993 0 - -3988 -3989 0 - -3988 -3990 0 - -3988 -3991 0 - -3988 -3992 0 - -3988 -3993 0 - -3989 -3990 0 - -3989 -3991 0 - -3989 -3992 0 - -3989 -3993 0 - -3990 -3991 0 - -3990 -3992 0 - -3990 -3993 0 - -3991 -3992 0 - -3991 -3993 0 - -3992 -3993 0 - -3994 -3995 0 - -3994 -3996 0 - -3994 -3997 0 - -3994 -3998 0 - -3994 -3999 0 - -3994 -4000 0 - -3994 -4001 0 - -3995 -3996 0 - -3995 -3997 0 - -3995 -3998 0 - -3995 -3999 0 - -3995 -4000 0 - -3995 -4001 0 - -3996 -3997 0 - -3996 -3998 0 - -3996 -3999 0 - -3996 -4000 0 - -3996 -4001 0 - -3997 -3998 0 - -3997 -3999 0 - -3997 -4000 0 - -3997 -4001 0 - -3998 -3999 0 - -3998 -4000 0 - -3998 -4001 0 - -3999 -4000 0 - -3999 -4001 0 - -4000 -4001 0 - -4002 -4003 0 - -4002 -4004 0 - -4002 -4005 0 - -4002 -4006 0 - -4002 -4007 0 - -4003 -4004 0 - -4003 -4005 0 - -4003 -4006 0 - -4003 -4007 0 - -4004 -4005 0 - -4004 -4006 0 - -4004 -4007 0 - -4005 -4006 0 - -4005 -4007 0 - -4006 -4007 0 - -4008 -4009 0 - -4008 -4010 0 - -4008 -4011 0 - -4008 -4012 0 - -4008 -4013 0 - -4008 -4014 0 - -4009 -4010 0 - -4009 -4011 0 - -4009 -4012 0 - -4009 -4013 0 - -4009 -4014 0 - -4010 -4011 0 - -4010 -4012 0 - -4010 -4013 0 - -4010 -4014 0 - -4011 -4012 0 - -4011 -4013 0 - -4011 -4014 0 - -4012 -4013 0 - -4012 -4014 0 - -4013 -4014 0 - -4015 -4016 0 - -4015 -4017 0 - -4015 -4018 0 - -4015 -4019 0 - -4015 -4020 0 - -4015 -4021 0 - -4015 -4022 0 - -4016 -4017 0 - -4016 -4018 0 - -4016 -4019 0 - -4016 -4020 0 - -4016 -4021 0 - -4016 -4022 0 - -4017 -4018 0 - -4017 -4019 0 - -4017 -4020 0 - -4017 -4021 0 - -4017 -4022 0 - -4018 -4019 0 - -4018 -4020 0 - -4018 -4021 0 - -4018 -4022 0 - -4019 -4020 0 - -4019 -4021 0 - -4019 -4022 0 - -4020 -4021 0 - -4020 -4022 0 - -4021 -4022 0 - -4023 -4024 0 - -4023 -4025 0 - -4023 -4026 0 - -4023 -4027 0 - -4023 -4028 0 - -4024 -4025 0 - -4024 -4026 0 - -4024 -4027 0 - -4024 -4028 0 - -4025 -4026 0 - -4025 -4027 0 - -4025 -4028 0 - -4026 -4027 0 - -4026 -4028 0 - -4027 -4028 0 - -4029 -4030 0 - -4029 -4031 0 - -4029 -4032 0 - -4029 -4033 0 - -4029 -4034 0 - -4030 -4031 0 - -4030 -4032 0 - -4030 -4033 0 - -4030 -4034 0 - -4031 -4032 0 - -4031 -4033 0 - -4031 -4034 0 - -4032 -4033 0 - -4032 -4034 0 - -4033 -4034 0 - -4035 -4036 0 - -4035 -4037 0 - -4036 -4037 0 - -4038 -4039 0 - -4038 -4040 0 - -4038 -4041 0 - -4039 -4040 0 - -4039 -4041 0 - -4040 -4041 0 - -4042 -4043 0 - -4042 -4044 0 - -4042 -4045 0 - -4043 -4044 0 - -4043 -4045 0 - -4044 -4045 0 - -4046 -4047 0 - -4046 -4048 0 - -4046 -4049 0 - -4046 -4050 0 - -4046 -4051 0 - -4047 -4048 0 - -4047 -4049 0 - -4047 -4050 0 - -4047 -4051 0 - -4048 -4049 0 - -4048 -4050 0 - -4048 -4051 0 - -4049 -4050 0 - -4049 -4051 0 - -4050 -4051 0 - -4052 -4053 0 - -4052 -4054 0 - -4052 -4055 0 - -4052 -4056 0 - -4053 -4054 0 - -4053 -4055 0 - -4053 -4056 0 - -4054 -4055 0 - -4054 -4056 0 - -4055 -4056 0 - -4057 -4058 0 - -4057 -4059 0 - -4057 -4060 0 - -4057 -4061 0 - -4058 -4059 0 - -4058 -4060 0 - -4058 -4061 0 - -4059 -4060 0 - -4059 -4061 0 - -4060 -4061 0 - -4062 -4063 0 - -4062 -4064 0 - -4063 -4064 0 - -4065 -4066 0 - -4065 -4067 0 - -4065 -4068 0 - -4065 -4069 0 - -4066 -4067 0 - -4066 -4068 0 - -4066 -4069 0 - -4067 -4068 0 - -4067 -4069 0 - -4068 -4069 0 - -4070 -4071 0 - -4070 -4072 0 - -4070 -4073 0 - -4070 -4074 0 - -4071 -4072 0 - -4071 -4073 0 - -4071 -4074 0 - -4072 -4073 0 - -4072 -4074 0 - -4073 -4074 0 - -4075 -4076 0 - -4075 -4077 0 - -4075 -4078 0 - -4075 -4079 0 - -4075 -4080 0 - -4075 -4081 0 - -4075 -4082 0 - -4076 -4077 0 - -4076 -4078 0 - -4076 -4079 0 - -4076 -4080 0 - -4076 -4081 0 - -4076 -4082 0 - -4077 -4078 0 - -4077 -4079 0 - -4077 -4080 0 - -4077 -4081 0 - -4077 -4082 0 - -4078 -4079 0 - -4078 -4080 0 - -4078 -4081 0 - -4078 -4082 0 - -4079 -4080 0 - -4079 -4081 0 - -4079 -4082 0 - -4080 -4081 0 - -4080 -4082 0 - -4081 -4082 0 - -4083 -4084 0 - -4083 -4085 0 - -4083 -4086 0 - -4083 -4087 0 - -4083 -4088 0 - -4083 -4089 0 - -4083 -4090 0 - -4083 -4091 0 - -4084 -4085 0 - -4084 -4086 0 - -4084 -4087 0 - -4084 -4088 0 - -4084 -4089 0 - -4084 -4090 0 - -4084 -4091 0 - -4085 -4086 0 - -4085 -4087 0 - -4085 -4088 0 - -4085 -4089 0 - -4085 -4090 0 - -4085 -4091 0 - -4086 -4087 0 - -4086 -4088 0 - -4086 -4089 0 - -4086 -4090 0 - -4086 -4091 0 - -4087 -4088 0 - -4087 -4089 0 - -4087 -4090 0 - -4087 -4091 0 - -4088 -4089 0 - -4088 -4090 0 - -4088 -4091 0 - -4089 -4090 0 - -4089 -4091 0 - -4090 -4091 0 - -4092 -4093 0 - -4092 -4094 0 - -4093 -4094 0 - -4095 -4096 0 - -4095 -4097 0 - -4095 -4098 0 - -4095 -4099 0 - -4095 -4100 0 - -4095 -4101 0 - -4095 -4102 0 - -4095 -4103 0 - -4095 -4104 0 - -4095 -4105 0 - -4096 -4097 0 - -4096 -4098 0 - -4096 -4099 0 - -4096 -4100 0 - -4096 -4101 0 - -4096 -4102 0 - -4096 -4103 0 - -4096 -4104 0 - -4096 -4105 0 - -4097 -4098 0 - -4097 -4099 0 - -4097 -4100 0 - -4097 -4101 0 - -4097 -4102 0 - -4097 -4103 0 - -4097 -4104 0 - -4097 -4105 0 - -4098 -4099 0 - -4098 -4100 0 - -4098 -4101 0 - -4098 -4102 0 - -4098 -4103 0 - -4098 -4104 0 - -4098 -4105 0 - -4099 -4100 0 - -4099 -4101 0 - -4099 -4102 0 - -4099 -4103 0 - -4099 -4104 0 - -4099 -4105 0 - -4100 -4101 0 - -4100 -4102 0 - -4100 -4103 0 - -4100 -4104 0 - -4100 -4105 0 - -4101 -4102 0 - -4101 -4103 0 - -4101 -4104 0 - -4101 -4105 0 - -4102 -4103 0 - -4102 -4104 0 - -4102 -4105 0 - -4103 -4104 0 - -4103 -4105 0 - -4104 -4105 0 - -4106 -4107 0 - -4106 -4108 0 - -4107 -4108 0 - -4109 -4110 0 - -4109 -4111 0 - -4109 -4112 0 - -4109 -4113 0 - -4109 -4114 0 - -4109 -4115 0 - -4109 -4116 0 - -4109 -4117 0 - -4110 -4111 0 - -4110 -4112 0 - -4110 -4113 0 - -4110 -4114 0 - -4110 -4115 0 - -4110 -4116 0 - -4110 -4117 0 - -4111 -4112 0 - -4111 -4113 0 - -4111 -4114 0 - -4111 -4115 0 - -4111 -4116 0 - -4111 -4117 0 - -4112 -4113 0 - -4112 -4114 0 - -4112 -4115 0 - -4112 -4116 0 - -4112 -4117 0 - -4113 -4114 0 - -4113 -4115 0 - -4113 -4116 0 - -4113 -4117 0 - -4114 -4115 0 - -4114 -4116 0 - -4114 -4117 0 - -4115 -4116 0 - -4115 -4117 0 - -4116 -4117 0 - -4118 -4119 0 - -4118 -4120 0 - -4118 -4121 0 - -4118 -4122 0 - -4118 -4123 0 - -4118 -4124 0 - -4118 -4125 0 - -4118 -4126 0 - -4118 -4127 0 - -4119 -4120 0 - -4119 -4121 0 - -4119 -4122 0 - -4119 -4123 0 - -4119 -4124 0 - -4119 -4125 0 - -4119 -4126 0 - -4119 -4127 0 - -4120 -4121 0 - -4120 -4122 0 - -4120 -4123 0 - -4120 -4124 0 - -4120 -4125 0 - -4120 -4126 0 - -4120 -4127 0 - -4121 -4122 0 - -4121 -4123 0 - -4121 -4124 0 - -4121 -4125 0 - -4121 -4126 0 - -4121 -4127 0 - -4122 -4123 0 - -4122 -4124 0 - -4122 -4125 0 - -4122 -4126 0 - -4122 -4127 0 - -4123 -4124 0 - -4123 -4125 0 - -4123 -4126 0 - -4123 -4127 0 - -4124 -4125 0 - -4124 -4126 0 - -4124 -4127 0 - -4125 -4126 0 - -4125 -4127 0 - -4126 -4127 0 - -4128 -4129 0 - -4128 -4130 0 - -4128 -4131 0 - -4128 -4132 0 - -4128 -4133 0 - -4129 -4130 0 - -4129 -4131 0 - -4129 -4132 0 - -4129 -4133 0 - -4130 -4131 0 - -4130 -4132 0 - -4130 -4133 0 - -4131 -4132 0 - -4131 -4133 0 - -4132 -4133 0 - -4134 -4135 0 - -4134 -4136 0 - -4134 -4137 0 - -4134 -4138 0 - -4134 -4139 0 - -4135 -4136 0 - -4135 -4137 0 - -4135 -4138 0 - -4135 -4139 0 - -4136 -4137 0 - -4136 -4138 0 - -4136 -4139 0 - -4137 -4138 0 - -4137 -4139 0 - -4138 -4139 0 - -4140 -4141 0 - -4140 -4142 0 - -4140 -4143 0 - -4140 -4144 0 - -4140 -4145 0 - -4140 -4146 0 - -4140 -4147 0 - -4140 -4148 0 - -4141 -4142 0 - -4141 -4143 0 - -4141 -4144 0 - -4141 -4145 0 - -4141 -4146 0 - -4141 -4147 0 - -4141 -4148 0 - -4142 -4143 0 - -4142 -4144 0 - -4142 -4145 0 - -4142 -4146 0 - -4142 -4147 0 - -4142 -4148 0 - -4143 -4144 0 - -4143 -4145 0 - -4143 -4146 0 - -4143 -4147 0 - -4143 -4148 0 - -4144 -4145 0 - -4144 -4146 0 - -4144 -4147 0 - -4144 -4148 0 - -4145 -4146 0 - -4145 -4147 0 - -4145 -4148 0 - -4146 -4147 0 - -4146 -4148 0 - -4147 -4148 0 - -4149 -4150 0 - -4149 -4151 0 - -4149 -4152 0 - -4149 -4153 0 - -4149 -4154 0 - -4149 -4155 0 - -4150 -4151 0 - -4150 -4152 0 - -4150 -4153 0 - -4150 -4154 0 - -4150 -4155 0 - -4151 -4152 0 - -4151 -4153 0 - -4151 -4154 0 - -4151 -4155 0 - -4152 -4153 0 - -4152 -4154 0 - -4152 -4155 0 - -4153 -4154 0 - -4153 -4155 0 - -4154 -4155 0 - -4156 -4157 0 - -4156 -4158 0 - -4156 -4159 0 - -4156 -4160 0 - -4156 -4161 0 - -4156 -4162 0 - -4157 -4158 0 - -4157 -4159 0 - -4157 -4160 0 - -4157 -4161 0 - -4157 -4162 0 - -4158 -4159 0 - -4158 -4160 0 - -4158 -4161 0 - -4158 -4162 0 - -4159 -4160 0 - -4159 -4161 0 - -4159 -4162 0 - -4160 -4161 0 - -4160 -4162 0 - -4161 -4162 0 - -4163 -4164 0 - -4163 -4165 0 - -4163 -4166 0 - -4163 -4167 0 - -4163 -4168 0 - -4163 -4169 0 - -4163 -4170 0 - -4163 -4171 0 - -4164 -4165 0 - -4164 -4166 0 - -4164 -4167 0 - -4164 -4168 0 - -4164 -4169 0 - -4164 -4170 0 - -4164 -4171 0 - -4165 -4166 0 - -4165 -4167 0 - -4165 -4168 0 - -4165 -4169 0 - -4165 -4170 0 - -4165 -4171 0 - -4166 -4167 0 - -4166 -4168 0 - -4166 -4169 0 - -4166 -4170 0 - -4166 -4171 0 - -4167 -4168 0 - -4167 -4169 0 - -4167 -4170 0 - -4167 -4171 0 - -4168 -4169 0 - -4168 -4170 0 - -4168 -4171 0 - -4169 -4170 0 - -4169 -4171 0 - -4170 -4171 0 - -4172 -4173 0 - -4172 -4174 0 - -4172 -4175 0 - -4172 -4176 0 - -4173 -4174 0 - -4173 -4175 0 - -4173 -4176 0 - -4174 -4175 0 - -4174 -4176 0 - -4175 -4176 0 - -4177 -4178 0 - -4177 -4179 0 - -4177 -4180 0 - -4177 -4181 0 - -4177 -4182 0 - -4177 -4183 0 - -4177 -4184 0 - -4177 -4185 0 - -4178 -4179 0 - -4178 -4180 0 - -4178 -4181 0 - -4178 -4182 0 - -4178 -4183 0 - -4178 -4184 0 - -4178 -4185 0 - -4179 -4180 0 - -4179 -4181 0 - -4179 -4182 0 - -4179 -4183 0 - -4179 -4184 0 - -4179 -4185 0 - -4180 -4181 0 - -4180 -4182 0 - -4180 -4183 0 - -4180 -4184 0 - -4180 -4185 0 - -4181 -4182 0 - -4181 -4183 0 - -4181 -4184 0 - -4181 -4185 0 - -4182 -4183 0 - -4182 -4184 0 - -4182 -4185 0 - -4183 -4184 0 - -4183 -4185 0 - -4184 -4185 0 - -4186 -4187 0 - -4186 -4188 0 - -4186 -4189 0 - -4186 -4190 0 - -4186 -4191 0 - -4186 -4192 0 - -4186 -4193 0 - -4187 -4188 0 - -4187 -4189 0 - -4187 -4190 0 - -4187 -4191 0 - -4187 -4192 0 - -4187 -4193 0 - -4188 -4189 0 - -4188 -4190 0 - -4188 -4191 0 - -4188 -4192 0 - -4188 -4193 0 - -4189 -4190 0 - -4189 -4191 0 - -4189 -4192 0 - -4189 -4193 0 - -4190 -4191 0 - -4190 -4192 0 - -4190 -4193 0 - -4191 -4192 0 - -4191 -4193 0 - -4192 -4193 0 - -4194 -4195 0 - -4194 -4196 0 - -4194 -4197 0 - -4194 -4198 0 - -4194 -4199 0 - -4194 -4200 0 - -4195 -4196 0 - -4195 -4197 0 - -4195 -4198 0 - -4195 -4199 0 - -4195 -4200 0 - -4196 -4197 0 - -4196 -4198 0 - -4196 -4199 0 - -4196 -4200 0 - -4197 -4198 0 - -4197 -4199 0 - -4197 -4200 0 - -4198 -4199 0 - -4198 -4200 0 - -4199 -4200 0 - -4201 -4202 0 - -4201 -4203 0 - -4201 -4204 0 - -4201 -4205 0 - -4201 -4206 0 - -4201 -4207 0 - -4202 -4203 0 - -4202 -4204 0 - -4202 -4205 0 - -4202 -4206 0 - -4202 -4207 0 - -4203 -4204 0 - -4203 -4205 0 - -4203 -4206 0 - -4203 -4207 0 - -4204 -4205 0 - -4204 -4206 0 - -4204 -4207 0 - -4205 -4206 0 - -4205 -4207 0 - -4206 -4207 0 - -4208 -4209 0 - -4208 -4210 0 - -4208 -4211 0 - -4208 -4212 0 - -4208 -4213 0 - -4208 -4214 0 - -4208 -4215 0 - -4209 -4210 0 - -4209 -4211 0 - -4209 -4212 0 - -4209 -4213 0 - -4209 -4214 0 - -4209 -4215 0 - -4210 -4211 0 - -4210 -4212 0 - -4210 -4213 0 - -4210 -4214 0 - -4210 -4215 0 - -4211 -4212 0 - -4211 -4213 0 - -4211 -4214 0 - -4211 -4215 0 - -4212 -4213 0 - -4212 -4214 0 - -4212 -4215 0 - -4213 -4214 0 - -4213 -4215 0 - -4214 -4215 0 - -4216 -4217 0 - -4216 -4218 0 - -4216 -4219 0 - -4216 -4220 0 - -4216 -4221 0 - -4216 -4222 0 - -4217 -4218 0 - -4217 -4219 0 - -4217 -4220 0 - -4217 -4221 0 - -4217 -4222 0 - -4218 -4219 0 - -4218 -4220 0 - -4218 -4221 0 - -4218 -4222 0 - -4219 -4220 0 - -4219 -4221 0 - -4219 -4222 0 - -4220 -4221 0 - -4220 -4222 0 - -4221 -4222 0 - -4223 -4224 0 - -4223 -4225 0 - -4223 -4226 0 - -4224 -4225 0 - -4224 -4226 0 - -4225 -4226 0 - -4227 -4228 0 - -4227 -4229 0 - -4227 -4230 0 - -4227 -4231 0 - -4227 -4232 0 - -4227 -4233 0 - -4228 -4229 0 - -4228 -4230 0 - -4228 -4231 0 - -4228 -4232 0 - -4228 -4233 0 - -4229 -4230 0 - -4229 -4231 0 - -4229 -4232 0 - -4229 -4233 0 - -4230 -4231 0 - -4230 -4232 0 - -4230 -4233 0 - -4231 -4232 0 - -4231 -4233 0 - -4232 -4233 0 - -4234 -4235 0 - -4234 -4236 0 - -4234 -4237 0 - -4234 -4238 0 - -4234 -4239 0 - -4235 -4236 0 - -4235 -4237 0 - -4235 -4238 0 - -4235 -4239 0 - -4236 -4237 0 - -4236 -4238 0 - -4236 -4239 0 - -4237 -4238 0 - -4237 -4239 0 - -4238 -4239 0 - -4240 -4241 0 - -4240 -4242 0 - -4240 -4243 0 - -4240 -4244 0 - -4240 -4245 0 - -4240 -4246 0 - -4240 -4247 0 - -4240 -4248 0 - -4240 -4249 0 - -4240 -4250 0 - -4241 -4242 0 - -4241 -4243 0 - -4241 -4244 0 - -4241 -4245 0 - -4241 -4246 0 - -4241 -4247 0 - -4241 -4248 0 - -4241 -4249 0 - -4241 -4250 0 - -4242 -4243 0 - -4242 -4244 0 - -4242 -4245 0 - -4242 -4246 0 - -4242 -4247 0 - -4242 -4248 0 - -4242 -4249 0 - -4242 -4250 0 - -4243 -4244 0 - -4243 -4245 0 - -4243 -4246 0 - -4243 -4247 0 - -4243 -4248 0 - -4243 -4249 0 - -4243 -4250 0 - -4244 -4245 0 - -4244 -4246 0 - -4244 -4247 0 - -4244 -4248 0 - -4244 -4249 0 - -4244 -4250 0 - -4245 -4246 0 - -4245 -4247 0 - -4245 -4248 0 - -4245 -4249 0 - -4245 -4250 0 - -4246 -4247 0 - -4246 -4248 0 - -4246 -4249 0 - -4246 -4250 0 - -4247 -4248 0 - -4247 -4249 0 - -4247 -4250 0 - -4248 -4249 0 - -4248 -4250 0 - -4249 -4250 0 - -4251 -4252 0 - -4251 -4253 0 - -4251 -4254 0 - -4251 -4255 0 - -4251 -4256 0 - -4252 -4253 0 - -4252 -4254 0 - -4252 -4255 0 - -4252 -4256 0 - -4253 -4254 0 - -4253 -4255 0 - -4253 -4256 0 - -4254 -4255 0 - -4254 -4256 0 - -4255 -4256 0 - -4257 -4258 0 - -4257 -4259 0 - -4257 -4260 0 - -4257 -4261 0 - -4257 -4262 0 - -4257 -4263 0 - -4258 -4259 0 - -4258 -4260 0 - -4258 -4261 0 - -4258 -4262 0 - -4258 -4263 0 - -4259 -4260 0 - -4259 -4261 0 - -4259 -4262 0 - -4259 -4263 0 - -4260 -4261 0 - -4260 -4262 0 - -4260 -4263 0 - -4261 -4262 0 - -4261 -4263 0 - -4262 -4263 0 - -4264 -4265 0 - -4264 -4266 0 - -4264 -4267 0 - -4264 -4268 0 - -4264 -4269 0 - -4264 -4270 0 - -4264 -4271 0 - -4265 -4266 0 - -4265 -4267 0 - -4265 -4268 0 - -4265 -4269 0 - -4265 -4270 0 - -4265 -4271 0 - -4266 -4267 0 - -4266 -4268 0 - -4266 -4269 0 - -4266 -4270 0 - -4266 -4271 0 - -4267 -4268 0 - -4267 -4269 0 - -4267 -4270 0 - -4267 -4271 0 - -4268 -4269 0 - -4268 -4270 0 - -4268 -4271 0 - -4269 -4270 0 - -4269 -4271 0 - -4270 -4271 0 - -4272 -4273 0 - -4272 -4274 0 - -4272 -4275 0 - -4273 -4274 0 - -4273 -4275 0 - -4274 -4275 0 - -4276 -4277 0 - -4276 -4278 0 - -4276 -4279 0 - -4276 -4280 0 - -4276 -4281 0 - -4277 -4278 0 - -4277 -4279 0 - -4277 -4280 0 - -4277 -4281 0 - -4278 -4279 0 - -4278 -4280 0 - -4278 -4281 0 - -4279 -4280 0 - -4279 -4281 0 - -4280 -4281 0 - -4282 -4283 0 - -4282 -4284 0 - -4282 -4285 0 - -4283 -4284 0 - -4283 -4285 0 - -4284 -4285 0 - -4286 -4287 0 - -4286 -4288 0 - -4286 -4289 0 - -4286 -4290 0 - -4286 -4291 0 - -4286 -4292 0 - -4286 -4293 0 - -4287 -4288 0 - -4287 -4289 0 - -4287 -4290 0 - -4287 -4291 0 - -4287 -4292 0 - -4287 -4293 0 - -4288 -4289 0 - -4288 -4290 0 - -4288 -4291 0 - -4288 -4292 0 - -4288 -4293 0 - -4289 -4290 0 - -4289 -4291 0 - -4289 -4292 0 - -4289 -4293 0 - -4290 -4291 0 - -4290 -4292 0 - -4290 -4293 0 - -4291 -4292 0 - -4291 -4293 0 - -4292 -4293 0 - -4294 -4295 0 - -4296 -4297 0 - -4296 -4298 0 - -4296 -4299 0 - -4296 -4300 0 - -4296 -4301 0 - -4296 -4302 0 - -4297 -4298 0 - -4297 -4299 0 - -4297 -4300 0 - -4297 -4301 0 - -4297 -4302 0 - -4298 -4299 0 - -4298 -4300 0 - -4298 -4301 0 - -4298 -4302 0 - -4299 -4300 0 - -4299 -4301 0 - -4299 -4302 0 - -4300 -4301 0 - -4300 -4302 0 - -4301 -4302 0 - -4303 -4304 0 - -4303 -4305 0 - -4303 -4306 0 - -4303 -4307 0 - -4304 -4305 0 - -4304 -4306 0 - -4304 -4307 0 - -4305 -4306 0 - -4305 -4307 0 - -4306 -4307 0 - -4308 -4309 0 - -4308 -4310 0 - -4308 -4311 0 - -4309 -4310 0 - -4309 -4311 0 - -4310 -4311 0 - -4312 -4313 0 - -4312 -4314 0 - -4313 -4314 0 - -4315 -4316 0 - -4315 -4317 0 - -4315 -4318 0 - -4315 -4319 0 - -4316 -4317 0 - -4316 -4318 0 - -4316 -4319 0 - -4317 -4318 0 - -4317 -4319 0 - -4318 -4319 0 - -4320 -4321 0 - -4320 -4322 0 - -4320 -4323 0 - -4320 -4324 0 - -4320 -4325 0 - -4320 -4326 0 - -4321 -4322 0 - -4321 -4323 0 - -4321 -4324 0 - -4321 -4325 0 - -4321 -4326 0 - -4322 -4323 0 - -4322 -4324 0 - -4322 -4325 0 - -4322 -4326 0 - -4323 -4324 0 - -4323 -4325 0 - -4323 -4326 0 - -4324 -4325 0 - -4324 -4326 0 - -4325 -4326 0 - -4327 -4328 0 - -4327 -4329 0 - -4327 -4330 0 - -4327 -4331 0 - -4327 -4332 0 - -4327 -4333 0 - -4327 -4334 0 - -4327 -4335 0 - -4327 -4336 0 - -4328 -4329 0 - -4328 -4330 0 - -4328 -4331 0 - -4328 -4332 0 - -4328 -4333 0 - -4328 -4334 0 - -4328 -4335 0 - -4328 -4336 0 - -4329 -4330 0 - -4329 -4331 0 - -4329 -4332 0 - -4329 -4333 0 - -4329 -4334 0 - -4329 -4335 0 - -4329 -4336 0 - -4330 -4331 0 - -4330 -4332 0 - -4330 -4333 0 - -4330 -4334 0 - -4330 -4335 0 - -4330 -4336 0 - -4331 -4332 0 - -4331 -4333 0 - -4331 -4334 0 - -4331 -4335 0 - -4331 -4336 0 - -4332 -4333 0 - -4332 -4334 0 - -4332 -4335 0 - -4332 -4336 0 - -4333 -4334 0 - -4333 -4335 0 - -4333 -4336 0 - -4334 -4335 0 - -4334 -4336 0 - -4335 -4336 0 - -4337 -4338 0 - -4337 -4339 0 - -4337 -4340 0 - -4337 -4341 0 - -4337 -4342 0 - -4337 -4343 0 - -4337 -4344 0 - -4337 -4345 0 - -4338 -4339 0 - -4338 -4340 0 - -4338 -4341 0 - -4338 -4342 0 - -4338 -4343 0 - -4338 -4344 0 - -4338 -4345 0 - -4339 -4340 0 - -4339 -4341 0 - -4339 -4342 0 - -4339 -4343 0 - -4339 -4344 0 - -4339 -4345 0 - -4340 -4341 0 - -4340 -4342 0 - -4340 -4343 0 - -4340 -4344 0 - -4340 -4345 0 - -4341 -4342 0 - -4341 -4343 0 - -4341 -4344 0 - -4341 -4345 0 - -4342 -4343 0 - -4342 -4344 0 - -4342 -4345 0 - -4343 -4344 0 - -4343 -4345 0 - -4344 -4345 0 - -4346 -4347 0 - -4346 -4348 0 - -4346 -4349 0 - -4346 -4350 0 - -4346 -4351 0 - -4347 -4348 0 - -4347 -4349 0 - -4347 -4350 0 - -4347 -4351 0 - -4348 -4349 0 - -4348 -4350 0 - -4348 -4351 0 - -4349 -4350 0 - -4349 -4351 0 - -4350 -4351 0 - -4352 -4353 0 - -4352 -4354 0 - -4352 -4355 0 - -4352 -4356 0 - -4353 -4354 0 - -4353 -4355 0 - -4353 -4356 0 - -4354 -4355 0 - -4354 -4356 0 - -4355 -4356 0 - -4357 -4358 0 - -4357 -4359 0 - -4357 -4360 0 - -4357 -4361 0 - -4357 -4362 0 - -4357 -4363 0 - -4357 -4364 0 - -4357 -4365 0 - -4358 -4359 0 - -4358 -4360 0 - -4358 -4361 0 - -4358 -4362 0 - -4358 -4363 0 - -4358 -4364 0 - -4358 -4365 0 - -4359 -4360 0 - -4359 -4361 0 - -4359 -4362 0 - -4359 -4363 0 - -4359 -4364 0 - -4359 -4365 0 - -4360 -4361 0 - -4360 -4362 0 - -4360 -4363 0 - -4360 -4364 0 - -4360 -4365 0 - -4361 -4362 0 - -4361 -4363 0 - -4361 -4364 0 - -4361 -4365 0 - -4362 -4363 0 - -4362 -4364 0 - -4362 -4365 0 - -4363 -4364 0 - -4363 -4365 0 - -4364 -4365 0 - -4366 -4367 0 - -4366 -4368 0 - -4366 -4369 0 - -4366 -4370 0 - -4366 -4371 0 - -4367 -4368 0 - -4367 -4369 0 - -4367 -4370 0 - -4367 -4371 0 - -4368 -4369 0 - -4368 -4370 0 - -4368 -4371 0 - -4369 -4370 0 - -4369 -4371 0 - -4370 -4371 0 - -4372 -4373 0 - -4372 -4374 0 - -4372 -4375 0 - -4372 -4376 0 - -4372 -4377 0 - -4372 -4378 0 - -4372 -4379 0 - -4372 -4380 0 - -4373 -4374 0 - -4373 -4375 0 - -4373 -4376 0 - -4373 -4377 0 - -4373 -4378 0 - -4373 -4379 0 - -4373 -4380 0 - -4374 -4375 0 - -4374 -4376 0 - -4374 -4377 0 - -4374 -4378 0 - -4374 -4379 0 - -4374 -4380 0 - -4375 -4376 0 - -4375 -4377 0 - -4375 -4378 0 - -4375 -4379 0 - -4375 -4380 0 - -4376 -4377 0 - -4376 -4378 0 - -4376 -4379 0 - -4376 -4380 0 - -4377 -4378 0 - -4377 -4379 0 - -4377 -4380 0 - -4378 -4379 0 - -4378 -4380 0 - -4379 -4380 0 - -4381 -4382 0 - -4381 -4383 0 - -4381 -4384 0 - -4381 -4385 0 - -4382 -4383 0 - -4382 -4384 0 - -4382 -4385 0 - -4383 -4384 0 - -4383 -4385 0 - -4384 -4385 0 - -4386 -4387 0 - -4386 -4388 0 - -4386 -4389 0 - -4386 -4390 0 - -4386 -4391 0 - -4386 -4392 0 - -4386 -4393 0 - -4387 -4388 0 - -4387 -4389 0 - -4387 -4390 0 - -4387 -4391 0 - -4387 -4392 0 - -4387 -4393 0 - -4388 -4389 0 - -4388 -4390 0 - -4388 -4391 0 - -4388 -4392 0 - -4388 -4393 0 - -4389 -4390 0 - -4389 -4391 0 - -4389 -4392 0 - -4389 -4393 0 - -4390 -4391 0 - -4390 -4392 0 - -4390 -4393 0 - -4391 -4392 0 - -4391 -4393 0 - -4392 -4393 0 - -4394 -4395 0 - -4394 -4396 0 - -4394 -4397 0 - -4394 -4398 0 - -4394 -4399 0 - -4394 -4400 0 - -4394 -4401 0 - -4395 -4396 0 - -4395 -4397 0 - -4395 -4398 0 - -4395 -4399 0 - -4395 -4400 0 - -4395 -4401 0 - -4396 -4397 0 - -4396 -4398 0 - -4396 -4399 0 - -4396 -4400 0 - -4396 -4401 0 - -4397 -4398 0 - -4397 -4399 0 - -4397 -4400 0 - -4397 -4401 0 - -4398 -4399 0 - -4398 -4400 0 - -4398 -4401 0 - -4399 -4400 0 - -4399 -4401 0 - -4400 -4401 0 - -4402 -4403 0 - -4402 -4404 0 - -4402 -4405 0 - -4402 -4406 0 - -4402 -4407 0 - -4403 -4404 0 - -4403 -4405 0 - -4403 -4406 0 - -4403 -4407 0 - -4404 -4405 0 - -4404 -4406 0 - -4404 -4407 0 - -4405 -4406 0 - -4405 -4407 0 - -4406 -4407 0 - -4408 -4409 0 - -4408 -4410 0 - -4408 -4411 0 - -4408 -4412 0 - -4408 -4413 0 - -4408 -4414 0 - -4409 -4410 0 - -4409 -4411 0 - -4409 -4412 0 - -4409 -4413 0 - -4409 -4414 0 - -4410 -4411 0 - -4410 -4412 0 - -4410 -4413 0 - -4410 -4414 0 - -4411 -4412 0 - -4411 -4413 0 - -4411 -4414 0 - -4412 -4413 0 - -4412 -4414 0 - -4413 -4414 0 - -831 -903 0 - -831 -1606 0 - -831 -4223 0 - -831 -4308 0 - -903 -1606 0 - -903 -4223 0 - -903 -4308 0 - -1606 -4223 0 - -1606 -4308 0 - -4223 -4308 0 - -441 -832 0 - -441 -904 0 - -441 -1363 0 - -441 -1607 0 - -441 -2007 0 - -441 -2162 0 - -441 -2799 0 - -441 -3300 0 - -441 -3613 0 - -832 -904 0 - -832 -1363 0 - -832 -1607 0 - -832 -2007 0 - -832 -2162 0 - -832 -2799 0 - -832 -3300 0 - -832 -3613 0 - -904 -1363 0 - -904 -1607 0 - -904 -2007 0 - -904 -2162 0 - -904 -2799 0 - -904 -3300 0 - -904 -3613 0 - -1363 -1607 0 - -1363 -2007 0 - -1363 -2162 0 - -1363 -2799 0 - -1363 -3300 0 - -1363 -3613 0 - -1607 -2007 0 - -1607 -2162 0 - -1607 -2799 0 - -1607 -3300 0 - -1607 -3613 0 - -2007 -2162 0 - -2007 -2799 0 - -2007 -3300 0 - -2007 -3613 0 - -2162 -2799 0 - -2162 -3300 0 - -2162 -3613 0 - -2799 -3300 0 - -2799 -3613 0 - -3300 -3613 0 - -905 -1364 0 - -905 -2008 0 - -905 -3301 0 - -905 -4309 0 - -1364 -2008 0 - -1364 -3301 0 - -1364 -4309 0 - -2008 -3301 0 - -2008 -4309 0 - -3301 -4309 0 - -442 -906 0 - -442 -1365 0 - -442 -1608 0 - -442 -2009 0 - -442 -2800 0 - -442 -3614 0 - -906 -1365 0 - -906 -1608 0 - -906 -2009 0 - -906 -2800 0 - -906 -3614 0 - -1365 -1608 0 - -1365 -2009 0 - -1365 -2800 0 - -1365 -3614 0 - -1608 -2009 0 - -1608 -2800 0 - -1608 -3614 0 - -2009 -2800 0 - -2009 -3614 0 - -2800 -3614 0 - -443 -833 0 - -443 -907 0 - -443 -1609 0 - -443 -2163 0 - -443 -3615 0 - -833 -907 0 - -833 -1609 0 - -833 -2163 0 - -833 -3615 0 - -907 -1609 0 - -907 -2163 0 - -907 -3615 0 - -1609 -2163 0 - -1609 -3615 0 - -2163 -3615 0 - -444 -908 0 - -444 -2010 0 - -444 -2164 0 - -444 -3302 0 - -444 -3616 0 - -908 -2010 0 - -908 -2164 0 - -908 -3302 0 - -908 -3616 0 - -2010 -2164 0 - -2010 -3302 0 - -2010 -3616 0 - -2164 -3302 0 - -2164 -3616 0 - -3302 -3616 0 - -445 -834 0 - -445 -1207 0 - -445 -1366 0 - -445 -1610 0 - -445 -2165 0 - -445 -3303 0 - -445 -3617 0 - -834 -1207 0 - -834 -1366 0 - -834 -1610 0 - -834 -2165 0 - -834 -3303 0 - -834 -3617 0 - -1207 -1366 0 - -1207 -1610 0 - -1207 -2165 0 - -1207 -3303 0 - -1207 -3617 0 - -1366 -1610 0 - -1366 -2165 0 - -1366 -3303 0 - -1366 -3617 0 - -1610 -2165 0 - -1610 -3303 0 - -1610 -3617 0 - -2165 -3303 0 - -2165 -3617 0 - -3303 -3617 0 - -909 -1611 0 - -909 -2011 0 - -909 -2801 0 - -909 -3304 0 - -1611 -2011 0 - -1611 -2801 0 - -1611 -3304 0 - -2011 -2801 0 - -2011 -3304 0 - -2801 -3304 0 - -1208 -1367 0 - -1208 -2012 0 - -1208 -2802 0 - -1208 -4310 0 - -1367 -2012 0 - -1367 -2802 0 - -1367 -4310 0 - -2012 -2802 0 - -2012 -4310 0 - -2802 -4310 0 - -835 -1209 0 - -835 -2803 0 - -835 -3305 0 - -835 -3618 0 - -835 -4224 0 - -1209 -2803 0 - -1209 -3305 0 - -1209 -3618 0 - -1209 -4224 0 - -2803 -3305 0 - -2803 -3618 0 - -2803 -4224 0 - -3305 -3618 0 - -3305 -4224 0 - -3618 -4224 0 - -446 -1368 0 - -446 -1612 0 - -446 -2166 0 - -446 -3619 0 - -446 -4225 0 - -1368 -1612 0 - -1368 -2166 0 - -1368 -3619 0 - -1368 -4225 0 - -1612 -2166 0 - -1612 -3619 0 - -1612 -4225 0 - -2166 -3619 0 - -2166 -4225 0 - -3619 -4225 0 - -910 -1210 0 - -910 -2013 0 - -910 -2167 0 - -910 -2804 0 - -1210 -2013 0 - -1210 -2167 0 - -1210 -2804 0 - -2013 -2167 0 - -2013 -2804 0 - -2167 -2804 0 - -836 -911 0 - -836 -1211 0 - -836 -1369 0 - -836 -2014 0 - -836 -3306 0 - -836 -4226 0 - -836 -4311 0 - -911 -1211 0 - -911 -1369 0 - -911 -2014 0 - -911 -3306 0 - -911 -4226 0 - -911 -4311 0 - -1211 -1369 0 - -1211 -2014 0 - -1211 -3306 0 - -1211 -4226 0 - -1211 -4311 0 - -1369 -2014 0 - -1369 -3306 0 - -1369 -4226 0 - -1369 -4311 0 - -2014 -3306 0 - -2014 -4226 0 - -2014 -4311 0 - -3306 -4226 0 - -3306 -4311 0 - -4226 -4311 0 - -912 -1748 0 - -912 -2168 0 - -912 -2424 0 - -912 -2805 0 - -912 -3620 0 - -912 -3802 0 - -1748 -2168 0 - -1748 -2424 0 - -1748 -2805 0 - -1748 -3620 0 - -1748 -3802 0 - -2168 -2424 0 - -2168 -2805 0 - -2168 -3620 0 - -2168 -3802 0 - -2424 -2805 0 - -2424 -3620 0 - -2424 -3802 0 - -2805 -3620 0 - -2805 -3802 0 - -3620 -3802 0 - -913 -1749 0 - -913 -2685 0 - -913 -2806 0 - -913 -2917 0 - -913 -3008 0 - -913 -3621 0 - -913 -3803 0 - -913 -3905 0 - -1749 -2685 0 - -1749 -2806 0 - -1749 -2917 0 - -1749 -3008 0 - -1749 -3621 0 - -1749 -3803 0 - -1749 -3905 0 - -2685 -2806 0 - -2685 -2917 0 - -2685 -3008 0 - -2685 -3621 0 - -2685 -3803 0 - -2685 -3905 0 - -2806 -2917 0 - -2806 -3008 0 - -2806 -3621 0 - -2806 -3803 0 - -2806 -3905 0 - -2917 -3008 0 - -2917 -3621 0 - -2917 -3803 0 - -2917 -3905 0 - -3008 -3621 0 - -3008 -3803 0 - -3008 -3905 0 - -3621 -3803 0 - -3621 -3905 0 - -3803 -3905 0 - -1212 -1750 0 - -1212 -2169 0 - -1212 -2425 0 - -1212 -2686 0 - -1212 -3009 0 - -1212 -3906 0 - -1750 -2169 0 - -1750 -2425 0 - -1750 -2686 0 - -1750 -3009 0 - -1750 -3906 0 - -2169 -2425 0 - -2169 -2686 0 - -2169 -3009 0 - -2169 -3906 0 - -2425 -2686 0 - -2425 -3009 0 - -2425 -3906 0 - -2686 -3009 0 - -2686 -3906 0 - -3009 -3906 0 - -914 -1751 0 - -914 -2170 0 - -914 -2918 0 - -914 -3010 0 - -914 -3622 0 - -914 -3907 0 - -914 -4023 0 - -1751 -2170 0 - -1751 -2918 0 - -1751 -3010 0 - -1751 -3622 0 - -1751 -3907 0 - -1751 -4023 0 - -2170 -2918 0 - -2170 -3010 0 - -2170 -3622 0 - -2170 -3907 0 - -2170 -4023 0 - -2918 -3010 0 - -2918 -3622 0 - -2918 -3907 0 - -2918 -4023 0 - -3010 -3622 0 - -3010 -3907 0 - -3010 -4023 0 - -3622 -3907 0 - -3622 -4023 0 - -3907 -4023 0 - -2171 -2426 0 - -2171 -2807 0 - -2171 -2919 0 - -2171 -3623 0 - -2171 -4024 0 - -2426 -2807 0 - -2426 -2919 0 - -2426 -3623 0 - -2426 -4024 0 - -2807 -2919 0 - -2807 -3623 0 - -2807 -4024 0 - -2919 -3623 0 - -2919 -4024 0 - -3623 -4024 0 - -691 -915 0 - -691 -2172 0 - -691 -3624 0 - -691 -4025 0 - -915 -2172 0 - -915 -3624 0 - -915 -4025 0 - -2172 -3624 0 - -2172 -4025 0 - -3624 -4025 0 - -1289 -2687 0 - -1289 -3011 0 - -1289 -3625 0 - -1289 -3804 0 - -1289 -4312 0 - -2687 -3011 0 - -2687 -3625 0 - -2687 -3804 0 - -2687 -4312 0 - -3011 -3625 0 - -3011 -3804 0 - -3011 -4312 0 - -3625 -3804 0 - -3625 -4312 0 - -3804 -4312 0 - -1213 -2173 0 - -1213 -3626 0 - -1213 -3805 0 - -1213 -3908 0 - -2173 -3626 0 - -2173 -3805 0 - -2173 -3908 0 - -3626 -3805 0 - -3626 -3908 0 - -3805 -3908 0 - -916 -2688 0 - -916 -2808 0 - -916 -2920 0 - -916 -3012 0 - -916 -3806 0 - -2688 -2808 0 - -2688 -2920 0 - -2688 -3012 0 - -2688 -3806 0 - -2808 -2920 0 - -2808 -3012 0 - -2808 -3806 0 - -2920 -3012 0 - -2920 -3806 0 - -3012 -3806 0 - -692 -917 0 - -692 -1290 0 - -692 -2689 0 - -692 -3013 0 - -917 -1290 0 - -917 -2689 0 - -917 -3013 0 - -1290 -2689 0 - -1290 -3013 0 - -2689 -3013 0 - -918 -1214 0 - -918 -1291 0 - -918 -2174 0 - -918 -2427 0 - -918 -2690 0 - -918 -2921 0 - -918 -3627 0 - -918 -4026 0 - -918 -4313 0 - -1214 -1291 0 - -1214 -2174 0 - -1214 -2427 0 - -1214 -2690 0 - -1214 -2921 0 - -1214 -3627 0 - -1214 -4026 0 - -1214 -4313 0 - -1291 -2174 0 - -1291 -2427 0 - -1291 -2690 0 - -1291 -2921 0 - -1291 -3627 0 - -1291 -4026 0 - -1291 -4313 0 - -2174 -2427 0 - -2174 -2690 0 - -2174 -2921 0 - -2174 -3627 0 - -2174 -4026 0 - -2174 -4313 0 - -2427 -2690 0 - -2427 -2921 0 - -2427 -3627 0 - -2427 -4026 0 - -2427 -4313 0 - -2690 -2921 0 - -2690 -3627 0 - -2690 -4026 0 - -2690 -4313 0 - -2921 -3627 0 - -2921 -4026 0 - -2921 -4313 0 - -3627 -4026 0 - -3627 -4313 0 - -4026 -4313 0 - -1292 -2809 0 - -1292 -2922 0 - -1292 -3014 0 - -1292 -3807 0 - -1292 -3909 0 - -1292 -4314 0 - -2809 -2922 0 - -2809 -3014 0 - -2809 -3807 0 - -2809 -3909 0 - -2809 -4314 0 - -2922 -3014 0 - -2922 -3807 0 - -2922 -3909 0 - -2922 -4314 0 - -3014 -3807 0 - -3014 -3909 0 - -3014 -4314 0 - -3807 -3909 0 - -3807 -4314 0 - -3909 -4314 0 - -693 -1215 0 - -693 -1293 0 - -693 -2923 0 - -693 -3808 0 - -693 -4027 0 - -1215 -1293 0 - -1215 -2923 0 - -1215 -3808 0 - -1215 -4027 0 - -1293 -2923 0 - -1293 -3808 0 - -1293 -4027 0 - -2923 -3808 0 - -2923 -4027 0 - -3808 -4027 0 - -694 -1752 0 - -694 -2428 0 - -694 -2691 0 - -694 -2924 0 - -694 -3628 0 - -1752 -2428 0 - -1752 -2691 0 - -1752 -2924 0 - -1752 -3628 0 - -2428 -2691 0 - -2428 -2924 0 - -2428 -3628 0 - -2691 -2924 0 - -2691 -3628 0 - -2924 -3628 0 - -1294 -2175 0 - -1294 -3015 0 - -1294 -3629 0 - -1294 -3910 0 - -1294 -4028 0 - -2175 -3015 0 - -2175 -3629 0 - -2175 -3910 0 - -2175 -4028 0 - -3015 -3629 0 - -3015 -3910 0 - -3015 -4028 0 - -3629 -3910 0 - -3629 -4028 0 - -3910 -4028 0 - -1295 -2176 0 - -1295 -2429 0 - -1295 -2692 0 - -1295 -3016 0 - -1295 -3630 0 - -2176 -2429 0 - -2176 -2692 0 - -2176 -3016 0 - -2176 -3630 0 - -2429 -2692 0 - -2429 -3016 0 - -2429 -3630 0 - -2692 -3016 0 - -2692 -3630 0 - -3016 -3630 0 - -1 -695 0 - -1 -1046 0 - -1 -1370 0 - -1 -2015 0 - -1 -2334 0 - -1 -2693 0 - -1 -2810 0 - -1 -3017 0 - -1 -3454 0 - -1 -4029 0 - -1 -4095 0 - -695 -1046 0 - -695 -1370 0 - -695 -2015 0 - -695 -2334 0 - -695 -2693 0 - -695 -2810 0 - -695 -3017 0 - -695 -3454 0 - -695 -4029 0 - -695 -4095 0 - -1046 -1370 0 - -1046 -2015 0 - -1046 -2334 0 - -1046 -2693 0 - -1046 -2810 0 - -1046 -3017 0 - -1046 -3454 0 - -1046 -4029 0 - -1046 -4095 0 - -1370 -2015 0 - -1370 -2334 0 - -1370 -2693 0 - -1370 -2810 0 - -1370 -3017 0 - -1370 -3454 0 - -1370 -4029 0 - -1370 -4095 0 - -2015 -2334 0 - -2015 -2693 0 - -2015 -2810 0 - -2015 -3017 0 - -2015 -3454 0 - -2015 -4029 0 - -2015 -4095 0 - -2334 -2693 0 - -2334 -2810 0 - -2334 -3017 0 - -2334 -3454 0 - -2334 -4029 0 - -2334 -4095 0 - -2693 -2810 0 - -2693 -3017 0 - -2693 -3454 0 - -2693 -4029 0 - -2693 -4095 0 - -2810 -3017 0 - -2810 -3454 0 - -2810 -4029 0 - -2810 -4095 0 - -3017 -3454 0 - -3017 -4029 0 - -3017 -4095 0 - -3454 -4029 0 - -3454 -4095 0 - -4029 -4095 0 - -95 -572 0 - -95 -1047 0 - -95 -1371 0 - -95 -1613 0 - -95 -2016 0 - -95 -2177 0 - -95 -2335 0 - -95 -2811 0 - -95 -3455 0 - -95 -4096 0 - -572 -1047 0 - -572 -1371 0 - -572 -1613 0 - -572 -2016 0 - -572 -2177 0 - -572 -2335 0 - -572 -2811 0 - -572 -3455 0 - -572 -4096 0 - -1047 -1371 0 - -1047 -1613 0 - -1047 -2016 0 - -1047 -2177 0 - -1047 -2335 0 - -1047 -2811 0 - -1047 -3455 0 - -1047 -4096 0 - -1371 -1613 0 - -1371 -2016 0 - -1371 -2177 0 - -1371 -2335 0 - -1371 -2811 0 - -1371 -3455 0 - -1371 -4096 0 - -1613 -2016 0 - -1613 -2177 0 - -1613 -2335 0 - -1613 -2811 0 - -1613 -3455 0 - -1613 -4096 0 - -2016 -2177 0 - -2016 -2335 0 - -2016 -2811 0 - -2016 -3455 0 - -2016 -4096 0 - -2177 -2335 0 - -2177 -2811 0 - -2177 -3455 0 - -2177 -4096 0 - -2335 -2811 0 - -2335 -3455 0 - -2335 -4096 0 - -2811 -3455 0 - -2811 -4096 0 - -3455 -4096 0 - -2 -573 0 - -2 -1048 0 - -2 -1372 0 - -2 -1614 0 - -2 -1871 0 - -2 -2017 0 - -2 -2694 0 - -2 -2812 0 - -2 -3018 0 - -2 -3456 0 - -573 -1048 0 - -573 -1372 0 - -573 -1614 0 - -573 -1871 0 - -573 -2017 0 - -573 -2694 0 - -573 -2812 0 - -573 -3018 0 - -573 -3456 0 - -1048 -1372 0 - -1048 -1614 0 - -1048 -1871 0 - -1048 -2017 0 - -1048 -2694 0 - -1048 -2812 0 - -1048 -3018 0 - -1048 -3456 0 - -1372 -1614 0 - -1372 -1871 0 - -1372 -2017 0 - -1372 -2694 0 - -1372 -2812 0 - -1372 -3018 0 - -1372 -3456 0 - -1614 -1871 0 - -1614 -2017 0 - -1614 -2694 0 - -1614 -2812 0 - -1614 -3018 0 - -1614 -3456 0 - -1871 -2017 0 - -1871 -2694 0 - -1871 -2812 0 - -1871 -3018 0 - -1871 -3456 0 - -2017 -2694 0 - -2017 -2812 0 - -2017 -3018 0 - -2017 -3456 0 - -2694 -2812 0 - -2694 -3018 0 - -2694 -3456 0 - -2812 -3018 0 - -2812 -3456 0 - -3018 -3456 0 - -3 -344 0 - -3 -1049 0 - -3 -1555 0 - -3 -1615 0 - -3 -2018 0 - -3 -2178 0 - -3 -2695 0 - -3 -3019 0 - -3 -3212 0 - -3 -3457 0 - -344 -1049 0 - -344 -1555 0 - -344 -1615 0 - -344 -2018 0 - -344 -2178 0 - -344 -2695 0 - -344 -3019 0 - -344 -3212 0 - -344 -3457 0 - -1049 -1555 0 - -1049 -1615 0 - -1049 -2018 0 - -1049 -2178 0 - -1049 -2695 0 - -1049 -3019 0 - -1049 -3212 0 - -1049 -3457 0 - -1555 -1615 0 - -1555 -2018 0 - -1555 -2178 0 - -1555 -2695 0 - -1555 -3019 0 - -1555 -3212 0 - -1555 -3457 0 - -1615 -2018 0 - -1615 -2178 0 - -1615 -2695 0 - -1615 -3019 0 - -1615 -3212 0 - -1615 -3457 0 - -2018 -2178 0 - -2018 -2695 0 - -2018 -3019 0 - -2018 -3212 0 - -2018 -3457 0 - -2178 -2695 0 - -2178 -3019 0 - -2178 -3212 0 - -2178 -3457 0 - -2695 -3019 0 - -2695 -3212 0 - -2695 -3457 0 - -3019 -3212 0 - -3019 -3457 0 - -3212 -3457 0 - -96 -1050 0 - -96 -1616 0 - -96 -1872 0 - -96 -2179 0 - -96 -3020 0 - -96 -3213 0 - -96 -3458 0 - -96 -4030 0 - -1050 -1616 0 - -1050 -1872 0 - -1050 -2179 0 - -1050 -3020 0 - -1050 -3213 0 - -1050 -3458 0 - -1050 -4030 0 - -1616 -1872 0 - -1616 -2179 0 - -1616 -3020 0 - -1616 -3213 0 - -1616 -3458 0 - -1616 -4030 0 - -1872 -2179 0 - -1872 -3020 0 - -1872 -3213 0 - -1872 -3458 0 - -1872 -4030 0 - -2179 -3020 0 - -2179 -3213 0 - -2179 -3458 0 - -2179 -4030 0 - -3020 -3213 0 - -3020 -3458 0 - -3020 -4030 0 - -3213 -3458 0 - -3213 -4030 0 - -3458 -4030 0 - -696 -1296 0 - -696 -1373 0 - -696 -1556 0 - -696 -1617 0 - -696 -2266 0 - -696 -2336 0 - -696 -3459 0 - -696 -4097 0 - -1296 -1373 0 - -1296 -1556 0 - -1296 -1617 0 - -1296 -2266 0 - -1296 -2336 0 - -1296 -3459 0 - -1296 -4097 0 - -1373 -1556 0 - -1373 -1617 0 - -1373 -2266 0 - -1373 -2336 0 - -1373 -3459 0 - -1373 -4097 0 - -1556 -1617 0 - -1556 -2266 0 - -1556 -2336 0 - -1556 -3459 0 - -1556 -4097 0 - -1617 -2266 0 - -1617 -2336 0 - -1617 -3459 0 - -1617 -4097 0 - -2266 -2336 0 - -2266 -3459 0 - -2266 -4097 0 - -2336 -3459 0 - -2336 -4097 0 - -3459 -4097 0 - -97 -345 0 - -97 -697 0 - -97 -1051 0 - -97 -1557 0 - -97 -1873 0 - -97 -2019 0 - -97 -2180 0 - -97 -4031 0 - -97 -4098 0 - -345 -697 0 - -345 -1051 0 - -345 -1557 0 - -345 -1873 0 - -345 -2019 0 - -345 -2180 0 - -345 -4031 0 - -345 -4098 0 - -697 -1051 0 - -697 -1557 0 - -697 -1873 0 - -697 -2019 0 - -697 -2180 0 - -697 -4031 0 - -697 -4098 0 - -1051 -1557 0 - -1051 -1873 0 - -1051 -2019 0 - -1051 -2180 0 - -1051 -4031 0 - -1051 -4098 0 - -1557 -1873 0 - -1557 -2019 0 - -1557 -2180 0 - -1557 -4031 0 - -1557 -4098 0 - -1873 -2019 0 - -1873 -2180 0 - -1873 -4031 0 - -1873 -4098 0 - -2019 -2180 0 - -2019 -4031 0 - -2019 -4098 0 - -2180 -4031 0 - -2180 -4098 0 - -4031 -4098 0 - -346 -698 0 - -346 -1618 0 - -346 -2020 0 - -346 -3021 0 - -346 -3214 0 - -698 -1618 0 - -698 -2020 0 - -698 -3021 0 - -698 -3214 0 - -1618 -2020 0 - -1618 -3021 0 - -1618 -3214 0 - -2020 -3021 0 - -2020 -3214 0 - -3021 -3214 0 - -4 -98 0 - -4 -1297 0 - -4 -1374 0 - -4 -1558 0 - -4 -2337 0 - -4 -2696 0 - -4 -3022 0 - -4 -4099 0 - -98 -1297 0 - -98 -1374 0 - -98 -1558 0 - -98 -2337 0 - -98 -2696 0 - -98 -3022 0 - -98 -4099 0 - -1297 -1374 0 - -1297 -1558 0 - -1297 -2337 0 - -1297 -2696 0 - -1297 -3022 0 - -1297 -4099 0 - -1374 -1558 0 - -1374 -2337 0 - -1374 -2696 0 - -1374 -3022 0 - -1374 -4099 0 - -1558 -2337 0 - -1558 -2696 0 - -1558 -3022 0 - -1558 -4099 0 - -2337 -2696 0 - -2337 -3022 0 - -2337 -4099 0 - -2696 -3022 0 - -2696 -4099 0 - -3022 -4099 0 - -5 -574 0 - -5 -699 0 - -5 -1052 0 - -5 -1375 0 - -5 -1559 0 - -5 -2021 0 - -5 -2338 0 - -5 -2813 0 - -5 -4227 0 - -574 -699 0 - -574 -1052 0 - -574 -1375 0 - -574 -1559 0 - -574 -2021 0 - -574 -2338 0 - -574 -2813 0 - -574 -4227 0 - -699 -1052 0 - -699 -1375 0 - -699 -1559 0 - -699 -2021 0 - -699 -2338 0 - -699 -2813 0 - -699 -4227 0 - -1052 -1375 0 - -1052 -1559 0 - -1052 -2021 0 - -1052 -2338 0 - -1052 -2813 0 - -1052 -4227 0 - -1375 -1559 0 - -1375 -2021 0 - -1375 -2338 0 - -1375 -2813 0 - -1375 -4227 0 - -1559 -2021 0 - -1559 -2338 0 - -1559 -2813 0 - -1559 -4227 0 - -2021 -2338 0 - -2021 -2813 0 - -2021 -4227 0 - -2338 -2813 0 - -2338 -4227 0 - -2813 -4227 0 - -99 -1619 0 - -99 -2022 0 - -99 -2267 0 - -99 -2697 0 - -99 -2814 0 - -99 -3023 0 - -99 -3215 0 - -99 -4100 0 - -1619 -2022 0 - -1619 -2267 0 - -1619 -2697 0 - -1619 -2814 0 - -1619 -3023 0 - -1619 -3215 0 - -1619 -4100 0 - -2022 -2267 0 - -2022 -2697 0 - -2022 -2814 0 - -2022 -3023 0 - -2022 -3215 0 - -2022 -4100 0 - -2267 -2697 0 - -2267 -2814 0 - -2267 -3023 0 - -2267 -3215 0 - -2267 -4100 0 - -2697 -2814 0 - -2697 -3023 0 - -2697 -3215 0 - -2697 -4100 0 - -2814 -3023 0 - -2814 -3215 0 - -2814 -4100 0 - -3023 -3215 0 - -3023 -4100 0 - -3215 -4100 0 - -6 -100 0 - -6 -347 0 - -6 -700 0 - -6 -1053 0 - -6 -1376 0 - -6 -2023 0 - -6 -2815 0 - -6 -3024 0 - -6 -3216 0 - -6 -4101 0 - -100 -347 0 - -100 -700 0 - -100 -1053 0 - -100 -1376 0 - -100 -2023 0 - -100 -2815 0 - -100 -3024 0 - -100 -3216 0 - -100 -4101 0 - -347 -700 0 - -347 -1053 0 - -347 -1376 0 - -347 -2023 0 - -347 -2815 0 - -347 -3024 0 - -347 -3216 0 - -347 -4101 0 - -700 -1053 0 - -700 -1376 0 - -700 -2023 0 - -700 -2815 0 - -700 -3024 0 - -700 -3216 0 - -700 -4101 0 - -1053 -1376 0 - -1053 -2023 0 - -1053 -2815 0 - -1053 -3024 0 - -1053 -3216 0 - -1053 -4101 0 - -1376 -2023 0 - -1376 -2815 0 - -1376 -3024 0 - -1376 -3216 0 - -1376 -4101 0 - -2023 -2815 0 - -2023 -3024 0 - -2023 -3216 0 - -2023 -4101 0 - -2815 -3024 0 - -2815 -3216 0 - -2815 -4101 0 - -3024 -3216 0 - -3024 -4101 0 - -3216 -4101 0 - -101 -348 0 - -101 -575 0 - -101 -701 0 - -101 -1054 0 - -101 -1298 0 - -101 -1560 0 - -101 -1874 0 - -101 -2339 0 - -101 -2698 0 - -101 -3025 0 - -101 -3217 0 - -101 -3460 0 - -348 -575 0 - -348 -701 0 - -348 -1054 0 - -348 -1298 0 - -348 -1560 0 - -348 -1874 0 - -348 -2339 0 - -348 -2698 0 - -348 -3025 0 - -348 -3217 0 - -348 -3460 0 - -575 -701 0 - -575 -1054 0 - -575 -1298 0 - -575 -1560 0 - -575 -1874 0 - -575 -2339 0 - -575 -2698 0 - -575 -3025 0 - -575 -3217 0 - -575 -3460 0 - -701 -1054 0 - -701 -1298 0 - -701 -1560 0 - -701 -1874 0 - -701 -2339 0 - -701 -2698 0 - -701 -3025 0 - -701 -3217 0 - -701 -3460 0 - -1054 -1298 0 - -1054 -1560 0 - -1054 -1874 0 - -1054 -2339 0 - -1054 -2698 0 - -1054 -3025 0 - -1054 -3217 0 - -1054 -3460 0 - -1298 -1560 0 - -1298 -1874 0 - -1298 -2339 0 - -1298 -2698 0 - -1298 -3025 0 - -1298 -3217 0 - -1298 -3460 0 - -1560 -1874 0 - -1560 -2339 0 - -1560 -2698 0 - -1560 -3025 0 - -1560 -3217 0 - -1560 -3460 0 - -1874 -2339 0 - -1874 -2698 0 - -1874 -3025 0 - -1874 -3217 0 - -1874 -3460 0 - -2339 -2698 0 - -2339 -3025 0 - -2339 -3217 0 - -2339 -3460 0 - -2698 -3025 0 - -2698 -3217 0 - -2698 -3460 0 - -3025 -3217 0 - -3025 -3460 0 - -3217 -3460 0 - -102 -576 0 - -102 -1299 0 - -102 -1377 0 - -102 -1561 0 - -102 -2181 0 - -102 -3026 0 - -102 -3218 0 - -102 -4032 0 - -576 -1299 0 - -576 -1377 0 - -576 -1561 0 - -576 -2181 0 - -576 -3026 0 - -576 -3218 0 - -576 -4032 0 - -1299 -1377 0 - -1299 -1561 0 - -1299 -2181 0 - -1299 -3026 0 - -1299 -3218 0 - -1299 -4032 0 - -1377 -1561 0 - -1377 -2181 0 - -1377 -3026 0 - -1377 -3218 0 - -1377 -4032 0 - -1561 -2181 0 - -1561 -3026 0 - -1561 -3218 0 - -1561 -4032 0 - -2181 -3026 0 - -2181 -3218 0 - -2181 -4032 0 - -3026 -3218 0 - -3026 -4032 0 - -3218 -4032 0 - -7 -577 0 - -7 -702 0 - -7 -1620 0 - -7 -1875 0 - -7 -2340 0 - -7 -2699 0 - -7 -3219 0 - -7 -3461 0 - -7 -4033 0 - -577 -702 0 - -577 -1620 0 - -577 -1875 0 - -577 -2340 0 - -577 -2699 0 - -577 -3219 0 - -577 -3461 0 - -577 -4033 0 - -702 -1620 0 - -702 -1875 0 - -702 -2340 0 - -702 -2699 0 - -702 -3219 0 - -702 -3461 0 - -702 -4033 0 - -1620 -1875 0 - -1620 -2340 0 - -1620 -2699 0 - -1620 -3219 0 - -1620 -3461 0 - -1620 -4033 0 - -1875 -2340 0 - -1875 -2699 0 - -1875 -3219 0 - -1875 -3461 0 - -1875 -4033 0 - -2340 -2699 0 - -2340 -3219 0 - -2340 -3461 0 - -2340 -4033 0 - -2699 -3219 0 - -2699 -3461 0 - -2699 -4033 0 - -3219 -3461 0 - -3219 -4033 0 - -3461 -4033 0 - -349 -703 0 - -349 -1300 0 - -349 -1378 0 - -349 -1562 0 - -349 -1876 0 - -349 -2182 0 - -349 -2700 0 - -349 -3027 0 - -349 -4228 0 - -703 -1300 0 - -703 -1378 0 - -703 -1562 0 - -703 -1876 0 - -703 -2182 0 - -703 -2700 0 - -703 -3027 0 - -703 -4228 0 - -1300 -1378 0 - -1300 -1562 0 - -1300 -1876 0 - -1300 -2182 0 - -1300 -2700 0 - -1300 -3027 0 - -1300 -4228 0 - -1378 -1562 0 - -1378 -1876 0 - -1378 -2182 0 - -1378 -2700 0 - -1378 -3027 0 - -1378 -4228 0 - -1562 -1876 0 - -1562 -2182 0 - -1562 -2700 0 - -1562 -3027 0 - -1562 -4228 0 - -1876 -2182 0 - -1876 -2700 0 - -1876 -3027 0 - -1876 -4228 0 - -2182 -2700 0 - -2182 -3027 0 - -2182 -4228 0 - -2700 -3027 0 - -2700 -4228 0 - -3027 -4228 0 - -8 -350 0 - -8 -704 0 - -8 -1055 0 - -8 -1379 0 - -8 -2341 0 - -8 -3028 0 - -8 -3462 0 - -8 -4229 0 - -350 -704 0 - -350 -1055 0 - -350 -1379 0 - -350 -2341 0 - -350 -3028 0 - -350 -3462 0 - -350 -4229 0 - -704 -1055 0 - -704 -1379 0 - -704 -2341 0 - -704 -3028 0 - -704 -3462 0 - -704 -4229 0 - -1055 -1379 0 - -1055 -2341 0 - -1055 -3028 0 - -1055 -3462 0 - -1055 -4229 0 - -1379 -2341 0 - -1379 -3028 0 - -1379 -3462 0 - -1379 -4229 0 - -2341 -3028 0 - -2341 -3462 0 - -2341 -4229 0 - -3028 -3462 0 - -3028 -4229 0 - -3462 -4229 0 - -9 -103 0 - -9 -578 0 - -9 -1380 0 - -9 -1877 0 - -9 -2816 0 - -9 -3029 0 - -9 -4102 0 - -9 -4230 0 - -103 -578 0 - -103 -1380 0 - -103 -1877 0 - -103 -2816 0 - -103 -3029 0 - -103 -4102 0 - -103 -4230 0 - -578 -1380 0 - -578 -1877 0 - -578 -2816 0 - -578 -3029 0 - -578 -4102 0 - -578 -4230 0 - -1380 -1877 0 - -1380 -2816 0 - -1380 -3029 0 - -1380 -4102 0 - -1380 -4230 0 - -1877 -2816 0 - -1877 -3029 0 - -1877 -4102 0 - -1877 -4230 0 - -2816 -3029 0 - -2816 -4102 0 - -2816 -4230 0 - -3029 -4102 0 - -3029 -4230 0 - -4102 -4230 0 - -104 -579 0 - -104 -1056 0 - -104 -1301 0 - -104 -1621 0 - -104 -2183 0 - -104 -2268 0 - -104 -2342 0 - -104 -2701 0 - -104 -3030 0 - -579 -1056 0 - -579 -1301 0 - -579 -1621 0 - -579 -2183 0 - -579 -2268 0 - -579 -2342 0 - -579 -2701 0 - -579 -3030 0 - -1056 -1301 0 - -1056 -1621 0 - -1056 -2183 0 - -1056 -2268 0 - -1056 -2342 0 - -1056 -2701 0 - -1056 -3030 0 - -1301 -1621 0 - -1301 -2183 0 - -1301 -2268 0 - -1301 -2342 0 - -1301 -2701 0 - -1301 -3030 0 - -1621 -2183 0 - -1621 -2268 0 - -1621 -2342 0 - -1621 -2701 0 - -1621 -3030 0 - -2183 -2268 0 - -2183 -2342 0 - -2183 -2701 0 - -2183 -3030 0 - -2268 -2342 0 - -2268 -2701 0 - -2268 -3030 0 - -2342 -2701 0 - -2342 -3030 0 - -2701 -3030 0 - -105 -351 0 - -105 -580 0 - -105 -705 0 - -105 -1057 0 - -105 -1622 0 - -105 -1878 0 - -105 -2184 0 - -105 -2269 0 - -105 -2343 0 - -105 -2817 0 - -105 -3031 0 - -105 -3220 0 - -105 -3463 0 - -351 -580 0 - -351 -705 0 - -351 -1057 0 - -351 -1622 0 - -351 -1878 0 - -351 -2184 0 - -351 -2269 0 - -351 -2343 0 - -351 -2817 0 - -351 -3031 0 - -351 -3220 0 - -351 -3463 0 - -580 -705 0 - -580 -1057 0 - -580 -1622 0 - -580 -1878 0 - -580 -2184 0 - -580 -2269 0 - -580 -2343 0 - -580 -2817 0 - -580 -3031 0 - -580 -3220 0 - -580 -3463 0 - -705 -1057 0 - -705 -1622 0 - -705 -1878 0 - -705 -2184 0 - -705 -2269 0 - -705 -2343 0 - -705 -2817 0 - -705 -3031 0 - -705 -3220 0 - -705 -3463 0 - -1057 -1622 0 - -1057 -1878 0 - -1057 -2184 0 - -1057 -2269 0 - -1057 -2343 0 - -1057 -2817 0 - -1057 -3031 0 - -1057 -3220 0 - -1057 -3463 0 - -1622 -1878 0 - -1622 -2184 0 - -1622 -2269 0 - -1622 -2343 0 - -1622 -2817 0 - -1622 -3031 0 - -1622 -3220 0 - -1622 -3463 0 - -1878 -2184 0 - -1878 -2269 0 - -1878 -2343 0 - -1878 -2817 0 - -1878 -3031 0 - -1878 -3220 0 - -1878 -3463 0 - -2184 -2269 0 - -2184 -2343 0 - -2184 -2817 0 - -2184 -3031 0 - -2184 -3220 0 - -2184 -3463 0 - -2269 -2343 0 - -2269 -2817 0 - -2269 -3031 0 - -2269 -3220 0 - -2269 -3463 0 - -2343 -2817 0 - -2343 -3031 0 - -2343 -3220 0 - -2343 -3463 0 - -2817 -3031 0 - -2817 -3220 0 - -2817 -3463 0 - -3031 -3220 0 - -3031 -3463 0 - -3220 -3463 0 - -10 -106 0 - -10 -1058 0 - -10 -1879 0 - -10 -2024 0 - -10 -2185 0 - -10 -2270 0 - -10 -2818 0 - -10 -4034 0 - -10 -4103 0 - -10 -4231 0 - -106 -1058 0 - -106 -1879 0 - -106 -2024 0 - -106 -2185 0 - -106 -2270 0 - -106 -2818 0 - -106 -4034 0 - -106 -4103 0 - -106 -4231 0 - -1058 -1879 0 - -1058 -2024 0 - -1058 -2185 0 - -1058 -2270 0 - -1058 -2818 0 - -1058 -4034 0 - -1058 -4103 0 - -1058 -4231 0 - -1879 -2024 0 - -1879 -2185 0 - -1879 -2270 0 - -1879 -2818 0 - -1879 -4034 0 - -1879 -4103 0 - -1879 -4231 0 - -2024 -2185 0 - -2024 -2270 0 - -2024 -2818 0 - -2024 -4034 0 - -2024 -4103 0 - -2024 -4231 0 - -2185 -2270 0 - -2185 -2818 0 - -2185 -4034 0 - -2185 -4103 0 - -2185 -4231 0 - -2270 -2818 0 - -2270 -4034 0 - -2270 -4103 0 - -2270 -4231 0 - -2818 -4034 0 - -2818 -4103 0 - -2818 -4231 0 - -4034 -4103 0 - -4034 -4231 0 - -4103 -4231 0 - -352 -1381 0 - -352 -1623 0 - -352 -1880 0 - -352 -2025 0 - -352 -2271 0 - -352 -2702 0 - -352 -2819 0 - -352 -4104 0 - -352 -4232 0 - -1381 -1623 0 - -1381 -1880 0 - -1381 -2025 0 - -1381 -2271 0 - -1381 -2702 0 - -1381 -2819 0 - -1381 -4104 0 - -1381 -4232 0 - -1623 -1880 0 - -1623 -2025 0 - -1623 -2271 0 - -1623 -2702 0 - -1623 -2819 0 - -1623 -4104 0 - -1623 -4232 0 - -1880 -2025 0 - -1880 -2271 0 - -1880 -2702 0 - -1880 -2819 0 - -1880 -4104 0 - -1880 -4232 0 - -2025 -2271 0 - -2025 -2702 0 - -2025 -2819 0 - -2025 -4104 0 - -2025 -4232 0 - -2271 -2702 0 - -2271 -2819 0 - -2271 -4104 0 - -2271 -4232 0 - -2702 -2819 0 - -2702 -4104 0 - -2702 -4232 0 - -2819 -4104 0 - -2819 -4232 0 - -4104 -4232 0 - -11 -1382 0 - -11 -1881 0 - -11 -2026 0 - -11 -2272 0 - -11 -3032 0 - -11 -4105 0 - -11 -4233 0 - -1382 -1881 0 - -1382 -2026 0 - -1382 -2272 0 - -1382 -3032 0 - -1382 -4105 0 - -1382 -4233 0 - -1881 -2026 0 - -1881 -2272 0 - -1881 -3032 0 - -1881 -4105 0 - -1881 -4233 0 - -2026 -2272 0 - -2026 -3032 0 - -2026 -4105 0 - -2026 -4233 0 - -2272 -3032 0 - -2272 -4105 0 - -2272 -4233 0 - -3032 -4105 0 - -3032 -4233 0 - -4105 -4233 0 - -12 -706 0 - -12 -1059 0 - -12 -3033 0 - -12 -3307 0 - -12 -3464 0 - -706 -1059 0 - -706 -3033 0 - -706 -3307 0 - -706 -3464 0 - -1059 -3033 0 - -1059 -3307 0 - -1059 -3464 0 - -3033 -3307 0 - -3033 -3464 0 - -3307 -3464 0 - -919 -1060 0 - -919 -1882 0 - -919 -3034 0 - -919 -3465 0 - -1060 -1882 0 - -1060 -3034 0 - -1060 -3465 0 - -1882 -3034 0 - -1882 -3465 0 - -3034 -3465 0 - -353 -581 0 - -353 -707 0 - -353 -3308 0 - -581 -707 0 - -581 -3308 0 - -707 -3308 0 - -13 -354 0 - -13 -582 0 - -13 -1883 0 - -13 -3309 0 - -13 -3466 0 - -13 -4234 0 - -354 -582 0 - -354 -1883 0 - -354 -3309 0 - -354 -3466 0 - -354 -4234 0 - -582 -1883 0 - -582 -3309 0 - -582 -3466 0 - -582 -4234 0 - -1883 -3309 0 - -1883 -3466 0 - -1883 -4234 0 - -3309 -3466 0 - -3309 -4234 0 - -3466 -4234 0 - -355 -708 0 - -355 -920 0 - -355 -3035 0 - -355 -3310 0 - -708 -920 0 - -708 -3035 0 - -708 -3310 0 - -920 -3035 0 - -920 -3310 0 - -3035 -3310 0 - -14 -1061 0 - -14 -3311 0 - -14 -3467 0 - -1061 -3311 0 - -1061 -3467 0 - -3311 -3467 0 - -15 -356 0 - -15 -709 0 - -15 -1062 0 - -15 -3036 0 - -356 -709 0 - -356 -1062 0 - -356 -3036 0 - -709 -1062 0 - -709 -3036 0 - -1062 -3036 0 - -357 -710 0 - -357 -921 0 - -357 -1302 0 - -357 -1884 0 - -357 -3037 0 - -357 -4235 0 - -710 -921 0 - -710 -1302 0 - -710 -1884 0 - -710 -3037 0 - -710 -4235 0 - -921 -1302 0 - -921 -1884 0 - -921 -3037 0 - -921 -4235 0 - -1302 -1884 0 - -1302 -3037 0 - -1302 -4235 0 - -1884 -3037 0 - -1884 -4235 0 - -3037 -4235 0 - -1063 -1303 0 - -1063 -2273 0 - -1063 -3038 0 - -1063 -4236 0 - -1303 -2273 0 - -1303 -3038 0 - -1303 -4236 0 - -2273 -3038 0 - -2273 -4236 0 - -3038 -4236 0 - -358 -583 0 - -358 -711 0 - -358 -1064 0 - -358 -1885 0 - -358 -2274 0 - -358 -3039 0 - -358 -3468 0 - -583 -711 0 - -583 -1064 0 - -583 -1885 0 - -583 -2274 0 - -583 -3039 0 - -583 -3468 0 - -711 -1064 0 - -711 -1885 0 - -711 -2274 0 - -711 -3039 0 - -711 -3468 0 - -1064 -1885 0 - -1064 -2274 0 - -1064 -3039 0 - -1064 -3468 0 - -1885 -2274 0 - -1885 -3039 0 - -1885 -3468 0 - -2274 -3039 0 - -2274 -3468 0 - -3039 -3468 0 - -16 -1065 0 - -16 -1886 0 - -16 -2275 0 - -16 -4237 0 - -1065 -1886 0 - -1065 -2275 0 - -1065 -4237 0 - -1886 -2275 0 - -1886 -4237 0 - -2275 -4237 0 - -359 -922 0 - -359 -1887 0 - -359 -2276 0 - -359 -3312 0 - -359 -4238 0 - -922 -1887 0 - -922 -2276 0 - -922 -3312 0 - -922 -4238 0 - -1887 -2276 0 - -1887 -3312 0 - -1887 -4238 0 - -2276 -3312 0 - -2276 -4238 0 - -3312 -4238 0 - -17 -923 0 - -17 -1888 0 - -17 -2277 0 - -17 -3040 0 - -17 -3313 0 - -17 -4239 0 - -923 -1888 0 - -923 -2277 0 - -923 -3040 0 - -923 -3313 0 - -923 -4239 0 - -1888 -2277 0 - -1888 -3040 0 - -1888 -3313 0 - -1888 -4239 0 - -2277 -3040 0 - -2277 -3313 0 - -2277 -4239 0 - -3040 -3313 0 - -3040 -4239 0 - -3313 -4239 0 - -712 -1066 0 - -712 -1753 0 - -712 -2027 0 - -712 -2344 0 - -712 -2703 0 - -712 -3041 0 - -712 -3314 0 - -712 -3469 0 - -712 -3911 0 - -1066 -1753 0 - -1066 -2027 0 - -1066 -2344 0 - -1066 -2703 0 - -1066 -3041 0 - -1066 -3314 0 - -1066 -3469 0 - -1066 -3911 0 - -1753 -2027 0 - -1753 -2344 0 - -1753 -2703 0 - -1753 -3041 0 - -1753 -3314 0 - -1753 -3469 0 - -1753 -3911 0 - -2027 -2344 0 - -2027 -2703 0 - -2027 -3041 0 - -2027 -3314 0 - -2027 -3469 0 - -2027 -3911 0 - -2344 -2703 0 - -2344 -3041 0 - -2344 -3314 0 - -2344 -3469 0 - -2344 -3911 0 - -2703 -3041 0 - -2703 -3314 0 - -2703 -3469 0 - -2703 -3911 0 - -3041 -3314 0 - -3041 -3469 0 - -3041 -3911 0 - -3314 -3469 0 - -3314 -3911 0 - -3469 -3911 0 - -107 -237 0 - -107 -447 0 - -107 -924 0 - -107 -1067 0 - -107 -1624 0 - -107 -1754 0 - -107 -2028 0 - -107 -2186 0 - -107 -2345 0 - -107 -2430 0 - -107 -2545 0 - -107 -3315 0 - -107 -3470 0 - -107 -3809 0 - -237 -447 0 - -237 -924 0 - -237 -1067 0 - -237 -1624 0 - -237 -1754 0 - -237 -2028 0 - -237 -2186 0 - -237 -2345 0 - -237 -2430 0 - -237 -2545 0 - -237 -3315 0 - -237 -3470 0 - -237 -3809 0 - -447 -924 0 - -447 -1067 0 - -447 -1624 0 - -447 -1754 0 - -447 -2028 0 - -447 -2186 0 - -447 -2345 0 - -447 -2430 0 - -447 -2545 0 - -447 -3315 0 - -447 -3470 0 - -447 -3809 0 - -924 -1067 0 - -924 -1624 0 - -924 -1754 0 - -924 -2028 0 - -924 -2186 0 - -924 -2345 0 - -924 -2430 0 - -924 -2545 0 - -924 -3315 0 - -924 -3470 0 - -924 -3809 0 - -1067 -1624 0 - -1067 -1754 0 - -1067 -2028 0 - -1067 -2186 0 - -1067 -2345 0 - -1067 -2430 0 - -1067 -2545 0 - -1067 -3315 0 - -1067 -3470 0 - -1067 -3809 0 - -1624 -1754 0 - -1624 -2028 0 - -1624 -2186 0 - -1624 -2345 0 - -1624 -2430 0 - -1624 -2545 0 - -1624 -3315 0 - -1624 -3470 0 - -1624 -3809 0 - -1754 -2028 0 - -1754 -2186 0 - -1754 -2345 0 - -1754 -2430 0 - -1754 -2545 0 - -1754 -3315 0 - -1754 -3470 0 - -1754 -3809 0 - -2028 -2186 0 - -2028 -2345 0 - -2028 -2430 0 - -2028 -2545 0 - -2028 -3315 0 - -2028 -3470 0 - -2028 -3809 0 - -2186 -2345 0 - -2186 -2430 0 - -2186 -2545 0 - -2186 -3315 0 - -2186 -3470 0 - -2186 -3809 0 - -2345 -2430 0 - -2345 -2545 0 - -2345 -3315 0 - -2345 -3470 0 - -2345 -3809 0 - -2430 -2545 0 - -2430 -3315 0 - -2430 -3470 0 - -2430 -3809 0 - -2545 -3315 0 - -2545 -3470 0 - -2545 -3809 0 - -3315 -3470 0 - -3315 -3809 0 - -3470 -3809 0 - -448 -925 0 - -448 -1068 0 - -448 -1625 0 - -448 -1755 0 - -448 -2029 0 - -448 -2546 0 - -448 -2704 0 - -448 -3042 0 - -448 -3471 0 - -448 -3810 0 - -448 -3912 0 - -925 -1068 0 - -925 -1625 0 - -925 -1755 0 - -925 -2029 0 - -925 -2546 0 - -925 -2704 0 - -925 -3042 0 - -925 -3471 0 - -925 -3810 0 - -925 -3912 0 - -1068 -1625 0 - -1068 -1755 0 - -1068 -2029 0 - -1068 -2546 0 - -1068 -2704 0 - -1068 -3042 0 - -1068 -3471 0 - -1068 -3810 0 - -1068 -3912 0 - -1625 -1755 0 - -1625 -2029 0 - -1625 -2546 0 - -1625 -2704 0 - -1625 -3042 0 - -1625 -3471 0 - -1625 -3810 0 - -1625 -3912 0 - -1755 -2029 0 - -1755 -2546 0 - -1755 -2704 0 - -1755 -3042 0 - -1755 -3471 0 - -1755 -3810 0 - -1755 -3912 0 - -2029 -2546 0 - -2029 -2704 0 - -2029 -3042 0 - -2029 -3471 0 - -2029 -3810 0 - -2029 -3912 0 - -2546 -2704 0 - -2546 -3042 0 - -2546 -3471 0 - -2546 -3810 0 - -2546 -3912 0 - -2704 -3042 0 - -2704 -3471 0 - -2704 -3810 0 - -2704 -3912 0 - -3042 -3471 0 - -3042 -3810 0 - -3042 -3912 0 - -3471 -3810 0 - -3471 -3912 0 - -3810 -3912 0 - -1069 -1563 0 - -1069 -1626 0 - -1069 -1756 0 - -1069 -2030 0 - -1069 -2187 0 - -1069 -2431 0 - -1069 -2705 0 - -1069 -3043 0 - -1069 -3221 0 - -1069 -3472 0 - -1069 -3913 0 - -1563 -1626 0 - -1563 -1756 0 - -1563 -2030 0 - -1563 -2187 0 - -1563 -2431 0 - -1563 -2705 0 - -1563 -3043 0 - -1563 -3221 0 - -1563 -3472 0 - -1563 -3913 0 - -1626 -1756 0 - -1626 -2030 0 - -1626 -2187 0 - -1626 -2431 0 - -1626 -2705 0 - -1626 -3043 0 - -1626 -3221 0 - -1626 -3472 0 - -1626 -3913 0 - -1756 -2030 0 - -1756 -2187 0 - -1756 -2431 0 - -1756 -2705 0 - -1756 -3043 0 - -1756 -3221 0 - -1756 -3472 0 - -1756 -3913 0 - -2030 -2187 0 - -2030 -2431 0 - -2030 -2705 0 - -2030 -3043 0 - -2030 -3221 0 - -2030 -3472 0 - -2030 -3913 0 - -2187 -2431 0 - -2187 -2705 0 - -2187 -3043 0 - -2187 -3221 0 - -2187 -3472 0 - -2187 -3913 0 - -2431 -2705 0 - -2431 -3043 0 - -2431 -3221 0 - -2431 -3472 0 - -2431 -3913 0 - -2705 -3043 0 - -2705 -3221 0 - -2705 -3472 0 - -2705 -3913 0 - -3043 -3221 0 - -3043 -3472 0 - -3043 -3913 0 - -3221 -3472 0 - -3221 -3913 0 - -3472 -3913 0 - -108 -1070 0 - -108 -1304 0 - -108 -2432 0 - -108 -3044 0 - -108 -3222 0 - -108 -3473 0 - -1070 -1304 0 - -1070 -2432 0 - -1070 -3044 0 - -1070 -3222 0 - -1070 -3473 0 - -1304 -2432 0 - -1304 -3044 0 - -1304 -3222 0 - -1304 -3473 0 - -2432 -3044 0 - -2432 -3222 0 - -2432 -3473 0 - -3044 -3222 0 - -3044 -3473 0 - -3222 -3473 0 - -238 -713 0 - -238 -1564 0 - -238 -1627 0 - -238 -2433 0 - -238 -3223 0 - -238 -3316 0 - -238 -3811 0 - -238 -3914 0 - -713 -1564 0 - -713 -1627 0 - -713 -2433 0 - -713 -3223 0 - -713 -3316 0 - -713 -3811 0 - -713 -3914 0 - -1564 -1627 0 - -1564 -2433 0 - -1564 -3223 0 - -1564 -3316 0 - -1564 -3811 0 - -1564 -3914 0 - -1627 -2433 0 - -1627 -3223 0 - -1627 -3316 0 - -1627 -3811 0 - -1627 -3914 0 - -2433 -3223 0 - -2433 -3316 0 - -2433 -3811 0 - -2433 -3914 0 - -3223 -3316 0 - -3223 -3811 0 - -3223 -3914 0 - -3316 -3811 0 - -3316 -3914 0 - -3811 -3914 0 - -449 -1628 0 - -449 -2188 0 - -449 -2434 0 - -449 -2547 0 - -449 -3317 0 - -449 -3474 0 - -449 -4240 0 - -1628 -2188 0 - -1628 -2434 0 - -1628 -2547 0 - -1628 -3317 0 - -1628 -3474 0 - -1628 -4240 0 - -2188 -2434 0 - -2188 -2547 0 - -2188 -3317 0 - -2188 -3474 0 - -2188 -4240 0 - -2434 -2547 0 - -2434 -3317 0 - -2434 -3474 0 - -2434 -4240 0 - -2547 -3317 0 - -2547 -3474 0 - -2547 -4240 0 - -3317 -3474 0 - -3317 -4240 0 - -3474 -4240 0 - -109 -239 0 - -109 -450 0 - -109 -714 0 - -109 -926 0 - -109 -1071 0 - -109 -1565 0 - -109 -2031 0 - -109 -2189 0 - -109 -2548 0 - -109 -3318 0 - -239 -450 0 - -239 -714 0 - -239 -926 0 - -239 -1071 0 - -239 -1565 0 - -239 -2031 0 - -239 -2189 0 - -239 -2548 0 - -239 -3318 0 - -450 -714 0 - -450 -926 0 - -450 -1071 0 - -450 -1565 0 - -450 -2031 0 - -450 -2189 0 - -450 -2548 0 - -450 -3318 0 - -714 -926 0 - -714 -1071 0 - -714 -1565 0 - -714 -2031 0 - -714 -2189 0 - -714 -2548 0 - -714 -3318 0 - -926 -1071 0 - -926 -1565 0 - -926 -2031 0 - -926 -2189 0 - -926 -2548 0 - -926 -3318 0 - -1071 -1565 0 - -1071 -2031 0 - -1071 -2189 0 - -1071 -2548 0 - -1071 -3318 0 - -1565 -2031 0 - -1565 -2189 0 - -1565 -2548 0 - -1565 -3318 0 - -2031 -2189 0 - -2031 -2548 0 - -2031 -3318 0 - -2189 -2548 0 - -2189 -3318 0 - -2548 -3318 0 - -240 -451 0 - -240 -715 0 - -240 -927 0 - -240 -1629 0 - -240 -2032 0 - -240 -2435 0 - -240 -2549 0 - -240 -3045 0 - -240 -3224 0 - -240 -3319 0 - -451 -715 0 - -451 -927 0 - -451 -1629 0 - -451 -2032 0 - -451 -2435 0 - -451 -2549 0 - -451 -3045 0 - -451 -3224 0 - -451 -3319 0 - -715 -927 0 - -715 -1629 0 - -715 -2032 0 - -715 -2435 0 - -715 -2549 0 - -715 -3045 0 - -715 -3224 0 - -715 -3319 0 - -927 -1629 0 - -927 -2032 0 - -927 -2435 0 - -927 -2549 0 - -927 -3045 0 - -927 -3224 0 - -927 -3319 0 - -1629 -2032 0 - -1629 -2435 0 - -1629 -2549 0 - -1629 -3045 0 - -1629 -3224 0 - -1629 -3319 0 - -2032 -2435 0 - -2032 -2549 0 - -2032 -3045 0 - -2032 -3224 0 - -2032 -3319 0 - -2435 -2549 0 - -2435 -3045 0 - -2435 -3224 0 - -2435 -3319 0 - -2549 -3045 0 - -2549 -3224 0 - -2549 -3319 0 - -3045 -3224 0 - -3045 -3319 0 - -3224 -3319 0 - -110 -241 0 - -110 -452 0 - -110 -1072 0 - -110 -1630 0 - -110 -2190 0 - -110 -2550 0 - -110 -3225 0 - -110 -3320 0 - -110 -3475 0 - -110 -3812 0 - -110 -3915 0 - -241 -452 0 - -241 -1072 0 - -241 -1630 0 - -241 -2190 0 - -241 -2550 0 - -241 -3225 0 - -241 -3320 0 - -241 -3475 0 - -241 -3812 0 - -241 -3915 0 - -452 -1072 0 - -452 -1630 0 - -452 -2190 0 - -452 -2550 0 - -452 -3225 0 - -452 -3320 0 - -452 -3475 0 - -452 -3812 0 - -452 -3915 0 - -1072 -1630 0 - -1072 -2190 0 - -1072 -2550 0 - -1072 -3225 0 - -1072 -3320 0 - -1072 -3475 0 - -1072 -3812 0 - -1072 -3915 0 - -1630 -2190 0 - -1630 -2550 0 - -1630 -3225 0 - -1630 -3320 0 - -1630 -3475 0 - -1630 -3812 0 - -1630 -3915 0 - -2190 -2550 0 - -2190 -3225 0 - -2190 -3320 0 - -2190 -3475 0 - -2190 -3812 0 - -2190 -3915 0 - -2550 -3225 0 - -2550 -3320 0 - -2550 -3475 0 - -2550 -3812 0 - -2550 -3915 0 - -3225 -3320 0 - -3225 -3475 0 - -3225 -3812 0 - -3225 -3915 0 - -3320 -3475 0 - -3320 -3812 0 - -3320 -3915 0 - -3475 -3812 0 - -3475 -3915 0 - -3812 -3915 0 - -453 -716 0 - -453 -1073 0 - -453 -1566 0 - -453 -1757 0 - -453 -2033 0 - -453 -2346 0 - -453 -4241 0 - -716 -1073 0 - -716 -1566 0 - -716 -1757 0 - -716 -2033 0 - -716 -2346 0 - -716 -4241 0 - -1073 -1566 0 - -1073 -1757 0 - -1073 -2033 0 - -1073 -2346 0 - -1073 -4241 0 - -1566 -1757 0 - -1566 -2033 0 - -1566 -2346 0 - -1566 -4241 0 - -1757 -2033 0 - -1757 -2346 0 - -1757 -4241 0 - -2033 -2346 0 - -2033 -4241 0 - -2346 -4241 0 - -111 -928 0 - -111 -1631 0 - -111 -2034 0 - -111 -2706 0 - -111 -3046 0 - -111 -3226 0 - -111 -3321 0 - -111 -3813 0 - -928 -1631 0 - -928 -2034 0 - -928 -2706 0 - -928 -3046 0 - -928 -3226 0 - -928 -3321 0 - -928 -3813 0 - -1631 -2034 0 - -1631 -2706 0 - -1631 -3046 0 - -1631 -3226 0 - -1631 -3321 0 - -1631 -3813 0 - -2034 -2706 0 - -2034 -3046 0 - -2034 -3226 0 - -2034 -3321 0 - -2034 -3813 0 - -2706 -3046 0 - -2706 -3226 0 - -2706 -3321 0 - -2706 -3813 0 - -3046 -3226 0 - -3046 -3321 0 - -3046 -3813 0 - -3226 -3321 0 - -3226 -3813 0 - -3321 -3813 0 - -112 -242 0 - -112 -454 0 - -112 -1305 0 - -112 -1567 0 - -112 -2191 0 - -112 -2551 0 - -112 -3047 0 - -112 -3227 0 - -112 -3814 0 - -242 -454 0 - -242 -1305 0 - -242 -1567 0 - -242 -2191 0 - -242 -2551 0 - -242 -3047 0 - -242 -3227 0 - -242 -3814 0 - -454 -1305 0 - -454 -1567 0 - -454 -2191 0 - -454 -2551 0 - -454 -3047 0 - -454 -3227 0 - -454 -3814 0 - -1305 -1567 0 - -1305 -2191 0 - -1305 -2551 0 - -1305 -3047 0 - -1305 -3227 0 - -1305 -3814 0 - -1567 -2191 0 - -1567 -2551 0 - -1567 -3047 0 - -1567 -3227 0 - -1567 -3814 0 - -2191 -2551 0 - -2191 -3047 0 - -2191 -3227 0 - -2191 -3814 0 - -2551 -3047 0 - -2551 -3227 0 - -2551 -3814 0 - -3047 -3227 0 - -3047 -3814 0 - -3227 -3814 0 - -113 -243 0 - -113 -455 0 - -113 -929 0 - -113 -1074 0 - -113 -1306 0 - -113 -2035 0 - -113 -2192 0 - -113 -2436 0 - -113 -2552 0 - -113 -2707 0 - -113 -3476 0 - -113 -4242 0 - -243 -455 0 - -243 -929 0 - -243 -1074 0 - -243 -1306 0 - -243 -2035 0 - -243 -2192 0 - -243 -2436 0 - -243 -2552 0 - -243 -2707 0 - -243 -3476 0 - -243 -4242 0 - -455 -929 0 - -455 -1074 0 - -455 -1306 0 - -455 -2035 0 - -455 -2192 0 - -455 -2436 0 - -455 -2552 0 - -455 -2707 0 - -455 -3476 0 - -455 -4242 0 - -929 -1074 0 - -929 -1306 0 - -929 -2035 0 - -929 -2192 0 - -929 -2436 0 - -929 -2552 0 - -929 -2707 0 - -929 -3476 0 - -929 -4242 0 - -1074 -1306 0 - -1074 -2035 0 - -1074 -2192 0 - -1074 -2436 0 - -1074 -2552 0 - -1074 -2707 0 - -1074 -3476 0 - -1074 -4242 0 - -1306 -2035 0 - -1306 -2192 0 - -1306 -2436 0 - -1306 -2552 0 - -1306 -2707 0 - -1306 -3476 0 - -1306 -4242 0 - -2035 -2192 0 - -2035 -2436 0 - -2035 -2552 0 - -2035 -2707 0 - -2035 -3476 0 - -2035 -4242 0 - -2192 -2436 0 - -2192 -2552 0 - -2192 -2707 0 - -2192 -3476 0 - -2192 -4242 0 - -2436 -2552 0 - -2436 -2707 0 - -2436 -3476 0 - -2436 -4242 0 - -2552 -2707 0 - -2552 -3476 0 - -2552 -4242 0 - -2707 -3476 0 - -2707 -4242 0 - -3476 -4242 0 - -244 -717 0 - -244 -930 0 - -244 -1307 0 - -244 -1568 0 - -244 -2193 0 - -244 -2437 0 - -244 -2708 0 - -244 -3048 0 - -244 -3815 0 - -244 -3916 0 - -244 -4243 0 - -717 -930 0 - -717 -1307 0 - -717 -1568 0 - -717 -2193 0 - -717 -2437 0 - -717 -2708 0 - -717 -3048 0 - -717 -3815 0 - -717 -3916 0 - -717 -4243 0 - -930 -1307 0 - -930 -1568 0 - -930 -2193 0 - -930 -2437 0 - -930 -2708 0 - -930 -3048 0 - -930 -3815 0 - -930 -3916 0 - -930 -4243 0 - -1307 -1568 0 - -1307 -2193 0 - -1307 -2437 0 - -1307 -2708 0 - -1307 -3048 0 - -1307 -3815 0 - -1307 -3916 0 - -1307 -4243 0 - -1568 -2193 0 - -1568 -2437 0 - -1568 -2708 0 - -1568 -3048 0 - -1568 -3815 0 - -1568 -3916 0 - -1568 -4243 0 - -2193 -2437 0 - -2193 -2708 0 - -2193 -3048 0 - -2193 -3815 0 - -2193 -3916 0 - -2193 -4243 0 - -2437 -2708 0 - -2437 -3048 0 - -2437 -3815 0 - -2437 -3916 0 - -2437 -4243 0 - -2708 -3048 0 - -2708 -3815 0 - -2708 -3916 0 - -2708 -4243 0 - -3048 -3815 0 - -3048 -3916 0 - -3048 -4243 0 - -3815 -3916 0 - -3815 -4243 0 - -3916 -4243 0 - -718 -931 0 - -718 -1075 0 - -718 -1758 0 - -718 -2347 0 - -718 -2438 0 - -718 -3049 0 - -718 -3477 0 - -718 -3917 0 - -718 -4244 0 - -931 -1075 0 - -931 -1758 0 - -931 -2347 0 - -931 -2438 0 - -931 -3049 0 - -931 -3477 0 - -931 -3917 0 - -931 -4244 0 - -1075 -1758 0 - -1075 -2347 0 - -1075 -2438 0 - -1075 -3049 0 - -1075 -3477 0 - -1075 -3917 0 - -1075 -4244 0 - -1758 -2347 0 - -1758 -2438 0 - -1758 -3049 0 - -1758 -3477 0 - -1758 -3917 0 - -1758 -4244 0 - -2347 -2438 0 - -2347 -3049 0 - -2347 -3477 0 - -2347 -3917 0 - -2347 -4244 0 - -2438 -3049 0 - -2438 -3477 0 - -2438 -3917 0 - -2438 -4244 0 - -3049 -3477 0 - -3049 -3917 0 - -3049 -4244 0 - -3477 -3917 0 - -3477 -4244 0 - -3917 -4244 0 - -114 -456 0 - -114 -932 0 - -114 -1759 0 - -114 -3050 0 - -114 -3322 0 - -114 -3816 0 - -114 -4245 0 - -456 -932 0 - -456 -1759 0 - -456 -3050 0 - -456 -3322 0 - -456 -3816 0 - -456 -4245 0 - -932 -1759 0 - -932 -3050 0 - -932 -3322 0 - -932 -3816 0 - -932 -4245 0 - -1759 -3050 0 - -1759 -3322 0 - -1759 -3816 0 - -1759 -4245 0 - -3050 -3322 0 - -3050 -3816 0 - -3050 -4245 0 - -3322 -3816 0 - -3322 -4245 0 - -3816 -4245 0 - -1076 -1308 0 - -1076 -1760 0 - -1076 -2348 0 - -1076 -3323 0 - -1076 -3918 0 - -1076 -4246 0 - -1308 -1760 0 - -1308 -2348 0 - -1308 -3323 0 - -1308 -3918 0 - -1308 -4246 0 - -1760 -2348 0 - -1760 -3323 0 - -1760 -3918 0 - -1760 -4246 0 - -2348 -3323 0 - -2348 -3918 0 - -2348 -4246 0 - -3323 -3918 0 - -3323 -4246 0 - -3918 -4246 0 - -115 -457 0 - -115 -1077 0 - -115 -1309 0 - -115 -1632 0 - -115 -2194 0 - -115 -2349 0 - -115 -3051 0 - -115 -3919 0 - -115 -4247 0 - -457 -1077 0 - -457 -1309 0 - -457 -1632 0 - -457 -2194 0 - -457 -2349 0 - -457 -3051 0 - -457 -3919 0 - -457 -4247 0 - -1077 -1309 0 - -1077 -1632 0 - -1077 -2194 0 - -1077 -2349 0 - -1077 -3051 0 - -1077 -3919 0 - -1077 -4247 0 - -1309 -1632 0 - -1309 -2194 0 - -1309 -2349 0 - -1309 -3051 0 - -1309 -3919 0 - -1309 -4247 0 - -1632 -2194 0 - -1632 -2349 0 - -1632 -3051 0 - -1632 -3919 0 - -1632 -4247 0 - -2194 -2349 0 - -2194 -3051 0 - -2194 -3919 0 - -2194 -4247 0 - -2349 -3051 0 - -2349 -3919 0 - -2349 -4247 0 - -3051 -3919 0 - -3051 -4247 0 - -3919 -4247 0 - -116 -1078 0 - -116 -1310 0 - -116 -1633 0 - -116 -2195 0 - -116 -2350 0 - -116 -2439 0 - -116 -2553 0 - -116 -2709 0 - -116 -3052 0 - -116 -3324 0 - -1078 -1310 0 - -1078 -1633 0 - -1078 -2195 0 - -1078 -2350 0 - -1078 -2439 0 - -1078 -2553 0 - -1078 -2709 0 - -1078 -3052 0 - -1078 -3324 0 - -1310 -1633 0 - -1310 -2195 0 - -1310 -2350 0 - -1310 -2439 0 - -1310 -2553 0 - -1310 -2709 0 - -1310 -3052 0 - -1310 -3324 0 - -1633 -2195 0 - -1633 -2350 0 - -1633 -2439 0 - -1633 -2553 0 - -1633 -2709 0 - -1633 -3052 0 - -1633 -3324 0 - -2195 -2350 0 - -2195 -2439 0 - -2195 -2553 0 - -2195 -2709 0 - -2195 -3052 0 - -2195 -3324 0 - -2350 -2439 0 - -2350 -2553 0 - -2350 -2709 0 - -2350 -3052 0 - -2350 -3324 0 - -2439 -2553 0 - -2439 -2709 0 - -2439 -3052 0 - -2439 -3324 0 - -2553 -2709 0 - -2553 -3052 0 - -2553 -3324 0 - -2709 -3052 0 - -2709 -3324 0 - -3052 -3324 0 - -117 -1079 0 - -117 -1761 0 - -117 -2036 0 - -117 -2196 0 - -117 -2440 0 - -117 -3817 0 - -117 -3920 0 - -117 -4248 0 - -1079 -1761 0 - -1079 -2036 0 - -1079 -2196 0 - -1079 -2440 0 - -1079 -3817 0 - -1079 -3920 0 - -1079 -4248 0 - -1761 -2036 0 - -1761 -2196 0 - -1761 -2440 0 - -1761 -3817 0 - -1761 -3920 0 - -1761 -4248 0 - -2036 -2196 0 - -2036 -2440 0 - -2036 -3817 0 - -2036 -3920 0 - -2036 -4248 0 - -2196 -2440 0 - -2196 -3817 0 - -2196 -3920 0 - -2196 -4248 0 - -2440 -3817 0 - -2440 -3920 0 - -2440 -4248 0 - -3817 -3920 0 - -3817 -4248 0 - -3920 -4248 0 - -245 -933 0 - -245 -1634 0 - -245 -1762 0 - -245 -2037 0 - -245 -2554 0 - -245 -2710 0 - -245 -3325 0 - -245 -4249 0 - -933 -1634 0 - -933 -1762 0 - -933 -2037 0 - -933 -2554 0 - -933 -2710 0 - -933 -3325 0 - -933 -4249 0 - -1634 -1762 0 - -1634 -2037 0 - -1634 -2554 0 - -1634 -2710 0 - -1634 -3325 0 - -1634 -4249 0 - -1762 -2037 0 - -1762 -2554 0 - -1762 -2710 0 - -1762 -3325 0 - -1762 -4249 0 - -2037 -2554 0 - -2037 -2710 0 - -2037 -3325 0 - -2037 -4249 0 - -2554 -2710 0 - -2554 -3325 0 - -2554 -4249 0 - -2710 -3325 0 - -2710 -4249 0 - -3325 -4249 0 - -246 -934 0 - -246 -1763 0 - -246 -2038 0 - -246 -3053 0 - -246 -3326 0 - -246 -4250 0 - -934 -1763 0 - -934 -2038 0 - -934 -3053 0 - -934 -3326 0 - -934 -4250 0 - -1763 -2038 0 - -1763 -3053 0 - -1763 -3326 0 - -1763 -4250 0 - -2038 -3053 0 - -2038 -3326 0 - -2038 -4250 0 - -3053 -3326 0 - -3053 -4250 0 - -3326 -4250 0 - -247 -719 0 - -247 -1383 0 - -247 -2820 0 - -247 -4315 0 - -719 -1383 0 - -719 -2820 0 - -719 -4315 0 - -1383 -2820 0 - -1383 -4315 0 - -2820 -4315 0 - -720 -1216 0 - -720 -1384 0 - -720 -2351 0 - -720 -2821 0 - -720 -4106 0 - -1216 -1384 0 - -1216 -2351 0 - -1216 -2821 0 - -1216 -4106 0 - -1384 -2351 0 - -1384 -2821 0 - -1384 -4106 0 - -2351 -2821 0 - -2351 -4106 0 - -2821 -4106 0 - -1217 -2441 0 - -1217 -3228 0 - -2441 -3228 0 - -458 -1385 0 - -458 -2442 0 - -458 -2822 0 - -1385 -2442 0 - -1385 -2822 0 - -2442 -2822 0 - -248 -459 0 - -248 -721 0 - -248 -2443 0 - -248 -2623 0 - -248 -3229 0 - -248 -4316 0 - -459 -721 0 - -459 -2443 0 - -459 -2623 0 - -459 -3229 0 - -459 -4316 0 - -721 -2443 0 - -721 -2623 0 - -721 -3229 0 - -721 -4316 0 - -2443 -2623 0 - -2443 -3229 0 - -2443 -4316 0 - -2623 -3229 0 - -2623 -4316 0 - -3229 -4316 0 - -249 -1386 0 - -249 -2352 0 - -249 -2624 0 - -249 -2823 0 - -249 -4317 0 - -1386 -2352 0 - -1386 -2624 0 - -1386 -2823 0 - -1386 -4317 0 - -2352 -2624 0 - -2352 -2823 0 - -2352 -4317 0 - -2624 -2823 0 - -2624 -4317 0 - -2823 -4317 0 - -250 -722 0 - -250 -1218 0 - -250 -1387 0 - -250 -2444 0 - -722 -1218 0 - -722 -1387 0 - -722 -2444 0 - -1218 -1387 0 - -1218 -2444 0 - -1387 -2444 0 - -460 -1219 0 - -460 -1388 0 - -460 -3230 0 - -460 -4318 0 - -1219 -1388 0 - -1219 -3230 0 - -1219 -4318 0 - -1388 -3230 0 - -1388 -4318 0 - -3230 -4318 0 - -118 -461 0 - -118 -1389 0 - -118 -2824 0 - -118 -4107 0 - -118 -4319 0 - -461 -1389 0 - -461 -2824 0 - -461 -4107 0 - -461 -4319 0 - -1389 -2824 0 - -1389 -4107 0 - -1389 -4319 0 - -2824 -4107 0 - -2824 -4319 0 - -4107 -4319 0 - -1220 -2353 0 - -1220 -2825 0 - -2353 -2825 0 - -119 -2354 0 - -119 -2445 0 - -2354 -2445 0 - -251 -723 0 - -251 -1221 0 - -251 -2355 0 - -251 -2446 0 - -251 -2826 0 - -251 -3231 0 - -723 -1221 0 - -723 -2355 0 - -723 -2446 0 - -723 -2826 0 - -723 -3231 0 - -1221 -2355 0 - -1221 -2446 0 - -1221 -2826 0 - -1221 -3231 0 - -2355 -2446 0 - -2355 -2826 0 - -2355 -3231 0 - -2446 -2826 0 - -2446 -3231 0 - -2826 -3231 0 - -252 -1390 0 - -252 -2625 0 - -252 -2827 0 - -252 -4108 0 - -1390 -2625 0 - -1390 -2827 0 - -1390 -4108 0 - -2625 -2827 0 - -2625 -4108 0 - -2827 -4108 0 - -18 -253 0 - -18 -584 0 - -18 -1391 0 - -18 -1764 0 - -18 -2711 0 - -18 -2828 0 - -18 -3327 0 - -253 -584 0 - -253 -1391 0 - -253 -1764 0 - -253 -2711 0 - -253 -2828 0 - -253 -3327 0 - -584 -1391 0 - -584 -1764 0 - -584 -2711 0 - -584 -2828 0 - -584 -3327 0 - -1391 -1764 0 - -1391 -2711 0 - -1391 -2828 0 - -1391 -3327 0 - -1764 -2711 0 - -1764 -2828 0 - -1764 -3327 0 - -2711 -2828 0 - -2711 -3327 0 - -2828 -3327 0 - -19 -1080 0 - -19 -1392 0 - -19 -1765 0 - -19 -2712 0 - -19 -2829 0 - -19 -3054 0 - -19 -3328 0 - -19 -3478 0 - -19 -3631 0 - -1080 -1392 0 - -1080 -1765 0 - -1080 -2712 0 - -1080 -2829 0 - -1080 -3054 0 - -1080 -3328 0 - -1080 -3478 0 - -1080 -3631 0 - -1392 -1765 0 - -1392 -2712 0 - -1392 -2829 0 - -1392 -3054 0 - -1392 -3328 0 - -1392 -3478 0 - -1392 -3631 0 - -1765 -2712 0 - -1765 -2829 0 - -1765 -3054 0 - -1765 -3328 0 - -1765 -3478 0 - -1765 -3631 0 - -2712 -2829 0 - -2712 -3054 0 - -2712 -3328 0 - -2712 -3478 0 - -2712 -3631 0 - -2829 -3054 0 - -2829 -3328 0 - -2829 -3478 0 - -2829 -3631 0 - -3054 -3328 0 - -3054 -3478 0 - -3054 -3631 0 - -3328 -3478 0 - -3328 -3631 0 - -3478 -3631 0 - -585 -2626 0 - -585 -2713 0 - -585 -3055 0 - -585 -3479 0 - -2626 -2713 0 - -2626 -3055 0 - -2626 -3479 0 - -2713 -3055 0 - -2713 -3479 0 - -3055 -3479 0 - -1393 -1766 0 - -1393 -2555 0 - -1393 -2627 0 - -1393 -2714 0 - -1393 -2925 0 - -1393 -3329 0 - -1393 -3480 0 - -1393 -3818 0 - -1766 -2555 0 - -1766 -2627 0 - -1766 -2714 0 - -1766 -2925 0 - -1766 -3329 0 - -1766 -3480 0 - -1766 -3818 0 - -2555 -2627 0 - -2555 -2714 0 - -2555 -2925 0 - -2555 -3329 0 - -2555 -3480 0 - -2555 -3818 0 - -2627 -2714 0 - -2627 -2925 0 - -2627 -3329 0 - -2627 -3480 0 - -2627 -3818 0 - -2714 -2925 0 - -2714 -3329 0 - -2714 -3480 0 - -2714 -3818 0 - -2925 -3329 0 - -2925 -3480 0 - -2925 -3818 0 - -3329 -3480 0 - -3329 -3818 0 - -3480 -3818 0 - -20 -462 0 - -20 -586 0 - -20 -1081 0 - -20 -1394 0 - -20 -1767 0 - -20 -2556 0 - -20 -2715 0 - -20 -2830 0 - -20 -2926 0 - -20 -3056 0 - -20 -3481 0 - -20 -3632 0 - -20 -3819 0 - -462 -586 0 - -462 -1081 0 - -462 -1394 0 - -462 -1767 0 - -462 -2556 0 - -462 -2715 0 - -462 -2830 0 - -462 -2926 0 - -462 -3056 0 - -462 -3481 0 - -462 -3632 0 - -462 -3819 0 - -586 -1081 0 - -586 -1394 0 - -586 -1767 0 - -586 -2556 0 - -586 -2715 0 - -586 -2830 0 - -586 -2926 0 - -586 -3056 0 - -586 -3481 0 - -586 -3632 0 - -586 -3819 0 - -1081 -1394 0 - -1081 -1767 0 - -1081 -2556 0 - -1081 -2715 0 - -1081 -2830 0 - -1081 -2926 0 - -1081 -3056 0 - -1081 -3481 0 - -1081 -3632 0 - -1081 -3819 0 - -1394 -1767 0 - -1394 -2556 0 - -1394 -2715 0 - -1394 -2830 0 - -1394 -2926 0 - -1394 -3056 0 - -1394 -3481 0 - -1394 -3632 0 - -1394 -3819 0 - -1767 -2556 0 - -1767 -2715 0 - -1767 -2830 0 - -1767 -2926 0 - -1767 -3056 0 - -1767 -3481 0 - -1767 -3632 0 - -1767 -3819 0 - -2556 -2715 0 - -2556 -2830 0 - -2556 -2926 0 - -2556 -3056 0 - -2556 -3481 0 - -2556 -3632 0 - -2556 -3819 0 - -2715 -2830 0 - -2715 -2926 0 - -2715 -3056 0 - -2715 -3481 0 - -2715 -3632 0 - -2715 -3819 0 - -2830 -2926 0 - -2830 -3056 0 - -2830 -3481 0 - -2830 -3632 0 - -2830 -3819 0 - -2926 -3056 0 - -2926 -3481 0 - -2926 -3632 0 - -2926 -3819 0 - -3056 -3481 0 - -3056 -3632 0 - -3056 -3819 0 - -3481 -3632 0 - -3481 -3819 0 - -3632 -3819 0 - -21 -1082 0 - -21 -1768 0 - -21 -2197 0 - -21 -2716 0 - -21 -3057 0 - -21 -3232 0 - -21 -3482 0 - -1082 -1768 0 - -1082 -2197 0 - -1082 -2716 0 - -1082 -3057 0 - -1082 -3232 0 - -1082 -3482 0 - -1768 -2197 0 - -1768 -2716 0 - -1768 -3057 0 - -1768 -3232 0 - -1768 -3482 0 - -2197 -2716 0 - -2197 -3057 0 - -2197 -3232 0 - -2197 -3482 0 - -2716 -3057 0 - -2716 -3232 0 - -2716 -3482 0 - -3057 -3232 0 - -3057 -3482 0 - -3232 -3482 0 - -1083 -2927 0 - -1083 -3058 0 - -1083 -3233 0 - -1083 -3483 0 - -2927 -3058 0 - -2927 -3233 0 - -2927 -3483 0 - -3058 -3233 0 - -3058 -3483 0 - -3233 -3483 0 - -463 -1395 0 - -463 -2928 0 - -463 -3330 0 - -463 -3484 0 - -1395 -2928 0 - -1395 -3330 0 - -1395 -3484 0 - -2928 -3330 0 - -2928 -3484 0 - -3330 -3484 0 - -254 -464 0 - -254 -1084 0 - -254 -2198 0 - -254 -2557 0 - -254 -3331 0 - -254 -3633 0 - -464 -1084 0 - -464 -2198 0 - -464 -2557 0 - -464 -3331 0 - -464 -3633 0 - -1084 -2198 0 - -1084 -2557 0 - -1084 -3331 0 - -1084 -3633 0 - -2198 -2557 0 - -2198 -3331 0 - -2198 -3633 0 - -2557 -3331 0 - -2557 -3633 0 - -3331 -3633 0 - -255 -465 0 - -255 -587 0 - -255 -1396 0 - -255 -2199 0 - -255 -2558 0 - -255 -2628 0 - -255 -2929 0 - -255 -3059 0 - -255 -3234 0 - -255 -3634 0 - -255 -3820 0 - -465 -587 0 - -465 -1396 0 - -465 -2199 0 - -465 -2558 0 - -465 -2628 0 - -465 -2929 0 - -465 -3059 0 - -465 -3234 0 - -465 -3634 0 - -465 -3820 0 - -587 -1396 0 - -587 -2199 0 - -587 -2558 0 - -587 -2628 0 - -587 -2929 0 - -587 -3059 0 - -587 -3234 0 - -587 -3634 0 - -587 -3820 0 - -1396 -2199 0 - -1396 -2558 0 - -1396 -2628 0 - -1396 -2929 0 - -1396 -3059 0 - -1396 -3234 0 - -1396 -3634 0 - -1396 -3820 0 - -2199 -2558 0 - -2199 -2628 0 - -2199 -2929 0 - -2199 -3059 0 - -2199 -3234 0 - -2199 -3634 0 - -2199 -3820 0 - -2558 -2628 0 - -2558 -2929 0 - -2558 -3059 0 - -2558 -3234 0 - -2558 -3634 0 - -2558 -3820 0 - -2628 -2929 0 - -2628 -3059 0 - -2628 -3234 0 - -2628 -3634 0 - -2628 -3820 0 - -2929 -3059 0 - -2929 -3234 0 - -2929 -3634 0 - -2929 -3820 0 - -3059 -3234 0 - -3059 -3634 0 - -3059 -3820 0 - -3234 -3634 0 - -3234 -3820 0 - -3634 -3820 0 - -22 -466 0 - -22 -588 0 - -22 -2629 0 - -22 -2717 0 - -22 -3235 0 - -22 -3485 0 - -22 -3635 0 - -466 -588 0 - -466 -2629 0 - -466 -2717 0 - -466 -3235 0 - -466 -3485 0 - -466 -3635 0 - -588 -2629 0 - -588 -2717 0 - -588 -3235 0 - -588 -3485 0 - -588 -3635 0 - -2629 -2717 0 - -2629 -3235 0 - -2629 -3485 0 - -2629 -3635 0 - -2717 -3235 0 - -2717 -3485 0 - -2717 -3635 0 - -3235 -3485 0 - -3235 -3635 0 - -3485 -3635 0 - -256 -467 0 - -256 -1085 0 - -256 -2200 0 - -256 -2559 0 - -256 -2718 0 - -256 -2930 0 - -256 -3486 0 - -256 -3636 0 - -467 -1085 0 - -467 -2200 0 - -467 -2559 0 - -467 -2718 0 - -467 -2930 0 - -467 -3486 0 - -467 -3636 0 - -1085 -2200 0 - -1085 -2559 0 - -1085 -2718 0 - -1085 -2930 0 - -1085 -3486 0 - -1085 -3636 0 - -2200 -2559 0 - -2200 -2718 0 - -2200 -2930 0 - -2200 -3486 0 - -2200 -3636 0 - -2559 -2718 0 - -2559 -2930 0 - -2559 -3486 0 - -2559 -3636 0 - -2718 -2930 0 - -2718 -3486 0 - -2718 -3636 0 - -2930 -3486 0 - -2930 -3636 0 - -3486 -3636 0 - -257 -1086 0 - -257 -1397 0 - -257 -2630 0 - -257 -2831 0 - -257 -2931 0 - -257 -3060 0 - -257 -3487 0 - -257 -3821 0 - -1086 -1397 0 - -1086 -2630 0 - -1086 -2831 0 - -1086 -2931 0 - -1086 -3060 0 - -1086 -3487 0 - -1086 -3821 0 - -1397 -2630 0 - -1397 -2831 0 - -1397 -2931 0 - -1397 -3060 0 - -1397 -3487 0 - -1397 -3821 0 - -2630 -2831 0 - -2630 -2931 0 - -2630 -3060 0 - -2630 -3487 0 - -2630 -3821 0 - -2831 -2931 0 - -2831 -3060 0 - -2831 -3487 0 - -2831 -3821 0 - -2931 -3060 0 - -2931 -3487 0 - -2931 -3821 0 - -3060 -3487 0 - -3060 -3821 0 - -3487 -3821 0 - -468 -589 0 - -468 -1398 0 - -468 -2201 0 - -468 -3061 0 - -468 -3236 0 - -468 -3332 0 - -589 -1398 0 - -589 -2201 0 - -589 -3061 0 - -589 -3236 0 - -589 -3332 0 - -1398 -2201 0 - -1398 -3061 0 - -1398 -3236 0 - -1398 -3332 0 - -2201 -3061 0 - -2201 -3236 0 - -2201 -3332 0 - -3061 -3236 0 - -3061 -3332 0 - -3236 -3332 0 - -469 -2932 0 - -469 -3488 0 - -469 -3822 0 - -2932 -3488 0 - -2932 -3822 0 - -3488 -3822 0 - -23 -470 0 - -23 -590 0 - -23 -1399 0 - -23 -1769 0 - -23 -2832 0 - -23 -2933 0 - -23 -3062 0 - -23 -3333 0 - -23 -3823 0 - -470 -590 0 - -470 -1399 0 - -470 -1769 0 - -470 -2832 0 - -470 -2933 0 - -470 -3062 0 - -470 -3333 0 - -470 -3823 0 - -590 -1399 0 - -590 -1769 0 - -590 -2832 0 - -590 -2933 0 - -590 -3062 0 - -590 -3333 0 - -590 -3823 0 - -1399 -1769 0 - -1399 -2832 0 - -1399 -2933 0 - -1399 -3062 0 - -1399 -3333 0 - -1399 -3823 0 - -1769 -2832 0 - -1769 -2933 0 - -1769 -3062 0 - -1769 -3333 0 - -1769 -3823 0 - -2832 -2933 0 - -2832 -3062 0 - -2832 -3333 0 - -2832 -3823 0 - -2933 -3062 0 - -2933 -3333 0 - -2933 -3823 0 - -3062 -3333 0 - -3062 -3823 0 - -3333 -3823 0 - -591 -1087 0 - -591 -1770 0 - -591 -2833 0 - -591 -3334 0 - -591 -3637 0 - -1087 -1770 0 - -1087 -2833 0 - -1087 -3334 0 - -1087 -3637 0 - -1770 -2833 0 - -1770 -3334 0 - -1770 -3637 0 - -2833 -3334 0 - -2833 -3637 0 - -3334 -3637 0 - -471 -1088 0 - -471 -1400 0 - -471 -2202 0 - -471 -3063 0 - -471 -3638 0 - -1088 -1400 0 - -1088 -2202 0 - -1088 -3063 0 - -1088 -3638 0 - -1400 -2202 0 - -1400 -3063 0 - -1400 -3638 0 - -2202 -3063 0 - -2202 -3638 0 - -3063 -3638 0 - -258 -1401 0 - -258 -1771 0 - -258 -2560 0 - -258 -2631 0 - -258 -2719 0 - -258 -2834 0 - -258 -3335 0 - -1401 -1771 0 - -1401 -2560 0 - -1401 -2631 0 - -1401 -2719 0 - -1401 -2834 0 - -1401 -3335 0 - -1771 -2560 0 - -1771 -2631 0 - -1771 -2719 0 - -1771 -2834 0 - -1771 -3335 0 - -2560 -2631 0 - -2560 -2719 0 - -2560 -2834 0 - -2560 -3335 0 - -2631 -2719 0 - -2631 -2834 0 - -2631 -3335 0 - -2719 -2834 0 - -2719 -3335 0 - -2834 -3335 0 - -724 -1402 0 - -724 -1772 0 - -724 -1889 0 - -724 -2447 0 - -724 -3336 0 - -1402 -1772 0 - -1402 -1889 0 - -1402 -2447 0 - -1402 -3336 0 - -1772 -1889 0 - -1772 -2447 0 - -1772 -3336 0 - -1889 -2447 0 - -1889 -3336 0 - -2447 -3336 0 - -1089 -1773 0 - -1089 -2203 0 - -1089 -2448 0 - -1089 -3064 0 - -1089 -3237 0 - -1773 -2203 0 - -1773 -2448 0 - -1773 -3064 0 - -1773 -3237 0 - -2203 -2448 0 - -2203 -3064 0 - -2203 -3237 0 - -2448 -3064 0 - -2448 -3237 0 - -3064 -3237 0 - -120 -472 0 - -120 -1090 0 - -120 -1774 0 - -120 -1890 0 - -120 -2204 0 - -120 -3065 0 - -120 -3238 0 - -472 -1090 0 - -472 -1774 0 - -472 -1890 0 - -472 -2204 0 - -472 -3065 0 - -472 -3238 0 - -1090 -1774 0 - -1090 -1890 0 - -1090 -2204 0 - -1090 -3065 0 - -1090 -3238 0 - -1774 -1890 0 - -1774 -2204 0 - -1774 -3065 0 - -1774 -3238 0 - -1890 -2204 0 - -1890 -3065 0 - -1890 -3238 0 - -2204 -3065 0 - -2204 -3238 0 - -3065 -3238 0 - -121 -473 0 - -121 -1091 0 - -121 -1403 0 - -121 -2205 0 - -121 -3239 0 - -121 -3337 0 - -473 -1091 0 - -473 -1403 0 - -473 -2205 0 - -473 -3239 0 - -473 -3337 0 - -1091 -1403 0 - -1091 -2205 0 - -1091 -3239 0 - -1091 -3337 0 - -1403 -2205 0 - -1403 -3239 0 - -1403 -3337 0 - -2205 -3239 0 - -2205 -3337 0 - -3239 -3337 0 - -474 -725 0 - -474 -1092 0 - -474 -1404 0 - -474 -1775 0 - -474 -2356 0 - -474 -4251 0 - -725 -1092 0 - -725 -1404 0 - -725 -1775 0 - -725 -2356 0 - -725 -4251 0 - -1092 -1404 0 - -1092 -1775 0 - -1092 -2356 0 - -1092 -4251 0 - -1404 -1775 0 - -1404 -2356 0 - -1404 -4251 0 - -1775 -2356 0 - -1775 -4251 0 - -2356 -4251 0 - -122 -726 0 - -122 -1093 0 - -122 -1891 0 - -122 -2357 0 - -122 -3066 0 - -122 -3240 0 - -726 -1093 0 - -726 -1891 0 - -726 -2357 0 - -726 -3066 0 - -726 -3240 0 - -1093 -1891 0 - -1093 -2357 0 - -1093 -3066 0 - -1093 -3240 0 - -1891 -2357 0 - -1891 -3066 0 - -1891 -3240 0 - -2357 -3066 0 - -2357 -3240 0 - -3066 -3240 0 - -123 -475 0 - -123 -1405 0 - -123 -2206 0 - -123 -3067 0 - -123 -3241 0 - -475 -1405 0 - -475 -2206 0 - -475 -3067 0 - -475 -3241 0 - -1405 -2206 0 - -1405 -3067 0 - -1405 -3241 0 - -2206 -3067 0 - -2206 -3241 0 - -3067 -3241 0 - -124 -476 0 - -124 -727 0 - -124 -2358 0 - -476 -727 0 - -476 -2358 0 - -727 -2358 0 - -125 -728 0 - -125 -1406 0 - -125 -1776 0 - -125 -2449 0 - -125 -3338 0 - -125 -4252 0 - -728 -1406 0 - -728 -1776 0 - -728 -2449 0 - -728 -3338 0 - -728 -4252 0 - -1406 -1776 0 - -1406 -2449 0 - -1406 -3338 0 - -1406 -4252 0 - -1776 -2449 0 - -1776 -3338 0 - -1776 -4252 0 - -2449 -3338 0 - -2449 -4252 0 - -3338 -4252 0 - -126 -477 0 - -126 -1407 0 - -126 -1777 0 - -126 -1892 0 - -126 -3068 0 - -126 -3339 0 - -126 -4253 0 - -477 -1407 0 - -477 -1777 0 - -477 -1892 0 - -477 -3068 0 - -477 -3339 0 - -477 -4253 0 - -1407 -1777 0 - -1407 -1892 0 - -1407 -3068 0 - -1407 -3339 0 - -1407 -4253 0 - -1777 -1892 0 - -1777 -3068 0 - -1777 -3339 0 - -1777 -4253 0 - -1892 -3068 0 - -1892 -3339 0 - -1892 -4253 0 - -3068 -3339 0 - -3068 -4253 0 - -3339 -4253 0 - -729 -1893 0 - -729 -2207 0 - -729 -2359 0 - -729 -2450 0 - -729 -3242 0 - -1893 -2207 0 - -1893 -2359 0 - -1893 -2450 0 - -1893 -3242 0 - -2207 -2359 0 - -2207 -2450 0 - -2207 -3242 0 - -2359 -2450 0 - -2359 -3242 0 - -2450 -3242 0 - -127 -1094 0 - -127 -1778 0 - -127 -1894 0 - -127 -2208 0 - -127 -2451 0 - -127 -4254 0 - -1094 -1778 0 - -1094 -1894 0 - -1094 -2208 0 - -1094 -2451 0 - -1094 -4254 0 - -1778 -1894 0 - -1778 -2208 0 - -1778 -2451 0 - -1778 -4254 0 - -1894 -2208 0 - -1894 -2451 0 - -1894 -4254 0 - -2208 -2451 0 - -2208 -4254 0 - -2451 -4254 0 - -1408 -1779 0 - -1408 -1895 0 - -1408 -3340 0 - -1408 -4255 0 - -1779 -1895 0 - -1779 -3340 0 - -1779 -4255 0 - -1895 -3340 0 - -1895 -4255 0 - -3340 -4255 0 - -1409 -1780 0 - -1409 -1896 0 - -1409 -3069 0 - -1409 -3341 0 - -1409 -4256 0 - -1780 -1896 0 - -1780 -3069 0 - -1780 -3341 0 - -1780 -4256 0 - -1896 -3069 0 - -1896 -3341 0 - -1896 -4256 0 - -3069 -3341 0 - -3069 -4256 0 - -3341 -4256 0 - -1410 -1635 0 - -1410 -2720 0 - -1410 -3342 0 - -1635 -2720 0 - -1635 -3342 0 - -2720 -3342 0 - -1311 -1636 0 - -1311 -2632 0 - -1311 -2721 0 - -1636 -2632 0 - -1636 -2721 0 - -2632 -2721 0 - -1411 -1637 0 - -1411 -2039 0 - -1411 -2209 0 - -1411 -3343 0 - -1411 -3824 0 - -1637 -2039 0 - -1637 -2209 0 - -1637 -3343 0 - -1637 -3824 0 - -2039 -2209 0 - -2039 -3343 0 - -2039 -3824 0 - -2209 -3343 0 - -2209 -3824 0 - -3343 -3824 0 - -1638 -2040 0 - -1638 -2633 0 - -1638 -2722 0 - -1638 -3344 0 - -1638 -3825 0 - -2040 -2633 0 - -2040 -2722 0 - -2040 -3344 0 - -2040 -3825 0 - -2633 -2722 0 - -2633 -3344 0 - -2633 -3825 0 - -2722 -3344 0 - -2722 -3825 0 - -3344 -3825 0 - -1639 -2634 0 - -1639 -2723 0 - -2634 -2723 0 - -1312 -1412 0 - -1312 -1640 0 - -1312 -2041 0 - -1312 -2635 0 - -1312 -3826 0 - -1312 -3921 0 - -1412 -1640 0 - -1412 -2041 0 - -1412 -2635 0 - -1412 -3826 0 - -1412 -3921 0 - -1640 -2041 0 - -1640 -2635 0 - -1640 -3826 0 - -1640 -3921 0 - -2041 -2635 0 - -2041 -3826 0 - -2041 -3921 0 - -2635 -3826 0 - -2635 -3921 0 - -3826 -3921 0 - -1313 -3827 0 - -2042 -2210 0 - -2042 -2724 0 - -2042 -3922 0 - -2210 -2724 0 - -2210 -3922 0 - -2724 -3922 0 - -1641 -2211 0 - -1641 -3828 0 - -1641 -3923 0 - -2211 -3828 0 - -2211 -3923 0 - -3828 -3923 0 - -2043 -2212 0 - -2043 -3829 0 - -2043 -3924 0 - -2212 -3829 0 - -2212 -3924 0 - -3829 -3924 0 - -24 -837 0 - -24 -1413 0 - -24 -2044 0 - -24 -2360 0 - -24 -3070 0 - -24 -3639 0 - -24 -4035 0 - -837 -1413 0 - -837 -2044 0 - -837 -2360 0 - -837 -3070 0 - -837 -3639 0 - -837 -4035 0 - -1413 -2044 0 - -1413 -2360 0 - -1413 -3070 0 - -1413 -3639 0 - -1413 -4035 0 - -2044 -2360 0 - -2044 -3070 0 - -2044 -3639 0 - -2044 -4035 0 - -2360 -3070 0 - -2360 -3639 0 - -2360 -4035 0 - -3070 -3639 0 - -3070 -4035 0 - -3639 -4035 0 - -128 -259 0 - -128 -838 0 - -128 -935 0 - -128 -1414 0 - -128 -2045 0 - -128 -2361 0 - -128 -3640 0 - -259 -838 0 - -259 -935 0 - -259 -1414 0 - -259 -2045 0 - -259 -2361 0 - -259 -3640 0 - -838 -935 0 - -838 -1414 0 - -838 -2045 0 - -838 -2361 0 - -838 -3640 0 - -935 -1414 0 - -935 -2045 0 - -935 -2361 0 - -935 -3640 0 - -1414 -2045 0 - -1414 -2361 0 - -1414 -3640 0 - -2045 -2361 0 - -2045 -3640 0 - -2361 -3640 0 - -25 -936 0 - -25 -1415 0 - -25 -1897 0 - -25 -2046 0 - -25 -2934 0 - -25 -3071 0 - -25 -3641 0 - -936 -1415 0 - -936 -1897 0 - -936 -2046 0 - -936 -2934 0 - -936 -3071 0 - -936 -3641 0 - -1415 -1897 0 - -1415 -2046 0 - -1415 -2934 0 - -1415 -3071 0 - -1415 -3641 0 - -1897 -2046 0 - -1897 -2934 0 - -1897 -3071 0 - -1897 -3641 0 - -2046 -2934 0 - -2046 -3071 0 - -2046 -3641 0 - -2934 -3071 0 - -2934 -3641 0 - -3071 -3641 0 - -26 -360 0 - -26 -2047 0 - -26 -3072 0 - -26 -3243 0 - -360 -2047 0 - -360 -3072 0 - -360 -3243 0 - -2047 -3072 0 - -2047 -3243 0 - -3072 -3243 0 - -260 -361 0 - -260 -2636 0 - -260 -3244 0 - -260 -3642 0 - -361 -2636 0 - -361 -3244 0 - -361 -3642 0 - -2636 -3244 0 - -2636 -3642 0 - -3244 -3642 0 - -261 -362 0 - -261 -839 0 - -261 -937 0 - -261 -2048 0 - -261 -2637 0 - -261 -3073 0 - -261 -3245 0 - -261 -3643 0 - -362 -839 0 - -362 -937 0 - -362 -2048 0 - -362 -2637 0 - -362 -3073 0 - -362 -3245 0 - -362 -3643 0 - -839 -937 0 - -839 -2048 0 - -839 -2637 0 - -839 -3073 0 - -839 -3245 0 - -839 -3643 0 - -937 -2048 0 - -937 -2637 0 - -937 -3073 0 - -937 -3245 0 - -937 -3643 0 - -2048 -2637 0 - -2048 -3073 0 - -2048 -3245 0 - -2048 -3643 0 - -2637 -3073 0 - -2637 -3245 0 - -2637 -3643 0 - -3073 -3245 0 - -3073 -3643 0 - -3245 -3643 0 - -27 -129 0 - -27 -1416 0 - -27 -2362 0 - -27 -2638 0 - -27 -3074 0 - -27 -3644 0 - -129 -1416 0 - -129 -2362 0 - -129 -2638 0 - -129 -3074 0 - -129 -3644 0 - -1416 -2362 0 - -1416 -2638 0 - -1416 -3074 0 - -1416 -3644 0 - -2362 -2638 0 - -2362 -3074 0 - -2362 -3644 0 - -2638 -3074 0 - -2638 -3644 0 - -3074 -3644 0 - -28 -130 0 - -28 -262 0 - -28 -840 0 - -28 -1417 0 - -28 -3246 0 - -28 -3645 0 - -130 -262 0 - -130 -840 0 - -130 -1417 0 - -130 -3246 0 - -130 -3645 0 - -262 -840 0 - -262 -1417 0 - -262 -3246 0 - -262 -3645 0 - -840 -1417 0 - -840 -3246 0 - -840 -3645 0 - -1417 -3246 0 - -1417 -3645 0 - -3246 -3645 0 - -29 -1418 0 - -29 -2049 0 - -29 -2363 0 - -29 -3646 0 - -1418 -2049 0 - -1418 -2363 0 - -1418 -3646 0 - -2049 -2363 0 - -2049 -3646 0 - -2363 -3646 0 - -131 -938 0 - -131 -2050 0 - -131 -2278 0 - -131 -2639 0 - -131 -2935 0 - -131 -3075 0 - -131 -3247 0 - -938 -2050 0 - -938 -2278 0 - -938 -2639 0 - -938 -2935 0 - -938 -3075 0 - -938 -3247 0 - -2050 -2278 0 - -2050 -2639 0 - -2050 -2935 0 - -2050 -3075 0 - -2050 -3247 0 - -2278 -2639 0 - -2278 -2935 0 - -2278 -3075 0 - -2278 -3247 0 - -2639 -2935 0 - -2639 -3075 0 - -2639 -3247 0 - -2935 -3075 0 - -2935 -3247 0 - -3075 -3247 0 - -30 -132 0 - -30 -363 0 - -30 -1419 0 - -30 -2051 0 - -30 -2640 0 - -30 -3076 0 - -30 -3248 0 - -132 -363 0 - -132 -1419 0 - -132 -2051 0 - -132 -2640 0 - -132 -3076 0 - -132 -3248 0 - -363 -1419 0 - -363 -2051 0 - -363 -2640 0 - -363 -3076 0 - -363 -3248 0 - -1419 -2051 0 - -1419 -2640 0 - -1419 -3076 0 - -1419 -3248 0 - -2051 -2640 0 - -2051 -3076 0 - -2051 -3248 0 - -2640 -3076 0 - -2640 -3248 0 - -3076 -3248 0 - -133 -364 0 - -133 -939 0 - -133 -1898 0 - -133 -2364 0 - -133 -3077 0 - -133 -3249 0 - -364 -939 0 - -364 -1898 0 - -364 -2364 0 - -364 -3077 0 - -364 -3249 0 - -939 -1898 0 - -939 -2364 0 - -939 -3077 0 - -939 -3249 0 - -1898 -2364 0 - -1898 -3077 0 - -1898 -3249 0 - -2364 -3077 0 - -2364 -3249 0 - -3077 -3249 0 - -134 -263 0 - -134 -940 0 - -134 -2052 0 - -134 -2279 0 - -134 -2936 0 - -134 -3647 0 - -134 -4036 0 - -263 -940 0 - -263 -2052 0 - -263 -2279 0 - -263 -2936 0 - -263 -3647 0 - -263 -4036 0 - -940 -2052 0 - -940 -2279 0 - -940 -2936 0 - -940 -3647 0 - -940 -4036 0 - -2052 -2279 0 - -2052 -2936 0 - -2052 -3647 0 - -2052 -4036 0 - -2279 -2936 0 - -2279 -3647 0 - -2279 -4036 0 - -2936 -3647 0 - -2936 -4036 0 - -3647 -4036 0 - -264 -365 0 - -264 -941 0 - -264 -1420 0 - -264 -1899 0 - -264 -3078 0 - -264 -3648 0 - -365 -941 0 - -365 -1420 0 - -365 -1899 0 - -365 -3078 0 - -365 -3648 0 - -941 -1420 0 - -941 -1899 0 - -941 -3078 0 - -941 -3648 0 - -1420 -1899 0 - -1420 -3078 0 - -1420 -3648 0 - -1899 -3078 0 - -1899 -3648 0 - -3078 -3648 0 - -135 -2280 0 - -135 -2365 0 - -135 -3079 0 - -135 -3649 0 - -2280 -2365 0 - -2280 -3079 0 - -2280 -3649 0 - -2365 -3079 0 - -2365 -3649 0 - -3079 -3649 0 - -31 -136 0 - -31 -1900 0 - -31 -2053 0 - -31 -2281 0 - -31 -3650 0 - -31 -4037 0 - -136 -1900 0 - -136 -2053 0 - -136 -2281 0 - -136 -3650 0 - -136 -4037 0 - -1900 -2053 0 - -1900 -2281 0 - -1900 -3650 0 - -1900 -4037 0 - -2053 -2281 0 - -2053 -3650 0 - -2053 -4037 0 - -2281 -3650 0 - -2281 -4037 0 - -3650 -4037 0 - -32 -265 0 - -32 -841 0 - -32 -942 0 - -32 -1421 0 - -32 -1901 0 - -32 -2054 0 - -32 -2282 0 - -32 -2641 0 - -32 -3080 0 - -265 -841 0 - -265 -942 0 - -265 -1421 0 - -265 -1901 0 - -265 -2054 0 - -265 -2282 0 - -265 -2641 0 - -265 -3080 0 - -841 -942 0 - -841 -1421 0 - -841 -1901 0 - -841 -2054 0 - -841 -2282 0 - -841 -2641 0 - -841 -3080 0 - -942 -1421 0 - -942 -1901 0 - -942 -2054 0 - -942 -2282 0 - -942 -2641 0 - -942 -3080 0 - -1421 -1901 0 - -1421 -2054 0 - -1421 -2282 0 - -1421 -2641 0 - -1421 -3080 0 - -1901 -2054 0 - -1901 -2282 0 - -1901 -2641 0 - -1901 -3080 0 - -2054 -2282 0 - -2054 -2641 0 - -2054 -3080 0 - -2282 -2641 0 - -2282 -3080 0 - -2641 -3080 0 - -1422 -1781 0 - -1422 -2055 0 - -1422 -4038 0 - -1781 -2055 0 - -1781 -4038 0 - -2055 -4038 0 - -366 -1222 0 - -366 -1782 0 - -366 -2056 0 - -1222 -1782 0 - -1222 -2056 0 - -1782 -2056 0 - -137 -2057 0 - -138 -367 0 - -138 -1223 0 - -138 -1423 0 - -138 -1783 0 - -138 -2058 0 - -367 -1223 0 - -367 -1423 0 - -367 -1783 0 - -367 -2058 0 - -1223 -1423 0 - -1223 -1783 0 - -1223 -2058 0 - -1423 -1783 0 - -1423 -2058 0 - -1783 -2058 0 - -139 -368 0 - -139 -478 0 - -139 -1224 0 - -139 -2366 0 - -139 -4039 0 - -368 -478 0 - -368 -1224 0 - -368 -2366 0 - -368 -4039 0 - -478 -1224 0 - -478 -2366 0 - -478 -4039 0 - -1224 -2366 0 - -1224 -4039 0 - -2366 -4039 0 - -140 -369 0 - -140 -1424 0 - -140 -1784 0 - -140 -2059 0 - -140 -3651 0 - -369 -1424 0 - -369 -1784 0 - -369 -2059 0 - -369 -3651 0 - -1424 -1784 0 - -1424 -2059 0 - -1424 -3651 0 - -1784 -2059 0 - -1784 -3651 0 - -2059 -3651 0 - -1225 -1785 0 - -1225 -2367 0 - -1225 -3652 0 - -1225 -4040 0 - -1785 -2367 0 - -1785 -3652 0 - -1785 -4040 0 - -2367 -3652 0 - -2367 -4040 0 - -3652 -4040 0 - -141 -370 0 - -141 -479 0 - -141 -1786 0 - -141 -2368 0 - -141 -3653 0 - -370 -479 0 - -370 -1786 0 - -370 -2368 0 - -370 -3653 0 - -479 -1786 0 - -479 -2368 0 - -479 -3653 0 - -1786 -2368 0 - -1786 -3653 0 - -2368 -3653 0 - -142 -1226 0 - -142 -1787 0 - -142 -2060 0 - -142 -3654 0 - -142 -4041 0 - -1226 -1787 0 - -1226 -2060 0 - -1226 -3654 0 - -1226 -4041 0 - -1787 -2060 0 - -1787 -3654 0 - -1787 -4041 0 - -2060 -3654 0 - -2060 -4041 0 - -3654 -4041 0 - -1227 -1425 0 - -1227 -1788 0 - -1227 -2061 0 - -1425 -1788 0 - -1425 -2061 0 - -1788 -2061 0 - -1228 -1426 0 - -1228 -2062 0 - -1228 -2369 0 - -1228 -2835 0 - -1228 -3081 0 - -1228 -3345 0 - -1228 -3489 0 - -1228 -3655 0 - -1228 -3925 0 - -1228 -4109 0 - -1426 -2062 0 - -1426 -2369 0 - -1426 -2835 0 - -1426 -3081 0 - -1426 -3345 0 - -1426 -3489 0 - -1426 -3655 0 - -1426 -3925 0 - -1426 -4109 0 - -2062 -2369 0 - -2062 -2835 0 - -2062 -3081 0 - -2062 -3345 0 - -2062 -3489 0 - -2062 -3655 0 - -2062 -3925 0 - -2062 -4109 0 - -2369 -2835 0 - -2369 -3081 0 - -2369 -3345 0 - -2369 -3489 0 - -2369 -3655 0 - -2369 -3925 0 - -2369 -4109 0 - -2835 -3081 0 - -2835 -3345 0 - -2835 -3489 0 - -2835 -3655 0 - -2835 -3925 0 - -2835 -4109 0 - -3081 -3345 0 - -3081 -3489 0 - -3081 -3655 0 - -3081 -3925 0 - -3081 -4109 0 - -3345 -3489 0 - -3345 -3655 0 - -3345 -3925 0 - -3345 -4109 0 - -3489 -3655 0 - -3489 -3925 0 - -3489 -4109 0 - -3655 -3925 0 - -3655 -4109 0 - -3925 -4109 0 - -480 -943 0 - -480 -1427 0 - -480 -2063 0 - -480 -2836 0 - -480 -3082 0 - -480 -3490 0 - -480 -3656 0 - -480 -3926 0 - -943 -1427 0 - -943 -2063 0 - -943 -2836 0 - -943 -3082 0 - -943 -3490 0 - -943 -3656 0 - -943 -3926 0 - -1427 -2063 0 - -1427 -2836 0 - -1427 -3082 0 - -1427 -3490 0 - -1427 -3656 0 - -1427 -3926 0 - -2063 -2836 0 - -2063 -3082 0 - -2063 -3490 0 - -2063 -3656 0 - -2063 -3926 0 - -2836 -3082 0 - -2836 -3490 0 - -2836 -3656 0 - -2836 -3926 0 - -3082 -3490 0 - -3082 -3656 0 - -3082 -3926 0 - -3490 -3656 0 - -3490 -3926 0 - -3656 -3926 0 - -371 -1229 0 - -371 -2064 0 - -371 -2452 0 - -371 -3083 0 - -371 -3491 0 - -371 -3927 0 - -1229 -2064 0 - -1229 -2452 0 - -1229 -3083 0 - -1229 -3491 0 - -1229 -3927 0 - -2064 -2452 0 - -2064 -3083 0 - -2064 -3491 0 - -2064 -3927 0 - -2452 -3083 0 - -2452 -3491 0 - -2452 -3927 0 - -3083 -3491 0 - -3083 -3927 0 - -3491 -3927 0 - -266 -372 0 - -266 -2453 0 - -266 -2642 0 - -266 -2837 0 - -266 -3346 0 - -266 -3657 0 - -266 -3928 0 - -266 -4110 0 - -372 -2453 0 - -372 -2642 0 - -372 -2837 0 - -372 -3346 0 - -372 -3657 0 - -372 -3928 0 - -372 -4110 0 - -2453 -2642 0 - -2453 -2837 0 - -2453 -3346 0 - -2453 -3657 0 - -2453 -3928 0 - -2453 -4110 0 - -2642 -2837 0 - -2642 -3346 0 - -2642 -3657 0 - -2642 -3928 0 - -2642 -4110 0 - -2837 -3346 0 - -2837 -3657 0 - -2837 -3928 0 - -2837 -4110 0 - -3346 -3657 0 - -3346 -3928 0 - -3346 -4110 0 - -3657 -3928 0 - -3657 -4110 0 - -3928 -4110 0 - -373 -481 0 - -373 -1428 0 - -373 -2454 0 - -373 -2838 0 - -373 -3347 0 - -373 -3492 0 - -373 -3658 0 - -373 -4257 0 - -481 -1428 0 - -481 -2454 0 - -481 -2838 0 - -481 -3347 0 - -481 -3492 0 - -481 -3658 0 - -481 -4257 0 - -1428 -2454 0 - -1428 -2838 0 - -1428 -3347 0 - -1428 -3492 0 - -1428 -3658 0 - -1428 -4257 0 - -2454 -2838 0 - -2454 -3347 0 - -2454 -3492 0 - -2454 -3658 0 - -2454 -4257 0 - -2838 -3347 0 - -2838 -3492 0 - -2838 -3658 0 - -2838 -4257 0 - -3347 -3492 0 - -3347 -3658 0 - -3347 -4257 0 - -3492 -3658 0 - -3492 -4257 0 - -3658 -4257 0 - -267 -374 0 - -267 -482 0 - -267 -944 0 - -267 -2065 0 - -267 -2455 0 - -267 -2643 0 - -267 -3084 0 - -267 -3348 0 - -267 -3659 0 - -374 -482 0 - -374 -944 0 - -374 -2065 0 - -374 -2455 0 - -374 -2643 0 - -374 -3084 0 - -374 -3348 0 - -374 -3659 0 - -482 -944 0 - -482 -2065 0 - -482 -2455 0 - -482 -2643 0 - -482 -3084 0 - -482 -3348 0 - -482 -3659 0 - -944 -2065 0 - -944 -2455 0 - -944 -2643 0 - -944 -3084 0 - -944 -3348 0 - -944 -3659 0 - -2065 -2455 0 - -2065 -2643 0 - -2065 -3084 0 - -2065 -3348 0 - -2065 -3659 0 - -2455 -2643 0 - -2455 -3084 0 - -2455 -3348 0 - -2455 -3659 0 - -2643 -3084 0 - -2643 -3348 0 - -2643 -3659 0 - -3084 -3348 0 - -3084 -3659 0 - -3348 -3659 0 - -143 -1314 0 - -143 -1429 0 - -143 -2370 0 - -143 -2644 0 - -143 -3085 0 - -143 -3349 0 - -143 -3660 0 - -143 -4111 0 - -1314 -1429 0 - -1314 -2370 0 - -1314 -2644 0 - -1314 -3085 0 - -1314 -3349 0 - -1314 -3660 0 - -1314 -4111 0 - -1429 -2370 0 - -1429 -2644 0 - -1429 -3085 0 - -1429 -3349 0 - -1429 -3660 0 - -1429 -4111 0 - -2370 -2644 0 - -2370 -3085 0 - -2370 -3349 0 - -2370 -3660 0 - -2370 -4111 0 - -2644 -3085 0 - -2644 -3349 0 - -2644 -3660 0 - -2644 -4111 0 - -3085 -3349 0 - -3085 -3660 0 - -3085 -4111 0 - -3349 -3660 0 - -3349 -4111 0 - -3660 -4111 0 - -144 -375 0 - -144 -1230 0 - -144 -1430 0 - -144 -2066 0 - -144 -2456 0 - -144 -2645 0 - -144 -2839 0 - -144 -3086 0 - -144 -3929 0 - -144 -4112 0 - -375 -1230 0 - -375 -1430 0 - -375 -2066 0 - -375 -2456 0 - -375 -2645 0 - -375 -2839 0 - -375 -3086 0 - -375 -3929 0 - -375 -4112 0 - -1230 -1430 0 - -1230 -2066 0 - -1230 -2456 0 - -1230 -2645 0 - -1230 -2839 0 - -1230 -3086 0 - -1230 -3929 0 - -1230 -4112 0 - -1430 -2066 0 - -1430 -2456 0 - -1430 -2645 0 - -1430 -2839 0 - -1430 -3086 0 - -1430 -3929 0 - -1430 -4112 0 - -2066 -2456 0 - -2066 -2645 0 - -2066 -2839 0 - -2066 -3086 0 - -2066 -3929 0 - -2066 -4112 0 - -2456 -2645 0 - -2456 -2839 0 - -2456 -3086 0 - -2456 -3929 0 - -2456 -4112 0 - -2645 -2839 0 - -2645 -3086 0 - -2645 -3929 0 - -2645 -4112 0 - -2839 -3086 0 - -2839 -3929 0 - -2839 -4112 0 - -3086 -3929 0 - -3086 -4112 0 - -3929 -4112 0 - -145 -376 0 - -145 -945 0 - -145 -1315 0 - -145 -2371 0 - -145 -3087 0 - -145 -3493 0 - -376 -945 0 - -376 -1315 0 - -376 -2371 0 - -376 -3087 0 - -376 -3493 0 - -945 -1315 0 - -945 -2371 0 - -945 -3087 0 - -945 -3493 0 - -1315 -2371 0 - -1315 -3087 0 - -1315 -3493 0 - -2371 -3087 0 - -2371 -3493 0 - -3087 -3493 0 - -146 -268 0 - -146 -483 0 - -146 -1316 0 - -146 -1431 0 - -146 -2646 0 - -146 -3088 0 - -146 -3661 0 - -268 -483 0 - -268 -1316 0 - -268 -1431 0 - -268 -2646 0 - -268 -3088 0 - -268 -3661 0 - -483 -1316 0 - -483 -1431 0 - -483 -2646 0 - -483 -3088 0 - -483 -3661 0 - -1316 -1431 0 - -1316 -2646 0 - -1316 -3088 0 - -1316 -3661 0 - -1431 -2646 0 - -1431 -3088 0 - -1431 -3661 0 - -2646 -3088 0 - -2646 -3661 0 - -3088 -3661 0 - -484 -946 0 - -484 -2372 0 - -484 -2647 0 - -484 -3494 0 - -484 -3662 0 - -946 -2372 0 - -946 -2647 0 - -946 -3494 0 - -946 -3662 0 - -2372 -2647 0 - -2372 -3494 0 - -2372 -3662 0 - -2647 -3494 0 - -2647 -3662 0 - -3494 -3662 0 - -147 -269 0 - -147 -485 0 - -147 -947 0 - -147 -1231 0 - -147 -1317 0 - -147 -2067 0 - -147 -2283 0 - -147 -2457 0 - -147 -3495 0 - -147 -3663 0 - -147 -4113 0 - -147 -4258 0 - -269 -485 0 - -269 -947 0 - -269 -1231 0 - -269 -1317 0 - -269 -2067 0 - -269 -2283 0 - -269 -2457 0 - -269 -3495 0 - -269 -3663 0 - -269 -4113 0 - -269 -4258 0 - -485 -947 0 - -485 -1231 0 - -485 -1317 0 - -485 -2067 0 - -485 -2283 0 - -485 -2457 0 - -485 -3495 0 - -485 -3663 0 - -485 -4113 0 - -485 -4258 0 - -947 -1231 0 - -947 -1317 0 - -947 -2067 0 - -947 -2283 0 - -947 -2457 0 - -947 -3495 0 - -947 -3663 0 - -947 -4113 0 - -947 -4258 0 - -1231 -1317 0 - -1231 -2067 0 - -1231 -2283 0 - -1231 -2457 0 - -1231 -3495 0 - -1231 -3663 0 - -1231 -4113 0 - -1231 -4258 0 - -1317 -2067 0 - -1317 -2283 0 - -1317 -2457 0 - -1317 -3495 0 - -1317 -3663 0 - -1317 -4113 0 - -1317 -4258 0 - -2067 -2283 0 - -2067 -2457 0 - -2067 -3495 0 - -2067 -3663 0 - -2067 -4113 0 - -2067 -4258 0 - -2283 -2457 0 - -2283 -3495 0 - -2283 -3663 0 - -2283 -4113 0 - -2283 -4258 0 - -2457 -3495 0 - -2457 -3663 0 - -2457 -4113 0 - -2457 -4258 0 - -3495 -3663 0 - -3495 -4113 0 - -3495 -4258 0 - -3663 -4113 0 - -3663 -4258 0 - -4113 -4258 0 - -270 -1318 0 - -270 -1432 0 - -270 -2068 0 - -270 -2284 0 - -270 -2373 0 - -270 -2648 0 - -270 -2840 0 - -270 -3089 0 - -270 -3496 0 - -270 -3930 0 - -1318 -1432 0 - -1318 -2068 0 - -1318 -2284 0 - -1318 -2373 0 - -1318 -2648 0 - -1318 -2840 0 - -1318 -3089 0 - -1318 -3496 0 - -1318 -3930 0 - -1432 -2068 0 - -1432 -2284 0 - -1432 -2373 0 - -1432 -2648 0 - -1432 -2840 0 - -1432 -3089 0 - -1432 -3496 0 - -1432 -3930 0 - -2068 -2284 0 - -2068 -2373 0 - -2068 -2648 0 - -2068 -2840 0 - -2068 -3089 0 - -2068 -3496 0 - -2068 -3930 0 - -2284 -2373 0 - -2284 -2648 0 - -2284 -2840 0 - -2284 -3089 0 - -2284 -3496 0 - -2284 -3930 0 - -2373 -2648 0 - -2373 -2840 0 - -2373 -3089 0 - -2373 -3496 0 - -2373 -3930 0 - -2648 -2840 0 - -2648 -3089 0 - -2648 -3496 0 - -2648 -3930 0 - -2840 -3089 0 - -2840 -3496 0 - -2840 -3930 0 - -3089 -3496 0 - -3089 -3930 0 - -3496 -3930 0 - -486 -1232 0 - -486 -1433 0 - -486 -2069 0 - -486 -3090 0 - -486 -3350 0 - -486 -4259 0 - -1232 -1433 0 - -1232 -2069 0 - -1232 -3090 0 - -1232 -3350 0 - -1232 -4259 0 - -1433 -2069 0 - -1433 -3090 0 - -1433 -3350 0 - -1433 -4259 0 - -2069 -3090 0 - -2069 -3350 0 - -2069 -4259 0 - -3090 -3350 0 - -3090 -4259 0 - -3350 -4259 0 - -148 -377 0 - -148 -487 0 - -148 -1233 0 - -148 -1319 0 - -148 -2285 0 - -148 -2374 0 - -148 -3497 0 - -377 -487 0 - -377 -1233 0 - -377 -1319 0 - -377 -2285 0 - -377 -2374 0 - -377 -3497 0 - -487 -1233 0 - -487 -1319 0 - -487 -2285 0 - -487 -2374 0 - -487 -3497 0 - -1233 -1319 0 - -1233 -2285 0 - -1233 -2374 0 - -1233 -3497 0 - -1319 -2285 0 - -1319 -2374 0 - -1319 -3497 0 - -2285 -2374 0 - -2285 -3497 0 - -2374 -3497 0 - -149 -378 0 - -149 -1434 0 - -149 -2070 0 - -149 -2458 0 - -149 -3351 0 - -149 -3664 0 - -149 -4114 0 - -149 -4260 0 - -378 -1434 0 - -378 -2070 0 - -378 -2458 0 - -378 -3351 0 - -378 -3664 0 - -378 -4114 0 - -378 -4260 0 - -1434 -2070 0 - -1434 -2458 0 - -1434 -3351 0 - -1434 -3664 0 - -1434 -4114 0 - -1434 -4260 0 - -2070 -2458 0 - -2070 -3351 0 - -2070 -3664 0 - -2070 -4114 0 - -2070 -4260 0 - -2458 -3351 0 - -2458 -3664 0 - -2458 -4114 0 - -2458 -4260 0 - -3351 -3664 0 - -3351 -4114 0 - -3351 -4260 0 - -3664 -4114 0 - -3664 -4260 0 - -4114 -4260 0 - -150 -488 0 - -150 -948 0 - -150 -1435 0 - -150 -2841 0 - -150 -3091 0 - -150 -3352 0 - -150 -4115 0 - -150 -4261 0 - -488 -948 0 - -488 -1435 0 - -488 -2841 0 - -488 -3091 0 - -488 -3352 0 - -488 -4115 0 - -488 -4261 0 - -948 -1435 0 - -948 -2841 0 - -948 -3091 0 - -948 -3352 0 - -948 -4115 0 - -948 -4261 0 - -1435 -2841 0 - -1435 -3091 0 - -1435 -3352 0 - -1435 -4115 0 - -1435 -4261 0 - -2841 -3091 0 - -2841 -3352 0 - -2841 -4115 0 - -2841 -4261 0 - -3091 -3352 0 - -3091 -4115 0 - -3091 -4261 0 - -3352 -4115 0 - -3352 -4261 0 - -4115 -4261 0 - -271 -379 0 - -271 -949 0 - -271 -1234 0 - -271 -2071 0 - -271 -2375 0 - -271 -2459 0 - -271 -2842 0 - -271 -3498 0 - -271 -3931 0 - -379 -949 0 - -379 -1234 0 - -379 -2071 0 - -379 -2375 0 - -379 -2459 0 - -379 -2842 0 - -379 -3498 0 - -379 -3931 0 - -949 -1234 0 - -949 -2071 0 - -949 -2375 0 - -949 -2459 0 - -949 -2842 0 - -949 -3498 0 - -949 -3931 0 - -1234 -2071 0 - -1234 -2375 0 - -1234 -2459 0 - -1234 -2842 0 - -1234 -3498 0 - -1234 -3931 0 - -2071 -2375 0 - -2071 -2459 0 - -2071 -2842 0 - -2071 -3498 0 - -2071 -3931 0 - -2375 -2459 0 - -2375 -2842 0 - -2375 -3498 0 - -2375 -3931 0 - -2459 -2842 0 - -2459 -3498 0 - -2459 -3931 0 - -2842 -3498 0 - -2842 -3931 0 - -3498 -3931 0 - -151 -272 0 - -151 -380 0 - -151 -489 0 - -151 -2286 0 - -151 -2376 0 - -151 -2843 0 - -151 -3092 0 - -151 -3499 0 - -151 -3665 0 - -151 -3932 0 - -272 -380 0 - -272 -489 0 - -272 -2286 0 - -272 -2376 0 - -272 -2843 0 - -272 -3092 0 - -272 -3499 0 - -272 -3665 0 - -272 -3932 0 - -380 -489 0 - -380 -2286 0 - -380 -2376 0 - -380 -2843 0 - -380 -3092 0 - -380 -3499 0 - -380 -3665 0 - -380 -3932 0 - -489 -2286 0 - -489 -2376 0 - -489 -2843 0 - -489 -3092 0 - -489 -3499 0 - -489 -3665 0 - -489 -3932 0 - -2286 -2376 0 - -2286 -2843 0 - -2286 -3092 0 - -2286 -3499 0 - -2286 -3665 0 - -2286 -3932 0 - -2376 -2843 0 - -2376 -3092 0 - -2376 -3499 0 - -2376 -3665 0 - -2376 -3932 0 - -2843 -3092 0 - -2843 -3499 0 - -2843 -3665 0 - -2843 -3932 0 - -3092 -3499 0 - -3092 -3665 0 - -3092 -3932 0 - -3499 -3665 0 - -3499 -3932 0 - -3665 -3932 0 - -152 -1235 0 - -152 -2072 0 - -152 -2287 0 - -152 -2460 0 - -152 -2844 0 - -152 -3666 0 - -152 -3933 0 - -152 -4116 0 - -152 -4262 0 - -1235 -2072 0 - -1235 -2287 0 - -1235 -2460 0 - -1235 -2844 0 - -1235 -3666 0 - -1235 -3933 0 - -1235 -4116 0 - -1235 -4262 0 - -2072 -2287 0 - -2072 -2460 0 - -2072 -2844 0 - -2072 -3666 0 - -2072 -3933 0 - -2072 -4116 0 - -2072 -4262 0 - -2287 -2460 0 - -2287 -2844 0 - -2287 -3666 0 - -2287 -3933 0 - -2287 -4116 0 - -2287 -4262 0 - -2460 -2844 0 - -2460 -3666 0 - -2460 -3933 0 - -2460 -4116 0 - -2460 -4262 0 - -2844 -3666 0 - -2844 -3933 0 - -2844 -4116 0 - -2844 -4262 0 - -3666 -3933 0 - -3666 -4116 0 - -3666 -4262 0 - -3933 -4116 0 - -3933 -4262 0 - -4116 -4262 0 - -273 -381 0 - -273 -950 0 - -273 -1436 0 - -273 -2073 0 - -273 -2288 0 - -273 -2649 0 - -273 -2845 0 - -273 -3353 0 - -273 -4117 0 - -273 -4263 0 - -381 -950 0 - -381 -1436 0 - -381 -2073 0 - -381 -2288 0 - -381 -2649 0 - -381 -2845 0 - -381 -3353 0 - -381 -4117 0 - -381 -4263 0 - -950 -1436 0 - -950 -2073 0 - -950 -2288 0 - -950 -2649 0 - -950 -2845 0 - -950 -3353 0 - -950 -4117 0 - -950 -4263 0 - -1436 -2073 0 - -1436 -2288 0 - -1436 -2649 0 - -1436 -2845 0 - -1436 -3353 0 - -1436 -4117 0 - -1436 -4263 0 - -2073 -2288 0 - -2073 -2649 0 - -2073 -2845 0 - -2073 -3353 0 - -2073 -4117 0 - -2073 -4263 0 - -2288 -2649 0 - -2288 -2845 0 - -2288 -3353 0 - -2288 -4117 0 - -2288 -4263 0 - -2649 -2845 0 - -2649 -3353 0 - -2649 -4117 0 - -2649 -4263 0 - -2845 -3353 0 - -2845 -4117 0 - -2845 -4263 0 - -3353 -4117 0 - -3353 -4263 0 - -4117 -4263 0 - -730 -951 0 - -730 -1320 0 - -730 -2937 0 - -730 -3354 0 - -730 -3830 0 - -730 -3934 0 - -951 -1320 0 - -951 -2937 0 - -951 -3354 0 - -951 -3830 0 - -951 -3934 0 - -1320 -2937 0 - -1320 -3354 0 - -1320 -3830 0 - -1320 -3934 0 - -2937 -3354 0 - -2937 -3830 0 - -2937 -3934 0 - -3354 -3830 0 - -3354 -3934 0 - -3830 -3934 0 - -490 -952 0 - -490 -1095 0 - -490 -1642 0 - -490 -2938 0 - -490 -3093 0 - -490 -3250 0 - -490 -3667 0 - -490 -3935 0 - -952 -1095 0 - -952 -1642 0 - -952 -2938 0 - -952 -3093 0 - -952 -3250 0 - -952 -3667 0 - -952 -3935 0 - -1095 -1642 0 - -1095 -2938 0 - -1095 -3093 0 - -1095 -3250 0 - -1095 -3667 0 - -1095 -3935 0 - -1642 -2938 0 - -1642 -3093 0 - -1642 -3250 0 - -1642 -3667 0 - -1642 -3935 0 - -2938 -3093 0 - -2938 -3250 0 - -2938 -3667 0 - -2938 -3935 0 - -3093 -3250 0 - -3093 -3667 0 - -3093 -3935 0 - -3250 -3667 0 - -3250 -3935 0 - -3667 -3935 0 - -592 -731 0 - -592 -1569 0 - -592 -1643 0 - -592 -3251 0 - -592 -3355 0 - -592 -3668 0 - -592 -3831 0 - -592 -3936 0 - -592 -4118 0 - -731 -1569 0 - -731 -1643 0 - -731 -3251 0 - -731 -3355 0 - -731 -3668 0 - -731 -3831 0 - -731 -3936 0 - -731 -4118 0 - -1569 -1643 0 - -1569 -3251 0 - -1569 -3355 0 - -1569 -3668 0 - -1569 -3831 0 - -1569 -3936 0 - -1569 -4118 0 - -1643 -3251 0 - -1643 -3355 0 - -1643 -3668 0 - -1643 -3831 0 - -1643 -3936 0 - -1643 -4118 0 - -3251 -3355 0 - -3251 -3668 0 - -3251 -3831 0 - -3251 -3936 0 - -3251 -4118 0 - -3355 -3668 0 - -3355 -3831 0 - -3355 -3936 0 - -3355 -4118 0 - -3668 -3831 0 - -3668 -3936 0 - -3668 -4118 0 - -3831 -3936 0 - -3831 -4118 0 - -3936 -4118 0 - -491 -732 0 - -491 -1321 0 - -491 -1570 0 - -491 -1644 0 - -491 -2289 0 - -491 -2939 0 - -491 -3356 0 - -491 -4119 0 - -732 -1321 0 - -732 -1570 0 - -732 -1644 0 - -732 -2289 0 - -732 -2939 0 - -732 -3356 0 - -732 -4119 0 - -1321 -1570 0 - -1321 -1644 0 - -1321 -2289 0 - -1321 -2939 0 - -1321 -3356 0 - -1321 -4119 0 - -1570 -1644 0 - -1570 -2289 0 - -1570 -2939 0 - -1570 -3356 0 - -1570 -4119 0 - -1644 -2289 0 - -1644 -2939 0 - -1644 -3356 0 - -1644 -4119 0 - -2289 -2939 0 - -2289 -3356 0 - -2289 -4119 0 - -2939 -3356 0 - -2939 -4119 0 - -3356 -4119 0 - -492 -733 0 - -492 -953 0 - -492 -1096 0 - -492 -1571 0 - -492 -3357 0 - -492 -3669 0 - -492 -4120 0 - -733 -953 0 - -733 -1096 0 - -733 -1571 0 - -733 -3357 0 - -733 -3669 0 - -733 -4120 0 - -953 -1096 0 - -953 -1571 0 - -953 -3357 0 - -953 -3669 0 - -953 -4120 0 - -1096 -1571 0 - -1096 -3357 0 - -1096 -3669 0 - -1096 -4120 0 - -1571 -3357 0 - -1571 -3669 0 - -1571 -4120 0 - -3357 -3669 0 - -3357 -4120 0 - -3669 -4120 0 - -1322 -1572 0 - -1322 -3094 0 - -1322 -3358 0 - -1322 -3670 0 - -1322 -3832 0 - -1322 -4121 0 - -1572 -3094 0 - -1572 -3358 0 - -1572 -3670 0 - -1572 -3832 0 - -1572 -4121 0 - -3094 -3358 0 - -3094 -3670 0 - -3094 -3832 0 - -3094 -4121 0 - -3358 -3670 0 - -3358 -3832 0 - -3358 -4121 0 - -3670 -3832 0 - -3670 -4121 0 - -3832 -4121 0 - -493 -1097 0 - -493 -1236 0 - -493 -1645 0 - -493 -3252 0 - -493 -3359 0 - -493 -3671 0 - -493 -3833 0 - -493 -3937 0 - -493 -4122 0 - -1097 -1236 0 - -1097 -1645 0 - -1097 -3252 0 - -1097 -3359 0 - -1097 -3671 0 - -1097 -3833 0 - -1097 -3937 0 - -1097 -4122 0 - -1236 -1645 0 - -1236 -3252 0 - -1236 -3359 0 - -1236 -3671 0 - -1236 -3833 0 - -1236 -3937 0 - -1236 -4122 0 - -1645 -3252 0 - -1645 -3359 0 - -1645 -3671 0 - -1645 -3833 0 - -1645 -3937 0 - -1645 -4122 0 - -3252 -3359 0 - -3252 -3671 0 - -3252 -3833 0 - -3252 -3937 0 - -3252 -4122 0 - -3359 -3671 0 - -3359 -3833 0 - -3359 -3937 0 - -3359 -4122 0 - -3671 -3833 0 - -3671 -3937 0 - -3671 -4122 0 - -3833 -3937 0 - -3833 -4122 0 - -3937 -4122 0 - -494 -593 0 - -494 -734 0 - -494 -1098 0 - -494 -1237 0 - -494 -1573 0 - -494 -3672 0 - -593 -734 0 - -593 -1098 0 - -593 -1237 0 - -593 -1573 0 - -593 -3672 0 - -734 -1098 0 - -734 -1237 0 - -734 -1573 0 - -734 -3672 0 - -1098 -1237 0 - -1098 -1573 0 - -1098 -3672 0 - -1237 -1573 0 - -1237 -3672 0 - -1573 -3672 0 - -735 -1099 0 - -735 -1238 0 - -735 -3095 0 - -735 -3253 0 - -735 -3834 0 - -735 -3938 0 - -735 -4123 0 - -1099 -1238 0 - -1099 -3095 0 - -1099 -3253 0 - -1099 -3834 0 - -1099 -3938 0 - -1099 -4123 0 - -1238 -3095 0 - -1238 -3253 0 - -1238 -3834 0 - -1238 -3938 0 - -1238 -4123 0 - -3095 -3253 0 - -3095 -3834 0 - -3095 -3938 0 - -3095 -4123 0 - -3253 -3834 0 - -3253 -3938 0 - -3253 -4123 0 - -3834 -3938 0 - -3834 -4123 0 - -3938 -4123 0 - -736 -954 0 - -736 -1239 0 - -736 -1323 0 - -736 -1574 0 - -736 -3096 0 - -736 -3673 0 - -736 -3835 0 - -736 -3939 0 - -954 -1239 0 - -954 -1323 0 - -954 -1574 0 - -954 -3096 0 - -954 -3673 0 - -954 -3835 0 - -954 -3939 0 - -1239 -1323 0 - -1239 -1574 0 - -1239 -3096 0 - -1239 -3673 0 - -1239 -3835 0 - -1239 -3939 0 - -1323 -1574 0 - -1323 -3096 0 - -1323 -3673 0 - -1323 -3835 0 - -1323 -3939 0 - -1574 -3096 0 - -1574 -3673 0 - -1574 -3835 0 - -1574 -3939 0 - -3096 -3673 0 - -3096 -3835 0 - -3096 -3939 0 - -3673 -3835 0 - -3673 -3939 0 - -3835 -3939 0 - -737 -2940 0 - -737 -3360 0 - -737 -3674 0 - -737 -4124 0 - -2940 -3360 0 - -2940 -3674 0 - -2940 -4124 0 - -3360 -3674 0 - -3360 -4124 0 - -3674 -4124 0 - -495 -594 0 - -495 -955 0 - -495 -2941 0 - -495 -3097 0 - -495 -3361 0 - -495 -3836 0 - -495 -4125 0 - -594 -955 0 - -594 -2941 0 - -594 -3097 0 - -594 -3361 0 - -594 -3836 0 - -594 -4125 0 - -955 -2941 0 - -955 -3097 0 - -955 -3361 0 - -955 -3836 0 - -955 -4125 0 - -2941 -3097 0 - -2941 -3361 0 - -2941 -3836 0 - -2941 -4125 0 - -3097 -3361 0 - -3097 -3836 0 - -3097 -4125 0 - -3361 -3836 0 - -3361 -4125 0 - -3836 -4125 0 - -595 -1100 0 - -595 -1240 0 - -595 -1324 0 - -595 -3362 0 - -595 -3675 0 - -595 -3940 0 - -1100 -1240 0 - -1100 -1324 0 - -1100 -3362 0 - -1100 -3675 0 - -1100 -3940 0 - -1240 -1324 0 - -1240 -3362 0 - -1240 -3675 0 - -1240 -3940 0 - -1324 -3362 0 - -1324 -3675 0 - -1324 -3940 0 - -3362 -3675 0 - -3362 -3940 0 - -3675 -3940 0 - -596 -1101 0 - -596 -1325 0 - -596 -1646 0 - -596 -2290 0 - -596 -3098 0 - -596 -3363 0 - -596 -3676 0 - -1101 -1325 0 - -1101 -1646 0 - -1101 -2290 0 - -1101 -3098 0 - -1101 -3363 0 - -1101 -3676 0 - -1325 -1646 0 - -1325 -2290 0 - -1325 -3098 0 - -1325 -3363 0 - -1325 -3676 0 - -1646 -2290 0 - -1646 -3098 0 - -1646 -3363 0 - -1646 -3676 0 - -2290 -3098 0 - -2290 -3363 0 - -2290 -3676 0 - -3098 -3363 0 - -3098 -3676 0 - -3363 -3676 0 - -597 -738 0 - -597 -956 0 - -597 -1241 0 - -597 -3254 0 - -597 -3941 0 - -738 -956 0 - -738 -1241 0 - -738 -3254 0 - -738 -3941 0 - -956 -1241 0 - -956 -3254 0 - -956 -3941 0 - -1241 -3254 0 - -1241 -3941 0 - -3254 -3941 0 - -496 -598 0 - -496 -739 0 - -496 -1102 0 - -496 -1647 0 - -496 -2291 0 - -496 -2942 0 - -496 -3099 0 - -496 -3255 0 - -496 -3677 0 - -496 -3837 0 - -496 -3942 0 - -598 -739 0 - -598 -1102 0 - -598 -1647 0 - -598 -2291 0 - -598 -2942 0 - -598 -3099 0 - -598 -3255 0 - -598 -3677 0 - -598 -3837 0 - -598 -3942 0 - -739 -1102 0 - -739 -1647 0 - -739 -2291 0 - -739 -2942 0 - -739 -3099 0 - -739 -3255 0 - -739 -3677 0 - -739 -3837 0 - -739 -3942 0 - -1102 -1647 0 - -1102 -2291 0 - -1102 -2942 0 - -1102 -3099 0 - -1102 -3255 0 - -1102 -3677 0 - -1102 -3837 0 - -1102 -3942 0 - -1647 -2291 0 - -1647 -2942 0 - -1647 -3099 0 - -1647 -3255 0 - -1647 -3677 0 - -1647 -3837 0 - -1647 -3942 0 - -2291 -2942 0 - -2291 -3099 0 - -2291 -3255 0 - -2291 -3677 0 - -2291 -3837 0 - -2291 -3942 0 - -2942 -3099 0 - -2942 -3255 0 - -2942 -3677 0 - -2942 -3837 0 - -2942 -3942 0 - -3099 -3255 0 - -3099 -3677 0 - -3099 -3837 0 - -3099 -3942 0 - -3255 -3677 0 - -3255 -3837 0 - -3255 -3942 0 - -3677 -3837 0 - -3677 -3942 0 - -3837 -3942 0 - -1103 -1242 0 - -1103 -2292 0 - -1103 -3678 0 - -1103 -3838 0 - -1103 -3943 0 - -1103 -4126 0 - -1242 -2292 0 - -1242 -3678 0 - -1242 -3838 0 - -1242 -3943 0 - -1242 -4126 0 - -2292 -3678 0 - -2292 -3838 0 - -2292 -3943 0 - -2292 -4126 0 - -3678 -3838 0 - -3678 -3943 0 - -3678 -4126 0 - -3838 -3943 0 - -3838 -4126 0 - -3943 -4126 0 - -957 -1243 0 - -957 -2293 0 - -957 -3100 0 - -957 -3364 0 - -957 -4127 0 - -1243 -2293 0 - -1243 -3100 0 - -1243 -3364 0 - -1243 -4127 0 - -2293 -3100 0 - -2293 -3364 0 - -2293 -4127 0 - -3100 -3364 0 - -3100 -4127 0 - -3364 -4127 0 - -153 -382 0 - -153 -2294 0 - -153 -3256 0 - -153 -4128 0 - -382 -2294 0 - -382 -3256 0 - -382 -4128 0 - -2294 -3256 0 - -2294 -4128 0 - -3256 -4128 0 - -1437 -2295 0 - -1437 -4129 0 - -2295 -4129 0 - -154 -383 0 - -154 -2561 0 - -154 -3679 0 - -154 -4042 0 - -154 -4130 0 - -383 -2561 0 - -383 -3679 0 - -383 -4042 0 - -383 -4130 0 - -2561 -3679 0 - -2561 -4042 0 - -2561 -4130 0 - -3679 -4042 0 - -3679 -4130 0 - -4042 -4130 0 - -155 -1438 0 - -155 -2562 0 - -155 -3257 0 - -155 -3680 0 - -155 -3944 0 - -155 -4131 0 - -1438 -2562 0 - -1438 -3257 0 - -1438 -3680 0 - -1438 -3944 0 - -1438 -4131 0 - -2562 -3257 0 - -2562 -3680 0 - -2562 -3944 0 - -2562 -4131 0 - -3257 -3680 0 - -3257 -3944 0 - -3257 -4131 0 - -3680 -3944 0 - -3680 -4131 0 - -3944 -4131 0 - -1439 -1789 0 - -1439 -3681 0 - -1789 -3681 0 - -156 -384 0 - -156 -1440 0 - -156 -1790 0 - -156 -3258 0 - -156 -3945 0 - -156 -4132 0 - -384 -1440 0 - -384 -1790 0 - -384 -3258 0 - -384 -3945 0 - -384 -4132 0 - -1440 -1790 0 - -1440 -3258 0 - -1440 -3945 0 - -1440 -4132 0 - -1790 -3258 0 - -1790 -3945 0 - -1790 -4132 0 - -3258 -3945 0 - -3258 -4132 0 - -3945 -4132 0 - -157 -385 0 - -157 -2563 0 - -157 -3259 0 - -385 -2563 0 - -385 -3259 0 - -2563 -3259 0 - -3260 -3682 0 - -3260 -4043 0 - -3682 -4043 0 - -158 -2296 0 - -158 -2564 0 - -158 -3683 0 - -158 -4044 0 - -158 -4133 0 - -2296 -2564 0 - -2296 -3683 0 - -2296 -4044 0 - -2296 -4133 0 - -2564 -3683 0 - -2564 -4044 0 - -2564 -4133 0 - -3683 -4044 0 - -3683 -4133 0 - -4044 -4133 0 - -1441 -2297 0 - -1441 -3946 0 - -2297 -3946 0 - -159 -1442 0 - -159 -2298 0 - -159 -3684 0 - -159 -3947 0 - -159 -4045 0 - -1442 -2298 0 - -1442 -3684 0 - -1442 -3947 0 - -1442 -4045 0 - -2298 -3684 0 - -2298 -3947 0 - -2298 -4045 0 - -3684 -3947 0 - -3684 -4045 0 - -3947 -4045 0 - -1648 -2725 0 - -1648 -3500 0 - -1648 -4134 0 - -2725 -3500 0 - -2725 -4134 0 - -3500 -4134 0 - -160 -1104 0 - -160 -1649 0 - -160 -2377 0 - -160 -3501 0 - -160 -3685 0 - -160 -4135 0 - -1104 -1649 0 - -1104 -2377 0 - -1104 -3501 0 - -1104 -3685 0 - -1104 -4135 0 - -1649 -2377 0 - -1649 -3501 0 - -1649 -3685 0 - -1649 -4135 0 - -2377 -3501 0 - -2377 -3685 0 - -2377 -4135 0 - -3501 -3685 0 - -3501 -4135 0 - -3685 -4135 0 - -386 -1650 0 - -386 -2943 0 - -386 -3502 0 - -386 -3686 0 - -1650 -2943 0 - -1650 -3502 0 - -1650 -3686 0 - -2943 -3502 0 - -2943 -3686 0 - -3502 -3686 0 - -161 -387 0 - -161 -1105 0 - -161 -3687 0 - -161 -4136 0 - -387 -1105 0 - -387 -3687 0 - -387 -4136 0 - -1105 -3687 0 - -1105 -4136 0 - -3687 -4136 0 - -162 -1651 0 - -162 -2299 0 - -162 -2726 0 - -162 -2944 0 - -162 -4137 0 - -1651 -2299 0 - -1651 -2726 0 - -1651 -2944 0 - -1651 -4137 0 - -2299 -2726 0 - -2299 -2944 0 - -2299 -4137 0 - -2726 -2944 0 - -2726 -4137 0 - -2944 -4137 0 - -163 -388 0 - -163 -1106 0 - -163 -1244 0 - -163 -4138 0 - -388 -1106 0 - -388 -1244 0 - -388 -4138 0 - -1106 -1244 0 - -1106 -4138 0 - -1244 -4138 0 - -164 -2945 0 - -164 -3688 0 - -2945 -3688 0 - -165 -1107 0 - -165 -1245 0 - -165 -2300 0 - -165 -2727 0 - -165 -2946 0 - -165 -3503 0 - -165 -3689 0 - -165 -4139 0 - -1107 -1245 0 - -1107 -2300 0 - -1107 -2727 0 - -1107 -2946 0 - -1107 -3503 0 - -1107 -3689 0 - -1107 -4139 0 - -1245 -2300 0 - -1245 -2727 0 - -1245 -2946 0 - -1245 -3503 0 - -1245 -3689 0 - -1245 -4139 0 - -2300 -2727 0 - -2300 -2946 0 - -2300 -3503 0 - -2300 -3689 0 - -2300 -4139 0 - -2727 -2946 0 - -2727 -3503 0 - -2727 -3689 0 - -2727 -4139 0 - -2946 -3503 0 - -2946 -3689 0 - -2946 -4139 0 - -3503 -3689 0 - -3503 -4139 0 - -3689 -4139 0 - -389 -1246 0 - -389 -2728 0 - -389 -3690 0 - -1246 -2728 0 - -1246 -3690 0 - -2728 -3690 0 - -390 -1108 0 - -390 -2378 0 - -390 -3504 0 - -1108 -2378 0 - -1108 -3504 0 - -2378 -3504 0 - -166 -391 0 - -166 -1247 0 - -166 -2301 0 - -166 -2379 0 - -166 -2947 0 - -166 -3505 0 - -391 -1247 0 - -391 -2301 0 - -391 -2379 0 - -391 -2947 0 - -391 -3505 0 - -1247 -2301 0 - -1247 -2379 0 - -1247 -2947 0 - -1247 -3505 0 - -2301 -2379 0 - -2301 -2947 0 - -2301 -3505 0 - -2379 -2947 0 - -2379 -3505 0 - -2947 -3505 0 - -1109 -1248 0 - -1109 -2380 0 - -1109 -3691 0 - -1248 -2380 0 - -1248 -3691 0 - -2380 -3691 0 - -740 -842 0 - -740 -1110 0 - -740 -1249 0 - -740 -2729 0 - -740 -3101 0 - -740 -3365 0 - -740 -3506 0 - -740 -3948 0 - -740 -4140 0 - -842 -1110 0 - -842 -1249 0 - -842 -2729 0 - -842 -3101 0 - -842 -3365 0 - -842 -3506 0 - -842 -3948 0 - -842 -4140 0 - -1110 -1249 0 - -1110 -2729 0 - -1110 -3101 0 - -1110 -3365 0 - -1110 -3506 0 - -1110 -3948 0 - -1110 -4140 0 - -1249 -2729 0 - -1249 -3101 0 - -1249 -3365 0 - -1249 -3506 0 - -1249 -3948 0 - -1249 -4140 0 - -2729 -3101 0 - -2729 -3365 0 - -2729 -3506 0 - -2729 -3948 0 - -2729 -4140 0 - -3101 -3365 0 - -3101 -3506 0 - -3101 -3948 0 - -3101 -4140 0 - -3365 -3506 0 - -3365 -3948 0 - -3365 -4140 0 - -3506 -3948 0 - -3506 -4140 0 - -3948 -4140 0 - -741 -843 0 - -741 -1652 0 - -741 -2461 0 - -741 -2650 0 - -741 -2730 0 - -741 -3102 0 - -741 -3507 0 - -741 -4141 0 - -741 -4264 0 - -843 -1652 0 - -843 -2461 0 - -843 -2650 0 - -843 -2730 0 - -843 -3102 0 - -843 -3507 0 - -843 -4141 0 - -843 -4264 0 - -1652 -2461 0 - -1652 -2650 0 - -1652 -2730 0 - -1652 -3102 0 - -1652 -3507 0 - -1652 -4141 0 - -1652 -4264 0 - -2461 -2650 0 - -2461 -2730 0 - -2461 -3102 0 - -2461 -3507 0 - -2461 -4141 0 - -2461 -4264 0 - -2650 -2730 0 - -2650 -3102 0 - -2650 -3507 0 - -2650 -4141 0 - -2650 -4264 0 - -2730 -3102 0 - -2730 -3507 0 - -2730 -4141 0 - -2730 -4264 0 - -3102 -3507 0 - -3102 -4141 0 - -3102 -4264 0 - -3507 -4141 0 - -3507 -4264 0 - -4141 -4264 0 - -167 -844 0 - -167 -1111 0 - -167 -1653 0 - -167 -2213 0 - -167 -2462 0 - -167 -3366 0 - -167 -3508 0 - -167 -3839 0 - -167 -4142 0 - -844 -1111 0 - -844 -1653 0 - -844 -2213 0 - -844 -2462 0 - -844 -3366 0 - -844 -3508 0 - -844 -3839 0 - -844 -4142 0 - -1111 -1653 0 - -1111 -2213 0 - -1111 -2462 0 - -1111 -3366 0 - -1111 -3508 0 - -1111 -3839 0 - -1111 -4142 0 - -1653 -2213 0 - -1653 -2462 0 - -1653 -3366 0 - -1653 -3508 0 - -1653 -3839 0 - -1653 -4142 0 - -2213 -2462 0 - -2213 -3366 0 - -2213 -3508 0 - -2213 -3839 0 - -2213 -4142 0 - -2462 -3366 0 - -2462 -3508 0 - -2462 -3839 0 - -2462 -4142 0 - -3366 -3508 0 - -3366 -3839 0 - -3366 -4142 0 - -3508 -3839 0 - -3508 -4142 0 - -3839 -4142 0 - -742 -2463 0 - -742 -2651 0 - -742 -2731 0 - -742 -3367 0 - -742 -3509 0 - -742 -3840 0 - -742 -3949 0 - -2463 -2651 0 - -2463 -2731 0 - -2463 -3367 0 - -2463 -3509 0 - -2463 -3840 0 - -2463 -3949 0 - -2651 -2731 0 - -2651 -3367 0 - -2651 -3509 0 - -2651 -3840 0 - -2651 -3949 0 - -2731 -3367 0 - -2731 -3509 0 - -2731 -3840 0 - -2731 -3949 0 - -3367 -3509 0 - -3367 -3840 0 - -3367 -3949 0 - -3509 -3840 0 - -3509 -3949 0 - -3840 -3949 0 - -1112 -1654 0 - -1112 -2732 0 - -1112 -3103 0 - -1112 -3510 0 - -1112 -3841 0 - -1112 -3950 0 - -1654 -2732 0 - -1654 -3103 0 - -1654 -3510 0 - -1654 -3841 0 - -1654 -3950 0 - -2732 -3103 0 - -2732 -3510 0 - -2732 -3841 0 - -2732 -3950 0 - -3103 -3510 0 - -3103 -3841 0 - -3103 -3950 0 - -3510 -3841 0 - -3510 -3950 0 - -3841 -3950 0 - -168 -845 0 - -168 -1113 0 - -168 -2302 0 - -168 -2464 0 - -168 -3104 0 - -168 -3511 0 - -168 -4143 0 - -845 -1113 0 - -845 -2302 0 - -845 -2464 0 - -845 -3104 0 - -845 -3511 0 - -845 -4143 0 - -1113 -2302 0 - -1113 -2464 0 - -1113 -3104 0 - -1113 -3511 0 - -1113 -4143 0 - -2302 -2464 0 - -2302 -3104 0 - -2302 -3511 0 - -2302 -4143 0 - -2464 -3104 0 - -2464 -3511 0 - -2464 -4143 0 - -3104 -3511 0 - -3104 -4143 0 - -3511 -4143 0 - -169 -846 0 - -169 -1114 0 - -169 -1655 0 - -169 -2214 0 - -169 -3105 0 - -169 -3512 0 - -169 -3951 0 - -846 -1114 0 - -846 -1655 0 - -846 -2214 0 - -846 -3105 0 - -846 -3512 0 - -846 -3951 0 - -1114 -1655 0 - -1114 -2214 0 - -1114 -3105 0 - -1114 -3512 0 - -1114 -3951 0 - -1655 -2214 0 - -1655 -3105 0 - -1655 -3512 0 - -1655 -3951 0 - -2214 -3105 0 - -2214 -3512 0 - -2214 -3951 0 - -3105 -3512 0 - -3105 -3951 0 - -3512 -3951 0 - -847 -1656 0 - -847 -2215 0 - -847 -2465 0 - -847 -3368 0 - -847 -3513 0 - -847 -4265 0 - -1656 -2215 0 - -1656 -2465 0 - -1656 -3368 0 - -1656 -3513 0 - -1656 -4265 0 - -2215 -2465 0 - -2215 -3368 0 - -2215 -3513 0 - -2215 -4265 0 - -2465 -3368 0 - -2465 -3513 0 - -2465 -4265 0 - -3368 -3513 0 - -3368 -4265 0 - -3513 -4265 0 - -170 -743 0 - -170 -1115 0 - -170 -1250 0 - -170 -2466 0 - -170 -2652 0 - -170 -3106 0 - -170 -3842 0 - -170 -3952 0 - -170 -4144 0 - -743 -1115 0 - -743 -1250 0 - -743 -2466 0 - -743 -2652 0 - -743 -3106 0 - -743 -3842 0 - -743 -3952 0 - -743 -4144 0 - -1115 -1250 0 - -1115 -2466 0 - -1115 -2652 0 - -1115 -3106 0 - -1115 -3842 0 - -1115 -3952 0 - -1115 -4144 0 - -1250 -2466 0 - -1250 -2652 0 - -1250 -3106 0 - -1250 -3842 0 - -1250 -3952 0 - -1250 -4144 0 - -2466 -2652 0 - -2466 -3106 0 - -2466 -3842 0 - -2466 -3952 0 - -2466 -4144 0 - -2652 -3106 0 - -2652 -3842 0 - -2652 -3952 0 - -2652 -4144 0 - -3106 -3842 0 - -3106 -3952 0 - -3106 -4144 0 - -3842 -3952 0 - -3842 -4144 0 - -3952 -4144 0 - -171 -2216 0 - -171 -2653 0 - -171 -3107 0 - -171 -3843 0 - -2216 -2653 0 - -2216 -3107 0 - -2216 -3843 0 - -2653 -3107 0 - -2653 -3843 0 - -3107 -3843 0 - -744 -1657 0 - -744 -2654 0 - -744 -2733 0 - -744 -3514 0 - -1657 -2654 0 - -1657 -2733 0 - -1657 -3514 0 - -2654 -2733 0 - -2654 -3514 0 - -2733 -3514 0 - -172 -1116 0 - -172 -1251 0 - -172 -2217 0 - -172 -2303 0 - -172 -2467 0 - -172 -2734 0 - -172 -3515 0 - -172 -4145 0 - -172 -4266 0 - -1116 -1251 0 - -1116 -2217 0 - -1116 -2303 0 - -1116 -2467 0 - -1116 -2734 0 - -1116 -3515 0 - -1116 -4145 0 - -1116 -4266 0 - -1251 -2217 0 - -1251 -2303 0 - -1251 -2467 0 - -1251 -2734 0 - -1251 -3515 0 - -1251 -4145 0 - -1251 -4266 0 - -2217 -2303 0 - -2217 -2467 0 - -2217 -2734 0 - -2217 -3515 0 - -2217 -4145 0 - -2217 -4266 0 - -2303 -2467 0 - -2303 -2734 0 - -2303 -3515 0 - -2303 -4145 0 - -2303 -4266 0 - -2467 -2734 0 - -2467 -3515 0 - -2467 -4145 0 - -2467 -4266 0 - -2734 -3515 0 - -2734 -4145 0 - -2734 -4266 0 - -3515 -4145 0 - -3515 -4266 0 - -4145 -4266 0 - -745 -1252 0 - -745 -2218 0 - -745 -2468 0 - -745 -2735 0 - -745 -3108 0 - -745 -3844 0 - -745 -3953 0 - -745 -4267 0 - -1252 -2218 0 - -1252 -2468 0 - -1252 -2735 0 - -1252 -3108 0 - -1252 -3844 0 - -1252 -3953 0 - -1252 -4267 0 - -2218 -2468 0 - -2218 -2735 0 - -2218 -3108 0 - -2218 -3844 0 - -2218 -3953 0 - -2218 -4267 0 - -2468 -2735 0 - -2468 -3108 0 - -2468 -3844 0 - -2468 -3953 0 - -2468 -4267 0 - -2735 -3108 0 - -2735 -3844 0 - -2735 -3953 0 - -2735 -4267 0 - -3108 -3844 0 - -3108 -3953 0 - -3108 -4267 0 - -3844 -3953 0 - -3844 -4267 0 - -3953 -4267 0 - -746 -1117 0 - -746 -2469 0 - -746 -3109 0 - -746 -3516 0 - -746 -3954 0 - -746 -4268 0 - -1117 -2469 0 - -1117 -3109 0 - -1117 -3516 0 - -1117 -3954 0 - -1117 -4268 0 - -2469 -3109 0 - -2469 -3516 0 - -2469 -3954 0 - -2469 -4268 0 - -3109 -3516 0 - -3109 -3954 0 - -3109 -4268 0 - -3516 -3954 0 - -3516 -4268 0 - -3954 -4268 0 - -173 -747 0 - -173 -848 0 - -173 -2470 0 - -173 -2736 0 - -173 -3369 0 - -173 -4146 0 - -173 -4269 0 - -747 -848 0 - -747 -2470 0 - -747 -2736 0 - -747 -3369 0 - -747 -4146 0 - -747 -4269 0 - -848 -2470 0 - -848 -2736 0 - -848 -3369 0 - -848 -4146 0 - -848 -4269 0 - -2470 -2736 0 - -2470 -3369 0 - -2470 -4146 0 - -2470 -4269 0 - -2736 -3369 0 - -2736 -4146 0 - -2736 -4269 0 - -3369 -4146 0 - -3369 -4269 0 - -4146 -4269 0 - -174 -1118 0 - -174 -1658 0 - -174 -2219 0 - -174 -2304 0 - -174 -2471 0 - -174 -2737 0 - -174 -3110 0 - -174 -3370 0 - -1118 -1658 0 - -1118 -2219 0 - -1118 -2304 0 - -1118 -2471 0 - -1118 -2737 0 - -1118 -3110 0 - -1118 -3370 0 - -1658 -2219 0 - -1658 -2304 0 - -1658 -2471 0 - -1658 -2737 0 - -1658 -3110 0 - -1658 -3370 0 - -2219 -2304 0 - -2219 -2471 0 - -2219 -2737 0 - -2219 -3110 0 - -2219 -3370 0 - -2304 -2471 0 - -2304 -2737 0 - -2304 -3110 0 - -2304 -3370 0 - -2471 -2737 0 - -2471 -3110 0 - -2471 -3370 0 - -2737 -3110 0 - -2737 -3370 0 - -3110 -3370 0 - -1659 -2305 0 - -1659 -2655 0 - -1659 -2738 0 - -1659 -3371 0 - -1659 -4147 0 - -1659 -4270 0 - -2305 -2655 0 - -2305 -2738 0 - -2305 -3371 0 - -2305 -4147 0 - -2305 -4270 0 - -2655 -2738 0 - -2655 -3371 0 - -2655 -4147 0 - -2655 -4270 0 - -2738 -3371 0 - -2738 -4147 0 - -2738 -4270 0 - -3371 -4147 0 - -3371 -4270 0 - -4147 -4270 0 - -849 -1253 0 - -849 -2306 0 - -849 -2656 0 - -849 -3111 0 - -849 -3372 0 - -849 -4148 0 - -849 -4271 0 - -1253 -2306 0 - -1253 -2656 0 - -1253 -3111 0 - -1253 -3372 0 - -1253 -4148 0 - -1253 -4271 0 - -2306 -2656 0 - -2306 -3111 0 - -2306 -3372 0 - -2306 -4148 0 - -2306 -4271 0 - -2656 -3111 0 - -2656 -3372 0 - -2656 -4148 0 - -2656 -4271 0 - -3111 -3372 0 - -3111 -4148 0 - -3111 -4271 0 - -3372 -4148 0 - -3372 -4271 0 - -4148 -4271 0 - -748 -2074 0 - -748 -2381 0 - -748 -2739 0 - -748 -3373 0 - -748 -3517 0 - -748 -4046 0 - -748 -4149 0 - -2074 -2381 0 - -2074 -2739 0 - -2074 -3373 0 - -2074 -3517 0 - -2074 -4046 0 - -2074 -4149 0 - -2381 -2739 0 - -2381 -3373 0 - -2381 -3517 0 - -2381 -4046 0 - -2381 -4149 0 - -2739 -3373 0 - -2739 -3517 0 - -2739 -4046 0 - -2739 -4149 0 - -3373 -3517 0 - -3373 -4046 0 - -3373 -4149 0 - -3517 -4046 0 - -3517 -4149 0 - -4046 -4149 0 - -749 -2472 0 - -749 -2740 0 - -749 -3518 0 - -749 -4150 0 - -749 -4320 0 - -2472 -2740 0 - -2472 -3518 0 - -2472 -4150 0 - -2472 -4320 0 - -2740 -3518 0 - -2740 -4150 0 - -2740 -4320 0 - -3518 -4150 0 - -3518 -4320 0 - -4150 -4320 0 - -175 -497 0 - -175 -2075 0 - -175 -2382 0 - -175 -2473 0 - -175 -3374 0 - -175 -3519 0 - -175 -4151 0 - -497 -2075 0 - -497 -2382 0 - -497 -2473 0 - -497 -3374 0 - -497 -3519 0 - -497 -4151 0 - -2075 -2382 0 - -2075 -2473 0 - -2075 -3374 0 - -2075 -3519 0 - -2075 -4151 0 - -2382 -2473 0 - -2382 -3374 0 - -2382 -3519 0 - -2382 -4151 0 - -2473 -3374 0 - -2473 -3519 0 - -2473 -4151 0 - -3374 -3519 0 - -3374 -4151 0 - -3519 -4151 0 - -750 -1902 0 - -750 -2076 0 - -750 -2474 0 - -750 -2741 0 - -750 -2948 0 - -750 -3375 0 - -750 -3520 0 - -750 -4047 0 - -750 -4321 0 - -1902 -2076 0 - -1902 -2474 0 - -1902 -2741 0 - -1902 -2948 0 - -1902 -3375 0 - -1902 -3520 0 - -1902 -4047 0 - -1902 -4321 0 - -2076 -2474 0 - -2076 -2741 0 - -2076 -2948 0 - -2076 -3375 0 - -2076 -3520 0 - -2076 -4047 0 - -2076 -4321 0 - -2474 -2741 0 - -2474 -2948 0 - -2474 -3375 0 - -2474 -3520 0 - -2474 -4047 0 - -2474 -4321 0 - -2741 -2948 0 - -2741 -3375 0 - -2741 -3520 0 - -2741 -4047 0 - -2741 -4321 0 - -2948 -3375 0 - -2948 -3520 0 - -2948 -4047 0 - -2948 -4321 0 - -3375 -3520 0 - -3375 -4047 0 - -3375 -4321 0 - -3520 -4047 0 - -3520 -4321 0 - -4047 -4321 0 - -498 -1903 0 - -498 -2077 0 - -498 -2742 0 - -498 -2949 0 - -498 -3521 0 - -1903 -2077 0 - -1903 -2742 0 - -1903 -2949 0 - -1903 -3521 0 - -2077 -2742 0 - -2077 -2949 0 - -2077 -3521 0 - -2742 -2949 0 - -2742 -3521 0 - -2949 -3521 0 - -392 -751 0 - -392 -1575 0 - -392 -2475 0 - -392 -3376 0 - -392 -4152 0 - -751 -1575 0 - -751 -2475 0 - -751 -3376 0 - -751 -4152 0 - -1575 -2475 0 - -1575 -3376 0 - -1575 -4152 0 - -2475 -3376 0 - -2475 -4152 0 - -3376 -4152 0 - -393 -499 0 - -393 -1904 0 - -393 -2476 0 - -393 -2950 0 - -393 -3377 0 - -393 -3522 0 - -393 -4048 0 - -499 -1904 0 - -499 -2476 0 - -499 -2950 0 - -499 -3377 0 - -499 -3522 0 - -499 -4048 0 - -1904 -2476 0 - -1904 -2950 0 - -1904 -3377 0 - -1904 -3522 0 - -1904 -4048 0 - -2476 -2950 0 - -2476 -3377 0 - -2476 -3522 0 - -2476 -4048 0 - -2950 -3377 0 - -2950 -3522 0 - -2950 -4048 0 - -3377 -3522 0 - -3377 -4048 0 - -3522 -4048 0 - -176 -500 0 - -176 -2078 0 - -176 -2477 0 - -176 -2743 0 - -176 -2951 0 - -176 -3523 0 - -176 -4049 0 - -176 -4153 0 - -176 -4322 0 - -500 -2078 0 - -500 -2477 0 - -500 -2743 0 - -500 -2951 0 - -500 -3523 0 - -500 -4049 0 - -500 -4153 0 - -500 -4322 0 - -2078 -2477 0 - -2078 -2743 0 - -2078 -2951 0 - -2078 -3523 0 - -2078 -4049 0 - -2078 -4153 0 - -2078 -4322 0 - -2477 -2743 0 - -2477 -2951 0 - -2477 -3523 0 - -2477 -4049 0 - -2477 -4153 0 - -2477 -4322 0 - -2743 -2951 0 - -2743 -3523 0 - -2743 -4049 0 - -2743 -4153 0 - -2743 -4322 0 - -2951 -3523 0 - -2951 -4049 0 - -2951 -4153 0 - -2951 -4322 0 - -3523 -4049 0 - -3523 -4153 0 - -3523 -4322 0 - -4049 -4153 0 - -4049 -4322 0 - -4153 -4322 0 - -1905 -2079 0 - -1905 -2383 0 - -1905 -2952 0 - -1905 -3524 0 - -1905 -4323 0 - -2079 -2383 0 - -2079 -2952 0 - -2079 -3524 0 - -2079 -4323 0 - -2383 -2952 0 - -2383 -3524 0 - -2383 -4323 0 - -2952 -3524 0 - -2952 -4323 0 - -3524 -4323 0 - -394 -752 0 - -394 -2384 0 - -394 -2478 0 - -394 -3525 0 - -752 -2384 0 - -752 -2478 0 - -752 -3525 0 - -2384 -2478 0 - -2384 -3525 0 - -2478 -3525 0 - -501 -1576 0 - -501 -2080 0 - -501 -3378 0 - -501 -4050 0 - -501 -4324 0 - -1576 -2080 0 - -1576 -3378 0 - -1576 -4050 0 - -1576 -4324 0 - -2080 -3378 0 - -2080 -4050 0 - -2080 -4324 0 - -3378 -4050 0 - -3378 -4324 0 - -4050 -4324 0 - -177 -502 0 - -177 -1906 0 - -177 -2953 0 - -177 -3379 0 - -177 -4154 0 - -177 -4325 0 - -502 -1906 0 - -502 -2953 0 - -502 -3379 0 - -502 -4154 0 - -502 -4325 0 - -1906 -2953 0 - -1906 -3379 0 - -1906 -4154 0 - -1906 -4325 0 - -2953 -3379 0 - -2953 -4154 0 - -2953 -4325 0 - -3379 -4154 0 - -3379 -4325 0 - -4154 -4325 0 - -178 -503 0 - -178 -2385 0 - -178 -4051 0 - -503 -2385 0 - -503 -4051 0 - -2385 -4051 0 - -395 -753 0 - -395 -1907 0 - -395 -2081 0 - -395 -2386 0 - -395 -2479 0 - -395 -2744 0 - -395 -3526 0 - -753 -1907 0 - -753 -2081 0 - -753 -2386 0 - -753 -2479 0 - -753 -2744 0 - -753 -3526 0 - -1907 -2081 0 - -1907 -2386 0 - -1907 -2479 0 - -1907 -2744 0 - -1907 -3526 0 - -2081 -2386 0 - -2081 -2479 0 - -2081 -2744 0 - -2081 -3526 0 - -2386 -2479 0 - -2386 -2744 0 - -2386 -3526 0 - -2479 -2744 0 - -2479 -3526 0 - -2744 -3526 0 - -179 -396 0 - -179 -504 0 - -179 -754 0 - -179 -1908 0 - -179 -2387 0 - -179 -2954 0 - -179 -3527 0 - -396 -504 0 - -396 -754 0 - -396 -1908 0 - -396 -2387 0 - -396 -2954 0 - -396 -3527 0 - -504 -754 0 - -504 -1908 0 - -504 -2387 0 - -504 -2954 0 - -504 -3527 0 - -754 -1908 0 - -754 -2387 0 - -754 -2954 0 - -754 -3527 0 - -1908 -2387 0 - -1908 -2954 0 - -1908 -3527 0 - -2387 -2954 0 - -2387 -3527 0 - -2954 -3527 0 - -1909 -2082 0 - -1909 -3380 0 - -1909 -4155 0 - -1909 -4326 0 - -2082 -3380 0 - -2082 -4155 0 - -2082 -4326 0 - -3380 -4155 0 - -3380 -4326 0 - -4155 -4326 0 - -33 -274 0 - -33 -599 0 - -33 -755 0 - -33 -958 0 - -33 -1443 0 - -33 -1660 0 - -274 -599 0 - -274 -755 0 - -274 -958 0 - -274 -1443 0 - -274 -1660 0 - -599 -755 0 - -599 -958 0 - -599 -1443 0 - -599 -1660 0 - -755 -958 0 - -755 -1443 0 - -755 -1660 0 - -958 -1443 0 - -958 -1660 0 - -1443 -1660 0 - -34 -756 0 - -34 -1119 0 - -34 -1444 0 - -34 -2083 0 - -34 -3112 0 - -756 -1119 0 - -756 -1444 0 - -756 -2083 0 - -756 -3112 0 - -1119 -1444 0 - -1119 -2083 0 - -1119 -3112 0 - -1444 -2083 0 - -1444 -3112 0 - -2083 -3112 0 - -275 -600 0 - -275 -959 0 - -275 -1120 0 - -275 -1445 0 - -275 -1661 0 - -275 -2084 0 - -275 -2480 0 - -600 -959 0 - -600 -1120 0 - -600 -1445 0 - -600 -1661 0 - -600 -2084 0 - -600 -2480 0 - -959 -1120 0 - -959 -1445 0 - -959 -1661 0 - -959 -2084 0 - -959 -2480 0 - -1120 -1445 0 - -1120 -1661 0 - -1120 -2084 0 - -1120 -2480 0 - -1445 -1661 0 - -1445 -2084 0 - -1445 -2480 0 - -1661 -2084 0 - -1661 -2480 0 - -2084 -2480 0 - -397 -1121 0 - -397 -1326 0 - -397 -2481 0 - -397 -3113 0 - -1121 -1326 0 - -1121 -2481 0 - -1121 -3113 0 - -1326 -2481 0 - -1326 -3113 0 - -2481 -3113 0 - -276 -960 0 - -276 -1122 0 - -276 -1662 0 - -276 -3114 0 - -960 -1122 0 - -960 -1662 0 - -960 -3114 0 - -1122 -1662 0 - -1122 -3114 0 - -1662 -3114 0 - -35 -398 0 - -35 -601 0 - -35 -1446 0 - -35 -1663 0 - -35 -2482 0 - -35 -4272 0 - -398 -601 0 - -398 -1446 0 - -398 -1663 0 - -398 -2482 0 - -398 -4272 0 - -601 -1446 0 - -601 -1663 0 - -601 -2482 0 - -601 -4272 0 - -1446 -1663 0 - -1446 -2482 0 - -1446 -4272 0 - -1663 -2482 0 - -1663 -4272 0 - -2482 -4272 0 - -277 -399 0 - -277 -757 0 - -277 -961 0 - -277 -1664 0 - -277 -2085 0 - -277 -2483 0 - -277 -3115 0 - -399 -757 0 - -399 -961 0 - -399 -1664 0 - -399 -2085 0 - -399 -2483 0 - -399 -3115 0 - -757 -961 0 - -757 -1664 0 - -757 -2085 0 - -757 -2483 0 - -757 -3115 0 - -961 -1664 0 - -961 -2085 0 - -961 -2483 0 - -961 -3115 0 - -1664 -2085 0 - -1664 -2483 0 - -1664 -3115 0 - -2085 -2483 0 - -2085 -3115 0 - -2483 -3115 0 - -36 -1327 0 - -36 -1447 0 - -36 -3116 0 - -1327 -1447 0 - -1327 -3116 0 - -1447 -3116 0 - -37 -278 0 - -37 -1123 0 - -37 -1448 0 - -37 -1665 0 - -278 -1123 0 - -278 -1448 0 - -278 -1665 0 - -1123 -1448 0 - -1123 -1665 0 - -1448 -1665 0 - -279 -602 0 - -279 -1328 0 - -279 -1449 0 - -279 -3117 0 - -602 -1328 0 - -602 -1449 0 - -602 -3117 0 - -1328 -1449 0 - -1328 -3117 0 - -1449 -3117 0 - -280 -962 0 - -280 -1124 0 - -280 -1329 0 - -280 -2086 0 - -280 -2484 0 - -280 -4273 0 - -962 -1124 0 - -962 -1329 0 - -962 -2086 0 - -962 -2484 0 - -962 -4273 0 - -1124 -1329 0 - -1124 -2086 0 - -1124 -2484 0 - -1124 -4273 0 - -1329 -2086 0 - -1329 -2484 0 - -1329 -4273 0 - -2086 -2484 0 - -2086 -4273 0 - -2484 -4273 0 - -281 -400 0 - -281 -758 0 - -281 -963 0 - -281 -1330 0 - -281 -1450 0 - -281 -2485 0 - -281 -3118 0 - -281 -4274 0 - -400 -758 0 - -400 -963 0 - -400 -1330 0 - -400 -1450 0 - -400 -2485 0 - -400 -3118 0 - -400 -4274 0 - -758 -963 0 - -758 -1330 0 - -758 -1450 0 - -758 -2485 0 - -758 -3118 0 - -758 -4274 0 - -963 -1330 0 - -963 -1450 0 - -963 -2485 0 - -963 -3118 0 - -963 -4274 0 - -1330 -1450 0 - -1330 -2485 0 - -1330 -3118 0 - -1330 -4274 0 - -1450 -2485 0 - -1450 -3118 0 - -1450 -4274 0 - -2485 -3118 0 - -2485 -4274 0 - -3118 -4274 0 - -38 -603 0 - -38 -964 0 - -38 -1451 0 - -38 -3119 0 - -38 -4275 0 - -603 -964 0 - -603 -1451 0 - -603 -3119 0 - -603 -4275 0 - -964 -1451 0 - -964 -3119 0 - -964 -4275 0 - -1451 -3119 0 - -1451 -4275 0 - -3119 -4275 0 - -282 -401 0 - -282 -604 0 - -282 -759 0 - -282 -965 0 - -282 -2087 0 - -282 -2486 0 - -401 -604 0 - -401 -759 0 - -401 -965 0 - -401 -2087 0 - -401 -2486 0 - -604 -759 0 - -604 -965 0 - -604 -2087 0 - -604 -2486 0 - -759 -965 0 - -759 -2087 0 - -759 -2486 0 - -965 -2087 0 - -965 -2486 0 - -2087 -2486 0 - -39 -850 0 - -39 -1125 0 - -39 -1254 0 - -39 -1452 0 - -39 -1791 0 - -39 -2088 0 - -39 -3120 0 - -39 -3692 0 - -39 -3955 0 - -850 -1125 0 - -850 -1254 0 - -850 -1452 0 - -850 -1791 0 - -850 -2088 0 - -850 -3120 0 - -850 -3692 0 - -850 -3955 0 - -1125 -1254 0 - -1125 -1452 0 - -1125 -1791 0 - -1125 -2088 0 - -1125 -3120 0 - -1125 -3692 0 - -1125 -3955 0 - -1254 -1452 0 - -1254 -1791 0 - -1254 -2088 0 - -1254 -3120 0 - -1254 -3692 0 - -1254 -3955 0 - -1452 -1791 0 - -1452 -2088 0 - -1452 -3120 0 - -1452 -3692 0 - -1452 -3955 0 - -1791 -2088 0 - -1791 -3120 0 - -1791 -3692 0 - -1791 -3955 0 - -2088 -3120 0 - -2088 -3692 0 - -2088 -3955 0 - -3120 -3692 0 - -3120 -3955 0 - -3692 -3955 0 - -605 -851 0 - -605 -1331 0 - -605 -1666 0 - -605 -3121 0 - -605 -4327 0 - -851 -1331 0 - -851 -1666 0 - -851 -3121 0 - -851 -4327 0 - -1331 -1666 0 - -1331 -3121 0 - -1331 -4327 0 - -1666 -3121 0 - -1666 -4327 0 - -3121 -4327 0 - -180 -606 0 - -180 -852 0 - -180 -1126 0 - -180 -1453 0 - -180 -1667 0 - -180 -1792 0 - -180 -2089 0 - -180 -3693 0 - -180 -3845 0 - -606 -852 0 - -606 -1126 0 - -606 -1453 0 - -606 -1667 0 - -606 -1792 0 - -606 -2089 0 - -606 -3693 0 - -606 -3845 0 - -852 -1126 0 - -852 -1453 0 - -852 -1667 0 - -852 -1792 0 - -852 -2089 0 - -852 -3693 0 - -852 -3845 0 - -1126 -1453 0 - -1126 -1667 0 - -1126 -1792 0 - -1126 -2089 0 - -1126 -3693 0 - -1126 -3845 0 - -1453 -1667 0 - -1453 -1792 0 - -1453 -2089 0 - -1453 -3693 0 - -1453 -3845 0 - -1667 -1792 0 - -1667 -2089 0 - -1667 -3693 0 - -1667 -3845 0 - -1792 -2089 0 - -1792 -3693 0 - -1792 -3845 0 - -2089 -3693 0 - -2089 -3845 0 - -3693 -3845 0 - -1332 -1454 0 - -1332 -1793 0 - -1332 -1910 0 - -1332 -2090 0 - -1332 -2955 0 - -1332 -3846 0 - -1332 -3956 0 - -1332 -4328 0 - -1454 -1793 0 - -1454 -1910 0 - -1454 -2090 0 - -1454 -2955 0 - -1454 -3846 0 - -1454 -3956 0 - -1454 -4328 0 - -1793 -1910 0 - -1793 -2090 0 - -1793 -2955 0 - -1793 -3846 0 - -1793 -3956 0 - -1793 -4328 0 - -1910 -2090 0 - -1910 -2955 0 - -1910 -3846 0 - -1910 -3956 0 - -1910 -4328 0 - -2090 -2955 0 - -2090 -3846 0 - -2090 -3956 0 - -2090 -4328 0 - -2955 -3846 0 - -2955 -3956 0 - -2955 -4328 0 - -3846 -3956 0 - -3846 -4328 0 - -3956 -4328 0 - -181 -402 0 - -181 -853 0 - -181 -1127 0 - -181 -1333 0 - -181 -1911 0 - -181 -2307 0 - -181 -2956 0 - -181 -3122 0 - -181 -4329 0 - -402 -853 0 - -402 -1127 0 - -402 -1333 0 - -402 -1911 0 - -402 -2307 0 - -402 -2956 0 - -402 -3122 0 - -402 -4329 0 - -853 -1127 0 - -853 -1333 0 - -853 -1911 0 - -853 -2307 0 - -853 -2956 0 - -853 -3122 0 - -853 -4329 0 - -1127 -1333 0 - -1127 -1911 0 - -1127 -2307 0 - -1127 -2956 0 - -1127 -3122 0 - -1127 -4329 0 - -1333 -1911 0 - -1333 -2307 0 - -1333 -2956 0 - -1333 -3122 0 - -1333 -4329 0 - -1911 -2307 0 - -1911 -2956 0 - -1911 -3122 0 - -1911 -4329 0 - -2307 -2956 0 - -2307 -3122 0 - -2307 -4329 0 - -2956 -3122 0 - -2956 -4329 0 - -3122 -4329 0 - -182 -854 0 - -182 -1128 0 - -182 -1668 0 - -182 -1794 0 - -182 -1912 0 - -182 -2957 0 - -182 -3123 0 - -182 -3694 0 - -182 -3957 0 - -854 -1128 0 - -854 -1668 0 - -854 -1794 0 - -854 -1912 0 - -854 -2957 0 - -854 -3123 0 - -854 -3694 0 - -854 -3957 0 - -1128 -1668 0 - -1128 -1794 0 - -1128 -1912 0 - -1128 -2957 0 - -1128 -3123 0 - -1128 -3694 0 - -1128 -3957 0 - -1668 -1794 0 - -1668 -1912 0 - -1668 -2957 0 - -1668 -3123 0 - -1668 -3694 0 - -1668 -3957 0 - -1794 -1912 0 - -1794 -2957 0 - -1794 -3123 0 - -1794 -3694 0 - -1794 -3957 0 - -1912 -2957 0 - -1912 -3123 0 - -1912 -3694 0 - -1912 -3957 0 - -2957 -3123 0 - -2957 -3694 0 - -2957 -3957 0 - -3123 -3694 0 - -3123 -3957 0 - -3694 -3957 0 - -40 -403 0 - -40 -607 0 - -40 -855 0 - -40 -1455 0 - -40 -1669 0 - -40 -1913 0 - -40 -2958 0 - -40 -3695 0 - -403 -607 0 - -403 -855 0 - -403 -1455 0 - -403 -1669 0 - -403 -1913 0 - -403 -2958 0 - -403 -3695 0 - -607 -855 0 - -607 -1455 0 - -607 -1669 0 - -607 -1913 0 - -607 -2958 0 - -607 -3695 0 - -855 -1455 0 - -855 -1669 0 - -855 -1913 0 - -855 -2958 0 - -855 -3695 0 - -1455 -1669 0 - -1455 -1913 0 - -1455 -2958 0 - -1455 -3695 0 - -1669 -1913 0 - -1669 -2958 0 - -1669 -3695 0 - -1913 -2958 0 - -1913 -3695 0 - -2958 -3695 0 - -41 -183 0 - -41 -1334 0 - -41 -1456 0 - -41 -1577 0 - -41 -3124 0 - -41 -3696 0 - -41 -3847 0 - -41 -4330 0 - -183 -1334 0 - -183 -1456 0 - -183 -1577 0 - -183 -3124 0 - -183 -3696 0 - -183 -3847 0 - -183 -4330 0 - -1334 -1456 0 - -1334 -1577 0 - -1334 -3124 0 - -1334 -3696 0 - -1334 -3847 0 - -1334 -4330 0 - -1456 -1577 0 - -1456 -3124 0 - -1456 -3696 0 - -1456 -3847 0 - -1456 -4330 0 - -1577 -3124 0 - -1577 -3696 0 - -1577 -3847 0 - -1577 -4330 0 - -3124 -3696 0 - -3124 -3847 0 - -3124 -4330 0 - -3696 -3847 0 - -3696 -4330 0 - -3847 -4330 0 - -42 -184 0 - -42 -856 0 - -42 -1129 0 - -42 -1255 0 - -42 -1457 0 - -42 -1670 0 - -42 -3697 0 - -42 -3848 0 - -42 -3958 0 - -184 -856 0 - -184 -1129 0 - -184 -1255 0 - -184 -1457 0 - -184 -1670 0 - -184 -3697 0 - -184 -3848 0 - -184 -3958 0 - -856 -1129 0 - -856 -1255 0 - -856 -1457 0 - -856 -1670 0 - -856 -3697 0 - -856 -3848 0 - -856 -3958 0 - -1129 -1255 0 - -1129 -1457 0 - -1129 -1670 0 - -1129 -3697 0 - -1129 -3848 0 - -1129 -3958 0 - -1255 -1457 0 - -1255 -1670 0 - -1255 -3697 0 - -1255 -3848 0 - -1255 -3958 0 - -1457 -1670 0 - -1457 -3697 0 - -1457 -3848 0 - -1457 -3958 0 - -1670 -3697 0 - -1670 -3848 0 - -1670 -3958 0 - -3697 -3848 0 - -3697 -3958 0 - -3848 -3958 0 - -185 -404 0 - -185 -608 0 - -185 -1130 0 - -185 -1335 0 - -185 -1578 0 - -185 -1914 0 - -185 -3125 0 - -404 -608 0 - -404 -1130 0 - -404 -1335 0 - -404 -1578 0 - -404 -1914 0 - -404 -3125 0 - -608 -1130 0 - -608 -1335 0 - -608 -1578 0 - -608 -1914 0 - -608 -3125 0 - -1130 -1335 0 - -1130 -1578 0 - -1130 -1914 0 - -1130 -3125 0 - -1335 -1578 0 - -1335 -1914 0 - -1335 -3125 0 - -1578 -1914 0 - -1578 -3125 0 - -1914 -3125 0 - -186 -609 0 - -186 -1336 0 - -186 -1458 0 - -186 -1579 0 - -186 -2959 0 - -186 -3126 0 - -186 -3698 0 - -186 -3849 0 - -186 -4331 0 - -609 -1336 0 - -609 -1458 0 - -609 -1579 0 - -609 -2959 0 - -609 -3126 0 - -609 -3698 0 - -609 -3849 0 - -609 -4331 0 - -1336 -1458 0 - -1336 -1579 0 - -1336 -2959 0 - -1336 -3126 0 - -1336 -3698 0 - -1336 -3849 0 - -1336 -4331 0 - -1458 -1579 0 - -1458 -2959 0 - -1458 -3126 0 - -1458 -3698 0 - -1458 -3849 0 - -1458 -4331 0 - -1579 -2959 0 - -1579 -3126 0 - -1579 -3698 0 - -1579 -3849 0 - -1579 -4331 0 - -2959 -3126 0 - -2959 -3698 0 - -2959 -3849 0 - -2959 -4331 0 - -3126 -3698 0 - -3126 -3849 0 - -3126 -4331 0 - -3698 -3849 0 - -3698 -4331 0 - -3849 -4331 0 - -187 -1131 0 - -187 -1256 0 - -187 -1337 0 - -187 -2091 0 - -187 -2308 0 - -187 -2960 0 - -187 -3699 0 - -187 -4332 0 - -1131 -1256 0 - -1131 -1337 0 - -1131 -2091 0 - -1131 -2308 0 - -1131 -2960 0 - -1131 -3699 0 - -1131 -4332 0 - -1256 -1337 0 - -1256 -2091 0 - -1256 -2308 0 - -1256 -2960 0 - -1256 -3699 0 - -1256 -4332 0 - -1337 -2091 0 - -1337 -2308 0 - -1337 -2960 0 - -1337 -3699 0 - -1337 -4332 0 - -2091 -2308 0 - -2091 -2960 0 - -2091 -3699 0 - -2091 -4332 0 - -2308 -2960 0 - -2308 -3699 0 - -2308 -4332 0 - -2960 -3699 0 - -2960 -4332 0 - -3699 -4332 0 - -1132 -1338 0 - -1132 -1459 0 - -1132 -1671 0 - -1132 -1915 0 - -1132 -2092 0 - -1132 -2309 0 - -1132 -2961 0 - -1132 -3127 0 - -1132 -3850 0 - -1132 -3959 0 - -1132 -4333 0 - -1338 -1459 0 - -1338 -1671 0 - -1338 -1915 0 - -1338 -2092 0 - -1338 -2309 0 - -1338 -2961 0 - -1338 -3127 0 - -1338 -3850 0 - -1338 -3959 0 - -1338 -4333 0 - -1459 -1671 0 - -1459 -1915 0 - -1459 -2092 0 - -1459 -2309 0 - -1459 -2961 0 - -1459 -3127 0 - -1459 -3850 0 - -1459 -3959 0 - -1459 -4333 0 - -1671 -1915 0 - -1671 -2092 0 - -1671 -2309 0 - -1671 -2961 0 - -1671 -3127 0 - -1671 -3850 0 - -1671 -3959 0 - -1671 -4333 0 - -1915 -2092 0 - -1915 -2309 0 - -1915 -2961 0 - -1915 -3127 0 - -1915 -3850 0 - -1915 -3959 0 - -1915 -4333 0 - -2092 -2309 0 - -2092 -2961 0 - -2092 -3127 0 - -2092 -3850 0 - -2092 -3959 0 - -2092 -4333 0 - -2309 -2961 0 - -2309 -3127 0 - -2309 -3850 0 - -2309 -3959 0 - -2309 -4333 0 - -2961 -3127 0 - -2961 -3850 0 - -2961 -3959 0 - -2961 -4333 0 - -3127 -3850 0 - -3127 -3959 0 - -3127 -4333 0 - -3850 -3959 0 - -3850 -4333 0 - -3959 -4333 0 - -405 -1257 0 - -405 -1339 0 - -405 -1460 0 - -405 -1580 0 - -405 -1916 0 - -405 -3128 0 - -405 -3700 0 - -405 -3851 0 - -405 -3960 0 - -1257 -1339 0 - -1257 -1460 0 - -1257 -1580 0 - -1257 -1916 0 - -1257 -3128 0 - -1257 -3700 0 - -1257 -3851 0 - -1257 -3960 0 - -1339 -1460 0 - -1339 -1580 0 - -1339 -1916 0 - -1339 -3128 0 - -1339 -3700 0 - -1339 -3851 0 - -1339 -3960 0 - -1460 -1580 0 - -1460 -1916 0 - -1460 -3128 0 - -1460 -3700 0 - -1460 -3851 0 - -1460 -3960 0 - -1580 -1916 0 - -1580 -3128 0 - -1580 -3700 0 - -1580 -3851 0 - -1580 -3960 0 - -1916 -3128 0 - -1916 -3700 0 - -1916 -3851 0 - -1916 -3960 0 - -3128 -3700 0 - -3128 -3851 0 - -3128 -3960 0 - -3700 -3851 0 - -3700 -3960 0 - -3851 -3960 0 - -43 -406 0 - -43 -1133 0 - -43 -1461 0 - -43 -1795 0 - -43 -3129 0 - -43 -3961 0 - -406 -1133 0 - -406 -1461 0 - -406 -1795 0 - -406 -3129 0 - -406 -3961 0 - -1133 -1461 0 - -1133 -1795 0 - -1133 -3129 0 - -1133 -3961 0 - -1461 -1795 0 - -1461 -3129 0 - -1461 -3961 0 - -1795 -3129 0 - -1795 -3961 0 - -3129 -3961 0 - -610 -857 0 - -610 -1258 0 - -610 -1462 0 - -610 -1581 0 - -610 -2093 0 - -610 -3130 0 - -610 -4334 0 - -857 -1258 0 - -857 -1462 0 - -857 -1581 0 - -857 -2093 0 - -857 -3130 0 - -857 -4334 0 - -1258 -1462 0 - -1258 -1581 0 - -1258 -2093 0 - -1258 -3130 0 - -1258 -4334 0 - -1462 -1581 0 - -1462 -2093 0 - -1462 -3130 0 - -1462 -4334 0 - -1581 -2093 0 - -1581 -3130 0 - -1581 -4334 0 - -2093 -3130 0 - -2093 -4334 0 - -3130 -4334 0 - -188 -611 0 - -188 -1134 0 - -188 -1340 0 - -188 -1672 0 - -188 -2310 0 - -188 -3131 0 - -188 -3701 0 - -611 -1134 0 - -611 -1340 0 - -611 -1672 0 - -611 -2310 0 - -611 -3131 0 - -611 -3701 0 - -1134 -1340 0 - -1134 -1672 0 - -1134 -2310 0 - -1134 -3131 0 - -1134 -3701 0 - -1340 -1672 0 - -1340 -2310 0 - -1340 -3131 0 - -1340 -3701 0 - -1672 -2310 0 - -1672 -3131 0 - -1672 -3701 0 - -2310 -3131 0 - -2310 -3701 0 - -3131 -3701 0 - -407 -612 0 - -407 -1259 0 - -407 -1917 0 - -407 -2094 0 - -407 -3962 0 - -612 -1259 0 - -612 -1917 0 - -612 -2094 0 - -612 -3962 0 - -1259 -1917 0 - -1259 -2094 0 - -1259 -3962 0 - -1917 -2094 0 - -1917 -3962 0 - -2094 -3962 0 - -44 -189 0 - -44 -1135 0 - -44 -1260 0 - -44 -1796 0 - -44 -1918 0 - -44 -2095 0 - -44 -2311 0 - -44 -3702 0 - -44 -3852 0 - -44 -3963 0 - -44 -4335 0 - -189 -1135 0 - -189 -1260 0 - -189 -1796 0 - -189 -1918 0 - -189 -2095 0 - -189 -2311 0 - -189 -3702 0 - -189 -3852 0 - -189 -3963 0 - -189 -4335 0 - -1135 -1260 0 - -1135 -1796 0 - -1135 -1918 0 - -1135 -2095 0 - -1135 -2311 0 - -1135 -3702 0 - -1135 -3852 0 - -1135 -3963 0 - -1135 -4335 0 - -1260 -1796 0 - -1260 -1918 0 - -1260 -2095 0 - -1260 -2311 0 - -1260 -3702 0 - -1260 -3852 0 - -1260 -3963 0 - -1260 -4335 0 - -1796 -1918 0 - -1796 -2095 0 - -1796 -2311 0 - -1796 -3702 0 - -1796 -3852 0 - -1796 -3963 0 - -1796 -4335 0 - -1918 -2095 0 - -1918 -2311 0 - -1918 -3702 0 - -1918 -3852 0 - -1918 -3963 0 - -1918 -4335 0 - -2095 -2311 0 - -2095 -3702 0 - -2095 -3852 0 - -2095 -3963 0 - -2095 -4335 0 - -2311 -3702 0 - -2311 -3852 0 - -2311 -3963 0 - -2311 -4335 0 - -3702 -3852 0 - -3702 -3963 0 - -3702 -4335 0 - -3852 -3963 0 - -3852 -4335 0 - -3963 -4335 0 - -408 -1463 0 - -408 -1673 0 - -408 -1797 0 - -408 -1919 0 - -408 -2096 0 - -408 -2312 0 - -1463 -1673 0 - -1463 -1797 0 - -1463 -1919 0 - -1463 -2096 0 - -1463 -2312 0 - -1673 -1797 0 - -1673 -1919 0 - -1673 -2096 0 - -1673 -2312 0 - -1797 -1919 0 - -1797 -2096 0 - -1797 -2312 0 - -1919 -2096 0 - -1919 -2312 0 - -2096 -2312 0 - -45 -858 0 - -45 -1261 0 - -45 -1464 0 - -45 -1798 0 - -45 -1920 0 - -45 -2097 0 - -45 -2313 0 - -45 -3132 0 - -45 -4336 0 - -858 -1261 0 - -858 -1464 0 - -858 -1798 0 - -858 -1920 0 - -858 -2097 0 - -858 -2313 0 - -858 -3132 0 - -858 -4336 0 - -1261 -1464 0 - -1261 -1798 0 - -1261 -1920 0 - -1261 -2097 0 - -1261 -2313 0 - -1261 -3132 0 - -1261 -4336 0 - -1464 -1798 0 - -1464 -1920 0 - -1464 -2097 0 - -1464 -2313 0 - -1464 -3132 0 - -1464 -4336 0 - -1798 -1920 0 - -1798 -2097 0 - -1798 -2313 0 - -1798 -3132 0 - -1798 -4336 0 - -1920 -2097 0 - -1920 -2313 0 - -1920 -3132 0 - -1920 -4336 0 - -2097 -2313 0 - -2097 -3132 0 - -2097 -4336 0 - -2313 -3132 0 - -2313 -4336 0 - -3132 -4336 0 - -613 -966 0 - -613 -1465 0 - -613 -1799 0 - -613 -2846 0 - -613 -4052 0 - -966 -1465 0 - -966 -1799 0 - -966 -2846 0 - -966 -4052 0 - -1465 -1799 0 - -1465 -2846 0 - -1465 -4052 0 - -1799 -2846 0 - -1799 -4052 0 - -2846 -4052 0 - -614 -967 0 - -614 -3528 0 - -614 -4156 0 - -614 -4276 0 - -967 -3528 0 - -967 -4156 0 - -967 -4276 0 - -3528 -4156 0 - -3528 -4276 0 - -4156 -4276 0 - -615 -968 0 - -615 -1136 0 - -615 -1466 0 - -615 -1800 0 - -615 -2098 0 - -615 -2220 0 - -615 -2847 0 - -615 -3529 0 - -615 -3853 0 - -615 -4157 0 - -968 -1136 0 - -968 -1466 0 - -968 -1800 0 - -968 -2098 0 - -968 -2220 0 - -968 -2847 0 - -968 -3529 0 - -968 -3853 0 - -968 -4157 0 - -1136 -1466 0 - -1136 -1800 0 - -1136 -2098 0 - -1136 -2220 0 - -1136 -2847 0 - -1136 -3529 0 - -1136 -3853 0 - -1136 -4157 0 - -1466 -1800 0 - -1466 -2098 0 - -1466 -2220 0 - -1466 -2847 0 - -1466 -3529 0 - -1466 -3853 0 - -1466 -4157 0 - -1800 -2098 0 - -1800 -2220 0 - -1800 -2847 0 - -1800 -3529 0 - -1800 -3853 0 - -1800 -4157 0 - -2098 -2220 0 - -2098 -2847 0 - -2098 -3529 0 - -2098 -3853 0 - -2098 -4157 0 - -2220 -2847 0 - -2220 -3529 0 - -2220 -3853 0 - -2220 -4157 0 - -2847 -3529 0 - -2847 -3853 0 - -2847 -4157 0 - -3529 -3853 0 - -3529 -4157 0 - -3853 -4157 0 - -616 -969 0 - -616 -1137 0 - -616 -1467 0 - -616 -1801 0 - -616 -2099 0 - -616 -2848 0 - -616 -2962 0 - -616 -3530 0 - -616 -3854 0 - -616 -3964 0 - -969 -1137 0 - -969 -1467 0 - -969 -1801 0 - -969 -2099 0 - -969 -2848 0 - -969 -2962 0 - -969 -3530 0 - -969 -3854 0 - -969 -3964 0 - -1137 -1467 0 - -1137 -1801 0 - -1137 -2099 0 - -1137 -2848 0 - -1137 -2962 0 - -1137 -3530 0 - -1137 -3854 0 - -1137 -3964 0 - -1467 -1801 0 - -1467 -2099 0 - -1467 -2848 0 - -1467 -2962 0 - -1467 -3530 0 - -1467 -3854 0 - -1467 -3964 0 - -1801 -2099 0 - -1801 -2848 0 - -1801 -2962 0 - -1801 -3530 0 - -1801 -3854 0 - -1801 -3964 0 - -2099 -2848 0 - -2099 -2962 0 - -2099 -3530 0 - -2099 -3854 0 - -2099 -3964 0 - -2848 -2962 0 - -2848 -3530 0 - -2848 -3854 0 - -2848 -3964 0 - -2962 -3530 0 - -2962 -3854 0 - -2962 -3964 0 - -3530 -3854 0 - -3530 -3964 0 - -3854 -3964 0 - -1138 -1802 0 - -1138 -2100 0 - -1138 -2221 0 - -1138 -3261 0 - -1138 -3531 0 - -1138 -3965 0 - -1802 -2100 0 - -1802 -2221 0 - -1802 -3261 0 - -1802 -3531 0 - -1802 -3965 0 - -2100 -2221 0 - -2100 -3261 0 - -2100 -3531 0 - -2100 -3965 0 - -2221 -3261 0 - -2221 -3531 0 - -2221 -3965 0 - -3261 -3531 0 - -3261 -3965 0 - -3531 -3965 0 - -970 -1139 0 - -970 -1803 0 - -970 -2222 0 - -970 -2963 0 - -970 -3262 0 - -970 -3532 0 - -970 -3966 0 - -970 -4053 0 - -1139 -1803 0 - -1139 -2222 0 - -1139 -2963 0 - -1139 -3262 0 - -1139 -3532 0 - -1139 -3966 0 - -1139 -4053 0 - -1803 -2222 0 - -1803 -2963 0 - -1803 -3262 0 - -1803 -3532 0 - -1803 -3966 0 - -1803 -4053 0 - -2222 -2963 0 - -2222 -3262 0 - -2222 -3532 0 - -2222 -3966 0 - -2222 -4053 0 - -2963 -3262 0 - -2963 -3532 0 - -2963 -3966 0 - -2963 -4053 0 - -3262 -3532 0 - -3262 -3966 0 - -3262 -4053 0 - -3532 -3966 0 - -3532 -4053 0 - -3966 -4053 0 - -617 -2849 0 - -617 -3263 0 - -617 -3855 0 - -617 -3967 0 - -617 -4158 0 - -2849 -3263 0 - -2849 -3855 0 - -2849 -3967 0 - -2849 -4158 0 - -3263 -3855 0 - -3263 -3967 0 - -3263 -4158 0 - -3855 -3967 0 - -3855 -4158 0 - -3967 -4158 0 - -971 -2101 0 - -971 -3264 0 - -2101 -3264 0 - -1140 -1468 0 - -1140 -2223 0 - -1140 -3265 0 - -1140 -3533 0 - -1140 -3856 0 - -1140 -3968 0 - -1140 -4159 0 - -1468 -2223 0 - -1468 -3265 0 - -1468 -3533 0 - -1468 -3856 0 - -1468 -3968 0 - -1468 -4159 0 - -2223 -3265 0 - -2223 -3533 0 - -2223 -3856 0 - -2223 -3968 0 - -2223 -4159 0 - -3265 -3533 0 - -3265 -3856 0 - -3265 -3968 0 - -3265 -4159 0 - -3533 -3856 0 - -3533 -3968 0 - -3533 -4159 0 - -3856 -3968 0 - -3856 -4159 0 - -3968 -4159 0 - -618 -1141 0 - -618 -1469 0 - -618 -1804 0 - -618 -2102 0 - -618 -2850 0 - -618 -4277 0 - -1141 -1469 0 - -1141 -1804 0 - -1141 -2102 0 - -1141 -2850 0 - -1141 -4277 0 - -1469 -1804 0 - -1469 -2102 0 - -1469 -2850 0 - -1469 -4277 0 - -1804 -2102 0 - -1804 -2850 0 - -1804 -4277 0 - -2102 -2850 0 - -2102 -4277 0 - -2850 -4277 0 - -972 -2103 0 - -972 -2851 0 - -972 -2964 0 - -972 -3266 0 - -972 -3857 0 - -972 -4160 0 - -2103 -2851 0 - -2103 -2964 0 - -2103 -3266 0 - -2103 -3857 0 - -2103 -4160 0 - -2851 -2964 0 - -2851 -3266 0 - -2851 -3857 0 - -2851 -4160 0 - -2964 -3266 0 - -2964 -3857 0 - -2964 -4160 0 - -3266 -3857 0 - -3266 -4160 0 - -3857 -4160 0 - -619 -1470 0 - -619 -2224 0 - -619 -2965 0 - -619 -3267 0 - -619 -3858 0 - -619 -4054 0 - -1470 -2224 0 - -1470 -2965 0 - -1470 -3267 0 - -1470 -3858 0 - -1470 -4054 0 - -2224 -2965 0 - -2224 -3267 0 - -2224 -3858 0 - -2224 -4054 0 - -2965 -3267 0 - -2965 -3858 0 - -2965 -4054 0 - -3267 -3858 0 - -3267 -4054 0 - -3858 -4054 0 - -620 -1471 0 - -620 -2104 0 - -620 -2225 0 - -620 -3268 0 - -620 -4055 0 - -620 -4278 0 - -1471 -2104 0 - -1471 -2225 0 - -1471 -3268 0 - -1471 -4055 0 - -1471 -4278 0 - -2104 -2225 0 - -2104 -3268 0 - -2104 -4055 0 - -2104 -4278 0 - -2225 -3268 0 - -2225 -4055 0 - -2225 -4278 0 - -3268 -4055 0 - -3268 -4278 0 - -4055 -4278 0 - -1472 -1805 0 - -1472 -2105 0 - -1472 -2966 0 - -1472 -4161 0 - -1472 -4279 0 - -1805 -2105 0 - -1805 -2966 0 - -1805 -4161 0 - -1805 -4279 0 - -2105 -2966 0 - -2105 -4161 0 - -2105 -4279 0 - -2966 -4161 0 - -2966 -4279 0 - -4161 -4279 0 - -621 -1142 0 - -621 -1806 0 - -621 -2852 0 - -621 -3969 0 - -621 -4056 0 - -621 -4280 0 - -1142 -1806 0 - -1142 -2852 0 - -1142 -3969 0 - -1142 -4056 0 - -1142 -4280 0 - -1806 -2852 0 - -1806 -3969 0 - -1806 -4056 0 - -1806 -4280 0 - -2852 -3969 0 - -2852 -4056 0 - -2852 -4280 0 - -3969 -4056 0 - -3969 -4280 0 - -4056 -4280 0 - -973 -1473 0 - -973 -1807 0 - -973 -2106 0 - -973 -4162 0 - -973 -4281 0 - -1473 -1807 0 - -1473 -2106 0 - -1473 -4162 0 - -1473 -4281 0 - -1807 -2106 0 - -1807 -4162 0 - -1807 -4281 0 - -2106 -4162 0 - -2106 -4281 0 - -4162 -4281 0 - -974 -1474 0 - -974 -1808 0 - -974 -1921 0 - -974 -2314 0 - -974 -4057 0 - -1474 -1808 0 - -1474 -1921 0 - -1474 -2314 0 - -1474 -4057 0 - -1808 -1921 0 - -1808 -2314 0 - -1808 -4057 0 - -1921 -2314 0 - -1921 -4057 0 - -2314 -4057 0 - -859 -1143 0 - -859 -1475 0 - -859 -1809 0 - -859 -3133 0 - -859 -3534 0 - -859 -3703 0 - -859 -3970 0 - -859 -4058 0 - -859 -4163 0 - -1143 -1475 0 - -1143 -1809 0 - -1143 -3133 0 - -1143 -3534 0 - -1143 -3703 0 - -1143 -3970 0 - -1143 -4058 0 - -1143 -4163 0 - -1475 -1809 0 - -1475 -3133 0 - -1475 -3534 0 - -1475 -3703 0 - -1475 -3970 0 - -1475 -4058 0 - -1475 -4163 0 - -1809 -3133 0 - -1809 -3534 0 - -1809 -3703 0 - -1809 -3970 0 - -1809 -4058 0 - -1809 -4163 0 - -3133 -3534 0 - -3133 -3703 0 - -3133 -3970 0 - -3133 -4058 0 - -3133 -4163 0 - -3534 -3703 0 - -3534 -3970 0 - -3534 -4058 0 - -3534 -4163 0 - -3703 -3970 0 - -3703 -4058 0 - -3703 -4163 0 - -3970 -4058 0 - -3970 -4163 0 - -4058 -4163 0 - -860 -975 0 - -860 -3134 0 - -860 -3535 0 - -860 -4164 0 - -975 -3134 0 - -975 -3535 0 - -975 -4164 0 - -3134 -3535 0 - -3134 -4164 0 - -3535 -4164 0 - -190 -505 0 - -190 -861 0 - -190 -976 0 - -190 -1144 0 - -190 -1476 0 - -190 -1810 0 - -190 -2565 0 - -190 -3536 0 - -190 -3704 0 - -190 -4165 0 - -505 -861 0 - -505 -976 0 - -505 -1144 0 - -505 -1476 0 - -505 -1810 0 - -505 -2565 0 - -505 -3536 0 - -505 -3704 0 - -505 -4165 0 - -861 -976 0 - -861 -1144 0 - -861 -1476 0 - -861 -1810 0 - -861 -2565 0 - -861 -3536 0 - -861 -3704 0 - -861 -4165 0 - -976 -1144 0 - -976 -1476 0 - -976 -1810 0 - -976 -2565 0 - -976 -3536 0 - -976 -3704 0 - -976 -4165 0 - -1144 -1476 0 - -1144 -1810 0 - -1144 -2565 0 - -1144 -3536 0 - -1144 -3704 0 - -1144 -4165 0 - -1476 -1810 0 - -1476 -2565 0 - -1476 -3536 0 - -1476 -3704 0 - -1476 -4165 0 - -1810 -2565 0 - -1810 -3536 0 - -1810 -3704 0 - -1810 -4165 0 - -2565 -3536 0 - -2565 -3704 0 - -2565 -4165 0 - -3536 -3704 0 - -3536 -4165 0 - -3704 -4165 0 - -191 -506 0 - -191 -862 0 - -191 -977 0 - -191 -1145 0 - -191 -1811 0 - -191 -1922 0 - -191 -2566 0 - -191 -3135 0 - -191 -3537 0 - -191 -3705 0 - -191 -3971 0 - -191 -4059 0 - -506 -862 0 - -506 -977 0 - -506 -1145 0 - -506 -1811 0 - -506 -1922 0 - -506 -2566 0 - -506 -3135 0 - -506 -3537 0 - -506 -3705 0 - -506 -3971 0 - -506 -4059 0 - -862 -977 0 - -862 -1145 0 - -862 -1811 0 - -862 -1922 0 - -862 -2566 0 - -862 -3135 0 - -862 -3537 0 - -862 -3705 0 - -862 -3971 0 - -862 -4059 0 - -977 -1145 0 - -977 -1811 0 - -977 -1922 0 - -977 -2566 0 - -977 -3135 0 - -977 -3537 0 - -977 -3705 0 - -977 -3971 0 - -977 -4059 0 - -1145 -1811 0 - -1145 -1922 0 - -1145 -2566 0 - -1145 -3135 0 - -1145 -3537 0 - -1145 -3705 0 - -1145 -3971 0 - -1145 -4059 0 - -1811 -1922 0 - -1811 -2566 0 - -1811 -3135 0 - -1811 -3537 0 - -1811 -3705 0 - -1811 -3971 0 - -1811 -4059 0 - -1922 -2566 0 - -1922 -3135 0 - -1922 -3537 0 - -1922 -3705 0 - -1922 -3971 0 - -1922 -4059 0 - -2566 -3135 0 - -2566 -3537 0 - -2566 -3705 0 - -2566 -3971 0 - -2566 -4059 0 - -3135 -3537 0 - -3135 -3705 0 - -3135 -3971 0 - -3135 -4059 0 - -3537 -3705 0 - -3537 -3971 0 - -3537 -4059 0 - -3705 -3971 0 - -3705 -4059 0 - -3971 -4059 0 - -1582 -3706 0 - -1582 -3972 0 - -1582 -4166 0 - -3706 -3972 0 - -3706 -4166 0 - -3972 -4166 0 - -507 -1477 0 - -507 -1583 0 - -507 -2315 0 - -507 -3538 0 - -507 -4167 0 - -1477 -1583 0 - -1477 -2315 0 - -1477 -3538 0 - -1477 -4167 0 - -1583 -2315 0 - -1583 -3538 0 - -1583 -4167 0 - -2315 -3538 0 - -2315 -4167 0 - -3538 -4167 0 - -508 -863 0 - -508 -978 0 - -508 -2567 0 - -508 -3136 0 - -508 -3707 0 - -863 -978 0 - -863 -2567 0 - -863 -3136 0 - -863 -3707 0 - -978 -2567 0 - -978 -3136 0 - -978 -3707 0 - -2567 -3136 0 - -2567 -3707 0 - -3136 -3707 0 - -509 -1146 0 - -509 -1478 0 - -509 -1584 0 - -509 -1812 0 - -509 -3708 0 - -1146 -1478 0 - -1146 -1584 0 - -1146 -1812 0 - -1146 -3708 0 - -1478 -1584 0 - -1478 -1812 0 - -1478 -3708 0 - -1584 -1812 0 - -1584 -3708 0 - -1812 -3708 0 - -192 -1147 0 - -192 -1479 0 - -192 -1813 0 - -192 -3137 0 - -192 -3973 0 - -192 -4168 0 - -1147 -1479 0 - -1147 -1813 0 - -1147 -3137 0 - -1147 -3973 0 - -1147 -4168 0 - -1479 -1813 0 - -1479 -3137 0 - -1479 -3973 0 - -1479 -4168 0 - -1813 -3137 0 - -1813 -3973 0 - -1813 -4168 0 - -3137 -3973 0 - -3137 -4168 0 - -3973 -4168 0 - -193 -979 0 - -193 -1148 0 - -193 -1585 0 - -193 -1923 0 - -193 -2568 0 - -193 -3138 0 - -193 -3539 0 - -979 -1148 0 - -979 -1585 0 - -979 -1923 0 - -979 -2568 0 - -979 -3138 0 - -979 -3539 0 - -1148 -1585 0 - -1148 -1923 0 - -1148 -2568 0 - -1148 -3138 0 - -1148 -3539 0 - -1585 -1923 0 - -1585 -2568 0 - -1585 -3138 0 - -1585 -3539 0 - -1923 -2568 0 - -1923 -3138 0 - -1923 -3539 0 - -2568 -3138 0 - -2568 -3539 0 - -3138 -3539 0 - -194 -510 0 - -194 -1480 0 - -194 -1586 0 - -194 -2569 0 - -194 -3139 0 - -194 -3709 0 - -194 -4060 0 - -510 -1480 0 - -510 -1586 0 - -510 -2569 0 - -510 -3139 0 - -510 -3709 0 - -510 -4060 0 - -1480 -1586 0 - -1480 -2569 0 - -1480 -3139 0 - -1480 -3709 0 - -1480 -4060 0 - -1586 -2569 0 - -1586 -3139 0 - -1586 -3709 0 - -1586 -4060 0 - -2569 -3139 0 - -2569 -3709 0 - -2569 -4060 0 - -3139 -3709 0 - -3139 -4060 0 - -3709 -4060 0 - -980 -1481 0 - -980 -1587 0 - -980 -1924 0 - -980 -3140 0 - -980 -3710 0 - -980 -3974 0 - -1481 -1587 0 - -1481 -1924 0 - -1481 -3140 0 - -1481 -3710 0 - -1481 -3974 0 - -1587 -1924 0 - -1587 -3140 0 - -1587 -3710 0 - -1587 -3974 0 - -1924 -3140 0 - -1924 -3710 0 - -1924 -3974 0 - -3140 -3710 0 - -3140 -3974 0 - -3710 -3974 0 - -195 -511 0 - -195 -981 0 - -195 -1482 0 - -195 -1814 0 - -195 -1925 0 - -195 -3141 0 - -195 -4169 0 - -511 -981 0 - -511 -1482 0 - -511 -1814 0 - -511 -1925 0 - -511 -3141 0 - -511 -4169 0 - -981 -1482 0 - -981 -1814 0 - -981 -1925 0 - -981 -3141 0 - -981 -4169 0 - -1482 -1814 0 - -1482 -1925 0 - -1482 -3141 0 - -1482 -4169 0 - -1814 -1925 0 - -1814 -3141 0 - -1814 -4169 0 - -1925 -3141 0 - -1925 -4169 0 - -3141 -4169 0 - -196 -512 0 - -196 -1149 0 - -196 -1815 0 - -196 -1926 0 - -196 -2316 0 - -196 -3142 0 - -196 -3540 0 - -196 -3711 0 - -196 -3975 0 - -512 -1149 0 - -512 -1815 0 - -512 -1926 0 - -512 -2316 0 - -512 -3142 0 - -512 -3540 0 - -512 -3711 0 - -512 -3975 0 - -1149 -1815 0 - -1149 -1926 0 - -1149 -2316 0 - -1149 -3142 0 - -1149 -3540 0 - -1149 -3711 0 - -1149 -3975 0 - -1815 -1926 0 - -1815 -2316 0 - -1815 -3142 0 - -1815 -3540 0 - -1815 -3711 0 - -1815 -3975 0 - -1926 -2316 0 - -1926 -3142 0 - -1926 -3540 0 - -1926 -3711 0 - -1926 -3975 0 - -2316 -3142 0 - -2316 -3540 0 - -2316 -3711 0 - -2316 -3975 0 - -3142 -3540 0 - -3142 -3711 0 - -3142 -3975 0 - -3540 -3711 0 - -3540 -3975 0 - -3711 -3975 0 - -197 -1150 0 - -197 -1816 0 - -197 -1927 0 - -197 -2317 0 - -197 -3712 0 - -197 -3976 0 - -197 -4061 0 - -197 -4170 0 - -1150 -1816 0 - -1150 -1927 0 - -1150 -2317 0 - -1150 -3712 0 - -1150 -3976 0 - -1150 -4061 0 - -1150 -4170 0 - -1816 -1927 0 - -1816 -2317 0 - -1816 -3712 0 - -1816 -3976 0 - -1816 -4061 0 - -1816 -4170 0 - -1927 -2317 0 - -1927 -3712 0 - -1927 -3976 0 - -1927 -4061 0 - -1927 -4170 0 - -2317 -3712 0 - -2317 -3976 0 - -2317 -4061 0 - -2317 -4170 0 - -3712 -3976 0 - -3712 -4061 0 - -3712 -4170 0 - -3976 -4061 0 - -3976 -4170 0 - -4061 -4170 0 - -864 -982 0 - -864 -1483 0 - -864 -1817 0 - -864 -1928 0 - -864 -2318 0 - -864 -3143 0 - -864 -4171 0 - -982 -1483 0 - -982 -1817 0 - -982 -1928 0 - -982 -2318 0 - -982 -3143 0 - -982 -4171 0 - -1483 -1817 0 - -1483 -1928 0 - -1483 -2318 0 - -1483 -3143 0 - -1483 -4171 0 - -1817 -1928 0 - -1817 -2318 0 - -1817 -3143 0 - -1817 -4171 0 - -1928 -2318 0 - -1928 -3143 0 - -1928 -4171 0 - -2318 -3143 0 - -2318 -4171 0 - -3143 -4171 0 - -46 -283 0 - -46 -760 0 - -46 -983 0 - -46 -1484 0 - -46 -1818 0 - -46 -3381 0 - -46 -4337 0 - -283 -760 0 - -283 -983 0 - -283 -1484 0 - -283 -1818 0 - -283 -3381 0 - -283 -4337 0 - -760 -983 0 - -760 -1484 0 - -760 -1818 0 - -760 -3381 0 - -760 -4337 0 - -983 -1484 0 - -983 -1818 0 - -983 -3381 0 - -983 -4337 0 - -1484 -1818 0 - -1484 -3381 0 - -1484 -4337 0 - -1818 -3381 0 - -1818 -4337 0 - -3381 -4337 0 - -47 -761 0 - -47 -1485 0 - -47 -1819 0 - -47 -2107 0 - -47 -2388 0 - -47 -3382 0 - -47 -3541 0 - -47 -3713 0 - -761 -1485 0 - -761 -1819 0 - -761 -2107 0 - -761 -2388 0 - -761 -3382 0 - -761 -3541 0 - -761 -3713 0 - -1485 -1819 0 - -1485 -2107 0 - -1485 -2388 0 - -1485 -3382 0 - -1485 -3541 0 - -1485 -3713 0 - -1819 -2107 0 - -1819 -2388 0 - -1819 -3382 0 - -1819 -3541 0 - -1819 -3713 0 - -2107 -2388 0 - -2107 -3382 0 - -2107 -3541 0 - -2107 -3713 0 - -2388 -3382 0 - -2388 -3541 0 - -2388 -3713 0 - -3382 -3541 0 - -3382 -3713 0 - -3541 -3713 0 - -762 -984 0 - -762 -1341 0 - -762 -1486 0 - -762 -1820 0 - -762 -2108 0 - -762 -2657 0 - -762 -2967 0 - -762 -3383 0 - -762 -3542 0 - -762 -3859 0 - -762 -4338 0 - -984 -1341 0 - -984 -1486 0 - -984 -1820 0 - -984 -2108 0 - -984 -2657 0 - -984 -2967 0 - -984 -3383 0 - -984 -3542 0 - -984 -3859 0 - -984 -4338 0 - -1341 -1486 0 - -1341 -1820 0 - -1341 -2108 0 - -1341 -2657 0 - -1341 -2967 0 - -1341 -3383 0 - -1341 -3542 0 - -1341 -3859 0 - -1341 -4338 0 - -1486 -1820 0 - -1486 -2108 0 - -1486 -2657 0 - -1486 -2967 0 - -1486 -3383 0 - -1486 -3542 0 - -1486 -3859 0 - -1486 -4338 0 - -1820 -2108 0 - -1820 -2657 0 - -1820 -2967 0 - -1820 -3383 0 - -1820 -3542 0 - -1820 -3859 0 - -1820 -4338 0 - -2108 -2657 0 - -2108 -2967 0 - -2108 -3383 0 - -2108 -3542 0 - -2108 -3859 0 - -2108 -4338 0 - -2657 -2967 0 - -2657 -3383 0 - -2657 -3542 0 - -2657 -3859 0 - -2657 -4338 0 - -2967 -3383 0 - -2967 -3542 0 - -2967 -3859 0 - -2967 -4338 0 - -3383 -3542 0 - -3383 -3859 0 - -3383 -4338 0 - -3542 -3859 0 - -3542 -4338 0 - -3859 -4338 0 - -48 -1821 0 - -48 -2109 0 - -48 -3543 0 - -1821 -2109 0 - -1821 -3543 0 - -2109 -3543 0 - -49 -1487 0 - -49 -2968 0 - -49 -3384 0 - -49 -3544 0 - -49 -3714 0 - -1487 -2968 0 - -1487 -3384 0 - -1487 -3544 0 - -1487 -3714 0 - -2968 -3384 0 - -2968 -3544 0 - -2968 -3714 0 - -3384 -3544 0 - -3384 -3714 0 - -3544 -3714 0 - -284 -763 0 - -284 -985 0 - -284 -2110 0 - -284 -3385 0 - -284 -3715 0 - -763 -985 0 - -763 -2110 0 - -763 -3385 0 - -763 -3715 0 - -985 -2110 0 - -985 -3385 0 - -985 -3715 0 - -2110 -3385 0 - -2110 -3715 0 - -3385 -3715 0 - -285 -764 0 - -285 -986 0 - -285 -2111 0 - -285 -2658 0 - -285 -3386 0 - -285 -3716 0 - -285 -4339 0 - -764 -986 0 - -764 -2111 0 - -764 -2658 0 - -764 -3386 0 - -764 -3716 0 - -764 -4339 0 - -986 -2111 0 - -986 -2658 0 - -986 -3386 0 - -986 -3716 0 - -986 -4339 0 - -2111 -2658 0 - -2111 -3386 0 - -2111 -3716 0 - -2111 -4339 0 - -2658 -3386 0 - -2658 -3716 0 - -2658 -4339 0 - -3386 -3716 0 - -3386 -4339 0 - -3716 -4339 0 - -50 -765 0 - -50 -1488 0 - -50 -1822 0 - -50 -2112 0 - -50 -2389 0 - -50 -3717 0 - -50 -4340 0 - -765 -1488 0 - -765 -1822 0 - -765 -2112 0 - -765 -2389 0 - -765 -3717 0 - -765 -4340 0 - -1488 -1822 0 - -1488 -2112 0 - -1488 -2389 0 - -1488 -3717 0 - -1488 -4340 0 - -1822 -2112 0 - -1822 -2389 0 - -1822 -3717 0 - -1822 -4340 0 - -2112 -2389 0 - -2112 -3717 0 - -2112 -4340 0 - -2389 -3717 0 - -2389 -4340 0 - -3717 -4340 0 - -51 -766 0 - -51 -987 0 - -51 -2390 0 - -51 -2659 0 - -51 -3545 0 - -51 -3718 0 - -51 -4341 0 - -766 -987 0 - -766 -2390 0 - -766 -2659 0 - -766 -3545 0 - -766 -3718 0 - -766 -4341 0 - -987 -2390 0 - -987 -2659 0 - -987 -3545 0 - -987 -3718 0 - -987 -4341 0 - -2390 -2659 0 - -2390 -3545 0 - -2390 -3718 0 - -2390 -4341 0 - -2659 -3545 0 - -2659 -3718 0 - -2659 -4341 0 - -3545 -3718 0 - -3545 -4341 0 - -3718 -4341 0 - -286 -988 0 - -286 -1342 0 - -286 -2113 0 - -286 -2969 0 - -286 -3546 0 - -286 -3719 0 - -286 -4342 0 - -988 -1342 0 - -988 -2113 0 - -988 -2969 0 - -988 -3546 0 - -988 -3719 0 - -988 -4342 0 - -1342 -2113 0 - -1342 -2969 0 - -1342 -3546 0 - -1342 -3719 0 - -1342 -4342 0 - -2113 -2969 0 - -2113 -3546 0 - -2113 -3719 0 - -2113 -4342 0 - -2969 -3546 0 - -2969 -3719 0 - -2969 -4342 0 - -3546 -3719 0 - -3546 -4342 0 - -3719 -4342 0 - -1489 -2114 0 - -1489 -3387 0 - -1489 -4343 0 - -2114 -3387 0 - -2114 -4343 0 - -3387 -4343 0 - -767 -1490 0 - -767 -1823 0 - -767 -2115 0 - -767 -2970 0 - -767 -3388 0 - -767 -3720 0 - -1490 -1823 0 - -1490 -2115 0 - -1490 -2970 0 - -1490 -3388 0 - -1490 -3720 0 - -1823 -2115 0 - -1823 -2970 0 - -1823 -3388 0 - -1823 -3720 0 - -2115 -2970 0 - -2115 -3388 0 - -2115 -3720 0 - -2970 -3388 0 - -2970 -3720 0 - -3388 -3720 0 - -52 -989 0 - -52 -1491 0 - -52 -1824 0 - -52 -2971 0 - -52 -3389 0 - -52 -3860 0 - -52 -4344 0 - -989 -1491 0 - -989 -1824 0 - -989 -2971 0 - -989 -3389 0 - -989 -3860 0 - -989 -4344 0 - -1491 -1824 0 - -1491 -2971 0 - -1491 -3389 0 - -1491 -3860 0 - -1491 -4344 0 - -1824 -2971 0 - -1824 -3389 0 - -1824 -3860 0 - -1824 -4344 0 - -2971 -3389 0 - -2971 -3860 0 - -2971 -4344 0 - -3389 -3860 0 - -3389 -4344 0 - -3860 -4344 0 - -1343 -2391 0 - -1343 -3390 0 - -1343 -3721 0 - -2391 -3390 0 - -2391 -3721 0 - -3390 -3721 0 - -53 -1825 0 - -53 -2116 0 - -53 -3722 0 - -53 -3861 0 - -53 -4345 0 - -1825 -2116 0 - -1825 -3722 0 - -1825 -3861 0 - -1825 -4345 0 - -2116 -3722 0 - -2116 -3861 0 - -2116 -4345 0 - -3722 -3861 0 - -3722 -4345 0 - -3861 -4345 0 - -287 -990 0 - -287 -1492 0 - -287 -1826 0 - -287 -2117 0 - -287 -2660 0 - -287 -3391 0 - -990 -1492 0 - -990 -1826 0 - -990 -2117 0 - -990 -2660 0 - -990 -3391 0 - -1492 -1826 0 - -1492 -2117 0 - -1492 -2660 0 - -1492 -3391 0 - -1826 -2117 0 - -1826 -2660 0 - -1826 -3391 0 - -2117 -2660 0 - -2117 -3391 0 - -2660 -3391 0 - -768 -1674 0 - -768 -2487 0 - -768 -2745 0 - -768 -3144 0 - -768 -3547 0 - -768 -4346 0 - -1674 -2487 0 - -1674 -2745 0 - -1674 -3144 0 - -1674 -3547 0 - -1674 -4346 0 - -2487 -2745 0 - -2487 -3144 0 - -2487 -3547 0 - -2487 -4346 0 - -2745 -3144 0 - -2745 -3547 0 - -2745 -4346 0 - -3144 -3547 0 - -3144 -4346 0 - -3547 -4346 0 - -769 -1827 0 - -769 -2488 0 - -769 -2570 0 - -769 -2746 0 - -769 -2972 0 - -769 -3548 0 - -769 -4347 0 - -1827 -2488 0 - -1827 -2570 0 - -1827 -2746 0 - -1827 -2972 0 - -1827 -3548 0 - -1827 -4347 0 - -2488 -2570 0 - -2488 -2746 0 - -2488 -2972 0 - -2488 -3548 0 - -2488 -4347 0 - -2570 -2746 0 - -2570 -2972 0 - -2570 -3548 0 - -2570 -4347 0 - -2746 -2972 0 - -2746 -3548 0 - -2746 -4347 0 - -2972 -3548 0 - -2972 -4347 0 - -3548 -4347 0 - -1151 -2489 0 - -1151 -2973 0 - -1151 -3145 0 - -1151 -3269 0 - -1151 -3549 0 - -1151 -4348 0 - -2489 -2973 0 - -2489 -3145 0 - -2489 -3269 0 - -2489 -3549 0 - -2489 -4348 0 - -2973 -3145 0 - -2973 -3269 0 - -2973 -3549 0 - -2973 -4348 0 - -3145 -3269 0 - -3145 -3549 0 - -3145 -4348 0 - -3269 -3549 0 - -3269 -4348 0 - -3549 -4348 0 - -288 -770 0 - -288 -1675 0 - -288 -2490 0 - -288 -2853 0 - -288 -3270 0 - -770 -1675 0 - -770 -2490 0 - -770 -2853 0 - -770 -3270 0 - -1675 -2490 0 - -1675 -2853 0 - -1675 -3270 0 - -2490 -2853 0 - -2490 -3270 0 - -2853 -3270 0 - -771 -1676 0 - -771 -2491 0 - -771 -2974 0 - -771 -3550 0 - -1676 -2491 0 - -1676 -2974 0 - -1676 -3550 0 - -2491 -2974 0 - -2491 -3550 0 - -2974 -3550 0 - -289 -772 0 - -289 -1152 0 - -289 -2226 0 - -289 -2571 0 - -772 -1152 0 - -772 -2226 0 - -772 -2571 0 - -1152 -2226 0 - -1152 -2571 0 - -2226 -2571 0 - -2747 -3146 0 - -2747 -4349 0 - -3146 -4349 0 - -1677 -2748 0 - -1677 -2854 0 - -1677 -2975 0 - -1677 -3147 0 - -1677 -3271 0 - -2748 -2854 0 - -2748 -2975 0 - -2748 -3147 0 - -2748 -3271 0 - -2854 -2975 0 - -2854 -3147 0 - -2854 -3271 0 - -2975 -3147 0 - -2975 -3271 0 - -3147 -3271 0 - -773 -1678 0 - -773 -2749 0 - -773 -3272 0 - -773 -3551 0 - -773 -4350 0 - -1678 -2749 0 - -1678 -3272 0 - -1678 -3551 0 - -1678 -4350 0 - -2749 -3272 0 - -2749 -3551 0 - -2749 -4350 0 - -3272 -3551 0 - -3272 -4350 0 - -3551 -4350 0 - -290 -1153 0 - -290 -1679 0 - -290 -2855 0 - -290 -2976 0 - -290 -3148 0 - -290 -3552 0 - -290 -4351 0 - -1153 -1679 0 - -1153 -2855 0 - -1153 -2976 0 - -1153 -3148 0 - -1153 -3552 0 - -1153 -4351 0 - -1679 -2855 0 - -1679 -2976 0 - -1679 -3148 0 - -1679 -3552 0 - -1679 -4351 0 - -2855 -2976 0 - -2855 -3148 0 - -2855 -3552 0 - -2855 -4351 0 - -2976 -3148 0 - -2976 -3552 0 - -2976 -4351 0 - -3148 -3552 0 - -3148 -4351 0 - -3552 -4351 0 - -774 -1154 0 - -774 -1828 0 - -774 -2492 0 - -774 -3149 0 - -774 -3553 0 - -1154 -1828 0 - -1154 -2492 0 - -1154 -3149 0 - -1154 -3553 0 - -1828 -2492 0 - -1828 -3149 0 - -1828 -3553 0 - -2492 -3149 0 - -2492 -3553 0 - -3149 -3553 0 - -775 -2977 0 - -775 -3554 0 - -2977 -3554 0 - -776 -1829 0 - -776 -2493 0 - -776 -2750 0 - -776 -2978 0 - -1829 -2493 0 - -1829 -2750 0 - -1829 -2978 0 - -2493 -2750 0 - -2493 -2978 0 - -2750 -2978 0 - -1155 -1680 0 - -1155 -2227 0 - -1155 -3150 0 - -1680 -2227 0 - -1680 -3150 0 - -2227 -3150 0 - -291 -777 0 - -291 -1156 0 - -291 -1681 0 - -291 -1830 0 - -291 -2228 0 - -291 -2856 0 - -291 -2979 0 - -291 -3151 0 - -291 -3273 0 - -291 -3555 0 - -777 -1156 0 - -777 -1681 0 - -777 -1830 0 - -777 -2228 0 - -777 -2856 0 - -777 -2979 0 - -777 -3151 0 - -777 -3273 0 - -777 -3555 0 - -1156 -1681 0 - -1156 -1830 0 - -1156 -2228 0 - -1156 -2856 0 - -1156 -2979 0 - -1156 -3151 0 - -1156 -3273 0 - -1156 -3555 0 - -1681 -1830 0 - -1681 -2228 0 - -1681 -2856 0 - -1681 -2979 0 - -1681 -3151 0 - -1681 -3273 0 - -1681 -3555 0 - -1830 -2228 0 - -1830 -2856 0 - -1830 -2979 0 - -1830 -3151 0 - -1830 -3273 0 - -1830 -3555 0 - -2228 -2856 0 - -2228 -2979 0 - -2228 -3151 0 - -2228 -3273 0 - -2228 -3555 0 - -2856 -2979 0 - -2856 -3151 0 - -2856 -3273 0 - -2856 -3555 0 - -2979 -3151 0 - -2979 -3273 0 - -2979 -3555 0 - -3151 -3273 0 - -3151 -3555 0 - -3273 -3555 0 - -198 -292 0 - -198 -865 0 - -198 -1493 0 - -198 -1682 0 - -198 -1831 0 - -198 -2229 0 - -198 -2392 0 - -198 -2857 0 - -198 -3392 0 - -198 -4172 0 - -292 -865 0 - -292 -1493 0 - -292 -1682 0 - -292 -1831 0 - -292 -2229 0 - -292 -2392 0 - -292 -2857 0 - -292 -3392 0 - -292 -4172 0 - -865 -1493 0 - -865 -1682 0 - -865 -1831 0 - -865 -2229 0 - -865 -2392 0 - -865 -2857 0 - -865 -3392 0 - -865 -4172 0 - -1493 -1682 0 - -1493 -1831 0 - -1493 -2229 0 - -1493 -2392 0 - -1493 -2857 0 - -1493 -3392 0 - -1493 -4172 0 - -1682 -1831 0 - -1682 -2229 0 - -1682 -2392 0 - -1682 -2857 0 - -1682 -3392 0 - -1682 -4172 0 - -1831 -2229 0 - -1831 -2392 0 - -1831 -2857 0 - -1831 -3392 0 - -1831 -4172 0 - -2229 -2392 0 - -2229 -2857 0 - -2229 -3392 0 - -2229 -4172 0 - -2392 -2857 0 - -2392 -3392 0 - -2392 -4172 0 - -2857 -3392 0 - -2857 -4172 0 - -3392 -4172 0 - -1494 -1683 0 - -1494 -1832 0 - -1494 -2751 0 - -1494 -2858 0 - -1494 -3977 0 - -1683 -1832 0 - -1683 -2751 0 - -1683 -2858 0 - -1683 -3977 0 - -1832 -2751 0 - -1832 -2858 0 - -1832 -3977 0 - -2751 -2858 0 - -2751 -3977 0 - -2858 -3977 0 - -199 -866 0 - -199 -4173 0 - -199 -4352 0 - -866 -4173 0 - -866 -4352 0 - -4173 -4352 0 - -200 -293 0 - -200 -867 0 - -200 -1684 0 - -200 -1833 0 - -200 -2230 0 - -200 -3978 0 - -293 -867 0 - -293 -1684 0 - -293 -1833 0 - -293 -2230 0 - -293 -3978 0 - -867 -1684 0 - -867 -1833 0 - -867 -2230 0 - -867 -3978 0 - -1684 -1833 0 - -1684 -2230 0 - -1684 -3978 0 - -1833 -2230 0 - -1833 -3978 0 - -2230 -3978 0 - -868 -1495 0 - -868 -1685 0 - -868 -2231 0 - -868 -2859 0 - -868 -3393 0 - -1495 -1685 0 - -1495 -2231 0 - -1495 -2859 0 - -1495 -3393 0 - -1685 -2231 0 - -1685 -2859 0 - -1685 -3393 0 - -2231 -2859 0 - -2231 -3393 0 - -2859 -3393 0 - -294 -778 0 - -294 -869 0 - -294 -1686 0 - -294 -2661 0 - -294 -3394 0 - -294 -4353 0 - -778 -869 0 - -778 -1686 0 - -778 -2661 0 - -778 -3394 0 - -778 -4353 0 - -869 -1686 0 - -869 -2661 0 - -869 -3394 0 - -869 -4353 0 - -1686 -2661 0 - -1686 -3394 0 - -1686 -4353 0 - -2661 -3394 0 - -2661 -4353 0 - -3394 -4353 0 - -201 -295 0 - -201 -870 0 - -201 -1496 0 - -201 -1687 0 - -201 -2232 0 - -201 -3395 0 - -201 -3979 0 - -201 -4174 0 - -295 -870 0 - -295 -1496 0 - -295 -1687 0 - -295 -2232 0 - -295 -3395 0 - -295 -3979 0 - -295 -4174 0 - -870 -1496 0 - -870 -1687 0 - -870 -2232 0 - -870 -3395 0 - -870 -3979 0 - -870 -4174 0 - -1496 -1687 0 - -1496 -2232 0 - -1496 -3395 0 - -1496 -3979 0 - -1496 -4174 0 - -1687 -2232 0 - -1687 -3395 0 - -1687 -3979 0 - -1687 -4174 0 - -2232 -3395 0 - -2232 -3979 0 - -2232 -4174 0 - -3395 -3979 0 - -3395 -4174 0 - -3979 -4174 0 - -779 -1688 0 - -779 -2393 0 - -779 -2662 0 - -779 -2752 0 - -779 -4354 0 - -1688 -2393 0 - -1688 -2662 0 - -1688 -2752 0 - -1688 -4354 0 - -2393 -2662 0 - -2393 -2752 0 - -2393 -4354 0 - -2662 -2752 0 - -2662 -4354 0 - -2752 -4354 0 - -296 -1497 0 - -296 -1689 0 - -296 -2394 0 - -296 -2663 0 - -296 -2860 0 - -296 -3980 0 - -296 -4355 0 - -1497 -1689 0 - -1497 -2394 0 - -1497 -2663 0 - -1497 -2860 0 - -1497 -3980 0 - -1497 -4355 0 - -1689 -2394 0 - -1689 -2663 0 - -1689 -2860 0 - -1689 -3980 0 - -1689 -4355 0 - -2394 -2663 0 - -2394 -2860 0 - -2394 -3980 0 - -2394 -4355 0 - -2663 -2860 0 - -2663 -3980 0 - -2663 -4355 0 - -2860 -3980 0 - -2860 -4355 0 - -3980 -4355 0 - -297 -780 0 - -297 -1498 0 - -297 -1588 0 - -297 -2233 0 - -297 -2753 0 - -297 -3981 0 - -780 -1498 0 - -780 -1588 0 - -780 -2233 0 - -780 -2753 0 - -780 -3981 0 - -1498 -1588 0 - -1498 -2233 0 - -1498 -2753 0 - -1498 -3981 0 - -1588 -2233 0 - -1588 -2753 0 - -1588 -3981 0 - -2233 -2753 0 - -2233 -3981 0 - -2753 -3981 0 - -781 -1499 0 - -781 -1834 0 - -781 -2395 0 - -781 -3982 0 - -1499 -1834 0 - -1499 -2395 0 - -1499 -3982 0 - -1834 -2395 0 - -1834 -3982 0 - -2395 -3982 0 - -871 -1500 0 - -871 -1589 0 - -871 -2234 0 - -871 -3396 0 - -871 -4356 0 - -1500 -1589 0 - -1500 -2234 0 - -1500 -3396 0 - -1500 -4356 0 - -1589 -2234 0 - -1589 -3396 0 - -1589 -4356 0 - -2234 -3396 0 - -2234 -4356 0 - -3396 -4356 0 - -202 -782 0 - -202 -872 0 - -202 -1501 0 - -202 -1835 0 - -202 -2754 0 - -202 -3397 0 - -202 -4175 0 - -782 -872 0 - -782 -1501 0 - -782 -1835 0 - -782 -2754 0 - -782 -3397 0 - -782 -4175 0 - -872 -1501 0 - -872 -1835 0 - -872 -2754 0 - -872 -3397 0 - -872 -4175 0 - -1501 -1835 0 - -1501 -2754 0 - -1501 -3397 0 - -1501 -4175 0 - -1835 -2754 0 - -1835 -3397 0 - -1835 -4175 0 - -2754 -3397 0 - -2754 -4175 0 - -3397 -4175 0 - -873 -1836 0 - -873 -2396 0 - -873 -2861 0 - -873 -3398 0 - -873 -3983 0 - -1836 -2396 0 - -1836 -2861 0 - -1836 -3398 0 - -1836 -3983 0 - -2396 -2861 0 - -2396 -3398 0 - -2396 -3983 0 - -2861 -3398 0 - -2861 -3983 0 - -3398 -3983 0 - -203 -1502 0 - -203 -1690 0 - -203 -2235 0 - -203 -2397 0 - -203 -3984 0 - -1502 -1690 0 - -1502 -2235 0 - -1502 -2397 0 - -1502 -3984 0 - -1690 -2235 0 - -1690 -2397 0 - -1690 -3984 0 - -2235 -2397 0 - -2235 -3984 0 - -2397 -3984 0 - -204 -298 0 - -204 -783 0 - -204 -1691 0 - -204 -1837 0 - -204 -2236 0 - -204 -2398 0 - -204 -2862 0 - -204 -3985 0 - -298 -783 0 - -298 -1691 0 - -298 -1837 0 - -298 -2236 0 - -298 -2398 0 - -298 -2862 0 - -298 -3985 0 - -783 -1691 0 - -783 -1837 0 - -783 -2236 0 - -783 -2398 0 - -783 -2862 0 - -783 -3985 0 - -1691 -1837 0 - -1691 -2236 0 - -1691 -2398 0 - -1691 -2862 0 - -1691 -3985 0 - -1837 -2236 0 - -1837 -2398 0 - -1837 -2862 0 - -1837 -3985 0 - -2236 -2398 0 - -2236 -2862 0 - -2236 -3985 0 - -2398 -2862 0 - -2398 -3985 0 - -2862 -3985 0 - -299 -1503 0 - -299 -1692 0 - -299 -1838 0 - -299 -2664 0 - -299 -2755 0 - -299 -2863 0 - -299 -3399 0 - -299 -4176 0 - -1503 -1692 0 - -1503 -1838 0 - -1503 -2664 0 - -1503 -2755 0 - -1503 -2863 0 - -1503 -3399 0 - -1503 -4176 0 - -1692 -1838 0 - -1692 -2664 0 - -1692 -2755 0 - -1692 -2863 0 - -1692 -3399 0 - -1692 -4176 0 - -1838 -2664 0 - -1838 -2755 0 - -1838 -2863 0 - -1838 -3399 0 - -1838 -4176 0 - -2664 -2755 0 - -2664 -2863 0 - -2664 -3399 0 - -2664 -4176 0 - -2755 -2863 0 - -2755 -3399 0 - -2755 -4176 0 - -2863 -3399 0 - -2863 -4176 0 - -3399 -4176 0 - -54 -300 0 - -54 -622 0 - -54 -991 0 - -54 -1504 0 - -54 -1839 0 - -54 -1929 0 - -54 -4357 0 - -300 -622 0 - -300 -991 0 - -300 -1504 0 - -300 -1839 0 - -300 -1929 0 - -300 -4357 0 - -622 -991 0 - -622 -1504 0 - -622 -1839 0 - -622 -1929 0 - -622 -4357 0 - -991 -1504 0 - -991 -1839 0 - -991 -1929 0 - -991 -4357 0 - -1504 -1839 0 - -1504 -1929 0 - -1504 -4357 0 - -1839 -1929 0 - -1839 -4357 0 - -1929 -4357 0 - -301 -623 0 - -301 -874 0 - -301 -992 0 - -301 -1505 0 - -301 -1840 0 - -301 -2399 0 - -301 -2494 0 - -301 -3556 0 - -301 -3723 0 - -623 -874 0 - -623 -992 0 - -623 -1505 0 - -623 -1840 0 - -623 -2399 0 - -623 -2494 0 - -623 -3556 0 - -623 -3723 0 - -874 -992 0 - -874 -1505 0 - -874 -1840 0 - -874 -2399 0 - -874 -2494 0 - -874 -3556 0 - -874 -3723 0 - -992 -1505 0 - -992 -1840 0 - -992 -2399 0 - -992 -2494 0 - -992 -3556 0 - -992 -3723 0 - -1505 -1840 0 - -1505 -2399 0 - -1505 -2494 0 - -1505 -3556 0 - -1505 -3723 0 - -1840 -2399 0 - -1840 -2494 0 - -1840 -3556 0 - -1840 -3723 0 - -2399 -2494 0 - -2399 -3556 0 - -2399 -3723 0 - -2494 -3556 0 - -2494 -3723 0 - -3556 -3723 0 - -55 -1506 0 - -55 -1841 0 - -55 -2495 0 - -55 -3152 0 - -55 -4358 0 - -1506 -1841 0 - -1506 -2495 0 - -1506 -3152 0 - -1506 -4358 0 - -1841 -2495 0 - -1841 -3152 0 - -1841 -4358 0 - -2495 -3152 0 - -2495 -4358 0 - -3152 -4358 0 - -302 -624 0 - -302 -1507 0 - -302 -3153 0 - -302 -3724 0 - -302 -4359 0 - -624 -1507 0 - -624 -3153 0 - -624 -3724 0 - -624 -4359 0 - -1507 -3153 0 - -1507 -3724 0 - -1507 -4359 0 - -3153 -3724 0 - -3153 -4359 0 - -3724 -4359 0 - -56 -625 0 - -56 -993 0 - -56 -1930 0 - -56 -2400 0 - -56 -3557 0 - -56 -3725 0 - -56 -4360 0 - -625 -993 0 - -625 -1930 0 - -625 -2400 0 - -625 -3557 0 - -625 -3725 0 - -625 -4360 0 - -993 -1930 0 - -993 -2400 0 - -993 -3557 0 - -993 -3725 0 - -993 -4360 0 - -1930 -2400 0 - -1930 -3557 0 - -1930 -3725 0 - -1930 -4360 0 - -2400 -3557 0 - -2400 -3725 0 - -2400 -4360 0 - -3557 -3725 0 - -3557 -4360 0 - -3725 -4360 0 - -303 -994 0 - -303 -2496 0 - -303 -3558 0 - -303 -3726 0 - -303 -4361 0 - -994 -2496 0 - -994 -3558 0 - -994 -3726 0 - -994 -4361 0 - -2496 -3558 0 - -2496 -3726 0 - -2496 -4361 0 - -3558 -3726 0 - -3558 -4361 0 - -3726 -4361 0 - -304 -1508 0 - -304 -1931 0 - -304 -2401 0 - -304 -3154 0 - -304 -3559 0 - -304 -4362 0 - -1508 -1931 0 - -1508 -2401 0 - -1508 -3154 0 - -1508 -3559 0 - -1508 -4362 0 - -1931 -2401 0 - -1931 -3154 0 - -1931 -3559 0 - -1931 -4362 0 - -2401 -3154 0 - -2401 -3559 0 - -2401 -4362 0 - -3154 -3559 0 - -3154 -4362 0 - -3559 -4362 0 - -305 -995 0 - -305 -1509 0 - -305 -1932 0 - -305 -2497 0 - -305 -3155 0 - -305 -3727 0 - -995 -1509 0 - -995 -1932 0 - -995 -2497 0 - -995 -3155 0 - -995 -3727 0 - -1509 -1932 0 - -1509 -2497 0 - -1509 -3155 0 - -1509 -3727 0 - -1932 -2497 0 - -1932 -3155 0 - -1932 -3727 0 - -2497 -3155 0 - -2497 -3727 0 - -3155 -3727 0 - -626 -875 0 - -626 -1510 0 - -626 -3156 0 - -626 -4363 0 - -875 -1510 0 - -875 -3156 0 - -875 -4363 0 - -1510 -3156 0 - -1510 -4363 0 - -3156 -4363 0 - -57 -627 0 - -57 -996 0 - -57 -1511 0 - -57 -1842 0 - -57 -1933 0 - -57 -3157 0 - -57 -4364 0 - -627 -996 0 - -627 -1511 0 - -627 -1842 0 - -627 -1933 0 - -627 -3157 0 - -627 -4364 0 - -996 -1511 0 - -996 -1842 0 - -996 -1933 0 - -996 -3157 0 - -996 -4364 0 - -1511 -1842 0 - -1511 -1933 0 - -1511 -3157 0 - -1511 -4364 0 - -1842 -1933 0 - -1842 -3157 0 - -1842 -4364 0 - -1933 -3157 0 - -1933 -4364 0 - -3157 -4364 0 - -628 -876 0 - -628 -1843 0 - -628 -2402 0 - -628 -3728 0 - -876 -1843 0 - -876 -2402 0 - -876 -3728 0 - -1843 -2402 0 - -1843 -3728 0 - -2402 -3728 0 - -1512 -2403 0 - -1512 -3158 0 - -1512 -3729 0 - -2403 -3158 0 - -2403 -3729 0 - -3158 -3729 0 - -306 -629 0 - -306 -997 0 - -306 -1934 0 - -306 -2404 0 - -306 -2498 0 - -306 -3560 0 - -629 -997 0 - -629 -1934 0 - -629 -2404 0 - -629 -2498 0 - -629 -3560 0 - -997 -1934 0 - -997 -2404 0 - -997 -2498 0 - -997 -3560 0 - -1934 -2404 0 - -1934 -2498 0 - -1934 -3560 0 - -2404 -2498 0 - -2404 -3560 0 - -2498 -3560 0 - -58 -1844 0 - -58 -1935 0 - -58 -2499 0 - -58 -3730 0 - -58 -4365 0 - -1844 -1935 0 - -1844 -2499 0 - -1844 -3730 0 - -1844 -4365 0 - -1935 -2499 0 - -1935 -3730 0 - -1935 -4365 0 - -2499 -3730 0 - -2499 -4365 0 - -3730 -4365 0 - -630 -1693 0 - -630 -1936 0 - -630 -2864 0 - -630 -3400 0 - -630 -4366 0 - -1693 -1936 0 - -1693 -2864 0 - -1693 -3400 0 - -1693 -4366 0 - -1936 -2864 0 - -1936 -3400 0 - -1936 -4366 0 - -2864 -3400 0 - -2864 -4366 0 - -3400 -4366 0 - -1937 -2118 0 - -1937 -2500 0 - -1937 -3401 0 - -1937 -3561 0 - -1937 -4367 0 - -2118 -2500 0 - -2118 -3401 0 - -2118 -3561 0 - -2118 -4367 0 - -2500 -3401 0 - -2500 -3561 0 - -2500 -4367 0 - -3401 -3561 0 - -3401 -4367 0 - -3561 -4367 0 - -631 -1694 0 - -631 -1938 0 - -631 -2119 0 - -631 -2865 0 - -631 -3562 0 - -1694 -1938 0 - -1694 -2119 0 - -1694 -2865 0 - -1694 -3562 0 - -1938 -2119 0 - -1938 -2865 0 - -1938 -3562 0 - -2119 -2865 0 - -2119 -3562 0 - -2865 -3562 0 - -205 -1939 0 - -205 -2120 0 - -205 -3402 0 - -1939 -2120 0 - -1939 -3402 0 - -2120 -3402 0 - -632 -2121 0 - -632 -2866 0 - -632 -4282 0 - -632 -4368 0 - -2121 -2866 0 - -2121 -4282 0 - -2121 -4368 0 - -2866 -4282 0 - -2866 -4368 0 - -4282 -4368 0 - -206 -2122 0 - -206 -2501 0 - -206 -3563 0 - -206 -4283 0 - -206 -4369 0 - -2122 -2501 0 - -2122 -3563 0 - -2122 -4283 0 - -2122 -4369 0 - -2501 -3563 0 - -2501 -4283 0 - -2501 -4369 0 - -3563 -4283 0 - -3563 -4369 0 - -4283 -4369 0 - -1695 -1940 0 - -1695 -2123 0 - -1695 -2867 0 - -1695 -3564 0 - -1695 -4370 0 - -1940 -2123 0 - -1940 -2867 0 - -1940 -3564 0 - -1940 -4370 0 - -2123 -2867 0 - -2123 -3564 0 - -2123 -4370 0 - -2867 -3564 0 - -2867 -4370 0 - -3564 -4370 0 - -207 -1696 0 - -207 -4284 0 - -1696 -4284 0 - -208 -633 0 - -208 -1697 0 - -208 -2502 0 - -208 -3403 0 - -633 -1697 0 - -633 -2502 0 - -633 -3403 0 - -1697 -2502 0 - -1697 -3403 0 - -2502 -3403 0 - -209 -634 0 - -209 -1698 0 - -209 -1941 0 - -209 -2868 0 - -209 -3565 0 - -634 -1698 0 - -634 -1941 0 - -634 -2868 0 - -634 -3565 0 - -1698 -1941 0 - -1698 -2868 0 - -1698 -3565 0 - -1941 -2868 0 - -1941 -3565 0 - -2868 -3565 0 - -210 -1942 0 - -210 -2124 0 - -210 -2503 0 - -210 -2869 0 - -210 -4285 0 - -210 -4371 0 - -1942 -2124 0 - -1942 -2503 0 - -1942 -2869 0 - -1942 -4285 0 - -1942 -4371 0 - -2124 -2503 0 - -2124 -2869 0 - -2124 -4285 0 - -2124 -4371 0 - -2503 -2869 0 - -2503 -4285 0 - -2503 -4371 0 - -2869 -4285 0 - -2869 -4371 0 - -4285 -4371 0 - -307 -998 0 - -307 -1699 0 - -307 -2870 0 - -307 -3404 0 - -307 -4062 0 - -998 -1699 0 - -998 -2870 0 - -998 -3404 0 - -998 -4062 0 - -1699 -2870 0 - -1699 -3404 0 - -1699 -4062 0 - -2870 -3404 0 - -2870 -4062 0 - -3404 -4062 0 - -308 -513 0 - -308 -999 0 - -308 -1700 0 - -308 -2125 0 - -308 -2237 0 - -308 -2871 0 - -308 -3405 0 - -308 -3566 0 - -308 -3731 0 - -308 -3862 0 - -308 -4177 0 - -513 -999 0 - -513 -1700 0 - -513 -2125 0 - -513 -2237 0 - -513 -2871 0 - -513 -3405 0 - -513 -3566 0 - -513 -3731 0 - -513 -3862 0 - -513 -4177 0 - -999 -1700 0 - -999 -2125 0 - -999 -2237 0 - -999 -2871 0 - -999 -3405 0 - -999 -3566 0 - -999 -3731 0 - -999 -3862 0 - -999 -4177 0 - -1700 -2125 0 - -1700 -2237 0 - -1700 -2871 0 - -1700 -3405 0 - -1700 -3566 0 - -1700 -3731 0 - -1700 -3862 0 - -1700 -4177 0 - -2125 -2237 0 - -2125 -2871 0 - -2125 -3405 0 - -2125 -3566 0 - -2125 -3731 0 - -2125 -3862 0 - -2125 -4177 0 - -2237 -2871 0 - -2237 -3405 0 - -2237 -3566 0 - -2237 -3731 0 - -2237 -3862 0 - -2237 -4177 0 - -2871 -3405 0 - -2871 -3566 0 - -2871 -3731 0 - -2871 -3862 0 - -2871 -4177 0 - -3405 -3566 0 - -3405 -3731 0 - -3405 -3862 0 - -3405 -4177 0 - -3566 -3731 0 - -3566 -3862 0 - -3566 -4177 0 - -3731 -3862 0 - -3731 -4177 0 - -3862 -4177 0 - -514 -1000 0 - -514 -1701 0 - -514 -2126 0 - -514 -2872 0 - -514 -2980 0 - -514 -3567 0 - -514 -3732 0 - -514 -3863 0 - -514 -3986 0 - -1000 -1701 0 - -1000 -2126 0 - -1000 -2872 0 - -1000 -2980 0 - -1000 -3567 0 - -1000 -3732 0 - -1000 -3863 0 - -1000 -3986 0 - -1701 -2126 0 - -1701 -2872 0 - -1701 -2980 0 - -1701 -3567 0 - -1701 -3732 0 - -1701 -3863 0 - -1701 -3986 0 - -2126 -2872 0 - -2126 -2980 0 - -2126 -3567 0 - -2126 -3732 0 - -2126 -3863 0 - -2126 -3986 0 - -2872 -2980 0 - -2872 -3567 0 - -2872 -3732 0 - -2872 -3863 0 - -2872 -3986 0 - -2980 -3567 0 - -2980 -3732 0 - -2980 -3863 0 - -2980 -3986 0 - -3567 -3732 0 - -3567 -3863 0 - -3567 -3986 0 - -3732 -3863 0 - -3732 -3986 0 - -3863 -3986 0 - -1702 -2127 0 - -1702 -2238 0 - -1702 -3274 0 - -1702 -3568 0 - -1702 -3987 0 - -2127 -2238 0 - -2127 -3274 0 - -2127 -3568 0 - -2127 -3987 0 - -2238 -3274 0 - -2238 -3568 0 - -2238 -3987 0 - -3274 -3568 0 - -3274 -3987 0 - -3568 -3987 0 - -2981 -3275 0 - -2981 -3569 0 - -2981 -4178 0 - -3275 -3569 0 - -3275 -4178 0 - -3569 -4178 0 - -515 -1703 0 - -515 -2982 0 - -515 -3406 0 - -515 -3570 0 - -515 -4179 0 - -1703 -2982 0 - -1703 -3406 0 - -1703 -3570 0 - -1703 -4179 0 - -2982 -3406 0 - -2982 -3570 0 - -2982 -4179 0 - -3406 -3570 0 - -3406 -4179 0 - -3570 -4179 0 - -3407 -3733 0 - -3407 -3864 0 - -3407 -4180 0 - -3733 -3864 0 - -3733 -4180 0 - -3864 -4180 0 - -309 -516 0 - -309 -1704 0 - -309 -2239 0 - -309 -3276 0 - -309 -3408 0 - -309 -3571 0 - -309 -3734 0 - -309 -3865 0 - -309 -3988 0 - -309 -4181 0 - -516 -1704 0 - -516 -2239 0 - -516 -3276 0 - -516 -3408 0 - -516 -3571 0 - -516 -3734 0 - -516 -3865 0 - -516 -3988 0 - -516 -4181 0 - -1704 -2239 0 - -1704 -3276 0 - -1704 -3408 0 - -1704 -3571 0 - -1704 -3734 0 - -1704 -3865 0 - -1704 -3988 0 - -1704 -4181 0 - -2239 -3276 0 - -2239 -3408 0 - -2239 -3571 0 - -2239 -3734 0 - -2239 -3865 0 - -2239 -3988 0 - -2239 -4181 0 - -3276 -3408 0 - -3276 -3571 0 - -3276 -3734 0 - -3276 -3865 0 - -3276 -3988 0 - -3276 -4181 0 - -3408 -3571 0 - -3408 -3734 0 - -3408 -3865 0 - -3408 -3988 0 - -3408 -4181 0 - -3571 -3734 0 - -3571 -3865 0 - -3571 -3988 0 - -3571 -4181 0 - -3734 -3865 0 - -3734 -3988 0 - -3734 -4181 0 - -3865 -3988 0 - -3865 -4181 0 - -3988 -4181 0 - -2128 -2873 0 - -2128 -3277 0 - -2128 -3866 0 - -2128 -3989 0 - -2128 -4182 0 - -2873 -3277 0 - -2873 -3866 0 - -2873 -3989 0 - -2873 -4182 0 - -3277 -3866 0 - -3277 -3989 0 - -3277 -4182 0 - -3866 -3989 0 - -3866 -4182 0 - -3989 -4182 0 - -1001 -3278 0 - -1001 -3572 0 - -3278 -3572 0 - -310 -1705 0 - -310 -2129 0 - -310 -2874 0 - -310 -2983 0 - -310 -3573 0 - -310 -3867 0 - -310 -3990 0 - -1705 -2129 0 - -1705 -2874 0 - -1705 -2983 0 - -1705 -3573 0 - -1705 -3867 0 - -1705 -3990 0 - -2129 -2874 0 - -2129 -2983 0 - -2129 -3573 0 - -2129 -3867 0 - -2129 -3990 0 - -2874 -2983 0 - -2874 -3573 0 - -2874 -3867 0 - -2874 -3990 0 - -2983 -3573 0 - -2983 -3867 0 - -2983 -3990 0 - -3573 -3867 0 - -3573 -3990 0 - -3867 -3990 0 - -517 -1002 0 - -517 -2875 0 - -517 -2984 0 - -517 -3409 0 - -517 -3868 0 - -517 -4183 0 - -1002 -2875 0 - -1002 -2984 0 - -1002 -3409 0 - -1002 -3868 0 - -1002 -4183 0 - -2875 -2984 0 - -2875 -3409 0 - -2875 -3868 0 - -2875 -4183 0 - -2984 -3409 0 - -2984 -3868 0 - -2984 -4183 0 - -3409 -3868 0 - -3409 -4183 0 - -3868 -4183 0 - -2876 -3410 0 - -2876 -3735 0 - -2876 -3991 0 - -2876 -4063 0 - -3410 -3735 0 - -3410 -3991 0 - -3410 -4063 0 - -3735 -3991 0 - -3735 -4063 0 - -3991 -4063 0 - -311 -518 0 - -311 -1706 0 - -311 -2240 0 - -311 -2877 0 - -311 -2985 0 - -311 -3279 0 - -311 -3574 0 - -311 -3736 0 - -311 -3869 0 - -311 -3992 0 - -518 -1706 0 - -518 -2240 0 - -518 -2877 0 - -518 -2985 0 - -518 -3279 0 - -518 -3574 0 - -518 -3736 0 - -518 -3869 0 - -518 -3992 0 - -1706 -2240 0 - -1706 -2877 0 - -1706 -2985 0 - -1706 -3279 0 - -1706 -3574 0 - -1706 -3736 0 - -1706 -3869 0 - -1706 -3992 0 - -2240 -2877 0 - -2240 -2985 0 - -2240 -3279 0 - -2240 -3574 0 - -2240 -3736 0 - -2240 -3869 0 - -2240 -3992 0 - -2877 -2985 0 - -2877 -3279 0 - -2877 -3574 0 - -2877 -3736 0 - -2877 -3869 0 - -2877 -3992 0 - -2985 -3279 0 - -2985 -3574 0 - -2985 -3736 0 - -2985 -3869 0 - -2985 -3992 0 - -3279 -3574 0 - -3279 -3736 0 - -3279 -3869 0 - -3279 -3992 0 - -3574 -3736 0 - -3574 -3869 0 - -3574 -3992 0 - -3736 -3869 0 - -3736 -3992 0 - -3869 -3992 0 - -2130 -2241 0 - -2130 -2878 0 - -2130 -3737 0 - -2130 -3870 0 - -2130 -3993 0 - -2130 -4064 0 - -2130 -4184 0 - -2241 -2878 0 - -2241 -3737 0 - -2241 -3870 0 - -2241 -3993 0 - -2241 -4064 0 - -2241 -4184 0 - -2878 -3737 0 - -2878 -3870 0 - -2878 -3993 0 - -2878 -4064 0 - -2878 -4184 0 - -3737 -3870 0 - -3737 -3993 0 - -3737 -4064 0 - -3737 -4184 0 - -3870 -3993 0 - -3870 -4064 0 - -3870 -4184 0 - -3993 -4064 0 - -3993 -4184 0 - -4064 -4184 0 - -312 -1003 0 - -312 -1707 0 - -312 -2131 0 - -312 -2879 0 - -312 -3411 0 - -312 -4185 0 - -1003 -1707 0 - -1003 -2131 0 - -1003 -2879 0 - -1003 -3411 0 - -1003 -4185 0 - -1707 -2131 0 - -1707 -2879 0 - -1707 -3411 0 - -1707 -4185 0 - -2131 -2879 0 - -2131 -3411 0 - -2131 -4185 0 - -2879 -3411 0 - -2879 -4185 0 - -3411 -4185 0 - -59 -877 0 - -59 -1157 0 - -59 -1262 0 - -59 -2405 0 - -59 -2880 0 - -59 -3738 0 - -59 -3994 0 - -877 -1157 0 - -877 -1262 0 - -877 -2405 0 - -877 -2880 0 - -877 -3738 0 - -877 -3994 0 - -1157 -1262 0 - -1157 -2405 0 - -1157 -2880 0 - -1157 -3738 0 - -1157 -3994 0 - -1262 -2405 0 - -1262 -2880 0 - -1262 -3738 0 - -1262 -3994 0 - -2405 -2880 0 - -2405 -3738 0 - -2405 -3994 0 - -2880 -3738 0 - -2880 -3994 0 - -3738 -3994 0 - -635 -878 0 - -635 -1004 0 - -635 -2504 0 - -635 -4286 0 - -878 -1004 0 - -878 -2504 0 - -878 -4286 0 - -1004 -2504 0 - -1004 -4286 0 - -2504 -4286 0 - -1005 -1943 0 - -1005 -2505 0 - -1005 -2572 0 - -1005 -2986 0 - -1005 -3871 0 - -1005 -3995 0 - -1943 -2505 0 - -1943 -2572 0 - -1943 -2986 0 - -1943 -3871 0 - -1943 -3995 0 - -2505 -2572 0 - -2505 -2986 0 - -2505 -3871 0 - -2505 -3995 0 - -2572 -2986 0 - -2572 -3871 0 - -2572 -3995 0 - -2986 -3871 0 - -2986 -3995 0 - -3871 -3995 0 - -60 -519 0 - -60 -636 0 - -60 -1006 0 - -60 -1158 0 - -60 -1944 0 - -60 -2573 0 - -60 -2881 0 - -60 -2987 0 - -60 -3739 0 - -60 -3872 0 - -60 -3996 0 - -519 -636 0 - -519 -1006 0 - -519 -1158 0 - -519 -1944 0 - -519 -2573 0 - -519 -2881 0 - -519 -2987 0 - -519 -3739 0 - -519 -3872 0 - -519 -3996 0 - -636 -1006 0 - -636 -1158 0 - -636 -1944 0 - -636 -2573 0 - -636 -2881 0 - -636 -2987 0 - -636 -3739 0 - -636 -3872 0 - -636 -3996 0 - -1006 -1158 0 - -1006 -1944 0 - -1006 -2573 0 - -1006 -2881 0 - -1006 -2987 0 - -1006 -3739 0 - -1006 -3872 0 - -1006 -3996 0 - -1158 -1944 0 - -1158 -2573 0 - -1158 -2881 0 - -1158 -2987 0 - -1158 -3739 0 - -1158 -3872 0 - -1158 -3996 0 - -1944 -2573 0 - -1944 -2881 0 - -1944 -2987 0 - -1944 -3739 0 - -1944 -3872 0 - -1944 -3996 0 - -2573 -2881 0 - -2573 -2987 0 - -2573 -3739 0 - -2573 -3872 0 - -2573 -3996 0 - -2881 -2987 0 - -2881 -3739 0 - -2881 -3872 0 - -2881 -3996 0 - -2987 -3739 0 - -2987 -3872 0 - -2987 -3996 0 - -3739 -3872 0 - -3739 -3996 0 - -3872 -3996 0 - -313 -409 0 - -313 -637 0 - -313 -2506 0 - -313 -2882 0 - -313 -3740 0 - -313 -3873 0 - -313 -3997 0 - -409 -637 0 - -409 -2506 0 - -409 -2882 0 - -409 -3740 0 - -409 -3873 0 - -409 -3997 0 - -637 -2506 0 - -637 -2882 0 - -637 -3740 0 - -637 -3873 0 - -637 -3997 0 - -2506 -2882 0 - -2506 -3740 0 - -2506 -3873 0 - -2506 -3997 0 - -2882 -3740 0 - -2882 -3873 0 - -2882 -3997 0 - -3740 -3873 0 - -3740 -3997 0 - -3873 -3997 0 - -314 -410 0 - -314 -520 0 - -314 -1007 0 - -314 -1159 0 - -314 -1945 0 - -314 -2574 0 - -314 -3741 0 - -410 -520 0 - -410 -1007 0 - -410 -1159 0 - -410 -1945 0 - -410 -2574 0 - -410 -3741 0 - -520 -1007 0 - -520 -1159 0 - -520 -1945 0 - -520 -2574 0 - -520 -3741 0 - -1007 -1159 0 - -1007 -1945 0 - -1007 -2574 0 - -1007 -3741 0 - -1159 -1945 0 - -1159 -2574 0 - -1159 -3741 0 - -1945 -2574 0 - -1945 -3741 0 - -2574 -3741 0 - -315 -411 0 - -315 -521 0 - -315 -879 0 - -315 -1008 0 - -315 -2507 0 - -315 -2575 0 - -315 -3742 0 - -411 -521 0 - -411 -879 0 - -411 -1008 0 - -411 -2507 0 - -411 -2575 0 - -411 -3742 0 - -521 -879 0 - -521 -1008 0 - -521 -2507 0 - -521 -2575 0 - -521 -3742 0 - -879 -1008 0 - -879 -2507 0 - -879 -2575 0 - -879 -3742 0 - -1008 -2507 0 - -1008 -2575 0 - -1008 -3742 0 - -2507 -2575 0 - -2507 -3742 0 - -2575 -3742 0 - -61 -316 0 - -61 -522 0 - -61 -880 0 - -61 -1160 0 - -61 -1263 0 - -61 -2576 0 - -61 -3743 0 - -61 -3874 0 - -61 -3998 0 - -316 -522 0 - -316 -880 0 - -316 -1160 0 - -316 -1263 0 - -316 -2576 0 - -316 -3743 0 - -316 -3874 0 - -316 -3998 0 - -522 -880 0 - -522 -1160 0 - -522 -1263 0 - -522 -2576 0 - -522 -3743 0 - -522 -3874 0 - -522 -3998 0 - -880 -1160 0 - -880 -1263 0 - -880 -2576 0 - -880 -3743 0 - -880 -3874 0 - -880 -3998 0 - -1160 -1263 0 - -1160 -2576 0 - -1160 -3743 0 - -1160 -3874 0 - -1160 -3998 0 - -1263 -2576 0 - -1263 -3743 0 - -1263 -3874 0 - -1263 -3998 0 - -2576 -3743 0 - -2576 -3874 0 - -2576 -3998 0 - -3743 -3874 0 - -3743 -3998 0 - -3874 -3998 0 - -62 -523 0 - -62 -638 0 - -62 -1161 0 - -62 -1264 0 - -62 -2406 0 - -62 -2883 0 - -62 -3744 0 - -62 -4287 0 - -523 -638 0 - -523 -1161 0 - -523 -1264 0 - -523 -2406 0 - -523 -2883 0 - -523 -3744 0 - -523 -4287 0 - -638 -1161 0 - -638 -1264 0 - -638 -2406 0 - -638 -2883 0 - -638 -3744 0 - -638 -4287 0 - -1161 -1264 0 - -1161 -2406 0 - -1161 -2883 0 - -1161 -3744 0 - -1161 -4287 0 - -1264 -2406 0 - -1264 -2883 0 - -1264 -3744 0 - -1264 -4287 0 - -2406 -2883 0 - -2406 -3744 0 - -2406 -4287 0 - -2883 -3744 0 - -2883 -4287 0 - -3744 -4287 0 - -412 -639 0 - -412 -1009 0 - -412 -1162 0 - -412 -1946 0 - -412 -2407 0 - -412 -2577 0 - -639 -1009 0 - -639 -1162 0 - -639 -1946 0 - -639 -2407 0 - -639 -2577 0 - -1009 -1162 0 - -1009 -1946 0 - -1009 -2407 0 - -1009 -2577 0 - -1162 -1946 0 - -1162 -2407 0 - -1162 -2577 0 - -1946 -2407 0 - -1946 -2577 0 - -2407 -2577 0 - -317 -524 0 - -317 -640 0 - -317 -2578 0 - -317 -2988 0 - -317 -3745 0 - -317 -3875 0 - -524 -640 0 - -524 -2578 0 - -524 -2988 0 - -524 -3745 0 - -524 -3875 0 - -640 -2578 0 - -640 -2988 0 - -640 -3745 0 - -640 -3875 0 - -2578 -2988 0 - -2578 -3745 0 - -2578 -3875 0 - -2988 -3745 0 - -2988 -3875 0 - -3745 -3875 0 - -318 -525 0 - -318 -1010 0 - -318 -1163 0 - -318 -1265 0 - -318 -2508 0 - -318 -2579 0 - -318 -2989 0 - -318 -3746 0 - -318 -4288 0 - -525 -1010 0 - -525 -1163 0 - -525 -1265 0 - -525 -2508 0 - -525 -2579 0 - -525 -2989 0 - -525 -3746 0 - -525 -4288 0 - -1010 -1163 0 - -1010 -1265 0 - -1010 -2508 0 - -1010 -2579 0 - -1010 -2989 0 - -1010 -3746 0 - -1010 -4288 0 - -1163 -1265 0 - -1163 -2508 0 - -1163 -2579 0 - -1163 -2989 0 - -1163 -3746 0 - -1163 -4288 0 - -1265 -2508 0 - -1265 -2579 0 - -1265 -2989 0 - -1265 -3746 0 - -1265 -4288 0 - -2508 -2579 0 - -2508 -2989 0 - -2508 -3746 0 - -2508 -4288 0 - -2579 -2989 0 - -2579 -3746 0 - -2579 -4288 0 - -2989 -3746 0 - -2989 -4288 0 - -3746 -4288 0 - -526 -641 0 - -526 -881 0 - -526 -1266 0 - -526 -4289 0 - -641 -881 0 - -641 -1266 0 - -641 -4289 0 - -881 -1266 0 - -881 -4289 0 - -1266 -4289 0 - -63 -527 0 - -63 -642 0 - -63 -1011 0 - -63 -1947 0 - -63 -2884 0 - -63 -2990 0 - -63 -3876 0 - -63 -4290 0 - -527 -642 0 - -527 -1011 0 - -527 -1947 0 - -527 -2884 0 - -527 -2990 0 - -527 -3876 0 - -527 -4290 0 - -642 -1011 0 - -642 -1947 0 - -642 -2884 0 - -642 -2990 0 - -642 -3876 0 - -642 -4290 0 - -1011 -1947 0 - -1011 -2884 0 - -1011 -2990 0 - -1011 -3876 0 - -1011 -4290 0 - -1947 -2884 0 - -1947 -2990 0 - -1947 -3876 0 - -1947 -4290 0 - -2884 -2990 0 - -2884 -3876 0 - -2884 -4290 0 - -2990 -3876 0 - -2990 -4290 0 - -3876 -4290 0 - -528 -1164 0 - -528 -2408 0 - -528 -3747 0 - -528 -3999 0 - -528 -4291 0 - -1164 -2408 0 - -1164 -3747 0 - -1164 -3999 0 - -1164 -4291 0 - -2408 -3747 0 - -2408 -3999 0 - -2408 -4291 0 - -3747 -3999 0 - -3747 -4291 0 - -3999 -4291 0 - -319 -413 0 - -319 -643 0 - -319 -1012 0 - -319 -1267 0 - -319 -1948 0 - -319 -2409 0 - -319 -2509 0 - -319 -2885 0 - -319 -4000 0 - -413 -643 0 - -413 -1012 0 - -413 -1267 0 - -413 -1948 0 - -413 -2409 0 - -413 -2509 0 - -413 -2885 0 - -413 -4000 0 - -643 -1012 0 - -643 -1267 0 - -643 -1948 0 - -643 -2409 0 - -643 -2509 0 - -643 -2885 0 - -643 -4000 0 - -1012 -1267 0 - -1012 -1948 0 - -1012 -2409 0 - -1012 -2509 0 - -1012 -2885 0 - -1012 -4000 0 - -1267 -1948 0 - -1267 -2409 0 - -1267 -2509 0 - -1267 -2885 0 - -1267 -4000 0 - -1948 -2409 0 - -1948 -2509 0 - -1948 -2885 0 - -1948 -4000 0 - -2409 -2509 0 - -2409 -2885 0 - -2409 -4000 0 - -2509 -2885 0 - -2509 -4000 0 - -2885 -4000 0 - -320 -414 0 - -320 -529 0 - -320 -644 0 - -320 -1165 0 - -320 -1949 0 - -320 -2410 0 - -320 -2886 0 - -320 -2991 0 - -320 -3748 0 - -320 -3877 0 - -320 -4001 0 - -414 -529 0 - -414 -644 0 - -414 -1165 0 - -414 -1949 0 - -414 -2410 0 - -414 -2886 0 - -414 -2991 0 - -414 -3748 0 - -414 -3877 0 - -414 -4001 0 - -529 -644 0 - -529 -1165 0 - -529 -1949 0 - -529 -2410 0 - -529 -2886 0 - -529 -2991 0 - -529 -3748 0 - -529 -3877 0 - -529 -4001 0 - -644 -1165 0 - -644 -1949 0 - -644 -2410 0 - -644 -2886 0 - -644 -2991 0 - -644 -3748 0 - -644 -3877 0 - -644 -4001 0 - -1165 -1949 0 - -1165 -2410 0 - -1165 -2886 0 - -1165 -2991 0 - -1165 -3748 0 - -1165 -3877 0 - -1165 -4001 0 - -1949 -2410 0 - -1949 -2886 0 - -1949 -2991 0 - -1949 -3748 0 - -1949 -3877 0 - -1949 -4001 0 - -2410 -2886 0 - -2410 -2991 0 - -2410 -3748 0 - -2410 -3877 0 - -2410 -4001 0 - -2886 -2991 0 - -2886 -3748 0 - -2886 -3877 0 - -2886 -4001 0 - -2991 -3748 0 - -2991 -3877 0 - -2991 -4001 0 - -3748 -3877 0 - -3748 -4001 0 - -3877 -4001 0 - -321 -415 0 - -321 -1013 0 - -321 -1950 0 - -321 -2580 0 - -321 -2887 0 - -321 -4292 0 - -415 -1013 0 - -415 -1950 0 - -415 -2580 0 - -415 -2887 0 - -415 -4292 0 - -1013 -1950 0 - -1013 -2580 0 - -1013 -2887 0 - -1013 -4292 0 - -1950 -2580 0 - -1950 -2887 0 - -1950 -4292 0 - -2580 -2887 0 - -2580 -4292 0 - -2887 -4292 0 - -64 -322 0 - -64 -882 0 - -64 -1014 0 - -64 -1268 0 - -64 -1951 0 - -64 -4293 0 - -322 -882 0 - -322 -1014 0 - -322 -1268 0 - -322 -1951 0 - -322 -4293 0 - -882 -1014 0 - -882 -1268 0 - -882 -1951 0 - -882 -4293 0 - -1014 -1268 0 - -1014 -1951 0 - -1014 -4293 0 - -1268 -1951 0 - -1268 -4293 0 - -1951 -4293 0 - -784 -1015 0 - -784 -1513 0 - -784 -1708 0 - -784 -1845 0 - -784 -2756 0 - -784 -3412 0 - -1015 -1513 0 - -1015 -1708 0 - -1015 -1845 0 - -1015 -2756 0 - -1015 -3412 0 - -1513 -1708 0 - -1513 -1845 0 - -1513 -2756 0 - -1513 -3412 0 - -1708 -1845 0 - -1708 -2756 0 - -1708 -3412 0 - -1845 -2756 0 - -1845 -3412 0 - -2756 -3412 0 - -785 -883 0 - -785 -1016 0 - -785 -1344 0 - -785 -1709 0 - -785 -2665 0 - -785 -2757 0 - -785 -3159 0 - -883 -1016 0 - -883 -1344 0 - -883 -1709 0 - -883 -2665 0 - -883 -2757 0 - -883 -3159 0 - -1016 -1344 0 - -1016 -1709 0 - -1016 -2665 0 - -1016 -2757 0 - -1016 -3159 0 - -1344 -1709 0 - -1344 -2665 0 - -1344 -2757 0 - -1344 -3159 0 - -1709 -2665 0 - -1709 -2757 0 - -1709 -3159 0 - -2665 -2757 0 - -2665 -3159 0 - -2757 -3159 0 - -211 -530 0 - -211 -884 0 - -211 -1017 0 - -211 -1166 0 - -211 -1514 0 - -211 -1710 0 - -211 -1846 0 - -211 -2242 0 - -211 -3413 0 - -530 -884 0 - -530 -1017 0 - -530 -1166 0 - -530 -1514 0 - -530 -1710 0 - -530 -1846 0 - -530 -2242 0 - -530 -3413 0 - -884 -1017 0 - -884 -1166 0 - -884 -1514 0 - -884 -1710 0 - -884 -1846 0 - -884 -2242 0 - -884 -3413 0 - -1017 -1166 0 - -1017 -1514 0 - -1017 -1710 0 - -1017 -1846 0 - -1017 -2242 0 - -1017 -3413 0 - -1166 -1514 0 - -1166 -1710 0 - -1166 -1846 0 - -1166 -2242 0 - -1166 -3413 0 - -1514 -1710 0 - -1514 -1846 0 - -1514 -2242 0 - -1514 -3413 0 - -1710 -1846 0 - -1710 -2242 0 - -1710 -3413 0 - -1846 -2242 0 - -1846 -3413 0 - -2242 -3413 0 - -531 -1018 0 - -531 -1167 0 - -531 -1515 0 - -531 -1711 0 - -531 -1847 0 - -531 -2758 0 - -531 -2992 0 - -531 -3160 0 - -531 -4002 0 - -1018 -1167 0 - -1018 -1515 0 - -1018 -1711 0 - -1018 -1847 0 - -1018 -2758 0 - -1018 -2992 0 - -1018 -3160 0 - -1018 -4002 0 - -1167 -1515 0 - -1167 -1711 0 - -1167 -1847 0 - -1167 -2758 0 - -1167 -2992 0 - -1167 -3160 0 - -1167 -4002 0 - -1515 -1711 0 - -1515 -1847 0 - -1515 -2758 0 - -1515 -2992 0 - -1515 -3160 0 - -1515 -4002 0 - -1711 -1847 0 - -1711 -2758 0 - -1711 -2992 0 - -1711 -3160 0 - -1711 -4002 0 - -1847 -2758 0 - -1847 -2992 0 - -1847 -3160 0 - -1847 -4002 0 - -2758 -2992 0 - -2758 -3160 0 - -2758 -4002 0 - -2992 -3160 0 - -2992 -4002 0 - -3160 -4002 0 - -212 -532 0 - -212 -885 0 - -212 -1019 0 - -212 -1168 0 - -212 -1712 0 - -212 -1848 0 - -212 -2243 0 - -212 -2993 0 - -212 -3161 0 - -212 -4003 0 - -532 -885 0 - -532 -1019 0 - -532 -1168 0 - -532 -1712 0 - -532 -1848 0 - -532 -2243 0 - -532 -2993 0 - -532 -3161 0 - -532 -4003 0 - -885 -1019 0 - -885 -1168 0 - -885 -1712 0 - -885 -1848 0 - -885 -2243 0 - -885 -2993 0 - -885 -3161 0 - -885 -4003 0 - -1019 -1168 0 - -1019 -1712 0 - -1019 -1848 0 - -1019 -2243 0 - -1019 -2993 0 - -1019 -3161 0 - -1019 -4003 0 - -1168 -1712 0 - -1168 -1848 0 - -1168 -2243 0 - -1168 -2993 0 - -1168 -3161 0 - -1168 -4003 0 - -1712 -1848 0 - -1712 -2243 0 - -1712 -2993 0 - -1712 -3161 0 - -1712 -4003 0 - -1848 -2243 0 - -1848 -2993 0 - -1848 -3161 0 - -1848 -4003 0 - -2243 -2993 0 - -2243 -3161 0 - -2243 -4003 0 - -2993 -3161 0 - -2993 -4003 0 - -3161 -4003 0 - -416 -533 0 - -416 -886 0 - -416 -1516 0 - -416 -1713 0 - -416 -2244 0 - -416 -2994 0 - -416 -3414 0 - -533 -886 0 - -533 -1516 0 - -533 -1713 0 - -533 -2244 0 - -533 -2994 0 - -533 -3414 0 - -886 -1516 0 - -886 -1713 0 - -886 -2244 0 - -886 -2994 0 - -886 -3414 0 - -1516 -1713 0 - -1516 -2244 0 - -1516 -2994 0 - -1516 -3414 0 - -1713 -2244 0 - -1713 -2994 0 - -1713 -3414 0 - -2244 -2994 0 - -2244 -3414 0 - -2994 -3414 0 - -534 -786 0 - -534 -1345 0 - -534 -1517 0 - -534 -1714 0 - -534 -2995 0 - -534 -3415 0 - -786 -1345 0 - -786 -1517 0 - -786 -1714 0 - -786 -2995 0 - -786 -3415 0 - -1345 -1517 0 - -1345 -1714 0 - -1345 -2995 0 - -1345 -3415 0 - -1517 -1714 0 - -1517 -2995 0 - -1517 -3415 0 - -1714 -2995 0 - -1714 -3415 0 - -2995 -3415 0 - -213 -417 0 - -213 -535 0 - -213 -787 0 - -213 -1020 0 - -213 -1169 0 - -213 -2245 0 - -213 -3416 0 - -417 -535 0 - -417 -787 0 - -417 -1020 0 - -417 -1169 0 - -417 -2245 0 - -417 -3416 0 - -535 -787 0 - -535 -1020 0 - -535 -1169 0 - -535 -2245 0 - -535 -3416 0 - -787 -1020 0 - -787 -1169 0 - -787 -2245 0 - -787 -3416 0 - -1020 -1169 0 - -1020 -2245 0 - -1020 -3416 0 - -1169 -2245 0 - -1169 -3416 0 - -2245 -3416 0 - -418 -536 0 - -418 -788 0 - -418 -887 0 - -418 -1021 0 - -418 -1715 0 - -418 -2666 0 - -418 -3162 0 - -418 -3417 0 - -536 -788 0 - -536 -887 0 - -536 -1021 0 - -536 -1715 0 - -536 -2666 0 - -536 -3162 0 - -536 -3417 0 - -788 -887 0 - -788 -1021 0 - -788 -1715 0 - -788 -2666 0 - -788 -3162 0 - -788 -3417 0 - -887 -1021 0 - -887 -1715 0 - -887 -2666 0 - -887 -3162 0 - -887 -3417 0 - -1021 -1715 0 - -1021 -2666 0 - -1021 -3162 0 - -1021 -3417 0 - -1715 -2666 0 - -1715 -3162 0 - -1715 -3417 0 - -2666 -3162 0 - -2666 -3417 0 - -3162 -3417 0 - -214 -537 0 - -214 -888 0 - -214 -1170 0 - -214 -1518 0 - -214 -1716 0 - -214 -2246 0 - -214 -3418 0 - -214 -4004 0 - -537 -888 0 - -537 -1170 0 - -537 -1518 0 - -537 -1716 0 - -537 -2246 0 - -537 -3418 0 - -537 -4004 0 - -888 -1170 0 - -888 -1518 0 - -888 -1716 0 - -888 -2246 0 - -888 -3418 0 - -888 -4004 0 - -1170 -1518 0 - -1170 -1716 0 - -1170 -2246 0 - -1170 -3418 0 - -1170 -4004 0 - -1518 -1716 0 - -1518 -2246 0 - -1518 -3418 0 - -1518 -4004 0 - -1716 -2246 0 - -1716 -3418 0 - -1716 -4004 0 - -2246 -3418 0 - -2246 -4004 0 - -3418 -4004 0 - -215 -419 0 - -215 -789 0 - -215 -1171 0 - -215 -1519 0 - -215 -1849 0 - -215 -2667 0 - -215 -3163 0 - -215 -4005 0 - -419 -789 0 - -419 -1171 0 - -419 -1519 0 - -419 -1849 0 - -419 -2667 0 - -419 -3163 0 - -419 -4005 0 - -789 -1171 0 - -789 -1519 0 - -789 -1849 0 - -789 -2667 0 - -789 -3163 0 - -789 -4005 0 - -1171 -1519 0 - -1171 -1849 0 - -1171 -2667 0 - -1171 -3163 0 - -1171 -4005 0 - -1519 -1849 0 - -1519 -2667 0 - -1519 -3163 0 - -1519 -4005 0 - -1849 -2667 0 - -1849 -3163 0 - -1849 -4005 0 - -2667 -3163 0 - -2667 -4005 0 - -3163 -4005 0 - -216 -420 0 - -216 -790 0 - -216 -1022 0 - -216 -1172 0 - -216 -1346 0 - -216 -2759 0 - -216 -3164 0 - -420 -790 0 - -420 -1022 0 - -420 -1172 0 - -420 -1346 0 - -420 -2759 0 - -420 -3164 0 - -790 -1022 0 - -790 -1172 0 - -790 -1346 0 - -790 -2759 0 - -790 -3164 0 - -1022 -1172 0 - -1022 -1346 0 - -1022 -2759 0 - -1022 -3164 0 - -1172 -1346 0 - -1172 -2759 0 - -1172 -3164 0 - -1346 -2759 0 - -1346 -3164 0 - -2759 -3164 0 - -538 -791 0 - -538 -1023 0 - -538 -1717 0 - -538 -2668 0 - -538 -2760 0 - -791 -1023 0 - -791 -1717 0 - -791 -2668 0 - -791 -2760 0 - -1023 -1717 0 - -1023 -2668 0 - -1023 -2760 0 - -1717 -2668 0 - -1717 -2760 0 - -2668 -2760 0 - -1173 -1347 0 - -1173 -1520 0 - -1173 -1718 0 - -1173 -2669 0 - -1173 -2996 0 - -1173 -3165 0 - -1173 -4006 0 - -1347 -1520 0 - -1347 -1718 0 - -1347 -2669 0 - -1347 -2996 0 - -1347 -3165 0 - -1347 -4006 0 - -1520 -1718 0 - -1520 -2669 0 - -1520 -2996 0 - -1520 -3165 0 - -1520 -4006 0 - -1718 -2669 0 - -1718 -2996 0 - -1718 -3165 0 - -1718 -4006 0 - -2669 -2996 0 - -2669 -3165 0 - -2669 -4006 0 - -2996 -3165 0 - -2996 -4006 0 - -3165 -4006 0 - -539 -889 0 - -539 -1521 0 - -539 -2247 0 - -539 -3166 0 - -539 -3419 0 - -889 -1521 0 - -889 -2247 0 - -889 -3166 0 - -889 -3419 0 - -1521 -2247 0 - -1521 -3166 0 - -1521 -3419 0 - -2247 -3166 0 - -2247 -3419 0 - -3166 -3419 0 - -217 -1174 0 - -217 -1348 0 - -217 -1719 0 - -217 -2248 0 - -217 -2761 0 - -217 -3167 0 - -217 -3420 0 - -1174 -1348 0 - -1174 -1719 0 - -1174 -2248 0 - -1174 -2761 0 - -1174 -3167 0 - -1174 -3420 0 - -1348 -1719 0 - -1348 -2248 0 - -1348 -2761 0 - -1348 -3167 0 - -1348 -3420 0 - -1719 -2248 0 - -1719 -2761 0 - -1719 -3167 0 - -1719 -3420 0 - -2248 -2761 0 - -2248 -3167 0 - -2248 -3420 0 - -2761 -3167 0 - -2761 -3420 0 - -3167 -3420 0 - -218 -421 0 - -218 -540 0 - -218 -792 0 - -218 -1175 0 - -218 -1720 0 - -218 -1850 0 - -218 -2249 0 - -218 -2997 0 - -218 -3168 0 - -218 -4007 0 - -421 -540 0 - -421 -792 0 - -421 -1175 0 - -421 -1720 0 - -421 -1850 0 - -421 -2249 0 - -421 -2997 0 - -421 -3168 0 - -421 -4007 0 - -540 -792 0 - -540 -1175 0 - -540 -1720 0 - -540 -1850 0 - -540 -2249 0 - -540 -2997 0 - -540 -3168 0 - -540 -4007 0 - -792 -1175 0 - -792 -1720 0 - -792 -1850 0 - -792 -2249 0 - -792 -2997 0 - -792 -3168 0 - -792 -4007 0 - -1175 -1720 0 - -1175 -1850 0 - -1175 -2249 0 - -1175 -2997 0 - -1175 -3168 0 - -1175 -4007 0 - -1720 -1850 0 - -1720 -2249 0 - -1720 -2997 0 - -1720 -3168 0 - -1720 -4007 0 - -1850 -2249 0 - -1850 -2997 0 - -1850 -3168 0 - -1850 -4007 0 - -2249 -2997 0 - -2249 -3168 0 - -2249 -4007 0 - -2997 -3168 0 - -2997 -4007 0 - -3168 -4007 0 - -422 -1024 0 - -422 -1522 0 - -422 -1721 0 - -422 -1851 0 - -422 -2670 0 - -422 -2762 0 - -422 -3421 0 - -1024 -1522 0 - -1024 -1721 0 - -1024 -1851 0 - -1024 -2670 0 - -1024 -2762 0 - -1024 -3421 0 - -1522 -1721 0 - -1522 -1851 0 - -1522 -2670 0 - -1522 -2762 0 - -1522 -3421 0 - -1721 -1851 0 - -1721 -2670 0 - -1721 -2762 0 - -1721 -3421 0 - -1851 -2670 0 - -1851 -2762 0 - -1851 -3421 0 - -2670 -2762 0 - -2670 -3421 0 - -2762 -3421 0 - -645 -793 0 - -645 -2510 0 - -645 -2763 0 - -645 -3169 0 - -645 -4186 0 - -645 -4372 0 - -793 -2510 0 - -793 -2763 0 - -793 -3169 0 - -793 -4186 0 - -793 -4372 0 - -2510 -2763 0 - -2510 -3169 0 - -2510 -4186 0 - -2510 -4372 0 - -2763 -3169 0 - -2763 -4186 0 - -2763 -4372 0 - -3169 -4186 0 - -3169 -4372 0 - -4186 -4372 0 - -794 -2132 0 - -794 -2511 0 - -794 -2581 0 - -794 -2764 0 - -794 -4373 0 - -2132 -2511 0 - -2132 -2581 0 - -2132 -2764 0 - -2132 -4373 0 - -2511 -2581 0 - -2511 -2764 0 - -2511 -4373 0 - -2581 -2764 0 - -2581 -4373 0 - -2764 -4373 0 - -219 -423 0 - -219 -2512 0 - -219 -3170 0 - -219 -4187 0 - -219 -4374 0 - -423 -2512 0 - -423 -3170 0 - -423 -4187 0 - -423 -4374 0 - -2512 -3170 0 - -2512 -4187 0 - -2512 -4374 0 - -3170 -4187 0 - -3170 -4374 0 - -4187 -4374 0 - -220 -541 0 - -220 -2582 0 - -220 -3171 0 - -220 -3749 0 - -541 -2582 0 - -541 -3171 0 - -541 -3749 0 - -2582 -3171 0 - -2582 -3749 0 - -3171 -3749 0 - -542 -795 0 - -542 -2411 0 - -542 -2513 0 - -542 -4188 0 - -795 -2411 0 - -795 -2513 0 - -795 -4188 0 - -2411 -2513 0 - -2411 -4188 0 - -2513 -4188 0 - -221 -424 0 - -221 -543 0 - -221 -796 0 - -221 -2133 0 - -221 -2583 0 - -221 -3750 0 - -221 -4189 0 - -424 -543 0 - -424 -796 0 - -424 -2133 0 - -424 -2583 0 - -424 -3750 0 - -424 -4189 0 - -543 -796 0 - -543 -2133 0 - -543 -2583 0 - -543 -3750 0 - -543 -4189 0 - -796 -2133 0 - -796 -2583 0 - -796 -3750 0 - -796 -4189 0 - -2133 -2583 0 - -2133 -3750 0 - -2133 -4189 0 - -2583 -3750 0 - -2583 -4189 0 - -3750 -4189 0 - -425 -544 0 - -425 -797 0 - -425 -2134 0 - -425 -2514 0 - -425 -2584 0 - -425 -3172 0 - -425 -3751 0 - -425 -4375 0 - -544 -797 0 - -544 -2134 0 - -544 -2514 0 - -544 -2584 0 - -544 -3172 0 - -544 -3751 0 - -544 -4375 0 - -797 -2134 0 - -797 -2514 0 - -797 -2584 0 - -797 -3172 0 - -797 -3751 0 - -797 -4375 0 - -2134 -2514 0 - -2134 -2584 0 - -2134 -3172 0 - -2134 -3751 0 - -2134 -4375 0 - -2514 -2584 0 - -2514 -3172 0 - -2514 -3751 0 - -2514 -4375 0 - -2584 -3172 0 - -2584 -3751 0 - -2584 -4375 0 - -3172 -3751 0 - -3172 -4375 0 - -3751 -4375 0 - -65 -222 0 - -65 -545 0 - -65 -1269 0 - -65 -2585 0 - -65 -3752 0 - -65 -4190 0 - -222 -545 0 - -222 -1269 0 - -222 -2585 0 - -222 -3752 0 - -222 -4190 0 - -545 -1269 0 - -545 -2585 0 - -545 -3752 0 - -545 -4190 0 - -1269 -2585 0 - -1269 -3752 0 - -1269 -4190 0 - -2585 -3752 0 - -2585 -4190 0 - -3752 -4190 0 - -66 -546 0 - -66 -646 0 - -66 -798 0 - -66 -1270 0 - -66 -2135 0 - -66 -2412 0 - -66 -2888 0 - -66 -3753 0 - -66 -4376 0 - -546 -646 0 - -546 -798 0 - -546 -1270 0 - -546 -2135 0 - -546 -2412 0 - -546 -2888 0 - -546 -3753 0 - -546 -4376 0 - -646 -798 0 - -646 -1270 0 - -646 -2135 0 - -646 -2412 0 - -646 -2888 0 - -646 -3753 0 - -646 -4376 0 - -798 -1270 0 - -798 -2135 0 - -798 -2412 0 - -798 -2888 0 - -798 -3753 0 - -798 -4376 0 - -1270 -2135 0 - -1270 -2412 0 - -1270 -2888 0 - -1270 -3753 0 - -1270 -4376 0 - -2135 -2412 0 - -2135 -2888 0 - -2135 -3753 0 - -2135 -4376 0 - -2412 -2888 0 - -2412 -3753 0 - -2412 -4376 0 - -2888 -3753 0 - -2888 -4376 0 - -3753 -4376 0 - -67 -547 0 - -67 -647 0 - -67 -799 0 - -67 -2413 0 - -67 -2765 0 - -67 -3754 0 - -67 -4377 0 - -547 -647 0 - -547 -799 0 - -547 -2413 0 - -547 -2765 0 - -547 -3754 0 - -547 -4377 0 - -647 -799 0 - -647 -2413 0 - -647 -2765 0 - -647 -3754 0 - -647 -4377 0 - -799 -2413 0 - -799 -2765 0 - -799 -3754 0 - -799 -4377 0 - -2413 -2765 0 - -2413 -3754 0 - -2413 -4377 0 - -2765 -3754 0 - -2765 -4377 0 - -3754 -4377 0 - -223 -548 0 - -223 -1271 0 - -223 -2136 0 - -223 -2515 0 - -223 -2586 0 - -223 -2766 0 - -223 -3755 0 - -223 -4191 0 - -223 -4378 0 - -548 -1271 0 - -548 -2136 0 - -548 -2515 0 - -548 -2586 0 - -548 -2766 0 - -548 -3755 0 - -548 -4191 0 - -548 -4378 0 - -1271 -2136 0 - -1271 -2515 0 - -1271 -2586 0 - -1271 -2766 0 - -1271 -3755 0 - -1271 -4191 0 - -1271 -4378 0 - -2136 -2515 0 - -2136 -2586 0 - -2136 -2766 0 - -2136 -3755 0 - -2136 -4191 0 - -2136 -4378 0 - -2515 -2586 0 - -2515 -2766 0 - -2515 -3755 0 - -2515 -4191 0 - -2515 -4378 0 - -2586 -2766 0 - -2586 -3755 0 - -2586 -4191 0 - -2586 -4378 0 - -2766 -3755 0 - -2766 -4191 0 - -2766 -4378 0 - -3755 -4191 0 - -3755 -4378 0 - -4191 -4378 0 - -2137 -2414 0 - -2137 -2889 0 - -2137 -3173 0 - -2137 -4379 0 - -2414 -2889 0 - -2414 -3173 0 - -2414 -4379 0 - -2889 -3173 0 - -2889 -4379 0 - -3173 -4379 0 - -224 -426 0 - -224 -549 0 - -224 -800 0 - -224 -1272 0 - -224 -2415 0 - -426 -549 0 - -426 -800 0 - -426 -1272 0 - -426 -2415 0 - -549 -800 0 - -549 -1272 0 - -549 -2415 0 - -800 -1272 0 - -800 -2415 0 - -1272 -2415 0 - -225 -427 0 - -225 -801 0 - -225 -2138 0 - -225 -2516 0 - -225 -2767 0 - -225 -3756 0 - -225 -4192 0 - -427 -801 0 - -427 -2138 0 - -427 -2516 0 - -427 -2767 0 - -427 -3756 0 - -427 -4192 0 - -801 -2138 0 - -801 -2516 0 - -801 -2767 0 - -801 -3756 0 - -801 -4192 0 - -2138 -2516 0 - -2138 -2767 0 - -2138 -3756 0 - -2138 -4192 0 - -2516 -2767 0 - -2516 -3756 0 - -2516 -4192 0 - -2767 -3756 0 - -2767 -4192 0 - -3756 -4192 0 - -68 -226 0 - -68 -550 0 - -68 -648 0 - -68 -2890 0 - -68 -3174 0 - -68 -4193 0 - -68 -4380 0 - -226 -550 0 - -226 -648 0 - -226 -2890 0 - -226 -3174 0 - -226 -4193 0 - -226 -4380 0 - -550 -648 0 - -550 -2890 0 - -550 -3174 0 - -550 -4193 0 - -550 -4380 0 - -648 -2890 0 - -648 -3174 0 - -648 -4193 0 - -648 -4380 0 - -2890 -3174 0 - -2890 -4193 0 - -2890 -4380 0 - -3174 -4193 0 - -3174 -4380 0 - -4193 -4380 0 - -428 -649 0 - -428 -802 0 - -428 -1273 0 - -428 -2139 0 - -428 -2416 0 - -428 -2517 0 - -428 -2768 0 - -428 -2891 0 - -649 -802 0 - -649 -1273 0 - -649 -2139 0 - -649 -2416 0 - -649 -2517 0 - -649 -2768 0 - -649 -2891 0 - -802 -1273 0 - -802 -2139 0 - -802 -2416 0 - -802 -2517 0 - -802 -2768 0 - -802 -2891 0 - -1273 -2139 0 - -1273 -2416 0 - -1273 -2517 0 - -1273 -2768 0 - -1273 -2891 0 - -2139 -2416 0 - -2139 -2517 0 - -2139 -2768 0 - -2139 -2891 0 - -2416 -2517 0 - -2416 -2768 0 - -2416 -2891 0 - -2517 -2768 0 - -2517 -2891 0 - -2768 -2891 0 - -227 -429 0 - -227 -551 0 - -227 -650 0 - -227 -803 0 - -227 -2417 0 - -227 -2892 0 - -227 -3175 0 - -227 -3757 0 - -429 -551 0 - -429 -650 0 - -429 -803 0 - -429 -2417 0 - -429 -2892 0 - -429 -3175 0 - -429 -3757 0 - -551 -650 0 - -551 -803 0 - -551 -2417 0 - -551 -2892 0 - -551 -3175 0 - -551 -3757 0 - -650 -803 0 - -650 -2417 0 - -650 -2892 0 - -650 -3175 0 - -650 -3757 0 - -803 -2417 0 - -803 -2892 0 - -803 -3175 0 - -803 -3757 0 - -2417 -2892 0 - -2417 -3175 0 - -2417 -3757 0 - -2892 -3175 0 - -2892 -3757 0 - -3175 -3757 0 - -228 -323 0 - -228 -651 0 - -228 -890 0 - -228 -1025 0 - -228 -1722 0 - -228 -2418 0 - -228 -2587 0 - -228 -3422 0 - -228 -3575 0 - -228 -3758 0 - -323 -651 0 - -323 -890 0 - -323 -1025 0 - -323 -1722 0 - -323 -2418 0 - -323 -2587 0 - -323 -3422 0 - -323 -3575 0 - -323 -3758 0 - -651 -890 0 - -651 -1025 0 - -651 -1722 0 - -651 -2418 0 - -651 -2587 0 - -651 -3422 0 - -651 -3575 0 - -651 -3758 0 - -890 -1025 0 - -890 -1722 0 - -890 -2418 0 - -890 -2587 0 - -890 -3422 0 - -890 -3575 0 - -890 -3758 0 - -1025 -1722 0 - -1025 -2418 0 - -1025 -2587 0 - -1025 -3422 0 - -1025 -3575 0 - -1025 -3758 0 - -1722 -2418 0 - -1722 -2587 0 - -1722 -3422 0 - -1722 -3575 0 - -1722 -3758 0 - -2418 -2587 0 - -2418 -3422 0 - -2418 -3575 0 - -2418 -3758 0 - -2587 -3422 0 - -2587 -3575 0 - -2587 -3758 0 - -3422 -3575 0 - -3422 -3758 0 - -3575 -3758 0 - -1026 -1349 0 - -1026 -1952 0 - -1026 -2588 0 - -1026 -3423 0 - -1026 -3576 0 - -1026 -4065 0 - -1026 -4381 0 - -1349 -1952 0 - -1349 -2588 0 - -1349 -3423 0 - -1349 -3576 0 - -1349 -4065 0 - -1349 -4381 0 - -1952 -2588 0 - -1952 -3423 0 - -1952 -3576 0 - -1952 -4065 0 - -1952 -4381 0 - -2588 -3423 0 - -2588 -3576 0 - -2588 -4065 0 - -2588 -4381 0 - -3423 -3576 0 - -3423 -4065 0 - -3423 -4381 0 - -3576 -4065 0 - -3576 -4381 0 - -4065 -4381 0 - -652 -1027 0 - -652 -1723 0 - -652 -1953 0 - -652 -2589 0 - -652 -3577 0 - -652 -3759 0 - -1027 -1723 0 - -1027 -1953 0 - -1027 -2589 0 - -1027 -3577 0 - -1027 -3759 0 - -1723 -1953 0 - -1723 -2589 0 - -1723 -3577 0 - -1723 -3759 0 - -1953 -2589 0 - -1953 -3577 0 - -1953 -3759 0 - -2589 -3577 0 - -2589 -3759 0 - -3577 -3759 0 - -229 -891 0 - -229 -1350 0 - -229 -1954 0 - -229 -2319 0 - -229 -3578 0 - -229 -4382 0 - -891 -1350 0 - -891 -1954 0 - -891 -2319 0 - -891 -3578 0 - -891 -4382 0 - -1350 -1954 0 - -1350 -2319 0 - -1350 -3578 0 - -1350 -4382 0 - -1954 -2319 0 - -1954 -3578 0 - -1954 -4382 0 - -2319 -3578 0 - -2319 -4382 0 - -3578 -4382 0 - -324 -653 0 - -324 -1724 0 - -324 -3424 0 - -324 -3760 0 - -653 -1724 0 - -653 -3424 0 - -653 -3760 0 - -1724 -3424 0 - -1724 -3760 0 - -3424 -3760 0 - -654 -892 0 - -654 -1725 0 - -654 -1955 0 - -654 -2590 0 - -654 -3425 0 - -654 -3579 0 - -654 -3761 0 - -654 -4066 0 - -892 -1725 0 - -892 -1955 0 - -892 -2590 0 - -892 -3425 0 - -892 -3579 0 - -892 -3761 0 - -892 -4066 0 - -1725 -1955 0 - -1725 -2590 0 - -1725 -3425 0 - -1725 -3579 0 - -1725 -3761 0 - -1725 -4066 0 - -1955 -2590 0 - -1955 -3425 0 - -1955 -3579 0 - -1955 -3761 0 - -1955 -4066 0 - -2590 -3425 0 - -2590 -3579 0 - -2590 -3761 0 - -2590 -4066 0 - -3425 -3579 0 - -3425 -3761 0 - -3425 -4066 0 - -3579 -3761 0 - -3579 -4066 0 - -3761 -4066 0 - -230 -325 0 - -230 -1028 0 - -230 -1956 0 - -230 -2591 0 - -230 -3426 0 - -230 -3762 0 - -230 -4067 0 - -325 -1028 0 - -325 -1956 0 - -325 -2591 0 - -325 -3426 0 - -325 -3762 0 - -325 -4067 0 - -1028 -1956 0 - -1028 -2591 0 - -1028 -3426 0 - -1028 -3762 0 - -1028 -4067 0 - -1956 -2591 0 - -1956 -3426 0 - -1956 -3762 0 - -1956 -4067 0 - -2591 -3426 0 - -2591 -3762 0 - -2591 -4067 0 - -3426 -3762 0 - -3426 -4067 0 - -3762 -4067 0 - -231 -1351 0 - -231 -2419 0 - -231 -3427 0 - -231 -3763 0 - -231 -4383 0 - -1351 -2419 0 - -1351 -3427 0 - -1351 -3763 0 - -1351 -4383 0 - -2419 -3427 0 - -2419 -3763 0 - -2419 -4383 0 - -3427 -3763 0 - -3427 -4383 0 - -3763 -4383 0 - -232 -655 0 - -232 -1029 0 - -232 -1352 0 - -232 -1957 0 - -232 -2420 0 - -232 -2592 0 - -232 -3580 0 - -655 -1029 0 - -655 -1352 0 - -655 -1957 0 - -655 -2420 0 - -655 -2592 0 - -655 -3580 0 - -1029 -1352 0 - -1029 -1957 0 - -1029 -2420 0 - -1029 -2592 0 - -1029 -3580 0 - -1352 -1957 0 - -1352 -2420 0 - -1352 -2592 0 - -1352 -3580 0 - -1957 -2420 0 - -1957 -2592 0 - -1957 -3580 0 - -2420 -2592 0 - -2420 -3580 0 - -2592 -3580 0 - -326 -1030 0 - -326 -1353 0 - -326 -1958 0 - -326 -3764 0 - -1030 -1353 0 - -1030 -1958 0 - -1030 -3764 0 - -1353 -1958 0 - -1353 -3764 0 - -1958 -3764 0 - -1031 -2421 0 - -1031 -3581 0 - -2421 -3581 0 - -656 -893 0 - -656 -3428 0 - -656 -4068 0 - -656 -4384 0 - -893 -3428 0 - -893 -4068 0 - -893 -4384 0 - -3428 -4068 0 - -3428 -4384 0 - -4068 -4384 0 - -233 -894 0 - -233 -3429 0 - -233 -3765 0 - -894 -3429 0 - -894 -3765 0 - -3429 -3765 0 - -234 -657 0 - -234 -1354 0 - -234 -1726 0 - -234 -2320 0 - -234 -2422 0 - -234 -2593 0 - -234 -3430 0 - -234 -3766 0 - -657 -1354 0 - -657 -1726 0 - -657 -2320 0 - -657 -2422 0 - -657 -2593 0 - -657 -3430 0 - -657 -3766 0 - -1354 -1726 0 - -1354 -2320 0 - -1354 -2422 0 - -1354 -2593 0 - -1354 -3430 0 - -1354 -3766 0 - -1726 -2320 0 - -1726 -2422 0 - -1726 -2593 0 - -1726 -3430 0 - -1726 -3766 0 - -2320 -2422 0 - -2320 -2593 0 - -2320 -3430 0 - -2320 -3766 0 - -2422 -2593 0 - -2422 -3430 0 - -2422 -3766 0 - -2593 -3430 0 - -2593 -3766 0 - -3430 -3766 0 - -235 -327 0 - -235 -658 0 - -235 -1727 0 - -235 -1959 0 - -235 -2321 0 - -235 -2423 0 - -235 -3582 0 - -235 -3767 0 - -327 -658 0 - -327 -1727 0 - -327 -1959 0 - -327 -2321 0 - -327 -2423 0 - -327 -3582 0 - -327 -3767 0 - -658 -1727 0 - -658 -1959 0 - -658 -2321 0 - -658 -2423 0 - -658 -3582 0 - -658 -3767 0 - -1727 -1959 0 - -1727 -2321 0 - -1727 -2423 0 - -1727 -3582 0 - -1727 -3767 0 - -1959 -2321 0 - -1959 -2423 0 - -1959 -3582 0 - -1959 -3767 0 - -2321 -2423 0 - -2321 -3582 0 - -2321 -3767 0 - -2423 -3582 0 - -2423 -3767 0 - -3582 -3767 0 - -236 -1960 0 - -236 -2322 0 - -236 -3768 0 - -236 -4069 0 - -236 -4385 0 - -1960 -2322 0 - -1960 -3768 0 - -1960 -4069 0 - -1960 -4385 0 - -2322 -3768 0 - -2322 -4069 0 - -2322 -4385 0 - -3768 -4069 0 - -3768 -4385 0 - -4069 -4385 0 - -69 -895 0 - -69 -1523 0 - -69 -2140 0 - -69 -2893 0 - -69 -3431 0 - -69 -4194 0 - -895 -1523 0 - -895 -2140 0 - -895 -2893 0 - -895 -3431 0 - -895 -4194 0 - -1523 -2140 0 - -1523 -2893 0 - -1523 -3431 0 - -1523 -4194 0 - -2140 -2893 0 - -2140 -3431 0 - -2140 -4194 0 - -2893 -3431 0 - -2893 -4194 0 - -3431 -4194 0 - -1355 -1524 0 - -1355 -1961 0 - -1355 -2141 0 - -1355 -3432 0 - -1355 -3878 0 - -1355 -4386 0 - -1524 -1961 0 - -1524 -2141 0 - -1524 -3432 0 - -1524 -3878 0 - -1524 -4386 0 - -1961 -2141 0 - -1961 -3432 0 - -1961 -3878 0 - -1961 -4386 0 - -2141 -3432 0 - -2141 -3878 0 - -2141 -4386 0 - -3432 -3878 0 - -3432 -4386 0 - -3878 -4386 0 - -70 -552 0 - -70 -659 0 - -70 -1525 0 - -70 -1728 0 - -70 -1962 0 - -70 -2142 0 - -70 -2894 0 - -70 -3879 0 - -552 -659 0 - -552 -1525 0 - -552 -1728 0 - -552 -1962 0 - -552 -2142 0 - -552 -2894 0 - -552 -3879 0 - -659 -1525 0 - -659 -1728 0 - -659 -1962 0 - -659 -2142 0 - -659 -2894 0 - -659 -3879 0 - -1525 -1728 0 - -1525 -1962 0 - -1525 -2142 0 - -1525 -2894 0 - -1525 -3879 0 - -1728 -1962 0 - -1728 -2142 0 - -1728 -2894 0 - -1728 -3879 0 - -1962 -2142 0 - -1962 -2894 0 - -1962 -3879 0 - -2142 -2894 0 - -2142 -3879 0 - -2894 -3879 0 - -430 -896 0 - -430 -1356 0 - -430 -1963 0 - -430 -4195 0 - -430 -4387 0 - -896 -1356 0 - -896 -1963 0 - -896 -4195 0 - -896 -4387 0 - -1356 -1963 0 - -1356 -4195 0 - -1356 -4387 0 - -1963 -4195 0 - -1963 -4387 0 - -4195 -4387 0 - -431 -660 0 - -431 -1729 0 - -431 -2895 0 - -431 -3433 0 - -431 -3880 0 - -431 -4196 0 - -660 -1729 0 - -660 -2895 0 - -660 -3433 0 - -660 -3880 0 - -660 -4196 0 - -1729 -2895 0 - -1729 -3433 0 - -1729 -3880 0 - -1729 -4196 0 - -2895 -3433 0 - -2895 -3880 0 - -2895 -4196 0 - -3433 -3880 0 - -3433 -4196 0 - -3880 -4196 0 - -71 -432 0 - -71 -553 0 - -71 -661 0 - -71 -897 0 - -71 -1526 0 - -71 -1730 0 - -71 -1964 0 - -71 -2896 0 - -71 -3434 0 - -432 -553 0 - -432 -661 0 - -432 -897 0 - -432 -1526 0 - -432 -1730 0 - -432 -1964 0 - -432 -2896 0 - -432 -3434 0 - -553 -661 0 - -553 -897 0 - -553 -1526 0 - -553 -1730 0 - -553 -1964 0 - -553 -2896 0 - -553 -3434 0 - -661 -897 0 - -661 -1526 0 - -661 -1730 0 - -661 -1964 0 - -661 -2896 0 - -661 -3434 0 - -897 -1526 0 - -897 -1730 0 - -897 -1964 0 - -897 -2896 0 - -897 -3434 0 - -1526 -1730 0 - -1526 -1964 0 - -1526 -2896 0 - -1526 -3434 0 - -1730 -1964 0 - -1730 -2896 0 - -1730 -3434 0 - -1964 -2896 0 - -1964 -3434 0 - -2896 -3434 0 - -72 -1357 0 - -72 -1527 0 - -72 -3435 0 - -72 -3881 0 - -72 -4197 0 - -72 -4388 0 - -1357 -1527 0 - -1357 -3435 0 - -1357 -3881 0 - -1357 -4197 0 - -1357 -4388 0 - -1527 -3435 0 - -1527 -3881 0 - -1527 -4197 0 - -1527 -4388 0 - -3435 -3881 0 - -3435 -4197 0 - -3435 -4388 0 - -3881 -4197 0 - -3881 -4388 0 - -4197 -4388 0 - -73 -433 0 - -73 -1528 0 - -73 -2143 0 - -73 -2897 0 - -73 -3882 0 - -73 -4198 0 - -73 -4389 0 - -433 -1528 0 - -433 -2143 0 - -433 -2897 0 - -433 -3882 0 - -433 -4198 0 - -433 -4389 0 - -1528 -2143 0 - -1528 -2897 0 - -1528 -3882 0 - -1528 -4198 0 - -1528 -4389 0 - -2143 -2897 0 - -2143 -3882 0 - -2143 -4198 0 - -2143 -4389 0 - -2897 -3882 0 - -2897 -4198 0 - -2897 -4389 0 - -3882 -4198 0 - -3882 -4389 0 - -4198 -4389 0 - -434 -662 0 - -434 -1358 0 - -434 -1965 0 - -662 -1358 0 - -662 -1965 0 - -1358 -1965 0 - -74 -554 0 - -74 -663 0 - -74 -1731 0 - -74 -1966 0 - -74 -4390 0 - -554 -663 0 - -554 -1731 0 - -554 -1966 0 - -554 -4390 0 - -663 -1731 0 - -663 -1966 0 - -663 -4390 0 - -1731 -1966 0 - -1731 -4390 0 - -1966 -4390 0 - -555 -664 0 - -555 -898 0 - -555 -1529 0 - -555 -2144 0 - -555 -3436 0 - -555 -4391 0 - -664 -898 0 - -664 -1529 0 - -664 -2144 0 - -664 -3436 0 - -664 -4391 0 - -898 -1529 0 - -898 -2144 0 - -898 -3436 0 - -898 -4391 0 - -1529 -2144 0 - -1529 -3436 0 - -1529 -4391 0 - -2144 -3436 0 - -2144 -4391 0 - -3436 -4391 0 - -75 -556 0 - -75 -665 0 - -75 -1530 0 - -75 -1967 0 - -75 -2898 0 - -75 -3437 0 - -75 -3883 0 - -75 -4199 0 - -75 -4392 0 - -556 -665 0 - -556 -1530 0 - -556 -1967 0 - -556 -2898 0 - -556 -3437 0 - -556 -3883 0 - -556 -4199 0 - -556 -4392 0 - -665 -1530 0 - -665 -1967 0 - -665 -2898 0 - -665 -3437 0 - -665 -3883 0 - -665 -4199 0 - -665 -4392 0 - -1530 -1967 0 - -1530 -2898 0 - -1530 -3437 0 - -1530 -3883 0 - -1530 -4199 0 - -1530 -4392 0 - -1967 -2898 0 - -1967 -3437 0 - -1967 -3883 0 - -1967 -4199 0 - -1967 -4392 0 - -2898 -3437 0 - -2898 -3883 0 - -2898 -4199 0 - -2898 -4392 0 - -3437 -3883 0 - -3437 -4199 0 - -3437 -4392 0 - -3883 -4199 0 - -3883 -4392 0 - -4199 -4392 0 - -666 -1359 0 - -666 -1732 0 - -666 -3438 0 - -1359 -1732 0 - -1359 -3438 0 - -1732 -3438 0 - -435 -557 0 - -435 -667 0 - -435 -1733 0 - -435 -1968 0 - -435 -2899 0 - -435 -3884 0 - -557 -667 0 - -557 -1733 0 - -557 -1968 0 - -557 -2899 0 - -557 -3884 0 - -667 -1733 0 - -667 -1968 0 - -667 -2899 0 - -667 -3884 0 - -1733 -1968 0 - -1733 -2899 0 - -1733 -3884 0 - -1968 -2899 0 - -1968 -3884 0 - -2899 -3884 0 - -76 -899 0 - -76 -1531 0 - -76 -1969 0 - -76 -2145 0 - -76 -3439 0 - -76 -4200 0 - -76 -4393 0 - -899 -1531 0 - -899 -1969 0 - -899 -2145 0 - -899 -3439 0 - -899 -4200 0 - -899 -4393 0 - -1531 -1969 0 - -1531 -2145 0 - -1531 -3439 0 - -1531 -4200 0 - -1531 -4393 0 - -1969 -2145 0 - -1969 -3439 0 - -1969 -4200 0 - -1969 -4393 0 - -2145 -3439 0 - -2145 -4200 0 - -2145 -4393 0 - -3439 -4200 0 - -3439 -4393 0 - -4200 -4393 0 - -1176 -1852 0 - -1176 -3176 0 - -1176 -3583 0 - -1176 -4008 0 - -1176 -4070 0 - -1176 -4201 0 - -1852 -3176 0 - -1852 -3583 0 - -1852 -4008 0 - -1852 -4070 0 - -1852 -4201 0 - -3176 -3583 0 - -3176 -4008 0 - -3176 -4070 0 - -3176 -4201 0 - -3583 -4008 0 - -3583 -4070 0 - -3583 -4201 0 - -4008 -4070 0 - -4008 -4201 0 - -4070 -4201 0 - -1177 -1734 0 - -1177 -1853 0 - -1177 -2518 0 - -1177 -2594 0 - -1177 -3584 0 - -1177 -3885 0 - -1177 -4202 0 - -1734 -1853 0 - -1734 -2518 0 - -1734 -2594 0 - -1734 -3584 0 - -1734 -3885 0 - -1734 -4202 0 - -1853 -2518 0 - -1853 -2594 0 - -1853 -3584 0 - -1853 -3885 0 - -1853 -4202 0 - -2518 -2594 0 - -2518 -3584 0 - -2518 -3885 0 - -2518 -4202 0 - -2594 -3584 0 - -2594 -3885 0 - -2594 -4202 0 - -3584 -3885 0 - -3584 -4202 0 - -3885 -4202 0 - -1178 -1970 0 - -1178 -2519 0 - -1178 -3177 0 - -1178 -3280 0 - -1178 -3585 0 - -1178 -4203 0 - -1970 -2519 0 - -1970 -3177 0 - -1970 -3280 0 - -1970 -3585 0 - -1970 -4203 0 - -2519 -3177 0 - -2519 -3280 0 - -2519 -3585 0 - -2519 -4203 0 - -3177 -3280 0 - -3177 -3585 0 - -3177 -4203 0 - -3280 -3585 0 - -3280 -4203 0 - -3585 -4203 0 - -1179 -1735 0 - -1179 -1854 0 - -1179 -1971 0 - -1179 -2595 0 - -1179 -3178 0 - -1179 -3281 0 - -1179 -3586 0 - -1179 -4009 0 - -1179 -4071 0 - -1735 -1854 0 - -1735 -1971 0 - -1735 -2595 0 - -1735 -3178 0 - -1735 -3281 0 - -1735 -3586 0 - -1735 -4009 0 - -1735 -4071 0 - -1854 -1971 0 - -1854 -2595 0 - -1854 -3178 0 - -1854 -3281 0 - -1854 -3586 0 - -1854 -4009 0 - -1854 -4071 0 - -1971 -2595 0 - -1971 -3178 0 - -1971 -3281 0 - -1971 -3586 0 - -1971 -4009 0 - -1971 -4071 0 - -2595 -3178 0 - -2595 -3281 0 - -2595 -3586 0 - -2595 -4009 0 - -2595 -4071 0 - -3178 -3281 0 - -3178 -3586 0 - -3178 -4009 0 - -3178 -4071 0 - -3281 -3586 0 - -3281 -4009 0 - -3281 -4071 0 - -3586 -4009 0 - -3586 -4071 0 - -4009 -4071 0 - -1736 -2520 0 - -1736 -3282 0 - -1736 -3886 0 - -1736 -4010 0 - -1736 -4204 0 - -2520 -3282 0 - -2520 -3886 0 - -2520 -4010 0 - -2520 -4204 0 - -3282 -3886 0 - -3282 -4010 0 - -3282 -4204 0 - -3886 -4010 0 - -3886 -4204 0 - -4010 -4204 0 - -1180 -1972 0 - -1180 -2596 0 - -1180 -4072 0 - -1180 -4205 0 - -1972 -2596 0 - -1972 -4072 0 - -1972 -4205 0 - -2596 -4072 0 - -2596 -4205 0 - -4072 -4205 0 - -1737 -3179 0 - -1737 -3283 0 - -1737 -3887 0 - -1737 -4206 0 - -3179 -3283 0 - -3179 -3887 0 - -3179 -4206 0 - -3283 -3887 0 - -3283 -4206 0 - -3887 -4206 0 - -1181 -1973 0 - -1181 -2597 0 - -1181 -3180 0 - -1181 -3284 0 - -1181 -3587 0 - -1973 -2597 0 - -1973 -3180 0 - -1973 -3284 0 - -1973 -3587 0 - -2597 -3180 0 - -2597 -3284 0 - -2597 -3587 0 - -3180 -3284 0 - -3180 -3587 0 - -3284 -3587 0 - -2598 -3181 0 - -2598 -3285 0 - -2598 -3888 0 - -2598 -4073 0 - -3181 -3285 0 - -3181 -3888 0 - -3181 -4073 0 - -3285 -3888 0 - -3285 -4073 0 - -3888 -4073 0 - -1182 -2521 0 - -1182 -2599 0 - -1182 -3588 0 - -1182 -4074 0 - -1182 -4207 0 - -1182 -4294 0 - -2521 -2599 0 - -2521 -3588 0 - -2521 -4074 0 - -2521 -4207 0 - -2521 -4294 0 - -2599 -3588 0 - -2599 -4074 0 - -2599 -4207 0 - -2599 -4294 0 - -3588 -4074 0 - -3588 -4207 0 - -3588 -4294 0 - -4074 -4207 0 - -4074 -4294 0 - -4207 -4294 0 - -1183 -1738 0 - -1183 -1974 0 - -1183 -3182 0 - -1183 -3589 0 - -1183 -3889 0 - -1183 -4011 0 - -1738 -1974 0 - -1738 -3182 0 - -1738 -3589 0 - -1738 -3889 0 - -1738 -4011 0 - -1974 -3182 0 - -1974 -3589 0 - -1974 -3889 0 - -1974 -4011 0 - -3182 -3589 0 - -3182 -3889 0 - -3182 -4011 0 - -3589 -3889 0 - -3589 -4011 0 - -3889 -4011 0 - -1184 -1855 0 - -1184 -2522 0 - -1184 -3183 0 - -1184 -3590 0 - -1184 -4012 0 - -1184 -4295 0 - -1855 -2522 0 - -1855 -3183 0 - -1855 -3590 0 - -1855 -4012 0 - -1855 -4295 0 - -2522 -3183 0 - -2522 -3590 0 - -2522 -4012 0 - -2522 -4295 0 - -3183 -3590 0 - -3183 -4012 0 - -3183 -4295 0 - -3590 -4012 0 - -3590 -4295 0 - -4012 -4295 0 - -1975 -2523 0 - -1975 -3286 0 - -1975 -3591 0 - -1975 -4013 0 - -2523 -3286 0 - -2523 -3591 0 - -2523 -4013 0 - -3286 -3591 0 - -3286 -4013 0 - -3591 -4013 0 - -1185 -1739 0 - -1185 -1856 0 - -1185 -1976 0 - -1185 -3184 0 - -1185 -3287 0 - -1185 -3592 0 - -1185 -3890 0 - -1185 -4014 0 - -1739 -1856 0 - -1739 -1976 0 - -1739 -3184 0 - -1739 -3287 0 - -1739 -3592 0 - -1739 -3890 0 - -1739 -4014 0 - -1856 -1976 0 - -1856 -3184 0 - -1856 -3287 0 - -1856 -3592 0 - -1856 -3890 0 - -1856 -4014 0 - -1976 -3184 0 - -1976 -3287 0 - -1976 -3592 0 - -1976 -3890 0 - -1976 -4014 0 - -3184 -3287 0 - -3184 -3592 0 - -3184 -3890 0 - -3184 -4014 0 - -3287 -3592 0 - -3287 -3890 0 - -3287 -4014 0 - -3592 -3890 0 - -3592 -4014 0 - -3890 -4014 0 - -328 -668 0 - -328 -1032 0 - -328 -1532 0 - -328 -1740 0 - -328 -1977 0 - -328 -2769 0 - -328 -4075 0 - -668 -1032 0 - -668 -1532 0 - -668 -1740 0 - -668 -1977 0 - -668 -2769 0 - -668 -4075 0 - -1032 -1532 0 - -1032 -1740 0 - -1032 -1977 0 - -1032 -2769 0 - -1032 -4075 0 - -1532 -1740 0 - -1532 -1977 0 - -1532 -2769 0 - -1532 -4075 0 - -1740 -1977 0 - -1740 -2769 0 - -1740 -4075 0 - -1977 -2769 0 - -1977 -4075 0 - -2769 -4075 0 - -329 -669 0 - -329 -1033 0 - -329 -1533 0 - -329 -1741 0 - -329 -2250 0 - -329 -2524 0 - -329 -2600 0 - -329 -3593 0 - -329 -3769 0 - -669 -1033 0 - -669 -1533 0 - -669 -1741 0 - -669 -2250 0 - -669 -2524 0 - -669 -2600 0 - -669 -3593 0 - -669 -3769 0 - -1033 -1533 0 - -1033 -1741 0 - -1033 -2250 0 - -1033 -2524 0 - -1033 -2600 0 - -1033 -3593 0 - -1033 -3769 0 - -1533 -1741 0 - -1533 -2250 0 - -1533 -2524 0 - -1533 -2600 0 - -1533 -3593 0 - -1533 -3769 0 - -1741 -2250 0 - -1741 -2524 0 - -1741 -2600 0 - -1741 -3593 0 - -1741 -3769 0 - -2250 -2524 0 - -2250 -2600 0 - -2250 -3593 0 - -2250 -3769 0 - -2524 -2600 0 - -2524 -3593 0 - -2524 -3769 0 - -2600 -3593 0 - -2600 -3769 0 - -3593 -3769 0 - -330 -1034 0 - -330 -1742 0 - -330 -1978 0 - -330 -2251 0 - -330 -2601 0 - -330 -3288 0 - -330 -3594 0 - -330 -3770 0 - -330 -4015 0 - -330 -4076 0 - -1034 -1742 0 - -1034 -1978 0 - -1034 -2251 0 - -1034 -2601 0 - -1034 -3288 0 - -1034 -3594 0 - -1034 -3770 0 - -1034 -4015 0 - -1034 -4076 0 - -1742 -1978 0 - -1742 -2251 0 - -1742 -2601 0 - -1742 -3288 0 - -1742 -3594 0 - -1742 -3770 0 - -1742 -4015 0 - -1742 -4076 0 - -1978 -2251 0 - -1978 -2601 0 - -1978 -3288 0 - -1978 -3594 0 - -1978 -3770 0 - -1978 -4015 0 - -1978 -4076 0 - -2251 -2601 0 - -2251 -3288 0 - -2251 -3594 0 - -2251 -3770 0 - -2251 -4015 0 - -2251 -4076 0 - -2601 -3288 0 - -2601 -3594 0 - -2601 -3770 0 - -2601 -4015 0 - -2601 -4076 0 - -3288 -3594 0 - -3288 -3770 0 - -3288 -4015 0 - -3288 -4076 0 - -3594 -3770 0 - -3594 -4015 0 - -3594 -4076 0 - -3770 -4015 0 - -3770 -4076 0 - -4015 -4076 0 - -1534 -1590 0 - -1534 -1743 0 - -1534 -2525 0 - -1534 -3595 0 - -1590 -1743 0 - -1590 -2525 0 - -1590 -3595 0 - -1743 -2525 0 - -1743 -3595 0 - -2525 -3595 0 - -331 -1274 0 - -331 -1535 0 - -331 -1744 0 - -331 -2252 0 - -331 -2602 0 - -331 -3289 0 - -331 -3596 0 - -331 -3771 0 - -331 -4016 0 - -1274 -1535 0 - -1274 -1744 0 - -1274 -2252 0 - -1274 -2602 0 - -1274 -3289 0 - -1274 -3596 0 - -1274 -3771 0 - -1274 -4016 0 - -1535 -1744 0 - -1535 -2252 0 - -1535 -2602 0 - -1535 -3289 0 - -1535 -3596 0 - -1535 -3771 0 - -1535 -4016 0 - -1744 -2252 0 - -1744 -2602 0 - -1744 -3289 0 - -1744 -3596 0 - -1744 -3771 0 - -1744 -4016 0 - -2252 -2602 0 - -2252 -3289 0 - -2252 -3596 0 - -2252 -3771 0 - -2252 -4016 0 - -2602 -3289 0 - -2602 -3596 0 - -2602 -3771 0 - -2602 -4016 0 - -3289 -3596 0 - -3289 -3771 0 - -3289 -4016 0 - -3596 -3771 0 - -3596 -4016 0 - -3771 -4016 0 - -670 -1275 0 - -670 -1536 0 - -670 -1591 0 - -670 -3772 0 - -670 -4296 0 - -1275 -1536 0 - -1275 -1591 0 - -1275 -3772 0 - -1275 -4296 0 - -1536 -1591 0 - -1536 -3772 0 - -1536 -4296 0 - -1591 -3772 0 - -1591 -4296 0 - -3772 -4296 0 - -1035 -1745 0 - -1035 -2671 0 - -1035 -2770 0 - -1035 -3290 0 - -1745 -2671 0 - -1745 -2770 0 - -1745 -3290 0 - -2671 -2770 0 - -2671 -3290 0 - -2770 -3290 0 - -1276 -1537 0 - -1276 -2526 0 - -1276 -2672 0 - -1276 -3291 0 - -1276 -4017 0 - -1537 -2526 0 - -1537 -2672 0 - -1537 -3291 0 - -1537 -4017 0 - -2526 -2672 0 - -2526 -3291 0 - -2526 -4017 0 - -2672 -3291 0 - -2672 -4017 0 - -3291 -4017 0 - -671 -1036 0 - -671 -1592 0 - -671 -1979 0 - -671 -2603 0 - -671 -2771 0 - -671 -3292 0 - -671 -3597 0 - -1036 -1592 0 - -1036 -1979 0 - -1036 -2603 0 - -1036 -2771 0 - -1036 -3292 0 - -1036 -3597 0 - -1592 -1979 0 - -1592 -2603 0 - -1592 -2771 0 - -1592 -3292 0 - -1592 -3597 0 - -1979 -2603 0 - -1979 -2771 0 - -1979 -3292 0 - -1979 -3597 0 - -2603 -2771 0 - -2603 -3292 0 - -2603 -3597 0 - -2771 -3292 0 - -2771 -3597 0 - -3292 -3597 0 - -332 -672 0 - -332 -1538 0 - -332 -1593 0 - -332 -2253 0 - -332 -2604 0 - -332 -2673 0 - -332 -3293 0 - -332 -3773 0 - -332 -4077 0 - -672 -1538 0 - -672 -1593 0 - -672 -2253 0 - -672 -2604 0 - -672 -2673 0 - -672 -3293 0 - -672 -3773 0 - -672 -4077 0 - -1538 -1593 0 - -1538 -2253 0 - -1538 -2604 0 - -1538 -2673 0 - -1538 -3293 0 - -1538 -3773 0 - -1538 -4077 0 - -1593 -2253 0 - -1593 -2604 0 - -1593 -2673 0 - -1593 -3293 0 - -1593 -3773 0 - -1593 -4077 0 - -2253 -2604 0 - -2253 -2673 0 - -2253 -3293 0 - -2253 -3773 0 - -2253 -4077 0 - -2604 -2673 0 - -2604 -3293 0 - -2604 -3773 0 - -2604 -4077 0 - -2673 -3293 0 - -2673 -3773 0 - -2673 -4077 0 - -3293 -3773 0 - -3293 -4077 0 - -3773 -4077 0 - -333 -1037 0 - -333 -1277 0 - -333 -2254 0 - -333 -2527 0 - -333 -2605 0 - -333 -2772 0 - -333 -3598 0 - -333 -3774 0 - -333 -4078 0 - -333 -4297 0 - -1037 -1277 0 - -1037 -2254 0 - -1037 -2527 0 - -1037 -2605 0 - -1037 -2772 0 - -1037 -3598 0 - -1037 -3774 0 - -1037 -4078 0 - -1037 -4297 0 - -1277 -2254 0 - -1277 -2527 0 - -1277 -2605 0 - -1277 -2772 0 - -1277 -3598 0 - -1277 -3774 0 - -1277 -4078 0 - -1277 -4297 0 - -2254 -2527 0 - -2254 -2605 0 - -2254 -2772 0 - -2254 -3598 0 - -2254 -3774 0 - -2254 -4078 0 - -2254 -4297 0 - -2527 -2605 0 - -2527 -2772 0 - -2527 -3598 0 - -2527 -3774 0 - -2527 -4078 0 - -2527 -4297 0 - -2605 -2772 0 - -2605 -3598 0 - -2605 -3774 0 - -2605 -4078 0 - -2605 -4297 0 - -2772 -3598 0 - -2772 -3774 0 - -2772 -4078 0 - -2772 -4297 0 - -3598 -3774 0 - -3598 -4078 0 - -3598 -4297 0 - -3774 -4078 0 - -3774 -4297 0 - -4078 -4297 0 - -1038 -1539 0 - -1038 -2528 0 - -1038 -3599 0 - -1038 -4018 0 - -1038 -4298 0 - -1539 -2528 0 - -1539 -3599 0 - -1539 -4018 0 - -1539 -4298 0 - -2528 -3599 0 - -2528 -4018 0 - -2528 -4298 0 - -3599 -4018 0 - -3599 -4298 0 - -4018 -4298 0 - -673 -1278 0 - -673 -1540 0 - -673 -1594 0 - -673 -2255 0 - -673 -3294 0 - -673 -4079 0 - -673 -4299 0 - -1278 -1540 0 - -1278 -1594 0 - -1278 -2255 0 - -1278 -3294 0 - -1278 -4079 0 - -1278 -4299 0 - -1540 -1594 0 - -1540 -2255 0 - -1540 -3294 0 - -1540 -4079 0 - -1540 -4299 0 - -1594 -2255 0 - -1594 -3294 0 - -1594 -4079 0 - -1594 -4299 0 - -2255 -3294 0 - -2255 -4079 0 - -2255 -4299 0 - -3294 -4079 0 - -3294 -4299 0 - -4079 -4299 0 - -1279 -3600 0 - -1279 -4080 0 - -3600 -4080 0 - -1541 -2529 0 - -1541 -2773 0 - -1541 -3775 0 - -1541 -4300 0 - -2529 -2773 0 - -2529 -3775 0 - -2529 -4300 0 - -2773 -3775 0 - -2773 -4300 0 - -3775 -4300 0 - -1542 -1746 0 - -1542 -2256 0 - -1542 -3776 0 - -1542 -4019 0 - -1542 -4081 0 - -1542 -4301 0 - -1746 -2256 0 - -1746 -3776 0 - -1746 -4019 0 - -1746 -4081 0 - -1746 -4301 0 - -2256 -3776 0 - -2256 -4019 0 - -2256 -4081 0 - -2256 -4301 0 - -3776 -4019 0 - -3776 -4081 0 - -3776 -4301 0 - -4019 -4081 0 - -4019 -4301 0 - -4081 -4301 0 - -334 -674 0 - -334 -1039 0 - -334 -1280 0 - -334 -1980 0 - -334 -2257 0 - -334 -2530 0 - -334 -2774 0 - -334 -3295 0 - -334 -3601 0 - -334 -4020 0 - -674 -1039 0 - -674 -1280 0 - -674 -1980 0 - -674 -2257 0 - -674 -2530 0 - -674 -2774 0 - -674 -3295 0 - -674 -3601 0 - -674 -4020 0 - -1039 -1280 0 - -1039 -1980 0 - -1039 -2257 0 - -1039 -2530 0 - -1039 -2774 0 - -1039 -3295 0 - -1039 -3601 0 - -1039 -4020 0 - -1280 -1980 0 - -1280 -2257 0 - -1280 -2530 0 - -1280 -2774 0 - -1280 -3295 0 - -1280 -3601 0 - -1280 -4020 0 - -1980 -2257 0 - -1980 -2530 0 - -1980 -2774 0 - -1980 -3295 0 - -1980 -3601 0 - -1980 -4020 0 - -2257 -2530 0 - -2257 -2774 0 - -2257 -3295 0 - -2257 -3601 0 - -2257 -4020 0 - -2530 -2774 0 - -2530 -3295 0 - -2530 -3601 0 - -2530 -4020 0 - -2774 -3295 0 - -2774 -3601 0 - -2774 -4020 0 - -3295 -3601 0 - -3295 -4020 0 - -3601 -4020 0 - -335 -675 0 - -335 -1747 0 - -335 -1981 0 - -335 -2258 0 - -335 -3296 0 - -335 -3602 0 - -335 -3777 0 - -335 -4021 0 - -675 -1747 0 - -675 -1981 0 - -675 -2258 0 - -675 -3296 0 - -675 -3602 0 - -675 -3777 0 - -675 -4021 0 - -1747 -1981 0 - -1747 -2258 0 - -1747 -3296 0 - -1747 -3602 0 - -1747 -3777 0 - -1747 -4021 0 - -1981 -2258 0 - -1981 -3296 0 - -1981 -3602 0 - -1981 -3777 0 - -1981 -4021 0 - -2258 -3296 0 - -2258 -3602 0 - -2258 -3777 0 - -2258 -4021 0 - -3296 -3602 0 - -3296 -3777 0 - -3296 -4021 0 - -3602 -3777 0 - -3602 -4021 0 - -3777 -4021 0 - -1281 -1982 0 - -1281 -2259 0 - -1281 -2531 0 - -1281 -3778 0 - -1281 -4022 0 - -1281 -4082 0 - -1281 -4302 0 - -1982 -2259 0 - -1982 -2531 0 - -1982 -3778 0 - -1982 -4022 0 - -1982 -4082 0 - -1982 -4302 0 - -2259 -2531 0 - -2259 -3778 0 - -2259 -4022 0 - -2259 -4082 0 - -2259 -4302 0 - -2531 -3778 0 - -2531 -4022 0 - -2531 -4082 0 - -2531 -4302 0 - -3778 -4022 0 - -3778 -4082 0 - -3778 -4302 0 - -4022 -4082 0 - -4022 -4302 0 - -4082 -4302 0 - -676 -804 0 - -676 -1040 0 - -676 -1543 0 - -676 -1983 0 - -676 -2323 0 - -676 -2775 0 - -676 -2900 0 - -676 -4083 0 - -676 -4394 0 - -804 -1040 0 - -804 -1543 0 - -804 -1983 0 - -804 -2323 0 - -804 -2775 0 - -804 -2900 0 - -804 -4083 0 - -804 -4394 0 - -1040 -1543 0 - -1040 -1983 0 - -1040 -2323 0 - -1040 -2775 0 - -1040 -2900 0 - -1040 -4083 0 - -1040 -4394 0 - -1543 -1983 0 - -1543 -2323 0 - -1543 -2775 0 - -1543 -2900 0 - -1543 -4083 0 - -1543 -4394 0 - -1983 -2323 0 - -1983 -2775 0 - -1983 -2900 0 - -1983 -4083 0 - -1983 -4394 0 - -2323 -2775 0 - -2323 -2900 0 - -2323 -4083 0 - -2323 -4394 0 - -2775 -2900 0 - -2775 -4083 0 - -2775 -4394 0 - -2900 -4083 0 - -2900 -4394 0 - -4083 -4394 0 - -805 -1186 0 - -805 -1544 0 - -805 -2146 0 - -805 -2776 0 - -805 -2901 0 - -805 -3185 0 - -805 -3603 0 - -805 -4084 0 - -805 -4208 0 - -1186 -1544 0 - -1186 -2146 0 - -1186 -2776 0 - -1186 -2901 0 - -1186 -3185 0 - -1186 -3603 0 - -1186 -4084 0 - -1186 -4208 0 - -1544 -2146 0 - -1544 -2776 0 - -1544 -2901 0 - -1544 -3185 0 - -1544 -3603 0 - -1544 -4084 0 - -1544 -4208 0 - -2146 -2776 0 - -2146 -2901 0 - -2146 -3185 0 - -2146 -3603 0 - -2146 -4084 0 - -2146 -4208 0 - -2776 -2901 0 - -2776 -3185 0 - -2776 -3603 0 - -2776 -4084 0 - -2776 -4208 0 - -2901 -3185 0 - -2901 -3603 0 - -2901 -4084 0 - -2901 -4208 0 - -3185 -3603 0 - -3185 -4084 0 - -3185 -4208 0 - -3603 -4084 0 - -3603 -4208 0 - -4084 -4208 0 - -806 -1041 0 - -806 -1545 0 - -806 -1984 0 - -806 -2147 0 - -806 -2606 0 - -806 -2674 0 - -806 -2777 0 - -806 -2998 0 - -806 -3604 0 - -806 -3891 0 - -806 -4085 0 - -806 -4395 0 - -1041 -1545 0 - -1041 -1984 0 - -1041 -2147 0 - -1041 -2606 0 - -1041 -2674 0 - -1041 -2777 0 - -1041 -2998 0 - -1041 -3604 0 - -1041 -3891 0 - -1041 -4085 0 - -1041 -4395 0 - -1545 -1984 0 - -1545 -2147 0 - -1545 -2606 0 - -1545 -2674 0 - -1545 -2777 0 - -1545 -2998 0 - -1545 -3604 0 - -1545 -3891 0 - -1545 -4085 0 - -1545 -4395 0 - -1984 -2147 0 - -1984 -2606 0 - -1984 -2674 0 - -1984 -2777 0 - -1984 -2998 0 - -1984 -3604 0 - -1984 -3891 0 - -1984 -4085 0 - -1984 -4395 0 - -2147 -2606 0 - -2147 -2674 0 - -2147 -2777 0 - -2147 -2998 0 - -2147 -3604 0 - -2147 -3891 0 - -2147 -4085 0 - -2147 -4395 0 - -2606 -2674 0 - -2606 -2777 0 - -2606 -2998 0 - -2606 -3604 0 - -2606 -3891 0 - -2606 -4085 0 - -2606 -4395 0 - -2674 -2777 0 - -2674 -2998 0 - -2674 -3604 0 - -2674 -3891 0 - -2674 -4085 0 - -2674 -4395 0 - -2777 -2998 0 - -2777 -3604 0 - -2777 -3891 0 - -2777 -4085 0 - -2777 -4395 0 - -2998 -3604 0 - -2998 -3891 0 - -2998 -4085 0 - -2998 -4395 0 - -3604 -3891 0 - -3604 -4085 0 - -3604 -4395 0 - -3891 -4085 0 - -3891 -4395 0 - -4085 -4395 0 - -558 -677 0 - -558 -1042 0 - -558 -1187 0 - -558 -1546 0 - -558 -1985 0 - -558 -2148 0 - -558 -2607 0 - -558 -2778 0 - -558 -2902 0 - -558 -2999 0 - -558 -3186 0 - -558 -3605 0 - -558 -3892 0 - -677 -1042 0 - -677 -1187 0 - -677 -1546 0 - -677 -1985 0 - -677 -2148 0 - -677 -2607 0 - -677 -2778 0 - -677 -2902 0 - -677 -2999 0 - -677 -3186 0 - -677 -3605 0 - -677 -3892 0 - -1042 -1187 0 - -1042 -1546 0 - -1042 -1985 0 - -1042 -2148 0 - -1042 -2607 0 - -1042 -2778 0 - -1042 -2902 0 - -1042 -2999 0 - -1042 -3186 0 - -1042 -3605 0 - -1042 -3892 0 - -1187 -1546 0 - -1187 -1985 0 - -1187 -2148 0 - -1187 -2607 0 - -1187 -2778 0 - -1187 -2902 0 - -1187 -2999 0 - -1187 -3186 0 - -1187 -3605 0 - -1187 -3892 0 - -1546 -1985 0 - -1546 -2148 0 - -1546 -2607 0 - -1546 -2778 0 - -1546 -2902 0 - -1546 -2999 0 - -1546 -3186 0 - -1546 -3605 0 - -1546 -3892 0 - -1985 -2148 0 - -1985 -2607 0 - -1985 -2778 0 - -1985 -2902 0 - -1985 -2999 0 - -1985 -3186 0 - -1985 -3605 0 - -1985 -3892 0 - -2148 -2607 0 - -2148 -2778 0 - -2148 -2902 0 - -2148 -2999 0 - -2148 -3186 0 - -2148 -3605 0 - -2148 -3892 0 - -2607 -2778 0 - -2607 -2902 0 - -2607 -2999 0 - -2607 -3186 0 - -2607 -3605 0 - -2607 -3892 0 - -2778 -2902 0 - -2778 -2999 0 - -2778 -3186 0 - -2778 -3605 0 - -2778 -3892 0 - -2902 -2999 0 - -2902 -3186 0 - -2902 -3605 0 - -2902 -3892 0 - -2999 -3186 0 - -2999 -3605 0 - -2999 -3892 0 - -3186 -3605 0 - -3186 -3892 0 - -3605 -3892 0 - -1188 -1595 0 - -1188 -2149 0 - -1188 -2779 0 - -1188 -3187 0 - -1188 -3606 0 - -1595 -2149 0 - -1595 -2779 0 - -1595 -3187 0 - -1595 -3606 0 - -2149 -2779 0 - -2149 -3187 0 - -2149 -3606 0 - -2779 -3187 0 - -2779 -3606 0 - -3187 -3606 0 - -1189 -1986 0 - -1189 -2324 0 - -1189 -3000 0 - -1189 -3188 0 - -1189 -3607 0 - -1189 -4209 0 - -1189 -4396 0 - -1986 -2324 0 - -1986 -3000 0 - -1986 -3188 0 - -1986 -3607 0 - -1986 -4209 0 - -1986 -4396 0 - -2324 -3000 0 - -2324 -3188 0 - -2324 -3607 0 - -2324 -4209 0 - -2324 -4396 0 - -3000 -3188 0 - -3000 -3607 0 - -3000 -4209 0 - -3000 -4396 0 - -3188 -3607 0 - -3188 -4209 0 - -3188 -4396 0 - -3607 -4209 0 - -3607 -4396 0 - -4209 -4396 0 - -559 -1043 0 - -559 -1190 0 - -559 -1987 0 - -559 -2608 0 - -559 -3001 0 - -559 -3189 0 - -559 -3608 0 - -559 -4086 0 - -1043 -1190 0 - -1043 -1987 0 - -1043 -2608 0 - -1043 -3001 0 - -1043 -3189 0 - -1043 -3608 0 - -1043 -4086 0 - -1190 -1987 0 - -1190 -2608 0 - -1190 -3001 0 - -1190 -3189 0 - -1190 -3608 0 - -1190 -4086 0 - -1987 -2608 0 - -1987 -3001 0 - -1987 -3189 0 - -1987 -3608 0 - -1987 -4086 0 - -2608 -3001 0 - -2608 -3189 0 - -2608 -3608 0 - -2608 -4086 0 - -3001 -3189 0 - -3001 -3608 0 - -3001 -4086 0 - -3189 -3608 0 - -3189 -4086 0 - -3608 -4086 0 - -678 -807 0 - -678 -1596 0 - -678 -2675 0 - -678 -2903 0 - -678 -3893 0 - -678 -4210 0 - -807 -1596 0 - -807 -2675 0 - -807 -2903 0 - -807 -3893 0 - -807 -4210 0 - -1596 -2675 0 - -1596 -2903 0 - -1596 -3893 0 - -1596 -4210 0 - -2675 -2903 0 - -2675 -3893 0 - -2675 -4210 0 - -2903 -3893 0 - -2903 -4210 0 - -3893 -4210 0 - -560 -679 0 - -560 -1547 0 - -560 -1988 0 - -560 -2609 0 - -560 -2904 0 - -560 -3002 0 - -560 -3609 0 - -560 -4087 0 - -679 -1547 0 - -679 -1988 0 - -679 -2609 0 - -679 -2904 0 - -679 -3002 0 - -679 -3609 0 - -679 -4087 0 - -1547 -1988 0 - -1547 -2609 0 - -1547 -2904 0 - -1547 -3002 0 - -1547 -3609 0 - -1547 -4087 0 - -1988 -2609 0 - -1988 -2904 0 - -1988 -3002 0 - -1988 -3609 0 - -1988 -4087 0 - -2609 -2904 0 - -2609 -3002 0 - -2609 -3609 0 - -2609 -4087 0 - -2904 -3002 0 - -2904 -3609 0 - -2904 -4087 0 - -3002 -3609 0 - -3002 -4087 0 - -3609 -4087 0 - -561 -808 0 - -561 -1548 0 - -561 -1597 0 - -561 -2325 0 - -561 -3003 0 - -561 -3610 0 - -561 -4211 0 - -808 -1548 0 - -808 -1597 0 - -808 -2325 0 - -808 -3003 0 - -808 -3610 0 - -808 -4211 0 - -1548 -1597 0 - -1548 -2325 0 - -1548 -3003 0 - -1548 -3610 0 - -1548 -4211 0 - -1597 -2325 0 - -1597 -3003 0 - -1597 -3610 0 - -1597 -4211 0 - -2325 -3003 0 - -2325 -3610 0 - -2325 -4211 0 - -3003 -3610 0 - -3003 -4211 0 - -3610 -4211 0 - -1549 -1598 0 - -1549 -2676 0 - -1549 -2780 0 - -1549 -3190 0 - -1549 -3894 0 - -1549 -4212 0 - -1549 -4397 0 - -1598 -2676 0 - -1598 -2780 0 - -1598 -3190 0 - -1598 -3894 0 - -1598 -4212 0 - -1598 -4397 0 - -2676 -2780 0 - -2676 -3190 0 - -2676 -3894 0 - -2676 -4212 0 - -2676 -4397 0 - -2780 -3190 0 - -2780 -3894 0 - -2780 -4212 0 - -2780 -4397 0 - -3190 -3894 0 - -3190 -4212 0 - -3190 -4397 0 - -3894 -4212 0 - -3894 -4397 0 - -4212 -4397 0 - -562 -680 0 - -562 -809 0 - -562 -1191 0 - -562 -1550 0 - -562 -1599 0 - -562 -2150 0 - -562 -2905 0 - -562 -4398 0 - -680 -809 0 - -680 -1191 0 - -680 -1550 0 - -680 -1599 0 - -680 -2150 0 - -680 -2905 0 - -680 -4398 0 - -809 -1191 0 - -809 -1550 0 - -809 -1599 0 - -809 -2150 0 - -809 -2905 0 - -809 -4398 0 - -1191 -1550 0 - -1191 -1599 0 - -1191 -2150 0 - -1191 -2905 0 - -1191 -4398 0 - -1550 -1599 0 - -1550 -2150 0 - -1550 -2905 0 - -1550 -4398 0 - -1599 -2150 0 - -1599 -2905 0 - -1599 -4398 0 - -2150 -2905 0 - -2150 -4398 0 - -2905 -4398 0 - -1044 -2151 0 - -1044 -2326 0 - -1044 -2677 0 - -1044 -2781 0 - -1044 -2906 0 - -1044 -3004 0 - -1044 -3191 0 - -1044 -3895 0 - -1044 -4213 0 - -2151 -2326 0 - -2151 -2677 0 - -2151 -2781 0 - -2151 -2906 0 - -2151 -3004 0 - -2151 -3191 0 - -2151 -3895 0 - -2151 -4213 0 - -2326 -2677 0 - -2326 -2781 0 - -2326 -2906 0 - -2326 -3004 0 - -2326 -3191 0 - -2326 -3895 0 - -2326 -4213 0 - -2677 -2781 0 - -2677 -2906 0 - -2677 -3004 0 - -2677 -3191 0 - -2677 -3895 0 - -2677 -4213 0 - -2781 -2906 0 - -2781 -3004 0 - -2781 -3191 0 - -2781 -3895 0 - -2781 -4213 0 - -2906 -3004 0 - -2906 -3191 0 - -2906 -3895 0 - -2906 -4213 0 - -3004 -3191 0 - -3004 -3895 0 - -3004 -4213 0 - -3191 -3895 0 - -3191 -4213 0 - -3895 -4213 0 - -810 -1192 0 - -810 -1551 0 - -810 -2152 0 - -810 -2678 0 - -810 -2907 0 - -810 -3192 0 - -810 -3896 0 - -810 -4214 0 - -810 -4399 0 - -1192 -1551 0 - -1192 -2152 0 - -1192 -2678 0 - -1192 -2907 0 - -1192 -3192 0 - -1192 -3896 0 - -1192 -4214 0 - -1192 -4399 0 - -1551 -2152 0 - -1551 -2678 0 - -1551 -2907 0 - -1551 -3192 0 - -1551 -3896 0 - -1551 -4214 0 - -1551 -4399 0 - -2152 -2678 0 - -2152 -2907 0 - -2152 -3192 0 - -2152 -3896 0 - -2152 -4214 0 - -2152 -4399 0 - -2678 -2907 0 - -2678 -3192 0 - -2678 -3896 0 - -2678 -4214 0 - -2678 -4399 0 - -2907 -3192 0 - -2907 -3896 0 - -2907 -4214 0 - -2907 -4399 0 - -3192 -3896 0 - -3192 -4214 0 - -3192 -4399 0 - -3896 -4214 0 - -3896 -4399 0 - -4214 -4399 0 - -563 -681 0 - -563 -1552 0 - -563 -1600 0 - -563 -2610 0 - -563 -2679 0 - -563 -3005 0 - -563 -3193 0 - -563 -3897 0 - -563 -4088 0 - -563 -4400 0 - -681 -1552 0 - -681 -1600 0 - -681 -2610 0 - -681 -2679 0 - -681 -3005 0 - -681 -3193 0 - -681 -3897 0 - -681 -4088 0 - -681 -4400 0 - -1552 -1600 0 - -1552 -2610 0 - -1552 -2679 0 - -1552 -3005 0 - -1552 -3193 0 - -1552 -3897 0 - -1552 -4088 0 - -1552 -4400 0 - -1600 -2610 0 - -1600 -2679 0 - -1600 -3005 0 - -1600 -3193 0 - -1600 -3897 0 - -1600 -4088 0 - -1600 -4400 0 - -2610 -2679 0 - -2610 -3005 0 - -2610 -3193 0 - -2610 -3897 0 - -2610 -4088 0 - -2610 -4400 0 - -2679 -3005 0 - -2679 -3193 0 - -2679 -3897 0 - -2679 -4088 0 - -2679 -4400 0 - -3005 -3193 0 - -3005 -3897 0 - -3005 -4088 0 - -3005 -4400 0 - -3193 -3897 0 - -3193 -4088 0 - -3193 -4400 0 - -3897 -4088 0 - -3897 -4400 0 - -4088 -4400 0 - -564 -682 0 - -564 -811 0 - -564 -1045 0 - -564 -1989 0 - -564 -2680 0 - -564 -2782 0 - -564 -3611 0 - -564 -4089 0 - -564 -4401 0 - -682 -811 0 - -682 -1045 0 - -682 -1989 0 - -682 -2680 0 - -682 -2782 0 - -682 -3611 0 - -682 -4089 0 - -682 -4401 0 - -811 -1045 0 - -811 -1989 0 - -811 -2680 0 - -811 -2782 0 - -811 -3611 0 - -811 -4089 0 - -811 -4401 0 - -1045 -1989 0 - -1045 -2680 0 - -1045 -2782 0 - -1045 -3611 0 - -1045 -4089 0 - -1045 -4401 0 - -1989 -2680 0 - -1989 -2782 0 - -1989 -3611 0 - -1989 -4089 0 - -1989 -4401 0 - -2680 -2782 0 - -2680 -3611 0 - -2680 -4089 0 - -2680 -4401 0 - -2782 -3611 0 - -2782 -4089 0 - -2782 -4401 0 - -3611 -4089 0 - -3611 -4401 0 - -4089 -4401 0 - -812 -1553 0 - -812 -2153 0 - -812 -2783 0 - -812 -3006 0 - -812 -4215 0 - -1553 -2153 0 - -1553 -2783 0 - -1553 -3006 0 - -1553 -4215 0 - -2153 -2783 0 - -2153 -3006 0 - -2153 -4215 0 - -2783 -3006 0 - -2783 -4215 0 - -3006 -4215 0 - -683 -1193 0 - -683 -2908 0 - -683 -4090 0 - -1193 -2908 0 - -1193 -4090 0 - -2908 -4090 0 - -565 -1194 0 - -565 -1554 0 - -565 -2327 0 - -565 -3194 0 - -565 -4091 0 - -1194 -1554 0 - -1194 -2327 0 - -1194 -3194 0 - -1194 -4091 0 - -1554 -2327 0 - -1554 -3194 0 - -1554 -4091 0 - -2327 -3194 0 - -2327 -4091 0 - -3194 -4091 0 - -684 -1195 0 - -684 -2328 0 - -684 -2611 0 - -684 -2784 0 - -684 -3195 0 - -1195 -2328 0 - -1195 -2611 0 - -1195 -2784 0 - -1195 -3195 0 - -2328 -2611 0 - -2328 -2784 0 - -2328 -3195 0 - -2611 -2784 0 - -2611 -3195 0 - -2784 -3195 0 - -566 -685 0 - -566 -813 0 - -566 -1196 0 - -566 -1990 0 - -566 -2329 0 - -566 -2909 0 - -566 -3007 0 - -566 -3196 0 - -566 -3612 0 - -566 -3898 0 - -685 -813 0 - -685 -1196 0 - -685 -1990 0 - -685 -2329 0 - -685 -2909 0 - -685 -3007 0 - -685 -3196 0 - -685 -3612 0 - -685 -3898 0 - -813 -1196 0 - -813 -1990 0 - -813 -2329 0 - -813 -2909 0 - -813 -3007 0 - -813 -3196 0 - -813 -3612 0 - -813 -3898 0 - -1196 -1990 0 - -1196 -2329 0 - -1196 -2909 0 - -1196 -3007 0 - -1196 -3196 0 - -1196 -3612 0 - -1196 -3898 0 - -1990 -2329 0 - -1990 -2909 0 - -1990 -3007 0 - -1990 -3196 0 - -1990 -3612 0 - -1990 -3898 0 - -2329 -2909 0 - -2329 -3007 0 - -2329 -3196 0 - -2329 -3612 0 - -2329 -3898 0 - -2909 -3007 0 - -2909 -3196 0 - -2909 -3612 0 - -2909 -3898 0 - -3007 -3196 0 - -3007 -3612 0 - -3007 -3898 0 - -3196 -3612 0 - -3196 -3898 0 - -3612 -3898 0 - -686 -814 0 - -686 -900 0 - -686 -3197 0 - -686 -4303 0 - -686 -4402 0 - -814 -900 0 - -814 -3197 0 - -814 -4303 0 - -814 -4402 0 - -900 -3197 0 - -900 -4303 0 - -900 -4402 0 - -3197 -4303 0 - -3197 -4402 0 - -4303 -4402 0 - -815 -1857 0 - -815 -1991 0 - -815 -3440 0 - -815 -4092 0 - -815 -4403 0 - -1857 -1991 0 - -1857 -3440 0 - -1857 -4092 0 - -1857 -4403 0 - -1991 -3440 0 - -1991 -4092 0 - -1991 -4403 0 - -3440 -4092 0 - -3440 -4403 0 - -4092 -4403 0 - -77 -1197 0 - -77 -1601 0 - -77 -1858 0 - -77 -2260 0 - -77 -3198 0 - -1197 -1601 0 - -1197 -1858 0 - -1197 -2260 0 - -1197 -3198 0 - -1601 -1858 0 - -1601 -2260 0 - -1601 -3198 0 - -1858 -2260 0 - -1858 -3198 0 - -2260 -3198 0 - -687 -816 0 - -687 -1602 0 - -687 -3441 0 - -687 -3779 0 - -816 -1602 0 - -816 -3441 0 - -816 -3779 0 - -1602 -3441 0 - -1602 -3779 0 - -3441 -3779 0 - -78 -688 0 - -78 -901 0 - -78 -1992 0 - -78 -2261 0 - -78 -3442 0 - -78 -3780 0 - -78 -4093 0 - -78 -4304 0 - -688 -901 0 - -688 -1992 0 - -688 -2261 0 - -688 -3442 0 - -688 -3780 0 - -688 -4093 0 - -688 -4304 0 - -901 -1992 0 - -901 -2261 0 - -901 -3442 0 - -901 -3780 0 - -901 -4093 0 - -901 -4304 0 - -1992 -2261 0 - -1992 -3442 0 - -1992 -3780 0 - -1992 -4093 0 - -1992 -4304 0 - -2261 -3442 0 - -2261 -3780 0 - -2261 -4093 0 - -2261 -4304 0 - -3442 -3780 0 - -3442 -4093 0 - -3442 -4304 0 - -3780 -4093 0 - -3780 -4304 0 - -4093 -4304 0 - -817 -1603 0 - -817 -3443 0 - -1603 -3443 0 - -818 -902 0 - -818 -3199 0 - -818 -3444 0 - -818 -3781 0 - -818 -4404 0 - -902 -3199 0 - -902 -3444 0 - -902 -3781 0 - -902 -4404 0 - -3199 -3444 0 - -3199 -3781 0 - -3199 -4404 0 - -3444 -3781 0 - -3444 -4404 0 - -3781 -4404 0 - -79 -1604 0 - -79 -3200 0 - -79 -3445 0 - -79 -3782 0 - -79 -4405 0 - -1604 -3200 0 - -1604 -3445 0 - -1604 -3782 0 - -1604 -4405 0 - -3200 -3445 0 - -3200 -3782 0 - -3200 -4405 0 - -3445 -3782 0 - -3445 -4405 0 - -3782 -4405 0 - -3201 -3446 0 - -1198 -2262 0 - -1198 -3783 0 - -1198 -4094 0 - -1198 -4305 0 - -1198 -4406 0 - -2262 -3783 0 - -2262 -4094 0 - -2262 -4305 0 - -2262 -4406 0 - -3783 -4094 0 - -3783 -4305 0 - -3783 -4406 0 - -4094 -4305 0 - -4094 -4406 0 - -4305 -4406 0 - -1199 -1993 0 - -1199 -3202 0 - -1199 -4407 0 - -1993 -3202 0 - -1993 -4407 0 - -3202 -4407 0 - -819 -1605 0 - -819 -1994 0 - -819 -2263 0 - -819 -3203 0 - -819 -3784 0 - -819 -4306 0 - -1605 -1994 0 - -1605 -2263 0 - -1605 -3203 0 - -1605 -3784 0 - -1605 -4306 0 - -1994 -2263 0 - -1994 -3203 0 - -1994 -3784 0 - -1994 -4306 0 - -2263 -3203 0 - -2263 -3784 0 - -2263 -4306 0 - -3203 -3784 0 - -3203 -4306 0 - -3784 -4306 0 - -80 -820 0 - -80 -1200 0 - -80 -1859 0 - -80 -3204 0 - -80 -4307 0 - -820 -1200 0 - -820 -1859 0 - -820 -3204 0 - -820 -4307 0 - -1200 -1859 0 - -1200 -3204 0 - -1200 -4307 0 - -1859 -3204 0 - -1859 -4307 0 - -3204 -4307 0 - -689 -821 0 - -689 -1995 0 - -689 -2264 0 - -821 -1995 0 - -821 -2264 0 - -1995 -2264 0 - -690 -822 0 - -690 -1201 0 - -690 -1860 0 - -690 -1996 0 - -690 -2265 0 - -690 -3205 0 - -690 -3785 0 - -822 -1201 0 - -822 -1860 0 - -822 -1996 0 - -822 -2265 0 - -822 -3205 0 - -822 -3785 0 - -1201 -1860 0 - -1201 -1996 0 - -1201 -2265 0 - -1201 -3205 0 - -1201 -3785 0 - -1860 -1996 0 - -1860 -2265 0 - -1860 -3205 0 - -1860 -3785 0 - -1996 -2265 0 - -1996 -3205 0 - -1996 -3785 0 - -2265 -3205 0 - -2265 -3785 0 - -3205 -3785 0 - -81 -336 0 - -81 -823 0 - -81 -1861 0 - -81 -1997 0 - -81 -2330 0 - -81 -2785 0 - -81 -2910 0 - -336 -823 0 - -336 -1861 0 - -336 -1997 0 - -336 -2330 0 - -336 -2785 0 - -336 -2910 0 - -823 -1861 0 - -823 -1997 0 - -823 -2330 0 - -823 -2785 0 - -823 -2910 0 - -1861 -1997 0 - -1861 -2330 0 - -1861 -2785 0 - -1861 -2910 0 - -1997 -2330 0 - -1997 -2785 0 - -1997 -2910 0 - -2330 -2785 0 - -2330 -2910 0 - -2785 -2910 0 - -337 -1862 0 - -337 -2154 0 - -337 -2532 0 - -337 -2612 0 - -337 -2911 0 - -337 -3786 0 - -337 -3899 0 - -1862 -2154 0 - -1862 -2532 0 - -1862 -2612 0 - -1862 -2911 0 - -1862 -3786 0 - -1862 -3899 0 - -2154 -2532 0 - -2154 -2612 0 - -2154 -2911 0 - -2154 -3786 0 - -2154 -3899 0 - -2532 -2612 0 - -2532 -2911 0 - -2532 -3786 0 - -2532 -3899 0 - -2612 -2911 0 - -2612 -3786 0 - -2612 -3899 0 - -2911 -3786 0 - -2911 -3899 0 - -3786 -3899 0 - -824 -1360 0 - -824 -1863 0 - -824 -1998 0 - -824 -2155 0 - -824 -2533 0 - -824 -2613 0 - -824 -2681 0 - -824 -2786 0 - -824 -3900 0 - -1360 -1863 0 - -1360 -1998 0 - -1360 -2155 0 - -1360 -2533 0 - -1360 -2613 0 - -1360 -2681 0 - -1360 -2786 0 - -1360 -3900 0 - -1863 -1998 0 - -1863 -2155 0 - -1863 -2533 0 - -1863 -2613 0 - -1863 -2681 0 - -1863 -2786 0 - -1863 -3900 0 - -1998 -2155 0 - -1998 -2533 0 - -1998 -2613 0 - -1998 -2681 0 - -1998 -2786 0 - -1998 -3900 0 - -2155 -2533 0 - -2155 -2613 0 - -2155 -2681 0 - -2155 -2786 0 - -2155 -3900 0 - -2533 -2613 0 - -2533 -2681 0 - -2533 -2786 0 - -2533 -3900 0 - -2613 -2681 0 - -2613 -2786 0 - -2613 -3900 0 - -2681 -2786 0 - -2681 -3900 0 - -2786 -3900 0 - -82 -1864 0 - -82 -1999 0 - -82 -2156 0 - -82 -2614 0 - -82 -2787 0 - -82 -2912 0 - -82 -3787 0 - -82 -3901 0 - -1864 -1999 0 - -1864 -2156 0 - -1864 -2614 0 - -1864 -2787 0 - -1864 -2912 0 - -1864 -3787 0 - -1864 -3901 0 - -1999 -2156 0 - -1999 -2614 0 - -1999 -2787 0 - -1999 -2912 0 - -1999 -3787 0 - -1999 -3901 0 - -2156 -2614 0 - -2156 -2787 0 - -2156 -2912 0 - -2156 -3787 0 - -2156 -3901 0 - -2614 -2787 0 - -2614 -2912 0 - -2614 -3787 0 - -2614 -3901 0 - -2787 -2912 0 - -2787 -3787 0 - -2787 -3901 0 - -2912 -3787 0 - -2912 -3901 0 - -3787 -3901 0 - -83 -1282 0 - -83 -1865 0 - -83 -2157 0 - -83 -2534 0 - -83 -2788 0 - -1282 -1865 0 - -1282 -2157 0 - -1282 -2534 0 - -1282 -2788 0 - -1865 -2157 0 - -1865 -2534 0 - -1865 -2788 0 - -2157 -2534 0 - -2157 -2788 0 - -2534 -2788 0 - -338 -1866 0 - -338 -2000 0 - -338 -2615 0 - -338 -3788 0 - -1866 -2000 0 - -1866 -2615 0 - -1866 -3788 0 - -2000 -2615 0 - -2000 -3788 0 - -2615 -3788 0 - -84 -2001 0 - -84 -2535 0 - -84 -2616 0 - -84 -2913 0 - -84 -3789 0 - -2001 -2535 0 - -2001 -2616 0 - -2001 -2913 0 - -2001 -3789 0 - -2535 -2616 0 - -2535 -2913 0 - -2535 -3789 0 - -2616 -2913 0 - -2616 -3789 0 - -2913 -3789 0 - -85 -339 0 - -85 -1283 0 - -85 -2617 0 - -85 -3790 0 - -85 -3902 0 - -339 -1283 0 - -339 -2617 0 - -339 -3790 0 - -339 -3902 0 - -1283 -2617 0 - -1283 -3790 0 - -1283 -3902 0 - -2617 -3790 0 - -2617 -3902 0 - -3790 -3902 0 - -86 -825 0 - -86 -1284 0 - -86 -1867 0 - -86 -2158 0 - -86 -2914 0 - -86 -3791 0 - -825 -1284 0 - -825 -1867 0 - -825 -2158 0 - -825 -2914 0 - -825 -3791 0 - -1284 -1867 0 - -1284 -2158 0 - -1284 -2914 0 - -1284 -3791 0 - -1867 -2158 0 - -1867 -2914 0 - -1867 -3791 0 - -2158 -2914 0 - -2158 -3791 0 - -2914 -3791 0 - -87 -826 0 - -87 -2002 0 - -87 -2682 0 - -87 -2789 0 - -87 -3792 0 - -826 -2002 0 - -826 -2682 0 - -826 -2789 0 - -826 -3792 0 - -2002 -2682 0 - -2002 -2789 0 - -2002 -3792 0 - -2682 -2789 0 - -2682 -3792 0 - -2789 -3792 0 - -340 -827 0 - -340 -1285 0 - -340 -1361 0 - -340 -2003 0 - -340 -2536 0 - -340 -2790 0 - -340 -3793 0 - -340 -3903 0 - -827 -1285 0 - -827 -1361 0 - -827 -2003 0 - -827 -2536 0 - -827 -2790 0 - -827 -3793 0 - -827 -3903 0 - -1285 -1361 0 - -1285 -2003 0 - -1285 -2536 0 - -1285 -2790 0 - -1285 -3793 0 - -1285 -3903 0 - -1361 -2003 0 - -1361 -2536 0 - -1361 -2790 0 - -1361 -3793 0 - -1361 -3903 0 - -2003 -2536 0 - -2003 -2790 0 - -2003 -3793 0 - -2003 -3903 0 - -2536 -2790 0 - -2536 -3793 0 - -2536 -3903 0 - -2790 -3793 0 - -2790 -3903 0 - -3793 -3903 0 - -88 -828 0 - -88 -1868 0 - -88 -2537 0 - -828 -1868 0 - -828 -2537 0 - -1868 -2537 0 - -829 -1286 0 - -829 -1362 0 - -829 -2331 0 - -829 -3904 0 - -1286 -1362 0 - -1286 -2331 0 - -1286 -3904 0 - -1362 -2331 0 - -1362 -3904 0 - -2331 -3904 0 - -341 -830 0 - -341 -1287 0 - -341 -2004 0 - -341 -2159 0 - -341 -2538 0 - -341 -2791 0 - -341 -2915 0 - -830 -1287 0 - -830 -2004 0 - -830 -2159 0 - -830 -2538 0 - -830 -2791 0 - -830 -2915 0 - -1287 -2004 0 - -1287 -2159 0 - -1287 -2538 0 - -1287 -2791 0 - -1287 -2915 0 - -2004 -2159 0 - -2004 -2538 0 - -2004 -2791 0 - -2004 -2915 0 - -2159 -2538 0 - -2159 -2791 0 - -2159 -2915 0 - -2538 -2791 0 - -2538 -2915 0 - -2791 -2915 0 - -342 -1869 0 - -342 -2005 0 - -342 -2160 0 - -342 -2332 0 - -342 -2618 0 - -342 -2683 0 - -342 -2792 0 - -342 -2916 0 - -1869 -2005 0 - -1869 -2160 0 - -1869 -2332 0 - -1869 -2618 0 - -1869 -2683 0 - -1869 -2792 0 - -1869 -2916 0 - -2005 -2160 0 - -2005 -2332 0 - -2005 -2618 0 - -2005 -2683 0 - -2005 -2792 0 - -2005 -2916 0 - -2160 -2332 0 - -2160 -2618 0 - -2160 -2683 0 - -2160 -2792 0 - -2160 -2916 0 - -2332 -2618 0 - -2332 -2683 0 - -2332 -2792 0 - -2332 -2916 0 - -2618 -2683 0 - -2618 -2792 0 - -2618 -2916 0 - -2683 -2792 0 - -2683 -2916 0 - -2792 -2916 0 - -89 -343 0 - -89 -1288 0 - -89 -1870 0 - -89 -2006 0 - -89 -2161 0 - -89 -2333 0 - -89 -2684 0 - -343 -1288 0 - -343 -1870 0 - -343 -2006 0 - -343 -2161 0 - -343 -2333 0 - -343 -2684 0 - -1288 -1870 0 - -1288 -2006 0 - -1288 -2161 0 - -1288 -2333 0 - -1288 -2684 0 - -1870 -2006 0 - -1870 -2161 0 - -1870 -2333 0 - -1870 -2684 0 - -2006 -2161 0 - -2006 -2333 0 - -2006 -2684 0 - -2161 -2333 0 - -2161 -2684 0 - -2333 -2684 0 - -90 -2793 0 - -90 -3447 0 - -90 -4408 0 - -2793 -3447 0 - -2793 -4408 0 - -3447 -4408 0 - -91 -1202 0 - -91 -2794 0 - -91 -3206 0 - -91 -3448 0 - -91 -3794 0 - -91 -4216 0 - -1202 -2794 0 - -1202 -3206 0 - -1202 -3448 0 - -1202 -3794 0 - -1202 -4216 0 - -2794 -3206 0 - -2794 -3448 0 - -2794 -3794 0 - -2794 -4216 0 - -3206 -3448 0 - -3206 -3794 0 - -3206 -4216 0 - -3448 -3794 0 - -3448 -4216 0 - -3794 -4216 0 - -567 -2539 0 - -567 -3449 0 - -567 -4217 0 - -2539 -3449 0 - -2539 -4217 0 - -3449 -4217 0 - -436 -568 0 - -436 -2540 0 - -436 -2619 0 - -436 -3207 0 - -436 -3297 0 - -436 -3450 0 - -436 -3795 0 - -436 -4409 0 - -568 -2540 0 - -568 -2619 0 - -568 -3207 0 - -568 -3297 0 - -568 -3450 0 - -568 -3795 0 - -568 -4409 0 - -2540 -2619 0 - -2540 -3207 0 - -2540 -3297 0 - -2540 -3450 0 - -2540 -3795 0 - -2540 -4409 0 - -2619 -3207 0 - -2619 -3297 0 - -2619 -3450 0 - -2619 -3795 0 - -2619 -4409 0 - -3207 -3297 0 - -3207 -3450 0 - -3207 -3795 0 - -3207 -4409 0 - -3297 -3450 0 - -3297 -3795 0 - -3297 -4409 0 - -3450 -3795 0 - -3450 -4409 0 - -3795 -4409 0 - -92 -2795 0 - -92 -3208 0 - -92 -3451 0 - -92 -3796 0 - -92 -4218 0 - -92 -4410 0 - -2795 -3208 0 - -2795 -3451 0 - -2795 -3796 0 - -2795 -4218 0 - -2795 -4410 0 - -3208 -3451 0 - -3208 -3796 0 - -3208 -4218 0 - -3208 -4410 0 - -3451 -3796 0 - -3451 -4218 0 - -3451 -4410 0 - -3796 -4218 0 - -3796 -4410 0 - -4218 -4410 0 - -93 -437 0 - -93 -1203 0 - -93 -2541 0 - -93 -3209 0 - -93 -3298 0 - -93 -4219 0 - -93 -4411 0 - -437 -1203 0 - -437 -2541 0 - -437 -3209 0 - -437 -3298 0 - -437 -4219 0 - -437 -4411 0 - -1203 -2541 0 - -1203 -3209 0 - -1203 -3298 0 - -1203 -4219 0 - -1203 -4411 0 - -2541 -3209 0 - -2541 -3298 0 - -2541 -4219 0 - -2541 -4411 0 - -3209 -3298 0 - -3209 -4219 0 - -3209 -4411 0 - -3298 -4219 0 - -3298 -4411 0 - -4219 -4411 0 - -569 -2620 0 - -569 -3210 0 - -569 -3299 0 - -569 -3797 0 - -569 -4412 0 - -2620 -3210 0 - -2620 -3299 0 - -2620 -3797 0 - -2620 -4412 0 - -3210 -3299 0 - -3210 -3797 0 - -3210 -4412 0 - -3299 -3797 0 - -3299 -4412 0 - -3797 -4412 0 - -570 -1204 0 - -570 -2542 0 - -570 -2621 0 - -570 -2796 0 - -570 -3798 0 - -570 -4220 0 - -570 -4413 0 - -1204 -2542 0 - -1204 -2621 0 - -1204 -2796 0 - -1204 -3798 0 - -1204 -4220 0 - -1204 -4413 0 - -2542 -2621 0 - -2542 -2796 0 - -2542 -3798 0 - -2542 -4220 0 - -2542 -4413 0 - -2621 -2796 0 - -2621 -3798 0 - -2621 -4220 0 - -2621 -4413 0 - -2796 -3798 0 - -2796 -4220 0 - -2796 -4413 0 - -3798 -4220 0 - -3798 -4413 0 - -4220 -4413 0 - -438 -2543 0 - -438 -2797 0 - -438 -3211 0 - -438 -3799 0 - -2543 -2797 0 - -2543 -3211 0 - -2543 -3799 0 - -2797 -3211 0 - -2797 -3799 0 - -3211 -3799 0 - -439 -571 0 - -1205 -3452 0 - -1205 -3800 0 - -3452 -3800 0 - -94 -1206 0 - -94 -2544 0 - -94 -3801 0 - -94 -4221 0 - -94 -4414 0 - -1206 -2544 0 - -1206 -3801 0 - -1206 -4221 0 - -1206 -4414 0 - -2544 -3801 0 - -2544 -4221 0 - -2544 -4414 0 - -3801 -4221 0 - -3801 -4414 0 - -4221 -4414 0 - -440 -2622 0 - -440 -2798 0 - -440 -3453 0 - -440 -4222 0 - -2622 -2798 0 - -2622 -3453 0 - -2622 -4222 0 - -2798 -3453 0 - -2798 -4222 0 - -3453 -4222 0 - -18 -33 0 - -18 -46 0 - -18 -54 0 - -18 -81 0 - -18 -90 0 - -33 -46 0 - -33 -54 0 - -33 -81 0 - -33 -90 0 - -46 -54 0 - -46 -81 0 - -46 -90 0 - -54 -81 0 - -54 -90 0 - -81 -90 0 - -1 -12 0 - -1 -19 0 - -1 -24 0 - -1 -34 0 - -1 -39 0 - -1 -47 0 - -1 -59 0 - -1 -69 0 - -1 -91 0 - -12 -19 0 - -12 -24 0 - -12 -34 0 - -12 -39 0 - -12 -47 0 - -12 -59 0 - -12 -69 0 - -12 -91 0 - -19 -24 0 - -19 -34 0 - -19 -39 0 - -19 -47 0 - -19 -59 0 - -19 -69 0 - -19 -91 0 - -24 -34 0 - -24 -39 0 - -24 -47 0 - -24 -59 0 - -24 -69 0 - -24 -91 0 - -34 -39 0 - -34 -47 0 - -34 -59 0 - -34 -69 0 - -34 -91 0 - -39 -47 0 - -39 -59 0 - -39 -69 0 - -39 -91 0 - -47 -59 0 - -47 -69 0 - -47 -91 0 - -59 -69 0 - -59 -91 0 - -69 -91 0 - -2 -20 0 - -2 -25 0 - -2 -60 0 - -2 -70 0 - -2 -82 0 - -20 -25 0 - -20 -60 0 - -20 -70 0 - -20 -82 0 - -25 -60 0 - -25 -70 0 - -25 -82 0 - -60 -70 0 - -60 -82 0 - -70 -82 0 - -3 -21 0 - -3 -26 0 - -3 -48 0 - -3 -77 0 - -3 -83 0 - -21 -26 0 - -21 -48 0 - -21 -77 0 - -21 -83 0 - -26 -48 0 - -26 -77 0 - -26 -83 0 - -48 -77 0 - -48 -83 0 - -77 -83 0 - -13 -35 0 - -13 -40 0 - -13 -49 0 - -13 -71 0 - -13 -78 0 - -13 -84 0 - -35 -40 0 - -35 -49 0 - -35 -71 0 - -35 -78 0 - -35 -84 0 - -40 -49 0 - -40 -71 0 - -40 -78 0 - -40 -84 0 - -49 -71 0 - -49 -78 0 - -49 -84 0 - -71 -78 0 - -71 -84 0 - -78 -84 0 - -4 -27 0 - -4 -36 0 - -4 -41 0 - -4 -72 0 - -4 -79 0 - -4 -92 0 - -27 -36 0 - -27 -41 0 - -27 -72 0 - -27 -79 0 - -27 -92 0 - -36 -41 0 - -36 -72 0 - -36 -79 0 - -36 -92 0 - -41 -72 0 - -41 -79 0 - -41 -92 0 - -72 -79 0 - -72 -92 0 - -79 -92 0 - -14 -28 0 - -14 -37 0 - -14 -42 0 - -14 -61 0 - -14 -65 0 - -14 -85 0 - -28 -37 0 - -28 -42 0 - -28 -61 0 - -28 -65 0 - -28 -85 0 - -37 -42 0 - -37 -61 0 - -37 -65 0 - -37 -85 0 - -42 -61 0 - -42 -65 0 - -42 -85 0 - -61 -65 0 - -61 -85 0 - -65 -85 0 - -5 -29 0 - -5 -50 0 - -5 -62 0 - -5 -66 0 - -5 -86 0 - -29 -50 0 - -29 -62 0 - -29 -66 0 - -29 -86 0 - -50 -62 0 - -50 -66 0 - -50 -86 0 - -62 -66 0 - -62 -86 0 - -66 -86 0 - -6 -15 0 - -6 -30 0 - -6 -55 0 - -6 -73 0 - -6 -93 0 - -15 -30 0 - -15 -55 0 - -15 -73 0 - -15 -93 0 - -30 -55 0 - -30 -73 0 - -30 -93 0 - -55 -73 0 - -55 -93 0 - -73 -93 0 - -7 -22 0 - -7 -51 0 - -7 -56 0 - -7 -67 0 - -7 -74 0 - -7 -87 0 - -22 -51 0 - -22 -56 0 - -22 -67 0 - -22 -74 0 - -22 -87 0 - -51 -56 0 - -51 -67 0 - -51 -74 0 - -51 -87 0 - -56 -67 0 - -56 -74 0 - -56 -87 0 - -67 -74 0 - -67 -87 0 - -74 -87 0 - -8 -43 0 - -8 -80 0 - -8 -88 0 - -43 -80 0 - -43 -88 0 - -80 -88 0 - -9 -23 0 - -9 -38 0 - -9 -52 0 - -9 -57 0 - -9 -63 0 - -9 -68 0 - -9 -75 0 - -23 -38 0 - -23 -52 0 - -23 -57 0 - -23 -63 0 - -23 -68 0 - -23 -75 0 - -38 -52 0 - -38 -57 0 - -38 -63 0 - -38 -68 0 - -38 -75 0 - -52 -57 0 - -52 -63 0 - -52 -68 0 - -52 -75 0 - -57 -63 0 - -57 -68 0 - -57 -75 0 - -63 -68 0 - -63 -75 0 - -68 -75 0 - -10 -16 0 - -10 -31 0 - -10 -44 0 - -10 -53 0 - -10 -58 0 - -10 -94 0 - -16 -31 0 - -16 -44 0 - -16 -53 0 - -16 -58 0 - -16 -94 0 - -31 -44 0 - -31 -53 0 - -31 -58 0 - -31 -94 0 - -44 -53 0 - -44 -58 0 - -44 -94 0 - -53 -58 0 - -53 -94 0 - -58 -94 0 - -11 -17 0 - -11 -32 0 - -11 -45 0 - -11 -64 0 - -11 -76 0 - -11 -89 0 - -17 -32 0 - -17 -45 0 - -17 -64 0 - -17 -76 0 - -17 -89 0 - -32 -45 0 - -32 -64 0 - -32 -76 0 - -32 -89 0 - -45 -64 0 - -45 -76 0 - -45 -89 0 - -64 -76 0 - -64 -89 0 - -76 -89 0 - -95 -107 0 - -95 -128 0 - -95 -160 0 - -95 -167 0 - -95 -175 0 - -95 -180 0 - -95 -190 0 - -95 -198 0 - -95 -211 0 - -95 -228 0 - -107 -128 0 - -107 -160 0 - -107 -167 0 - -107 -175 0 - -107 -180 0 - -107 -190 0 - -107 -198 0 - -107 -211 0 - -107 -228 0 - -128 -160 0 - -128 -167 0 - -128 -175 0 - -128 -180 0 - -128 -190 0 - -128 -198 0 - -128 -211 0 - -128 -228 0 - -160 -167 0 - -160 -175 0 - -160 -180 0 - -160 -190 0 - -160 -198 0 - -160 -211 0 - -160 -228 0 - -167 -175 0 - -167 -180 0 - -167 -190 0 - -167 -198 0 - -167 -211 0 - -167 -228 0 - -175 -180 0 - -175 -190 0 - -175 -198 0 - -175 -211 0 - -175 -228 0 - -180 -190 0 - -180 -198 0 - -180 -211 0 - -180 -228 0 - -190 -198 0 - -190 -211 0 - -190 -228 0 - -198 -211 0 - -198 -228 0 - -211 -228 0 - -108 -153 0 - -108 -168 0 - -108 -181 0 - -108 -199 0 - -108 -219 0 - -108 -229 0 - -153 -168 0 - -153 -181 0 - -153 -199 0 - -153 -219 0 - -153 -229 0 - -168 -181 0 - -168 -199 0 - -168 -219 0 - -168 -229 0 - -181 -199 0 - -181 -219 0 - -181 -229 0 - -199 -219 0 - -199 -229 0 - -219 -229 0 - -96 -120 0 - -96 -169 0 - -96 -182 0 - -96 -191 0 - -96 -200 0 - -96 -212 0 - -96 -220 0 - -120 -169 0 - -120 -182 0 - -120 -191 0 - -120 -200 0 - -120 -212 0 - -120 -220 0 - -169 -182 0 - -169 -191 0 - -169 -200 0 - -169 -212 0 - -169 -220 0 - -182 -191 0 - -182 -200 0 - -182 -212 0 - -182 -220 0 - -191 -200 0 - -191 -212 0 - -191 -220 0 - -200 -212 0 - -200 -220 0 - -212 -220 0 - -97 -109 0 - -97 -154 0 - -97 -161 0 - -97 -205 0 - -97 -213 0 - -97 -221 0 - -97 -230 0 - -109 -154 0 - -109 -161 0 - -109 -205 0 - -109 -213 0 - -109 -221 0 - -109 -230 0 - -154 -161 0 - -154 -205 0 - -154 -213 0 - -154 -221 0 - -154 -230 0 - -161 -205 0 - -161 -213 0 - -161 -221 0 - -161 -230 0 - -205 -213 0 - -205 -221 0 - -205 -230 0 - -213 -221 0 - -213 -230 0 - -221 -230 0 - -98 -129 0 - -98 -143 0 - -98 -183 0 - -98 -231 0 - -129 -143 0 - -129 -183 0 - -129 -231 0 - -143 -183 0 - -143 -231 0 - -183 -231 0 - -110 -121 0 - -110 -130 0 - -110 -155 0 - -110 -184 0 - -110 -201 0 - -110 -214 0 - -110 -222 0 - -121 -130 0 - -121 -155 0 - -121 -184 0 - -121 -201 0 - -121 -214 0 - -121 -222 0 - -130 -155 0 - -130 -184 0 - -130 -201 0 - -130 -214 0 - -130 -222 0 - -155 -184 0 - -155 -201 0 - -155 -214 0 - -155 -222 0 - -184 -201 0 - -184 -214 0 - -184 -222 0 - -201 -214 0 - -201 -222 0 - -214 -222 0 - -99 -111 0 - -99 -131 0 - -99 -137 0 - -99 -162 0 - -111 -131 0 - -111 -137 0 - -111 -162 0 - -131 -137 0 - -131 -162 0 - -137 -162 0 - -100 -132 0 - -100 -138 0 - -100 -144 0 - -100 -156 0 - -100 -163 0 - -100 -170 0 - -100 -192 0 - -100 -215 0 - -132 -138 0 - -132 -144 0 - -132 -156 0 - -132 -163 0 - -132 -170 0 - -132 -192 0 - -132 -215 0 - -138 -144 0 - -138 -156 0 - -138 -163 0 - -138 -170 0 - -138 -192 0 - -138 -215 0 - -144 -156 0 - -144 -163 0 - -144 -170 0 - -144 -192 0 - -144 -215 0 - -156 -163 0 - -156 -170 0 - -156 -192 0 - -156 -215 0 - -163 -170 0 - -163 -192 0 - -163 -215 0 - -170 -192 0 - -170 -215 0 - -192 -215 0 - -101 -122 0 - -101 -133 0 - -101 -145 0 - -101 -157 0 - -101 -185 0 - -101 -193 0 - -101 -216 0 - -101 -232 0 - -122 -133 0 - -122 -145 0 - -122 -157 0 - -122 -185 0 - -122 -193 0 - -122 -216 0 - -122 -232 0 - -133 -145 0 - -133 -157 0 - -133 -185 0 - -133 -193 0 - -133 -216 0 - -133 -232 0 - -145 -157 0 - -145 -185 0 - -145 -193 0 - -145 -216 0 - -145 -232 0 - -157 -185 0 - -157 -193 0 - -157 -216 0 - -157 -232 0 - -185 -193 0 - -185 -216 0 - -185 -232 0 - -193 -216 0 - -193 -232 0 - -216 -232 0 - -102 -112 0 - -102 -123 0 - -102 -146 0 - -102 -164 0 - -102 -171 0 - -102 -186 0 - -102 -194 0 - -112 -123 0 - -112 -146 0 - -112 -164 0 - -112 -171 0 - -112 -186 0 - -112 -194 0 - -123 -146 0 - -123 -164 0 - -123 -171 0 - -123 -186 0 - -123 -194 0 - -146 -164 0 - -146 -171 0 - -146 -186 0 - -146 -194 0 - -164 -171 0 - -164 -186 0 - -164 -194 0 - -171 -186 0 - -171 -194 0 - -186 -194 0 - -113 -134 0 - -113 -147 0 - -113 -158 0 - -113 -165 0 - -113 -172 0 - -113 -176 0 - -113 -187 0 - -113 -206 0 - -113 -223 0 - -134 -147 0 - -134 -158 0 - -134 -165 0 - -134 -172 0 - -134 -176 0 - -134 -187 0 - -134 -206 0 - -134 -223 0 - -147 -158 0 - -147 -165 0 - -147 -172 0 - -147 -176 0 - -147 -187 0 - -147 -206 0 - -147 -223 0 - -158 -165 0 - -158 -172 0 - -158 -176 0 - -158 -187 0 - -158 -206 0 - -158 -223 0 - -165 -172 0 - -165 -176 0 - -165 -187 0 - -165 -206 0 - -165 -223 0 - -172 -176 0 - -172 -187 0 - -172 -206 0 - -172 -223 0 - -176 -187 0 - -176 -206 0 - -176 -223 0 - -187 -206 0 - -187 -223 0 - -206 -223 0 - -124 -139 0 - -124 -148 0 - -124 -166 0 - -124 -224 0 - -139 -148 0 - -139 -166 0 - -139 -224 0 - -148 -166 0 - -148 -224 0 - -166 -224 0 - -125 -140 0 - -125 -149 0 - -125 -173 0 - -125 -202 0 - -125 -225 0 - -125 -233 0 - -140 -149 0 - -140 -173 0 - -140 -202 0 - -140 -225 0 - -140 -233 0 - -149 -173 0 - -149 -202 0 - -149 -225 0 - -149 -233 0 - -173 -202 0 - -173 -225 0 - -173 -233 0 - -202 -225 0 - -202 -233 0 - -225 -233 0 - -103 -114 0 - -103 -118 0 - -103 -126 0 - -103 -150 0 - -103 -177 0 - -103 -195 0 - -103 -226 0 - -114 -118 0 - -114 -126 0 - -114 -150 0 - -114 -177 0 - -114 -195 0 - -114 -226 0 - -118 -126 0 - -118 -150 0 - -118 -177 0 - -118 -195 0 - -118 -226 0 - -126 -150 0 - -126 -177 0 - -126 -195 0 - -126 -226 0 - -150 -177 0 - -150 -195 0 - -150 -226 0 - -177 -195 0 - -177 -226 0 - -195 -226 0 - -115 -159 0 - -115 -178 0 - -115 -203 0 - -115 -207 0 - -159 -178 0 - -159 -203 0 - -159 -207 0 - -178 -203 0 - -178 -207 0 - -203 -207 0 - -104 -116 0 - -104 -119 0 - -104 -135 0 - -104 -174 0 - -104 -188 0 - -104 -208 0 - -104 -217 0 - -104 -234 0 - -116 -119 0 - -116 -135 0 - -116 -174 0 - -116 -188 0 - -116 -208 0 - -116 -217 0 - -116 -234 0 - -119 -135 0 - -119 -174 0 - -119 -188 0 - -119 -208 0 - -119 -217 0 - -119 -234 0 - -135 -174 0 - -135 -188 0 - -135 -208 0 - -135 -217 0 - -135 -234 0 - -174 -188 0 - -174 -208 0 - -174 -217 0 - -174 -234 0 - -188 -208 0 - -188 -217 0 - -188 -234 0 - -208 -217 0 - -208 -234 0 - -217 -234 0 - -105 -141 0 - -105 -151 0 - -105 -179 0 - -105 -196 0 - -105 -204 0 - -105 -209 0 - -105 -218 0 - -105 -227 0 - -105 -235 0 - -141 -151 0 - -141 -179 0 - -141 -196 0 - -141 -204 0 - -141 -209 0 - -141 -218 0 - -141 -227 0 - -141 -235 0 - -151 -179 0 - -151 -196 0 - -151 -204 0 - -151 -209 0 - -151 -218 0 - -151 -227 0 - -151 -235 0 - -179 -196 0 - -179 -204 0 - -179 -209 0 - -179 -218 0 - -179 -227 0 - -179 -235 0 - -196 -204 0 - -196 -209 0 - -196 -218 0 - -196 -227 0 - -196 -235 0 - -204 -209 0 - -204 -218 0 - -204 -227 0 - -204 -235 0 - -209 -218 0 - -209 -227 0 - -209 -235 0 - -218 -227 0 - -218 -235 0 - -227 -235 0 - -106 -117 0 - -106 -127 0 - -106 -136 0 - -106 -142 0 - -106 -152 0 - -106 -189 0 - -106 -197 0 - -106 -210 0 - -106 -236 0 - -117 -127 0 - -117 -136 0 - -117 -142 0 - -117 -152 0 - -117 -189 0 - -117 -197 0 - -117 -210 0 - -117 -236 0 - -127 -136 0 - -127 -142 0 - -127 -152 0 - -127 -189 0 - -127 -197 0 - -127 -210 0 - -127 -236 0 - -136 -142 0 - -136 -152 0 - -136 -189 0 - -136 -197 0 - -136 -210 0 - -136 -236 0 - -142 -152 0 - -142 -189 0 - -142 -197 0 - -142 -210 0 - -142 -236 0 - -152 -189 0 - -152 -197 0 - -152 -210 0 - -152 -236 0 - -189 -197 0 - -189 -210 0 - -189 -236 0 - -197 -210 0 - -197 -236 0 - -210 -236 0 - -247 -253 0 - -247 -274 0 - -247 -283 0 - -247 -300 0 - -247 -307 0 - -247 -328 0 - -247 -336 0 - -253 -274 0 - -253 -283 0 - -253 -300 0 - -253 -307 0 - -253 -328 0 - -253 -336 0 - -274 -283 0 - -274 -300 0 - -274 -307 0 - -274 -328 0 - -274 -336 0 - -283 -300 0 - -283 -307 0 - -283 -328 0 - -283 -336 0 - -300 -307 0 - -300 -328 0 - -300 -336 0 - -307 -328 0 - -307 -336 0 - -328 -336 0 - -237 -259 0 - -237 -275 0 - -237 -292 0 - -237 -301 0 - -237 -308 0 - -237 -323 0 - -237 -329 0 - -237 -337 0 - -259 -275 0 - -259 -292 0 - -259 -301 0 - -259 -308 0 - -259 -323 0 - -259 -329 0 - -259 -337 0 - -275 -292 0 - -275 -301 0 - -275 -308 0 - -275 -323 0 - -275 -329 0 - -275 -337 0 - -292 -301 0 - -292 -308 0 - -292 -323 0 - -292 -329 0 - -292 -337 0 - -301 -308 0 - -301 -323 0 - -301 -329 0 - -301 -337 0 - -308 -323 0 - -308 -329 0 - -308 -337 0 - -323 -329 0 - -323 -337 0 - -329 -337 0 - -276 -293 0 - -276 -330 0 - -276 -338 0 - -293 -330 0 - -293 -338 0 - -330 -338 0 - -238 -260 0 - -238 -266 0 - -238 -288 0 - -238 -313 0 - -238 -324 0 - -260 -266 0 - -260 -288 0 - -260 -313 0 - -260 -324 0 - -266 -288 0 - -266 -313 0 - -266 -324 0 - -288 -313 0 - -288 -324 0 - -313 -324 0 - -239 -254 0 - -239 -284 0 - -239 -289 0 - -239 -314 0 - -239 -325 0 - -254 -284 0 - -254 -289 0 - -254 -314 0 - -254 -325 0 - -284 -289 0 - -284 -314 0 - -284 -325 0 - -289 -314 0 - -289 -325 0 - -314 -325 0 - -240 -248 0 - -240 -261 0 - -240 -267 0 - -240 -277 0 - -240 -285 0 - -240 -294 0 - -240 -315 0 - -248 -261 0 - -248 -267 0 - -248 -277 0 - -248 -285 0 - -248 -294 0 - -248 -315 0 - -261 -267 0 - -261 -277 0 - -261 -285 0 - -261 -294 0 - -261 -315 0 - -267 -277 0 - -267 -285 0 - -267 -294 0 - -267 -315 0 - -277 -285 0 - -277 -294 0 - -277 -315 0 - -285 -294 0 - -285 -315 0 - -294 -315 0 - -241 -262 0 - -241 -278 0 - -241 -295 0 - -241 -309 0 - -241 -316 0 - -241 -331 0 - -241 -339 0 - -262 -278 0 - -262 -295 0 - -262 -309 0 - -262 -316 0 - -262 -331 0 - -262 -339 0 - -278 -295 0 - -278 -309 0 - -278 -316 0 - -278 -331 0 - -278 -339 0 - -295 -309 0 - -295 -316 0 - -295 -331 0 - -295 -339 0 - -309 -316 0 - -309 -331 0 - -309 -339 0 - -316 -331 0 - -316 -339 0 - -331 -339 0 - -242 -255 0 - -242 -268 0 - -242 -279 0 - -242 -302 0 - -242 -317 0 - -242 -332 0 - -255 -268 0 - -255 -279 0 - -255 -302 0 - -255 -317 0 - -255 -332 0 - -268 -279 0 - -268 -302 0 - -268 -317 0 - -268 -332 0 - -279 -302 0 - -279 -317 0 - -279 -332 0 - -302 -317 0 - -302 -332 0 - -317 -332 0 - -243 -256 0 - -243 -263 0 - -243 -269 0 - -243 -280 0 - -243 -286 0 - -243 -303 0 - -243 -318 0 - -243 -333 0 - -256 -263 0 - -256 -269 0 - -256 -280 0 - -256 -286 0 - -256 -303 0 - -256 -318 0 - -256 -333 0 - -263 -269 0 - -263 -280 0 - -263 -286 0 - -263 -303 0 - -263 -318 0 - -263 -333 0 - -269 -280 0 - -269 -286 0 - -269 -303 0 - -269 -318 0 - -269 -333 0 - -280 -286 0 - -280 -303 0 - -280 -318 0 - -280 -333 0 - -286 -303 0 - -286 -318 0 - -286 -333 0 - -303 -318 0 - -303 -333 0 - -318 -333 0 - -249 -257 0 - -249 -270 0 - -249 -290 0 - -249 -296 0 - -249 -304 0 - -249 -310 0 - -257 -270 0 - -257 -290 0 - -257 -296 0 - -257 -304 0 - -257 -310 0 - -270 -290 0 - -270 -296 0 - -270 -304 0 - -270 -310 0 - -290 -296 0 - -290 -304 0 - -290 -310 0 - -296 -304 0 - -296 -310 0 - -304 -310 0 - -244 -250 0 - -244 -264 0 - -244 -281 0 - -244 -297 0 - -244 -305 0 - -244 -326 0 - -244 -340 0 - -250 -264 0 - -250 -281 0 - -250 -297 0 - -250 -305 0 - -250 -326 0 - -250 -340 0 - -264 -281 0 - -264 -297 0 - -264 -305 0 - -264 -326 0 - -264 -340 0 - -281 -297 0 - -281 -305 0 - -281 -326 0 - -281 -340 0 - -297 -305 0 - -297 -326 0 - -297 -340 0 - -305 -326 0 - -305 -340 0 - -326 -340 0 - -251 -271 0 - -251 -282 0 - -251 -306 0 - -251 -319 0 - -251 -334 0 - -251 -341 0 - -271 -282 0 - -271 -306 0 - -271 -319 0 - -271 -334 0 - -271 -341 0 - -282 -306 0 - -282 -319 0 - -282 -334 0 - -282 -341 0 - -306 -319 0 - -306 -334 0 - -306 -341 0 - -319 -334 0 - -319 -341 0 - -334 -341 0 - -272 -291 0 - -272 -298 0 - -272 -311 0 - -272 -320 0 - -272 -327 0 - -272 -335 0 - -291 -298 0 - -291 -311 0 - -291 -320 0 - -291 -327 0 - -291 -335 0 - -298 -311 0 - -298 -320 0 - -298 -327 0 - -298 -335 0 - -311 -320 0 - -311 -327 0 - -311 -335 0 - -320 -327 0 - -320 -335 0 - -327 -335 0 - -245 -252 0 - -245 -258 0 - -245 -273 0 - -245 -287 0 - -245 -299 0 - -245 -312 0 - -245 -321 0 - -245 -342 0 - -252 -258 0 - -252 -273 0 - -252 -287 0 - -252 -299 0 - -252 -312 0 - -252 -321 0 - -252 -342 0 - -258 -273 0 - -258 -287 0 - -258 -299 0 - -258 -312 0 - -258 -321 0 - -258 -342 0 - -273 -287 0 - -273 -299 0 - -273 -312 0 - -273 -321 0 - -273 -342 0 - -287 -299 0 - -287 -312 0 - -287 -321 0 - -287 -342 0 - -299 -312 0 - -299 -321 0 - -299 -342 0 - -312 -321 0 - -312 -342 0 - -321 -342 0 - -246 -265 0 - -246 -322 0 - -246 -343 0 - -265 -322 0 - -265 -343 0 - -322 -343 0 - -344 -360 0 - -344 -366 0 - -344 -371 0 - -360 -366 0 - -360 -371 0 - -366 -371 0 - -382 -397 0 - -382 -402 0 - -382 -423 0 - -382 -430 0 - -397 -402 0 - -397 -423 0 - -397 -430 0 - -402 -423 0 - -402 -430 0 - -423 -430 0 - -353 -361 0 - -353 -372 0 - -353 -392 0 - -353 -409 0 - -353 -431 0 - -361 -372 0 - -361 -392 0 - -361 -409 0 - -361 -431 0 - -372 -392 0 - -372 -409 0 - -372 -431 0 - -392 -409 0 - -392 -431 0 - -409 -431 0 - -354 -373 0 - -354 -386 0 - -354 -393 0 - -354 -398 0 - -354 -403 0 - -354 -416 0 - -354 -432 0 - -373 -386 0 - -373 -393 0 - -373 -398 0 - -373 -403 0 - -373 -416 0 - -373 -432 0 - -386 -393 0 - -386 -398 0 - -386 -403 0 - -386 -416 0 - -386 -432 0 - -393 -398 0 - -393 -403 0 - -393 -416 0 - -393 -432 0 - -398 -403 0 - -398 -416 0 - -398 -432 0 - -403 -416 0 - -403 -432 0 - -416 -432 0 - -345 -383 0 - -345 -387 0 - -345 -410 0 - -345 -417 0 - -345 -424 0 - -383 -387 0 - -383 -410 0 - -383 -417 0 - -383 -424 0 - -387 -410 0 - -387 -417 0 - -387 -424 0 - -410 -417 0 - -410 -424 0 - -417 -424 0 - -346 -355 0 - -346 -362 0 - -346 -374 0 - -346 -399 0 - -346 -411 0 - -346 -418 0 - -346 -425 0 - -346 -436 0 - -355 -362 0 - -355 -374 0 - -355 -399 0 - -355 -411 0 - -355 -418 0 - -355 -425 0 - -355 -436 0 - -362 -374 0 - -362 -399 0 - -362 -411 0 - -362 -418 0 - -362 -425 0 - -362 -436 0 - -374 -399 0 - -374 -411 0 - -374 -418 0 - -374 -425 0 - -374 -436 0 - -399 -411 0 - -399 -418 0 - -399 -425 0 - -399 -436 0 - -411 -418 0 - -411 -425 0 - -411 -436 0 - -418 -425 0 - -418 -436 0 - -425 -436 0 - -347 -356 0 - -347 -363 0 - -347 -367 0 - -347 -375 0 - -347 -384 0 - -347 -388 0 - -347 -419 0 - -347 -433 0 - -347 -437 0 - -356 -363 0 - -356 -367 0 - -356 -375 0 - -356 -384 0 - -356 -388 0 - -356 -419 0 - -356 -433 0 - -356 -437 0 - -363 -367 0 - -363 -375 0 - -363 -384 0 - -363 -388 0 - -363 -419 0 - -363 -433 0 - -363 -437 0 - -367 -375 0 - -367 -384 0 - -367 -388 0 - -367 -419 0 - -367 -433 0 - -367 -437 0 - -375 -384 0 - -375 -388 0 - -375 -419 0 - -375 -433 0 - -375 -437 0 - -384 -388 0 - -384 -419 0 - -384 -433 0 - -384 -437 0 - -388 -419 0 - -388 -433 0 - -388 -437 0 - -419 -433 0 - -419 -437 0 - -433 -437 0 - -348 -364 0 - -348 -376 0 - -348 -385 0 - -348 -404 0 - -348 -412 0 - -348 -420 0 - -348 -434 0 - -364 -376 0 - -364 -385 0 - -364 -404 0 - -364 -412 0 - -364 -420 0 - -364 -434 0 - -376 -385 0 - -376 -404 0 - -376 -412 0 - -376 -420 0 - -376 -434 0 - -385 -404 0 - -385 -412 0 - -385 -420 0 - -385 -434 0 - -404 -412 0 - -404 -420 0 - -404 -434 0 - -412 -420 0 - -412 -434 0 - -420 -434 0 - -349 -357 0 - -349 -365 0 - -349 -389 0 - -349 -400 0 - -349 -405 0 - -349 -438 0 - -357 -365 0 - -357 -389 0 - -357 -400 0 - -357 -405 0 - -357 -438 0 - -365 -389 0 - -365 -400 0 - -365 -405 0 - -365 -438 0 - -389 -400 0 - -389 -405 0 - -389 -438 0 - -400 -405 0 - -400 -438 0 - -405 -438 0 - -350 -390 0 - -350 -394 0 - -350 -406 0 - -390 -394 0 - -390 -406 0 - -394 -406 0 - -368 -377 0 - -368 -391 0 - -368 -426 0 - -368 -439 0 - -377 -391 0 - -377 -426 0 - -377 -439 0 - -391 -426 0 - -391 -439 0 - -426 -439 0 - -369 -378 0 - -369 -427 0 - -378 -427 0 - -379 -395 0 - -379 -401 0 - -379 -407 0 - -379 -413 0 - -379 -428 0 - -395 -401 0 - -395 -407 0 - -395 -413 0 - -395 -428 0 - -401 -407 0 - -401 -413 0 - -401 -428 0 - -407 -413 0 - -407 -428 0 - -413 -428 0 - -351 -358 0 - -351 -370 0 - -351 -380 0 - -351 -396 0 - -351 -414 0 - -351 -421 0 - -351 -429 0 - -351 -435 0 - -358 -370 0 - -358 -380 0 - -358 -396 0 - -358 -414 0 - -358 -421 0 - -358 -429 0 - -358 -435 0 - -370 -380 0 - -370 -396 0 - -370 -414 0 - -370 -421 0 - -370 -429 0 - -370 -435 0 - -380 -396 0 - -380 -414 0 - -380 -421 0 - -380 -429 0 - -380 -435 0 - -396 -414 0 - -396 -421 0 - -396 -429 0 - -396 -435 0 - -414 -421 0 - -414 -429 0 - -414 -435 0 - -421 -429 0 - -421 -435 0 - -429 -435 0 - -352 -359 0 - -352 -381 0 - -352 -408 0 - -352 -415 0 - -352 -422 0 - -352 -440 0 - -359 -381 0 - -359 -408 0 - -359 -415 0 - -359 -422 0 - -359 -440 0 - -381 -408 0 - -381 -415 0 - -381 -422 0 - -381 -440 0 - -408 -415 0 - -408 -422 0 - -408 -440 0 - -415 -422 0 - -415 -440 0 - -422 -440 0 - -441 -447 0 - -441 -497 0 - -441 -505 0 - -441 -513 0 - -441 -530 0 - -447 -497 0 - -447 -505 0 - -447 -513 0 - -447 -530 0 - -497 -505 0 - -497 -513 0 - -497 -530 0 - -505 -513 0 - -505 -530 0 - -513 -530 0 - -442 -448 0 - -442 -462 0 - -442 -480 0 - -442 -498 0 - -442 -514 0 - -442 -519 0 - -442 -531 0 - -442 -552 0 - -442 -558 0 - -448 -462 0 - -448 -480 0 - -448 -498 0 - -448 -514 0 - -448 -519 0 - -448 -531 0 - -448 -552 0 - -448 -558 0 - -462 -480 0 - -462 -498 0 - -462 -514 0 - -462 -519 0 - -462 -531 0 - -462 -552 0 - -462 -558 0 - -480 -498 0 - -480 -514 0 - -480 -519 0 - -480 -531 0 - -480 -552 0 - -480 -558 0 - -498 -514 0 - -498 -519 0 - -498 -531 0 - -498 -552 0 - -498 -558 0 - -514 -519 0 - -514 -531 0 - -514 -552 0 - -514 -558 0 - -519 -531 0 - -519 -552 0 - -519 -558 0 - -531 -552 0 - -531 -558 0 - -552 -558 0 - -443 -472 0 - -443 -490 0 - -443 -506 0 - -443 -532 0 - -443 -541 0 - -443 -559 0 - -472 -490 0 - -472 -506 0 - -472 -532 0 - -472 -541 0 - -472 -559 0 - -490 -506 0 - -490 -532 0 - -490 -541 0 - -490 -559 0 - -506 -532 0 - -506 -541 0 - -506 -559 0 - -532 -541 0 - -532 -559 0 - -541 -559 0 - -449 -458 0 - -449 -481 0 - -449 -499 0 - -449 -533 0 - -449 -553 0 - -449 -560 0 - -458 -481 0 - -458 -499 0 - -458 -533 0 - -458 -553 0 - -458 -560 0 - -481 -499 0 - -481 -533 0 - -481 -553 0 - -481 -560 0 - -499 -533 0 - -499 -553 0 - -499 -560 0 - -533 -553 0 - -533 -560 0 - -553 -560 0 - -463 -491 0 - -463 -507 0 - -463 -515 0 - -463 -534 0 - -463 -542 0 - -463 -561 0 - -463 -567 0 - -491 -507 0 - -491 -515 0 - -491 -534 0 - -491 -542 0 - -491 -561 0 - -491 -567 0 - -507 -515 0 - -507 -534 0 - -507 -542 0 - -507 -561 0 - -507 -567 0 - -515 -534 0 - -515 -542 0 - -515 -561 0 - -515 -567 0 - -534 -542 0 - -534 -561 0 - -534 -567 0 - -542 -561 0 - -542 -567 0 - -561 -567 0 - -444 -450 0 - -444 -464 0 - -444 -492 0 - -444 -520 0 - -444 -535 0 - -444 -543 0 - -450 -464 0 - -450 -492 0 - -450 -520 0 - -450 -535 0 - -450 -543 0 - -464 -492 0 - -464 -520 0 - -464 -535 0 - -464 -543 0 - -492 -520 0 - -492 -535 0 - -492 -543 0 - -520 -535 0 - -520 -543 0 - -535 -543 0 - -451 -459 0 - -451 -482 0 - -451 -508 0 - -451 -521 0 - -451 -536 0 - -451 -544 0 - -451 -568 0 - -459 -482 0 - -459 -508 0 - -459 -521 0 - -459 -536 0 - -459 -544 0 - -459 -568 0 - -482 -508 0 - -482 -521 0 - -482 -536 0 - -482 -544 0 - -482 -568 0 - -508 -521 0 - -508 -536 0 - -508 -544 0 - -508 -568 0 - -521 -536 0 - -521 -544 0 - -521 -568 0 - -536 -544 0 - -536 -568 0 - -544 -568 0 - -445 -452 0 - -445 -473 0 - -445 -493 0 - -445 -516 0 - -445 -522 0 - -445 -537 0 - -445 -545 0 - -452 -473 0 - -452 -493 0 - -452 -516 0 - -452 -522 0 - -452 -537 0 - -452 -545 0 - -473 -493 0 - -473 -516 0 - -473 -522 0 - -473 -537 0 - -473 -545 0 - -493 -516 0 - -493 -522 0 - -493 -537 0 - -493 -545 0 - -516 -522 0 - -516 -537 0 - -516 -545 0 - -522 -537 0 - -522 -545 0 - -537 -545 0 - -453 -474 0 - -453 -494 0 - -453 -509 0 - -453 -523 0 - -453 -546 0 - -453 -562 0 - -474 -494 0 - -474 -509 0 - -474 -523 0 - -474 -546 0 - -474 -562 0 - -494 -509 0 - -494 -523 0 - -494 -546 0 - -494 -562 0 - -509 -523 0 - -509 -546 0 - -509 -562 0 - -523 -546 0 - -523 -562 0 - -546 -562 0 - -454 -465 0 - -454 -475 0 - -454 -483 0 - -454 -510 0 - -454 -524 0 - -454 -563 0 - -454 -569 0 - -465 -475 0 - -465 -483 0 - -465 -510 0 - -465 -524 0 - -465 -563 0 - -465 -569 0 - -475 -483 0 - -475 -510 0 - -475 -524 0 - -475 -563 0 - -475 -569 0 - -483 -510 0 - -483 -524 0 - -483 -563 0 - -483 -569 0 - -510 -524 0 - -510 -563 0 - -510 -569 0 - -524 -563 0 - -524 -569 0 - -563 -569 0 - -466 -484 0 - -466 -538 0 - -466 -547 0 - -466 -554 0 - -466 -564 0 - -484 -538 0 - -484 -547 0 - -484 -554 0 - -484 -564 0 - -538 -547 0 - -538 -554 0 - -538 -564 0 - -547 -554 0 - -547 -564 0 - -554 -564 0 - -455 -467 0 - -455 -485 0 - -455 -500 0 - -455 -525 0 - -455 -548 0 - -455 -570 0 - -467 -485 0 - -467 -500 0 - -467 -525 0 - -467 -548 0 - -467 -570 0 - -485 -500 0 - -485 -525 0 - -485 -548 0 - -485 -570 0 - -500 -525 0 - -500 -548 0 - -500 -570 0 - -525 -548 0 - -525 -570 0 - -548 -570 0 - -460 -468 0 - -460 -486 0 - -460 -501 0 - -460 -526 0 - -460 -539 0 - -460 -555 0 - -468 -486 0 - -468 -501 0 - -468 -526 0 - -468 -539 0 - -468 -555 0 - -486 -501 0 - -486 -526 0 - -486 -539 0 - -486 -555 0 - -501 -526 0 - -501 -539 0 - -501 -555 0 - -526 -539 0 - -526 -555 0 - -539 -555 0 - -469 -476 0 - -469 -478 0 - -469 -487 0 - -469 -549 0 - -469 -571 0 - -476 -478 0 - -476 -487 0 - -476 -549 0 - -476 -571 0 - -478 -487 0 - -478 -549 0 - -478 -571 0 - -487 -549 0 - -487 -571 0 - -549 -571 0 - -456 -461 0 - -456 -470 0 - -456 -477 0 - -456 -488 0 - -456 -495 0 - -456 -502 0 - -456 -511 0 - -456 -517 0 - -456 -527 0 - -456 -550 0 - -456 -556 0 - -461 -470 0 - -461 -477 0 - -461 -488 0 - -461 -495 0 - -461 -502 0 - -461 -511 0 - -461 -517 0 - -461 -527 0 - -461 -550 0 - -461 -556 0 - -470 -477 0 - -470 -488 0 - -470 -495 0 - -470 -502 0 - -470 -511 0 - -470 -517 0 - -470 -527 0 - -470 -550 0 - -470 -556 0 - -477 -488 0 - -477 -495 0 - -477 -502 0 - -477 -511 0 - -477 -517 0 - -477 -527 0 - -477 -550 0 - -477 -556 0 - -488 -495 0 - -488 -502 0 - -488 -511 0 - -488 -517 0 - -488 -527 0 - -488 -550 0 - -488 -556 0 - -495 -502 0 - -495 -511 0 - -495 -517 0 - -495 -527 0 - -495 -550 0 - -495 -556 0 - -502 -511 0 - -502 -517 0 - -502 -527 0 - -502 -550 0 - -502 -556 0 - -511 -517 0 - -511 -527 0 - -511 -550 0 - -511 -556 0 - -517 -527 0 - -517 -550 0 - -517 -556 0 - -527 -550 0 - -527 -556 0 - -550 -556 0 - -446 -457 0 - -446 -471 0 - -446 -503 0 - -446 -528 0 - -446 -565 0 - -457 -471 0 - -457 -503 0 - -457 -528 0 - -457 -565 0 - -471 -503 0 - -471 -528 0 - -471 -565 0 - -503 -528 0 - -503 -565 0 - -528 -565 0 - -479 -489 0 - -479 -496 0 - -479 -504 0 - -479 -512 0 - -479 -518 0 - -479 -529 0 - -479 -540 0 - -479 -551 0 - -479 -557 0 - -479 -566 0 - -489 -496 0 - -489 -504 0 - -489 -512 0 - -489 -518 0 - -489 -529 0 - -489 -540 0 - -489 -551 0 - -489 -557 0 - -489 -566 0 - -496 -504 0 - -496 -512 0 - -496 -518 0 - -496 -529 0 - -496 -540 0 - -496 -551 0 - -496 -557 0 - -496 -566 0 - -504 -512 0 - -504 -518 0 - -504 -529 0 - -504 -540 0 - -504 -551 0 - -504 -557 0 - -504 -566 0 - -512 -518 0 - -512 -529 0 - -512 -540 0 - -512 -551 0 - -512 -557 0 - -512 -566 0 - -518 -529 0 - -518 -540 0 - -518 -551 0 - -518 -557 0 - -518 -566 0 - -529 -540 0 - -529 -551 0 - -529 -557 0 - -529 -566 0 - -540 -551 0 - -540 -557 0 - -540 -566 0 - -551 -557 0 - -551 -566 0 - -557 -566 0 - -584 -599 0 - -584 -613 0 - -584 -622 0 - -584 -630 0 - -584 -668 0 - -584 -676 0 - -599 -613 0 - -599 -622 0 - -599 -630 0 - -599 -668 0 - -599 -676 0 - -613 -622 0 - -613 -630 0 - -613 -668 0 - -613 -676 0 - -622 -630 0 - -622 -668 0 - -622 -676 0 - -630 -668 0 - -630 -676 0 - -668 -676 0 - -585 -605 0 - -585 -614 0 - -585 -635 0 - -585 -645 0 - -585 -686 0 - -605 -614 0 - -605 -635 0 - -605 -645 0 - -605 -686 0 - -614 -635 0 - -614 -645 0 - -614 -686 0 - -635 -645 0 - -635 -686 0 - -645 -686 0 - -572 -600 0 - -572 -606 0 - -572 -615 0 - -572 -623 0 - -572 -651 0 - -572 -669 0 - -600 -606 0 - -600 -615 0 - -600 -623 0 - -600 -651 0 - -600 -669 0 - -606 -615 0 - -606 -623 0 - -606 -651 0 - -606 -669 0 - -615 -623 0 - -615 -651 0 - -615 -669 0 - -623 -651 0 - -623 -669 0 - -651 -669 0 - -573 -586 0 - -573 -616 0 - -573 -631 0 - -573 -636 0 - -573 -652 0 - -573 -659 0 - -573 -677 0 - -586 -616 0 - -586 -631 0 - -586 -636 0 - -586 -652 0 - -586 -659 0 - -586 -677 0 - -616 -631 0 - -616 -636 0 - -616 -652 0 - -616 -659 0 - -616 -677 0 - -631 -636 0 - -631 -652 0 - -631 -659 0 - -631 -677 0 - -636 -652 0 - -636 -659 0 - -636 -677 0 - -652 -659 0 - -652 -677 0 - -659 -677 0 - -581 -592 0 - -581 -617 0 - -581 -637 0 - -581 -653 0 - -581 -660 0 - -581 -678 0 - -581 -687 0 - -592 -617 0 - -592 -637 0 - -592 -653 0 - -592 -660 0 - -592 -678 0 - -592 -687 0 - -617 -637 0 - -617 -653 0 - -617 -660 0 - -617 -678 0 - -617 -687 0 - -637 -653 0 - -637 -660 0 - -637 -678 0 - -637 -687 0 - -653 -660 0 - -653 -678 0 - -653 -687 0 - -660 -678 0 - -660 -687 0 - -678 -687 0 - -582 -601 0 - -582 -607 0 - -582 -654 0 - -582 -661 0 - -582 -679 0 - -582 -688 0 - -601 -607 0 - -601 -654 0 - -601 -661 0 - -601 -679 0 - -601 -688 0 - -607 -654 0 - -607 -661 0 - -607 -679 0 - -607 -688 0 - -654 -661 0 - -654 -679 0 - -654 -688 0 - -661 -679 0 - -661 -688 0 - -679 -688 0 - -574 -593 0 - -574 -618 0 - -574 -632 0 - -574 -638 0 - -574 -646 0 - -574 -670 0 - -574 -680 0 - -593 -618 0 - -593 -632 0 - -593 -638 0 - -593 -646 0 - -593 -670 0 - -593 -680 0 - -618 -632 0 - -618 -638 0 - -618 -646 0 - -618 -670 0 - -618 -680 0 - -632 -638 0 - -632 -646 0 - -632 -670 0 - -632 -680 0 - -638 -646 0 - -638 -670 0 - -638 -680 0 - -646 -670 0 - -646 -680 0 - -670 -680 0 - -575 -608 0 - -575 -639 0 - -575 -655 0 - -575 -662 0 - -575 -671 0 - -608 -639 0 - -608 -655 0 - -608 -662 0 - -608 -671 0 - -639 -655 0 - -639 -662 0 - -639 -671 0 - -655 -662 0 - -655 -671 0 - -662 -671 0 - -576 -587 0 - -576 -602 0 - -576 -609 0 - -576 -619 0 - -576 -624 0 - -576 -640 0 - -576 -672 0 - -576 -681 0 - -587 -602 0 - -587 -609 0 - -587 -619 0 - -587 -624 0 - -587 -640 0 - -587 -672 0 - -587 -681 0 - -602 -609 0 - -602 -619 0 - -602 -624 0 - -602 -640 0 - -602 -672 0 - -602 -681 0 - -609 -619 0 - -609 -624 0 - -609 -640 0 - -609 -672 0 - -609 -681 0 - -619 -624 0 - -619 -640 0 - -619 -672 0 - -619 -681 0 - -624 -640 0 - -624 -672 0 - -624 -681 0 - -640 -672 0 - -640 -681 0 - -672 -681 0 - -577 -588 0 - -577 -625 0 - -577 -647 0 - -577 -663 0 - -577 -682 0 - -588 -625 0 - -588 -647 0 - -588 -663 0 - -588 -682 0 - -625 -647 0 - -625 -663 0 - -625 -682 0 - -647 -663 0 - -647 -682 0 - -663 -682 0 - -589 -610 0 - -589 -620 0 - -589 -626 0 - -589 -641 0 - -589 -656 0 - -589 -664 0 - -589 -673 0 - -610 -620 0 - -610 -626 0 - -610 -641 0 - -610 -656 0 - -610 -664 0 - -610 -673 0 - -620 -626 0 - -620 -641 0 - -620 -656 0 - -620 -664 0 - -620 -673 0 - -626 -641 0 - -626 -656 0 - -626 -664 0 - -626 -673 0 - -641 -656 0 - -641 -664 0 - -641 -673 0 - -656 -664 0 - -656 -673 0 - -664 -673 0 - -578 -590 0 - -578 -594 0 - -578 -603 0 - -578 -627 0 - -578 -642 0 - -578 -648 0 - -578 -665 0 - -590 -594 0 - -590 -603 0 - -590 -627 0 - -590 -642 0 - -590 -648 0 - -590 -665 0 - -594 -603 0 - -594 -627 0 - -594 -642 0 - -594 -648 0 - -594 -665 0 - -603 -627 0 - -603 -642 0 - -603 -648 0 - -603 -665 0 - -627 -642 0 - -627 -648 0 - -627 -665 0 - -642 -648 0 - -642 -665 0 - -648 -665 0 - -591 -595 0 - -591 -621 0 - -591 -628 0 - -591 -683 0 - -595 -621 0 - -595 -628 0 - -595 -683 0 - -621 -628 0 - -621 -683 0 - -628 -683 0 - -579 -596 0 - -579 -611 0 - -579 -633 0 - -579 -657 0 - -579 -666 0 - -579 -684 0 - -596 -611 0 - -596 -633 0 - -596 -657 0 - -596 -666 0 - -596 -684 0 - -611 -633 0 - -611 -657 0 - -611 -666 0 - -611 -684 0 - -633 -657 0 - -633 -666 0 - -633 -684 0 - -657 -666 0 - -657 -684 0 - -666 -684 0 - -597 -604 0 - -597 -612 0 - -597 -629 0 - -597 -643 0 - -597 -649 0 - -597 -674 0 - -597 -689 0 - -604 -612 0 - -604 -629 0 - -604 -643 0 - -604 -649 0 - -604 -674 0 - -604 -689 0 - -612 -629 0 - -612 -643 0 - -612 -649 0 - -612 -674 0 - -612 -689 0 - -629 -643 0 - -629 -649 0 - -629 -674 0 - -629 -689 0 - -643 -649 0 - -643 -674 0 - -643 -689 0 - -649 -674 0 - -649 -689 0 - -674 -689 0 - -580 -583 0 - -580 -598 0 - -580 -634 0 - -580 -644 0 - -580 -650 0 - -580 -658 0 - -580 -667 0 - -580 -675 0 - -580 -685 0 - -580 -690 0 - -583 -598 0 - -583 -634 0 - -583 -644 0 - -583 -650 0 - -583 -658 0 - -583 -667 0 - -583 -675 0 - -583 -685 0 - -583 -690 0 - -598 -634 0 - -598 -644 0 - -598 -650 0 - -598 -658 0 - -598 -667 0 - -598 -675 0 - -598 -685 0 - -598 -690 0 - -634 -644 0 - -634 -650 0 - -634 -658 0 - -634 -667 0 - -634 -675 0 - -634 -685 0 - -634 -690 0 - -644 -650 0 - -644 -658 0 - -644 -667 0 - -644 -675 0 - -644 -685 0 - -644 -690 0 - -650 -658 0 - -650 -667 0 - -650 -675 0 - -650 -685 0 - -650 -690 0 - -658 -667 0 - -658 -675 0 - -658 -685 0 - -658 -690 0 - -667 -675 0 - -667 -685 0 - -667 -690 0 - -675 -685 0 - -675 -690 0 - -685 -690 0 - -719 -755 0 - -719 -760 0 - -719 -784 0 - -719 -804 0 - -719 -823 0 - -755 -760 0 - -755 -784 0 - -755 -804 0 - -755 -823 0 - -760 -784 0 - -760 -804 0 - -760 -823 0 - -784 -804 0 - -784 -823 0 - -804 -823 0 - -695 -706 0 - -695 -712 0 - -695 -720 0 - -695 -740 0 - -695 -748 0 - -695 -756 0 - -695 -761 0 - -695 -805 0 - -706 -712 0 - -706 -720 0 - -706 -740 0 - -706 -748 0 - -706 -756 0 - -706 -761 0 - -706 -805 0 - -712 -720 0 - -712 -740 0 - -712 -748 0 - -712 -756 0 - -712 -761 0 - -712 -805 0 - -720 -740 0 - -720 -748 0 - -720 -756 0 - -720 -761 0 - -720 -805 0 - -740 -748 0 - -740 -756 0 - -740 -761 0 - -740 -805 0 - -748 -756 0 - -748 -761 0 - -748 -805 0 - -756 -761 0 - -756 -805 0 - -761 -805 0 - -741 -749 0 - -741 -768 0 - -741 -785 0 - -741 -793 0 - -741 -814 0 - -749 -768 0 - -749 -785 0 - -749 -793 0 - -749 -814 0 - -768 -785 0 - -768 -793 0 - -768 -814 0 - -785 -793 0 - -785 -814 0 - -793 -814 0 - -724 -730 0 - -724 -742 0 - -724 -750 0 - -724 -762 0 - -724 -769 0 - -724 -794 0 - -724 -806 0 - -724 -815 0 - -724 -824 0 - -730 -742 0 - -730 -750 0 - -730 -762 0 - -730 -769 0 - -730 -794 0 - -730 -806 0 - -730 -815 0 - -730 -824 0 - -742 -750 0 - -742 -762 0 - -742 -769 0 - -742 -794 0 - -742 -806 0 - -742 -815 0 - -742 -824 0 - -750 -762 0 - -750 -769 0 - -750 -794 0 - -750 -806 0 - -750 -815 0 - -750 -824 0 - -762 -769 0 - -762 -794 0 - -762 -806 0 - -762 -815 0 - -762 -824 0 - -769 -794 0 - -769 -806 0 - -769 -815 0 - -769 -824 0 - -794 -806 0 - -794 -815 0 - -794 -824 0 - -806 -815 0 - -806 -824 0 - -815 -824 0 - -707 -713 0 - -707 -731 0 - -707 -751 0 - -707 -770 0 - -707 -807 0 - -707 -816 0 - -713 -731 0 - -713 -751 0 - -713 -770 0 - -713 -807 0 - -713 -816 0 - -731 -751 0 - -731 -770 0 - -731 -807 0 - -731 -816 0 - -751 -770 0 - -751 -807 0 - -751 -816 0 - -770 -807 0 - -770 -816 0 - -807 -816 0 - -696 -732 0 - -696 -771 0 - -696 -786 0 - -696 -795 0 - -696 -808 0 - -696 -817 0 - -732 -771 0 - -732 -786 0 - -732 -795 0 - -732 -808 0 - -732 -817 0 - -771 -786 0 - -771 -795 0 - -771 -808 0 - -771 -817 0 - -786 -795 0 - -786 -808 0 - -786 -817 0 - -795 -808 0 - -795 -817 0 - -808 -817 0 - -691 -697 0 - -691 -714 0 - -691 -733 0 - -691 -763 0 - -691 -772 0 - -691 -787 0 - -691 -796 0 - -697 -714 0 - -697 -733 0 - -697 -763 0 - -697 -772 0 - -697 -787 0 - -697 -796 0 - -714 -733 0 - -714 -763 0 - -714 -772 0 - -714 -787 0 - -714 -796 0 - -733 -763 0 - -733 -772 0 - -733 -787 0 - -733 -796 0 - -763 -772 0 - -763 -787 0 - -763 -796 0 - -772 -787 0 - -772 -796 0 - -787 -796 0 - -698 -708 0 - -698 -715 0 - -698 -721 0 - -698 -757 0 - -698 -764 0 - -698 -778 0 - -698 -788 0 - -698 -797 0 - -698 -818 0 - -708 -715 0 - -708 -721 0 - -708 -757 0 - -708 -764 0 - -708 -778 0 - -708 -788 0 - -708 -797 0 - -708 -818 0 - -715 -721 0 - -715 -757 0 - -715 -764 0 - -715 -778 0 - -715 -788 0 - -715 -797 0 - -715 -818 0 - -721 -757 0 - -721 -764 0 - -721 -778 0 - -721 -788 0 - -721 -797 0 - -721 -818 0 - -757 -764 0 - -757 -778 0 - -757 -788 0 - -757 -797 0 - -757 -818 0 - -764 -778 0 - -764 -788 0 - -764 -797 0 - -764 -818 0 - -778 -788 0 - -778 -797 0 - -778 -818 0 - -788 -797 0 - -788 -818 0 - -797 -818 0 - -699 -716 0 - -699 -725 0 - -699 -734 0 - -699 -765 0 - -699 -798 0 - -699 -809 0 - -699 -825 0 - -716 -725 0 - -716 -734 0 - -716 -765 0 - -716 -798 0 - -716 -809 0 - -716 -825 0 - -725 -734 0 - -725 -765 0 - -725 -798 0 - -725 -809 0 - -725 -825 0 - -734 -765 0 - -734 -798 0 - -734 -809 0 - -734 -825 0 - -765 -798 0 - -765 -809 0 - -765 -825 0 - -798 -809 0 - -798 -825 0 - -809 -825 0 - -700 -709 0 - -700 -735 0 - -700 -743 0 - -700 -789 0 - -700 -810 0 - -709 -735 0 - -709 -743 0 - -709 -789 0 - -709 -810 0 - -735 -743 0 - -735 -789 0 - -735 -810 0 - -743 -789 0 - -743 -810 0 - -789 -810 0 - -692 -701 0 - -692 -726 0 - -692 -790 0 - -701 -726 0 - -701 -790 0 - -726 -790 0 - -702 -744 0 - -702 -766 0 - -702 -773 0 - -702 -779 0 - -702 -791 0 - -702 -799 0 - -702 -811 0 - -702 -826 0 - -744 -766 0 - -744 -773 0 - -744 -779 0 - -744 -791 0 - -744 -799 0 - -744 -811 0 - -744 -826 0 - -766 -773 0 - -766 -779 0 - -766 -791 0 - -766 -799 0 - -766 -811 0 - -766 -826 0 - -773 -779 0 - -773 -791 0 - -773 -799 0 - -773 -811 0 - -773 -826 0 - -779 -791 0 - -779 -799 0 - -779 -811 0 - -779 -826 0 - -791 -799 0 - -791 -811 0 - -791 -826 0 - -799 -811 0 - -799 -826 0 - -811 -826 0 - -703 -710 0 - -703 -717 0 - -703 -722 0 - -703 -736 0 - -703 -745 0 - -703 -758 0 - -703 -780 0 - -703 -819 0 - -703 -827 0 - -710 -717 0 - -710 -722 0 - -710 -736 0 - -710 -745 0 - -710 -758 0 - -710 -780 0 - -710 -819 0 - -710 -827 0 - -717 -722 0 - -717 -736 0 - -717 -745 0 - -717 -758 0 - -717 -780 0 - -717 -819 0 - -717 -827 0 - -722 -736 0 - -722 -745 0 - -722 -758 0 - -722 -780 0 - -722 -819 0 - -722 -827 0 - -736 -745 0 - -736 -758 0 - -736 -780 0 - -736 -819 0 - -736 -827 0 - -745 -758 0 - -745 -780 0 - -745 -819 0 - -745 -827 0 - -758 -780 0 - -758 -819 0 - -758 -827 0 - -780 -819 0 - -780 -827 0 - -819 -827 0 - -704 -718 0 - -704 -746 0 - -704 -752 0 - -704 -774 0 - -704 -781 0 - -704 -820 0 - -704 -828 0 - -718 -746 0 - -718 -752 0 - -718 -774 0 - -718 -781 0 - -718 -820 0 - -718 -828 0 - -746 -752 0 - -746 -774 0 - -746 -781 0 - -746 -820 0 - -746 -828 0 - -752 -774 0 - -752 -781 0 - -752 -820 0 - -752 -828 0 - -774 -781 0 - -774 -820 0 - -774 -828 0 - -781 -820 0 - -781 -828 0 - -820 -828 0 - -693 -727 0 - -693 -775 0 - -693 -800 0 - -693 -829 0 - -727 -775 0 - -727 -800 0 - -727 -829 0 - -775 -800 0 - -775 -829 0 - -800 -829 0 - -694 -728 0 - -694 -737 0 - -694 -747 0 - -694 -767 0 - -694 -776 0 - -694 -782 0 - -694 -801 0 - -694 -812 0 - -728 -737 0 - -728 -747 0 - -728 -767 0 - -728 -776 0 - -728 -782 0 - -728 -801 0 - -728 -812 0 - -737 -747 0 - -737 -767 0 - -737 -776 0 - -737 -782 0 - -737 -801 0 - -737 -812 0 - -747 -767 0 - -747 -776 0 - -747 -782 0 - -747 -801 0 - -747 -812 0 - -767 -776 0 - -767 -782 0 - -767 -801 0 - -767 -812 0 - -776 -782 0 - -776 -801 0 - -776 -812 0 - -782 -801 0 - -782 -812 0 - -801 -812 0 - -723 -729 0 - -723 -738 0 - -723 -753 0 - -723 -759 0 - -723 -802 0 - -723 -821 0 - -723 -830 0 - -729 -738 0 - -729 -753 0 - -729 -759 0 - -729 -802 0 - -729 -821 0 - -729 -830 0 - -738 -753 0 - -738 -759 0 - -738 -802 0 - -738 -821 0 - -738 -830 0 - -753 -759 0 - -753 -802 0 - -753 -821 0 - -753 -830 0 - -759 -802 0 - -759 -821 0 - -759 -830 0 - -802 -821 0 - -802 -830 0 - -821 -830 0 - -705 -711 0 - -705 -739 0 - -705 -754 0 - -705 -777 0 - -705 -783 0 - -705 -792 0 - -705 -803 0 - -705 -813 0 - -705 -822 0 - -711 -739 0 - -711 -754 0 - -711 -777 0 - -711 -783 0 - -711 -792 0 - -711 -803 0 - -711 -813 0 - -711 -822 0 - -739 -754 0 - -739 -777 0 - -739 -783 0 - -739 -792 0 - -739 -803 0 - -739 -813 0 - -739 -822 0 - -754 -777 0 - -754 -783 0 - -754 -792 0 - -754 -803 0 - -754 -813 0 - -754 -822 0 - -777 -783 0 - -777 -792 0 - -777 -803 0 - -777 -813 0 - -777 -822 0 - -783 -792 0 - -783 -803 0 - -783 -813 0 - -783 -822 0 - -792 -803 0 - -792 -813 0 - -792 -822 0 - -803 -813 0 - -803 -822 0 - -813 -822 0 - -837 -842 0 - -837 -850 0 - -837 -859 0 - -837 -877 0 - -837 -895 0 - -842 -850 0 - -842 -859 0 - -842 -877 0 - -842 -895 0 - -850 -859 0 - -850 -877 0 - -850 -895 0 - -859 -877 0 - -859 -895 0 - -877 -895 0 - -831 -843 0 - -831 -851 0 - -831 -860 0 - -831 -878 0 - -831 -883 0 - -831 -900 0 - -843 -851 0 - -843 -860 0 - -843 -878 0 - -843 -883 0 - -843 -900 0 - -851 -860 0 - -851 -878 0 - -851 -883 0 - -851 -900 0 - -860 -878 0 - -860 -883 0 - -860 -900 0 - -878 -883 0 - -878 -900 0 - -883 -900 0 - -832 -838 0 - -832 -844 0 - -832 -852 0 - -832 -861 0 - -832 -865 0 - -832 -874 0 - -832 -884 0 - -832 -890 0 - -838 -844 0 - -838 -852 0 - -838 -861 0 - -838 -865 0 - -838 -874 0 - -838 -884 0 - -838 -890 0 - -844 -852 0 - -844 -861 0 - -844 -865 0 - -844 -874 0 - -844 -884 0 - -844 -890 0 - -852 -861 0 - -852 -865 0 - -852 -874 0 - -852 -884 0 - -852 -890 0 - -861 -865 0 - -861 -874 0 - -861 -884 0 - -861 -890 0 - -865 -874 0 - -865 -884 0 - -865 -890 0 - -874 -884 0 - -874 -890 0 - -884 -890 0 - -845 -853 0 - -845 -866 0 - -845 -891 0 - -845 -896 0 - -853 -866 0 - -853 -891 0 - -853 -896 0 - -866 -891 0 - -866 -896 0 - -891 -896 0 - -833 -846 0 - -833 -854 0 - -833 -862 0 - -833 -867 0 - -833 -885 0 - -846 -854 0 - -846 -862 0 - -846 -867 0 - -846 -885 0 - -854 -862 0 - -854 -867 0 - -854 -885 0 - -862 -867 0 - -862 -885 0 - -867 -885 0 - -847 -855 0 - -847 -868 0 - -847 -886 0 - -847 -892 0 - -847 -897 0 - -847 -901 0 - -855 -868 0 - -855 -886 0 - -855 -892 0 - -855 -897 0 - -855 -901 0 - -868 -886 0 - -868 -892 0 - -868 -897 0 - -868 -901 0 - -886 -892 0 - -886 -897 0 - -886 -901 0 - -892 -897 0 - -892 -901 0 - -897 -901 0 - -839 -863 0 - -839 -869 0 - -839 -879 0 - -839 -887 0 - -839 -902 0 - -863 -869 0 - -863 -879 0 - -863 -887 0 - -863 -902 0 - -869 -879 0 - -869 -887 0 - -869 -902 0 - -879 -887 0 - -879 -902 0 - -887 -902 0 - -834 -840 0 - -834 -856 0 - -834 -870 0 - -834 -880 0 - -834 -888 0 - -840 -856 0 - -840 -870 0 - -840 -880 0 - -840 -888 0 - -856 -870 0 - -856 -880 0 - -856 -888 0 - -870 -880 0 - -870 -888 0 - -880 -888 0 - -857 -871 0 - -857 -875 0 - -857 -881 0 - -857 -889 0 - -857 -893 0 - -857 -898 0 - -871 -875 0 - -871 -881 0 - -871 -889 0 - -871 -893 0 - -871 -898 0 - -875 -881 0 - -875 -889 0 - -875 -893 0 - -875 -898 0 - -881 -889 0 - -881 -893 0 - -881 -898 0 - -889 -893 0 - -889 -898 0 - -893 -898 0 - -848 -872 0 - -848 -894 0 - -872 -894 0 - -835 -873 0 - -835 -876 0 - -873 -876 0 - -836 -841 0 - -836 -849 0 - -836 -858 0 - -836 -864 0 - -836 -882 0 - -836 -899 0 - -841 -849 0 - -841 -858 0 - -841 -864 0 - -841 -882 0 - -841 -899 0 - -849 -858 0 - -849 -864 0 - -849 -882 0 - -849 -899 0 - -858 -864 0 - -858 -882 0 - -858 -899 0 - -864 -882 0 - -864 -899 0 - -882 -899 0 - -958 -966 0 - -958 -974 0 - -958 -983 0 - -958 -991 0 - -958 -998 0 - -958 -1015 0 - -958 -1032 0 - -958 -1040 0 - -966 -974 0 - -966 -983 0 - -966 -991 0 - -966 -998 0 - -966 -1015 0 - -966 -1032 0 - -966 -1040 0 - -974 -983 0 - -974 -991 0 - -974 -998 0 - -974 -1015 0 - -974 -1032 0 - -974 -1040 0 - -983 -991 0 - -983 -998 0 - -983 -1015 0 - -983 -1032 0 - -983 -1040 0 - -991 -998 0 - -991 -1015 0 - -991 -1032 0 - -991 -1040 0 - -998 -1015 0 - -998 -1032 0 - -998 -1040 0 - -1015 -1032 0 - -1015 -1040 0 - -1032 -1040 0 - -903 -967 0 - -903 -975 0 - -903 -1004 0 - -903 -1016 0 - -967 -975 0 - -967 -1004 0 - -967 -1016 0 - -975 -1004 0 - -975 -1016 0 - -1004 -1016 0 - -904 -912 0 - -904 -924 0 - -904 -935 0 - -904 -959 0 - -904 -968 0 - -904 -976 0 - -904 -992 0 - -904 -999 0 - -904 -1017 0 - -904 -1025 0 - -904 -1033 0 - -912 -924 0 - -912 -935 0 - -912 -959 0 - -912 -968 0 - -912 -976 0 - -912 -992 0 - -912 -999 0 - -912 -1017 0 - -912 -1025 0 - -912 -1033 0 - -924 -935 0 - -924 -959 0 - -924 -968 0 - -924 -976 0 - -924 -992 0 - -924 -999 0 - -924 -1017 0 - -924 -1025 0 - -924 -1033 0 - -935 -959 0 - -935 -968 0 - -935 -976 0 - -935 -992 0 - -935 -999 0 - -935 -1017 0 - -935 -1025 0 - -935 -1033 0 - -959 -968 0 - -959 -976 0 - -959 -992 0 - -959 -999 0 - -959 -1017 0 - -959 -1025 0 - -959 -1033 0 - -968 -976 0 - -968 -992 0 - -968 -999 0 - -968 -1017 0 - -968 -1025 0 - -968 -1033 0 - -976 -992 0 - -976 -999 0 - -976 -1017 0 - -976 -1025 0 - -976 -1033 0 - -992 -999 0 - -992 -1017 0 - -992 -1025 0 - -992 -1033 0 - -999 -1017 0 - -999 -1025 0 - -999 -1033 0 - -1017 -1025 0 - -1017 -1033 0 - -1025 -1033 0 - -905 -951 0 - -905 -984 0 - -905 -1005 0 - -905 -1026 0 - -905 -1041 0 - -951 -984 0 - -951 -1005 0 - -951 -1026 0 - -951 -1041 0 - -984 -1005 0 - -984 -1026 0 - -984 -1041 0 - -1005 -1026 0 - -1005 -1041 0 - -1026 -1041 0 - -906 -913 0 - -906 -925 0 - -906 -936 0 - -906 -943 0 - -906 -969 0 - -906 -1000 0 - -906 -1006 0 - -906 -1018 0 - -906 -1027 0 - -906 -1042 0 - -913 -925 0 - -913 -936 0 - -913 -943 0 - -913 -969 0 - -913 -1000 0 - -913 -1006 0 - -913 -1018 0 - -913 -1027 0 - -913 -1042 0 - -925 -936 0 - -925 -943 0 - -925 -969 0 - -925 -1000 0 - -925 -1006 0 - -925 -1018 0 - -925 -1027 0 - -925 -1042 0 - -936 -943 0 - -936 -969 0 - -936 -1000 0 - -936 -1006 0 - -936 -1018 0 - -936 -1027 0 - -936 -1042 0 - -943 -969 0 - -943 -1000 0 - -943 -1006 0 - -943 -1018 0 - -943 -1027 0 - -943 -1042 0 - -969 -1000 0 - -969 -1006 0 - -969 -1018 0 - -969 -1027 0 - -969 -1042 0 - -1000 -1006 0 - -1000 -1018 0 - -1000 -1027 0 - -1000 -1042 0 - -1006 -1018 0 - -1006 -1027 0 - -1006 -1042 0 - -1018 -1027 0 - -1018 -1042 0 - -1027 -1042 0 - -907 -914 0 - -907 -919 0 - -907 -952 0 - -907 -960 0 - -907 -970 0 - -907 -977 0 - -907 -1019 0 - -907 -1034 0 - -907 -1043 0 - -914 -919 0 - -914 -952 0 - -914 -960 0 - -914 -970 0 - -914 -977 0 - -914 -1019 0 - -914 -1034 0 - -914 -1043 0 - -919 -952 0 - -919 -960 0 - -919 -970 0 - -919 -977 0 - -919 -1019 0 - -919 -1034 0 - -919 -1043 0 - -952 -960 0 - -952 -970 0 - -952 -977 0 - -952 -1019 0 - -952 -1034 0 - -952 -1043 0 - -960 -970 0 - -960 -977 0 - -960 -1019 0 - -960 -1034 0 - -960 -1043 0 - -970 -977 0 - -970 -1019 0 - -970 -1034 0 - -970 -1043 0 - -977 -1019 0 - -977 -1034 0 - -977 -1043 0 - -1019 -1034 0 - -1019 -1043 0 - -1034 -1043 0 - -908 -915 0 - -908 -926 0 - -908 -953 0 - -908 -985 0 - -908 -1007 0 - -908 -1020 0 - -908 -1028 0 - -915 -926 0 - -915 -953 0 - -915 -985 0 - -915 -1007 0 - -915 -1020 0 - -915 -1028 0 - -926 -953 0 - -926 -985 0 - -926 -1007 0 - -926 -1020 0 - -926 -1028 0 - -953 -985 0 - -953 -1007 0 - -953 -1020 0 - -953 -1028 0 - -985 -1007 0 - -985 -1020 0 - -985 -1028 0 - -1007 -1020 0 - -1007 -1028 0 - -1020 -1028 0 - -920 -927 0 - -920 -937 0 - -920 -944 0 - -920 -961 0 - -920 -971 0 - -920 -978 0 - -920 -986 0 - -920 -1008 0 - -920 -1021 0 - -927 -937 0 - -927 -944 0 - -927 -961 0 - -927 -971 0 - -927 -978 0 - -927 -986 0 - -927 -1008 0 - -927 -1021 0 - -937 -944 0 - -937 -961 0 - -937 -971 0 - -937 -978 0 - -937 -986 0 - -937 -1008 0 - -937 -1021 0 - -944 -961 0 - -944 -971 0 - -944 -978 0 - -944 -986 0 - -944 -1008 0 - -944 -1021 0 - -961 -971 0 - -961 -978 0 - -961 -986 0 - -961 -1008 0 - -961 -1021 0 - -971 -978 0 - -971 -986 0 - -971 -1008 0 - -971 -1021 0 - -978 -986 0 - -978 -1008 0 - -978 -1021 0 - -986 -1008 0 - -986 -1021 0 - -1008 -1021 0 - -909 -916 0 - -909 -928 0 - -909 -938 0 - -909 -972 0 - -909 -1035 0 - -909 -1044 0 - -916 -928 0 - -916 -938 0 - -916 -972 0 - -916 -1035 0 - -916 -1044 0 - -928 -938 0 - -928 -972 0 - -928 -1035 0 - -928 -1044 0 - -938 -972 0 - -938 -1035 0 - -938 -1044 0 - -972 -1035 0 - -972 -1044 0 - -1035 -1044 0 - -917 -939 0 - -917 -945 0 - -917 -979 0 - -917 -1001 0 - -917 -1009 0 - -917 -1022 0 - -917 -1029 0 - -917 -1036 0 - -939 -945 0 - -939 -979 0 - -939 -1001 0 - -939 -1009 0 - -939 -1022 0 - -939 -1029 0 - -939 -1036 0 - -945 -979 0 - -945 -1001 0 - -945 -1009 0 - -945 -1022 0 - -945 -1029 0 - -945 -1036 0 - -979 -1001 0 - -979 -1009 0 - -979 -1022 0 - -979 -1029 0 - -979 -1036 0 - -1001 -1009 0 - -1001 -1022 0 - -1001 -1029 0 - -1001 -1036 0 - -1009 -1022 0 - -1009 -1029 0 - -1009 -1036 0 - -1022 -1029 0 - -1022 -1036 0 - -1029 -1036 0 - -946 -987 0 - -946 -993 0 - -946 -1023 0 - -946 -1045 0 - -987 -993 0 - -987 -1023 0 - -987 -1045 0 - -993 -1023 0 - -993 -1045 0 - -1023 -1045 0 - -918 -929 0 - -918 -940 0 - -918 -947 0 - -918 -962 0 - -918 -988 0 - -918 -994 0 - -918 -1010 0 - -918 -1037 0 - -929 -940 0 - -929 -947 0 - -929 -962 0 - -929 -988 0 - -929 -994 0 - -929 -1010 0 - -929 -1037 0 - -940 -947 0 - -940 -962 0 - -940 -988 0 - -940 -994 0 - -940 -1010 0 - -940 -1037 0 - -947 -962 0 - -947 -988 0 - -947 -994 0 - -947 -1010 0 - -947 -1037 0 - -962 -988 0 - -962 -994 0 - -962 -1010 0 - -962 -1037 0 - -988 -994 0 - -988 -1010 0 - -988 -1037 0 - -994 -1010 0 - -994 -1037 0 - -1010 -1037 0 - -921 -930 0 - -921 -941 0 - -921 -954 0 - -921 -963 0 - -921 -980 0 - -921 -995 0 - -921 -1030 0 - -930 -941 0 - -930 -954 0 - -930 -963 0 - -930 -980 0 - -930 -995 0 - -930 -1030 0 - -941 -954 0 - -941 -963 0 - -941 -980 0 - -941 -995 0 - -941 -1030 0 - -954 -963 0 - -954 -980 0 - -954 -995 0 - -954 -1030 0 - -963 -980 0 - -963 -995 0 - -963 -1030 0 - -980 -995 0 - -980 -1030 0 - -995 -1030 0 - -931 -1031 0 - -931 -1038 0 - -1031 -1038 0 - -932 -948 0 - -932 -955 0 - -932 -964 0 - -932 -981 0 - -932 -989 0 - -932 -996 0 - -932 -1002 0 - -932 -1011 0 - -948 -955 0 - -948 -964 0 - -948 -981 0 - -948 -989 0 - -948 -996 0 - -948 -1002 0 - -948 -1011 0 - -955 -964 0 - -955 -981 0 - -955 -989 0 - -955 -996 0 - -955 -1002 0 - -955 -1011 0 - -964 -981 0 - -964 -989 0 - -964 -996 0 - -964 -1002 0 - -964 -1011 0 - -981 -989 0 - -981 -996 0 - -981 -1002 0 - -981 -1011 0 - -989 -996 0 - -989 -1002 0 - -989 -1011 0 - -996 -1002 0 - -996 -1011 0 - -1002 -1011 0 - -910 -949 0 - -910 -956 0 - -910 -965 0 - -910 -997 0 - -910 -1012 0 - -910 -1039 0 - -949 -956 0 - -949 -965 0 - -949 -997 0 - -949 -1012 0 - -949 -1039 0 - -956 -965 0 - -956 -997 0 - -956 -1012 0 - -956 -1039 0 - -965 -997 0 - -965 -1012 0 - -965 -1039 0 - -997 -1012 0 - -997 -1039 0 - -1012 -1039 0 - -922 -933 0 - -922 -950 0 - -922 -990 0 - -922 -1003 0 - -922 -1013 0 - -922 -1024 0 - -933 -950 0 - -933 -990 0 - -933 -1003 0 - -933 -1013 0 - -933 -1024 0 - -950 -990 0 - -950 -1003 0 - -950 -1013 0 - -950 -1024 0 - -990 -1003 0 - -990 -1013 0 - -990 -1024 0 - -1003 -1013 0 - -1003 -1024 0 - -1013 -1024 0 - -911 -923 0 - -911 -934 0 - -911 -942 0 - -911 -957 0 - -911 -973 0 - -911 -982 0 - -911 -1014 0 - -923 -934 0 - -923 -942 0 - -923 -957 0 - -923 -973 0 - -923 -982 0 - -923 -1014 0 - -934 -942 0 - -934 -957 0 - -934 -973 0 - -934 -982 0 - -934 -1014 0 - -942 -957 0 - -942 -973 0 - -942 -982 0 - -942 -1014 0 - -957 -973 0 - -957 -982 0 - -957 -1014 0 - -973 -982 0 - -973 -1014 0 - -982 -1014 0 - -1046 -1059 0 - -1046 -1066 0 - -1046 -1080 0 - -1046 -1110 0 - -1046 -1119 0 - -1046 -1125 0 - -1046 -1143 0 - -1046 -1157 0 - -1046 -1176 0 - -1046 -1186 0 - -1046 -1202 0 - -1059 -1066 0 - -1059 -1080 0 - -1059 -1110 0 - -1059 -1119 0 - -1059 -1125 0 - -1059 -1143 0 - -1059 -1157 0 - -1059 -1176 0 - -1059 -1186 0 - -1059 -1202 0 - -1066 -1080 0 - -1066 -1110 0 - -1066 -1119 0 - -1066 -1125 0 - -1066 -1143 0 - -1066 -1157 0 - -1066 -1176 0 - -1066 -1186 0 - -1066 -1202 0 - -1080 -1110 0 - -1080 -1119 0 - -1080 -1125 0 - -1080 -1143 0 - -1080 -1157 0 - -1080 -1176 0 - -1080 -1186 0 - -1080 -1202 0 - -1110 -1119 0 - -1110 -1125 0 - -1110 -1143 0 - -1110 -1157 0 - -1110 -1176 0 - -1110 -1186 0 - -1110 -1202 0 - -1119 -1125 0 - -1119 -1143 0 - -1119 -1157 0 - -1119 -1176 0 - -1119 -1186 0 - -1119 -1202 0 - -1125 -1143 0 - -1125 -1157 0 - -1125 -1176 0 - -1125 -1186 0 - -1125 -1202 0 - -1143 -1157 0 - -1143 -1176 0 - -1143 -1186 0 - -1143 -1202 0 - -1157 -1176 0 - -1157 -1186 0 - -1157 -1202 0 - -1176 -1186 0 - -1176 -1202 0 - -1186 -1202 0 - -1047 -1067 0 - -1047 -1104 0 - -1047 -1111 0 - -1047 -1120 0 - -1047 -1126 0 - -1047 -1136 0 - -1047 -1144 0 - -1047 -1166 0 - -1047 -1177 0 - -1067 -1104 0 - -1067 -1111 0 - -1067 -1120 0 - -1067 -1126 0 - -1067 -1136 0 - -1067 -1144 0 - -1067 -1166 0 - -1067 -1177 0 - -1104 -1111 0 - -1104 -1120 0 - -1104 -1126 0 - -1104 -1136 0 - -1104 -1144 0 - -1104 -1166 0 - -1104 -1177 0 - -1111 -1120 0 - -1111 -1126 0 - -1111 -1136 0 - -1111 -1144 0 - -1111 -1166 0 - -1111 -1177 0 - -1120 -1126 0 - -1120 -1136 0 - -1120 -1144 0 - -1120 -1166 0 - -1120 -1177 0 - -1126 -1136 0 - -1126 -1144 0 - -1126 -1166 0 - -1126 -1177 0 - -1136 -1144 0 - -1136 -1166 0 - -1136 -1177 0 - -1144 -1166 0 - -1144 -1177 0 - -1166 -1177 0 - -1048 -1068 0 - -1048 -1081 0 - -1048 -1112 0 - -1048 -1137 0 - -1048 -1158 0 - -1048 -1167 0 - -1048 -1187 0 - -1068 -1081 0 - -1068 -1112 0 - -1068 -1137 0 - -1068 -1158 0 - -1068 -1167 0 - -1068 -1187 0 - -1081 -1112 0 - -1081 -1137 0 - -1081 -1158 0 - -1081 -1167 0 - -1081 -1187 0 - -1112 -1137 0 - -1112 -1158 0 - -1112 -1167 0 - -1112 -1187 0 - -1137 -1158 0 - -1137 -1167 0 - -1137 -1187 0 - -1158 -1167 0 - -1158 -1187 0 - -1167 -1187 0 - -1049 -1069 0 - -1049 -1082 0 - -1049 -1089 0 - -1049 -1138 0 - -1049 -1188 0 - -1049 -1197 0 - -1069 -1082 0 - -1069 -1089 0 - -1069 -1138 0 - -1069 -1188 0 - -1069 -1197 0 - -1082 -1089 0 - -1082 -1138 0 - -1082 -1188 0 - -1082 -1197 0 - -1089 -1138 0 - -1089 -1188 0 - -1089 -1197 0 - -1138 -1188 0 - -1138 -1197 0 - -1188 -1197 0 - -1070 -1083 0 - -1070 -1113 0 - -1070 -1121 0 - -1070 -1127 0 - -1070 -1151 0 - -1070 -1178 0 - -1070 -1189 0 - -1083 -1113 0 - -1083 -1121 0 - -1083 -1127 0 - -1083 -1151 0 - -1083 -1178 0 - -1083 -1189 0 - -1113 -1121 0 - -1113 -1127 0 - -1113 -1151 0 - -1113 -1178 0 - -1113 -1189 0 - -1121 -1127 0 - -1121 -1151 0 - -1121 -1178 0 - -1121 -1189 0 - -1127 -1151 0 - -1127 -1178 0 - -1127 -1189 0 - -1151 -1178 0 - -1151 -1189 0 - -1178 -1189 0 - -1050 -1060 0 - -1050 -1090 0 - -1050 -1095 0 - -1050 -1114 0 - -1050 -1122 0 - -1050 -1128 0 - -1050 -1139 0 - -1050 -1145 0 - -1050 -1168 0 - -1050 -1179 0 - -1050 -1190 0 - -1060 -1090 0 - -1060 -1095 0 - -1060 -1114 0 - -1060 -1122 0 - -1060 -1128 0 - -1060 -1139 0 - -1060 -1145 0 - -1060 -1168 0 - -1060 -1179 0 - -1060 -1190 0 - -1090 -1095 0 - -1090 -1114 0 - -1090 -1122 0 - -1090 -1128 0 - -1090 -1139 0 - -1090 -1145 0 - -1090 -1168 0 - -1090 -1179 0 - -1090 -1190 0 - -1095 -1114 0 - -1095 -1122 0 - -1095 -1128 0 - -1095 -1139 0 - -1095 -1145 0 - -1095 -1168 0 - -1095 -1179 0 - -1095 -1190 0 - -1114 -1122 0 - -1114 -1128 0 - -1114 -1139 0 - -1114 -1145 0 - -1114 -1168 0 - -1114 -1179 0 - -1114 -1190 0 - -1122 -1128 0 - -1122 -1139 0 - -1122 -1145 0 - -1122 -1168 0 - -1122 -1179 0 - -1122 -1190 0 - -1128 -1139 0 - -1128 -1145 0 - -1128 -1168 0 - -1128 -1179 0 - -1128 -1190 0 - -1139 -1145 0 - -1139 -1168 0 - -1139 -1179 0 - -1139 -1190 0 - -1145 -1168 0 - -1145 -1179 0 - -1145 -1190 0 - -1168 -1179 0 - -1168 -1190 0 - -1179 -1190 0 - -1051 -1071 0 - -1051 -1084 0 - -1051 -1096 0 - -1051 -1105 0 - -1051 -1152 0 - -1051 -1159 0 - -1051 -1169 0 - -1051 -1180 0 - -1071 -1084 0 - -1071 -1096 0 - -1071 -1105 0 - -1071 -1152 0 - -1071 -1159 0 - -1071 -1169 0 - -1071 -1180 0 - -1084 -1096 0 - -1084 -1105 0 - -1084 -1152 0 - -1084 -1159 0 - -1084 -1169 0 - -1084 -1180 0 - -1096 -1105 0 - -1096 -1152 0 - -1096 -1159 0 - -1096 -1169 0 - -1096 -1180 0 - -1105 -1152 0 - -1105 -1159 0 - -1105 -1169 0 - -1105 -1180 0 - -1152 -1159 0 - -1152 -1169 0 - -1152 -1180 0 - -1159 -1169 0 - -1159 -1180 0 - -1169 -1180 0 - -1061 -1072 0 - -1061 -1091 0 - -1061 -1097 0 - -1061 -1123 0 - -1061 -1129 0 - -1061 -1140 0 - -1061 -1160 0 - -1061 -1170 0 - -1072 -1091 0 - -1072 -1097 0 - -1072 -1123 0 - -1072 -1129 0 - -1072 -1140 0 - -1072 -1160 0 - -1072 -1170 0 - -1091 -1097 0 - -1091 -1123 0 - -1091 -1129 0 - -1091 -1140 0 - -1091 -1160 0 - -1091 -1170 0 - -1097 -1123 0 - -1097 -1129 0 - -1097 -1140 0 - -1097 -1160 0 - -1097 -1170 0 - -1123 -1129 0 - -1123 -1140 0 - -1123 -1160 0 - -1123 -1170 0 - -1129 -1140 0 - -1129 -1160 0 - -1129 -1170 0 - -1140 -1160 0 - -1140 -1170 0 - -1160 -1170 0 - -1052 -1073 0 - -1052 -1092 0 - -1052 -1098 0 - -1052 -1141 0 - -1052 -1146 0 - -1052 -1161 0 - -1052 -1191 0 - -1073 -1092 0 - -1073 -1098 0 - -1073 -1141 0 - -1073 -1146 0 - -1073 -1161 0 - -1073 -1191 0 - -1092 -1098 0 - -1092 -1141 0 - -1092 -1146 0 - -1092 -1161 0 - -1092 -1191 0 - -1098 -1141 0 - -1098 -1146 0 - -1098 -1161 0 - -1098 -1191 0 - -1141 -1146 0 - -1141 -1161 0 - -1141 -1191 0 - -1146 -1161 0 - -1146 -1191 0 - -1161 -1191 0 - -1053 -1062 0 - -1053 -1099 0 - -1053 -1106 0 - -1053 -1115 0 - -1053 -1147 0 - -1053 -1171 0 - -1053 -1192 0 - -1053 -1203 0 - -1062 -1099 0 - -1062 -1106 0 - -1062 -1115 0 - -1062 -1147 0 - -1062 -1171 0 - -1062 -1192 0 - -1062 -1203 0 - -1099 -1106 0 - -1099 -1115 0 - -1099 -1147 0 - -1099 -1171 0 - -1099 -1192 0 - -1099 -1203 0 - -1106 -1115 0 - -1106 -1147 0 - -1106 -1171 0 - -1106 -1192 0 - -1106 -1203 0 - -1115 -1147 0 - -1115 -1171 0 - -1115 -1192 0 - -1115 -1203 0 - -1147 -1171 0 - -1147 -1192 0 - -1147 -1203 0 - -1171 -1192 0 - -1171 -1203 0 - -1192 -1203 0 - -1054 -1093 0 - -1054 -1130 0 - -1054 -1148 0 - -1054 -1162 0 - -1054 -1172 0 - -1054 -1181 0 - -1093 -1130 0 - -1093 -1148 0 - -1093 -1162 0 - -1093 -1172 0 - -1093 -1181 0 - -1130 -1148 0 - -1130 -1162 0 - -1130 -1172 0 - -1130 -1181 0 - -1148 -1162 0 - -1148 -1172 0 - -1148 -1181 0 - -1162 -1172 0 - -1162 -1181 0 - -1172 -1181 0 - -1074 -1085 0 - -1074 -1107 0 - -1074 -1116 0 - -1074 -1124 0 - -1074 -1131 0 - -1074 -1163 0 - -1074 -1182 0 - -1074 -1198 0 - -1074 -1204 0 - -1085 -1107 0 - -1085 -1116 0 - -1085 -1124 0 - -1085 -1131 0 - -1085 -1163 0 - -1085 -1182 0 - -1085 -1198 0 - -1085 -1204 0 - -1107 -1116 0 - -1107 -1124 0 - -1107 -1131 0 - -1107 -1163 0 - -1107 -1182 0 - -1107 -1198 0 - -1107 -1204 0 - -1116 -1124 0 - -1116 -1131 0 - -1116 -1163 0 - -1116 -1182 0 - -1116 -1198 0 - -1116 -1204 0 - -1124 -1131 0 - -1124 -1163 0 - -1124 -1182 0 - -1124 -1198 0 - -1124 -1204 0 - -1131 -1163 0 - -1131 -1182 0 - -1131 -1198 0 - -1131 -1204 0 - -1163 -1182 0 - -1163 -1198 0 - -1163 -1204 0 - -1182 -1198 0 - -1182 -1204 0 - -1198 -1204 0 - -1086 -1132 0 - -1086 -1153 0 - -1086 -1173 0 - -1086 -1183 0 - -1086 -1199 0 - -1132 -1153 0 - -1132 -1173 0 - -1132 -1183 0 - -1132 -1199 0 - -1153 -1173 0 - -1153 -1183 0 - -1153 -1199 0 - -1173 -1183 0 - -1173 -1199 0 - -1183 -1199 0 - -1055 -1075 0 - -1055 -1108 0 - -1055 -1117 0 - -1055 -1133 0 - -1055 -1154 0 - -1055 -1184 0 - -1055 -1200 0 - -1075 -1108 0 - -1075 -1117 0 - -1075 -1133 0 - -1075 -1154 0 - -1075 -1184 0 - -1075 -1200 0 - -1108 -1117 0 - -1108 -1133 0 - -1108 -1154 0 - -1108 -1184 0 - -1108 -1200 0 - -1117 -1133 0 - -1117 -1154 0 - -1117 -1184 0 - -1117 -1200 0 - -1133 -1154 0 - -1133 -1184 0 - -1133 -1200 0 - -1154 -1184 0 - -1154 -1200 0 - -1184 -1200 0 - -1076 -1087 0 - -1076 -1100 0 - -1076 -1109 0 - -1076 -1142 0 - -1076 -1193 0 - -1076 -1205 0 - -1087 -1100 0 - -1087 -1109 0 - -1087 -1142 0 - -1087 -1193 0 - -1087 -1205 0 - -1100 -1109 0 - -1100 -1142 0 - -1100 -1193 0 - -1100 -1205 0 - -1109 -1142 0 - -1109 -1193 0 - -1109 -1205 0 - -1142 -1193 0 - -1142 -1205 0 - -1193 -1205 0 - -1063 -1077 0 - -1063 -1088 0 - -1063 -1155 0 - -1063 -1164 0 - -1063 -1194 0 - -1077 -1088 0 - -1077 -1155 0 - -1077 -1164 0 - -1077 -1194 0 - -1088 -1155 0 - -1088 -1164 0 - -1088 -1194 0 - -1155 -1164 0 - -1155 -1194 0 - -1164 -1194 0 - -1056 -1078 0 - -1056 -1101 0 - -1056 -1118 0 - -1056 -1134 0 - -1056 -1174 0 - -1056 -1195 0 - -1078 -1101 0 - -1078 -1118 0 - -1078 -1134 0 - -1078 -1174 0 - -1078 -1195 0 - -1101 -1118 0 - -1101 -1134 0 - -1101 -1174 0 - -1101 -1195 0 - -1118 -1134 0 - -1118 -1174 0 - -1118 -1195 0 - -1134 -1174 0 - -1134 -1195 0 - -1174 -1195 0 - -1057 -1064 0 - -1057 -1102 0 - -1057 -1149 0 - -1057 -1156 0 - -1057 -1165 0 - -1057 -1175 0 - -1057 -1185 0 - -1057 -1196 0 - -1057 -1201 0 - -1064 -1102 0 - -1064 -1149 0 - -1064 -1156 0 - -1064 -1165 0 - -1064 -1175 0 - -1064 -1185 0 - -1064 -1196 0 - -1064 -1201 0 - -1102 -1149 0 - -1102 -1156 0 - -1102 -1165 0 - -1102 -1175 0 - -1102 -1185 0 - -1102 -1196 0 - -1102 -1201 0 - -1149 -1156 0 - -1149 -1165 0 - -1149 -1175 0 - -1149 -1185 0 - -1149 -1196 0 - -1149 -1201 0 - -1156 -1165 0 - -1156 -1175 0 - -1156 -1185 0 - -1156 -1196 0 - -1156 -1201 0 - -1165 -1175 0 - -1165 -1185 0 - -1165 -1196 0 - -1165 -1201 0 - -1175 -1185 0 - -1175 -1196 0 - -1175 -1201 0 - -1185 -1196 0 - -1185 -1201 0 - -1196 -1201 0 - -1058 -1065 0 - -1058 -1079 0 - -1058 -1094 0 - -1058 -1103 0 - -1058 -1135 0 - -1058 -1150 0 - -1058 -1206 0 - -1065 -1079 0 - -1065 -1094 0 - -1065 -1103 0 - -1065 -1135 0 - -1065 -1150 0 - -1065 -1206 0 - -1079 -1094 0 - -1079 -1103 0 - -1079 -1135 0 - -1079 -1150 0 - -1079 -1206 0 - -1094 -1103 0 - -1094 -1135 0 - -1094 -1150 0 - -1094 -1206 0 - -1103 -1135 0 - -1103 -1150 0 - -1103 -1206 0 - -1135 -1150 0 - -1135 -1206 0 - -1150 -1206 0 - -1216 -1228 0 - -1216 -1249 0 - -1216 -1254 0 - -1216 -1262 0 - -1228 -1249 0 - -1228 -1254 0 - -1228 -1262 0 - -1249 -1254 0 - -1249 -1262 0 - -1254 -1262 0 - -1212 -1217 0 - -1212 -1222 0 - -1212 -1229 0 - -1212 -1282 0 - -1217 -1222 0 - -1217 -1229 0 - -1217 -1282 0 - -1222 -1229 0 - -1222 -1282 0 - -1229 -1282 0 - -1207 -1213 0 - -1207 -1236 0 - -1207 -1255 0 - -1207 -1263 0 - -1207 -1269 0 - -1207 -1274 0 - -1207 -1283 0 - -1213 -1236 0 - -1213 -1255 0 - -1213 -1263 0 - -1213 -1269 0 - -1213 -1274 0 - -1213 -1283 0 - -1236 -1255 0 - -1236 -1263 0 - -1236 -1269 0 - -1236 -1274 0 - -1236 -1283 0 - -1255 -1263 0 - -1255 -1269 0 - -1255 -1274 0 - -1255 -1283 0 - -1263 -1269 0 - -1263 -1274 0 - -1263 -1283 0 - -1269 -1274 0 - -1269 -1283 0 - -1274 -1283 0 - -1237 -1264 0 - -1237 -1270 0 - -1237 -1275 0 - -1237 -1284 0 - -1264 -1270 0 - -1264 -1275 0 - -1264 -1284 0 - -1270 -1275 0 - -1270 -1284 0 - -1275 -1284 0 - -1208 -1223 0 - -1208 -1230 0 - -1208 -1238 0 - -1208 -1244 0 - -1208 -1250 0 - -1208 -1276 0 - -1223 -1230 0 - -1223 -1238 0 - -1223 -1244 0 - -1223 -1250 0 - -1223 -1276 0 - -1230 -1238 0 - -1230 -1244 0 - -1230 -1250 0 - -1230 -1276 0 - -1238 -1244 0 - -1238 -1250 0 - -1238 -1276 0 - -1244 -1250 0 - -1244 -1276 0 - -1250 -1276 0 - -1214 -1231 0 - -1214 -1245 0 - -1214 -1251 0 - -1214 -1256 0 - -1214 -1265 0 - -1214 -1271 0 - -1214 -1277 0 - -1231 -1245 0 - -1231 -1251 0 - -1231 -1256 0 - -1231 -1265 0 - -1231 -1271 0 - -1231 -1277 0 - -1245 -1251 0 - -1245 -1256 0 - -1245 -1265 0 - -1245 -1271 0 - -1245 -1277 0 - -1251 -1256 0 - -1251 -1265 0 - -1251 -1271 0 - -1251 -1277 0 - -1256 -1265 0 - -1256 -1271 0 - -1256 -1277 0 - -1265 -1271 0 - -1265 -1277 0 - -1271 -1277 0 - -1218 -1239 0 - -1218 -1246 0 - -1218 -1252 0 - -1218 -1257 0 - -1218 -1285 0 - -1239 -1246 0 - -1239 -1252 0 - -1239 -1257 0 - -1239 -1285 0 - -1246 -1252 0 - -1246 -1257 0 - -1246 -1285 0 - -1252 -1257 0 - -1252 -1285 0 - -1257 -1285 0 - -1219 -1232 0 - -1219 -1258 0 - -1219 -1266 0 - -1219 -1278 0 - -1232 -1258 0 - -1232 -1266 0 - -1232 -1278 0 - -1258 -1266 0 - -1258 -1278 0 - -1266 -1278 0 - -1215 -1224 0 - -1215 -1233 0 - -1215 -1247 0 - -1215 -1272 0 - -1215 -1279 0 - -1215 -1286 0 - -1224 -1233 0 - -1224 -1247 0 - -1224 -1272 0 - -1224 -1279 0 - -1224 -1286 0 - -1233 -1247 0 - -1233 -1272 0 - -1233 -1279 0 - -1233 -1286 0 - -1247 -1272 0 - -1247 -1279 0 - -1247 -1286 0 - -1272 -1279 0 - -1272 -1286 0 - -1279 -1286 0 - -1209 -1220 0 - -1209 -1225 0 - -1209 -1240 0 - -1209 -1248 0 - -1220 -1225 0 - -1220 -1240 0 - -1220 -1248 0 - -1225 -1240 0 - -1225 -1248 0 - -1240 -1248 0 - -1210 -1221 0 - -1210 -1234 0 - -1210 -1241 0 - -1210 -1259 0 - -1210 -1267 0 - -1210 -1273 0 - -1210 -1280 0 - -1210 -1287 0 - -1221 -1234 0 - -1221 -1241 0 - -1221 -1259 0 - -1221 -1267 0 - -1221 -1273 0 - -1221 -1280 0 - -1221 -1287 0 - -1234 -1241 0 - -1234 -1259 0 - -1234 -1267 0 - -1234 -1273 0 - -1234 -1280 0 - -1234 -1287 0 - -1241 -1259 0 - -1241 -1267 0 - -1241 -1273 0 - -1241 -1280 0 - -1241 -1287 0 - -1259 -1267 0 - -1259 -1273 0 - -1259 -1280 0 - -1259 -1287 0 - -1267 -1273 0 - -1267 -1280 0 - -1267 -1287 0 - -1273 -1280 0 - -1273 -1287 0 - -1280 -1287 0 - -1226 -1235 0 - -1226 -1242 0 - -1226 -1260 0 - -1226 -1281 0 - -1235 -1242 0 - -1235 -1260 0 - -1235 -1281 0 - -1242 -1260 0 - -1242 -1281 0 - -1260 -1281 0 - -1211 -1227 0 - -1211 -1243 0 - -1211 -1253 0 - -1211 -1261 0 - -1211 -1268 0 - -1211 -1288 0 - -1227 -1243 0 - -1227 -1253 0 - -1227 -1261 0 - -1227 -1268 0 - -1227 -1288 0 - -1243 -1253 0 - -1243 -1261 0 - -1243 -1268 0 - -1243 -1288 0 - -1253 -1261 0 - -1253 -1268 0 - -1253 -1288 0 - -1261 -1268 0 - -1261 -1288 0 - -1268 -1288 0 - -1311 -1331 0 - -1311 -1344 0 - -1331 -1344 0 - -1320 -1332 0 - -1320 -1341 0 - -1320 -1349 0 - -1320 -1355 0 - -1320 -1360 0 - -1332 -1341 0 - -1332 -1349 0 - -1332 -1355 0 - -1332 -1360 0 - -1341 -1349 0 - -1341 -1355 0 - -1341 -1360 0 - -1349 -1355 0 - -1349 -1360 0 - -1355 -1360 0 - -1304 -1326 0 - -1304 -1333 0 - -1304 -1350 0 - -1304 -1356 0 - -1326 -1333 0 - -1326 -1350 0 - -1326 -1356 0 - -1333 -1350 0 - -1333 -1356 0 - -1350 -1356 0 - -1296 -1321 0 - -1296 -1345 0 - -1321 -1345 0 - -1289 -1297 0 - -1289 -1314 0 - -1289 -1322 0 - -1289 -1327 0 - -1289 -1334 0 - -1289 -1351 0 - -1289 -1357 0 - -1297 -1314 0 - -1297 -1322 0 - -1297 -1327 0 - -1297 -1334 0 - -1297 -1351 0 - -1297 -1357 0 - -1314 -1322 0 - -1314 -1327 0 - -1314 -1334 0 - -1314 -1351 0 - -1314 -1357 0 - -1322 -1327 0 - -1322 -1334 0 - -1322 -1351 0 - -1322 -1357 0 - -1327 -1334 0 - -1327 -1351 0 - -1327 -1357 0 - -1334 -1351 0 - -1334 -1357 0 - -1351 -1357 0 - -1290 -1298 0 - -1290 -1315 0 - -1290 -1335 0 - -1290 -1346 0 - -1290 -1352 0 - -1290 -1358 0 - -1298 -1315 0 - -1298 -1335 0 - -1298 -1346 0 - -1298 -1352 0 - -1298 -1358 0 - -1315 -1335 0 - -1315 -1346 0 - -1315 -1352 0 - -1315 -1358 0 - -1335 -1346 0 - -1335 -1352 0 - -1335 -1358 0 - -1346 -1352 0 - -1346 -1358 0 - -1352 -1358 0 - -1299 -1305 0 - -1299 -1316 0 - -1299 -1328 0 - -1299 -1336 0 - -1305 -1316 0 - -1305 -1328 0 - -1305 -1336 0 - -1316 -1328 0 - -1316 -1336 0 - -1328 -1336 0 - -1291 -1306 0 - -1291 -1317 0 - -1291 -1329 0 - -1291 -1337 0 - -1291 -1342 0 - -1306 -1317 0 - -1306 -1329 0 - -1306 -1337 0 - -1306 -1342 0 - -1317 -1329 0 - -1317 -1337 0 - -1317 -1342 0 - -1329 -1337 0 - -1329 -1342 0 - -1337 -1342 0 - -1292 -1312 0 - -1292 -1318 0 - -1292 -1338 0 - -1292 -1347 0 - -1312 -1318 0 - -1312 -1338 0 - -1312 -1347 0 - -1318 -1338 0 - -1318 -1347 0 - -1338 -1347 0 - -1300 -1302 0 - -1300 -1307 0 - -1300 -1323 0 - -1300 -1330 0 - -1300 -1339 0 - -1300 -1353 0 - -1300 -1361 0 - -1302 -1307 0 - -1302 -1323 0 - -1302 -1330 0 - -1302 -1339 0 - -1302 -1353 0 - -1302 -1361 0 - -1307 -1323 0 - -1307 -1330 0 - -1307 -1339 0 - -1307 -1353 0 - -1307 -1361 0 - -1323 -1330 0 - -1323 -1339 0 - -1323 -1353 0 - -1323 -1361 0 - -1330 -1339 0 - -1330 -1353 0 - -1330 -1361 0 - -1339 -1353 0 - -1339 -1361 0 - -1353 -1361 0 - -1293 -1313 0 - -1293 -1319 0 - -1293 -1362 0 - -1313 -1319 0 - -1313 -1362 0 - -1319 -1362 0 - -1308 -1324 0 - -1294 -1303 0 - -1294 -1309 0 - -1303 -1309 0 - -1295 -1301 0 - -1295 -1310 0 - -1295 -1325 0 - -1295 -1340 0 - -1295 -1343 0 - -1295 -1348 0 - -1295 -1354 0 - -1295 -1359 0 - -1301 -1310 0 - -1301 -1325 0 - -1301 -1340 0 - -1301 -1343 0 - -1301 -1348 0 - -1301 -1354 0 - -1301 -1359 0 - -1310 -1325 0 - -1310 -1340 0 - -1310 -1343 0 - -1310 -1348 0 - -1310 -1354 0 - -1310 -1359 0 - -1325 -1340 0 - -1325 -1343 0 - -1325 -1348 0 - -1325 -1354 0 - -1325 -1359 0 - -1340 -1343 0 - -1340 -1348 0 - -1340 -1354 0 - -1340 -1359 0 - -1343 -1348 0 - -1343 -1354 0 - -1343 -1359 0 - -1348 -1354 0 - -1348 -1359 0 - -1354 -1359 0 - -1383 -1391 0 - -1383 -1410 0 - -1383 -1443 0 - -1383 -1465 0 - -1383 -1474 0 - -1383 -1484 0 - -1383 -1504 0 - -1383 -1513 0 - -1383 -1532 0 - -1383 -1543 0 - -1391 -1410 0 - -1391 -1443 0 - -1391 -1465 0 - -1391 -1474 0 - -1391 -1484 0 - -1391 -1504 0 - -1391 -1513 0 - -1391 -1532 0 - -1391 -1543 0 - -1410 -1443 0 - -1410 -1465 0 - -1410 -1474 0 - -1410 -1484 0 - -1410 -1504 0 - -1410 -1513 0 - -1410 -1532 0 - -1410 -1543 0 - -1443 -1465 0 - -1443 -1474 0 - -1443 -1484 0 - -1443 -1504 0 - -1443 -1513 0 - -1443 -1532 0 - -1443 -1543 0 - -1465 -1474 0 - -1465 -1484 0 - -1465 -1504 0 - -1465 -1513 0 - -1465 -1532 0 - -1465 -1543 0 - -1474 -1484 0 - -1474 -1504 0 - -1474 -1513 0 - -1474 -1532 0 - -1474 -1543 0 - -1484 -1504 0 - -1484 -1513 0 - -1484 -1532 0 - -1484 -1543 0 - -1504 -1513 0 - -1504 -1532 0 - -1504 -1543 0 - -1513 -1532 0 - -1513 -1543 0 - -1532 -1543 0 - -1370 -1384 0 - -1370 -1392 0 - -1370 -1413 0 - -1370 -1426 0 - -1370 -1444 0 - -1370 -1452 0 - -1370 -1475 0 - -1370 -1485 0 - -1370 -1523 0 - -1370 -1544 0 - -1384 -1392 0 - -1384 -1413 0 - -1384 -1426 0 - -1384 -1444 0 - -1384 -1452 0 - -1384 -1475 0 - -1384 -1485 0 - -1384 -1523 0 - -1384 -1544 0 - -1392 -1413 0 - -1392 -1426 0 - -1392 -1444 0 - -1392 -1452 0 - -1392 -1475 0 - -1392 -1485 0 - -1392 -1523 0 - -1392 -1544 0 - -1413 -1426 0 - -1413 -1444 0 - -1413 -1452 0 - -1413 -1475 0 - -1413 -1485 0 - -1413 -1523 0 - -1413 -1544 0 - -1426 -1444 0 - -1426 -1452 0 - -1426 -1475 0 - -1426 -1485 0 - -1426 -1523 0 - -1426 -1544 0 - -1444 -1452 0 - -1444 -1475 0 - -1444 -1485 0 - -1444 -1523 0 - -1444 -1544 0 - -1452 -1475 0 - -1452 -1485 0 - -1452 -1523 0 - -1452 -1544 0 - -1475 -1485 0 - -1475 -1523 0 - -1475 -1544 0 - -1485 -1523 0 - -1485 -1544 0 - -1523 -1544 0 - -1363 -1371 0 - -1363 -1411 0 - -1363 -1414 0 - -1363 -1445 0 - -1363 -1453 0 - -1363 -1466 0 - -1363 -1476 0 - -1363 -1493 0 - -1363 -1505 0 - -1363 -1514 0 - -1363 -1533 0 - -1371 -1411 0 - -1371 -1414 0 - -1371 -1445 0 - -1371 -1453 0 - -1371 -1466 0 - -1371 -1476 0 - -1371 -1493 0 - -1371 -1505 0 - -1371 -1514 0 - -1371 -1533 0 - -1411 -1414 0 - -1411 -1445 0 - -1411 -1453 0 - -1411 -1466 0 - -1411 -1476 0 - -1411 -1493 0 - -1411 -1505 0 - -1411 -1514 0 - -1411 -1533 0 - -1414 -1445 0 - -1414 -1453 0 - -1414 -1466 0 - -1414 -1476 0 - -1414 -1493 0 - -1414 -1505 0 - -1414 -1514 0 - -1414 -1533 0 - -1445 -1453 0 - -1445 -1466 0 - -1445 -1476 0 - -1445 -1493 0 - -1445 -1505 0 - -1445 -1514 0 - -1445 -1533 0 - -1453 -1466 0 - -1453 -1476 0 - -1453 -1493 0 - -1453 -1505 0 - -1453 -1514 0 - -1453 -1533 0 - -1466 -1476 0 - -1466 -1493 0 - -1466 -1505 0 - -1466 -1514 0 - -1466 -1533 0 - -1476 -1493 0 - -1476 -1505 0 - -1476 -1514 0 - -1476 -1533 0 - -1493 -1505 0 - -1493 -1514 0 - -1493 -1533 0 - -1505 -1514 0 - -1505 -1533 0 - -1514 -1533 0 - -1364 -1393 0 - -1364 -1402 0 - -1364 -1422 0 - -1364 -1454 0 - -1364 -1486 0 - -1364 -1524 0 - -1364 -1545 0 - -1393 -1402 0 - -1393 -1422 0 - -1393 -1454 0 - -1393 -1486 0 - -1393 -1524 0 - -1393 -1545 0 - -1402 -1422 0 - -1402 -1454 0 - -1402 -1486 0 - -1402 -1524 0 - -1402 -1545 0 - -1422 -1454 0 - -1422 -1486 0 - -1422 -1524 0 - -1422 -1545 0 - -1454 -1486 0 - -1454 -1524 0 - -1454 -1545 0 - -1486 -1524 0 - -1486 -1545 0 - -1524 -1545 0 - -1365 -1372 0 - -1365 -1394 0 - -1365 -1415 0 - -1365 -1427 0 - -1365 -1467 0 - -1365 -1494 0 - -1365 -1515 0 - -1365 -1525 0 - -1365 -1546 0 - -1372 -1394 0 - -1372 -1415 0 - -1372 -1427 0 - -1372 -1467 0 - -1372 -1494 0 - -1372 -1515 0 - -1372 -1525 0 - -1372 -1546 0 - -1394 -1415 0 - -1394 -1427 0 - -1394 -1467 0 - -1394 -1494 0 - -1394 -1515 0 - -1394 -1525 0 - -1394 -1546 0 - -1415 -1427 0 - -1415 -1467 0 - -1415 -1494 0 - -1415 -1515 0 - -1415 -1525 0 - -1415 -1546 0 - -1427 -1467 0 - -1427 -1494 0 - -1427 -1515 0 - -1427 -1525 0 - -1427 -1546 0 - -1467 -1494 0 - -1467 -1515 0 - -1467 -1525 0 - -1467 -1546 0 - -1494 -1515 0 - -1494 -1525 0 - -1494 -1546 0 - -1515 -1525 0 - -1515 -1546 0 - -1525 -1546 0 - -1385 -1428 0 - -1385 -1446 0 - -1385 -1455 0 - -1385 -1487 0 - -1385 -1495 0 - -1385 -1516 0 - -1385 -1526 0 - -1385 -1547 0 - -1428 -1446 0 - -1428 -1455 0 - -1428 -1487 0 - -1428 -1495 0 - -1428 -1516 0 - -1428 -1526 0 - -1428 -1547 0 - -1446 -1455 0 - -1446 -1487 0 - -1446 -1495 0 - -1446 -1516 0 - -1446 -1526 0 - -1446 -1547 0 - -1455 -1487 0 - -1455 -1495 0 - -1455 -1516 0 - -1455 -1526 0 - -1455 -1547 0 - -1487 -1495 0 - -1487 -1516 0 - -1487 -1526 0 - -1487 -1547 0 - -1495 -1516 0 - -1495 -1526 0 - -1495 -1547 0 - -1516 -1526 0 - -1516 -1547 0 - -1526 -1547 0 - -1373 -1395 0 - -1373 -1437 0 - -1373 -1477 0 - -1373 -1517 0 - -1373 -1534 0 - -1373 -1548 0 - -1395 -1437 0 - -1395 -1477 0 - -1395 -1517 0 - -1395 -1534 0 - -1395 -1548 0 - -1437 -1477 0 - -1437 -1517 0 - -1437 -1534 0 - -1437 -1548 0 - -1477 -1517 0 - -1477 -1534 0 - -1477 -1548 0 - -1517 -1534 0 - -1517 -1548 0 - -1534 -1548 0 - -1374 -1416 0 - -1374 -1429 0 - -1374 -1447 0 - -1374 -1456 0 - -1374 -1527 0 - -1374 -1549 0 - -1416 -1429 0 - -1416 -1447 0 - -1416 -1456 0 - -1416 -1527 0 - -1416 -1549 0 - -1429 -1447 0 - -1429 -1456 0 - -1429 -1527 0 - -1429 -1549 0 - -1447 -1456 0 - -1447 -1527 0 - -1447 -1549 0 - -1456 -1527 0 - -1456 -1549 0 - -1527 -1549 0 - -1366 -1403 0 - -1366 -1417 0 - -1366 -1438 0 - -1366 -1448 0 - -1366 -1457 0 - -1366 -1468 0 - -1366 -1496 0 - -1366 -1518 0 - -1366 -1535 0 - -1403 -1417 0 - -1403 -1438 0 - -1403 -1448 0 - -1403 -1457 0 - -1403 -1468 0 - -1403 -1496 0 - -1403 -1518 0 - -1403 -1535 0 - -1417 -1438 0 - -1417 -1448 0 - -1417 -1457 0 - -1417 -1468 0 - -1417 -1496 0 - -1417 -1518 0 - -1417 -1535 0 - -1438 -1448 0 - -1438 -1457 0 - -1438 -1468 0 - -1438 -1496 0 - -1438 -1518 0 - -1438 -1535 0 - -1448 -1457 0 - -1448 -1468 0 - -1448 -1496 0 - -1448 -1518 0 - -1448 -1535 0 - -1457 -1468 0 - -1457 -1496 0 - -1457 -1518 0 - -1457 -1535 0 - -1468 -1496 0 - -1468 -1518 0 - -1468 -1535 0 - -1496 -1518 0 - -1496 -1535 0 - -1518 -1535 0 - -1375 -1404 0 - -1375 -1418 0 - -1375 -1439 0 - -1375 -1469 0 - -1375 -1478 0 - -1375 -1488 0 - -1375 -1536 0 - -1375 -1550 0 - -1404 -1418 0 - -1404 -1439 0 - -1404 -1469 0 - -1404 -1478 0 - -1404 -1488 0 - -1404 -1536 0 - -1404 -1550 0 - -1418 -1439 0 - -1418 -1469 0 - -1418 -1478 0 - -1418 -1488 0 - -1418 -1536 0 - -1418 -1550 0 - -1439 -1469 0 - -1439 -1478 0 - -1439 -1488 0 - -1439 -1536 0 - -1439 -1550 0 - -1469 -1478 0 - -1469 -1488 0 - -1469 -1536 0 - -1469 -1550 0 - -1478 -1488 0 - -1478 -1536 0 - -1478 -1550 0 - -1488 -1536 0 - -1488 -1550 0 - -1536 -1550 0 - -1367 -1376 0 - -1367 -1419 0 - -1367 -1423 0 - -1367 -1430 0 - -1367 -1440 0 - -1367 -1479 0 - -1367 -1506 0 - -1367 -1519 0 - -1367 -1528 0 - -1367 -1537 0 - -1367 -1551 0 - -1376 -1419 0 - -1376 -1423 0 - -1376 -1430 0 - -1376 -1440 0 - -1376 -1479 0 - -1376 -1506 0 - -1376 -1519 0 - -1376 -1528 0 - -1376 -1537 0 - -1376 -1551 0 - -1419 -1423 0 - -1419 -1430 0 - -1419 -1440 0 - -1419 -1479 0 - -1419 -1506 0 - -1419 -1519 0 - -1419 -1528 0 - -1419 -1537 0 - -1419 -1551 0 - -1423 -1430 0 - -1423 -1440 0 - -1423 -1479 0 - -1423 -1506 0 - -1423 -1519 0 - -1423 -1528 0 - -1423 -1537 0 - -1423 -1551 0 - -1430 -1440 0 - -1430 -1479 0 - -1430 -1506 0 - -1430 -1519 0 - -1430 -1528 0 - -1430 -1537 0 - -1430 -1551 0 - -1440 -1479 0 - -1440 -1506 0 - -1440 -1519 0 - -1440 -1528 0 - -1440 -1537 0 - -1440 -1551 0 - -1479 -1506 0 - -1479 -1519 0 - -1479 -1528 0 - -1479 -1537 0 - -1479 -1551 0 - -1506 -1519 0 - -1506 -1528 0 - -1506 -1537 0 - -1506 -1551 0 - -1519 -1528 0 - -1519 -1537 0 - -1519 -1551 0 - -1528 -1537 0 - -1528 -1551 0 - -1537 -1551 0 - -1377 -1396 0 - -1377 -1405 0 - -1377 -1431 0 - -1377 -1449 0 - -1377 -1458 0 - -1377 -1470 0 - -1377 -1480 0 - -1377 -1507 0 - -1377 -1538 0 - -1377 -1552 0 - -1396 -1405 0 - -1396 -1431 0 - -1396 -1449 0 - -1396 -1458 0 - -1396 -1470 0 - -1396 -1480 0 - -1396 -1507 0 - -1396 -1538 0 - -1396 -1552 0 - -1405 -1431 0 - -1405 -1449 0 - -1405 -1458 0 - -1405 -1470 0 - -1405 -1480 0 - -1405 -1507 0 - -1405 -1538 0 - -1405 -1552 0 - -1431 -1449 0 - -1431 -1458 0 - -1431 -1470 0 - -1431 -1480 0 - -1431 -1507 0 - -1431 -1538 0 - -1431 -1552 0 - -1449 -1458 0 - -1449 -1470 0 - -1449 -1480 0 - -1449 -1507 0 - -1449 -1538 0 - -1449 -1552 0 - -1458 -1470 0 - -1458 -1480 0 - -1458 -1507 0 - -1458 -1538 0 - -1458 -1552 0 - -1470 -1480 0 - -1470 -1507 0 - -1470 -1538 0 - -1470 -1552 0 - -1480 -1507 0 - -1480 -1538 0 - -1480 -1552 0 - -1507 -1538 0 - -1507 -1552 0 - -1538 -1552 0 - -1386 -1397 0 - -1386 -1412 0 - -1386 -1432 0 - -1386 -1441 0 - -1386 -1459 0 - -1386 -1497 0 - -1386 -1508 0 - -1386 -1520 0 - -1397 -1412 0 - -1397 -1432 0 - -1397 -1441 0 - -1397 -1459 0 - -1397 -1497 0 - -1397 -1508 0 - -1397 -1520 0 - -1412 -1432 0 - -1412 -1441 0 - -1412 -1459 0 - -1412 -1497 0 - -1412 -1508 0 - -1412 -1520 0 - -1432 -1441 0 - -1432 -1459 0 - -1432 -1497 0 - -1432 -1508 0 - -1432 -1520 0 - -1441 -1459 0 - -1441 -1497 0 - -1441 -1508 0 - -1441 -1520 0 - -1459 -1497 0 - -1459 -1508 0 - -1459 -1520 0 - -1497 -1508 0 - -1497 -1520 0 - -1508 -1520 0 - -1378 -1387 0 - -1378 -1420 0 - -1378 -1450 0 - -1378 -1460 0 - -1378 -1481 0 - -1378 -1498 0 - -1378 -1509 0 - -1387 -1420 0 - -1387 -1450 0 - -1387 -1460 0 - -1387 -1481 0 - -1387 -1498 0 - -1387 -1509 0 - -1420 -1450 0 - -1420 -1460 0 - -1420 -1481 0 - -1420 -1498 0 - -1420 -1509 0 - -1450 -1460 0 - -1450 -1481 0 - -1450 -1498 0 - -1450 -1509 0 - -1460 -1481 0 - -1460 -1498 0 - -1460 -1509 0 - -1481 -1498 0 - -1481 -1509 0 - -1498 -1509 0 - -1379 -1461 0 - -1379 -1499 0 - -1379 -1539 0 - -1461 -1499 0 - -1461 -1539 0 - -1499 -1539 0 - -1388 -1398 0 - -1388 -1433 0 - -1388 -1462 0 - -1388 -1471 0 - -1388 -1489 0 - -1388 -1500 0 - -1388 -1510 0 - -1388 -1521 0 - -1388 -1529 0 - -1388 -1540 0 - -1398 -1433 0 - -1398 -1462 0 - -1398 -1471 0 - -1398 -1489 0 - -1398 -1500 0 - -1398 -1510 0 - -1398 -1521 0 - -1398 -1529 0 - -1398 -1540 0 - -1433 -1462 0 - -1433 -1471 0 - -1433 -1489 0 - -1433 -1500 0 - -1433 -1510 0 - -1433 -1521 0 - -1433 -1529 0 - -1433 -1540 0 - -1462 -1471 0 - -1462 -1489 0 - -1462 -1500 0 - -1462 -1510 0 - -1462 -1521 0 - -1462 -1529 0 - -1462 -1540 0 - -1471 -1489 0 - -1471 -1500 0 - -1471 -1510 0 - -1471 -1521 0 - -1471 -1529 0 - -1471 -1540 0 - -1489 -1500 0 - -1489 -1510 0 - -1489 -1521 0 - -1489 -1529 0 - -1489 -1540 0 - -1500 -1510 0 - -1500 -1521 0 - -1500 -1529 0 - -1500 -1540 0 - -1510 -1521 0 - -1510 -1529 0 - -1510 -1540 0 - -1521 -1529 0 - -1521 -1540 0 - -1529 -1540 0 - -1406 -1424 0 - -1406 -1434 0 - -1406 -1472 0 - -1406 -1490 0 - -1406 -1501 0 - -1406 -1541 0 - -1406 -1553 0 - -1424 -1434 0 - -1424 -1472 0 - -1424 -1490 0 - -1424 -1501 0 - -1424 -1541 0 - -1424 -1553 0 - -1434 -1472 0 - -1434 -1490 0 - -1434 -1501 0 - -1434 -1541 0 - -1434 -1553 0 - -1472 -1490 0 - -1472 -1501 0 - -1472 -1541 0 - -1472 -1553 0 - -1490 -1501 0 - -1490 -1541 0 - -1490 -1553 0 - -1501 -1541 0 - -1501 -1553 0 - -1541 -1553 0 - -1380 -1389 0 - -1380 -1399 0 - -1380 -1407 0 - -1380 -1435 0 - -1380 -1451 0 - -1380 -1482 0 - -1380 -1491 0 - -1380 -1511 0 - -1380 -1530 0 - -1389 -1399 0 - -1389 -1407 0 - -1389 -1435 0 - -1389 -1451 0 - -1389 -1482 0 - -1389 -1491 0 - -1389 -1511 0 - -1389 -1530 0 - -1399 -1407 0 - -1399 -1435 0 - -1399 -1451 0 - -1399 -1482 0 - -1399 -1491 0 - -1399 -1511 0 - -1399 -1530 0 - -1407 -1435 0 - -1407 -1451 0 - -1407 -1482 0 - -1407 -1491 0 - -1407 -1511 0 - -1407 -1530 0 - -1435 -1451 0 - -1435 -1482 0 - -1435 -1491 0 - -1435 -1511 0 - -1435 -1530 0 - -1451 -1482 0 - -1451 -1491 0 - -1451 -1511 0 - -1451 -1530 0 - -1482 -1491 0 - -1482 -1511 0 - -1482 -1530 0 - -1491 -1511 0 - -1491 -1530 0 - -1511 -1530 0 - -1368 -1400 0 - -1368 -1442 0 - -1368 -1502 0 - -1368 -1512 0 - -1368 -1542 0 - -1368 -1554 0 - -1400 -1442 0 - -1400 -1502 0 - -1400 -1512 0 - -1400 -1542 0 - -1400 -1554 0 - -1442 -1502 0 - -1442 -1512 0 - -1442 -1542 0 - -1442 -1554 0 - -1502 -1512 0 - -1502 -1542 0 - -1502 -1554 0 - -1512 -1542 0 - -1512 -1554 0 - -1542 -1554 0 - -1381 -1390 0 - -1381 -1401 0 - -1381 -1408 0 - -1381 -1436 0 - -1381 -1463 0 - -1381 -1492 0 - -1381 -1503 0 - -1381 -1522 0 - -1390 -1401 0 - -1390 -1408 0 - -1390 -1436 0 - -1390 -1463 0 - -1390 -1492 0 - -1390 -1503 0 - -1390 -1522 0 - -1401 -1408 0 - -1401 -1436 0 - -1401 -1463 0 - -1401 -1492 0 - -1401 -1503 0 - -1401 -1522 0 - -1408 -1436 0 - -1408 -1463 0 - -1408 -1492 0 - -1408 -1503 0 - -1408 -1522 0 - -1436 -1463 0 - -1436 -1492 0 - -1436 -1503 0 - -1436 -1522 0 - -1463 -1492 0 - -1463 -1503 0 - -1463 -1522 0 - -1492 -1503 0 - -1492 -1522 0 - -1503 -1522 0 - -1369 -1382 0 - -1369 -1409 0 - -1369 -1421 0 - -1369 -1425 0 - -1369 -1464 0 - -1369 -1473 0 - -1369 -1483 0 - -1369 -1531 0 - -1382 -1409 0 - -1382 -1421 0 - -1382 -1425 0 - -1382 -1464 0 - -1382 -1473 0 - -1382 -1483 0 - -1382 -1531 0 - -1409 -1421 0 - -1409 -1425 0 - -1409 -1464 0 - -1409 -1473 0 - -1409 -1483 0 - -1409 -1531 0 - -1421 -1425 0 - -1421 -1464 0 - -1421 -1473 0 - -1421 -1483 0 - -1421 -1531 0 - -1425 -1464 0 - -1425 -1473 0 - -1425 -1483 0 - -1425 -1531 0 - -1464 -1473 0 - -1464 -1483 0 - -1464 -1531 0 - -1473 -1483 0 - -1473 -1531 0 - -1483 -1531 0 - -1555 -1563 0 - -1555 -1595 0 - -1555 -1601 0 - -1563 -1595 0 - -1563 -1601 0 - -1595 -1601 0 - -1564 -1569 0 - -1564 -1575 0 - -1564 -1582 0 - -1564 -1596 0 - -1564 -1602 0 - -1569 -1575 0 - -1569 -1582 0 - -1569 -1596 0 - -1569 -1602 0 - -1575 -1582 0 - -1575 -1596 0 - -1575 -1602 0 - -1582 -1596 0 - -1582 -1602 0 - -1596 -1602 0 - -1556 -1570 0 - -1556 -1583 0 - -1556 -1590 0 - -1556 -1597 0 - -1556 -1603 0 - -1570 -1583 0 - -1570 -1590 0 - -1570 -1597 0 - -1570 -1603 0 - -1583 -1590 0 - -1583 -1597 0 - -1583 -1603 0 - -1590 -1597 0 - -1590 -1603 0 - -1597 -1603 0 - -1557 -1565 0 - -1557 -1571 0 - -1565 -1571 0 - -1558 -1572 0 - -1558 -1577 0 - -1558 -1598 0 - -1558 -1604 0 - -1572 -1577 0 - -1572 -1598 0 - -1572 -1604 0 - -1577 -1598 0 - -1577 -1604 0 - -1598 -1604 0 - -1559 -1566 0 - -1559 -1573 0 - -1559 -1584 0 - -1559 -1591 0 - -1559 -1599 0 - -1566 -1573 0 - -1566 -1584 0 - -1566 -1591 0 - -1566 -1599 0 - -1573 -1584 0 - -1573 -1591 0 - -1573 -1599 0 - -1584 -1591 0 - -1584 -1599 0 - -1591 -1599 0 - -1560 -1578 0 - -1560 -1585 0 - -1560 -1592 0 - -1578 -1585 0 - -1578 -1592 0 - -1585 -1592 0 - -1561 -1567 0 - -1561 -1579 0 - -1561 -1586 0 - -1561 -1593 0 - -1561 -1600 0 - -1567 -1579 0 - -1567 -1586 0 - -1567 -1593 0 - -1567 -1600 0 - -1579 -1586 0 - -1579 -1593 0 - -1579 -1600 0 - -1586 -1593 0 - -1586 -1600 0 - -1593 -1600 0 - -1562 -1568 0 - -1562 -1574 0 - -1562 -1580 0 - -1562 -1587 0 - -1562 -1588 0 - -1562 -1605 0 - -1568 -1574 0 - -1568 -1580 0 - -1568 -1587 0 - -1568 -1588 0 - -1568 -1605 0 - -1574 -1580 0 - -1574 -1587 0 - -1574 -1588 0 - -1574 -1605 0 - -1580 -1587 0 - -1580 -1588 0 - -1580 -1605 0 - -1587 -1588 0 - -1587 -1605 0 - -1588 -1605 0 - -1576 -1581 0 - -1576 -1589 0 - -1576 -1594 0 - -1581 -1589 0 - -1581 -1594 0 - -1589 -1594 0 - -1635 -1660 0 - -1635 -1693 0 - -1635 -1699 0 - -1635 -1708 0 - -1635 -1740 0 - -1660 -1693 0 - -1660 -1699 0 - -1660 -1708 0 - -1660 -1740 0 - -1693 -1699 0 - -1693 -1708 0 - -1693 -1740 0 - -1699 -1708 0 - -1699 -1740 0 - -1708 -1740 0 - -1606 -1636 0 - -1606 -1648 0 - -1606 -1652 0 - -1606 -1666 0 - -1606 -1674 0 - -1606 -1709 0 - -1636 -1648 0 - -1636 -1652 0 - -1636 -1666 0 - -1636 -1674 0 - -1636 -1709 0 - -1648 -1652 0 - -1648 -1666 0 - -1648 -1674 0 - -1648 -1709 0 - -1652 -1666 0 - -1652 -1674 0 - -1652 -1709 0 - -1666 -1674 0 - -1666 -1709 0 - -1674 -1709 0 - -1607 -1613 0 - -1607 -1624 0 - -1607 -1637 0 - -1607 -1649 0 - -1607 -1653 0 - -1607 -1661 0 - -1607 -1667 0 - -1607 -1682 0 - -1607 -1700 0 - -1607 -1710 0 - -1607 -1722 0 - -1607 -1734 0 - -1607 -1741 0 - -1613 -1624 0 - -1613 -1637 0 - -1613 -1649 0 - -1613 -1653 0 - -1613 -1661 0 - -1613 -1667 0 - -1613 -1682 0 - -1613 -1700 0 - -1613 -1710 0 - -1613 -1722 0 - -1613 -1734 0 - -1613 -1741 0 - -1624 -1637 0 - -1624 -1649 0 - -1624 -1653 0 - -1624 -1661 0 - -1624 -1667 0 - -1624 -1682 0 - -1624 -1700 0 - -1624 -1710 0 - -1624 -1722 0 - -1624 -1734 0 - -1624 -1741 0 - -1637 -1649 0 - -1637 -1653 0 - -1637 -1661 0 - -1637 -1667 0 - -1637 -1682 0 - -1637 -1700 0 - -1637 -1710 0 - -1637 -1722 0 - -1637 -1734 0 - -1637 -1741 0 - -1649 -1653 0 - -1649 -1661 0 - -1649 -1667 0 - -1649 -1682 0 - -1649 -1700 0 - -1649 -1710 0 - -1649 -1722 0 - -1649 -1734 0 - -1649 -1741 0 - -1653 -1661 0 - -1653 -1667 0 - -1653 -1682 0 - -1653 -1700 0 - -1653 -1710 0 - -1653 -1722 0 - -1653 -1734 0 - -1653 -1741 0 - -1661 -1667 0 - -1661 -1682 0 - -1661 -1700 0 - -1661 -1710 0 - -1661 -1722 0 - -1661 -1734 0 - -1661 -1741 0 - -1667 -1682 0 - -1667 -1700 0 - -1667 -1710 0 - -1667 -1722 0 - -1667 -1734 0 - -1667 -1741 0 - -1682 -1700 0 - -1682 -1710 0 - -1682 -1722 0 - -1682 -1734 0 - -1682 -1741 0 - -1700 -1710 0 - -1700 -1722 0 - -1700 -1734 0 - -1700 -1741 0 - -1710 -1722 0 - -1710 -1734 0 - -1710 -1741 0 - -1722 -1734 0 - -1722 -1741 0 - -1734 -1741 0 - -1608 -1614 0 - -1608 -1625 0 - -1608 -1654 0 - -1608 -1683 0 - -1608 -1694 0 - -1608 -1701 0 - -1608 -1711 0 - -1608 -1723 0 - -1608 -1728 0 - -1614 -1625 0 - -1614 -1654 0 - -1614 -1683 0 - -1614 -1694 0 - -1614 -1701 0 - -1614 -1711 0 - -1614 -1723 0 - -1614 -1728 0 - -1625 -1654 0 - -1625 -1683 0 - -1625 -1694 0 - -1625 -1701 0 - -1625 -1711 0 - -1625 -1723 0 - -1625 -1728 0 - -1654 -1683 0 - -1654 -1694 0 - -1654 -1701 0 - -1654 -1711 0 - -1654 -1723 0 - -1654 -1728 0 - -1683 -1694 0 - -1683 -1701 0 - -1683 -1711 0 - -1683 -1723 0 - -1683 -1728 0 - -1694 -1701 0 - -1694 -1711 0 - -1694 -1723 0 - -1694 -1728 0 - -1701 -1711 0 - -1701 -1723 0 - -1701 -1728 0 - -1711 -1723 0 - -1711 -1728 0 - -1723 -1728 0 - -1615 -1626 0 - -1615 -1702 0 - -1626 -1702 0 - -1609 -1616 0 - -1609 -1642 0 - -1609 -1655 0 - -1609 -1662 0 - -1609 -1668 0 - -1609 -1684 0 - -1609 -1712 0 - -1609 -1735 0 - -1609 -1742 0 - -1616 -1642 0 - -1616 -1655 0 - -1616 -1662 0 - -1616 -1668 0 - -1616 -1684 0 - -1616 -1712 0 - -1616 -1735 0 - -1616 -1742 0 - -1642 -1655 0 - -1642 -1662 0 - -1642 -1668 0 - -1642 -1684 0 - -1642 -1712 0 - -1642 -1735 0 - -1642 -1742 0 - -1655 -1662 0 - -1655 -1668 0 - -1655 -1684 0 - -1655 -1712 0 - -1655 -1735 0 - -1655 -1742 0 - -1662 -1668 0 - -1662 -1684 0 - -1662 -1712 0 - -1662 -1735 0 - -1662 -1742 0 - -1668 -1684 0 - -1668 -1712 0 - -1668 -1735 0 - -1668 -1742 0 - -1684 -1712 0 - -1684 -1735 0 - -1684 -1742 0 - -1712 -1735 0 - -1712 -1742 0 - -1735 -1742 0 - -1627 -1643 0 - -1627 -1675 0 - -1627 -1724 0 - -1627 -1729 0 - -1627 -1736 0 - -1643 -1675 0 - -1643 -1724 0 - -1643 -1729 0 - -1643 -1736 0 - -1675 -1724 0 - -1675 -1729 0 - -1675 -1736 0 - -1724 -1729 0 - -1724 -1736 0 - -1729 -1736 0 - -1628 -1650 0 - -1628 -1656 0 - -1628 -1663 0 - -1628 -1669 0 - -1628 -1685 0 - -1628 -1713 0 - -1628 -1725 0 - -1628 -1730 0 - -1650 -1656 0 - -1650 -1663 0 - -1650 -1669 0 - -1650 -1685 0 - -1650 -1713 0 - -1650 -1725 0 - -1650 -1730 0 - -1656 -1663 0 - -1656 -1669 0 - -1656 -1685 0 - -1656 -1713 0 - -1656 -1725 0 - -1656 -1730 0 - -1663 -1669 0 - -1663 -1685 0 - -1663 -1713 0 - -1663 -1725 0 - -1663 -1730 0 - -1669 -1685 0 - -1669 -1713 0 - -1669 -1725 0 - -1669 -1730 0 - -1685 -1713 0 - -1685 -1725 0 - -1685 -1730 0 - -1713 -1725 0 - -1713 -1730 0 - -1725 -1730 0 - -1617 -1644 0 - -1617 -1676 0 - -1617 -1703 0 - -1617 -1714 0 - -1617 -1743 0 - -1644 -1676 0 - -1644 -1703 0 - -1644 -1714 0 - -1644 -1743 0 - -1676 -1703 0 - -1676 -1714 0 - -1676 -1743 0 - -1703 -1714 0 - -1703 -1743 0 - -1714 -1743 0 - -1618 -1629 0 - -1618 -1664 0 - -1618 -1686 0 - -1618 -1715 0 - -1629 -1664 0 - -1629 -1686 0 - -1629 -1715 0 - -1664 -1686 0 - -1664 -1715 0 - -1686 -1715 0 - -1610 -1630 0 - -1610 -1645 0 - -1610 -1665 0 - -1610 -1670 0 - -1610 -1687 0 - -1610 -1704 0 - -1610 -1716 0 - -1610 -1744 0 - -1630 -1645 0 - -1630 -1665 0 - -1630 -1670 0 - -1630 -1687 0 - -1630 -1704 0 - -1630 -1716 0 - -1630 -1744 0 - -1645 -1665 0 - -1645 -1670 0 - -1645 -1687 0 - -1645 -1704 0 - -1645 -1716 0 - -1645 -1744 0 - -1665 -1670 0 - -1665 -1687 0 - -1665 -1704 0 - -1665 -1716 0 - -1665 -1744 0 - -1670 -1687 0 - -1670 -1704 0 - -1670 -1716 0 - -1670 -1744 0 - -1687 -1704 0 - -1687 -1716 0 - -1687 -1744 0 - -1704 -1716 0 - -1704 -1744 0 - -1716 -1744 0 - -1611 -1619 0 - -1611 -1631 0 - -1611 -1638 0 - -1611 -1651 0 - -1611 -1677 0 - -1611 -1737 0 - -1611 -1745 0 - -1619 -1631 0 - -1619 -1638 0 - -1619 -1651 0 - -1619 -1677 0 - -1619 -1737 0 - -1619 -1745 0 - -1631 -1638 0 - -1631 -1651 0 - -1631 -1677 0 - -1631 -1737 0 - -1631 -1745 0 - -1638 -1651 0 - -1638 -1677 0 - -1638 -1737 0 - -1638 -1745 0 - -1651 -1677 0 - -1651 -1737 0 - -1651 -1745 0 - -1677 -1737 0 - -1677 -1745 0 - -1737 -1745 0 - -1620 -1639 0 - -1620 -1657 0 - -1620 -1678 0 - -1620 -1688 0 - -1620 -1717 0 - -1620 -1731 0 - -1639 -1657 0 - -1639 -1678 0 - -1639 -1688 0 - -1639 -1717 0 - -1639 -1731 0 - -1657 -1678 0 - -1657 -1688 0 - -1657 -1717 0 - -1657 -1731 0 - -1678 -1688 0 - -1678 -1717 0 - -1678 -1731 0 - -1688 -1717 0 - -1688 -1731 0 - -1717 -1731 0 - -1640 -1671 0 - -1640 -1679 0 - -1640 -1689 0 - -1640 -1695 0 - -1640 -1705 0 - -1640 -1718 0 - -1640 -1738 0 - -1671 -1679 0 - -1671 -1689 0 - -1671 -1695 0 - -1671 -1705 0 - -1671 -1718 0 - -1671 -1738 0 - -1679 -1689 0 - -1679 -1695 0 - -1679 -1705 0 - -1679 -1718 0 - -1679 -1738 0 - -1689 -1695 0 - -1689 -1705 0 - -1689 -1718 0 - -1689 -1738 0 - -1695 -1705 0 - -1695 -1718 0 - -1695 -1738 0 - -1705 -1718 0 - -1705 -1738 0 - -1718 -1738 0 - -1612 -1632 0 - -1612 -1680 0 - -1612 -1690 0 - -1612 -1696 0 - -1612 -1746 0 - -1632 -1680 0 - -1632 -1690 0 - -1632 -1696 0 - -1632 -1746 0 - -1680 -1690 0 - -1680 -1696 0 - -1680 -1746 0 - -1690 -1696 0 - -1690 -1746 0 - -1696 -1746 0 - -1621 -1633 0 - -1621 -1646 0 - -1621 -1658 0 - -1621 -1672 0 - -1621 -1697 0 - -1621 -1719 0 - -1621 -1726 0 - -1621 -1732 0 - -1633 -1646 0 - -1633 -1658 0 - -1633 -1672 0 - -1633 -1697 0 - -1633 -1719 0 - -1633 -1726 0 - -1633 -1732 0 - -1646 -1658 0 - -1646 -1672 0 - -1646 -1697 0 - -1646 -1719 0 - -1646 -1726 0 - -1646 -1732 0 - -1658 -1672 0 - -1658 -1697 0 - -1658 -1719 0 - -1658 -1726 0 - -1658 -1732 0 - -1672 -1697 0 - -1672 -1719 0 - -1672 -1726 0 - -1672 -1732 0 - -1697 -1719 0 - -1697 -1726 0 - -1697 -1732 0 - -1719 -1726 0 - -1719 -1732 0 - -1726 -1732 0 - -1622 -1641 0 - -1622 -1647 0 - -1622 -1681 0 - -1622 -1691 0 - -1622 -1698 0 - -1622 -1706 0 - -1622 -1720 0 - -1622 -1727 0 - -1622 -1733 0 - -1622 -1739 0 - -1622 -1747 0 - -1641 -1647 0 - -1641 -1681 0 - -1641 -1691 0 - -1641 -1698 0 - -1641 -1706 0 - -1641 -1720 0 - -1641 -1727 0 - -1641 -1733 0 - -1641 -1739 0 - -1641 -1747 0 - -1647 -1681 0 - -1647 -1691 0 - -1647 -1698 0 - -1647 -1706 0 - -1647 -1720 0 - -1647 -1727 0 - -1647 -1733 0 - -1647 -1739 0 - -1647 -1747 0 - -1681 -1691 0 - -1681 -1698 0 - -1681 -1706 0 - -1681 -1720 0 - -1681 -1727 0 - -1681 -1733 0 - -1681 -1739 0 - -1681 -1747 0 - -1691 -1698 0 - -1691 -1706 0 - -1691 -1720 0 - -1691 -1727 0 - -1691 -1733 0 - -1691 -1739 0 - -1691 -1747 0 - -1698 -1706 0 - -1698 -1720 0 - -1698 -1727 0 - -1698 -1733 0 - -1698 -1739 0 - -1698 -1747 0 - -1706 -1720 0 - -1706 -1727 0 - -1706 -1733 0 - -1706 -1739 0 - -1706 -1747 0 - -1720 -1727 0 - -1720 -1733 0 - -1720 -1739 0 - -1720 -1747 0 - -1727 -1733 0 - -1727 -1739 0 - -1727 -1747 0 - -1733 -1739 0 - -1733 -1747 0 - -1739 -1747 0 - -1623 -1634 0 - -1623 -1659 0 - -1623 -1673 0 - -1623 -1692 0 - -1623 -1707 0 - -1623 -1721 0 - -1634 -1659 0 - -1634 -1673 0 - -1634 -1692 0 - -1634 -1707 0 - -1634 -1721 0 - -1659 -1673 0 - -1659 -1692 0 - -1659 -1707 0 - -1659 -1721 0 - -1673 -1692 0 - -1673 -1707 0 - -1673 -1721 0 - -1692 -1707 0 - -1692 -1721 0 - -1707 -1721 0 - -1764 -1799 0 - -1764 -1808 0 - -1764 -1818 0 - -1764 -1839 0 - -1764 -1845 0 - -1764 -1861 0 - -1799 -1808 0 - -1799 -1818 0 - -1799 -1839 0 - -1799 -1845 0 - -1799 -1861 0 - -1808 -1818 0 - -1808 -1839 0 - -1808 -1845 0 - -1808 -1861 0 - -1818 -1839 0 - -1818 -1845 0 - -1818 -1861 0 - -1839 -1845 0 - -1839 -1861 0 - -1845 -1861 0 - -1753 -1765 0 - -1753 -1791 0 - -1753 -1809 0 - -1753 -1819 0 - -1753 -1852 0 - -1765 -1791 0 - -1765 -1809 0 - -1765 -1819 0 - -1765 -1852 0 - -1791 -1809 0 - -1791 -1819 0 - -1791 -1852 0 - -1809 -1819 0 - -1809 -1852 0 - -1819 -1852 0 - -1748 -1754 0 - -1748 -1792 0 - -1748 -1800 0 - -1748 -1810 0 - -1748 -1831 0 - -1748 -1840 0 - -1748 -1846 0 - -1748 -1853 0 - -1748 -1862 0 - -1754 -1792 0 - -1754 -1800 0 - -1754 -1810 0 - -1754 -1831 0 - -1754 -1840 0 - -1754 -1846 0 - -1754 -1853 0 - -1754 -1862 0 - -1792 -1800 0 - -1792 -1810 0 - -1792 -1831 0 - -1792 -1840 0 - -1792 -1846 0 - -1792 -1853 0 - -1792 -1862 0 - -1800 -1810 0 - -1800 -1831 0 - -1800 -1840 0 - -1800 -1846 0 - -1800 -1853 0 - -1800 -1862 0 - -1810 -1831 0 - -1810 -1840 0 - -1810 -1846 0 - -1810 -1853 0 - -1810 -1862 0 - -1831 -1840 0 - -1831 -1846 0 - -1831 -1853 0 - -1831 -1862 0 - -1840 -1846 0 - -1840 -1853 0 - -1840 -1862 0 - -1846 -1853 0 - -1846 -1862 0 - -1853 -1862 0 - -1766 -1772 0 - -1766 -1781 0 - -1766 -1793 0 - -1766 -1820 0 - -1766 -1827 0 - -1766 -1857 0 - -1766 -1863 0 - -1772 -1781 0 - -1772 -1793 0 - -1772 -1820 0 - -1772 -1827 0 - -1772 -1857 0 - -1772 -1863 0 - -1781 -1793 0 - -1781 -1820 0 - -1781 -1827 0 - -1781 -1857 0 - -1781 -1863 0 - -1793 -1820 0 - -1793 -1827 0 - -1793 -1857 0 - -1793 -1863 0 - -1820 -1827 0 - -1820 -1857 0 - -1820 -1863 0 - -1827 -1857 0 - -1827 -1863 0 - -1857 -1863 0 - -1749 -1755 0 - -1749 -1767 0 - -1749 -1801 0 - -1749 -1832 0 - -1749 -1847 0 - -1749 -1864 0 - -1755 -1767 0 - -1755 -1801 0 - -1755 -1832 0 - -1755 -1847 0 - -1755 -1864 0 - -1767 -1801 0 - -1767 -1832 0 - -1767 -1847 0 - -1767 -1864 0 - -1801 -1832 0 - -1801 -1847 0 - -1801 -1864 0 - -1832 -1847 0 - -1832 -1864 0 - -1847 -1864 0 - -1750 -1756 0 - -1750 -1768 0 - -1750 -1773 0 - -1750 -1782 0 - -1750 -1802 0 - -1750 -1821 0 - -1750 -1858 0 - -1750 -1865 0 - -1756 -1768 0 - -1756 -1773 0 - -1756 -1782 0 - -1756 -1802 0 - -1756 -1821 0 - -1756 -1858 0 - -1756 -1865 0 - -1768 -1773 0 - -1768 -1782 0 - -1768 -1802 0 - -1768 -1821 0 - -1768 -1858 0 - -1768 -1865 0 - -1773 -1782 0 - -1773 -1802 0 - -1773 -1821 0 - -1773 -1858 0 - -1773 -1865 0 - -1782 -1802 0 - -1782 -1821 0 - -1782 -1858 0 - -1782 -1865 0 - -1802 -1821 0 - -1802 -1858 0 - -1802 -1865 0 - -1821 -1858 0 - -1821 -1865 0 - -1858 -1865 0 - -1751 -1774 0 - -1751 -1794 0 - -1751 -1803 0 - -1751 -1811 0 - -1751 -1833 0 - -1751 -1848 0 - -1751 -1854 0 - -1751 -1866 0 - -1774 -1794 0 - -1774 -1803 0 - -1774 -1811 0 - -1774 -1833 0 - -1774 -1848 0 - -1774 -1854 0 - -1774 -1866 0 - -1794 -1803 0 - -1794 -1811 0 - -1794 -1833 0 - -1794 -1848 0 - -1794 -1854 0 - -1794 -1866 0 - -1803 -1811 0 - -1803 -1833 0 - -1803 -1848 0 - -1803 -1854 0 - -1803 -1866 0 - -1811 -1833 0 - -1811 -1848 0 - -1811 -1854 0 - -1811 -1866 0 - -1833 -1848 0 - -1833 -1854 0 - -1833 -1866 0 - -1848 -1854 0 - -1848 -1866 0 - -1854 -1866 0 - -1757 -1775 0 - -1757 -1789 0 - -1757 -1804 0 - -1757 -1812 0 - -1757 -1822 0 - -1757 -1867 0 - -1775 -1789 0 - -1775 -1804 0 - -1775 -1812 0 - -1775 -1822 0 - -1775 -1867 0 - -1789 -1804 0 - -1789 -1812 0 - -1789 -1822 0 - -1789 -1867 0 - -1804 -1812 0 - -1804 -1822 0 - -1804 -1867 0 - -1812 -1822 0 - -1812 -1867 0 - -1822 -1867 0 - -1783 -1790 0 - -1783 -1813 0 - -1783 -1841 0 - -1783 -1849 0 - -1790 -1813 0 - -1790 -1841 0 - -1790 -1849 0 - -1813 -1841 0 - -1813 -1849 0 - -1841 -1849 0 - -1758 -1795 0 - -1758 -1828 0 - -1758 -1834 0 - -1758 -1855 0 - -1758 -1859 0 - -1758 -1868 0 - -1795 -1828 0 - -1795 -1834 0 - -1795 -1855 0 - -1795 -1859 0 - -1795 -1868 0 - -1828 -1834 0 - -1828 -1855 0 - -1828 -1859 0 - -1828 -1868 0 - -1834 -1855 0 - -1834 -1859 0 - -1834 -1868 0 - -1855 -1859 0 - -1855 -1868 0 - -1859 -1868 0 - -1752 -1776 0 - -1752 -1784 0 - -1752 -1805 0 - -1752 -1823 0 - -1752 -1829 0 - -1752 -1835 0 - -1776 -1784 0 - -1776 -1805 0 - -1776 -1823 0 - -1776 -1829 0 - -1776 -1835 0 - -1784 -1805 0 - -1784 -1823 0 - -1784 -1829 0 - -1784 -1835 0 - -1805 -1823 0 - -1805 -1829 0 - -1805 -1835 0 - -1823 -1829 0 - -1823 -1835 0 - -1829 -1835 0 - -1759 -1769 0 - -1759 -1777 0 - -1759 -1814 0 - -1759 -1824 0 - -1759 -1842 0 - -1769 -1777 0 - -1769 -1814 0 - -1769 -1824 0 - -1769 -1842 0 - -1777 -1814 0 - -1777 -1824 0 - -1777 -1842 0 - -1814 -1824 0 - -1814 -1842 0 - -1824 -1842 0 - -1760 -1770 0 - -1760 -1785 0 - -1760 -1806 0 - -1760 -1836 0 - -1760 -1843 0 - -1770 -1785 0 - -1770 -1806 0 - -1770 -1836 0 - -1770 -1843 0 - -1785 -1806 0 - -1785 -1836 0 - -1785 -1843 0 - -1806 -1836 0 - -1806 -1843 0 - -1836 -1843 0 - -1786 -1815 0 - -1786 -1830 0 - -1786 -1837 0 - -1786 -1850 0 - -1786 -1856 0 - -1786 -1860 0 - -1815 -1830 0 - -1815 -1837 0 - -1815 -1850 0 - -1815 -1856 0 - -1815 -1860 0 - -1830 -1837 0 - -1830 -1850 0 - -1830 -1856 0 - -1830 -1860 0 - -1837 -1850 0 - -1837 -1856 0 - -1837 -1860 0 - -1850 -1856 0 - -1850 -1860 0 - -1856 -1860 0 - -1761 -1778 0 - -1761 -1787 0 - -1761 -1796 0 - -1761 -1816 0 - -1761 -1825 0 - -1761 -1844 0 - -1778 -1787 0 - -1778 -1796 0 - -1778 -1816 0 - -1778 -1825 0 - -1778 -1844 0 - -1787 -1796 0 - -1787 -1816 0 - -1787 -1825 0 - -1787 -1844 0 - -1796 -1816 0 - -1796 -1825 0 - -1796 -1844 0 - -1816 -1825 0 - -1816 -1844 0 - -1825 -1844 0 - -1762 -1771 0 - -1762 -1779 0 - -1762 -1797 0 - -1762 -1826 0 - -1762 -1838 0 - -1762 -1851 0 - -1762 -1869 0 - -1771 -1779 0 - -1771 -1797 0 - -1771 -1826 0 - -1771 -1838 0 - -1771 -1851 0 - -1771 -1869 0 - -1779 -1797 0 - -1779 -1826 0 - -1779 -1838 0 - -1779 -1851 0 - -1779 -1869 0 - -1797 -1826 0 - -1797 -1838 0 - -1797 -1851 0 - -1797 -1869 0 - -1826 -1838 0 - -1826 -1851 0 - -1826 -1869 0 - -1838 -1851 0 - -1838 -1869 0 - -1851 -1869 0 - -1763 -1780 0 - -1763 -1788 0 - -1763 -1798 0 - -1763 -1807 0 - -1763 -1817 0 - -1763 -1870 0 - -1780 -1788 0 - -1780 -1798 0 - -1780 -1807 0 - -1780 -1817 0 - -1780 -1870 0 - -1788 -1798 0 - -1788 -1807 0 - -1788 -1817 0 - -1788 -1870 0 - -1798 -1807 0 - -1798 -1817 0 - -1798 -1870 0 - -1807 -1817 0 - -1807 -1870 0 - -1817 -1870 0 - -1921 -1929 0 - -1921 -1936 0 - -1921 -1977 0 - -1921 -1983 0 - -1921 -1997 0 - -1929 -1936 0 - -1929 -1977 0 - -1929 -1983 0 - -1929 -1997 0 - -1936 -1977 0 - -1936 -1983 0 - -1936 -1997 0 - -1977 -1983 0 - -1977 -1997 0 - -1983 -1997 0 - -1889 -1902 0 - -1889 -1910 0 - -1889 -1937 0 - -1889 -1943 0 - -1889 -1952 0 - -1889 -1961 0 - -1889 -1984 0 - -1889 -1991 0 - -1889 -1998 0 - -1902 -1910 0 - -1902 -1937 0 - -1902 -1943 0 - -1902 -1952 0 - -1902 -1961 0 - -1902 -1984 0 - -1902 -1991 0 - -1902 -1998 0 - -1910 -1937 0 - -1910 -1943 0 - -1910 -1952 0 - -1910 -1961 0 - -1910 -1984 0 - -1910 -1991 0 - -1910 -1998 0 - -1937 -1943 0 - -1937 -1952 0 - -1937 -1961 0 - -1937 -1984 0 - -1937 -1991 0 - -1937 -1998 0 - -1943 -1952 0 - -1943 -1961 0 - -1943 -1984 0 - -1943 -1991 0 - -1943 -1998 0 - -1952 -1961 0 - -1952 -1984 0 - -1952 -1991 0 - -1952 -1998 0 - -1961 -1984 0 - -1961 -1991 0 - -1961 -1998 0 - -1984 -1991 0 - -1984 -1998 0 - -1991 -1998 0 - -1871 -1897 0 - -1871 -1903 0 - -1871 -1938 0 - -1871 -1944 0 - -1871 -1953 0 - -1871 -1962 0 - -1871 -1985 0 - -1871 -1999 0 - -1897 -1903 0 - -1897 -1938 0 - -1897 -1944 0 - -1897 -1953 0 - -1897 -1962 0 - -1897 -1985 0 - -1897 -1999 0 - -1903 -1938 0 - -1903 -1944 0 - -1903 -1953 0 - -1903 -1962 0 - -1903 -1985 0 - -1903 -1999 0 - -1938 -1944 0 - -1938 -1953 0 - -1938 -1962 0 - -1938 -1985 0 - -1938 -1999 0 - -1944 -1953 0 - -1944 -1962 0 - -1944 -1985 0 - -1944 -1999 0 - -1953 -1962 0 - -1953 -1985 0 - -1953 -1999 0 - -1962 -1985 0 - -1962 -1999 0 - -1985 -1999 0 - -1911 -1954 0 - -1911 -1963 0 - -1911 -1970 0 - -1911 -1986 0 - -1954 -1963 0 - -1954 -1970 0 - -1954 -1986 0 - -1963 -1970 0 - -1963 -1986 0 - -1970 -1986 0 - -1872 -1882 0 - -1872 -1890 0 - -1872 -1912 0 - -1872 -1922 0 - -1872 -1971 0 - -1872 -1978 0 - -1872 -1987 0 - -1872 -2000 0 - -1882 -1890 0 - -1882 -1912 0 - -1882 -1922 0 - -1882 -1971 0 - -1882 -1978 0 - -1882 -1987 0 - -1882 -2000 0 - -1890 -1912 0 - -1890 -1922 0 - -1890 -1971 0 - -1890 -1978 0 - -1890 -1987 0 - -1890 -2000 0 - -1912 -1922 0 - -1912 -1971 0 - -1912 -1978 0 - -1912 -1987 0 - -1912 -2000 0 - -1922 -1971 0 - -1922 -1978 0 - -1922 -1987 0 - -1922 -2000 0 - -1971 -1978 0 - -1971 -1987 0 - -1971 -2000 0 - -1978 -1987 0 - -1978 -2000 0 - -1987 -2000 0 - -1883 -1904 0 - -1883 -1913 0 - -1883 -1955 0 - -1883 -1964 0 - -1883 -1988 0 - -1883 -1992 0 - -1883 -2001 0 - -1904 -1913 0 - -1904 -1955 0 - -1904 -1964 0 - -1904 -1988 0 - -1904 -1992 0 - -1904 -2001 0 - -1913 -1955 0 - -1913 -1964 0 - -1913 -1988 0 - -1913 -1992 0 - -1913 -2001 0 - -1955 -1964 0 - -1955 -1988 0 - -1955 -1992 0 - -1955 -2001 0 - -1964 -1988 0 - -1964 -1992 0 - -1964 -2001 0 - -1988 -1992 0 - -1988 -2001 0 - -1992 -2001 0 - -1873 -1939 0 - -1873 -1945 0 - -1873 -1956 0 - -1873 -1972 0 - -1939 -1945 0 - -1939 -1956 0 - -1939 -1972 0 - -1945 -1956 0 - -1945 -1972 0 - -1956 -1972 0 - -1874 -1891 0 - -1874 -1898 0 - -1874 -1914 0 - -1874 -1923 0 - -1874 -1946 0 - -1874 -1957 0 - -1874 -1965 0 - -1874 -1973 0 - -1874 -1979 0 - -1891 -1898 0 - -1891 -1914 0 - -1891 -1923 0 - -1891 -1946 0 - -1891 -1957 0 - -1891 -1965 0 - -1891 -1973 0 - -1891 -1979 0 - -1898 -1914 0 - -1898 -1923 0 - -1898 -1946 0 - -1898 -1957 0 - -1898 -1965 0 - -1898 -1973 0 - -1898 -1979 0 - -1914 -1923 0 - -1914 -1946 0 - -1914 -1957 0 - -1914 -1965 0 - -1914 -1973 0 - -1914 -1979 0 - -1923 -1946 0 - -1923 -1957 0 - -1923 -1965 0 - -1923 -1973 0 - -1923 -1979 0 - -1946 -1957 0 - -1946 -1965 0 - -1946 -1973 0 - -1946 -1979 0 - -1957 -1965 0 - -1957 -1973 0 - -1957 -1979 0 - -1965 -1973 0 - -1965 -1979 0 - -1973 -1979 0 - -1875 -1930 0 - -1875 -1966 0 - -1875 -1989 0 - -1875 -2002 0 - -1930 -1966 0 - -1930 -1989 0 - -1930 -2002 0 - -1966 -1989 0 - -1966 -2002 0 - -1989 -2002 0 - -1905 -1915 0 - -1905 -1931 0 - -1905 -1940 0 - -1905 -1974 0 - -1905 -1993 0 - -1915 -1931 0 - -1915 -1940 0 - -1915 -1974 0 - -1915 -1993 0 - -1931 -1940 0 - -1931 -1974 0 - -1931 -1993 0 - -1940 -1974 0 - -1940 -1993 0 - -1974 -1993 0 - -1876 -1884 0 - -1876 -1899 0 - -1876 -1916 0 - -1876 -1924 0 - -1876 -1932 0 - -1876 -1958 0 - -1876 -1994 0 - -1876 -2003 0 - -1884 -1899 0 - -1884 -1916 0 - -1884 -1924 0 - -1884 -1932 0 - -1884 -1958 0 - -1884 -1994 0 - -1884 -2003 0 - -1899 -1916 0 - -1899 -1924 0 - -1899 -1932 0 - -1899 -1958 0 - -1899 -1994 0 - -1899 -2003 0 - -1916 -1924 0 - -1916 -1932 0 - -1916 -1958 0 - -1916 -1994 0 - -1916 -2003 0 - -1924 -1932 0 - -1924 -1958 0 - -1924 -1994 0 - -1924 -2003 0 - -1932 -1958 0 - -1932 -1994 0 - -1932 -2003 0 - -1958 -1994 0 - -1958 -2003 0 - -1994 -2003 0 - -1877 -1892 0 - -1877 -1906 0 - -1877 -1925 0 - -1877 -1933 0 - -1877 -1947 0 - -1877 -1967 0 - -1892 -1906 0 - -1892 -1925 0 - -1892 -1933 0 - -1892 -1947 0 - -1892 -1967 0 - -1906 -1925 0 - -1906 -1933 0 - -1906 -1947 0 - -1906 -1967 0 - -1925 -1933 0 - -1925 -1947 0 - -1925 -1967 0 - -1933 -1947 0 - -1933 -1967 0 - -1947 -1967 0 - -1893 -1907 0 - -1893 -1917 0 - -1893 -1934 0 - -1893 -1948 0 - -1893 -1975 0 - -1893 -1980 0 - -1893 -1995 0 - -1893 -2004 0 - -1907 -1917 0 - -1907 -1934 0 - -1907 -1948 0 - -1907 -1975 0 - -1907 -1980 0 - -1907 -1995 0 - -1907 -2004 0 - -1917 -1934 0 - -1917 -1948 0 - -1917 -1975 0 - -1917 -1980 0 - -1917 -1995 0 - -1917 -2004 0 - -1934 -1948 0 - -1934 -1975 0 - -1934 -1980 0 - -1934 -1995 0 - -1934 -2004 0 - -1948 -1975 0 - -1948 -1980 0 - -1948 -1995 0 - -1948 -2004 0 - -1975 -1980 0 - -1975 -1995 0 - -1975 -2004 0 - -1980 -1995 0 - -1980 -2004 0 - -1995 -2004 0 - -1878 -1885 0 - -1878 -1908 0 - -1878 -1926 0 - -1878 -1941 0 - -1878 -1949 0 - -1878 -1959 0 - -1878 -1968 0 - -1878 -1976 0 - -1878 -1981 0 - -1878 -1990 0 - -1878 -1996 0 - -1885 -1908 0 - -1885 -1926 0 - -1885 -1941 0 - -1885 -1949 0 - -1885 -1959 0 - -1885 -1968 0 - -1885 -1976 0 - -1885 -1981 0 - -1885 -1990 0 - -1885 -1996 0 - -1908 -1926 0 - -1908 -1941 0 - -1908 -1949 0 - -1908 -1959 0 - -1908 -1968 0 - -1908 -1976 0 - -1908 -1981 0 - -1908 -1990 0 - -1908 -1996 0 - -1926 -1941 0 - -1926 -1949 0 - -1926 -1959 0 - -1926 -1968 0 - -1926 -1976 0 - -1926 -1981 0 - -1926 -1990 0 - -1926 -1996 0 - -1941 -1949 0 - -1941 -1959 0 - -1941 -1968 0 - -1941 -1976 0 - -1941 -1981 0 - -1941 -1990 0 - -1941 -1996 0 - -1949 -1959 0 - -1949 -1968 0 - -1949 -1976 0 - -1949 -1981 0 - -1949 -1990 0 - -1949 -1996 0 - -1959 -1968 0 - -1959 -1976 0 - -1959 -1981 0 - -1959 -1990 0 - -1959 -1996 0 - -1968 -1976 0 - -1968 -1981 0 - -1968 -1990 0 - -1968 -1996 0 - -1976 -1981 0 - -1976 -1990 0 - -1976 -1996 0 - -1981 -1990 0 - -1981 -1996 0 - -1990 -1996 0 - -1879 -1886 0 - -1879 -1894 0 - -1879 -1900 0 - -1879 -1918 0 - -1879 -1927 0 - -1879 -1935 0 - -1879 -1942 0 - -1879 -1960 0 - -1879 -1982 0 - -1886 -1894 0 - -1886 -1900 0 - -1886 -1918 0 - -1886 -1927 0 - -1886 -1935 0 - -1886 -1942 0 - -1886 -1960 0 - -1886 -1982 0 - -1894 -1900 0 - -1894 -1918 0 - -1894 -1927 0 - -1894 -1935 0 - -1894 -1942 0 - -1894 -1960 0 - -1894 -1982 0 - -1900 -1918 0 - -1900 -1927 0 - -1900 -1935 0 - -1900 -1942 0 - -1900 -1960 0 - -1900 -1982 0 - -1918 -1927 0 - -1918 -1935 0 - -1918 -1942 0 - -1918 -1960 0 - -1918 -1982 0 - -1927 -1935 0 - -1927 -1942 0 - -1927 -1960 0 - -1927 -1982 0 - -1935 -1942 0 - -1935 -1960 0 - -1935 -1982 0 - -1942 -1960 0 - -1942 -1982 0 - -1960 -1982 0 - -1880 -1887 0 - -1880 -1895 0 - -1880 -1919 0 - -1880 -1950 0 - -1880 -2005 0 - -1887 -1895 0 - -1887 -1919 0 - -1887 -1950 0 - -1887 -2005 0 - -1895 -1919 0 - -1895 -1950 0 - -1895 -2005 0 - -1919 -1950 0 - -1919 -2005 0 - -1950 -2005 0 - -1881 -1888 0 - -1881 -1896 0 - -1881 -1901 0 - -1881 -1909 0 - -1881 -1920 0 - -1881 -1928 0 - -1881 -1951 0 - -1881 -1969 0 - -1881 -2006 0 - -1888 -1896 0 - -1888 -1901 0 - -1888 -1909 0 - -1888 -1920 0 - -1888 -1928 0 - -1888 -1951 0 - -1888 -1969 0 - -1888 -2006 0 - -1896 -1901 0 - -1896 -1909 0 - -1896 -1920 0 - -1896 -1928 0 - -1896 -1951 0 - -1896 -1969 0 - -1896 -2006 0 - -1901 -1909 0 - -1901 -1920 0 - -1901 -1928 0 - -1901 -1951 0 - -1901 -1969 0 - -1901 -2006 0 - -1909 -1920 0 - -1909 -1928 0 - -1909 -1951 0 - -1909 -1969 0 - -1909 -2006 0 - -1920 -1928 0 - -1920 -1951 0 - -1920 -1969 0 - -1920 -2006 0 - -1928 -1951 0 - -1928 -1969 0 - -1928 -2006 0 - -1951 -1969 0 - -1951 -2006 0 - -1969 -2006 0 - -2015 -2027 0 - -2015 -2044 0 - -2015 -2062 0 - -2015 -2074 0 - -2015 -2083 0 - -2015 -2088 0 - -2015 -2107 0 - -2015 -2140 0 - -2015 -2146 0 - -2027 -2044 0 - -2027 -2062 0 - -2027 -2074 0 - -2027 -2083 0 - -2027 -2088 0 - -2027 -2107 0 - -2027 -2140 0 - -2027 -2146 0 - -2044 -2062 0 - -2044 -2074 0 - -2044 -2083 0 - -2044 -2088 0 - -2044 -2107 0 - -2044 -2140 0 - -2044 -2146 0 - -2062 -2074 0 - -2062 -2083 0 - -2062 -2088 0 - -2062 -2107 0 - -2062 -2140 0 - -2062 -2146 0 - -2074 -2083 0 - -2074 -2088 0 - -2074 -2107 0 - -2074 -2140 0 - -2074 -2146 0 - -2083 -2088 0 - -2083 -2107 0 - -2083 -2140 0 - -2083 -2146 0 - -2088 -2107 0 - -2088 -2140 0 - -2088 -2146 0 - -2107 -2140 0 - -2107 -2146 0 - -2140 -2146 0 - -2007 -2016 0 - -2007 -2028 0 - -2007 -2039 0 - -2007 -2045 0 - -2007 -2075 0 - -2007 -2084 0 - -2007 -2089 0 - -2007 -2098 0 - -2007 -2125 0 - -2007 -2154 0 - -2016 -2028 0 - -2016 -2039 0 - -2016 -2045 0 - -2016 -2075 0 - -2016 -2084 0 - -2016 -2089 0 - -2016 -2098 0 - -2016 -2125 0 - -2016 -2154 0 - -2028 -2039 0 - -2028 -2045 0 - -2028 -2075 0 - -2028 -2084 0 - -2028 -2089 0 - -2028 -2098 0 - -2028 -2125 0 - -2028 -2154 0 - -2039 -2045 0 - -2039 -2075 0 - -2039 -2084 0 - -2039 -2089 0 - -2039 -2098 0 - -2039 -2125 0 - -2039 -2154 0 - -2045 -2075 0 - -2045 -2084 0 - -2045 -2089 0 - -2045 -2098 0 - -2045 -2125 0 - -2045 -2154 0 - -2075 -2084 0 - -2075 -2089 0 - -2075 -2098 0 - -2075 -2125 0 - -2075 -2154 0 - -2084 -2089 0 - -2084 -2098 0 - -2084 -2125 0 - -2084 -2154 0 - -2089 -2098 0 - -2089 -2125 0 - -2089 -2154 0 - -2098 -2125 0 - -2098 -2154 0 - -2125 -2154 0 - -2008 -2055 0 - -2008 -2076 0 - -2008 -2090 0 - -2008 -2108 0 - -2008 -2118 0 - -2008 -2132 0 - -2008 -2141 0 - -2008 -2147 0 - -2008 -2155 0 - -2055 -2076 0 - -2055 -2090 0 - -2055 -2108 0 - -2055 -2118 0 - -2055 -2132 0 - -2055 -2141 0 - -2055 -2147 0 - -2055 -2155 0 - -2076 -2090 0 - -2076 -2108 0 - -2076 -2118 0 - -2076 -2132 0 - -2076 -2141 0 - -2076 -2147 0 - -2076 -2155 0 - -2090 -2108 0 - -2090 -2118 0 - -2090 -2132 0 - -2090 -2141 0 - -2090 -2147 0 - -2090 -2155 0 - -2108 -2118 0 - -2108 -2132 0 - -2108 -2141 0 - -2108 -2147 0 - -2108 -2155 0 - -2118 -2132 0 - -2118 -2141 0 - -2118 -2147 0 - -2118 -2155 0 - -2132 -2141 0 - -2132 -2147 0 - -2132 -2155 0 - -2141 -2147 0 - -2141 -2155 0 - -2147 -2155 0 - -2009 -2017 0 - -2009 -2029 0 - -2009 -2046 0 - -2009 -2063 0 - -2009 -2077 0 - -2009 -2099 0 - -2009 -2119 0 - -2009 -2126 0 - -2009 -2142 0 - -2009 -2148 0 - -2009 -2156 0 - -2017 -2029 0 - -2017 -2046 0 - -2017 -2063 0 - -2017 -2077 0 - -2017 -2099 0 - -2017 -2119 0 - -2017 -2126 0 - -2017 -2142 0 - -2017 -2148 0 - -2017 -2156 0 - -2029 -2046 0 - -2029 -2063 0 - -2029 -2077 0 - -2029 -2099 0 - -2029 -2119 0 - -2029 -2126 0 - -2029 -2142 0 - -2029 -2148 0 - -2029 -2156 0 - -2046 -2063 0 - -2046 -2077 0 - -2046 -2099 0 - -2046 -2119 0 - -2046 -2126 0 - -2046 -2142 0 - -2046 -2148 0 - -2046 -2156 0 - -2063 -2077 0 - -2063 -2099 0 - -2063 -2119 0 - -2063 -2126 0 - -2063 -2142 0 - -2063 -2148 0 - -2063 -2156 0 - -2077 -2099 0 - -2077 -2119 0 - -2077 -2126 0 - -2077 -2142 0 - -2077 -2148 0 - -2077 -2156 0 - -2099 -2119 0 - -2099 -2126 0 - -2099 -2142 0 - -2099 -2148 0 - -2099 -2156 0 - -2119 -2126 0 - -2119 -2142 0 - -2119 -2148 0 - -2119 -2156 0 - -2126 -2142 0 - -2126 -2148 0 - -2126 -2156 0 - -2142 -2148 0 - -2142 -2156 0 - -2148 -2156 0 - -2018 -2030 0 - -2018 -2047 0 - -2018 -2056 0 - -2018 -2064 0 - -2018 -2100 0 - -2018 -2109 0 - -2018 -2127 0 - -2018 -2149 0 - -2018 -2157 0 - -2030 -2047 0 - -2030 -2056 0 - -2030 -2064 0 - -2030 -2100 0 - -2030 -2109 0 - -2030 -2127 0 - -2030 -2149 0 - -2030 -2157 0 - -2047 -2056 0 - -2047 -2064 0 - -2047 -2100 0 - -2047 -2109 0 - -2047 -2127 0 - -2047 -2149 0 - -2047 -2157 0 - -2056 -2064 0 - -2056 -2100 0 - -2056 -2109 0 - -2056 -2127 0 - -2056 -2149 0 - -2056 -2157 0 - -2064 -2100 0 - -2064 -2109 0 - -2064 -2127 0 - -2064 -2149 0 - -2064 -2157 0 - -2100 -2109 0 - -2100 -2127 0 - -2100 -2149 0 - -2100 -2157 0 - -2109 -2127 0 - -2109 -2149 0 - -2109 -2157 0 - -2127 -2149 0 - -2127 -2157 0 - -2149 -2157 0 - -2010 -2019 0 - -2010 -2031 0 - -2010 -2110 0 - -2010 -2120 0 - -2010 -2133 0 - -2019 -2031 0 - -2019 -2110 0 - -2019 -2120 0 - -2019 -2133 0 - -2031 -2110 0 - -2031 -2120 0 - -2031 -2133 0 - -2110 -2120 0 - -2110 -2133 0 - -2120 -2133 0 - -2020 -2032 0 - -2020 -2048 0 - -2020 -2065 0 - -2020 -2085 0 - -2020 -2101 0 - -2020 -2111 0 - -2020 -2134 0 - -2032 -2048 0 - -2032 -2065 0 - -2032 -2085 0 - -2032 -2101 0 - -2032 -2111 0 - -2032 -2134 0 - -2048 -2065 0 - -2048 -2085 0 - -2048 -2101 0 - -2048 -2111 0 - -2048 -2134 0 - -2065 -2085 0 - -2065 -2101 0 - -2065 -2111 0 - -2065 -2134 0 - -2085 -2101 0 - -2085 -2111 0 - -2085 -2134 0 - -2101 -2111 0 - -2101 -2134 0 - -2111 -2134 0 - -2021 -2033 0 - -2021 -2049 0 - -2021 -2102 0 - -2021 -2112 0 - -2021 -2121 0 - -2021 -2135 0 - -2021 -2150 0 - -2021 -2158 0 - -2033 -2049 0 - -2033 -2102 0 - -2033 -2112 0 - -2033 -2121 0 - -2033 -2135 0 - -2033 -2150 0 - -2033 -2158 0 - -2049 -2102 0 - -2049 -2112 0 - -2049 -2121 0 - -2049 -2135 0 - -2049 -2150 0 - -2049 -2158 0 - -2102 -2112 0 - -2102 -2121 0 - -2102 -2135 0 - -2102 -2150 0 - -2102 -2158 0 - -2112 -2121 0 - -2112 -2135 0 - -2112 -2150 0 - -2112 -2158 0 - -2121 -2135 0 - -2121 -2150 0 - -2121 -2158 0 - -2135 -2150 0 - -2135 -2158 0 - -2150 -2158 0 - -2011 -2022 0 - -2011 -2034 0 - -2011 -2040 0 - -2011 -2050 0 - -2011 -2057 0 - -2011 -2103 0 - -2011 -2151 0 - -2022 -2034 0 - -2022 -2040 0 - -2022 -2050 0 - -2022 -2057 0 - -2022 -2103 0 - -2022 -2151 0 - -2034 -2040 0 - -2034 -2050 0 - -2034 -2057 0 - -2034 -2103 0 - -2034 -2151 0 - -2040 -2050 0 - -2040 -2057 0 - -2040 -2103 0 - -2040 -2151 0 - -2050 -2057 0 - -2050 -2103 0 - -2050 -2151 0 - -2057 -2103 0 - -2057 -2151 0 - -2103 -2151 0 - -2012 -2023 0 - -2012 -2051 0 - -2012 -2058 0 - -2012 -2066 0 - -2012 -2128 0 - -2012 -2143 0 - -2012 -2152 0 - -2023 -2051 0 - -2023 -2058 0 - -2023 -2066 0 - -2023 -2128 0 - -2023 -2143 0 - -2023 -2152 0 - -2051 -2058 0 - -2051 -2066 0 - -2051 -2128 0 - -2051 -2143 0 - -2051 -2152 0 - -2058 -2066 0 - -2058 -2128 0 - -2058 -2143 0 - -2058 -2152 0 - -2066 -2128 0 - -2066 -2143 0 - -2066 -2152 0 - -2128 -2143 0 - -2128 -2152 0 - -2143 -2152 0 - -2035 -2052 0 - -2035 -2067 0 - -2035 -2078 0 - -2035 -2086 0 - -2035 -2091 0 - -2035 -2113 0 - -2035 -2122 0 - -2035 -2136 0 - -2052 -2067 0 - -2052 -2078 0 - -2052 -2086 0 - -2052 -2091 0 - -2052 -2113 0 - -2052 -2122 0 - -2052 -2136 0 - -2067 -2078 0 - -2067 -2086 0 - -2067 -2091 0 - -2067 -2113 0 - -2067 -2122 0 - -2067 -2136 0 - -2078 -2086 0 - -2078 -2091 0 - -2078 -2113 0 - -2078 -2122 0 - -2078 -2136 0 - -2086 -2091 0 - -2086 -2113 0 - -2086 -2122 0 - -2086 -2136 0 - -2091 -2113 0 - -2091 -2122 0 - -2091 -2136 0 - -2113 -2122 0 - -2113 -2136 0 - -2122 -2136 0 - -2041 -2068 0 - -2041 -2079 0 - -2041 -2092 0 - -2041 -2123 0 - -2041 -2129 0 - -2041 -2137 0 - -2068 -2079 0 - -2068 -2092 0 - -2068 -2123 0 - -2068 -2129 0 - -2068 -2137 0 - -2079 -2092 0 - -2079 -2123 0 - -2079 -2129 0 - -2079 -2137 0 - -2092 -2123 0 - -2092 -2129 0 - -2092 -2137 0 - -2123 -2129 0 - -2123 -2137 0 - -2129 -2137 0 - -2069 -2080 0 - -2069 -2093 0 - -2069 -2104 0 - -2069 -2114 0 - -2069 -2144 0 - -2080 -2093 0 - -2080 -2104 0 - -2080 -2114 0 - -2080 -2144 0 - -2093 -2104 0 - -2093 -2114 0 - -2093 -2144 0 - -2104 -2114 0 - -2104 -2144 0 - -2114 -2144 0 - -2059 -2070 0 - -2059 -2105 0 - -2059 -2115 0 - -2059 -2138 0 - -2059 -2153 0 - -2070 -2105 0 - -2070 -2115 0 - -2070 -2138 0 - -2070 -2153 0 - -2105 -2115 0 - -2105 -2138 0 - -2105 -2153 0 - -2115 -2138 0 - -2115 -2153 0 - -2138 -2153 0 - -2013 -2042 0 - -2013 -2071 0 - -2013 -2081 0 - -2013 -2087 0 - -2013 -2094 0 - -2013 -2139 0 - -2013 -2159 0 - -2042 -2071 0 - -2042 -2081 0 - -2042 -2087 0 - -2042 -2094 0 - -2042 -2139 0 - -2042 -2159 0 - -2071 -2081 0 - -2071 -2087 0 - -2071 -2094 0 - -2071 -2139 0 - -2071 -2159 0 - -2081 -2087 0 - -2081 -2094 0 - -2081 -2139 0 - -2081 -2159 0 - -2087 -2094 0 - -2087 -2139 0 - -2087 -2159 0 - -2094 -2139 0 - -2094 -2159 0 - -2139 -2159 0 - -2024 -2036 0 - -2024 -2043 0 - -2024 -2053 0 - -2024 -2060 0 - -2024 -2072 0 - -2024 -2095 0 - -2024 -2116 0 - -2024 -2124 0 - -2024 -2130 0 - -2036 -2043 0 - -2036 -2053 0 - -2036 -2060 0 - -2036 -2072 0 - -2036 -2095 0 - -2036 -2116 0 - -2036 -2124 0 - -2036 -2130 0 - -2043 -2053 0 - -2043 -2060 0 - -2043 -2072 0 - -2043 -2095 0 - -2043 -2116 0 - -2043 -2124 0 - -2043 -2130 0 - -2053 -2060 0 - -2053 -2072 0 - -2053 -2095 0 - -2053 -2116 0 - -2053 -2124 0 - -2053 -2130 0 - -2060 -2072 0 - -2060 -2095 0 - -2060 -2116 0 - -2060 -2124 0 - -2060 -2130 0 - -2072 -2095 0 - -2072 -2116 0 - -2072 -2124 0 - -2072 -2130 0 - -2095 -2116 0 - -2095 -2124 0 - -2095 -2130 0 - -2116 -2124 0 - -2116 -2130 0 - -2124 -2130 0 - -2025 -2037 0 - -2025 -2073 0 - -2025 -2096 0 - -2025 -2117 0 - -2025 -2131 0 - -2025 -2160 0 - -2037 -2073 0 - -2037 -2096 0 - -2037 -2117 0 - -2037 -2131 0 - -2037 -2160 0 - -2073 -2096 0 - -2073 -2117 0 - -2073 -2131 0 - -2073 -2160 0 - -2096 -2117 0 - -2096 -2131 0 - -2096 -2160 0 - -2117 -2131 0 - -2117 -2160 0 - -2131 -2160 0 - -2014 -2026 0 - -2014 -2038 0 - -2014 -2054 0 - -2014 -2061 0 - -2014 -2082 0 - -2014 -2097 0 - -2014 -2106 0 - -2014 -2145 0 - -2014 -2161 0 - -2026 -2038 0 - -2026 -2054 0 - -2026 -2061 0 - -2026 -2082 0 - -2026 -2097 0 - -2026 -2106 0 - -2026 -2145 0 - -2026 -2161 0 - -2038 -2054 0 - -2038 -2061 0 - -2038 -2082 0 - -2038 -2097 0 - -2038 -2106 0 - -2038 -2145 0 - -2038 -2161 0 - -2054 -2061 0 - -2054 -2082 0 - -2054 -2097 0 - -2054 -2106 0 - -2054 -2145 0 - -2054 -2161 0 - -2061 -2082 0 - -2061 -2097 0 - -2061 -2106 0 - -2061 -2145 0 - -2061 -2161 0 - -2082 -2097 0 - -2082 -2106 0 - -2082 -2145 0 - -2082 -2161 0 - -2097 -2106 0 - -2097 -2145 0 - -2097 -2161 0 - -2106 -2145 0 - -2106 -2161 0 - -2145 -2161 0 - -2162 -2168 0 - -2162 -2177 0 - -2162 -2186 0 - -2162 -2209 0 - -2162 -2213 0 - -2162 -2220 0 - -2162 -2229 0 - -2162 -2237 0 - -2162 -2242 0 - -2162 -2250 0 - -2168 -2177 0 - -2168 -2186 0 - -2168 -2209 0 - -2168 -2213 0 - -2168 -2220 0 - -2168 -2229 0 - -2168 -2237 0 - -2168 -2242 0 - -2168 -2250 0 - -2177 -2186 0 - -2177 -2209 0 - -2177 -2213 0 - -2177 -2220 0 - -2177 -2229 0 - -2177 -2237 0 - -2177 -2242 0 - -2177 -2250 0 - -2186 -2209 0 - -2186 -2213 0 - -2186 -2220 0 - -2186 -2229 0 - -2186 -2237 0 - -2186 -2242 0 - -2186 -2250 0 - -2209 -2213 0 - -2209 -2220 0 - -2209 -2229 0 - -2209 -2237 0 - -2209 -2242 0 - -2209 -2250 0 - -2213 -2220 0 - -2213 -2229 0 - -2213 -2237 0 - -2213 -2242 0 - -2213 -2250 0 - -2220 -2229 0 - -2220 -2237 0 - -2220 -2242 0 - -2220 -2250 0 - -2229 -2237 0 - -2229 -2242 0 - -2229 -2250 0 - -2237 -2242 0 - -2237 -2250 0 - -2242 -2250 0 - -2169 -2178 0 - -2169 -2187 0 - -2169 -2197 0 - -2169 -2203 0 - -2169 -2221 0 - -2169 -2238 0 - -2169 -2260 0 - -2178 -2187 0 - -2178 -2197 0 - -2178 -2203 0 - -2178 -2221 0 - -2178 -2238 0 - -2178 -2260 0 - -2187 -2197 0 - -2187 -2203 0 - -2187 -2221 0 - -2187 -2238 0 - -2187 -2260 0 - -2197 -2203 0 - -2197 -2221 0 - -2197 -2238 0 - -2197 -2260 0 - -2203 -2221 0 - -2203 -2238 0 - -2203 -2260 0 - -2221 -2238 0 - -2221 -2260 0 - -2238 -2260 0 - -2163 -2170 0 - -2163 -2179 0 - -2163 -2204 0 - -2163 -2214 0 - -2163 -2222 0 - -2163 -2230 0 - -2163 -2243 0 - -2163 -2251 0 - -2170 -2179 0 - -2170 -2204 0 - -2170 -2214 0 - -2170 -2222 0 - -2170 -2230 0 - -2170 -2243 0 - -2170 -2251 0 - -2179 -2204 0 - -2179 -2214 0 - -2179 -2222 0 - -2179 -2230 0 - -2179 -2243 0 - -2179 -2251 0 - -2204 -2214 0 - -2204 -2222 0 - -2204 -2230 0 - -2204 -2243 0 - -2204 -2251 0 - -2214 -2222 0 - -2214 -2230 0 - -2214 -2243 0 - -2214 -2251 0 - -2222 -2230 0 - -2222 -2243 0 - -2222 -2251 0 - -2230 -2243 0 - -2230 -2251 0 - -2243 -2251 0 - -2171 -2188 0 - -2171 -2215 0 - -2171 -2231 0 - -2171 -2244 0 - -2171 -2261 0 - -2188 -2215 0 - -2188 -2231 0 - -2188 -2244 0 - -2188 -2261 0 - -2215 -2231 0 - -2215 -2244 0 - -2215 -2261 0 - -2231 -2244 0 - -2231 -2261 0 - -2244 -2261 0 - -2164 -2172 0 - -2164 -2180 0 - -2164 -2189 0 - -2164 -2198 0 - -2164 -2226 0 - -2164 -2245 0 - -2172 -2180 0 - -2172 -2189 0 - -2172 -2198 0 - -2172 -2226 0 - -2172 -2245 0 - -2180 -2189 0 - -2180 -2198 0 - -2180 -2226 0 - -2180 -2245 0 - -2189 -2198 0 - -2189 -2226 0 - -2189 -2245 0 - -2198 -2226 0 - -2198 -2245 0 - -2226 -2245 0 - -2165 -2173 0 - -2165 -2190 0 - -2165 -2205 0 - -2165 -2223 0 - -2165 -2232 0 - -2165 -2239 0 - -2165 -2246 0 - -2165 -2252 0 - -2173 -2190 0 - -2173 -2205 0 - -2173 -2223 0 - -2173 -2232 0 - -2173 -2239 0 - -2173 -2246 0 - -2173 -2252 0 - -2190 -2205 0 - -2190 -2223 0 - -2190 -2232 0 - -2190 -2239 0 - -2190 -2246 0 - -2190 -2252 0 - -2205 -2223 0 - -2205 -2232 0 - -2205 -2239 0 - -2205 -2246 0 - -2205 -2252 0 - -2223 -2232 0 - -2223 -2239 0 - -2223 -2246 0 - -2223 -2252 0 - -2232 -2239 0 - -2232 -2246 0 - -2232 -2252 0 - -2239 -2246 0 - -2239 -2252 0 - -2246 -2252 0 - -2181 -2191 0 - -2181 -2199 0 - -2181 -2206 0 - -2181 -2216 0 - -2181 -2224 0 - -2181 -2253 0 - -2191 -2199 0 - -2191 -2206 0 - -2191 -2216 0 - -2191 -2224 0 - -2191 -2253 0 - -2199 -2206 0 - -2199 -2216 0 - -2199 -2224 0 - -2199 -2253 0 - -2206 -2216 0 - -2206 -2224 0 - -2206 -2253 0 - -2216 -2224 0 - -2216 -2253 0 - -2224 -2253 0 - -2174 -2192 0 - -2174 -2200 0 - -2174 -2217 0 - -2174 -2254 0 - -2174 -2262 0 - -2192 -2200 0 - -2192 -2217 0 - -2192 -2254 0 - -2192 -2262 0 - -2200 -2217 0 - -2200 -2254 0 - -2200 -2262 0 - -2217 -2254 0 - -2217 -2262 0 - -2254 -2262 0 - -2182 -2193 0 - -2182 -2218 0 - -2182 -2233 0 - -2182 -2263 0 - -2193 -2218 0 - -2193 -2233 0 - -2193 -2263 0 - -2218 -2233 0 - -2218 -2263 0 - -2233 -2263 0 - -2201 -2225 0 - -2201 -2234 0 - -2201 -2247 0 - -2201 -2255 0 - -2225 -2234 0 - -2225 -2247 0 - -2225 -2255 0 - -2234 -2247 0 - -2234 -2255 0 - -2247 -2255 0 - -2166 -2175 0 - -2166 -2194 0 - -2166 -2202 0 - -2166 -2227 0 - -2166 -2235 0 - -2166 -2256 0 - -2175 -2194 0 - -2175 -2202 0 - -2175 -2227 0 - -2175 -2235 0 - -2175 -2256 0 - -2194 -2202 0 - -2194 -2227 0 - -2194 -2235 0 - -2194 -2256 0 - -2202 -2227 0 - -2202 -2235 0 - -2202 -2256 0 - -2227 -2235 0 - -2227 -2256 0 - -2235 -2256 0 - -2176 -2183 0 - -2176 -2195 0 - -2176 -2219 0 - -2176 -2248 0 - -2183 -2195 0 - -2183 -2219 0 - -2183 -2248 0 - -2195 -2219 0 - -2195 -2248 0 - -2219 -2248 0 - -2167 -2207 0 - -2167 -2210 0 - -2167 -2257 0 - -2167 -2264 0 - -2207 -2210 0 - -2207 -2257 0 - -2207 -2264 0 - -2210 -2257 0 - -2210 -2264 0 - -2257 -2264 0 - -2184 -2211 0 - -2184 -2228 0 - -2184 -2236 0 - -2184 -2240 0 - -2184 -2249 0 - -2184 -2258 0 - -2184 -2265 0 - -2211 -2228 0 - -2211 -2236 0 - -2211 -2240 0 - -2211 -2249 0 - -2211 -2258 0 - -2211 -2265 0 - -2228 -2236 0 - -2228 -2240 0 - -2228 -2249 0 - -2228 -2258 0 - -2228 -2265 0 - -2236 -2240 0 - -2236 -2249 0 - -2236 -2258 0 - -2236 -2265 0 - -2240 -2249 0 - -2240 -2258 0 - -2240 -2265 0 - -2249 -2258 0 - -2249 -2265 0 - -2258 -2265 0 - -2185 -2196 0 - -2185 -2208 0 - -2185 -2212 0 - -2185 -2241 0 - -2185 -2259 0 - -2196 -2208 0 - -2196 -2212 0 - -2196 -2241 0 - -2196 -2259 0 - -2208 -2212 0 - -2208 -2241 0 - -2208 -2259 0 - -2212 -2241 0 - -2212 -2259 0 - -2241 -2259 0 - -2314 -2323 0 - -2314 -2330 0 - -2323 -2330 0 - -2294 -2302 0 - -2294 -2307 0 - -2294 -2319 0 - -2294 -2324 0 - -2302 -2307 0 - -2302 -2319 0 - -2302 -2324 0 - -2307 -2319 0 - -2307 -2324 0 - -2319 -2324 0 - -2266 -2289 0 - -2266 -2295 0 - -2266 -2315 0 - -2266 -2325 0 - -2289 -2295 0 - -2289 -2315 0 - -2289 -2325 0 - -2295 -2315 0 - -2295 -2325 0 - -2315 -2325 0 - -2267 -2278 0 - -2267 -2299 0 - -2267 -2326 0 - -2278 -2299 0 - -2278 -2326 0 - -2299 -2326 0 - -2279 -2283 0 - -2279 -2296 0 - -2279 -2300 0 - -2279 -2303 0 - -2279 -2308 0 - -2283 -2296 0 - -2283 -2300 0 - -2283 -2303 0 - -2283 -2308 0 - -2296 -2300 0 - -2296 -2303 0 - -2296 -2308 0 - -2300 -2303 0 - -2300 -2308 0 - -2303 -2308 0 - -2284 -2297 0 - -2284 -2309 0 - -2297 -2309 0 - -2285 -2301 0 - -2285 -2331 0 - -2301 -2331 0 - -2273 -2298 0 - -2273 -2327 0 - -2298 -2327 0 - -2268 -2280 0 - -2268 -2290 0 - -2268 -2304 0 - -2268 -2310 0 - -2268 -2320 0 - -2268 -2328 0 - -2280 -2290 0 - -2280 -2304 0 - -2280 -2310 0 - -2280 -2320 0 - -2280 -2328 0 - -2290 -2304 0 - -2290 -2310 0 - -2290 -2320 0 - -2290 -2328 0 - -2304 -2310 0 - -2304 -2320 0 - -2304 -2328 0 - -2310 -2320 0 - -2310 -2328 0 - -2320 -2328 0 - -2269 -2274 0 - -2269 -2286 0 - -2269 -2291 0 - -2269 -2316 0 - -2269 -2321 0 - -2269 -2329 0 - -2274 -2286 0 - -2274 -2291 0 - -2274 -2316 0 - -2274 -2321 0 - -2274 -2329 0 - -2286 -2291 0 - -2286 -2316 0 - -2286 -2321 0 - -2286 -2329 0 - -2291 -2316 0 - -2291 -2321 0 - -2291 -2329 0 - -2316 -2321 0 - -2316 -2329 0 - -2321 -2329 0 - -2270 -2275 0 - -2270 -2281 0 - -2270 -2287 0 - -2270 -2292 0 - -2270 -2311 0 - -2270 -2317 0 - -2270 -2322 0 - -2275 -2281 0 - -2275 -2287 0 - -2275 -2292 0 - -2275 -2311 0 - -2275 -2317 0 - -2275 -2322 0 - -2281 -2287 0 - -2281 -2292 0 - -2281 -2311 0 - -2281 -2317 0 - -2281 -2322 0 - -2287 -2292 0 - -2287 -2311 0 - -2287 -2317 0 - -2287 -2322 0 - -2292 -2311 0 - -2292 -2317 0 - -2292 -2322 0 - -2311 -2317 0 - -2311 -2322 0 - -2317 -2322 0 - -2271 -2276 0 - -2271 -2288 0 - -2271 -2305 0 - -2271 -2312 0 - -2271 -2332 0 - -2276 -2288 0 - -2276 -2305 0 - -2276 -2312 0 - -2276 -2332 0 - -2288 -2305 0 - -2288 -2312 0 - -2288 -2332 0 - -2305 -2312 0 - -2305 -2332 0 - -2312 -2332 0 - -2272 -2277 0 - -2272 -2282 0 - -2272 -2293 0 - -2272 -2306 0 - -2272 -2313 0 - -2272 -2318 0 - -2272 -2333 0 - -2277 -2282 0 - -2277 -2293 0 - -2277 -2306 0 - -2277 -2313 0 - -2277 -2318 0 - -2277 -2333 0 - -2282 -2293 0 - -2282 -2306 0 - -2282 -2313 0 - -2282 -2318 0 - -2282 -2333 0 - -2293 -2306 0 - -2293 -2313 0 - -2293 -2318 0 - -2293 -2333 0 - -2306 -2313 0 - -2306 -2318 0 - -2306 -2333 0 - -2313 -2318 0 - -2313 -2333 0 - -2318 -2333 0 - -2334 -2344 0 - -2334 -2351 0 - -2334 -2360 0 - -2334 -2369 0 - -2334 -2381 0 - -2334 -2388 0 - -2334 -2405 0 - -2344 -2351 0 - -2344 -2360 0 - -2344 -2369 0 - -2344 -2381 0 - -2344 -2388 0 - -2344 -2405 0 - -2351 -2360 0 - -2351 -2369 0 - -2351 -2381 0 - -2351 -2388 0 - -2351 -2405 0 - -2360 -2369 0 - -2360 -2381 0 - -2360 -2388 0 - -2360 -2405 0 - -2369 -2381 0 - -2369 -2388 0 - -2369 -2405 0 - -2381 -2388 0 - -2381 -2405 0 - -2388 -2405 0 - -2335 -2345 0 - -2335 -2361 0 - -2335 -2377 0 - -2335 -2382 0 - -2335 -2392 0 - -2335 -2399 0 - -2335 -2418 0 - -2345 -2361 0 - -2345 -2377 0 - -2345 -2382 0 - -2345 -2392 0 - -2345 -2399 0 - -2345 -2418 0 - -2361 -2377 0 - -2361 -2382 0 - -2361 -2392 0 - -2361 -2399 0 - -2361 -2418 0 - -2377 -2382 0 - -2377 -2392 0 - -2377 -2399 0 - -2377 -2418 0 - -2382 -2392 0 - -2382 -2399 0 - -2382 -2418 0 - -2392 -2399 0 - -2392 -2418 0 - -2399 -2418 0 - -2336 -2411 0 - -2337 -2362 0 - -2337 -2370 0 - -2337 -2419 0 - -2362 -2370 0 - -2362 -2419 0 - -2370 -2419 0 - -2338 -2346 0 - -2338 -2356 0 - -2338 -2363 0 - -2338 -2389 0 - -2338 -2406 0 - -2338 -2412 0 - -2346 -2356 0 - -2346 -2363 0 - -2346 -2389 0 - -2346 -2406 0 - -2346 -2412 0 - -2356 -2363 0 - -2356 -2389 0 - -2356 -2406 0 - -2356 -2412 0 - -2363 -2389 0 - -2363 -2406 0 - -2363 -2412 0 - -2389 -2406 0 - -2389 -2412 0 - -2406 -2412 0 - -2339 -2357 0 - -2339 -2364 0 - -2339 -2371 0 - -2339 -2407 0 - -2339 -2420 0 - -2357 -2364 0 - -2357 -2371 0 - -2357 -2407 0 - -2357 -2420 0 - -2364 -2371 0 - -2364 -2407 0 - -2364 -2420 0 - -2371 -2407 0 - -2371 -2420 0 - -2407 -2420 0 - -2340 -2372 0 - -2340 -2390 0 - -2340 -2393 0 - -2340 -2400 0 - -2340 -2413 0 - -2372 -2390 0 - -2372 -2393 0 - -2372 -2400 0 - -2372 -2413 0 - -2390 -2393 0 - -2390 -2400 0 - -2390 -2413 0 - -2393 -2400 0 - -2393 -2413 0 - -2400 -2413 0 - -2352 -2373 0 - -2352 -2383 0 - -2352 -2394 0 - -2352 -2401 0 - -2352 -2414 0 - -2373 -2383 0 - -2373 -2394 0 - -2373 -2401 0 - -2373 -2414 0 - -2383 -2394 0 - -2383 -2401 0 - -2383 -2414 0 - -2394 -2401 0 - -2394 -2414 0 - -2401 -2414 0 - -2341 -2347 0 - -2341 -2378 0 - -2341 -2384 0 - -2341 -2395 0 - -2341 -2421 0 - -2347 -2378 0 - -2347 -2384 0 - -2347 -2395 0 - -2347 -2421 0 - -2378 -2384 0 - -2378 -2395 0 - -2378 -2421 0 - -2384 -2395 0 - -2384 -2421 0 - -2395 -2421 0 - -2358 -2366 0 - -2358 -2374 0 - -2358 -2379 0 - -2358 -2415 0 - -2366 -2374 0 - -2366 -2379 0 - -2366 -2415 0 - -2374 -2379 0 - -2374 -2415 0 - -2379 -2415 0 - -2348 -2353 0 - -2348 -2367 0 - -2348 -2380 0 - -2348 -2396 0 - -2348 -2402 0 - -2353 -2367 0 - -2353 -2380 0 - -2353 -2396 0 - -2353 -2402 0 - -2367 -2380 0 - -2367 -2396 0 - -2367 -2402 0 - -2380 -2396 0 - -2380 -2402 0 - -2396 -2402 0 - -2349 -2385 0 - -2349 -2397 0 - -2349 -2403 0 - -2349 -2408 0 - -2385 -2397 0 - -2385 -2403 0 - -2385 -2408 0 - -2397 -2403 0 - -2397 -2408 0 - -2403 -2408 0 - -2342 -2350 0 - -2342 -2354 0 - -2342 -2365 0 - -2342 -2391 0 - -2342 -2422 0 - -2350 -2354 0 - -2350 -2365 0 - -2350 -2391 0 - -2350 -2422 0 - -2354 -2365 0 - -2354 -2391 0 - -2354 -2422 0 - -2365 -2391 0 - -2365 -2422 0 - -2391 -2422 0 - -2355 -2359 0 - -2355 -2375 0 - -2355 -2386 0 - -2355 -2404 0 - -2355 -2409 0 - -2355 -2416 0 - -2359 -2375 0 - -2359 -2386 0 - -2359 -2404 0 - -2359 -2409 0 - -2359 -2416 0 - -2375 -2386 0 - -2375 -2404 0 - -2375 -2409 0 - -2375 -2416 0 - -2386 -2404 0 - -2386 -2409 0 - -2386 -2416 0 - -2404 -2409 0 - -2404 -2416 0 - -2409 -2416 0 - -2343 -2368 0 - -2343 -2376 0 - -2343 -2387 0 - -2343 -2398 0 - -2343 -2410 0 - -2343 -2417 0 - -2343 -2423 0 - -2368 -2376 0 - -2368 -2387 0 - -2368 -2398 0 - -2368 -2410 0 - -2368 -2417 0 - -2368 -2423 0 - -2376 -2387 0 - -2376 -2398 0 - -2376 -2410 0 - -2376 -2417 0 - -2376 -2423 0 - -2387 -2398 0 - -2387 -2410 0 - -2387 -2417 0 - -2387 -2423 0 - -2398 -2410 0 - -2398 -2417 0 - -2398 -2423 0 - -2410 -2417 0 - -2410 -2423 0 - -2417 -2423 0 - -2461 -2472 0 - -2461 -2487 0 - -2461 -2504 0 - -2461 -2510 0 - -2472 -2487 0 - -2472 -2504 0 - -2472 -2510 0 - -2487 -2504 0 - -2487 -2510 0 - -2504 -2510 0 - -2424 -2430 0 - -2424 -2462 0 - -2424 -2473 0 - -2424 -2480 0 - -2424 -2494 0 - -2424 -2518 0 - -2424 -2524 0 - -2424 -2532 0 - -2430 -2462 0 - -2430 -2473 0 - -2430 -2480 0 - -2430 -2494 0 - -2430 -2518 0 - -2430 -2524 0 - -2430 -2532 0 - -2462 -2473 0 - -2462 -2480 0 - -2462 -2494 0 - -2462 -2518 0 - -2462 -2524 0 - -2462 -2532 0 - -2473 -2480 0 - -2473 -2494 0 - -2473 -2518 0 - -2473 -2524 0 - -2473 -2532 0 - -2480 -2494 0 - -2480 -2518 0 - -2480 -2524 0 - -2480 -2532 0 - -2494 -2518 0 - -2494 -2524 0 - -2494 -2532 0 - -2518 -2524 0 - -2518 -2532 0 - -2524 -2532 0 - -2447 -2463 0 - -2447 -2474 0 - -2447 -2488 0 - -2447 -2500 0 - -2447 -2505 0 - -2447 -2511 0 - -2447 -2533 0 - -2463 -2474 0 - -2463 -2488 0 - -2463 -2500 0 - -2463 -2505 0 - -2463 -2511 0 - -2463 -2533 0 - -2474 -2488 0 - -2474 -2500 0 - -2474 -2505 0 - -2474 -2511 0 - -2474 -2533 0 - -2488 -2500 0 - -2488 -2505 0 - -2488 -2511 0 - -2488 -2533 0 - -2500 -2505 0 - -2500 -2511 0 - -2500 -2533 0 - -2505 -2511 0 - -2505 -2533 0 - -2511 -2533 0 - -2425 -2431 0 - -2425 -2441 0 - -2425 -2448 0 - -2425 -2452 0 - -2425 -2534 0 - -2431 -2441 0 - -2431 -2448 0 - -2431 -2452 0 - -2431 -2534 0 - -2441 -2448 0 - -2441 -2452 0 - -2441 -2534 0 - -2448 -2452 0 - -2448 -2534 0 - -2452 -2534 0 - -2432 -2464 0 - -2432 -2481 0 - -2432 -2489 0 - -2432 -2512 0 - -2432 -2519 0 - -2464 -2481 0 - -2464 -2489 0 - -2464 -2512 0 - -2464 -2519 0 - -2481 -2489 0 - -2481 -2512 0 - -2481 -2519 0 - -2489 -2512 0 - -2489 -2519 0 - -2512 -2519 0 - -2433 -2453 0 - -2433 -2475 0 - -2433 -2490 0 - -2433 -2506 0 - -2433 -2520 0 - -2453 -2475 0 - -2453 -2490 0 - -2453 -2506 0 - -2453 -2520 0 - -2475 -2490 0 - -2475 -2506 0 - -2475 -2520 0 - -2490 -2506 0 - -2490 -2520 0 - -2506 -2520 0 - -2426 -2434 0 - -2426 -2442 0 - -2426 -2454 0 - -2426 -2465 0 - -2426 -2476 0 - -2426 -2482 0 - -2426 -2535 0 - -2434 -2442 0 - -2434 -2454 0 - -2434 -2465 0 - -2434 -2476 0 - -2434 -2482 0 - -2434 -2535 0 - -2442 -2454 0 - -2442 -2465 0 - -2442 -2476 0 - -2442 -2482 0 - -2442 -2535 0 - -2454 -2465 0 - -2454 -2476 0 - -2454 -2482 0 - -2454 -2535 0 - -2465 -2476 0 - -2465 -2482 0 - -2465 -2535 0 - -2476 -2482 0 - -2476 -2535 0 - -2482 -2535 0 - -2491 -2513 0 - -2491 -2525 0 - -2491 -2539 0 - -2513 -2525 0 - -2513 -2539 0 - -2525 -2539 0 - -2435 -2443 0 - -2435 -2455 0 - -2435 -2483 0 - -2435 -2507 0 - -2435 -2514 0 - -2435 -2540 0 - -2443 -2455 0 - -2443 -2483 0 - -2443 -2507 0 - -2443 -2514 0 - -2443 -2540 0 - -2455 -2483 0 - -2455 -2507 0 - -2455 -2514 0 - -2455 -2540 0 - -2483 -2507 0 - -2483 -2514 0 - -2483 -2540 0 - -2507 -2514 0 - -2507 -2540 0 - -2514 -2540 0 - -2456 -2466 0 - -2456 -2495 0 - -2456 -2526 0 - -2456 -2541 0 - -2466 -2495 0 - -2466 -2526 0 - -2466 -2541 0 - -2495 -2526 0 - -2495 -2541 0 - -2526 -2541 0 - -2427 -2436 0 - -2427 -2457 0 - -2427 -2467 0 - -2427 -2477 0 - -2427 -2484 0 - -2427 -2496 0 - -2427 -2501 0 - -2427 -2508 0 - -2427 -2515 0 - -2427 -2521 0 - -2427 -2527 0 - -2427 -2542 0 - -2436 -2457 0 - -2436 -2467 0 - -2436 -2477 0 - -2436 -2484 0 - -2436 -2496 0 - -2436 -2501 0 - -2436 -2508 0 - -2436 -2515 0 - -2436 -2521 0 - -2436 -2527 0 - -2436 -2542 0 - -2457 -2467 0 - -2457 -2477 0 - -2457 -2484 0 - -2457 -2496 0 - -2457 -2501 0 - -2457 -2508 0 - -2457 -2515 0 - -2457 -2521 0 - -2457 -2527 0 - -2457 -2542 0 - -2467 -2477 0 - -2467 -2484 0 - -2467 -2496 0 - -2467 -2501 0 - -2467 -2508 0 - -2467 -2515 0 - -2467 -2521 0 - -2467 -2527 0 - -2467 -2542 0 - -2477 -2484 0 - -2477 -2496 0 - -2477 -2501 0 - -2477 -2508 0 - -2477 -2515 0 - -2477 -2521 0 - -2477 -2527 0 - -2477 -2542 0 - -2484 -2496 0 - -2484 -2501 0 - -2484 -2508 0 - -2484 -2515 0 - -2484 -2521 0 - -2484 -2527 0 - -2484 -2542 0 - -2496 -2501 0 - -2496 -2508 0 - -2496 -2515 0 - -2496 -2521 0 - -2496 -2527 0 - -2496 -2542 0 - -2501 -2508 0 - -2501 -2515 0 - -2501 -2521 0 - -2501 -2527 0 - -2501 -2542 0 - -2508 -2515 0 - -2508 -2521 0 - -2508 -2527 0 - -2508 -2542 0 - -2515 -2521 0 - -2515 -2527 0 - -2515 -2542 0 - -2521 -2527 0 - -2521 -2542 0 - -2527 -2542 0 - -2437 -2444 0 - -2437 -2468 0 - -2437 -2485 0 - -2437 -2497 0 - -2437 -2536 0 - -2437 -2543 0 - -2444 -2468 0 - -2444 -2485 0 - -2444 -2497 0 - -2444 -2536 0 - -2444 -2543 0 - -2468 -2485 0 - -2468 -2497 0 - -2468 -2536 0 - -2468 -2543 0 - -2485 -2497 0 - -2485 -2536 0 - -2485 -2543 0 - -2497 -2536 0 - -2497 -2543 0 - -2536 -2543 0 - -2438 -2469 0 - -2438 -2478 0 - -2438 -2492 0 - -2438 -2522 0 - -2438 -2528 0 - -2438 -2537 0 - -2469 -2478 0 - -2469 -2492 0 - -2469 -2522 0 - -2469 -2528 0 - -2469 -2537 0 - -2478 -2492 0 - -2478 -2522 0 - -2478 -2528 0 - -2478 -2537 0 - -2492 -2522 0 - -2492 -2528 0 - -2492 -2537 0 - -2522 -2528 0 - -2522 -2537 0 - -2528 -2537 0 - -2428 -2449 0 - -2428 -2458 0 - -2428 -2470 0 - -2428 -2493 0 - -2428 -2516 0 - -2428 -2529 0 - -2449 -2458 0 - -2449 -2470 0 - -2449 -2493 0 - -2449 -2516 0 - -2449 -2529 0 - -2458 -2470 0 - -2458 -2493 0 - -2458 -2516 0 - -2458 -2529 0 - -2470 -2493 0 - -2470 -2516 0 - -2470 -2529 0 - -2493 -2516 0 - -2493 -2529 0 - -2516 -2529 0 - -2429 -2439 0 - -2429 -2445 0 - -2429 -2471 0 - -2429 -2502 0 - -2439 -2445 0 - -2439 -2471 0 - -2439 -2502 0 - -2445 -2471 0 - -2445 -2502 0 - -2471 -2502 0 - -2446 -2450 0 - -2446 -2459 0 - -2446 -2479 0 - -2446 -2486 0 - -2446 -2498 0 - -2446 -2509 0 - -2446 -2517 0 - -2446 -2523 0 - -2446 -2530 0 - -2446 -2538 0 - -2450 -2459 0 - -2450 -2479 0 - -2450 -2486 0 - -2450 -2498 0 - -2450 -2509 0 - -2450 -2517 0 - -2450 -2523 0 - -2450 -2530 0 - -2450 -2538 0 - -2459 -2479 0 - -2459 -2486 0 - -2459 -2498 0 - -2459 -2509 0 - -2459 -2517 0 - -2459 -2523 0 - -2459 -2530 0 - -2459 -2538 0 - -2479 -2486 0 - -2479 -2498 0 - -2479 -2509 0 - -2479 -2517 0 - -2479 -2523 0 - -2479 -2530 0 - -2479 -2538 0 - -2486 -2498 0 - -2486 -2509 0 - -2486 -2517 0 - -2486 -2523 0 - -2486 -2530 0 - -2486 -2538 0 - -2498 -2509 0 - -2498 -2517 0 - -2498 -2523 0 - -2498 -2530 0 - -2498 -2538 0 - -2509 -2517 0 - -2509 -2523 0 - -2509 -2530 0 - -2509 -2538 0 - -2517 -2523 0 - -2517 -2530 0 - -2517 -2538 0 - -2523 -2530 0 - -2523 -2538 0 - -2530 -2538 0 - -2440 -2451 0 - -2440 -2460 0 - -2440 -2499 0 - -2440 -2503 0 - -2440 -2531 0 - -2440 -2544 0 - -2451 -2460 0 - -2451 -2499 0 - -2451 -2503 0 - -2451 -2531 0 - -2451 -2544 0 - -2460 -2499 0 - -2460 -2503 0 - -2460 -2531 0 - -2460 -2544 0 - -2499 -2503 0 - -2499 -2531 0 - -2499 -2544 0 - -2503 -2531 0 - -2503 -2544 0 - -2531 -2544 0 - -2545 -2565 0 - -2545 -2587 0 - -2545 -2594 0 - -2545 -2600 0 - -2545 -2612 0 - -2565 -2587 0 - -2565 -2594 0 - -2565 -2600 0 - -2565 -2612 0 - -2587 -2594 0 - -2587 -2600 0 - -2587 -2612 0 - -2594 -2600 0 - -2594 -2612 0 - -2600 -2612 0 - -2555 -2570 0 - -2555 -2572 0 - -2555 -2581 0 - -2555 -2588 0 - -2555 -2606 0 - -2555 -2613 0 - -2570 -2572 0 - -2570 -2581 0 - -2570 -2588 0 - -2570 -2606 0 - -2570 -2613 0 - -2572 -2581 0 - -2572 -2588 0 - -2572 -2606 0 - -2572 -2613 0 - -2581 -2588 0 - -2581 -2606 0 - -2581 -2613 0 - -2588 -2606 0 - -2588 -2613 0 - -2606 -2613 0 - -2546 -2556 0 - -2546 -2573 0 - -2546 -2589 0 - -2546 -2607 0 - -2546 -2614 0 - -2556 -2573 0 - -2556 -2589 0 - -2556 -2607 0 - -2556 -2614 0 - -2573 -2589 0 - -2573 -2607 0 - -2573 -2614 0 - -2589 -2607 0 - -2589 -2614 0 - -2607 -2614 0 - -2566 -2582 0 - -2566 -2595 0 - -2566 -2601 0 - -2566 -2608 0 - -2566 -2615 0 - -2582 -2595 0 - -2582 -2601 0 - -2582 -2608 0 - -2582 -2615 0 - -2595 -2601 0 - -2595 -2608 0 - -2595 -2615 0 - -2601 -2608 0 - -2601 -2615 0 - -2608 -2615 0 - -2547 -2590 0 - -2547 -2609 0 - -2547 -2616 0 - -2590 -2609 0 - -2590 -2616 0 - -2609 -2616 0 - -2548 -2557 0 - -2548 -2561 0 - -2548 -2571 0 - -2548 -2574 0 - -2548 -2583 0 - -2548 -2591 0 - -2548 -2596 0 - -2557 -2561 0 - -2557 -2571 0 - -2557 -2574 0 - -2557 -2583 0 - -2557 -2591 0 - -2557 -2596 0 - -2561 -2571 0 - -2561 -2574 0 - -2561 -2583 0 - -2561 -2591 0 - -2561 -2596 0 - -2571 -2574 0 - -2571 -2583 0 - -2571 -2591 0 - -2571 -2596 0 - -2574 -2583 0 - -2574 -2591 0 - -2574 -2596 0 - -2583 -2591 0 - -2583 -2596 0 - -2591 -2596 0 - -2549 -2567 0 - -2549 -2575 0 - -2549 -2584 0 - -2549 -2619 0 - -2567 -2575 0 - -2567 -2584 0 - -2567 -2619 0 - -2575 -2584 0 - -2575 -2619 0 - -2584 -2619 0 - -2550 -2562 0 - -2550 -2576 0 - -2550 -2585 0 - -2550 -2602 0 - -2550 -2617 0 - -2562 -2576 0 - -2562 -2585 0 - -2562 -2602 0 - -2562 -2617 0 - -2576 -2585 0 - -2576 -2602 0 - -2576 -2617 0 - -2585 -2602 0 - -2585 -2617 0 - -2602 -2617 0 - -2563 -2568 0 - -2563 -2577 0 - -2563 -2592 0 - -2563 -2597 0 - -2563 -2603 0 - -2568 -2577 0 - -2568 -2592 0 - -2568 -2597 0 - -2568 -2603 0 - -2577 -2592 0 - -2577 -2597 0 - -2577 -2603 0 - -2592 -2597 0 - -2592 -2603 0 - -2597 -2603 0 - -2551 -2558 0 - -2551 -2569 0 - -2551 -2578 0 - -2551 -2598 0 - -2551 -2604 0 - -2551 -2610 0 - -2551 -2620 0 - -2558 -2569 0 - -2558 -2578 0 - -2558 -2598 0 - -2558 -2604 0 - -2558 -2610 0 - -2558 -2620 0 - -2569 -2578 0 - -2569 -2598 0 - -2569 -2604 0 - -2569 -2610 0 - -2569 -2620 0 - -2578 -2598 0 - -2578 -2604 0 - -2578 -2610 0 - -2578 -2620 0 - -2598 -2604 0 - -2598 -2610 0 - -2598 -2620 0 - -2604 -2610 0 - -2604 -2620 0 - -2610 -2620 0 - -2552 -2559 0 - -2552 -2564 0 - -2552 -2579 0 - -2552 -2586 0 - -2552 -2599 0 - -2552 -2605 0 - -2552 -2621 0 - -2559 -2564 0 - -2559 -2579 0 - -2559 -2586 0 - -2559 -2599 0 - -2559 -2605 0 - -2559 -2621 0 - -2564 -2579 0 - -2564 -2586 0 - -2564 -2599 0 - -2564 -2605 0 - -2564 -2621 0 - -2579 -2586 0 - -2579 -2599 0 - -2579 -2605 0 - -2579 -2621 0 - -2586 -2599 0 - -2586 -2605 0 - -2586 -2621 0 - -2599 -2605 0 - -2599 -2621 0 - -2605 -2621 0 - -2553 -2593 0 - -2553 -2611 0 - -2593 -2611 0 - -2554 -2560 0 - -2554 -2580 0 - -2554 -2618 0 - -2554 -2622 0 - -2560 -2580 0 - -2560 -2618 0 - -2560 -2622 0 - -2580 -2618 0 - -2580 -2622 0 - -2618 -2622 0 - -2626 -2632 0 - -2626 -2650 0 - -2626 -2665 0 - -2632 -2650 0 - -2632 -2665 0 - -2650 -2665 0 - -2627 -2651 0 - -2627 -2657 0 - -2627 -2674 0 - -2627 -2681 0 - -2651 -2657 0 - -2651 -2674 0 - -2651 -2681 0 - -2657 -2674 0 - -2657 -2681 0 - -2674 -2681 0 - -2636 -2642 0 - -2636 -2675 0 - -2642 -2675 0 - -2623 -2637 0 - -2623 -2643 0 - -2623 -2658 0 - -2623 -2661 0 - -2623 -2666 0 - -2637 -2643 0 - -2637 -2658 0 - -2637 -2661 0 - -2637 -2666 0 - -2643 -2658 0 - -2643 -2661 0 - -2643 -2666 0 - -2658 -2661 0 - -2658 -2666 0 - -2661 -2666 0 - -2638 -2644 0 - -2638 -2676 0 - -2644 -2676 0 - -2633 -2639 0 - -2633 -2671 0 - -2633 -2677 0 - -2639 -2671 0 - -2639 -2677 0 - -2671 -2677 0 - -2640 -2645 0 - -2640 -2652 0 - -2640 -2667 0 - -2640 -2672 0 - -2640 -2678 0 - -2645 -2652 0 - -2645 -2667 0 - -2645 -2672 0 - -2645 -2678 0 - -2652 -2667 0 - -2652 -2672 0 - -2652 -2678 0 - -2667 -2672 0 - -2667 -2678 0 - -2672 -2678 0 - -2628 -2646 0 - -2628 -2653 0 - -2628 -2673 0 - -2628 -2679 0 - -2646 -2653 0 - -2646 -2673 0 - -2646 -2679 0 - -2653 -2673 0 - -2653 -2679 0 - -2673 -2679 0 - -2629 -2634 0 - -2629 -2647 0 - -2629 -2654 0 - -2629 -2659 0 - -2629 -2662 0 - -2629 -2668 0 - -2629 -2680 0 - -2629 -2682 0 - -2634 -2647 0 - -2634 -2654 0 - -2634 -2659 0 - -2634 -2662 0 - -2634 -2668 0 - -2634 -2680 0 - -2634 -2682 0 - -2647 -2654 0 - -2647 -2659 0 - -2647 -2662 0 - -2647 -2668 0 - -2647 -2680 0 - -2647 -2682 0 - -2654 -2659 0 - -2654 -2662 0 - -2654 -2668 0 - -2654 -2680 0 - -2654 -2682 0 - -2659 -2662 0 - -2659 -2668 0 - -2659 -2680 0 - -2659 -2682 0 - -2662 -2668 0 - -2662 -2680 0 - -2662 -2682 0 - -2668 -2680 0 - -2668 -2682 0 - -2680 -2682 0 - -2624 -2630 0 - -2624 -2635 0 - -2624 -2648 0 - -2624 -2663 0 - -2624 -2669 0 - -2630 -2635 0 - -2630 -2648 0 - -2630 -2663 0 - -2630 -2669 0 - -2635 -2648 0 - -2635 -2663 0 - -2635 -2669 0 - -2648 -2663 0 - -2648 -2669 0 - -2663 -2669 0 - -2625 -2631 0 - -2625 -2649 0 - -2625 -2655 0 - -2625 -2660 0 - -2625 -2664 0 - -2625 -2670 0 - -2625 -2683 0 - -2631 -2649 0 - -2631 -2655 0 - -2631 -2660 0 - -2631 -2664 0 - -2631 -2670 0 - -2631 -2683 0 - -2649 -2655 0 - -2649 -2660 0 - -2649 -2664 0 - -2649 -2670 0 - -2649 -2683 0 - -2655 -2660 0 - -2655 -2664 0 - -2655 -2670 0 - -2655 -2683 0 - -2660 -2664 0 - -2660 -2670 0 - -2660 -2683 0 - -2664 -2670 0 - -2664 -2683 0 - -2670 -2683 0 - -2641 -2656 0 - -2641 -2684 0 - -2656 -2684 0 - -2711 -2720 0 - -2711 -2756 0 - -2711 -2769 0 - -2711 -2775 0 - -2711 -2785 0 - -2711 -2793 0 - -2720 -2756 0 - -2720 -2769 0 - -2720 -2775 0 - -2720 -2785 0 - -2720 -2793 0 - -2756 -2769 0 - -2756 -2775 0 - -2756 -2785 0 - -2756 -2793 0 - -2769 -2775 0 - -2769 -2785 0 - -2769 -2793 0 - -2775 -2785 0 - -2775 -2793 0 - -2785 -2793 0 - -2693 -2703 0 - -2693 -2712 0 - -2693 -2729 0 - -2693 -2739 0 - -2693 -2776 0 - -2693 -2794 0 - -2703 -2712 0 - -2703 -2729 0 - -2703 -2739 0 - -2703 -2776 0 - -2703 -2794 0 - -2712 -2729 0 - -2712 -2739 0 - -2712 -2776 0 - -2712 -2794 0 - -2729 -2739 0 - -2729 -2776 0 - -2729 -2794 0 - -2739 -2776 0 - -2739 -2794 0 - -2776 -2794 0 - -2713 -2721 0 - -2713 -2725 0 - -2713 -2730 0 - -2713 -2740 0 - -2713 -2745 0 - -2713 -2757 0 - -2713 -2763 0 - -2721 -2725 0 - -2721 -2730 0 - -2721 -2740 0 - -2721 -2745 0 - -2721 -2757 0 - -2721 -2763 0 - -2725 -2730 0 - -2725 -2740 0 - -2725 -2745 0 - -2725 -2757 0 - -2725 -2763 0 - -2730 -2740 0 - -2730 -2745 0 - -2730 -2757 0 - -2730 -2763 0 - -2740 -2745 0 - -2740 -2757 0 - -2740 -2763 0 - -2745 -2757 0 - -2745 -2763 0 - -2757 -2763 0 - -2714 -2731 0 - -2714 -2741 0 - -2714 -2746 0 - -2714 -2764 0 - -2714 -2777 0 - -2714 -2786 0 - -2731 -2741 0 - -2731 -2746 0 - -2731 -2764 0 - -2731 -2777 0 - -2731 -2786 0 - -2741 -2746 0 - -2741 -2764 0 - -2741 -2777 0 - -2741 -2786 0 - -2746 -2764 0 - -2746 -2777 0 - -2746 -2786 0 - -2764 -2777 0 - -2764 -2786 0 - -2777 -2786 0 - -2685 -2694 0 - -2685 -2704 0 - -2685 -2715 0 - -2685 -2732 0 - -2685 -2742 0 - -2685 -2751 0 - -2685 -2758 0 - -2685 -2778 0 - -2685 -2787 0 - -2694 -2704 0 - -2694 -2715 0 - -2694 -2732 0 - -2694 -2742 0 - -2694 -2751 0 - -2694 -2758 0 - -2694 -2778 0 - -2694 -2787 0 - -2704 -2715 0 - -2704 -2732 0 - -2704 -2742 0 - -2704 -2751 0 - -2704 -2758 0 - -2704 -2778 0 - -2704 -2787 0 - -2715 -2732 0 - -2715 -2742 0 - -2715 -2751 0 - -2715 -2758 0 - -2715 -2778 0 - -2715 -2787 0 - -2732 -2742 0 - -2732 -2751 0 - -2732 -2758 0 - -2732 -2778 0 - -2732 -2787 0 - -2742 -2751 0 - -2742 -2758 0 - -2742 -2778 0 - -2742 -2787 0 - -2751 -2758 0 - -2751 -2778 0 - -2751 -2787 0 - -2758 -2778 0 - -2758 -2787 0 - -2778 -2787 0 - -2686 -2695 0 - -2686 -2705 0 - -2686 -2716 0 - -2686 -2779 0 - -2686 -2788 0 - -2695 -2705 0 - -2695 -2716 0 - -2695 -2779 0 - -2695 -2788 0 - -2705 -2716 0 - -2705 -2779 0 - -2705 -2788 0 - -2716 -2779 0 - -2716 -2788 0 - -2779 -2788 0 - -2687 -2696 0 - -2687 -2747 0 - -2687 -2780 0 - -2687 -2795 0 - -2696 -2747 0 - -2696 -2780 0 - -2696 -2795 0 - -2747 -2780 0 - -2747 -2795 0 - -2780 -2795 0 - -2688 -2697 0 - -2688 -2706 0 - -2688 -2722 0 - -2688 -2726 0 - -2688 -2748 0 - -2688 -2770 0 - -2688 -2781 0 - -2697 -2706 0 - -2697 -2722 0 - -2697 -2726 0 - -2697 -2748 0 - -2697 -2770 0 - -2697 -2781 0 - -2706 -2722 0 - -2706 -2726 0 - -2706 -2748 0 - -2706 -2770 0 - -2706 -2781 0 - -2722 -2726 0 - -2722 -2748 0 - -2722 -2770 0 - -2722 -2781 0 - -2726 -2748 0 - -2726 -2770 0 - -2726 -2781 0 - -2748 -2770 0 - -2748 -2781 0 - -2770 -2781 0 - -2689 -2698 0 - -2689 -2759 0 - -2689 -2771 0 - -2698 -2759 0 - -2698 -2771 0 - -2759 -2771 0 - -2699 -2717 0 - -2699 -2723 0 - -2699 -2733 0 - -2699 -2749 0 - -2699 -2752 0 - -2699 -2760 0 - -2699 -2765 0 - -2699 -2782 0 - -2699 -2789 0 - -2717 -2723 0 - -2717 -2733 0 - -2717 -2749 0 - -2717 -2752 0 - -2717 -2760 0 - -2717 -2765 0 - -2717 -2782 0 - -2717 -2789 0 - -2723 -2733 0 - -2723 -2749 0 - -2723 -2752 0 - -2723 -2760 0 - -2723 -2765 0 - -2723 -2782 0 - -2723 -2789 0 - -2733 -2749 0 - -2733 -2752 0 - -2733 -2760 0 - -2733 -2765 0 - -2733 -2782 0 - -2733 -2789 0 - -2749 -2752 0 - -2749 -2760 0 - -2749 -2765 0 - -2749 -2782 0 - -2749 -2789 0 - -2752 -2760 0 - -2752 -2765 0 - -2752 -2782 0 - -2752 -2789 0 - -2760 -2765 0 - -2760 -2782 0 - -2760 -2789 0 - -2765 -2782 0 - -2765 -2789 0 - -2782 -2789 0 - -2690 -2707 0 - -2690 -2718 0 - -2690 -2727 0 - -2690 -2734 0 - -2690 -2743 0 - -2690 -2766 0 - -2690 -2772 0 - -2690 -2796 0 - -2707 -2718 0 - -2707 -2727 0 - -2707 -2734 0 - -2707 -2743 0 - -2707 -2766 0 - -2707 -2772 0 - -2707 -2796 0 - -2718 -2727 0 - -2718 -2734 0 - -2718 -2743 0 - -2718 -2766 0 - -2718 -2772 0 - -2718 -2796 0 - -2727 -2734 0 - -2727 -2743 0 - -2727 -2766 0 - -2727 -2772 0 - -2727 -2796 0 - -2734 -2743 0 - -2734 -2766 0 - -2734 -2772 0 - -2734 -2796 0 - -2743 -2766 0 - -2743 -2772 0 - -2743 -2796 0 - -2766 -2772 0 - -2766 -2796 0 - -2772 -2796 0 - -2700 -2708 0 - -2700 -2728 0 - -2700 -2735 0 - -2700 -2753 0 - -2700 -2790 0 - -2700 -2797 0 - -2708 -2728 0 - -2708 -2735 0 - -2708 -2753 0 - -2708 -2790 0 - -2708 -2797 0 - -2728 -2735 0 - -2728 -2753 0 - -2728 -2790 0 - -2728 -2797 0 - -2735 -2753 0 - -2735 -2790 0 - -2735 -2797 0 - -2753 -2790 0 - -2753 -2797 0 - -2790 -2797 0 - -2691 -2736 0 - -2691 -2750 0 - -2691 -2754 0 - -2691 -2767 0 - -2691 -2773 0 - -2691 -2783 0 - -2736 -2750 0 - -2736 -2754 0 - -2736 -2767 0 - -2736 -2773 0 - -2736 -2783 0 - -2750 -2754 0 - -2750 -2767 0 - -2750 -2773 0 - -2750 -2783 0 - -2754 -2767 0 - -2754 -2773 0 - -2754 -2783 0 - -2767 -2773 0 - -2767 -2783 0 - -2773 -2783 0 - -2692 -2701 0 - -2692 -2709 0 - -2692 -2737 0 - -2692 -2761 0 - -2692 -2784 0 - -2701 -2709 0 - -2701 -2737 0 - -2701 -2761 0 - -2701 -2784 0 - -2709 -2737 0 - -2709 -2761 0 - -2709 -2784 0 - -2737 -2761 0 - -2737 -2784 0 - -2761 -2784 0 - -2724 -2744 0 - -2724 -2768 0 - -2724 -2774 0 - -2724 -2791 0 - -2744 -2768 0 - -2744 -2774 0 - -2744 -2791 0 - -2768 -2774 0 - -2768 -2791 0 - -2774 -2791 0 - -2702 -2710 0 - -2702 -2719 0 - -2702 -2738 0 - -2702 -2755 0 - -2702 -2762 0 - -2702 -2792 0 - -2702 -2798 0 - -2710 -2719 0 - -2710 -2738 0 - -2710 -2755 0 - -2710 -2762 0 - -2710 -2792 0 - -2710 -2798 0 - -2719 -2738 0 - -2719 -2755 0 - -2719 -2762 0 - -2719 -2792 0 - -2719 -2798 0 - -2738 -2755 0 - -2738 -2762 0 - -2738 -2792 0 - -2738 -2798 0 - -2755 -2762 0 - -2755 -2792 0 - -2755 -2798 0 - -2762 -2792 0 - -2762 -2798 0 - -2792 -2798 0 - -2820 -2828 0 - -2820 -2846 0 - -2820 -2864 0 - -2820 -2870 0 - -2820 -2900 0 - -2820 -2910 0 - -2828 -2846 0 - -2828 -2864 0 - -2828 -2870 0 - -2828 -2900 0 - -2828 -2910 0 - -2846 -2864 0 - -2846 -2870 0 - -2846 -2900 0 - -2846 -2910 0 - -2864 -2870 0 - -2864 -2900 0 - -2864 -2910 0 - -2870 -2900 0 - -2870 -2910 0 - -2900 -2910 0 - -2810 -2821 0 - -2810 -2829 0 - -2810 -2835 0 - -2810 -2880 0 - -2810 -2893 0 - -2810 -2901 0 - -2821 -2829 0 - -2821 -2835 0 - -2821 -2880 0 - -2821 -2893 0 - -2821 -2901 0 - -2829 -2835 0 - -2829 -2880 0 - -2829 -2893 0 - -2829 -2901 0 - -2835 -2880 0 - -2835 -2893 0 - -2835 -2901 0 - -2880 -2893 0 - -2880 -2901 0 - -2893 -2901 0 - -2799 -2805 0 - -2799 -2811 0 - -2799 -2847 0 - -2799 -2857 0 - -2799 -2871 0 - -2799 -2911 0 - -2805 -2811 0 - -2805 -2847 0 - -2805 -2857 0 - -2805 -2871 0 - -2805 -2911 0 - -2811 -2847 0 - -2811 -2857 0 - -2811 -2871 0 - -2811 -2911 0 - -2847 -2857 0 - -2847 -2871 0 - -2847 -2911 0 - -2857 -2871 0 - -2857 -2911 0 - -2871 -2911 0 - -2800 -2806 0 - -2800 -2812 0 - -2800 -2830 0 - -2800 -2836 0 - -2800 -2848 0 - -2800 -2858 0 - -2800 -2865 0 - -2800 -2872 0 - -2800 -2881 0 - -2800 -2894 0 - -2800 -2902 0 - -2800 -2912 0 - -2806 -2812 0 - -2806 -2830 0 - -2806 -2836 0 - -2806 -2848 0 - -2806 -2858 0 - -2806 -2865 0 - -2806 -2872 0 - -2806 -2881 0 - -2806 -2894 0 - -2806 -2902 0 - -2806 -2912 0 - -2812 -2830 0 - -2812 -2836 0 - -2812 -2848 0 - -2812 -2858 0 - -2812 -2865 0 - -2812 -2872 0 - -2812 -2881 0 - -2812 -2894 0 - -2812 -2902 0 - -2812 -2912 0 - -2830 -2836 0 - -2830 -2848 0 - -2830 -2858 0 - -2830 -2865 0 - -2830 -2872 0 - -2830 -2881 0 - -2830 -2894 0 - -2830 -2902 0 - -2830 -2912 0 - -2836 -2848 0 - -2836 -2858 0 - -2836 -2865 0 - -2836 -2872 0 - -2836 -2881 0 - -2836 -2894 0 - -2836 -2902 0 - -2836 -2912 0 - -2848 -2858 0 - -2848 -2865 0 - -2848 -2872 0 - -2848 -2881 0 - -2848 -2894 0 - -2848 -2902 0 - -2848 -2912 0 - -2858 -2865 0 - -2858 -2872 0 - -2858 -2881 0 - -2858 -2894 0 - -2858 -2902 0 - -2858 -2912 0 - -2865 -2872 0 - -2865 -2881 0 - -2865 -2894 0 - -2865 -2902 0 - -2865 -2912 0 - -2872 -2881 0 - -2872 -2894 0 - -2872 -2902 0 - -2872 -2912 0 - -2881 -2894 0 - -2881 -2902 0 - -2881 -2912 0 - -2894 -2902 0 - -2894 -2912 0 - -2902 -2912 0 - -2837 -2849 0 - -2837 -2853 0 - -2837 -2882 0 - -2837 -2895 0 - -2837 -2903 0 - -2849 -2853 0 - -2849 -2882 0 - -2849 -2895 0 - -2849 -2903 0 - -2853 -2882 0 - -2853 -2895 0 - -2853 -2903 0 - -2882 -2895 0 - -2882 -2903 0 - -2895 -2903 0 - -2807 -2822 0 - -2807 -2838 0 - -2807 -2859 0 - -2807 -2896 0 - -2807 -2904 0 - -2807 -2913 0 - -2822 -2838 0 - -2822 -2859 0 - -2822 -2896 0 - -2822 -2904 0 - -2822 -2913 0 - -2838 -2859 0 - -2838 -2896 0 - -2838 -2904 0 - -2838 -2913 0 - -2859 -2896 0 - -2859 -2904 0 - -2859 -2913 0 - -2896 -2904 0 - -2896 -2913 0 - -2904 -2913 0 - -2813 -2850 0 - -2813 -2866 0 - -2813 -2883 0 - -2813 -2888 0 - -2813 -2905 0 - -2813 -2914 0 - -2850 -2866 0 - -2850 -2883 0 - -2850 -2888 0 - -2850 -2905 0 - -2850 -2914 0 - -2866 -2883 0 - -2866 -2888 0 - -2866 -2905 0 - -2866 -2914 0 - -2883 -2888 0 - -2883 -2905 0 - -2883 -2914 0 - -2888 -2905 0 - -2888 -2914 0 - -2905 -2914 0 - -2801 -2808 0 - -2801 -2814 0 - -2801 -2851 0 - -2801 -2854 0 - -2801 -2906 0 - -2808 -2814 0 - -2808 -2851 0 - -2808 -2854 0 - -2808 -2906 0 - -2814 -2851 0 - -2814 -2854 0 - -2814 -2906 0 - -2851 -2854 0 - -2851 -2906 0 - -2854 -2906 0 - -2802 -2815 0 - -2802 -2839 0 - -2802 -2873 0 - -2802 -2897 0 - -2802 -2907 0 - -2815 -2839 0 - -2815 -2873 0 - -2815 -2897 0 - -2815 -2907 0 - -2839 -2873 0 - -2839 -2897 0 - -2839 -2907 0 - -2873 -2897 0 - -2873 -2907 0 - -2897 -2907 0 - -2809 -2823 0 - -2809 -2831 0 - -2809 -2840 0 - -2809 -2855 0 - -2809 -2860 0 - -2809 -2867 0 - -2809 -2874 0 - -2809 -2889 0 - -2823 -2831 0 - -2823 -2840 0 - -2823 -2855 0 - -2823 -2860 0 - -2823 -2867 0 - -2823 -2874 0 - -2823 -2889 0 - -2831 -2840 0 - -2831 -2855 0 - -2831 -2860 0 - -2831 -2867 0 - -2831 -2874 0 - -2831 -2889 0 - -2840 -2855 0 - -2840 -2860 0 - -2840 -2867 0 - -2840 -2874 0 - -2840 -2889 0 - -2855 -2860 0 - -2855 -2867 0 - -2855 -2874 0 - -2855 -2889 0 - -2860 -2867 0 - -2860 -2874 0 - -2860 -2889 0 - -2867 -2874 0 - -2867 -2889 0 - -2874 -2889 0 - -2816 -2824 0 - -2816 -2832 0 - -2816 -2841 0 - -2816 -2875 0 - -2816 -2884 0 - -2816 -2890 0 - -2816 -2898 0 - -2824 -2832 0 - -2824 -2841 0 - -2824 -2875 0 - -2824 -2884 0 - -2824 -2890 0 - -2824 -2898 0 - -2832 -2841 0 - -2832 -2875 0 - -2832 -2884 0 - -2832 -2890 0 - -2832 -2898 0 - -2841 -2875 0 - -2841 -2884 0 - -2841 -2890 0 - -2841 -2898 0 - -2875 -2884 0 - -2875 -2890 0 - -2875 -2898 0 - -2884 -2890 0 - -2884 -2898 0 - -2890 -2898 0 - -2803 -2825 0 - -2803 -2833 0 - -2803 -2852 0 - -2803 -2861 0 - -2803 -2876 0 - -2803 -2908 0 - -2825 -2833 0 - -2825 -2852 0 - -2825 -2861 0 - -2825 -2876 0 - -2825 -2908 0 - -2833 -2852 0 - -2833 -2861 0 - -2833 -2876 0 - -2833 -2908 0 - -2852 -2861 0 - -2852 -2876 0 - -2852 -2908 0 - -2861 -2876 0 - -2861 -2908 0 - -2876 -2908 0 - -2804 -2826 0 - -2804 -2842 0 - -2804 -2885 0 - -2804 -2891 0 - -2804 -2915 0 - -2826 -2842 0 - -2826 -2885 0 - -2826 -2891 0 - -2826 -2915 0 - -2842 -2885 0 - -2842 -2891 0 - -2842 -2915 0 - -2885 -2891 0 - -2885 -2915 0 - -2891 -2915 0 - -2817 -2843 0 - -2817 -2856 0 - -2817 -2862 0 - -2817 -2868 0 - -2817 -2877 0 - -2817 -2886 0 - -2817 -2892 0 - -2817 -2899 0 - -2817 -2909 0 - -2843 -2856 0 - -2843 -2862 0 - -2843 -2868 0 - -2843 -2877 0 - -2843 -2886 0 - -2843 -2892 0 - -2843 -2899 0 - -2843 -2909 0 - -2856 -2862 0 - -2856 -2868 0 - -2856 -2877 0 - -2856 -2886 0 - -2856 -2892 0 - -2856 -2899 0 - -2856 -2909 0 - -2862 -2868 0 - -2862 -2877 0 - -2862 -2886 0 - -2862 -2892 0 - -2862 -2899 0 - -2862 -2909 0 - -2868 -2877 0 - -2868 -2886 0 - -2868 -2892 0 - -2868 -2899 0 - -2868 -2909 0 - -2877 -2886 0 - -2877 -2892 0 - -2877 -2899 0 - -2877 -2909 0 - -2886 -2892 0 - -2886 -2899 0 - -2886 -2909 0 - -2892 -2899 0 - -2892 -2909 0 - -2899 -2909 0 - -2818 -2844 0 - -2818 -2869 0 - -2818 -2878 0 - -2844 -2869 0 - -2844 -2878 0 - -2869 -2878 0 - -2819 -2827 0 - -2819 -2834 0 - -2819 -2845 0 - -2819 -2863 0 - -2819 -2879 0 - -2819 -2887 0 - -2819 -2916 0 - -2827 -2834 0 - -2827 -2845 0 - -2827 -2863 0 - -2827 -2879 0 - -2827 -2887 0 - -2827 -2916 0 - -2834 -2845 0 - -2834 -2863 0 - -2834 -2879 0 - -2834 -2887 0 - -2834 -2916 0 - -2845 -2863 0 - -2845 -2879 0 - -2845 -2887 0 - -2845 -2916 0 - -2863 -2879 0 - -2863 -2887 0 - -2863 -2916 0 - -2879 -2887 0 - -2879 -2916 0 - -2887 -2916 0 - -2925 -2937 0 - -2925 -2948 0 - -2925 -2955 0 - -2925 -2967 0 - -2925 -2972 0 - -2925 -2986 0 - -2925 -2998 0 - -2937 -2948 0 - -2937 -2955 0 - -2937 -2967 0 - -2937 -2972 0 - -2937 -2986 0 - -2937 -2998 0 - -2948 -2955 0 - -2948 -2967 0 - -2948 -2972 0 - -2948 -2986 0 - -2948 -2998 0 - -2955 -2967 0 - -2955 -2972 0 - -2955 -2986 0 - -2955 -2998 0 - -2967 -2972 0 - -2967 -2986 0 - -2967 -2998 0 - -2972 -2986 0 - -2972 -2998 0 - -2986 -2998 0 - -2917 -2926 0 - -2917 -2934 0 - -2917 -2949 0 - -2917 -2962 0 - -2917 -2980 0 - -2917 -2987 0 - -2917 -2992 0 - -2917 -2999 0 - -2926 -2934 0 - -2926 -2949 0 - -2926 -2962 0 - -2926 -2980 0 - -2926 -2987 0 - -2926 -2992 0 - -2926 -2999 0 - -2934 -2949 0 - -2934 -2962 0 - -2934 -2980 0 - -2934 -2987 0 - -2934 -2992 0 - -2934 -2999 0 - -2949 -2962 0 - -2949 -2980 0 - -2949 -2987 0 - -2949 -2992 0 - -2949 -2999 0 - -2962 -2980 0 - -2962 -2987 0 - -2962 -2992 0 - -2962 -2999 0 - -2980 -2987 0 - -2980 -2992 0 - -2980 -2999 0 - -2987 -2992 0 - -2987 -2999 0 - -2992 -2999 0 - -2927 -2956 0 - -2927 -2973 0 - -2927 -2981 0 - -2927 -3000 0 - -2956 -2973 0 - -2956 -2981 0 - -2956 -3000 0 - -2973 -2981 0 - -2973 -3000 0 - -2981 -3000 0 - -2918 -2938 0 - -2918 -2957 0 - -2918 -2963 0 - -2918 -2993 0 - -2918 -3001 0 - -2938 -2957 0 - -2938 -2963 0 - -2938 -2993 0 - -2938 -3001 0 - -2957 -2963 0 - -2957 -2993 0 - -2957 -3001 0 - -2963 -2993 0 - -2963 -3001 0 - -2993 -3001 0 - -2919 -2943 0 - -2919 -2950 0 - -2919 -2958 0 - -2919 -2968 0 - -2919 -2994 0 - -2919 -3002 0 - -2943 -2950 0 - -2943 -2958 0 - -2943 -2968 0 - -2943 -2994 0 - -2943 -3002 0 - -2950 -2958 0 - -2950 -2968 0 - -2950 -2994 0 - -2950 -3002 0 - -2958 -2968 0 - -2958 -2994 0 - -2958 -3002 0 - -2968 -2994 0 - -2968 -3002 0 - -2994 -3002 0 - -2928 -2939 0 - -2928 -2974 0 - -2928 -2982 0 - -2928 -2995 0 - -2928 -3003 0 - -2939 -2974 0 - -2939 -2982 0 - -2939 -2995 0 - -2939 -3003 0 - -2974 -2982 0 - -2974 -2995 0 - -2974 -3003 0 - -2982 -2995 0 - -2982 -3003 0 - -2995 -3003 0 - -2920 -2935 0 - -2920 -2944 0 - -2920 -2964 0 - -2920 -2975 0 - -2920 -3004 0 - -2935 -2944 0 - -2935 -2964 0 - -2935 -2975 0 - -2935 -3004 0 - -2944 -2964 0 - -2944 -2975 0 - -2944 -3004 0 - -2964 -2975 0 - -2964 -3004 0 - -2975 -3004 0 - -2929 -2945 0 - -2929 -2959 0 - -2929 -2965 0 - -2929 -2988 0 - -2929 -3005 0 - -2945 -2959 0 - -2945 -2965 0 - -2945 -2988 0 - -2945 -3005 0 - -2959 -2965 0 - -2959 -2988 0 - -2959 -3005 0 - -2965 -2988 0 - -2965 -3005 0 - -2988 -3005 0 - -2921 -2930 0 - -2921 -2936 0 - -2921 -2946 0 - -2921 -2951 0 - -2921 -2960 0 - -2921 -2969 0 - -2921 -2989 0 - -2930 -2936 0 - -2930 -2946 0 - -2930 -2951 0 - -2930 -2960 0 - -2930 -2969 0 - -2930 -2989 0 - -2936 -2946 0 - -2936 -2951 0 - -2936 -2960 0 - -2936 -2969 0 - -2936 -2989 0 - -2946 -2951 0 - -2946 -2960 0 - -2946 -2969 0 - -2946 -2989 0 - -2951 -2960 0 - -2951 -2969 0 - -2951 -2989 0 - -2960 -2969 0 - -2960 -2989 0 - -2969 -2989 0 - -2922 -2931 0 - -2922 -2952 0 - -2922 -2961 0 - -2922 -2976 0 - -2922 -2983 0 - -2922 -2996 0 - -2931 -2952 0 - -2931 -2961 0 - -2931 -2976 0 - -2931 -2983 0 - -2931 -2996 0 - -2952 -2961 0 - -2952 -2976 0 - -2952 -2983 0 - -2952 -2996 0 - -2961 -2976 0 - -2961 -2983 0 - -2961 -2996 0 - -2976 -2983 0 - -2976 -2996 0 - -2983 -2996 0 - -2923 -2932 0 - -2923 -2947 0 - -2923 -2977 0 - -2932 -2947 0 - -2932 -2977 0 - -2947 -2977 0 - -2924 -2940 0 - -2924 -2966 0 - -2924 -2970 0 - -2924 -2978 0 - -2924 -3006 0 - -2940 -2966 0 - -2940 -2970 0 - -2940 -2978 0 - -2940 -3006 0 - -2966 -2970 0 - -2966 -2978 0 - -2966 -3006 0 - -2970 -2978 0 - -2970 -3006 0 - -2978 -3006 0 - -2933 -2941 0 - -2933 -2953 0 - -2933 -2971 0 - -2933 -2984 0 - -2933 -2990 0 - -2941 -2953 0 - -2941 -2971 0 - -2941 -2984 0 - -2941 -2990 0 - -2953 -2971 0 - -2953 -2984 0 - -2953 -2990 0 - -2971 -2984 0 - -2971 -2990 0 - -2984 -2990 0 - -2942 -2954 0 - -2942 -2979 0 - -2942 -2985 0 - -2942 -2991 0 - -2942 -2997 0 - -2942 -3007 0 - -2954 -2979 0 - -2954 -2985 0 - -2954 -2991 0 - -2954 -2997 0 - -2954 -3007 0 - -2979 -2985 0 - -2979 -2991 0 - -2979 -2997 0 - -2979 -3007 0 - -2985 -2991 0 - -2985 -2997 0 - -2985 -3007 0 - -2991 -2997 0 - -2991 -3007 0 - -2997 -3007 0 - -3017 -3033 0 - -3017 -3041 0 - -3017 -3054 0 - -3017 -3070 0 - -3017 -3081 0 - -3017 -3101 0 - -3017 -3112 0 - -3017 -3120 0 - -3017 -3133 0 - -3017 -3176 0 - -3017 -3185 0 - -3017 -3206 0 - -3033 -3041 0 - -3033 -3054 0 - -3033 -3070 0 - -3033 -3081 0 - -3033 -3101 0 - -3033 -3112 0 - -3033 -3120 0 - -3033 -3133 0 - -3033 -3176 0 - -3033 -3185 0 - -3033 -3206 0 - -3041 -3054 0 - -3041 -3070 0 - -3041 -3081 0 - -3041 -3101 0 - -3041 -3112 0 - -3041 -3120 0 - -3041 -3133 0 - -3041 -3176 0 - -3041 -3185 0 - -3041 -3206 0 - -3054 -3070 0 - -3054 -3081 0 - -3054 -3101 0 - -3054 -3112 0 - -3054 -3120 0 - -3054 -3133 0 - -3054 -3176 0 - -3054 -3185 0 - -3054 -3206 0 - -3070 -3081 0 - -3070 -3101 0 - -3070 -3112 0 - -3070 -3120 0 - -3070 -3133 0 - -3070 -3176 0 - -3070 -3185 0 - -3070 -3206 0 - -3081 -3101 0 - -3081 -3112 0 - -3081 -3120 0 - -3081 -3133 0 - -3081 -3176 0 - -3081 -3185 0 - -3081 -3206 0 - -3101 -3112 0 - -3101 -3120 0 - -3101 -3133 0 - -3101 -3176 0 - -3101 -3185 0 - -3101 -3206 0 - -3112 -3120 0 - -3112 -3133 0 - -3112 -3176 0 - -3112 -3185 0 - -3112 -3206 0 - -3120 -3133 0 - -3120 -3176 0 - -3120 -3185 0 - -3120 -3206 0 - -3133 -3176 0 - -3133 -3185 0 - -3133 -3206 0 - -3176 -3185 0 - -3176 -3206 0 - -3185 -3206 0 - -3055 -3102 0 - -3055 -3121 0 - -3055 -3134 0 - -3055 -3144 0 - -3055 -3159 0 - -3055 -3169 0 - -3055 -3197 0 - -3102 -3121 0 - -3102 -3134 0 - -3102 -3144 0 - -3102 -3159 0 - -3102 -3169 0 - -3102 -3197 0 - -3121 -3134 0 - -3121 -3144 0 - -3121 -3159 0 - -3121 -3169 0 - -3121 -3197 0 - -3134 -3144 0 - -3134 -3159 0 - -3134 -3169 0 - -3134 -3197 0 - -3144 -3159 0 - -3144 -3169 0 - -3144 -3197 0 - -3159 -3169 0 - -3159 -3197 0 - -3169 -3197 0 - -3008 -3018 0 - -3008 -3042 0 - -3008 -3056 0 - -3008 -3071 0 - -3008 -3082 0 - -3008 -3103 0 - -3008 -3160 0 - -3008 -3186 0 - -3018 -3042 0 - -3018 -3056 0 - -3018 -3071 0 - -3018 -3082 0 - -3018 -3103 0 - -3018 -3160 0 - -3018 -3186 0 - -3042 -3056 0 - -3042 -3071 0 - -3042 -3082 0 - -3042 -3103 0 - -3042 -3160 0 - -3042 -3186 0 - -3056 -3071 0 - -3056 -3082 0 - -3056 -3103 0 - -3056 -3160 0 - -3056 -3186 0 - -3071 -3082 0 - -3071 -3103 0 - -3071 -3160 0 - -3071 -3186 0 - -3082 -3103 0 - -3082 -3160 0 - -3082 -3186 0 - -3103 -3160 0 - -3103 -3186 0 - -3160 -3186 0 - -3009 -3019 0 - -3009 -3043 0 - -3009 -3057 0 - -3009 -3064 0 - -3009 -3072 0 - -3009 -3083 0 - -3009 -3187 0 - -3009 -3198 0 - -3019 -3043 0 - -3019 -3057 0 - -3019 -3064 0 - -3019 -3072 0 - -3019 -3083 0 - -3019 -3187 0 - -3019 -3198 0 - -3043 -3057 0 - -3043 -3064 0 - -3043 -3072 0 - -3043 -3083 0 - -3043 -3187 0 - -3043 -3198 0 - -3057 -3064 0 - -3057 -3072 0 - -3057 -3083 0 - -3057 -3187 0 - -3057 -3198 0 - -3064 -3072 0 - -3064 -3083 0 - -3064 -3187 0 - -3064 -3198 0 - -3072 -3083 0 - -3072 -3187 0 - -3072 -3198 0 - -3083 -3187 0 - -3083 -3198 0 - -3187 -3198 0 - -3044 -3058 0 - -3044 -3104 0 - -3044 -3113 0 - -3044 -3122 0 - -3044 -3145 0 - -3044 -3170 0 - -3044 -3177 0 - -3044 -3188 0 - -3058 -3104 0 - -3058 -3113 0 - -3058 -3122 0 - -3058 -3145 0 - -3058 -3170 0 - -3058 -3177 0 - -3058 -3188 0 - -3104 -3113 0 - -3104 -3122 0 - -3104 -3145 0 - -3104 -3170 0 - -3104 -3177 0 - -3104 -3188 0 - -3113 -3122 0 - -3113 -3145 0 - -3113 -3170 0 - -3113 -3177 0 - -3113 -3188 0 - -3122 -3145 0 - -3122 -3170 0 - -3122 -3177 0 - -3122 -3188 0 - -3145 -3170 0 - -3145 -3177 0 - -3145 -3188 0 - -3170 -3177 0 - -3170 -3188 0 - -3177 -3188 0 - -3010 -3020 0 - -3010 -3034 0 - -3010 -3065 0 - -3010 -3093 0 - -3010 -3105 0 - -3010 -3114 0 - -3010 -3123 0 - -3010 -3135 0 - -3010 -3161 0 - -3010 -3171 0 - -3010 -3178 0 - -3010 -3189 0 - -3020 -3034 0 - -3020 -3065 0 - -3020 -3093 0 - -3020 -3105 0 - -3020 -3114 0 - -3020 -3123 0 - -3020 -3135 0 - -3020 -3161 0 - -3020 -3171 0 - -3020 -3178 0 - -3020 -3189 0 - -3034 -3065 0 - -3034 -3093 0 - -3034 -3105 0 - -3034 -3114 0 - -3034 -3123 0 - -3034 -3135 0 - -3034 -3161 0 - -3034 -3171 0 - -3034 -3178 0 - -3034 -3189 0 - -3065 -3093 0 - -3065 -3105 0 - -3065 -3114 0 - -3065 -3123 0 - -3065 -3135 0 - -3065 -3161 0 - -3065 -3171 0 - -3065 -3178 0 - -3065 -3189 0 - -3093 -3105 0 - -3093 -3114 0 - -3093 -3123 0 - -3093 -3135 0 - -3093 -3161 0 - -3093 -3171 0 - -3093 -3178 0 - -3093 -3189 0 - -3105 -3114 0 - -3105 -3123 0 - -3105 -3135 0 - -3105 -3161 0 - -3105 -3171 0 - -3105 -3178 0 - -3105 -3189 0 - -3114 -3123 0 - -3114 -3135 0 - -3114 -3161 0 - -3114 -3171 0 - -3114 -3178 0 - -3114 -3189 0 - -3123 -3135 0 - -3123 -3161 0 - -3123 -3171 0 - -3123 -3178 0 - -3123 -3189 0 - -3135 -3161 0 - -3135 -3171 0 - -3135 -3178 0 - -3135 -3189 0 - -3161 -3171 0 - -3161 -3178 0 - -3161 -3189 0 - -3171 -3178 0 - -3171 -3189 0 - -3178 -3189 0 - -3021 -3035 0 - -3021 -3045 0 - -3021 -3073 0 - -3021 -3084 0 - -3021 -3115 0 - -3021 -3136 0 - -3021 -3162 0 - -3021 -3172 0 - -3021 -3199 0 - -3021 -3207 0 - -3035 -3045 0 - -3035 -3073 0 - -3035 -3084 0 - -3035 -3115 0 - -3035 -3136 0 - -3035 -3162 0 - -3035 -3172 0 - -3035 -3199 0 - -3035 -3207 0 - -3045 -3073 0 - -3045 -3084 0 - -3045 -3115 0 - -3045 -3136 0 - -3045 -3162 0 - -3045 -3172 0 - -3045 -3199 0 - -3045 -3207 0 - -3073 -3084 0 - -3073 -3115 0 - -3073 -3136 0 - -3073 -3162 0 - -3073 -3172 0 - -3073 -3199 0 - -3073 -3207 0 - -3084 -3115 0 - -3084 -3136 0 - -3084 -3162 0 - -3084 -3172 0 - -3084 -3199 0 - -3084 -3207 0 - -3115 -3136 0 - -3115 -3162 0 - -3115 -3172 0 - -3115 -3199 0 - -3115 -3207 0 - -3136 -3162 0 - -3136 -3172 0 - -3136 -3199 0 - -3136 -3207 0 - -3162 -3172 0 - -3162 -3199 0 - -3162 -3207 0 - -3172 -3199 0 - -3172 -3207 0 - -3199 -3207 0 - -3011 -3022 0 - -3011 -3074 0 - -3011 -3085 0 - -3011 -3094 0 - -3011 -3116 0 - -3011 -3124 0 - -3011 -3146 0 - -3011 -3190 0 - -3011 -3200 0 - -3011 -3208 0 - -3022 -3074 0 - -3022 -3085 0 - -3022 -3094 0 - -3022 -3116 0 - -3022 -3124 0 - -3022 -3146 0 - -3022 -3190 0 - -3022 -3200 0 - -3022 -3208 0 - -3074 -3085 0 - -3074 -3094 0 - -3074 -3116 0 - -3074 -3124 0 - -3074 -3146 0 - -3074 -3190 0 - -3074 -3200 0 - -3074 -3208 0 - -3085 -3094 0 - -3085 -3116 0 - -3085 -3124 0 - -3085 -3146 0 - -3085 -3190 0 - -3085 -3200 0 - -3085 -3208 0 - -3094 -3116 0 - -3094 -3124 0 - -3094 -3146 0 - -3094 -3190 0 - -3094 -3200 0 - -3094 -3208 0 - -3116 -3124 0 - -3116 -3146 0 - -3116 -3190 0 - -3116 -3200 0 - -3116 -3208 0 - -3124 -3146 0 - -3124 -3190 0 - -3124 -3200 0 - -3124 -3208 0 - -3146 -3190 0 - -3146 -3200 0 - -3146 -3208 0 - -3190 -3200 0 - -3190 -3208 0 - -3200 -3208 0 - -3012 -3023 0 - -3012 -3046 0 - -3012 -3075 0 - -3012 -3147 0 - -3012 -3179 0 - -3012 -3191 0 - -3012 -3201 0 - -3023 -3046 0 - -3023 -3075 0 - -3023 -3147 0 - -3023 -3179 0 - -3023 -3191 0 - -3023 -3201 0 - -3046 -3075 0 - -3046 -3147 0 - -3046 -3179 0 - -3046 -3191 0 - -3046 -3201 0 - -3075 -3147 0 - -3075 -3179 0 - -3075 -3191 0 - -3075 -3201 0 - -3147 -3179 0 - -3147 -3191 0 - -3147 -3201 0 - -3179 -3191 0 - -3179 -3201 0 - -3191 -3201 0 - -3024 -3036 0 - -3024 -3076 0 - -3024 -3086 0 - -3024 -3095 0 - -3024 -3106 0 - -3024 -3137 0 - -3024 -3152 0 - -3024 -3163 0 - -3024 -3192 0 - -3024 -3209 0 - -3036 -3076 0 - -3036 -3086 0 - -3036 -3095 0 - -3036 -3106 0 - -3036 -3137 0 - -3036 -3152 0 - -3036 -3163 0 - -3036 -3192 0 - -3036 -3209 0 - -3076 -3086 0 - -3076 -3095 0 - -3076 -3106 0 - -3076 -3137 0 - -3076 -3152 0 - -3076 -3163 0 - -3076 -3192 0 - -3076 -3209 0 - -3086 -3095 0 - -3086 -3106 0 - -3086 -3137 0 - -3086 -3152 0 - -3086 -3163 0 - -3086 -3192 0 - -3086 -3209 0 - -3095 -3106 0 - -3095 -3137 0 - -3095 -3152 0 - -3095 -3163 0 - -3095 -3192 0 - -3095 -3209 0 - -3106 -3137 0 - -3106 -3152 0 - -3106 -3163 0 - -3106 -3192 0 - -3106 -3209 0 - -3137 -3152 0 - -3137 -3163 0 - -3137 -3192 0 - -3137 -3209 0 - -3152 -3163 0 - -3152 -3192 0 - -3152 -3209 0 - -3163 -3192 0 - -3163 -3209 0 - -3192 -3209 0 - -3013 -3025 0 - -3013 -3066 0 - -3013 -3077 0 - -3013 -3087 0 - -3013 -3125 0 - -3013 -3138 0 - -3013 -3164 0 - -3013 -3180 0 - -3025 -3066 0 - -3025 -3077 0 - -3025 -3087 0 - -3025 -3125 0 - -3025 -3138 0 - -3025 -3164 0 - -3025 -3180 0 - -3066 -3077 0 - -3066 -3087 0 - -3066 -3125 0 - -3066 -3138 0 - -3066 -3164 0 - -3066 -3180 0 - -3077 -3087 0 - -3077 -3125 0 - -3077 -3138 0 - -3077 -3164 0 - -3077 -3180 0 - -3087 -3125 0 - -3087 -3138 0 - -3087 -3164 0 - -3087 -3180 0 - -3125 -3138 0 - -3125 -3164 0 - -3125 -3180 0 - -3138 -3164 0 - -3138 -3180 0 - -3164 -3180 0 - -3026 -3047 0 - -3026 -3059 0 - -3026 -3067 0 - -3026 -3088 0 - -3026 -3107 0 - -3026 -3117 0 - -3026 -3126 0 - -3026 -3139 0 - -3026 -3153 0 - -3026 -3181 0 - -3026 -3193 0 - -3026 -3210 0 - -3047 -3059 0 - -3047 -3067 0 - -3047 -3088 0 - -3047 -3107 0 - -3047 -3117 0 - -3047 -3126 0 - -3047 -3139 0 - -3047 -3153 0 - -3047 -3181 0 - -3047 -3193 0 - -3047 -3210 0 - -3059 -3067 0 - -3059 -3088 0 - -3059 -3107 0 - -3059 -3117 0 - -3059 -3126 0 - -3059 -3139 0 - -3059 -3153 0 - -3059 -3181 0 - -3059 -3193 0 - -3059 -3210 0 - -3067 -3088 0 - -3067 -3107 0 - -3067 -3117 0 - -3067 -3126 0 - -3067 -3139 0 - -3067 -3153 0 - -3067 -3181 0 - -3067 -3193 0 - -3067 -3210 0 - -3088 -3107 0 - -3088 -3117 0 - -3088 -3126 0 - -3088 -3139 0 - -3088 -3153 0 - -3088 -3181 0 - -3088 -3193 0 - -3088 -3210 0 - -3107 -3117 0 - -3107 -3126 0 - -3107 -3139 0 - -3107 -3153 0 - -3107 -3181 0 - -3107 -3193 0 - -3107 -3210 0 - -3117 -3126 0 - -3117 -3139 0 - -3117 -3153 0 - -3117 -3181 0 - -3117 -3193 0 - -3117 -3210 0 - -3126 -3139 0 - -3126 -3153 0 - -3126 -3181 0 - -3126 -3193 0 - -3126 -3210 0 - -3139 -3153 0 - -3139 -3181 0 - -3139 -3193 0 - -3139 -3210 0 - -3153 -3181 0 - -3153 -3193 0 - -3153 -3210 0 - -3181 -3193 0 - -3181 -3210 0 - -3193 -3210 0 - -3014 -3060 0 - -3014 -3089 0 - -3014 -3127 0 - -3014 -3148 0 - -3014 -3154 0 - -3014 -3165 0 - -3014 -3173 0 - -3014 -3182 0 - -3014 -3202 0 - -3060 -3089 0 - -3060 -3127 0 - -3060 -3148 0 - -3060 -3154 0 - -3060 -3165 0 - -3060 -3173 0 - -3060 -3182 0 - -3060 -3202 0 - -3089 -3127 0 - -3089 -3148 0 - -3089 -3154 0 - -3089 -3165 0 - -3089 -3173 0 - -3089 -3182 0 - -3089 -3202 0 - -3127 -3148 0 - -3127 -3154 0 - -3127 -3165 0 - -3127 -3173 0 - -3127 -3182 0 - -3127 -3202 0 - -3148 -3154 0 - -3148 -3165 0 - -3148 -3173 0 - -3148 -3182 0 - -3148 -3202 0 - -3154 -3165 0 - -3154 -3173 0 - -3154 -3182 0 - -3154 -3202 0 - -3165 -3173 0 - -3165 -3182 0 - -3165 -3202 0 - -3173 -3182 0 - -3173 -3202 0 - -3182 -3202 0 - -3027 -3037 0 - -3027 -3048 0 - -3027 -3078 0 - -3027 -3096 0 - -3027 -3108 0 - -3027 -3118 0 - -3027 -3128 0 - -3027 -3140 0 - -3027 -3155 0 - -3027 -3203 0 - -3027 -3211 0 - -3037 -3048 0 - -3037 -3078 0 - -3037 -3096 0 - -3037 -3108 0 - -3037 -3118 0 - -3037 -3128 0 - -3037 -3140 0 - -3037 -3155 0 - -3037 -3203 0 - -3037 -3211 0 - -3048 -3078 0 - -3048 -3096 0 - -3048 -3108 0 - -3048 -3118 0 - -3048 -3128 0 - -3048 -3140 0 - -3048 -3155 0 - -3048 -3203 0 - -3048 -3211 0 - -3078 -3096 0 - -3078 -3108 0 - -3078 -3118 0 - -3078 -3128 0 - -3078 -3140 0 - -3078 -3155 0 - -3078 -3203 0 - -3078 -3211 0 - -3096 -3108 0 - -3096 -3118 0 - -3096 -3128 0 - -3096 -3140 0 - -3096 -3155 0 - -3096 -3203 0 - -3096 -3211 0 - -3108 -3118 0 - -3108 -3128 0 - -3108 -3140 0 - -3108 -3155 0 - -3108 -3203 0 - -3108 -3211 0 - -3118 -3128 0 - -3118 -3140 0 - -3118 -3155 0 - -3118 -3203 0 - -3118 -3211 0 - -3128 -3140 0 - -3128 -3155 0 - -3128 -3203 0 - -3128 -3211 0 - -3140 -3155 0 - -3140 -3203 0 - -3140 -3211 0 - -3155 -3203 0 - -3155 -3211 0 - -3203 -3211 0 - -3028 -3049 0 - -3028 -3109 0 - -3028 -3129 0 - -3028 -3149 0 - -3028 -3183 0 - -3028 -3204 0 - -3049 -3109 0 - -3049 -3129 0 - -3049 -3149 0 - -3049 -3183 0 - -3049 -3204 0 - -3109 -3129 0 - -3109 -3149 0 - -3109 -3183 0 - -3109 -3204 0 - -3129 -3149 0 - -3129 -3183 0 - -3129 -3204 0 - -3149 -3183 0 - -3149 -3204 0 - -3183 -3204 0 - -3061 -3090 0 - -3061 -3130 0 - -3061 -3156 0 - -3061 -3166 0 - -3090 -3130 0 - -3090 -3156 0 - -3090 -3166 0 - -3130 -3156 0 - -3130 -3166 0 - -3156 -3166 0 - -3029 -3050 0 - -3029 -3062 0 - -3029 -3068 0 - -3029 -3091 0 - -3029 -3097 0 - -3029 -3119 0 - -3029 -3141 0 - -3029 -3157 0 - -3029 -3174 0 - -3050 -3062 0 - -3050 -3068 0 - -3050 -3091 0 - -3050 -3097 0 - -3050 -3119 0 - -3050 -3141 0 - -3050 -3157 0 - -3050 -3174 0 - -3062 -3068 0 - -3062 -3091 0 - -3062 -3097 0 - -3062 -3119 0 - -3062 -3141 0 - -3062 -3157 0 - -3062 -3174 0 - -3068 -3091 0 - -3068 -3097 0 - -3068 -3119 0 - -3068 -3141 0 - -3068 -3157 0 - -3068 -3174 0 - -3091 -3097 0 - -3091 -3119 0 - -3091 -3141 0 - -3091 -3157 0 - -3091 -3174 0 - -3097 -3119 0 - -3097 -3141 0 - -3097 -3157 0 - -3097 -3174 0 - -3119 -3141 0 - -3119 -3157 0 - -3119 -3174 0 - -3141 -3157 0 - -3141 -3174 0 - -3157 -3174 0 - -3015 -3038 0 - -3015 -3051 0 - -3015 -3063 0 - -3015 -3150 0 - -3015 -3158 0 - -3015 -3194 0 - -3038 -3051 0 - -3038 -3063 0 - -3038 -3150 0 - -3038 -3158 0 - -3038 -3194 0 - -3051 -3063 0 - -3051 -3150 0 - -3051 -3158 0 - -3051 -3194 0 - -3063 -3150 0 - -3063 -3158 0 - -3063 -3194 0 - -3150 -3158 0 - -3150 -3194 0 - -3158 -3194 0 - -3016 -3030 0 - -3016 -3052 0 - -3016 -3079 0 - -3016 -3098 0 - -3016 -3110 0 - -3016 -3131 0 - -3016 -3167 0 - -3016 -3195 0 - -3030 -3052 0 - -3030 -3079 0 - -3030 -3098 0 - -3030 -3110 0 - -3030 -3131 0 - -3030 -3167 0 - -3030 -3195 0 - -3052 -3079 0 - -3052 -3098 0 - -3052 -3110 0 - -3052 -3131 0 - -3052 -3167 0 - -3052 -3195 0 - -3079 -3098 0 - -3079 -3110 0 - -3079 -3131 0 - -3079 -3167 0 - -3079 -3195 0 - -3098 -3110 0 - -3098 -3131 0 - -3098 -3167 0 - -3098 -3195 0 - -3110 -3131 0 - -3110 -3167 0 - -3110 -3195 0 - -3131 -3167 0 - -3131 -3195 0 - -3167 -3195 0 - -3031 -3039 0 - -3031 -3092 0 - -3031 -3099 0 - -3031 -3142 0 - -3031 -3151 0 - -3031 -3168 0 - -3031 -3175 0 - -3031 -3184 0 - -3031 -3196 0 - -3031 -3205 0 - -3039 -3092 0 - -3039 -3099 0 - -3039 -3142 0 - -3039 -3151 0 - -3039 -3168 0 - -3039 -3175 0 - -3039 -3184 0 - -3039 -3196 0 - -3039 -3205 0 - -3092 -3099 0 - -3092 -3142 0 - -3092 -3151 0 - -3092 -3168 0 - -3092 -3175 0 - -3092 -3184 0 - -3092 -3196 0 - -3092 -3205 0 - -3099 -3142 0 - -3099 -3151 0 - -3099 -3168 0 - -3099 -3175 0 - -3099 -3184 0 - -3099 -3196 0 - -3099 -3205 0 - -3142 -3151 0 - -3142 -3168 0 - -3142 -3175 0 - -3142 -3184 0 - -3142 -3196 0 - -3142 -3205 0 - -3151 -3168 0 - -3151 -3175 0 - -3151 -3184 0 - -3151 -3196 0 - -3151 -3205 0 - -3168 -3175 0 - -3168 -3184 0 - -3168 -3196 0 - -3168 -3205 0 - -3175 -3184 0 - -3175 -3196 0 - -3175 -3205 0 - -3184 -3196 0 - -3184 -3205 0 - -3196 -3205 0 - -3032 -3040 0 - -3032 -3053 0 - -3032 -3069 0 - -3032 -3080 0 - -3032 -3100 0 - -3032 -3111 0 - -3032 -3132 0 - -3032 -3143 0 - -3040 -3053 0 - -3040 -3069 0 - -3040 -3080 0 - -3040 -3100 0 - -3040 -3111 0 - -3040 -3132 0 - -3040 -3143 0 - -3053 -3069 0 - -3053 -3080 0 - -3053 -3100 0 - -3053 -3111 0 - -3053 -3132 0 - -3053 -3143 0 - -3069 -3080 0 - -3069 -3100 0 - -3069 -3111 0 - -3069 -3132 0 - -3069 -3143 0 - -3080 -3100 0 - -3080 -3111 0 - -3080 -3132 0 - -3080 -3143 0 - -3100 -3111 0 - -3100 -3132 0 - -3100 -3143 0 - -3111 -3132 0 - -3111 -3143 0 - -3132 -3143 0 - -3212 -3221 0 - -3212 -3228 0 - -3212 -3232 0 - -3212 -3237 0 - -3212 -3243 0 - -3212 -3261 0 - -3212 -3274 0 - -3221 -3228 0 - -3221 -3232 0 - -3221 -3237 0 - -3221 -3243 0 - -3221 -3261 0 - -3221 -3274 0 - -3228 -3232 0 - -3228 -3237 0 - -3228 -3243 0 - -3228 -3261 0 - -3228 -3274 0 - -3232 -3237 0 - -3232 -3243 0 - -3232 -3261 0 - -3232 -3274 0 - -3237 -3243 0 - -3237 -3261 0 - -3237 -3274 0 - -3243 -3261 0 - -3243 -3274 0 - -3261 -3274 0 - -3222 -3233 0 - -3222 -3256 0 - -3222 -3269 0 - -3222 -3275 0 - -3222 -3280 0 - -3233 -3256 0 - -3233 -3269 0 - -3233 -3275 0 - -3233 -3280 0 - -3256 -3269 0 - -3256 -3275 0 - -3256 -3280 0 - -3269 -3275 0 - -3269 -3280 0 - -3275 -3280 0 - -3213 -3238 0 - -3213 -3250 0 - -3213 -3262 0 - -3213 -3281 0 - -3213 -3288 0 - -3238 -3250 0 - -3238 -3262 0 - -3238 -3281 0 - -3238 -3288 0 - -3250 -3262 0 - -3250 -3281 0 - -3250 -3288 0 - -3262 -3281 0 - -3262 -3288 0 - -3281 -3288 0 - -3223 -3244 0 - -3223 -3251 0 - -3223 -3263 0 - -3223 -3270 0 - -3223 -3282 0 - -3244 -3251 0 - -3244 -3263 0 - -3244 -3270 0 - -3244 -3282 0 - -3251 -3263 0 - -3251 -3270 0 - -3251 -3282 0 - -3263 -3270 0 - -3263 -3282 0 - -3270 -3282 0 - -3214 -3224 0 - -3214 -3229 0 - -3214 -3245 0 - -3214 -3264 0 - -3214 -3297 0 - -3224 -3229 0 - -3224 -3245 0 - -3224 -3264 0 - -3224 -3297 0 - -3229 -3245 0 - -3229 -3264 0 - -3229 -3297 0 - -3245 -3264 0 - -3245 -3297 0 - -3264 -3297 0 - -3225 -3239 0 - -3225 -3246 0 - -3225 -3252 0 - -3225 -3257 0 - -3225 -3265 0 - -3225 -3276 0 - -3225 -3289 0 - -3239 -3246 0 - -3239 -3252 0 - -3239 -3257 0 - -3239 -3265 0 - -3239 -3276 0 - -3239 -3289 0 - -3246 -3252 0 - -3246 -3257 0 - -3246 -3265 0 - -3246 -3276 0 - -3246 -3289 0 - -3252 -3257 0 - -3252 -3265 0 - -3252 -3276 0 - -3252 -3289 0 - -3257 -3265 0 - -3257 -3276 0 - -3257 -3289 0 - -3265 -3276 0 - -3265 -3289 0 - -3276 -3289 0 - -3215 -3226 0 - -3215 -3247 0 - -3215 -3266 0 - -3215 -3271 0 - -3215 -3283 0 - -3215 -3290 0 - -3226 -3247 0 - -3226 -3266 0 - -3226 -3271 0 - -3226 -3283 0 - -3226 -3290 0 - -3247 -3266 0 - -3247 -3271 0 - -3247 -3283 0 - -3247 -3290 0 - -3266 -3271 0 - -3266 -3283 0 - -3266 -3290 0 - -3271 -3283 0 - -3271 -3290 0 - -3283 -3290 0 - -3216 -3248 0 - -3216 -3253 0 - -3216 -3258 0 - -3216 -3277 0 - -3216 -3291 0 - -3216 -3298 0 - -3248 -3253 0 - -3248 -3258 0 - -3248 -3277 0 - -3248 -3291 0 - -3248 -3298 0 - -3253 -3258 0 - -3253 -3277 0 - -3253 -3291 0 - -3253 -3298 0 - -3258 -3277 0 - -3258 -3291 0 - -3258 -3298 0 - -3277 -3291 0 - -3277 -3298 0 - -3291 -3298 0 - -3217 -3240 0 - -3217 -3249 0 - -3217 -3259 0 - -3217 -3278 0 - -3217 -3284 0 - -3217 -3292 0 - -3240 -3249 0 - -3240 -3259 0 - -3240 -3278 0 - -3240 -3284 0 - -3240 -3292 0 - -3249 -3259 0 - -3249 -3278 0 - -3249 -3284 0 - -3249 -3292 0 - -3259 -3278 0 - -3259 -3284 0 - -3259 -3292 0 - -3278 -3284 0 - -3278 -3292 0 - -3284 -3292 0 - -3218 -3227 0 - -3218 -3234 0 - -3218 -3241 0 - -3218 -3267 0 - -3218 -3285 0 - -3218 -3293 0 - -3218 -3299 0 - -3227 -3234 0 - -3227 -3241 0 - -3227 -3267 0 - -3227 -3285 0 - -3227 -3293 0 - -3227 -3299 0 - -3234 -3241 0 - -3234 -3267 0 - -3234 -3285 0 - -3234 -3293 0 - -3234 -3299 0 - -3241 -3267 0 - -3241 -3285 0 - -3241 -3293 0 - -3241 -3299 0 - -3267 -3285 0 - -3267 -3293 0 - -3267 -3299 0 - -3285 -3293 0 - -3285 -3299 0 - -3293 -3299 0 - -3219 -3235 0 - -3219 -3260 0 - -3219 -3272 0 - -3235 -3260 0 - -3235 -3272 0 - -3260 -3272 0 - -3230 -3236 0 - -3230 -3268 0 - -3230 -3294 0 - -3236 -3268 0 - -3236 -3294 0 - -3268 -3294 0 - -3231 -3242 0 - -3231 -3254 0 - -3231 -3286 0 - -3231 -3295 0 - -3242 -3254 0 - -3242 -3286 0 - -3242 -3295 0 - -3254 -3286 0 - -3254 -3295 0 - -3286 -3295 0 - -3220 -3255 0 - -3220 -3273 0 - -3220 -3279 0 - -3220 -3287 0 - -3220 -3296 0 - -3255 -3273 0 - -3255 -3279 0 - -3255 -3287 0 - -3255 -3296 0 - -3273 -3279 0 - -3273 -3287 0 - -3273 -3296 0 - -3279 -3287 0 - -3279 -3296 0 - -3287 -3296 0 - -3327 -3342 0 - -3327 -3381 0 - -3327 -3400 0 - -3327 -3404 0 - -3327 -3412 0 - -3327 -3447 0 - -3342 -3381 0 - -3342 -3400 0 - -3342 -3404 0 - -3342 -3412 0 - -3342 -3447 0 - -3381 -3400 0 - -3381 -3404 0 - -3381 -3412 0 - -3381 -3447 0 - -3400 -3404 0 - -3400 -3412 0 - -3400 -3447 0 - -3404 -3412 0 - -3404 -3447 0 - -3412 -3447 0 - -3307 -3314 0 - -3307 -3328 0 - -3307 -3345 0 - -3307 -3365 0 - -3307 -3373 0 - -3307 -3382 0 - -3307 -3431 0 - -3307 -3448 0 - -3314 -3328 0 - -3314 -3345 0 - -3314 -3365 0 - -3314 -3373 0 - -3314 -3382 0 - -3314 -3431 0 - -3314 -3448 0 - -3328 -3345 0 - -3328 -3365 0 - -3328 -3373 0 - -3328 -3382 0 - -3328 -3431 0 - -3328 -3448 0 - -3345 -3365 0 - -3345 -3373 0 - -3345 -3382 0 - -3345 -3431 0 - -3345 -3448 0 - -3365 -3373 0 - -3365 -3382 0 - -3365 -3431 0 - -3365 -3448 0 - -3373 -3382 0 - -3373 -3431 0 - -3373 -3448 0 - -3382 -3431 0 - -3382 -3448 0 - -3431 -3448 0 - -3300 -3315 0 - -3300 -3343 0 - -3300 -3366 0 - -3300 -3374 0 - -3300 -3392 0 - -3300 -3405 0 - -3300 -3413 0 - -3300 -3422 0 - -3315 -3343 0 - -3315 -3366 0 - -3315 -3374 0 - -3315 -3392 0 - -3315 -3405 0 - -3315 -3413 0 - -3315 -3422 0 - -3343 -3366 0 - -3343 -3374 0 - -3343 -3392 0 - -3343 -3405 0 - -3343 -3413 0 - -3343 -3422 0 - -3366 -3374 0 - -3366 -3392 0 - -3366 -3405 0 - -3366 -3413 0 - -3366 -3422 0 - -3374 -3392 0 - -3374 -3405 0 - -3374 -3413 0 - -3374 -3422 0 - -3392 -3405 0 - -3392 -3413 0 - -3392 -3422 0 - -3405 -3413 0 - -3405 -3422 0 - -3413 -3422 0 - -3301 -3329 0 - -3301 -3336 0 - -3301 -3354 0 - -3301 -3367 0 - -3301 -3375 0 - -3301 -3383 0 - -3301 -3401 0 - -3301 -3423 0 - -3301 -3432 0 - -3301 -3440 0 - -3329 -3336 0 - -3329 -3354 0 - -3329 -3367 0 - -3329 -3375 0 - -3329 -3383 0 - -3329 -3401 0 - -3329 -3423 0 - -3329 -3432 0 - -3329 -3440 0 - -3336 -3354 0 - -3336 -3367 0 - -3336 -3375 0 - -3336 -3383 0 - -3336 -3401 0 - -3336 -3423 0 - -3336 -3432 0 - -3336 -3440 0 - -3354 -3367 0 - -3354 -3375 0 - -3354 -3383 0 - -3354 -3401 0 - -3354 -3423 0 - -3354 -3432 0 - -3354 -3440 0 - -3367 -3375 0 - -3367 -3383 0 - -3367 -3401 0 - -3367 -3423 0 - -3367 -3432 0 - -3367 -3440 0 - -3375 -3383 0 - -3375 -3401 0 - -3375 -3423 0 - -3375 -3432 0 - -3375 -3440 0 - -3383 -3401 0 - -3383 -3423 0 - -3383 -3432 0 - -3383 -3440 0 - -3401 -3423 0 - -3401 -3432 0 - -3401 -3440 0 - -3423 -3432 0 - -3423 -3440 0 - -3432 -3440 0 - -3308 -3316 0 - -3308 -3346 0 - -3308 -3355 0 - -3308 -3376 0 - -3308 -3424 0 - -3308 -3433 0 - -3308 -3441 0 - -3316 -3346 0 - -3316 -3355 0 - -3316 -3376 0 - -3316 -3424 0 - -3316 -3433 0 - -3316 -3441 0 - -3346 -3355 0 - -3346 -3376 0 - -3346 -3424 0 - -3346 -3433 0 - -3346 -3441 0 - -3355 -3376 0 - -3355 -3424 0 - -3355 -3433 0 - -3355 -3441 0 - -3376 -3424 0 - -3376 -3433 0 - -3376 -3441 0 - -3424 -3433 0 - -3424 -3441 0 - -3433 -3441 0 - -3309 -3317 0 - -3309 -3347 0 - -3309 -3368 0 - -3309 -3377 0 - -3309 -3384 0 - -3309 -3393 0 - -3309 -3414 0 - -3309 -3425 0 - -3309 -3434 0 - -3309 -3442 0 - -3317 -3347 0 - -3317 -3368 0 - -3317 -3377 0 - -3317 -3384 0 - -3317 -3393 0 - -3317 -3414 0 - -3317 -3425 0 - -3317 -3434 0 - -3317 -3442 0 - -3347 -3368 0 - -3347 -3377 0 - -3347 -3384 0 - -3347 -3393 0 - -3347 -3414 0 - -3347 -3425 0 - -3347 -3434 0 - -3347 -3442 0 - -3368 -3377 0 - -3368 -3384 0 - -3368 -3393 0 - -3368 -3414 0 - -3368 -3425 0 - -3368 -3434 0 - -3368 -3442 0 - -3377 -3384 0 - -3377 -3393 0 - -3377 -3414 0 - -3377 -3425 0 - -3377 -3434 0 - -3377 -3442 0 - -3384 -3393 0 - -3384 -3414 0 - -3384 -3425 0 - -3384 -3434 0 - -3384 -3442 0 - -3393 -3414 0 - -3393 -3425 0 - -3393 -3434 0 - -3393 -3442 0 - -3414 -3425 0 - -3414 -3434 0 - -3414 -3442 0 - -3425 -3434 0 - -3425 -3442 0 - -3434 -3442 0 - -3330 -3356 0 - -3330 -3406 0 - -3330 -3415 0 - -3330 -3443 0 - -3330 -3449 0 - -3356 -3406 0 - -3356 -3415 0 - -3356 -3443 0 - -3356 -3449 0 - -3406 -3415 0 - -3406 -3443 0 - -3406 -3449 0 - -3415 -3443 0 - -3415 -3449 0 - -3443 -3449 0 - -3302 -3318 0 - -3302 -3331 0 - -3302 -3357 0 - -3302 -3385 0 - -3302 -3402 0 - -3302 -3416 0 - -3302 -3426 0 - -3318 -3331 0 - -3318 -3357 0 - -3318 -3385 0 - -3318 -3402 0 - -3318 -3416 0 - -3318 -3426 0 - -3331 -3357 0 - -3331 -3385 0 - -3331 -3402 0 - -3331 -3416 0 - -3331 -3426 0 - -3357 -3385 0 - -3357 -3402 0 - -3357 -3416 0 - -3357 -3426 0 - -3385 -3402 0 - -3385 -3416 0 - -3385 -3426 0 - -3402 -3416 0 - -3402 -3426 0 - -3416 -3426 0 - -3310 -3319 0 - -3310 -3348 0 - -3310 -3386 0 - -3310 -3394 0 - -3310 -3417 0 - -3310 -3444 0 - -3310 -3450 0 - -3319 -3348 0 - -3319 -3386 0 - -3319 -3394 0 - -3319 -3417 0 - -3319 -3444 0 - -3319 -3450 0 - -3348 -3386 0 - -3348 -3394 0 - -3348 -3417 0 - -3348 -3444 0 - -3348 -3450 0 - -3386 -3394 0 - -3386 -3417 0 - -3386 -3444 0 - -3386 -3450 0 - -3394 -3417 0 - -3394 -3444 0 - -3394 -3450 0 - -3417 -3444 0 - -3417 -3450 0 - -3444 -3450 0 - -3349 -3358 0 - -3349 -3407 0 - -3349 -3427 0 - -3349 -3435 0 - -3349 -3445 0 - -3349 -3451 0 - -3358 -3407 0 - -3358 -3427 0 - -3358 -3435 0 - -3358 -3445 0 - -3358 -3451 0 - -3407 -3427 0 - -3407 -3435 0 - -3407 -3445 0 - -3407 -3451 0 - -3427 -3435 0 - -3427 -3445 0 - -3427 -3451 0 - -3435 -3445 0 - -3435 -3451 0 - -3445 -3451 0 - -3303 -3311 0 - -3303 -3320 0 - -3303 -3337 0 - -3303 -3359 0 - -3303 -3395 0 - -3303 -3408 0 - -3303 -3418 0 - -3311 -3320 0 - -3311 -3337 0 - -3311 -3359 0 - -3311 -3395 0 - -3311 -3408 0 - -3311 -3418 0 - -3320 -3337 0 - -3320 -3359 0 - -3320 -3395 0 - -3320 -3408 0 - -3320 -3418 0 - -3337 -3359 0 - -3337 -3395 0 - -3337 -3408 0 - -3337 -3418 0 - -3359 -3395 0 - -3359 -3408 0 - -3359 -3418 0 - -3395 -3408 0 - -3395 -3418 0 - -3408 -3418 0 - -3304 -3321 0 - -3304 -3344 0 - -3304 -3446 0 - -3321 -3344 0 - -3321 -3446 0 - -3344 -3446 0 - -3332 -3350 0 - -3332 -3378 0 - -3332 -3387 0 - -3332 -3396 0 - -3332 -3419 0 - -3332 -3428 0 - -3332 -3436 0 - -3350 -3378 0 - -3350 -3387 0 - -3350 -3396 0 - -3350 -3419 0 - -3350 -3428 0 - -3350 -3436 0 - -3378 -3387 0 - -3378 -3396 0 - -3378 -3419 0 - -3378 -3428 0 - -3378 -3436 0 - -3387 -3396 0 - -3387 -3419 0 - -3387 -3428 0 - -3387 -3436 0 - -3396 -3419 0 - -3396 -3428 0 - -3396 -3436 0 - -3419 -3428 0 - -3419 -3436 0 - -3428 -3436 0 - -3338 -3351 0 - -3338 -3360 0 - -3338 -3369 0 - -3338 -3388 0 - -3338 -3397 0 - -3338 -3429 0 - -3351 -3360 0 - -3351 -3369 0 - -3351 -3388 0 - -3351 -3397 0 - -3351 -3429 0 - -3360 -3369 0 - -3360 -3388 0 - -3360 -3397 0 - -3360 -3429 0 - -3369 -3388 0 - -3369 -3397 0 - -3369 -3429 0 - -3388 -3397 0 - -3388 -3429 0 - -3397 -3429 0 - -3322 -3333 0 - -3322 -3339 0 - -3322 -3352 0 - -3322 -3361 0 - -3322 -3379 0 - -3322 -3389 0 - -3322 -3409 0 - -3322 -3437 0 - -3333 -3339 0 - -3333 -3352 0 - -3333 -3361 0 - -3333 -3379 0 - -3333 -3389 0 - -3333 -3409 0 - -3333 -3437 0 - -3339 -3352 0 - -3339 -3361 0 - -3339 -3379 0 - -3339 -3389 0 - -3339 -3409 0 - -3339 -3437 0 - -3352 -3361 0 - -3352 -3379 0 - -3352 -3389 0 - -3352 -3409 0 - -3352 -3437 0 - -3361 -3379 0 - -3361 -3389 0 - -3361 -3409 0 - -3361 -3437 0 - -3379 -3389 0 - -3379 -3409 0 - -3379 -3437 0 - -3389 -3409 0 - -3389 -3437 0 - -3409 -3437 0 - -3305 -3323 0 - -3305 -3334 0 - -3305 -3362 0 - -3305 -3398 0 - -3305 -3410 0 - -3305 -3452 0 - -3323 -3334 0 - -3323 -3362 0 - -3323 -3398 0 - -3323 -3410 0 - -3323 -3452 0 - -3334 -3362 0 - -3334 -3398 0 - -3334 -3410 0 - -3334 -3452 0 - -3362 -3398 0 - -3362 -3410 0 - -3362 -3452 0 - -3398 -3410 0 - -3398 -3452 0 - -3410 -3452 0 - -3324 -3363 0 - -3324 -3370 0 - -3324 -3390 0 - -3324 -3403 0 - -3324 -3420 0 - -3324 -3430 0 - -3324 -3438 0 - -3363 -3370 0 - -3363 -3390 0 - -3363 -3403 0 - -3363 -3420 0 - -3363 -3430 0 - -3363 -3438 0 - -3370 -3390 0 - -3370 -3403 0 - -3370 -3420 0 - -3370 -3430 0 - -3370 -3438 0 - -3390 -3403 0 - -3390 -3420 0 - -3390 -3430 0 - -3390 -3438 0 - -3403 -3420 0 - -3403 -3430 0 - -3403 -3438 0 - -3420 -3430 0 - -3420 -3438 0 - -3430 -3438 0 - -3312 -3325 0 - -3312 -3335 0 - -3312 -3340 0 - -3312 -3353 0 - -3312 -3371 0 - -3312 -3391 0 - -3312 -3399 0 - -3312 -3411 0 - -3312 -3421 0 - -3312 -3453 0 - -3325 -3335 0 - -3325 -3340 0 - -3325 -3353 0 - -3325 -3371 0 - -3325 -3391 0 - -3325 -3399 0 - -3325 -3411 0 - -3325 -3421 0 - -3325 -3453 0 - -3335 -3340 0 - -3335 -3353 0 - -3335 -3371 0 - -3335 -3391 0 - -3335 -3399 0 - -3335 -3411 0 - -3335 -3421 0 - -3335 -3453 0 - -3340 -3353 0 - -3340 -3371 0 - -3340 -3391 0 - -3340 -3399 0 - -3340 -3411 0 - -3340 -3421 0 - -3340 -3453 0 - -3353 -3371 0 - -3353 -3391 0 - -3353 -3399 0 - -3353 -3411 0 - -3353 -3421 0 - -3353 -3453 0 - -3371 -3391 0 - -3371 -3399 0 - -3371 -3411 0 - -3371 -3421 0 - -3371 -3453 0 - -3391 -3399 0 - -3391 -3411 0 - -3391 -3421 0 - -3391 -3453 0 - -3399 -3411 0 - -3399 -3421 0 - -3399 -3453 0 - -3411 -3421 0 - -3411 -3453 0 - -3421 -3453 0 - -3306 -3313 0 - -3306 -3326 0 - -3306 -3341 0 - -3306 -3364 0 - -3306 -3372 0 - -3306 -3380 0 - -3306 -3439 0 - -3313 -3326 0 - -3313 -3341 0 - -3313 -3364 0 - -3313 -3372 0 - -3313 -3380 0 - -3313 -3439 0 - -3326 -3341 0 - -3326 -3364 0 - -3326 -3372 0 - -3326 -3380 0 - -3326 -3439 0 - -3341 -3364 0 - -3341 -3372 0 - -3341 -3380 0 - -3341 -3439 0 - -3364 -3372 0 - -3364 -3380 0 - -3364 -3439 0 - -3372 -3380 0 - -3372 -3439 0 - -3380 -3439 0 - -3454 -3464 0 - -3454 -3469 0 - -3454 -3478 0 - -3454 -3489 0 - -3454 -3506 0 - -3454 -3517 0 - -3454 -3534 0 - -3454 -3541 0 - -3454 -3583 0 - -3454 -3603 0 - -3464 -3469 0 - -3464 -3478 0 - -3464 -3489 0 - -3464 -3506 0 - -3464 -3517 0 - -3464 -3534 0 - -3464 -3541 0 - -3464 -3583 0 - -3464 -3603 0 - -3469 -3478 0 - -3469 -3489 0 - -3469 -3506 0 - -3469 -3517 0 - -3469 -3534 0 - -3469 -3541 0 - -3469 -3583 0 - -3469 -3603 0 - -3478 -3489 0 - -3478 -3506 0 - -3478 -3517 0 - -3478 -3534 0 - -3478 -3541 0 - -3478 -3583 0 - -3478 -3603 0 - -3489 -3506 0 - -3489 -3517 0 - -3489 -3534 0 - -3489 -3541 0 - -3489 -3583 0 - -3489 -3603 0 - -3506 -3517 0 - -3506 -3534 0 - -3506 -3541 0 - -3506 -3583 0 - -3506 -3603 0 - -3517 -3534 0 - -3517 -3541 0 - -3517 -3583 0 - -3517 -3603 0 - -3534 -3541 0 - -3534 -3583 0 - -3534 -3603 0 - -3541 -3583 0 - -3541 -3603 0 - -3583 -3603 0 - -3479 -3500 0 - -3479 -3507 0 - -3479 -3518 0 - -3479 -3528 0 - -3479 -3535 0 - -3479 -3547 0 - -3500 -3507 0 - -3500 -3518 0 - -3500 -3528 0 - -3500 -3535 0 - -3500 -3547 0 - -3507 -3518 0 - -3507 -3528 0 - -3507 -3535 0 - -3507 -3547 0 - -3518 -3528 0 - -3518 -3535 0 - -3518 -3547 0 - -3528 -3535 0 - -3528 -3547 0 - -3535 -3547 0 - -3455 -3470 0 - -3455 -3501 0 - -3455 -3508 0 - -3455 -3519 0 - -3455 -3529 0 - -3455 -3536 0 - -3455 -3556 0 - -3455 -3566 0 - -3455 -3575 0 - -3455 -3584 0 - -3455 -3593 0 - -3470 -3501 0 - -3470 -3508 0 - -3470 -3519 0 - -3470 -3529 0 - -3470 -3536 0 - -3470 -3556 0 - -3470 -3566 0 - -3470 -3575 0 - -3470 -3584 0 - -3470 -3593 0 - -3501 -3508 0 - -3501 -3519 0 - -3501 -3529 0 - -3501 -3536 0 - -3501 -3556 0 - -3501 -3566 0 - -3501 -3575 0 - -3501 -3584 0 - -3501 -3593 0 - -3508 -3519 0 - -3508 -3529 0 - -3508 -3536 0 - -3508 -3556 0 - -3508 -3566 0 - -3508 -3575 0 - -3508 -3584 0 - -3508 -3593 0 - -3519 -3529 0 - -3519 -3536 0 - -3519 -3556 0 - -3519 -3566 0 - -3519 -3575 0 - -3519 -3584 0 - -3519 -3593 0 - -3529 -3536 0 - -3529 -3556 0 - -3529 -3566 0 - -3529 -3575 0 - -3529 -3584 0 - -3529 -3593 0 - -3536 -3556 0 - -3536 -3566 0 - -3536 -3575 0 - -3536 -3584 0 - -3536 -3593 0 - -3556 -3566 0 - -3556 -3575 0 - -3556 -3584 0 - -3556 -3593 0 - -3566 -3575 0 - -3566 -3584 0 - -3566 -3593 0 - -3575 -3584 0 - -3575 -3593 0 - -3584 -3593 0 - -3480 -3509 0 - -3480 -3520 0 - -3480 -3542 0 - -3480 -3548 0 - -3480 -3561 0 - -3480 -3576 0 - -3480 -3604 0 - -3509 -3520 0 - -3509 -3542 0 - -3509 -3548 0 - -3509 -3561 0 - -3509 -3576 0 - -3509 -3604 0 - -3520 -3542 0 - -3520 -3548 0 - -3520 -3561 0 - -3520 -3576 0 - -3520 -3604 0 - -3542 -3548 0 - -3542 -3561 0 - -3542 -3576 0 - -3542 -3604 0 - -3548 -3561 0 - -3548 -3576 0 - -3548 -3604 0 - -3561 -3576 0 - -3561 -3604 0 - -3576 -3604 0 - -3456 -3471 0 - -3456 -3481 0 - -3456 -3490 0 - -3456 -3510 0 - -3456 -3521 0 - -3456 -3530 0 - -3456 -3562 0 - -3456 -3567 0 - -3456 -3577 0 - -3456 -3605 0 - -3471 -3481 0 - -3471 -3490 0 - -3471 -3510 0 - -3471 -3521 0 - -3471 -3530 0 - -3471 -3562 0 - -3471 -3567 0 - -3471 -3577 0 - -3471 -3605 0 - -3481 -3490 0 - -3481 -3510 0 - -3481 -3521 0 - -3481 -3530 0 - -3481 -3562 0 - -3481 -3567 0 - -3481 -3577 0 - -3481 -3605 0 - -3490 -3510 0 - -3490 -3521 0 - -3490 -3530 0 - -3490 -3562 0 - -3490 -3567 0 - -3490 -3577 0 - -3490 -3605 0 - -3510 -3521 0 - -3510 -3530 0 - -3510 -3562 0 - -3510 -3567 0 - -3510 -3577 0 - -3510 -3605 0 - -3521 -3530 0 - -3521 -3562 0 - -3521 -3567 0 - -3521 -3577 0 - -3521 -3605 0 - -3530 -3562 0 - -3530 -3567 0 - -3530 -3577 0 - -3530 -3605 0 - -3562 -3567 0 - -3562 -3577 0 - -3562 -3605 0 - -3567 -3577 0 - -3567 -3605 0 - -3577 -3605 0 - -3457 -3472 0 - -3457 -3482 0 - -3457 -3491 0 - -3457 -3531 0 - -3457 -3543 0 - -3457 -3568 0 - -3457 -3606 0 - -3472 -3482 0 - -3472 -3491 0 - -3472 -3531 0 - -3472 -3543 0 - -3472 -3568 0 - -3472 -3606 0 - -3482 -3491 0 - -3482 -3531 0 - -3482 -3543 0 - -3482 -3568 0 - -3482 -3606 0 - -3491 -3531 0 - -3491 -3543 0 - -3491 -3568 0 - -3491 -3606 0 - -3531 -3543 0 - -3531 -3568 0 - -3531 -3606 0 - -3543 -3568 0 - -3543 -3606 0 - -3568 -3606 0 - -3473 -3483 0 - -3473 -3511 0 - -3473 -3549 0 - -3473 -3569 0 - -3473 -3578 0 - -3473 -3585 0 - -3473 -3607 0 - -3483 -3511 0 - -3483 -3549 0 - -3483 -3569 0 - -3483 -3578 0 - -3483 -3585 0 - -3483 -3607 0 - -3511 -3549 0 - -3511 -3569 0 - -3511 -3578 0 - -3511 -3585 0 - -3511 -3607 0 - -3549 -3569 0 - -3549 -3578 0 - -3549 -3585 0 - -3549 -3607 0 - -3569 -3578 0 - -3569 -3585 0 - -3569 -3607 0 - -3578 -3585 0 - -3578 -3607 0 - -3585 -3607 0 - -3458 -3465 0 - -3458 -3512 0 - -3458 -3532 0 - -3458 -3537 0 - -3458 -3586 0 - -3458 -3594 0 - -3458 -3608 0 - -3465 -3512 0 - -3465 -3532 0 - -3465 -3537 0 - -3465 -3586 0 - -3465 -3594 0 - -3465 -3608 0 - -3512 -3532 0 - -3512 -3537 0 - -3512 -3586 0 - -3512 -3594 0 - -3512 -3608 0 - -3532 -3537 0 - -3532 -3586 0 - -3532 -3594 0 - -3532 -3608 0 - -3537 -3586 0 - -3537 -3594 0 - -3537 -3608 0 - -3586 -3594 0 - -3586 -3608 0 - -3594 -3608 0 - -3466 -3474 0 - -3466 -3492 0 - -3466 -3502 0 - -3466 -3513 0 - -3466 -3522 0 - -3466 -3544 0 - -3466 -3579 0 - -3466 -3609 0 - -3474 -3492 0 - -3474 -3502 0 - -3474 -3513 0 - -3474 -3522 0 - -3474 -3544 0 - -3474 -3579 0 - -3474 -3609 0 - -3492 -3502 0 - -3492 -3513 0 - -3492 -3522 0 - -3492 -3544 0 - -3492 -3579 0 - -3492 -3609 0 - -3502 -3513 0 - -3502 -3522 0 - -3502 -3544 0 - -3502 -3579 0 - -3502 -3609 0 - -3513 -3522 0 - -3513 -3544 0 - -3513 -3579 0 - -3513 -3609 0 - -3522 -3544 0 - -3522 -3579 0 - -3522 -3609 0 - -3544 -3579 0 - -3544 -3609 0 - -3579 -3609 0 - -3459 -3484 0 - -3459 -3538 0 - -3459 -3550 0 - -3459 -3570 0 - -3459 -3595 0 - -3459 -3610 0 - -3484 -3538 0 - -3484 -3550 0 - -3484 -3570 0 - -3484 -3595 0 - -3484 -3610 0 - -3538 -3550 0 - -3538 -3570 0 - -3538 -3595 0 - -3538 -3610 0 - -3550 -3570 0 - -3550 -3595 0 - -3550 -3610 0 - -3570 -3595 0 - -3570 -3610 0 - -3595 -3610 0 - -3467 -3475 0 - -3467 -3533 0 - -3467 -3571 0 - -3467 -3596 0 - -3475 -3533 0 - -3475 -3571 0 - -3475 -3596 0 - -3533 -3571 0 - -3533 -3596 0 - -3571 -3596 0 - -3460 -3493 0 - -3460 -3539 0 - -3460 -3572 0 - -3460 -3580 0 - -3460 -3587 0 - -3460 -3597 0 - -3493 -3539 0 - -3493 -3572 0 - -3493 -3580 0 - -3493 -3587 0 - -3493 -3597 0 - -3539 -3572 0 - -3539 -3580 0 - -3539 -3587 0 - -3539 -3597 0 - -3572 -3580 0 - -3572 -3587 0 - -3572 -3597 0 - -3580 -3587 0 - -3580 -3597 0 - -3587 -3597 0 - -3461 -3485 0 - -3461 -3494 0 - -3461 -3514 0 - -3461 -3545 0 - -3461 -3551 0 - -3461 -3557 0 - -3461 -3611 0 - -3485 -3494 0 - -3485 -3514 0 - -3485 -3545 0 - -3485 -3551 0 - -3485 -3557 0 - -3485 -3611 0 - -3494 -3514 0 - -3494 -3545 0 - -3494 -3551 0 - -3494 -3557 0 - -3494 -3611 0 - -3514 -3545 0 - -3514 -3551 0 - -3514 -3557 0 - -3514 -3611 0 - -3545 -3551 0 - -3545 -3557 0 - -3545 -3611 0 - -3551 -3557 0 - -3551 -3611 0 - -3557 -3611 0 - -3476 -3486 0 - -3476 -3495 0 - -3476 -3503 0 - -3476 -3515 0 - -3476 -3523 0 - -3476 -3546 0 - -3476 -3558 0 - -3476 -3563 0 - -3476 -3588 0 - -3476 -3598 0 - -3486 -3495 0 - -3486 -3503 0 - -3486 -3515 0 - -3486 -3523 0 - -3486 -3546 0 - -3486 -3558 0 - -3486 -3563 0 - -3486 -3588 0 - -3486 -3598 0 - -3495 -3503 0 - -3495 -3515 0 - -3495 -3523 0 - -3495 -3546 0 - -3495 -3558 0 - -3495 -3563 0 - -3495 -3588 0 - -3495 -3598 0 - -3503 -3515 0 - -3503 -3523 0 - -3503 -3546 0 - -3503 -3558 0 - -3503 -3563 0 - -3503 -3588 0 - -3503 -3598 0 - -3515 -3523 0 - -3515 -3546 0 - -3515 -3558 0 - -3515 -3563 0 - -3515 -3588 0 - -3515 -3598 0 - -3523 -3546 0 - -3523 -3558 0 - -3523 -3563 0 - -3523 -3588 0 - -3523 -3598 0 - -3546 -3558 0 - -3546 -3563 0 - -3546 -3588 0 - -3546 -3598 0 - -3558 -3563 0 - -3558 -3588 0 - -3558 -3598 0 - -3563 -3588 0 - -3563 -3598 0 - -3588 -3598 0 - -3487 -3496 0 - -3487 -3524 0 - -3487 -3552 0 - -3487 -3559 0 - -3487 -3564 0 - -3487 -3573 0 - -3487 -3589 0 - -3496 -3524 0 - -3496 -3552 0 - -3496 -3559 0 - -3496 -3564 0 - -3496 -3573 0 - -3496 -3589 0 - -3524 -3552 0 - -3524 -3559 0 - -3524 -3564 0 - -3524 -3573 0 - -3524 -3589 0 - -3552 -3559 0 - -3552 -3564 0 - -3552 -3573 0 - -3552 -3589 0 - -3559 -3564 0 - -3559 -3573 0 - -3559 -3589 0 - -3564 -3573 0 - -3564 -3589 0 - -3573 -3589 0 - -3462 -3477 0 - -3462 -3504 0 - -3462 -3516 0 - -3462 -3525 0 - -3462 -3553 0 - -3462 -3581 0 - -3462 -3590 0 - -3462 -3599 0 - -3477 -3504 0 - -3477 -3516 0 - -3477 -3525 0 - -3477 -3553 0 - -3477 -3581 0 - -3477 -3590 0 - -3477 -3599 0 - -3504 -3516 0 - -3504 -3525 0 - -3504 -3553 0 - -3504 -3581 0 - -3504 -3590 0 - -3504 -3599 0 - -3516 -3525 0 - -3516 -3553 0 - -3516 -3581 0 - -3516 -3590 0 - -3516 -3599 0 - -3525 -3553 0 - -3525 -3581 0 - -3525 -3590 0 - -3525 -3599 0 - -3553 -3581 0 - -3553 -3590 0 - -3553 -3599 0 - -3581 -3590 0 - -3581 -3599 0 - -3590 -3599 0 - -3488 -3497 0 - -3488 -3505 0 - -3488 -3554 0 - -3488 -3600 0 - -3497 -3505 0 - -3497 -3554 0 - -3497 -3600 0 - -3505 -3554 0 - -3505 -3600 0 - -3554 -3600 0 - -3498 -3526 0 - -3498 -3560 0 - -3498 -3591 0 - -3498 -3601 0 - -3526 -3560 0 - -3526 -3591 0 - -3526 -3601 0 - -3560 -3591 0 - -3560 -3601 0 - -3591 -3601 0 - -3463 -3468 0 - -3463 -3499 0 - -3463 -3527 0 - -3463 -3540 0 - -3463 -3555 0 - -3463 -3565 0 - -3463 -3574 0 - -3463 -3582 0 - -3463 -3592 0 - -3463 -3602 0 - -3463 -3612 0 - -3468 -3499 0 - -3468 -3527 0 - -3468 -3540 0 - -3468 -3555 0 - -3468 -3565 0 - -3468 -3574 0 - -3468 -3582 0 - -3468 -3592 0 - -3468 -3602 0 - -3468 -3612 0 - -3499 -3527 0 - -3499 -3540 0 - -3499 -3555 0 - -3499 -3565 0 - -3499 -3574 0 - -3499 -3582 0 - -3499 -3592 0 - -3499 -3602 0 - -3499 -3612 0 - -3527 -3540 0 - -3527 -3555 0 - -3527 -3565 0 - -3527 -3574 0 - -3527 -3582 0 - -3527 -3592 0 - -3527 -3602 0 - -3527 -3612 0 - -3540 -3555 0 - -3540 -3565 0 - -3540 -3574 0 - -3540 -3582 0 - -3540 -3592 0 - -3540 -3602 0 - -3540 -3612 0 - -3555 -3565 0 - -3555 -3574 0 - -3555 -3582 0 - -3555 -3592 0 - -3555 -3602 0 - -3555 -3612 0 - -3565 -3574 0 - -3565 -3582 0 - -3565 -3592 0 - -3565 -3602 0 - -3565 -3612 0 - -3574 -3582 0 - -3574 -3592 0 - -3574 -3602 0 - -3574 -3612 0 - -3582 -3592 0 - -3582 -3602 0 - -3582 -3612 0 - -3592 -3602 0 - -3592 -3612 0 - -3602 -3612 0 - -3631 -3639 0 - -3631 -3655 0 - -3631 -3692 0 - -3631 -3703 0 - -3631 -3713 0 - -3631 -3738 0 - -3631 -3794 0 - -3639 -3655 0 - -3639 -3692 0 - -3639 -3703 0 - -3639 -3713 0 - -3639 -3738 0 - -3639 -3794 0 - -3655 -3692 0 - -3655 -3703 0 - -3655 -3713 0 - -3655 -3738 0 - -3655 -3794 0 - -3692 -3703 0 - -3692 -3713 0 - -3692 -3738 0 - -3692 -3794 0 - -3703 -3713 0 - -3703 -3738 0 - -3703 -3794 0 - -3713 -3738 0 - -3713 -3794 0 - -3738 -3794 0 - -3613 -3620 0 - -3613 -3640 0 - -3613 -3685 0 - -3613 -3693 0 - -3613 -3704 0 - -3613 -3723 0 - -3613 -3731 0 - -3613 -3758 0 - -3613 -3769 0 - -3613 -3786 0 - -3620 -3640 0 - -3620 -3685 0 - -3620 -3693 0 - -3620 -3704 0 - -3620 -3723 0 - -3620 -3731 0 - -3620 -3758 0 - -3620 -3769 0 - -3620 -3786 0 - -3640 -3685 0 - -3640 -3693 0 - -3640 -3704 0 - -3640 -3723 0 - -3640 -3731 0 - -3640 -3758 0 - -3640 -3769 0 - -3640 -3786 0 - -3685 -3693 0 - -3685 -3704 0 - -3685 -3723 0 - -3685 -3731 0 - -3685 -3758 0 - -3685 -3769 0 - -3685 -3786 0 - -3693 -3704 0 - -3693 -3723 0 - -3693 -3731 0 - -3693 -3758 0 - -3693 -3769 0 - -3693 -3786 0 - -3704 -3723 0 - -3704 -3731 0 - -3704 -3758 0 - -3704 -3769 0 - -3704 -3786 0 - -3723 -3731 0 - -3723 -3758 0 - -3723 -3769 0 - -3723 -3786 0 - -3731 -3758 0 - -3731 -3769 0 - -3731 -3786 0 - -3758 -3769 0 - -3758 -3786 0 - -3769 -3786 0 - -3614 -3621 0 - -3614 -3632 0 - -3614 -3641 0 - -3614 -3656 0 - -3614 -3732 0 - -3614 -3739 0 - -3614 -3759 0 - -3614 -3787 0 - -3621 -3632 0 - -3621 -3641 0 - -3621 -3656 0 - -3621 -3732 0 - -3621 -3739 0 - -3621 -3759 0 - -3621 -3787 0 - -3632 -3641 0 - -3632 -3656 0 - -3632 -3732 0 - -3632 -3739 0 - -3632 -3759 0 - -3632 -3787 0 - -3641 -3656 0 - -3641 -3732 0 - -3641 -3739 0 - -3641 -3759 0 - -3641 -3787 0 - -3656 -3732 0 - -3656 -3739 0 - -3656 -3759 0 - -3656 -3787 0 - -3732 -3739 0 - -3732 -3759 0 - -3732 -3787 0 - -3739 -3759 0 - -3739 -3787 0 - -3759 -3787 0 - -3615 -3622 0 - -3615 -3667 0 - -3615 -3694 0 - -3615 -3705 0 - -3615 -3749 0 - -3615 -3770 0 - -3615 -3788 0 - -3622 -3667 0 - -3622 -3694 0 - -3622 -3705 0 - -3622 -3749 0 - -3622 -3770 0 - -3622 -3788 0 - -3667 -3694 0 - -3667 -3705 0 - -3667 -3749 0 - -3667 -3770 0 - -3667 -3788 0 - -3694 -3705 0 - -3694 -3749 0 - -3694 -3770 0 - -3694 -3788 0 - -3705 -3749 0 - -3705 -3770 0 - -3705 -3788 0 - -3749 -3770 0 - -3749 -3788 0 - -3770 -3788 0 - -3642 -3657 0 - -3642 -3668 0 - -3642 -3706 0 - -3642 -3740 0 - -3642 -3760 0 - -3642 -3779 0 - -3657 -3668 0 - -3657 -3706 0 - -3657 -3740 0 - -3657 -3760 0 - -3657 -3779 0 - -3668 -3706 0 - -3668 -3740 0 - -3668 -3760 0 - -3668 -3779 0 - -3706 -3740 0 - -3706 -3760 0 - -3706 -3779 0 - -3740 -3760 0 - -3740 -3779 0 - -3760 -3779 0 - -3623 -3658 0 - -3623 -3686 0 - -3623 -3695 0 - -3623 -3714 0 - -3623 -3761 0 - -3623 -3780 0 - -3623 -3789 0 - -3658 -3686 0 - -3658 -3695 0 - -3658 -3714 0 - -3658 -3761 0 - -3658 -3780 0 - -3658 -3789 0 - -3686 -3695 0 - -3686 -3714 0 - -3686 -3761 0 - -3686 -3780 0 - -3686 -3789 0 - -3695 -3714 0 - -3695 -3761 0 - -3695 -3780 0 - -3695 -3789 0 - -3714 -3761 0 - -3714 -3780 0 - -3714 -3789 0 - -3761 -3780 0 - -3761 -3789 0 - -3780 -3789 0 - -3616 -3624 0 - -3616 -3633 0 - -3616 -3669 0 - -3616 -3679 0 - -3616 -3687 0 - -3616 -3715 0 - -3616 -3741 0 - -3616 -3750 0 - -3616 -3762 0 - -3624 -3633 0 - -3624 -3669 0 - -3624 -3679 0 - -3624 -3687 0 - -3624 -3715 0 - -3624 -3741 0 - -3624 -3750 0 - -3624 -3762 0 - -3633 -3669 0 - -3633 -3679 0 - -3633 -3687 0 - -3633 -3715 0 - -3633 -3741 0 - -3633 -3750 0 - -3633 -3762 0 - -3669 -3679 0 - -3669 -3687 0 - -3669 -3715 0 - -3669 -3741 0 - -3669 -3750 0 - -3669 -3762 0 - -3679 -3687 0 - -3679 -3715 0 - -3679 -3741 0 - -3679 -3750 0 - -3679 -3762 0 - -3687 -3715 0 - -3687 -3741 0 - -3687 -3750 0 - -3687 -3762 0 - -3715 -3741 0 - -3715 -3750 0 - -3715 -3762 0 - -3741 -3750 0 - -3741 -3762 0 - -3750 -3762 0 - -3643 -3659 0 - -3643 -3707 0 - -3643 -3716 0 - -3643 -3742 0 - -3643 -3751 0 - -3643 -3781 0 - -3643 -3795 0 - -3659 -3707 0 - -3659 -3716 0 - -3659 -3742 0 - -3659 -3751 0 - -3659 -3781 0 - -3659 -3795 0 - -3707 -3716 0 - -3707 -3742 0 - -3707 -3751 0 - -3707 -3781 0 - -3707 -3795 0 - -3716 -3742 0 - -3716 -3751 0 - -3716 -3781 0 - -3716 -3795 0 - -3742 -3751 0 - -3742 -3781 0 - -3742 -3795 0 - -3751 -3781 0 - -3751 -3795 0 - -3781 -3795 0 - -3625 -3644 0 - -3625 -3660 0 - -3625 -3670 0 - -3625 -3696 0 - -3625 -3733 0 - -3625 -3763 0 - -3625 -3782 0 - -3625 -3796 0 - -3644 -3660 0 - -3644 -3670 0 - -3644 -3696 0 - -3644 -3733 0 - -3644 -3763 0 - -3644 -3782 0 - -3644 -3796 0 - -3660 -3670 0 - -3660 -3696 0 - -3660 -3733 0 - -3660 -3763 0 - -3660 -3782 0 - -3660 -3796 0 - -3670 -3696 0 - -3670 -3733 0 - -3670 -3763 0 - -3670 -3782 0 - -3670 -3796 0 - -3696 -3733 0 - -3696 -3763 0 - -3696 -3782 0 - -3696 -3796 0 - -3733 -3763 0 - -3733 -3782 0 - -3733 -3796 0 - -3763 -3782 0 - -3763 -3796 0 - -3782 -3796 0 - -3617 -3626 0 - -3617 -3645 0 - -3617 -3671 0 - -3617 -3680 0 - -3617 -3697 0 - -3617 -3734 0 - -3617 -3743 0 - -3617 -3752 0 - -3617 -3771 0 - -3617 -3790 0 - -3626 -3645 0 - -3626 -3671 0 - -3626 -3680 0 - -3626 -3697 0 - -3626 -3734 0 - -3626 -3743 0 - -3626 -3752 0 - -3626 -3771 0 - -3626 -3790 0 - -3645 -3671 0 - -3645 -3680 0 - -3645 -3697 0 - -3645 -3734 0 - -3645 -3743 0 - -3645 -3752 0 - -3645 -3771 0 - -3645 -3790 0 - -3671 -3680 0 - -3671 -3697 0 - -3671 -3734 0 - -3671 -3743 0 - -3671 -3752 0 - -3671 -3771 0 - -3671 -3790 0 - -3680 -3697 0 - -3680 -3734 0 - -3680 -3743 0 - -3680 -3752 0 - -3680 -3771 0 - -3680 -3790 0 - -3697 -3734 0 - -3697 -3743 0 - -3697 -3752 0 - -3697 -3771 0 - -3697 -3790 0 - -3734 -3743 0 - -3734 -3752 0 - -3734 -3771 0 - -3734 -3790 0 - -3743 -3752 0 - -3743 -3771 0 - -3743 -3790 0 - -3752 -3771 0 - -3752 -3790 0 - -3771 -3790 0 - -3646 -3672 0 - -3646 -3681 0 - -3646 -3708 0 - -3646 -3717 0 - -3646 -3744 0 - -3646 -3753 0 - -3646 -3772 0 - -3646 -3791 0 - -3672 -3681 0 - -3672 -3708 0 - -3672 -3717 0 - -3672 -3744 0 - -3672 -3753 0 - -3672 -3772 0 - -3672 -3791 0 - -3681 -3708 0 - -3681 -3717 0 - -3681 -3744 0 - -3681 -3753 0 - -3681 -3772 0 - -3681 -3791 0 - -3708 -3717 0 - -3708 -3744 0 - -3708 -3753 0 - -3708 -3772 0 - -3708 -3791 0 - -3717 -3744 0 - -3717 -3753 0 - -3717 -3772 0 - -3717 -3791 0 - -3744 -3753 0 - -3744 -3772 0 - -3744 -3791 0 - -3753 -3772 0 - -3753 -3791 0 - -3772 -3791 0 - -3634 -3661 0 - -3634 -3688 0 - -3634 -3698 0 - -3634 -3709 0 - -3634 -3724 0 - -3634 -3745 0 - -3634 -3773 0 - -3634 -3797 0 - -3661 -3688 0 - -3661 -3698 0 - -3661 -3709 0 - -3661 -3724 0 - -3661 -3745 0 - -3661 -3773 0 - -3661 -3797 0 - -3688 -3698 0 - -3688 -3709 0 - -3688 -3724 0 - -3688 -3745 0 - -3688 -3773 0 - -3688 -3797 0 - -3698 -3709 0 - -3698 -3724 0 - -3698 -3745 0 - -3698 -3773 0 - -3698 -3797 0 - -3709 -3724 0 - -3709 -3745 0 - -3709 -3773 0 - -3709 -3797 0 - -3724 -3745 0 - -3724 -3773 0 - -3724 -3797 0 - -3745 -3773 0 - -3745 -3797 0 - -3773 -3797 0 - -3635 -3662 0 - -3635 -3682 0 - -3635 -3718 0 - -3635 -3725 0 - -3635 -3754 0 - -3635 -3792 0 - -3662 -3682 0 - -3662 -3718 0 - -3662 -3725 0 - -3662 -3754 0 - -3662 -3792 0 - -3682 -3718 0 - -3682 -3725 0 - -3682 -3754 0 - -3682 -3792 0 - -3718 -3725 0 - -3718 -3754 0 - -3718 -3792 0 - -3725 -3754 0 - -3725 -3792 0 - -3754 -3792 0 - -3627 -3636 0 - -3627 -3647 0 - -3627 -3663 0 - -3627 -3683 0 - -3627 -3689 0 - -3627 -3699 0 - -3627 -3719 0 - -3627 -3726 0 - -3627 -3746 0 - -3627 -3755 0 - -3627 -3774 0 - -3627 -3783 0 - -3627 -3798 0 - -3636 -3647 0 - -3636 -3663 0 - -3636 -3683 0 - -3636 -3689 0 - -3636 -3699 0 - -3636 -3719 0 - -3636 -3726 0 - -3636 -3746 0 - -3636 -3755 0 - -3636 -3774 0 - -3636 -3783 0 - -3636 -3798 0 - -3647 -3663 0 - -3647 -3683 0 - -3647 -3689 0 - -3647 -3699 0 - -3647 -3719 0 - -3647 -3726 0 - -3647 -3746 0 - -3647 -3755 0 - -3647 -3774 0 - -3647 -3783 0 - -3647 -3798 0 - -3663 -3683 0 - -3663 -3689 0 - -3663 -3699 0 - -3663 -3719 0 - -3663 -3726 0 - -3663 -3746 0 - -3663 -3755 0 - -3663 -3774 0 - -3663 -3783 0 - -3663 -3798 0 - -3683 -3689 0 - -3683 -3699 0 - -3683 -3719 0 - -3683 -3726 0 - -3683 -3746 0 - -3683 -3755 0 - -3683 -3774 0 - -3683 -3783 0 - -3683 -3798 0 - -3689 -3699 0 - -3689 -3719 0 - -3689 -3726 0 - -3689 -3746 0 - -3689 -3755 0 - -3689 -3774 0 - -3689 -3783 0 - -3689 -3798 0 - -3699 -3719 0 - -3699 -3726 0 - -3699 -3746 0 - -3699 -3755 0 - -3699 -3774 0 - -3699 -3783 0 - -3699 -3798 0 - -3719 -3726 0 - -3719 -3746 0 - -3719 -3755 0 - -3719 -3774 0 - -3719 -3783 0 - -3719 -3798 0 - -3726 -3746 0 - -3726 -3755 0 - -3726 -3774 0 - -3726 -3783 0 - -3726 -3798 0 - -3746 -3755 0 - -3746 -3774 0 - -3746 -3783 0 - -3746 -3798 0 - -3755 -3774 0 - -3755 -3783 0 - -3755 -3798 0 - -3774 -3783 0 - -3774 -3798 0 - -3783 -3798 0 - -3648 -3673 0 - -3648 -3690 0 - -3648 -3700 0 - -3648 -3710 0 - -3648 -3727 0 - -3648 -3764 0 - -3648 -3784 0 - -3648 -3793 0 - -3648 -3799 0 - -3673 -3690 0 - -3673 -3700 0 - -3673 -3710 0 - -3673 -3727 0 - -3673 -3764 0 - -3673 -3784 0 - -3673 -3793 0 - -3673 -3799 0 - -3690 -3700 0 - -3690 -3710 0 - -3690 -3727 0 - -3690 -3764 0 - -3690 -3784 0 - -3690 -3793 0 - -3690 -3799 0 - -3700 -3710 0 - -3700 -3727 0 - -3700 -3764 0 - -3700 -3784 0 - -3700 -3793 0 - -3700 -3799 0 - -3710 -3727 0 - -3710 -3764 0 - -3710 -3784 0 - -3710 -3793 0 - -3710 -3799 0 - -3727 -3764 0 - -3727 -3784 0 - -3727 -3793 0 - -3727 -3799 0 - -3764 -3784 0 - -3764 -3793 0 - -3764 -3799 0 - -3784 -3793 0 - -3784 -3799 0 - -3793 -3799 0 - -3628 -3651 0 - -3628 -3664 0 - -3628 -3674 0 - -3628 -3720 0 - -3628 -3756 0 - -3628 -3765 0 - -3628 -3775 0 - -3651 -3664 0 - -3651 -3674 0 - -3651 -3720 0 - -3651 -3756 0 - -3651 -3765 0 - -3651 -3775 0 - -3664 -3674 0 - -3664 -3720 0 - -3664 -3756 0 - -3664 -3765 0 - -3664 -3775 0 - -3674 -3720 0 - -3674 -3756 0 - -3674 -3765 0 - -3674 -3775 0 - -3720 -3756 0 - -3720 -3765 0 - -3720 -3775 0 - -3756 -3765 0 - -3756 -3775 0 - -3765 -3775 0 - -3618 -3637 0 - -3618 -3652 0 - -3618 -3675 0 - -3618 -3691 0 - -3618 -3728 0 - -3618 -3735 0 - -3618 -3800 0 - -3637 -3652 0 - -3637 -3675 0 - -3637 -3691 0 - -3637 -3728 0 - -3637 -3735 0 - -3637 -3800 0 - -3652 -3675 0 - -3652 -3691 0 - -3652 -3728 0 - -3652 -3735 0 - -3652 -3800 0 - -3675 -3691 0 - -3675 -3728 0 - -3675 -3735 0 - -3675 -3800 0 - -3691 -3728 0 - -3691 -3735 0 - -3691 -3800 0 - -3728 -3735 0 - -3728 -3800 0 - -3735 -3800 0 - -3619 -3629 0 - -3619 -3638 0 - -3619 -3684 0 - -3619 -3729 0 - -3619 -3747 0 - -3619 -3776 0 - -3629 -3638 0 - -3629 -3684 0 - -3629 -3729 0 - -3629 -3747 0 - -3629 -3776 0 - -3638 -3684 0 - -3638 -3729 0 - -3638 -3747 0 - -3638 -3776 0 - -3684 -3729 0 - -3684 -3747 0 - -3684 -3776 0 - -3729 -3747 0 - -3729 -3776 0 - -3747 -3776 0 - -3630 -3649 0 - -3630 -3676 0 - -3630 -3701 0 - -3630 -3721 0 - -3630 -3766 0 - -3649 -3676 0 - -3649 -3701 0 - -3649 -3721 0 - -3649 -3766 0 - -3676 -3701 0 - -3676 -3721 0 - -3676 -3766 0 - -3701 -3721 0 - -3701 -3766 0 - -3721 -3766 0 - -3653 -3665 0 - -3653 -3677 0 - -3653 -3711 0 - -3653 -3736 0 - -3653 -3748 0 - -3653 -3757 0 - -3653 -3767 0 - -3653 -3777 0 - -3653 -3785 0 - -3665 -3677 0 - -3665 -3711 0 - -3665 -3736 0 - -3665 -3748 0 - -3665 -3757 0 - -3665 -3767 0 - -3665 -3777 0 - -3665 -3785 0 - -3677 -3711 0 - -3677 -3736 0 - -3677 -3748 0 - -3677 -3757 0 - -3677 -3767 0 - -3677 -3777 0 - -3677 -3785 0 - -3711 -3736 0 - -3711 -3748 0 - -3711 -3757 0 - -3711 -3767 0 - -3711 -3777 0 - -3711 -3785 0 - -3736 -3748 0 - -3736 -3757 0 - -3736 -3767 0 - -3736 -3777 0 - -3736 -3785 0 - -3748 -3757 0 - -3748 -3767 0 - -3748 -3777 0 - -3748 -3785 0 - -3757 -3767 0 - -3757 -3777 0 - -3757 -3785 0 - -3767 -3777 0 - -3767 -3785 0 - -3777 -3785 0 - -3650 -3654 0 - -3650 -3666 0 - -3650 -3678 0 - -3650 -3702 0 - -3650 -3712 0 - -3650 -3722 0 - -3650 -3730 0 - -3650 -3737 0 - -3650 -3768 0 - -3650 -3778 0 - -3650 -3801 0 - -3654 -3666 0 - -3654 -3678 0 - -3654 -3702 0 - -3654 -3712 0 - -3654 -3722 0 - -3654 -3730 0 - -3654 -3737 0 - -3654 -3768 0 - -3654 -3778 0 - -3654 -3801 0 - -3666 -3678 0 - -3666 -3702 0 - -3666 -3712 0 - -3666 -3722 0 - -3666 -3730 0 - -3666 -3737 0 - -3666 -3768 0 - -3666 -3778 0 - -3666 -3801 0 - -3678 -3702 0 - -3678 -3712 0 - -3678 -3722 0 - -3678 -3730 0 - -3678 -3737 0 - -3678 -3768 0 - -3678 -3778 0 - -3678 -3801 0 - -3702 -3712 0 - -3702 -3722 0 - -3702 -3730 0 - -3702 -3737 0 - -3702 -3768 0 - -3702 -3778 0 - -3702 -3801 0 - -3712 -3722 0 - -3712 -3730 0 - -3712 -3737 0 - -3712 -3768 0 - -3712 -3778 0 - -3712 -3801 0 - -3722 -3730 0 - -3722 -3737 0 - -3722 -3768 0 - -3722 -3778 0 - -3722 -3801 0 - -3730 -3737 0 - -3730 -3768 0 - -3730 -3778 0 - -3730 -3801 0 - -3737 -3768 0 - -3737 -3778 0 - -3737 -3801 0 - -3768 -3778 0 - -3768 -3801 0 - -3778 -3801 0 - -3802 -3809 0 - -3802 -3824 0 - -3802 -3839 0 - -3802 -3845 0 - -3802 -3853 0 - -3802 -3862 0 - -3802 -3885 0 - -3802 -3899 0 - -3809 -3824 0 - -3809 -3839 0 - -3809 -3845 0 - -3809 -3853 0 - -3809 -3862 0 - -3809 -3885 0 - -3809 -3899 0 - -3824 -3839 0 - -3824 -3845 0 - -3824 -3853 0 - -3824 -3862 0 - -3824 -3885 0 - -3824 -3899 0 - -3839 -3845 0 - -3839 -3853 0 - -3839 -3862 0 - -3839 -3885 0 - -3839 -3899 0 - -3845 -3853 0 - -3845 -3862 0 - -3845 -3885 0 - -3845 -3899 0 - -3853 -3862 0 - -3853 -3885 0 - -3853 -3899 0 - -3862 -3885 0 - -3862 -3899 0 - -3885 -3899 0 - -3818 -3830 0 - -3818 -3840 0 - -3818 -3846 0 - -3818 -3859 0 - -3818 -3871 0 - -3818 -3878 0 - -3818 -3891 0 - -3818 -3900 0 - -3830 -3840 0 - -3830 -3846 0 - -3830 -3859 0 - -3830 -3871 0 - -3830 -3878 0 - -3830 -3891 0 - -3830 -3900 0 - -3840 -3846 0 - -3840 -3859 0 - -3840 -3871 0 - -3840 -3878 0 - -3840 -3891 0 - -3840 -3900 0 - -3846 -3859 0 - -3846 -3871 0 - -3846 -3878 0 - -3846 -3891 0 - -3846 -3900 0 - -3859 -3871 0 - -3859 -3878 0 - -3859 -3891 0 - -3859 -3900 0 - -3871 -3878 0 - -3871 -3891 0 - -3871 -3900 0 - -3878 -3891 0 - -3878 -3900 0 - -3891 -3900 0 - -3803 -3810 0 - -3803 -3819 0 - -3803 -3841 0 - -3803 -3854 0 - -3803 -3863 0 - -3803 -3872 0 - -3803 -3879 0 - -3803 -3892 0 - -3803 -3901 0 - -3810 -3819 0 - -3810 -3841 0 - -3810 -3854 0 - -3810 -3863 0 - -3810 -3872 0 - -3810 -3879 0 - -3810 -3892 0 - -3810 -3901 0 - -3819 -3841 0 - -3819 -3854 0 - -3819 -3863 0 - -3819 -3872 0 - -3819 -3879 0 - -3819 -3892 0 - -3819 -3901 0 - -3841 -3854 0 - -3841 -3863 0 - -3841 -3872 0 - -3841 -3879 0 - -3841 -3892 0 - -3841 -3901 0 - -3854 -3863 0 - -3854 -3872 0 - -3854 -3879 0 - -3854 -3892 0 - -3854 -3901 0 - -3863 -3872 0 - -3863 -3879 0 - -3863 -3892 0 - -3863 -3901 0 - -3872 -3879 0 - -3872 -3892 0 - -3872 -3901 0 - -3879 -3892 0 - -3879 -3901 0 - -3892 -3901 0 - -3811 -3831 0 - -3811 -3855 0 - -3811 -3873 0 - -3811 -3880 0 - -3811 -3886 0 - -3811 -3893 0 - -3831 -3855 0 - -3831 -3873 0 - -3831 -3880 0 - -3831 -3886 0 - -3831 -3893 0 - -3855 -3873 0 - -3855 -3880 0 - -3855 -3886 0 - -3855 -3893 0 - -3873 -3880 0 - -3873 -3886 0 - -3873 -3893 0 - -3880 -3886 0 - -3880 -3893 0 - -3886 -3893 0 - -3804 -3832 0 - -3804 -3847 0 - -3804 -3864 0 - -3804 -3881 0 - -3804 -3894 0 - -3832 -3847 0 - -3832 -3864 0 - -3832 -3881 0 - -3832 -3894 0 - -3847 -3864 0 - -3847 -3881 0 - -3847 -3894 0 - -3864 -3881 0 - -3864 -3894 0 - -3881 -3894 0 - -3805 -3812 0 - -3805 -3833 0 - -3805 -3848 0 - -3805 -3856 0 - -3805 -3865 0 - -3805 -3874 0 - -3805 -3902 0 - -3812 -3833 0 - -3812 -3848 0 - -3812 -3856 0 - -3812 -3865 0 - -3812 -3874 0 - -3812 -3902 0 - -3833 -3848 0 - -3833 -3856 0 - -3833 -3865 0 - -3833 -3874 0 - -3833 -3902 0 - -3848 -3856 0 - -3848 -3865 0 - -3848 -3874 0 - -3848 -3902 0 - -3856 -3865 0 - -3856 -3874 0 - -3856 -3902 0 - -3865 -3874 0 - -3865 -3902 0 - -3874 -3902 0 - -3806 -3813 0 - -3806 -3825 0 - -3806 -3857 0 - -3806 -3887 0 - -3806 -3895 0 - -3813 -3825 0 - -3813 -3857 0 - -3813 -3887 0 - -3813 -3895 0 - -3825 -3857 0 - -3825 -3887 0 - -3825 -3895 0 - -3857 -3887 0 - -3857 -3895 0 - -3887 -3895 0 - -3834 -3842 0 - -3834 -3866 0 - -3834 -3882 0 - -3834 -3896 0 - -3842 -3866 0 - -3842 -3882 0 - -3842 -3896 0 - -3866 -3882 0 - -3866 -3896 0 - -3882 -3896 0 - -3814 -3820 0 - -3814 -3843 0 - -3814 -3849 0 - -3814 -3858 0 - -3814 -3875 0 - -3814 -3888 0 - -3814 -3897 0 - -3820 -3843 0 - -3820 -3849 0 - -3820 -3858 0 - -3820 -3875 0 - -3820 -3888 0 - -3820 -3897 0 - -3843 -3849 0 - -3843 -3858 0 - -3843 -3875 0 - -3843 -3888 0 - -3843 -3897 0 - -3849 -3858 0 - -3849 -3875 0 - -3849 -3888 0 - -3849 -3897 0 - -3858 -3875 0 - -3858 -3888 0 - -3858 -3897 0 - -3875 -3888 0 - -3875 -3897 0 - -3888 -3897 0 - -3807 -3821 0 - -3807 -3826 0 - -3807 -3850 0 - -3807 -3867 0 - -3807 -3889 0 - -3821 -3826 0 - -3821 -3850 0 - -3821 -3867 0 - -3821 -3889 0 - -3826 -3850 0 - -3826 -3867 0 - -3826 -3889 0 - -3850 -3867 0 - -3850 -3889 0 - -3867 -3889 0 - -3815 -3835 0 - -3815 -3844 0 - -3815 -3851 0 - -3815 -3903 0 - -3835 -3844 0 - -3835 -3851 0 - -3835 -3903 0 - -3844 -3851 0 - -3844 -3903 0 - -3851 -3903 0 - -3808 -3822 0 - -3808 -3827 0 - -3808 -3904 0 - -3822 -3827 0 - -3822 -3904 0 - -3827 -3904 0 - -3816 -3823 0 - -3816 -3836 0 - -3816 -3860 0 - -3816 -3868 0 - -3816 -3876 0 - -3816 -3883 0 - -3823 -3836 0 - -3823 -3860 0 - -3823 -3868 0 - -3823 -3876 0 - -3823 -3883 0 - -3836 -3860 0 - -3836 -3868 0 - -3836 -3876 0 - -3836 -3883 0 - -3860 -3868 0 - -3860 -3876 0 - -3860 -3883 0 - -3868 -3876 0 - -3868 -3883 0 - -3876 -3883 0 - -3828 -3837 0 - -3828 -3869 0 - -3828 -3877 0 - -3828 -3884 0 - -3828 -3890 0 - -3828 -3898 0 - -3837 -3869 0 - -3837 -3877 0 - -3837 -3884 0 - -3837 -3890 0 - -3837 -3898 0 - -3869 -3877 0 - -3869 -3884 0 - -3869 -3890 0 - -3869 -3898 0 - -3877 -3884 0 - -3877 -3890 0 - -3877 -3898 0 - -3884 -3890 0 - -3884 -3898 0 - -3890 -3898 0 - -3817 -3829 0 - -3817 -3838 0 - -3817 -3852 0 - -3817 -3861 0 - -3817 -3870 0 - -3829 -3838 0 - -3829 -3852 0 - -3829 -3861 0 - -3829 -3870 0 - -3838 -3852 0 - -3838 -3861 0 - -3838 -3870 0 - -3852 -3861 0 - -3852 -3870 0 - -3861 -3870 0 - -3911 -3925 0 - -3911 -3948 0 - -3911 -3955 0 - -3911 -3970 0 - -3911 -3994 0 - -3911 -4008 0 - -3925 -3948 0 - -3925 -3955 0 - -3925 -3970 0 - -3925 -3994 0 - -3925 -4008 0 - -3948 -3955 0 - -3948 -3970 0 - -3948 -3994 0 - -3948 -4008 0 - -3955 -3970 0 - -3955 -3994 0 - -3955 -4008 0 - -3970 -3994 0 - -3970 -4008 0 - -3994 -4008 0 - -3934 -3949 0 - -3934 -3956 0 - -3934 -3995 0 - -3949 -3956 0 - -3949 -3995 0 - -3956 -3995 0 - -3905 -3912 0 - -3905 -3926 0 - -3905 -3950 0 - -3905 -3964 0 - -3905 -3977 0 - -3905 -3986 0 - -3905 -3996 0 - -3905 -4002 0 - -3912 -3926 0 - -3912 -3950 0 - -3912 -3964 0 - -3912 -3977 0 - -3912 -3986 0 - -3912 -3996 0 - -3912 -4002 0 - -3926 -3950 0 - -3926 -3964 0 - -3926 -3977 0 - -3926 -3986 0 - -3926 -3996 0 - -3926 -4002 0 - -3950 -3964 0 - -3950 -3977 0 - -3950 -3986 0 - -3950 -3996 0 - -3950 -4002 0 - -3964 -3977 0 - -3964 -3986 0 - -3964 -3996 0 - -3964 -4002 0 - -3977 -3986 0 - -3977 -3996 0 - -3977 -4002 0 - -3986 -3996 0 - -3986 -4002 0 - -3996 -4002 0 - -3906 -3913 0 - -3906 -3927 0 - -3906 -3965 0 - -3906 -3987 0 - -3913 -3927 0 - -3913 -3965 0 - -3913 -3987 0 - -3927 -3965 0 - -3927 -3987 0 - -3965 -3987 0 - -3907 -3935 0 - -3907 -3951 0 - -3907 -3957 0 - -3907 -3966 0 - -3907 -3971 0 - -3907 -3978 0 - -3907 -4003 0 - -3907 -4009 0 - -3907 -4015 0 - -3935 -3951 0 - -3935 -3957 0 - -3935 -3966 0 - -3935 -3971 0 - -3935 -3978 0 - -3935 -4003 0 - -3935 -4009 0 - -3935 -4015 0 - -3951 -3957 0 - -3951 -3966 0 - -3951 -3971 0 - -3951 -3978 0 - -3951 -4003 0 - -3951 -4009 0 - -3951 -4015 0 - -3957 -3966 0 - -3957 -3971 0 - -3957 -3978 0 - -3957 -4003 0 - -3957 -4009 0 - -3957 -4015 0 - -3966 -3971 0 - -3966 -3978 0 - -3966 -4003 0 - -3966 -4009 0 - -3966 -4015 0 - -3971 -3978 0 - -3971 -4003 0 - -3971 -4009 0 - -3971 -4015 0 - -3978 -4003 0 - -3978 -4009 0 - -3978 -4015 0 - -4003 -4009 0 - -4003 -4015 0 - -4009 -4015 0 - -3914 -3928 0 - -3914 -3936 0 - -3914 -3967 0 - -3914 -3972 0 - -3914 -3997 0 - -3914 -4010 0 - -3928 -3936 0 - -3928 -3967 0 - -3928 -3972 0 - -3928 -3997 0 - -3928 -4010 0 - -3936 -3967 0 - -3936 -3972 0 - -3936 -3997 0 - -3936 -4010 0 - -3967 -3972 0 - -3967 -3997 0 - -3967 -4010 0 - -3972 -3997 0 - -3972 -4010 0 - -3997 -4010 0 - -3908 -3915 0 - -3908 -3937 0 - -3908 -3944 0 - -3908 -3958 0 - -3908 -3968 0 - -3908 -3979 0 - -3908 -3988 0 - -3908 -3998 0 - -3908 -4004 0 - -3908 -4016 0 - -3915 -3937 0 - -3915 -3944 0 - -3915 -3958 0 - -3915 -3968 0 - -3915 -3979 0 - -3915 -3988 0 - -3915 -3998 0 - -3915 -4004 0 - -3915 -4016 0 - -3937 -3944 0 - -3937 -3958 0 - -3937 -3968 0 - -3937 -3979 0 - -3937 -3988 0 - -3937 -3998 0 - -3937 -4004 0 - -3937 -4016 0 - -3944 -3958 0 - -3944 -3968 0 - -3944 -3979 0 - -3944 -3988 0 - -3944 -3998 0 - -3944 -4004 0 - -3944 -4016 0 - -3958 -3968 0 - -3958 -3979 0 - -3958 -3988 0 - -3958 -3998 0 - -3958 -4004 0 - -3958 -4016 0 - -3968 -3979 0 - -3968 -3988 0 - -3968 -3998 0 - -3968 -4004 0 - -3968 -4016 0 - -3979 -3988 0 - -3979 -3998 0 - -3979 -4004 0 - -3979 -4016 0 - -3988 -3998 0 - -3988 -4004 0 - -3988 -4016 0 - -3998 -4004 0 - -3998 -4016 0 - -4004 -4016 0 - -3929 -3938 0 - -3929 -3945 0 - -3929 -3952 0 - -3929 -3973 0 - -3929 -3989 0 - -3929 -4005 0 - -3929 -4017 0 - -3938 -3945 0 - -3938 -3952 0 - -3938 -3973 0 - -3938 -3989 0 - -3938 -4005 0 - -3938 -4017 0 - -3945 -3952 0 - -3945 -3973 0 - -3945 -3989 0 - -3945 -4005 0 - -3945 -4017 0 - -3952 -3973 0 - -3952 -3989 0 - -3952 -4005 0 - -3952 -4017 0 - -3973 -3989 0 - -3973 -4005 0 - -3973 -4017 0 - -3989 -4005 0 - -3989 -4017 0 - -4005 -4017 0 - -3909 -3921 0 - -3909 -3930 0 - -3909 -3946 0 - -3909 -3959 0 - -3909 -3980 0 - -3909 -3990 0 - -3909 -4006 0 - -3909 -4011 0 - -3921 -3930 0 - -3921 -3946 0 - -3921 -3959 0 - -3921 -3980 0 - -3921 -3990 0 - -3921 -4006 0 - -3921 -4011 0 - -3930 -3946 0 - -3930 -3959 0 - -3930 -3980 0 - -3930 -3990 0 - -3930 -4006 0 - -3930 -4011 0 - -3946 -3959 0 - -3946 -3980 0 - -3946 -3990 0 - -3946 -4006 0 - -3946 -4011 0 - -3959 -3980 0 - -3959 -3990 0 - -3959 -4006 0 - -3959 -4011 0 - -3980 -3990 0 - -3980 -4006 0 - -3980 -4011 0 - -3990 -4006 0 - -3990 -4011 0 - -4006 -4011 0 - -3916 -3939 0 - -3916 -3953 0 - -3916 -3960 0 - -3916 -3974 0 - -3916 -3981 0 - -3939 -3953 0 - -3939 -3960 0 - -3939 -3974 0 - -3939 -3981 0 - -3953 -3960 0 - -3953 -3974 0 - -3953 -3981 0 - -3960 -3974 0 - -3960 -3981 0 - -3974 -3981 0 - -3917 -3954 0 - -3917 -3961 0 - -3917 -3982 0 - -3917 -4012 0 - -3917 -4018 0 - -3954 -3961 0 - -3954 -3982 0 - -3954 -4012 0 - -3954 -4018 0 - -3961 -3982 0 - -3961 -4012 0 - -3961 -4018 0 - -3982 -4012 0 - -3982 -4018 0 - -4012 -4018 0 - -3918 -3940 0 - -3918 -3969 0 - -3918 -3983 0 - -3918 -3991 0 - -3940 -3969 0 - -3940 -3983 0 - -3940 -3991 0 - -3969 -3983 0 - -3969 -3991 0 - -3983 -3991 0 - -3910 -3919 0 - -3910 -3947 0 - -3910 -3984 0 - -3910 -3999 0 - -3910 -4019 0 - -3919 -3947 0 - -3919 -3984 0 - -3919 -3999 0 - -3919 -4019 0 - -3947 -3984 0 - -3947 -3999 0 - -3947 -4019 0 - -3984 -3999 0 - -3984 -4019 0 - -3999 -4019 0 - -3922 -3931 0 - -3922 -3941 0 - -3922 -3962 0 - -3922 -4000 0 - -3922 -4013 0 - -3922 -4020 0 - -3931 -3941 0 - -3931 -3962 0 - -3931 -4000 0 - -3931 -4013 0 - -3931 -4020 0 - -3941 -3962 0 - -3941 -4000 0 - -3941 -4013 0 - -3941 -4020 0 - -3962 -4000 0 - -3962 -4013 0 - -3962 -4020 0 - -4000 -4013 0 - -4000 -4020 0 - -4013 -4020 0 - -3923 -3932 0 - -3923 -3942 0 - -3923 -3975 0 - -3923 -3985 0 - -3923 -3992 0 - -3923 -4001 0 - -3923 -4007 0 - -3923 -4014 0 - -3923 -4021 0 - -3932 -3942 0 - -3932 -3975 0 - -3932 -3985 0 - -3932 -3992 0 - -3932 -4001 0 - -3932 -4007 0 - -3932 -4014 0 - -3932 -4021 0 - -3942 -3975 0 - -3942 -3985 0 - -3942 -3992 0 - -3942 -4001 0 - -3942 -4007 0 - -3942 -4014 0 - -3942 -4021 0 - -3975 -3985 0 - -3975 -3992 0 - -3975 -4001 0 - -3975 -4007 0 - -3975 -4014 0 - -3975 -4021 0 - -3985 -3992 0 - -3985 -4001 0 - -3985 -4007 0 - -3985 -4014 0 - -3985 -4021 0 - -3992 -4001 0 - -3992 -4007 0 - -3992 -4014 0 - -3992 -4021 0 - -4001 -4007 0 - -4001 -4014 0 - -4001 -4021 0 - -4007 -4014 0 - -4007 -4021 0 - -4014 -4021 0 - -3920 -3924 0 - -3920 -3933 0 - -3920 -3943 0 - -3920 -3963 0 - -3920 -3976 0 - -3920 -3993 0 - -3920 -4022 0 - -3924 -3933 0 - -3924 -3943 0 - -3924 -3963 0 - -3924 -3976 0 - -3924 -3993 0 - -3924 -4022 0 - -3933 -3943 0 - -3933 -3963 0 - -3933 -3976 0 - -3933 -3993 0 - -3933 -4022 0 - -3943 -3963 0 - -3943 -3976 0 - -3943 -3993 0 - -3943 -4022 0 - -3963 -3976 0 - -3963 -3993 0 - -3963 -4022 0 - -3976 -3993 0 - -3976 -4022 0 - -3993 -4022 0 - -4052 -4057 0 - -4052 -4062 0 - -4052 -4075 0 - -4052 -4083 0 - -4057 -4062 0 - -4057 -4075 0 - -4057 -4083 0 - -4062 -4075 0 - -4062 -4083 0 - -4075 -4083 0 - -4029 -4035 0 - -4029 -4046 0 - -4029 -4058 0 - -4029 -4070 0 - -4029 -4084 0 - -4035 -4046 0 - -4035 -4058 0 - -4035 -4070 0 - -4035 -4084 0 - -4046 -4058 0 - -4046 -4070 0 - -4046 -4084 0 - -4058 -4070 0 - -4058 -4084 0 - -4070 -4084 0 - -4038 -4047 0 - -4038 -4065 0 - -4038 -4085 0 - -4038 -4092 0 - -4047 -4065 0 - -4047 -4085 0 - -4047 -4092 0 - -4065 -4085 0 - -4065 -4092 0 - -4085 -4092 0 - -4023 -4030 0 - -4023 -4053 0 - -4023 -4059 0 - -4023 -4071 0 - -4023 -4076 0 - -4023 -4086 0 - -4030 -4053 0 - -4030 -4059 0 - -4030 -4071 0 - -4030 -4076 0 - -4030 -4086 0 - -4053 -4059 0 - -4053 -4071 0 - -4053 -4076 0 - -4053 -4086 0 - -4059 -4071 0 - -4059 -4076 0 - -4059 -4086 0 - -4071 -4076 0 - -4071 -4086 0 - -4076 -4086 0 - -4024 -4048 0 - -4024 -4066 0 - -4024 -4087 0 - -4024 -4093 0 - -4048 -4066 0 - -4048 -4087 0 - -4048 -4093 0 - -4066 -4087 0 - -4066 -4093 0 - -4087 -4093 0 - -4025 -4031 0 - -4025 -4042 0 - -4025 -4067 0 - -4025 -4072 0 - -4031 -4042 0 - -4031 -4067 0 - -4031 -4072 0 - -4042 -4067 0 - -4042 -4072 0 - -4067 -4072 0 - -4032 -4054 0 - -4032 -4060 0 - -4032 -4073 0 - -4032 -4077 0 - -4032 -4088 0 - -4054 -4060 0 - -4054 -4073 0 - -4054 -4077 0 - -4054 -4088 0 - -4060 -4073 0 - -4060 -4077 0 - -4060 -4088 0 - -4073 -4077 0 - -4073 -4088 0 - -4077 -4088 0 - -4033 -4043 0 - -4033 -4089 0 - -4043 -4089 0 - -4026 -4036 0 - -4026 -4044 0 - -4026 -4049 0 - -4026 -4074 0 - -4026 -4078 0 - -4026 -4094 0 - -4036 -4044 0 - -4036 -4049 0 - -4036 -4074 0 - -4036 -4078 0 - -4036 -4094 0 - -4044 -4049 0 - -4044 -4074 0 - -4044 -4078 0 - -4044 -4094 0 - -4049 -4074 0 - -4049 -4078 0 - -4049 -4094 0 - -4074 -4078 0 - -4074 -4094 0 - -4078 -4094 0 - -4050 -4055 0 - -4050 -4068 0 - -4050 -4079 0 - -4055 -4068 0 - -4055 -4079 0 - -4068 -4079 0 - -4027 -4039 0 - -4027 -4080 0 - -4039 -4080 0 - -4040 -4056 0 - -4040 -4063 0 - -4040 -4090 0 - -4056 -4063 0 - -4056 -4090 0 - -4063 -4090 0 - -4028 -4045 0 - -4028 -4051 0 - -4028 -4081 0 - -4028 -4091 0 - -4045 -4051 0 - -4045 -4081 0 - -4045 -4091 0 - -4051 -4081 0 - -4051 -4091 0 - -4081 -4091 0 - -4034 -4037 0 - -4034 -4041 0 - -4034 -4061 0 - -4034 -4064 0 - -4034 -4069 0 - -4034 -4082 0 - -4037 -4041 0 - -4037 -4061 0 - -4037 -4064 0 - -4037 -4069 0 - -4037 -4082 0 - -4041 -4061 0 - -4041 -4064 0 - -4041 -4069 0 - -4041 -4082 0 - -4061 -4064 0 - -4061 -4069 0 - -4061 -4082 0 - -4064 -4069 0 - -4064 -4082 0 - -4069 -4082 0 - -4095 -4106 0 - -4095 -4109 0 - -4095 -4140 0 - -4095 -4149 0 - -4095 -4163 0 - -4095 -4194 0 - -4095 -4201 0 - -4095 -4208 0 - -4095 -4216 0 - -4106 -4109 0 - -4106 -4140 0 - -4106 -4149 0 - -4106 -4163 0 - -4106 -4194 0 - -4106 -4201 0 - -4106 -4208 0 - -4106 -4216 0 - -4109 -4140 0 - -4109 -4149 0 - -4109 -4163 0 - -4109 -4194 0 - -4109 -4201 0 - -4109 -4208 0 - -4109 -4216 0 - -4140 -4149 0 - -4140 -4163 0 - -4140 -4194 0 - -4140 -4201 0 - -4140 -4208 0 - -4140 -4216 0 - -4149 -4163 0 - -4149 -4194 0 - -4149 -4201 0 - -4149 -4208 0 - -4149 -4216 0 - -4163 -4194 0 - -4163 -4201 0 - -4163 -4208 0 - -4163 -4216 0 - -4194 -4201 0 - -4194 -4208 0 - -4194 -4216 0 - -4201 -4208 0 - -4201 -4216 0 - -4208 -4216 0 - -4134 -4141 0 - -4134 -4150 0 - -4134 -4156 0 - -4134 -4164 0 - -4134 -4186 0 - -4141 -4150 0 - -4141 -4156 0 - -4141 -4164 0 - -4141 -4186 0 - -4150 -4156 0 - -4150 -4164 0 - -4150 -4186 0 - -4156 -4164 0 - -4156 -4186 0 - -4164 -4186 0 - -4096 -4135 0 - -4096 -4142 0 - -4096 -4151 0 - -4096 -4157 0 - -4096 -4165 0 - -4096 -4172 0 - -4096 -4177 0 - -4096 -4202 0 - -4135 -4142 0 - -4135 -4151 0 - -4135 -4157 0 - -4135 -4165 0 - -4135 -4172 0 - -4135 -4177 0 - -4135 -4202 0 - -4142 -4151 0 - -4142 -4157 0 - -4142 -4165 0 - -4142 -4172 0 - -4142 -4177 0 - -4142 -4202 0 - -4151 -4157 0 - -4151 -4165 0 - -4151 -4172 0 - -4151 -4177 0 - -4151 -4202 0 - -4157 -4165 0 - -4157 -4172 0 - -4157 -4177 0 - -4157 -4202 0 - -4165 -4172 0 - -4165 -4177 0 - -4165 -4202 0 - -4172 -4177 0 - -4172 -4202 0 - -4177 -4202 0 - -4128 -4143 0 - -4128 -4173 0 - -4128 -4178 0 - -4128 -4187 0 - -4128 -4195 0 - -4128 -4203 0 - -4128 -4209 0 - -4143 -4173 0 - -4143 -4178 0 - -4143 -4187 0 - -4143 -4195 0 - -4143 -4203 0 - -4143 -4209 0 - -4173 -4178 0 - -4173 -4187 0 - -4173 -4195 0 - -4173 -4203 0 - -4173 -4209 0 - -4178 -4187 0 - -4178 -4195 0 - -4178 -4203 0 - -4178 -4209 0 - -4187 -4195 0 - -4187 -4203 0 - -4187 -4209 0 - -4195 -4203 0 - -4195 -4209 0 - -4203 -4209 0 - -4110 -4118 0 - -4110 -4152 0 - -4110 -4158 0 - -4110 -4166 0 - -4110 -4196 0 - -4110 -4204 0 - -4110 -4210 0 - -4118 -4152 0 - -4118 -4158 0 - -4118 -4166 0 - -4118 -4196 0 - -4118 -4204 0 - -4118 -4210 0 - -4152 -4158 0 - -4152 -4166 0 - -4152 -4196 0 - -4152 -4204 0 - -4152 -4210 0 - -4158 -4166 0 - -4158 -4196 0 - -4158 -4204 0 - -4158 -4210 0 - -4166 -4196 0 - -4166 -4204 0 - -4166 -4210 0 - -4196 -4204 0 - -4196 -4210 0 - -4204 -4210 0 - -4097 -4119 0 - -4097 -4129 0 - -4097 -4167 0 - -4097 -4179 0 - -4097 -4188 0 - -4097 -4211 0 - -4097 -4217 0 - -4119 -4129 0 - -4119 -4167 0 - -4119 -4179 0 - -4119 -4188 0 - -4119 -4211 0 - -4119 -4217 0 - -4129 -4167 0 - -4129 -4179 0 - -4129 -4188 0 - -4129 -4211 0 - -4129 -4217 0 - -4167 -4179 0 - -4167 -4188 0 - -4167 -4211 0 - -4167 -4217 0 - -4179 -4188 0 - -4179 -4211 0 - -4179 -4217 0 - -4188 -4211 0 - -4188 -4217 0 - -4211 -4217 0 - -4098 -4120 0 - -4098 -4130 0 - -4098 -4136 0 - -4098 -4189 0 - -4098 -4205 0 - -4120 -4130 0 - -4120 -4136 0 - -4120 -4189 0 - -4120 -4205 0 - -4130 -4136 0 - -4130 -4189 0 - -4130 -4205 0 - -4136 -4189 0 - -4136 -4205 0 - -4189 -4205 0 - -4099 -4111 0 - -4099 -4121 0 - -4099 -4180 0 - -4099 -4197 0 - -4099 -4212 0 - -4099 -4218 0 - -4111 -4121 0 - -4111 -4180 0 - -4111 -4197 0 - -4111 -4212 0 - -4111 -4218 0 - -4121 -4180 0 - -4121 -4197 0 - -4121 -4212 0 - -4121 -4218 0 - -4180 -4197 0 - -4180 -4212 0 - -4180 -4218 0 - -4197 -4212 0 - -4197 -4218 0 - -4212 -4218 0 - -4122 -4131 0 - -4122 -4159 0 - -4122 -4174 0 - -4122 -4181 0 - -4122 -4190 0 - -4131 -4159 0 - -4131 -4174 0 - -4131 -4181 0 - -4131 -4190 0 - -4159 -4174 0 - -4159 -4181 0 - -4159 -4190 0 - -4174 -4181 0 - -4174 -4190 0 - -4181 -4190 0 - -4100 -4137 0 - -4100 -4160 0 - -4100 -4206 0 - -4100 -4213 0 - -4137 -4160 0 - -4137 -4206 0 - -4137 -4213 0 - -4160 -4206 0 - -4160 -4213 0 - -4206 -4213 0 - -4101 -4112 0 - -4101 -4123 0 - -4101 -4132 0 - -4101 -4138 0 - -4101 -4144 0 - -4101 -4168 0 - -4101 -4182 0 - -4101 -4198 0 - -4101 -4214 0 - -4101 -4219 0 - -4112 -4123 0 - -4112 -4132 0 - -4112 -4138 0 - -4112 -4144 0 - -4112 -4168 0 - -4112 -4182 0 - -4112 -4198 0 - -4112 -4214 0 - -4112 -4219 0 - -4123 -4132 0 - -4123 -4138 0 - -4123 -4144 0 - -4123 -4168 0 - -4123 -4182 0 - -4123 -4198 0 - -4123 -4214 0 - -4123 -4219 0 - -4132 -4138 0 - -4132 -4144 0 - -4132 -4168 0 - -4132 -4182 0 - -4132 -4198 0 - -4132 -4214 0 - -4132 -4219 0 - -4138 -4144 0 - -4138 -4168 0 - -4138 -4182 0 - -4138 -4198 0 - -4138 -4214 0 - -4138 -4219 0 - -4144 -4168 0 - -4144 -4182 0 - -4144 -4198 0 - -4144 -4214 0 - -4144 -4219 0 - -4168 -4182 0 - -4168 -4198 0 - -4168 -4214 0 - -4168 -4219 0 - -4182 -4198 0 - -4182 -4214 0 - -4182 -4219 0 - -4198 -4214 0 - -4198 -4219 0 - -4214 -4219 0 - -4113 -4133 0 - -4113 -4139 0 - -4113 -4145 0 - -4113 -4153 0 - -4113 -4191 0 - -4113 -4207 0 - -4113 -4220 0 - -4133 -4139 0 - -4133 -4145 0 - -4133 -4153 0 - -4133 -4191 0 - -4133 -4207 0 - -4133 -4220 0 - -4139 -4145 0 - -4139 -4153 0 - -4139 -4191 0 - -4139 -4207 0 - -4139 -4220 0 - -4145 -4153 0 - -4145 -4191 0 - -4145 -4207 0 - -4145 -4220 0 - -4153 -4191 0 - -4153 -4207 0 - -4153 -4220 0 - -4191 -4207 0 - -4191 -4220 0 - -4207 -4220 0 - -4114 -4124 0 - -4114 -4146 0 - -4114 -4161 0 - -4114 -4175 0 - -4114 -4192 0 - -4114 -4215 0 - -4124 -4146 0 - -4124 -4161 0 - -4124 -4175 0 - -4124 -4192 0 - -4124 -4215 0 - -4146 -4161 0 - -4146 -4175 0 - -4146 -4192 0 - -4146 -4215 0 - -4161 -4175 0 - -4161 -4192 0 - -4161 -4215 0 - -4175 -4192 0 - -4175 -4215 0 - -4192 -4215 0 - -4102 -4107 0 - -4102 -4115 0 - -4102 -4125 0 - -4102 -4154 0 - -4102 -4169 0 - -4102 -4183 0 - -4102 -4193 0 - -4102 -4199 0 - -4107 -4115 0 - -4107 -4125 0 - -4107 -4154 0 - -4107 -4169 0 - -4107 -4183 0 - -4107 -4193 0 - -4107 -4199 0 - -4115 -4125 0 - -4115 -4154 0 - -4115 -4169 0 - -4115 -4183 0 - -4115 -4193 0 - -4115 -4199 0 - -4125 -4154 0 - -4125 -4169 0 - -4125 -4183 0 - -4125 -4193 0 - -4125 -4199 0 - -4154 -4169 0 - -4154 -4183 0 - -4154 -4193 0 - -4154 -4199 0 - -4169 -4183 0 - -4169 -4193 0 - -4169 -4199 0 - -4183 -4193 0 - -4183 -4199 0 - -4193 -4199 0 - -4103 -4116 0 - -4103 -4126 0 - -4103 -4170 0 - -4103 -4184 0 - -4103 -4221 0 - -4116 -4126 0 - -4116 -4170 0 - -4116 -4184 0 - -4116 -4221 0 - -4126 -4170 0 - -4126 -4184 0 - -4126 -4221 0 - -4170 -4184 0 - -4170 -4221 0 - -4184 -4221 0 - -4104 -4108 0 - -4104 -4117 0 - -4104 -4147 0 - -4104 -4176 0 - -4104 -4185 0 - -4104 -4222 0 - -4108 -4117 0 - -4108 -4147 0 - -4108 -4176 0 - -4108 -4185 0 - -4108 -4222 0 - -4117 -4147 0 - -4117 -4176 0 - -4117 -4185 0 - -4117 -4222 0 - -4147 -4176 0 - -4147 -4185 0 - -4147 -4222 0 - -4176 -4185 0 - -4176 -4222 0 - -4185 -4222 0 - -4105 -4127 0 - -4105 -4148 0 - -4105 -4155 0 - -4105 -4162 0 - -4105 -4171 0 - -4105 -4200 0 - -4127 -4148 0 - -4127 -4155 0 - -4127 -4162 0 - -4127 -4171 0 - -4127 -4200 0 - -4148 -4155 0 - -4148 -4162 0 - -4148 -4171 0 - -4148 -4200 0 - -4155 -4162 0 - -4155 -4171 0 - -4155 -4200 0 - -4162 -4171 0 - -4162 -4200 0 - -4171 -4200 0 - -4223 -4264 0 - -4223 -4276 0 - -4223 -4286 0 - -4223 -4303 0 - -4264 -4276 0 - -4264 -4286 0 - -4264 -4303 0 - -4276 -4286 0 - -4276 -4303 0 - -4286 -4303 0 - -4234 -4240 0 - -4234 -4257 0 - -4234 -4265 0 - -4234 -4272 0 - -4234 -4304 0 - -4240 -4257 0 - -4240 -4265 0 - -4240 -4272 0 - -4240 -4304 0 - -4257 -4265 0 - -4257 -4272 0 - -4257 -4304 0 - -4265 -4272 0 - -4265 -4304 0 - -4272 -4304 0 - -4227 -4241 0 - -4227 -4251 0 - -4227 -4277 0 - -4227 -4282 0 - -4227 -4287 0 - -4227 -4296 0 - -4241 -4251 0 - -4241 -4277 0 - -4241 -4282 0 - -4241 -4287 0 - -4241 -4296 0 - -4251 -4277 0 - -4251 -4282 0 - -4251 -4287 0 - -4251 -4296 0 - -4277 -4282 0 - -4277 -4287 0 - -4277 -4296 0 - -4282 -4287 0 - -4282 -4296 0 - -4287 -4296 0 - -4242 -4258 0 - -4242 -4266 0 - -4242 -4273 0 - -4242 -4283 0 - -4242 -4288 0 - -4242 -4294 0 - -4242 -4297 0 - -4242 -4305 0 - -4258 -4266 0 - -4258 -4273 0 - -4258 -4283 0 - -4258 -4288 0 - -4258 -4294 0 - -4258 -4297 0 - -4258 -4305 0 - -4266 -4273 0 - -4266 -4283 0 - -4266 -4288 0 - -4266 -4294 0 - -4266 -4297 0 - -4266 -4305 0 - -4273 -4283 0 - -4273 -4288 0 - -4273 -4294 0 - -4273 -4297 0 - -4273 -4305 0 - -4283 -4288 0 - -4283 -4294 0 - -4283 -4297 0 - -4283 -4305 0 - -4288 -4294 0 - -4288 -4297 0 - -4288 -4305 0 - -4294 -4297 0 - -4294 -4305 0 - -4297 -4305 0 - -4228 -4235 0 - -4228 -4243 0 - -4228 -4267 0 - -4228 -4274 0 - -4228 -4306 0 - -4235 -4243 0 - -4235 -4267 0 - -4235 -4274 0 - -4235 -4306 0 - -4243 -4267 0 - -4243 -4274 0 - -4243 -4306 0 - -4267 -4274 0 - -4267 -4306 0 - -4274 -4306 0 - -4229 -4244 0 - -4229 -4268 0 - -4229 -4295 0 - -4229 -4298 0 - -4229 -4307 0 - -4244 -4268 0 - -4244 -4295 0 - -4244 -4298 0 - -4244 -4307 0 - -4268 -4295 0 - -4268 -4298 0 - -4268 -4307 0 - -4295 -4298 0 - -4295 -4307 0 - -4298 -4307 0 - -4259 -4278 0 - -4259 -4289 0 - -4259 -4299 0 - -4278 -4289 0 - -4278 -4299 0 - -4289 -4299 0 - -4252 -4260 0 - -4252 -4269 0 - -4252 -4279 0 - -4252 -4300 0 - -4260 -4269 0 - -4260 -4279 0 - -4260 -4300 0 - -4269 -4279 0 - -4269 -4300 0 - -4279 -4300 0 - -4230 -4245 0 - -4230 -4253 0 - -4230 -4261 0 - -4230 -4275 0 - -4230 -4290 0 - -4245 -4253 0 - -4245 -4261 0 - -4245 -4275 0 - -4245 -4290 0 - -4253 -4261 0 - -4253 -4275 0 - -4253 -4290 0 - -4261 -4275 0 - -4261 -4290 0 - -4275 -4290 0 - -4224 -4246 0 - -4224 -4280 0 - -4246 -4280 0 - -4225 -4236 0 - -4225 -4247 0 - -4225 -4284 0 - -4225 -4291 0 - -4225 -4301 0 - -4236 -4247 0 - -4236 -4284 0 - -4236 -4291 0 - -4236 -4301 0 - -4247 -4284 0 - -4247 -4291 0 - -4247 -4301 0 - -4284 -4291 0 - -4284 -4301 0 - -4291 -4301 0 - -4231 -4237 0 - -4231 -4248 0 - -4231 -4254 0 - -4231 -4262 0 - -4231 -4285 0 - -4231 -4302 0 - -4237 -4248 0 - -4237 -4254 0 - -4237 -4262 0 - -4237 -4285 0 - -4237 -4302 0 - -4248 -4254 0 - -4248 -4262 0 - -4248 -4285 0 - -4248 -4302 0 - -4254 -4262 0 - -4254 -4285 0 - -4254 -4302 0 - -4262 -4285 0 - -4262 -4302 0 - -4285 -4302 0 - -4232 -4238 0 - -4232 -4249 0 - -4232 -4255 0 - -4232 -4263 0 - -4232 -4270 0 - -4232 -4292 0 - -4238 -4249 0 - -4238 -4255 0 - -4238 -4263 0 - -4238 -4270 0 - -4238 -4292 0 - -4249 -4255 0 - -4249 -4263 0 - -4249 -4270 0 - -4249 -4292 0 - -4255 -4263 0 - -4255 -4270 0 - -4255 -4292 0 - -4263 -4270 0 - -4263 -4292 0 - -4270 -4292 0 - -4226 -4233 0 - -4226 -4239 0 - -4226 -4250 0 - -4226 -4256 0 - -4226 -4271 0 - -4226 -4281 0 - -4226 -4293 0 - -4233 -4239 0 - -4233 -4250 0 - -4233 -4256 0 - -4233 -4271 0 - -4233 -4281 0 - -4233 -4293 0 - -4239 -4250 0 - -4239 -4256 0 - -4239 -4271 0 - -4239 -4281 0 - -4239 -4293 0 - -4250 -4256 0 - -4250 -4271 0 - -4250 -4281 0 - -4250 -4293 0 - -4256 -4271 0 - -4256 -4281 0 - -4256 -4293 0 - -4271 -4281 0 - -4271 -4293 0 - -4281 -4293 0 - -4315 -4337 0 - -4315 -4357 0 - -4315 -4366 0 - -4315 -4394 0 - -4315 -4408 0 - -4337 -4357 0 - -4337 -4366 0 - -4337 -4394 0 - -4337 -4408 0 - -4357 -4366 0 - -4357 -4394 0 - -4357 -4408 0 - -4366 -4394 0 - -4366 -4408 0 - -4394 -4408 0 - -4308 -4320 0 - -4308 -4327 0 - -4308 -4346 0 - -4308 -4372 0 - -4308 -4402 0 - -4320 -4327 0 - -4320 -4346 0 - -4320 -4372 0 - -4320 -4402 0 - -4327 -4346 0 - -4327 -4372 0 - -4327 -4402 0 - -4346 -4372 0 - -4346 -4402 0 - -4372 -4402 0 - -4309 -4321 0 - -4309 -4328 0 - -4309 -4338 0 - -4309 -4347 0 - -4309 -4367 0 - -4309 -4373 0 - -4309 -4381 0 - -4309 -4386 0 - -4309 -4395 0 - -4309 -4403 0 - -4321 -4328 0 - -4321 -4338 0 - -4321 -4347 0 - -4321 -4367 0 - -4321 -4373 0 - -4321 -4381 0 - -4321 -4386 0 - -4321 -4395 0 - -4321 -4403 0 - -4328 -4338 0 - -4328 -4347 0 - -4328 -4367 0 - -4328 -4373 0 - -4328 -4381 0 - -4328 -4386 0 - -4328 -4395 0 - -4328 -4403 0 - -4338 -4347 0 - -4338 -4367 0 - -4338 -4373 0 - -4338 -4381 0 - -4338 -4386 0 - -4338 -4395 0 - -4338 -4403 0 - -4347 -4367 0 - -4347 -4373 0 - -4347 -4381 0 - -4347 -4386 0 - -4347 -4395 0 - -4347 -4403 0 - -4367 -4373 0 - -4367 -4381 0 - -4367 -4386 0 - -4367 -4395 0 - -4367 -4403 0 - -4373 -4381 0 - -4373 -4386 0 - -4373 -4395 0 - -4373 -4403 0 - -4381 -4386 0 - -4381 -4395 0 - -4381 -4403 0 - -4386 -4395 0 - -4386 -4403 0 - -4395 -4403 0 - -4329 -4348 0 - -4329 -4352 0 - -4329 -4374 0 - -4329 -4382 0 - -4329 -4387 0 - -4329 -4396 0 - -4348 -4352 0 - -4348 -4374 0 - -4348 -4382 0 - -4348 -4387 0 - -4348 -4396 0 - -4352 -4374 0 - -4352 -4382 0 - -4352 -4387 0 - -4352 -4396 0 - -4374 -4382 0 - -4374 -4387 0 - -4374 -4396 0 - -4382 -4387 0 - -4382 -4396 0 - -4387 -4396 0 - -4316 -4339 0 - -4316 -4353 0 - -4316 -4375 0 - -4316 -4404 0 - -4316 -4409 0 - -4339 -4353 0 - -4339 -4375 0 - -4339 -4404 0 - -4339 -4409 0 - -4353 -4375 0 - -4353 -4404 0 - -4353 -4409 0 - -4375 -4404 0 - -4375 -4409 0 - -4404 -4409 0 - -4312 -4330 0 - -4312 -4349 0 - -4312 -4383 0 - -4312 -4388 0 - -4312 -4397 0 - -4312 -4405 0 - -4312 -4410 0 - -4330 -4349 0 - -4330 -4383 0 - -4330 -4388 0 - -4330 -4397 0 - -4330 -4405 0 - -4330 -4410 0 - -4349 -4383 0 - -4349 -4388 0 - -4349 -4397 0 - -4349 -4405 0 - -4349 -4410 0 - -4383 -4388 0 - -4383 -4397 0 - -4383 -4405 0 - -4383 -4410 0 - -4388 -4397 0 - -4388 -4405 0 - -4388 -4410 0 - -4397 -4405 0 - -4397 -4410 0 - -4405 -4410 0 - -4340 -4368 0 - -4340 -4376 0 - -4340 -4398 0 - -4368 -4376 0 - -4368 -4398 0 - -4376 -4398 0 - -4310 -4358 0 - -4310 -4389 0 - -4310 -4399 0 - -4310 -4411 0 - -4358 -4389 0 - -4358 -4399 0 - -4358 -4411 0 - -4389 -4399 0 - -4389 -4411 0 - -4399 -4411 0 - -4331 -4359 0 - -4331 -4400 0 - -4331 -4412 0 - -4359 -4400 0 - -4359 -4412 0 - -4400 -4412 0 - -4341 -4350 0 - -4341 -4354 0 - -4341 -4360 0 - -4341 -4377 0 - -4341 -4390 0 - -4341 -4401 0 - -4350 -4354 0 - -4350 -4360 0 - -4350 -4377 0 - -4350 -4390 0 - -4350 -4401 0 - -4354 -4360 0 - -4354 -4377 0 - -4354 -4390 0 - -4354 -4401 0 - -4360 -4377 0 - -4360 -4390 0 - -4360 -4401 0 - -4377 -4390 0 - -4377 -4401 0 - -4390 -4401 0 - -4313 -4322 0 - -4313 -4332 0 - -4313 -4342 0 - -4313 -4361 0 - -4313 -4369 0 - -4313 -4378 0 - -4313 -4406 0 - -4313 -4413 0 - -4322 -4332 0 - -4322 -4342 0 - -4322 -4361 0 - -4322 -4369 0 - -4322 -4378 0 - -4322 -4406 0 - -4322 -4413 0 - -4332 -4342 0 - -4332 -4361 0 - -4332 -4369 0 - -4332 -4378 0 - -4332 -4406 0 - -4332 -4413 0 - -4342 -4361 0 - -4342 -4369 0 - -4342 -4378 0 - -4342 -4406 0 - -4342 -4413 0 - -4361 -4369 0 - -4361 -4378 0 - -4361 -4406 0 - -4361 -4413 0 - -4369 -4378 0 - -4369 -4406 0 - -4369 -4413 0 - -4378 -4406 0 - -4378 -4413 0 - -4406 -4413 0 - -4314 -4317 0 - -4314 -4323 0 - -4314 -4333 0 - -4314 -4351 0 - -4314 -4355 0 - -4314 -4362 0 - -4314 -4370 0 - -4314 -4379 0 - -4314 -4407 0 - -4317 -4323 0 - -4317 -4333 0 - -4317 -4351 0 - -4317 -4355 0 - -4317 -4362 0 - -4317 -4370 0 - -4317 -4379 0 - -4317 -4407 0 - -4323 -4333 0 - -4323 -4351 0 - -4323 -4355 0 - -4323 -4362 0 - -4323 -4370 0 - -4323 -4379 0 - -4323 -4407 0 - -4333 -4351 0 - -4333 -4355 0 - -4333 -4362 0 - -4333 -4370 0 - -4333 -4379 0 - -4333 -4407 0 - -4351 -4355 0 - -4351 -4362 0 - -4351 -4370 0 - -4351 -4379 0 - -4351 -4407 0 - -4355 -4362 0 - -4355 -4370 0 - -4355 -4379 0 - -4355 -4407 0 - -4362 -4370 0 - -4362 -4379 0 - -4362 -4407 0 - -4370 -4379 0 - -4370 -4407 0 - -4379 -4407 0 - -4318 -4324 0 - -4318 -4334 0 - -4318 -4343 0 - -4318 -4356 0 - -4318 -4363 0 - -4318 -4384 0 - -4318 -4391 0 - -4324 -4334 0 - -4324 -4343 0 - -4324 -4356 0 - -4324 -4363 0 - -4324 -4384 0 - -4324 -4391 0 - -4334 -4343 0 - -4334 -4356 0 - -4334 -4363 0 - -4334 -4384 0 - -4334 -4391 0 - -4343 -4356 0 - -4343 -4363 0 - -4343 -4384 0 - -4343 -4391 0 - -4356 -4363 0 - -4356 -4384 0 - -4356 -4391 0 - -4363 -4384 0 - -4363 -4391 0 - -4384 -4391 0 - -4319 -4325 0 - -4319 -4344 0 - -4319 -4364 0 - -4319 -4380 0 - -4319 -4392 0 - -4325 -4344 0 - -4325 -4364 0 - -4325 -4380 0 - -4325 -4392 0 - -4344 -4364 0 - -4344 -4380 0 - -4344 -4392 0 - -4364 -4380 0 - -4364 -4392 0 - -4380 -4392 0 - -4335 -4345 0 - -4335 -4365 0 - -4335 -4371 0 - -4335 -4385 0 - -4335 -4414 0 - -4345 -4365 0 - -4345 -4371 0 - -4345 -4385 0 - -4345 -4414 0 - -4365 -4371 0 - -4365 -4385 0 - -4365 -4414 0 - -4371 -4385 0 - -4371 -4414 0 - -4385 -4414 0 - -4311 -4326 0 - -4311 -4336 0 - -4311 -4393 0 - -4326 -4336 0 - -4326 -4393 0 - -4336 -4393 0 - 1 2 3 4 5 6 7 8 9 10 11 0 - 12 13 14 15 16 17 0 - 18 19 20 21 22 23 0 - 24 25 26 27 28 29 30 31 32 0 - 33 34 35 36 37 38 0 - 39 40 41 42 43 44 45 0 - 46 47 48 49 50 51 52 53 0 - 54 55 56 57 58 0 - 59 60 61 62 63 64 0 - 65 66 67 68 0 - 69 70 71 72 73 74 75 76 0 - 77 78 79 80 0 - 81 82 83 84 85 86 87 88 89 0 - 90 91 92 93 94 0 - 95 96 97 98 99 100 101 102 103 104 105 106 0 - 107 108 109 110 111 112 113 114 115 116 117 0 - 118 119 0 - 120 121 122 123 124 125 126 127 0 - 128 129 130 131 132 133 134 135 136 0 - 137 138 139 140 141 142 0 - 143 144 145 146 147 148 149 150 151 152 0 - 153 154 155 156 157 158 159 0 - 160 161 162 163 164 165 166 0 - 167 168 169 170 171 172 173 174 0 - 175 176 177 178 179 0 - 180 181 182 183 184 185 186 187 188 189 0 - 190 191 192 193 194 195 196 197 0 - 198 199 200 201 202 203 204 0 - 205 206 207 208 209 210 0 - 211 212 213 214 215 216 217 218 0 - 219 220 221 222 223 224 225 226 227 0 - 228 229 230 231 232 233 234 235 236 0 - 237 238 239 240 241 242 243 244 245 246 0 - 247 248 249 250 251 252 0 - 253 254 255 256 257 258 0 - 259 260 261 262 263 264 265 0 - 266 267 268 269 270 271 272 273 0 - 274 275 276 277 278 279 280 281 282 0 - 283 284 285 286 287 0 - 288 289 290 291 0 - 292 293 294 295 296 297 298 299 0 - 300 301 302 303 304 305 306 0 - 307 308 309 310 311 312 0 - 313 314 315 316 317 318 319 320 321 322 0 - 323 324 325 326 327 0 - 328 329 330 331 332 333 334 335 0 - 336 337 338 339 340 341 342 343 0 - 344 345 346 347 348 349 350 351 352 0 - 353 354 355 356 357 358 359 0 - 360 361 362 363 364 365 0 - 366 367 368 369 370 0 - 371 372 373 374 375 376 377 378 379 380 381 0 - 382 383 384 385 0 - 386 387 388 389 390 391 0 - 392 393 394 395 396 0 - 397 398 399 400 401 0 - 402 403 404 405 406 407 408 0 - 409 410 411 412 413 414 415 0 - 416 417 418 419 420 421 422 0 - 423 424 425 426 427 428 429 0 - 430 431 432 433 434 435 0 - 436 437 438 439 440 0 - 441 442 443 444 445 446 0 - 447 448 449 450 451 452 453 454 455 456 457 0 - 458 459 460 461 0 - 462 463 464 465 466 467 468 469 470 471 0 - 472 473 474 475 476 477 0 - 478 479 0 - 480 481 482 483 484 485 486 487 488 489 0 - 490 491 492 493 494 495 496 0 - 497 498 499 500 501 502 503 504 0 - 505 506 507 508 509 510 511 512 0 - 513 514 515 516 517 518 0 - 519 520 521 522 523 524 525 526 527 528 529 0 - 530 531 532 533 534 535 536 537 538 539 540 0 - 541 542 543 544 545 546 547 548 549 550 551 0 - 552 553 554 555 556 557 0 - 558 559 560 561 562 563 564 565 566 0 - 567 568 569 570 571 0 - 572 573 574 575 576 577 578 579 580 0 - 581 582 583 0 - 584 585 586 587 588 589 590 591 0 - 592 593 594 595 596 597 598 0 - 599 600 601 602 603 604 0 - 605 606 607 608 609 610 611 612 0 - 613 614 615 616 617 618 619 620 621 0 - 622 623 624 625 626 627 628 629 0 - 630 631 632 633 634 0 - 635 636 637 638 639 640 641 642 643 644 0 - 645 646 647 648 649 650 0 - 651 652 653 654 655 656 657 658 0 - 659 660 661 662 663 664 665 666 667 0 - 668 669 670 671 672 673 674 675 0 - 676 677 678 679 680 681 682 683 684 685 0 - 686 687 688 689 690 0 - 691 692 693 694 0 - 695 696 697 698 699 700 701 702 703 704 705 0 - 706 707 708 709 710 711 0 - 712 713 714 715 716 717 718 0 - 719 720 721 722 723 0 - 724 725 726 727 728 729 0 - 730 731 732 733 734 735 736 737 738 739 0 - 740 741 742 743 744 745 746 747 0 - 748 749 750 751 752 753 754 0 - 755 756 757 758 759 0 - 760 761 762 763 764 765 766 767 0 - 768 769 770 771 772 773 774 775 776 777 0 - 778 779 780 781 782 783 0 - 784 785 786 787 788 789 790 791 792 0 - 793 794 795 796 797 798 799 800 801 802 803 0 - 804 805 806 807 808 809 810 811 812 813 0 - 814 815 816 817 818 819 820 821 822 0 - 823 824 825 826 827 828 829 830 0 - 831 832 833 834 835 836 0 - 837 838 839 840 841 0 - 842 843 844 845 846 847 848 849 0 - 850 851 852 853 854 855 856 857 858 0 - 859 860 861 862 863 864 0 - 865 866 867 868 869 870 871 872 873 0 - 874 875 876 0 - 877 878 879 880 881 882 0 - 883 884 885 886 887 888 889 0 - 890 891 892 893 894 0 - 895 896 897 898 899 0 - 900 901 902 0 - 903 904 905 906 907 908 909 910 911 0 - 912 913 914 915 916 917 918 0 - 919 920 921 922 923 0 - 924 925 926 927 928 929 930 931 932 933 934 0 - 935 936 937 938 939 940 941 942 0 - 943 944 945 946 947 948 949 950 0 - 951 952 953 954 955 956 957 0 - 958 959 960 961 962 963 964 965 0 - 966 967 968 969 970 971 972 973 0 - 974 975 976 977 978 979 980 981 982 0 - 983 984 985 986 987 988 989 990 0 - 991 992 993 994 995 996 997 0 - 998 999 1000 1001 1002 1003 0 - 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 0 - 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 0 - 1025 1026 1027 1028 1029 1030 1031 0 - 1032 1033 1034 1035 1036 1037 1038 1039 0 - 1040 1041 1042 1043 1044 1045 0 - 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 0 - 1059 1060 1061 1062 1063 1064 1065 0 - 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 0 - 1080 1081 1082 1083 1084 1085 1086 1087 1088 0 - 1089 1090 1091 1092 1093 1094 0 - 1095 1096 1097 1098 1099 1100 1101 1102 1103 0 - 1104 1105 1106 1107 1108 1109 0 - 1110 1111 1112 1113 1114 1115 1116 1117 1118 0 - 1119 1120 1121 1122 1123 1124 0 - 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 0 - 1136 1137 1138 1139 1140 1141 1142 0 - 1143 1144 1145 1146 1147 1148 1149 1150 0 - 1151 1152 1153 1154 1155 1156 0 - 1157 1158 1159 1160 1161 1162 1163 1164 1165 0 - 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 0 - 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 0 - 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 0 - 1197 1198 1199 1200 1201 0 - 1202 1203 1204 1205 1206 0 - 1207 1208 1209 1210 1211 0 - 1212 1213 1214 1215 0 - 1216 1217 1218 1219 1220 1221 0 - 1222 1223 1224 1225 1226 1227 0 - 1228 1229 1230 1231 1232 1233 1234 1235 0 - 1236 1237 1238 1239 1240 1241 1242 1243 0 - 1244 1245 1246 1247 1248 0 - 1249 1250 1251 1252 1253 0 - 1254 1255 1256 1257 1258 1259 1260 1261 0 - 1262 1263 1264 1265 1266 1267 1268 0 - 1269 1270 1271 1272 1273 0 - 1274 1275 1276 1277 1278 1279 1280 1281 0 - 1282 1283 1284 1285 1286 1287 1288 0 - 1289 1290 1291 1292 1293 1294 1295 0 - 1296 1297 1298 1299 1300 1301 0 - 1302 1303 0 - 1304 1305 1306 1307 1308 1309 1310 0 - 1311 1312 1313 0 - 1314 1315 1316 1317 1318 1319 0 - 1320 1321 1322 1323 1324 1325 0 - 1326 1327 1328 1329 1330 0 - 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 0 - 1341 1342 1343 0 - 1344 1345 1346 1347 1348 0 - 1349 1350 1351 1352 1353 1354 0 - 1355 1356 1357 1358 1359 0 - 1360 1361 1362 0 - 1363 1364 1365 1366 1367 1368 1369 0 - 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 0 - 1383 1384 1385 1386 1387 1388 1389 1390 0 - 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 0 - 1402 1403 1404 1405 1406 1407 1408 1409 0 - 1410 1411 1412 0 - 1413 1414 1415 1416 1417 1418 1419 1420 1421 0 - 1422 1423 1424 1425 0 - 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 0 - 1437 1438 1439 1440 1441 1442 0 - 1443 1444 1445 1446 1447 1448 1449 1450 1451 0 - 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 0 - 1465 1466 1467 1468 1469 1470 1471 1472 1473 0 - 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 0 - 1484 1485 1486 1487 1488 1489 1490 1491 1492 0 - 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 0 - 1504 1505 1506 1507 1508 1509 1510 1511 1512 0 - 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 0 - 1523 1524 1525 1526 1527 1528 1529 1530 1531 0 - 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 0 - 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 0 - 1555 1556 1557 1558 1559 1560 1561 1562 0 - 1563 1564 1565 1566 1567 1568 0 - 1569 1570 1571 1572 1573 1574 0 - 1575 1576 0 - 1577 1578 1579 1580 1581 0 - 1582 1583 1584 1585 1586 1587 0 - 1588 1589 0 - 1590 1591 1592 1593 1594 0 - 1595 1596 1597 1598 1599 1600 0 - 1601 1602 1603 1604 1605 0 - 1606 1607 1608 1609 1610 1611 1612 0 - 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 0 - 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 0 - 1635 1636 1637 1638 1639 1640 1641 0 - 1642 1643 1644 1645 1646 1647 0 - 1648 1649 1650 1651 0 - 1652 1653 1654 1655 1656 1657 1658 1659 0 - 1660 1661 1662 1663 1664 1665 0 - 1666 1667 1668 1669 1670 1671 1672 1673 0 - 1674 1675 1676 1677 1678 1679 1680 1681 0 - 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 0 - 1693 1694 1695 1696 1697 1698 0 - 1699 1700 1701 1702 1703 1704 1705 1706 1707 0 - 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 0 - 1722 1723 1724 1725 1726 1727 0 - 1728 1729 1730 1731 1732 1733 0 - 1734 1735 1736 1737 1738 1739 0 - 1740 1741 1742 1743 1744 1745 1746 1747 0 - 1748 1749 1750 1751 1752 0 - 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 0 - 1764 1765 1766 1767 1768 1769 1770 1771 0 - 1772 1773 1774 1775 1776 1777 1778 1779 1780 0 - 1781 1782 1783 1784 1785 1786 1787 1788 0 - 1789 1790 0 - 1791 1792 1793 1794 1795 1796 1797 1798 0 - 1799 1800 1801 1802 1803 1804 1805 1806 1807 0 - 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 0 - 1818 1819 1820 1821 1822 1823 1824 1825 1826 0 - 1827 1828 1829 1830 0 - 1831 1832 1833 1834 1835 1836 1837 1838 0 - 1839 1840 1841 1842 1843 1844 0 - 1845 1846 1847 1848 1849 1850 1851 0 - 1852 1853 1854 1855 1856 0 - 1857 1858 1859 1860 0 - 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 0 - 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 0 - 1882 1883 1884 1885 1886 1887 1888 0 - 1889 1890 1891 1892 1893 1894 1895 1896 0 - 1897 1898 1899 1900 1901 0 - 1902 1903 1904 1905 1906 1907 1908 1909 0 - 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 0 - 1921 1922 1923 1924 1925 1926 1927 1928 0 - 1929 1930 1931 1932 1933 1934 1935 0 - 1936 1937 1938 1939 1940 1941 1942 0 - 1943 1944 1945 1946 1947 1948 1949 1950 1951 0 - 1952 1953 1954 1955 1956 1957 1958 1959 1960 0 - 1961 1962 1963 1964 1965 1966 1967 1968 1969 0 - 1970 1971 1972 1973 1974 1975 1976 0 - 1977 1978 1979 1980 1981 1982 0 - 1983 1984 1985 1986 1987 1988 1989 1990 0 - 1991 1992 1993 1994 1995 1996 0 - 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 0 - 2007 2008 2009 2010 2011 2012 2013 2014 0 - 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 0 - 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 0 - 2039 2040 2041 2042 2043 0 - 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 0 - 2055 2056 2057 2058 2059 2060 2061 0 - 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 0 - 2074 2075 2076 2077 2078 2079 2080 2081 2082 0 - 2083 2084 2085 2086 2087 0 - 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 0 - 2098 2099 2100 2101 2102 2103 2104 2105 2106 0 - 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 0 - 2118 2119 2120 2121 2122 2123 2124 0 - 2125 2126 2127 2128 2129 2130 2131 0 - 2132 2133 2134 2135 2136 2137 2138 2139 0 - 2140 2141 2142 2143 2144 2145 0 - 2146 2147 2148 2149 2150 2151 2152 2153 0 - 2154 2155 2156 2157 2158 2159 2160 2161 0 - 2162 2163 2164 2165 2166 2167 0 - 2168 2169 2170 2171 2172 2173 2174 2175 2176 0 - 2177 2178 2179 2180 2181 2182 2183 2184 2185 0 - 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 0 - 2197 2198 2199 2200 2201 2202 0 - 2203 2204 2205 2206 2207 2208 0 - 2209 2210 2211 2212 0 - 2213 2214 2215 2216 2217 2218 2219 0 - 2220 2221 2222 2223 2224 2225 0 - 2226 2227 2228 0 - 2229 2230 2231 2232 2233 2234 2235 2236 0 - 2237 2238 2239 2240 2241 0 - 2242 2243 2244 2245 2246 2247 2248 2249 0 - 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 0 - 2260 2261 2262 2263 2264 2265 0 - 2266 2267 2268 2269 2270 2271 2272 0 - 2273 2274 2275 2276 2277 0 - 2278 2279 2280 2281 2282 0 - 2283 2284 2285 2286 2287 2288 0 - 2289 2290 2291 2292 2293 0 - 2294 2295 2296 2297 2298 0 - 2299 2300 2301 0 - 2302 2303 2304 2305 2306 0 - 2307 2308 2309 2310 2311 2312 2313 0 - 2314 2315 2316 2317 2318 0 - 2319 2320 2321 2322 0 - 2323 2324 2325 2326 2327 2328 2329 0 - 2330 2331 2332 2333 0 - 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 0 - 2344 2345 2346 2347 2348 2349 2350 0 - 2351 2352 2353 2354 2355 0 - 2356 2357 2358 2359 0 - 2360 2361 2362 2363 2364 2365 0 - 2366 2367 2368 0 - 2369 2370 2371 2372 2373 2374 2375 2376 0 - 2377 2378 2379 2380 0 - 2381 2382 2383 2384 2385 2386 2387 0 - 2388 2389 2390 2391 0 - 2392 2393 2394 2395 2396 2397 2398 0 - 2399 2400 2401 2402 2403 2404 0 - 2405 2406 2407 2408 2409 2410 0 - 2411 2412 2413 2414 2415 2416 2417 0 - 2418 2419 2420 2421 2422 2423 0 - 2424 2425 2426 2427 2428 2429 0 - 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 0 - 2441 2442 2443 2444 2445 2446 0 - 2447 2448 2449 2450 2451 0 - 2452 2453 2454 2455 2456 2457 2458 2459 2460 0 - 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 0 - 2472 2473 2474 2475 2476 2477 2478 2479 0 - 2480 2481 2482 2483 2484 2485 2486 0 - 2487 2488 2489 2490 2491 2492 2493 0 - 2494 2495 2496 2497 2498 2499 0 - 2500 2501 2502 2503 0 - 2504 2505 2506 2507 2508 2509 0 - 2510 2511 2512 2513 2514 2515 2516 2517 0 - 2518 2519 2520 2521 2522 2523 0 - 2524 2525 2526 2527 2528 2529 2530 2531 0 - 2532 2533 2534 2535 2536 2537 2538 0 - 2539 2540 2541 2542 2543 2544 0 - 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 0 - 2555 2556 2557 2558 2559 2560 0 - 2561 2562 2563 2564 0 - 2565 2566 2567 2568 2569 0 - 2570 2571 0 - 2572 2573 2574 2575 2576 2577 2578 2579 2580 0 - 2581 2582 2583 2584 2585 2586 0 - 2587 2588 2589 2590 2591 2592 2593 0 - 2594 2595 2596 2597 2598 2599 0 - 2600 2601 2602 2603 2604 2605 0 - 2606 2607 2608 2609 2610 2611 0 - 2612 2613 2614 2615 2616 2617 2618 0 - 2619 2620 2621 2622 0 - 2623 2624 2625 0 - 2626 2627 2628 2629 2630 2631 0 - 2632 2633 2634 2635 0 - 2636 2637 2638 2639 2640 2641 0 - 2642 2643 2644 2645 2646 2647 2648 2649 0 - 2650 2651 2652 2653 2654 2655 2656 0 - 2657 2658 2659 2660 0 - 2661 2662 2663 2664 0 - 2665 2666 2667 2668 2669 2670 0 - 2671 2672 2673 0 - 2674 2675 2676 2677 2678 2679 2680 0 - 2681 2682 2683 2684 0 - 2685 2686 2687 2688 2689 2690 2691 2692 0 - 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 0 - 2703 2704 2705 2706 2707 2708 2709 2710 0 - 2711 2712 2713 2714 2715 2716 2717 2718 2719 0 - 2720 2721 2722 2723 2724 0 - 2725 2726 2727 2728 0 - 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 0 - 2739 2740 2741 2742 2743 2744 0 - 2745 2746 2747 2748 2749 2750 0 - 2751 2752 2753 2754 2755 0 - 2756 2757 2758 2759 2760 2761 2762 0 - 2763 2764 2765 2766 2767 2768 0 - 2769 2770 2771 2772 2773 2774 0 - 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 0 - 2785 2786 2787 2788 2789 2790 2791 2792 0 - 2793 2794 2795 2796 2797 2798 0 - 2799 2800 2801 2802 2803 2804 0 - 2805 2806 2807 2808 2809 0 - 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 0 - 2820 2821 2822 2823 2824 2825 2826 2827 0 - 2828 2829 2830 2831 2832 2833 2834 0 - 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 0 - 2846 2847 2848 2849 2850 2851 2852 0 - 2853 2854 2855 2856 0 - 2857 2858 2859 2860 2861 2862 2863 0 - 2864 2865 2866 2867 2868 2869 0 - 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 0 - 2880 2881 2882 2883 2884 2885 2886 2887 0 - 2888 2889 2890 2891 2892 0 - 2893 2894 2895 2896 2897 2898 2899 0 - 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 0 - 2910 2911 2912 2913 2914 2915 2916 0 - 2917 2918 2919 2920 2921 2922 2923 2924 0 - 2925 2926 2927 2928 2929 2930 2931 2932 2933 0 - 2934 2935 2936 0 - 2937 2938 2939 2940 2941 2942 0 - 2943 2944 2945 2946 2947 0 - 2948 2949 2950 2951 2952 2953 2954 0 - 2955 2956 2957 2958 2959 2960 2961 0 - 2962 2963 2964 2965 2966 0 - 2967 2968 2969 2970 2971 0 - 2972 2973 2974 2975 2976 2977 2978 2979 0 - 2980 2981 2982 2983 2984 2985 0 - 2986 2987 2988 2989 2990 2991 0 - 2992 2993 2994 2995 2996 2997 0 - 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 0 - 3008 3009 3010 3011 3012 3013 3014 3015 3016 0 - 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 0 - 3033 3034 3035 3036 3037 3038 3039 3040 0 - 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 0 - 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 0 - 3064 3065 3066 3067 3068 3069 0 - 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 0 - 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 0 - 3093 3094 3095 3096 3097 3098 3099 3100 0 - 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 0 - 3112 3113 3114 3115 3116 3117 3118 3119 0 - 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 0 - 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 0 - 3144 3145 3146 3147 3148 3149 3150 3151 0 - 3152 3153 3154 3155 3156 3157 3158 0 - 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 0 - 3169 3170 3171 3172 3173 3174 3175 0 - 3176 3177 3178 3179 3180 3181 3182 3183 3184 0 - 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 0 - 3197 3198 3199 3200 3201 3202 3203 3204 3205 0 - 3206 3207 3208 3209 3210 3211 0 - 3212 3213 3214 3215 3216 3217 3218 3219 3220 0 - 3221 3222 3223 3224 3225 3226 3227 0 - 3228 3229 3230 3231 0 - 3232 3233 3234 3235 3236 0 - 3237 3238 3239 3240 3241 3242 0 - 3243 3244 3245 3246 3247 3248 3249 0 - 3250 3251 3252 3253 3254 3255 0 - 3256 3257 3258 3259 3260 0 - 3261 3262 3263 3264 3265 3266 3267 3268 0 - 3269 3270 3271 3272 3273 0 - 3274 3275 3276 3277 3278 3279 0 - 3280 3281 3282 3283 3284 3285 3286 3287 0 - 3288 3289 3290 3291 3292 3293 3294 3295 3296 0 - 3297 3298 3299 0 - 3300 3301 3302 3303 3304 3305 3306 0 - 3307 3308 3309 3310 3311 3312 3313 0 - 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 0 - 3327 3328 3329 3330 3331 3332 3333 3334 3335 0 - 3336 3337 3338 3339 3340 3341 0 - 3342 3343 3344 0 - 3345 3346 3347 3348 3349 3350 3351 3352 3353 0 - 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 0 - 3365 3366 3367 3368 3369 3370 3371 3372 0 - 3373 3374 3375 3376 3377 3378 3379 3380 0 - 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 0 - 3392 3393 3394 3395 3396 3397 3398 3399 0 - 3400 3401 3402 3403 0 - 3404 3405 3406 3407 3408 3409 3410 3411 0 - 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 0 - 3422 3423 3424 3425 3426 3427 3428 3429 3430 0 - 3431 3432 3433 3434 3435 3436 3437 3438 3439 0 - 3440 3441 3442 3443 3444 3445 3446 0 - 3447 3448 3449 3450 3451 3452 3453 0 - 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 0 - 3464 3465 3466 3467 3468 0 - 3469 3470 3471 3472 3473 3474 3475 3476 3477 0 - 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 0 - 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 0 - 3500 3501 3502 3503 3504 3505 0 - 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 0 - 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 0 - 3528 3529 3530 3531 3532 3533 0 - 3534 3535 3536 3537 3538 3539 3540 0 - 3541 3542 3543 3544 3545 3546 0 - 3547 3548 3549 3550 3551 3552 3553 3554 3555 0 - 3556 3557 3558 3559 3560 0 - 3561 3562 3563 3564 3565 0 - 3566 3567 3568 3569 3570 3571 3572 3573 3574 0 - 3575 3576 3577 3578 3579 3580 3581 3582 0 - 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 0 - 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 0 - 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 0 - 3613 3614 3615 3616 3617 3618 3619 0 - 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 0 - 3631 3632 3633 3634 3635 3636 3637 3638 0 - 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 0 - 3651 3652 3653 3654 0 - 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 0 - 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 0 - 3679 3680 3681 3682 3683 3684 0 - 3685 3686 3687 3688 3689 3690 3691 0 - 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 0 - 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 0 - 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 0 - 3723 3724 3725 3726 3727 3728 3729 3730 0 - 3731 3732 3733 3734 3735 3736 3737 0 - 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 0 - 3749 3750 3751 3752 3753 3754 3755 3756 3757 0 - 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 0 - 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 0 - 3779 3780 3781 3782 3783 3784 3785 0 - 3786 3787 3788 3789 3790 3791 3792 3793 0 - 3794 3795 3796 3797 3798 3799 3800 3801 0 - 3802 3803 3804 3805 3806 3807 3808 0 - 3809 3810 3811 3812 3813 3814 3815 3816 3817 0 - 3818 3819 3820 3821 3822 3823 0 - 3824 3825 3826 3827 3828 3829 0 - 3830 3831 3832 3833 3834 3835 3836 3837 3838 0 - 3839 3840 3841 3842 3843 3844 0 - 3845 3846 3847 3848 3849 3850 3851 3852 0 - 3853 3854 3855 3856 3857 3858 0 - 3859 3860 3861 0 - 3862 3863 3864 3865 3866 3867 3868 3869 3870 0 - 3871 3872 3873 3874 3875 3876 3877 0 - 3878 3879 3880 3881 3882 3883 3884 0 - 3885 3886 3887 3888 3889 3890 0 - 3891 3892 3893 3894 3895 3896 3897 3898 0 - 3899 3900 3901 3902 3903 3904 0 - 3905 3906 3907 3908 3909 3910 0 - 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 0 - 3921 3922 3923 3924 0 - 3925 3926 3927 3928 3929 3930 3931 3932 3933 0 - 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 0 - 3944 3945 3946 3947 0 - 3948 3949 3950 3951 3952 3953 3954 0 - 3955 3956 3957 3958 3959 3960 3961 3962 3963 0 - 3964 3965 3966 3967 3968 3969 0 - 3970 3971 3972 3973 3974 3975 3976 0 - 3977 3978 3979 3980 3981 3982 3983 3984 3985 0 - 3986 3987 3988 3989 3990 3991 3992 3993 0 - 3994 3995 3996 3997 3998 3999 4000 4001 0 - 4002 4003 4004 4005 4006 4007 0 - 4008 4009 4010 4011 4012 4013 4014 0 - 4015 4016 4017 4018 4019 4020 4021 4022 0 - 4023 4024 4025 4026 4027 4028 0 - 4029 4030 4031 4032 4033 4034 0 - 4035 4036 4037 0 - 4038 4039 4040 4041 0 - 4042 4043 4044 4045 0 - 4046 4047 4048 4049 4050 4051 0 - 4052 4053 4054 4055 4056 0 - 4057 4058 4059 4060 4061 0 - 4062 4063 4064 0 - 4065 4066 4067 4068 4069 0 - 4070 4071 4072 4073 4074 0 - 4075 4076 4077 4078 4079 4080 4081 4082 0 - 4083 4084 4085 4086 4087 4088 4089 4090 4091 0 - 4092 4093 4094 0 - 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 0 - 4106 4107 4108 0 - 4109 4110 4111 4112 4113 4114 4115 4116 4117 0 - 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 0 - 4128 4129 4130 4131 4132 4133 0 - 4134 4135 4136 4137 4138 4139 0 - 4140 4141 4142 4143 4144 4145 4146 4147 4148 0 - 4149 4150 4151 4152 4153 4154 4155 0 - 4156 4157 4158 4159 4160 4161 4162 0 - 4163 4164 4165 4166 4167 4168 4169 4170 4171 0 - 4172 4173 4174 4175 4176 0 - 4177 4178 4179 4180 4181 4182 4183 4184 4185 0 - 4186 4187 4188 4189 4190 4191 4192 4193 0 - 4194 4195 4196 4197 4198 4199 4200 0 - 4201 4202 4203 4204 4205 4206 4207 0 - 4208 4209 4210 4211 4212 4213 4214 4215 0 - 4216 4217 4218 4219 4220 4221 4222 0 - 4223 4224 4225 4226 0 - 4227 4228 4229 4230 4231 4232 4233 0 - 4234 4235 4236 4237 4238 4239 0 - 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 0 - 4251 4252 4253 4254 4255 4256 0 - 4257 4258 4259 4260 4261 4262 4263 0 - 4264 4265 4266 4267 4268 4269 4270 4271 0 - 4272 4273 4274 4275 0 - 4276 4277 4278 4279 4280 4281 0 - 4282 4283 4284 4285 0 - 4286 4287 4288 4289 4290 4291 4292 4293 0 - 4294 4295 0 - 4296 4297 4298 4299 4300 4301 4302 0 - 4303 4304 4305 4306 4307 0 - 4308 4309 4310 4311 0 - 4312 4313 4314 0 - 4315 4316 4317 4318 4319 0 - 4320 4321 4322 4323 4324 4325 4326 0 - 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 0 - 4337 4338 4339 4340 4341 4342 4343 4344 4345 0 - 4346 4347 4348 4349 4350 4351 0 - 4352 4353 4354 4355 4356 0 - 4357 4358 4359 4360 4361 4362 4363 4364 4365 0 - 4366 4367 4368 4369 4370 4371 0 - 4372 4373 4374 4375 4376 4377 4378 4379 4380 0 - 4381 4382 4383 4384 4385 0 - 4386 4387 4388 4389 4390 4391 4392 4393 0 - 4394 4395 4396 4397 4398 4399 4400 4401 0 - 4402 4403 4404 4405 4406 4407 0 - 4408 4409 4410 4411 4412 4413 4414 0 - 831 903 1606 4223 4308 0 - 441 832 904 1363 1607 2007 2162 2799 3300 3613 0 - 905 1364 2008 3301 4309 0 - 442 906 1365 1608 2009 2800 3614 0 - 443 833 907 1609 2163 3615 0 - 444 908 2010 2164 3302 3616 0 - 445 834 1207 1366 1610 2165 3303 3617 0 - 909 1611 2011 2801 3304 0 - 1208 1367 2012 2802 4310 0 - 835 1209 2803 3305 3618 4224 0 - 446 1368 1612 2166 3619 4225 0 - 910 1210 2013 2167 2804 0 - 836 911 1211 1369 2014 3306 4226 4311 0 - 912 1748 2168 2424 2805 3620 3802 0 - 913 1749 2685 2806 2917 3008 3621 3803 3905 0 - 1212 1750 2169 2425 2686 3009 3906 0 - 914 1751 2170 2918 3010 3622 3907 4023 0 - 2171 2426 2807 2919 3623 4024 0 - 691 915 2172 3624 4025 0 - 1289 2687 3011 3625 3804 4312 0 - 1213 2173 3626 3805 3908 0 - 916 2688 2808 2920 3012 3806 0 - 692 917 1290 2689 3013 0 - 918 1214 1291 2174 2427 2690 2921 3627 4026 4313 0 - 1292 2809 2922 3014 3807 3909 4314 0 - 693 1215 1293 2923 3808 4027 0 - 694 1752 2428 2691 2924 3628 0 - 1294 2175 3015 3629 3910 4028 0 - 1295 2176 2429 2692 3016 3630 0 - 1 695 1046 1370 2015 2334 2693 2810 3017 3454 4029 4095 0 - 95 572 1047 1371 1613 2016 2177 2335 2811 3455 4096 0 - 2 573 1048 1372 1614 1871 2017 2694 2812 3018 3456 0 - 3 344 1049 1555 1615 2018 2178 2695 3019 3212 3457 0 - 96 1050 1616 1872 2179 3020 3213 3458 4030 0 - 696 1296 1373 1556 1617 2266 2336 3459 4097 0 - 97 345 697 1051 1557 1873 2019 2180 4031 4098 0 - 346 698 1618 2020 3021 3214 0 - 4 98 1297 1374 1558 2337 2696 3022 4099 0 - 5 574 699 1052 1375 1559 2021 2338 2813 4227 0 - 99 1619 2022 2267 2697 2814 3023 3215 4100 0 - 6 100 347 700 1053 1376 2023 2815 3024 3216 4101 0 - 101 348 575 701 1054 1298 1560 1874 2339 2698 3025 3217 3460 0 - 102 576 1299 1377 1561 2181 3026 3218 4032 0 - 7 577 702 1620 1875 2340 2699 3219 3461 4033 0 - 349 703 1300 1378 1562 1876 2182 2700 3027 4228 0 - 8 350 704 1055 1379 2341 3028 3462 4229 0 - 9 103 578 1380 1877 2816 3029 4102 4230 0 - 104 579 1056 1301 1621 2183 2268 2342 2701 3030 0 - 105 351 580 705 1057 1622 1878 2184 2269 2343 2817 3031 3220 3463 0 - 10 106 1058 1879 2024 2185 2270 2818 4034 4103 4231 0 - 352 1381 1623 1880 2025 2271 2702 2819 4104 4232 0 - 11 1382 1881 2026 2272 3032 4105 4233 0 - 12 706 1059 3033 3307 3464 0 - 919 1060 1882 3034 3465 0 - 353 581 707 3308 0 - 13 354 582 1883 3309 3466 4234 0 - 355 708 920 3035 3310 0 - 14 1061 3311 3467 0 - 15 356 709 1062 3036 0 - 357 710 921 1302 1884 3037 4235 0 - 1063 1303 2273 3038 4236 0 - 358 583 711 1064 1885 2274 3039 3468 0 - 16 1065 1886 2275 4237 0 - 359 922 1887 2276 3312 4238 0 - 17 923 1888 2277 3040 3313 4239 0 - 712 1066 1753 2027 2344 2703 3041 3314 3469 3911 0 - 107 237 447 924 1067 1624 1754 2028 2186 2345 2430 2545 3315 3470 3809 0 - 448 925 1068 1625 1755 2029 2546 2704 3042 3471 3810 3912 0 - 1069 1563 1626 1756 2030 2187 2431 2705 3043 3221 3472 3913 0 - 108 1070 1304 2432 3044 3222 3473 0 - 238 713 1564 1627 2433 3223 3316 3811 3914 0 - 449 1628 2188 2434 2547 3317 3474 4240 0 - 109 239 450 714 926 1071 1565 2031 2189 2548 3318 0 - 240 451 715 927 1629 2032 2435 2549 3045 3224 3319 0 - 110 241 452 1072 1630 2190 2550 3225 3320 3475 3812 3915 0 - 453 716 1073 1566 1757 2033 2346 4241 0 - 111 928 1631 2034 2706 3046 3226 3321 3813 0 - 112 242 454 1305 1567 2191 2551 3047 3227 3814 0 - 113 243 455 929 1074 1306 2035 2192 2436 2552 2707 3476 4242 0 - 244 717 930 1307 1568 2193 2437 2708 3048 3815 3916 4243 0 - 718 931 1075 1758 2347 2438 3049 3477 3917 4244 0 - 114 456 932 1759 3050 3322 3816 4245 0 - 1076 1308 1760 2348 3323 3918 4246 0 - 115 457 1077 1309 1632 2194 2349 3051 3919 4247 0 - 116 1078 1310 1633 2195 2350 2439 2553 2709 3052 3324 0 - 117 1079 1761 2036 2196 2440 3817 3920 4248 0 - 245 933 1634 1762 2037 2554 2710 3325 4249 0 - 246 934 1763 2038 3053 3326 4250 0 - 247 719 1383 2820 4315 0 - 720 1216 1384 2351 2821 4106 0 - 1217 2441 3228 0 - 458 1385 2442 2822 0 - 248 459 721 2443 2623 3229 4316 0 - 249 1386 2352 2624 2823 4317 0 - 250 722 1218 1387 2444 0 - 460 1219 1388 3230 4318 0 - 118 461 1389 2824 4107 4319 0 - 1220 2353 2825 0 - 119 2354 2445 0 - 251 723 1221 2355 2446 2826 3231 0 - 252 1390 2625 2827 4108 0 - 18 253 584 1391 1764 2711 2828 3327 0 - 19 1080 1392 1765 2712 2829 3054 3328 3478 3631 0 - 585 2626 2713 3055 3479 0 - 1393 1766 2555 2627 2714 2925 3329 3480 3818 0 - 20 462 586 1081 1394 1767 2556 2715 2830 2926 3056 3481 3632 3819 0 - 21 1082 1768 2197 2716 3057 3232 3482 0 - 1083 2927 3058 3233 3483 0 - 463 1395 2928 3330 3484 0 - 254 464 1084 2198 2557 3331 3633 0 - 255 465 587 1396 2199 2558 2628 2929 3059 3234 3634 3820 0 - 22 466 588 2629 2717 3235 3485 3635 0 - 256 467 1085 2200 2559 2718 2930 3486 3636 0 - 257 1086 1397 2630 2831 2931 3060 3487 3821 0 - 468 589 1398 2201 3061 3236 3332 0 - 469 2932 3488 3822 0 - 23 470 590 1399 1769 2832 2933 3062 3333 3823 0 - 591 1087 1770 2833 3334 3637 0 - 471 1088 1400 2202 3063 3638 0 - 258 1401 1771 2560 2631 2719 2834 3335 0 - 724 1402 1772 1889 2447 3336 0 - 1089 1773 2203 2448 3064 3237 0 - 120 472 1090 1774 1890 2204 3065 3238 0 - 121 473 1091 1403 2205 3239 3337 0 - 474 725 1092 1404 1775 2356 4251 0 - 122 726 1093 1891 2357 3066 3240 0 - 123 475 1405 2206 3067 3241 0 - 124 476 727 2358 0 - 125 728 1406 1776 2449 3338 4252 0 - 126 477 1407 1777 1892 3068 3339 4253 0 - 729 1893 2207 2359 2450 3242 0 - 127 1094 1778 1894 2208 2451 4254 0 - 1408 1779 1895 3340 4255 0 - 1409 1780 1896 3069 3341 4256 0 - 1410 1635 2720 3342 0 - 1311 1636 2632 2721 0 - 1411 1637 2039 2209 3343 3824 0 - 1638 2040 2633 2722 3344 3825 0 - 1639 2634 2723 0 - 1312 1412 1640 2041 2635 3826 3921 0 - 1313 3827 0 - 2042 2210 2724 3922 0 - 1641 2211 3828 3923 0 - 2043 2212 3829 3924 0 - 24 837 1413 2044 2360 3070 3639 4035 0 - 128 259 838 935 1414 2045 2361 3640 0 - 25 936 1415 1897 2046 2934 3071 3641 0 - 26 360 2047 3072 3243 0 - 260 361 2636 3244 3642 0 - 261 362 839 937 2048 2637 3073 3245 3643 0 - 27 129 1416 2362 2638 3074 3644 0 - 28 130 262 840 1417 3246 3645 0 - 29 1418 2049 2363 3646 0 - 131 938 2050 2278 2639 2935 3075 3247 0 - 30 132 363 1419 2051 2640 3076 3248 0 - 133 364 939 1898 2364 3077 3249 0 - 134 263 940 2052 2279 2936 3647 4036 0 - 264 365 941 1420 1899 3078 3648 0 - 135 2280 2365 3079 3649 0 - 31 136 1900 2053 2281 3650 4037 0 - 32 265 841 942 1421 1901 2054 2282 2641 3080 0 - 1422 1781 2055 4038 0 - 366 1222 1782 2056 0 - 137 2057 0 - 138 367 1223 1423 1783 2058 0 - 139 368 478 1224 2366 4039 0 - 140 369 1424 1784 2059 3651 0 - 1225 1785 2367 3652 4040 0 - 141 370 479 1786 2368 3653 0 - 142 1226 1787 2060 3654 4041 0 - 1227 1425 1788 2061 0 - 1228 1426 2062 2369 2835 3081 3345 3489 3655 3925 4109 0 - 480 943 1427 2063 2836 3082 3490 3656 3926 0 - 371 1229 2064 2452 3083 3491 3927 0 - 266 372 2453 2642 2837 3346 3657 3928 4110 0 - 373 481 1428 2454 2838 3347 3492 3658 4257 0 - 267 374 482 944 2065 2455 2643 3084 3348 3659 0 - 143 1314 1429 2370 2644 3085 3349 3660 4111 0 - 144 375 1230 1430 2066 2456 2645 2839 3086 3929 4112 0 - 145 376 945 1315 2371 3087 3493 0 - 146 268 483 1316 1431 2646 3088 3661 0 - 484 946 2372 2647 3494 3662 0 - 147 269 485 947 1231 1317 2067 2283 2457 3495 3663 4113 4258 0 - 270 1318 1432 2068 2284 2373 2648 2840 3089 3496 3930 0 - 486 1232 1433 2069 3090 3350 4259 0 - 148 377 487 1233 1319 2285 2374 3497 0 - 149 378 1434 2070 2458 3351 3664 4114 4260 0 - 150 488 948 1435 2841 3091 3352 4115 4261 0 - 271 379 949 1234 2071 2375 2459 2842 3498 3931 0 - 151 272 380 489 2286 2376 2843 3092 3499 3665 3932 0 - 152 1235 2072 2287 2460 2844 3666 3933 4116 4262 0 - 273 381 950 1436 2073 2288 2649 2845 3353 4117 4263 0 - 730 951 1320 2937 3354 3830 3934 0 - 490 952 1095 1642 2938 3093 3250 3667 3935 0 - 592 731 1569 1643 3251 3355 3668 3831 3936 4118 0 - 491 732 1321 1570 1644 2289 2939 3356 4119 0 - 492 733 953 1096 1571 3357 3669 4120 0 - 1322 1572 3094 3358 3670 3832 4121 0 - 493 1097 1236 1645 3252 3359 3671 3833 3937 4122 0 - 494 593 734 1098 1237 1573 3672 0 - 735 1099 1238 3095 3253 3834 3938 4123 0 - 736 954 1239 1323 1574 3096 3673 3835 3939 0 - 737 2940 3360 3674 4124 0 - 495 594 955 2941 3097 3361 3836 4125 0 - 595 1100 1240 1324 3362 3675 3940 0 - 596 1101 1325 1646 2290 3098 3363 3676 0 - 597 738 956 1241 3254 3941 0 - 496 598 739 1102 1647 2291 2942 3099 3255 3677 3837 3942 0 - 1103 1242 2292 3678 3838 3943 4126 0 - 957 1243 2293 3100 3364 4127 0 - 153 382 2294 3256 4128 0 - 1437 2295 4129 0 - 154 383 2561 3679 4042 4130 0 - 155 1438 2562 3257 3680 3944 4131 0 - 1439 1789 3681 0 - 156 384 1440 1790 3258 3945 4132 0 - 157 385 2563 3259 0 - 3260 3682 4043 0 - 158 2296 2564 3683 4044 4133 0 - 1441 2297 3946 0 - 159 1442 2298 3684 3947 4045 0 - 1648 2725 3500 4134 0 - 160 1104 1649 2377 3501 3685 4135 0 - 386 1650 2943 3502 3686 0 - 161 387 1105 3687 4136 0 - 162 1651 2299 2726 2944 4137 0 - 163 388 1106 1244 4138 0 - 164 2945 3688 0 - 165 1107 1245 2300 2727 2946 3503 3689 4139 0 - 389 1246 2728 3690 0 - 390 1108 2378 3504 0 - 166 391 1247 2301 2379 2947 3505 0 - 1109 1248 2380 3691 0 - 740 842 1110 1249 2729 3101 3365 3506 3948 4140 0 - 741 843 1652 2461 2650 2730 3102 3507 4141 4264 0 - 167 844 1111 1653 2213 2462 3366 3508 3839 4142 0 - 742 2463 2651 2731 3367 3509 3840 3949 0 - 1112 1654 2732 3103 3510 3841 3950 0 - 168 845 1113 2302 2464 3104 3511 4143 0 - 169 846 1114 1655 2214 3105 3512 3951 0 - 847 1656 2215 2465 3368 3513 4265 0 - 170 743 1115 1250 2466 2652 3106 3842 3952 4144 0 - 171 2216 2653 3107 3843 0 - 744 1657 2654 2733 3514 0 - 172 1116 1251 2217 2303 2467 2734 3515 4145 4266 0 - 745 1252 2218 2468 2735 3108 3844 3953 4267 0 - 746 1117 2469 3109 3516 3954 4268 0 - 173 747 848 2470 2736 3369 4146 4269 0 - 174 1118 1658 2219 2304 2471 2737 3110 3370 0 - 1659 2305 2655 2738 3371 4147 4270 0 - 849 1253 2306 2656 3111 3372 4148 4271 0 - 748 2074 2381 2739 3373 3517 4046 4149 0 - 749 2472 2740 3518 4150 4320 0 - 175 497 2075 2382 2473 3374 3519 4151 0 - 750 1902 2076 2474 2741 2948 3375 3520 4047 4321 0 - 498 1903 2077 2742 2949 3521 0 - 392 751 1575 2475 3376 4152 0 - 393 499 1904 2476 2950 3377 3522 4048 0 - 176 500 2078 2477 2743 2951 3523 4049 4153 4322 0 - 1905 2079 2383 2952 3524 4323 0 - 394 752 2384 2478 3525 0 - 501 1576 2080 3378 4050 4324 0 - 177 502 1906 2953 3379 4154 4325 0 - 178 503 2385 4051 0 - 395 753 1907 2081 2386 2479 2744 3526 0 - 179 396 504 754 1908 2387 2954 3527 0 - 1909 2082 3380 4155 4326 0 - 33 274 599 755 958 1443 1660 0 - 34 756 1119 1444 2083 3112 0 - 275 600 959 1120 1445 1661 2084 2480 0 - 397 1121 1326 2481 3113 0 - 276 960 1122 1662 3114 0 - 35 398 601 1446 1663 2482 4272 0 - 277 399 757 961 1664 2085 2483 3115 0 - 36 1327 1447 3116 0 - 37 278 1123 1448 1665 0 - 279 602 1328 1449 3117 0 - 280 962 1124 1329 2086 2484 4273 0 - 281 400 758 963 1330 1450 2485 3118 4274 0 - 38 603 964 1451 3119 4275 0 - 282 401 604 759 965 2087 2486 0 - 39 850 1125 1254 1452 1791 2088 3120 3692 3955 0 - 605 851 1331 1666 3121 4327 0 - 180 606 852 1126 1453 1667 1792 2089 3693 3845 0 - 1332 1454 1793 1910 2090 2955 3846 3956 4328 0 - 181 402 853 1127 1333 1911 2307 2956 3122 4329 0 - 182 854 1128 1668 1794 1912 2957 3123 3694 3957 0 - 40 403 607 855 1455 1669 1913 2958 3695 0 - 41 183 1334 1456 1577 3124 3696 3847 4330 0 - 42 184 856 1129 1255 1457 1670 3697 3848 3958 0 - 185 404 608 1130 1335 1578 1914 3125 0 - 186 609 1336 1458 1579 2959 3126 3698 3849 4331 0 - 187 1131 1256 1337 2091 2308 2960 3699 4332 0 - 1132 1338 1459 1671 1915 2092 2309 2961 3127 3850 3959 4333 0 - 405 1257 1339 1460 1580 1916 3128 3700 3851 3960 0 - 43 406 1133 1461 1795 3129 3961 0 - 610 857 1258 1462 1581 2093 3130 4334 0 - 188 611 1134 1340 1672 2310 3131 3701 0 - 407 612 1259 1917 2094 3962 0 - 44 189 1135 1260 1796 1918 2095 2311 3702 3852 3963 4335 0 - 408 1463 1673 1797 1919 2096 2312 0 - 45 858 1261 1464 1798 1920 2097 2313 3132 4336 0 - 613 966 1465 1799 2846 4052 0 - 614 967 3528 4156 4276 0 - 615 968 1136 1466 1800 2098 2220 2847 3529 3853 4157 0 - 616 969 1137 1467 1801 2099 2848 2962 3530 3854 3964 0 - 1138 1802 2100 2221 3261 3531 3965 0 - 970 1139 1803 2222 2963 3262 3532 3966 4053 0 - 617 2849 3263 3855 3967 4158 0 - 971 2101 3264 0 - 1140 1468 2223 3265 3533 3856 3968 4159 0 - 618 1141 1469 1804 2102 2850 4277 0 - 972 2103 2851 2964 3266 3857 4160 0 - 619 1470 2224 2965 3267 3858 4054 0 - 620 1471 2104 2225 3268 4055 4278 0 - 1472 1805 2105 2966 4161 4279 0 - 621 1142 1806 2852 3969 4056 4280 0 - 973 1473 1807 2106 4162 4281 0 - 974 1474 1808 1921 2314 4057 0 - 859 1143 1475 1809 3133 3534 3703 3970 4058 4163 0 - 860 975 3134 3535 4164 0 - 190 505 861 976 1144 1476 1810 2565 3536 3704 4165 0 - 191 506 862 977 1145 1811 1922 2566 3135 3537 3705 3971 4059 0 - 1582 3706 3972 4166 0 - 507 1477 1583 2315 3538 4167 0 - 508 863 978 2567 3136 3707 0 - 509 1146 1478 1584 1812 3708 0 - 192 1147 1479 1813 3137 3973 4168 0 - 193 979 1148 1585 1923 2568 3138 3539 0 - 194 510 1480 1586 2569 3139 3709 4060 0 - 980 1481 1587 1924 3140 3710 3974 0 - 195 511 981 1482 1814 1925 3141 4169 0 - 196 512 1149 1815 1926 2316 3142 3540 3711 3975 0 - 197 1150 1816 1927 2317 3712 3976 4061 4170 0 - 864 982 1483 1817 1928 2318 3143 4171 0 - 46 283 760 983 1484 1818 3381 4337 0 - 47 761 1485 1819 2107 2388 3382 3541 3713 0 - 762 984 1341 1486 1820 2108 2657 2967 3383 3542 3859 4338 0 - 48 1821 2109 3543 0 - 49 1487 2968 3384 3544 3714 0 - 284 763 985 2110 3385 3715 0 - 285 764 986 2111 2658 3386 3716 4339 0 - 50 765 1488 1822 2112 2389 3717 4340 0 - 51 766 987 2390 2659 3545 3718 4341 0 - 286 988 1342 2113 2969 3546 3719 4342 0 - 1489 2114 3387 4343 0 - 767 1490 1823 2115 2970 3388 3720 0 - 52 989 1491 1824 2971 3389 3860 4344 0 - 1343 2391 3390 3721 0 - 53 1825 2116 3722 3861 4345 0 - 287 990 1492 1826 2117 2660 3391 0 - 768 1674 2487 2745 3144 3547 4346 0 - 769 1827 2488 2570 2746 2972 3548 4347 0 - 1151 2489 2973 3145 3269 3549 4348 0 - 288 770 1675 2490 2853 3270 0 - 771 1676 2491 2974 3550 0 - 289 772 1152 2226 2571 0 - 2747 3146 4349 0 - 1677 2748 2854 2975 3147 3271 0 - 773 1678 2749 3272 3551 4350 0 - 290 1153 1679 2855 2976 3148 3552 4351 0 - 774 1154 1828 2492 3149 3553 0 - 775 2977 3554 0 - 776 1829 2493 2750 2978 0 - 1155 1680 2227 3150 0 - 291 777 1156 1681 1830 2228 2856 2979 3151 3273 3555 0 - 198 292 865 1493 1682 1831 2229 2392 2857 3392 4172 0 - 1494 1683 1832 2751 2858 3977 0 - 199 866 4173 4352 0 - 200 293 867 1684 1833 2230 3978 0 - 868 1495 1685 2231 2859 3393 0 - 294 778 869 1686 2661 3394 4353 0 - 201 295 870 1496 1687 2232 3395 3979 4174 0 - 779 1688 2393 2662 2752 4354 0 - 296 1497 1689 2394 2663 2860 3980 4355 0 - 297 780 1498 1588 2233 2753 3981 0 - 781 1499 1834 2395 3982 0 - 871 1500 1589 2234 3396 4356 0 - 202 782 872 1501 1835 2754 3397 4175 0 - 873 1836 2396 2861 3398 3983 0 - 203 1502 1690 2235 2397 3984 0 - 204 298 783 1691 1837 2236 2398 2862 3985 0 - 299 1503 1692 1838 2664 2755 2863 3399 4176 0 - 54 300 622 991 1504 1839 1929 4357 0 - 301 623 874 992 1505 1840 2399 2494 3556 3723 0 - 55 1506 1841 2495 3152 4358 0 - 302 624 1507 3153 3724 4359 0 - 56 625 993 1930 2400 3557 3725 4360 0 - 303 994 2496 3558 3726 4361 0 - 304 1508 1931 2401 3154 3559 4362 0 - 305 995 1509 1932 2497 3155 3727 0 - 626 875 1510 3156 4363 0 - 57 627 996 1511 1842 1933 3157 4364 0 - 628 876 1843 2402 3728 0 - 1512 2403 3158 3729 0 - 306 629 997 1934 2404 2498 3560 0 - 58 1844 1935 2499 3730 4365 0 - 630 1693 1936 2864 3400 4366 0 - 1937 2118 2500 3401 3561 4367 0 - 631 1694 1938 2119 2865 3562 0 - 205 1939 2120 3402 0 - 632 2121 2866 4282 4368 0 - 206 2122 2501 3563 4283 4369 0 - 1695 1940 2123 2867 3564 4370 0 - 207 1696 4284 0 - 208 633 1697 2502 3403 0 - 209 634 1698 1941 2868 3565 0 - 210 1942 2124 2503 2869 4285 4371 0 - 307 998 1699 2870 3404 4062 0 - 308 513 999 1700 2125 2237 2871 3405 3566 3731 3862 4177 0 - 514 1000 1701 2126 2872 2980 3567 3732 3863 3986 0 - 1702 2127 2238 3274 3568 3987 0 - 2981 3275 3569 4178 0 - 515 1703 2982 3406 3570 4179 0 - 3407 3733 3864 4180 0 - 309 516 1704 2239 3276 3408 3571 3734 3865 3988 4181 0 - 2128 2873 3277 3866 3989 4182 0 - 1001 3278 3572 0 - 310 1705 2129 2874 2983 3573 3867 3990 0 - 517 1002 2875 2984 3409 3868 4183 0 - 2876 3410 3735 3991 4063 0 - 311 518 1706 2240 2877 2985 3279 3574 3736 3869 3992 0 - 2130 2241 2878 3737 3870 3993 4064 4184 0 - 312 1003 1707 2131 2879 3411 4185 0 - 59 877 1157 1262 2405 2880 3738 3994 0 - 635 878 1004 2504 4286 0 - 1005 1943 2505 2572 2986 3871 3995 0 - 60 519 636 1006 1158 1944 2573 2881 2987 3739 3872 3996 0 - 313 409 637 2506 2882 3740 3873 3997 0 - 314 410 520 1007 1159 1945 2574 3741 0 - 315 411 521 879 1008 2507 2575 3742 0 - 61 316 522 880 1160 1263 2576 3743 3874 3998 0 - 62 523 638 1161 1264 2406 2883 3744 4287 0 - 412 639 1009 1162 1946 2407 2577 0 - 317 524 640 2578 2988 3745 3875 0 - 318 525 1010 1163 1265 2508 2579 2989 3746 4288 0 - 526 641 881 1266 4289 0 - 63 527 642 1011 1947 2884 2990 3876 4290 0 - 528 1164 2408 3747 3999 4291 0 - 319 413 643 1012 1267 1948 2409 2509 2885 4000 0 - 320 414 529 644 1165 1949 2410 2886 2991 3748 3877 4001 0 - 321 415 1013 1950 2580 2887 4292 0 - 64 322 882 1014 1268 1951 4293 0 - 784 1015 1513 1708 1845 2756 3412 0 - 785 883 1016 1344 1709 2665 2757 3159 0 - 211 530 884 1017 1166 1514 1710 1846 2242 3413 0 - 531 1018 1167 1515 1711 1847 2758 2992 3160 4002 0 - 212 532 885 1019 1168 1712 1848 2243 2993 3161 4003 0 - 416 533 886 1516 1713 2244 2994 3414 0 - 534 786 1345 1517 1714 2995 3415 0 - 213 417 535 787 1020 1169 2245 3416 0 - 418 536 788 887 1021 1715 2666 3162 3417 0 - 214 537 888 1170 1518 1716 2246 3418 4004 0 - 215 419 789 1171 1519 1849 2667 3163 4005 0 - 216 420 790 1022 1172 1346 2759 3164 0 - 538 791 1023 1717 2668 2760 0 - 1173 1347 1520 1718 2669 2996 3165 4006 0 - 539 889 1521 2247 3166 3419 0 - 217 1174 1348 1719 2248 2761 3167 3420 0 - 218 421 540 792 1175 1720 1850 2249 2997 3168 4007 0 - 422 1024 1522 1721 1851 2670 2762 3421 0 - 645 793 2510 2763 3169 4186 4372 0 - 794 2132 2511 2581 2764 4373 0 - 219 423 2512 3170 4187 4374 0 - 220 541 2582 3171 3749 0 - 542 795 2411 2513 4188 0 - 221 424 543 796 2133 2583 3750 4189 0 - 425 544 797 2134 2514 2584 3172 3751 4375 0 - 65 222 545 1269 2585 3752 4190 0 - 66 546 646 798 1270 2135 2412 2888 3753 4376 0 - 67 547 647 799 2413 2765 3754 4377 0 - 223 548 1271 2136 2515 2586 2766 3755 4191 4378 0 - 2137 2414 2889 3173 4379 0 - 224 426 549 800 1272 2415 0 - 225 427 801 2138 2516 2767 3756 4192 0 - 68 226 550 648 2890 3174 4193 4380 0 - 428 649 802 1273 2139 2416 2517 2768 2891 0 - 227 429 551 650 803 2417 2892 3175 3757 0 - 228 323 651 890 1025 1722 2418 2587 3422 3575 3758 0 - 1026 1349 1952 2588 3423 3576 4065 4381 0 - 652 1027 1723 1953 2589 3577 3759 0 - 229 891 1350 1954 2319 3578 4382 0 - 324 653 1724 3424 3760 0 - 654 892 1725 1955 2590 3425 3579 3761 4066 0 - 230 325 1028 1956 2591 3426 3762 4067 0 - 231 1351 2419 3427 3763 4383 0 - 232 655 1029 1352 1957 2420 2592 3580 0 - 326 1030 1353 1958 3764 0 - 1031 2421 3581 0 - 656 893 3428 4068 4384 0 - 233 894 3429 3765 0 - 234 657 1354 1726 2320 2422 2593 3430 3766 0 - 235 327 658 1727 1959 2321 2423 3582 3767 0 - 236 1960 2322 3768 4069 4385 0 - 69 895 1523 2140 2893 3431 4194 0 - 1355 1524 1961 2141 3432 3878 4386 0 - 70 552 659 1525 1728 1962 2142 2894 3879 0 - 430 896 1356 1963 4195 4387 0 - 431 660 1729 2895 3433 3880 4196 0 - 71 432 553 661 897 1526 1730 1964 2896 3434 0 - 72 1357 1527 3435 3881 4197 4388 0 - 73 433 1528 2143 2897 3882 4198 4389 0 - 434 662 1358 1965 0 - 74 554 663 1731 1966 4390 0 - 555 664 898 1529 2144 3436 4391 0 - 75 556 665 1530 1967 2898 3437 3883 4199 4392 0 - 666 1359 1732 3438 0 - 435 557 667 1733 1968 2899 3884 0 - 76 899 1531 1969 2145 3439 4200 4393 0 - 1176 1852 3176 3583 4008 4070 4201 0 - 1177 1734 1853 2518 2594 3584 3885 4202 0 - 1178 1970 2519 3177 3280 3585 4203 0 - 1179 1735 1854 1971 2595 3178 3281 3586 4009 4071 0 - 1736 2520 3282 3886 4010 4204 0 - 1180 1972 2596 4072 4205 0 - 1737 3179 3283 3887 4206 0 - 1181 1973 2597 3180 3284 3587 0 - 2598 3181 3285 3888 4073 0 - 1182 2521 2599 3588 4074 4207 4294 0 - 1183 1738 1974 3182 3589 3889 4011 0 - 1184 1855 2522 3183 3590 4012 4295 0 - 1975 2523 3286 3591 4013 0 - 1185 1739 1856 1976 3184 3287 3592 3890 4014 0 - 328 668 1032 1532 1740 1977 2769 4075 0 - 329 669 1033 1533 1741 2250 2524 2600 3593 3769 0 - 330 1034 1742 1978 2251 2601 3288 3594 3770 4015 4076 0 - 1534 1590 1743 2525 3595 0 - 331 1274 1535 1744 2252 2602 3289 3596 3771 4016 0 - 670 1275 1536 1591 3772 4296 0 - 1035 1745 2671 2770 3290 0 - 1276 1537 2526 2672 3291 4017 0 - 671 1036 1592 1979 2603 2771 3292 3597 0 - 332 672 1538 1593 2253 2604 2673 3293 3773 4077 0 - 333 1037 1277 2254 2527 2605 2772 3598 3774 4078 4297 0 - 1038 1539 2528 3599 4018 4298 0 - 673 1278 1540 1594 2255 3294 4079 4299 0 - 1279 3600 4080 0 - 1541 2529 2773 3775 4300 0 - 1542 1746 2256 3776 4019 4081 4301 0 - 334 674 1039 1280 1980 2257 2530 2774 3295 3601 4020 0 - 335 675 1747 1981 2258 3296 3602 3777 4021 0 - 1281 1982 2259 2531 3778 4022 4082 4302 0 - 676 804 1040 1543 1983 2323 2775 2900 4083 4394 0 - 805 1186 1544 2146 2776 2901 3185 3603 4084 4208 0 - 806 1041 1545 1984 2147 2606 2674 2777 2998 3604 3891 4085 4395 0 - 558 677 1042 1187 1546 1985 2148 2607 2778 2902 2999 3186 3605 3892 0 - 1188 1595 2149 2779 3187 3606 0 - 1189 1986 2324 3000 3188 3607 4209 4396 0 - 559 1043 1190 1987 2608 3001 3189 3608 4086 0 - 678 807 1596 2675 2903 3893 4210 0 - 560 679 1547 1988 2609 2904 3002 3609 4087 0 - 561 808 1548 1597 2325 3003 3610 4211 0 - 1549 1598 2676 2780 3190 3894 4212 4397 0 - 562 680 809 1191 1550 1599 2150 2905 4398 0 - 1044 2151 2326 2677 2781 2906 3004 3191 3895 4213 0 - 810 1192 1551 2152 2678 2907 3192 3896 4214 4399 0 - 563 681 1552 1600 2610 2679 3005 3193 3897 4088 4400 0 - 564 682 811 1045 1989 2680 2782 3611 4089 4401 0 - 812 1553 2153 2783 3006 4215 0 - 683 1193 2908 4090 0 - 565 1194 1554 2327 3194 4091 0 - 684 1195 2328 2611 2784 3195 0 - 566 685 813 1196 1990 2329 2909 3007 3196 3612 3898 0 - 686 814 900 3197 4303 4402 0 - 815 1857 1991 3440 4092 4403 0 - 77 1197 1601 1858 2260 3198 0 - 687 816 1602 3441 3779 0 - 78 688 901 1992 2261 3442 3780 4093 4304 0 - 817 1603 3443 0 - 818 902 3199 3444 3781 4404 0 - 79 1604 3200 3445 3782 4405 0 - 3201 3446 0 - 1198 2262 3783 4094 4305 4406 0 - 1199 1993 3202 4407 0 - 819 1605 1994 2263 3203 3784 4306 0 - 80 820 1200 1859 3204 4307 0 - 689 821 1995 2264 0 - 690 822 1201 1860 1996 2265 3205 3785 0 - 81 336 823 1861 1997 2330 2785 2910 0 - 337 1862 2154 2532 2612 2911 3786 3899 0 - 824 1360 1863 1998 2155 2533 2613 2681 2786 3900 0 - 82 1864 1999 2156 2614 2787 2912 3787 3901 0 - 83 1282 1865 2157 2534 2788 0 - 338 1866 2000 2615 3788 0 - 84 2001 2535 2616 2913 3789 0 - 85 339 1283 2617 3790 3902 0 - 86 825 1284 1867 2158 2914 3791 0 - 87 826 2002 2682 2789 3792 0 - 340 827 1285 1361 2003 2536 2790 3793 3903 0 - 88 828 1868 2537 0 - 829 1286 1362 2331 3904 0 - 341 830 1287 2004 2159 2538 2791 2915 0 - 342 1869 2005 2160 2332 2618 2683 2792 2916 0 - 89 343 1288 1870 2006 2161 2333 2684 0 - 90 2793 3447 4408 0 - 91 1202 2794 3206 3448 3794 4216 0 - 567 2539 3449 4217 0 - 436 568 2540 2619 3207 3297 3450 3795 4409 0 - 92 2795 3208 3451 3796 4218 4410 0 - 93 437 1203 2541 3209 3298 4219 4411 0 - 569 2620 3210 3299 3797 4412 0 - 570 1204 2542 2621 2796 3798 4220 4413 0 - 438 2543 2797 3211 3799 0 - 439 571 0 - 1205 3452 3800 0 - 94 1206 2544 3801 4221 4414 0 - 440 2622 2798 3453 4222 0 - 18 33 46 54 81 90 0 - 1 12 19 24 34 39 47 59 69 91 0 - 2 20 25 60 70 82 0 - 3 21 26 48 77 83 0 - 13 35 40 49 71 78 84 0 - 4 27 36 41 72 79 92 0 - 14 28 37 42 61 65 85 0 - 5 29 50 62 66 86 0 - 6 15 30 55 73 93 0 - 7 22 51 56 67 74 87 0 - 8 43 80 88 0 - 9 23 38 52 57 63 68 75 0 - 10 16 31 44 53 58 94 0 - 11 17 32 45 64 76 89 0 - 95 107 128 160 167 175 180 190 198 211 228 0 - 108 153 168 181 199 219 229 0 - 96 120 169 182 191 200 212 220 0 - 97 109 154 161 205 213 221 230 0 - 98 129 143 183 231 0 - 110 121 130 155 184 201 214 222 0 - 99 111 131 137 162 0 - 100 132 138 144 156 163 170 192 215 0 - 101 122 133 145 157 185 193 216 232 0 - 102 112 123 146 164 171 186 194 0 - 113 134 147 158 165 172 176 187 206 223 0 - 124 139 148 166 224 0 - 125 140 149 173 202 225 233 0 - 103 114 118 126 150 177 195 226 0 - 115 159 178 203 207 0 - 104 116 119 135 174 188 208 217 234 0 - 105 141 151 179 196 204 209 218 227 235 0 - 106 117 127 136 142 152 189 197 210 236 0 - 247 253 274 283 300 307 328 336 0 - 237 259 275 292 301 308 323 329 337 0 - 276 293 330 338 0 - 238 260 266 288 313 324 0 - 239 254 284 289 314 325 0 - 240 248 261 267 277 285 294 315 0 - 241 262 278 295 309 316 331 339 0 - 242 255 268 279 302 317 332 0 - 243 256 263 269 280 286 303 318 333 0 - 249 257 270 290 296 304 310 0 - 244 250 264 281 297 305 326 340 0 - 251 271 282 306 319 334 341 0 - 272 291 298 311 320 327 335 0 - 245 252 258 273 287 299 312 321 342 0 - 246 265 322 343 0 - 344 360 366 371 0 - 382 397 402 423 430 0 - 353 361 372 392 409 431 0 - 354 373 386 393 398 403 416 432 0 - 345 383 387 410 417 424 0 - 346 355 362 374 399 411 418 425 436 0 - 347 356 363 367 375 384 388 419 433 437 0 - 348 364 376 385 404 412 420 434 0 - 349 357 365 389 400 405 438 0 - 350 390 394 406 0 - 368 377 391 426 439 0 - 369 378 427 0 - 379 395 401 407 413 428 0 - 351 358 370 380 396 414 421 429 435 0 - 352 359 381 408 415 422 440 0 - 441 447 497 505 513 530 0 - 442 448 462 480 498 514 519 531 552 558 0 - 443 472 490 506 532 541 559 0 - 449 458 481 499 533 553 560 0 - 463 491 507 515 534 542 561 567 0 - 444 450 464 492 520 535 543 0 - 451 459 482 508 521 536 544 568 0 - 445 452 473 493 516 522 537 545 0 - 453 474 494 509 523 546 562 0 - 454 465 475 483 510 524 563 569 0 - 466 484 538 547 554 564 0 - 455 467 485 500 525 548 570 0 - 460 468 486 501 526 539 555 0 - 469 476 478 487 549 571 0 - 456 461 470 477 488 495 502 511 517 527 550 556 0 - 446 457 471 503 528 565 0 - 479 489 496 504 512 518 529 540 551 557 566 0 - 584 599 613 622 630 668 676 0 - 585 605 614 635 645 686 0 - 572 600 606 615 623 651 669 0 - 573 586 616 631 636 652 659 677 0 - 581 592 617 637 653 660 678 687 0 - 582 601 607 654 661 679 688 0 - 574 593 618 632 638 646 670 680 0 - 575 608 639 655 662 671 0 - 576 587 602 609 619 624 640 672 681 0 - 577 588 625 647 663 682 0 - 589 610 620 626 641 656 664 673 0 - 578 590 594 603 627 642 648 665 0 - 591 595 621 628 683 0 - 579 596 611 633 657 666 684 0 - 597 604 612 629 643 649 674 689 0 - 580 583 598 634 644 650 658 667 675 685 690 0 - 719 755 760 784 804 823 0 - 695 706 712 720 740 748 756 761 805 0 - 741 749 768 785 793 814 0 - 724 730 742 750 762 769 794 806 815 824 0 - 707 713 731 751 770 807 816 0 - 696 732 771 786 795 808 817 0 - 691 697 714 733 763 772 787 796 0 - 698 708 715 721 757 764 778 788 797 818 0 - 699 716 725 734 765 798 809 825 0 - 700 709 735 743 789 810 0 - 692 701 726 790 0 - 702 744 766 773 779 791 799 811 826 0 - 703 710 717 722 736 745 758 780 819 827 0 - 704 718 746 752 774 781 820 828 0 - 693 727 775 800 829 0 - 694 728 737 747 767 776 782 801 812 0 - 723 729 738 753 759 802 821 830 0 - 705 711 739 754 777 783 792 803 813 822 0 - 837 842 850 859 877 895 0 - 831 843 851 860 878 883 900 0 - 832 838 844 852 861 865 874 884 890 0 - 845 853 866 891 896 0 - 833 846 854 862 867 885 0 - 847 855 868 886 892 897 901 0 - 839 863 869 879 887 902 0 - 834 840 856 870 880 888 0 - 857 871 875 881 889 893 898 0 - 848 872 894 0 - 835 873 876 0 - 836 841 849 858 864 882 899 0 - 958 966 974 983 991 998 1015 1032 1040 0 - 903 967 975 1004 1016 0 - 904 912 924 935 959 968 976 992 999 1017 1025 1033 0 - 905 951 984 1005 1026 1041 0 - 906 913 925 936 943 969 1000 1006 1018 1027 1042 0 - 907 914 919 952 960 970 977 1019 1034 1043 0 - 908 915 926 953 985 1007 1020 1028 0 - 920 927 937 944 961 971 978 986 1008 1021 0 - 909 916 928 938 972 1035 1044 0 - 917 939 945 979 1001 1009 1022 1029 1036 0 - 946 987 993 1023 1045 0 - 918 929 940 947 962 988 994 1010 1037 0 - 921 930 941 954 963 980 995 1030 0 - 931 1031 1038 0 - 932 948 955 964 981 989 996 1002 1011 0 - 910 949 956 965 997 1012 1039 0 - 922 933 950 990 1003 1013 1024 0 - 911 923 934 942 957 973 982 1014 0 - 1046 1059 1066 1080 1110 1119 1125 1143 1157 1176 1186 1202 0 - 1047 1067 1104 1111 1120 1126 1136 1144 1166 1177 0 - 1048 1068 1081 1112 1137 1158 1167 1187 0 - 1049 1069 1082 1089 1138 1188 1197 0 - 1070 1083 1113 1121 1127 1151 1178 1189 0 - 1050 1060 1090 1095 1114 1122 1128 1139 1145 1168 1179 1190 0 - 1051 1071 1084 1096 1105 1152 1159 1169 1180 0 - 1061 1072 1091 1097 1123 1129 1140 1160 1170 0 - 1052 1073 1092 1098 1141 1146 1161 1191 0 - 1053 1062 1099 1106 1115 1147 1171 1192 1203 0 - 1054 1093 1130 1148 1162 1172 1181 0 - 1074 1085 1107 1116 1124 1131 1163 1182 1198 1204 0 - 1086 1132 1153 1173 1183 1199 0 - 1055 1075 1108 1117 1133 1154 1184 1200 0 - 1076 1087 1100 1109 1142 1193 1205 0 - 1063 1077 1088 1155 1164 1194 0 - 1056 1078 1101 1118 1134 1174 1195 0 - 1057 1064 1102 1149 1156 1165 1175 1185 1196 1201 0 - 1058 1065 1079 1094 1103 1135 1150 1206 0 - 1216 1228 1249 1254 1262 0 - 1212 1217 1222 1229 1282 0 - 1207 1213 1236 1255 1263 1269 1274 1283 0 - 1237 1264 1270 1275 1284 0 - 1208 1223 1230 1238 1244 1250 1276 0 - 1214 1231 1245 1251 1256 1265 1271 1277 0 - 1218 1239 1246 1252 1257 1285 0 - 1219 1232 1258 1266 1278 0 - 1215 1224 1233 1247 1272 1279 1286 0 - 1209 1220 1225 1240 1248 0 - 1210 1221 1234 1241 1259 1267 1273 1280 1287 0 - 1226 1235 1242 1260 1281 0 - 1211 1227 1243 1253 1261 1268 1288 0 - 1311 1331 1344 0 - 1320 1332 1341 1349 1355 1360 0 - 1304 1326 1333 1350 1356 0 - 1296 1321 1345 0 - 1289 1297 1314 1322 1327 1334 1351 1357 0 - 1290 1298 1315 1335 1346 1352 1358 0 - 1299 1305 1316 1328 1336 0 - 1291 1306 1317 1329 1337 1342 0 - 1292 1312 1318 1338 1347 0 - 1300 1302 1307 1323 1330 1339 1353 1361 0 - 1293 1313 1319 1362 0 - 1308 1324 0 - 1294 1303 1309 0 - 1295 1301 1310 1325 1340 1343 1348 1354 1359 0 - 1383 1391 1410 1443 1465 1474 1484 1504 1513 1532 1543 0 - 1370 1384 1392 1413 1426 1444 1452 1475 1485 1523 1544 0 - 1363 1371 1411 1414 1445 1453 1466 1476 1493 1505 1514 1533 0 - 1364 1393 1402 1422 1454 1486 1524 1545 0 - 1365 1372 1394 1415 1427 1467 1494 1515 1525 1546 0 - 1385 1428 1446 1455 1487 1495 1516 1526 1547 0 - 1373 1395 1437 1477 1517 1534 1548 0 - 1374 1416 1429 1447 1456 1527 1549 0 - 1366 1403 1417 1438 1448 1457 1468 1496 1518 1535 0 - 1375 1404 1418 1439 1469 1478 1488 1536 1550 0 - 1367 1376 1419 1423 1430 1440 1479 1506 1519 1528 1537 1551 0 - 1377 1396 1405 1431 1449 1458 1470 1480 1507 1538 1552 0 - 1386 1397 1412 1432 1441 1459 1497 1508 1520 0 - 1378 1387 1420 1450 1460 1481 1498 1509 0 - 1379 1461 1499 1539 0 - 1388 1398 1433 1462 1471 1489 1500 1510 1521 1529 1540 0 - 1406 1424 1434 1472 1490 1501 1541 1553 0 - 1380 1389 1399 1407 1435 1451 1482 1491 1511 1530 0 - 1368 1400 1442 1502 1512 1542 1554 0 - 1381 1390 1401 1408 1436 1463 1492 1503 1522 0 - 1369 1382 1409 1421 1425 1464 1473 1483 1531 0 - 1555 1563 1595 1601 0 - 1564 1569 1575 1582 1596 1602 0 - 1556 1570 1583 1590 1597 1603 0 - 1557 1565 1571 0 - 1558 1572 1577 1598 1604 0 - 1559 1566 1573 1584 1591 1599 0 - 1560 1578 1585 1592 0 - 1561 1567 1579 1586 1593 1600 0 - 1562 1568 1574 1580 1587 1588 1605 0 - 1576 1581 1589 1594 0 - 1635 1660 1693 1699 1708 1740 0 - 1606 1636 1648 1652 1666 1674 1709 0 - 1607 1613 1624 1637 1649 1653 1661 1667 1682 1700 1710 1722 1734 1741 0 - 1608 1614 1625 1654 1683 1694 1701 1711 1723 1728 0 - 1615 1626 1702 0 - 1609 1616 1642 1655 1662 1668 1684 1712 1735 1742 0 - 1627 1643 1675 1724 1729 1736 0 - 1628 1650 1656 1663 1669 1685 1713 1725 1730 0 - 1617 1644 1676 1703 1714 1743 0 - 1618 1629 1664 1686 1715 0 - 1610 1630 1645 1665 1670 1687 1704 1716 1744 0 - 1611 1619 1631 1638 1651 1677 1737 1745 0 - 1620 1639 1657 1678 1688 1717 1731 0 - 1640 1671 1679 1689 1695 1705 1718 1738 0 - 1612 1632 1680 1690 1696 1746 0 - 1621 1633 1646 1658 1672 1697 1719 1726 1732 0 - 1622 1641 1647 1681 1691 1698 1706 1720 1727 1733 1739 1747 0 - 1623 1634 1659 1673 1692 1707 1721 0 - 1764 1799 1808 1818 1839 1845 1861 0 - 1753 1765 1791 1809 1819 1852 0 - 1748 1754 1792 1800 1810 1831 1840 1846 1853 1862 0 - 1766 1772 1781 1793 1820 1827 1857 1863 0 - 1749 1755 1767 1801 1832 1847 1864 0 - 1750 1756 1768 1773 1782 1802 1821 1858 1865 0 - 1751 1774 1794 1803 1811 1833 1848 1854 1866 0 - 1757 1775 1789 1804 1812 1822 1867 0 - 1783 1790 1813 1841 1849 0 - 1758 1795 1828 1834 1855 1859 1868 0 - 1752 1776 1784 1805 1823 1829 1835 0 - 1759 1769 1777 1814 1824 1842 0 - 1760 1770 1785 1806 1836 1843 0 - 1786 1815 1830 1837 1850 1856 1860 0 - 1761 1778 1787 1796 1816 1825 1844 0 - 1762 1771 1779 1797 1826 1838 1851 1869 0 - 1763 1780 1788 1798 1807 1817 1870 0 - 1921 1929 1936 1977 1983 1997 0 - 1889 1902 1910 1937 1943 1952 1961 1984 1991 1998 0 - 1871 1897 1903 1938 1944 1953 1962 1985 1999 0 - 1911 1954 1963 1970 1986 0 - 1872 1882 1890 1912 1922 1971 1978 1987 2000 0 - 1883 1904 1913 1955 1964 1988 1992 2001 0 - 1873 1939 1945 1956 1972 0 - 1874 1891 1898 1914 1923 1946 1957 1965 1973 1979 0 - 1875 1930 1966 1989 2002 0 - 1905 1915 1931 1940 1974 1993 0 - 1876 1884 1899 1916 1924 1932 1958 1994 2003 0 - 1877 1892 1906 1925 1933 1947 1967 0 - 1893 1907 1917 1934 1948 1975 1980 1995 2004 0 - 1878 1885 1908 1926 1941 1949 1959 1968 1976 1981 1990 1996 0 - 1879 1886 1894 1900 1918 1927 1935 1942 1960 1982 0 - 1880 1887 1895 1919 1950 2005 0 - 1881 1888 1896 1901 1909 1920 1928 1951 1969 2006 0 - 2015 2027 2044 2062 2074 2083 2088 2107 2140 2146 0 - 2007 2016 2028 2039 2045 2075 2084 2089 2098 2125 2154 0 - 2008 2055 2076 2090 2108 2118 2132 2141 2147 2155 0 - 2009 2017 2029 2046 2063 2077 2099 2119 2126 2142 2148 2156 0 - 2018 2030 2047 2056 2064 2100 2109 2127 2149 2157 0 - 2010 2019 2031 2110 2120 2133 0 - 2020 2032 2048 2065 2085 2101 2111 2134 0 - 2021 2033 2049 2102 2112 2121 2135 2150 2158 0 - 2011 2022 2034 2040 2050 2057 2103 2151 0 - 2012 2023 2051 2058 2066 2128 2143 2152 0 - 2035 2052 2067 2078 2086 2091 2113 2122 2136 0 - 2041 2068 2079 2092 2123 2129 2137 0 - 2069 2080 2093 2104 2114 2144 0 - 2059 2070 2105 2115 2138 2153 0 - 2013 2042 2071 2081 2087 2094 2139 2159 0 - 2024 2036 2043 2053 2060 2072 2095 2116 2124 2130 0 - 2025 2037 2073 2096 2117 2131 2160 0 - 2014 2026 2038 2054 2061 2082 2097 2106 2145 2161 0 - 2162 2168 2177 2186 2209 2213 2220 2229 2237 2242 2250 0 - 2169 2178 2187 2197 2203 2221 2238 2260 0 - 2163 2170 2179 2204 2214 2222 2230 2243 2251 0 - 2171 2188 2215 2231 2244 2261 0 - 2164 2172 2180 2189 2198 2226 2245 0 - 2165 2173 2190 2205 2223 2232 2239 2246 2252 0 - 2181 2191 2199 2206 2216 2224 2253 0 - 2174 2192 2200 2217 2254 2262 0 - 2182 2193 2218 2233 2263 0 - 2201 2225 2234 2247 2255 0 - 2166 2175 2194 2202 2227 2235 2256 0 - 2176 2183 2195 2219 2248 0 - 2167 2207 2210 2257 2264 0 - 2184 2211 2228 2236 2240 2249 2258 2265 0 - 2185 2196 2208 2212 2241 2259 0 - 2314 2323 2330 0 - 2294 2302 2307 2319 2324 0 - 2266 2289 2295 2315 2325 0 - 2267 2278 2299 2326 0 - 2279 2283 2296 2300 2303 2308 0 - 2284 2297 2309 0 - 2285 2301 2331 0 - 2273 2298 2327 0 - 2268 2280 2290 2304 2310 2320 2328 0 - 2269 2274 2286 2291 2316 2321 2329 0 - 2270 2275 2281 2287 2292 2311 2317 2322 0 - 2271 2276 2288 2305 2312 2332 0 - 2272 2277 2282 2293 2306 2313 2318 2333 0 - 2334 2344 2351 2360 2369 2381 2388 2405 0 - 2335 2345 2361 2377 2382 2392 2399 2418 0 - 2336 2411 0 - 2337 2362 2370 2419 0 - 2338 2346 2356 2363 2389 2406 2412 0 - 2339 2357 2364 2371 2407 2420 0 - 2340 2372 2390 2393 2400 2413 0 - 2352 2373 2383 2394 2401 2414 0 - 2341 2347 2378 2384 2395 2421 0 - 2358 2366 2374 2379 2415 0 - 2348 2353 2367 2380 2396 2402 0 - 2349 2385 2397 2403 2408 0 - 2342 2350 2354 2365 2391 2422 0 - 2355 2359 2375 2386 2404 2409 2416 0 - 2343 2368 2376 2387 2398 2410 2417 2423 0 - 2461 2472 2487 2504 2510 0 - 2424 2430 2462 2473 2480 2494 2518 2524 2532 0 - 2447 2463 2474 2488 2500 2505 2511 2533 0 - 2425 2431 2441 2448 2452 2534 0 - 2432 2464 2481 2489 2512 2519 0 - 2433 2453 2475 2490 2506 2520 0 - 2426 2434 2442 2454 2465 2476 2482 2535 0 - 2491 2513 2525 2539 0 - 2435 2443 2455 2483 2507 2514 2540 0 - 2456 2466 2495 2526 2541 0 - 2427 2436 2457 2467 2477 2484 2496 2501 2508 2515 2521 2527 2542 0 - 2437 2444 2468 2485 2497 2536 2543 0 - 2438 2469 2478 2492 2522 2528 2537 0 - 2428 2449 2458 2470 2493 2516 2529 0 - 2429 2439 2445 2471 2502 0 - 2446 2450 2459 2479 2486 2498 2509 2517 2523 2530 2538 0 - 2440 2451 2460 2499 2503 2531 2544 0 - 2545 2565 2587 2594 2600 2612 0 - 2555 2570 2572 2581 2588 2606 2613 0 - 2546 2556 2573 2589 2607 2614 0 - 2566 2582 2595 2601 2608 2615 0 - 2547 2590 2609 2616 0 - 2548 2557 2561 2571 2574 2583 2591 2596 0 - 2549 2567 2575 2584 2619 0 - 2550 2562 2576 2585 2602 2617 0 - 2563 2568 2577 2592 2597 2603 0 - 2551 2558 2569 2578 2598 2604 2610 2620 0 - 2552 2559 2564 2579 2586 2599 2605 2621 0 - 2553 2593 2611 0 - 2554 2560 2580 2618 2622 0 - 2626 2632 2650 2665 0 - 2627 2651 2657 2674 2681 0 - 2636 2642 2675 0 - 2623 2637 2643 2658 2661 2666 0 - 2638 2644 2676 0 - 2633 2639 2671 2677 0 - 2640 2645 2652 2667 2672 2678 0 - 2628 2646 2653 2673 2679 0 - 2629 2634 2647 2654 2659 2662 2668 2680 2682 0 - 2624 2630 2635 2648 2663 2669 0 - 2625 2631 2649 2655 2660 2664 2670 2683 0 - 2641 2656 2684 0 - 2711 2720 2756 2769 2775 2785 2793 0 - 2693 2703 2712 2729 2739 2776 2794 0 - 2713 2721 2725 2730 2740 2745 2757 2763 0 - 2714 2731 2741 2746 2764 2777 2786 0 - 2685 2694 2704 2715 2732 2742 2751 2758 2778 2787 0 - 2686 2695 2705 2716 2779 2788 0 - 2687 2696 2747 2780 2795 0 - 2688 2697 2706 2722 2726 2748 2770 2781 0 - 2689 2698 2759 2771 0 - 2699 2717 2723 2733 2749 2752 2760 2765 2782 2789 0 - 2690 2707 2718 2727 2734 2743 2766 2772 2796 0 - 2700 2708 2728 2735 2753 2790 2797 0 - 2691 2736 2750 2754 2767 2773 2783 0 - 2692 2701 2709 2737 2761 2784 0 - 2724 2744 2768 2774 2791 0 - 2702 2710 2719 2738 2755 2762 2792 2798 0 - 2820 2828 2846 2864 2870 2900 2910 0 - 2810 2821 2829 2835 2880 2893 2901 0 - 2799 2805 2811 2847 2857 2871 2911 0 - 2800 2806 2812 2830 2836 2848 2858 2865 2872 2881 2894 2902 2912 0 - 2837 2849 2853 2882 2895 2903 0 - 2807 2822 2838 2859 2896 2904 2913 0 - 2813 2850 2866 2883 2888 2905 2914 0 - 2801 2808 2814 2851 2854 2906 0 - 2802 2815 2839 2873 2897 2907 0 - 2809 2823 2831 2840 2855 2860 2867 2874 2889 0 - 2816 2824 2832 2841 2875 2884 2890 2898 0 - 2803 2825 2833 2852 2861 2876 2908 0 - 2804 2826 2842 2885 2891 2915 0 - 2817 2843 2856 2862 2868 2877 2886 2892 2899 2909 0 - 2818 2844 2869 2878 0 - 2819 2827 2834 2845 2863 2879 2887 2916 0 - 2925 2937 2948 2955 2967 2972 2986 2998 0 - 2917 2926 2934 2949 2962 2980 2987 2992 2999 0 - 2927 2956 2973 2981 3000 0 - 2918 2938 2957 2963 2993 3001 0 - 2919 2943 2950 2958 2968 2994 3002 0 - 2928 2939 2974 2982 2995 3003 0 - 2920 2935 2944 2964 2975 3004 0 - 2929 2945 2959 2965 2988 3005 0 - 2921 2930 2936 2946 2951 2960 2969 2989 0 - 2922 2931 2952 2961 2976 2983 2996 0 - 2923 2932 2947 2977 0 - 2924 2940 2966 2970 2978 3006 0 - 2933 2941 2953 2971 2984 2990 0 - 2942 2954 2979 2985 2991 2997 3007 0 - 3017 3033 3041 3054 3070 3081 3101 3112 3120 3133 3176 3185 3206 0 - 3055 3102 3121 3134 3144 3159 3169 3197 0 - 3008 3018 3042 3056 3071 3082 3103 3160 3186 0 - 3009 3019 3043 3057 3064 3072 3083 3187 3198 0 - 3044 3058 3104 3113 3122 3145 3170 3177 3188 0 - 3010 3020 3034 3065 3093 3105 3114 3123 3135 3161 3171 3178 3189 0 - 3021 3035 3045 3073 3084 3115 3136 3162 3172 3199 3207 0 - 3011 3022 3074 3085 3094 3116 3124 3146 3190 3200 3208 0 - 3012 3023 3046 3075 3147 3179 3191 3201 0 - 3024 3036 3076 3086 3095 3106 3137 3152 3163 3192 3209 0 - 3013 3025 3066 3077 3087 3125 3138 3164 3180 0 - 3026 3047 3059 3067 3088 3107 3117 3126 3139 3153 3181 3193 3210 0 - 3014 3060 3089 3127 3148 3154 3165 3173 3182 3202 0 - 3027 3037 3048 3078 3096 3108 3118 3128 3140 3155 3203 3211 0 - 3028 3049 3109 3129 3149 3183 3204 0 - 3061 3090 3130 3156 3166 0 - 3029 3050 3062 3068 3091 3097 3119 3141 3157 3174 0 - 3015 3038 3051 3063 3150 3158 3194 0 - 3016 3030 3052 3079 3098 3110 3131 3167 3195 0 - 3031 3039 3092 3099 3142 3151 3168 3175 3184 3196 3205 0 - 3032 3040 3053 3069 3080 3100 3111 3132 3143 0 - 3212 3221 3228 3232 3237 3243 3261 3274 0 - 3222 3233 3256 3269 3275 3280 0 - 3213 3238 3250 3262 3281 3288 0 - 3223 3244 3251 3263 3270 3282 0 - 3214 3224 3229 3245 3264 3297 0 - 3225 3239 3246 3252 3257 3265 3276 3289 0 - 3215 3226 3247 3266 3271 3283 3290 0 - 3216 3248 3253 3258 3277 3291 3298 0 - 3217 3240 3249 3259 3278 3284 3292 0 - 3218 3227 3234 3241 3267 3285 3293 3299 0 - 3219 3235 3260 3272 0 - 3230 3236 3268 3294 0 - 3231 3242 3254 3286 3295 0 - 3220 3255 3273 3279 3287 3296 0 - 3327 3342 3381 3400 3404 3412 3447 0 - 3307 3314 3328 3345 3365 3373 3382 3431 3448 0 - 3300 3315 3343 3366 3374 3392 3405 3413 3422 0 - 3301 3329 3336 3354 3367 3375 3383 3401 3423 3432 3440 0 - 3308 3316 3346 3355 3376 3424 3433 3441 0 - 3309 3317 3347 3368 3377 3384 3393 3414 3425 3434 3442 0 - 3330 3356 3406 3415 3443 3449 0 - 3302 3318 3331 3357 3385 3402 3416 3426 0 - 3310 3319 3348 3386 3394 3417 3444 3450 0 - 3349 3358 3407 3427 3435 3445 3451 0 - 3303 3311 3320 3337 3359 3395 3408 3418 0 - 3304 3321 3344 3446 0 - 3332 3350 3378 3387 3396 3419 3428 3436 0 - 3338 3351 3360 3369 3388 3397 3429 0 - 3322 3333 3339 3352 3361 3379 3389 3409 3437 0 - 3305 3323 3334 3362 3398 3410 3452 0 - 3324 3363 3370 3390 3403 3420 3430 3438 0 - 3312 3325 3335 3340 3353 3371 3391 3399 3411 3421 3453 0 - 3306 3313 3326 3341 3364 3372 3380 3439 0 - 3454 3464 3469 3478 3489 3506 3517 3534 3541 3583 3603 0 - 3479 3500 3507 3518 3528 3535 3547 0 - 3455 3470 3501 3508 3519 3529 3536 3556 3566 3575 3584 3593 0 - 3480 3509 3520 3542 3548 3561 3576 3604 0 - 3456 3471 3481 3490 3510 3521 3530 3562 3567 3577 3605 0 - 3457 3472 3482 3491 3531 3543 3568 3606 0 - 3473 3483 3511 3549 3569 3578 3585 3607 0 - 3458 3465 3512 3532 3537 3586 3594 3608 0 - 3466 3474 3492 3502 3513 3522 3544 3579 3609 0 - 3459 3484 3538 3550 3570 3595 3610 0 - 3467 3475 3533 3571 3596 0 - 3460 3493 3539 3572 3580 3587 3597 0 - 3461 3485 3494 3514 3545 3551 3557 3611 0 - 3476 3486 3495 3503 3515 3523 3546 3558 3563 3588 3598 0 - 3487 3496 3524 3552 3559 3564 3573 3589 0 - 3462 3477 3504 3516 3525 3553 3581 3590 3599 0 - 3488 3497 3505 3554 3600 0 - 3498 3526 3560 3591 3601 0 - 3463 3468 3499 3527 3540 3555 3565 3574 3582 3592 3602 3612 0 - 3631 3639 3655 3692 3703 3713 3738 3794 0 - 3613 3620 3640 3685 3693 3704 3723 3731 3758 3769 3786 0 - 3614 3621 3632 3641 3656 3732 3739 3759 3787 0 - 3615 3622 3667 3694 3705 3749 3770 3788 0 - 3642 3657 3668 3706 3740 3760 3779 0 - 3623 3658 3686 3695 3714 3761 3780 3789 0 - 3616 3624 3633 3669 3679 3687 3715 3741 3750 3762 0 - 3643 3659 3707 3716 3742 3751 3781 3795 0 - 3625 3644 3660 3670 3696 3733 3763 3782 3796 0 - 3617 3626 3645 3671 3680 3697 3734 3743 3752 3771 3790 0 - 3646 3672 3681 3708 3717 3744 3753 3772 3791 0 - 3634 3661 3688 3698 3709 3724 3745 3773 3797 0 - 3635 3662 3682 3718 3725 3754 3792 0 - 3627 3636 3647 3663 3683 3689 3699 3719 3726 3746 3755 3774 3783 3798 0 - 3648 3673 3690 3700 3710 3727 3764 3784 3793 3799 0 - 3628 3651 3664 3674 3720 3756 3765 3775 0 - 3618 3637 3652 3675 3691 3728 3735 3800 0 - 3619 3629 3638 3684 3729 3747 3776 0 - 3630 3649 3676 3701 3721 3766 0 - 3653 3665 3677 3711 3736 3748 3757 3767 3777 3785 0 - 3650 3654 3666 3678 3702 3712 3722 3730 3737 3768 3778 3801 0 - 3802 3809 3824 3839 3845 3853 3862 3885 3899 0 - 3818 3830 3840 3846 3859 3871 3878 3891 3900 0 - 3803 3810 3819 3841 3854 3863 3872 3879 3892 3901 0 - 3811 3831 3855 3873 3880 3886 3893 0 - 3804 3832 3847 3864 3881 3894 0 - 3805 3812 3833 3848 3856 3865 3874 3902 0 - 3806 3813 3825 3857 3887 3895 0 - 3834 3842 3866 3882 3896 0 - 3814 3820 3843 3849 3858 3875 3888 3897 0 - 3807 3821 3826 3850 3867 3889 0 - 3815 3835 3844 3851 3903 0 - 3808 3822 3827 3904 0 - 3816 3823 3836 3860 3868 3876 3883 0 - 3828 3837 3869 3877 3884 3890 3898 0 - 3817 3829 3838 3852 3861 3870 0 - 3911 3925 3948 3955 3970 3994 4008 0 - 3934 3949 3956 3995 0 - 3905 3912 3926 3950 3964 3977 3986 3996 4002 0 - 3906 3913 3927 3965 3987 0 - 3907 3935 3951 3957 3966 3971 3978 4003 4009 4015 0 - 3914 3928 3936 3967 3972 3997 4010 0 - 3908 3915 3937 3944 3958 3968 3979 3988 3998 4004 4016 0 - 3929 3938 3945 3952 3973 3989 4005 4017 0 - 3909 3921 3930 3946 3959 3980 3990 4006 4011 0 - 3916 3939 3953 3960 3974 3981 0 - 3917 3954 3961 3982 4012 4018 0 - 3918 3940 3969 3983 3991 0 - 3910 3919 3947 3984 3999 4019 0 - 3922 3931 3941 3962 4000 4013 4020 0 - 3923 3932 3942 3975 3985 3992 4001 4007 4014 4021 0 - 3920 3924 3933 3943 3963 3976 3993 4022 0 - 4052 4057 4062 4075 4083 0 - 4029 4035 4046 4058 4070 4084 0 - 4038 4047 4065 4085 4092 0 - 4023 4030 4053 4059 4071 4076 4086 0 - 4024 4048 4066 4087 4093 0 - 4025 4031 4042 4067 4072 0 - 4032 4054 4060 4073 4077 4088 0 - 4033 4043 4089 0 - 4026 4036 4044 4049 4074 4078 4094 0 - 4050 4055 4068 4079 0 - 4027 4039 4080 0 - 4040 4056 4063 4090 0 - 4028 4045 4051 4081 4091 0 - 4034 4037 4041 4061 4064 4069 4082 0 - 4095 4106 4109 4140 4149 4163 4194 4201 4208 4216 0 - 4134 4141 4150 4156 4164 4186 0 - 4096 4135 4142 4151 4157 4165 4172 4177 4202 0 - 4128 4143 4173 4178 4187 4195 4203 4209 0 - 4110 4118 4152 4158 4166 4196 4204 4210 0 - 4097 4119 4129 4167 4179 4188 4211 4217 0 - 4098 4120 4130 4136 4189 4205 0 - 4099 4111 4121 4180 4197 4212 4218 0 - 4122 4131 4159 4174 4181 4190 0 - 4100 4137 4160 4206 4213 0 - 4101 4112 4123 4132 4138 4144 4168 4182 4198 4214 4219 0 - 4113 4133 4139 4145 4153 4191 4207 4220 0 - 4114 4124 4146 4161 4175 4192 4215 0 - 4102 4107 4115 4125 4154 4169 4183 4193 4199 0 - 4103 4116 4126 4170 4184 4221 0 - 4104 4108 4117 4147 4176 4185 4222 0 - 4105 4127 4148 4155 4162 4171 4200 0 - 4223 4264 4276 4286 4303 0 - 4234 4240 4257 4265 4272 4304 0 - 4227 4241 4251 4277 4282 4287 4296 0 - 4242 4258 4266 4273 4283 4288 4294 4297 4305 0 - 4228 4235 4243 4267 4274 4306 0 - 4229 4244 4268 4295 4298 4307 0 - 4259 4278 4289 4299 0 - 4252 4260 4269 4279 4300 0 - 4230 4245 4253 4261 4275 4290 0 - 4224 4246 4280 0 - 4225 4236 4247 4284 4291 4301 0 - 4231 4237 4248 4254 4262 4285 4302 0 - 4232 4238 4249 4255 4263 4270 4292 0 - 4226 4233 4239 4250 4256 4271 4281 4293 0 - 4315 4337 4357 4366 4394 4408 0 - 4308 4320 4327 4346 4372 4402 0 - 4309 4321 4328 4338 4347 4367 4373 4381 4386 4395 4403 0 - 4329 4348 4352 4374 4382 4387 4396 0 - 4316 4339 4353 4375 4404 4409 0 - 4312 4330 4349 4383 4388 4397 4405 4410 0 - 4340 4368 4376 4398 0 - 4310 4358 4389 4399 4411 0 - 4331 4359 4400 4412 0 - 4341 4350 4354 4360 4377 4390 4401 0 - 4313 4322 4332 4342 4361 4369 4378 4406 4413 0 - 4314 4317 4323 4333 4351 4355 4362 4370 4379 4407 0 - 4318 4324 4334 4343 4356 4363 4384 4391 0 - 4319 4325 4344 4364 4380 4392 0 - 4335 4345 4365 4371 4385 4414 0 - 4311 4326 4336 4393 0 diff --git a/examples/commandline/spear_qcp/instances/qcplin2006.10556.cnf b/examples/commandline/spear_qcp/instances/qcplin2006.10556.cnf deleted file mode 100755 index 88939d41b..000000000 --- a/examples/commandline/spear_qcp/instances/qcplin2006.10556.cnf +++ /dev/null @@ -1,5265 +0,0 @@ -p cnf 700 5264 - -1 -2 0 - -1 -3 0 - -2 -3 0 - -4 -5 0 - -6 -7 0 - -6 -8 0 - -6 -9 0 - -6 -10 0 - -7 -8 0 - -7 -9 0 - -7 -10 0 - -8 -9 0 - -8 -10 0 - -9 -10 0 - -11 -12 0 - -11 -13 0 - -12 -13 0 - -14 -15 0 - -14 -16 0 - -15 -16 0 - -17 -18 0 - -17 -19 0 - -17 -20 0 - -18 -19 0 - -18 -20 0 - -19 -20 0 - -21 -22 0 - -21 -23 0 - -21 -24 0 - -22 -23 0 - -22 -24 0 - -23 -24 0 - -25 -26 0 - -25 -27 0 - -25 -28 0 - -25 -29 0 - -25 -30 0 - -25 -31 0 - -26 -27 0 - -26 -28 0 - -26 -29 0 - -26 -30 0 - -26 -31 0 - -27 -28 0 - -27 -29 0 - -27 -30 0 - -27 -31 0 - -28 -29 0 - -28 -30 0 - -28 -31 0 - -29 -30 0 - -29 -31 0 - -30 -31 0 - -32 -33 0 - -32 -34 0 - -32 -35 0 - -33 -34 0 - -33 -35 0 - -34 -35 0 - -36 -37 0 - -36 -38 0 - -36 -39 0 - -36 -40 0 - -37 -38 0 - -37 -39 0 - -37 -40 0 - -38 -39 0 - -38 -40 0 - -39 -40 0 - -41 -42 0 - -41 -43 0 - -41 -44 0 - -41 -45 0 - -42 -43 0 - -42 -44 0 - -42 -45 0 - -43 -44 0 - -43 -45 0 - -44 -45 0 - -46 -47 0 - -46 -48 0 - -46 -49 0 - -46 -50 0 - -46 -51 0 - -46 -52 0 - -47 -48 0 - -47 -49 0 - -47 -50 0 - -47 -51 0 - -47 -52 0 - -48 -49 0 - -48 -50 0 - -48 -51 0 - -48 -52 0 - -49 -50 0 - -49 -51 0 - -49 -52 0 - -50 -51 0 - -50 -52 0 - -51 -52 0 - -53 -54 0 - -53 -55 0 - -53 -56 0 - -53 -57 0 - -53 -58 0 - -53 -59 0 - -54 -55 0 - -54 -56 0 - -54 -57 0 - -54 -58 0 - -54 -59 0 - -55 -56 0 - -55 -57 0 - -55 -58 0 - -55 -59 0 - -56 -57 0 - -56 -58 0 - -56 -59 0 - -57 -58 0 - -57 -59 0 - -58 -59 0 - -60 -61 0 - -60 -62 0 - -60 -63 0 - -60 -64 0 - -60 -65 0 - -60 -66 0 - -61 -62 0 - -61 -63 0 - -61 -64 0 - -61 -65 0 - -61 -66 0 - -62 -63 0 - -62 -64 0 - -62 -65 0 - -62 -66 0 - -63 -64 0 - -63 -65 0 - -63 -66 0 - -64 -65 0 - -64 -66 0 - -65 -66 0 - -67 -68 0 - -67 -69 0 - -67 -70 0 - -67 -71 0 - -67 -72 0 - -67 -73 0 - -68 -69 0 - -68 -70 0 - -68 -71 0 - -68 -72 0 - -68 -73 0 - -69 -70 0 - -69 -71 0 - -69 -72 0 - -69 -73 0 - -70 -71 0 - -70 -72 0 - -70 -73 0 - -71 -72 0 - -71 -73 0 - -72 -73 0 - -74 -75 0 - -74 -76 0 - -74 -77 0 - -74 -78 0 - -74 -79 0 - -74 -80 0 - -74 -81 0 - -75 -76 0 - -75 -77 0 - -75 -78 0 - -75 -79 0 - -75 -80 0 - -75 -81 0 - -76 -77 0 - -76 -78 0 - -76 -79 0 - -76 -80 0 - -76 -81 0 - -77 -78 0 - -77 -79 0 - -77 -80 0 - -77 -81 0 - -78 -79 0 - -78 -80 0 - -78 -81 0 - -79 -80 0 - -79 -81 0 - -80 -81 0 - -82 -83 0 - -82 -84 0 - -82 -85 0 - -83 -84 0 - -83 -85 0 - -84 -85 0 - -86 -87 0 - -86 -88 0 - -86 -89 0 - -86 -90 0 - -86 -91 0 - -87 -88 0 - -87 -89 0 - -87 -90 0 - -87 -91 0 - -88 -89 0 - -88 -90 0 - -88 -91 0 - -89 -90 0 - -89 -91 0 - -90 -91 0 - -92 -93 0 - -92 -94 0 - -92 -95 0 - -92 -96 0 - -92 -97 0 - -93 -94 0 - -93 -95 0 - -93 -96 0 - -93 -97 0 - -94 -95 0 - -94 -96 0 - -94 -97 0 - -95 -96 0 - -95 -97 0 - -96 -97 0 - -98 -99 0 - -98 -100 0 - -98 -101 0 - -99 -100 0 - -99 -101 0 - -100 -101 0 - -102 -103 0 - -102 -104 0 - -103 -104 0 - -105 -106 0 - -105 -107 0 - -105 -108 0 - -105 -109 0 - -106 -107 0 - -106 -108 0 - -106 -109 0 - -107 -108 0 - -107 -109 0 - -108 -109 0 - -110 -111 0 - -110 -112 0 - -110 -113 0 - -110 -114 0 - -110 -115 0 - -110 -116 0 - -110 -117 0 - -111 -112 0 - -111 -113 0 - -111 -114 0 - -111 -115 0 - -111 -116 0 - -111 -117 0 - -112 -113 0 - -112 -114 0 - -112 -115 0 - -112 -116 0 - -112 -117 0 - -113 -114 0 - -113 -115 0 - -113 -116 0 - -113 -117 0 - -114 -115 0 - -114 -116 0 - -114 -117 0 - -115 -116 0 - -115 -117 0 - -116 -117 0 - -118 -119 0 - -118 -120 0 - -118 -121 0 - -118 -122 0 - -119 -120 0 - -119 -121 0 - -119 -122 0 - -120 -121 0 - -120 -122 0 - -121 -122 0 - -123 -124 0 - -123 -125 0 - -123 -126 0 - -123 -127 0 - -123 -128 0 - -124 -125 0 - -124 -126 0 - -124 -127 0 - -124 -128 0 - -125 -126 0 - -125 -127 0 - -125 -128 0 - -126 -127 0 - -126 -128 0 - -127 -128 0 - -129 -130 0 - -129 -131 0 - -130 -131 0 - -132 -133 0 - -132 -134 0 - -132 -135 0 - -133 -134 0 - -133 -135 0 - -134 -135 0 - -136 -137 0 - -136 -138 0 - -136 -139 0 - -137 -138 0 - -137 -139 0 - -138 -139 0 - -140 -141 0 - -142 -143 0 - -142 -144 0 - -142 -145 0 - -142 -146 0 - -142 -147 0 - -142 -148 0 - -143 -144 0 - -143 -145 0 - -143 -146 0 - -143 -147 0 - -143 -148 0 - -144 -145 0 - -144 -146 0 - -144 -147 0 - -144 -148 0 - -145 -146 0 - -145 -147 0 - -145 -148 0 - -146 -147 0 - -146 -148 0 - -147 -148 0 - -149 -150 0 - -149 -151 0 - -149 -152 0 - -149 -153 0 - -150 -151 0 - -150 -152 0 - -150 -153 0 - -151 -152 0 - -151 -153 0 - -152 -153 0 - -154 -155 0 - -154 -156 0 - -154 -157 0 - -154 -158 0 - -155 -156 0 - -155 -157 0 - -155 -158 0 - -156 -157 0 - -156 -158 0 - -157 -158 0 - -159 -160 0 - -159 -161 0 - -160 -161 0 - -162 -163 0 - -162 -164 0 - -162 -165 0 - -163 -164 0 - -163 -165 0 - -164 -165 0 - -166 -167 0 - -166 -168 0 - -166 -169 0 - -166 -170 0 - -167 -168 0 - -167 -169 0 - -167 -170 0 - -168 -169 0 - -168 -170 0 - -169 -170 0 - -171 -172 0 - -171 -173 0 - -171 -174 0 - -172 -173 0 - -172 -174 0 - -173 -174 0 - -175 -176 0 - -175 -177 0 - -175 -178 0 - -175 -179 0 - -176 -177 0 - -176 -178 0 - -176 -179 0 - -177 -178 0 - -177 -179 0 - -178 -179 0 - -180 -181 0 - -180 -182 0 - -180 -183 0 - -180 -184 0 - -180 -185 0 - -181 -182 0 - -181 -183 0 - -181 -184 0 - -181 -185 0 - -182 -183 0 - -182 -184 0 - -182 -185 0 - -183 -184 0 - -183 -185 0 - -184 -185 0 - -186 -187 0 - -186 -188 0 - -186 -189 0 - -186 -190 0 - -186 -191 0 - -187 -188 0 - -187 -189 0 - -187 -190 0 - -187 -191 0 - -188 -189 0 - -188 -190 0 - -188 -191 0 - -189 -190 0 - -189 -191 0 - -190 -191 0 - -192 -193 0 - -192 -194 0 - -192 -195 0 - -192 -196 0 - -192 -197 0 - -192 -198 0 - -193 -194 0 - -193 -195 0 - -193 -196 0 - -193 -197 0 - -193 -198 0 - -194 -195 0 - -194 -196 0 - -194 -197 0 - -194 -198 0 - -195 -196 0 - -195 -197 0 - -195 -198 0 - -196 -197 0 - -196 -198 0 - -197 -198 0 - -199 -200 0 - -201 -202 0 - -201 -203 0 - -202 -203 0 - -204 -205 0 - -204 -206 0 - -205 -206 0 - -207 -208 0 - -209 -210 0 - -209 -211 0 - -209 -212 0 - -209 -213 0 - -210 -211 0 - -210 -212 0 - -210 -213 0 - -211 -212 0 - -211 -213 0 - -212 -213 0 - -214 -215 0 - -214 -216 0 - -214 -217 0 - -214 -218 0 - -215 -216 0 - -215 -217 0 - -215 -218 0 - -216 -217 0 - -216 -218 0 - -217 -218 0 - -219 -220 0 - -219 -221 0 - -219 -222 0 - -219 -223 0 - -219 -224 0 - -220 -221 0 - -220 -222 0 - -220 -223 0 - -220 -224 0 - -221 -222 0 - -221 -223 0 - -221 -224 0 - -222 -223 0 - -222 -224 0 - -223 -224 0 - -225 -226 0 - -225 -227 0 - -225 -228 0 - -225 -229 0 - -225 -230 0 - -225 -231 0 - -226 -227 0 - -226 -228 0 - -226 -229 0 - -226 -230 0 - -226 -231 0 - -227 -228 0 - -227 -229 0 - -227 -230 0 - -227 -231 0 - -228 -229 0 - -228 -230 0 - -228 -231 0 - -229 -230 0 - -229 -231 0 - -230 -231 0 - -232 -233 0 - -232 -234 0 - -232 -235 0 - -232 -236 0 - -232 -237 0 - -232 -238 0 - -232 -239 0 - -232 -240 0 - -233 -234 0 - -233 -235 0 - -233 -236 0 - -233 -237 0 - -233 -238 0 - -233 -239 0 - -233 -240 0 - -234 -235 0 - -234 -236 0 - -234 -237 0 - -234 -238 0 - -234 -239 0 - -234 -240 0 - -235 -236 0 - -235 -237 0 - -235 -238 0 - -235 -239 0 - -235 -240 0 - -236 -237 0 - -236 -238 0 - -236 -239 0 - -236 -240 0 - -237 -238 0 - -237 -239 0 - -237 -240 0 - -238 -239 0 - -238 -240 0 - -239 -240 0 - -241 -242 0 - -241 -243 0 - -241 -244 0 - -241 -245 0 - -241 -246 0 - -242 -243 0 - -242 -244 0 - -242 -245 0 - -242 -246 0 - -243 -244 0 - -243 -245 0 - -243 -246 0 - -244 -245 0 - -244 -246 0 - -245 -246 0 - -247 -248 0 - -247 -249 0 - -247 -250 0 - -247 -251 0 - -247 -252 0 - -247 -253 0 - -248 -249 0 - -248 -250 0 - -248 -251 0 - -248 -252 0 - -248 -253 0 - -249 -250 0 - -249 -251 0 - -249 -252 0 - -249 -253 0 - -250 -251 0 - -250 -252 0 - -250 -253 0 - -251 -252 0 - -251 -253 0 - -252 -253 0 - -254 -255 0 - -254 -256 0 - -254 -257 0 - -254 -258 0 - -254 -259 0 - -255 -256 0 - -255 -257 0 - -255 -258 0 - -255 -259 0 - -256 -257 0 - -256 -258 0 - -256 -259 0 - -257 -258 0 - -257 -259 0 - -258 -259 0 - -260 -261 0 - -260 -262 0 - -260 -263 0 - -260 -264 0 - -260 -265 0 - -261 -262 0 - -261 -263 0 - -261 -264 0 - -261 -265 0 - -262 -263 0 - -262 -264 0 - -262 -265 0 - -263 -264 0 - -263 -265 0 - -264 -265 0 - -266 -267 0 - -266 -268 0 - -266 -269 0 - -266 -270 0 - -267 -268 0 - -267 -269 0 - -267 -270 0 - -268 -269 0 - -268 -270 0 - -269 -270 0 - -271 -272 0 - -273 -274 0 - -273 -275 0 - -273 -276 0 - -273 -277 0 - -273 -278 0 - -273 -279 0 - -274 -275 0 - -274 -276 0 - -274 -277 0 - -274 -278 0 - -274 -279 0 - -275 -276 0 - -275 -277 0 - -275 -278 0 - -275 -279 0 - -276 -277 0 - -276 -278 0 - -276 -279 0 - -277 -278 0 - -277 -279 0 - -278 -279 0 - -280 -281 0 - -280 -282 0 - -280 -283 0 - -280 -284 0 - -280 -285 0 - -280 -286 0 - -280 -287 0 - -280 -288 0 - -280 -289 0 - -281 -282 0 - -281 -283 0 - -281 -284 0 - -281 -285 0 - -281 -286 0 - -281 -287 0 - -281 -288 0 - -281 -289 0 - -282 -283 0 - -282 -284 0 - -282 -285 0 - -282 -286 0 - -282 -287 0 - -282 -288 0 - -282 -289 0 - -283 -284 0 - -283 -285 0 - -283 -286 0 - -283 -287 0 - -283 -288 0 - -283 -289 0 - -284 -285 0 - -284 -286 0 - -284 -287 0 - -284 -288 0 - -284 -289 0 - -285 -286 0 - -285 -287 0 - -285 -288 0 - -285 -289 0 - -286 -287 0 - -286 -288 0 - -286 -289 0 - -287 -288 0 - -287 -289 0 - -288 -289 0 - -290 -291 0 - -290 -292 0 - -290 -293 0 - -290 -294 0 - -290 -295 0 - -290 -296 0 - -290 -297 0 - -290 -298 0 - -291 -292 0 - -291 -293 0 - -291 -294 0 - -291 -295 0 - -291 -296 0 - -291 -297 0 - -291 -298 0 - -292 -293 0 - -292 -294 0 - -292 -295 0 - -292 -296 0 - -292 -297 0 - -292 -298 0 - -293 -294 0 - -293 -295 0 - -293 -296 0 - -293 -297 0 - -293 -298 0 - -294 -295 0 - -294 -296 0 - -294 -297 0 - -294 -298 0 - -295 -296 0 - -295 -297 0 - -295 -298 0 - -296 -297 0 - -296 -298 0 - -297 -298 0 - -299 -300 0 - -299 -301 0 - -299 -302 0 - -299 -303 0 - -299 -304 0 - -299 -305 0 - -300 -301 0 - -300 -302 0 - -300 -303 0 - -300 -304 0 - -300 -305 0 - -301 -302 0 - -301 -303 0 - -301 -304 0 - -301 -305 0 - -302 -303 0 - -302 -304 0 - -302 -305 0 - -303 -304 0 - -303 -305 0 - -304 -305 0 - -306 -307 0 - -306 -308 0 - -306 -309 0 - -306 -310 0 - -306 -311 0 - -306 -312 0 - -306 -313 0 - -306 -314 0 - -307 -308 0 - -307 -309 0 - -307 -310 0 - -307 -311 0 - -307 -312 0 - -307 -313 0 - -307 -314 0 - -308 -309 0 - -308 -310 0 - -308 -311 0 - -308 -312 0 - -308 -313 0 - -308 -314 0 - -309 -310 0 - -309 -311 0 - -309 -312 0 - -309 -313 0 - -309 -314 0 - -310 -311 0 - -310 -312 0 - -310 -313 0 - -310 -314 0 - -311 -312 0 - -311 -313 0 - -311 -314 0 - -312 -313 0 - -312 -314 0 - -313 -314 0 - -315 -316 0 - -315 -317 0 - -316 -317 0 - -318 -319 0 - -318 -320 0 - -318 -321 0 - -319 -320 0 - -319 -321 0 - -320 -321 0 - -322 -323 0 - -322 -324 0 - -322 -325 0 - -322 -326 0 - -323 -324 0 - -323 -325 0 - -323 -326 0 - -324 -325 0 - -324 -326 0 - -325 -326 0 - -327 -328 0 - -327 -329 0 - -327 -330 0 - -328 -329 0 - -328 -330 0 - -329 -330 0 - -331 -332 0 - -331 -333 0 - -331 -334 0 - -331 -335 0 - -331 -336 0 - -332 -333 0 - -332 -334 0 - -332 -335 0 - -332 -336 0 - -333 -334 0 - -333 -335 0 - -333 -336 0 - -334 -335 0 - -334 -336 0 - -335 -336 0 - -337 -338 0 - -337 -339 0 - -337 -340 0 - -337 -341 0 - -337 -342 0 - -338 -339 0 - -338 -340 0 - -338 -341 0 - -338 -342 0 - -339 -340 0 - -339 -341 0 - -339 -342 0 - -340 -341 0 - -340 -342 0 - -341 -342 0 - -343 -344 0 - -343 -345 0 - -343 -346 0 - -343 -347 0 - -344 -345 0 - -344 -346 0 - -344 -347 0 - -345 -346 0 - -345 -347 0 - -346 -347 0 - -348 -349 0 - -348 -350 0 - -348 -351 0 - -348 -352 0 - -348 -353 0 - -349 -350 0 - -349 -351 0 - -349 -352 0 - -349 -353 0 - -350 -351 0 - -350 -352 0 - -350 -353 0 - -351 -352 0 - -351 -353 0 - -352 -353 0 - -354 -355 0 - -354 -356 0 - -354 -357 0 - -354 -358 0 - -354 -359 0 - -354 -360 0 - -355 -356 0 - -355 -357 0 - -355 -358 0 - -355 -359 0 - -355 -360 0 - -356 -357 0 - -356 -358 0 - -356 -359 0 - -356 -360 0 - -357 -358 0 - -357 -359 0 - -357 -360 0 - -358 -359 0 - -358 -360 0 - -359 -360 0 - -361 -362 0 - -361 -363 0 - -361 -364 0 - -361 -365 0 - -361 -366 0 - -362 -363 0 - -362 -364 0 - -362 -365 0 - -362 -366 0 - -363 -364 0 - -363 -365 0 - -363 -366 0 - -364 -365 0 - -364 -366 0 - -365 -366 0 - -367 -368 0 - -367 -369 0 - -367 -370 0 - -368 -369 0 - -368 -370 0 - -369 -370 0 - -371 -372 0 - -373 -374 0 - -373 -375 0 - -373 -376 0 - -373 -377 0 - -373 -378 0 - -373 -379 0 - -373 -380 0 - -373 -381 0 - -374 -375 0 - -374 -376 0 - -374 -377 0 - -374 -378 0 - -374 -379 0 - -374 -380 0 - -374 -381 0 - -375 -376 0 - -375 -377 0 - -375 -378 0 - -375 -379 0 - -375 -380 0 - -375 -381 0 - -376 -377 0 - -376 -378 0 - -376 -379 0 - -376 -380 0 - -376 -381 0 - -377 -378 0 - -377 -379 0 - -377 -380 0 - -377 -381 0 - -378 -379 0 - -378 -380 0 - -378 -381 0 - -379 -380 0 - -379 -381 0 - -380 -381 0 - -382 -383 0 - -382 -384 0 - -382 -385 0 - -382 -386 0 - -382 -387 0 - -382 -388 0 - -383 -384 0 - -383 -385 0 - -383 -386 0 - -383 -387 0 - -383 -388 0 - -384 -385 0 - -384 -386 0 - -384 -387 0 - -384 -388 0 - -385 -386 0 - -385 -387 0 - -385 -388 0 - -386 -387 0 - -386 -388 0 - -387 -388 0 - -389 -390 0 - -389 -391 0 - -389 -392 0 - -389 -393 0 - -390 -391 0 - -390 -392 0 - -390 -393 0 - -391 -392 0 - -391 -393 0 - -392 -393 0 - -394 -395 0 - -394 -396 0 - -394 -397 0 - -394 -398 0 - -394 -399 0 - -395 -396 0 - -395 -397 0 - -395 -398 0 - -395 -399 0 - -396 -397 0 - -396 -398 0 - -396 -399 0 - -397 -398 0 - -397 -399 0 - -398 -399 0 - -400 -401 0 - -400 -402 0 - -400 -403 0 - -400 -404 0 - -400 -405 0 - -401 -402 0 - -401 -403 0 - -401 -404 0 - -401 -405 0 - -402 -403 0 - -402 -404 0 - -402 -405 0 - -403 -404 0 - -403 -405 0 - -404 -405 0 - -406 -407 0 - -406 -408 0 - -406 -409 0 - -406 -410 0 - -407 -408 0 - -407 -409 0 - -407 -410 0 - -408 -409 0 - -408 -410 0 - -409 -410 0 - -411 -412 0 - -411 -413 0 - -411 -414 0 - -412 -413 0 - -412 -414 0 - -413 -414 0 - -415 -416 0 - -415 -417 0 - -415 -418 0 - -415 -419 0 - -416 -417 0 - -416 -418 0 - -416 -419 0 - -417 -418 0 - -417 -419 0 - -418 -419 0 - -420 -421 0 - -420 -422 0 - -420 -423 0 - -421 -422 0 - -421 -423 0 - -422 -423 0 - -424 -425 0 - -424 -426 0 - -424 -427 0 - -424 -428 0 - -425 -426 0 - -425 -427 0 - -425 -428 0 - -426 -427 0 - -426 -428 0 - -427 -428 0 - -429 -430 0 - -429 -431 0 - -429 -432 0 - -430 -431 0 - -430 -432 0 - -431 -432 0 - -433 -434 0 - -433 -435 0 - -433 -436 0 - -434 -435 0 - -434 -436 0 - -435 -436 0 - -437 -438 0 - -437 -439 0 - -438 -439 0 - -440 -441 0 - -440 -442 0 - -441 -442 0 - -443 -444 0 - -443 -445 0 - -443 -446 0 - -443 -447 0 - -443 -448 0 - -444 -445 0 - -444 -446 0 - -444 -447 0 - -444 -448 0 - -445 -446 0 - -445 -447 0 - -445 -448 0 - -446 -447 0 - -446 -448 0 - -447 -448 0 - -449 -450 0 - -449 -451 0 - -449 -452 0 - -450 -451 0 - -450 -452 0 - -451 -452 0 - -453 -454 0 - -453 -455 0 - -453 -456 0 - -453 -457 0 - -454 -455 0 - -454 -456 0 - -454 -457 0 - -455 -456 0 - -455 -457 0 - -456 -457 0 - -458 -459 0 - -458 -460 0 - -458 -461 0 - -458 -462 0 - -459 -460 0 - -459 -461 0 - -459 -462 0 - -460 -461 0 - -460 -462 0 - -461 -462 0 - -463 -464 0 - -463 -465 0 - -464 -465 0 - -466 -467 0 - -466 -468 0 - -466 -469 0 - -466 -470 0 - -467 -468 0 - -467 -469 0 - -467 -470 0 - -468 -469 0 - -468 -470 0 - -469 -470 0 - -471 -472 0 - -471 -473 0 - -471 -474 0 - -471 -475 0 - -471 -476 0 - -471 -477 0 - -472 -473 0 - -472 -474 0 - -472 -475 0 - -472 -476 0 - -472 -477 0 - -473 -474 0 - -473 -475 0 - -473 -476 0 - -473 -477 0 - -474 -475 0 - -474 -476 0 - -474 -477 0 - -475 -476 0 - -475 -477 0 - -476 -477 0 - -478 -479 0 - -478 -480 0 - -478 -481 0 - -478 -482 0 - -478 -483 0 - -479 -480 0 - -479 -481 0 - -479 -482 0 - -479 -483 0 - -480 -481 0 - -480 -482 0 - -480 -483 0 - -481 -482 0 - -481 -483 0 - -482 -483 0 - -484 -485 0 - -484 -486 0 - -484 -487 0 - -484 -488 0 - -484 -489 0 - -485 -486 0 - -485 -487 0 - -485 -488 0 - -485 -489 0 - -486 -487 0 - -486 -488 0 - -486 -489 0 - -487 -488 0 - -487 -489 0 - -488 -489 0 - -490 -491 0 - -490 -492 0 - -490 -493 0 - -491 -492 0 - -491 -493 0 - -492 -493 0 - -494 -495 0 - -494 -496 0 - -494 -497 0 - -494 -498 0 - -495 -496 0 - -495 -497 0 - -495 -498 0 - -496 -497 0 - -496 -498 0 - -497 -498 0 - -499 -500 0 - -499 -501 0 - -500 -501 0 - -502 -503 0 - -502 -504 0 - -502 -505 0 - -502 -506 0 - -502 -507 0 - -503 -504 0 - -503 -505 0 - -503 -506 0 - -503 -507 0 - -504 -505 0 - -504 -506 0 - -504 -507 0 - -505 -506 0 - -505 -507 0 - -506 -507 0 - -508 -509 0 - -508 -510 0 - -508 -511 0 - -509 -510 0 - -509 -511 0 - -510 -511 0 - -512 -513 0 - -512 -514 0 - -512 -515 0 - -512 -516 0 - -512 -517 0 - -512 -518 0 - -512 -519 0 - -513 -514 0 - -513 -515 0 - -513 -516 0 - -513 -517 0 - -513 -518 0 - -513 -519 0 - -514 -515 0 - -514 -516 0 - -514 -517 0 - -514 -518 0 - -514 -519 0 - -515 -516 0 - -515 -517 0 - -515 -518 0 - -515 -519 0 - -516 -517 0 - -516 -518 0 - -516 -519 0 - -517 -518 0 - -517 -519 0 - -518 -519 0 - -520 -521 0 - -520 -522 0 - -520 -523 0 - -520 -524 0 - -521 -522 0 - -521 -523 0 - -521 -524 0 - -522 -523 0 - -522 -524 0 - -523 -524 0 - -525 -526 0 - -525 -527 0 - -525 -528 0 - -525 -529 0 - -525 -530 0 - -526 -527 0 - -526 -528 0 - -526 -529 0 - -526 -530 0 - -527 -528 0 - -527 -529 0 - -527 -530 0 - -528 -529 0 - -528 -530 0 - -529 -530 0 - -531 -532 0 - -531 -533 0 - -531 -534 0 - -532 -533 0 - -532 -534 0 - -533 -534 0 - -535 -536 0 - -535 -537 0 - -535 -538 0 - -535 -539 0 - -536 -537 0 - -536 -538 0 - -536 -539 0 - -537 -538 0 - -537 -539 0 - -538 -539 0 - -540 -541 0 - -540 -542 0 - -540 -543 0 - -541 -542 0 - -541 -543 0 - -542 -543 0 - -544 -545 0 - -544 -546 0 - -544 -547 0 - -544 -548 0 - -544 -549 0 - -545 -546 0 - -545 -547 0 - -545 -548 0 - -545 -549 0 - -546 -547 0 - -546 -548 0 - -546 -549 0 - -547 -548 0 - -547 -549 0 - -548 -549 0 - -550 -551 0 - -550 -552 0 - -550 -553 0 - -550 -554 0 - -551 -552 0 - -551 -553 0 - -551 -554 0 - -552 -553 0 - -552 -554 0 - -553 -554 0 - -555 -556 0 - -555 -557 0 - -555 -558 0 - -555 -559 0 - -555 -560 0 - -555 -561 0 - -555 -562 0 - -556 -557 0 - -556 -558 0 - -556 -559 0 - -556 -560 0 - -556 -561 0 - -556 -562 0 - -557 -558 0 - -557 -559 0 - -557 -560 0 - -557 -561 0 - -557 -562 0 - -558 -559 0 - -558 -560 0 - -558 -561 0 - -558 -562 0 - -559 -560 0 - -559 -561 0 - -559 -562 0 - -560 -561 0 - -560 -562 0 - -561 -562 0 - -563 -564 0 - -563 -565 0 - -563 -566 0 - -563 -567 0 - -563 -568 0 - -563 -569 0 - -564 -565 0 - -564 -566 0 - -564 -567 0 - -564 -568 0 - -564 -569 0 - -565 -566 0 - -565 -567 0 - -565 -568 0 - -565 -569 0 - -566 -567 0 - -566 -568 0 - -566 -569 0 - -567 -568 0 - -567 -569 0 - -568 -569 0 - -570 -571 0 - -570 -572 0 - -570 -573 0 - -570 -574 0 - -571 -572 0 - -571 -573 0 - -571 -574 0 - -572 -573 0 - -572 -574 0 - -573 -574 0 - -575 -576 0 - -575 -577 0 - -575 -578 0 - -575 -579 0 - -575 -580 0 - -575 -581 0 - -576 -577 0 - -576 -578 0 - -576 -579 0 - -576 -580 0 - -576 -581 0 - -577 -578 0 - -577 -579 0 - -577 -580 0 - -577 -581 0 - -578 -579 0 - -578 -580 0 - -578 -581 0 - -579 -580 0 - -579 -581 0 - -580 -581 0 - -582 -583 0 - -582 -584 0 - -582 -585 0 - -582 -586 0 - -582 -587 0 - -583 -584 0 - -583 -585 0 - -583 -586 0 - -583 -587 0 - -584 -585 0 - -584 -586 0 - -584 -587 0 - -585 -586 0 - -585 -587 0 - -586 -587 0 - -588 -589 0 - -588 -590 0 - -588 -591 0 - -588 -592 0 - -588 -593 0 - -589 -590 0 - -589 -591 0 - -589 -592 0 - -589 -593 0 - -590 -591 0 - -590 -592 0 - -590 -593 0 - -591 -592 0 - -591 -593 0 - -592 -593 0 - -594 -595 0 - -594 -596 0 - -594 -597 0 - -594 -598 0 - -594 -599 0 - -595 -596 0 - -595 -597 0 - -595 -598 0 - -595 -599 0 - -596 -597 0 - -596 -598 0 - -596 -599 0 - -597 -598 0 - -597 -599 0 - -598 -599 0 - -600 -601 0 - -600 -602 0 - -600 -603 0 - -600 -604 0 - -601 -602 0 - -601 -603 0 - -601 -604 0 - -602 -603 0 - -602 -604 0 - -603 -604 0 - -605 -606 0 - -607 -608 0 - -607 -609 0 - -607 -610 0 - -607 -611 0 - -607 -612 0 - -607 -613 0 - -608 -609 0 - -608 -610 0 - -608 -611 0 - -608 -612 0 - -608 -613 0 - -609 -610 0 - -609 -611 0 - -609 -612 0 - -609 -613 0 - -610 -611 0 - -610 -612 0 - -610 -613 0 - -611 -612 0 - -611 -613 0 - -612 -613 0 - -614 -615 0 - -614 -616 0 - -614 -617 0 - -614 -618 0 - -614 -619 0 - -614 -620 0 - -615 -616 0 - -615 -617 0 - -615 -618 0 - -615 -619 0 - -615 -620 0 - -616 -617 0 - -616 -618 0 - -616 -619 0 - -616 -620 0 - -617 -618 0 - -617 -619 0 - -617 -620 0 - -618 -619 0 - -618 -620 0 - -619 -620 0 - -621 -622 0 - -621 -623 0 - -621 -624 0 - -621 -625 0 - -621 -626 0 - -621 -627 0 - -622 -623 0 - -622 -624 0 - -622 -625 0 - -622 -626 0 - -622 -627 0 - -623 -624 0 - -623 -625 0 - -623 -626 0 - -623 -627 0 - -624 -625 0 - -624 -626 0 - -624 -627 0 - -625 -626 0 - -625 -627 0 - -626 -627 0 - -628 -629 0 - -628 -630 0 - -629 -630 0 - -631 -632 0 - -633 -634 0 - -633 -635 0 - -633 -636 0 - -633 -637 0 - -634 -635 0 - -634 -636 0 - -634 -637 0 - -635 -636 0 - -635 -637 0 - -636 -637 0 - -638 -639 0 - -638 -640 0 - -639 -640 0 - -641 -642 0 - -641 -643 0 - -641 -644 0 - -641 -645 0 - -641 -646 0 - -641 -647 0 - -641 -648 0 - -642 -643 0 - -642 -644 0 - -642 -645 0 - -642 -646 0 - -642 -647 0 - -642 -648 0 - -643 -644 0 - -643 -645 0 - -643 -646 0 - -643 -647 0 - -643 -648 0 - -644 -645 0 - -644 -646 0 - -644 -647 0 - -644 -648 0 - -645 -646 0 - -645 -647 0 - -645 -648 0 - -646 -647 0 - -646 -648 0 - -647 -648 0 - -649 -650 0 - -649 -651 0 - -649 -652 0 - -650 -651 0 - -650 -652 0 - -651 -652 0 - -653 -654 0 - -653 -655 0 - -654 -655 0 - -656 -657 0 - -656 -658 0 - -656 -659 0 - -656 -660 0 - -656 -661 0 - -657 -658 0 - -657 -659 0 - -657 -660 0 - -657 -661 0 - -658 -659 0 - -658 -660 0 - -658 -661 0 - -659 -660 0 - -659 -661 0 - -660 -661 0 - -662 -663 0 - -662 -664 0 - -662 -665 0 - -662 -666 0 - -663 -664 0 - -663 -665 0 - -663 -666 0 - -664 -665 0 - -664 -666 0 - -665 -666 0 - -667 -668 0 - -667 -669 0 - -667 -670 0 - -667 -671 0 - -667 -672 0 - -668 -669 0 - -668 -670 0 - -668 -671 0 - -668 -672 0 - -669 -670 0 - -669 -671 0 - -669 -672 0 - -670 -671 0 - -670 -672 0 - -671 -672 0 - -673 -674 0 - -673 -675 0 - -673 -676 0 - -673 -677 0 - -673 -678 0 - -674 -675 0 - -674 -676 0 - -674 -677 0 - -674 -678 0 - -675 -676 0 - -675 -677 0 - -675 -678 0 - -676 -677 0 - -676 -678 0 - -677 -678 0 - -679 -680 0 - -681 -682 0 - -681 -683 0 - -682 -683 0 - -684 -685 0 - -684 -686 0 - -684 -687 0 - -684 -688 0 - -685 -686 0 - -685 -687 0 - -685 -688 0 - -686 -687 0 - -686 -688 0 - -687 -688 0 - -689 -690 0 - -689 -691 0 - -689 -692 0 - -689 -693 0 - -689 -694 0 - -690 -691 0 - -690 -692 0 - -690 -693 0 - -690 -694 0 - -691 -692 0 - -691 -693 0 - -691 -694 0 - -692 -693 0 - -692 -694 0 - -693 -694 0 - -695 -696 0 - -695 -697 0 - -695 -698 0 - -695 -699 0 - -695 -700 0 - -696 -697 0 - -696 -698 0 - -696 -699 0 - -696 -700 0 - -697 -698 0 - -697 -699 0 - -697 -700 0 - -698 -699 0 - -698 -700 0 - -699 -700 0 - -21 -82 0 - -21 -225 0 - -21 -570 0 - -82 -225 0 - -82 -570 0 - -225 -570 0 - -22 -83 0 - -22 -226 0 - -22 -571 0 - -22 -628 0 - -83 -226 0 - -83 -571 0 - -83 -628 0 - -226 -571 0 - -226 -628 0 - -571 -628 0 - -23 -227 0 - -23 -315 0 - -23 -629 0 - -227 -315 0 - -227 -629 0 - -315 -629 0 - -24 -84 0 - -24 -228 0 - -24 -316 0 - -24 -572 0 - -24 -630 0 - -84 -228 0 - -84 -316 0 - -84 -572 0 - -84 -630 0 - -228 -316 0 - -228 -572 0 - -228 -630 0 - -316 -572 0 - -316 -630 0 - -572 -630 0 - -1 -229 0 - -1 -573 0 - -229 -573 0 - -2 -230 0 - -2 -574 0 - -230 -574 0 - -3 -85 0 - -3 -231 0 - -3 -317 0 - -85 -231 0 - -85 -317 0 - -231 -317 0 - -25 -86 0 - -25 -232 0 - -25 -348 0 - -25 -471 0 - -25 -520 0 - -25 -575 0 - -25 -662 0 - -86 -232 0 - -86 -348 0 - -86 -471 0 - -86 -520 0 - -86 -575 0 - -86 -662 0 - -232 -348 0 - -232 -471 0 - -232 -520 0 - -232 -575 0 - -232 -662 0 - -348 -471 0 - -348 -520 0 - -348 -575 0 - -348 -662 0 - -471 -520 0 - -471 -575 0 - -471 -662 0 - -520 -575 0 - -520 -662 0 - -575 -662 0 - -26 -406 0 - -26 -437 0 - -26 -576 0 - -406 -437 0 - -406 -576 0 - -437 -576 0 - -27 -87 0 - -27 -233 0 - -27 -349 0 - -27 -472 0 - -27 -577 0 - -87 -233 0 - -87 -349 0 - -87 -472 0 - -87 -577 0 - -233 -349 0 - -233 -472 0 - -233 -577 0 - -349 -472 0 - -349 -577 0 - -472 -577 0 - -28 -88 0 - -28 -234 0 - -28 -350 0 - -28 -473 0 - -28 -521 0 - -28 -578 0 - -88 -234 0 - -88 -350 0 - -88 -473 0 - -88 -521 0 - -88 -578 0 - -234 -350 0 - -234 -473 0 - -234 -521 0 - -234 -578 0 - -350 -473 0 - -350 -521 0 - -350 -578 0 - -473 -521 0 - -473 -578 0 - -521 -578 0 - -29 -235 0 - -29 -351 0 - -29 -407 0 - -29 -438 0 - -29 -663 0 - -235 -351 0 - -235 -407 0 - -235 -438 0 - -235 -663 0 - -351 -407 0 - -351 -438 0 - -351 -663 0 - -407 -438 0 - -407 -663 0 - -438 -663 0 - -30 -89 0 - -30 -236 0 - -30 -408 0 - -30 -474 0 - -30 -522 0 - -30 -579 0 - -30 -664 0 - -89 -236 0 - -89 -408 0 - -89 -474 0 - -89 -522 0 - -89 -579 0 - -89 -664 0 - -236 -408 0 - -236 -474 0 - -236 -522 0 - -236 -579 0 - -236 -664 0 - -408 -474 0 - -408 -522 0 - -408 -579 0 - -408 -664 0 - -474 -522 0 - -474 -579 0 - -474 -664 0 - -522 -579 0 - -522 -664 0 - -579 -664 0 - -237 -352 0 - -237 -409 0 - -237 -475 0 - -237 -580 0 - -352 -409 0 - -352 -475 0 - -352 -580 0 - -409 -475 0 - -409 -580 0 - -475 -580 0 - -238 -410 0 - -238 -476 0 - -238 -523 0 - -238 -581 0 - -238 -665 0 - -410 -476 0 - -410 -523 0 - -410 -581 0 - -410 -665 0 - -476 -523 0 - -476 -581 0 - -476 -665 0 - -523 -581 0 - -523 -665 0 - -581 -665 0 - -90 -239 0 - -90 -353 0 - -90 -439 0 - -90 -477 0 - -90 -666 0 - -239 -353 0 - -239 -439 0 - -239 -477 0 - -239 -666 0 - -353 -439 0 - -353 -477 0 - -353 -666 0 - -439 -477 0 - -439 -666 0 - -477 -666 0 - -31 -91 0 - -31 -240 0 - -31 -524 0 - -91 -240 0 - -91 -524 0 - -240 -524 0 - -32 -92 0 - -32 -159 0 - -32 -354 0 - -32 -478 0 - -32 -525 0 - -32 -582 0 - -92 -159 0 - -92 -354 0 - -92 -478 0 - -92 -525 0 - -92 -582 0 - -159 -354 0 - -159 -478 0 - -159 -525 0 - -159 -582 0 - -354 -478 0 - -354 -525 0 - -354 -582 0 - -478 -525 0 - -478 -582 0 - -525 -582 0 - -33 -93 0 - -33 -355 0 - -33 -479 0 - -33 -526 0 - -33 -583 0 - -33 -631 0 - -93 -355 0 - -93 -479 0 - -93 -526 0 - -93 -583 0 - -93 -631 0 - -355 -479 0 - -355 -526 0 - -355 -583 0 - -355 -631 0 - -479 -526 0 - -479 -583 0 - -479 -631 0 - -526 -583 0 - -526 -631 0 - -583 -631 0 - -34 -94 0 - -34 -411 0 - -34 -584 0 - -94 -411 0 - -94 -584 0 - -411 -584 0 - -356 -412 0 - -356 -480 0 - -356 -585 0 - -412 -480 0 - -412 -585 0 - -480 -585 0 - -357 -413 0 - -357 -527 0 - -357 -586 0 - -357 -632 0 - -413 -527 0 - -413 -586 0 - -413 -632 0 - -527 -586 0 - -527 -632 0 - -586 -632 0 - -160 -414 0 - -160 -481 0 - -160 -528 0 - -160 -587 0 - -414 -481 0 - -414 -528 0 - -414 -587 0 - -481 -528 0 - -481 -587 0 - -528 -587 0 - -95 -161 0 - -95 -358 0 - -95 -482 0 - -95 -529 0 - -161 -358 0 - -161 -482 0 - -161 -529 0 - -358 -482 0 - -358 -529 0 - -482 -529 0 - -35 -96 0 - -35 -359 0 - -35 -530 0 - -96 -359 0 - -96 -530 0 - -359 -530 0 - -97 -360 0 - -97 -483 0 - -360 -483 0 - -36 -199 0 - -36 -241 0 - -36 -531 0 - -36 -588 0 - -199 -241 0 - -199 -531 0 - -199 -588 0 - -241 -531 0 - -241 -588 0 - -531 -588 0 - -37 -129 0 - -37 -242 0 - -37 -589 0 - -129 -242 0 - -129 -589 0 - -242 -589 0 - -38 -130 0 - -38 -243 0 - -38 -440 0 - -38 -590 0 - -130 -243 0 - -130 -440 0 - -130 -590 0 - -243 -440 0 - -243 -590 0 - -440 -590 0 - -39 -244 0 - -39 -532 0 - -39 -591 0 - -244 -532 0 - -244 -591 0 - -532 -591 0 - -441 -533 0 - -441 -592 0 - -533 -592 0 - -245 -442 0 - -245 -534 0 - -442 -534 0 - -40 -131 0 - -40 -200 0 - -40 -246 0 - -40 -593 0 - -131 -200 0 - -131 -246 0 - -131 -593 0 - -200 -246 0 - -200 -593 0 - -246 -593 0 - -318 -443 0 - -318 -594 0 - -318 -633 0 - -443 -594 0 - -443 -633 0 - -594 -633 0 - -98 -132 0 - -98 -247 0 - -98 -361 0 - -98 -484 0 - -98 -595 0 - -98 -634 0 - -132 -247 0 - -132 -361 0 - -132 -484 0 - -132 -595 0 - -132 -634 0 - -247 -361 0 - -247 -484 0 - -247 -595 0 - -247 -634 0 - -361 -484 0 - -361 -595 0 - -361 -634 0 - -484 -595 0 - -484 -634 0 - -595 -634 0 - -444 -485 0 - -444 -667 0 - -485 -667 0 - -248 -319 0 - -248 -362 0 - -248 -445 0 - -248 -635 0 - -248 -668 0 - -319 -362 0 - -319 -445 0 - -319 -635 0 - -319 -668 0 - -362 -445 0 - -362 -635 0 - -362 -668 0 - -445 -635 0 - -445 -668 0 - -635 -668 0 - -99 -249 0 - -99 -320 0 - -99 -486 0 - -99 -596 0 - -99 -636 0 - -99 -669 0 - -249 -320 0 - -249 -486 0 - -249 -596 0 - -249 -636 0 - -249 -669 0 - -320 -486 0 - -320 -596 0 - -320 -636 0 - -320 -669 0 - -486 -596 0 - -486 -636 0 - -486 -669 0 - -596 -636 0 - -596 -669 0 - -636 -669 0 - -133 -250 0 - -133 -363 0 - -133 -487 0 - -133 -597 0 - -250 -363 0 - -250 -487 0 - -250 -597 0 - -363 -487 0 - -363 -597 0 - -487 -597 0 - -364 -446 0 - -364 -598 0 - -364 -637 0 - -364 -670 0 - -446 -598 0 - -446 -637 0 - -446 -670 0 - -598 -637 0 - -598 -670 0 - -637 -670 0 - -251 -488 0 - -251 -599 0 - -251 -671 0 - -488 -599 0 - -488 -671 0 - -599 -671 0 - -100 -134 0 - -100 -252 0 - -100 -321 0 - -100 -365 0 - -100 -447 0 - -134 -252 0 - -134 -321 0 - -134 -365 0 - -134 -447 0 - -252 -321 0 - -252 -365 0 - -252 -447 0 - -321 -365 0 - -321 -447 0 - -365 -447 0 - -101 -135 0 - -101 -253 0 - -101 -366 0 - -101 -448 0 - -101 -489 0 - -101 -672 0 - -135 -253 0 - -135 -366 0 - -135 -448 0 - -135 -489 0 - -135 -672 0 - -253 -366 0 - -253 -448 0 - -253 -489 0 - -253 -672 0 - -366 -448 0 - -366 -489 0 - -366 -672 0 - -448 -489 0 - -448 -672 0 - -489 -672 0 - -254 -367 0 - -254 -449 0 - -254 -673 0 - -367 -449 0 - -367 -673 0 - -449 -673 0 - -255 -490 0 - -255 -535 0 - -255 -674 0 - -490 -535 0 - -490 -674 0 - -535 -674 0 - -368 -450 0 - -368 -536 0 - -368 -675 0 - -450 -536 0 - -450 -675 0 - -536 -675 0 - -201 -256 0 - -201 -491 0 - -201 -537 0 - -201 -676 0 - -256 -491 0 - -256 -537 0 - -256 -676 0 - -491 -537 0 - -491 -676 0 - -537 -676 0 - -257 -369 0 - -257 -451 0 - -257 -492 0 - -257 -538 0 - -257 -677 0 - -369 -451 0 - -369 -492 0 - -369 -538 0 - -369 -677 0 - -451 -492 0 - -451 -538 0 - -451 -677 0 - -492 -538 0 - -492 -677 0 - -538 -677 0 - -202 -258 0 - -202 -370 0 - -202 -452 0 - -202 -493 0 - -202 -678 0 - -258 -370 0 - -258 -452 0 - -258 -493 0 - -258 -678 0 - -370 -452 0 - -370 -493 0 - -370 -678 0 - -452 -493 0 - -452 -678 0 - -493 -678 0 - -203 -259 0 - -203 -539 0 - -259 -539 0 - -41 -162 0 - -41 -204 0 - -41 -600 0 - -162 -204 0 - -162 -600 0 - -204 -600 0 - -42 -136 0 - -42 -163 0 - -42 -260 0 - -42 -494 0 - -42 -601 0 - -136 -163 0 - -136 -260 0 - -136 -494 0 - -136 -601 0 - -163 -260 0 - -163 -494 0 - -163 -601 0 - -260 -494 0 - -260 -601 0 - -494 -601 0 - -495 -540 0 - -43 -137 0 - -43 -261 0 - -43 -602 0 - -137 -261 0 - -137 -602 0 - -261 -602 0 - -44 -164 0 - -44 -262 0 - -44 -496 0 - -44 -541 0 - -44 -603 0 - -164 -262 0 - -164 -496 0 - -164 -541 0 - -164 -603 0 - -262 -496 0 - -262 -541 0 - -262 -603 0 - -496 -541 0 - -496 -603 0 - -541 -603 0 - -165 -205 0 - -165 -263 0 - -165 -497 0 - -165 -542 0 - -165 -604 0 - -205 -263 0 - -205 -497 0 - -205 -542 0 - -205 -604 0 - -263 -497 0 - -263 -542 0 - -263 -604 0 - -497 -542 0 - -497 -604 0 - -542 -604 0 - -45 -138 0 - -45 -264 0 - -45 -543 0 - -138 -264 0 - -138 -543 0 - -264 -543 0 - -139 -206 0 - -139 -265 0 - -139 -498 0 - -206 -265 0 - -206 -498 0 - -265 -498 0 - -266 -638 0 - -266 -679 0 - -638 -679 0 - -102 -267 0 - -102 -605 0 - -267 -605 0 - -103 -268 0 - -103 -680 0 - -268 -680 0 - -269 -606 0 - -269 -639 0 - -606 -639 0 - -104 -270 0 - -104 -640 0 - -270 -640 0 - -140 -271 0 - -140 -371 0 - -271 -371 0 - -141 -272 0 - -141 -372 0 - -272 -372 0 - -166 -322 0 - -166 -415 0 - -166 -641 0 - -322 -415 0 - -322 -641 0 - -415 -641 0 - -105 -167 0 - -105 -273 0 - -105 -499 0 - -105 -642 0 - -167 -273 0 - -167 -499 0 - -167 -642 0 - -273 -499 0 - -273 -642 0 - -499 -642 0 - -106 -274 0 - -106 -500 0 - -106 -643 0 - -274 -500 0 - -274 -643 0 - -500 -643 0 - -168 -275 0 - -168 -323 0 - -168 -416 0 - -168 -644 0 - -168 -681 0 - -275 -323 0 - -275 -416 0 - -275 -644 0 - -275 -681 0 - -323 -416 0 - -323 -644 0 - -323 -681 0 - -416 -644 0 - -416 -681 0 - -644 -681 0 - -4 -107 0 - -4 -276 0 - -4 -417 0 - -107 -276 0 - -107 -417 0 - -276 -417 0 - -108 -169 0 - -108 -277 0 - -108 -324 0 - -108 -418 0 - -108 -501 0 - -108 -645 0 - -108 -682 0 - -169 -277 0 - -169 -324 0 - -169 -418 0 - -169 -501 0 - -169 -645 0 - -169 -682 0 - -277 -324 0 - -277 -418 0 - -277 -501 0 - -277 -645 0 - -277 -682 0 - -324 -418 0 - -324 -501 0 - -324 -645 0 - -324 -682 0 - -418 -501 0 - -418 -645 0 - -418 -682 0 - -501 -645 0 - -501 -682 0 - -645 -682 0 - -5 -419 0 - -5 -646 0 - -5 -683 0 - -419 -646 0 - -419 -683 0 - -646 -683 0 - -278 -325 0 - -278 -647 0 - -325 -647 0 - -109 -170 0 - -109 -279 0 - -109 -326 0 - -109 -648 0 - -170 -279 0 - -170 -326 0 - -170 -648 0 - -279 -326 0 - -279 -648 0 - -326 -648 0 - -46 -110 0 - -46 -171 0 - -46 -280 0 - -46 -373 0 - -46 -502 0 - -46 -607 0 - -110 -171 0 - -110 -280 0 - -110 -373 0 - -110 -502 0 - -110 -607 0 - -171 -280 0 - -171 -373 0 - -171 -502 0 - -171 -607 0 - -280 -373 0 - -280 -502 0 - -280 -607 0 - -373 -502 0 - -373 -607 0 - -502 -607 0 - -47 -111 0 - -47 -142 0 - -47 -172 0 - -47 -281 0 - -47 -374 0 - -47 -503 0 - -47 -608 0 - -111 -142 0 - -111 -172 0 - -111 -281 0 - -111 -374 0 - -111 -503 0 - -111 -608 0 - -142 -172 0 - -142 -281 0 - -142 -374 0 - -142 -503 0 - -142 -608 0 - -172 -281 0 - -172 -374 0 - -172 -503 0 - -172 -608 0 - -281 -374 0 - -281 -503 0 - -281 -608 0 - -374 -503 0 - -374 -608 0 - -503 -608 0 - -48 -112 0 - -48 -143 0 - -48 -282 0 - -48 -375 0 - -48 -504 0 - -48 -609 0 - -112 -143 0 - -112 -282 0 - -112 -375 0 - -112 -504 0 - -112 -609 0 - -143 -282 0 - -143 -375 0 - -143 -504 0 - -143 -609 0 - -282 -375 0 - -282 -504 0 - -282 -609 0 - -375 -504 0 - -375 -609 0 - -504 -609 0 - -6 -49 0 - -6 -113 0 - -6 -144 0 - -6 -283 0 - -6 -453 0 - -6 -610 0 - -49 -113 0 - -49 -144 0 - -49 -283 0 - -49 -453 0 - -49 -610 0 - -113 -144 0 - -113 -283 0 - -113 -453 0 - -113 -610 0 - -144 -283 0 - -144 -453 0 - -144 -610 0 - -283 -453 0 - -283 -610 0 - -453 -610 0 - -7 -145 0 - -7 -284 0 - -7 -376 0 - -7 -505 0 - -7 -611 0 - -145 -284 0 - -145 -376 0 - -145 -505 0 - -145 -611 0 - -284 -376 0 - -284 -505 0 - -284 -611 0 - -376 -505 0 - -376 -611 0 - -505 -611 0 - -8 -377 0 - -8 -454 0 - -8 -612 0 - -377 -454 0 - -377 -612 0 - -454 -612 0 - -9 -114 0 - -9 -173 0 - -9 -285 0 - -9 -327 0 - -9 -378 0 - -9 -455 0 - -9 -506 0 - -114 -173 0 - -114 -285 0 - -114 -327 0 - -114 -378 0 - -114 -455 0 - -114 -506 0 - -173 -285 0 - -173 -327 0 - -173 -378 0 - -173 -455 0 - -173 -506 0 - -285 -327 0 - -285 -378 0 - -285 -455 0 - -285 -506 0 - -327 -378 0 - -327 -455 0 - -327 -506 0 - -378 -455 0 - -378 -506 0 - -455 -506 0 - -50 -115 0 - -50 -146 0 - -50 -286 0 - -50 -328 0 - -50 -379 0 - -50 -456 0 - -115 -146 0 - -115 -286 0 - -115 -328 0 - -115 -379 0 - -115 -456 0 - -146 -286 0 - -146 -328 0 - -146 -379 0 - -146 -456 0 - -286 -328 0 - -286 -379 0 - -286 -456 0 - -328 -379 0 - -328 -456 0 - -379 -456 0 - -10 -116 0 - -10 -147 0 - -10 -287 0 - -10 -380 0 - -10 -457 0 - -10 -507 0 - -116 -147 0 - -116 -287 0 - -116 -380 0 - -116 -457 0 - -116 -507 0 - -147 -287 0 - -147 -380 0 - -147 -457 0 - -147 -507 0 - -287 -380 0 - -287 -457 0 - -287 -507 0 - -380 -457 0 - -380 -507 0 - -457 -507 0 - -51 -148 0 - -51 -288 0 - -51 -329 0 - -51 -381 0 - -51 -613 0 - -148 -288 0 - -148 -329 0 - -148 -381 0 - -148 -613 0 - -288 -329 0 - -288 -381 0 - -288 -613 0 - -329 -381 0 - -329 -613 0 - -381 -613 0 - -52 -117 0 - -52 -174 0 - -52 -289 0 - -52 -330 0 - -117 -174 0 - -117 -289 0 - -117 -330 0 - -174 -289 0 - -174 -330 0 - -289 -330 0 - -53 -207 0 - -53 -290 0 - -53 -382 0 - -53 -544 0 - -53 -684 0 - -207 -290 0 - -207 -382 0 - -207 -544 0 - -207 -684 0 - -290 -382 0 - -290 -544 0 - -290 -684 0 - -382 -544 0 - -382 -684 0 - -544 -684 0 - -54 -291 0 - -54 -383 0 - -54 -649 0 - -291 -383 0 - -291 -649 0 - -383 -649 0 - -55 -292 0 - -55 -384 0 - -55 -545 0 - -55 -650 0 - -292 -384 0 - -292 -545 0 - -292 -650 0 - -384 -545 0 - -384 -650 0 - -545 -650 0 - -458 -546 0 - -458 -685 0 - -546 -685 0 - -56 -293 0 - -56 -385 0 - -56 -420 0 - -56 -459 0 - -56 -651 0 - -56 -686 0 - -293 -385 0 - -293 -420 0 - -293 -459 0 - -293 -651 0 - -293 -686 0 - -385 -420 0 - -385 -459 0 - -385 -651 0 - -385 -686 0 - -420 -459 0 - -420 -651 0 - -420 -686 0 - -459 -651 0 - -459 -686 0 - -651 -686 0 - -11 -57 0 - -11 -294 0 - -11 -421 0 - -11 -460 0 - -57 -294 0 - -57 -421 0 - -57 -460 0 - -294 -421 0 - -294 -460 0 - -421 -460 0 - -58 -295 0 - -58 -422 0 - -58 -547 0 - -58 -652 0 - -58 -687 0 - -295 -422 0 - -295 -547 0 - -295 -652 0 - -295 -687 0 - -422 -547 0 - -422 -652 0 - -422 -687 0 - -547 -652 0 - -547 -687 0 - -652 -687 0 - -12 -208 0 - -12 -296 0 - -12 -386 0 - -12 -423 0 - -208 -296 0 - -208 -386 0 - -208 -423 0 - -296 -386 0 - -296 -423 0 - -386 -423 0 - -13 -297 0 - -13 -387 0 - -13 -461 0 - -13 -548 0 - -13 -688 0 - -297 -387 0 - -297 -461 0 - -297 -548 0 - -297 -688 0 - -387 -461 0 - -387 -548 0 - -387 -688 0 - -461 -548 0 - -461 -688 0 - -548 -688 0 - -59 -298 0 - -59 -388 0 - -59 -462 0 - -59 -549 0 - -298 -388 0 - -298 -462 0 - -298 -549 0 - -388 -462 0 - -388 -549 0 - -462 -549 0 - -118 -175 0 - -118 -299 0 - -118 -389 0 - -118 -508 0 - -175 -299 0 - -175 -389 0 - -175 -508 0 - -299 -389 0 - -299 -508 0 - -389 -508 0 - -119 -300 0 - -119 -390 0 - -119 -509 0 - -119 -653 0 - -300 -390 0 - -300 -509 0 - -300 -653 0 - -390 -509 0 - -390 -653 0 - -509 -653 0 - -176 -301 0 - -176 -391 0 - -176 -463 0 - -176 -654 0 - -301 -391 0 - -301 -463 0 - -301 -654 0 - -391 -463 0 - -391 -654 0 - -463 -654 0 - -177 -302 0 - -177 -510 0 - -302 -510 0 - -120 -178 0 - -120 -303 0 - -120 -392 0 - -120 -464 0 - -120 -511 0 - -178 -303 0 - -178 -392 0 - -178 -464 0 - -178 -511 0 - -303 -392 0 - -303 -464 0 - -303 -511 0 - -392 -464 0 - -392 -511 0 - -464 -511 0 - -121 -304 0 - -121 -393 0 - -121 -465 0 - -304 -393 0 - -304 -465 0 - -393 -465 0 - -122 -179 0 - -122 -305 0 - -122 -655 0 - -179 -305 0 - -179 -655 0 - -305 -655 0 - -60 -180 0 - -60 -209 0 - -60 -306 0 - -60 -550 0 - -60 -614 0 - -60 -689 0 - -180 -209 0 - -180 -306 0 - -180 -550 0 - -180 -614 0 - -180 -689 0 - -209 -306 0 - -209 -550 0 - -209 -614 0 - -209 -689 0 - -306 -550 0 - -306 -614 0 - -306 -689 0 - -550 -614 0 - -550 -689 0 - -614 -689 0 - -61 -181 0 - -61 -210 0 - -61 -331 0 - -61 -424 0 - -61 -615 0 - -61 -656 0 - -181 -210 0 - -181 -331 0 - -181 -424 0 - -181 -615 0 - -181 -656 0 - -210 -331 0 - -210 -424 0 - -210 -615 0 - -210 -656 0 - -331 -424 0 - -331 -615 0 - -331 -656 0 - -424 -615 0 - -424 -656 0 - -615 -656 0 - -62 -149 0 - -62 -182 0 - -62 -307 0 - -62 -616 0 - -62 -657 0 - -149 -182 0 - -149 -307 0 - -149 -616 0 - -149 -657 0 - -182 -307 0 - -182 -616 0 - -182 -657 0 - -307 -616 0 - -307 -657 0 - -616 -657 0 - -63 -183 0 - -63 -308 0 - -63 -332 0 - -63 -425 0 - -63 -658 0 - -63 -690 0 - -183 -308 0 - -183 -332 0 - -183 -425 0 - -183 -658 0 - -183 -690 0 - -308 -332 0 - -308 -425 0 - -308 -658 0 - -308 -690 0 - -332 -425 0 - -332 -658 0 - -332 -690 0 - -425 -658 0 - -425 -690 0 - -658 -690 0 - -64 -184 0 - -64 -309 0 - -64 -333 0 - -64 -426 0 - -64 -551 0 - -64 -617 0 - -64 -659 0 - -64 -691 0 - -184 -309 0 - -184 -333 0 - -184 -426 0 - -184 -551 0 - -184 -617 0 - -184 -659 0 - -184 -691 0 - -309 -333 0 - -309 -426 0 - -309 -551 0 - -309 -617 0 - -309 -659 0 - -309 -691 0 - -333 -426 0 - -333 -551 0 - -333 -617 0 - -333 -659 0 - -333 -691 0 - -426 -551 0 - -426 -617 0 - -426 -659 0 - -426 -691 0 - -551 -617 0 - -551 -659 0 - -551 -691 0 - -617 -659 0 - -617 -691 0 - -659 -691 0 - -150 -211 0 - -150 -310 0 - -150 -427 0 - -150 -618 0 - -211 -310 0 - -211 -427 0 - -211 -618 0 - -310 -427 0 - -310 -618 0 - -427 -618 0 - -428 -552 0 - -428 -619 0 - -428 -660 0 - -428 -692 0 - -552 -619 0 - -552 -660 0 - -552 -692 0 - -619 -660 0 - -619 -692 0 - -660 -692 0 - -185 -311 0 - -185 -334 0 - -185 -553 0 - -185 -693 0 - -311 -334 0 - -311 -553 0 - -311 -693 0 - -334 -553 0 - -334 -693 0 - -553 -693 0 - -65 -151 0 - -65 -312 0 - -65 -335 0 - -65 -554 0 - -151 -312 0 - -151 -335 0 - -151 -554 0 - -312 -335 0 - -312 -554 0 - -335 -554 0 - -152 -212 0 - -152 -313 0 - -152 -694 0 - -212 -313 0 - -212 -694 0 - -313 -694 0 - -66 -153 0 - -66 -213 0 - -66 -314 0 - -66 -336 0 - -66 -620 0 - -66 -661 0 - -153 -213 0 - -153 -314 0 - -153 -336 0 - -153 -620 0 - -153 -661 0 - -213 -314 0 - -213 -336 0 - -213 -620 0 - -213 -661 0 - -314 -336 0 - -314 -620 0 - -314 -661 0 - -336 -620 0 - -336 -661 0 - -620 -661 0 - -67 -123 0 - -67 -186 0 - -67 -214 0 - -67 -394 0 - -67 -555 0 - -67 -621 0 - -123 -186 0 - -123 -214 0 - -123 -394 0 - -123 -555 0 - -123 -621 0 - -186 -214 0 - -186 -394 0 - -186 -555 0 - -186 -621 0 - -214 -394 0 - -214 -555 0 - -214 -621 0 - -394 -555 0 - -394 -621 0 - -555 -621 0 - -68 -187 0 - -68 -215 0 - -68 -337 0 - -68 -429 0 - -68 -622 0 - -187 -215 0 - -187 -337 0 - -187 -429 0 - -187 -622 0 - -215 -337 0 - -215 -429 0 - -215 -622 0 - -337 -429 0 - -337 -622 0 - -429 -622 0 - -69 -124 0 - -69 -395 0 - -69 -556 0 - -69 -623 0 - -124 -395 0 - -124 -556 0 - -124 -623 0 - -395 -556 0 - -395 -623 0 - -556 -623 0 - -70 -125 0 - -70 -188 0 - -70 -338 0 - -70 -430 0 - -70 -557 0 - -70 -624 0 - -125 -188 0 - -125 -338 0 - -125 -430 0 - -125 -557 0 - -125 -624 0 - -188 -338 0 - -188 -430 0 - -188 -557 0 - -188 -624 0 - -338 -430 0 - -338 -557 0 - -338 -624 0 - -430 -557 0 - -430 -624 0 - -557 -624 0 - -14 -396 0 - -14 -431 0 - -14 -558 0 - -14 -625 0 - -396 -431 0 - -396 -558 0 - -396 -625 0 - -431 -558 0 - -431 -625 0 - -558 -625 0 - -15 -189 0 - -15 -216 0 - -15 -432 0 - -15 -559 0 - -15 -626 0 - -189 -216 0 - -189 -432 0 - -189 -559 0 - -189 -626 0 - -216 -432 0 - -216 -559 0 - -216 -626 0 - -432 -559 0 - -432 -626 0 - -559 -626 0 - -16 -126 0 - -16 -190 0 - -16 -339 0 - -16 -397 0 - -16 -560 0 - -126 -190 0 - -126 -339 0 - -126 -397 0 - -126 -560 0 - -190 -339 0 - -190 -397 0 - -190 -560 0 - -339 -397 0 - -339 -560 0 - -397 -560 0 - -71 -127 0 - -71 -340 0 - -71 -398 0 - -71 -561 0 - -127 -340 0 - -127 -398 0 - -127 -561 0 - -340 -398 0 - -340 -561 0 - -398 -561 0 - -72 -217 0 - -72 -341 0 - -72 -399 0 - -72 -627 0 - -217 -341 0 - -217 -399 0 - -217 -627 0 - -341 -399 0 - -341 -627 0 - -399 -627 0 - -73 -128 0 - -73 -191 0 - -73 -218 0 - -73 -342 0 - -73 -562 0 - -128 -191 0 - -128 -218 0 - -128 -342 0 - -128 -562 0 - -191 -218 0 - -191 -342 0 - -191 -562 0 - -218 -342 0 - -218 -562 0 - -342 -562 0 - -74 -192 0 - -74 -219 0 - -74 -400 0 - -74 -512 0 - -74 -563 0 - -74 -695 0 - -192 -219 0 - -192 -400 0 - -192 -512 0 - -192 -563 0 - -192 -695 0 - -219 -400 0 - -219 -512 0 - -219 -563 0 - -219 -695 0 - -400 -512 0 - -400 -563 0 - -400 -695 0 - -512 -563 0 - -512 -695 0 - -563 -695 0 - -75 -193 0 - -75 -220 0 - -75 -343 0 - -75 -433 0 - -75 -466 0 - -193 -220 0 - -193 -343 0 - -193 -433 0 - -193 -466 0 - -220 -343 0 - -220 -433 0 - -220 -466 0 - -343 -433 0 - -343 -466 0 - -433 -466 0 - -76 -154 0 - -76 -194 0 - -76 -401 0 - -76 -513 0 - -154 -194 0 - -154 -401 0 - -154 -513 0 - -194 -401 0 - -194 -513 0 - -401 -513 0 - -77 -155 0 - -77 -402 0 - -77 -514 0 - -77 -564 0 - -155 -402 0 - -155 -514 0 - -155 -564 0 - -402 -514 0 - -402 -564 0 - -514 -564 0 - -467 -515 0 - -467 -565 0 - -467 -696 0 - -515 -565 0 - -515 -696 0 - -565 -696 0 - -17 -78 0 - -17 -156 0 - -17 -434 0 - -17 -468 0 - -78 -156 0 - -78 -434 0 - -78 -468 0 - -156 -434 0 - -156 -468 0 - -434 -468 0 - -79 -195 0 - -79 -344 0 - -79 -435 0 - -79 -516 0 - -79 -566 0 - -79 -697 0 - -195 -344 0 - -195 -435 0 - -195 -516 0 - -195 -566 0 - -195 -697 0 - -344 -435 0 - -344 -516 0 - -344 -566 0 - -344 -697 0 - -435 -516 0 - -435 -566 0 - -435 -697 0 - -516 -566 0 - -516 -697 0 - -566 -697 0 - -18 -196 0 - -18 -221 0 - -18 -436 0 - -18 -517 0 - -18 -567 0 - -18 -698 0 - -196 -221 0 - -196 -436 0 - -196 -517 0 - -196 -567 0 - -196 -698 0 - -221 -436 0 - -221 -517 0 - -221 -567 0 - -221 -698 0 - -436 -517 0 - -436 -567 0 - -436 -698 0 - -517 -567 0 - -517 -698 0 - -567 -698 0 - -19 -197 0 - -19 -345 0 - -19 -403 0 - -19 -469 0 - -19 -518 0 - -19 -568 0 - -19 -699 0 - -197 -345 0 - -197 -403 0 - -197 -469 0 - -197 -518 0 - -197 -568 0 - -197 -699 0 - -345 -403 0 - -345 -469 0 - -345 -518 0 - -345 -568 0 - -345 -699 0 - -403 -469 0 - -403 -518 0 - -403 -568 0 - -403 -699 0 - -469 -518 0 - -469 -568 0 - -469 -699 0 - -518 -568 0 - -518 -699 0 - -568 -699 0 - -20 -157 0 - -20 -222 0 - -20 -404 0 - -20 -470 0 - -20 -519 0 - -20 -700 0 - -157 -222 0 - -157 -404 0 - -157 -470 0 - -157 -519 0 - -157 -700 0 - -222 -404 0 - -222 -470 0 - -222 -519 0 - -222 -700 0 - -404 -470 0 - -404 -519 0 - -404 -700 0 - -470 -519 0 - -470 -700 0 - -519 -700 0 - -80 -158 0 - -80 -223 0 - -80 -346 0 - -80 -405 0 - -158 -223 0 - -158 -346 0 - -158 -405 0 - -223 -346 0 - -223 -405 0 - -346 -405 0 - -81 -198 0 - -81 -224 0 - -81 -347 0 - -81 -569 0 - -198 -224 0 - -198 -347 0 - -198 -569 0 - -224 -347 0 - -224 -569 0 - -347 -569 0 - -4 -6 0 - -4 -11 0 - -4 -17 0 - -6 -11 0 - -6 -17 0 - -11 -17 0 - -1 -7 0 - -1 -12 0 - -7 -12 0 - -5 -8 0 - -5 -14 0 - -8 -14 0 - -2 -15 0 - -2 -18 0 - -15 -18 0 - -3 -9 0 - -3 -13 0 - -3 -16 0 - -3 -19 0 - -9 -13 0 - -9 -16 0 - -9 -19 0 - -13 -16 0 - -13 -19 0 - -16 -19 0 - -10 -20 0 - -21 -25 0 - -21 -32 0 - -21 -36 0 - -21 -46 0 - -21 -53 0 - -21 -60 0 - -21 -67 0 - -21 -74 0 - -25 -32 0 - -25 -36 0 - -25 -46 0 - -25 -53 0 - -25 -60 0 - -25 -67 0 - -25 -74 0 - -32 -36 0 - -32 -46 0 - -32 -53 0 - -32 -60 0 - -32 -67 0 - -32 -74 0 - -36 -46 0 - -36 -53 0 - -36 -60 0 - -36 -67 0 - -36 -74 0 - -46 -53 0 - -46 -60 0 - -46 -67 0 - -46 -74 0 - -53 -60 0 - -53 -67 0 - -53 -74 0 - -60 -67 0 - -60 -74 0 - -67 -74 0 - -26 -41 0 - -26 -61 0 - -26 -68 0 - -26 -75 0 - -41 -61 0 - -41 -68 0 - -41 -75 0 - -61 -68 0 - -61 -75 0 - -68 -75 0 - -27 -37 0 - -27 -42 0 - -27 -47 0 - -27 -54 0 - -27 -62 0 - -27 -76 0 - -37 -42 0 - -37 -47 0 - -37 -54 0 - -37 -62 0 - -37 -76 0 - -42 -47 0 - -42 -54 0 - -42 -62 0 - -42 -76 0 - -47 -54 0 - -47 -62 0 - -47 -76 0 - -54 -62 0 - -54 -76 0 - -62 -76 0 - -22 -28 0 - -22 -33 0 - -22 -48 0 - -22 -55 0 - -22 -69 0 - -22 -77 0 - -28 -33 0 - -28 -48 0 - -28 -55 0 - -28 -69 0 - -28 -77 0 - -33 -48 0 - -33 -55 0 - -33 -69 0 - -33 -77 0 - -48 -55 0 - -48 -69 0 - -48 -77 0 - -55 -69 0 - -55 -77 0 - -69 -77 0 - -23 -29 0 - -23 -56 0 - -23 -63 0 - -29 -56 0 - -29 -63 0 - -56 -63 0 - -34 -38 0 - -34 -43 0 - -34 -49 0 - -34 -57 0 - -34 -78 0 - -38 -43 0 - -38 -49 0 - -38 -57 0 - -38 -78 0 - -43 -49 0 - -43 -57 0 - -43 -78 0 - -49 -57 0 - -49 -78 0 - -57 -78 0 - -24 -30 0 - -24 -39 0 - -24 -44 0 - -24 -58 0 - -24 -64 0 - -24 -70 0 - -24 -79 0 - -30 -39 0 - -30 -44 0 - -30 -58 0 - -30 -64 0 - -30 -70 0 - -30 -79 0 - -39 -44 0 - -39 -58 0 - -39 -64 0 - -39 -70 0 - -39 -79 0 - -44 -58 0 - -44 -64 0 - -44 -70 0 - -44 -79 0 - -58 -64 0 - -58 -70 0 - -58 -79 0 - -64 -70 0 - -64 -79 0 - -70 -79 0 - -35 -45 0 - -35 -50 0 - -35 -59 0 - -35 -65 0 - -35 -71 0 - -45 -50 0 - -45 -59 0 - -45 -65 0 - -45 -71 0 - -50 -59 0 - -50 -65 0 - -50 -71 0 - -59 -65 0 - -59 -71 0 - -65 -71 0 - -40 -51 0 - -40 -66 0 - -40 -72 0 - -40 -80 0 - -51 -66 0 - -51 -72 0 - -51 -80 0 - -66 -72 0 - -66 -80 0 - -72 -80 0 - -31 -52 0 - -31 -73 0 - -31 -81 0 - -52 -73 0 - -52 -81 0 - -73 -81 0 - -82 -86 0 - -82 -92 0 - -82 -110 0 - -82 -118 0 - -82 -123 0 - -86 -92 0 - -86 -110 0 - -86 -118 0 - -86 -123 0 - -92 -110 0 - -92 -118 0 - -92 -123 0 - -110 -118 0 - -110 -123 0 - -118 -123 0 - -87 -98 0 - -87 -105 0 - -87 -111 0 - -98 -105 0 - -98 -111 0 - -105 -111 0 - -83 -88 0 - -83 -93 0 - -83 -106 0 - -83 -112 0 - -83 -119 0 - -83 -124 0 - -88 -93 0 - -88 -106 0 - -88 -112 0 - -88 -119 0 - -88 -124 0 - -93 -106 0 - -93 -112 0 - -93 -119 0 - -93 -124 0 - -106 -112 0 - -106 -119 0 - -106 -124 0 - -112 -119 0 - -112 -124 0 - -119 -124 0 - -94 -102 0 - -94 -107 0 - -94 -113 0 - -102 -107 0 - -102 -113 0 - -107 -113 0 - -84 -89 0 - -84 -99 0 - -84 -108 0 - -84 -125 0 - -89 -99 0 - -89 -108 0 - -89 -125 0 - -99 -108 0 - -99 -125 0 - -108 -125 0 - -85 -95 0 - -85 -114 0 - -85 -120 0 - -85 -126 0 - -95 -114 0 - -95 -120 0 - -95 -126 0 - -114 -120 0 - -114 -126 0 - -120 -126 0 - -96 -100 0 - -96 -115 0 - -96 -121 0 - -96 -127 0 - -100 -115 0 - -100 -121 0 - -100 -127 0 - -115 -121 0 - -115 -127 0 - -121 -127 0 - -90 -97 0 - -90 -101 0 - -90 -103 0 - -90 -116 0 - -97 -101 0 - -97 -103 0 - -97 -116 0 - -101 -103 0 - -101 -116 0 - -103 -116 0 - -91 -104 0 - -91 -109 0 - -91 -117 0 - -91 -122 0 - -91 -128 0 - -104 -109 0 - -104 -117 0 - -104 -122 0 - -104 -128 0 - -109 -117 0 - -109 -122 0 - -109 -128 0 - -117 -122 0 - -117 -128 0 - -122 -128 0 - -129 -132 0 - -129 -136 0 - -129 -142 0 - -129 -149 0 - -129 -154 0 - -132 -136 0 - -132 -142 0 - -132 -149 0 - -132 -154 0 - -136 -142 0 - -136 -149 0 - -136 -154 0 - -142 -149 0 - -142 -154 0 - -149 -154 0 - -143 -155 0 - -130 -137 0 - -130 -144 0 - -130 -156 0 - -137 -144 0 - -137 -156 0 - -144 -156 0 - -133 -140 0 - -133 -145 0 - -133 -150 0 - -140 -145 0 - -140 -150 0 - -145 -150 0 - -134 -138 0 - -134 -146 0 - -134 -151 0 - -138 -146 0 - -138 -151 0 - -146 -151 0 - -135 -139 0 - -135 -147 0 - -135 -152 0 - -135 -157 0 - -139 -147 0 - -139 -152 0 - -139 -157 0 - -147 -152 0 - -147 -157 0 - -152 -157 0 - -131 -141 0 - -131 -148 0 - -131 -153 0 - -131 -158 0 - -141 -148 0 - -141 -153 0 - -141 -158 0 - -148 -153 0 - -148 -158 0 - -153 -158 0 - -159 -171 0 - -159 -175 0 - -159 -180 0 - -159 -186 0 - -159 -192 0 - -171 -175 0 - -171 -180 0 - -171 -186 0 - -171 -192 0 - -175 -180 0 - -175 -186 0 - -175 -192 0 - -180 -186 0 - -180 -192 0 - -186 -192 0 - -162 -166 0 - -162 -181 0 - -162 -187 0 - -162 -193 0 - -166 -181 0 - -166 -187 0 - -166 -193 0 - -181 -187 0 - -181 -193 0 - -187 -193 0 - -163 -167 0 - -163 -172 0 - -163 -182 0 - -163 -194 0 - -167 -172 0 - -167 -182 0 - -167 -194 0 - -172 -182 0 - -172 -194 0 - -182 -194 0 - -168 -176 0 - -168 -183 0 - -176 -183 0 - -164 -169 0 - -164 -184 0 - -164 -188 0 - -164 -195 0 - -169 -184 0 - -169 -188 0 - -169 -195 0 - -184 -188 0 - -184 -195 0 - -188 -195 0 - -160 -165 0 - -160 -177 0 - -160 -189 0 - -160 -196 0 - -165 -177 0 - -165 -189 0 - -165 -196 0 - -177 -189 0 - -177 -196 0 - -189 -196 0 - -161 -173 0 - -161 -178 0 - -161 -185 0 - -161 -190 0 - -161 -197 0 - -173 -178 0 - -173 -185 0 - -173 -190 0 - -173 -197 0 - -178 -185 0 - -178 -190 0 - -178 -197 0 - -185 -190 0 - -185 -197 0 - -190 -197 0 - -170 -174 0 - -170 -179 0 - -170 -191 0 - -170 -198 0 - -174 -179 0 - -174 -191 0 - -174 -198 0 - -179 -191 0 - -179 -198 0 - -191 -198 0 - -199 -207 0 - -199 -209 0 - -199 -214 0 - -199 -219 0 - -207 -209 0 - -207 -214 0 - -207 -219 0 - -209 -214 0 - -209 -219 0 - -214 -219 0 - -204 -210 0 - -204 -215 0 - -204 -220 0 - -210 -215 0 - -210 -220 0 - -215 -220 0 - -208 -211 0 - -201 -205 0 - -201 -216 0 - -201 -221 0 - -205 -216 0 - -205 -221 0 - -216 -221 0 - -202 -206 0 - -202 -212 0 - -202 -222 0 - -206 -212 0 - -206 -222 0 - -212 -222 0 - -200 -213 0 - -200 -217 0 - -200 -223 0 - -213 -217 0 - -213 -223 0 - -217 -223 0 - -203 -218 0 - -203 -224 0 - -218 -224 0 - -225 -232 0 - -225 -241 0 - -225 -280 0 - -225 -290 0 - -225 -299 0 - -225 -306 0 - -232 -241 0 - -232 -280 0 - -232 -290 0 - -232 -299 0 - -232 -306 0 - -241 -280 0 - -241 -290 0 - -241 -299 0 - -241 -306 0 - -280 -290 0 - -280 -299 0 - -280 -306 0 - -290 -299 0 - -290 -306 0 - -299 -306 0 - -233 -242 0 - -233 -247 0 - -233 -260 0 - -233 -273 0 - -233 -281 0 - -233 -291 0 - -233 -307 0 - -242 -247 0 - -242 -260 0 - -242 -273 0 - -242 -281 0 - -242 -291 0 - -242 -307 0 - -247 -260 0 - -247 -273 0 - -247 -281 0 - -247 -291 0 - -247 -307 0 - -260 -273 0 - -260 -281 0 - -260 -291 0 - -260 -307 0 - -273 -281 0 - -273 -291 0 - -273 -307 0 - -281 -291 0 - -281 -307 0 - -291 -307 0 - -226 -234 0 - -226 -274 0 - -226 -282 0 - -226 -292 0 - -226 -300 0 - -234 -274 0 - -234 -282 0 - -234 -292 0 - -234 -300 0 - -274 -282 0 - -274 -292 0 - -274 -300 0 - -282 -292 0 - -282 -300 0 - -292 -300 0 - -227 -235 0 - -227 -248 0 - -227 -254 0 - -227 -266 0 - -227 -275 0 - -227 -293 0 - -227 -301 0 - -227 -308 0 - -235 -248 0 - -235 -254 0 - -235 -266 0 - -235 -275 0 - -235 -293 0 - -235 -301 0 - -235 -308 0 - -248 -254 0 - -248 -266 0 - -248 -275 0 - -248 -293 0 - -248 -301 0 - -248 -308 0 - -254 -266 0 - -254 -275 0 - -254 -293 0 - -254 -301 0 - -254 -308 0 - -266 -275 0 - -266 -293 0 - -266 -301 0 - -266 -308 0 - -275 -293 0 - -275 -301 0 - -275 -308 0 - -293 -301 0 - -293 -308 0 - -301 -308 0 - -243 -261 0 - -243 -267 0 - -243 -276 0 - -243 -283 0 - -243 -294 0 - -261 -267 0 - -261 -276 0 - -261 -283 0 - -261 -294 0 - -267 -276 0 - -267 -283 0 - -267 -294 0 - -276 -283 0 - -276 -294 0 - -283 -294 0 - -228 -236 0 - -228 -244 0 - -228 -249 0 - -228 -255 0 - -228 -262 0 - -228 -277 0 - -228 -295 0 - -228 -309 0 - -236 -244 0 - -236 -249 0 - -236 -255 0 - -236 -262 0 - -236 -277 0 - -236 -295 0 - -236 -309 0 - -244 -249 0 - -244 -255 0 - -244 -262 0 - -244 -277 0 - -244 -295 0 - -244 -309 0 - -249 -255 0 - -249 -262 0 - -249 -277 0 - -249 -295 0 - -249 -309 0 - -255 -262 0 - -255 -277 0 - -255 -295 0 - -255 -309 0 - -262 -277 0 - -262 -295 0 - -262 -309 0 - -277 -295 0 - -277 -309 0 - -295 -309 0 - -229 -237 0 - -229 -250 0 - -229 -271 0 - -229 -284 0 - -229 -296 0 - -229 -310 0 - -237 -250 0 - -237 -271 0 - -237 -284 0 - -237 -296 0 - -237 -310 0 - -250 -271 0 - -250 -284 0 - -250 -296 0 - -250 -310 0 - -271 -284 0 - -271 -296 0 - -271 -310 0 - -284 -296 0 - -284 -310 0 - -296 -310 0 - -230 -238 0 - -230 -251 0 - -230 -256 0 - -230 -263 0 - -230 -302 0 - -238 -251 0 - -238 -256 0 - -238 -263 0 - -238 -302 0 - -251 -256 0 - -251 -263 0 - -251 -302 0 - -256 -263 0 - -256 -302 0 - -263 -302 0 - -231 -245 0 - -231 -257 0 - -231 -285 0 - -231 -297 0 - -231 -303 0 - -231 -311 0 - -245 -257 0 - -245 -285 0 - -245 -297 0 - -245 -303 0 - -245 -311 0 - -257 -285 0 - -257 -297 0 - -257 -303 0 - -257 -311 0 - -285 -297 0 - -285 -303 0 - -285 -311 0 - -297 -303 0 - -297 -311 0 - -303 -311 0 - -252 -264 0 - -252 -286 0 - -252 -298 0 - -252 -304 0 - -252 -312 0 - -264 -286 0 - -264 -298 0 - -264 -304 0 - -264 -312 0 - -286 -298 0 - -286 -304 0 - -286 -312 0 - -298 -304 0 - -298 -312 0 - -304 -312 0 - -239 -253 0 - -239 -258 0 - -239 -265 0 - -239 -268 0 - -239 -287 0 - -239 -313 0 - -253 -258 0 - -253 -265 0 - -253 -268 0 - -253 -287 0 - -253 -313 0 - -258 -265 0 - -258 -268 0 - -258 -287 0 - -258 -313 0 - -265 -268 0 - -265 -287 0 - -265 -313 0 - -268 -287 0 - -268 -313 0 - -287 -313 0 - -246 -269 0 - -246 -272 0 - -246 -278 0 - -246 -288 0 - -246 -314 0 - -269 -272 0 - -269 -278 0 - -269 -288 0 - -269 -314 0 - -272 -278 0 - -272 -288 0 - -272 -314 0 - -278 -288 0 - -278 -314 0 - -288 -314 0 - -240 -259 0 - -240 -270 0 - -240 -279 0 - -240 -289 0 - -240 -305 0 - -259 -270 0 - -259 -279 0 - -259 -289 0 - -259 -305 0 - -270 -279 0 - -270 -289 0 - -270 -305 0 - -279 -289 0 - -279 -305 0 - -289 -305 0 - -318 -322 0 - -318 -331 0 - -318 -337 0 - -318 -343 0 - -322 -331 0 - -322 -337 0 - -322 -343 0 - -331 -337 0 - -331 -343 0 - -337 -343 0 - -315 -319 0 - -315 -323 0 - -315 -332 0 - -319 -323 0 - -319 -332 0 - -323 -332 0 - -316 -320 0 - -316 -324 0 - -316 -333 0 - -316 -338 0 - -316 -344 0 - -320 -324 0 - -320 -333 0 - -320 -338 0 - -320 -344 0 - -324 -333 0 - -324 -338 0 - -324 -344 0 - -333 -338 0 - -333 -344 0 - -338 -344 0 - -317 -327 0 - -317 -334 0 - -317 -339 0 - -317 -345 0 - -327 -334 0 - -327 -339 0 - -327 -345 0 - -334 -339 0 - -334 -345 0 - -339 -345 0 - -321 -328 0 - -321 -335 0 - -321 -340 0 - -328 -335 0 - -328 -340 0 - -335 -340 0 - -325 -329 0 - -325 -336 0 - -325 -341 0 - -325 -346 0 - -329 -336 0 - -329 -341 0 - -329 -346 0 - -336 -341 0 - -336 -346 0 - -341 -346 0 - -326 -330 0 - -326 -342 0 - -326 -347 0 - -330 -342 0 - -330 -347 0 - -342 -347 0 - -348 -354 0 - -348 -373 0 - -348 -382 0 - -348 -389 0 - -348 -394 0 - -348 -400 0 - -354 -373 0 - -354 -382 0 - -354 -389 0 - -354 -394 0 - -354 -400 0 - -373 -382 0 - -373 -389 0 - -373 -394 0 - -373 -400 0 - -382 -389 0 - -382 -394 0 - -382 -400 0 - -389 -394 0 - -389 -400 0 - -394 -400 0 - -349 -361 0 - -349 -374 0 - -349 -383 0 - -349 -401 0 - -361 -374 0 - -361 -383 0 - -361 -401 0 - -374 -383 0 - -374 -401 0 - -383 -401 0 - -350 -355 0 - -350 -375 0 - -350 -384 0 - -350 -390 0 - -350 -395 0 - -350 -402 0 - -355 -375 0 - -355 -384 0 - -355 -390 0 - -355 -395 0 - -355 -402 0 - -375 -384 0 - -375 -390 0 - -375 -395 0 - -375 -402 0 - -384 -390 0 - -384 -395 0 - -384 -402 0 - -390 -395 0 - -390 -402 0 - -395 -402 0 - -351 -362 0 - -351 -367 0 - -351 -385 0 - -351 -391 0 - -362 -367 0 - -362 -385 0 - -362 -391 0 - -367 -385 0 - -367 -391 0 - -385 -391 0 - -352 -356 0 - -352 -363 0 - -352 -371 0 - -352 -376 0 - -352 -386 0 - -356 -363 0 - -356 -371 0 - -356 -376 0 - -356 -386 0 - -363 -371 0 - -363 -376 0 - -363 -386 0 - -371 -376 0 - -371 -386 0 - -376 -386 0 - -357 -364 0 - -357 -368 0 - -357 -377 0 - -357 -396 0 - -364 -368 0 - -364 -377 0 - -364 -396 0 - -368 -377 0 - -368 -396 0 - -377 -396 0 - -358 -369 0 - -358 -378 0 - -358 -387 0 - -358 -392 0 - -358 -397 0 - -358 -403 0 - -369 -378 0 - -369 -387 0 - -369 -392 0 - -369 -397 0 - -369 -403 0 - -378 -387 0 - -378 -392 0 - -378 -397 0 - -378 -403 0 - -387 -392 0 - -387 -397 0 - -387 -403 0 - -392 -397 0 - -392 -403 0 - -397 -403 0 - -359 -365 0 - -359 -379 0 - -359 -388 0 - -359 -393 0 - -359 -398 0 - -365 -379 0 - -365 -388 0 - -365 -393 0 - -365 -398 0 - -379 -388 0 - -379 -393 0 - -379 -398 0 - -388 -393 0 - -388 -398 0 - -393 -398 0 - -353 -360 0 - -353 -366 0 - -353 -370 0 - -353 -380 0 - -353 -404 0 - -360 -366 0 - -360 -370 0 - -360 -380 0 - -360 -404 0 - -366 -370 0 - -366 -380 0 - -366 -404 0 - -370 -380 0 - -370 -404 0 - -380 -404 0 - -372 -381 0 - -372 -399 0 - -372 -405 0 - -381 -399 0 - -381 -405 0 - -399 -405 0 - -406 -415 0 - -406 -424 0 - -406 -429 0 - -406 -433 0 - -415 -424 0 - -415 -429 0 - -415 -433 0 - -424 -429 0 - -424 -433 0 - -429 -433 0 - -407 -416 0 - -407 -420 0 - -407 -425 0 - -416 -420 0 - -416 -425 0 - -420 -425 0 - -411 -417 0 - -411 -421 0 - -411 -434 0 - -417 -421 0 - -417 -434 0 - -421 -434 0 - -408 -418 0 - -408 -422 0 - -408 -426 0 - -408 -430 0 - -408 -435 0 - -418 -422 0 - -418 -426 0 - -418 -430 0 - -418 -435 0 - -422 -426 0 - -422 -430 0 - -422 -435 0 - -426 -430 0 - -426 -435 0 - -430 -435 0 - -409 -412 0 - -409 -423 0 - -409 -427 0 - -412 -423 0 - -412 -427 0 - -423 -427 0 - -413 -419 0 - -413 -428 0 - -413 -431 0 - -419 -428 0 - -419 -431 0 - -428 -431 0 - -410 -414 0 - -410 -432 0 - -410 -436 0 - -414 -432 0 - -414 -436 0 - -432 -436 0 - -437 -443 0 - -437 -466 0 - -443 -466 0 - -444 -458 0 - -444 -467 0 - -458 -467 0 - -438 -445 0 - -438 -449 0 - -438 -459 0 - -438 -463 0 - -445 -449 0 - -445 -459 0 - -445 -463 0 - -449 -459 0 - -449 -463 0 - -459 -463 0 - -440 -453 0 - -440 -460 0 - -440 -468 0 - -453 -460 0 - -453 -468 0 - -460 -468 0 - -441 -446 0 - -441 -450 0 - -441 -454 0 - -446 -450 0 - -446 -454 0 - -450 -454 0 - -442 -451 0 - -442 -455 0 - -442 -461 0 - -442 -464 0 - -442 -469 0 - -451 -455 0 - -451 -461 0 - -451 -464 0 - -451 -469 0 - -455 -461 0 - -455 -464 0 - -455 -469 0 - -461 -464 0 - -461 -469 0 - -464 -469 0 - -447 -456 0 - -447 -462 0 - -447 -465 0 - -456 -462 0 - -456 -465 0 - -462 -465 0 - -439 -448 0 - -439 -452 0 - -439 -457 0 - -439 -470 0 - -448 -452 0 - -448 -457 0 - -448 -470 0 - -452 -457 0 - -452 -470 0 - -457 -470 0 - -471 -478 0 - -471 -502 0 - -471 -508 0 - -471 -512 0 - -478 -502 0 - -478 -508 0 - -478 -512 0 - -502 -508 0 - -502 -512 0 - -508 -512 0 - -472 -484 0 - -472 -494 0 - -472 -499 0 - -472 -503 0 - -472 -513 0 - -484 -494 0 - -484 -499 0 - -484 -503 0 - -484 -513 0 - -494 -499 0 - -494 -503 0 - -494 -513 0 - -499 -503 0 - -499 -513 0 - -503 -513 0 - -473 -479 0 - -473 -500 0 - -473 -504 0 - -473 -509 0 - -473 -514 0 - -479 -500 0 - -479 -504 0 - -479 -509 0 - -479 -514 0 - -500 -504 0 - -500 -509 0 - -500 -514 0 - -504 -509 0 - -504 -514 0 - -509 -514 0 - -485 -495 0 - -485 -515 0 - -495 -515 0 - -474 -486 0 - -474 -490 0 - -474 -496 0 - -474 -501 0 - -474 -516 0 - -486 -490 0 - -486 -496 0 - -486 -501 0 - -486 -516 0 - -490 -496 0 - -490 -501 0 - -490 -516 0 - -496 -501 0 - -496 -516 0 - -501 -516 0 - -475 -480 0 - -475 -487 0 - -475 -505 0 - -480 -487 0 - -480 -505 0 - -487 -505 0 - -476 -481 0 - -476 -488 0 - -476 -491 0 - -476 -497 0 - -476 -510 0 - -476 -517 0 - -481 -488 0 - -481 -491 0 - -481 -497 0 - -481 -510 0 - -481 -517 0 - -488 -491 0 - -488 -497 0 - -488 -510 0 - -488 -517 0 - -491 -497 0 - -491 -510 0 - -491 -517 0 - -497 -510 0 - -497 -517 0 - -510 -517 0 - -482 -492 0 - -482 -506 0 - -482 -511 0 - -482 -518 0 - -492 -506 0 - -492 -511 0 - -492 -518 0 - -506 -511 0 - -506 -518 0 - -511 -518 0 - -477 -483 0 - -477 -489 0 - -477 -493 0 - -477 -498 0 - -477 -507 0 - -477 -519 0 - -483 -489 0 - -483 -493 0 - -483 -498 0 - -483 -507 0 - -483 -519 0 - -489 -493 0 - -489 -498 0 - -489 -507 0 - -489 -519 0 - -493 -498 0 - -493 -507 0 - -493 -519 0 - -498 -507 0 - -498 -519 0 - -507 -519 0 - -520 -525 0 - -520 -531 0 - -520 -544 0 - -520 -550 0 - -520 -555 0 - -520 -563 0 - -525 -531 0 - -525 -544 0 - -525 -550 0 - -525 -555 0 - -525 -563 0 - -531 -544 0 - -531 -550 0 - -531 -555 0 - -531 -563 0 - -544 -550 0 - -544 -555 0 - -544 -563 0 - -550 -555 0 - -550 -563 0 - -555 -563 0 - -521 -526 0 - -521 -545 0 - -521 -556 0 - -521 -564 0 - -526 -545 0 - -526 -556 0 - -526 -564 0 - -545 -556 0 - -545 -564 0 - -556 -564 0 - -540 -546 0 - -540 -565 0 - -546 -565 0 - -522 -532 0 - -522 -535 0 - -522 -541 0 - -522 -547 0 - -522 -551 0 - -522 -557 0 - -522 -566 0 - -532 -535 0 - -532 -541 0 - -532 -547 0 - -532 -551 0 - -532 -557 0 - -532 -566 0 - -535 -541 0 - -535 -547 0 - -535 -551 0 - -535 -557 0 - -535 -566 0 - -541 -547 0 - -541 -551 0 - -541 -557 0 - -541 -566 0 - -547 -551 0 - -547 -557 0 - -547 -566 0 - -551 -557 0 - -551 -566 0 - -557 -566 0 - -527 -533 0 - -527 -536 0 - -527 -552 0 - -527 -558 0 - -533 -536 0 - -533 -552 0 - -533 -558 0 - -536 -552 0 - -536 -558 0 - -552 -558 0 - -523 -528 0 - -523 -537 0 - -523 -542 0 - -523 -559 0 - -523 -567 0 - -528 -537 0 - -528 -542 0 - -528 -559 0 - -528 -567 0 - -537 -542 0 - -537 -559 0 - -537 -567 0 - -542 -559 0 - -542 -567 0 - -559 -567 0 - -529 -534 0 - -529 -538 0 - -529 -548 0 - -529 -553 0 - -529 -560 0 - -529 -568 0 - -534 -538 0 - -534 -548 0 - -534 -553 0 - -534 -560 0 - -534 -568 0 - -538 -548 0 - -538 -553 0 - -538 -560 0 - -538 -568 0 - -548 -553 0 - -548 -560 0 - -548 -568 0 - -553 -560 0 - -553 -568 0 - -560 -568 0 - -530 -543 0 - -530 -549 0 - -530 -554 0 - -530 -561 0 - -543 -549 0 - -543 -554 0 - -543 -561 0 - -549 -554 0 - -549 -561 0 - -554 -561 0 - -524 -539 0 - -524 -562 0 - -524 -569 0 - -539 -562 0 - -539 -569 0 - -562 -569 0 - -570 -575 0 - -570 -582 0 - -570 -588 0 - -570 -607 0 - -570 -614 0 - -570 -621 0 - -575 -582 0 - -575 -588 0 - -575 -607 0 - -575 -614 0 - -575 -621 0 - -582 -588 0 - -582 -607 0 - -582 -614 0 - -582 -621 0 - -588 -607 0 - -588 -614 0 - -588 -621 0 - -607 -614 0 - -607 -621 0 - -614 -621 0 - -576 -594 0 - -576 -600 0 - -576 -615 0 - -576 -622 0 - -594 -600 0 - -594 -615 0 - -594 -622 0 - -600 -615 0 - -600 -622 0 - -615 -622 0 - -577 -589 0 - -577 -595 0 - -577 -601 0 - -577 -608 0 - -577 -616 0 - -589 -595 0 - -589 -601 0 - -589 -608 0 - -589 -616 0 - -595 -601 0 - -595 -608 0 - -595 -616 0 - -601 -608 0 - -601 -616 0 - -608 -616 0 - -571 -578 0 - -571 -583 0 - -571 -609 0 - -571 -623 0 - -578 -583 0 - -578 -609 0 - -578 -623 0 - -583 -609 0 - -583 -623 0 - -609 -623 0 - -584 -590 0 - -584 -602 0 - -584 -605 0 - -584 -610 0 - -590 -602 0 - -590 -605 0 - -590 -610 0 - -602 -605 0 - -602 -610 0 - -605 -610 0 - -572 -579 0 - -572 -591 0 - -572 -596 0 - -572 -603 0 - -572 -617 0 - -572 -624 0 - -579 -591 0 - -579 -596 0 - -579 -603 0 - -579 -617 0 - -579 -624 0 - -591 -596 0 - -591 -603 0 - -591 -617 0 - -591 -624 0 - -596 -603 0 - -596 -617 0 - -596 -624 0 - -603 -617 0 - -603 -624 0 - -617 -624 0 - -573 -580 0 - -573 -585 0 - -573 -597 0 - -573 -611 0 - -573 -618 0 - -580 -585 0 - -580 -597 0 - -580 -611 0 - -580 -618 0 - -585 -597 0 - -585 -611 0 - -585 -618 0 - -597 -611 0 - -597 -618 0 - -611 -618 0 - -586 -592 0 - -586 -598 0 - -586 -612 0 - -586 -619 0 - -586 -625 0 - -592 -598 0 - -592 -612 0 - -592 -619 0 - -592 -625 0 - -598 -612 0 - -598 -619 0 - -598 -625 0 - -612 -619 0 - -612 -625 0 - -619 -625 0 - -574 -581 0 - -574 -587 0 - -574 -599 0 - -574 -604 0 - -574 -626 0 - -581 -587 0 - -581 -599 0 - -581 -604 0 - -581 -626 0 - -587 -599 0 - -587 -604 0 - -587 -626 0 - -599 -604 0 - -599 -626 0 - -604 -626 0 - -593 -606 0 - -593 -613 0 - -593 -620 0 - -593 -627 0 - -606 -613 0 - -606 -620 0 - -606 -627 0 - -613 -620 0 - -613 -627 0 - -620 -627 0 - -633 -641 0 - -633 -656 0 - -641 -656 0 - -634 -642 0 - -634 -649 0 - -634 -657 0 - -642 -649 0 - -642 -657 0 - -649 -657 0 - -628 -631 0 - -628 -643 0 - -628 -650 0 - -628 -653 0 - -631 -643 0 - -631 -650 0 - -631 -653 0 - -643 -650 0 - -643 -653 0 - -650 -653 0 - -629 -635 0 - -629 -638 0 - -629 -644 0 - -629 -651 0 - -629 -654 0 - -629 -658 0 - -635 -638 0 - -635 -644 0 - -635 -651 0 - -635 -654 0 - -635 -658 0 - -638 -644 0 - -638 -651 0 - -638 -654 0 - -638 -658 0 - -644 -651 0 - -644 -654 0 - -644 -658 0 - -651 -654 0 - -651 -658 0 - -654 -658 0 - -630 -636 0 - -630 -645 0 - -630 -652 0 - -630 -659 0 - -636 -645 0 - -636 -652 0 - -636 -659 0 - -645 -652 0 - -645 -659 0 - -652 -659 0 - -632 -637 0 - -632 -646 0 - -632 -660 0 - -637 -646 0 - -637 -660 0 - -646 -660 0 - -639 -647 0 - -639 -661 0 - -647 -661 0 - -640 -648 0 - -640 -655 0 - -648 -655 0 - -662 -684 0 - -662 -689 0 - -662 -695 0 - -684 -689 0 - -684 -695 0 - -689 -695 0 - -667 -685 0 - -667 -696 0 - -685 -696 0 - -663 -668 0 - -663 -673 0 - -663 -679 0 - -663 -681 0 - -663 -686 0 - -663 -690 0 - -668 -673 0 - -668 -679 0 - -668 -681 0 - -668 -686 0 - -668 -690 0 - -673 -679 0 - -673 -681 0 - -673 -686 0 - -673 -690 0 - -679 -681 0 - -679 -686 0 - -679 -690 0 - -681 -686 0 - -681 -690 0 - -686 -690 0 - -664 -669 0 - -664 -674 0 - -664 -682 0 - -664 -687 0 - -664 -691 0 - -664 -697 0 - -669 -674 0 - -669 -682 0 - -669 -687 0 - -669 -691 0 - -669 -697 0 - -674 -682 0 - -674 -687 0 - -674 -691 0 - -674 -697 0 - -682 -687 0 - -682 -691 0 - -682 -697 0 - -687 -691 0 - -687 -697 0 - -691 -697 0 - -670 -675 0 - -670 -683 0 - -670 -692 0 - -675 -683 0 - -675 -692 0 - -683 -692 0 - -665 -671 0 - -665 -676 0 - -665 -698 0 - -671 -676 0 - -671 -698 0 - -676 -698 0 - -677 -688 0 - -677 -693 0 - -677 -699 0 - -688 -693 0 - -688 -699 0 - -693 -699 0 - -666 -672 0 - -666 -678 0 - -666 -680 0 - -666 -694 0 - -666 -700 0 - -672 -678 0 - -672 -680 0 - -672 -694 0 - -672 -700 0 - -678 -680 0 - -678 -694 0 - -678 -700 0 - -680 -694 0 - -680 -700 0 - -694 -700 0 - 1 2 3 0 - 4 5 0 - 6 7 8 9 10 0 - 11 12 13 0 - 14 15 16 0 - 17 18 19 20 0 - 21 22 23 24 0 - 25 26 27 28 29 30 31 0 - 32 33 34 35 0 - 36 37 38 39 40 0 - 41 42 43 44 45 0 - 46 47 48 49 50 51 52 0 - 53 54 55 56 57 58 59 0 - 60 61 62 63 64 65 66 0 - 67 68 69 70 71 72 73 0 - 74 75 76 77 78 79 80 81 0 - 82 83 84 85 0 - 86 87 88 89 90 91 0 - 92 93 94 95 96 97 0 - 98 99 100 101 0 - 102 103 104 0 - 105 106 107 108 109 0 - 110 111 112 113 114 115 116 117 0 - 118 119 120 121 122 0 - 123 124 125 126 127 128 0 - 129 130 131 0 - 132 133 134 135 0 - 136 137 138 139 0 - 140 141 0 - 142 143 144 145 146 147 148 0 - 149 150 151 152 153 0 - 154 155 156 157 158 0 - 159 160 161 0 - 162 163 164 165 0 - 166 167 168 169 170 0 - 171 172 173 174 0 - 175 176 177 178 179 0 - 180 181 182 183 184 185 0 - 186 187 188 189 190 191 0 - 192 193 194 195 196 197 198 0 - 199 200 0 - 201 202 203 0 - 204 205 206 0 - 207 208 0 - 209 210 211 212 213 0 - 214 215 216 217 218 0 - 219 220 221 222 223 224 0 - 225 226 227 228 229 230 231 0 - 232 233 234 235 236 237 238 239 240 0 - 241 242 243 244 245 246 0 - 247 248 249 250 251 252 253 0 - 254 255 256 257 258 259 0 - 260 261 262 263 264 265 0 - 266 267 268 269 270 0 - 271 272 0 - 273 274 275 276 277 278 279 0 - 280 281 282 283 284 285 286 287 288 289 0 - 290 291 292 293 294 295 296 297 298 0 - 299 300 301 302 303 304 305 0 - 306 307 308 309 310 311 312 313 314 0 - 315 316 317 0 - 318 319 320 321 0 - 322 323 324 325 326 0 - 327 328 329 330 0 - 331 332 333 334 335 336 0 - 337 338 339 340 341 342 0 - 343 344 345 346 347 0 - 348 349 350 351 352 353 0 - 354 355 356 357 358 359 360 0 - 361 362 363 364 365 366 0 - 367 368 369 370 0 - 371 372 0 - 373 374 375 376 377 378 379 380 381 0 - 382 383 384 385 386 387 388 0 - 389 390 391 392 393 0 - 394 395 396 397 398 399 0 - 400 401 402 403 404 405 0 - 406 407 408 409 410 0 - 411 412 413 414 0 - 415 416 417 418 419 0 - 420 421 422 423 0 - 424 425 426 427 428 0 - 429 430 431 432 0 - 433 434 435 436 0 - 437 438 439 0 - 440 441 442 0 - 443 444 445 446 447 448 0 - 449 450 451 452 0 - 453 454 455 456 457 0 - 458 459 460 461 462 0 - 463 464 465 0 - 466 467 468 469 470 0 - 471 472 473 474 475 476 477 0 - 478 479 480 481 482 483 0 - 484 485 486 487 488 489 0 - 490 491 492 493 0 - 494 495 496 497 498 0 - 499 500 501 0 - 502 503 504 505 506 507 0 - 508 509 510 511 0 - 512 513 514 515 516 517 518 519 0 - 520 521 522 523 524 0 - 525 526 527 528 529 530 0 - 531 532 533 534 0 - 535 536 537 538 539 0 - 540 541 542 543 0 - 544 545 546 547 548 549 0 - 550 551 552 553 554 0 - 555 556 557 558 559 560 561 562 0 - 563 564 565 566 567 568 569 0 - 570 571 572 573 574 0 - 575 576 577 578 579 580 581 0 - 582 583 584 585 586 587 0 - 588 589 590 591 592 593 0 - 594 595 596 597 598 599 0 - 600 601 602 603 604 0 - 605 606 0 - 607 608 609 610 611 612 613 0 - 614 615 616 617 618 619 620 0 - 621 622 623 624 625 626 627 0 - 628 629 630 0 - 631 632 0 - 633 634 635 636 637 0 - 638 639 640 0 - 641 642 643 644 645 646 647 648 0 - 649 650 651 652 0 - 653 654 655 0 - 656 657 658 659 660 661 0 - 662 663 664 665 666 0 - 667 668 669 670 671 672 0 - 673 674 675 676 677 678 0 - 679 680 0 - 681 682 683 0 - 684 685 686 687 688 0 - 689 690 691 692 693 694 0 - 695 696 697 698 699 700 0 - 21 82 225 570 0 - 22 83 226 571 628 0 - 23 227 315 629 0 - 24 84 228 316 572 630 0 - 1 229 573 0 - 2 230 574 0 - 3 85 231 317 0 - 25 86 232 348 471 520 575 662 0 - 26 406 437 576 0 - 27 87 233 349 472 577 0 - 28 88 234 350 473 521 578 0 - 29 235 351 407 438 663 0 - 30 89 236 408 474 522 579 664 0 - 237 352 409 475 580 0 - 238 410 476 523 581 665 0 - 90 239 353 439 477 666 0 - 31 91 240 524 0 - 32 92 159 354 478 525 582 0 - 33 93 355 479 526 583 631 0 - 34 94 411 584 0 - 356 412 480 585 0 - 357 413 527 586 632 0 - 160 414 481 528 587 0 - 95 161 358 482 529 0 - 35 96 359 530 0 - 97 360 483 0 - 36 199 241 531 588 0 - 37 129 242 589 0 - 38 130 243 440 590 0 - 39 244 532 591 0 - 441 533 592 0 - 245 442 534 0 - 40 131 200 246 593 0 - 318 443 594 633 0 - 98 132 247 361 484 595 634 0 - 444 485 667 0 - 248 319 362 445 635 668 0 - 99 249 320 486 596 636 669 0 - 133 250 363 487 597 0 - 364 446 598 637 670 0 - 251 488 599 671 0 - 100 134 252 321 365 447 0 - 101 135 253 366 448 489 672 0 - 254 367 449 673 0 - 255 490 535 674 0 - 368 450 536 675 0 - 201 256 491 537 676 0 - 257 369 451 492 538 677 0 - 202 258 370 452 493 678 0 - 203 259 539 0 - 41 162 204 600 0 - 42 136 163 260 494 601 0 - 495 540 0 - 43 137 261 602 0 - 44 164 262 496 541 603 0 - 165 205 263 497 542 604 0 - 45 138 264 543 0 - 139 206 265 498 0 - 266 638 679 0 - 102 267 605 0 - 103 268 680 0 - 269 606 639 0 - 104 270 640 0 - 140 271 371 0 - 141 272 372 0 - 166 322 415 641 0 - 105 167 273 499 642 0 - 106 274 500 643 0 - 168 275 323 416 644 681 0 - 4 107 276 417 0 - 108 169 277 324 418 501 645 682 0 - 5 419 646 683 0 - 278 325 647 0 - 109 170 279 326 648 0 - 46 110 171 280 373 502 607 0 - 47 111 142 172 281 374 503 608 0 - 48 112 143 282 375 504 609 0 - 6 49 113 144 283 453 610 0 - 7 145 284 376 505 611 0 - 8 377 454 612 0 - 9 114 173 285 327 378 455 506 0 - 50 115 146 286 328 379 456 0 - 10 116 147 287 380 457 507 0 - 51 148 288 329 381 613 0 - 52 117 174 289 330 0 - 53 207 290 382 544 684 0 - 54 291 383 649 0 - 55 292 384 545 650 0 - 458 546 685 0 - 56 293 385 420 459 651 686 0 - 11 57 294 421 460 0 - 58 295 422 547 652 687 0 - 12 208 296 386 423 0 - 13 297 387 461 548 688 0 - 59 298 388 462 549 0 - 118 175 299 389 508 0 - 119 300 390 509 653 0 - 176 301 391 463 654 0 - 177 302 510 0 - 120 178 303 392 464 511 0 - 121 304 393 465 0 - 122 179 305 655 0 - 60 180 209 306 550 614 689 0 - 61 181 210 331 424 615 656 0 - 62 149 182 307 616 657 0 - 63 183 308 332 425 658 690 0 - 64 184 309 333 426 551 617 659 691 0 - 150 211 310 427 618 0 - 428 552 619 660 692 0 - 185 311 334 553 693 0 - 65 151 312 335 554 0 - 152 212 313 694 0 - 66 153 213 314 336 620 661 0 - 67 123 186 214 394 555 621 0 - 68 187 215 337 429 622 0 - 69 124 395 556 623 0 - 70 125 188 338 430 557 624 0 - 14 396 431 558 625 0 - 15 189 216 432 559 626 0 - 16 126 190 339 397 560 0 - 71 127 340 398 561 0 - 72 217 341 399 627 0 - 73 128 191 218 342 562 0 - 74 192 219 400 512 563 695 0 - 75 193 220 343 433 466 0 - 76 154 194 401 513 0 - 77 155 402 514 564 0 - 467 515 565 696 0 - 17 78 156 434 468 0 - 79 195 344 435 516 566 697 0 - 18 196 221 436 517 567 698 0 - 19 197 345 403 469 518 568 699 0 - 20 157 222 404 470 519 700 0 - 80 158 223 346 405 0 - 81 198 224 347 569 0 - 4 6 11 17 0 - 1 7 12 0 - 5 8 14 0 - 2 15 18 0 - 3 9 13 16 19 0 - 10 20 0 - 21 25 32 36 46 53 60 67 74 0 - 26 41 61 68 75 0 - 27 37 42 47 54 62 76 0 - 22 28 33 48 55 69 77 0 - 23 29 56 63 0 - 34 38 43 49 57 78 0 - 24 30 39 44 58 64 70 79 0 - 35 45 50 59 65 71 0 - 40 51 66 72 80 0 - 31 52 73 81 0 - 82 86 92 110 118 123 0 - 87 98 105 111 0 - 83 88 93 106 112 119 124 0 - 94 102 107 113 0 - 84 89 99 108 125 0 - 85 95 114 120 126 0 - 96 100 115 121 127 0 - 90 97 101 103 116 0 - 91 104 109 117 122 128 0 - 129 132 136 142 149 154 0 - 143 155 0 - 130 137 144 156 0 - 133 140 145 150 0 - 134 138 146 151 0 - 135 139 147 152 157 0 - 131 141 148 153 158 0 - 159 171 175 180 186 192 0 - 162 166 181 187 193 0 - 163 167 172 182 194 0 - 168 176 183 0 - 164 169 184 188 195 0 - 160 165 177 189 196 0 - 161 173 178 185 190 197 0 - 170 174 179 191 198 0 - 199 207 209 214 219 0 - 204 210 215 220 0 - 208 211 0 - 201 205 216 221 0 - 202 206 212 222 0 - 200 213 217 223 0 - 203 218 224 0 - 225 232 241 280 290 299 306 0 - 233 242 247 260 273 281 291 307 0 - 226 234 274 282 292 300 0 - 227 235 248 254 266 275 293 301 308 0 - 243 261 267 276 283 294 0 - 228 236 244 249 255 262 277 295 309 0 - 229 237 250 271 284 296 310 0 - 230 238 251 256 263 302 0 - 231 245 257 285 297 303 311 0 - 252 264 286 298 304 312 0 - 239 253 258 265 268 287 313 0 - 246 269 272 278 288 314 0 - 240 259 270 279 289 305 0 - 318 322 331 337 343 0 - 315 319 323 332 0 - 316 320 324 333 338 344 0 - 317 327 334 339 345 0 - 321 328 335 340 0 - 325 329 336 341 346 0 - 326 330 342 347 0 - 348 354 373 382 389 394 400 0 - 349 361 374 383 401 0 - 350 355 375 384 390 395 402 0 - 351 362 367 385 391 0 - 352 356 363 371 376 386 0 - 357 364 368 377 396 0 - 358 369 378 387 392 397 403 0 - 359 365 379 388 393 398 0 - 353 360 366 370 380 404 0 - 372 381 399 405 0 - 406 415 424 429 433 0 - 407 416 420 425 0 - 411 417 421 434 0 - 408 418 422 426 430 435 0 - 409 412 423 427 0 - 413 419 428 431 0 - 410 414 432 436 0 - 437 443 466 0 - 444 458 467 0 - 438 445 449 459 463 0 - 440 453 460 468 0 - 441 446 450 454 0 - 442 451 455 461 464 469 0 - 447 456 462 465 0 - 439 448 452 457 470 0 - 471 478 502 508 512 0 - 472 484 494 499 503 513 0 - 473 479 500 504 509 514 0 - 485 495 515 0 - 474 486 490 496 501 516 0 - 475 480 487 505 0 - 476 481 488 491 497 510 517 0 - 482 492 506 511 518 0 - 477 483 489 493 498 507 519 0 - 520 525 531 544 550 555 563 0 - 521 526 545 556 564 0 - 540 546 565 0 - 522 532 535 541 547 551 557 566 0 - 527 533 536 552 558 0 - 523 528 537 542 559 567 0 - 529 534 538 548 553 560 568 0 - 530 543 549 554 561 0 - 524 539 562 569 0 - 570 575 582 588 607 614 621 0 - 576 594 600 615 622 0 - 577 589 595 601 608 616 0 - 571 578 583 609 623 0 - 584 590 602 605 610 0 - 572 579 591 596 603 617 624 0 - 573 580 585 597 611 618 0 - 586 592 598 612 619 625 0 - 574 581 587 599 604 626 0 - 593 606 613 620 627 0 - 633 641 656 0 - 634 642 649 657 0 - 628 631 643 650 653 0 - 629 635 638 644 651 654 658 0 - 630 636 645 652 659 0 - 632 637 646 660 0 - 639 647 661 0 - 640 648 655 0 - 662 684 689 695 0 - 667 685 696 0 - 663 668 673 679 681 686 690 0 - 664 669 674 682 687 691 697 0 - 670 675 683 692 0 - 665 671 676 698 0 - 677 688 693 699 0 - 666 672 678 680 694 700 0 diff --git a/examples/commandline/spear_qcp/instances/qcplin2006.10641.cnf b/examples/commandline/spear_qcp/instances/qcplin2006.10641.cnf deleted file mode 100755 index c18dee2ef..000000000 --- a/examples/commandline/spear_qcp/instances/qcplin2006.10641.cnf +++ /dev/null @@ -1,23451 +0,0 @@ -p cnf 2601 23450 - -1 -2 0 - -1 -3 0 - -1 -4 0 - -1 -5 0 - -1 -6 0 - -2 -3 0 - -2 -4 0 - -2 -5 0 - -2 -6 0 - -3 -4 0 - -3 -5 0 - -3 -6 0 - -4 -5 0 - -4 -6 0 - -5 -6 0 - -7 -8 0 - -7 -9 0 - -7 -10 0 - -7 -11 0 - -7 -12 0 - -8 -9 0 - -8 -10 0 - -8 -11 0 - -8 -12 0 - -9 -10 0 - -9 -11 0 - -9 -12 0 - -10 -11 0 - -10 -12 0 - -11 -12 0 - -13 -14 0 - -13 -15 0 - -13 -16 0 - -14 -15 0 - -14 -16 0 - -15 -16 0 - -17 -18 0 - -17 -19 0 - -17 -20 0 - -18 -19 0 - -18 -20 0 - -19 -20 0 - -21 -22 0 - -21 -23 0 - -22 -23 0 - -24 -25 0 - -24 -26 0 - -24 -27 0 - -24 -28 0 - -25 -26 0 - -25 -27 0 - -25 -28 0 - -26 -27 0 - -26 -28 0 - -27 -28 0 - -29 -30 0 - -29 -31 0 - -29 -32 0 - -29 -33 0 - -29 -34 0 - -29 -35 0 - -29 -36 0 - -30 -31 0 - -30 -32 0 - -30 -33 0 - -30 -34 0 - -30 -35 0 - -30 -36 0 - -31 -32 0 - -31 -33 0 - -31 -34 0 - -31 -35 0 - -31 -36 0 - -32 -33 0 - -32 -34 0 - -32 -35 0 - -32 -36 0 - -33 -34 0 - -33 -35 0 - -33 -36 0 - -34 -35 0 - -34 -36 0 - -35 -36 0 - -37 -38 0 - -37 -39 0 - -37 -40 0 - -38 -39 0 - -38 -40 0 - -39 -40 0 - -41 -42 0 - -41 -43 0 - -42 -43 0 - -44 -45 0 - -46 -47 0 - -46 -48 0 - -46 -49 0 - -46 -50 0 - -46 -51 0 - -47 -48 0 - -47 -49 0 - -47 -50 0 - -47 -51 0 - -48 -49 0 - -48 -50 0 - -48 -51 0 - -49 -50 0 - -49 -51 0 - -50 -51 0 - -52 -53 0 - -52 -54 0 - -52 -55 0 - -53 -54 0 - -53 -55 0 - -54 -55 0 - -56 -57 0 - -56 -58 0 - -57 -58 0 - -59 -60 0 - -59 -61 0 - -59 -62 0 - -59 -63 0 - -60 -61 0 - -60 -62 0 - -60 -63 0 - -61 -62 0 - -61 -63 0 - -62 -63 0 - -64 -65 0 - -64 -66 0 - -64 -67 0 - -64 -68 0 - -65 -66 0 - -65 -67 0 - -65 -68 0 - -66 -67 0 - -66 -68 0 - -67 -68 0 - -69 -70 0 - -69 -71 0 - -69 -72 0 - -69 -73 0 - -69 -74 0 - -70 -71 0 - -70 -72 0 - -70 -73 0 - -70 -74 0 - -71 -72 0 - -71 -73 0 - -71 -74 0 - -72 -73 0 - -72 -74 0 - -73 -74 0 - -75 -76 0 - -75 -77 0 - -76 -77 0 - -78 -79 0 - -80 -81 0 - -80 -82 0 - -80 -83 0 - -80 -84 0 - -80 -85 0 - -80 -86 0 - -81 -82 0 - -81 -83 0 - -81 -84 0 - -81 -85 0 - -81 -86 0 - -82 -83 0 - -82 -84 0 - -82 -85 0 - -82 -86 0 - -83 -84 0 - -83 -85 0 - -83 -86 0 - -84 -85 0 - -84 -86 0 - -85 -86 0 - -87 -88 0 - -87 -89 0 - -88 -89 0 - -90 -91 0 - -90 -92 0 - -91 -92 0 - -93 -94 0 - -93 -95 0 - -93 -96 0 - -93 -97 0 - -94 -95 0 - -94 -96 0 - -94 -97 0 - -95 -96 0 - -95 -97 0 - -96 -97 0 - -98 -99 0 - -98 -100 0 - -98 -101 0 - -98 -102 0 - -99 -100 0 - -99 -101 0 - -99 -102 0 - -100 -101 0 - -100 -102 0 - -101 -102 0 - -103 -104 0 - -103 -105 0 - -103 -106 0 - -104 -105 0 - -104 -106 0 - -105 -106 0 - -107 -108 0 - -107 -109 0 - -107 -110 0 - -107 -111 0 - -108 -109 0 - -108 -110 0 - -108 -111 0 - -109 -110 0 - -109 -111 0 - -110 -111 0 - -112 -113 0 - -112 -114 0 - -112 -115 0 - -112 -116 0 - -112 -117 0 - -112 -118 0 - -113 -114 0 - -113 -115 0 - -113 -116 0 - -113 -117 0 - -113 -118 0 - -114 -115 0 - -114 -116 0 - -114 -117 0 - -114 -118 0 - -115 -116 0 - -115 -117 0 - -115 -118 0 - -116 -117 0 - -116 -118 0 - -117 -118 0 - -119 -120 0 - -119 -121 0 - -119 -122 0 - -119 -123 0 - -119 -124 0 - -119 -125 0 - -120 -121 0 - -120 -122 0 - -120 -123 0 - -120 -124 0 - -120 -125 0 - -121 -122 0 - -121 -123 0 - -121 -124 0 - -121 -125 0 - -122 -123 0 - -122 -124 0 - -122 -125 0 - -123 -124 0 - -123 -125 0 - -124 -125 0 - -126 -127 0 - -126 -128 0 - -126 -129 0 - -126 -130 0 - -126 -131 0 - -127 -128 0 - -127 -129 0 - -127 -130 0 - -127 -131 0 - -128 -129 0 - -128 -130 0 - -128 -131 0 - -129 -130 0 - -129 -131 0 - -130 -131 0 - -132 -133 0 - -132 -134 0 - -132 -135 0 - -132 -136 0 - -133 -134 0 - -133 -135 0 - -133 -136 0 - -134 -135 0 - -134 -136 0 - -135 -136 0 - -137 -138 0 - -137 -139 0 - -137 -140 0 - -137 -141 0 - -138 -139 0 - -138 -140 0 - -138 -141 0 - -139 -140 0 - -139 -141 0 - -140 -141 0 - -142 -143 0 - -142 -144 0 - -142 -145 0 - -142 -146 0 - -142 -147 0 - -142 -148 0 - -143 -144 0 - -143 -145 0 - -143 -146 0 - -143 -147 0 - -143 -148 0 - -144 -145 0 - -144 -146 0 - -144 -147 0 - -144 -148 0 - -145 -146 0 - -145 -147 0 - -145 -148 0 - -146 -147 0 - -146 -148 0 - -147 -148 0 - -149 -150 0 - -149 -151 0 - -149 -152 0 - -150 -151 0 - -150 -152 0 - -151 -152 0 - -153 -154 0 - -155 -156 0 - -155 -157 0 - -155 -158 0 - -155 -159 0 - -155 -160 0 - -155 -161 0 - -156 -157 0 - -156 -158 0 - -156 -159 0 - -156 -160 0 - -156 -161 0 - -157 -158 0 - -157 -159 0 - -157 -160 0 - -157 -161 0 - -158 -159 0 - -158 -160 0 - -158 -161 0 - -159 -160 0 - -159 -161 0 - -160 -161 0 - -162 -163 0 - -162 -164 0 - -162 -165 0 - -163 -164 0 - -163 -165 0 - -164 -165 0 - -166 -167 0 - -166 -168 0 - -166 -169 0 - -166 -170 0 - -166 -171 0 - -166 -172 0 - -167 -168 0 - -167 -169 0 - -167 -170 0 - -167 -171 0 - -167 -172 0 - -168 -169 0 - -168 -170 0 - -168 -171 0 - -168 -172 0 - -169 -170 0 - -169 -171 0 - -169 -172 0 - -170 -171 0 - -170 -172 0 - -171 -172 0 - -173 -174 0 - -173 -175 0 - -174 -175 0 - -176 -177 0 - -176 -178 0 - -177 -178 0 - -179 -180 0 - -179 -181 0 - -179 -182 0 - -179 -183 0 - -179 -184 0 - -180 -181 0 - -180 -182 0 - -180 -183 0 - -180 -184 0 - -181 -182 0 - -181 -183 0 - -181 -184 0 - -182 -183 0 - -182 -184 0 - -183 -184 0 - -185 -186 0 - -185 -187 0 - -185 -188 0 - -185 -189 0 - -185 -190 0 - -185 -191 0 - -186 -187 0 - -186 -188 0 - -186 -189 0 - -186 -190 0 - -186 -191 0 - -187 -188 0 - -187 -189 0 - -187 -190 0 - -187 -191 0 - -188 -189 0 - -188 -190 0 - -188 -191 0 - -189 -190 0 - -189 -191 0 - -190 -191 0 - -192 -193 0 - -192 -194 0 - -192 -195 0 - -193 -194 0 - -193 -195 0 - -194 -195 0 - -196 -197 0 - -196 -198 0 - -197 -198 0 - -199 -200 0 - -199 -201 0 - -199 -202 0 - -199 -203 0 - -199 -204 0 - -199 -205 0 - -200 -201 0 - -200 -202 0 - -200 -203 0 - -200 -204 0 - -200 -205 0 - -201 -202 0 - -201 -203 0 - -201 -204 0 - -201 -205 0 - -202 -203 0 - -202 -204 0 - -202 -205 0 - -203 -204 0 - -203 -205 0 - -204 -205 0 - -206 -207 0 - -206 -208 0 - -206 -209 0 - -207 -208 0 - -207 -209 0 - -208 -209 0 - -210 -211 0 - -210 -212 0 - -210 -213 0 - -210 -214 0 - -210 -215 0 - -210 -216 0 - -211 -212 0 - -211 -213 0 - -211 -214 0 - -211 -215 0 - -211 -216 0 - -212 -213 0 - -212 -214 0 - -212 -215 0 - -212 -216 0 - -213 -214 0 - -213 -215 0 - -213 -216 0 - -214 -215 0 - -214 -216 0 - -215 -216 0 - -217 -218 0 - -217 -219 0 - -217 -220 0 - -218 -219 0 - -218 -220 0 - -219 -220 0 - -221 -222 0 - -221 -223 0 - -221 -224 0 - -221 -225 0 - -221 -226 0 - -222 -223 0 - -222 -224 0 - -222 -225 0 - -222 -226 0 - -223 -224 0 - -223 -225 0 - -223 -226 0 - -224 -225 0 - -224 -226 0 - -225 -226 0 - -227 -228 0 - -227 -229 0 - -227 -230 0 - -227 -231 0 - -227 -232 0 - -228 -229 0 - -228 -230 0 - -228 -231 0 - -228 -232 0 - -229 -230 0 - -229 -231 0 - -229 -232 0 - -230 -231 0 - -230 -232 0 - -231 -232 0 - -233 -234 0 - -233 -235 0 - -233 -236 0 - -233 -237 0 - -233 -238 0 - -233 -239 0 - -234 -235 0 - -234 -236 0 - -234 -237 0 - -234 -238 0 - -234 -239 0 - -235 -236 0 - -235 -237 0 - -235 -238 0 - -235 -239 0 - -236 -237 0 - -236 -238 0 - -236 -239 0 - -237 -238 0 - -237 -239 0 - -238 -239 0 - -240 -241 0 - -240 -242 0 - -240 -243 0 - -240 -244 0 - -240 -245 0 - -240 -246 0 - -240 -247 0 - -240 -248 0 - -241 -242 0 - -241 -243 0 - -241 -244 0 - -241 -245 0 - -241 -246 0 - -241 -247 0 - -241 -248 0 - -242 -243 0 - -242 -244 0 - -242 -245 0 - -242 -246 0 - -242 -247 0 - -242 -248 0 - -243 -244 0 - -243 -245 0 - -243 -246 0 - -243 -247 0 - -243 -248 0 - -244 -245 0 - -244 -246 0 - -244 -247 0 - -244 -248 0 - -245 -246 0 - -245 -247 0 - -245 -248 0 - -246 -247 0 - -246 -248 0 - -247 -248 0 - -249 -250 0 - -249 -251 0 - -249 -252 0 - -250 -251 0 - -250 -252 0 - -251 -252 0 - -253 -254 0 - -253 -255 0 - -253 -256 0 - -254 -255 0 - -254 -256 0 - -255 -256 0 - -257 -258 0 - -257 -259 0 - -257 -260 0 - -258 -259 0 - -258 -260 0 - -259 -260 0 - -261 -262 0 - -261 -263 0 - -261 -264 0 - -261 -265 0 - -261 -266 0 - -261 -267 0 - -261 -268 0 - -262 -263 0 - -262 -264 0 - -262 -265 0 - -262 -266 0 - -262 -267 0 - -262 -268 0 - -263 -264 0 - -263 -265 0 - -263 -266 0 - -263 -267 0 - -263 -268 0 - -264 -265 0 - -264 -266 0 - -264 -267 0 - -264 -268 0 - -265 -266 0 - -265 -267 0 - -265 -268 0 - -266 -267 0 - -266 -268 0 - -267 -268 0 - -269 -270 0 - -269 -271 0 - -269 -272 0 - -269 -273 0 - -270 -271 0 - -270 -272 0 - -270 -273 0 - -271 -272 0 - -271 -273 0 - -272 -273 0 - -274 -275 0 - -274 -276 0 - -274 -277 0 - -274 -278 0 - -274 -279 0 - -274 -280 0 - -275 -276 0 - -275 -277 0 - -275 -278 0 - -275 -279 0 - -275 -280 0 - -276 -277 0 - -276 -278 0 - -276 -279 0 - -276 -280 0 - -277 -278 0 - -277 -279 0 - -277 -280 0 - -278 -279 0 - -278 -280 0 - -279 -280 0 - -281 -282 0 - -281 -283 0 - -281 -284 0 - -282 -283 0 - -282 -284 0 - -283 -284 0 - -285 -286 0 - -285 -287 0 - -285 -288 0 - -286 -287 0 - -286 -288 0 - -287 -288 0 - -289 -290 0 - -289 -291 0 - -289 -292 0 - -289 -293 0 - -289 -294 0 - -290 -291 0 - -290 -292 0 - -290 -293 0 - -290 -294 0 - -291 -292 0 - -291 -293 0 - -291 -294 0 - -292 -293 0 - -292 -294 0 - -293 -294 0 - -295 -296 0 - -295 -297 0 - -295 -298 0 - -296 -297 0 - -296 -298 0 - -297 -298 0 - -299 -300 0 - -299 -301 0 - -299 -302 0 - -299 -303 0 - -299 -304 0 - -300 -301 0 - -300 -302 0 - -300 -303 0 - -300 -304 0 - -301 -302 0 - -301 -303 0 - -301 -304 0 - -302 -303 0 - -302 -304 0 - -303 -304 0 - -305 -306 0 - -305 -307 0 - -305 -308 0 - -305 -309 0 - -305 -310 0 - -306 -307 0 - -306 -308 0 - -306 -309 0 - -306 -310 0 - -307 -308 0 - -307 -309 0 - -307 -310 0 - -308 -309 0 - -308 -310 0 - -309 -310 0 - -311 -312 0 - -311 -313 0 - -311 -314 0 - -311 -315 0 - -311 -316 0 - -311 -317 0 - -311 -318 0 - -311 -319 0 - -312 -313 0 - -312 -314 0 - -312 -315 0 - -312 -316 0 - -312 -317 0 - -312 -318 0 - -312 -319 0 - -313 -314 0 - -313 -315 0 - -313 -316 0 - -313 -317 0 - -313 -318 0 - -313 -319 0 - -314 -315 0 - -314 -316 0 - -314 -317 0 - -314 -318 0 - -314 -319 0 - -315 -316 0 - -315 -317 0 - -315 -318 0 - -315 -319 0 - -316 -317 0 - -316 -318 0 - -316 -319 0 - -317 -318 0 - -317 -319 0 - -318 -319 0 - -320 -321 0 - -320 -322 0 - -320 -323 0 - -320 -324 0 - -320 -325 0 - -320 -326 0 - -320 -327 0 - -321 -322 0 - -321 -323 0 - -321 -324 0 - -321 -325 0 - -321 -326 0 - -321 -327 0 - -322 -323 0 - -322 -324 0 - -322 -325 0 - -322 -326 0 - -322 -327 0 - -323 -324 0 - -323 -325 0 - -323 -326 0 - -323 -327 0 - -324 -325 0 - -324 -326 0 - -324 -327 0 - -325 -326 0 - -325 -327 0 - -326 -327 0 - -328 -329 0 - -328 -330 0 - -328 -331 0 - -328 -332 0 - -329 -330 0 - -329 -331 0 - -329 -332 0 - -330 -331 0 - -330 -332 0 - -331 -332 0 - -333 -334 0 - -335 -336 0 - -335 -337 0 - -335 -338 0 - -335 -339 0 - -335 -340 0 - -335 -341 0 - -335 -342 0 - -336 -337 0 - -336 -338 0 - -336 -339 0 - -336 -340 0 - -336 -341 0 - -336 -342 0 - -337 -338 0 - -337 -339 0 - -337 -340 0 - -337 -341 0 - -337 -342 0 - -338 -339 0 - -338 -340 0 - -338 -341 0 - -338 -342 0 - -339 -340 0 - -339 -341 0 - -339 -342 0 - -340 -341 0 - -340 -342 0 - -341 -342 0 - -343 -344 0 - -343 -345 0 - -343 -346 0 - -343 -347 0 - -343 -348 0 - -343 -349 0 - -343 -350 0 - -343 -351 0 - -344 -345 0 - -344 -346 0 - -344 -347 0 - -344 -348 0 - -344 -349 0 - -344 -350 0 - -344 -351 0 - -345 -346 0 - -345 -347 0 - -345 -348 0 - -345 -349 0 - -345 -350 0 - -345 -351 0 - -346 -347 0 - -346 -348 0 - -346 -349 0 - -346 -350 0 - -346 -351 0 - -347 -348 0 - -347 -349 0 - -347 -350 0 - -347 -351 0 - -348 -349 0 - -348 -350 0 - -348 -351 0 - -349 -350 0 - -349 -351 0 - -350 -351 0 - -352 -353 0 - -352 -354 0 - -352 -355 0 - -352 -356 0 - -353 -354 0 - -353 -355 0 - -353 -356 0 - -354 -355 0 - -354 -356 0 - -355 -356 0 - -357 -358 0 - -357 -359 0 - -357 -360 0 - -357 -361 0 - -357 -362 0 - -358 -359 0 - -358 -360 0 - -358 -361 0 - -358 -362 0 - -359 -360 0 - -359 -361 0 - -359 -362 0 - -360 -361 0 - -360 -362 0 - -361 -362 0 - -363 -364 0 - -363 -365 0 - -363 -366 0 - -363 -367 0 - -363 -368 0 - -363 -369 0 - -363 -370 0 - -363 -371 0 - -363 -372 0 - -364 -365 0 - -364 -366 0 - -364 -367 0 - -364 -368 0 - -364 -369 0 - -364 -370 0 - -364 -371 0 - -364 -372 0 - -365 -366 0 - -365 -367 0 - -365 -368 0 - -365 -369 0 - -365 -370 0 - -365 -371 0 - -365 -372 0 - -366 -367 0 - -366 -368 0 - -366 -369 0 - -366 -370 0 - -366 -371 0 - -366 -372 0 - -367 -368 0 - -367 -369 0 - -367 -370 0 - -367 -371 0 - -367 -372 0 - -368 -369 0 - -368 -370 0 - -368 -371 0 - -368 -372 0 - -369 -370 0 - -369 -371 0 - -369 -372 0 - -370 -371 0 - -370 -372 0 - -371 -372 0 - -373 -374 0 - -373 -375 0 - -373 -376 0 - -373 -377 0 - -373 -378 0 - -373 -379 0 - -373 -380 0 - -374 -375 0 - -374 -376 0 - -374 -377 0 - -374 -378 0 - -374 -379 0 - -374 -380 0 - -375 -376 0 - -375 -377 0 - -375 -378 0 - -375 -379 0 - -375 -380 0 - -376 -377 0 - -376 -378 0 - -376 -379 0 - -376 -380 0 - -377 -378 0 - -377 -379 0 - -377 -380 0 - -378 -379 0 - -378 -380 0 - -379 -380 0 - -381 -382 0 - -381 -383 0 - -381 -384 0 - -382 -383 0 - -382 -384 0 - -383 -384 0 - -385 -386 0 - -385 -387 0 - -385 -388 0 - -385 -389 0 - -385 -390 0 - -385 -391 0 - -385 -392 0 - -386 -387 0 - -386 -388 0 - -386 -389 0 - -386 -390 0 - -386 -391 0 - -386 -392 0 - -387 -388 0 - -387 -389 0 - -387 -390 0 - -387 -391 0 - -387 -392 0 - -388 -389 0 - -388 -390 0 - -388 -391 0 - -388 -392 0 - -389 -390 0 - -389 -391 0 - -389 -392 0 - -390 -391 0 - -390 -392 0 - -391 -392 0 - -393 -394 0 - -393 -395 0 - -393 -396 0 - -393 -397 0 - -393 -398 0 - -393 -399 0 - -394 -395 0 - -394 -396 0 - -394 -397 0 - -394 -398 0 - -394 -399 0 - -395 -396 0 - -395 -397 0 - -395 -398 0 - -395 -399 0 - -396 -397 0 - -396 -398 0 - -396 -399 0 - -397 -398 0 - -397 -399 0 - -398 -399 0 - -400 -401 0 - -400 -402 0 - -400 -403 0 - -400 -404 0 - -400 -405 0 - -400 -406 0 - -400 -407 0 - -400 -408 0 - -400 -409 0 - -401 -402 0 - -401 -403 0 - -401 -404 0 - -401 -405 0 - -401 -406 0 - -401 -407 0 - -401 -408 0 - -401 -409 0 - -402 -403 0 - -402 -404 0 - -402 -405 0 - -402 -406 0 - -402 -407 0 - -402 -408 0 - -402 -409 0 - -403 -404 0 - -403 -405 0 - -403 -406 0 - -403 -407 0 - -403 -408 0 - -403 -409 0 - -404 -405 0 - -404 -406 0 - -404 -407 0 - -404 -408 0 - -404 -409 0 - -405 -406 0 - -405 -407 0 - -405 -408 0 - -405 -409 0 - -406 -407 0 - -406 -408 0 - -406 -409 0 - -407 -408 0 - -407 -409 0 - -408 -409 0 - -410 -411 0 - -410 -412 0 - -410 -413 0 - -410 -414 0 - -410 -415 0 - -411 -412 0 - -411 -413 0 - -411 -414 0 - -411 -415 0 - -412 -413 0 - -412 -414 0 - -412 -415 0 - -413 -414 0 - -413 -415 0 - -414 -415 0 - -416 -417 0 - -416 -418 0 - -416 -419 0 - -416 -420 0 - -416 -421 0 - -416 -422 0 - -416 -423 0 - -417 -418 0 - -417 -419 0 - -417 -420 0 - -417 -421 0 - -417 -422 0 - -417 -423 0 - -418 -419 0 - -418 -420 0 - -418 -421 0 - -418 -422 0 - -418 -423 0 - -419 -420 0 - -419 -421 0 - -419 -422 0 - -419 -423 0 - -420 -421 0 - -420 -422 0 - -420 -423 0 - -421 -422 0 - -421 -423 0 - -422 -423 0 - -424 -425 0 - -424 -426 0 - -424 -427 0 - -424 -428 0 - -424 -429 0 - -424 -430 0 - -424 -431 0 - -425 -426 0 - -425 -427 0 - -425 -428 0 - -425 -429 0 - -425 -430 0 - -425 -431 0 - -426 -427 0 - -426 -428 0 - -426 -429 0 - -426 -430 0 - -426 -431 0 - -427 -428 0 - -427 -429 0 - -427 -430 0 - -427 -431 0 - -428 -429 0 - -428 -430 0 - -428 -431 0 - -429 -430 0 - -429 -431 0 - -430 -431 0 - -432 -433 0 - -432 -434 0 - -432 -435 0 - -432 -436 0 - -432 -437 0 - -432 -438 0 - -432 -439 0 - -432 -440 0 - -433 -434 0 - -433 -435 0 - -433 -436 0 - -433 -437 0 - -433 -438 0 - -433 -439 0 - -433 -440 0 - -434 -435 0 - -434 -436 0 - -434 -437 0 - -434 -438 0 - -434 -439 0 - -434 -440 0 - -435 -436 0 - -435 -437 0 - -435 -438 0 - -435 -439 0 - -435 -440 0 - -436 -437 0 - -436 -438 0 - -436 -439 0 - -436 -440 0 - -437 -438 0 - -437 -439 0 - -437 -440 0 - -438 -439 0 - -438 -440 0 - -439 -440 0 - -441 -442 0 - -441 -443 0 - -441 -444 0 - -441 -445 0 - -441 -446 0 - -441 -447 0 - -441 -448 0 - -441 -449 0 - -441 -450 0 - -442 -443 0 - -442 -444 0 - -442 -445 0 - -442 -446 0 - -442 -447 0 - -442 -448 0 - -442 -449 0 - -442 -450 0 - -443 -444 0 - -443 -445 0 - -443 -446 0 - -443 -447 0 - -443 -448 0 - -443 -449 0 - -443 -450 0 - -444 -445 0 - -444 -446 0 - -444 -447 0 - -444 -448 0 - -444 -449 0 - -444 -450 0 - -445 -446 0 - -445 -447 0 - -445 -448 0 - -445 -449 0 - -445 -450 0 - -446 -447 0 - -446 -448 0 - -446 -449 0 - -446 -450 0 - -447 -448 0 - -447 -449 0 - -447 -450 0 - -448 -449 0 - -448 -450 0 - -449 -450 0 - -451 -452 0 - -451 -453 0 - -451 -454 0 - -451 -455 0 - -451 -456 0 - -451 -457 0 - -451 -458 0 - -452 -453 0 - -452 -454 0 - -452 -455 0 - -452 -456 0 - -452 -457 0 - -452 -458 0 - -453 -454 0 - -453 -455 0 - -453 -456 0 - -453 -457 0 - -453 -458 0 - -454 -455 0 - -454 -456 0 - -454 -457 0 - -454 -458 0 - -455 -456 0 - -455 -457 0 - -455 -458 0 - -456 -457 0 - -456 -458 0 - -457 -458 0 - -459 -460 0 - -459 -461 0 - -459 -462 0 - -459 -463 0 - -459 -464 0 - -460 -461 0 - -460 -462 0 - -460 -463 0 - -460 -464 0 - -461 -462 0 - -461 -463 0 - -461 -464 0 - -462 -463 0 - -462 -464 0 - -463 -464 0 - -465 -466 0 - -465 -467 0 - -465 -468 0 - -465 -469 0 - -465 -470 0 - -465 -471 0 - -465 -472 0 - -465 -473 0 - -465 -474 0 - -465 -475 0 - -466 -467 0 - -466 -468 0 - -466 -469 0 - -466 -470 0 - -466 -471 0 - -466 -472 0 - -466 -473 0 - -466 -474 0 - -466 -475 0 - -467 -468 0 - -467 -469 0 - -467 -470 0 - -467 -471 0 - -467 -472 0 - -467 -473 0 - -467 -474 0 - -467 -475 0 - -468 -469 0 - -468 -470 0 - -468 -471 0 - -468 -472 0 - -468 -473 0 - -468 -474 0 - -468 -475 0 - -469 -470 0 - -469 -471 0 - -469 -472 0 - -469 -473 0 - -469 -474 0 - -469 -475 0 - -470 -471 0 - -470 -472 0 - -470 -473 0 - -470 -474 0 - -470 -475 0 - -471 -472 0 - -471 -473 0 - -471 -474 0 - -471 -475 0 - -472 -473 0 - -472 -474 0 - -472 -475 0 - -473 -474 0 - -473 -475 0 - -474 -475 0 - -476 -477 0 - -476 -478 0 - -476 -479 0 - -476 -480 0 - -477 -478 0 - -477 -479 0 - -477 -480 0 - -478 -479 0 - -478 -480 0 - -479 -480 0 - -481 -482 0 - -481 -483 0 - -482 -483 0 - -484 -485 0 - -484 -486 0 - -484 -487 0 - -484 -488 0 - -484 -489 0 - -484 -490 0 - -484 -491 0 - -485 -486 0 - -485 -487 0 - -485 -488 0 - -485 -489 0 - -485 -490 0 - -485 -491 0 - -486 -487 0 - -486 -488 0 - -486 -489 0 - -486 -490 0 - -486 -491 0 - -487 -488 0 - -487 -489 0 - -487 -490 0 - -487 -491 0 - -488 -489 0 - -488 -490 0 - -488 -491 0 - -489 -490 0 - -489 -491 0 - -490 -491 0 - -492 -493 0 - -492 -494 0 - -492 -495 0 - -492 -496 0 - -492 -497 0 - -492 -498 0 - -492 -499 0 - -493 -494 0 - -493 -495 0 - -493 -496 0 - -493 -497 0 - -493 -498 0 - -493 -499 0 - -494 -495 0 - -494 -496 0 - -494 -497 0 - -494 -498 0 - -494 -499 0 - -495 -496 0 - -495 -497 0 - -495 -498 0 - -495 -499 0 - -496 -497 0 - -496 -498 0 - -496 -499 0 - -497 -498 0 - -497 -499 0 - -498 -499 0 - -500 -501 0 - -500 -502 0 - -500 -503 0 - -500 -504 0 - -500 -505 0 - -500 -506 0 - -500 -507 0 - -500 -508 0 - -500 -509 0 - -501 -502 0 - -501 -503 0 - -501 -504 0 - -501 -505 0 - -501 -506 0 - -501 -507 0 - -501 -508 0 - -501 -509 0 - -502 -503 0 - -502 -504 0 - -502 -505 0 - -502 -506 0 - -502 -507 0 - -502 -508 0 - -502 -509 0 - -503 -504 0 - -503 -505 0 - -503 -506 0 - -503 -507 0 - -503 -508 0 - -503 -509 0 - -504 -505 0 - -504 -506 0 - -504 -507 0 - -504 -508 0 - -504 -509 0 - -505 -506 0 - -505 -507 0 - -505 -508 0 - -505 -509 0 - -506 -507 0 - -506 -508 0 - -506 -509 0 - -507 -508 0 - -507 -509 0 - -508 -509 0 - -510 -511 0 - -510 -512 0 - -510 -513 0 - -511 -512 0 - -511 -513 0 - -512 -513 0 - -514 -515 0 - -514 -516 0 - -514 -517 0 - -514 -518 0 - -514 -519 0 - -514 -520 0 - -514 -521 0 - -515 -516 0 - -515 -517 0 - -515 -518 0 - -515 -519 0 - -515 -520 0 - -515 -521 0 - -516 -517 0 - -516 -518 0 - -516 -519 0 - -516 -520 0 - -516 -521 0 - -517 -518 0 - -517 -519 0 - -517 -520 0 - -517 -521 0 - -518 -519 0 - -518 -520 0 - -518 -521 0 - -519 -520 0 - -519 -521 0 - -520 -521 0 - -522 -523 0 - -522 -524 0 - -522 -525 0 - -522 -526 0 - -522 -527 0 - -522 -528 0 - -522 -529 0 - -522 -530 0 - -523 -524 0 - -523 -525 0 - -523 -526 0 - -523 -527 0 - -523 -528 0 - -523 -529 0 - -523 -530 0 - -524 -525 0 - -524 -526 0 - -524 -527 0 - -524 -528 0 - -524 -529 0 - -524 -530 0 - -525 -526 0 - -525 -527 0 - -525 -528 0 - -525 -529 0 - -525 -530 0 - -526 -527 0 - -526 -528 0 - -526 -529 0 - -526 -530 0 - -527 -528 0 - -527 -529 0 - -527 -530 0 - -528 -529 0 - -528 -530 0 - -529 -530 0 - -531 -532 0 - -531 -533 0 - -531 -534 0 - -531 -535 0 - -531 -536 0 - -532 -533 0 - -532 -534 0 - -532 -535 0 - -532 -536 0 - -533 -534 0 - -533 -535 0 - -533 -536 0 - -534 -535 0 - -534 -536 0 - -535 -536 0 - -537 -538 0 - -537 -539 0 - -537 -540 0 - -537 -541 0 - -537 -542 0 - -537 -543 0 - -537 -544 0 - -537 -545 0 - -537 -546 0 - -537 -547 0 - -537 -548 0 - -538 -539 0 - -538 -540 0 - -538 -541 0 - -538 -542 0 - -538 -543 0 - -538 -544 0 - -538 -545 0 - -538 -546 0 - -538 -547 0 - -538 -548 0 - -539 -540 0 - -539 -541 0 - -539 -542 0 - -539 -543 0 - -539 -544 0 - -539 -545 0 - -539 -546 0 - -539 -547 0 - -539 -548 0 - -540 -541 0 - -540 -542 0 - -540 -543 0 - -540 -544 0 - -540 -545 0 - -540 -546 0 - -540 -547 0 - -540 -548 0 - -541 -542 0 - -541 -543 0 - -541 -544 0 - -541 -545 0 - -541 -546 0 - -541 -547 0 - -541 -548 0 - -542 -543 0 - -542 -544 0 - -542 -545 0 - -542 -546 0 - -542 -547 0 - -542 -548 0 - -543 -544 0 - -543 -545 0 - -543 -546 0 - -543 -547 0 - -543 -548 0 - -544 -545 0 - -544 -546 0 - -544 -547 0 - -544 -548 0 - -545 -546 0 - -545 -547 0 - -545 -548 0 - -546 -547 0 - -546 -548 0 - -547 -548 0 - -549 -550 0 - -549 -551 0 - -549 -552 0 - -549 -553 0 - -549 -554 0 - -550 -551 0 - -550 -552 0 - -550 -553 0 - -550 -554 0 - -551 -552 0 - -551 -553 0 - -551 -554 0 - -552 -553 0 - -552 -554 0 - -553 -554 0 - -555 -556 0 - -555 -557 0 - -555 -558 0 - -555 -559 0 - -556 -557 0 - -556 -558 0 - -556 -559 0 - -557 -558 0 - -557 -559 0 - -558 -559 0 - -560 -561 0 - -560 -562 0 - -560 -563 0 - -561 -562 0 - -561 -563 0 - -562 -563 0 - -564 -565 0 - -564 -566 0 - -564 -567 0 - -564 -568 0 - -564 -569 0 - -565 -566 0 - -565 -567 0 - -565 -568 0 - -565 -569 0 - -566 -567 0 - -566 -568 0 - -566 -569 0 - -567 -568 0 - -567 -569 0 - -568 -569 0 - -570 -571 0 - -570 -572 0 - -570 -573 0 - -571 -572 0 - -571 -573 0 - -572 -573 0 - -574 -575 0 - -574 -576 0 - -574 -577 0 - -574 -578 0 - -574 -579 0 - -574 -580 0 - -575 -576 0 - -575 -577 0 - -575 -578 0 - -575 -579 0 - -575 -580 0 - -576 -577 0 - -576 -578 0 - -576 -579 0 - -576 -580 0 - -577 -578 0 - -577 -579 0 - -577 -580 0 - -578 -579 0 - -578 -580 0 - -579 -580 0 - -581 -582 0 - -581 -583 0 - -581 -584 0 - -581 -585 0 - -581 -586 0 - -581 -587 0 - -582 -583 0 - -582 -584 0 - -582 -585 0 - -582 -586 0 - -582 -587 0 - -583 -584 0 - -583 -585 0 - -583 -586 0 - -583 -587 0 - -584 -585 0 - -584 -586 0 - -584 -587 0 - -585 -586 0 - -585 -587 0 - -586 -587 0 - -588 -589 0 - -588 -590 0 - -588 -591 0 - -588 -592 0 - -589 -590 0 - -589 -591 0 - -589 -592 0 - -590 -591 0 - -590 -592 0 - -591 -592 0 - -593 -594 0 - -593 -595 0 - -593 -596 0 - -594 -595 0 - -594 -596 0 - -595 -596 0 - -597 -598 0 - -597 -599 0 - -597 -600 0 - -597 -601 0 - -597 -602 0 - -598 -599 0 - -598 -600 0 - -598 -601 0 - -598 -602 0 - -599 -600 0 - -599 -601 0 - -599 -602 0 - -600 -601 0 - -600 -602 0 - -601 -602 0 - -603 -604 0 - -603 -605 0 - -603 -606 0 - -603 -607 0 - -603 -608 0 - -604 -605 0 - -604 -606 0 - -604 -607 0 - -604 -608 0 - -605 -606 0 - -605 -607 0 - -605 -608 0 - -606 -607 0 - -606 -608 0 - -607 -608 0 - -609 -610 0 - -609 -611 0 - -609 -612 0 - -610 -611 0 - -610 -612 0 - -611 -612 0 - -613 -614 0 - -613 -615 0 - -613 -616 0 - -613 -617 0 - -614 -615 0 - -614 -616 0 - -614 -617 0 - -615 -616 0 - -615 -617 0 - -616 -617 0 - -618 -619 0 - -618 -620 0 - -619 -620 0 - -621 -622 0 - -621 -623 0 - -621 -624 0 - -621 -625 0 - -621 -626 0 - -621 -627 0 - -621 -628 0 - -621 -629 0 - -622 -623 0 - -622 -624 0 - -622 -625 0 - -622 -626 0 - -622 -627 0 - -622 -628 0 - -622 -629 0 - -623 -624 0 - -623 -625 0 - -623 -626 0 - -623 -627 0 - -623 -628 0 - -623 -629 0 - -624 -625 0 - -624 -626 0 - -624 -627 0 - -624 -628 0 - -624 -629 0 - -625 -626 0 - -625 -627 0 - -625 -628 0 - -625 -629 0 - -626 -627 0 - -626 -628 0 - -626 -629 0 - -627 -628 0 - -627 -629 0 - -628 -629 0 - -630 -631 0 - -630 -632 0 - -630 -633 0 - -630 -634 0 - -630 -635 0 - -630 -636 0 - -631 -632 0 - -631 -633 0 - -631 -634 0 - -631 -635 0 - -631 -636 0 - -632 -633 0 - -632 -634 0 - -632 -635 0 - -632 -636 0 - -633 -634 0 - -633 -635 0 - -633 -636 0 - -634 -635 0 - -634 -636 0 - -635 -636 0 - -637 -638 0 - -637 -639 0 - -637 -640 0 - -637 -641 0 - -638 -639 0 - -638 -640 0 - -638 -641 0 - -639 -640 0 - -639 -641 0 - -640 -641 0 - -642 -643 0 - -642 -644 0 - -642 -645 0 - -642 -646 0 - -642 -647 0 - -642 -648 0 - -643 -644 0 - -643 -645 0 - -643 -646 0 - -643 -647 0 - -643 -648 0 - -644 -645 0 - -644 -646 0 - -644 -647 0 - -644 -648 0 - -645 -646 0 - -645 -647 0 - -645 -648 0 - -646 -647 0 - -646 -648 0 - -647 -648 0 - -649 -650 0 - -649 -651 0 - -649 -652 0 - -649 -653 0 - -649 -654 0 - -649 -655 0 - -650 -651 0 - -650 -652 0 - -650 -653 0 - -650 -654 0 - -650 -655 0 - -651 -652 0 - -651 -653 0 - -651 -654 0 - -651 -655 0 - -652 -653 0 - -652 -654 0 - -652 -655 0 - -653 -654 0 - -653 -655 0 - -654 -655 0 - -656 -657 0 - -656 -658 0 - -656 -659 0 - -656 -660 0 - -656 -661 0 - -656 -662 0 - -656 -663 0 - -656 -664 0 - -657 -658 0 - -657 -659 0 - -657 -660 0 - -657 -661 0 - -657 -662 0 - -657 -663 0 - -657 -664 0 - -658 -659 0 - -658 -660 0 - -658 -661 0 - -658 -662 0 - -658 -663 0 - -658 -664 0 - -659 -660 0 - -659 -661 0 - -659 -662 0 - -659 -663 0 - -659 -664 0 - -660 -661 0 - -660 -662 0 - -660 -663 0 - -660 -664 0 - -661 -662 0 - -661 -663 0 - -661 -664 0 - -662 -663 0 - -662 -664 0 - -663 -664 0 - -665 -666 0 - -665 -667 0 - -665 -668 0 - -665 -669 0 - -665 -670 0 - -665 -671 0 - -666 -667 0 - -666 -668 0 - -666 -669 0 - -666 -670 0 - -666 -671 0 - -667 -668 0 - -667 -669 0 - -667 -670 0 - -667 -671 0 - -668 -669 0 - -668 -670 0 - -668 -671 0 - -669 -670 0 - -669 -671 0 - -670 -671 0 - -672 -673 0 - -672 -674 0 - -672 -675 0 - -672 -676 0 - -672 -677 0 - -673 -674 0 - -673 -675 0 - -673 -676 0 - -673 -677 0 - -674 -675 0 - -674 -676 0 - -674 -677 0 - -675 -676 0 - -675 -677 0 - -676 -677 0 - -678 -679 0 - -678 -680 0 - -678 -681 0 - -678 -682 0 - -678 -683 0 - -678 -684 0 - -678 -685 0 - -678 -686 0 - -678 -687 0 - -678 -688 0 - -679 -680 0 - -679 -681 0 - -679 -682 0 - -679 -683 0 - -679 -684 0 - -679 -685 0 - -679 -686 0 - -679 -687 0 - -679 -688 0 - -680 -681 0 - -680 -682 0 - -680 -683 0 - -680 -684 0 - -680 -685 0 - -680 -686 0 - -680 -687 0 - -680 -688 0 - -681 -682 0 - -681 -683 0 - -681 -684 0 - -681 -685 0 - -681 -686 0 - -681 -687 0 - -681 -688 0 - -682 -683 0 - -682 -684 0 - -682 -685 0 - -682 -686 0 - -682 -687 0 - -682 -688 0 - -683 -684 0 - -683 -685 0 - -683 -686 0 - -683 -687 0 - -683 -688 0 - -684 -685 0 - -684 -686 0 - -684 -687 0 - -684 -688 0 - -685 -686 0 - -685 -687 0 - -685 -688 0 - -686 -687 0 - -686 -688 0 - -687 -688 0 - -689 -690 0 - -689 -691 0 - -689 -692 0 - -689 -693 0 - -690 -691 0 - -690 -692 0 - -690 -693 0 - -691 -692 0 - -691 -693 0 - -692 -693 0 - -694 -695 0 - -694 -696 0 - -694 -697 0 - -694 -698 0 - -695 -696 0 - -695 -697 0 - -695 -698 0 - -696 -697 0 - -696 -698 0 - -697 -698 0 - -699 -700 0 - -699 -701 0 - -699 -702 0 - -700 -701 0 - -700 -702 0 - -701 -702 0 - -703 -704 0 - -703 -705 0 - -703 -706 0 - -703 -707 0 - -703 -708 0 - -704 -705 0 - -704 -706 0 - -704 -707 0 - -704 -708 0 - -705 -706 0 - -705 -707 0 - -705 -708 0 - -706 -707 0 - -706 -708 0 - -707 -708 0 - -709 -710 0 - -709 -711 0 - -709 -712 0 - -709 -713 0 - -710 -711 0 - -710 -712 0 - -710 -713 0 - -711 -712 0 - -711 -713 0 - -712 -713 0 - -714 -715 0 - -714 -716 0 - -715 -716 0 - -717 -718 0 - -717 -719 0 - -717 -720 0 - -717 -721 0 - -718 -719 0 - -718 -720 0 - -718 -721 0 - -719 -720 0 - -719 -721 0 - -720 -721 0 - -722 -723 0 - -722 -724 0 - -722 -725 0 - -722 -726 0 - -722 -727 0 - -722 -728 0 - -722 -729 0 - -723 -724 0 - -723 -725 0 - -723 -726 0 - -723 -727 0 - -723 -728 0 - -723 -729 0 - -724 -725 0 - -724 -726 0 - -724 -727 0 - -724 -728 0 - -724 -729 0 - -725 -726 0 - -725 -727 0 - -725 -728 0 - -725 -729 0 - -726 -727 0 - -726 -728 0 - -726 -729 0 - -727 -728 0 - -727 -729 0 - -728 -729 0 - -730 -731 0 - -730 -732 0 - -730 -733 0 - -730 -734 0 - -731 -732 0 - -731 -733 0 - -731 -734 0 - -732 -733 0 - -732 -734 0 - -733 -734 0 - -735 -736 0 - -735 -737 0 - -735 -738 0 - -735 -739 0 - -735 -740 0 - -735 -741 0 - -736 -737 0 - -736 -738 0 - -736 -739 0 - -736 -740 0 - -736 -741 0 - -737 -738 0 - -737 -739 0 - -737 -740 0 - -737 -741 0 - -738 -739 0 - -738 -740 0 - -738 -741 0 - -739 -740 0 - -739 -741 0 - -740 -741 0 - -742 -743 0 - -742 -744 0 - -742 -745 0 - -742 -746 0 - -743 -744 0 - -743 -745 0 - -743 -746 0 - -744 -745 0 - -744 -746 0 - -745 -746 0 - -747 -748 0 - -747 -749 0 - -747 -750 0 - -747 -751 0 - -747 -752 0 - -748 -749 0 - -748 -750 0 - -748 -751 0 - -748 -752 0 - -749 -750 0 - -749 -751 0 - -749 -752 0 - -750 -751 0 - -750 -752 0 - -751 -752 0 - -753 -754 0 - -753 -755 0 - -754 -755 0 - -756 -757 0 - -756 -758 0 - -756 -759 0 - -756 -760 0 - -756 -761 0 - -757 -758 0 - -757 -759 0 - -757 -760 0 - -757 -761 0 - -758 -759 0 - -758 -760 0 - -758 -761 0 - -759 -760 0 - -759 -761 0 - -760 -761 0 - -762 -763 0 - -762 -764 0 - -763 -764 0 - -765 -766 0 - -765 -767 0 - -765 -768 0 - -765 -769 0 - -766 -767 0 - -766 -768 0 - -766 -769 0 - -767 -768 0 - -767 -769 0 - -768 -769 0 - -770 -771 0 - -770 -772 0 - -770 -773 0 - -770 -774 0 - -771 -772 0 - -771 -773 0 - -771 -774 0 - -772 -773 0 - -772 -774 0 - -773 -774 0 - -775 -776 0 - -775 -777 0 - -775 -778 0 - -775 -779 0 - -775 -780 0 - -775 -781 0 - -775 -782 0 - -776 -777 0 - -776 -778 0 - -776 -779 0 - -776 -780 0 - -776 -781 0 - -776 -782 0 - -777 -778 0 - -777 -779 0 - -777 -780 0 - -777 -781 0 - -777 -782 0 - -778 -779 0 - -778 -780 0 - -778 -781 0 - -778 -782 0 - -779 -780 0 - -779 -781 0 - -779 -782 0 - -780 -781 0 - -780 -782 0 - -781 -782 0 - -783 -784 0 - -783 -785 0 - -783 -786 0 - -783 -787 0 - -784 -785 0 - -784 -786 0 - -784 -787 0 - -785 -786 0 - -785 -787 0 - -786 -787 0 - -788 -789 0 - -788 -790 0 - -788 -791 0 - -789 -790 0 - -789 -791 0 - -790 -791 0 - -792 -793 0 - -792 -794 0 - -792 -795 0 - -792 -796 0 - -792 -797 0 - -792 -798 0 - -793 -794 0 - -793 -795 0 - -793 -796 0 - -793 -797 0 - -793 -798 0 - -794 -795 0 - -794 -796 0 - -794 -797 0 - -794 -798 0 - -795 -796 0 - -795 -797 0 - -795 -798 0 - -796 -797 0 - -796 -798 0 - -797 -798 0 - -799 -800 0 - -799 -801 0 - -799 -802 0 - -799 -803 0 - -799 -804 0 - -799 -805 0 - -799 -806 0 - -800 -801 0 - -800 -802 0 - -800 -803 0 - -800 -804 0 - -800 -805 0 - -800 -806 0 - -801 -802 0 - -801 -803 0 - -801 -804 0 - -801 -805 0 - -801 -806 0 - -802 -803 0 - -802 -804 0 - -802 -805 0 - -802 -806 0 - -803 -804 0 - -803 -805 0 - -803 -806 0 - -804 -805 0 - -804 -806 0 - -805 -806 0 - -807 -808 0 - -807 -809 0 - -807 -810 0 - -807 -811 0 - -807 -812 0 - -807 -813 0 - -807 -814 0 - -807 -815 0 - -808 -809 0 - -808 -810 0 - -808 -811 0 - -808 -812 0 - -808 -813 0 - -808 -814 0 - -808 -815 0 - -809 -810 0 - -809 -811 0 - -809 -812 0 - -809 -813 0 - -809 -814 0 - -809 -815 0 - -810 -811 0 - -810 -812 0 - -810 -813 0 - -810 -814 0 - -810 -815 0 - -811 -812 0 - -811 -813 0 - -811 -814 0 - -811 -815 0 - -812 -813 0 - -812 -814 0 - -812 -815 0 - -813 -814 0 - -813 -815 0 - -814 -815 0 - -816 -817 0 - -816 -818 0 - -816 -819 0 - -816 -820 0 - -816 -821 0 - -816 -822 0 - -817 -818 0 - -817 -819 0 - -817 -820 0 - -817 -821 0 - -817 -822 0 - -818 -819 0 - -818 -820 0 - -818 -821 0 - -818 -822 0 - -819 -820 0 - -819 -821 0 - -819 -822 0 - -820 -821 0 - -820 -822 0 - -821 -822 0 - -823 -824 0 - -823 -825 0 - -823 -826 0 - -823 -827 0 - -823 -828 0 - -823 -829 0 - -823 -830 0 - -823 -831 0 - -823 -832 0 - -824 -825 0 - -824 -826 0 - -824 -827 0 - -824 -828 0 - -824 -829 0 - -824 -830 0 - -824 -831 0 - -824 -832 0 - -825 -826 0 - -825 -827 0 - -825 -828 0 - -825 -829 0 - -825 -830 0 - -825 -831 0 - -825 -832 0 - -826 -827 0 - -826 -828 0 - -826 -829 0 - -826 -830 0 - -826 -831 0 - -826 -832 0 - -827 -828 0 - -827 -829 0 - -827 -830 0 - -827 -831 0 - -827 -832 0 - -828 -829 0 - -828 -830 0 - -828 -831 0 - -828 -832 0 - -829 -830 0 - -829 -831 0 - -829 -832 0 - -830 -831 0 - -830 -832 0 - -831 -832 0 - -833 -834 0 - -833 -835 0 - -833 -836 0 - -833 -837 0 - -833 -838 0 - -833 -839 0 - -834 -835 0 - -834 -836 0 - -834 -837 0 - -834 -838 0 - -834 -839 0 - -835 -836 0 - -835 -837 0 - -835 -838 0 - -835 -839 0 - -836 -837 0 - -836 -838 0 - -836 -839 0 - -837 -838 0 - -837 -839 0 - -838 -839 0 - -840 -841 0 - -840 -842 0 - -840 -843 0 - -841 -842 0 - -841 -843 0 - -842 -843 0 - -844 -845 0 - -844 -846 0 - -844 -847 0 - -844 -848 0 - -845 -846 0 - -845 -847 0 - -845 -848 0 - -846 -847 0 - -846 -848 0 - -847 -848 0 - -849 -850 0 - -849 -851 0 - -849 -852 0 - -850 -851 0 - -850 -852 0 - -851 -852 0 - -853 -854 0 - -853 -855 0 - -853 -856 0 - -853 -857 0 - -853 -858 0 - -853 -859 0 - -854 -855 0 - -854 -856 0 - -854 -857 0 - -854 -858 0 - -854 -859 0 - -855 -856 0 - -855 -857 0 - -855 -858 0 - -855 -859 0 - -856 -857 0 - -856 -858 0 - -856 -859 0 - -857 -858 0 - -857 -859 0 - -858 -859 0 - -860 -861 0 - -860 -862 0 - -860 -863 0 - -860 -864 0 - -860 -865 0 - -860 -866 0 - -860 -867 0 - -860 -868 0 - -861 -862 0 - -861 -863 0 - -861 -864 0 - -861 -865 0 - -861 -866 0 - -861 -867 0 - -861 -868 0 - -862 -863 0 - -862 -864 0 - -862 -865 0 - -862 -866 0 - -862 -867 0 - -862 -868 0 - -863 -864 0 - -863 -865 0 - -863 -866 0 - -863 -867 0 - -863 -868 0 - -864 -865 0 - -864 -866 0 - -864 -867 0 - -864 -868 0 - -865 -866 0 - -865 -867 0 - -865 -868 0 - -866 -867 0 - -866 -868 0 - -867 -868 0 - -869 -870 0 - -869 -871 0 - -869 -872 0 - -870 -871 0 - -870 -872 0 - -871 -872 0 - -873 -874 0 - -873 -875 0 - -873 -876 0 - -873 -877 0 - -873 -878 0 - -873 -879 0 - -873 -880 0 - -873 -881 0 - -874 -875 0 - -874 -876 0 - -874 -877 0 - -874 -878 0 - -874 -879 0 - -874 -880 0 - -874 -881 0 - -875 -876 0 - -875 -877 0 - -875 -878 0 - -875 -879 0 - -875 -880 0 - -875 -881 0 - -876 -877 0 - -876 -878 0 - -876 -879 0 - -876 -880 0 - -876 -881 0 - -877 -878 0 - -877 -879 0 - -877 -880 0 - -877 -881 0 - -878 -879 0 - -878 -880 0 - -878 -881 0 - -879 -880 0 - -879 -881 0 - -880 -881 0 - -882 -883 0 - -882 -884 0 - -883 -884 0 - -885 -886 0 - -885 -887 0 - -885 -888 0 - -885 -889 0 - -886 -887 0 - -886 -888 0 - -886 -889 0 - -887 -888 0 - -887 -889 0 - -888 -889 0 - -890 -891 0 - -890 -892 0 - -890 -893 0 - -890 -894 0 - -891 -892 0 - -891 -893 0 - -891 -894 0 - -892 -893 0 - -892 -894 0 - -893 -894 0 - -895 -896 0 - -895 -897 0 - -896 -897 0 - -898 -899 0 - -898 -900 0 - -898 -901 0 - -899 -900 0 - -899 -901 0 - -900 -901 0 - -902 -903 0 - -902 -904 0 - -902 -905 0 - -902 -906 0 - -903 -904 0 - -903 -905 0 - -903 -906 0 - -904 -905 0 - -904 -906 0 - -905 -906 0 - -907 -908 0 - -907 -909 0 - -907 -910 0 - -908 -909 0 - -908 -910 0 - -909 -910 0 - -911 -912 0 - -913 -914 0 - -913 -915 0 - -913 -916 0 - -913 -917 0 - -914 -915 0 - -914 -916 0 - -914 -917 0 - -915 -916 0 - -915 -917 0 - -916 -917 0 - -918 -919 0 - -918 -920 0 - -918 -921 0 - -918 -922 0 - -918 -923 0 - -919 -920 0 - -919 -921 0 - -919 -922 0 - -919 -923 0 - -920 -921 0 - -920 -922 0 - -920 -923 0 - -921 -922 0 - -921 -923 0 - -922 -923 0 - -924 -925 0 - -924 -926 0 - -924 -927 0 - -924 -928 0 - -924 -929 0 - -924 -930 0 - -925 -926 0 - -925 -927 0 - -925 -928 0 - -925 -929 0 - -925 -930 0 - -926 -927 0 - -926 -928 0 - -926 -929 0 - -926 -930 0 - -927 -928 0 - -927 -929 0 - -927 -930 0 - -928 -929 0 - -928 -930 0 - -929 -930 0 - -931 -932 0 - -931 -933 0 - -931 -934 0 - -931 -935 0 - -932 -933 0 - -932 -934 0 - -932 -935 0 - -933 -934 0 - -933 -935 0 - -934 -935 0 - -936 -937 0 - -936 -938 0 - -936 -939 0 - -936 -940 0 - -937 -938 0 - -937 -939 0 - -937 -940 0 - -938 -939 0 - -938 -940 0 - -939 -940 0 - -941 -942 0 - -941 -943 0 - -941 -944 0 - -941 -945 0 - -941 -946 0 - -942 -943 0 - -942 -944 0 - -942 -945 0 - -942 -946 0 - -943 -944 0 - -943 -945 0 - -943 -946 0 - -944 -945 0 - -944 -946 0 - -945 -946 0 - -947 -948 0 - -947 -949 0 - -947 -950 0 - -947 -951 0 - -947 -952 0 - -947 -953 0 - -948 -949 0 - -948 -950 0 - -948 -951 0 - -948 -952 0 - -948 -953 0 - -949 -950 0 - -949 -951 0 - -949 -952 0 - -949 -953 0 - -950 -951 0 - -950 -952 0 - -950 -953 0 - -951 -952 0 - -951 -953 0 - -952 -953 0 - -954 -955 0 - -954 -956 0 - -954 -957 0 - -954 -958 0 - -954 -959 0 - -955 -956 0 - -955 -957 0 - -955 -958 0 - -955 -959 0 - -956 -957 0 - -956 -958 0 - -956 -959 0 - -957 -958 0 - -957 -959 0 - -958 -959 0 - -960 -961 0 - -960 -962 0 - -961 -962 0 - -963 -964 0 - -963 -965 0 - -964 -965 0 - -966 -967 0 - -966 -968 0 - -966 -969 0 - -966 -970 0 - -966 -971 0 - -966 -972 0 - -966 -973 0 - -966 -974 0 - -966 -975 0 - -967 -968 0 - -967 -969 0 - -967 -970 0 - -967 -971 0 - -967 -972 0 - -967 -973 0 - -967 -974 0 - -967 -975 0 - -968 -969 0 - -968 -970 0 - -968 -971 0 - -968 -972 0 - -968 -973 0 - -968 -974 0 - -968 -975 0 - -969 -970 0 - -969 -971 0 - -969 -972 0 - -969 -973 0 - -969 -974 0 - -969 -975 0 - -970 -971 0 - -970 -972 0 - -970 -973 0 - -970 -974 0 - -970 -975 0 - -971 -972 0 - -971 -973 0 - -971 -974 0 - -971 -975 0 - -972 -973 0 - -972 -974 0 - -972 -975 0 - -973 -974 0 - -973 -975 0 - -974 -975 0 - -976 -977 0 - -978 -979 0 - -978 -980 0 - -978 -981 0 - -978 -982 0 - -978 -983 0 - -979 -980 0 - -979 -981 0 - -979 -982 0 - -979 -983 0 - -980 -981 0 - -980 -982 0 - -980 -983 0 - -981 -982 0 - -981 -983 0 - -982 -983 0 - -984 -985 0 - -984 -986 0 - -984 -987 0 - -984 -988 0 - -984 -989 0 - -985 -986 0 - -985 -987 0 - -985 -988 0 - -985 -989 0 - -986 -987 0 - -986 -988 0 - -986 -989 0 - -987 -988 0 - -987 -989 0 - -988 -989 0 - -990 -991 0 - -990 -992 0 - -990 -993 0 - -990 -994 0 - -990 -995 0 - -990 -996 0 - -990 -997 0 - -990 -998 0 - -991 -992 0 - -991 -993 0 - -991 -994 0 - -991 -995 0 - -991 -996 0 - -991 -997 0 - -991 -998 0 - -992 -993 0 - -992 -994 0 - -992 -995 0 - -992 -996 0 - -992 -997 0 - -992 -998 0 - -993 -994 0 - -993 -995 0 - -993 -996 0 - -993 -997 0 - -993 -998 0 - -994 -995 0 - -994 -996 0 - -994 -997 0 - -994 -998 0 - -995 -996 0 - -995 -997 0 - -995 -998 0 - -996 -997 0 - -996 -998 0 - -997 -998 0 - -999 -1000 0 - -999 -1001 0 - -999 -1002 0 - -999 -1003 0 - -999 -1004 0 - -1000 -1001 0 - -1000 -1002 0 - -1000 -1003 0 - -1000 -1004 0 - -1001 -1002 0 - -1001 -1003 0 - -1001 -1004 0 - -1002 -1003 0 - -1002 -1004 0 - -1003 -1004 0 - -1005 -1006 0 - -1005 -1007 0 - -1005 -1008 0 - -1005 -1009 0 - -1005 -1010 0 - -1005 -1011 0 - -1006 -1007 0 - -1006 -1008 0 - -1006 -1009 0 - -1006 -1010 0 - -1006 -1011 0 - -1007 -1008 0 - -1007 -1009 0 - -1007 -1010 0 - -1007 -1011 0 - -1008 -1009 0 - -1008 -1010 0 - -1008 -1011 0 - -1009 -1010 0 - -1009 -1011 0 - -1010 -1011 0 - -1012 -1013 0 - -1012 -1014 0 - -1012 -1015 0 - -1012 -1016 0 - -1012 -1017 0 - -1012 -1018 0 - -1012 -1019 0 - -1012 -1020 0 - -1013 -1014 0 - -1013 -1015 0 - -1013 -1016 0 - -1013 -1017 0 - -1013 -1018 0 - -1013 -1019 0 - -1013 -1020 0 - -1014 -1015 0 - -1014 -1016 0 - -1014 -1017 0 - -1014 -1018 0 - -1014 -1019 0 - -1014 -1020 0 - -1015 -1016 0 - -1015 -1017 0 - -1015 -1018 0 - -1015 -1019 0 - -1015 -1020 0 - -1016 -1017 0 - -1016 -1018 0 - -1016 -1019 0 - -1016 -1020 0 - -1017 -1018 0 - -1017 -1019 0 - -1017 -1020 0 - -1018 -1019 0 - -1018 -1020 0 - -1019 -1020 0 - -1021 -1022 0 - -1021 -1023 0 - -1021 -1024 0 - -1021 -1025 0 - -1021 -1026 0 - -1021 -1027 0 - -1021 -1028 0 - -1021 -1029 0 - -1022 -1023 0 - -1022 -1024 0 - -1022 -1025 0 - -1022 -1026 0 - -1022 -1027 0 - -1022 -1028 0 - -1022 -1029 0 - -1023 -1024 0 - -1023 -1025 0 - -1023 -1026 0 - -1023 -1027 0 - -1023 -1028 0 - -1023 -1029 0 - -1024 -1025 0 - -1024 -1026 0 - -1024 -1027 0 - -1024 -1028 0 - -1024 -1029 0 - -1025 -1026 0 - -1025 -1027 0 - -1025 -1028 0 - -1025 -1029 0 - -1026 -1027 0 - -1026 -1028 0 - -1026 -1029 0 - -1027 -1028 0 - -1027 -1029 0 - -1028 -1029 0 - -1030 -1031 0 - -1030 -1032 0 - -1030 -1033 0 - -1030 -1034 0 - -1030 -1035 0 - -1030 -1036 0 - -1030 -1037 0 - -1030 -1038 0 - -1031 -1032 0 - -1031 -1033 0 - -1031 -1034 0 - -1031 -1035 0 - -1031 -1036 0 - -1031 -1037 0 - -1031 -1038 0 - -1032 -1033 0 - -1032 -1034 0 - -1032 -1035 0 - -1032 -1036 0 - -1032 -1037 0 - -1032 -1038 0 - -1033 -1034 0 - -1033 -1035 0 - -1033 -1036 0 - -1033 -1037 0 - -1033 -1038 0 - -1034 -1035 0 - -1034 -1036 0 - -1034 -1037 0 - -1034 -1038 0 - -1035 -1036 0 - -1035 -1037 0 - -1035 -1038 0 - -1036 -1037 0 - -1036 -1038 0 - -1037 -1038 0 - -1039 -1040 0 - -1039 -1041 0 - -1039 -1042 0 - -1039 -1043 0 - -1039 -1044 0 - -1039 -1045 0 - -1040 -1041 0 - -1040 -1042 0 - -1040 -1043 0 - -1040 -1044 0 - -1040 -1045 0 - -1041 -1042 0 - -1041 -1043 0 - -1041 -1044 0 - -1041 -1045 0 - -1042 -1043 0 - -1042 -1044 0 - -1042 -1045 0 - -1043 -1044 0 - -1043 -1045 0 - -1044 -1045 0 - -1046 -1047 0 - -1046 -1048 0 - -1046 -1049 0 - -1046 -1050 0 - -1047 -1048 0 - -1047 -1049 0 - -1047 -1050 0 - -1048 -1049 0 - -1048 -1050 0 - -1049 -1050 0 - -1051 -1052 0 - -1051 -1053 0 - -1052 -1053 0 - -1054 -1055 0 - -1054 -1056 0 - -1054 -1057 0 - -1054 -1058 0 - -1054 -1059 0 - -1054 -1060 0 - -1054 -1061 0 - -1055 -1056 0 - -1055 -1057 0 - -1055 -1058 0 - -1055 -1059 0 - -1055 -1060 0 - -1055 -1061 0 - -1056 -1057 0 - -1056 -1058 0 - -1056 -1059 0 - -1056 -1060 0 - -1056 -1061 0 - -1057 -1058 0 - -1057 -1059 0 - -1057 -1060 0 - -1057 -1061 0 - -1058 -1059 0 - -1058 -1060 0 - -1058 -1061 0 - -1059 -1060 0 - -1059 -1061 0 - -1060 -1061 0 - -1062 -1063 0 - -1062 -1064 0 - -1062 -1065 0 - -1062 -1066 0 - -1062 -1067 0 - -1062 -1068 0 - -1062 -1069 0 - -1062 -1070 0 - -1063 -1064 0 - -1063 -1065 0 - -1063 -1066 0 - -1063 -1067 0 - -1063 -1068 0 - -1063 -1069 0 - -1063 -1070 0 - -1064 -1065 0 - -1064 -1066 0 - -1064 -1067 0 - -1064 -1068 0 - -1064 -1069 0 - -1064 -1070 0 - -1065 -1066 0 - -1065 -1067 0 - -1065 -1068 0 - -1065 -1069 0 - -1065 -1070 0 - -1066 -1067 0 - -1066 -1068 0 - -1066 -1069 0 - -1066 -1070 0 - -1067 -1068 0 - -1067 -1069 0 - -1067 -1070 0 - -1068 -1069 0 - -1068 -1070 0 - -1069 -1070 0 - -1071 -1072 0 - -1071 -1073 0 - -1071 -1074 0 - -1071 -1075 0 - -1071 -1076 0 - -1071 -1077 0 - -1071 -1078 0 - -1071 -1079 0 - -1071 -1080 0 - -1071 -1081 0 - -1072 -1073 0 - -1072 -1074 0 - -1072 -1075 0 - -1072 -1076 0 - -1072 -1077 0 - -1072 -1078 0 - -1072 -1079 0 - -1072 -1080 0 - -1072 -1081 0 - -1073 -1074 0 - -1073 -1075 0 - -1073 -1076 0 - -1073 -1077 0 - -1073 -1078 0 - -1073 -1079 0 - -1073 -1080 0 - -1073 -1081 0 - -1074 -1075 0 - -1074 -1076 0 - -1074 -1077 0 - -1074 -1078 0 - -1074 -1079 0 - -1074 -1080 0 - -1074 -1081 0 - -1075 -1076 0 - -1075 -1077 0 - -1075 -1078 0 - -1075 -1079 0 - -1075 -1080 0 - -1075 -1081 0 - -1076 -1077 0 - -1076 -1078 0 - -1076 -1079 0 - -1076 -1080 0 - -1076 -1081 0 - -1077 -1078 0 - -1077 -1079 0 - -1077 -1080 0 - -1077 -1081 0 - -1078 -1079 0 - -1078 -1080 0 - -1078 -1081 0 - -1079 -1080 0 - -1079 -1081 0 - -1080 -1081 0 - -1082 -1083 0 - -1082 -1084 0 - -1083 -1084 0 - -1085 -1086 0 - -1085 -1087 0 - -1085 -1088 0 - -1085 -1089 0 - -1085 -1090 0 - -1085 -1091 0 - -1086 -1087 0 - -1086 -1088 0 - -1086 -1089 0 - -1086 -1090 0 - -1086 -1091 0 - -1087 -1088 0 - -1087 -1089 0 - -1087 -1090 0 - -1087 -1091 0 - -1088 -1089 0 - -1088 -1090 0 - -1088 -1091 0 - -1089 -1090 0 - -1089 -1091 0 - -1090 -1091 0 - -1092 -1093 0 - -1092 -1094 0 - -1092 -1095 0 - -1092 -1096 0 - -1092 -1097 0 - -1092 -1098 0 - -1092 -1099 0 - -1092 -1100 0 - -1092 -1101 0 - -1092 -1102 0 - -1093 -1094 0 - -1093 -1095 0 - -1093 -1096 0 - -1093 -1097 0 - -1093 -1098 0 - -1093 -1099 0 - -1093 -1100 0 - -1093 -1101 0 - -1093 -1102 0 - -1094 -1095 0 - -1094 -1096 0 - -1094 -1097 0 - -1094 -1098 0 - -1094 -1099 0 - -1094 -1100 0 - -1094 -1101 0 - -1094 -1102 0 - -1095 -1096 0 - -1095 -1097 0 - -1095 -1098 0 - -1095 -1099 0 - -1095 -1100 0 - -1095 -1101 0 - -1095 -1102 0 - -1096 -1097 0 - -1096 -1098 0 - -1096 -1099 0 - -1096 -1100 0 - -1096 -1101 0 - -1096 -1102 0 - -1097 -1098 0 - -1097 -1099 0 - -1097 -1100 0 - -1097 -1101 0 - -1097 -1102 0 - -1098 -1099 0 - -1098 -1100 0 - -1098 -1101 0 - -1098 -1102 0 - -1099 -1100 0 - -1099 -1101 0 - -1099 -1102 0 - -1100 -1101 0 - -1100 -1102 0 - -1101 -1102 0 - -1103 -1104 0 - -1103 -1105 0 - -1103 -1106 0 - -1103 -1107 0 - -1103 -1108 0 - -1104 -1105 0 - -1104 -1106 0 - -1104 -1107 0 - -1104 -1108 0 - -1105 -1106 0 - -1105 -1107 0 - -1105 -1108 0 - -1106 -1107 0 - -1106 -1108 0 - -1107 -1108 0 - -1109 -1110 0 - -1109 -1111 0 - -1109 -1112 0 - -1109 -1113 0 - -1109 -1114 0 - -1109 -1115 0 - -1109 -1116 0 - -1110 -1111 0 - -1110 -1112 0 - -1110 -1113 0 - -1110 -1114 0 - -1110 -1115 0 - -1110 -1116 0 - -1111 -1112 0 - -1111 -1113 0 - -1111 -1114 0 - -1111 -1115 0 - -1111 -1116 0 - -1112 -1113 0 - -1112 -1114 0 - -1112 -1115 0 - -1112 -1116 0 - -1113 -1114 0 - -1113 -1115 0 - -1113 -1116 0 - -1114 -1115 0 - -1114 -1116 0 - -1115 -1116 0 - -1117 -1118 0 - -1117 -1119 0 - -1117 -1120 0 - -1118 -1119 0 - -1118 -1120 0 - -1119 -1120 0 - -1121 -1122 0 - -1121 -1123 0 - -1122 -1123 0 - -1124 -1125 0 - -1124 -1126 0 - -1124 -1127 0 - -1124 -1128 0 - -1125 -1126 0 - -1125 -1127 0 - -1125 -1128 0 - -1126 -1127 0 - -1126 -1128 0 - -1127 -1128 0 - -1129 -1130 0 - -1129 -1131 0 - -1130 -1131 0 - -1132 -1133 0 - -1134 -1135 0 - -1134 -1136 0 - -1134 -1137 0 - -1135 -1136 0 - -1135 -1137 0 - -1136 -1137 0 - -1138 -1139 0 - -1138 -1140 0 - -1138 -1141 0 - -1138 -1142 0 - -1138 -1143 0 - -1139 -1140 0 - -1139 -1141 0 - -1139 -1142 0 - -1139 -1143 0 - -1140 -1141 0 - -1140 -1142 0 - -1140 -1143 0 - -1141 -1142 0 - -1141 -1143 0 - -1142 -1143 0 - -1144 -1145 0 - -1146 -1147 0 - -1146 -1148 0 - -1146 -1149 0 - -1146 -1150 0 - -1147 -1148 0 - -1147 -1149 0 - -1147 -1150 0 - -1148 -1149 0 - -1148 -1150 0 - -1149 -1150 0 - -1151 -1152 0 - -1151 -1153 0 - -1151 -1154 0 - -1152 -1153 0 - -1152 -1154 0 - -1153 -1154 0 - -1155 -1156 0 - -1157 -1158 0 - -1157 -1159 0 - -1157 -1160 0 - -1157 -1161 0 - -1157 -1162 0 - -1157 -1163 0 - -1158 -1159 0 - -1158 -1160 0 - -1158 -1161 0 - -1158 -1162 0 - -1158 -1163 0 - -1159 -1160 0 - -1159 -1161 0 - -1159 -1162 0 - -1159 -1163 0 - -1160 -1161 0 - -1160 -1162 0 - -1160 -1163 0 - -1161 -1162 0 - -1161 -1163 0 - -1162 -1163 0 - -1164 -1165 0 - -1164 -1166 0 - -1164 -1167 0 - -1164 -1168 0 - -1165 -1166 0 - -1165 -1167 0 - -1165 -1168 0 - -1166 -1167 0 - -1166 -1168 0 - -1167 -1168 0 - -1169 -1170 0 - -1169 -1171 0 - -1170 -1171 0 - -1172 -1173 0 - -1172 -1174 0 - -1172 -1175 0 - -1173 -1174 0 - -1173 -1175 0 - -1174 -1175 0 - -1176 -1177 0 - -1178 -1179 0 - -1178 -1180 0 - -1178 -1181 0 - -1178 -1182 0 - -1179 -1180 0 - -1179 -1181 0 - -1179 -1182 0 - -1180 -1181 0 - -1180 -1182 0 - -1181 -1182 0 - -1183 -1184 0 - -1183 -1185 0 - -1183 -1186 0 - -1183 -1187 0 - -1183 -1188 0 - -1183 -1189 0 - -1184 -1185 0 - -1184 -1186 0 - -1184 -1187 0 - -1184 -1188 0 - -1184 -1189 0 - -1185 -1186 0 - -1185 -1187 0 - -1185 -1188 0 - -1185 -1189 0 - -1186 -1187 0 - -1186 -1188 0 - -1186 -1189 0 - -1187 -1188 0 - -1187 -1189 0 - -1188 -1189 0 - -1190 -1191 0 - -1190 -1192 0 - -1191 -1192 0 - -1193 -1194 0 - -1193 -1195 0 - -1193 -1196 0 - -1193 -1197 0 - -1193 -1198 0 - -1193 -1199 0 - -1194 -1195 0 - -1194 -1196 0 - -1194 -1197 0 - -1194 -1198 0 - -1194 -1199 0 - -1195 -1196 0 - -1195 -1197 0 - -1195 -1198 0 - -1195 -1199 0 - -1196 -1197 0 - -1196 -1198 0 - -1196 -1199 0 - -1197 -1198 0 - -1197 -1199 0 - -1198 -1199 0 - -1200 -1201 0 - -1200 -1202 0 - -1200 -1203 0 - -1200 -1204 0 - -1200 -1205 0 - -1201 -1202 0 - -1201 -1203 0 - -1201 -1204 0 - -1201 -1205 0 - -1202 -1203 0 - -1202 -1204 0 - -1202 -1205 0 - -1203 -1204 0 - -1203 -1205 0 - -1204 -1205 0 - -1206 -1207 0 - -1206 -1208 0 - -1206 -1209 0 - -1206 -1210 0 - -1206 -1211 0 - -1206 -1212 0 - -1207 -1208 0 - -1207 -1209 0 - -1207 -1210 0 - -1207 -1211 0 - -1207 -1212 0 - -1208 -1209 0 - -1208 -1210 0 - -1208 -1211 0 - -1208 -1212 0 - -1209 -1210 0 - -1209 -1211 0 - -1209 -1212 0 - -1210 -1211 0 - -1210 -1212 0 - -1211 -1212 0 - -1213 -1214 0 - -1213 -1215 0 - -1213 -1216 0 - -1213 -1217 0 - -1213 -1218 0 - -1213 -1219 0 - -1214 -1215 0 - -1214 -1216 0 - -1214 -1217 0 - -1214 -1218 0 - -1214 -1219 0 - -1215 -1216 0 - -1215 -1217 0 - -1215 -1218 0 - -1215 -1219 0 - -1216 -1217 0 - -1216 -1218 0 - -1216 -1219 0 - -1217 -1218 0 - -1217 -1219 0 - -1218 -1219 0 - -1220 -1221 0 - -1220 -1222 0 - -1220 -1223 0 - -1220 -1224 0 - -1220 -1225 0 - -1220 -1226 0 - -1220 -1227 0 - -1220 -1228 0 - -1220 -1229 0 - -1221 -1222 0 - -1221 -1223 0 - -1221 -1224 0 - -1221 -1225 0 - -1221 -1226 0 - -1221 -1227 0 - -1221 -1228 0 - -1221 -1229 0 - -1222 -1223 0 - -1222 -1224 0 - -1222 -1225 0 - -1222 -1226 0 - -1222 -1227 0 - -1222 -1228 0 - -1222 -1229 0 - -1223 -1224 0 - -1223 -1225 0 - -1223 -1226 0 - -1223 -1227 0 - -1223 -1228 0 - -1223 -1229 0 - -1224 -1225 0 - -1224 -1226 0 - -1224 -1227 0 - -1224 -1228 0 - -1224 -1229 0 - -1225 -1226 0 - -1225 -1227 0 - -1225 -1228 0 - -1225 -1229 0 - -1226 -1227 0 - -1226 -1228 0 - -1226 -1229 0 - -1227 -1228 0 - -1227 -1229 0 - -1228 -1229 0 - -1230 -1231 0 - -1230 -1232 0 - -1230 -1233 0 - -1230 -1234 0 - -1230 -1235 0 - -1230 -1236 0 - -1230 -1237 0 - -1230 -1238 0 - -1231 -1232 0 - -1231 -1233 0 - -1231 -1234 0 - -1231 -1235 0 - -1231 -1236 0 - -1231 -1237 0 - -1231 -1238 0 - -1232 -1233 0 - -1232 -1234 0 - -1232 -1235 0 - -1232 -1236 0 - -1232 -1237 0 - -1232 -1238 0 - -1233 -1234 0 - -1233 -1235 0 - -1233 -1236 0 - -1233 -1237 0 - -1233 -1238 0 - -1234 -1235 0 - -1234 -1236 0 - -1234 -1237 0 - -1234 -1238 0 - -1235 -1236 0 - -1235 -1237 0 - -1235 -1238 0 - -1236 -1237 0 - -1236 -1238 0 - -1237 -1238 0 - -1239 -1240 0 - -1239 -1241 0 - -1239 -1242 0 - -1239 -1243 0 - -1239 -1244 0 - -1239 -1245 0 - -1240 -1241 0 - -1240 -1242 0 - -1240 -1243 0 - -1240 -1244 0 - -1240 -1245 0 - -1241 -1242 0 - -1241 -1243 0 - -1241 -1244 0 - -1241 -1245 0 - -1242 -1243 0 - -1242 -1244 0 - -1242 -1245 0 - -1243 -1244 0 - -1243 -1245 0 - -1244 -1245 0 - -1246 -1247 0 - -1246 -1248 0 - -1246 -1249 0 - -1246 -1250 0 - -1246 -1251 0 - -1246 -1252 0 - -1246 -1253 0 - -1247 -1248 0 - -1247 -1249 0 - -1247 -1250 0 - -1247 -1251 0 - -1247 -1252 0 - -1247 -1253 0 - -1248 -1249 0 - -1248 -1250 0 - -1248 -1251 0 - -1248 -1252 0 - -1248 -1253 0 - -1249 -1250 0 - -1249 -1251 0 - -1249 -1252 0 - -1249 -1253 0 - -1250 -1251 0 - -1250 -1252 0 - -1250 -1253 0 - -1251 -1252 0 - -1251 -1253 0 - -1252 -1253 0 - -1254 -1255 0 - -1254 -1256 0 - -1254 -1257 0 - -1254 -1258 0 - -1254 -1259 0 - -1254 -1260 0 - -1254 -1261 0 - -1254 -1262 0 - -1254 -1263 0 - -1254 -1264 0 - -1254 -1265 0 - -1254 -1266 0 - -1255 -1256 0 - -1255 -1257 0 - -1255 -1258 0 - -1255 -1259 0 - -1255 -1260 0 - -1255 -1261 0 - -1255 -1262 0 - -1255 -1263 0 - -1255 -1264 0 - -1255 -1265 0 - -1255 -1266 0 - -1256 -1257 0 - -1256 -1258 0 - -1256 -1259 0 - -1256 -1260 0 - -1256 -1261 0 - -1256 -1262 0 - -1256 -1263 0 - -1256 -1264 0 - -1256 -1265 0 - -1256 -1266 0 - -1257 -1258 0 - -1257 -1259 0 - -1257 -1260 0 - -1257 -1261 0 - -1257 -1262 0 - -1257 -1263 0 - -1257 -1264 0 - -1257 -1265 0 - -1257 -1266 0 - -1258 -1259 0 - -1258 -1260 0 - -1258 -1261 0 - -1258 -1262 0 - -1258 -1263 0 - -1258 -1264 0 - -1258 -1265 0 - -1258 -1266 0 - -1259 -1260 0 - -1259 -1261 0 - -1259 -1262 0 - -1259 -1263 0 - -1259 -1264 0 - -1259 -1265 0 - -1259 -1266 0 - -1260 -1261 0 - -1260 -1262 0 - -1260 -1263 0 - -1260 -1264 0 - -1260 -1265 0 - -1260 -1266 0 - -1261 -1262 0 - -1261 -1263 0 - -1261 -1264 0 - -1261 -1265 0 - -1261 -1266 0 - -1262 -1263 0 - -1262 -1264 0 - -1262 -1265 0 - -1262 -1266 0 - -1263 -1264 0 - -1263 -1265 0 - -1263 -1266 0 - -1264 -1265 0 - -1264 -1266 0 - -1265 -1266 0 - -1267 -1268 0 - -1267 -1269 0 - -1267 -1270 0 - -1267 -1271 0 - -1267 -1272 0 - -1268 -1269 0 - -1268 -1270 0 - -1268 -1271 0 - -1268 -1272 0 - -1269 -1270 0 - -1269 -1271 0 - -1269 -1272 0 - -1270 -1271 0 - -1270 -1272 0 - -1271 -1272 0 - -1273 -1274 0 - -1273 -1275 0 - -1273 -1276 0 - -1273 -1277 0 - -1273 -1278 0 - -1273 -1279 0 - -1273 -1280 0 - -1273 -1281 0 - -1274 -1275 0 - -1274 -1276 0 - -1274 -1277 0 - -1274 -1278 0 - -1274 -1279 0 - -1274 -1280 0 - -1274 -1281 0 - -1275 -1276 0 - -1275 -1277 0 - -1275 -1278 0 - -1275 -1279 0 - -1275 -1280 0 - -1275 -1281 0 - -1276 -1277 0 - -1276 -1278 0 - -1276 -1279 0 - -1276 -1280 0 - -1276 -1281 0 - -1277 -1278 0 - -1277 -1279 0 - -1277 -1280 0 - -1277 -1281 0 - -1278 -1279 0 - -1278 -1280 0 - -1278 -1281 0 - -1279 -1280 0 - -1279 -1281 0 - -1280 -1281 0 - -1282 -1283 0 - -1282 -1284 0 - -1282 -1285 0 - -1282 -1286 0 - -1283 -1284 0 - -1283 -1285 0 - -1283 -1286 0 - -1284 -1285 0 - -1284 -1286 0 - -1285 -1286 0 - -1287 -1288 0 - -1287 -1289 0 - -1287 -1290 0 - -1287 -1291 0 - -1287 -1292 0 - -1287 -1293 0 - -1288 -1289 0 - -1288 -1290 0 - -1288 -1291 0 - -1288 -1292 0 - -1288 -1293 0 - -1289 -1290 0 - -1289 -1291 0 - -1289 -1292 0 - -1289 -1293 0 - -1290 -1291 0 - -1290 -1292 0 - -1290 -1293 0 - -1291 -1292 0 - -1291 -1293 0 - -1292 -1293 0 - -1294 -1295 0 - -1294 -1296 0 - -1294 -1297 0 - -1294 -1298 0 - -1294 -1299 0 - -1294 -1300 0 - -1294 -1301 0 - -1294 -1302 0 - -1295 -1296 0 - -1295 -1297 0 - -1295 -1298 0 - -1295 -1299 0 - -1295 -1300 0 - -1295 -1301 0 - -1295 -1302 0 - -1296 -1297 0 - -1296 -1298 0 - -1296 -1299 0 - -1296 -1300 0 - -1296 -1301 0 - -1296 -1302 0 - -1297 -1298 0 - -1297 -1299 0 - -1297 -1300 0 - -1297 -1301 0 - -1297 -1302 0 - -1298 -1299 0 - -1298 -1300 0 - -1298 -1301 0 - -1298 -1302 0 - -1299 -1300 0 - -1299 -1301 0 - -1299 -1302 0 - -1300 -1301 0 - -1300 -1302 0 - -1301 -1302 0 - -1303 -1304 0 - -1303 -1305 0 - -1303 -1306 0 - -1303 -1307 0 - -1303 -1308 0 - -1303 -1309 0 - -1303 -1310 0 - -1303 -1311 0 - -1303 -1312 0 - -1304 -1305 0 - -1304 -1306 0 - -1304 -1307 0 - -1304 -1308 0 - -1304 -1309 0 - -1304 -1310 0 - -1304 -1311 0 - -1304 -1312 0 - -1305 -1306 0 - -1305 -1307 0 - -1305 -1308 0 - -1305 -1309 0 - -1305 -1310 0 - -1305 -1311 0 - -1305 -1312 0 - -1306 -1307 0 - -1306 -1308 0 - -1306 -1309 0 - -1306 -1310 0 - -1306 -1311 0 - -1306 -1312 0 - -1307 -1308 0 - -1307 -1309 0 - -1307 -1310 0 - -1307 -1311 0 - -1307 -1312 0 - -1308 -1309 0 - -1308 -1310 0 - -1308 -1311 0 - -1308 -1312 0 - -1309 -1310 0 - -1309 -1311 0 - -1309 -1312 0 - -1310 -1311 0 - -1310 -1312 0 - -1311 -1312 0 - -1313 -1314 0 - -1313 -1315 0 - -1313 -1316 0 - -1313 -1317 0 - -1313 -1318 0 - -1314 -1315 0 - -1314 -1316 0 - -1314 -1317 0 - -1314 -1318 0 - -1315 -1316 0 - -1315 -1317 0 - -1315 -1318 0 - -1316 -1317 0 - -1316 -1318 0 - -1317 -1318 0 - -1319 -1320 0 - -1319 -1321 0 - -1319 -1322 0 - -1319 -1323 0 - -1319 -1324 0 - -1319 -1325 0 - -1319 -1326 0 - -1320 -1321 0 - -1320 -1322 0 - -1320 -1323 0 - -1320 -1324 0 - -1320 -1325 0 - -1320 -1326 0 - -1321 -1322 0 - -1321 -1323 0 - -1321 -1324 0 - -1321 -1325 0 - -1321 -1326 0 - -1322 -1323 0 - -1322 -1324 0 - -1322 -1325 0 - -1322 -1326 0 - -1323 -1324 0 - -1323 -1325 0 - -1323 -1326 0 - -1324 -1325 0 - -1324 -1326 0 - -1325 -1326 0 - -1327 -1328 0 - -1327 -1329 0 - -1327 -1330 0 - -1327 -1331 0 - -1327 -1332 0 - -1327 -1333 0 - -1328 -1329 0 - -1328 -1330 0 - -1328 -1331 0 - -1328 -1332 0 - -1328 -1333 0 - -1329 -1330 0 - -1329 -1331 0 - -1329 -1332 0 - -1329 -1333 0 - -1330 -1331 0 - -1330 -1332 0 - -1330 -1333 0 - -1331 -1332 0 - -1331 -1333 0 - -1332 -1333 0 - -1334 -1335 0 - -1334 -1336 0 - -1334 -1337 0 - -1334 -1338 0 - -1334 -1339 0 - -1334 -1340 0 - -1335 -1336 0 - -1335 -1337 0 - -1335 -1338 0 - -1335 -1339 0 - -1335 -1340 0 - -1336 -1337 0 - -1336 -1338 0 - -1336 -1339 0 - -1336 -1340 0 - -1337 -1338 0 - -1337 -1339 0 - -1337 -1340 0 - -1338 -1339 0 - -1338 -1340 0 - -1339 -1340 0 - -1341 -1342 0 - -1341 -1343 0 - -1341 -1344 0 - -1341 -1345 0 - -1341 -1346 0 - -1342 -1343 0 - -1342 -1344 0 - -1342 -1345 0 - -1342 -1346 0 - -1343 -1344 0 - -1343 -1345 0 - -1343 -1346 0 - -1344 -1345 0 - -1344 -1346 0 - -1345 -1346 0 - -1347 -1348 0 - -1347 -1349 0 - -1347 -1350 0 - -1347 -1351 0 - -1348 -1349 0 - -1348 -1350 0 - -1348 -1351 0 - -1349 -1350 0 - -1349 -1351 0 - -1350 -1351 0 - -1352 -1353 0 - -1352 -1354 0 - -1352 -1355 0 - -1352 -1356 0 - -1352 -1357 0 - -1352 -1358 0 - -1353 -1354 0 - -1353 -1355 0 - -1353 -1356 0 - -1353 -1357 0 - -1353 -1358 0 - -1354 -1355 0 - -1354 -1356 0 - -1354 -1357 0 - -1354 -1358 0 - -1355 -1356 0 - -1355 -1357 0 - -1355 -1358 0 - -1356 -1357 0 - -1356 -1358 0 - -1357 -1358 0 - -1359 -1360 0 - -1359 -1361 0 - -1359 -1362 0 - -1359 -1363 0 - -1359 -1364 0 - -1360 -1361 0 - -1360 -1362 0 - -1360 -1363 0 - -1360 -1364 0 - -1361 -1362 0 - -1361 -1363 0 - -1361 -1364 0 - -1362 -1363 0 - -1362 -1364 0 - -1363 -1364 0 - -1365 -1366 0 - -1365 -1367 0 - -1365 -1368 0 - -1365 -1369 0 - -1365 -1370 0 - -1366 -1367 0 - -1366 -1368 0 - -1366 -1369 0 - -1366 -1370 0 - -1367 -1368 0 - -1367 -1369 0 - -1367 -1370 0 - -1368 -1369 0 - -1368 -1370 0 - -1369 -1370 0 - -1371 -1372 0 - -1371 -1373 0 - -1371 -1374 0 - -1371 -1375 0 - -1371 -1376 0 - -1371 -1377 0 - -1371 -1378 0 - -1372 -1373 0 - -1372 -1374 0 - -1372 -1375 0 - -1372 -1376 0 - -1372 -1377 0 - -1372 -1378 0 - -1373 -1374 0 - -1373 -1375 0 - -1373 -1376 0 - -1373 -1377 0 - -1373 -1378 0 - -1374 -1375 0 - -1374 -1376 0 - -1374 -1377 0 - -1374 -1378 0 - -1375 -1376 0 - -1375 -1377 0 - -1375 -1378 0 - -1376 -1377 0 - -1376 -1378 0 - -1377 -1378 0 - -1379 -1380 0 - -1379 -1381 0 - -1379 -1382 0 - -1379 -1383 0 - -1379 -1384 0 - -1379 -1385 0 - -1379 -1386 0 - -1380 -1381 0 - -1380 -1382 0 - -1380 -1383 0 - -1380 -1384 0 - -1380 -1385 0 - -1380 -1386 0 - -1381 -1382 0 - -1381 -1383 0 - -1381 -1384 0 - -1381 -1385 0 - -1381 -1386 0 - -1382 -1383 0 - -1382 -1384 0 - -1382 -1385 0 - -1382 -1386 0 - -1383 -1384 0 - -1383 -1385 0 - -1383 -1386 0 - -1384 -1385 0 - -1384 -1386 0 - -1385 -1386 0 - -1387 -1388 0 - -1387 -1389 0 - -1387 -1390 0 - -1387 -1391 0 - -1387 -1392 0 - -1387 -1393 0 - -1388 -1389 0 - -1388 -1390 0 - -1388 -1391 0 - -1388 -1392 0 - -1388 -1393 0 - -1389 -1390 0 - -1389 -1391 0 - -1389 -1392 0 - -1389 -1393 0 - -1390 -1391 0 - -1390 -1392 0 - -1390 -1393 0 - -1391 -1392 0 - -1391 -1393 0 - -1392 -1393 0 - -1394 -1395 0 - -1394 -1396 0 - -1394 -1397 0 - -1394 -1398 0 - -1395 -1396 0 - -1395 -1397 0 - -1395 -1398 0 - -1396 -1397 0 - -1396 -1398 0 - -1397 -1398 0 - -1399 -1400 0 - -1399 -1401 0 - -1399 -1402 0 - -1399 -1403 0 - -1399 -1404 0 - -1399 -1405 0 - -1399 -1406 0 - -1400 -1401 0 - -1400 -1402 0 - -1400 -1403 0 - -1400 -1404 0 - -1400 -1405 0 - -1400 -1406 0 - -1401 -1402 0 - -1401 -1403 0 - -1401 -1404 0 - -1401 -1405 0 - -1401 -1406 0 - -1402 -1403 0 - -1402 -1404 0 - -1402 -1405 0 - -1402 -1406 0 - -1403 -1404 0 - -1403 -1405 0 - -1403 -1406 0 - -1404 -1405 0 - -1404 -1406 0 - -1405 -1406 0 - -1407 -1408 0 - -1407 -1409 0 - -1407 -1410 0 - -1407 -1411 0 - -1407 -1412 0 - -1408 -1409 0 - -1408 -1410 0 - -1408 -1411 0 - -1408 -1412 0 - -1409 -1410 0 - -1409 -1411 0 - -1409 -1412 0 - -1410 -1411 0 - -1410 -1412 0 - -1411 -1412 0 - -1413 -1414 0 - -1413 -1415 0 - -1413 -1416 0 - -1414 -1415 0 - -1414 -1416 0 - -1415 -1416 0 - -1417 -1418 0 - -1417 -1419 0 - -1417 -1420 0 - -1417 -1421 0 - -1417 -1422 0 - -1417 -1423 0 - -1417 -1424 0 - -1417 -1425 0 - -1417 -1426 0 - -1417 -1427 0 - -1418 -1419 0 - -1418 -1420 0 - -1418 -1421 0 - -1418 -1422 0 - -1418 -1423 0 - -1418 -1424 0 - -1418 -1425 0 - -1418 -1426 0 - -1418 -1427 0 - -1419 -1420 0 - -1419 -1421 0 - -1419 -1422 0 - -1419 -1423 0 - -1419 -1424 0 - -1419 -1425 0 - -1419 -1426 0 - -1419 -1427 0 - -1420 -1421 0 - -1420 -1422 0 - -1420 -1423 0 - -1420 -1424 0 - -1420 -1425 0 - -1420 -1426 0 - -1420 -1427 0 - -1421 -1422 0 - -1421 -1423 0 - -1421 -1424 0 - -1421 -1425 0 - -1421 -1426 0 - -1421 -1427 0 - -1422 -1423 0 - -1422 -1424 0 - -1422 -1425 0 - -1422 -1426 0 - -1422 -1427 0 - -1423 -1424 0 - -1423 -1425 0 - -1423 -1426 0 - -1423 -1427 0 - -1424 -1425 0 - -1424 -1426 0 - -1424 -1427 0 - -1425 -1426 0 - -1425 -1427 0 - -1426 -1427 0 - -1428 -1429 0 - -1428 -1430 0 - -1428 -1431 0 - -1428 -1432 0 - -1428 -1433 0 - -1429 -1430 0 - -1429 -1431 0 - -1429 -1432 0 - -1429 -1433 0 - -1430 -1431 0 - -1430 -1432 0 - -1430 -1433 0 - -1431 -1432 0 - -1431 -1433 0 - -1432 -1433 0 - -1434 -1435 0 - -1434 -1436 0 - -1434 -1437 0 - -1434 -1438 0 - -1434 -1439 0 - -1434 -1440 0 - -1434 -1441 0 - -1435 -1436 0 - -1435 -1437 0 - -1435 -1438 0 - -1435 -1439 0 - -1435 -1440 0 - -1435 -1441 0 - -1436 -1437 0 - -1436 -1438 0 - -1436 -1439 0 - -1436 -1440 0 - -1436 -1441 0 - -1437 -1438 0 - -1437 -1439 0 - -1437 -1440 0 - -1437 -1441 0 - -1438 -1439 0 - -1438 -1440 0 - -1438 -1441 0 - -1439 -1440 0 - -1439 -1441 0 - -1440 -1441 0 - -1442 -1443 0 - -1442 -1444 0 - -1442 -1445 0 - -1442 -1446 0 - -1442 -1447 0 - -1442 -1448 0 - -1442 -1449 0 - -1442 -1450 0 - -1442 -1451 0 - -1443 -1444 0 - -1443 -1445 0 - -1443 -1446 0 - -1443 -1447 0 - -1443 -1448 0 - -1443 -1449 0 - -1443 -1450 0 - -1443 -1451 0 - -1444 -1445 0 - -1444 -1446 0 - -1444 -1447 0 - -1444 -1448 0 - -1444 -1449 0 - -1444 -1450 0 - -1444 -1451 0 - -1445 -1446 0 - -1445 -1447 0 - -1445 -1448 0 - -1445 -1449 0 - -1445 -1450 0 - -1445 -1451 0 - -1446 -1447 0 - -1446 -1448 0 - -1446 -1449 0 - -1446 -1450 0 - -1446 -1451 0 - -1447 -1448 0 - -1447 -1449 0 - -1447 -1450 0 - -1447 -1451 0 - -1448 -1449 0 - -1448 -1450 0 - -1448 -1451 0 - -1449 -1450 0 - -1449 -1451 0 - -1450 -1451 0 - -1452 -1453 0 - -1452 -1454 0 - -1452 -1455 0 - -1452 -1456 0 - -1452 -1457 0 - -1452 -1458 0 - -1453 -1454 0 - -1453 -1455 0 - -1453 -1456 0 - -1453 -1457 0 - -1453 -1458 0 - -1454 -1455 0 - -1454 -1456 0 - -1454 -1457 0 - -1454 -1458 0 - -1455 -1456 0 - -1455 -1457 0 - -1455 -1458 0 - -1456 -1457 0 - -1456 -1458 0 - -1457 -1458 0 - -1459 -1460 0 - -1459 -1461 0 - -1459 -1462 0 - -1460 -1461 0 - -1460 -1462 0 - -1461 -1462 0 - -1463 -1464 0 - -1463 -1465 0 - -1463 -1466 0 - -1463 -1467 0 - -1463 -1468 0 - -1464 -1465 0 - -1464 -1466 0 - -1464 -1467 0 - -1464 -1468 0 - -1465 -1466 0 - -1465 -1467 0 - -1465 -1468 0 - -1466 -1467 0 - -1466 -1468 0 - -1467 -1468 0 - -1469 -1470 0 - -1469 -1471 0 - -1469 -1472 0 - -1470 -1471 0 - -1470 -1472 0 - -1471 -1472 0 - -1473 -1474 0 - -1473 -1475 0 - -1474 -1475 0 - -1476 -1477 0 - -1476 -1478 0 - -1476 -1479 0 - -1476 -1480 0 - -1476 -1481 0 - -1476 -1482 0 - -1477 -1478 0 - -1477 -1479 0 - -1477 -1480 0 - -1477 -1481 0 - -1477 -1482 0 - -1478 -1479 0 - -1478 -1480 0 - -1478 -1481 0 - -1478 -1482 0 - -1479 -1480 0 - -1479 -1481 0 - -1479 -1482 0 - -1480 -1481 0 - -1480 -1482 0 - -1481 -1482 0 - -1483 -1484 0 - -1483 -1485 0 - -1484 -1485 0 - -1486 -1487 0 - -1486 -1488 0 - -1487 -1488 0 - -1489 -1490 0 - -1489 -1491 0 - -1489 -1492 0 - -1489 -1493 0 - -1489 -1494 0 - -1489 -1495 0 - -1489 -1496 0 - -1490 -1491 0 - -1490 -1492 0 - -1490 -1493 0 - -1490 -1494 0 - -1490 -1495 0 - -1490 -1496 0 - -1491 -1492 0 - -1491 -1493 0 - -1491 -1494 0 - -1491 -1495 0 - -1491 -1496 0 - -1492 -1493 0 - -1492 -1494 0 - -1492 -1495 0 - -1492 -1496 0 - -1493 -1494 0 - -1493 -1495 0 - -1493 -1496 0 - -1494 -1495 0 - -1494 -1496 0 - -1495 -1496 0 - -1497 -1498 0 - -1497 -1499 0 - -1497 -1500 0 - -1497 -1501 0 - -1498 -1499 0 - -1498 -1500 0 - -1498 -1501 0 - -1499 -1500 0 - -1499 -1501 0 - -1500 -1501 0 - -1502 -1503 0 - -1502 -1504 0 - -1502 -1505 0 - -1502 -1506 0 - -1502 -1507 0 - -1502 -1508 0 - -1502 -1509 0 - -1503 -1504 0 - -1503 -1505 0 - -1503 -1506 0 - -1503 -1507 0 - -1503 -1508 0 - -1503 -1509 0 - -1504 -1505 0 - -1504 -1506 0 - -1504 -1507 0 - -1504 -1508 0 - -1504 -1509 0 - -1505 -1506 0 - -1505 -1507 0 - -1505 -1508 0 - -1505 -1509 0 - -1506 -1507 0 - -1506 -1508 0 - -1506 -1509 0 - -1507 -1508 0 - -1507 -1509 0 - -1508 -1509 0 - -1510 -1511 0 - -1510 -1512 0 - -1510 -1513 0 - -1510 -1514 0 - -1511 -1512 0 - -1511 -1513 0 - -1511 -1514 0 - -1512 -1513 0 - -1512 -1514 0 - -1513 -1514 0 - -1515 -1516 0 - -1515 -1517 0 - -1515 -1518 0 - -1515 -1519 0 - -1515 -1520 0 - -1515 -1521 0 - -1516 -1517 0 - -1516 -1518 0 - -1516 -1519 0 - -1516 -1520 0 - -1516 -1521 0 - -1517 -1518 0 - -1517 -1519 0 - -1517 -1520 0 - -1517 -1521 0 - -1518 -1519 0 - -1518 -1520 0 - -1518 -1521 0 - -1519 -1520 0 - -1519 -1521 0 - -1520 -1521 0 - -1522 -1523 0 - -1522 -1524 0 - -1522 -1525 0 - -1522 -1526 0 - -1522 -1527 0 - -1522 -1528 0 - -1522 -1529 0 - -1523 -1524 0 - -1523 -1525 0 - -1523 -1526 0 - -1523 -1527 0 - -1523 -1528 0 - -1523 -1529 0 - -1524 -1525 0 - -1524 -1526 0 - -1524 -1527 0 - -1524 -1528 0 - -1524 -1529 0 - -1525 -1526 0 - -1525 -1527 0 - -1525 -1528 0 - -1525 -1529 0 - -1526 -1527 0 - -1526 -1528 0 - -1526 -1529 0 - -1527 -1528 0 - -1527 -1529 0 - -1528 -1529 0 - -1530 -1531 0 - -1530 -1532 0 - -1530 -1533 0 - -1530 -1534 0 - -1530 -1535 0 - -1530 -1536 0 - -1530 -1537 0 - -1531 -1532 0 - -1531 -1533 0 - -1531 -1534 0 - -1531 -1535 0 - -1531 -1536 0 - -1531 -1537 0 - -1532 -1533 0 - -1532 -1534 0 - -1532 -1535 0 - -1532 -1536 0 - -1532 -1537 0 - -1533 -1534 0 - -1533 -1535 0 - -1533 -1536 0 - -1533 -1537 0 - -1534 -1535 0 - -1534 -1536 0 - -1534 -1537 0 - -1535 -1536 0 - -1535 -1537 0 - -1536 -1537 0 - -1538 -1539 0 - -1538 -1540 0 - -1538 -1541 0 - -1538 -1542 0 - -1538 -1543 0 - -1539 -1540 0 - -1539 -1541 0 - -1539 -1542 0 - -1539 -1543 0 - -1540 -1541 0 - -1540 -1542 0 - -1540 -1543 0 - -1541 -1542 0 - -1541 -1543 0 - -1542 -1543 0 - -1544 -1545 0 - -1544 -1546 0 - -1544 -1547 0 - -1544 -1548 0 - -1544 -1549 0 - -1544 -1550 0 - -1544 -1551 0 - -1544 -1552 0 - -1544 -1553 0 - -1545 -1546 0 - -1545 -1547 0 - -1545 -1548 0 - -1545 -1549 0 - -1545 -1550 0 - -1545 -1551 0 - -1545 -1552 0 - -1545 -1553 0 - -1546 -1547 0 - -1546 -1548 0 - -1546 -1549 0 - -1546 -1550 0 - -1546 -1551 0 - -1546 -1552 0 - -1546 -1553 0 - -1547 -1548 0 - -1547 -1549 0 - -1547 -1550 0 - -1547 -1551 0 - -1547 -1552 0 - -1547 -1553 0 - -1548 -1549 0 - -1548 -1550 0 - -1548 -1551 0 - -1548 -1552 0 - -1548 -1553 0 - -1549 -1550 0 - -1549 -1551 0 - -1549 -1552 0 - -1549 -1553 0 - -1550 -1551 0 - -1550 -1552 0 - -1550 -1553 0 - -1551 -1552 0 - -1551 -1553 0 - -1552 -1553 0 - -1554 -1555 0 - -1554 -1556 0 - -1554 -1557 0 - -1555 -1556 0 - -1555 -1557 0 - -1556 -1557 0 - -1558 -1559 0 - -1558 -1560 0 - -1558 -1561 0 - -1558 -1562 0 - -1558 -1563 0 - -1558 -1564 0 - -1558 -1565 0 - -1559 -1560 0 - -1559 -1561 0 - -1559 -1562 0 - -1559 -1563 0 - -1559 -1564 0 - -1559 -1565 0 - -1560 -1561 0 - -1560 -1562 0 - -1560 -1563 0 - -1560 -1564 0 - -1560 -1565 0 - -1561 -1562 0 - -1561 -1563 0 - -1561 -1564 0 - -1561 -1565 0 - -1562 -1563 0 - -1562 -1564 0 - -1562 -1565 0 - -1563 -1564 0 - -1563 -1565 0 - -1564 -1565 0 - -1566 -1567 0 - -1566 -1568 0 - -1566 -1569 0 - -1566 -1570 0 - -1566 -1571 0 - -1567 -1568 0 - -1567 -1569 0 - -1567 -1570 0 - -1567 -1571 0 - -1568 -1569 0 - -1568 -1570 0 - -1568 -1571 0 - -1569 -1570 0 - -1569 -1571 0 - -1570 -1571 0 - -1572 -1573 0 - -1572 -1574 0 - -1572 -1575 0 - -1572 -1576 0 - -1572 -1577 0 - -1572 -1578 0 - -1572 -1579 0 - -1572 -1580 0 - -1573 -1574 0 - -1573 -1575 0 - -1573 -1576 0 - -1573 -1577 0 - -1573 -1578 0 - -1573 -1579 0 - -1573 -1580 0 - -1574 -1575 0 - -1574 -1576 0 - -1574 -1577 0 - -1574 -1578 0 - -1574 -1579 0 - -1574 -1580 0 - -1575 -1576 0 - -1575 -1577 0 - -1575 -1578 0 - -1575 -1579 0 - -1575 -1580 0 - -1576 -1577 0 - -1576 -1578 0 - -1576 -1579 0 - -1576 -1580 0 - -1577 -1578 0 - -1577 -1579 0 - -1577 -1580 0 - -1578 -1579 0 - -1578 -1580 0 - -1579 -1580 0 - -1581 -1582 0 - -1581 -1583 0 - -1581 -1584 0 - -1581 -1585 0 - -1581 -1586 0 - -1581 -1587 0 - -1581 -1588 0 - -1582 -1583 0 - -1582 -1584 0 - -1582 -1585 0 - -1582 -1586 0 - -1582 -1587 0 - -1582 -1588 0 - -1583 -1584 0 - -1583 -1585 0 - -1583 -1586 0 - -1583 -1587 0 - -1583 -1588 0 - -1584 -1585 0 - -1584 -1586 0 - -1584 -1587 0 - -1584 -1588 0 - -1585 -1586 0 - -1585 -1587 0 - -1585 -1588 0 - -1586 -1587 0 - -1586 -1588 0 - -1587 -1588 0 - -1589 -1590 0 - -1589 -1591 0 - -1589 -1592 0 - -1589 -1593 0 - -1589 -1594 0 - -1590 -1591 0 - -1590 -1592 0 - -1590 -1593 0 - -1590 -1594 0 - -1591 -1592 0 - -1591 -1593 0 - -1591 -1594 0 - -1592 -1593 0 - -1592 -1594 0 - -1593 -1594 0 - -1595 -1596 0 - -1595 -1597 0 - -1595 -1598 0 - -1595 -1599 0 - -1595 -1600 0 - -1595 -1601 0 - -1595 -1602 0 - -1595 -1603 0 - -1595 -1604 0 - -1596 -1597 0 - -1596 -1598 0 - -1596 -1599 0 - -1596 -1600 0 - -1596 -1601 0 - -1596 -1602 0 - -1596 -1603 0 - -1596 -1604 0 - -1597 -1598 0 - -1597 -1599 0 - -1597 -1600 0 - -1597 -1601 0 - -1597 -1602 0 - -1597 -1603 0 - -1597 -1604 0 - -1598 -1599 0 - -1598 -1600 0 - -1598 -1601 0 - -1598 -1602 0 - -1598 -1603 0 - -1598 -1604 0 - -1599 -1600 0 - -1599 -1601 0 - -1599 -1602 0 - -1599 -1603 0 - -1599 -1604 0 - -1600 -1601 0 - -1600 -1602 0 - -1600 -1603 0 - -1600 -1604 0 - -1601 -1602 0 - -1601 -1603 0 - -1601 -1604 0 - -1602 -1603 0 - -1602 -1604 0 - -1603 -1604 0 - -1605 -1606 0 - -1605 -1607 0 - -1605 -1608 0 - -1605 -1609 0 - -1605 -1610 0 - -1605 -1611 0 - -1605 -1612 0 - -1605 -1613 0 - -1606 -1607 0 - -1606 -1608 0 - -1606 -1609 0 - -1606 -1610 0 - -1606 -1611 0 - -1606 -1612 0 - -1606 -1613 0 - -1607 -1608 0 - -1607 -1609 0 - -1607 -1610 0 - -1607 -1611 0 - -1607 -1612 0 - -1607 -1613 0 - -1608 -1609 0 - -1608 -1610 0 - -1608 -1611 0 - -1608 -1612 0 - -1608 -1613 0 - -1609 -1610 0 - -1609 -1611 0 - -1609 -1612 0 - -1609 -1613 0 - -1610 -1611 0 - -1610 -1612 0 - -1610 -1613 0 - -1611 -1612 0 - -1611 -1613 0 - -1612 -1613 0 - -1614 -1615 0 - -1614 -1616 0 - -1614 -1617 0 - -1615 -1616 0 - -1615 -1617 0 - -1616 -1617 0 - -1618 -1619 0 - -1618 -1620 0 - -1618 -1621 0 - -1618 -1622 0 - -1618 -1623 0 - -1619 -1620 0 - -1619 -1621 0 - -1619 -1622 0 - -1619 -1623 0 - -1620 -1621 0 - -1620 -1622 0 - -1620 -1623 0 - -1621 -1622 0 - -1621 -1623 0 - -1622 -1623 0 - -1624 -1625 0 - -1624 -1626 0 - -1624 -1627 0 - -1624 -1628 0 - -1625 -1626 0 - -1625 -1627 0 - -1625 -1628 0 - -1626 -1627 0 - -1626 -1628 0 - -1627 -1628 0 - -1629 -1630 0 - -1629 -1631 0 - -1629 -1632 0 - -1629 -1633 0 - -1629 -1634 0 - -1630 -1631 0 - -1630 -1632 0 - -1630 -1633 0 - -1630 -1634 0 - -1631 -1632 0 - -1631 -1633 0 - -1631 -1634 0 - -1632 -1633 0 - -1632 -1634 0 - -1633 -1634 0 - -1635 -1636 0 - -1635 -1637 0 - -1635 -1638 0 - -1635 -1639 0 - -1635 -1640 0 - -1635 -1641 0 - -1636 -1637 0 - -1636 -1638 0 - -1636 -1639 0 - -1636 -1640 0 - -1636 -1641 0 - -1637 -1638 0 - -1637 -1639 0 - -1637 -1640 0 - -1637 -1641 0 - -1638 -1639 0 - -1638 -1640 0 - -1638 -1641 0 - -1639 -1640 0 - -1639 -1641 0 - -1640 -1641 0 - -1642 -1643 0 - -1642 -1644 0 - -1643 -1644 0 - -1645 -1646 0 - -1645 -1647 0 - -1645 -1648 0 - -1646 -1647 0 - -1646 -1648 0 - -1647 -1648 0 - -1649 -1650 0 - -1649 -1651 0 - -1650 -1651 0 - -1652 -1653 0 - -1652 -1654 0 - -1652 -1655 0 - -1652 -1656 0 - -1653 -1654 0 - -1653 -1655 0 - -1653 -1656 0 - -1654 -1655 0 - -1654 -1656 0 - -1655 -1656 0 - -1657 -1658 0 - -1657 -1659 0 - -1657 -1660 0 - -1657 -1661 0 - -1657 -1662 0 - -1658 -1659 0 - -1658 -1660 0 - -1658 -1661 0 - -1658 -1662 0 - -1659 -1660 0 - -1659 -1661 0 - -1659 -1662 0 - -1660 -1661 0 - -1660 -1662 0 - -1661 -1662 0 - -1663 -1664 0 - -1663 -1665 0 - -1664 -1665 0 - -1666 -1667 0 - -1666 -1668 0 - -1667 -1668 0 - -1669 -1670 0 - -1669 -1671 0 - -1670 -1671 0 - -1672 -1673 0 - -1672 -1674 0 - -1673 -1674 0 - -1675 -1676 0 - -1675 -1677 0 - -1676 -1677 0 - -1678 -1679 0 - -1678 -1680 0 - -1679 -1680 0 - -1681 -1682 0 - -1683 -1684 0 - -1683 -1685 0 - -1683 -1686 0 - -1683 -1687 0 - -1684 -1685 0 - -1684 -1686 0 - -1684 -1687 0 - -1685 -1686 0 - -1685 -1687 0 - -1686 -1687 0 - -1688 -1689 0 - -1688 -1690 0 - -1688 -1691 0 - -1689 -1690 0 - -1689 -1691 0 - -1690 -1691 0 - -1692 -1693 0 - -1692 -1694 0 - -1693 -1694 0 - -1695 -1696 0 - -1695 -1697 0 - -1695 -1698 0 - -1695 -1699 0 - -1695 -1700 0 - -1696 -1697 0 - -1696 -1698 0 - -1696 -1699 0 - -1696 -1700 0 - -1697 -1698 0 - -1697 -1699 0 - -1697 -1700 0 - -1698 -1699 0 - -1698 -1700 0 - -1699 -1700 0 - -1701 -1702 0 - -1701 -1703 0 - -1701 -1704 0 - -1701 -1705 0 - -1701 -1706 0 - -1702 -1703 0 - -1702 -1704 0 - -1702 -1705 0 - -1702 -1706 0 - -1703 -1704 0 - -1703 -1705 0 - -1703 -1706 0 - -1704 -1705 0 - -1704 -1706 0 - -1705 -1706 0 - -1707 -1708 0 - -1707 -1709 0 - -1707 -1710 0 - -1707 -1711 0 - -1707 -1712 0 - -1707 -1713 0 - -1707 -1714 0 - -1708 -1709 0 - -1708 -1710 0 - -1708 -1711 0 - -1708 -1712 0 - -1708 -1713 0 - -1708 -1714 0 - -1709 -1710 0 - -1709 -1711 0 - -1709 -1712 0 - -1709 -1713 0 - -1709 -1714 0 - -1710 -1711 0 - -1710 -1712 0 - -1710 -1713 0 - -1710 -1714 0 - -1711 -1712 0 - -1711 -1713 0 - -1711 -1714 0 - -1712 -1713 0 - -1712 -1714 0 - -1713 -1714 0 - -1715 -1716 0 - -1715 -1717 0 - -1715 -1718 0 - -1715 -1719 0 - -1715 -1720 0 - -1715 -1721 0 - -1716 -1717 0 - -1716 -1718 0 - -1716 -1719 0 - -1716 -1720 0 - -1716 -1721 0 - -1717 -1718 0 - -1717 -1719 0 - -1717 -1720 0 - -1717 -1721 0 - -1718 -1719 0 - -1718 -1720 0 - -1718 -1721 0 - -1719 -1720 0 - -1719 -1721 0 - -1720 -1721 0 - -1722 -1723 0 - -1722 -1724 0 - -1722 -1725 0 - -1722 -1726 0 - -1722 -1727 0 - -1722 -1728 0 - -1722 -1729 0 - -1723 -1724 0 - -1723 -1725 0 - -1723 -1726 0 - -1723 -1727 0 - -1723 -1728 0 - -1723 -1729 0 - -1724 -1725 0 - -1724 -1726 0 - -1724 -1727 0 - -1724 -1728 0 - -1724 -1729 0 - -1725 -1726 0 - -1725 -1727 0 - -1725 -1728 0 - -1725 -1729 0 - -1726 -1727 0 - -1726 -1728 0 - -1726 -1729 0 - -1727 -1728 0 - -1727 -1729 0 - -1728 -1729 0 - -1730 -1731 0 - -1730 -1732 0 - -1730 -1733 0 - -1730 -1734 0 - -1730 -1735 0 - -1730 -1736 0 - -1730 -1737 0 - -1731 -1732 0 - -1731 -1733 0 - -1731 -1734 0 - -1731 -1735 0 - -1731 -1736 0 - -1731 -1737 0 - -1732 -1733 0 - -1732 -1734 0 - -1732 -1735 0 - -1732 -1736 0 - -1732 -1737 0 - -1733 -1734 0 - -1733 -1735 0 - -1733 -1736 0 - -1733 -1737 0 - -1734 -1735 0 - -1734 -1736 0 - -1734 -1737 0 - -1735 -1736 0 - -1735 -1737 0 - -1736 -1737 0 - -1738 -1739 0 - -1738 -1740 0 - -1738 -1741 0 - -1738 -1742 0 - -1738 -1743 0 - -1738 -1744 0 - -1739 -1740 0 - -1739 -1741 0 - -1739 -1742 0 - -1739 -1743 0 - -1739 -1744 0 - -1740 -1741 0 - -1740 -1742 0 - -1740 -1743 0 - -1740 -1744 0 - -1741 -1742 0 - -1741 -1743 0 - -1741 -1744 0 - -1742 -1743 0 - -1742 -1744 0 - -1743 -1744 0 - -1745 -1746 0 - -1745 -1747 0 - -1745 -1748 0 - -1746 -1747 0 - -1746 -1748 0 - -1747 -1748 0 - -1749 -1750 0 - -1749 -1751 0 - -1749 -1752 0 - -1749 -1753 0 - -1750 -1751 0 - -1750 -1752 0 - -1750 -1753 0 - -1751 -1752 0 - -1751 -1753 0 - -1752 -1753 0 - -1754 -1755 0 - -1754 -1756 0 - -1754 -1757 0 - -1754 -1758 0 - -1754 -1759 0 - -1754 -1760 0 - -1754 -1761 0 - -1754 -1762 0 - -1754 -1763 0 - -1755 -1756 0 - -1755 -1757 0 - -1755 -1758 0 - -1755 -1759 0 - -1755 -1760 0 - -1755 -1761 0 - -1755 -1762 0 - -1755 -1763 0 - -1756 -1757 0 - -1756 -1758 0 - -1756 -1759 0 - -1756 -1760 0 - -1756 -1761 0 - -1756 -1762 0 - -1756 -1763 0 - -1757 -1758 0 - -1757 -1759 0 - -1757 -1760 0 - -1757 -1761 0 - -1757 -1762 0 - -1757 -1763 0 - -1758 -1759 0 - -1758 -1760 0 - -1758 -1761 0 - -1758 -1762 0 - -1758 -1763 0 - -1759 -1760 0 - -1759 -1761 0 - -1759 -1762 0 - -1759 -1763 0 - -1760 -1761 0 - -1760 -1762 0 - -1760 -1763 0 - -1761 -1762 0 - -1761 -1763 0 - -1762 -1763 0 - -1764 -1765 0 - -1764 -1766 0 - -1764 -1767 0 - -1764 -1768 0 - -1764 -1769 0 - -1764 -1770 0 - -1764 -1771 0 - -1764 -1772 0 - -1765 -1766 0 - -1765 -1767 0 - -1765 -1768 0 - -1765 -1769 0 - -1765 -1770 0 - -1765 -1771 0 - -1765 -1772 0 - -1766 -1767 0 - -1766 -1768 0 - -1766 -1769 0 - -1766 -1770 0 - -1766 -1771 0 - -1766 -1772 0 - -1767 -1768 0 - -1767 -1769 0 - -1767 -1770 0 - -1767 -1771 0 - -1767 -1772 0 - -1768 -1769 0 - -1768 -1770 0 - -1768 -1771 0 - -1768 -1772 0 - -1769 -1770 0 - -1769 -1771 0 - -1769 -1772 0 - -1770 -1771 0 - -1770 -1772 0 - -1771 -1772 0 - -1773 -1774 0 - -1773 -1775 0 - -1773 -1776 0 - -1773 -1777 0 - -1774 -1775 0 - -1774 -1776 0 - -1774 -1777 0 - -1775 -1776 0 - -1775 -1777 0 - -1776 -1777 0 - -1778 -1779 0 - -1778 -1780 0 - -1778 -1781 0 - -1778 -1782 0 - -1778 -1783 0 - -1778 -1784 0 - -1778 -1785 0 - -1778 -1786 0 - -1779 -1780 0 - -1779 -1781 0 - -1779 -1782 0 - -1779 -1783 0 - -1779 -1784 0 - -1779 -1785 0 - -1779 -1786 0 - -1780 -1781 0 - -1780 -1782 0 - -1780 -1783 0 - -1780 -1784 0 - -1780 -1785 0 - -1780 -1786 0 - -1781 -1782 0 - -1781 -1783 0 - -1781 -1784 0 - -1781 -1785 0 - -1781 -1786 0 - -1782 -1783 0 - -1782 -1784 0 - -1782 -1785 0 - -1782 -1786 0 - -1783 -1784 0 - -1783 -1785 0 - -1783 -1786 0 - -1784 -1785 0 - -1784 -1786 0 - -1785 -1786 0 - -1787 -1788 0 - -1787 -1789 0 - -1788 -1789 0 - -1790 -1791 0 - -1790 -1792 0 - -1790 -1793 0 - -1790 -1794 0 - -1790 -1795 0 - -1790 -1796 0 - -1790 -1797 0 - -1791 -1792 0 - -1791 -1793 0 - -1791 -1794 0 - -1791 -1795 0 - -1791 -1796 0 - -1791 -1797 0 - -1792 -1793 0 - -1792 -1794 0 - -1792 -1795 0 - -1792 -1796 0 - -1792 -1797 0 - -1793 -1794 0 - -1793 -1795 0 - -1793 -1796 0 - -1793 -1797 0 - -1794 -1795 0 - -1794 -1796 0 - -1794 -1797 0 - -1795 -1796 0 - -1795 -1797 0 - -1796 -1797 0 - -1798 -1799 0 - -1798 -1800 0 - -1798 -1801 0 - -1798 -1802 0 - -1799 -1800 0 - -1799 -1801 0 - -1799 -1802 0 - -1800 -1801 0 - -1800 -1802 0 - -1801 -1802 0 - -1803 -1804 0 - -1803 -1805 0 - -1803 -1806 0 - -1804 -1805 0 - -1804 -1806 0 - -1805 -1806 0 - -1807 -1808 0 - -1807 -1809 0 - -1807 -1810 0 - -1807 -1811 0 - -1807 -1812 0 - -1808 -1809 0 - -1808 -1810 0 - -1808 -1811 0 - -1808 -1812 0 - -1809 -1810 0 - -1809 -1811 0 - -1809 -1812 0 - -1810 -1811 0 - -1810 -1812 0 - -1811 -1812 0 - -1813 -1814 0 - -1813 -1815 0 - -1813 -1816 0 - -1813 -1817 0 - -1814 -1815 0 - -1814 -1816 0 - -1814 -1817 0 - -1815 -1816 0 - -1815 -1817 0 - -1816 -1817 0 - -1818 -1819 0 - -1818 -1820 0 - -1818 -1821 0 - -1818 -1822 0 - -1819 -1820 0 - -1819 -1821 0 - -1819 -1822 0 - -1820 -1821 0 - -1820 -1822 0 - -1821 -1822 0 - -1823 -1824 0 - -1823 -1825 0 - -1823 -1826 0 - -1823 -1827 0 - -1823 -1828 0 - -1824 -1825 0 - -1824 -1826 0 - -1824 -1827 0 - -1824 -1828 0 - -1825 -1826 0 - -1825 -1827 0 - -1825 -1828 0 - -1826 -1827 0 - -1826 -1828 0 - -1827 -1828 0 - -1829 -1830 0 - -1829 -1831 0 - -1829 -1832 0 - -1829 -1833 0 - -1830 -1831 0 - -1830 -1832 0 - -1830 -1833 0 - -1831 -1832 0 - -1831 -1833 0 - -1832 -1833 0 - -1834 -1835 0 - -1834 -1836 0 - -1835 -1836 0 - -1837 -1838 0 - -1837 -1839 0 - -1838 -1839 0 - -1840 -1841 0 - -1840 -1842 0 - -1840 -1843 0 - -1840 -1844 0 - -1841 -1842 0 - -1841 -1843 0 - -1841 -1844 0 - -1842 -1843 0 - -1842 -1844 0 - -1843 -1844 0 - -1845 -1846 0 - -1845 -1847 0 - -1845 -1848 0 - -1845 -1849 0 - -1846 -1847 0 - -1846 -1848 0 - -1846 -1849 0 - -1847 -1848 0 - -1847 -1849 0 - -1848 -1849 0 - -1850 -1851 0 - -1852 -1853 0 - -1852 -1854 0 - -1852 -1855 0 - -1852 -1856 0 - -1853 -1854 0 - -1853 -1855 0 - -1853 -1856 0 - -1854 -1855 0 - -1854 -1856 0 - -1855 -1856 0 - -1857 -1858 0 - -1857 -1859 0 - -1857 -1860 0 - -1857 -1861 0 - -1857 -1862 0 - -1858 -1859 0 - -1858 -1860 0 - -1858 -1861 0 - -1858 -1862 0 - -1859 -1860 0 - -1859 -1861 0 - -1859 -1862 0 - -1860 -1861 0 - -1860 -1862 0 - -1861 -1862 0 - -1863 -1864 0 - -1865 -1866 0 - -1865 -1867 0 - -1865 -1868 0 - -1866 -1867 0 - -1866 -1868 0 - -1867 -1868 0 - -1869 -1870 0 - -1869 -1871 0 - -1869 -1872 0 - -1869 -1873 0 - -1870 -1871 0 - -1870 -1872 0 - -1870 -1873 0 - -1871 -1872 0 - -1871 -1873 0 - -1872 -1873 0 - -1874 -1875 0 - -1876 -1877 0 - -1876 -1878 0 - -1876 -1879 0 - -1876 -1880 0 - -1876 -1881 0 - -1877 -1878 0 - -1877 -1879 0 - -1877 -1880 0 - -1877 -1881 0 - -1878 -1879 0 - -1878 -1880 0 - -1878 -1881 0 - -1879 -1880 0 - -1879 -1881 0 - -1880 -1881 0 - -1882 -1883 0 - -1882 -1884 0 - -1882 -1885 0 - -1882 -1886 0 - -1882 -1887 0 - -1882 -1888 0 - -1882 -1889 0 - -1882 -1890 0 - -1883 -1884 0 - -1883 -1885 0 - -1883 -1886 0 - -1883 -1887 0 - -1883 -1888 0 - -1883 -1889 0 - -1883 -1890 0 - -1884 -1885 0 - -1884 -1886 0 - -1884 -1887 0 - -1884 -1888 0 - -1884 -1889 0 - -1884 -1890 0 - -1885 -1886 0 - -1885 -1887 0 - -1885 -1888 0 - -1885 -1889 0 - -1885 -1890 0 - -1886 -1887 0 - -1886 -1888 0 - -1886 -1889 0 - -1886 -1890 0 - -1887 -1888 0 - -1887 -1889 0 - -1887 -1890 0 - -1888 -1889 0 - -1888 -1890 0 - -1889 -1890 0 - -1891 -1892 0 - -1891 -1893 0 - -1891 -1894 0 - -1891 -1895 0 - -1892 -1893 0 - -1892 -1894 0 - -1892 -1895 0 - -1893 -1894 0 - -1893 -1895 0 - -1894 -1895 0 - -1896 -1897 0 - -1896 -1898 0 - -1896 -1899 0 - -1897 -1898 0 - -1897 -1899 0 - -1898 -1899 0 - -1900 -1901 0 - -1900 -1902 0 - -1900 -1903 0 - -1900 -1904 0 - -1900 -1905 0 - -1901 -1902 0 - -1901 -1903 0 - -1901 -1904 0 - -1901 -1905 0 - -1902 -1903 0 - -1902 -1904 0 - -1902 -1905 0 - -1903 -1904 0 - -1903 -1905 0 - -1904 -1905 0 - -1906 -1907 0 - -1906 -1908 0 - -1906 -1909 0 - -1906 -1910 0 - -1906 -1911 0 - -1906 -1912 0 - -1906 -1913 0 - -1907 -1908 0 - -1907 -1909 0 - -1907 -1910 0 - -1907 -1911 0 - -1907 -1912 0 - -1907 -1913 0 - -1908 -1909 0 - -1908 -1910 0 - -1908 -1911 0 - -1908 -1912 0 - -1908 -1913 0 - -1909 -1910 0 - -1909 -1911 0 - -1909 -1912 0 - -1909 -1913 0 - -1910 -1911 0 - -1910 -1912 0 - -1910 -1913 0 - -1911 -1912 0 - -1911 -1913 0 - -1912 -1913 0 - -1914 -1915 0 - -1914 -1916 0 - -1914 -1917 0 - -1914 -1918 0 - -1915 -1916 0 - -1915 -1917 0 - -1915 -1918 0 - -1916 -1917 0 - -1916 -1918 0 - -1917 -1918 0 - -1919 -1920 0 - -1919 -1921 0 - -1919 -1922 0 - -1919 -1923 0 - -1920 -1921 0 - -1920 -1922 0 - -1920 -1923 0 - -1921 -1922 0 - -1921 -1923 0 - -1922 -1923 0 - -1924 -1925 0 - -1924 -1926 0 - -1924 -1927 0 - -1924 -1928 0 - -1924 -1929 0 - -1924 -1930 0 - -1925 -1926 0 - -1925 -1927 0 - -1925 -1928 0 - -1925 -1929 0 - -1925 -1930 0 - -1926 -1927 0 - -1926 -1928 0 - -1926 -1929 0 - -1926 -1930 0 - -1927 -1928 0 - -1927 -1929 0 - -1927 -1930 0 - -1928 -1929 0 - -1928 -1930 0 - -1929 -1930 0 - -1931 -1932 0 - -1931 -1933 0 - -1931 -1934 0 - -1931 -1935 0 - -1931 -1936 0 - -1931 -1937 0 - -1931 -1938 0 - -1932 -1933 0 - -1932 -1934 0 - -1932 -1935 0 - -1932 -1936 0 - -1932 -1937 0 - -1932 -1938 0 - -1933 -1934 0 - -1933 -1935 0 - -1933 -1936 0 - -1933 -1937 0 - -1933 -1938 0 - -1934 -1935 0 - -1934 -1936 0 - -1934 -1937 0 - -1934 -1938 0 - -1935 -1936 0 - -1935 -1937 0 - -1935 -1938 0 - -1936 -1937 0 - -1936 -1938 0 - -1937 -1938 0 - -1939 -1940 0 - -1939 -1941 0 - -1939 -1942 0 - -1939 -1943 0 - -1939 -1944 0 - -1939 -1945 0 - -1939 -1946 0 - -1939 -1947 0 - -1939 -1948 0 - -1940 -1941 0 - -1940 -1942 0 - -1940 -1943 0 - -1940 -1944 0 - -1940 -1945 0 - -1940 -1946 0 - -1940 -1947 0 - -1940 -1948 0 - -1941 -1942 0 - -1941 -1943 0 - -1941 -1944 0 - -1941 -1945 0 - -1941 -1946 0 - -1941 -1947 0 - -1941 -1948 0 - -1942 -1943 0 - -1942 -1944 0 - -1942 -1945 0 - -1942 -1946 0 - -1942 -1947 0 - -1942 -1948 0 - -1943 -1944 0 - -1943 -1945 0 - -1943 -1946 0 - -1943 -1947 0 - -1943 -1948 0 - -1944 -1945 0 - -1944 -1946 0 - -1944 -1947 0 - -1944 -1948 0 - -1945 -1946 0 - -1945 -1947 0 - -1945 -1948 0 - -1946 -1947 0 - -1946 -1948 0 - -1947 -1948 0 - -1949 -1950 0 - -1949 -1951 0 - -1949 -1952 0 - -1949 -1953 0 - -1949 -1954 0 - -1949 -1955 0 - -1950 -1951 0 - -1950 -1952 0 - -1950 -1953 0 - -1950 -1954 0 - -1950 -1955 0 - -1951 -1952 0 - -1951 -1953 0 - -1951 -1954 0 - -1951 -1955 0 - -1952 -1953 0 - -1952 -1954 0 - -1952 -1955 0 - -1953 -1954 0 - -1953 -1955 0 - -1954 -1955 0 - -1956 -1957 0 - -1956 -1958 0 - -1956 -1959 0 - -1956 -1960 0 - -1956 -1961 0 - -1956 -1962 0 - -1956 -1963 0 - -1957 -1958 0 - -1957 -1959 0 - -1957 -1960 0 - -1957 -1961 0 - -1957 -1962 0 - -1957 -1963 0 - -1958 -1959 0 - -1958 -1960 0 - -1958 -1961 0 - -1958 -1962 0 - -1958 -1963 0 - -1959 -1960 0 - -1959 -1961 0 - -1959 -1962 0 - -1959 -1963 0 - -1960 -1961 0 - -1960 -1962 0 - -1960 -1963 0 - -1961 -1962 0 - -1961 -1963 0 - -1962 -1963 0 - -1964 -1965 0 - -1964 -1966 0 - -1964 -1967 0 - -1964 -1968 0 - -1964 -1969 0 - -1964 -1970 0 - -1964 -1971 0 - -1964 -1972 0 - -1965 -1966 0 - -1965 -1967 0 - -1965 -1968 0 - -1965 -1969 0 - -1965 -1970 0 - -1965 -1971 0 - -1965 -1972 0 - -1966 -1967 0 - -1966 -1968 0 - -1966 -1969 0 - -1966 -1970 0 - -1966 -1971 0 - -1966 -1972 0 - -1967 -1968 0 - -1967 -1969 0 - -1967 -1970 0 - -1967 -1971 0 - -1967 -1972 0 - -1968 -1969 0 - -1968 -1970 0 - -1968 -1971 0 - -1968 -1972 0 - -1969 -1970 0 - -1969 -1971 0 - -1969 -1972 0 - -1970 -1971 0 - -1970 -1972 0 - -1971 -1972 0 - -1973 -1974 0 - -1973 -1975 0 - -1973 -1976 0 - -1973 -1977 0 - -1973 -1978 0 - -1973 -1979 0 - -1974 -1975 0 - -1974 -1976 0 - -1974 -1977 0 - -1974 -1978 0 - -1974 -1979 0 - -1975 -1976 0 - -1975 -1977 0 - -1975 -1978 0 - -1975 -1979 0 - -1976 -1977 0 - -1976 -1978 0 - -1976 -1979 0 - -1977 -1978 0 - -1977 -1979 0 - -1978 -1979 0 - -1980 -1981 0 - -1980 -1982 0 - -1980 -1983 0 - -1980 -1984 0 - -1980 -1985 0 - -1980 -1986 0 - -1980 -1987 0 - -1980 -1988 0 - -1981 -1982 0 - -1981 -1983 0 - -1981 -1984 0 - -1981 -1985 0 - -1981 -1986 0 - -1981 -1987 0 - -1981 -1988 0 - -1982 -1983 0 - -1982 -1984 0 - -1982 -1985 0 - -1982 -1986 0 - -1982 -1987 0 - -1982 -1988 0 - -1983 -1984 0 - -1983 -1985 0 - -1983 -1986 0 - -1983 -1987 0 - -1983 -1988 0 - -1984 -1985 0 - -1984 -1986 0 - -1984 -1987 0 - -1984 -1988 0 - -1985 -1986 0 - -1985 -1987 0 - -1985 -1988 0 - -1986 -1987 0 - -1986 -1988 0 - -1987 -1988 0 - -1989 -1990 0 - -1989 -1991 0 - -1989 -1992 0 - -1990 -1991 0 - -1990 -1992 0 - -1991 -1992 0 - -1993 -1994 0 - -1993 -1995 0 - -1993 -1996 0 - -1993 -1997 0 - -1994 -1995 0 - -1994 -1996 0 - -1994 -1997 0 - -1995 -1996 0 - -1995 -1997 0 - -1996 -1997 0 - -1998 -1999 0 - -1998 -2000 0 - -1998 -2001 0 - -1998 -2002 0 - -1998 -2003 0 - -1999 -2000 0 - -1999 -2001 0 - -1999 -2002 0 - -1999 -2003 0 - -2000 -2001 0 - -2000 -2002 0 - -2000 -2003 0 - -2001 -2002 0 - -2001 -2003 0 - -2002 -2003 0 - -2004 -2005 0 - -2004 -2006 0 - -2004 -2007 0 - -2004 -2008 0 - -2004 -2009 0 - -2005 -2006 0 - -2005 -2007 0 - -2005 -2008 0 - -2005 -2009 0 - -2006 -2007 0 - -2006 -2008 0 - -2006 -2009 0 - -2007 -2008 0 - -2007 -2009 0 - -2008 -2009 0 - -2010 -2011 0 - -2010 -2012 0 - -2010 -2013 0 - -2011 -2012 0 - -2011 -2013 0 - -2012 -2013 0 - -2014 -2015 0 - -2014 -2016 0 - -2014 -2017 0 - -2014 -2018 0 - -2014 -2019 0 - -2015 -2016 0 - -2015 -2017 0 - -2015 -2018 0 - -2015 -2019 0 - -2016 -2017 0 - -2016 -2018 0 - -2016 -2019 0 - -2017 -2018 0 - -2017 -2019 0 - -2018 -2019 0 - -2020 -2021 0 - -2020 -2022 0 - -2020 -2023 0 - -2020 -2024 0 - -2021 -2022 0 - -2021 -2023 0 - -2021 -2024 0 - -2022 -2023 0 - -2022 -2024 0 - -2023 -2024 0 - -2025 -2026 0 - -2025 -2027 0 - -2025 -2028 0 - -2025 -2029 0 - -2025 -2030 0 - -2025 -2031 0 - -2025 -2032 0 - -2026 -2027 0 - -2026 -2028 0 - -2026 -2029 0 - -2026 -2030 0 - -2026 -2031 0 - -2026 -2032 0 - -2027 -2028 0 - -2027 -2029 0 - -2027 -2030 0 - -2027 -2031 0 - -2027 -2032 0 - -2028 -2029 0 - -2028 -2030 0 - -2028 -2031 0 - -2028 -2032 0 - -2029 -2030 0 - -2029 -2031 0 - -2029 -2032 0 - -2030 -2031 0 - -2030 -2032 0 - -2031 -2032 0 - -2033 -2034 0 - -2033 -2035 0 - -2034 -2035 0 - -2036 -2037 0 - -2036 -2038 0 - -2036 -2039 0 - -2037 -2038 0 - -2037 -2039 0 - -2038 -2039 0 - -2040 -2041 0 - -2040 -2042 0 - -2040 -2043 0 - -2040 -2044 0 - -2040 -2045 0 - -2040 -2046 0 - -2041 -2042 0 - -2041 -2043 0 - -2041 -2044 0 - -2041 -2045 0 - -2041 -2046 0 - -2042 -2043 0 - -2042 -2044 0 - -2042 -2045 0 - -2042 -2046 0 - -2043 -2044 0 - -2043 -2045 0 - -2043 -2046 0 - -2044 -2045 0 - -2044 -2046 0 - -2045 -2046 0 - -2047 -2048 0 - -2047 -2049 0 - -2047 -2050 0 - -2047 -2051 0 - -2047 -2052 0 - -2048 -2049 0 - -2048 -2050 0 - -2048 -2051 0 - -2048 -2052 0 - -2049 -2050 0 - -2049 -2051 0 - -2049 -2052 0 - -2050 -2051 0 - -2050 -2052 0 - -2051 -2052 0 - -2053 -2054 0 - -2053 -2055 0 - -2053 -2056 0 - -2054 -2055 0 - -2054 -2056 0 - -2055 -2056 0 - -2057 -2058 0 - -2057 -2059 0 - -2057 -2060 0 - -2058 -2059 0 - -2058 -2060 0 - -2059 -2060 0 - -2061 -2062 0 - -2061 -2063 0 - -2061 -2064 0 - -2061 -2065 0 - -2061 -2066 0 - -2062 -2063 0 - -2062 -2064 0 - -2062 -2065 0 - -2062 -2066 0 - -2063 -2064 0 - -2063 -2065 0 - -2063 -2066 0 - -2064 -2065 0 - -2064 -2066 0 - -2065 -2066 0 - -2067 -2068 0 - -2067 -2069 0 - -2067 -2070 0 - -2067 -2071 0 - -2068 -2069 0 - -2068 -2070 0 - -2068 -2071 0 - -2069 -2070 0 - -2069 -2071 0 - -2070 -2071 0 - -2072 -2073 0 - -2072 -2074 0 - -2072 -2075 0 - -2072 -2076 0 - -2072 -2077 0 - -2073 -2074 0 - -2073 -2075 0 - -2073 -2076 0 - -2073 -2077 0 - -2074 -2075 0 - -2074 -2076 0 - -2074 -2077 0 - -2075 -2076 0 - -2075 -2077 0 - -2076 -2077 0 - -2078 -2079 0 - -2078 -2080 0 - -2078 -2081 0 - -2079 -2080 0 - -2079 -2081 0 - -2080 -2081 0 - -2082 -2083 0 - -2082 -2084 0 - -2082 -2085 0 - -2082 -2086 0 - -2082 -2087 0 - -2082 -2088 0 - -2083 -2084 0 - -2083 -2085 0 - -2083 -2086 0 - -2083 -2087 0 - -2083 -2088 0 - -2084 -2085 0 - -2084 -2086 0 - -2084 -2087 0 - -2084 -2088 0 - -2085 -2086 0 - -2085 -2087 0 - -2085 -2088 0 - -2086 -2087 0 - -2086 -2088 0 - -2087 -2088 0 - -2089 -2090 0 - -2089 -2091 0 - -2089 -2092 0 - -2089 -2093 0 - -2090 -2091 0 - -2090 -2092 0 - -2090 -2093 0 - -2091 -2092 0 - -2091 -2093 0 - -2092 -2093 0 - -2094 -2095 0 - -2096 -2097 0 - -2096 -2098 0 - -2096 -2099 0 - -2096 -2100 0 - -2096 -2101 0 - -2097 -2098 0 - -2097 -2099 0 - -2097 -2100 0 - -2097 -2101 0 - -2098 -2099 0 - -2098 -2100 0 - -2098 -2101 0 - -2099 -2100 0 - -2099 -2101 0 - -2100 -2101 0 - -2102 -2103 0 - -2102 -2104 0 - -2102 -2105 0 - -2102 -2106 0 - -2103 -2104 0 - -2103 -2105 0 - -2103 -2106 0 - -2104 -2105 0 - -2104 -2106 0 - -2105 -2106 0 - -2107 -2108 0 - -2107 -2109 0 - -2107 -2110 0 - -2107 -2111 0 - -2108 -2109 0 - -2108 -2110 0 - -2108 -2111 0 - -2109 -2110 0 - -2109 -2111 0 - -2110 -2111 0 - -2112 -2113 0 - -2114 -2115 0 - -2114 -2116 0 - -2114 -2117 0 - -2114 -2118 0 - -2114 -2119 0 - -2114 -2120 0 - -2114 -2121 0 - -2115 -2116 0 - -2115 -2117 0 - -2115 -2118 0 - -2115 -2119 0 - -2115 -2120 0 - -2115 -2121 0 - -2116 -2117 0 - -2116 -2118 0 - -2116 -2119 0 - -2116 -2120 0 - -2116 -2121 0 - -2117 -2118 0 - -2117 -2119 0 - -2117 -2120 0 - -2117 -2121 0 - -2118 -2119 0 - -2118 -2120 0 - -2118 -2121 0 - -2119 -2120 0 - -2119 -2121 0 - -2120 -2121 0 - -2122 -2123 0 - -2122 -2124 0 - -2123 -2124 0 - -2125 -2126 0 - -2127 -2128 0 - -2127 -2129 0 - -2127 -2130 0 - -2127 -2131 0 - -2128 -2129 0 - -2128 -2130 0 - -2128 -2131 0 - -2129 -2130 0 - -2129 -2131 0 - -2130 -2131 0 - -2132 -2133 0 - -2132 -2134 0 - -2132 -2135 0 - -2132 -2136 0 - -2132 -2137 0 - -2133 -2134 0 - -2133 -2135 0 - -2133 -2136 0 - -2133 -2137 0 - -2134 -2135 0 - -2134 -2136 0 - -2134 -2137 0 - -2135 -2136 0 - -2135 -2137 0 - -2136 -2137 0 - -2138 -2139 0 - -2138 -2140 0 - -2138 -2141 0 - -2138 -2142 0 - -2138 -2143 0 - -2139 -2140 0 - -2139 -2141 0 - -2139 -2142 0 - -2139 -2143 0 - -2140 -2141 0 - -2140 -2142 0 - -2140 -2143 0 - -2141 -2142 0 - -2141 -2143 0 - -2142 -2143 0 - -2144 -2145 0 - -2144 -2146 0 - -2144 -2147 0 - -2144 -2148 0 - -2145 -2146 0 - -2145 -2147 0 - -2145 -2148 0 - -2146 -2147 0 - -2146 -2148 0 - -2147 -2148 0 - -2149 -2150 0 - -2149 -2151 0 - -2149 -2152 0 - -2149 -2153 0 - -2149 -2154 0 - -2149 -2155 0 - -2149 -2156 0 - -2150 -2151 0 - -2150 -2152 0 - -2150 -2153 0 - -2150 -2154 0 - -2150 -2155 0 - -2150 -2156 0 - -2151 -2152 0 - -2151 -2153 0 - -2151 -2154 0 - -2151 -2155 0 - -2151 -2156 0 - -2152 -2153 0 - -2152 -2154 0 - -2152 -2155 0 - -2152 -2156 0 - -2153 -2154 0 - -2153 -2155 0 - -2153 -2156 0 - -2154 -2155 0 - -2154 -2156 0 - -2155 -2156 0 - -2157 -2158 0 - -2157 -2159 0 - -2157 -2160 0 - -2157 -2161 0 - -2158 -2159 0 - -2158 -2160 0 - -2158 -2161 0 - -2159 -2160 0 - -2159 -2161 0 - -2160 -2161 0 - -2162 -2163 0 - -2162 -2164 0 - -2162 -2165 0 - -2162 -2166 0 - -2162 -2167 0 - -2163 -2164 0 - -2163 -2165 0 - -2163 -2166 0 - -2163 -2167 0 - -2164 -2165 0 - -2164 -2166 0 - -2164 -2167 0 - -2165 -2166 0 - -2165 -2167 0 - -2166 -2167 0 - -2168 -2169 0 - -2168 -2170 0 - -2168 -2171 0 - -2168 -2172 0 - -2168 -2173 0 - -2168 -2174 0 - -2168 -2175 0 - -2169 -2170 0 - -2169 -2171 0 - -2169 -2172 0 - -2169 -2173 0 - -2169 -2174 0 - -2169 -2175 0 - -2170 -2171 0 - -2170 -2172 0 - -2170 -2173 0 - -2170 -2174 0 - -2170 -2175 0 - -2171 -2172 0 - -2171 -2173 0 - -2171 -2174 0 - -2171 -2175 0 - -2172 -2173 0 - -2172 -2174 0 - -2172 -2175 0 - -2173 -2174 0 - -2173 -2175 0 - -2174 -2175 0 - -2176 -2177 0 - -2176 -2178 0 - -2176 -2179 0 - -2176 -2180 0 - -2176 -2181 0 - -2177 -2178 0 - -2177 -2179 0 - -2177 -2180 0 - -2177 -2181 0 - -2178 -2179 0 - -2178 -2180 0 - -2178 -2181 0 - -2179 -2180 0 - -2179 -2181 0 - -2180 -2181 0 - -2182 -2183 0 - -2182 -2184 0 - -2182 -2185 0 - -2182 -2186 0 - -2182 -2187 0 - -2182 -2188 0 - -2182 -2189 0 - -2182 -2190 0 - -2183 -2184 0 - -2183 -2185 0 - -2183 -2186 0 - -2183 -2187 0 - -2183 -2188 0 - -2183 -2189 0 - -2183 -2190 0 - -2184 -2185 0 - -2184 -2186 0 - -2184 -2187 0 - -2184 -2188 0 - -2184 -2189 0 - -2184 -2190 0 - -2185 -2186 0 - -2185 -2187 0 - -2185 -2188 0 - -2185 -2189 0 - -2185 -2190 0 - -2186 -2187 0 - -2186 -2188 0 - -2186 -2189 0 - -2186 -2190 0 - -2187 -2188 0 - -2187 -2189 0 - -2187 -2190 0 - -2188 -2189 0 - -2188 -2190 0 - -2189 -2190 0 - -2191 -2192 0 - -2191 -2193 0 - -2191 -2194 0 - -2191 -2195 0 - -2191 -2196 0 - -2191 -2197 0 - -2191 -2198 0 - -2191 -2199 0 - -2192 -2193 0 - -2192 -2194 0 - -2192 -2195 0 - -2192 -2196 0 - -2192 -2197 0 - -2192 -2198 0 - -2192 -2199 0 - -2193 -2194 0 - -2193 -2195 0 - -2193 -2196 0 - -2193 -2197 0 - -2193 -2198 0 - -2193 -2199 0 - -2194 -2195 0 - -2194 -2196 0 - -2194 -2197 0 - -2194 -2198 0 - -2194 -2199 0 - -2195 -2196 0 - -2195 -2197 0 - -2195 -2198 0 - -2195 -2199 0 - -2196 -2197 0 - -2196 -2198 0 - -2196 -2199 0 - -2197 -2198 0 - -2197 -2199 0 - -2198 -2199 0 - -2200 -2201 0 - -2200 -2202 0 - -2200 -2203 0 - -2200 -2204 0 - -2200 -2205 0 - -2200 -2206 0 - -2200 -2207 0 - -2201 -2202 0 - -2201 -2203 0 - -2201 -2204 0 - -2201 -2205 0 - -2201 -2206 0 - -2201 -2207 0 - -2202 -2203 0 - -2202 -2204 0 - -2202 -2205 0 - -2202 -2206 0 - -2202 -2207 0 - -2203 -2204 0 - -2203 -2205 0 - -2203 -2206 0 - -2203 -2207 0 - -2204 -2205 0 - -2204 -2206 0 - -2204 -2207 0 - -2205 -2206 0 - -2205 -2207 0 - -2206 -2207 0 - -2208 -2209 0 - -2208 -2210 0 - -2208 -2211 0 - -2208 -2212 0 - -2208 -2213 0 - -2208 -2214 0 - -2208 -2215 0 - -2208 -2216 0 - -2209 -2210 0 - -2209 -2211 0 - -2209 -2212 0 - -2209 -2213 0 - -2209 -2214 0 - -2209 -2215 0 - -2209 -2216 0 - -2210 -2211 0 - -2210 -2212 0 - -2210 -2213 0 - -2210 -2214 0 - -2210 -2215 0 - -2210 -2216 0 - -2211 -2212 0 - -2211 -2213 0 - -2211 -2214 0 - -2211 -2215 0 - -2211 -2216 0 - -2212 -2213 0 - -2212 -2214 0 - -2212 -2215 0 - -2212 -2216 0 - -2213 -2214 0 - -2213 -2215 0 - -2213 -2216 0 - -2214 -2215 0 - -2214 -2216 0 - -2215 -2216 0 - -2217 -2218 0 - -2217 -2219 0 - -2217 -2220 0 - -2217 -2221 0 - -2218 -2219 0 - -2218 -2220 0 - -2218 -2221 0 - -2219 -2220 0 - -2219 -2221 0 - -2220 -2221 0 - -2222 -2223 0 - -2222 -2224 0 - -2222 -2225 0 - -2222 -2226 0 - -2222 -2227 0 - -2222 -2228 0 - -2222 -2229 0 - -2222 -2230 0 - -2222 -2231 0 - -2223 -2224 0 - -2223 -2225 0 - -2223 -2226 0 - -2223 -2227 0 - -2223 -2228 0 - -2223 -2229 0 - -2223 -2230 0 - -2223 -2231 0 - -2224 -2225 0 - -2224 -2226 0 - -2224 -2227 0 - -2224 -2228 0 - -2224 -2229 0 - -2224 -2230 0 - -2224 -2231 0 - -2225 -2226 0 - -2225 -2227 0 - -2225 -2228 0 - -2225 -2229 0 - -2225 -2230 0 - -2225 -2231 0 - -2226 -2227 0 - -2226 -2228 0 - -2226 -2229 0 - -2226 -2230 0 - -2226 -2231 0 - -2227 -2228 0 - -2227 -2229 0 - -2227 -2230 0 - -2227 -2231 0 - -2228 -2229 0 - -2228 -2230 0 - -2228 -2231 0 - -2229 -2230 0 - -2229 -2231 0 - -2230 -2231 0 - -2232 -2233 0 - -2232 -2234 0 - -2232 -2235 0 - -2233 -2234 0 - -2233 -2235 0 - -2234 -2235 0 - -2236 -2237 0 - -2236 -2238 0 - -2236 -2239 0 - -2236 -2240 0 - -2236 -2241 0 - -2236 -2242 0 - -2236 -2243 0 - -2236 -2244 0 - -2237 -2238 0 - -2237 -2239 0 - -2237 -2240 0 - -2237 -2241 0 - -2237 -2242 0 - -2237 -2243 0 - -2237 -2244 0 - -2238 -2239 0 - -2238 -2240 0 - -2238 -2241 0 - -2238 -2242 0 - -2238 -2243 0 - -2238 -2244 0 - -2239 -2240 0 - -2239 -2241 0 - -2239 -2242 0 - -2239 -2243 0 - -2239 -2244 0 - -2240 -2241 0 - -2240 -2242 0 - -2240 -2243 0 - -2240 -2244 0 - -2241 -2242 0 - -2241 -2243 0 - -2241 -2244 0 - -2242 -2243 0 - -2242 -2244 0 - -2243 -2244 0 - -2245 -2246 0 - -2245 -2247 0 - -2245 -2248 0 - -2245 -2249 0 - -2245 -2250 0 - -2245 -2251 0 - -2246 -2247 0 - -2246 -2248 0 - -2246 -2249 0 - -2246 -2250 0 - -2246 -2251 0 - -2247 -2248 0 - -2247 -2249 0 - -2247 -2250 0 - -2247 -2251 0 - -2248 -2249 0 - -2248 -2250 0 - -2248 -2251 0 - -2249 -2250 0 - -2249 -2251 0 - -2250 -2251 0 - -2252 -2253 0 - -2252 -2254 0 - -2252 -2255 0 - -2252 -2256 0 - -2252 -2257 0 - -2252 -2258 0 - -2252 -2259 0 - -2252 -2260 0 - -2252 -2261 0 - -2252 -2262 0 - -2252 -2263 0 - -2253 -2254 0 - -2253 -2255 0 - -2253 -2256 0 - -2253 -2257 0 - -2253 -2258 0 - -2253 -2259 0 - -2253 -2260 0 - -2253 -2261 0 - -2253 -2262 0 - -2253 -2263 0 - -2254 -2255 0 - -2254 -2256 0 - -2254 -2257 0 - -2254 -2258 0 - -2254 -2259 0 - -2254 -2260 0 - -2254 -2261 0 - -2254 -2262 0 - -2254 -2263 0 - -2255 -2256 0 - -2255 -2257 0 - -2255 -2258 0 - -2255 -2259 0 - -2255 -2260 0 - -2255 -2261 0 - -2255 -2262 0 - -2255 -2263 0 - -2256 -2257 0 - -2256 -2258 0 - -2256 -2259 0 - -2256 -2260 0 - -2256 -2261 0 - -2256 -2262 0 - -2256 -2263 0 - -2257 -2258 0 - -2257 -2259 0 - -2257 -2260 0 - -2257 -2261 0 - -2257 -2262 0 - -2257 -2263 0 - -2258 -2259 0 - -2258 -2260 0 - -2258 -2261 0 - -2258 -2262 0 - -2258 -2263 0 - -2259 -2260 0 - -2259 -2261 0 - -2259 -2262 0 - -2259 -2263 0 - -2260 -2261 0 - -2260 -2262 0 - -2260 -2263 0 - -2261 -2262 0 - -2261 -2263 0 - -2262 -2263 0 - -2264 -2265 0 - -2264 -2266 0 - -2265 -2266 0 - -2267 -2268 0 - -2267 -2269 0 - -2267 -2270 0 - -2267 -2271 0 - -2267 -2272 0 - -2267 -2273 0 - -2267 -2274 0 - -2267 -2275 0 - -2267 -2276 0 - -2268 -2269 0 - -2268 -2270 0 - -2268 -2271 0 - -2268 -2272 0 - -2268 -2273 0 - -2268 -2274 0 - -2268 -2275 0 - -2268 -2276 0 - -2269 -2270 0 - -2269 -2271 0 - -2269 -2272 0 - -2269 -2273 0 - -2269 -2274 0 - -2269 -2275 0 - -2269 -2276 0 - -2270 -2271 0 - -2270 -2272 0 - -2270 -2273 0 - -2270 -2274 0 - -2270 -2275 0 - -2270 -2276 0 - -2271 -2272 0 - -2271 -2273 0 - -2271 -2274 0 - -2271 -2275 0 - -2271 -2276 0 - -2272 -2273 0 - -2272 -2274 0 - -2272 -2275 0 - -2272 -2276 0 - -2273 -2274 0 - -2273 -2275 0 - -2273 -2276 0 - -2274 -2275 0 - -2274 -2276 0 - -2275 -2276 0 - -2277 -2278 0 - -2277 -2279 0 - -2277 -2280 0 - -2277 -2281 0 - -2278 -2279 0 - -2278 -2280 0 - -2278 -2281 0 - -2279 -2280 0 - -2279 -2281 0 - -2280 -2281 0 - -2282 -2283 0 - -2282 -2284 0 - -2282 -2285 0 - -2282 -2286 0 - -2282 -2287 0 - -2282 -2288 0 - -2283 -2284 0 - -2283 -2285 0 - -2283 -2286 0 - -2283 -2287 0 - -2283 -2288 0 - -2284 -2285 0 - -2284 -2286 0 - -2284 -2287 0 - -2284 -2288 0 - -2285 -2286 0 - -2285 -2287 0 - -2285 -2288 0 - -2286 -2287 0 - -2286 -2288 0 - -2287 -2288 0 - -2289 -2290 0 - -2289 -2291 0 - -2289 -2292 0 - -2289 -2293 0 - -2289 -2294 0 - -2289 -2295 0 - -2289 -2296 0 - -2289 -2297 0 - -2289 -2298 0 - -2289 -2299 0 - -2289 -2300 0 - -2289 -2301 0 - -2290 -2291 0 - -2290 -2292 0 - -2290 -2293 0 - -2290 -2294 0 - -2290 -2295 0 - -2290 -2296 0 - -2290 -2297 0 - -2290 -2298 0 - -2290 -2299 0 - -2290 -2300 0 - -2290 -2301 0 - -2291 -2292 0 - -2291 -2293 0 - -2291 -2294 0 - -2291 -2295 0 - -2291 -2296 0 - -2291 -2297 0 - -2291 -2298 0 - -2291 -2299 0 - -2291 -2300 0 - -2291 -2301 0 - -2292 -2293 0 - -2292 -2294 0 - -2292 -2295 0 - -2292 -2296 0 - -2292 -2297 0 - -2292 -2298 0 - -2292 -2299 0 - -2292 -2300 0 - -2292 -2301 0 - -2293 -2294 0 - -2293 -2295 0 - -2293 -2296 0 - -2293 -2297 0 - -2293 -2298 0 - -2293 -2299 0 - -2293 -2300 0 - -2293 -2301 0 - -2294 -2295 0 - -2294 -2296 0 - -2294 -2297 0 - -2294 -2298 0 - -2294 -2299 0 - -2294 -2300 0 - -2294 -2301 0 - -2295 -2296 0 - -2295 -2297 0 - -2295 -2298 0 - -2295 -2299 0 - -2295 -2300 0 - -2295 -2301 0 - -2296 -2297 0 - -2296 -2298 0 - -2296 -2299 0 - -2296 -2300 0 - -2296 -2301 0 - -2297 -2298 0 - -2297 -2299 0 - -2297 -2300 0 - -2297 -2301 0 - -2298 -2299 0 - -2298 -2300 0 - -2298 -2301 0 - -2299 -2300 0 - -2299 -2301 0 - -2300 -2301 0 - -2302 -2303 0 - -2302 -2304 0 - -2302 -2305 0 - -2302 -2306 0 - -2302 -2307 0 - -2303 -2304 0 - -2303 -2305 0 - -2303 -2306 0 - -2303 -2307 0 - -2304 -2305 0 - -2304 -2306 0 - -2304 -2307 0 - -2305 -2306 0 - -2305 -2307 0 - -2306 -2307 0 - -2308 -2309 0 - -2308 -2310 0 - -2308 -2311 0 - -2308 -2312 0 - -2308 -2313 0 - -2308 -2314 0 - -2308 -2315 0 - -2308 -2316 0 - -2308 -2317 0 - -2309 -2310 0 - -2309 -2311 0 - -2309 -2312 0 - -2309 -2313 0 - -2309 -2314 0 - -2309 -2315 0 - -2309 -2316 0 - -2309 -2317 0 - -2310 -2311 0 - -2310 -2312 0 - -2310 -2313 0 - -2310 -2314 0 - -2310 -2315 0 - -2310 -2316 0 - -2310 -2317 0 - -2311 -2312 0 - -2311 -2313 0 - -2311 -2314 0 - -2311 -2315 0 - -2311 -2316 0 - -2311 -2317 0 - -2312 -2313 0 - -2312 -2314 0 - -2312 -2315 0 - -2312 -2316 0 - -2312 -2317 0 - -2313 -2314 0 - -2313 -2315 0 - -2313 -2316 0 - -2313 -2317 0 - -2314 -2315 0 - -2314 -2316 0 - -2314 -2317 0 - -2315 -2316 0 - -2315 -2317 0 - -2316 -2317 0 - -2318 -2319 0 - -2318 -2320 0 - -2318 -2321 0 - -2318 -2322 0 - -2318 -2323 0 - -2318 -2324 0 - -2319 -2320 0 - -2319 -2321 0 - -2319 -2322 0 - -2319 -2323 0 - -2319 -2324 0 - -2320 -2321 0 - -2320 -2322 0 - -2320 -2323 0 - -2320 -2324 0 - -2321 -2322 0 - -2321 -2323 0 - -2321 -2324 0 - -2322 -2323 0 - -2322 -2324 0 - -2323 -2324 0 - -2325 -2326 0 - -2325 -2327 0 - -2325 -2328 0 - -2325 -2329 0 - -2325 -2330 0 - -2325 -2331 0 - -2325 -2332 0 - -2325 -2333 0 - -2325 -2334 0 - -2326 -2327 0 - -2326 -2328 0 - -2326 -2329 0 - -2326 -2330 0 - -2326 -2331 0 - -2326 -2332 0 - -2326 -2333 0 - -2326 -2334 0 - -2327 -2328 0 - -2327 -2329 0 - -2327 -2330 0 - -2327 -2331 0 - -2327 -2332 0 - -2327 -2333 0 - -2327 -2334 0 - -2328 -2329 0 - -2328 -2330 0 - -2328 -2331 0 - -2328 -2332 0 - -2328 -2333 0 - -2328 -2334 0 - -2329 -2330 0 - -2329 -2331 0 - -2329 -2332 0 - -2329 -2333 0 - -2329 -2334 0 - -2330 -2331 0 - -2330 -2332 0 - -2330 -2333 0 - -2330 -2334 0 - -2331 -2332 0 - -2331 -2333 0 - -2331 -2334 0 - -2332 -2333 0 - -2332 -2334 0 - -2333 -2334 0 - -2335 -2336 0 - -2335 -2337 0 - -2335 -2338 0 - -2335 -2339 0 - -2336 -2337 0 - -2336 -2338 0 - -2336 -2339 0 - -2337 -2338 0 - -2337 -2339 0 - -2338 -2339 0 - -2340 -2341 0 - -2340 -2342 0 - -2341 -2342 0 - -2343 -2344 0 - -2343 -2345 0 - -2343 -2346 0 - -2343 -2347 0 - -2344 -2345 0 - -2344 -2346 0 - -2344 -2347 0 - -2345 -2346 0 - -2345 -2347 0 - -2346 -2347 0 - -2348 -2349 0 - -2348 -2350 0 - -2348 -2351 0 - -2349 -2350 0 - -2349 -2351 0 - -2350 -2351 0 - -2352 -2353 0 - -2352 -2354 0 - -2352 -2355 0 - -2352 -2356 0 - -2353 -2354 0 - -2353 -2355 0 - -2353 -2356 0 - -2354 -2355 0 - -2354 -2356 0 - -2355 -2356 0 - -2357 -2358 0 - -2357 -2359 0 - -2357 -2360 0 - -2357 -2361 0 - -2357 -2362 0 - -2357 -2363 0 - -2357 -2364 0 - -2358 -2359 0 - -2358 -2360 0 - -2358 -2361 0 - -2358 -2362 0 - -2358 -2363 0 - -2358 -2364 0 - -2359 -2360 0 - -2359 -2361 0 - -2359 -2362 0 - -2359 -2363 0 - -2359 -2364 0 - -2360 -2361 0 - -2360 -2362 0 - -2360 -2363 0 - -2360 -2364 0 - -2361 -2362 0 - -2361 -2363 0 - -2361 -2364 0 - -2362 -2363 0 - -2362 -2364 0 - -2363 -2364 0 - -2365 -2366 0 - -2365 -2367 0 - -2365 -2368 0 - -2365 -2369 0 - -2365 -2370 0 - -2365 -2371 0 - -2365 -2372 0 - -2366 -2367 0 - -2366 -2368 0 - -2366 -2369 0 - -2366 -2370 0 - -2366 -2371 0 - -2366 -2372 0 - -2367 -2368 0 - -2367 -2369 0 - -2367 -2370 0 - -2367 -2371 0 - -2367 -2372 0 - -2368 -2369 0 - -2368 -2370 0 - -2368 -2371 0 - -2368 -2372 0 - -2369 -2370 0 - -2369 -2371 0 - -2369 -2372 0 - -2370 -2371 0 - -2370 -2372 0 - -2371 -2372 0 - -2373 -2374 0 - -2373 -2375 0 - -2374 -2375 0 - -2376 -2377 0 - -2376 -2378 0 - -2376 -2379 0 - -2376 -2380 0 - -2376 -2381 0 - -2377 -2378 0 - -2377 -2379 0 - -2377 -2380 0 - -2377 -2381 0 - -2378 -2379 0 - -2378 -2380 0 - -2378 -2381 0 - -2379 -2380 0 - -2379 -2381 0 - -2380 -2381 0 - -2382 -2383 0 - -2382 -2384 0 - -2382 -2385 0 - -2382 -2386 0 - -2383 -2384 0 - -2383 -2385 0 - -2383 -2386 0 - -2384 -2385 0 - -2384 -2386 0 - -2385 -2386 0 - -2387 -2388 0 - -2387 -2389 0 - -2387 -2390 0 - -2387 -2391 0 - -2388 -2389 0 - -2388 -2390 0 - -2388 -2391 0 - -2389 -2390 0 - -2389 -2391 0 - -2390 -2391 0 - -2392 -2393 0 - -2392 -2394 0 - -2393 -2394 0 - -2395 -2396 0 - -2395 -2397 0 - -2396 -2397 0 - -2398 -2399 0 - -2398 -2400 0 - -2398 -2401 0 - -2399 -2400 0 - -2399 -2401 0 - -2400 -2401 0 - -2402 -2403 0 - -2402 -2404 0 - -2403 -2404 0 - -2405 -2406 0 - -2405 -2407 0 - -2405 -2408 0 - -2406 -2407 0 - -2406 -2408 0 - -2407 -2408 0 - -2409 -2410 0 - -2409 -2411 0 - -2409 -2412 0 - -2409 -2413 0 - -2410 -2411 0 - -2410 -2412 0 - -2410 -2413 0 - -2411 -2412 0 - -2411 -2413 0 - -2412 -2413 0 - -2414 -2415 0 - -2414 -2416 0 - -2414 -2417 0 - -2415 -2416 0 - -2415 -2417 0 - -2416 -2417 0 - -2418 -2419 0 - -2418 -2420 0 - -2418 -2421 0 - -2418 -2422 0 - -2418 -2423 0 - -2418 -2424 0 - -2419 -2420 0 - -2419 -2421 0 - -2419 -2422 0 - -2419 -2423 0 - -2419 -2424 0 - -2420 -2421 0 - -2420 -2422 0 - -2420 -2423 0 - -2420 -2424 0 - -2421 -2422 0 - -2421 -2423 0 - -2421 -2424 0 - -2422 -2423 0 - -2422 -2424 0 - -2423 -2424 0 - -2425 -2426 0 - -2425 -2427 0 - -2425 -2428 0 - -2425 -2429 0 - -2425 -2430 0 - -2425 -2431 0 - -2425 -2432 0 - -2425 -2433 0 - -2426 -2427 0 - -2426 -2428 0 - -2426 -2429 0 - -2426 -2430 0 - -2426 -2431 0 - -2426 -2432 0 - -2426 -2433 0 - -2427 -2428 0 - -2427 -2429 0 - -2427 -2430 0 - -2427 -2431 0 - -2427 -2432 0 - -2427 -2433 0 - -2428 -2429 0 - -2428 -2430 0 - -2428 -2431 0 - -2428 -2432 0 - -2428 -2433 0 - -2429 -2430 0 - -2429 -2431 0 - -2429 -2432 0 - -2429 -2433 0 - -2430 -2431 0 - -2430 -2432 0 - -2430 -2433 0 - -2431 -2432 0 - -2431 -2433 0 - -2432 -2433 0 - -2434 -2435 0 - -2434 -2436 0 - -2434 -2437 0 - -2434 -2438 0 - -2434 -2439 0 - -2435 -2436 0 - -2435 -2437 0 - -2435 -2438 0 - -2435 -2439 0 - -2436 -2437 0 - -2436 -2438 0 - -2436 -2439 0 - -2437 -2438 0 - -2437 -2439 0 - -2438 -2439 0 - -2440 -2441 0 - -2440 -2442 0 - -2440 -2443 0 - -2440 -2444 0 - -2440 -2445 0 - -2441 -2442 0 - -2441 -2443 0 - -2441 -2444 0 - -2441 -2445 0 - -2442 -2443 0 - -2442 -2444 0 - -2442 -2445 0 - -2443 -2444 0 - -2443 -2445 0 - -2444 -2445 0 - -2446 -2447 0 - -2446 -2448 0 - -2446 -2449 0 - -2446 -2450 0 - -2446 -2451 0 - -2446 -2452 0 - -2446 -2453 0 - -2446 -2454 0 - -2447 -2448 0 - -2447 -2449 0 - -2447 -2450 0 - -2447 -2451 0 - -2447 -2452 0 - -2447 -2453 0 - -2447 -2454 0 - -2448 -2449 0 - -2448 -2450 0 - -2448 -2451 0 - -2448 -2452 0 - -2448 -2453 0 - -2448 -2454 0 - -2449 -2450 0 - -2449 -2451 0 - -2449 -2452 0 - -2449 -2453 0 - -2449 -2454 0 - -2450 -2451 0 - -2450 -2452 0 - -2450 -2453 0 - -2450 -2454 0 - -2451 -2452 0 - -2451 -2453 0 - -2451 -2454 0 - -2452 -2453 0 - -2452 -2454 0 - -2453 -2454 0 - -2455 -2456 0 - -2455 -2457 0 - -2455 -2458 0 - -2455 -2459 0 - -2456 -2457 0 - -2456 -2458 0 - -2456 -2459 0 - -2457 -2458 0 - -2457 -2459 0 - -2458 -2459 0 - -2460 -2461 0 - -2460 -2462 0 - -2460 -2463 0 - -2460 -2464 0 - -2460 -2465 0 - -2460 -2466 0 - -2460 -2467 0 - -2460 -2468 0 - -2461 -2462 0 - -2461 -2463 0 - -2461 -2464 0 - -2461 -2465 0 - -2461 -2466 0 - -2461 -2467 0 - -2461 -2468 0 - -2462 -2463 0 - -2462 -2464 0 - -2462 -2465 0 - -2462 -2466 0 - -2462 -2467 0 - -2462 -2468 0 - -2463 -2464 0 - -2463 -2465 0 - -2463 -2466 0 - -2463 -2467 0 - -2463 -2468 0 - -2464 -2465 0 - -2464 -2466 0 - -2464 -2467 0 - -2464 -2468 0 - -2465 -2466 0 - -2465 -2467 0 - -2465 -2468 0 - -2466 -2467 0 - -2466 -2468 0 - -2467 -2468 0 - -2469 -2470 0 - -2469 -2471 0 - -2469 -2472 0 - -2469 -2473 0 - -2469 -2474 0 - -2469 -2475 0 - -2470 -2471 0 - -2470 -2472 0 - -2470 -2473 0 - -2470 -2474 0 - -2470 -2475 0 - -2471 -2472 0 - -2471 -2473 0 - -2471 -2474 0 - -2471 -2475 0 - -2472 -2473 0 - -2472 -2474 0 - -2472 -2475 0 - -2473 -2474 0 - -2473 -2475 0 - -2474 -2475 0 - -2476 -2477 0 - -2476 -2478 0 - -2476 -2479 0 - -2476 -2480 0 - -2476 -2481 0 - -2476 -2482 0 - -2476 -2483 0 - -2476 -2484 0 - -2477 -2478 0 - -2477 -2479 0 - -2477 -2480 0 - -2477 -2481 0 - -2477 -2482 0 - -2477 -2483 0 - -2477 -2484 0 - -2478 -2479 0 - -2478 -2480 0 - -2478 -2481 0 - -2478 -2482 0 - -2478 -2483 0 - -2478 -2484 0 - -2479 -2480 0 - -2479 -2481 0 - -2479 -2482 0 - -2479 -2483 0 - -2479 -2484 0 - -2480 -2481 0 - -2480 -2482 0 - -2480 -2483 0 - -2480 -2484 0 - -2481 -2482 0 - -2481 -2483 0 - -2481 -2484 0 - -2482 -2483 0 - -2482 -2484 0 - -2483 -2484 0 - -2485 -2486 0 - -2485 -2487 0 - -2485 -2488 0 - -2485 -2489 0 - -2485 -2490 0 - -2486 -2487 0 - -2486 -2488 0 - -2486 -2489 0 - -2486 -2490 0 - -2487 -2488 0 - -2487 -2489 0 - -2487 -2490 0 - -2488 -2489 0 - -2488 -2490 0 - -2489 -2490 0 - -2491 -2492 0 - -2491 -2493 0 - -2491 -2494 0 - -2491 -2495 0 - -2491 -2496 0 - -2491 -2497 0 - -2491 -2498 0 - -2491 -2499 0 - -2491 -2500 0 - -2492 -2493 0 - -2492 -2494 0 - -2492 -2495 0 - -2492 -2496 0 - -2492 -2497 0 - -2492 -2498 0 - -2492 -2499 0 - -2492 -2500 0 - -2493 -2494 0 - -2493 -2495 0 - -2493 -2496 0 - -2493 -2497 0 - -2493 -2498 0 - -2493 -2499 0 - -2493 -2500 0 - -2494 -2495 0 - -2494 -2496 0 - -2494 -2497 0 - -2494 -2498 0 - -2494 -2499 0 - -2494 -2500 0 - -2495 -2496 0 - -2495 -2497 0 - -2495 -2498 0 - -2495 -2499 0 - -2495 -2500 0 - -2496 -2497 0 - -2496 -2498 0 - -2496 -2499 0 - -2496 -2500 0 - -2497 -2498 0 - -2497 -2499 0 - -2497 -2500 0 - -2498 -2499 0 - -2498 -2500 0 - -2499 -2500 0 - -2501 -2502 0 - -2501 -2503 0 - -2501 -2504 0 - -2501 -2505 0 - -2501 -2506 0 - -2501 -2507 0 - -2501 -2508 0 - -2501 -2509 0 - -2501 -2510 0 - -2501 -2511 0 - -2502 -2503 0 - -2502 -2504 0 - -2502 -2505 0 - -2502 -2506 0 - -2502 -2507 0 - -2502 -2508 0 - -2502 -2509 0 - -2502 -2510 0 - -2502 -2511 0 - -2503 -2504 0 - -2503 -2505 0 - -2503 -2506 0 - -2503 -2507 0 - -2503 -2508 0 - -2503 -2509 0 - -2503 -2510 0 - -2503 -2511 0 - -2504 -2505 0 - -2504 -2506 0 - -2504 -2507 0 - -2504 -2508 0 - -2504 -2509 0 - -2504 -2510 0 - -2504 -2511 0 - -2505 -2506 0 - -2505 -2507 0 - -2505 -2508 0 - -2505 -2509 0 - -2505 -2510 0 - -2505 -2511 0 - -2506 -2507 0 - -2506 -2508 0 - -2506 -2509 0 - -2506 -2510 0 - -2506 -2511 0 - -2507 -2508 0 - -2507 -2509 0 - -2507 -2510 0 - -2507 -2511 0 - -2508 -2509 0 - -2508 -2510 0 - -2508 -2511 0 - -2509 -2510 0 - -2509 -2511 0 - -2510 -2511 0 - -2512 -2513 0 - -2512 -2514 0 - -2512 -2515 0 - -2512 -2516 0 - -2512 -2517 0 - -2512 -2518 0 - -2512 -2519 0 - -2512 -2520 0 - -2513 -2514 0 - -2513 -2515 0 - -2513 -2516 0 - -2513 -2517 0 - -2513 -2518 0 - -2513 -2519 0 - -2513 -2520 0 - -2514 -2515 0 - -2514 -2516 0 - -2514 -2517 0 - -2514 -2518 0 - -2514 -2519 0 - -2514 -2520 0 - -2515 -2516 0 - -2515 -2517 0 - -2515 -2518 0 - -2515 -2519 0 - -2515 -2520 0 - -2516 -2517 0 - -2516 -2518 0 - -2516 -2519 0 - -2516 -2520 0 - -2517 -2518 0 - -2517 -2519 0 - -2517 -2520 0 - -2518 -2519 0 - -2518 -2520 0 - -2519 -2520 0 - -2521 -2522 0 - -2521 -2523 0 - -2521 -2524 0 - -2522 -2523 0 - -2522 -2524 0 - -2523 -2524 0 - -2525 -2526 0 - -2525 -2527 0 - -2525 -2528 0 - -2525 -2529 0 - -2525 -2530 0 - -2526 -2527 0 - -2526 -2528 0 - -2526 -2529 0 - -2526 -2530 0 - -2527 -2528 0 - -2527 -2529 0 - -2527 -2530 0 - -2528 -2529 0 - -2528 -2530 0 - -2529 -2530 0 - -2531 -2532 0 - -2531 -2533 0 - -2531 -2534 0 - -2531 -2535 0 - -2531 -2536 0 - -2532 -2533 0 - -2532 -2534 0 - -2532 -2535 0 - -2532 -2536 0 - -2533 -2534 0 - -2533 -2535 0 - -2533 -2536 0 - -2534 -2535 0 - -2534 -2536 0 - -2535 -2536 0 - -2537 -2538 0 - -2537 -2539 0 - -2537 -2540 0 - -2537 -2541 0 - -2538 -2539 0 - -2538 -2540 0 - -2538 -2541 0 - -2539 -2540 0 - -2539 -2541 0 - -2540 -2541 0 - -2542 -2543 0 - -2542 -2544 0 - -2542 -2545 0 - -2542 -2546 0 - -2542 -2547 0 - -2543 -2544 0 - -2543 -2545 0 - -2543 -2546 0 - -2543 -2547 0 - -2544 -2545 0 - -2544 -2546 0 - -2544 -2547 0 - -2545 -2546 0 - -2545 -2547 0 - -2546 -2547 0 - -2548 -2549 0 - -2548 -2550 0 - -2548 -2551 0 - -2548 -2552 0 - -2549 -2550 0 - -2549 -2551 0 - -2549 -2552 0 - -2550 -2551 0 - -2550 -2552 0 - -2551 -2552 0 - -2553 -2554 0 - -2553 -2555 0 - -2553 -2556 0 - -2553 -2557 0 - -2554 -2555 0 - -2554 -2556 0 - -2554 -2557 0 - -2555 -2556 0 - -2555 -2557 0 - -2556 -2557 0 - -2558 -2559 0 - -2558 -2560 0 - -2558 -2561 0 - -2559 -2560 0 - -2559 -2561 0 - -2560 -2561 0 - -2562 -2563 0 - -2562 -2564 0 - -2563 -2564 0 - -2565 -2566 0 - -2565 -2567 0 - -2565 -2568 0 - -2565 -2569 0 - -2565 -2570 0 - -2565 -2571 0 - -2566 -2567 0 - -2566 -2568 0 - -2566 -2569 0 - -2566 -2570 0 - -2566 -2571 0 - -2567 -2568 0 - -2567 -2569 0 - -2567 -2570 0 - -2567 -2571 0 - -2568 -2569 0 - -2568 -2570 0 - -2568 -2571 0 - -2569 -2570 0 - -2569 -2571 0 - -2570 -2571 0 - -2572 -2573 0 - -2572 -2574 0 - -2572 -2575 0 - -2572 -2576 0 - -2572 -2577 0 - -2572 -2578 0 - -2572 -2579 0 - -2572 -2580 0 - -2573 -2574 0 - -2573 -2575 0 - -2573 -2576 0 - -2573 -2577 0 - -2573 -2578 0 - -2573 -2579 0 - -2573 -2580 0 - -2574 -2575 0 - -2574 -2576 0 - -2574 -2577 0 - -2574 -2578 0 - -2574 -2579 0 - -2574 -2580 0 - -2575 -2576 0 - -2575 -2577 0 - -2575 -2578 0 - -2575 -2579 0 - -2575 -2580 0 - -2576 -2577 0 - -2576 -2578 0 - -2576 -2579 0 - -2576 -2580 0 - -2577 -2578 0 - -2577 -2579 0 - -2577 -2580 0 - -2578 -2579 0 - -2578 -2580 0 - -2579 -2580 0 - -2581 -2582 0 - -2581 -2583 0 - -2581 -2584 0 - -2582 -2583 0 - -2582 -2584 0 - -2583 -2584 0 - -2585 -2586 0 - -2585 -2587 0 - -2586 -2587 0 - -2588 -2589 0 - -2588 -2590 0 - -2588 -2591 0 - -2588 -2592 0 - -2589 -2590 0 - -2589 -2591 0 - -2589 -2592 0 - -2590 -2591 0 - -2590 -2592 0 - -2591 -2592 0 - -2593 -2594 0 - -2593 -2595 0 - -2593 -2596 0 - -2593 -2597 0 - -2594 -2595 0 - -2594 -2596 0 - -2594 -2597 0 - -2595 -2596 0 - -2595 -2597 0 - -2596 -2597 0 - -2598 -2599 0 - -2598 -2600 0 - -2598 -2601 0 - -2599 -2600 0 - -2599 -2601 0 - -2600 -2601 0 - -393 -703 0 - -393 -990 0 - -393 -1193 0 - -393 -1334 0 - -393 -1701 0 - -393 -2182 0 - -703 -990 0 - -703 -1193 0 - -703 -1334 0 - -703 -1701 0 - -703 -2182 0 - -990 -1193 0 - -990 -1334 0 - -990 -1701 0 - -990 -2182 0 - -1193 -1334 0 - -1193 -1701 0 - -1193 -2182 0 - -1334 -1701 0 - -1334 -2182 0 - -1701 -2182 0 - -173 -394 0 - -173 -704 0 - -173 -1117 0 - -173 -1194 0 - -173 -1515 0 - -173 -1629 0 - -173 -2047 0 - -394 -704 0 - -394 -1117 0 - -394 -1194 0 - -394 -1515 0 - -394 -1629 0 - -394 -2047 0 - -704 -1117 0 - -704 -1194 0 - -704 -1515 0 - -704 -1629 0 - -704 -2047 0 - -1117 -1194 0 - -1117 -1515 0 - -1117 -1629 0 - -1117 -2047 0 - -1194 -1515 0 - -1194 -1629 0 - -1194 -2047 0 - -1515 -1629 0 - -1515 -2047 0 - -1629 -2047 0 - -395 -705 0 - -395 -1118 0 - -395 -1516 0 - -395 -1702 0 - -395 -2183 0 - -395 -2335 0 - -705 -1118 0 - -705 -1516 0 - -705 -1702 0 - -705 -2183 0 - -705 -2335 0 - -1118 -1516 0 - -1118 -1702 0 - -1118 -2183 0 - -1118 -2335 0 - -1516 -1702 0 - -1516 -2183 0 - -1516 -2335 0 - -1702 -2183 0 - -1702 -2335 0 - -2183 -2335 0 - -98 -396 0 - -98 -706 0 - -98 -991 0 - -98 -1195 0 - -98 -1517 0 - -98 -2184 0 - -98 -2336 0 - -396 -706 0 - -396 -991 0 - -396 -1195 0 - -396 -1517 0 - -396 -2184 0 - -396 -2336 0 - -706 -991 0 - -706 -1195 0 - -706 -1517 0 - -706 -2184 0 - -706 -2336 0 - -991 -1195 0 - -991 -1517 0 - -991 -2184 0 - -991 -2336 0 - -1195 -1517 0 - -1195 -2184 0 - -1195 -2336 0 - -1517 -2184 0 - -1517 -2336 0 - -2184 -2336 0 - -992 -1119 0 - -992 -1335 0 - -992 -1630 0 - -992 -1703 0 - -992 -2048 0 - -1119 -1335 0 - -1119 -1630 0 - -1119 -1703 0 - -1119 -2048 0 - -1335 -1630 0 - -1335 -1703 0 - -1335 -2048 0 - -1630 -1703 0 - -1630 -2048 0 - -1703 -2048 0 - -174 -707 0 - -174 -993 0 - -174 -2049 0 - -174 -2185 0 - -707 -993 0 - -707 -2049 0 - -707 -2185 0 - -993 -2049 0 - -993 -2185 0 - -2049 -2185 0 - -994 -1631 0 - -994 -1704 0 - -994 -2186 0 - -1631 -1704 0 - -1631 -2186 0 - -1704 -2186 0 - -99 -708 0 - -99 -995 0 - -99 -1196 0 - -99 -1336 0 - -99 -1518 0 - -99 -1632 0 - -708 -995 0 - -708 -1196 0 - -708 -1336 0 - -708 -1518 0 - -708 -1632 0 - -995 -1196 0 - -995 -1336 0 - -995 -1518 0 - -995 -1632 0 - -1196 -1336 0 - -1196 -1518 0 - -1196 -1632 0 - -1336 -1518 0 - -1336 -1632 0 - -1518 -1632 0 - -100 -996 0 - -100 -1197 0 - -100 -1337 0 - -100 -1519 0 - -100 -2187 0 - -996 -1197 0 - -996 -1337 0 - -996 -1519 0 - -996 -2187 0 - -1197 -1337 0 - -1197 -1519 0 - -1197 -2187 0 - -1337 -1519 0 - -1337 -2187 0 - -1519 -2187 0 - -175 -397 0 - -175 -997 0 - -175 -1198 0 - -175 -1338 0 - -175 -1705 0 - -175 -2337 0 - -397 -997 0 - -397 -1198 0 - -397 -1338 0 - -397 -1705 0 - -397 -2337 0 - -997 -1198 0 - -997 -1338 0 - -997 -1705 0 - -997 -2337 0 - -1198 -1338 0 - -1198 -1705 0 - -1198 -2337 0 - -1338 -1705 0 - -1338 -2337 0 - -1705 -2337 0 - -398 -1120 0 - -398 -1520 0 - -398 -1706 0 - -398 -2050 0 - -1120 -1520 0 - -1120 -1706 0 - -1120 -2050 0 - -1520 -1706 0 - -1520 -2050 0 - -1706 -2050 0 - -101 -998 0 - -101 -1339 0 - -101 -2051 0 - -101 -2188 0 - -998 -1339 0 - -998 -2051 0 - -998 -2188 0 - -1339 -2051 0 - -1339 -2188 0 - -2051 -2188 0 - -102 -399 0 - -102 -1340 0 - -102 -1633 0 - -102 -2052 0 - -102 -2189 0 - -102 -2338 0 - -399 -1340 0 - -399 -1633 0 - -399 -2052 0 - -399 -2189 0 - -399 -2338 0 - -1340 -1633 0 - -1340 -2052 0 - -1340 -2189 0 - -1340 -2338 0 - -1633 -2052 0 - -1633 -2189 0 - -1633 -2338 0 - -2052 -2189 0 - -2052 -2338 0 - -2189 -2338 0 - -1199 -1521 0 - -1199 -1634 0 - -1199 -2190 0 - -1199 -2339 0 - -1521 -1634 0 - -1521 -2190 0 - -1521 -2339 0 - -1634 -2190 0 - -1634 -2339 0 - -2190 -2339 0 - -221 -400 0 - -221 -549 0 - -221 -1341 0 - -221 -1452 0 - -221 -1852 0 - -221 -2191 0 - -400 -549 0 - -400 -1341 0 - -400 -1452 0 - -400 -1852 0 - -400 -2191 0 - -549 -1341 0 - -549 -1452 0 - -549 -1852 0 - -549 -2191 0 - -1341 -1452 0 - -1341 -1852 0 - -1341 -2191 0 - -1452 -1852 0 - -1452 -2191 0 - -1852 -2191 0 - -222 -401 0 - -222 -775 0 - -222 -1342 0 - -222 -1453 0 - -222 -1522 0 - -222 -2192 0 - -401 -775 0 - -401 -1342 0 - -401 -1453 0 - -401 -1522 0 - -401 -2192 0 - -775 -1342 0 - -775 -1453 0 - -775 -1522 0 - -775 -2192 0 - -1342 -1453 0 - -1342 -1522 0 - -1342 -2192 0 - -1453 -1522 0 - -1453 -2192 0 - -1522 -2192 0 - -402 -1454 0 - -402 -1523 0 - -402 -1853 0 - -402 -2053 0 - -402 -2537 0 - -1454 -1523 0 - -1454 -1853 0 - -1454 -2053 0 - -1454 -2537 0 - -1523 -1853 0 - -1523 -2053 0 - -1523 -2537 0 - -1853 -2053 0 - -1853 -2537 0 - -2053 -2537 0 - -103 -223 0 - -103 -295 0 - -103 -403 0 - -103 -1524 0 - -103 -2538 0 - -223 -295 0 - -223 -403 0 - -223 -1524 0 - -223 -2538 0 - -295 -403 0 - -295 -1524 0 - -295 -2538 0 - -403 -1524 0 - -403 -2538 0 - -1524 -2538 0 - -104 -296 0 - -104 -404 0 - -104 -776 0 - -104 -1455 0 - -104 -1525 0 - -104 -2193 0 - -296 -404 0 - -296 -776 0 - -296 -1455 0 - -296 -1525 0 - -296 -2193 0 - -404 -776 0 - -404 -1455 0 - -404 -1525 0 - -404 -2193 0 - -776 -1455 0 - -776 -1525 0 - -776 -2193 0 - -1455 -1525 0 - -1455 -2193 0 - -1525 -2193 0 - -297 -550 0 - -297 -777 0 - -297 -1854 0 - -297 -2107 0 - -297 -2194 0 - -550 -777 0 - -550 -1854 0 - -550 -2107 0 - -550 -2194 0 - -777 -1854 0 - -777 -2107 0 - -777 -2194 0 - -1854 -2107 0 - -1854 -2194 0 - -2107 -2194 0 - -405 -778 0 - -405 -1343 0 - -405 -1456 0 - -405 -1526 0 - -405 -1855 0 - -405 -2195 0 - -778 -1343 0 - -778 -1456 0 - -778 -1526 0 - -778 -1855 0 - -778 -2195 0 - -1343 -1456 0 - -1343 -1526 0 - -1343 -1855 0 - -1343 -2195 0 - -1456 -1526 0 - -1456 -1855 0 - -1456 -2195 0 - -1526 -1855 0 - -1526 -2195 0 - -1855 -2195 0 - -105 -224 0 - -105 -406 0 - -105 -551 0 - -105 -779 0 - -105 -2054 0 - -105 -2108 0 - -105 -2196 0 - -224 -406 0 - -224 -551 0 - -224 -779 0 - -224 -2054 0 - -224 -2108 0 - -224 -2196 0 - -406 -551 0 - -406 -779 0 - -406 -2054 0 - -406 -2108 0 - -406 -2196 0 - -551 -779 0 - -551 -2054 0 - -551 -2108 0 - -551 -2196 0 - -779 -2054 0 - -779 -2108 0 - -779 -2196 0 - -2054 -2108 0 - -2054 -2196 0 - -2108 -2196 0 - -552 -1457 0 - -552 -2109 0 - -552 -2539 0 - -1457 -2109 0 - -1457 -2539 0 - -2109 -2539 0 - -1344 -1527 0 - -1344 -2055 0 - -1344 -2540 0 - -1527 -2055 0 - -1527 -2540 0 - -2055 -2540 0 - -407 -553 0 - -407 -1458 0 - -407 -1528 0 - -407 -2197 0 - -553 -1458 0 - -553 -1528 0 - -553 -2197 0 - -1458 -1528 0 - -1458 -2197 0 - -1528 -2197 0 - -225 -298 0 - -225 -408 0 - -225 -554 0 - -225 -780 0 - -225 -1345 0 - -225 -1856 0 - -298 -408 0 - -298 -554 0 - -298 -780 0 - -298 -1345 0 - -298 -1856 0 - -408 -554 0 - -408 -780 0 - -408 -1345 0 - -408 -1856 0 - -554 -780 0 - -554 -1345 0 - -554 -1856 0 - -780 -1345 0 - -780 -1856 0 - -1345 -1856 0 - -106 -409 0 - -106 -781 0 - -106 -1346 0 - -106 -2056 0 - -106 -2110 0 - -106 -2198 0 - -106 -2541 0 - -409 -781 0 - -409 -1346 0 - -409 -2056 0 - -409 -2110 0 - -409 -2198 0 - -409 -2541 0 - -781 -1346 0 - -781 -2056 0 - -781 -2110 0 - -781 -2198 0 - -781 -2541 0 - -1346 -2056 0 - -1346 -2110 0 - -1346 -2198 0 - -1346 -2541 0 - -2056 -2110 0 - -2056 -2198 0 - -2056 -2541 0 - -2110 -2198 0 - -2110 -2541 0 - -2198 -2541 0 - -226 -782 0 - -226 -1529 0 - -226 -2111 0 - -226 -2199 0 - -782 -1529 0 - -782 -2111 0 - -782 -2199 0 - -1529 -2111 0 - -1529 -2199 0 - -2111 -2199 0 - -410 -613 0 - -410 -709 0 - -410 -1200 0 - -410 -1347 0 - -410 -1459 0 - -410 -1707 0 - -613 -709 0 - -613 -1200 0 - -613 -1347 0 - -613 -1459 0 - -613 -1707 0 - -709 -1200 0 - -709 -1347 0 - -709 -1459 0 - -709 -1707 0 - -1200 -1347 0 - -1200 -1459 0 - -1200 -1707 0 - -1347 -1459 0 - -1347 -1707 0 - -1459 -1707 0 - -107 -176 0 - -107 -411 0 - -107 -614 0 - -107 -710 0 - -107 -1201 0 - -107 -1708 0 - -176 -411 0 - -176 -614 0 - -176 -710 0 - -176 -1201 0 - -176 -1708 0 - -411 -614 0 - -411 -710 0 - -411 -1201 0 - -411 -1708 0 - -614 -710 0 - -614 -1201 0 - -614 -1708 0 - -710 -1201 0 - -710 -1708 0 - -1201 -1708 0 - -177 -412 0 - -177 -711 0 - -177 -1202 0 - -177 -1348 0 - -177 -1709 0 - -412 -711 0 - -412 -1202 0 - -412 -1348 0 - -412 -1709 0 - -711 -1202 0 - -711 -1348 0 - -711 -1709 0 - -1202 -1348 0 - -1202 -1709 0 - -1348 -1709 0 - -108 -712 0 - -108 -783 0 - -108 -1121 0 - -108 -1203 0 - -108 -1349 0 - -108 -1710 0 - -712 -783 0 - -712 -1121 0 - -712 -1203 0 - -712 -1349 0 - -712 -1710 0 - -783 -1121 0 - -783 -1203 0 - -783 -1349 0 - -783 -1710 0 - -1121 -1203 0 - -1121 -1349 0 - -1121 -1710 0 - -1203 -1349 0 - -1203 -1710 0 - -1349 -1710 0 - -109 -413 0 - -109 -615 0 - -109 -713 0 - -109 -1350 0 - -109 -1460 0 - -109 -1711 0 - -413 -615 0 - -413 -713 0 - -413 -1350 0 - -413 -1460 0 - -413 -1711 0 - -615 -713 0 - -615 -1350 0 - -615 -1460 0 - -615 -1711 0 - -713 -1350 0 - -713 -1460 0 - -713 -1711 0 - -1350 -1460 0 - -1350 -1711 0 - -1460 -1711 0 - -784 -1712 0 - -110 -178 0 - -110 -414 0 - -110 -616 0 - -110 -785 0 - -110 -1122 0 - -178 -414 0 - -178 -616 0 - -178 -785 0 - -178 -1122 0 - -414 -616 0 - -414 -785 0 - -414 -1122 0 - -616 -785 0 - -616 -1122 0 - -785 -1122 0 - -111 -1351 0 - -111 -1461 0 - -1351 -1461 0 - -1204 -1462 0 - -1204 -1713 0 - -1462 -1713 0 - -415 -617 0 - -415 -786 0 - -415 -1123 0 - -415 -1714 0 - -617 -786 0 - -617 -1123 0 - -617 -1714 0 - -786 -1123 0 - -786 -1714 0 - -1123 -1714 0 - -787 -1205 0 - -1 -227 0 - -1 -1124 0 - -1 -1206 0 - -1 -1530 0 - -227 -1124 0 - -227 -1206 0 - -227 -1530 0 - -1124 -1206 0 - -1124 -1530 0 - -1206 -1530 0 - -2 -228 0 - -2 -714 0 - -2 -1207 0 - -2 -1531 0 - -228 -714 0 - -228 -1207 0 - -228 -1531 0 - -714 -1207 0 - -714 -1531 0 - -1207 -1531 0 - -59 -999 0 - -59 -1208 0 - -59 -2112 0 - -999 -1208 0 - -999 -2112 0 - -1208 -2112 0 - -715 -1125 0 - -715 -1209 0 - -715 -2113 0 - -1125 -1209 0 - -1125 -2113 0 - -1209 -2113 0 - -3 -229 0 - -3 -1000 0 - -3 -1126 0 - -3 -2057 0 - -3 -2418 0 - -229 -1000 0 - -229 -1126 0 - -229 -2057 0 - -229 -2418 0 - -1000 -1126 0 - -1000 -2057 0 - -1000 -2418 0 - -1126 -2057 0 - -1126 -2418 0 - -2057 -2418 0 - -4 -60 0 - -4 -1001 0 - -4 -1532 0 - -60 -1001 0 - -60 -1532 0 - -1001 -1532 0 - -1533 -2419 0 - -5 -882 0 - -5 -1002 0 - -5 -1672 0 - -882 -1002 0 - -882 -1672 0 - -1002 -1672 0 - -6 -61 0 - -6 -716 0 - -6 -1210 0 - -6 -1534 0 - -6 -2058 0 - -6 -2420 0 - -61 -716 0 - -61 -1210 0 - -61 -1534 0 - -61 -2058 0 - -61 -2420 0 - -716 -1210 0 - -716 -1534 0 - -716 -2058 0 - -716 -2420 0 - -1210 -1534 0 - -1210 -2058 0 - -1210 -2420 0 - -1534 -2058 0 - -1534 -2420 0 - -2058 -2420 0 - -62 -883 0 - -62 -1003 0 - -62 -1211 0 - -62 -1535 0 - -62 -1673 0 - -62 -2421 0 - -883 -1003 0 - -883 -1211 0 - -883 -1535 0 - -883 -1673 0 - -883 -2421 0 - -1003 -1211 0 - -1003 -1535 0 - -1003 -1673 0 - -1003 -2421 0 - -1211 -1535 0 - -1211 -1673 0 - -1211 -2421 0 - -1535 -1673 0 - -1535 -2421 0 - -1673 -2421 0 - -63 -230 0 - -63 -1004 0 - -63 -1212 0 - -63 -1674 0 - -63 -2422 0 - -230 -1004 0 - -230 -1212 0 - -230 -1674 0 - -230 -2422 0 - -1004 -1212 0 - -1004 -1674 0 - -1004 -2422 0 - -1212 -1674 0 - -1212 -2422 0 - -1674 -2422 0 - -231 -1127 0 - -231 -1536 0 - -231 -2059 0 - -231 -2423 0 - -1127 -1536 0 - -1127 -2059 0 - -1127 -2423 0 - -1536 -2059 0 - -1536 -2423 0 - -2059 -2423 0 - -232 -884 0 - -232 -1128 0 - -232 -1537 0 - -232 -2060 0 - -232 -2424 0 - -884 -1128 0 - -884 -1537 0 - -884 -2060 0 - -884 -2424 0 - -1128 -1537 0 - -1128 -2060 0 - -1128 -2424 0 - -1537 -2060 0 - -1537 -2424 0 - -2060 -2424 0 - -299 -717 0 - -299 -1213 0 - -299 -1538 0 - -299 -1715 0 - -299 -1980 0 - -299 -2200 0 - -717 -1213 0 - -717 -1538 0 - -717 -1715 0 - -717 -1980 0 - -717 -2200 0 - -1213 -1538 0 - -1213 -1715 0 - -1213 -1980 0 - -1213 -2200 0 - -1538 -1715 0 - -1538 -1980 0 - -1538 -2200 0 - -1715 -1980 0 - -1715 -2200 0 - -1980 -2200 0 - -300 -718 0 - -300 -1214 0 - -300 -1716 0 - -718 -1214 0 - -718 -1716 0 - -1214 -1716 0 - -618 -719 0 - -618 -913 0 - -618 -1675 0 - -618 -1717 0 - -618 -2201 0 - -719 -913 0 - -719 -1675 0 - -719 -1717 0 - -719 -2201 0 - -913 -1675 0 - -913 -1717 0 - -913 -2201 0 - -1675 -1717 0 - -1675 -2201 0 - -1717 -2201 0 - -7 -1718 0 - -7 -1981 0 - -1718 -1981 0 - -8 -914 0 - -8 -1539 0 - -8 -1982 0 - -8 -2202 0 - -914 -1539 0 - -914 -1982 0 - -914 -2202 0 - -1539 -1982 0 - -1539 -2202 0 - -1982 -2202 0 - -301 -555 0 - -301 -915 0 - -301 -1155 0 - -301 -1540 0 - -301 -2203 0 - -555 -915 0 - -555 -1155 0 - -555 -1540 0 - -555 -2203 0 - -915 -1155 0 - -915 -1540 0 - -915 -2203 0 - -1155 -1540 0 - -1155 -2203 0 - -1540 -2203 0 - -302 -916 0 - -302 -1215 0 - -302 -1719 0 - -302 -1983 0 - -302 -2204 0 - -916 -1215 0 - -916 -1719 0 - -916 -1983 0 - -916 -2204 0 - -1215 -1719 0 - -1215 -1983 0 - -1215 -2204 0 - -1719 -1983 0 - -1719 -2204 0 - -1983 -2204 0 - -556 -917 0 - -556 -1216 0 - -556 -1541 0 - -556 -2205 0 - -917 -1216 0 - -917 -1541 0 - -917 -2205 0 - -1216 -1541 0 - -1216 -2205 0 - -1541 -2205 0 - -9 -557 0 - -9 -1676 0 - -9 -1984 0 - -557 -1676 0 - -557 -1984 0 - -1676 -1984 0 - -10 -619 0 - -10 -720 0 - -10 -1217 0 - -10 -1542 0 - -10 -1985 0 - -10 -2340 0 - -619 -720 0 - -619 -1217 0 - -619 -1542 0 - -619 -1985 0 - -619 -2340 0 - -720 -1217 0 - -720 -1542 0 - -720 -1985 0 - -720 -2340 0 - -1217 -1542 0 - -1217 -1985 0 - -1217 -2340 0 - -1542 -1985 0 - -1542 -2340 0 - -1985 -2340 0 - -11 -303 0 - -11 -721 0 - -11 -1986 0 - -303 -721 0 - -303 -1986 0 - -721 -1986 0 - -304 -558 0 - -304 -1218 0 - -304 -1677 0 - -304 -1720 0 - -304 -1987 0 - -304 -2341 0 - -558 -1218 0 - -558 -1677 0 - -558 -1720 0 - -558 -1987 0 - -558 -2341 0 - -1218 -1677 0 - -1218 -1720 0 - -1218 -1987 0 - -1218 -2341 0 - -1677 -1720 0 - -1677 -1987 0 - -1677 -2341 0 - -1720 -1987 0 - -1720 -2341 0 - -1987 -2341 0 - -559 -620 0 - -559 -1156 0 - -559 -1721 0 - -559 -1988 0 - -559 -2206 0 - -620 -1156 0 - -620 -1721 0 - -620 -1988 0 - -620 -2206 0 - -1156 -1721 0 - -1156 -1988 0 - -1156 -2206 0 - -1721 -1988 0 - -1721 -2206 0 - -1988 -2206 0 - -12 -1219 0 - -12 -1543 0 - -12 -2207 0 - -12 -2342 0 - -1219 -1543 0 - -1219 -2207 0 - -1219 -2342 0 - -1543 -2207 0 - -1543 -2342 0 - -2207 -2342 0 - -621 -885 0 - -621 -1005 0 - -621 -1220 0 - -621 -1352 0 - -621 -1722 0 - -621 -1807 0 - -621 -1857 0 - -885 -1005 0 - -885 -1220 0 - -885 -1352 0 - -885 -1722 0 - -885 -1807 0 - -885 -1857 0 - -1005 -1220 0 - -1005 -1352 0 - -1005 -1722 0 - -1005 -1807 0 - -1005 -1857 0 - -1220 -1352 0 - -1220 -1722 0 - -1220 -1807 0 - -1220 -1857 0 - -1352 -1722 0 - -1352 -1807 0 - -1352 -1857 0 - -1722 -1807 0 - -1722 -1857 0 - -1807 -1857 0 - -622 -1157 0 - -622 -1221 0 - -622 -1353 0 - -622 -1544 0 - -622 -1882 0 - -1157 -1221 0 - -1157 -1353 0 - -1157 -1544 0 - -1157 -1882 0 - -1221 -1353 0 - -1221 -1544 0 - -1221 -1882 0 - -1353 -1544 0 - -1353 -1882 0 - -1544 -1882 0 - -179 -1222 0 - -179 -1545 0 - -179 -1635 0 - -179 -1808 0 - -179 -1858 0 - -179 -1883 0 - -179 -2061 0 - -179 -2425 0 - -179 -2542 0 - -1222 -1545 0 - -1222 -1635 0 - -1222 -1808 0 - -1222 -1858 0 - -1222 -1883 0 - -1222 -2061 0 - -1222 -2425 0 - -1222 -2542 0 - -1545 -1635 0 - -1545 -1808 0 - -1545 -1858 0 - -1545 -1883 0 - -1545 -2061 0 - -1545 -2425 0 - -1545 -2542 0 - -1635 -1808 0 - -1635 -1858 0 - -1635 -1883 0 - -1635 -2061 0 - -1635 -2425 0 - -1635 -2542 0 - -1808 -1858 0 - -1808 -1883 0 - -1808 -2061 0 - -1808 -2425 0 - -1808 -2542 0 - -1858 -1883 0 - -1858 -2061 0 - -1858 -2425 0 - -1858 -2542 0 - -1883 -2061 0 - -1883 -2425 0 - -1883 -2542 0 - -2061 -2425 0 - -2061 -2542 0 - -2425 -2542 0 - -886 -1158 0 - -886 -1546 0 - -886 -1723 0 - -886 -1884 0 - -886 -2114 0 - -886 -2426 0 - -1158 -1546 0 - -1158 -1723 0 - -1158 -1884 0 - -1158 -2114 0 - -1158 -2426 0 - -1546 -1723 0 - -1546 -1884 0 - -1546 -2114 0 - -1546 -2426 0 - -1723 -1884 0 - -1723 -2114 0 - -1723 -2426 0 - -1884 -2114 0 - -1884 -2426 0 - -2114 -2426 0 - -623 -1006 0 - -623 -1223 0 - -623 -1547 0 - -623 -1885 0 - -623 -2427 0 - -1006 -1223 0 - -1006 -1547 0 - -1006 -1885 0 - -1006 -2427 0 - -1223 -1547 0 - -1223 -1885 0 - -1223 -2427 0 - -1547 -1885 0 - -1547 -2427 0 - -1885 -2427 0 - -180 -887 0 - -180 -1224 0 - -180 -1354 0 - -180 -1548 0 - -180 -1724 0 - -180 -2115 0 - -180 -2428 0 - -887 -1224 0 - -887 -1354 0 - -887 -1548 0 - -887 -1724 0 - -887 -2115 0 - -887 -2428 0 - -1224 -1354 0 - -1224 -1548 0 - -1224 -1724 0 - -1224 -2115 0 - -1224 -2428 0 - -1354 -1548 0 - -1354 -1724 0 - -1354 -2115 0 - -1354 -2428 0 - -1548 -1724 0 - -1548 -2115 0 - -1548 -2428 0 - -1724 -2115 0 - -1724 -2428 0 - -2115 -2428 0 - -181 -1007 0 - -181 -1225 0 - -181 -1636 0 - -181 -1886 0 - -181 -2062 0 - -181 -2116 0 - -181 -2543 0 - -1007 -1225 0 - -1007 -1636 0 - -1007 -1886 0 - -1007 -2062 0 - -1007 -2116 0 - -1007 -2543 0 - -1225 -1636 0 - -1225 -1886 0 - -1225 -2062 0 - -1225 -2116 0 - -1225 -2543 0 - -1636 -1886 0 - -1636 -2062 0 - -1636 -2116 0 - -1636 -2543 0 - -1886 -2062 0 - -1886 -2116 0 - -1886 -2543 0 - -2062 -2116 0 - -2062 -2543 0 - -2116 -2543 0 - -888 -1008 0 - -888 -1159 0 - -888 -1637 0 - -888 -1725 0 - -888 -1809 0 - -888 -1859 0 - -888 -2117 0 - -1008 -1159 0 - -1008 -1637 0 - -1008 -1725 0 - -1008 -1809 0 - -1008 -1859 0 - -1008 -2117 0 - -1159 -1637 0 - -1159 -1725 0 - -1159 -1809 0 - -1159 -1859 0 - -1159 -2117 0 - -1637 -1725 0 - -1637 -1809 0 - -1637 -1859 0 - -1637 -2117 0 - -1725 -1809 0 - -1725 -1859 0 - -1725 -2117 0 - -1809 -1859 0 - -1809 -2117 0 - -1859 -2117 0 - -64 -182 0 - -64 -1009 0 - -64 -1355 0 - -64 -1549 0 - -64 -1860 0 - -64 -1887 0 - -182 -1009 0 - -182 -1355 0 - -182 -1549 0 - -182 -1860 0 - -182 -1887 0 - -1009 -1355 0 - -1009 -1549 0 - -1009 -1860 0 - -1009 -1887 0 - -1355 -1549 0 - -1355 -1860 0 - -1355 -1887 0 - -1549 -1860 0 - -1549 -1887 0 - -1860 -1887 0 - -183 -624 0 - -183 -1160 0 - -183 -2063 0 - -183 -2118 0 - -183 -2429 0 - -624 -1160 0 - -624 -2063 0 - -624 -2118 0 - -624 -2429 0 - -1160 -2063 0 - -1160 -2118 0 - -1160 -2429 0 - -2063 -2118 0 - -2063 -2429 0 - -2118 -2429 0 - -625 -1010 0 - -625 -1226 0 - -625 -1356 0 - -625 -1550 0 - -625 -1638 0 - -1010 -1226 0 - -1010 -1356 0 - -1010 -1550 0 - -1010 -1638 0 - -1226 -1356 0 - -1226 -1550 0 - -1226 -1638 0 - -1356 -1550 0 - -1356 -1638 0 - -1550 -1638 0 - -1227 -1726 0 - -1227 -1810 0 - -1227 -2119 0 - -1227 -2544 0 - -1726 -1810 0 - -1726 -2119 0 - -1726 -2544 0 - -1810 -2119 0 - -1810 -2544 0 - -2119 -2544 0 - -65 -184 0 - -65 -1011 0 - -65 -1228 0 - -65 -1357 0 - -65 -1727 0 - -65 -1811 0 - -65 -1861 0 - -65 -1888 0 - -65 -2430 0 - -184 -1011 0 - -184 -1228 0 - -184 -1357 0 - -184 -1727 0 - -184 -1811 0 - -184 -1861 0 - -184 -1888 0 - -184 -2430 0 - -1011 -1228 0 - -1011 -1357 0 - -1011 -1727 0 - -1011 -1811 0 - -1011 -1861 0 - -1011 -1888 0 - -1011 -2430 0 - -1228 -1357 0 - -1228 -1727 0 - -1228 -1811 0 - -1228 -1861 0 - -1228 -1888 0 - -1228 -2430 0 - -1357 -1727 0 - -1357 -1811 0 - -1357 -1861 0 - -1357 -1888 0 - -1357 -2430 0 - -1727 -1811 0 - -1727 -1861 0 - -1727 -1888 0 - -1727 -2430 0 - -1811 -1861 0 - -1811 -1888 0 - -1811 -2430 0 - -1861 -1888 0 - -1861 -2430 0 - -1888 -2430 0 - -626 -1551 0 - -626 -1728 0 - -626 -2064 0 - -626 -2431 0 - -626 -2545 0 - -1551 -1728 0 - -1551 -2064 0 - -1551 -2431 0 - -1551 -2545 0 - -1728 -2064 0 - -1728 -2431 0 - -1728 -2545 0 - -2064 -2431 0 - -2064 -2545 0 - -2431 -2545 0 - -66 -627 0 - -66 -1161 0 - -66 -1639 0 - -66 -1729 0 - -66 -2546 0 - -627 -1161 0 - -627 -1639 0 - -627 -1729 0 - -627 -2546 0 - -1161 -1639 0 - -1161 -1729 0 - -1161 -2546 0 - -1639 -1729 0 - -1639 -2546 0 - -1729 -2546 0 - -628 -889 0 - -628 -1162 0 - -628 -1552 0 - -628 -1862 0 - -628 -1889 0 - -628 -2065 0 - -628 -2432 0 - -889 -1162 0 - -889 -1552 0 - -889 -1862 0 - -889 -1889 0 - -889 -2065 0 - -889 -2432 0 - -1162 -1552 0 - -1162 -1862 0 - -1162 -1889 0 - -1162 -2065 0 - -1162 -2432 0 - -1552 -1862 0 - -1552 -1889 0 - -1552 -2065 0 - -1552 -2432 0 - -1862 -1889 0 - -1862 -2065 0 - -1862 -2432 0 - -1889 -2065 0 - -1889 -2432 0 - -2065 -2432 0 - -67 -629 0 - -67 -1163 0 - -67 -1358 0 - -67 -1640 0 - -67 -1812 0 - -67 -1890 0 - -67 -2066 0 - -67 -2120 0 - -67 -2433 0 - -67 -2547 0 - -629 -1163 0 - -629 -1358 0 - -629 -1640 0 - -629 -1812 0 - -629 -1890 0 - -629 -2066 0 - -629 -2120 0 - -629 -2433 0 - -629 -2547 0 - -1163 -1358 0 - -1163 -1640 0 - -1163 -1812 0 - -1163 -1890 0 - -1163 -2066 0 - -1163 -2120 0 - -1163 -2433 0 - -1163 -2547 0 - -1358 -1640 0 - -1358 -1812 0 - -1358 -1890 0 - -1358 -2066 0 - -1358 -2120 0 - -1358 -2433 0 - -1358 -2547 0 - -1640 -1812 0 - -1640 -1890 0 - -1640 -2066 0 - -1640 -2120 0 - -1640 -2433 0 - -1640 -2547 0 - -1812 -1890 0 - -1812 -2066 0 - -1812 -2120 0 - -1812 -2433 0 - -1812 -2547 0 - -1890 -2066 0 - -1890 -2120 0 - -1890 -2433 0 - -1890 -2547 0 - -2066 -2120 0 - -2066 -2433 0 - -2066 -2547 0 - -2120 -2433 0 - -2120 -2547 0 - -2433 -2547 0 - -68 -1229 0 - -68 -1553 0 - -68 -1641 0 - -68 -2121 0 - -1229 -1553 0 - -1229 -1641 0 - -1229 -2121 0 - -1553 -1641 0 - -1553 -2121 0 - -1641 -2121 0 - -416 -560 0 - -416 -630 0 - -416 -1012 0 - -416 -1359 0 - -416 -1463 0 - -416 -1989 0 - -416 -2208 0 - -560 -630 0 - -560 -1012 0 - -560 -1359 0 - -560 -1463 0 - -560 -1989 0 - -560 -2208 0 - -630 -1012 0 - -630 -1359 0 - -630 -1463 0 - -630 -1989 0 - -630 -2208 0 - -1012 -1359 0 - -1012 -1463 0 - -1012 -1989 0 - -1012 -2208 0 - -1359 -1463 0 - -1359 -1989 0 - -1359 -2208 0 - -1463 -1989 0 - -1463 -2208 0 - -1989 -2208 0 - -417 -631 0 - -417 -788 0 - -417 -1360 0 - -417 -1464 0 - -417 -1891 0 - -417 -2209 0 - -417 -2343 0 - -631 -788 0 - -631 -1360 0 - -631 -1464 0 - -631 -1891 0 - -631 -2209 0 - -631 -2343 0 - -788 -1360 0 - -788 -1464 0 - -788 -1891 0 - -788 -2209 0 - -788 -2343 0 - -1360 -1464 0 - -1360 -1891 0 - -1360 -2209 0 - -1360 -2343 0 - -1464 -1891 0 - -1464 -2209 0 - -1464 -2343 0 - -1891 -2209 0 - -1891 -2343 0 - -2209 -2343 0 - -305 -418 0 - -305 -632 0 - -418 -632 0 - -306 -419 0 - -306 -633 0 - -306 -789 0 - -306 -1013 0 - -306 -1465 0 - -306 -1892 0 - -306 -2210 0 - -306 -2344 0 - -419 -633 0 - -419 -789 0 - -419 -1013 0 - -419 -1465 0 - -419 -1892 0 - -419 -2210 0 - -419 -2344 0 - -633 -789 0 - -633 -1013 0 - -633 -1465 0 - -633 -1892 0 - -633 -2210 0 - -633 -2344 0 - -789 -1013 0 - -789 -1465 0 - -789 -1892 0 - -789 -2210 0 - -789 -2344 0 - -1013 -1465 0 - -1013 -1892 0 - -1013 -2210 0 - -1013 -2344 0 - -1465 -1892 0 - -1465 -2210 0 - -1465 -2344 0 - -1892 -2210 0 - -1892 -2344 0 - -2210 -2344 0 - -420 -634 0 - -420 -1014 0 - -420 -1361 0 - -420 -1466 0 - -420 -1893 0 - -420 -2211 0 - -634 -1014 0 - -634 -1361 0 - -634 -1466 0 - -634 -1893 0 - -634 -2211 0 - -1014 -1361 0 - -1014 -1466 0 - -1014 -1893 0 - -1014 -2211 0 - -1361 -1466 0 - -1361 -1893 0 - -1361 -2211 0 - -1466 -1893 0 - -1466 -2211 0 - -1893 -2211 0 - -307 -421 0 - -307 -1015 0 - -307 -1894 0 - -307 -2212 0 - -307 -2345 0 - -421 -1015 0 - -421 -1894 0 - -421 -2212 0 - -421 -2345 0 - -1015 -1894 0 - -1015 -2212 0 - -1015 -2345 0 - -1894 -2212 0 - -1894 -2345 0 - -2212 -2345 0 - -308 -561 0 - -308 -790 0 - -308 -1016 0 - -308 -2213 0 - -561 -790 0 - -561 -1016 0 - -561 -2213 0 - -790 -1016 0 - -790 -2213 0 - -1016 -2213 0 - -309 -1017 0 - -309 -1467 0 - -309 -1895 0 - -309 -1990 0 - -309 -2214 0 - -1017 -1467 0 - -1017 -1895 0 - -1017 -1990 0 - -1017 -2214 0 - -1467 -1895 0 - -1467 -1990 0 - -1467 -2214 0 - -1895 -1990 0 - -1895 -2214 0 - -1990 -2214 0 - -422 -562 0 - -422 -791 0 - -422 -1018 0 - -422 -1362 0 - -422 -1991 0 - -562 -791 0 - -562 -1018 0 - -562 -1362 0 - -562 -1991 0 - -791 -1018 0 - -791 -1362 0 - -791 -1991 0 - -1018 -1362 0 - -1018 -1991 0 - -1362 -1991 0 - -635 -1363 0 - -635 -1992 0 - -635 -2346 0 - -1363 -1992 0 - -1363 -2346 0 - -1992 -2346 0 - -423 -563 0 - -423 -636 0 - -423 -1019 0 - -423 -1468 0 - -423 -2215 0 - -423 -2347 0 - -563 -636 0 - -563 -1019 0 - -563 -1468 0 - -563 -2215 0 - -563 -2347 0 - -636 -1019 0 - -636 -1468 0 - -636 -2215 0 - -636 -2347 0 - -1019 -1468 0 - -1019 -2215 0 - -1019 -2347 0 - -1468 -2215 0 - -1468 -2347 0 - -2215 -2347 0 - -310 -1020 0 - -310 -1364 0 - -310 -2216 0 - -1020 -1364 0 - -1020 -2216 0 - -1364 -2216 0 - -424 -637 0 - -424 -1365 0 - -424 -1678 0 - -424 -1993 0 - -424 -2217 0 - -637 -1365 0 - -637 -1678 0 - -637 -1993 0 - -637 -2217 0 - -1365 -1678 0 - -1365 -1993 0 - -1365 -2217 0 - -1678 -1993 0 - -1678 -2217 0 - -1993 -2217 0 - -425 -1642 0 - -425 -1896 0 - -425 -2067 0 - -425 -2434 0 - -425 -2548 0 - -1642 -1896 0 - -1642 -2067 0 - -1642 -2434 0 - -1642 -2548 0 - -1896 -2067 0 - -1896 -2434 0 - -1896 -2548 0 - -2067 -2434 0 - -2067 -2548 0 - -2434 -2548 0 - -426 -638 0 - -426 -2549 0 - -638 -2549 0 - -1366 -2068 0 - -1366 -2550 0 - -2068 -2550 0 - -427 -639 0 - -427 -792 0 - -427 -1679 0 - -427 -1897 0 - -427 -2218 0 - -427 -2435 0 - -639 -792 0 - -639 -1679 0 - -639 -1897 0 - -639 -2218 0 - -639 -2435 0 - -792 -1679 0 - -792 -1897 0 - -792 -2218 0 - -792 -2435 0 - -1679 -1897 0 - -1679 -2218 0 - -1679 -2435 0 - -1897 -2218 0 - -1897 -2435 0 - -2218 -2435 0 - -793 -1367 0 - -793 -1643 0 - -793 -1994 0 - -793 -2069 0 - -793 -2436 0 - -793 -2551 0 - -1367 -1643 0 - -1367 -1994 0 - -1367 -2069 0 - -1367 -2436 0 - -1367 -2551 0 - -1643 -1994 0 - -1643 -2069 0 - -1643 -2436 0 - -1643 -2551 0 - -1994 -2069 0 - -1994 -2436 0 - -1994 -2551 0 - -2069 -2436 0 - -2069 -2551 0 - -2436 -2551 0 - -428 -794 0 - -428 -1368 0 - -428 -1898 0 - -428 -1995 0 - -428 -2219 0 - -794 -1368 0 - -794 -1898 0 - -794 -1995 0 - -794 -2219 0 - -1368 -1898 0 - -1368 -1995 0 - -1368 -2219 0 - -1898 -1995 0 - -1898 -2219 0 - -1995 -2219 0 - -1369 -2220 0 - -1369 -2437 0 - -2220 -2437 0 - -429 -795 0 - -429 -1370 0 - -429 -1680 0 - -429 -1996 0 - -795 -1370 0 - -795 -1680 0 - -795 -1996 0 - -1370 -1680 0 - -1370 -1996 0 - -1680 -1996 0 - -430 -640 0 - -430 -796 0 - -430 -2070 0 - -430 -2438 0 - -430 -2552 0 - -640 -796 0 - -640 -2070 0 - -640 -2438 0 - -640 -2552 0 - -796 -2070 0 - -796 -2438 0 - -796 -2552 0 - -2070 -2438 0 - -2070 -2552 0 - -2438 -2552 0 - -431 -641 0 - -431 -797 0 - -431 -1899 0 - -431 -1997 0 - -431 -2071 0 - -431 -2439 0 - -641 -797 0 - -641 -1899 0 - -641 -1997 0 - -641 -2071 0 - -641 -2439 0 - -797 -1899 0 - -797 -1997 0 - -797 -2071 0 - -797 -2439 0 - -1899 -1997 0 - -1899 -2071 0 - -1899 -2439 0 - -1997 -2071 0 - -1997 -2439 0 - -2071 -2439 0 - -798 -1644 0 - -798 -2221 0 - -1644 -2221 0 - -233 -432 0 - -233 -564 0 - -233 -722 0 - -233 -890 0 - -233 -918 0 - -233 -1021 0 - -233 -1230 0 - -233 -1813 0 - -233 -1863 0 - -233 -1998 0 - -432 -564 0 - -432 -722 0 - -432 -890 0 - -432 -918 0 - -432 -1021 0 - -432 -1230 0 - -432 -1813 0 - -432 -1863 0 - -432 -1998 0 - -564 -722 0 - -564 -890 0 - -564 -918 0 - -564 -1021 0 - -564 -1230 0 - -564 -1813 0 - -564 -1863 0 - -564 -1998 0 - -722 -890 0 - -722 -918 0 - -722 -1021 0 - -722 -1230 0 - -722 -1813 0 - -722 -1863 0 - -722 -1998 0 - -890 -918 0 - -890 -1021 0 - -890 -1230 0 - -890 -1813 0 - -890 -1863 0 - -890 -1998 0 - -918 -1021 0 - -918 -1230 0 - -918 -1813 0 - -918 -1863 0 - -918 -1998 0 - -1021 -1230 0 - -1021 -1813 0 - -1021 -1863 0 - -1021 -1998 0 - -1230 -1813 0 - -1230 -1863 0 - -1230 -1998 0 - -1813 -1863 0 - -1813 -1998 0 - -1863 -1998 0 - -13 -112 0 - -13 -234 0 - -13 -433 0 - -13 -723 0 - -13 -1231 0 - -13 -2553 0 - -112 -234 0 - -112 -433 0 - -112 -723 0 - -112 -1231 0 - -112 -2553 0 - -234 -433 0 - -234 -723 0 - -234 -1231 0 - -234 -2553 0 - -433 -723 0 - -433 -1231 0 - -433 -2553 0 - -723 -1231 0 - -723 -2553 0 - -1231 -2553 0 - -434 -565 0 - -434 -724 0 - -434 -891 0 - -434 -1999 0 - -434 -2440 0 - -565 -724 0 - -565 -891 0 - -565 -1999 0 - -565 -2440 0 - -724 -891 0 - -724 -1999 0 - -724 -2440 0 - -891 -1999 0 - -891 -2440 0 - -1999 -2440 0 - -113 -435 0 - -113 -566 0 - -113 -919 0 - -113 -1022 0 - -113 -1232 0 - -435 -566 0 - -435 -919 0 - -435 -1022 0 - -435 -1232 0 - -566 -919 0 - -566 -1022 0 - -566 -1232 0 - -919 -1022 0 - -919 -1232 0 - -1022 -1232 0 - -436 -725 0 - -436 -892 0 - -436 -1233 0 - -436 -2000 0 - -436 -2441 0 - -725 -892 0 - -725 -1233 0 - -725 -2000 0 - -725 -2441 0 - -892 -1233 0 - -892 -2000 0 - -892 -2441 0 - -1233 -2000 0 - -1233 -2441 0 - -2000 -2441 0 - -114 -726 0 - -114 -1234 0 - -114 -1814 0 - -726 -1234 0 - -726 -1814 0 - -1234 -1814 0 - -14 -235 0 - -14 -1023 0 - -14 -1815 0 - -14 -2001 0 - -14 -2442 0 - -14 -2554 0 - -235 -1023 0 - -235 -1815 0 - -235 -2001 0 - -235 -2442 0 - -235 -2554 0 - -1023 -1815 0 - -1023 -2001 0 - -1023 -2442 0 - -1023 -2554 0 - -1815 -2001 0 - -1815 -2442 0 - -1815 -2554 0 - -2001 -2442 0 - -2001 -2554 0 - -2442 -2554 0 - -236 -437 0 - -236 -1024 0 - -236 -1235 0 - -236 -2555 0 - -437 -1024 0 - -437 -1235 0 - -437 -2555 0 - -1024 -1235 0 - -1024 -2555 0 - -1235 -2555 0 - -727 -893 0 - -727 -920 0 - -727 -1025 0 - -893 -920 0 - -893 -1025 0 - -920 -1025 0 - -115 -567 0 - -115 -921 0 - -115 -2443 0 - -115 -2556 0 - -567 -921 0 - -567 -2443 0 - -567 -2556 0 - -921 -2443 0 - -921 -2556 0 - -2443 -2556 0 - -15 -116 0 - -15 -237 0 - -15 -728 0 - -15 -922 0 - -15 -1026 0 - -15 -1236 0 - -116 -237 0 - -116 -728 0 - -116 -922 0 - -116 -1026 0 - -116 -1236 0 - -237 -728 0 - -237 -922 0 - -237 -1026 0 - -237 -1236 0 - -728 -922 0 - -728 -1026 0 - -728 -1236 0 - -922 -1026 0 - -922 -1236 0 - -1026 -1236 0 - -16 -117 0 - -16 -438 0 - -16 -729 0 - -16 -2002 0 - -117 -438 0 - -117 -729 0 - -117 -2002 0 - -438 -729 0 - -438 -2002 0 - -729 -2002 0 - -439 -568 0 - -439 -894 0 - -439 -923 0 - -439 -1027 0 - -439 -1237 0 - -439 -1816 0 - -439 -2444 0 - -568 -894 0 - -568 -923 0 - -568 -1027 0 - -568 -1237 0 - -568 -1816 0 - -568 -2444 0 - -894 -923 0 - -894 -1027 0 - -894 -1237 0 - -894 -1816 0 - -894 -2444 0 - -923 -1027 0 - -923 -1237 0 - -923 -1816 0 - -923 -2444 0 - -1027 -1237 0 - -1027 -1816 0 - -1027 -2444 0 - -1237 -1816 0 - -1237 -2444 0 - -1816 -2444 0 - -238 -440 0 - -238 -569 0 - -238 -1028 0 - -238 -1238 0 - -238 -1817 0 - -238 -1864 0 - -238 -2003 0 - -238 -2445 0 - -440 -569 0 - -440 -1028 0 - -440 -1238 0 - -440 -1817 0 - -440 -1864 0 - -440 -2003 0 - -440 -2445 0 - -569 -1028 0 - -569 -1238 0 - -569 -1817 0 - -569 -1864 0 - -569 -2003 0 - -569 -2445 0 - -1028 -1238 0 - -1028 -1817 0 - -1028 -1864 0 - -1028 -2003 0 - -1028 -2445 0 - -1238 -1817 0 - -1238 -1864 0 - -1238 -2003 0 - -1238 -2445 0 - -1817 -1864 0 - -1817 -2003 0 - -1817 -2445 0 - -1864 -2003 0 - -1864 -2445 0 - -2003 -2445 0 - -118 -239 0 - -118 -1029 0 - -118 -2557 0 - -239 -1029 0 - -239 -2557 0 - -1029 -2557 0 - -119 -311 0 - -119 -441 0 - -119 -730 0 - -119 -1239 0 - -119 -2446 0 - -311 -441 0 - -311 -730 0 - -311 -1239 0 - -311 -2446 0 - -441 -730 0 - -441 -1239 0 - -441 -2446 0 - -730 -1239 0 - -730 -2446 0 - -1239 -2446 0 - -240 -442 0 - -240 -799 0 - -240 -1240 0 - -240 -1371 0 - -240 -2222 0 - -442 -799 0 - -442 -1240 0 - -442 -1371 0 - -442 -2222 0 - -799 -1240 0 - -799 -1371 0 - -799 -2222 0 - -1240 -1371 0 - -1240 -2222 0 - -1371 -2222 0 - -312 -443 0 - -312 -731 0 - -312 -895 0 - -312 -1730 0 - -312 -2004 0 - -312 -2223 0 - -312 -2447 0 - -443 -731 0 - -443 -895 0 - -443 -1730 0 - -443 -2004 0 - -443 -2223 0 - -443 -2447 0 - -731 -895 0 - -731 -1730 0 - -731 -2004 0 - -731 -2223 0 - -731 -2447 0 - -895 -1730 0 - -895 -2004 0 - -895 -2223 0 - -895 -2447 0 - -1730 -2004 0 - -1730 -2223 0 - -1730 -2447 0 - -2004 -2223 0 - -2004 -2447 0 - -2223 -2447 0 - -241 -732 0 - -241 -924 0 - -241 -1030 0 - -241 -1372 0 - -241 -1731 0 - -241 -1818 0 - -732 -924 0 - -732 -1030 0 - -732 -1372 0 - -732 -1731 0 - -732 -1818 0 - -924 -1030 0 - -924 -1372 0 - -924 -1731 0 - -924 -1818 0 - -1030 -1372 0 - -1030 -1731 0 - -1030 -1818 0 - -1372 -1731 0 - -1372 -1818 0 - -1731 -1818 0 - -120 -444 0 - -120 -733 0 - -120 -925 0 - -120 -1031 0 - -120 -1373 0 - -120 -1681 0 - -120 -1732 0 - -120 -2224 0 - -120 -2448 0 - -444 -733 0 - -444 -925 0 - -444 -1031 0 - -444 -1373 0 - -444 -1681 0 - -444 -1732 0 - -444 -2224 0 - -444 -2448 0 - -733 -925 0 - -733 -1031 0 - -733 -1373 0 - -733 -1681 0 - -733 -1732 0 - -733 -2224 0 - -733 -2448 0 - -925 -1031 0 - -925 -1373 0 - -925 -1681 0 - -925 -1732 0 - -925 -2224 0 - -925 -2448 0 - -1031 -1373 0 - -1031 -1681 0 - -1031 -1732 0 - -1031 -2224 0 - -1031 -2448 0 - -1373 -1681 0 - -1373 -1732 0 - -1373 -2224 0 - -1373 -2448 0 - -1681 -1732 0 - -1681 -2224 0 - -1681 -2448 0 - -1732 -2224 0 - -1732 -2448 0 - -2224 -2448 0 - -242 -800 0 - -242 -1032 0 - -242 -1374 0 - -242 -1733 0 - -242 -1819 0 - -242 -2005 0 - -242 -2449 0 - -800 -1032 0 - -800 -1374 0 - -800 -1733 0 - -800 -1819 0 - -800 -2005 0 - -800 -2449 0 - -1032 -1374 0 - -1032 -1733 0 - -1032 -1819 0 - -1032 -2005 0 - -1032 -2449 0 - -1374 -1733 0 - -1374 -1819 0 - -1374 -2005 0 - -1374 -2449 0 - -1733 -1819 0 - -1733 -2005 0 - -1733 -2449 0 - -1819 -2005 0 - -1819 -2449 0 - -2005 -2449 0 - -243 -313 0 - -243 -445 0 - -243 -1033 0 - -243 -1241 0 - -243 -2225 0 - -313 -445 0 - -313 -1033 0 - -313 -1241 0 - -313 -2225 0 - -445 -1033 0 - -445 -1241 0 - -445 -2225 0 - -1033 -1241 0 - -1033 -2225 0 - -1241 -2225 0 - -314 -801 0 - -314 -896 0 - -314 -926 0 - -314 -1034 0 - -314 -1734 0 - -314 -1820 0 - -314 -2226 0 - -801 -896 0 - -801 -926 0 - -801 -1034 0 - -801 -1734 0 - -801 -1820 0 - -801 -2226 0 - -896 -926 0 - -896 -1034 0 - -896 -1734 0 - -896 -1820 0 - -896 -2226 0 - -926 -1034 0 - -926 -1734 0 - -926 -1820 0 - -926 -2226 0 - -1034 -1734 0 - -1034 -1820 0 - -1034 -2226 0 - -1734 -1820 0 - -1734 -2226 0 - -1820 -2226 0 - -121 -315 0 - -121 -927 0 - -121 -1035 0 - -121 -1242 0 - -121 -1735 0 - -121 -2006 0 - -121 -2227 0 - -315 -927 0 - -315 -1035 0 - -315 -1242 0 - -315 -1735 0 - -315 -2006 0 - -315 -2227 0 - -927 -1035 0 - -927 -1242 0 - -927 -1735 0 - -927 -2006 0 - -927 -2227 0 - -1035 -1242 0 - -1035 -1735 0 - -1035 -2006 0 - -1035 -2227 0 - -1242 -1735 0 - -1242 -2006 0 - -1242 -2227 0 - -1735 -2006 0 - -1735 -2227 0 - -2006 -2227 0 - -122 -928 0 - -122 -1036 0 - -122 -1243 0 - -122 -1375 0 - -122 -2228 0 - -122 -2450 0 - -928 -1036 0 - -928 -1243 0 - -928 -1375 0 - -928 -2228 0 - -928 -2450 0 - -1036 -1243 0 - -1036 -1375 0 - -1036 -2228 0 - -1036 -2450 0 - -1243 -1375 0 - -1243 -2228 0 - -1243 -2450 0 - -1375 -2228 0 - -1375 -2450 0 - -2228 -2450 0 - -123 -316 0 - -123 -446 0 - -123 -734 0 - -123 -2007 0 - -316 -446 0 - -316 -734 0 - -316 -2007 0 - -446 -734 0 - -446 -2007 0 - -734 -2007 0 - -244 -317 0 - -244 -447 0 - -244 -802 0 - -244 -1037 0 - -244 -1244 0 - -244 -1376 0 - -244 -1682 0 - -244 -1736 0 - -244 -1821 0 - -244 -2008 0 - -244 -2451 0 - -317 -447 0 - -317 -802 0 - -317 -1037 0 - -317 -1244 0 - -317 -1376 0 - -317 -1682 0 - -317 -1736 0 - -317 -1821 0 - -317 -2008 0 - -317 -2451 0 - -447 -802 0 - -447 -1037 0 - -447 -1244 0 - -447 -1376 0 - -447 -1682 0 - -447 -1736 0 - -447 -1821 0 - -447 -2008 0 - -447 -2451 0 - -802 -1037 0 - -802 -1244 0 - -802 -1376 0 - -802 -1682 0 - -802 -1736 0 - -802 -1821 0 - -802 -2008 0 - -802 -2451 0 - -1037 -1244 0 - -1037 -1376 0 - -1037 -1682 0 - -1037 -1736 0 - -1037 -1821 0 - -1037 -2008 0 - -1037 -2451 0 - -1244 -1376 0 - -1244 -1682 0 - -1244 -1736 0 - -1244 -1821 0 - -1244 -2008 0 - -1244 -2451 0 - -1376 -1682 0 - -1376 -1736 0 - -1376 -1821 0 - -1376 -2008 0 - -1376 -2451 0 - -1682 -1736 0 - -1682 -1821 0 - -1682 -2008 0 - -1682 -2451 0 - -1736 -1821 0 - -1736 -2008 0 - -1736 -2451 0 - -1821 -2008 0 - -1821 -2451 0 - -2008 -2451 0 - -245 -448 0 - -245 -803 0 - -245 -929 0 - -245 -1737 0 - -245 -2452 0 - -448 -803 0 - -448 -929 0 - -448 -1737 0 - -448 -2452 0 - -803 -929 0 - -803 -1737 0 - -803 -2452 0 - -929 -1737 0 - -929 -2452 0 - -1737 -2452 0 - -124 -246 0 - -124 -318 0 - -124 -1038 0 - -124 -1377 0 - -124 -2229 0 - -246 -318 0 - -246 -1038 0 - -246 -1377 0 - -246 -2229 0 - -318 -1038 0 - -318 -1377 0 - -318 -2229 0 - -1038 -1377 0 - -1038 -2229 0 - -1377 -2229 0 - -247 -319 0 - -247 -449 0 - -247 -804 0 - -247 -897 0 - -247 -930 0 - -247 -2009 0 - -247 -2453 0 - -319 -449 0 - -319 -804 0 - -319 -897 0 - -319 -930 0 - -319 -2009 0 - -319 -2453 0 - -449 -804 0 - -449 -897 0 - -449 -930 0 - -449 -2009 0 - -449 -2453 0 - -804 -897 0 - -804 -930 0 - -804 -2009 0 - -804 -2453 0 - -897 -930 0 - -897 -2009 0 - -897 -2453 0 - -930 -2009 0 - -930 -2453 0 - -2009 -2453 0 - -125 -450 0 - -125 -805 0 - -125 -1378 0 - -125 -1822 0 - -125 -2230 0 - -125 -2454 0 - -450 -805 0 - -450 -1378 0 - -450 -1822 0 - -450 -2230 0 - -450 -2454 0 - -805 -1378 0 - -805 -1822 0 - -805 -2230 0 - -805 -2454 0 - -1378 -1822 0 - -1378 -2230 0 - -1378 -2454 0 - -1822 -2230 0 - -1822 -2454 0 - -2230 -2454 0 - -248 -806 0 - -248 -1245 0 - -248 -2231 0 - -806 -1245 0 - -806 -2231 0 - -1245 -2231 0 - -320 -451 0 - -320 -735 0 - -320 -1554 0 - -320 -2455 0 - -451 -735 0 - -451 -1554 0 - -451 -2455 0 - -735 -1554 0 - -735 -2455 0 - -1554 -2455 0 - -249 -452 0 - -249 -642 0 - -249 -736 0 - -249 -1738 0 - -249 -1865 0 - -452 -642 0 - -452 -736 0 - -452 -1738 0 - -452 -1865 0 - -642 -736 0 - -642 -1738 0 - -642 -1865 0 - -736 -1738 0 - -736 -1865 0 - -1738 -1865 0 - -453 -643 0 - -453 -807 0 - -453 -1739 0 - -453 -1900 0 - -453 -2348 0 - -643 -807 0 - -643 -1739 0 - -643 -1900 0 - -643 -2348 0 - -807 -1739 0 - -807 -1900 0 - -807 -2348 0 - -1739 -1900 0 - -1739 -2348 0 - -1900 -2348 0 - -321 -737 0 - -321 -808 0 - -321 -1129 0 - -321 -1740 0 - -321 -1901 0 - -737 -808 0 - -737 -1129 0 - -737 -1740 0 - -737 -1901 0 - -808 -1129 0 - -808 -1740 0 - -808 -1901 0 - -1129 -1740 0 - -1129 -1901 0 - -1740 -1901 0 - -738 -809 0 - -738 -1164 0 - -809 -1164 0 - -322 -810 0 - -322 -1165 0 - -322 -1741 0 - -322 -1866 0 - -810 -1165 0 - -810 -1741 0 - -810 -1866 0 - -1165 -1741 0 - -1165 -1866 0 - -1741 -1866 0 - -250 -323 0 - -250 -644 0 - -250 -739 0 - -250 -811 0 - -250 -1555 0 - -323 -644 0 - -323 -739 0 - -323 -811 0 - -323 -1555 0 - -644 -739 0 - -644 -811 0 - -644 -1555 0 - -739 -811 0 - -739 -1555 0 - -811 -1555 0 - -324 -1742 0 - -324 -1902 0 - -324 -2558 0 - -1742 -1902 0 - -1742 -2558 0 - -1902 -2558 0 - -454 -812 0 - -645 -740 0 - -645 -1556 0 - -645 -2349 0 - -645 -2456 0 - -645 -2559 0 - -740 -1556 0 - -740 -2349 0 - -740 -2456 0 - -740 -2559 0 - -1556 -2349 0 - -1556 -2456 0 - -1556 -2559 0 - -2349 -2456 0 - -2349 -2559 0 - -2456 -2559 0 - -325 -455 0 - -325 -741 0 - -325 -1130 0 - -455 -741 0 - -455 -1130 0 - -741 -1130 0 - -251 -326 0 - -251 -456 0 - -251 -813 0 - -251 -1743 0 - -251 -1867 0 - -251 -1903 0 - -251 -2350 0 - -251 -2457 0 - -326 -456 0 - -326 -813 0 - -326 -1743 0 - -326 -1867 0 - -326 -1903 0 - -326 -2350 0 - -326 -2457 0 - -456 -813 0 - -456 -1743 0 - -456 -1867 0 - -456 -1903 0 - -456 -2350 0 - -456 -2457 0 - -813 -1743 0 - -813 -1867 0 - -813 -1903 0 - -813 -2350 0 - -813 -2457 0 - -1743 -1867 0 - -1743 -1903 0 - -1743 -2350 0 - -1743 -2457 0 - -1867 -1903 0 - -1867 -2350 0 - -1867 -2457 0 - -1903 -2350 0 - -1903 -2457 0 - -2350 -2457 0 - -646 -1166 0 - -646 -1744 0 - -646 -2560 0 - -1166 -1744 0 - -1166 -2560 0 - -1744 -2560 0 - -252 -327 0 - -252 -457 0 - -252 -647 0 - -252 -814 0 - -252 -1131 0 - -252 -1167 0 - -252 -1557 0 - -252 -1868 0 - -252 -1904 0 - -252 -2458 0 - -327 -457 0 - -327 -647 0 - -327 -814 0 - -327 -1131 0 - -327 -1167 0 - -327 -1557 0 - -327 -1868 0 - -327 -1904 0 - -327 -2458 0 - -457 -647 0 - -457 -814 0 - -457 -1131 0 - -457 -1167 0 - -457 -1557 0 - -457 -1868 0 - -457 -1904 0 - -457 -2458 0 - -647 -814 0 - -647 -1131 0 - -647 -1167 0 - -647 -1557 0 - -647 -1868 0 - -647 -1904 0 - -647 -2458 0 - -814 -1131 0 - -814 -1167 0 - -814 -1557 0 - -814 -1868 0 - -814 -1904 0 - -814 -2458 0 - -1131 -1167 0 - -1131 -1557 0 - -1131 -1868 0 - -1131 -1904 0 - -1131 -2458 0 - -1167 -1557 0 - -1167 -1868 0 - -1167 -1904 0 - -1167 -2458 0 - -1557 -1868 0 - -1557 -1904 0 - -1557 -2458 0 - -1868 -1904 0 - -1868 -2458 0 - -1904 -2458 0 - -458 -648 0 - -458 -815 0 - -458 -1168 0 - -458 -1905 0 - -458 -2351 0 - -458 -2459 0 - -458 -2561 0 - -648 -815 0 - -648 -1168 0 - -648 -1905 0 - -648 -2351 0 - -648 -2459 0 - -648 -2561 0 - -815 -1168 0 - -815 -1905 0 - -815 -2351 0 - -815 -2459 0 - -815 -2561 0 - -1168 -1905 0 - -1168 -2351 0 - -1168 -2459 0 - -1168 -2561 0 - -1905 -2351 0 - -1905 -2459 0 - -1905 -2561 0 - -2351 -2459 0 - -2351 -2561 0 - -2459 -2561 0 - -742 -1039 0 - -742 -1246 0 - -742 -1379 0 - -742 -1683 0 - -742 -1745 0 - -742 -1869 0 - -742 -2010 0 - -1039 -1246 0 - -1039 -1379 0 - -1039 -1683 0 - -1039 -1745 0 - -1039 -1869 0 - -1039 -2010 0 - -1246 -1379 0 - -1246 -1683 0 - -1246 -1745 0 - -1246 -1869 0 - -1246 -2010 0 - -1379 -1683 0 - -1379 -1745 0 - -1379 -1869 0 - -1379 -2010 0 - -1683 -1745 0 - -1683 -1869 0 - -1683 -2010 0 - -1745 -1869 0 - -1745 -2010 0 - -1869 -2010 0 - -1247 -1380 0 - -1247 -1558 0 - -1247 -1906 0 - -1380 -1558 0 - -1380 -1906 0 - -1558 -1906 0 - -185 -743 0 - -185 -1248 0 - -185 -1559 0 - -185 -1870 0 - -185 -1907 0 - -185 -2072 0 - -185 -2460 0 - -743 -1248 0 - -743 -1559 0 - -743 -1870 0 - -743 -1907 0 - -743 -2072 0 - -743 -2460 0 - -1248 -1559 0 - -1248 -1870 0 - -1248 -1907 0 - -1248 -2072 0 - -1248 -2460 0 - -1559 -1870 0 - -1559 -1907 0 - -1559 -2072 0 - -1559 -2460 0 - -1870 -1907 0 - -1870 -2072 0 - -1870 -2460 0 - -1907 -2072 0 - -1907 -2460 0 - -2072 -2460 0 - -328 -744 0 - -328 -1040 0 - -328 -1249 0 - -328 -1560 0 - -328 -1684 0 - -328 -1908 0 - -328 -2461 0 - -744 -1040 0 - -744 -1249 0 - -744 -1560 0 - -744 -1684 0 - -744 -1908 0 - -744 -2461 0 - -1040 -1249 0 - -1040 -1560 0 - -1040 -1684 0 - -1040 -1908 0 - -1040 -2461 0 - -1249 -1560 0 - -1249 -1684 0 - -1249 -1908 0 - -1249 -2461 0 - -1560 -1684 0 - -1560 -1908 0 - -1560 -2461 0 - -1684 -1908 0 - -1684 -2461 0 - -1908 -2461 0 - -186 -329 0 - -186 -745 0 - -186 -1250 0 - -186 -1381 0 - -186 -1561 0 - -186 -1746 0 - -186 -2011 0 - -186 -2462 0 - -329 -745 0 - -329 -1250 0 - -329 -1381 0 - -329 -1561 0 - -329 -1746 0 - -329 -2011 0 - -329 -2462 0 - -745 -1250 0 - -745 -1381 0 - -745 -1561 0 - -745 -1746 0 - -745 -2011 0 - -745 -2462 0 - -1250 -1381 0 - -1250 -1561 0 - -1250 -1746 0 - -1250 -2011 0 - -1250 -2462 0 - -1381 -1561 0 - -1381 -1746 0 - -1381 -2011 0 - -1381 -2462 0 - -1561 -1746 0 - -1561 -2011 0 - -1561 -2462 0 - -1746 -2011 0 - -1746 -2462 0 - -2011 -2462 0 - -187 -330 0 - -187 -1041 0 - -187 -1251 0 - -187 -1909 0 - -187 -2073 0 - -330 -1041 0 - -330 -1251 0 - -330 -1909 0 - -330 -2073 0 - -1041 -1251 0 - -1041 -1909 0 - -1041 -2073 0 - -1251 -1909 0 - -1251 -2073 0 - -1909 -2073 0 - -188 -746 0 - -188 -1042 0 - -188 -1685 0 - -188 -2074 0 - -746 -1042 0 - -746 -1685 0 - -746 -2074 0 - -1042 -1685 0 - -1042 -2074 0 - -1685 -2074 0 - -189 -1043 0 - -189 -1382 0 - -189 -1562 0 - -189 -1871 0 - -189 -1910 0 - -189 -2012 0 - -1043 -1382 0 - -1043 -1562 0 - -1043 -1871 0 - -1043 -1910 0 - -1043 -2012 0 - -1382 -1562 0 - -1382 -1871 0 - -1382 -1910 0 - -1382 -2012 0 - -1562 -1871 0 - -1562 -1910 0 - -1562 -2012 0 - -1871 -1910 0 - -1871 -2012 0 - -1910 -2012 0 - -190 -1686 0 - -190 -2075 0 - -190 -2463 0 - -1686 -2075 0 - -1686 -2463 0 - -2075 -2463 0 - -331 -1383 0 - -331 -1563 0 - -331 -1911 0 - -331 -2464 0 - -1383 -1563 0 - -1383 -1911 0 - -1383 -2464 0 - -1563 -1911 0 - -1563 -2464 0 - -1911 -2464 0 - -1044 -1252 0 - -1044 -1384 0 - -1044 -1564 0 - -1044 -1872 0 - -1044 -2465 0 - -1252 -1384 0 - -1252 -1564 0 - -1252 -1872 0 - -1252 -2465 0 - -1384 -1564 0 - -1384 -1872 0 - -1384 -2465 0 - -1564 -1872 0 - -1564 -2465 0 - -1872 -2465 0 - -191 -332 0 - -191 -1045 0 - -191 -1253 0 - -191 -1385 0 - -191 -1687 0 - -191 -1747 0 - -191 -1873 0 - -191 -1912 0 - -191 -2013 0 - -191 -2466 0 - -332 -1045 0 - -332 -1253 0 - -332 -1385 0 - -332 -1687 0 - -332 -1747 0 - -332 -1873 0 - -332 -1912 0 - -332 -2013 0 - -332 -2466 0 - -1045 -1253 0 - -1045 -1385 0 - -1045 -1687 0 - -1045 -1747 0 - -1045 -1873 0 - -1045 -1912 0 - -1045 -2013 0 - -1045 -2466 0 - -1253 -1385 0 - -1253 -1687 0 - -1253 -1747 0 - -1253 -1873 0 - -1253 -1912 0 - -1253 -2013 0 - -1253 -2466 0 - -1385 -1687 0 - -1385 -1747 0 - -1385 -1873 0 - -1385 -1912 0 - -1385 -2013 0 - -1385 -2466 0 - -1687 -1747 0 - -1687 -1873 0 - -1687 -1912 0 - -1687 -2013 0 - -1687 -2466 0 - -1747 -1873 0 - -1747 -1912 0 - -1747 -2013 0 - -1747 -2466 0 - -1873 -1912 0 - -1873 -2013 0 - -1873 -2466 0 - -1912 -2013 0 - -1912 -2466 0 - -2013 -2466 0 - -1565 -1748 0 - -1565 -2076 0 - -1565 -2467 0 - -1748 -2076 0 - -1748 -2467 0 - -2076 -2467 0 - -1386 -1913 0 - -1386 -2077 0 - -1386 -2468 0 - -1913 -2077 0 - -1913 -2468 0 - -2077 -2468 0 - -747 -1645 0 - -748 -931 0 - -748 -1387 0 - -748 -1469 0 - -748 -1749 0 - -748 -1823 0 - -931 -1387 0 - -931 -1469 0 - -931 -1749 0 - -931 -1823 0 - -1387 -1469 0 - -1387 -1749 0 - -1387 -1823 0 - -1469 -1749 0 - -1469 -1823 0 - -1749 -1823 0 - -17 -749 0 - -17 -1470 0 - -17 -1646 0 - -17 -1824 0 - -17 -1914 0 - -749 -1470 0 - -749 -1646 0 - -749 -1824 0 - -749 -1914 0 - -1470 -1646 0 - -1470 -1824 0 - -1470 -1914 0 - -1646 -1824 0 - -1646 -1914 0 - -1824 -1914 0 - -18 -750 0 - -18 -932 0 - -18 -1388 0 - -18 -1750 0 - -18 -1825 0 - -750 -932 0 - -750 -1388 0 - -750 -1750 0 - -750 -1825 0 - -932 -1388 0 - -932 -1750 0 - -932 -1825 0 - -1388 -1750 0 - -1388 -1825 0 - -1750 -1825 0 - -816 -933 0 - -816 -1389 0 - -816 -1471 0 - -816 -1751 0 - -816 -1915 0 - -933 -1389 0 - -933 -1471 0 - -933 -1751 0 - -933 -1915 0 - -1389 -1471 0 - -1389 -1751 0 - -1389 -1915 0 - -1471 -1751 0 - -1471 -1915 0 - -1751 -1915 0 - -751 -817 0 - -751 -1390 0 - -751 -1752 0 - -751 -1826 0 - -751 -1916 0 - -817 -1390 0 - -817 -1752 0 - -817 -1826 0 - -817 -1916 0 - -1390 -1752 0 - -1390 -1826 0 - -1390 -1916 0 - -1752 -1826 0 - -1752 -1916 0 - -1826 -1916 0 - -19 -818 0 - -19 -1391 0 - -19 -1647 0 - -19 -1753 0 - -19 -1827 0 - -818 -1391 0 - -818 -1647 0 - -818 -1753 0 - -818 -1827 0 - -1391 -1647 0 - -1391 -1753 0 - -1391 -1827 0 - -1647 -1753 0 - -1647 -1827 0 - -1753 -1827 0 - -752 -819 0 - -752 -934 0 - -752 -1169 0 - -752 -1472 0 - -819 -934 0 - -819 -1169 0 - -819 -1472 0 - -934 -1169 0 - -934 -1472 0 - -1169 -1472 0 - -20 -820 0 - -20 -1392 0 - -820 -1392 0 - -821 -935 0 - -821 -1170 0 - -821 -1917 0 - -935 -1170 0 - -935 -1917 0 - -1170 -1917 0 - -822 -1171 0 - -822 -1393 0 - -822 -1648 0 - -822 -1828 0 - -822 -1918 0 - -1171 -1393 0 - -1171 -1648 0 - -1171 -1828 0 - -1171 -1918 0 - -1393 -1648 0 - -1393 -1828 0 - -1393 -1918 0 - -1648 -1828 0 - -1648 -1918 0 - -1828 -1918 0 - -333 -753 0 - -333 -2078 0 - -753 -2078 0 - -21 -253 0 - -22 -254 0 - -22 -754 0 - -22 -1046 0 - -22 -2079 0 - -22 -2562 0 - -254 -754 0 - -254 -1046 0 - -254 -2079 0 - -254 -2562 0 - -754 -1046 0 - -754 -2079 0 - -754 -2562 0 - -1046 -2079 0 - -1046 -2562 0 - -2079 -2562 0 - -755 -1047 0 - -755 -2122 0 - -1047 -2122 0 - -23 -255 0 - -23 -1048 0 - -23 -2080 0 - -23 -2563 0 - -255 -1048 0 - -255 -2080 0 - -255 -2563 0 - -1048 -2080 0 - -1048 -2563 0 - -2080 -2563 0 - -256 -2081 0 - -256 -2123 0 - -2081 -2123 0 - -334 -1049 0 - -334 -2564 0 - -1049 -2564 0 - -1050 -2124 0 - -649 -936 0 - -649 -1051 0 - -649 -1394 0 - -649 -1919 0 - -649 -2125 0 - -936 -1051 0 - -936 -1394 0 - -936 -1919 0 - -936 -2125 0 - -1051 -1394 0 - -1051 -1919 0 - -1051 -2125 0 - -1394 -1919 0 - -1394 -2125 0 - -1919 -2125 0 - -937 -1395 0 - -937 -1920 0 - -937 -2232 0 - -937 -2469 0 - -1395 -1920 0 - -1395 -2232 0 - -1395 -2469 0 - -1920 -2232 0 - -1920 -2469 0 - -2232 -2469 0 - -650 -1396 0 - -650 -1649 0 - -650 -2470 0 - -1396 -1649 0 - -1396 -2470 0 - -1649 -2470 0 - -651 -938 0 - -651 -1052 0 - -651 -2233 0 - -651 -2471 0 - -938 -1052 0 - -938 -2233 0 - -938 -2471 0 - -1052 -2233 0 - -1052 -2471 0 - -2233 -2471 0 - -1053 -1397 0 - -1053 -1921 0 - -1053 -2472 0 - -1397 -1921 0 - -1397 -2472 0 - -1921 -2472 0 - -652 -939 0 - -652 -2473 0 - -939 -2473 0 - -653 -1650 0 - -653 -2234 0 - -1650 -2234 0 - -654 -940 0 - -654 -1922 0 - -654 -2474 0 - -940 -1922 0 - -940 -2474 0 - -1922 -2474 0 - -655 -1398 0 - -655 -1651 0 - -655 -1923 0 - -655 -2126 0 - -655 -2235 0 - -655 -2475 0 - -1398 -1651 0 - -1398 -1923 0 - -1398 -2126 0 - -1398 -2235 0 - -1398 -2475 0 - -1651 -1923 0 - -1651 -2126 0 - -1651 -2235 0 - -1651 -2475 0 - -1923 -2126 0 - -1923 -2235 0 - -1923 -2475 0 - -2126 -2235 0 - -2126 -2475 0 - -2235 -2475 0 - -257 -656 0 - -257 -756 0 - -257 -898 0 - -257 -941 0 - -257 -1399 0 - -257 -2236 0 - -656 -756 0 - -656 -898 0 - -656 -941 0 - -656 -1399 0 - -656 -2236 0 - -756 -898 0 - -756 -941 0 - -756 -1399 0 - -756 -2236 0 - -898 -941 0 - -898 -1399 0 - -898 -2236 0 - -941 -1399 0 - -941 -2236 0 - -1399 -2236 0 - -126 -258 0 - -126 -657 0 - -126 -757 0 - -258 -657 0 - -258 -757 0 - -657 -757 0 - -758 -899 0 - -758 -1132 0 - -758 -1924 0 - -758 -2127 0 - -758 -2237 0 - -758 -2476 0 - -899 -1132 0 - -899 -1924 0 - -899 -2127 0 - -899 -2237 0 - -899 -2476 0 - -1132 -1924 0 - -1132 -2127 0 - -1132 -2237 0 - -1132 -2476 0 - -1924 -2127 0 - -1924 -2237 0 - -1924 -2476 0 - -2127 -2237 0 - -2127 -2476 0 - -2237 -2476 0 - -69 -127 0 - -69 -658 0 - -69 -942 0 - -69 -1400 0 - -69 -1925 0 - -69 -2128 0 - -127 -658 0 - -127 -942 0 - -127 -1400 0 - -127 -1925 0 - -127 -2128 0 - -658 -942 0 - -658 -1400 0 - -658 -1925 0 - -658 -2128 0 - -942 -1400 0 - -942 -1925 0 - -942 -2128 0 - -1400 -1925 0 - -1400 -2128 0 - -1925 -2128 0 - -128 -659 0 - -128 -759 0 - -128 -1926 0 - -128 -2238 0 - -128 -2477 0 - -659 -759 0 - -659 -1926 0 - -659 -2238 0 - -659 -2477 0 - -759 -1926 0 - -759 -2238 0 - -759 -2477 0 - -1926 -2238 0 - -1926 -2477 0 - -2238 -2477 0 - -129 -660 0 - -129 -760 0 - -129 -943 0 - -129 -1401 0 - -129 -1927 0 - -129 -2129 0 - -129 -2239 0 - -129 -2478 0 - -660 -760 0 - -660 -943 0 - -660 -1401 0 - -660 -1927 0 - -660 -2129 0 - -660 -2239 0 - -660 -2478 0 - -760 -943 0 - -760 -1401 0 - -760 -1927 0 - -760 -2129 0 - -760 -2239 0 - -760 -2478 0 - -943 -1401 0 - -943 -1927 0 - -943 -2129 0 - -943 -2239 0 - -943 -2478 0 - -1401 -1927 0 - -1401 -2129 0 - -1401 -2239 0 - -1401 -2478 0 - -1927 -2129 0 - -1927 -2239 0 - -1927 -2478 0 - -2129 -2239 0 - -2129 -2478 0 - -2239 -2478 0 - -259 -1133 0 - -259 -1402 0 - -259 -1652 0 - -259 -2479 0 - -1133 -1402 0 - -1133 -1652 0 - -1133 -2479 0 - -1402 -1652 0 - -1402 -2479 0 - -1652 -2479 0 - -900 -944 0 - -900 -1653 0 - -900 -2130 0 - -900 -2240 0 - -944 -1653 0 - -944 -2130 0 - -944 -2240 0 - -1653 -2130 0 - -1653 -2240 0 - -2130 -2240 0 - -130 -945 0 - -130 -1403 0 - -130 -1928 0 - -130 -2241 0 - -130 -2480 0 - -945 -1403 0 - -945 -1928 0 - -945 -2241 0 - -945 -2480 0 - -1403 -1928 0 - -1403 -2241 0 - -1403 -2480 0 - -1928 -2241 0 - -1928 -2480 0 - -2241 -2480 0 - -70 -661 0 - -70 -761 0 - -70 -1404 0 - -70 -1654 0 - -70 -2481 0 - -661 -761 0 - -661 -1404 0 - -661 -1654 0 - -661 -2481 0 - -761 -1404 0 - -761 -1654 0 - -761 -2481 0 - -1404 -1654 0 - -1404 -2481 0 - -1654 -2481 0 - -71 -662 0 - -71 -901 0 - -71 -946 0 - -71 -2242 0 - -71 -2482 0 - -662 -901 0 - -662 -946 0 - -662 -2242 0 - -662 -2482 0 - -901 -946 0 - -901 -2242 0 - -901 -2482 0 - -946 -2242 0 - -946 -2482 0 - -2242 -2482 0 - -72 -260 0 - -72 -1405 0 - -72 -1929 0 - -72 -2483 0 - -260 -1405 0 - -260 -1929 0 - -260 -2483 0 - -1405 -1929 0 - -1405 -2483 0 - -1929 -2483 0 - -73 -663 0 - -73 -1655 0 - -73 -2243 0 - -663 -1655 0 - -663 -2243 0 - -1655 -2243 0 - -74 -131 0 - -74 -664 0 - -74 -1406 0 - -74 -1656 0 - -74 -1930 0 - -74 -2131 0 - -74 -2244 0 - -74 -2484 0 - -131 -664 0 - -131 -1406 0 - -131 -1656 0 - -131 -1930 0 - -131 -2131 0 - -131 -2244 0 - -131 -2484 0 - -664 -1406 0 - -664 -1656 0 - -664 -1930 0 - -664 -2131 0 - -664 -2244 0 - -664 -2484 0 - -1406 -1656 0 - -1406 -1930 0 - -1406 -2131 0 - -1406 -2244 0 - -1406 -2484 0 - -1656 -1930 0 - -1656 -2131 0 - -1656 -2244 0 - -1656 -2484 0 - -1930 -2131 0 - -1930 -2244 0 - -1930 -2484 0 - -2131 -2244 0 - -2131 -2484 0 - -2244 -2484 0 - -459 -570 0 - -459 -1566 0 - -459 -2014 0 - -459 -2245 0 - -459 -2485 0 - -570 -1566 0 - -570 -2014 0 - -570 -2245 0 - -570 -2485 0 - -1566 -2014 0 - -1566 -2245 0 - -1566 -2485 0 - -2014 -2245 0 - -2014 -2485 0 - -2245 -2485 0 - -24 -947 0 - -24 -1054 0 - -947 -1054 0 - -460 -1567 0 - -460 -2015 0 - -460 -2246 0 - -460 -2486 0 - -1567 -2015 0 - -1567 -2246 0 - -1567 -2486 0 - -2015 -2246 0 - -2015 -2486 0 - -2246 -2486 0 - -461 -948 0 - -461 -1055 0 - -461 -1473 0 - -461 -2247 0 - -461 -2487 0 - -948 -1055 0 - -948 -1473 0 - -948 -2247 0 - -948 -2487 0 - -1055 -1473 0 - -1055 -2247 0 - -1055 -2487 0 - -1473 -2247 0 - -1473 -2487 0 - -2247 -2487 0 - -25 -571 0 - -25 -949 0 - -25 -1056 0 - -25 -1874 0 - -25 -2248 0 - -571 -949 0 - -571 -1056 0 - -571 -1874 0 - -571 -2248 0 - -949 -1056 0 - -949 -1874 0 - -949 -2248 0 - -1056 -1874 0 - -1056 -2248 0 - -1874 -2248 0 - -26 -950 0 - -26 -1057 0 - -26 -1568 0 - -950 -1057 0 - -950 -1568 0 - -1057 -1568 0 - -75 -951 0 - -75 -1058 0 - -75 -1474 0 - -75 -2016 0 - -75 -2249 0 - -951 -1058 0 - -951 -1474 0 - -951 -2016 0 - -951 -2249 0 - -1058 -1474 0 - -1058 -2016 0 - -1058 -2249 0 - -1474 -2016 0 - -1474 -2249 0 - -2016 -2249 0 - -27 -462 0 - -27 -572 0 - -27 -1059 0 - -27 -2017 0 - -462 -572 0 - -462 -1059 0 - -462 -2017 0 - -572 -1059 0 - -572 -2017 0 - -1059 -2017 0 - -28 -76 0 - -28 -1569 0 - -28 -2018 0 - -28 -2488 0 - -76 -1569 0 - -76 -2018 0 - -76 -2488 0 - -1569 -2018 0 - -1569 -2488 0 - -2018 -2488 0 - -77 -463 0 - -77 -573 0 - -77 -952 0 - -77 -1060 0 - -77 -1475 0 - -77 -1570 0 - -77 -2250 0 - -77 -2489 0 - -463 -573 0 - -463 -952 0 - -463 -1060 0 - -463 -1475 0 - -463 -1570 0 - -463 -2250 0 - -463 -2489 0 - -573 -952 0 - -573 -1060 0 - -573 -1475 0 - -573 -1570 0 - -573 -2250 0 - -573 -2489 0 - -952 -1060 0 - -952 -1475 0 - -952 -1570 0 - -952 -2250 0 - -952 -2489 0 - -1060 -1475 0 - -1060 -1570 0 - -1060 -2250 0 - -1060 -2489 0 - -1475 -1570 0 - -1475 -2250 0 - -1475 -2489 0 - -1570 -2250 0 - -1570 -2489 0 - -2250 -2489 0 - -1061 -2251 0 - -464 -953 0 - -464 -1571 0 - -464 -1875 0 - -464 -2019 0 - -464 -2490 0 - -953 -1571 0 - -953 -1875 0 - -953 -2019 0 - -953 -2490 0 - -1571 -1875 0 - -1571 -2019 0 - -1571 -2490 0 - -1875 -2019 0 - -1875 -2490 0 - -2019 -2490 0 - -1572 -1657 0 - -1572 -2491 0 - -1657 -2491 0 - -29 -1573 0 - -29 -1658 0 - -29 -2492 0 - -29 -2565 0 - -1573 -1658 0 - -1573 -2492 0 - -1573 -2565 0 - -1658 -2492 0 - -1658 -2565 0 - -2492 -2565 0 - -30 -665 0 - -30 -1574 0 - -30 -1754 0 - -30 -2566 0 - -665 -1574 0 - -665 -1754 0 - -665 -2566 0 - -1574 -1754 0 - -1574 -2566 0 - -1754 -2566 0 - -574 -902 0 - -574 -1575 0 - -574 -1755 0 - -574 -2020 0 - -574 -2132 0 - -574 -2352 0 - -574 -2493 0 - -902 -1575 0 - -902 -1755 0 - -902 -2020 0 - -902 -2132 0 - -902 -2352 0 - -902 -2493 0 - -1575 -1755 0 - -1575 -2020 0 - -1575 -2132 0 - -1575 -2352 0 - -1575 -2493 0 - -1755 -2020 0 - -1755 -2132 0 - -1755 -2352 0 - -1755 -2493 0 - -2020 -2132 0 - -2020 -2352 0 - -2020 -2493 0 - -2132 -2352 0 - -2132 -2493 0 - -2352 -2493 0 - -31 -954 0 - -31 -1062 0 - -31 -1407 0 - -31 -1756 0 - -31 -2387 0 - -31 -2567 0 - -954 -1062 0 - -954 -1407 0 - -954 -1756 0 - -954 -2387 0 - -954 -2567 0 - -1062 -1407 0 - -1062 -1756 0 - -1062 -2387 0 - -1062 -2567 0 - -1407 -1756 0 - -1407 -2387 0 - -1407 -2567 0 - -1756 -2387 0 - -1756 -2567 0 - -2387 -2567 0 - -666 -823 0 - -666 -1063 0 - -666 -1576 0 - -666 -2353 0 - -666 -2494 0 - -823 -1063 0 - -823 -1576 0 - -823 -2353 0 - -823 -2494 0 - -1063 -1576 0 - -1063 -2353 0 - -1063 -2494 0 - -1576 -2353 0 - -1576 -2494 0 - -2353 -2494 0 - -824 -1408 0 - -824 -1757 0 - -824 -2133 0 - -824 -2388 0 - -1408 -1757 0 - -1408 -2133 0 - -1408 -2388 0 - -1757 -2133 0 - -1757 -2388 0 - -2133 -2388 0 - -32 -825 0 - -32 -1064 0 - -32 -1409 0 - -32 -1659 0 - -32 -1758 0 - -32 -2021 0 - -32 -2495 0 - -32 -2568 0 - -825 -1064 0 - -825 -1409 0 - -825 -1659 0 - -825 -1758 0 - -825 -2021 0 - -825 -2495 0 - -825 -2568 0 - -1064 -1409 0 - -1064 -1659 0 - -1064 -1758 0 - -1064 -2021 0 - -1064 -2495 0 - -1064 -2568 0 - -1409 -1659 0 - -1409 -1758 0 - -1409 -2021 0 - -1409 -2495 0 - -1409 -2568 0 - -1659 -1758 0 - -1659 -2021 0 - -1659 -2495 0 - -1659 -2568 0 - -1758 -2021 0 - -1758 -2495 0 - -1758 -2568 0 - -2021 -2495 0 - -2021 -2568 0 - -2495 -2568 0 - -1065 -1660 0 - -1065 -2134 0 - -1065 -2354 0 - -1065 -2569 0 - -1660 -2134 0 - -1660 -2354 0 - -1660 -2569 0 - -2134 -2354 0 - -2134 -2569 0 - -2354 -2569 0 - -33 -575 0 - -33 -826 0 - -33 -903 0 - -33 -955 0 - -33 -1066 0 - -33 -1661 0 - -33 -1759 0 - -33 -2135 0 - -575 -826 0 - -575 -903 0 - -575 -955 0 - -575 -1066 0 - -575 -1661 0 - -575 -1759 0 - -575 -2135 0 - -826 -903 0 - -826 -955 0 - -826 -1066 0 - -826 -1661 0 - -826 -1759 0 - -826 -2135 0 - -903 -955 0 - -903 -1066 0 - -903 -1661 0 - -903 -1759 0 - -903 -2135 0 - -955 -1066 0 - -955 -1661 0 - -955 -1759 0 - -955 -2135 0 - -1066 -1661 0 - -1066 -1759 0 - -1066 -2135 0 - -1661 -1759 0 - -1661 -2135 0 - -1759 -2135 0 - -576 -667 0 - -576 -827 0 - -576 -2136 0 - -576 -2496 0 - -667 -827 0 - -667 -2136 0 - -667 -2496 0 - -827 -2136 0 - -827 -2496 0 - -2136 -2496 0 - -34 -668 0 - -34 -828 0 - -34 -956 0 - -34 -1067 0 - -34 -1410 0 - -34 -1577 0 - -34 -1662 0 - -668 -828 0 - -668 -956 0 - -668 -1067 0 - -668 -1410 0 - -668 -1577 0 - -668 -1662 0 - -828 -956 0 - -828 -1067 0 - -828 -1410 0 - -828 -1577 0 - -828 -1662 0 - -956 -1067 0 - -956 -1410 0 - -956 -1577 0 - -956 -1662 0 - -1067 -1410 0 - -1067 -1577 0 - -1067 -1662 0 - -1410 -1577 0 - -1410 -1662 0 - -1577 -1662 0 - -35 -577 0 - -35 -1760 0 - -35 -2137 0 - -35 -2389 0 - -35 -2570 0 - -577 -1760 0 - -577 -2137 0 - -577 -2389 0 - -577 -2570 0 - -1760 -2137 0 - -1760 -2389 0 - -1760 -2570 0 - -2137 -2389 0 - -2137 -2570 0 - -2389 -2570 0 - -36 -578 0 - -36 -829 0 - -36 -904 0 - -36 -1068 0 - -36 -1411 0 - -36 -2022 0 - -578 -829 0 - -578 -904 0 - -578 -1068 0 - -578 -1411 0 - -578 -2022 0 - -829 -904 0 - -829 -1068 0 - -829 -1411 0 - -829 -2022 0 - -904 -1068 0 - -904 -1411 0 - -904 -2022 0 - -1068 -1411 0 - -1068 -2022 0 - -1411 -2022 0 - -78 -579 0 - -78 -669 0 - -78 -905 0 - -78 -957 0 - -78 -1069 0 - -78 -1578 0 - -78 -1761 0 - -78 -2355 0 - -78 -2497 0 - -579 -669 0 - -579 -905 0 - -579 -957 0 - -579 -1069 0 - -579 -1578 0 - -579 -1761 0 - -579 -2355 0 - -579 -2497 0 - -669 -905 0 - -669 -957 0 - -669 -1069 0 - -669 -1578 0 - -669 -1761 0 - -669 -2355 0 - -669 -2497 0 - -905 -957 0 - -905 -1069 0 - -905 -1578 0 - -905 -1761 0 - -905 -2355 0 - -905 -2497 0 - -957 -1069 0 - -957 -1578 0 - -957 -1761 0 - -957 -2355 0 - -957 -2497 0 - -1069 -1578 0 - -1069 -1761 0 - -1069 -2355 0 - -1069 -2497 0 - -1578 -1761 0 - -1578 -2355 0 - -1578 -2497 0 - -1761 -2355 0 - -1761 -2497 0 - -2355 -2497 0 - -79 -580 0 - -79 -830 0 - -79 -1070 0 - -79 -1412 0 - -79 -1762 0 - -79 -2023 0 - -79 -2356 0 - -79 -2498 0 - -580 -830 0 - -580 -1070 0 - -580 -1412 0 - -580 -1762 0 - -580 -2023 0 - -580 -2356 0 - -580 -2498 0 - -830 -1070 0 - -830 -1412 0 - -830 -1762 0 - -830 -2023 0 - -830 -2356 0 - -830 -2498 0 - -1070 -1412 0 - -1070 -1762 0 - -1070 -2023 0 - -1070 -2356 0 - -1070 -2498 0 - -1412 -1762 0 - -1412 -2023 0 - -1412 -2356 0 - -1412 -2498 0 - -1762 -2023 0 - -1762 -2356 0 - -1762 -2498 0 - -2023 -2356 0 - -2023 -2498 0 - -2356 -2498 0 - -670 -831 0 - -670 -958 0 - -670 -1579 0 - -670 -1763 0 - -670 -2390 0 - -670 -2499 0 - -670 -2571 0 - -831 -958 0 - -831 -1579 0 - -831 -1763 0 - -831 -2390 0 - -831 -2499 0 - -831 -2571 0 - -958 -1579 0 - -958 -1763 0 - -958 -2390 0 - -958 -2499 0 - -958 -2571 0 - -1579 -1763 0 - -1579 -2390 0 - -1579 -2499 0 - -1579 -2571 0 - -1763 -2390 0 - -1763 -2499 0 - -1763 -2571 0 - -2390 -2499 0 - -2390 -2571 0 - -2499 -2571 0 - -671 -832 0 - -671 -906 0 - -671 -959 0 - -671 -1580 0 - -671 -2024 0 - -671 -2391 0 - -671 -2500 0 - -832 -906 0 - -832 -959 0 - -832 -1580 0 - -832 -2024 0 - -832 -2391 0 - -832 -2500 0 - -906 -959 0 - -906 -1580 0 - -906 -2024 0 - -906 -2391 0 - -906 -2500 0 - -959 -1580 0 - -959 -2024 0 - -959 -2391 0 - -959 -2500 0 - -1580 -2024 0 - -1580 -2391 0 - -1580 -2500 0 - -2024 -2391 0 - -2024 -2500 0 - -2391 -2500 0 - -132 -335 0 - -132 -465 0 - -132 -1254 0 - -132 -2082 0 - -335 -465 0 - -335 -1254 0 - -335 -2082 0 - -465 -1254 0 - -465 -2082 0 - -1254 -2082 0 - -261 -466 0 - -261 -1071 0 - -261 -1255 0 - -261 -1688 0 - -261 -1764 0 - -261 -1829 0 - -261 -2025 0 - -261 -2252 0 - -261 -2392 0 - -466 -1071 0 - -466 -1255 0 - -466 -1688 0 - -466 -1764 0 - -466 -1829 0 - -466 -2025 0 - -466 -2252 0 - -466 -2392 0 - -1071 -1255 0 - -1071 -1688 0 - -1071 -1764 0 - -1071 -1829 0 - -1071 -2025 0 - -1071 -2252 0 - -1071 -2392 0 - -1255 -1688 0 - -1255 -1764 0 - -1255 -1829 0 - -1255 -2025 0 - -1255 -2252 0 - -1255 -2392 0 - -1688 -1764 0 - -1688 -1829 0 - -1688 -2025 0 - -1688 -2252 0 - -1688 -2392 0 - -1764 -1829 0 - -1764 -2025 0 - -1764 -2252 0 - -1764 -2392 0 - -1829 -2025 0 - -1829 -2252 0 - -1829 -2392 0 - -2025 -2252 0 - -2025 -2392 0 - -2252 -2392 0 - -262 -467 0 - -262 -833 0 - -262 -1134 0 - -262 -1172 0 - -262 -1256 0 - -262 -1931 0 - -262 -2253 0 - -262 -2357 0 - -467 -833 0 - -467 -1134 0 - -467 -1172 0 - -467 -1256 0 - -467 -1931 0 - -467 -2253 0 - -467 -2357 0 - -833 -1134 0 - -833 -1172 0 - -833 -1256 0 - -833 -1931 0 - -833 -2253 0 - -833 -2357 0 - -1134 -1172 0 - -1134 -1256 0 - -1134 -1931 0 - -1134 -2253 0 - -1134 -2357 0 - -1172 -1256 0 - -1172 -1931 0 - -1172 -2253 0 - -1172 -2357 0 - -1256 -1931 0 - -1256 -2253 0 - -1256 -2357 0 - -1931 -2253 0 - -1931 -2357 0 - -2253 -2357 0 - -468 -1135 0 - -468 -1257 0 - -468 -1830 0 - -468 -1932 0 - -468 -2083 0 - -1135 -1257 0 - -1135 -1830 0 - -1135 -1932 0 - -1135 -2083 0 - -1257 -1830 0 - -1257 -1932 0 - -1257 -2083 0 - -1830 -1932 0 - -1830 -2083 0 - -1932 -2083 0 - -336 -469 0 - -336 -1136 0 - -336 -1173 0 - -336 -1765 0 - -336 -1933 0 - -336 -2026 0 - -336 -2138 0 - -336 -2254 0 - -336 -2358 0 - -469 -1136 0 - -469 -1173 0 - -469 -1765 0 - -469 -1933 0 - -469 -2026 0 - -469 -2138 0 - -469 -2254 0 - -469 -2358 0 - -1136 -1173 0 - -1136 -1765 0 - -1136 -1933 0 - -1136 -2026 0 - -1136 -2138 0 - -1136 -2254 0 - -1136 -2358 0 - -1173 -1765 0 - -1173 -1933 0 - -1173 -2026 0 - -1173 -2138 0 - -1173 -2254 0 - -1173 -2358 0 - -1765 -1933 0 - -1765 -2026 0 - -1765 -2138 0 - -1765 -2254 0 - -1765 -2358 0 - -1933 -2026 0 - -1933 -2138 0 - -1933 -2254 0 - -1933 -2358 0 - -2026 -2138 0 - -2026 -2254 0 - -2026 -2358 0 - -2138 -2254 0 - -2138 -2358 0 - -2254 -2358 0 - -263 -1072 0 - -263 -1766 0 - -263 -1831 0 - -263 -2084 0 - -263 -2393 0 - -1072 -1766 0 - -1072 -1831 0 - -1072 -2084 0 - -1072 -2393 0 - -1766 -1831 0 - -1766 -2084 0 - -1766 -2393 0 - -1831 -2084 0 - -1831 -2393 0 - -2084 -2393 0 - -133 -470 0 - -133 -834 0 - -133 -1073 0 - -133 -1258 0 - -133 -1767 0 - -133 -1934 0 - -133 -2139 0 - -133 -2359 0 - -470 -834 0 - -470 -1073 0 - -470 -1258 0 - -470 -1767 0 - -470 -1934 0 - -470 -2139 0 - -470 -2359 0 - -834 -1073 0 - -834 -1258 0 - -834 -1767 0 - -834 -1934 0 - -834 -2139 0 - -834 -2359 0 - -1073 -1258 0 - -1073 -1767 0 - -1073 -1934 0 - -1073 -2139 0 - -1073 -2359 0 - -1258 -1767 0 - -1258 -1934 0 - -1258 -2139 0 - -1258 -2359 0 - -1767 -1934 0 - -1767 -2139 0 - -1767 -2359 0 - -1934 -2139 0 - -1934 -2359 0 - -2139 -2359 0 - -337 -471 0 - -337 -1259 0 - -337 -1768 0 - -337 -2027 0 - -337 -2140 0 - -337 -2255 0 - -471 -1259 0 - -471 -1768 0 - -471 -2027 0 - -471 -2140 0 - -471 -2255 0 - -1259 -1768 0 - -1259 -2027 0 - -1259 -2140 0 - -1259 -2255 0 - -1768 -2027 0 - -1768 -2140 0 - -1768 -2255 0 - -2027 -2140 0 - -2027 -2255 0 - -2140 -2255 0 - -264 -338 0 - -264 -472 0 - -264 -1074 0 - -264 -1137 0 - -264 -1260 0 - -264 -1935 0 - -264 -2085 0 - -264 -2141 0 - -264 -2256 0 - -264 -2360 0 - -338 -472 0 - -338 -1074 0 - -338 -1137 0 - -338 -1260 0 - -338 -1935 0 - -338 -2085 0 - -338 -2141 0 - -338 -2256 0 - -338 -2360 0 - -472 -1074 0 - -472 -1137 0 - -472 -1260 0 - -472 -1935 0 - -472 -2085 0 - -472 -2141 0 - -472 -2256 0 - -472 -2360 0 - -1074 -1137 0 - -1074 -1260 0 - -1074 -1935 0 - -1074 -2085 0 - -1074 -2141 0 - -1074 -2256 0 - -1074 -2360 0 - -1137 -1260 0 - -1137 -1935 0 - -1137 -2085 0 - -1137 -2141 0 - -1137 -2256 0 - -1137 -2360 0 - -1260 -1935 0 - -1260 -2085 0 - -1260 -2141 0 - -1260 -2256 0 - -1260 -2360 0 - -1935 -2085 0 - -1935 -2141 0 - -1935 -2256 0 - -1935 -2360 0 - -2085 -2141 0 - -2085 -2256 0 - -2085 -2360 0 - -2141 -2256 0 - -2141 -2360 0 - -2256 -2360 0 - -835 -1075 0 - -835 -1174 0 - -835 -1689 0 - -835 -2086 0 - -835 -2257 0 - -1075 -1174 0 - -1075 -1689 0 - -1075 -2086 0 - -1075 -2257 0 - -1174 -1689 0 - -1174 -2086 0 - -1174 -2257 0 - -1689 -2086 0 - -1689 -2257 0 - -2086 -2257 0 - -473 -836 0 - -473 -1076 0 - -473 -1936 0 - -473 -2028 0 - -473 -2258 0 - -836 -1076 0 - -836 -1936 0 - -836 -2028 0 - -836 -2258 0 - -1076 -1936 0 - -1076 -2028 0 - -1076 -2258 0 - -1936 -2028 0 - -1936 -2258 0 - -2028 -2258 0 - -134 -265 0 - -134 -339 0 - -134 -837 0 - -134 -1077 0 - -134 -1261 0 - -265 -339 0 - -265 -837 0 - -265 -1077 0 - -265 -1261 0 - -339 -837 0 - -339 -1077 0 - -339 -1261 0 - -837 -1077 0 - -837 -1261 0 - -1077 -1261 0 - -135 -340 0 - -135 -1078 0 - -135 -1262 0 - -135 -1769 0 - -135 -1937 0 - -135 -2029 0 - -135 -2259 0 - -135 -2394 0 - -340 -1078 0 - -340 -1262 0 - -340 -1769 0 - -340 -1937 0 - -340 -2029 0 - -340 -2259 0 - -340 -2394 0 - -1078 -1262 0 - -1078 -1769 0 - -1078 -1937 0 - -1078 -2029 0 - -1078 -2259 0 - -1078 -2394 0 - -1262 -1769 0 - -1262 -1937 0 - -1262 -2029 0 - -1262 -2259 0 - -1262 -2394 0 - -1769 -1937 0 - -1769 -2029 0 - -1769 -2259 0 - -1769 -2394 0 - -1937 -2029 0 - -1937 -2259 0 - -1937 -2394 0 - -2029 -2259 0 - -2029 -2394 0 - -2259 -2394 0 - -1263 -2030 0 - -1263 -2087 0 - -1263 -2361 0 - -2030 -2087 0 - -2030 -2361 0 - -2087 -2361 0 - -474 -1079 0 - -474 -1264 0 - -474 -1690 0 - -474 -1770 0 - -474 -1832 0 - -474 -2260 0 - -474 -2362 0 - -1079 -1264 0 - -1079 -1690 0 - -1079 -1770 0 - -1079 -1832 0 - -1079 -2260 0 - -1079 -2362 0 - -1264 -1690 0 - -1264 -1770 0 - -1264 -1832 0 - -1264 -2260 0 - -1264 -2362 0 - -1690 -1770 0 - -1690 -1832 0 - -1690 -2260 0 - -1690 -2362 0 - -1770 -1832 0 - -1770 -2260 0 - -1770 -2362 0 - -1832 -2260 0 - -1832 -2362 0 - -2260 -2362 0 - -266 -341 0 - -266 -475 0 - -266 -838 0 - -266 -1080 0 - -266 -1265 0 - -266 -1691 0 - -266 -1771 0 - -266 -1833 0 - -266 -1938 0 - -266 -2031 0 - -266 -2363 0 - -341 -475 0 - -341 -838 0 - -341 -1080 0 - -341 -1265 0 - -341 -1691 0 - -341 -1771 0 - -341 -1833 0 - -341 -1938 0 - -341 -2031 0 - -341 -2363 0 - -475 -838 0 - -475 -1080 0 - -475 -1265 0 - -475 -1691 0 - -475 -1771 0 - -475 -1833 0 - -475 -1938 0 - -475 -2031 0 - -475 -2363 0 - -838 -1080 0 - -838 -1265 0 - -838 -1691 0 - -838 -1771 0 - -838 -1833 0 - -838 -1938 0 - -838 -2031 0 - -838 -2363 0 - -1080 -1265 0 - -1080 -1691 0 - -1080 -1771 0 - -1080 -1833 0 - -1080 -1938 0 - -1080 -2031 0 - -1080 -2363 0 - -1265 -1691 0 - -1265 -1771 0 - -1265 -1833 0 - -1265 -1938 0 - -1265 -2031 0 - -1265 -2363 0 - -1691 -1771 0 - -1691 -1833 0 - -1691 -1938 0 - -1691 -2031 0 - -1691 -2363 0 - -1771 -1833 0 - -1771 -1938 0 - -1771 -2031 0 - -1771 -2363 0 - -1833 -1938 0 - -1833 -2031 0 - -1833 -2363 0 - -1938 -2031 0 - -1938 -2363 0 - -2031 -2363 0 - -136 -267 0 - -136 -342 0 - -136 -1081 0 - -136 -2088 0 - -136 -2142 0 - -136 -2261 0 - -267 -342 0 - -267 -1081 0 - -267 -2088 0 - -267 -2142 0 - -267 -2261 0 - -342 -1081 0 - -342 -2088 0 - -342 -2142 0 - -342 -2261 0 - -1081 -2088 0 - -1081 -2142 0 - -1081 -2261 0 - -2088 -2142 0 - -2088 -2261 0 - -2142 -2261 0 - -1175 -1772 0 - -1175 -2032 0 - -1175 -2262 0 - -1772 -2032 0 - -1772 -2262 0 - -2032 -2262 0 - -268 -839 0 - -268 -1266 0 - -268 -2143 0 - -268 -2263 0 - -268 -2364 0 - -839 -1266 0 - -839 -2143 0 - -839 -2263 0 - -839 -2364 0 - -1266 -2143 0 - -1266 -2263 0 - -1266 -2364 0 - -2143 -2263 0 - -2143 -2364 0 - -2263 -2364 0 - -37 -476 0 - -37 -1267 0 - -476 -1267 0 - -137 -477 0 - -137 -840 0 - -137 -1268 0 - -137 -1773 0 - -477 -840 0 - -477 -1268 0 - -477 -1773 0 - -840 -1268 0 - -840 -1773 0 - -1268 -1773 0 - -138 -841 0 - -138 -1269 0 - -138 -1774 0 - -138 -2395 0 - -841 -1269 0 - -841 -1774 0 - -841 -2395 0 - -1269 -1774 0 - -1269 -2395 0 - -1774 -2395 0 - -38 -139 0 - -38 -842 0 - -38 -1270 0 - -139 -842 0 - -139 -1270 0 - -842 -1270 0 - -39 -1271 0 - -39 -1775 0 - -39 -2396 0 - -1271 -1775 0 - -1271 -2396 0 - -1775 -2396 0 - -40 -140 0 - -40 -478 0 - -140 -478 0 - -479 -1272 0 - -479 -1776 0 - -479 -2264 0 - -1272 -1776 0 - -1272 -2264 0 - -1776 -2264 0 - -1176 -1777 0 - -1176 -2265 0 - -1777 -2265 0 - -141 -480 0 - -141 -843 0 - -141 -1177 0 - -141 -2266 0 - -141 -2397 0 - -480 -843 0 - -480 -1177 0 - -480 -2266 0 - -480 -2397 0 - -843 -1177 0 - -843 -2266 0 - -843 -2397 0 - -1177 -2266 0 - -1177 -2397 0 - -2266 -2397 0 - -481 -762 0 - -481 -907 0 - -481 -960 0 - -481 -1082 0 - -762 -907 0 - -762 -960 0 - -762 -1082 0 - -907 -960 0 - -907 -1082 0 - -960 -1082 0 - -482 -763 0 - -482 -908 0 - -763 -908 0 - -764 -909 0 - -764 -961 0 - -764 -1083 0 - -909 -961 0 - -909 -1083 0 - -961 -1083 0 - -483 -910 0 - -483 -962 0 - -483 -1084 0 - -910 -962 0 - -910 -1084 0 - -962 -1084 0 - -41 -1273 0 - -41 -1476 0 - -41 -1581 0 - -41 -1939 0 - -41 -2501 0 - -41 -2572 0 - -1273 -1476 0 - -1273 -1581 0 - -1273 -1939 0 - -1273 -2501 0 - -1273 -2572 0 - -1476 -1581 0 - -1476 -1939 0 - -1476 -2501 0 - -1476 -2572 0 - -1581 -1939 0 - -1581 -2501 0 - -1581 -2572 0 - -1939 -2501 0 - -1939 -2572 0 - -2501 -2572 0 - -581 -911 0 - -581 -1582 0 - -581 -1778 0 - -581 -1940 0 - -581 -2267 0 - -581 -2365 0 - -581 -2502 0 - -911 -1582 0 - -911 -1778 0 - -911 -1940 0 - -911 -2267 0 - -911 -2365 0 - -911 -2502 0 - -1582 -1778 0 - -1582 -1940 0 - -1582 -2267 0 - -1582 -2365 0 - -1582 -2502 0 - -1778 -1940 0 - -1778 -2267 0 - -1778 -2365 0 - -1778 -2502 0 - -1940 -2267 0 - -1940 -2365 0 - -1940 -2502 0 - -2267 -2365 0 - -2267 -2502 0 - -2365 -2502 0 - -42 -1779 0 - -42 -2398 0 - -42 -2573 0 - -1779 -2398 0 - -1779 -2573 0 - -2398 -2573 0 - -80 -142 0 - -80 -582 0 - -80 -844 0 - -80 -1274 0 - -80 -1477 0 - -80 -1780 0 - -80 -1941 0 - -80 -2366 0 - -142 -582 0 - -142 -844 0 - -142 -1274 0 - -142 -1477 0 - -142 -1780 0 - -142 -1941 0 - -142 -2366 0 - -582 -844 0 - -582 -1274 0 - -582 -1477 0 - -582 -1780 0 - -582 -1941 0 - -582 -2366 0 - -844 -1274 0 - -844 -1477 0 - -844 -1780 0 - -844 -1941 0 - -844 -2366 0 - -1274 -1477 0 - -1274 -1780 0 - -1274 -1941 0 - -1274 -2366 0 - -1477 -1780 0 - -1477 -1941 0 - -1477 -2366 0 - -1780 -1941 0 - -1780 -2366 0 - -1941 -2366 0 - -143 -845 0 - -143 -1275 0 - -143 -1478 0 - -143 -1583 0 - -143 -1942 0 - -143 -2268 0 - -143 -2367 0 - -143 -2503 0 - -845 -1275 0 - -845 -1478 0 - -845 -1583 0 - -845 -1942 0 - -845 -2268 0 - -845 -2367 0 - -845 -2503 0 - -1275 -1478 0 - -1275 -1583 0 - -1275 -1942 0 - -1275 -2268 0 - -1275 -2367 0 - -1275 -2503 0 - -1478 -1583 0 - -1478 -1942 0 - -1478 -2268 0 - -1478 -2367 0 - -1478 -2503 0 - -1583 -1942 0 - -1583 -2268 0 - -1583 -2367 0 - -1583 -2503 0 - -1942 -2268 0 - -1942 -2367 0 - -1942 -2503 0 - -2268 -2367 0 - -2268 -2503 0 - -2367 -2503 0 - -144 -1479 0 - -144 -1781 0 - -144 -1943 0 - -144 -2269 0 - -144 -2504 0 - -1479 -1781 0 - -1479 -1943 0 - -1479 -2269 0 - -1479 -2504 0 - -1781 -1943 0 - -1781 -2269 0 - -1781 -2504 0 - -1943 -2269 0 - -1943 -2504 0 - -2269 -2504 0 - -1276 -1944 0 - -1276 -2270 0 - -1276 -2368 0 - -1276 -2574 0 - -1944 -2270 0 - -1944 -2368 0 - -1944 -2574 0 - -2270 -2368 0 - -2270 -2574 0 - -2368 -2574 0 - -145 -583 0 - -145 -1480 0 - -145 -1584 0 - -145 -1945 0 - -145 -2271 0 - -145 -2505 0 - -145 -2575 0 - -583 -1480 0 - -583 -1584 0 - -583 -1945 0 - -583 -2271 0 - -583 -2505 0 - -583 -2575 0 - -1480 -1584 0 - -1480 -1945 0 - -1480 -2271 0 - -1480 -2505 0 - -1480 -2575 0 - -1584 -1945 0 - -1584 -2271 0 - -1584 -2505 0 - -1584 -2575 0 - -1945 -2271 0 - -1945 -2505 0 - -1945 -2575 0 - -2271 -2505 0 - -2271 -2575 0 - -2505 -2575 0 - -81 -146 0 - -81 -1277 0 - -81 -1481 0 - -81 -1782 0 - -81 -1946 0 - -81 -2272 0 - -81 -2399 0 - -81 -2576 0 - -146 -1277 0 - -146 -1481 0 - -146 -1782 0 - -146 -1946 0 - -146 -2272 0 - -146 -2399 0 - -146 -2576 0 - -1277 -1481 0 - -1277 -1782 0 - -1277 -1946 0 - -1277 -2272 0 - -1277 -2399 0 - -1277 -2576 0 - -1481 -1782 0 - -1481 -1946 0 - -1481 -2272 0 - -1481 -2399 0 - -1481 -2576 0 - -1782 -1946 0 - -1782 -2272 0 - -1782 -2399 0 - -1782 -2576 0 - -1946 -2272 0 - -1946 -2399 0 - -1946 -2576 0 - -2272 -2399 0 - -2272 -2576 0 - -2399 -2576 0 - -147 -584 0 - -147 -1278 0 - -147 -1585 0 - -147 -2273 0 - -147 -2506 0 - -584 -1278 0 - -584 -1585 0 - -584 -2273 0 - -584 -2506 0 - -1278 -1585 0 - -1278 -2273 0 - -1278 -2506 0 - -1585 -2273 0 - -1585 -2506 0 - -2273 -2506 0 - -43 -82 0 - -43 -1279 0 - -43 -1586 0 - -43 -2369 0 - -43 -2507 0 - -43 -2577 0 - -82 -1279 0 - -82 -1586 0 - -82 -2369 0 - -82 -2507 0 - -82 -2577 0 - -1279 -1586 0 - -1279 -2369 0 - -1279 -2507 0 - -1279 -2577 0 - -1586 -2369 0 - -1586 -2507 0 - -1586 -2577 0 - -2369 -2507 0 - -2369 -2577 0 - -2507 -2577 0 - -83 -585 0 - -83 -912 0 - -83 -1280 0 - -83 -1482 0 - -83 -1587 0 - -83 -1783 0 - -83 -2274 0 - -83 -2370 0 - -83 -2508 0 - -585 -912 0 - -585 -1280 0 - -585 -1482 0 - -585 -1587 0 - -585 -1783 0 - -585 -2274 0 - -585 -2370 0 - -585 -2508 0 - -912 -1280 0 - -912 -1482 0 - -912 -1587 0 - -912 -1783 0 - -912 -2274 0 - -912 -2370 0 - -912 -2508 0 - -1280 -1482 0 - -1280 -1587 0 - -1280 -1783 0 - -1280 -2274 0 - -1280 -2370 0 - -1280 -2508 0 - -1482 -1587 0 - -1482 -1783 0 - -1482 -2274 0 - -1482 -2370 0 - -1482 -2508 0 - -1587 -1783 0 - -1587 -2274 0 - -1587 -2370 0 - -1587 -2508 0 - -1783 -2274 0 - -1783 -2370 0 - -1783 -2508 0 - -2274 -2370 0 - -2274 -2508 0 - -2370 -2508 0 - -84 -586 0 - -84 -846 0 - -84 -1281 0 - -84 -1784 0 - -84 -1947 0 - -84 -2371 0 - -84 -2509 0 - -586 -846 0 - -586 -1281 0 - -586 -1784 0 - -586 -1947 0 - -586 -2371 0 - -586 -2509 0 - -846 -1281 0 - -846 -1784 0 - -846 -1947 0 - -846 -2371 0 - -846 -2509 0 - -1281 -1784 0 - -1281 -1947 0 - -1281 -2371 0 - -1281 -2509 0 - -1784 -1947 0 - -1784 -2371 0 - -1784 -2509 0 - -1947 -2371 0 - -1947 -2509 0 - -2371 -2509 0 - -847 -1588 0 - -847 -1785 0 - -847 -2400 0 - -847 -2510 0 - -847 -2578 0 - -1588 -1785 0 - -1588 -2400 0 - -1588 -2510 0 - -1588 -2578 0 - -1785 -2400 0 - -1785 -2510 0 - -1785 -2578 0 - -2400 -2510 0 - -2400 -2578 0 - -2510 -2578 0 - -85 -587 0 - -85 -1786 0 - -85 -2275 0 - -85 -2579 0 - -587 -1786 0 - -587 -2275 0 - -587 -2579 0 - -1786 -2275 0 - -1786 -2579 0 - -2275 -2579 0 - -86 -148 0 - -86 -848 0 - -86 -1948 0 - -86 -2276 0 - -86 -2372 0 - -86 -2401 0 - -86 -2511 0 - -86 -2580 0 - -148 -848 0 - -148 -1948 0 - -148 -2276 0 - -148 -2372 0 - -148 -2401 0 - -148 -2511 0 - -148 -2580 0 - -848 -1948 0 - -848 -2276 0 - -848 -2372 0 - -848 -2401 0 - -848 -2511 0 - -848 -2580 0 - -1948 -2276 0 - -1948 -2372 0 - -1948 -2401 0 - -1948 -2511 0 - -1948 -2580 0 - -2276 -2372 0 - -2276 -2401 0 - -2276 -2511 0 - -2276 -2580 0 - -2372 -2401 0 - -2372 -2511 0 - -2372 -2580 0 - -2401 -2511 0 - -2401 -2580 0 - -2511 -2580 0 - -149 -1589 0 - -588 -1834 0 - -588 -2033 0 - -588 -2277 0 - -1834 -2033 0 - -1834 -2277 0 - -2033 -2277 0 - -44 -192 0 - -44 -1590 0 - -44 -1835 0 - -192 -1590 0 - -192 -1835 0 - -1590 -1835 0 - -150 -193 0 - -150 -589 0 - -150 -849 0 - -193 -589 0 - -193 -849 0 - -589 -849 0 - -45 -194 0 - -45 -850 0 - -45 -1591 0 - -45 -2034 0 - -45 -2278 0 - -194 -850 0 - -194 -1591 0 - -194 -2034 0 - -194 -2278 0 - -850 -1591 0 - -850 -2034 0 - -850 -2278 0 - -1591 -2034 0 - -1591 -2278 0 - -2034 -2278 0 - -151 -195 0 - -151 -590 0 - -151 -851 0 - -151 -2279 0 - -195 -590 0 - -195 -851 0 - -195 -2279 0 - -590 -851 0 - -590 -2279 0 - -851 -2279 0 - -152 -591 0 - -152 -1592 0 - -152 -2280 0 - -591 -1592 0 - -591 -2280 0 - -1592 -2280 0 - -592 -1593 0 - -592 -1836 0 - -592 -2281 0 - -1593 -1836 0 - -1593 -2281 0 - -1836 -2281 0 - -852 -1594 0 - -852 -2035 0 - -1594 -2035 0 - -343 -484 0 - -343 -1595 0 - -343 -2512 0 - -484 -1595 0 - -484 -2512 0 - -1595 -2512 0 - -344 -485 0 - -344 -1178 0 - -344 -1596 0 - -344 -1949 0 - -344 -2144 0 - -344 -2282 0 - -344 -2513 0 - -485 -1178 0 - -485 -1596 0 - -485 -1949 0 - -485 -2144 0 - -485 -2282 0 - -485 -2513 0 - -1178 -1596 0 - -1178 -1949 0 - -1178 -2144 0 - -1178 -2282 0 - -1178 -2513 0 - -1596 -1949 0 - -1596 -2144 0 - -1596 -2282 0 - -1596 -2513 0 - -1949 -2144 0 - -1949 -2282 0 - -1949 -2513 0 - -2144 -2282 0 - -2144 -2513 0 - -2282 -2513 0 - -345 -486 0 - -345 -672 0 - -345 -1483 0 - -345 -1597 0 - -345 -1950 0 - -345 -2283 0 - -345 -2514 0 - -486 -672 0 - -486 -1483 0 - -486 -1597 0 - -486 -1950 0 - -486 -2283 0 - -486 -2514 0 - -672 -1483 0 - -672 -1597 0 - -672 -1950 0 - -672 -2283 0 - -672 -2514 0 - -1483 -1597 0 - -1483 -1950 0 - -1483 -2283 0 - -1483 -2514 0 - -1597 -1950 0 - -1597 -2283 0 - -1597 -2514 0 - -1950 -2283 0 - -1950 -2514 0 - -2283 -2514 0 - -346 -487 0 - -346 -1598 0 - -346 -2145 0 - -346 -2284 0 - -346 -2515 0 - -487 -1598 0 - -487 -2145 0 - -487 -2284 0 - -487 -2515 0 - -1598 -2145 0 - -1598 -2284 0 - -1598 -2515 0 - -2145 -2284 0 - -2145 -2515 0 - -2284 -2515 0 - -347 -1837 0 - -347 -1951 0 - -347 -2146 0 - -1837 -1951 0 - -1837 -2146 0 - -1951 -2146 0 - -348 -1179 0 - -348 -1838 0 - -348 -2147 0 - -348 -2285 0 - -1179 -1838 0 - -1179 -2147 0 - -1179 -2285 0 - -1838 -2147 0 - -1838 -2285 0 - -2147 -2285 0 - -87 -488 0 - -87 -1484 0 - -87 -1599 0 - -87 -1952 0 - -87 -2286 0 - -488 -1484 0 - -488 -1599 0 - -488 -1952 0 - -488 -2286 0 - -1484 -1599 0 - -1484 -1952 0 - -1484 -2286 0 - -1599 -1952 0 - -1599 -2286 0 - -1952 -2286 0 - -349 -1180 0 - -349 -1485 0 - -349 -1600 0 - -349 -1953 0 - -349 -2287 0 - -349 -2516 0 - -349 -2581 0 - -1180 -1485 0 - -1180 -1600 0 - -1180 -1953 0 - -1180 -2287 0 - -1180 -2516 0 - -1180 -2581 0 - -1485 -1600 0 - -1485 -1953 0 - -1485 -2287 0 - -1485 -2516 0 - -1485 -2581 0 - -1600 -1953 0 - -1600 -2287 0 - -1600 -2516 0 - -1600 -2581 0 - -1953 -2287 0 - -1953 -2516 0 - -1953 -2581 0 - -2287 -2516 0 - -2287 -2581 0 - -2516 -2581 0 - -350 -673 0 - -350 -1601 0 - -673 -1601 0 - -88 -674 0 - -88 -1602 0 - -88 -2517 0 - -88 -2582 0 - -674 -1602 0 - -674 -2517 0 - -674 -2582 0 - -1602 -2517 0 - -1602 -2582 0 - -2517 -2582 0 - -489 -675 0 - -489 -1603 0 - -489 -2518 0 - -489 -2583 0 - -675 -1603 0 - -675 -2518 0 - -675 -2583 0 - -1603 -2518 0 - -1603 -2583 0 - -2518 -2583 0 - -351 -490 0 - -351 -676 0 - -351 -1181 0 - -351 -1604 0 - -351 -1954 0 - -351 -2519 0 - -490 -676 0 - -490 -1181 0 - -490 -1604 0 - -490 -1954 0 - -490 -2519 0 - -676 -1181 0 - -676 -1604 0 - -676 -1954 0 - -676 -2519 0 - -1181 -1604 0 - -1181 -1954 0 - -1181 -2519 0 - -1604 -1954 0 - -1604 -2519 0 - -1954 -2519 0 - -89 -491 0 - -89 -677 0 - -89 -1182 0 - -89 -1839 0 - -89 -1955 0 - -89 -2148 0 - -89 -2288 0 - -89 -2520 0 - -89 -2584 0 - -491 -677 0 - -491 -1182 0 - -491 -1839 0 - -491 -1955 0 - -491 -2148 0 - -491 -2288 0 - -491 -2520 0 - -491 -2584 0 - -677 -1182 0 - -677 -1839 0 - -677 -1955 0 - -677 -2148 0 - -677 -2288 0 - -677 -2520 0 - -677 -2584 0 - -1182 -1839 0 - -1182 -1955 0 - -1182 -2148 0 - -1182 -2288 0 - -1182 -2520 0 - -1182 -2584 0 - -1839 -1955 0 - -1839 -2148 0 - -1839 -2288 0 - -1839 -2520 0 - -1839 -2584 0 - -1955 -2148 0 - -1955 -2288 0 - -1955 -2520 0 - -1955 -2584 0 - -2148 -2288 0 - -2148 -2520 0 - -2148 -2584 0 - -2288 -2520 0 - -2288 -2584 0 - -2520 -2584 0 - -269 -492 0 - -269 -1085 0 - -269 -1282 0 - -269 -1413 0 - -269 -1692 0 - -269 -2402 0 - -492 -1085 0 - -492 -1282 0 - -492 -1413 0 - -492 -1692 0 - -492 -2402 0 - -1085 -1282 0 - -1085 -1413 0 - -1085 -1692 0 - -1085 -2402 0 - -1282 -1413 0 - -1282 -1692 0 - -1282 -2402 0 - -1413 -1692 0 - -1413 -2402 0 - -1692 -2402 0 - -493 -1283 0 - -270 -1086 0 - -270 -1414 0 - -270 -2403 0 - -1086 -1414 0 - -1086 -2403 0 - -1414 -2403 0 - -153 -352 0 - -153 -494 0 - -153 -1087 0 - -153 -1284 0 - -153 -1693 0 - -153 -2373 0 - -352 -494 0 - -352 -1087 0 - -352 -1284 0 - -352 -1693 0 - -352 -2373 0 - -494 -1087 0 - -494 -1284 0 - -494 -1693 0 - -494 -2373 0 - -1087 -1284 0 - -1087 -1693 0 - -1087 -2373 0 - -1284 -1693 0 - -1284 -2373 0 - -1693 -2373 0 - -271 -353 0 - -271 -495 0 - -271 -1088 0 - -271 -1285 0 - -271 -2374 0 - -353 -495 0 - -353 -1088 0 - -353 -1285 0 - -353 -2374 0 - -495 -1088 0 - -495 -1285 0 - -495 -2374 0 - -1088 -1285 0 - -1088 -2374 0 - -1285 -2374 0 - -354 -1089 0 - -496 -1090 0 - -496 -1415 0 - -1090 -1415 0 - -154 -355 0 - -154 -497 0 - -355 -497 0 - -272 -356 0 - -272 -498 0 - -272 -1091 0 - -272 -1286 0 - -272 -1416 0 - -272 -1694 0 - -272 -2375 0 - -356 -498 0 - -356 -1091 0 - -356 -1286 0 - -356 -1416 0 - -356 -1694 0 - -356 -2375 0 - -498 -1091 0 - -498 -1286 0 - -498 -1416 0 - -498 -1694 0 - -498 -2375 0 - -1091 -1286 0 - -1091 -1416 0 - -1091 -1694 0 - -1091 -2375 0 - -1286 -1416 0 - -1286 -1694 0 - -1286 -2375 0 - -1416 -1694 0 - -1416 -2375 0 - -1694 -2375 0 - -273 -499 0 - -273 -2404 0 - -499 -2404 0 - -155 -357 0 - -155 -1287 0 - -155 -2089 0 - -357 -1287 0 - -357 -2089 0 - -1287 -2089 0 - -853 -1288 0 - -853 -1956 0 - -1288 -1956 0 - -156 -196 0 - -156 -593 0 - -156 -854 0 - -156 -963 0 - -156 -1289 0 - -156 -1787 0 - -156 -1957 0 - -196 -593 0 - -196 -854 0 - -196 -963 0 - -196 -1289 0 - -196 -1787 0 - -196 -1957 0 - -593 -854 0 - -593 -963 0 - -593 -1289 0 - -593 -1787 0 - -593 -1957 0 - -854 -963 0 - -854 -1289 0 - -854 -1787 0 - -854 -1957 0 - -963 -1289 0 - -963 -1787 0 - -963 -1957 0 - -1289 -1787 0 - -1289 -1957 0 - -1787 -1957 0 - -157 -358 0 - -157 -855 0 - -157 -1290 0 - -157 -1958 0 - -358 -855 0 - -358 -1290 0 - -358 -1958 0 - -855 -1290 0 - -855 -1958 0 - -1290 -1958 0 - -158 -359 0 - -158 -856 0 - -158 -1291 0 - -158 -1788 0 - -158 -1959 0 - -158 -2405 0 - -359 -856 0 - -359 -1291 0 - -359 -1788 0 - -359 -1959 0 - -359 -2405 0 - -856 -1291 0 - -856 -1788 0 - -856 -1959 0 - -856 -2405 0 - -1291 -1788 0 - -1291 -1959 0 - -1291 -2405 0 - -1788 -1959 0 - -1788 -2405 0 - -1959 -2405 0 - -197 -360 0 - -197 -1292 0 - -197 -1960 0 - -197 -2090 0 - -360 -1292 0 - -360 -1960 0 - -360 -2090 0 - -1292 -1960 0 - -1292 -2090 0 - -1960 -2090 0 - -159 -198 0 - -159 -594 0 - -159 -857 0 - -159 -2091 0 - -198 -594 0 - -198 -857 0 - -198 -2091 0 - -594 -857 0 - -594 -2091 0 - -857 -2091 0 - -160 -361 0 - -160 -595 0 - -160 -964 0 - -160 -1961 0 - -361 -595 0 - -361 -964 0 - -361 -1961 0 - -595 -964 0 - -595 -1961 0 - -964 -1961 0 - -596 -1293 0 - -596 -1789 0 - -596 -2406 0 - -1293 -1789 0 - -1293 -2406 0 - -1789 -2406 0 - -362 -858 0 - -362 -965 0 - -362 -1962 0 - -362 -2092 0 - -362 -2407 0 - -858 -965 0 - -858 -1962 0 - -858 -2092 0 - -858 -2407 0 - -965 -1962 0 - -965 -2092 0 - -965 -2407 0 - -1962 -2092 0 - -1962 -2407 0 - -2092 -2407 0 - -161 -859 0 - -161 -1963 0 - -161 -2093 0 - -161 -2408 0 - -859 -1963 0 - -859 -2093 0 - -859 -2408 0 - -1963 -2093 0 - -1963 -2408 0 - -2093 -2408 0 - -597 -678 0 - -597 -966 0 - -597 -1092 0 - -597 -1294 0 - -597 -1417 0 - -597 -1695 0 - -597 -1840 0 - -597 -2289 0 - -597 -2409 0 - -678 -966 0 - -678 -1092 0 - -678 -1294 0 - -678 -1417 0 - -678 -1695 0 - -678 -1840 0 - -678 -2289 0 - -678 -2409 0 - -966 -1092 0 - -966 -1294 0 - -966 -1417 0 - -966 -1695 0 - -966 -1840 0 - -966 -2289 0 - -966 -2409 0 - -1092 -1294 0 - -1092 -1417 0 - -1092 -1695 0 - -1092 -1840 0 - -1092 -2289 0 - -1092 -2409 0 - -1294 -1417 0 - -1294 -1695 0 - -1294 -1840 0 - -1294 -2289 0 - -1294 -2409 0 - -1417 -1695 0 - -1417 -1840 0 - -1417 -2289 0 - -1417 -2409 0 - -1695 -1840 0 - -1695 -2289 0 - -1695 -2409 0 - -1840 -2289 0 - -1840 -2409 0 - -2289 -2409 0 - -46 -679 0 - -46 -860 0 - -46 -1138 0 - -46 -1183 0 - -46 -1295 0 - -46 -1418 0 - -46 -1605 0 - -46 -1964 0 - -46 -2290 0 - -46 -2376 0 - -679 -860 0 - -679 -1138 0 - -679 -1183 0 - -679 -1295 0 - -679 -1418 0 - -679 -1605 0 - -679 -1964 0 - -679 -2290 0 - -679 -2376 0 - -860 -1138 0 - -860 -1183 0 - -860 -1295 0 - -860 -1418 0 - -860 -1605 0 - -860 -1964 0 - -860 -2290 0 - -860 -2376 0 - -1138 -1183 0 - -1138 -1295 0 - -1138 -1418 0 - -1138 -1605 0 - -1138 -1964 0 - -1138 -2290 0 - -1138 -2376 0 - -1183 -1295 0 - -1183 -1418 0 - -1183 -1605 0 - -1183 -1964 0 - -1183 -2290 0 - -1183 -2376 0 - -1295 -1418 0 - -1295 -1605 0 - -1295 -1964 0 - -1295 -2290 0 - -1295 -2376 0 - -1418 -1605 0 - -1418 -1964 0 - -1418 -2290 0 - -1418 -2376 0 - -1605 -1964 0 - -1605 -2290 0 - -1605 -2376 0 - -1964 -2290 0 - -1964 -2376 0 - -2290 -2376 0 - -47 -363 0 - -47 -680 0 - -47 -1296 0 - -47 -1606 0 - -363 -680 0 - -363 -1296 0 - -363 -1606 0 - -680 -1296 0 - -680 -1606 0 - -1296 -1606 0 - -364 -598 0 - -364 -1139 0 - -364 -1184 0 - -364 -1607 0 - -364 -1965 0 - -364 -2149 0 - -364 -2291 0 - -364 -2377 0 - -598 -1139 0 - -598 -1184 0 - -598 -1607 0 - -598 -1965 0 - -598 -2149 0 - -598 -2291 0 - -598 -2377 0 - -1139 -1184 0 - -1139 -1607 0 - -1139 -1965 0 - -1139 -2149 0 - -1139 -2291 0 - -1139 -2377 0 - -1184 -1607 0 - -1184 -1965 0 - -1184 -2149 0 - -1184 -2291 0 - -1184 -2377 0 - -1607 -1965 0 - -1607 -2149 0 - -1607 -2291 0 - -1607 -2377 0 - -1965 -2149 0 - -1965 -2291 0 - -1965 -2377 0 - -2149 -2291 0 - -2149 -2377 0 - -2291 -2377 0 - -48 -967 0 - -48 -1093 0 - -48 -1419 0 - -48 -1841 0 - -48 -2410 0 - -967 -1093 0 - -967 -1419 0 - -967 -1841 0 - -967 -2410 0 - -1093 -1419 0 - -1093 -1841 0 - -1093 -2410 0 - -1419 -1841 0 - -1419 -2410 0 - -1841 -2410 0 - -599 -681 0 - -599 -861 0 - -599 -968 0 - -599 -1094 0 - -599 -1297 0 - -599 -1420 0 - -599 -1966 0 - -599 -2150 0 - -599 -2378 0 - -681 -861 0 - -681 -968 0 - -681 -1094 0 - -681 -1297 0 - -681 -1420 0 - -681 -1966 0 - -681 -2150 0 - -681 -2378 0 - -861 -968 0 - -861 -1094 0 - -861 -1297 0 - -861 -1420 0 - -861 -1966 0 - -861 -2150 0 - -861 -2378 0 - -968 -1094 0 - -968 -1297 0 - -968 -1420 0 - -968 -1966 0 - -968 -2150 0 - -968 -2378 0 - -1094 -1297 0 - -1094 -1420 0 - -1094 -1966 0 - -1094 -2150 0 - -1094 -2378 0 - -1297 -1420 0 - -1297 -1966 0 - -1297 -2150 0 - -1297 -2378 0 - -1420 -1966 0 - -1420 -2150 0 - -1420 -2378 0 - -1966 -2150 0 - -1966 -2378 0 - -2150 -2378 0 - -365 -682 0 - -365 -862 0 - -365 -1095 0 - -365 -1298 0 - -365 -1608 0 - -365 -1696 0 - -365 -1967 0 - -365 -2292 0 - -365 -2379 0 - -682 -862 0 - -682 -1095 0 - -682 -1298 0 - -682 -1608 0 - -682 -1696 0 - -682 -1967 0 - -682 -2292 0 - -682 -2379 0 - -862 -1095 0 - -862 -1298 0 - -862 -1608 0 - -862 -1696 0 - -862 -1967 0 - -862 -2292 0 - -862 -2379 0 - -1095 -1298 0 - -1095 -1608 0 - -1095 -1696 0 - -1095 -1967 0 - -1095 -2292 0 - -1095 -2379 0 - -1298 -1608 0 - -1298 -1696 0 - -1298 -1967 0 - -1298 -2292 0 - -1298 -2379 0 - -1608 -1696 0 - -1608 -1967 0 - -1608 -2292 0 - -1608 -2379 0 - -1696 -1967 0 - -1696 -2292 0 - -1696 -2379 0 - -1967 -2292 0 - -1967 -2379 0 - -2292 -2379 0 - -366 -1299 0 - -366 -1421 0 - -366 -1609 0 - -366 -2151 0 - -366 -2293 0 - -1299 -1421 0 - -1299 -1609 0 - -1299 -2151 0 - -1299 -2293 0 - -1421 -1609 0 - -1421 -2151 0 - -1421 -2293 0 - -1609 -2151 0 - -1609 -2293 0 - -2151 -2293 0 - -683 -969 0 - -683 -1096 0 - -683 -1422 0 - -683 -1697 0 - -683 -1968 0 - -683 -2152 0 - -683 -2294 0 - -969 -1096 0 - -969 -1422 0 - -969 -1697 0 - -969 -1968 0 - -969 -2152 0 - -969 -2294 0 - -1096 -1422 0 - -1096 -1697 0 - -1096 -1968 0 - -1096 -2152 0 - -1096 -2294 0 - -1422 -1697 0 - -1422 -1968 0 - -1422 -2152 0 - -1422 -2294 0 - -1697 -1968 0 - -1697 -2152 0 - -1697 -2294 0 - -1968 -2152 0 - -1968 -2294 0 - -2152 -2294 0 - -49 -863 0 - -49 -1097 0 - -49 -1140 0 - -49 -1423 0 - -49 -1663 0 - -49 -1842 0 - -863 -1097 0 - -863 -1140 0 - -863 -1423 0 - -863 -1663 0 - -863 -1842 0 - -1097 -1140 0 - -1097 -1423 0 - -1097 -1663 0 - -1097 -1842 0 - -1140 -1423 0 - -1140 -1663 0 - -1140 -1842 0 - -1423 -1663 0 - -1423 -1842 0 - -1663 -1842 0 - -864 -970 0 - -864 -1098 0 - -864 -1185 0 - -864 -1698 0 - -864 -2295 0 - -970 -1098 0 - -970 -1185 0 - -970 -1698 0 - -970 -2295 0 - -1098 -1185 0 - -1098 -1698 0 - -1098 -2295 0 - -1185 -1698 0 - -1185 -2295 0 - -1698 -2295 0 - -600 -684 0 - -600 -865 0 - -600 -1141 0 - -600 -1186 0 - -600 -1699 0 - -600 -2153 0 - -600 -2296 0 - -684 -865 0 - -684 -1141 0 - -684 -1186 0 - -684 -1699 0 - -684 -2153 0 - -684 -2296 0 - -865 -1141 0 - -865 -1186 0 - -865 -1699 0 - -865 -2153 0 - -865 -2296 0 - -1141 -1186 0 - -1141 -1699 0 - -1141 -2153 0 - -1141 -2296 0 - -1186 -1699 0 - -1186 -2153 0 - -1186 -2296 0 - -1699 -2153 0 - -1699 -2296 0 - -2153 -2296 0 - -367 -601 0 - -367 -971 0 - -367 -1187 0 - -367 -1424 0 - -367 -1610 0 - -367 -1969 0 - -367 -2297 0 - -601 -971 0 - -601 -1187 0 - -601 -1424 0 - -601 -1610 0 - -601 -1969 0 - -601 -2297 0 - -971 -1187 0 - -971 -1424 0 - -971 -1610 0 - -971 -1969 0 - -971 -2297 0 - -1187 -1424 0 - -1187 -1610 0 - -1187 -1969 0 - -1187 -2297 0 - -1424 -1610 0 - -1424 -1969 0 - -1424 -2297 0 - -1610 -1969 0 - -1610 -2297 0 - -1969 -2297 0 - -50 -368 0 - -50 -685 0 - -50 -866 0 - -50 -972 0 - -50 -1099 0 - -50 -1300 0 - -50 -1425 0 - -50 -1611 0 - -50 -1664 0 - -368 -685 0 - -368 -866 0 - -368 -972 0 - -368 -1099 0 - -368 -1300 0 - -368 -1425 0 - -368 -1611 0 - -368 -1664 0 - -685 -866 0 - -685 -972 0 - -685 -1099 0 - -685 -1300 0 - -685 -1425 0 - -685 -1611 0 - -685 -1664 0 - -866 -972 0 - -866 -1099 0 - -866 -1300 0 - -866 -1425 0 - -866 -1611 0 - -866 -1664 0 - -972 -1099 0 - -972 -1300 0 - -972 -1425 0 - -972 -1611 0 - -972 -1664 0 - -1099 -1300 0 - -1099 -1425 0 - -1099 -1611 0 - -1099 -1664 0 - -1300 -1425 0 - -1300 -1611 0 - -1300 -1664 0 - -1425 -1611 0 - -1425 -1664 0 - -1611 -1664 0 - -369 -973 0 - -369 -1100 0 - -369 -1301 0 - -369 -1970 0 - -369 -2298 0 - -369 -2411 0 - -973 -1100 0 - -973 -1301 0 - -973 -1970 0 - -973 -2298 0 - -973 -2411 0 - -1100 -1301 0 - -1100 -1970 0 - -1100 -2298 0 - -1100 -2411 0 - -1301 -1970 0 - -1301 -2298 0 - -1301 -2411 0 - -1970 -2298 0 - -1970 -2411 0 - -2298 -2411 0 - -51 -370 0 - -51 -1142 0 - -51 -2154 0 - -370 -1142 0 - -370 -2154 0 - -1142 -2154 0 - -602 -686 0 - -602 -974 0 - -602 -1101 0 - -602 -1302 0 - -602 -1612 0 - -602 -1700 0 - -602 -1843 0 - -602 -2299 0 - -602 -2380 0 - -686 -974 0 - -686 -1101 0 - -686 -1302 0 - -686 -1612 0 - -686 -1700 0 - -686 -1843 0 - -686 -2299 0 - -686 -2380 0 - -974 -1101 0 - -974 -1302 0 - -974 -1612 0 - -974 -1700 0 - -974 -1843 0 - -974 -2299 0 - -974 -2380 0 - -1101 -1302 0 - -1101 -1612 0 - -1101 -1700 0 - -1101 -1843 0 - -1101 -2299 0 - -1101 -2380 0 - -1302 -1612 0 - -1302 -1700 0 - -1302 -1843 0 - -1302 -2299 0 - -1302 -2380 0 - -1612 -1700 0 - -1612 -1843 0 - -1612 -2299 0 - -1612 -2380 0 - -1700 -1843 0 - -1700 -2299 0 - -1700 -2380 0 - -1843 -2299 0 - -1843 -2380 0 - -2299 -2380 0 - -371 -1102 0 - -371 -1426 0 - -371 -2155 0 - -371 -2300 0 - -1102 -1426 0 - -1102 -2155 0 - -1102 -2300 0 - -1426 -2155 0 - -1426 -2300 0 - -2155 -2300 0 - -372 -687 0 - -372 -867 0 - -372 -975 0 - -372 -1143 0 - -372 -1188 0 - -372 -1613 0 - -372 -1971 0 - -372 -2412 0 - -687 -867 0 - -687 -975 0 - -687 -1143 0 - -687 -1188 0 - -687 -1613 0 - -687 -1971 0 - -687 -2412 0 - -867 -975 0 - -867 -1143 0 - -867 -1188 0 - -867 -1613 0 - -867 -1971 0 - -867 -2412 0 - -975 -1143 0 - -975 -1188 0 - -975 -1613 0 - -975 -1971 0 - -975 -2412 0 - -1143 -1188 0 - -1143 -1613 0 - -1143 -1971 0 - -1143 -2412 0 - -1188 -1613 0 - -1188 -1971 0 - -1188 -2412 0 - -1613 -1971 0 - -1613 -2412 0 - -1971 -2412 0 - -688 -868 0 - -688 -1189 0 - -688 -1427 0 - -688 -1665 0 - -688 -1844 0 - -688 -1972 0 - -688 -2156 0 - -688 -2301 0 - -688 -2381 0 - -688 -2413 0 - -868 -1189 0 - -868 -1427 0 - -868 -1665 0 - -868 -1844 0 - -868 -1972 0 - -868 -2156 0 - -868 -2301 0 - -868 -2381 0 - -868 -2413 0 - -1189 -1427 0 - -1189 -1665 0 - -1189 -1844 0 - -1189 -1972 0 - -1189 -2156 0 - -1189 -2301 0 - -1189 -2381 0 - -1189 -2413 0 - -1427 -1665 0 - -1427 -1844 0 - -1427 -1972 0 - -1427 -2156 0 - -1427 -2301 0 - -1427 -2381 0 - -1427 -2413 0 - -1665 -1844 0 - -1665 -1972 0 - -1665 -2156 0 - -1665 -2301 0 - -1665 -2381 0 - -1665 -2413 0 - -1844 -1972 0 - -1844 -2156 0 - -1844 -2301 0 - -1844 -2381 0 - -1844 -2413 0 - -1972 -2156 0 - -1972 -2301 0 - -1972 -2381 0 - -1972 -2413 0 - -2156 -2301 0 - -2156 -2381 0 - -2156 -2413 0 - -2301 -2381 0 - -2301 -2413 0 - -2381 -2413 0 - -274 -500 0 - -274 -1303 0 - -274 -1790 0 - -274 -1845 0 - -274 -2302 0 - -500 -1303 0 - -500 -1790 0 - -500 -1845 0 - -500 -2302 0 - -1303 -1790 0 - -1303 -1845 0 - -1303 -2302 0 - -1790 -1845 0 - -1790 -2302 0 - -1845 -2302 0 - -275 -501 0 - -275 -1190 0 - -275 -1304 0 - -275 -1973 0 - -275 -2303 0 - -501 -1190 0 - -501 -1304 0 - -501 -1973 0 - -501 -2303 0 - -1190 -1304 0 - -1190 -1973 0 - -1190 -2303 0 - -1304 -1973 0 - -1304 -2303 0 - -1973 -2303 0 - -162 -199 0 - -162 -276 0 - -162 -373 0 - -162 -502 0 - -162 -1305 0 - -162 -1791 0 - -162 -2585 0 - -199 -276 0 - -199 -373 0 - -199 -502 0 - -199 -1305 0 - -199 -1791 0 - -199 -2585 0 - -276 -373 0 - -276 -502 0 - -276 -1305 0 - -276 -1791 0 - -276 -2585 0 - -373 -502 0 - -373 -1305 0 - -373 -1791 0 - -373 -2585 0 - -502 -1305 0 - -502 -1791 0 - -502 -2585 0 - -1305 -1791 0 - -1305 -2585 0 - -1791 -2585 0 - -163 -200 0 - -163 -503 0 - -163 -1306 0 - -163 -1792 0 - -163 -1974 0 - -200 -503 0 - -200 -1306 0 - -200 -1792 0 - -200 -1974 0 - -503 -1306 0 - -503 -1792 0 - -503 -1974 0 - -1306 -1792 0 - -1306 -1974 0 - -1792 -1974 0 - -201 -374 0 - -201 -504 0 - -201 -1307 0 - -201 -1793 0 - -201 -2304 0 - -374 -504 0 - -374 -1307 0 - -374 -1793 0 - -374 -2304 0 - -504 -1307 0 - -504 -1793 0 - -504 -2304 0 - -1307 -1793 0 - -1307 -2304 0 - -1793 -2304 0 - -164 -375 0 - -164 -1308 0 - -164 -1794 0 - -164 -1846 0 - -164 -1975 0 - -375 -1308 0 - -375 -1794 0 - -375 -1846 0 - -375 -1975 0 - -1308 -1794 0 - -1308 -1846 0 - -1308 -1975 0 - -1794 -1846 0 - -1794 -1975 0 - -1846 -1975 0 - -202 -277 0 - -202 -376 0 - -202 -505 0 - -202 -1309 0 - -202 -1666 0 - -202 -1976 0 - -202 -2094 0 - -202 -2305 0 - -202 -2586 0 - -277 -376 0 - -277 -505 0 - -277 -1309 0 - -277 -1666 0 - -277 -1976 0 - -277 -2094 0 - -277 -2305 0 - -277 -2586 0 - -376 -505 0 - -376 -1309 0 - -376 -1666 0 - -376 -1976 0 - -376 -2094 0 - -376 -2305 0 - -376 -2586 0 - -505 -1309 0 - -505 -1666 0 - -505 -1976 0 - -505 -2094 0 - -505 -2305 0 - -505 -2586 0 - -1309 -1666 0 - -1309 -1976 0 - -1309 -2094 0 - -1309 -2305 0 - -1309 -2586 0 - -1666 -1976 0 - -1666 -2094 0 - -1666 -2305 0 - -1666 -2586 0 - -1976 -2094 0 - -1976 -2305 0 - -1976 -2586 0 - -2094 -2305 0 - -2094 -2586 0 - -2305 -2586 0 - -377 -1191 0 - -377 -1667 0 - -377 -1795 0 - -377 -1847 0 - -377 -2306 0 - -1191 -1667 0 - -1191 -1795 0 - -1191 -1847 0 - -1191 -2306 0 - -1667 -1795 0 - -1667 -1847 0 - -1667 -2306 0 - -1795 -1847 0 - -1795 -2306 0 - -1847 -2306 0 - -203 -506 0 - -203 -1977 0 - -203 -2307 0 - -506 -1977 0 - -506 -2307 0 - -1977 -2307 0 - -165 -278 0 - -165 -378 0 - -165 -1310 0 - -165 -1668 0 - -278 -378 0 - -278 -1310 0 - -278 -1668 0 - -378 -1310 0 - -378 -1668 0 - -1310 -1668 0 - -1311 -1796 0 - -1311 -1848 0 - -1311 -2587 0 - -1796 -1848 0 - -1796 -2587 0 - -1848 -2587 0 - -204 -507 0 - -205 -279 0 - -205 -379 0 - -205 -508 0 - -205 -1312 0 - -205 -1797 0 - -205 -1849 0 - -205 -1978 0 - -279 -379 0 - -279 -508 0 - -279 -1312 0 - -279 -1797 0 - -279 -1849 0 - -279 -1978 0 - -379 -508 0 - -379 -1312 0 - -379 -1797 0 - -379 -1849 0 - -379 -1978 0 - -508 -1312 0 - -508 -1797 0 - -508 -1849 0 - -508 -1978 0 - -1312 -1797 0 - -1312 -1849 0 - -1312 -1978 0 - -1797 -1849 0 - -1797 -1978 0 - -1849 -1978 0 - -280 -380 0 - -280 -509 0 - -280 -1192 0 - -280 -1979 0 - -280 -2095 0 - -380 -509 0 - -380 -1192 0 - -380 -1979 0 - -380 -2095 0 - -509 -1192 0 - -509 -1979 0 - -509 -2095 0 - -1192 -1979 0 - -1192 -2095 0 - -1979 -2095 0 - -281 -510 0 - -281 -765 0 - -281 -1313 0 - -281 -1798 0 - -281 -2588 0 - -510 -765 0 - -510 -1313 0 - -510 -1798 0 - -510 -2588 0 - -765 -1313 0 - -765 -1798 0 - -765 -2588 0 - -1313 -1798 0 - -1313 -2588 0 - -1798 -2588 0 - -282 -766 0 - -282 -1103 0 - -282 -1799 0 - -282 -1850 0 - -282 -2589 0 - -766 -1103 0 - -766 -1799 0 - -766 -1850 0 - -766 -2589 0 - -1103 -1799 0 - -1103 -1850 0 - -1103 -2589 0 - -1799 -1850 0 - -1799 -2589 0 - -1850 -2589 0 - -511 -1104 0 - -511 -1314 0 - -511 -1800 0 - -511 -2157 0 - -1104 -1314 0 - -1104 -1800 0 - -1104 -2157 0 - -1314 -1800 0 - -1314 -2157 0 - -1800 -2157 0 - -1105 -1801 0 - -1105 -1851 0 - -1105 -2158 0 - -1801 -1851 0 - -1801 -2158 0 - -1851 -2158 0 - -283 -512 0 - -283 -1144 0 - -283 -2159 0 - -283 -2521 0 - -512 -1144 0 - -512 -2159 0 - -512 -2521 0 - -1144 -2159 0 - -1144 -2521 0 - -2159 -2521 0 - -2522 -2590 0 - -284 -767 0 - -284 -1106 0 - -284 -1315 0 - -767 -1106 0 - -767 -1315 0 - -1106 -1315 0 - -1107 -1316 0 - -1107 -1802 0 - -1107 -2591 0 - -1316 -1802 0 - -1316 -2591 0 - -1802 -2591 0 - -1108 -1317 0 - -1108 -2160 0 - -1108 -2523 0 - -1317 -2160 0 - -1317 -2523 0 - -2160 -2523 0 - -768 -1318 0 - -768 -2524 0 - -768 -2592 0 - -1318 -2524 0 - -1318 -2592 0 - -2524 -2592 0 - -513 -769 0 - -513 -1145 0 - -513 -2161 0 - -769 -1145 0 - -769 -2161 0 - -1145 -2161 0 - -52 -514 0 - -52 -689 0 - -52 -770 0 - -514 -689 0 - -514 -770 0 - -689 -770 0 - -515 -771 0 - -515 -2036 0 - -515 -2162 0 - -771 -2036 0 - -771 -2162 0 - -2036 -2162 0 - -516 -690 0 - -516 -772 0 - -516 -1486 0 - -690 -772 0 - -690 -1486 0 - -772 -1486 0 - -517 -773 0 - -517 -2037 0 - -517 -2163 0 - -773 -2037 0 - -773 -2163 0 - -2037 -2163 0 - -518 -691 0 - -518 -774 0 - -518 -976 0 - -518 -1487 0 - -518 -2164 0 - -691 -774 0 - -691 -976 0 - -691 -1487 0 - -691 -2164 0 - -774 -976 0 - -774 -1487 0 - -774 -2164 0 - -976 -1487 0 - -976 -2164 0 - -1487 -2164 0 - -53 -1669 0 - -53 -2038 0 - -1669 -2038 0 - -54 -90 0 - -54 -519 0 - -54 -977 0 - -54 -1488 0 - -54 -2039 0 - -90 -519 0 - -90 -977 0 - -90 -1488 0 - -90 -2039 0 - -519 -977 0 - -519 -1488 0 - -519 -2039 0 - -977 -1488 0 - -977 -2039 0 - -1488 -2039 0 - -520 -692 0 - -520 -2165 0 - -692 -2165 0 - -91 -521 0 - -91 -693 0 - -91 -1670 0 - -91 -2166 0 - -521 -693 0 - -521 -1670 0 - -521 -2166 0 - -693 -1670 0 - -693 -2166 0 - -1670 -2166 0 - -55 -92 0 - -55 -1671 0 - -55 -2167 0 - -92 -1671 0 - -92 -2167 0 - -1671 -2167 0 - -285 -522 0 - -285 -694 0 - -285 -869 0 - -285 -1146 0 - -285 -1428 0 - -285 -1489 0 - -285 -2308 0 - -522 -694 0 - -522 -869 0 - -522 -1146 0 - -522 -1428 0 - -522 -1489 0 - -522 -2308 0 - -694 -869 0 - -694 -1146 0 - -694 -1428 0 - -694 -1489 0 - -694 -2308 0 - -869 -1146 0 - -869 -1428 0 - -869 -1489 0 - -869 -2308 0 - -1146 -1428 0 - -1146 -1489 0 - -1146 -2308 0 - -1428 -1489 0 - -1428 -2308 0 - -1489 -2308 0 - -523 -1147 0 - -523 -1490 0 - -523 -2525 0 - -523 -2593 0 - -1147 -1490 0 - -1147 -2525 0 - -1147 -2593 0 - -1490 -2525 0 - -1490 -2593 0 - -2525 -2593 0 - -524 -1148 0 - -524 -2168 0 - -524 -2309 0 - -524 -2526 0 - -1148 -2168 0 - -1148 -2309 0 - -1148 -2526 0 - -2168 -2309 0 - -2168 -2526 0 - -2309 -2526 0 - -525 -695 0 - -525 -870 0 - -525 -978 0 - -525 -1109 0 - -525 -1429 0 - -525 -1491 0 - -525 -2169 0 - -695 -870 0 - -695 -978 0 - -695 -1109 0 - -695 -1429 0 - -695 -1491 0 - -695 -2169 0 - -870 -978 0 - -870 -1109 0 - -870 -1429 0 - -870 -1491 0 - -870 -2169 0 - -978 -1109 0 - -978 -1429 0 - -978 -1491 0 - -978 -2169 0 - -1109 -1429 0 - -1109 -1491 0 - -1109 -2169 0 - -1429 -1491 0 - -1429 -2169 0 - -1491 -2169 0 - -526 -696 0 - -526 -979 0 - -526 -1110 0 - -526 -1430 0 - -526 -1492 0 - -526 -2170 0 - -526 -2310 0 - -526 -2527 0 - -696 -979 0 - -696 -1110 0 - -696 -1430 0 - -696 -1492 0 - -696 -2170 0 - -696 -2310 0 - -696 -2527 0 - -979 -1110 0 - -979 -1430 0 - -979 -1492 0 - -979 -2170 0 - -979 -2310 0 - -979 -2527 0 - -1110 -1430 0 - -1110 -1492 0 - -1110 -2170 0 - -1110 -2310 0 - -1110 -2527 0 - -1430 -1492 0 - -1430 -2170 0 - -1430 -2310 0 - -1430 -2527 0 - -1492 -2170 0 - -1492 -2310 0 - -1492 -2527 0 - -2170 -2310 0 - -2170 -2527 0 - -2310 -2527 0 - -286 -527 0 - -286 -1111 0 - -286 -1149 0 - -286 -2171 0 - -286 -2311 0 - -286 -2594 0 - -527 -1111 0 - -527 -1149 0 - -527 -2171 0 - -527 -2311 0 - -527 -2594 0 - -1111 -1149 0 - -1111 -2171 0 - -1111 -2311 0 - -1111 -2594 0 - -1149 -2171 0 - -1149 -2311 0 - -1149 -2594 0 - -2171 -2311 0 - -2171 -2594 0 - -2311 -2594 0 - -528 -871 0 - -528 -980 0 - -528 -1112 0 - -528 -1431 0 - -528 -1493 0 - -528 -2312 0 - -871 -980 0 - -871 -1112 0 - -871 -1431 0 - -871 -1493 0 - -871 -2312 0 - -980 -1112 0 - -980 -1431 0 - -980 -1493 0 - -980 -2312 0 - -1112 -1431 0 - -1112 -1493 0 - -1112 -2312 0 - -1431 -1493 0 - -1431 -2312 0 - -1493 -2312 0 - -287 -529 0 - -287 -697 0 - -287 -872 0 - -287 -1150 0 - -287 -2172 0 - -287 -2313 0 - -287 -2528 0 - -529 -697 0 - -529 -872 0 - -529 -1150 0 - -529 -2172 0 - -529 -2313 0 - -529 -2528 0 - -697 -872 0 - -697 -1150 0 - -697 -2172 0 - -697 -2313 0 - -697 -2528 0 - -872 -1150 0 - -872 -2172 0 - -872 -2313 0 - -872 -2528 0 - -1150 -2172 0 - -1150 -2313 0 - -1150 -2528 0 - -2172 -2313 0 - -2172 -2528 0 - -2313 -2528 0 - -1494 -2173 0 - -1494 -2595 0 - -2173 -2595 0 - -981 -1113 0 - -981 -1495 0 - -981 -2314 0 - -981 -2596 0 - -1113 -1495 0 - -1113 -2314 0 - -1113 -2596 0 - -1495 -2314 0 - -1495 -2596 0 - -2314 -2596 0 - -982 -1114 0 - -982 -1432 0 - -982 -2174 0 - -982 -2315 0 - -982 -2529 0 - -1114 -1432 0 - -1114 -2174 0 - -1114 -2315 0 - -1114 -2529 0 - -1432 -2174 0 - -1432 -2315 0 - -1432 -2529 0 - -2174 -2315 0 - -2174 -2529 0 - -2315 -2529 0 - -530 -698 0 - -530 -983 0 - -530 -1115 0 - -530 -1496 0 - -530 -2316 0 - -530 -2530 0 - -698 -983 0 - -698 -1115 0 - -698 -1496 0 - -698 -2316 0 - -698 -2530 0 - -983 -1115 0 - -983 -1496 0 - -983 -2316 0 - -983 -2530 0 - -1115 -1496 0 - -1115 -2316 0 - -1115 -2530 0 - -1496 -2316 0 - -1496 -2530 0 - -2316 -2530 0 - -288 -1116 0 - -288 -1433 0 - -288 -2175 0 - -288 -2317 0 - -288 -2597 0 - -1116 -1433 0 - -1116 -2175 0 - -1116 -2317 0 - -1116 -2597 0 - -1433 -2175 0 - -1433 -2317 0 - -1433 -2597 0 - -2175 -2317 0 - -2175 -2597 0 - -2317 -2597 0 - -206 -531 0 - -206 -1151 0 - -206 -1497 0 - -206 -1614 0 - -206 -2531 0 - -531 -1151 0 - -531 -1497 0 - -531 -1614 0 - -531 -2531 0 - -1151 -1497 0 - -1151 -1614 0 - -1151 -2531 0 - -1497 -1614 0 - -1497 -2531 0 - -1614 -2531 0 - -532 -1152 0 - -532 -1615 0 - -532 -1803 0 - -532 -2318 0 - -532 -2532 0 - -1152 -1615 0 - -1152 -1803 0 - -1152 -2318 0 - -1152 -2532 0 - -1615 -1803 0 - -1615 -2318 0 - -1615 -2532 0 - -1803 -2318 0 - -1803 -2532 0 - -2318 -2532 0 - -533 -699 0 - -533 -1498 0 - -533 -1804 0 - -533 -2319 0 - -533 -2533 0 - -699 -1498 0 - -699 -1804 0 - -699 -2319 0 - -699 -2533 0 - -1498 -1804 0 - -1498 -2319 0 - -1498 -2533 0 - -1804 -2319 0 - -1804 -2533 0 - -2319 -2533 0 - -207 -534 0 - -207 -1153 0 - -207 -2320 0 - -534 -1153 0 - -534 -2320 0 - -1153 -2320 0 - -208 -1499 0 - -208 -2321 0 - -1499 -2321 0 - -209 -535 0 - -209 -700 0 - -209 -1154 0 - -209 -2322 0 - -209 -2534 0 - -535 -700 0 - -535 -1154 0 - -535 -2322 0 - -535 -2534 0 - -700 -1154 0 - -700 -2322 0 - -700 -2534 0 - -1154 -2322 0 - -1154 -2534 0 - -2322 -2534 0 - -1500 -1805 0 - -1500 -2323 0 - -1805 -2323 0 - -701 -1616 0 - -701 -2535 0 - -1616 -2535 0 - -536 -702 0 - -536 -1501 0 - -536 -1617 0 - -536 -1806 0 - -536 -2324 0 - -536 -2536 0 - -702 -1501 0 - -702 -1617 0 - -702 -1806 0 - -702 -2324 0 - -702 -2536 0 - -1501 -1617 0 - -1501 -1806 0 - -1501 -2324 0 - -1501 -2536 0 - -1617 -1806 0 - -1617 -2324 0 - -1617 -2536 0 - -1806 -2324 0 - -1806 -2536 0 - -2324 -2536 0 - -289 -537 0 - -289 -603 0 - -289 -1319 0 - -289 -1434 0 - -289 -1502 0 - -289 -1876 0 - -289 -2325 0 - -289 -2414 0 - -537 -603 0 - -537 -1319 0 - -537 -1434 0 - -537 -1502 0 - -537 -1876 0 - -537 -2325 0 - -537 -2414 0 - -603 -1319 0 - -603 -1434 0 - -603 -1502 0 - -603 -1876 0 - -603 -2325 0 - -603 -2414 0 - -1319 -1434 0 - -1319 -1502 0 - -1319 -1876 0 - -1319 -2325 0 - -1319 -2414 0 - -1434 -1502 0 - -1434 -1876 0 - -1434 -2325 0 - -1434 -2414 0 - -1502 -1876 0 - -1502 -2325 0 - -1502 -2414 0 - -1876 -2325 0 - -1876 -2414 0 - -2325 -2414 0 - -290 -538 0 - -290 -1320 0 - -290 -1435 0 - -290 -1503 0 - -290 -1618 0 - -290 -2326 0 - -538 -1320 0 - -538 -1435 0 - -538 -1503 0 - -538 -1618 0 - -538 -2326 0 - -1320 -1435 0 - -1320 -1503 0 - -1320 -1618 0 - -1320 -2326 0 - -1435 -1503 0 - -1435 -1618 0 - -1435 -2326 0 - -1503 -1618 0 - -1503 -2326 0 - -1618 -2326 0 - -210 -539 0 - -210 -1321 0 - -210 -1504 0 - -210 -1619 0 - -210 -1877 0 - -210 -2096 0 - -210 -2598 0 - -539 -1321 0 - -539 -1504 0 - -539 -1619 0 - -539 -1877 0 - -539 -2096 0 - -539 -2598 0 - -1321 -1504 0 - -1321 -1619 0 - -1321 -1877 0 - -1321 -2096 0 - -1321 -2598 0 - -1504 -1619 0 - -1504 -1877 0 - -1504 -2096 0 - -1504 -2598 0 - -1619 -1877 0 - -1619 -2096 0 - -1619 -2598 0 - -1877 -2096 0 - -1877 -2598 0 - -2096 -2598 0 - -381 -540 0 - -381 -1322 0 - -381 -1505 0 - -381 -1620 0 - -381 -2327 0 - -540 -1322 0 - -540 -1505 0 - -540 -1620 0 - -540 -2327 0 - -1322 -1505 0 - -1322 -1620 0 - -1322 -2327 0 - -1505 -1620 0 - -1505 -2327 0 - -1620 -2327 0 - -541 -1436 0 - -541 -1506 0 - -541 -2176 0 - -541 -2328 0 - -1436 -1506 0 - -1436 -2176 0 - -1436 -2328 0 - -1506 -2176 0 - -1506 -2328 0 - -2176 -2328 0 - -211 -291 0 - -211 -382 0 - -211 -542 0 - -211 -1323 0 - -211 -2097 0 - -211 -2177 0 - -211 -2329 0 - -211 -2599 0 - -291 -382 0 - -291 -542 0 - -291 -1323 0 - -291 -2097 0 - -291 -2177 0 - -291 -2329 0 - -291 -2599 0 - -382 -542 0 - -382 -1323 0 - -382 -2097 0 - -382 -2177 0 - -382 -2329 0 - -382 -2599 0 - -542 -1323 0 - -542 -2097 0 - -542 -2177 0 - -542 -2329 0 - -542 -2599 0 - -1323 -2097 0 - -1323 -2177 0 - -1323 -2329 0 - -1323 -2599 0 - -2097 -2177 0 - -2097 -2329 0 - -2097 -2599 0 - -2177 -2329 0 - -2177 -2599 0 - -2329 -2599 0 - -212 -1507 0 - -212 -2098 0 - -212 -2330 0 - -1507 -2098 0 - -1507 -2330 0 - -2098 -2330 0 - -213 -543 0 - -213 -1437 0 - -213 -1508 0 - -213 -1621 0 - -213 -1878 0 - -213 -2331 0 - -543 -1437 0 - -543 -1508 0 - -543 -1621 0 - -543 -1878 0 - -543 -2331 0 - -1437 -1508 0 - -1437 -1621 0 - -1437 -1878 0 - -1437 -2331 0 - -1508 -1621 0 - -1508 -1878 0 - -1508 -2331 0 - -1621 -1878 0 - -1621 -2331 0 - -1878 -2331 0 - -214 -292 0 - -214 -544 0 - -214 -604 0 - -214 -2099 0 - -214 -2178 0 - -214 -2332 0 - -292 -544 0 - -292 -604 0 - -292 -2099 0 - -292 -2178 0 - -292 -2332 0 - -544 -604 0 - -544 -2099 0 - -544 -2178 0 - -544 -2332 0 - -604 -2099 0 - -604 -2178 0 - -604 -2332 0 - -2099 -2178 0 - -2099 -2332 0 - -2178 -2332 0 - -605 -1324 0 - -605 -1509 0 - -605 -2179 0 - -605 -2415 0 - -605 -2600 0 - -1324 -1509 0 - -1324 -2179 0 - -1324 -2415 0 - -1324 -2600 0 - -1509 -2179 0 - -1509 -2415 0 - -1509 -2600 0 - -2179 -2415 0 - -2179 -2600 0 - -2415 -2600 0 - -606 -1325 0 - -606 -1438 0 - -606 -1622 0 - -606 -1879 0 - -606 -2180 0 - -606 -2333 0 - -1325 -1438 0 - -1325 -1622 0 - -1325 -1879 0 - -1325 -2180 0 - -1325 -2333 0 - -1438 -1622 0 - -1438 -1879 0 - -1438 -2180 0 - -1438 -2333 0 - -1622 -1879 0 - -1622 -2180 0 - -1622 -2333 0 - -1879 -2180 0 - -1879 -2333 0 - -2180 -2333 0 - -215 -545 0 - -215 -607 0 - -215 -1439 0 - -545 -607 0 - -545 -1439 0 - -607 -1439 0 - -216 -293 0 - -216 -383 0 - -216 -546 0 - -216 -608 0 - -216 -1326 0 - -216 -1440 0 - -216 -1880 0 - -293 -383 0 - -293 -546 0 - -293 -608 0 - -293 -1326 0 - -293 -1440 0 - -293 -1880 0 - -383 -546 0 - -383 -608 0 - -383 -1326 0 - -383 -1440 0 - -383 -1880 0 - -546 -608 0 - -546 -1326 0 - -546 -1440 0 - -546 -1880 0 - -608 -1326 0 - -608 -1440 0 - -608 -1880 0 - -1326 -1440 0 - -1326 -1880 0 - -1440 -1880 0 - -294 -384 0 - -294 -547 0 - -294 -1623 0 - -294 -1881 0 - -294 -2100 0 - -294 -2416 0 - -384 -547 0 - -384 -1623 0 - -384 -1881 0 - -384 -2100 0 - -384 -2416 0 - -547 -1623 0 - -547 -1881 0 - -547 -2100 0 - -547 -2416 0 - -1623 -1881 0 - -1623 -2100 0 - -1623 -2416 0 - -1881 -2100 0 - -1881 -2416 0 - -2100 -2416 0 - -548 -1441 0 - -548 -2101 0 - -548 -2181 0 - -548 -2334 0 - -548 -2417 0 - -548 -2601 0 - -1441 -2101 0 - -1441 -2181 0 - -1441 -2334 0 - -1441 -2417 0 - -1441 -2601 0 - -2101 -2181 0 - -2101 -2334 0 - -2101 -2417 0 - -2101 -2601 0 - -2181 -2334 0 - -2181 -2417 0 - -2181 -2601 0 - -2334 -2417 0 - -2334 -2601 0 - -2417 -2601 0 - -56 -873 0 - -56 -1327 0 - -56 -1442 0 - -56 -1510 0 - -56 -1624 0 - -56 -2382 0 - -873 -1327 0 - -873 -1442 0 - -873 -1510 0 - -873 -1624 0 - -873 -2382 0 - -1327 -1442 0 - -1327 -1510 0 - -1327 -1624 0 - -1327 -2382 0 - -1442 -1510 0 - -1442 -1624 0 - -1442 -2382 0 - -1510 -1624 0 - -1510 -2382 0 - -1624 -2382 0 - -385 -609 0 - -385 -1625 0 - -385 -2040 0 - -385 -2383 0 - -609 -1625 0 - -609 -2040 0 - -609 -2383 0 - -1625 -2040 0 - -1625 -2383 0 - -2040 -2383 0 - -93 -166 0 - -93 -217 0 - -93 -610 0 - -93 -874 0 - -93 -984 0 - -93 -1328 0 - -93 -1443 0 - -93 -1511 0 - -93 -2384 0 - -166 -217 0 - -166 -610 0 - -166 -874 0 - -166 -984 0 - -166 -1328 0 - -166 -1443 0 - -166 -1511 0 - -166 -2384 0 - -217 -610 0 - -217 -874 0 - -217 -984 0 - -217 -1328 0 - -217 -1443 0 - -217 -1511 0 - -217 -2384 0 - -610 -874 0 - -610 -984 0 - -610 -1328 0 - -610 -1443 0 - -610 -1511 0 - -610 -2384 0 - -874 -984 0 - -874 -1328 0 - -874 -1443 0 - -874 -1511 0 - -874 -2384 0 - -984 -1328 0 - -984 -1443 0 - -984 -1511 0 - -984 -2384 0 - -1328 -1443 0 - -1328 -1511 0 - -1328 -2384 0 - -1443 -1511 0 - -1443 -2384 0 - -1511 -2384 0 - -218 -386 0 - -218 -1329 0 - -218 -1444 0 - -218 -1626 0 - -218 -2041 0 - -386 -1329 0 - -386 -1444 0 - -386 -1626 0 - -386 -2041 0 - -1329 -1444 0 - -1329 -1626 0 - -1329 -2041 0 - -1444 -1626 0 - -1444 -2041 0 - -1626 -2041 0 - -167 -387 0 - -167 -875 0 - -167 -1330 0 - -167 -1445 0 - -387 -875 0 - -387 -1330 0 - -387 -1445 0 - -875 -1330 0 - -875 -1445 0 - -1330 -1445 0 - -168 -985 0 - -168 -1446 0 - -168 -1512 0 - -985 -1446 0 - -985 -1512 0 - -1446 -1512 0 - -57 -876 0 - -57 -1447 0 - -57 -2042 0 - -57 -2102 0 - -876 -1447 0 - -876 -2042 0 - -876 -2102 0 - -1447 -2042 0 - -1447 -2102 0 - -2042 -2102 0 - -219 -877 0 - -219 -986 0 - -219 -1513 0 - -219 -2103 0 - -877 -986 0 - -877 -1513 0 - -877 -2103 0 - -986 -1513 0 - -986 -2103 0 - -1513 -2103 0 - -58 -169 0 - -58 -388 0 - -58 -878 0 - -58 -987 0 - -58 -1331 0 - -58 -1448 0 - -58 -1627 0 - -169 -388 0 - -169 -878 0 - -169 -987 0 - -169 -1331 0 - -169 -1448 0 - -169 -1627 0 - -388 -878 0 - -388 -987 0 - -388 -1331 0 - -388 -1448 0 - -388 -1627 0 - -878 -987 0 - -878 -1331 0 - -878 -1448 0 - -878 -1627 0 - -987 -1331 0 - -987 -1448 0 - -987 -1627 0 - -1331 -1448 0 - -1331 -1627 0 - -1448 -1627 0 - -94 -170 0 - -94 -389 0 - -94 -988 0 - -94 -1332 0 - -94 -1514 0 - -94 -2043 0 - -170 -389 0 - -170 -988 0 - -170 -1332 0 - -170 -1514 0 - -170 -2043 0 - -389 -988 0 - -389 -1332 0 - -389 -1514 0 - -389 -2043 0 - -988 -1332 0 - -988 -1514 0 - -988 -2043 0 - -1332 -1514 0 - -1332 -2043 0 - -1514 -2043 0 - -95 -220 0 - -95 -390 0 - -95 -611 0 - -95 -879 0 - -95 -1333 0 - -95 -1449 0 - -95 -2044 0 - -95 -2385 0 - -220 -390 0 - -220 -611 0 - -220 -879 0 - -220 -1333 0 - -220 -1449 0 - -220 -2044 0 - -220 -2385 0 - -390 -611 0 - -390 -879 0 - -390 -1333 0 - -390 -1449 0 - -390 -2044 0 - -390 -2385 0 - -611 -879 0 - -611 -1333 0 - -611 -1449 0 - -611 -2044 0 - -611 -2385 0 - -879 -1333 0 - -879 -1449 0 - -879 -2044 0 - -879 -2385 0 - -1333 -1449 0 - -1333 -2044 0 - -1333 -2385 0 - -1449 -2044 0 - -1449 -2385 0 - -2044 -2385 0 - -171 -391 0 - -171 -1450 0 - -171 -2104 0 - -391 -1450 0 - -391 -2104 0 - -1450 -2104 0 - -96 -612 0 - -96 -2045 0 - -612 -2045 0 - -392 -880 0 - -392 -989 0 - -392 -1628 0 - -392 -2046 0 - -392 -2105 0 - -880 -989 0 - -880 -1628 0 - -880 -2046 0 - -880 -2105 0 - -989 -1628 0 - -989 -2046 0 - -989 -2105 0 - -1628 -2046 0 - -1628 -2105 0 - -2046 -2105 0 - -97 -172 0 - -97 -881 0 - -97 -1451 0 - -97 -2106 0 - -97 -2386 0 - -172 -881 0 - -172 -1451 0 - -172 -2106 0 - -172 -2386 0 - -881 -1451 0 - -881 -2106 0 - -881 -2386 0 - -1451 -2106 0 - -1451 -2386 0 - -2106 -2386 0 - -1 -21 0 - -1 -46 0 - -1 -56 0 - -21 -46 0 - -21 -56 0 - -46 -56 0 - -17 -29 0 - -17 -37 0 - -17 -41 0 - -17 -44 0 - -29 -37 0 - -29 -41 0 - -29 -44 0 - -37 -41 0 - -37 -44 0 - -41 -44 0 - -2 -13 0 - -2 -30 0 - -2 -47 0 - -2 -52 0 - -13 -30 0 - -13 -47 0 - -13 -52 0 - -30 -47 0 - -30 -52 0 - -47 -52 0 - -18 -22 0 - -18 -24 0 - -18 -31 0 - -18 -42 0 - -18 -48 0 - -22 -24 0 - -22 -31 0 - -22 -42 0 - -22 -48 0 - -24 -31 0 - -24 -42 0 - -24 -48 0 - -31 -42 0 - -31 -48 0 - -42 -48 0 - -3 -7 0 - -3 -14 0 - -3 -19 0 - -3 -23 0 - -3 -32 0 - -3 -49 0 - -3 -53 0 - -3 -57 0 - -7 -14 0 - -7 -19 0 - -7 -23 0 - -7 -32 0 - -7 -49 0 - -7 -53 0 - -7 -57 0 - -14 -19 0 - -14 -23 0 - -14 -32 0 - -14 -49 0 - -14 -53 0 - -14 -57 0 - -19 -23 0 - -19 -32 0 - -19 -49 0 - -19 -53 0 - -19 -57 0 - -23 -32 0 - -23 -49 0 - -23 -53 0 - -23 -57 0 - -32 -49 0 - -32 -53 0 - -32 -57 0 - -49 -53 0 - -49 -57 0 - -53 -57 0 - -25 -33 0 - -4 -8 0 - -4 -45 0 - -4 -54 0 - -8 -45 0 - -8 -54 0 - -45 -54 0 - -15 -26 0 - -15 -34 0 - -15 -38 0 - -15 -50 0 - -15 -58 0 - -26 -34 0 - -26 -38 0 - -26 -50 0 - -26 -58 0 - -34 -38 0 - -34 -50 0 - -34 -58 0 - -38 -50 0 - -38 -58 0 - -50 -58 0 - -35 -39 0 - -5 -9 0 - -5 -20 0 - -5 -27 0 - -5 -36 0 - -9 -20 0 - -9 -27 0 - -9 -36 0 - -20 -27 0 - -20 -36 0 - -27 -36 0 - -6 -10 0 - -6 -28 0 - -6 -43 0 - -10 -28 0 - -10 -43 0 - -28 -43 0 - -11 -16 0 - -11 -40 0 - -11 -51 0 - -16 -40 0 - -16 -51 0 - -40 -51 0 - -12 -55 0 - -59 -69 0 - -59 -80 0 - -59 -93 0 - -69 -80 0 - -69 -93 0 - -80 -93 0 - -60 -64 0 - -60 -87 0 - -60 -90 0 - -64 -87 0 - -64 -90 0 - -87 -90 0 - -75 -81 0 - -75 -94 0 - -81 -94 0 - -61 -70 0 - -61 -76 0 - -61 -82 0 - -61 -88 0 - -70 -76 0 - -70 -82 0 - -70 -88 0 - -76 -82 0 - -76 -88 0 - -82 -88 0 - -62 -71 0 - -62 -77 0 - -62 -78 0 - -62 -83 0 - -71 -77 0 - -71 -78 0 - -71 -83 0 - -77 -78 0 - -77 -83 0 - -78 -83 0 - -63 -65 0 - -63 -72 0 - -63 -79 0 - -63 -84 0 - -63 -95 0 - -65 -72 0 - -65 -79 0 - -65 -84 0 - -65 -95 0 - -72 -79 0 - -72 -84 0 - -72 -95 0 - -79 -84 0 - -79 -95 0 - -84 -95 0 - -66 -73 0 - -66 -85 0 - -66 -96 0 - -73 -85 0 - -73 -96 0 - -85 -96 0 - -67 -74 0 - -67 -86 0 - -67 -89 0 - -67 -91 0 - -67 -97 0 - -74 -86 0 - -74 -89 0 - -74 -91 0 - -74 -97 0 - -86 -89 0 - -86 -91 0 - -86 -97 0 - -89 -91 0 - -89 -97 0 - -91 -97 0 - -68 -92 0 - -119 -132 0 - -119 -149 0 - -119 -155 0 - -132 -149 0 - -132 -155 0 - -149 -155 0 - -103 -107 0 - -103 -112 0 - -103 -126 0 - -103 -162 0 - -107 -112 0 - -107 -126 0 - -107 -162 0 - -112 -126 0 - -112 -162 0 - -126 -162 0 - -113 -127 0 - -113 -133 0 - -113 -137 0 - -113 -142 0 - -113 -150 0 - -113 -156 0 - -113 -163 0 - -113 -166 0 - -127 -133 0 - -127 -137 0 - -127 -142 0 - -127 -150 0 - -127 -156 0 - -127 -163 0 - -127 -166 0 - -133 -137 0 - -133 -142 0 - -133 -150 0 - -133 -156 0 - -133 -163 0 - -133 -166 0 - -137 -142 0 - -137 -150 0 - -137 -156 0 - -137 -163 0 - -137 -166 0 - -142 -150 0 - -142 -156 0 - -142 -163 0 - -142 -166 0 - -150 -156 0 - -150 -163 0 - -150 -166 0 - -156 -163 0 - -156 -166 0 - -163 -166 0 - -98 -104 0 - -98 -128 0 - -98 -143 0 - -98 -153 0 - -98 -157 0 - -104 -128 0 - -104 -143 0 - -104 -153 0 - -104 -157 0 - -128 -143 0 - -128 -153 0 - -128 -157 0 - -143 -153 0 - -143 -157 0 - -153 -157 0 - -108 -114 0 - -108 -138 0 - -108 -158 0 - -108 -164 0 - -108 -167 0 - -114 -138 0 - -114 -158 0 - -114 -164 0 - -114 -167 0 - -138 -158 0 - -138 -164 0 - -138 -167 0 - -158 -164 0 - -158 -167 0 - -164 -167 0 - -109 -120 0 - -109 -129 0 - -109 -144 0 - -109 -168 0 - -120 -129 0 - -120 -144 0 - -120 -168 0 - -129 -144 0 - -129 -168 0 - -144 -168 0 - -105 -110 0 - -105 -151 0 - -105 -159 0 - -110 -151 0 - -110 -159 0 - -151 -159 0 - -111 -115 0 - -111 -130 0 - -111 -145 0 - -111 -160 0 - -115 -130 0 - -115 -145 0 - -115 -160 0 - -130 -145 0 - -130 -160 0 - -145 -160 0 - -99 -116 0 - -99 -134 0 - -99 -139 0 - -99 -165 0 - -99 -169 0 - -116 -134 0 - -116 -139 0 - -116 -165 0 - -116 -169 0 - -134 -139 0 - -134 -165 0 - -134 -169 0 - -139 -165 0 - -139 -169 0 - -165 -169 0 - -121 -135 0 - -121 -146 0 - -121 -170 0 - -135 -146 0 - -135 -170 0 - -146 -170 0 - -100 -122 0 - -100 -147 0 - -100 -152 0 - -122 -147 0 - -122 -152 0 - -147 -152 0 - -117 -123 0 - -117 -140 0 - -117 -154 0 - -123 -140 0 - -123 -154 0 - -140 -154 0 - -101 -118 0 - -101 -124 0 - -101 -136 0 - -101 -171 0 - -118 -124 0 - -118 -136 0 - -118 -171 0 - -124 -136 0 - -124 -171 0 - -136 -171 0 - -102 -106 0 - -102 -125 0 - -102 -131 0 - -102 -141 0 - -102 -148 0 - -102 -161 0 - -102 -172 0 - -106 -125 0 - -106 -131 0 - -106 -141 0 - -106 -148 0 - -106 -161 0 - -106 -172 0 - -125 -131 0 - -125 -141 0 - -125 -148 0 - -125 -161 0 - -125 -172 0 - -131 -141 0 - -131 -148 0 - -131 -161 0 - -131 -172 0 - -141 -148 0 - -141 -161 0 - -141 -172 0 - -148 -161 0 - -148 -172 0 - -161 -172 0 - -173 -179 0 - -173 -185 0 - -173 -192 0 - -173 -206 0 - -173 -210 0 - -179 -185 0 - -179 -192 0 - -179 -206 0 - -179 -210 0 - -185 -192 0 - -185 -206 0 - -185 -210 0 - -192 -206 0 - -192 -210 0 - -206 -210 0 - -176 -199 0 - -193 -196 0 - -193 -200 0 - -193 -217 0 - -196 -200 0 - -196 -217 0 - -200 -217 0 - -177 -180 0 - -177 -186 0 - -177 -201 0 - -177 -218 0 - -180 -186 0 - -180 -201 0 - -180 -218 0 - -186 -201 0 - -186 -218 0 - -201 -218 0 - -181 -187 0 - -181 -197 0 - -181 -202 0 - -181 -207 0 - -181 -211 0 - -187 -197 0 - -187 -202 0 - -187 -207 0 - -187 -211 0 - -197 -202 0 - -197 -207 0 - -197 -211 0 - -202 -207 0 - -202 -211 0 - -207 -211 0 - -174 -188 0 - -174 -208 0 - -174 -212 0 - -174 -219 0 - -188 -208 0 - -188 -212 0 - -188 -219 0 - -208 -212 0 - -208 -219 0 - -212 -219 0 - -182 -189 0 - -182 -194 0 - -182 -203 0 - -182 -213 0 - -189 -194 0 - -189 -203 0 - -189 -213 0 - -194 -203 0 - -194 -213 0 - -203 -213 0 - -178 -183 0 - -178 -190 0 - -178 -195 0 - -178 -198 0 - -178 -209 0 - -178 -214 0 - -183 -190 0 - -183 -195 0 - -183 -198 0 - -183 -209 0 - -183 -214 0 - -190 -195 0 - -190 -198 0 - -190 -209 0 - -190 -214 0 - -195 -198 0 - -195 -209 0 - -195 -214 0 - -198 -209 0 - -198 -214 0 - -209 -214 0 - -204 -215 0 - -175 -184 0 - -175 -191 0 - -175 -205 0 - -175 -216 0 - -175 -220 0 - -184 -191 0 - -184 -205 0 - -184 -216 0 - -184 -220 0 - -191 -205 0 - -191 -216 0 - -191 -220 0 - -205 -216 0 - -205 -220 0 - -216 -220 0 - -221 -233 0 - -221 -249 0 - -221 -257 0 - -221 -261 0 - -221 -269 0 - -221 -274 0 - -221 -289 0 - -233 -249 0 - -233 -257 0 - -233 -261 0 - -233 -269 0 - -233 -274 0 - -233 -289 0 - -249 -257 0 - -249 -261 0 - -249 -269 0 - -249 -274 0 - -249 -289 0 - -257 -261 0 - -257 -269 0 - -257 -274 0 - -257 -289 0 - -261 -269 0 - -261 -274 0 - -261 -289 0 - -269 -274 0 - -269 -289 0 - -274 -289 0 - -222 -227 0 - -222 -240 0 - -222 -253 0 - -222 -262 0 - -222 -275 0 - -222 -285 0 - -222 -290 0 - -227 -240 0 - -227 -253 0 - -227 -262 0 - -227 -275 0 - -227 -285 0 - -227 -290 0 - -240 -253 0 - -240 -262 0 - -240 -275 0 - -240 -285 0 - -240 -290 0 - -253 -262 0 - -253 -275 0 - -253 -285 0 - -253 -290 0 - -262 -275 0 - -262 -285 0 - -262 -290 0 - -275 -285 0 - -275 -290 0 - -285 -290 0 - -223 -228 0 - -223 -234 0 - -223 -258 0 - -223 -276 0 - -223 -281 0 - -228 -234 0 - -228 -258 0 - -228 -276 0 - -228 -281 0 - -234 -258 0 - -234 -276 0 - -234 -281 0 - -258 -276 0 - -258 -281 0 - -276 -281 0 - -241 -254 0 - -241 -263 0 - -241 -270 0 - -241 -282 0 - -254 -263 0 - -254 -270 0 - -254 -282 0 - -263 -270 0 - -263 -282 0 - -270 -282 0 - -229 -235 0 - -229 -242 0 - -229 -255 0 - -229 -259 0 - -235 -242 0 - -235 -255 0 - -235 -259 0 - -242 -255 0 - -242 -259 0 - -255 -259 0 - -236 -243 0 - -236 -264 0 - -236 -271 0 - -236 -277 0 - -236 -286 0 - -236 -291 0 - -243 -264 0 - -243 -271 0 - -243 -277 0 - -243 -286 0 - -243 -291 0 - -264 -271 0 - -264 -277 0 - -264 -286 0 - -264 -291 0 - -271 -277 0 - -271 -286 0 - -271 -291 0 - -277 -286 0 - -277 -291 0 - -286 -291 0 - -224 -256 0 - -224 -283 0 - -224 -287 0 - -224 -292 0 - -256 -283 0 - -256 -287 0 - -256 -292 0 - -283 -287 0 - -283 -292 0 - -287 -292 0 - -237 -250 0 - -237 -265 0 - -237 -278 0 - -237 -284 0 - -250 -265 0 - -250 -278 0 - -250 -284 0 - -265 -278 0 - -265 -284 0 - -278 -284 0 - -225 -230 0 - -225 -238 0 - -225 -244 0 - -225 -251 0 - -225 -260 0 - -225 -266 0 - -225 -272 0 - -225 -279 0 - -225 -293 0 - -230 -238 0 - -230 -244 0 - -230 -251 0 - -230 -260 0 - -230 -266 0 - -230 -272 0 - -230 -279 0 - -230 -293 0 - -238 -244 0 - -238 -251 0 - -238 -260 0 - -238 -266 0 - -238 -272 0 - -238 -279 0 - -238 -293 0 - -244 -251 0 - -244 -260 0 - -244 -266 0 - -244 -272 0 - -244 -279 0 - -244 -293 0 - -251 -260 0 - -251 -266 0 - -251 -272 0 - -251 -279 0 - -251 -293 0 - -260 -266 0 - -260 -272 0 - -260 -279 0 - -260 -293 0 - -266 -272 0 - -266 -279 0 - -266 -293 0 - -272 -279 0 - -272 -293 0 - -279 -293 0 - -231 -245 0 - -231 -273 0 - -245 -273 0 - -239 -246 0 - -239 -267 0 - -239 -288 0 - -246 -267 0 - -246 -288 0 - -267 -288 0 - -232 -247 0 - -232 -252 0 - -232 -280 0 - -232 -294 0 - -247 -252 0 - -247 -280 0 - -247 -294 0 - -252 -280 0 - -252 -294 0 - -280 -294 0 - -226 -248 0 - -226 -268 0 - -248 -268 0 - -311 -320 0 - -311 -333 0 - -311 -335 0 - -311 -343 0 - -311 -357 0 - -320 -333 0 - -320 -335 0 - -320 -343 0 - -320 -357 0 - -333 -335 0 - -333 -343 0 - -333 -357 0 - -335 -343 0 - -335 -357 0 - -343 -357 0 - -295 -305 0 - -295 -363 0 - -295 -373 0 - -305 -363 0 - -305 -373 0 - -363 -373 0 - -312 -336 0 - -312 -344 0 - -312 -364 0 - -312 -385 0 - -336 -344 0 - -336 -364 0 - -336 -385 0 - -344 -364 0 - -344 -385 0 - -364 -385 0 - -296 -306 0 - -296 -328 0 - -296 -345 0 - -296 -352 0 - -296 -358 0 - -296 -365 0 - -296 -381 0 - -306 -328 0 - -306 -345 0 - -306 -352 0 - -306 -358 0 - -306 -365 0 - -306 -381 0 - -328 -345 0 - -328 -352 0 - -328 -358 0 - -328 -365 0 - -328 -381 0 - -345 -352 0 - -345 -358 0 - -345 -365 0 - -345 -381 0 - -352 -358 0 - -352 -365 0 - -352 -381 0 - -358 -365 0 - -358 -381 0 - -365 -381 0 - -299 -329 0 - -299 -337 0 - -299 -346 0 - -299 -366 0 - -299 -374 0 - -299 -386 0 - -329 -337 0 - -329 -346 0 - -329 -366 0 - -329 -374 0 - -329 -386 0 - -337 -346 0 - -337 -366 0 - -337 -374 0 - -337 -386 0 - -346 -366 0 - -346 -374 0 - -346 -386 0 - -366 -374 0 - -366 -386 0 - -374 -386 0 - -300 -321 0 - -300 -347 0 - -300 -359 0 - -300 -375 0 - -300 -387 0 - -321 -347 0 - -321 -359 0 - -321 -375 0 - -321 -387 0 - -347 -359 0 - -347 -375 0 - -347 -387 0 - -359 -375 0 - -359 -387 0 - -375 -387 0 - -307 -313 0 - -307 -330 0 - -307 -338 0 - -307 -353 0 - -307 -360 0 - -307 -376 0 - -307 -382 0 - -313 -330 0 - -313 -338 0 - -313 -353 0 - -313 -360 0 - -313 -376 0 - -313 -382 0 - -330 -338 0 - -330 -353 0 - -330 -360 0 - -330 -376 0 - -330 -382 0 - -338 -353 0 - -338 -360 0 - -338 -376 0 - -338 -382 0 - -353 -360 0 - -353 -376 0 - -353 -382 0 - -360 -376 0 - -360 -382 0 - -376 -382 0 - -297 -308 0 - -297 -314 0 - -297 -322 0 - -297 -348 0 - -297 -354 0 - -297 -377 0 - -308 -314 0 - -308 -322 0 - -308 -348 0 - -308 -354 0 - -308 -377 0 - -314 -322 0 - -314 -348 0 - -314 -354 0 - -314 -377 0 - -322 -348 0 - -322 -354 0 - -322 -377 0 - -348 -354 0 - -348 -377 0 - -354 -377 0 - -301 -331 0 - -301 -349 0 - -301 -361 0 - -301 -367 0 - -331 -349 0 - -331 -361 0 - -331 -367 0 - -349 -361 0 - -349 -367 0 - -361 -367 0 - -323 -339 0 - -323 -350 0 - -323 -368 0 - -323 -378 0 - -323 -388 0 - -339 -350 0 - -339 -368 0 - -339 -378 0 - -339 -388 0 - -350 -368 0 - -350 -378 0 - -350 -388 0 - -368 -378 0 - -368 -388 0 - -378 -388 0 - -302 -309 0 - -302 -315 0 - -302 -324 0 - -302 -334 0 - -302 -340 0 - -302 -369 0 - -302 -389 0 - -309 -315 0 - -309 -324 0 - -309 -334 0 - -309 -340 0 - -309 -369 0 - -309 -389 0 - -315 -324 0 - -315 -334 0 - -315 -340 0 - -315 -369 0 - -315 -389 0 - -324 -334 0 - -324 -340 0 - -324 -369 0 - -324 -389 0 - -334 -340 0 - -334 -369 0 - -334 -389 0 - -340 -369 0 - -340 -389 0 - -369 -389 0 - -303 -316 0 - -303 -325 0 - -303 -355 0 - -303 -370 0 - -316 -325 0 - -316 -355 0 - -316 -370 0 - -325 -355 0 - -325 -370 0 - -355 -370 0 - -298 -304 0 - -298 -317 0 - -298 -326 0 - -298 -332 0 - -298 -341 0 - -298 -356 0 - -298 -379 0 - -298 -383 0 - -298 -390 0 - -304 -317 0 - -304 -326 0 - -304 -332 0 - -304 -341 0 - -304 -356 0 - -304 -379 0 - -304 -383 0 - -304 -390 0 - -317 -326 0 - -317 -332 0 - -317 -341 0 - -317 -356 0 - -317 -379 0 - -317 -383 0 - -317 -390 0 - -326 -332 0 - -326 -341 0 - -326 -356 0 - -326 -379 0 - -326 -383 0 - -326 -390 0 - -332 -341 0 - -332 -356 0 - -332 -379 0 - -332 -383 0 - -332 -390 0 - -341 -356 0 - -341 -379 0 - -341 -383 0 - -341 -390 0 - -356 -379 0 - -356 -383 0 - -356 -390 0 - -379 -383 0 - -379 -390 0 - -383 -390 0 - -310 -318 0 - -310 -342 0 - -310 -371 0 - -310 -391 0 - -318 -342 0 - -318 -371 0 - -318 -391 0 - -342 -371 0 - -342 -391 0 - -371 -391 0 - -319 -327 0 - -319 -351 0 - -319 -362 0 - -319 -372 0 - -319 -380 0 - -319 -384 0 - -319 -392 0 - -327 -351 0 - -327 -362 0 - -327 -372 0 - -327 -380 0 - -327 -384 0 - -327 -392 0 - -351 -362 0 - -351 -372 0 - -351 -380 0 - -351 -384 0 - -351 -392 0 - -362 -372 0 - -362 -380 0 - -362 -384 0 - -362 -392 0 - -372 -380 0 - -372 -384 0 - -372 -392 0 - -380 -384 0 - -380 -392 0 - -384 -392 0 - -441 -451 0 - -441 -465 0 - -441 -484 0 - -451 -465 0 - -451 -484 0 - -465 -484 0 - -393 -400 0 - -393 -410 0 - -393 -416 0 - -393 -424 0 - -393 -432 0 - -393 -452 0 - -393 -466 0 - -393 -481 0 - -393 -492 0 - -393 -500 0 - -393 -537 0 - -400 -410 0 - -400 -416 0 - -400 -424 0 - -400 -432 0 - -400 -452 0 - -400 -466 0 - -400 -481 0 - -400 -492 0 - -400 -500 0 - -400 -537 0 - -410 -416 0 - -410 -424 0 - -410 -432 0 - -410 -452 0 - -410 -466 0 - -410 -481 0 - -410 -492 0 - -410 -500 0 - -410 -537 0 - -416 -424 0 - -416 -432 0 - -416 -452 0 - -416 -466 0 - -416 -481 0 - -416 -492 0 - -416 -500 0 - -416 -537 0 - -424 -432 0 - -424 -452 0 - -424 -466 0 - -424 -481 0 - -424 -492 0 - -424 -500 0 - -424 -537 0 - -432 -452 0 - -432 -466 0 - -432 -481 0 - -432 -492 0 - -432 -500 0 - -432 -537 0 - -452 -466 0 - -452 -481 0 - -452 -492 0 - -452 -500 0 - -452 -537 0 - -466 -481 0 - -466 -492 0 - -466 -500 0 - -466 -537 0 - -481 -492 0 - -481 -500 0 - -481 -537 0 - -492 -500 0 - -492 -537 0 - -500 -537 0 - -401 -417 0 - -401 -442 0 - -401 -467 0 - -401 -501 0 - -401 -522 0 - -401 -538 0 - -417 -442 0 - -417 -467 0 - -417 -501 0 - -417 -522 0 - -417 -538 0 - -442 -467 0 - -442 -501 0 - -442 -522 0 - -442 -538 0 - -467 -501 0 - -467 -522 0 - -467 -538 0 - -501 -522 0 - -501 -538 0 - -522 -538 0 - -394 -402 0 - -394 -425 0 - -394 -468 0 - -394 -476 0 - -394 -493 0 - -394 -523 0 - -394 -531 0 - -394 -539 0 - -402 -425 0 - -402 -468 0 - -402 -476 0 - -402 -493 0 - -402 -523 0 - -402 -531 0 - -402 -539 0 - -425 -468 0 - -425 -476 0 - -425 -493 0 - -425 -523 0 - -425 -531 0 - -425 -539 0 - -468 -476 0 - -468 -493 0 - -468 -523 0 - -468 -531 0 - -468 -539 0 - -476 -493 0 - -476 -523 0 - -476 -531 0 - -476 -539 0 - -493 -523 0 - -493 -531 0 - -493 -539 0 - -523 -531 0 - -523 -539 0 - -531 -539 0 - -403 -411 0 - -403 -418 0 - -403 -426 0 - -403 -433 0 - -403 -502 0 - -403 -510 0 - -403 -514 0 - -411 -418 0 - -411 -426 0 - -411 -433 0 - -411 -502 0 - -411 -510 0 - -411 -514 0 - -418 -426 0 - -418 -433 0 - -418 -502 0 - -418 -510 0 - -418 -514 0 - -426 -433 0 - -426 -502 0 - -426 -510 0 - -426 -514 0 - -433 -502 0 - -433 -510 0 - -433 -514 0 - -502 -510 0 - -502 -514 0 - -510 -514 0 - -395 -434 0 - -395 -443 0 - -395 -459 0 - -395 -469 0 - -395 -485 0 - -395 -515 0 - -395 -524 0 - -395 -532 0 - -434 -443 0 - -434 -459 0 - -434 -469 0 - -434 -485 0 - -434 -515 0 - -434 -524 0 - -434 -532 0 - -443 -459 0 - -443 -469 0 - -443 -485 0 - -443 -515 0 - -443 -524 0 - -443 -532 0 - -459 -469 0 - -459 -485 0 - -459 -515 0 - -459 -524 0 - -459 -532 0 - -469 -485 0 - -469 -515 0 - -469 -524 0 - -469 -532 0 - -485 -515 0 - -485 -524 0 - -485 -532 0 - -515 -524 0 - -515 -532 0 - -524 -532 0 - -435 -453 0 - -435 -470 0 - -435 -477 0 - -435 -503 0 - -435 -511 0 - -435 -525 0 - -453 -470 0 - -453 -477 0 - -453 -503 0 - -453 -511 0 - -453 -525 0 - -470 -477 0 - -470 -503 0 - -470 -511 0 - -470 -525 0 - -477 -503 0 - -477 -511 0 - -477 -525 0 - -503 -511 0 - -503 -525 0 - -511 -525 0 - -396 -404 0 - -396 -419 0 - -396 -427 0 - -396 -486 0 - -396 -494 0 - -396 -516 0 - -396 -540 0 - -404 -419 0 - -404 -427 0 - -404 -486 0 - -404 -494 0 - -404 -516 0 - -404 -540 0 - -419 -427 0 - -419 -486 0 - -419 -494 0 - -419 -516 0 - -419 -540 0 - -427 -486 0 - -427 -494 0 - -427 -516 0 - -427 -540 0 - -486 -494 0 - -486 -516 0 - -486 -540 0 - -494 -516 0 - -494 -540 0 - -516 -540 0 - -412 -436 0 - -412 -460 0 - -412 -471 0 - -412 -482 0 - -412 -487 0 - -412 -504 0 - -412 -517 0 - -436 -460 0 - -436 -471 0 - -436 -482 0 - -436 -487 0 - -436 -504 0 - -436 -517 0 - -460 -471 0 - -460 -482 0 - -460 -487 0 - -460 -504 0 - -460 -517 0 - -471 -482 0 - -471 -487 0 - -471 -504 0 - -471 -517 0 - -482 -487 0 - -482 -504 0 - -482 -517 0 - -487 -504 0 - -487 -517 0 - -504 -517 0 - -413 -420 0 - -413 -444 0 - -413 -461 0 - -413 -518 0 - -413 -526 0 - -413 -533 0 - -413 -541 0 - -420 -444 0 - -420 -461 0 - -420 -518 0 - -420 -526 0 - -420 -533 0 - -420 -541 0 - -444 -461 0 - -444 -518 0 - -444 -526 0 - -444 -533 0 - -444 -541 0 - -461 -518 0 - -461 -526 0 - -461 -533 0 - -461 -541 0 - -518 -526 0 - -518 -533 0 - -518 -541 0 - -526 -533 0 - -526 -541 0 - -533 -541 0 - -421 -437 0 - -421 -445 0 - -421 -472 0 - -421 -495 0 - -421 -505 0 - -421 -527 0 - -421 -534 0 - -421 -542 0 - -437 -445 0 - -437 -472 0 - -437 -495 0 - -437 -505 0 - -437 -527 0 - -437 -534 0 - -437 -542 0 - -445 -472 0 - -445 -495 0 - -445 -505 0 - -445 -527 0 - -445 -534 0 - -445 -542 0 - -472 -495 0 - -472 -505 0 - -472 -527 0 - -472 -534 0 - -472 -542 0 - -495 -505 0 - -495 -527 0 - -495 -534 0 - -495 -542 0 - -505 -527 0 - -505 -534 0 - -505 -542 0 - -527 -534 0 - -527 -542 0 - -534 -542 0 - -405 -428 0 - -405 -473 0 - -405 -488 0 - -405 -496 0 - -405 -506 0 - -405 -519 0 - -405 -528 0 - -405 -543 0 - -428 -473 0 - -428 -488 0 - -428 -496 0 - -428 -506 0 - -428 -519 0 - -428 -528 0 - -428 -543 0 - -473 -488 0 - -473 -496 0 - -473 -506 0 - -473 -519 0 - -473 -528 0 - -473 -543 0 - -488 -496 0 - -488 -506 0 - -488 -519 0 - -488 -528 0 - -488 -543 0 - -496 -506 0 - -496 -519 0 - -496 -528 0 - -496 -543 0 - -506 -519 0 - -506 -528 0 - -506 -543 0 - -519 -528 0 - -519 -543 0 - -528 -543 0 - -406 -414 0 - -406 -512 0 - -406 -520 0 - -406 -529 0 - -406 -535 0 - -406 -544 0 - -414 -512 0 - -414 -520 0 - -414 -529 0 - -414 -535 0 - -414 -544 0 - -512 -520 0 - -512 -529 0 - -512 -535 0 - -512 -544 0 - -520 -529 0 - -520 -535 0 - -520 -544 0 - -529 -535 0 - -529 -544 0 - -535 -544 0 - -422 -429 0 - -422 -454 0 - -422 -462 0 - -422 -507 0 - -422 -545 0 - -429 -454 0 - -429 -462 0 - -429 -507 0 - -429 -545 0 - -454 -462 0 - -454 -507 0 - -454 -545 0 - -462 -507 0 - -462 -545 0 - -507 -545 0 - -438 -446 0 - -438 -455 0 - -438 -478 0 - -438 -497 0 - -438 -513 0 - -446 -455 0 - -446 -478 0 - -446 -497 0 - -446 -513 0 - -455 -478 0 - -455 -497 0 - -455 -513 0 - -478 -497 0 - -478 -513 0 - -497 -513 0 - -407 -423 0 - -407 -439 0 - -407 -463 0 - -407 -474 0 - -407 -479 0 - -407 -483 0 - -407 -530 0 - -407 -536 0 - -423 -439 0 - -423 -463 0 - -423 -474 0 - -423 -479 0 - -423 -483 0 - -423 -530 0 - -423 -536 0 - -439 -463 0 - -439 -474 0 - -439 -479 0 - -439 -483 0 - -439 -530 0 - -439 -536 0 - -463 -474 0 - -463 -479 0 - -463 -483 0 - -463 -530 0 - -463 -536 0 - -474 -479 0 - -474 -483 0 - -474 -530 0 - -474 -536 0 - -479 -483 0 - -479 -530 0 - -479 -536 0 - -483 -530 0 - -483 -536 0 - -530 -536 0 - -397 -408 0 - -397 -440 0 - -397 -447 0 - -397 -456 0 - -397 -475 0 - -397 -498 0 - -397 -508 0 - -397 -546 0 - -408 -440 0 - -408 -447 0 - -408 -456 0 - -408 -475 0 - -408 -498 0 - -408 -508 0 - -408 -546 0 - -440 -447 0 - -440 -456 0 - -440 -475 0 - -440 -498 0 - -440 -508 0 - -440 -546 0 - -447 -456 0 - -447 -475 0 - -447 -498 0 - -447 -508 0 - -447 -546 0 - -456 -475 0 - -456 -498 0 - -456 -508 0 - -456 -546 0 - -475 -498 0 - -475 -508 0 - -475 -546 0 - -498 -508 0 - -498 -546 0 - -508 -546 0 - -398 -415 0 - -398 -430 0 - -398 -448 0 - -398 -489 0 - -398 -499 0 - -415 -430 0 - -415 -448 0 - -415 -489 0 - -415 -499 0 - -430 -448 0 - -430 -489 0 - -430 -499 0 - -448 -489 0 - -448 -499 0 - -489 -499 0 - -431 -449 0 - -431 -457 0 - -431 -464 0 - -431 -490 0 - -431 -509 0 - -431 -547 0 - -449 -457 0 - -449 -464 0 - -449 -490 0 - -449 -509 0 - -449 -547 0 - -457 -464 0 - -457 -490 0 - -457 -509 0 - -457 -547 0 - -464 -490 0 - -464 -509 0 - -464 -547 0 - -490 -509 0 - -490 -547 0 - -509 -547 0 - -399 -409 0 - -399 -450 0 - -399 -458 0 - -399 -480 0 - -399 -491 0 - -399 -521 0 - -399 -548 0 - -409 -450 0 - -409 -458 0 - -409 -480 0 - -409 -491 0 - -409 -521 0 - -409 -548 0 - -450 -458 0 - -450 -480 0 - -450 -491 0 - -450 -521 0 - -450 -548 0 - -458 -480 0 - -458 -491 0 - -458 -521 0 - -458 -548 0 - -480 -491 0 - -480 -521 0 - -480 -548 0 - -491 -521 0 - -491 -548 0 - -521 -548 0 - -549 -560 0 - -549 -564 0 - -549 -588 0 - -549 -597 0 - -549 -603 0 - -560 -564 0 - -560 -588 0 - -560 -597 0 - -560 -603 0 - -564 -588 0 - -564 -597 0 - -564 -603 0 - -588 -597 0 - -588 -603 0 - -597 -603 0 - -565 -570 0 - -565 -574 0 - -565 -581 0 - -565 -598 0 - -565 -609 0 - -570 -574 0 - -570 -581 0 - -570 -598 0 - -570 -609 0 - -574 -581 0 - -574 -598 0 - -574 -609 0 - -581 -598 0 - -581 -609 0 - -598 -609 0 - -566 -582 0 - -566 -589 0 - -566 -593 0 - -566 -599 0 - -566 -610 0 - -582 -589 0 - -582 -593 0 - -582 -599 0 - -582 -610 0 - -589 -593 0 - -589 -599 0 - -589 -610 0 - -593 -599 0 - -593 -610 0 - -599 -610 0 - -550 -561 0 - -550 -571 0 - -550 -575 0 - -561 -571 0 - -561 -575 0 - -571 -575 0 - -551 -576 0 - -551 -590 0 - -551 -594 0 - -551 -600 0 - -551 -604 0 - -576 -590 0 - -576 -594 0 - -576 -600 0 - -576 -604 0 - -590 -594 0 - -590 -600 0 - -590 -604 0 - -594 -600 0 - -594 -604 0 - -600 -604 0 - -555 -567 0 - -555 -583 0 - -555 -595 0 - -555 -601 0 - -567 -583 0 - -567 -595 0 - -567 -601 0 - -583 -595 0 - -583 -601 0 - -595 -601 0 - -552 -577 0 - -552 -596 0 - -552 -605 0 - -577 -596 0 - -577 -605 0 - -596 -605 0 - -556 -584 0 - -556 -591 0 - -556 -606 0 - -584 -591 0 - -584 -606 0 - -591 -606 0 - -557 -562 0 - -557 -572 0 - -557 -578 0 - -557 -607 0 - -562 -572 0 - -562 -578 0 - -562 -607 0 - -572 -578 0 - -572 -607 0 - -578 -607 0 - -553 -563 0 - -553 -568 0 - -553 -573 0 - -553 -579 0 - -553 -585 0 - -553 -592 0 - -553 -602 0 - -563 -568 0 - -563 -573 0 - -563 -579 0 - -563 -585 0 - -563 -592 0 - -563 -602 0 - -568 -573 0 - -568 -579 0 - -568 -585 0 - -568 -592 0 - -568 -602 0 - -573 -579 0 - -573 -585 0 - -573 -592 0 - -573 -602 0 - -579 -585 0 - -579 -592 0 - -579 -602 0 - -585 -592 0 - -585 -602 0 - -592 -602 0 - -554 -558 0 - -554 -569 0 - -554 -580 0 - -554 -586 0 - -554 -608 0 - -554 -611 0 - -558 -569 0 - -558 -580 0 - -558 -586 0 - -558 -608 0 - -558 -611 0 - -569 -580 0 - -569 -586 0 - -569 -608 0 - -569 -611 0 - -580 -586 0 - -580 -608 0 - -580 -611 0 - -586 -608 0 - -586 -611 0 - -608 -611 0 - -559 -587 0 - -559 -612 0 - -587 -612 0 - -613 -621 0 - -613 -630 0 - -613 -637 0 - -613 -642 0 - -613 -656 0 - -613 -678 0 - -621 -630 0 - -621 -637 0 - -621 -642 0 - -621 -656 0 - -621 -678 0 - -630 -637 0 - -630 -642 0 - -630 -656 0 - -630 -678 0 - -637 -642 0 - -637 -656 0 - -637 -678 0 - -642 -656 0 - -642 -678 0 - -656 -678 0 - -622 -631 0 - -622 -679 0 - -622 -694 0 - -631 -679 0 - -631 -694 0 - -679 -694 0 - -614 -632 0 - -614 -638 0 - -614 -657 0 - -614 -665 0 - -614 -680 0 - -614 -689 0 - -632 -638 0 - -632 -657 0 - -632 -665 0 - -632 -680 0 - -632 -689 0 - -638 -657 0 - -638 -665 0 - -638 -680 0 - -638 -689 0 - -657 -665 0 - -657 -680 0 - -657 -689 0 - -665 -680 0 - -665 -689 0 - -680 -689 0 - -643 -649 0 - -643 -658 0 - -643 -681 0 - -643 -695 0 - -649 -658 0 - -649 -681 0 - -649 -695 0 - -658 -681 0 - -658 -695 0 - -681 -695 0 - -623 -633 0 - -623 -639 0 - -623 -659 0 - -623 -666 0 - -623 -672 0 - -623 -682 0 - -623 -690 0 - -633 -639 0 - -633 -659 0 - -633 -666 0 - -633 -672 0 - -633 -682 0 - -633 -690 0 - -639 -659 0 - -639 -666 0 - -639 -672 0 - -639 -682 0 - -639 -690 0 - -659 -666 0 - -659 -672 0 - -659 -682 0 - -659 -690 0 - -666 -672 0 - -666 -682 0 - -666 -690 0 - -672 -682 0 - -672 -690 0 - -682 -690 0 - -615 -618 0 - -615 -634 0 - -615 -660 0 - -615 -683 0 - -615 -691 0 - -615 -696 0 - -615 -699 0 - -618 -634 0 - -618 -660 0 - -618 -683 0 - -618 -691 0 - -618 -696 0 - -618 -699 0 - -634 -660 0 - -634 -683 0 - -634 -691 0 - -634 -696 0 - -634 -699 0 - -660 -683 0 - -660 -691 0 - -660 -696 0 - -660 -699 0 - -683 -691 0 - -683 -696 0 - -683 -699 0 - -691 -696 0 - -691 -699 0 - -696 -699 0 - -616 -624 0 - -616 -667 0 - -616 -684 0 - -616 -692 0 - -616 -697 0 - -616 -700 0 - -624 -667 0 - -624 -684 0 - -624 -692 0 - -624 -697 0 - -624 -700 0 - -667 -684 0 - -667 -692 0 - -667 -697 0 - -667 -700 0 - -684 -692 0 - -684 -697 0 - -684 -700 0 - -692 -697 0 - -692 -700 0 - -697 -700 0 - -625 -644 0 - -625 -668 0 - -625 -673 0 - -625 -685 0 - -644 -668 0 - -644 -673 0 - -644 -685 0 - -668 -673 0 - -668 -685 0 - -673 -685 0 - -619 -635 0 - -619 -645 0 - -619 -650 0 - -619 -661 0 - -619 -674 0 - -619 -701 0 - -635 -645 0 - -635 -650 0 - -635 -661 0 - -635 -674 0 - -635 -701 0 - -645 -650 0 - -645 -661 0 - -645 -674 0 - -645 -701 0 - -650 -661 0 - -650 -674 0 - -650 -701 0 - -661 -674 0 - -661 -701 0 - -674 -701 0 - -636 -651 0 - -636 -662 0 - -636 -669 0 - -636 -686 0 - -636 -698 0 - -636 -702 0 - -651 -662 0 - -651 -669 0 - -651 -686 0 - -651 -698 0 - -651 -702 0 - -662 -669 0 - -662 -686 0 - -662 -698 0 - -662 -702 0 - -669 -686 0 - -669 -698 0 - -669 -702 0 - -686 -698 0 - -686 -702 0 - -698 -702 0 - -617 -626 0 - -617 -640 0 - -617 -652 0 - -617 -670 0 - -617 -675 0 - -626 -640 0 - -626 -652 0 - -626 -670 0 - -626 -675 0 - -640 -652 0 - -640 -670 0 - -640 -675 0 - -652 -670 0 - -652 -675 0 - -670 -675 0 - -620 -627 0 - -620 -646 0 - -620 -653 0 - -620 -663 0 - -627 -646 0 - -627 -653 0 - -627 -663 0 - -646 -653 0 - -646 -663 0 - -653 -663 0 - -628 -641 0 - -628 -647 0 - -628 -654 0 - -628 -671 0 - -628 -676 0 - -628 -687 0 - -641 -647 0 - -641 -654 0 - -641 -671 0 - -641 -676 0 - -641 -687 0 - -647 -654 0 - -647 -671 0 - -647 -676 0 - -647 -687 0 - -654 -671 0 - -654 -676 0 - -654 -687 0 - -671 -676 0 - -671 -687 0 - -676 -687 0 - -629 -648 0 - -629 -655 0 - -629 -664 0 - -629 -677 0 - -629 -688 0 - -629 -693 0 - -648 -655 0 - -648 -664 0 - -648 -677 0 - -648 -688 0 - -648 -693 0 - -655 -664 0 - -655 -677 0 - -655 -688 0 - -655 -693 0 - -664 -677 0 - -664 -688 0 - -664 -693 0 - -677 -688 0 - -677 -693 0 - -688 -693 0 - -730 -735 0 - -730 -747 0 - -730 -753 0 - -735 -747 0 - -735 -753 0 - -747 -753 0 - -703 -709 0 - -703 -722 0 - -703 -736 0 - -703 -742 0 - -703 -748 0 - -703 -756 0 - -703 -762 0 - -709 -722 0 - -709 -736 0 - -709 -742 0 - -709 -748 0 - -709 -756 0 - -709 -762 0 - -722 -736 0 - -722 -742 0 - -722 -748 0 - -722 -756 0 - -722 -762 0 - -736 -742 0 - -736 -748 0 - -736 -756 0 - -736 -762 0 - -742 -748 0 - -742 -756 0 - -742 -762 0 - -748 -756 0 - -748 -762 0 - -756 -762 0 - -704 -743 0 - -704 -749 0 - -743 -749 0 - -710 -714 0 - -710 -723 0 - -710 -757 0 - -710 -765 0 - -710 -770 0 - -714 -723 0 - -714 -757 0 - -714 -765 0 - -714 -770 0 - -723 -757 0 - -723 -765 0 - -723 -770 0 - -757 -765 0 - -757 -770 0 - -765 -770 0 - -705 -724 0 - -705 -731 0 - -705 -758 0 - -705 -771 0 - -724 -731 0 - -724 -758 0 - -724 -771 0 - -731 -758 0 - -731 -771 0 - -758 -771 0 - -732 -750 0 - -732 -754 0 - -732 -766 0 - -750 -754 0 - -750 -766 0 - -754 -766 0 - -706 -744 0 - -706 -759 0 - -706 -772 0 - -744 -759 0 - -744 -772 0 - -759 -772 0 - -711 -717 0 - -711 -725 0 - -711 -745 0 - -711 -763 0 - -711 -773 0 - -717 -725 0 - -717 -745 0 - -717 -763 0 - -717 -773 0 - -725 -745 0 - -725 -763 0 - -725 -773 0 - -745 -763 0 - -745 -773 0 - -763 -773 0 - -712 -715 0 - -712 -718 0 - -712 -726 0 - -712 -737 0 - -712 -751 0 - -715 -718 0 - -715 -726 0 - -715 -737 0 - -715 -751 0 - -718 -726 0 - -718 -737 0 - -718 -751 0 - -726 -737 0 - -726 -751 0 - -737 -751 0 - -713 -719 0 - -713 -733 0 - -713 -755 0 - -713 -760 0 - -713 -774 0 - -719 -733 0 - -719 -755 0 - -719 -760 0 - -719 -774 0 - -733 -755 0 - -733 -760 0 - -733 -774 0 - -755 -760 0 - -755 -774 0 - -760 -774 0 - -707 -727 0 - -707 -738 0 - -707 -746 0 - -707 -752 0 - -707 -764 0 - -727 -738 0 - -727 -746 0 - -727 -752 0 - -727 -764 0 - -738 -746 0 - -738 -752 0 - -738 -764 0 - -746 -752 0 - -746 -764 0 - -752 -764 0 - -708 -728 0 - -708 -739 0 - -708 -767 0 - -728 -739 0 - -728 -767 0 - -739 -767 0 - -716 -720 0 - -716 -740 0 - -716 -761 0 - -716 -768 0 - -720 -740 0 - -720 -761 0 - -720 -768 0 - -740 -761 0 - -740 -768 0 - -761 -768 0 - -721 -729 0 - -721 -734 0 - -721 -741 0 - -721 -769 0 - -729 -734 0 - -729 -741 0 - -729 -769 0 - -734 -741 0 - -734 -769 0 - -741 -769 0 - -775 -788 0 - -775 -799 0 - -775 -833 0 - -775 -853 0 - -775 -860 0 - -775 -869 0 - -775 -873 0 - -788 -799 0 - -788 -833 0 - -788 -853 0 - -788 -860 0 - -788 -869 0 - -788 -873 0 - -799 -833 0 - -799 -853 0 - -799 -860 0 - -799 -869 0 - -799 -873 0 - -833 -853 0 - -833 -860 0 - -833 -869 0 - -833 -873 0 - -853 -860 0 - -853 -869 0 - -853 -873 0 - -860 -869 0 - -860 -873 0 - -869 -873 0 - -807 -816 0 - -807 -834 0 - -807 -840 0 - -807 -844 0 - -807 -849 0 - -807 -854 0 - -807 -861 0 - -807 -870 0 - -807 -874 0 - -816 -834 0 - -816 -840 0 - -816 -844 0 - -816 -849 0 - -816 -854 0 - -816 -861 0 - -816 -870 0 - -816 -874 0 - -834 -840 0 - -834 -844 0 - -834 -849 0 - -834 -854 0 - -834 -861 0 - -834 -870 0 - -834 -874 0 - -840 -844 0 - -840 -849 0 - -840 -854 0 - -840 -861 0 - -840 -870 0 - -840 -874 0 - -844 -849 0 - -844 -854 0 - -844 -861 0 - -844 -870 0 - -844 -874 0 - -849 -854 0 - -849 -861 0 - -849 -870 0 - -849 -874 0 - -854 -861 0 - -854 -870 0 - -854 -874 0 - -861 -870 0 - -861 -874 0 - -870 -874 0 - -776 -789 0 - -776 -792 0 - -776 -823 0 - -776 -845 0 - -776 -855 0 - -776 -862 0 - -789 -792 0 - -789 -823 0 - -789 -845 0 - -789 -855 0 - -789 -862 0 - -792 -823 0 - -792 -845 0 - -792 -855 0 - -792 -862 0 - -823 -845 0 - -823 -855 0 - -823 -862 0 - -845 -855 0 - -845 -862 0 - -855 -862 0 - -783 -808 0 - -783 -817 0 - -783 -824 0 - -783 -841 0 - -783 -856 0 - -783 -875 0 - -808 -817 0 - -808 -824 0 - -808 -841 0 - -808 -856 0 - -808 -875 0 - -817 -824 0 - -817 -841 0 - -817 -856 0 - -817 -875 0 - -824 -841 0 - -824 -856 0 - -824 -875 0 - -841 -856 0 - -841 -875 0 - -856 -875 0 - -793 -800 0 - -793 -818 0 - -793 -825 0 - -793 -863 0 - -793 -876 0 - -800 -818 0 - -800 -825 0 - -800 -863 0 - -800 -876 0 - -818 -825 0 - -818 -863 0 - -818 -876 0 - -825 -863 0 - -825 -876 0 - -863 -876 0 - -809 -819 0 - -809 -835 0 - -809 -864 0 - -809 -877 0 - -819 -835 0 - -819 -864 0 - -819 -877 0 - -835 -864 0 - -835 -877 0 - -864 -877 0 - -777 -784 0 - -777 -790 0 - -777 -801 0 - -777 -810 0 - -777 -826 0 - -784 -790 0 - -784 -801 0 - -784 -810 0 - -784 -826 0 - -790 -801 0 - -790 -810 0 - -790 -826 0 - -801 -810 0 - -801 -826 0 - -810 -826 0 - -778 -794 0 - -778 -836 0 - -778 -850 0 - -778 -871 0 - -794 -836 0 - -794 -850 0 - -794 -871 0 - -836 -850 0 - -836 -871 0 - -850 -871 0 - -779 -785 0 - -779 -827 0 - -779 -851 0 - -779 -857 0 - -779 -865 0 - -779 -872 0 - -785 -827 0 - -785 -851 0 - -785 -857 0 - -785 -865 0 - -785 -872 0 - -827 -851 0 - -827 -857 0 - -827 -865 0 - -827 -872 0 - -851 -857 0 - -851 -865 0 - -851 -872 0 - -857 -865 0 - -857 -872 0 - -865 -872 0 - -811 -828 0 - -811 -837 0 - -811 -842 0 - -811 -866 0 - -811 -878 0 - -828 -837 0 - -828 -842 0 - -828 -866 0 - -828 -878 0 - -837 -842 0 - -837 -866 0 - -837 -878 0 - -842 -866 0 - -842 -878 0 - -866 -878 0 - -791 -795 0 - -791 -812 0 - -791 -820 0 - -791 -829 0 - -795 -812 0 - -795 -820 0 - -795 -829 0 - -812 -820 0 - -812 -829 0 - -820 -829 0 - -780 -802 0 - -780 -813 0 - -780 -830 0 - -780 -838 0 - -780 -846 0 - -780 -879 0 - -802 -813 0 - -802 -830 0 - -802 -838 0 - -802 -846 0 - -802 -879 0 - -813 -830 0 - -813 -838 0 - -813 -846 0 - -813 -879 0 - -830 -838 0 - -830 -846 0 - -830 -879 0 - -838 -846 0 - -838 -879 0 - -846 -879 0 - -786 -796 0 - -786 -803 0 - -786 -831 0 - -786 -847 0 - -796 -803 0 - -796 -831 0 - -796 -847 0 - -803 -831 0 - -803 -847 0 - -831 -847 0 - -797 -804 0 - -797 -814 0 - -797 -821 0 - -797 -832 0 - -797 -852 0 - -797 -858 0 - -797 -867 0 - -797 -880 0 - -804 -814 0 - -804 -821 0 - -804 -832 0 - -804 -852 0 - -804 -858 0 - -804 -867 0 - -804 -880 0 - -814 -821 0 - -814 -832 0 - -814 -852 0 - -814 -858 0 - -814 -867 0 - -814 -880 0 - -821 -832 0 - -821 -852 0 - -821 -858 0 - -821 -867 0 - -821 -880 0 - -832 -852 0 - -832 -858 0 - -832 -867 0 - -832 -880 0 - -852 -858 0 - -852 -867 0 - -852 -880 0 - -858 -867 0 - -858 -880 0 - -867 -880 0 - -781 -805 0 - -781 -815 0 - -781 -822 0 - -781 -843 0 - -781 -848 0 - -781 -859 0 - -781 -868 0 - -781 -881 0 - -805 -815 0 - -805 -822 0 - -805 -843 0 - -805 -848 0 - -805 -859 0 - -805 -868 0 - -805 -881 0 - -815 -822 0 - -815 -843 0 - -815 -848 0 - -815 -859 0 - -815 -868 0 - -815 -881 0 - -822 -843 0 - -822 -848 0 - -822 -859 0 - -822 -868 0 - -822 -881 0 - -843 -848 0 - -843 -859 0 - -843 -868 0 - -843 -881 0 - -848 -859 0 - -848 -868 0 - -848 -881 0 - -859 -868 0 - -859 -881 0 - -868 -881 0 - -782 -787 0 - -782 -798 0 - -782 -806 0 - -782 -839 0 - -787 -798 0 - -787 -806 0 - -787 -839 0 - -798 -806 0 - -798 -839 0 - -806 -839 0 - -885 -890 0 - -885 -898 0 - -885 -907 0 - -890 -898 0 - -890 -907 0 - -898 -907 0 - -886 -891 0 - -886 -895 0 - -886 -899 0 - -886 -902 0 - -886 -911 0 - -891 -895 0 - -891 -899 0 - -891 -902 0 - -891 -911 0 - -895 -899 0 - -895 -902 0 - -895 -911 0 - -899 -902 0 - -899 -911 0 - -902 -911 0 - -887 -892 0 - -887 -908 0 - -892 -908 0 - -893 -909 0 - -888 -896 0 - -888 -900 0 - -888 -903 0 - -896 -900 0 - -896 -903 0 - -900 -903 0 - -882 -904 0 - -883 -894 0 - -883 -901 0 - -883 -905 0 - -883 -910 0 - -883 -912 0 - -894 -901 0 - -894 -905 0 - -894 -910 0 - -894 -912 0 - -901 -905 0 - -901 -910 0 - -901 -912 0 - -905 -910 0 - -905 -912 0 - -910 -912 0 - -884 -889 0 - -884 -897 0 - -884 -906 0 - -889 -897 0 - -889 -906 0 - -897 -906 0 - -918 -931 0 - -918 -941 0 - -918 -960 0 - -918 -966 0 - -931 -941 0 - -931 -960 0 - -931 -966 0 - -941 -960 0 - -941 -966 0 - -960 -966 0 - -924 -932 0 - -924 -947 0 - -924 -954 0 - -924 -967 0 - -932 -947 0 - -932 -954 0 - -932 -967 0 - -947 -954 0 - -947 -967 0 - -954 -967 0 - -919 -933 0 - -919 -936 0 - -919 -942 0 - -919 -963 0 - -919 -968 0 - -919 -978 0 - -919 -984 0 - -933 -936 0 - -933 -942 0 - -933 -963 0 - -933 -968 0 - -933 -978 0 - -933 -984 0 - -936 -942 0 - -936 -963 0 - -936 -968 0 - -936 -978 0 - -936 -984 0 - -942 -963 0 - -942 -968 0 - -942 -978 0 - -942 -984 0 - -963 -968 0 - -963 -978 0 - -963 -984 0 - -968 -978 0 - -968 -984 0 - -978 -984 0 - -913 -925 0 - -913 -943 0 - -913 -948 0 - -913 -969 0 - -913 -976 0 - -913 -979 0 - -913 -985 0 - -925 -943 0 - -925 -948 0 - -925 -969 0 - -925 -976 0 - -925 -979 0 - -925 -985 0 - -943 -948 0 - -943 -969 0 - -943 -976 0 - -943 -979 0 - -943 -985 0 - -948 -969 0 - -948 -976 0 - -948 -979 0 - -948 -985 0 - -969 -976 0 - -969 -979 0 - -969 -985 0 - -976 -979 0 - -976 -985 0 - -979 -985 0 - -920 -934 0 - -920 -961 0 - -920 -970 0 - -920 -986 0 - -934 -961 0 - -934 -970 0 - -934 -986 0 - -961 -970 0 - -961 -986 0 - -970 -986 0 - -926 -944 0 - -926 -949 0 - -926 -955 0 - -944 -949 0 - -944 -955 0 - -949 -955 0 - -914 -977 0 - -914 -980 0 - -977 -980 0 - -915 -921 0 - -915 -937 0 - -915 -945 0 - -915 -964 0 - -915 -971 0 - -921 -937 0 - -921 -945 0 - -921 -964 0 - -921 -971 0 - -937 -945 0 - -937 -964 0 - -937 -971 0 - -945 -964 0 - -945 -971 0 - -964 -971 0 - -922 -950 0 - -922 -956 0 - -922 -972 0 - -922 -987 0 - -950 -956 0 - -950 -972 0 - -950 -987 0 - -956 -972 0 - -956 -987 0 - -972 -987 0 - -916 -927 0 - -916 -951 0 - -916 -973 0 - -916 -981 0 - -916 -988 0 - -927 -951 0 - -927 -973 0 - -927 -981 0 - -927 -988 0 - -951 -973 0 - -951 -981 0 - -951 -988 0 - -973 -981 0 - -973 -988 0 - -981 -988 0 - -917 -928 0 - -917 -982 0 - -928 -982 0 - -923 -938 0 - -923 -946 0 - -923 -952 0 - -923 -957 0 - -923 -962 0 - -923 -974 0 - -923 -983 0 - -938 -946 0 - -938 -952 0 - -938 -957 0 - -938 -962 0 - -938 -974 0 - -938 -983 0 - -946 -952 0 - -946 -957 0 - -946 -962 0 - -946 -974 0 - -946 -983 0 - -952 -957 0 - -952 -962 0 - -952 -974 0 - -952 -983 0 - -957 -962 0 - -957 -974 0 - -957 -983 0 - -962 -974 0 - -962 -983 0 - -974 -983 0 - -929 -939 0 - -929 -958 0 - -939 -958 0 - -930 -935 0 - -930 -940 0 - -930 -953 0 - -930 -959 0 - -930 -965 0 - -930 -975 0 - -930 -989 0 - -935 -940 0 - -935 -953 0 - -935 -959 0 - -935 -965 0 - -935 -975 0 - -935 -989 0 - -940 -953 0 - -940 -959 0 - -940 -965 0 - -940 -975 0 - -940 -989 0 - -953 -959 0 - -953 -965 0 - -953 -975 0 - -953 -989 0 - -959 -965 0 - -959 -975 0 - -959 -989 0 - -965 -975 0 - -965 -989 0 - -975 -989 0 - -990 -1005 0 - -990 -1012 0 - -990 -1021 0 - -990 -1039 0 - -990 -1071 0 - -990 -1082 0 - -990 -1085 0 - -990 -1092 0 - -1005 -1012 0 - -1005 -1021 0 - -1005 -1039 0 - -1005 -1071 0 - -1005 -1082 0 - -1005 -1085 0 - -1005 -1092 0 - -1012 -1021 0 - -1012 -1039 0 - -1012 -1071 0 - -1012 -1082 0 - -1012 -1085 0 - -1012 -1092 0 - -1021 -1039 0 - -1021 -1071 0 - -1021 -1082 0 - -1021 -1085 0 - -1021 -1092 0 - -1039 -1071 0 - -1039 -1082 0 - -1039 -1085 0 - -1039 -1092 0 - -1071 -1082 0 - -1071 -1085 0 - -1071 -1092 0 - -1082 -1085 0 - -1082 -1092 0 - -1085 -1092 0 - -1030 -1046 0 - -1030 -1054 0 - -1030 -1062 0 - -1030 -1072 0 - -1030 -1086 0 - -1030 -1093 0 - -1030 -1103 0 - -1046 -1054 0 - -1046 -1062 0 - -1046 -1072 0 - -1046 -1086 0 - -1046 -1093 0 - -1046 -1103 0 - -1054 -1062 0 - -1054 -1072 0 - -1054 -1086 0 - -1054 -1093 0 - -1054 -1103 0 - -1062 -1072 0 - -1062 -1086 0 - -1062 -1093 0 - -1062 -1103 0 - -1072 -1086 0 - -1072 -1093 0 - -1072 -1103 0 - -1086 -1093 0 - -1086 -1103 0 - -1093 -1103 0 - -999 -1022 0 - -999 -1051 0 - -999 -1073 0 - -999 -1094 0 - -999 -1104 0 - -999 -1109 0 - -1022 -1051 0 - -1022 -1073 0 - -1022 -1094 0 - -1022 -1104 0 - -1022 -1109 0 - -1051 -1073 0 - -1051 -1094 0 - -1051 -1104 0 - -1051 -1109 0 - -1073 -1094 0 - -1073 -1104 0 - -1073 -1109 0 - -1094 -1104 0 - -1094 -1109 0 - -1104 -1109 0 - -991 -1006 0 - -991 -1013 0 - -991 -1040 0 - -991 -1063 0 - -991 -1087 0 - -991 -1095 0 - -1006 -1013 0 - -1006 -1040 0 - -1006 -1063 0 - -1006 -1087 0 - -1006 -1095 0 - -1013 -1040 0 - -1013 -1063 0 - -1013 -1087 0 - -1013 -1095 0 - -1040 -1063 0 - -1040 -1087 0 - -1040 -1095 0 - -1063 -1087 0 - -1063 -1095 0 - -1087 -1095 0 - -1014 -1031 0 - -1014 -1047 0 - -1014 -1055 0 - -1014 -1096 0 - -1014 -1110 0 - -1031 -1047 0 - -1031 -1055 0 - -1031 -1096 0 - -1031 -1110 0 - -1047 -1055 0 - -1047 -1096 0 - -1047 -1110 0 - -1055 -1096 0 - -1055 -1110 0 - -1096 -1110 0 - -992 -1000 0 - -992 -1023 0 - -992 -1032 0 - -992 -1048 0 - -992 -1064 0 - -992 -1097 0 - -1000 -1023 0 - -1000 -1032 0 - -1000 -1048 0 - -1000 -1064 0 - -1000 -1097 0 - -1023 -1032 0 - -1023 -1048 0 - -1023 -1064 0 - -1023 -1097 0 - -1032 -1048 0 - -1032 -1064 0 - -1032 -1097 0 - -1048 -1064 0 - -1048 -1097 0 - -1064 -1097 0 - -1007 -1015 0 - -1007 -1024 0 - -1007 -1033 0 - -1007 -1041 0 - -1007 -1065 0 - -1007 -1074 0 - -1007 -1088 0 - -1007 -1111 0 - -1015 -1024 0 - -1015 -1033 0 - -1015 -1041 0 - -1015 -1065 0 - -1015 -1074 0 - -1015 -1088 0 - -1015 -1111 0 - -1024 -1033 0 - -1024 -1041 0 - -1024 -1065 0 - -1024 -1074 0 - -1024 -1088 0 - -1024 -1111 0 - -1033 -1041 0 - -1033 -1065 0 - -1033 -1074 0 - -1033 -1088 0 - -1033 -1111 0 - -1041 -1065 0 - -1041 -1074 0 - -1041 -1088 0 - -1041 -1111 0 - -1065 -1074 0 - -1065 -1088 0 - -1065 -1111 0 - -1074 -1088 0 - -1074 -1111 0 - -1088 -1111 0 - -993 -1025 0 - -993 -1042 0 - -993 -1075 0 - -993 -1083 0 - -993 -1098 0 - -1025 -1042 0 - -1025 -1075 0 - -1025 -1083 0 - -1025 -1098 0 - -1042 -1075 0 - -1042 -1083 0 - -1042 -1098 0 - -1075 -1083 0 - -1075 -1098 0 - -1083 -1098 0 - -994 -1008 0 - -994 -1016 0 - -994 -1034 0 - -994 -1056 0 - -994 -1066 0 - -994 -1089 0 - -994 -1105 0 - -1008 -1016 0 - -1008 -1034 0 - -1008 -1056 0 - -1008 -1066 0 - -1008 -1089 0 - -1008 -1105 0 - -1016 -1034 0 - -1016 -1056 0 - -1016 -1066 0 - -1016 -1089 0 - -1016 -1105 0 - -1034 -1056 0 - -1034 -1066 0 - -1034 -1089 0 - -1034 -1105 0 - -1056 -1066 0 - -1056 -1089 0 - -1056 -1105 0 - -1066 -1089 0 - -1066 -1105 0 - -1089 -1105 0 - -1001 -1009 0 - -1001 -1043 0 - -1001 -1076 0 - -1001 -1090 0 - -1001 -1112 0 - -1009 -1043 0 - -1009 -1076 0 - -1009 -1090 0 - -1009 -1112 0 - -1043 -1076 0 - -1043 -1090 0 - -1043 -1112 0 - -1076 -1090 0 - -1076 -1112 0 - -1090 -1112 0 - -995 -1010 0 - -995 -1026 0 - -995 -1057 0 - -995 -1067 0 - -995 -1077 0 - -995 -1099 0 - -995 -1106 0 - -1010 -1026 0 - -1010 -1057 0 - -1010 -1067 0 - -1010 -1077 0 - -1010 -1099 0 - -1010 -1106 0 - -1026 -1057 0 - -1026 -1067 0 - -1026 -1077 0 - -1026 -1099 0 - -1026 -1106 0 - -1057 -1067 0 - -1057 -1077 0 - -1057 -1099 0 - -1057 -1106 0 - -1067 -1077 0 - -1067 -1099 0 - -1067 -1106 0 - -1077 -1099 0 - -1077 -1106 0 - -1099 -1106 0 - -1017 -1035 0 - -1017 -1049 0 - -1017 -1058 0 - -1017 -1078 0 - -1017 -1100 0 - -1017 -1107 0 - -1017 -1113 0 - -1035 -1049 0 - -1035 -1058 0 - -1035 -1078 0 - -1035 -1100 0 - -1035 -1107 0 - -1035 -1113 0 - -1049 -1058 0 - -1049 -1078 0 - -1049 -1100 0 - -1049 -1107 0 - -1049 -1113 0 - -1058 -1078 0 - -1058 -1100 0 - -1058 -1107 0 - -1058 -1113 0 - -1078 -1100 0 - -1078 -1107 0 - -1078 -1113 0 - -1100 -1107 0 - -1100 -1113 0 - -1107 -1113 0 - -996 -1036 0 - -996 -1044 0 - -996 -1050 0 - -996 -1108 0 - -996 -1114 0 - -1036 -1044 0 - -1036 -1050 0 - -1036 -1108 0 - -1036 -1114 0 - -1044 -1050 0 - -1044 -1108 0 - -1044 -1114 0 - -1050 -1108 0 - -1050 -1114 0 - -1108 -1114 0 - -1002 -1018 0 - -1002 -1059 0 - -1002 -1068 0 - -1018 -1059 0 - -1018 -1068 0 - -1059 -1068 0 - -1003 -1019 0 - -1003 -1027 0 - -1003 -1052 0 - -1003 -1060 0 - -1003 -1069 0 - -1003 -1079 0 - -1003 -1084 0 - -1003 -1101 0 - -1003 -1115 0 - -1019 -1027 0 - -1019 -1052 0 - -1019 -1060 0 - -1019 -1069 0 - -1019 -1079 0 - -1019 -1084 0 - -1019 -1101 0 - -1019 -1115 0 - -1027 -1052 0 - -1027 -1060 0 - -1027 -1069 0 - -1027 -1079 0 - -1027 -1084 0 - -1027 -1101 0 - -1027 -1115 0 - -1052 -1060 0 - -1052 -1069 0 - -1052 -1079 0 - -1052 -1084 0 - -1052 -1101 0 - -1052 -1115 0 - -1060 -1069 0 - -1060 -1079 0 - -1060 -1084 0 - -1060 -1101 0 - -1060 -1115 0 - -1069 -1079 0 - -1069 -1084 0 - -1069 -1101 0 - -1069 -1115 0 - -1079 -1084 0 - -1079 -1101 0 - -1079 -1115 0 - -1084 -1101 0 - -1084 -1115 0 - -1101 -1115 0 - -997 -1004 0 - -997 -1011 0 - -997 -1028 0 - -997 -1037 0 - -997 -1045 0 - -997 -1053 0 - -997 -1070 0 - -997 -1080 0 - -997 -1091 0 - -1004 -1011 0 - -1004 -1028 0 - -1004 -1037 0 - -1004 -1045 0 - -1004 -1053 0 - -1004 -1070 0 - -1004 -1080 0 - -1004 -1091 0 - -1011 -1028 0 - -1011 -1037 0 - -1011 -1045 0 - -1011 -1053 0 - -1011 -1070 0 - -1011 -1080 0 - -1011 -1091 0 - -1028 -1037 0 - -1028 -1045 0 - -1028 -1053 0 - -1028 -1070 0 - -1028 -1080 0 - -1028 -1091 0 - -1037 -1045 0 - -1037 -1053 0 - -1037 -1070 0 - -1037 -1080 0 - -1037 -1091 0 - -1045 -1053 0 - -1045 -1070 0 - -1045 -1080 0 - -1045 -1091 0 - -1053 -1070 0 - -1053 -1080 0 - -1053 -1091 0 - -1070 -1080 0 - -1070 -1091 0 - -1080 -1091 0 - -998 -1020 0 - -998 -1029 0 - -998 -1038 0 - -998 -1061 0 - -998 -1081 0 - -998 -1102 0 - -998 -1116 0 - -1020 -1029 0 - -1020 -1038 0 - -1020 -1061 0 - -1020 -1081 0 - -1020 -1102 0 - -1020 -1116 0 - -1029 -1038 0 - -1029 -1061 0 - -1029 -1081 0 - -1029 -1102 0 - -1029 -1116 0 - -1038 -1061 0 - -1038 -1081 0 - -1038 -1102 0 - -1038 -1116 0 - -1061 -1081 0 - -1061 -1102 0 - -1061 -1116 0 - -1081 -1102 0 - -1081 -1116 0 - -1102 -1116 0 - -1124 -1134 0 - -1124 -1138 0 - -1124 -1146 0 - -1134 -1138 0 - -1134 -1146 0 - -1138 -1146 0 - -1117 -1135 0 - -1117 -1147 0 - -1117 -1151 0 - -1135 -1147 0 - -1135 -1151 0 - -1147 -1151 0 - -1118 -1132 0 - -1118 -1136 0 - -1118 -1139 0 - -1118 -1148 0 - -1118 -1152 0 - -1132 -1136 0 - -1132 -1139 0 - -1132 -1148 0 - -1132 -1152 0 - -1136 -1139 0 - -1136 -1148 0 - -1136 -1152 0 - -1139 -1148 0 - -1139 -1152 0 - -1148 -1152 0 - -1121 -1125 0 - -1121 -1129 0 - -1125 -1129 0 - -1119 -1126 0 - -1119 -1133 0 - -1119 -1140 0 - -1126 -1133 0 - -1126 -1140 0 - -1133 -1140 0 - -1137 -1149 0 - -1137 -1153 0 - -1149 -1153 0 - -1122 -1141 0 - -1122 -1144 0 - -1122 -1150 0 - -1122 -1154 0 - -1141 -1144 0 - -1141 -1150 0 - -1141 -1154 0 - -1144 -1150 0 - -1144 -1154 0 - -1150 -1154 0 - -1130 -1142 0 - -1130 -1145 0 - -1142 -1145 0 - -1120 -1123 0 - -1120 -1127 0 - -1123 -1127 0 - -1128 -1131 0 - -1128 -1143 0 - -1131 -1143 0 - -1157 -1172 0 - -1157 -1183 0 - -1157 -1190 0 - -1172 -1183 0 - -1172 -1190 0 - -1183 -1190 0 - -1158 -1173 0 - -1158 -1178 0 - -1158 -1184 0 - -1173 -1178 0 - -1173 -1184 0 - -1178 -1184 0 - -1164 -1169 0 - -1164 -1174 0 - -1164 -1185 0 - -1169 -1174 0 - -1169 -1185 0 - -1174 -1185 0 - -1159 -1165 0 - -1159 -1179 0 - -1159 -1191 0 - -1165 -1179 0 - -1165 -1191 0 - -1179 -1191 0 - -1160 -1186 0 - -1155 -1180 0 - -1155 -1187 0 - -1180 -1187 0 - -1156 -1161 0 - -1156 -1166 0 - -1156 -1175 0 - -1156 -1176 0 - -1161 -1166 0 - -1161 -1175 0 - -1161 -1176 0 - -1166 -1175 0 - -1166 -1176 0 - -1175 -1176 0 - -1162 -1167 0 - -1162 -1170 0 - -1162 -1181 0 - -1162 -1188 0 - -1162 -1192 0 - -1167 -1170 0 - -1167 -1181 0 - -1167 -1188 0 - -1167 -1192 0 - -1170 -1181 0 - -1170 -1188 0 - -1170 -1192 0 - -1181 -1188 0 - -1181 -1192 0 - -1188 -1192 0 - -1163 -1168 0 - -1163 -1171 0 - -1163 -1177 0 - -1163 -1182 0 - -1163 -1189 0 - -1168 -1171 0 - -1168 -1177 0 - -1168 -1182 0 - -1168 -1189 0 - -1171 -1177 0 - -1171 -1182 0 - -1171 -1189 0 - -1177 -1182 0 - -1177 -1189 0 - -1182 -1189 0 - -1239 -1254 0 - -1239 -1287 0 - -1254 -1287 0 - -1193 -1200 0 - -1193 -1220 0 - -1193 -1230 0 - -1193 -1246 0 - -1193 -1255 0 - -1193 -1282 0 - -1193 -1294 0 - -1193 -1303 0 - -1193 -1319 0 - -1200 -1220 0 - -1200 -1230 0 - -1200 -1246 0 - -1200 -1255 0 - -1200 -1282 0 - -1200 -1294 0 - -1200 -1303 0 - -1200 -1319 0 - -1220 -1230 0 - -1220 -1246 0 - -1220 -1255 0 - -1220 -1282 0 - -1220 -1294 0 - -1220 -1303 0 - -1220 -1319 0 - -1230 -1246 0 - -1230 -1255 0 - -1230 -1282 0 - -1230 -1294 0 - -1230 -1303 0 - -1230 -1319 0 - -1246 -1255 0 - -1246 -1282 0 - -1246 -1294 0 - -1246 -1303 0 - -1246 -1319 0 - -1255 -1282 0 - -1255 -1294 0 - -1255 -1303 0 - -1255 -1319 0 - -1282 -1294 0 - -1282 -1303 0 - -1282 -1319 0 - -1294 -1303 0 - -1294 -1319 0 - -1303 -1319 0 - -1206 -1221 0 - -1206 -1240 0 - -1206 -1247 0 - -1206 -1256 0 - -1206 -1288 0 - -1206 -1295 0 - -1206 -1304 0 - -1206 -1320 0 - -1206 -1327 0 - -1221 -1240 0 - -1221 -1247 0 - -1221 -1256 0 - -1221 -1288 0 - -1221 -1295 0 - -1221 -1304 0 - -1221 -1320 0 - -1221 -1327 0 - -1240 -1247 0 - -1240 -1256 0 - -1240 -1288 0 - -1240 -1295 0 - -1240 -1304 0 - -1240 -1320 0 - -1240 -1327 0 - -1247 -1256 0 - -1247 -1288 0 - -1247 -1295 0 - -1247 -1304 0 - -1247 -1320 0 - -1247 -1327 0 - -1256 -1288 0 - -1256 -1295 0 - -1256 -1304 0 - -1256 -1320 0 - -1256 -1327 0 - -1288 -1295 0 - -1288 -1304 0 - -1288 -1320 0 - -1288 -1327 0 - -1295 -1304 0 - -1295 -1320 0 - -1295 -1327 0 - -1304 -1320 0 - -1304 -1327 0 - -1320 -1327 0 - -1194 -1222 0 - -1194 -1248 0 - -1194 -1257 0 - -1194 -1267 0 - -1194 -1273 0 - -1194 -1283 0 - -1194 -1321 0 - -1222 -1248 0 - -1222 -1257 0 - -1222 -1267 0 - -1222 -1273 0 - -1222 -1283 0 - -1222 -1321 0 - -1248 -1257 0 - -1248 -1267 0 - -1248 -1273 0 - -1248 -1283 0 - -1248 -1321 0 - -1257 -1267 0 - -1257 -1273 0 - -1257 -1283 0 - -1257 -1321 0 - -1267 -1273 0 - -1267 -1283 0 - -1267 -1321 0 - -1273 -1283 0 - -1273 -1321 0 - -1283 -1321 0 - -1201 -1207 0 - -1201 -1231 0 - -1201 -1296 0 - -1201 -1305 0 - -1201 -1313 0 - -1207 -1231 0 - -1207 -1296 0 - -1207 -1305 0 - -1207 -1313 0 - -1231 -1296 0 - -1231 -1305 0 - -1231 -1313 0 - -1296 -1305 0 - -1296 -1313 0 - -1305 -1313 0 - -1208 -1232 0 - -1208 -1258 0 - -1208 -1268 0 - -1208 -1274 0 - -1208 -1289 0 - -1208 -1297 0 - -1208 -1306 0 - -1208 -1314 0 - -1208 -1328 0 - -1232 -1258 0 - -1232 -1268 0 - -1232 -1274 0 - -1232 -1289 0 - -1232 -1297 0 - -1232 -1306 0 - -1232 -1314 0 - -1232 -1328 0 - -1258 -1268 0 - -1258 -1274 0 - -1258 -1289 0 - -1258 -1297 0 - -1258 -1306 0 - -1258 -1314 0 - -1258 -1328 0 - -1268 -1274 0 - -1268 -1289 0 - -1268 -1297 0 - -1268 -1306 0 - -1268 -1314 0 - -1268 -1328 0 - -1274 -1289 0 - -1274 -1297 0 - -1274 -1306 0 - -1274 -1314 0 - -1274 -1328 0 - -1289 -1297 0 - -1289 -1306 0 - -1289 -1314 0 - -1289 -1328 0 - -1297 -1306 0 - -1297 -1314 0 - -1297 -1328 0 - -1306 -1314 0 - -1306 -1328 0 - -1314 -1328 0 - -1195 -1223 0 - -1195 -1249 0 - -1195 -1275 0 - -1195 -1284 0 - -1195 -1290 0 - -1195 -1298 0 - -1195 -1322 0 - -1223 -1249 0 - -1223 -1275 0 - -1223 -1284 0 - -1223 -1290 0 - -1223 -1298 0 - -1223 -1322 0 - -1249 -1275 0 - -1249 -1284 0 - -1249 -1290 0 - -1249 -1298 0 - -1249 -1322 0 - -1275 -1284 0 - -1275 -1290 0 - -1275 -1298 0 - -1275 -1322 0 - -1284 -1290 0 - -1284 -1298 0 - -1284 -1322 0 - -1290 -1298 0 - -1290 -1322 0 - -1298 -1322 0 - -1202 -1213 0 - -1202 -1224 0 - -1202 -1233 0 - -1202 -1250 0 - -1202 -1259 0 - -1202 -1299 0 - -1202 -1307 0 - -1202 -1329 0 - -1213 -1224 0 - -1213 -1233 0 - -1213 -1250 0 - -1213 -1259 0 - -1213 -1299 0 - -1213 -1307 0 - -1213 -1329 0 - -1224 -1233 0 - -1224 -1250 0 - -1224 -1259 0 - -1224 -1299 0 - -1224 -1307 0 - -1224 -1329 0 - -1233 -1250 0 - -1233 -1259 0 - -1233 -1299 0 - -1233 -1307 0 - -1233 -1329 0 - -1250 -1259 0 - -1250 -1299 0 - -1250 -1307 0 - -1250 -1329 0 - -1259 -1299 0 - -1259 -1307 0 - -1259 -1329 0 - -1299 -1307 0 - -1299 -1329 0 - -1307 -1329 0 - -1203 -1209 0 - -1203 -1214 0 - -1203 -1234 0 - -1203 -1269 0 - -1203 -1291 0 - -1203 -1308 0 - -1203 -1330 0 - -1209 -1214 0 - -1209 -1234 0 - -1209 -1269 0 - -1209 -1291 0 - -1209 -1308 0 - -1209 -1330 0 - -1214 -1234 0 - -1214 -1269 0 - -1214 -1291 0 - -1214 -1308 0 - -1214 -1330 0 - -1234 -1269 0 - -1234 -1291 0 - -1234 -1308 0 - -1234 -1330 0 - -1269 -1291 0 - -1269 -1308 0 - -1269 -1330 0 - -1291 -1308 0 - -1291 -1330 0 - -1308 -1330 0 - -1225 -1235 0 - -1225 -1241 0 - -1225 -1251 0 - -1225 -1260 0 - -1225 -1276 0 - -1225 -1285 0 - -1225 -1292 0 - -1225 -1309 0 - -1225 -1323 0 - -1235 -1241 0 - -1235 -1251 0 - -1235 -1260 0 - -1235 -1276 0 - -1235 -1285 0 - -1235 -1292 0 - -1235 -1309 0 - -1235 -1323 0 - -1241 -1251 0 - -1241 -1260 0 - -1241 -1276 0 - -1241 -1285 0 - -1241 -1292 0 - -1241 -1309 0 - -1241 -1323 0 - -1251 -1260 0 - -1251 -1276 0 - -1251 -1285 0 - -1251 -1292 0 - -1251 -1309 0 - -1251 -1323 0 - -1260 -1276 0 - -1260 -1285 0 - -1260 -1292 0 - -1260 -1309 0 - -1260 -1323 0 - -1276 -1285 0 - -1276 -1292 0 - -1276 -1309 0 - -1276 -1323 0 - -1285 -1292 0 - -1285 -1309 0 - -1285 -1323 0 - -1292 -1309 0 - -1292 -1323 0 - -1309 -1323 0 - -1196 -1226 0 - -1196 -1236 0 - -1196 -1261 0 - -1196 -1270 0 - -1196 -1300 0 - -1196 -1310 0 - -1196 -1315 0 - -1196 -1331 0 - -1226 -1236 0 - -1226 -1261 0 - -1226 -1270 0 - -1226 -1300 0 - -1226 -1310 0 - -1226 -1315 0 - -1226 -1331 0 - -1236 -1261 0 - -1236 -1270 0 - -1236 -1300 0 - -1236 -1310 0 - -1236 -1315 0 - -1236 -1331 0 - -1261 -1270 0 - -1261 -1300 0 - -1261 -1310 0 - -1261 -1315 0 - -1261 -1331 0 - -1270 -1300 0 - -1270 -1310 0 - -1270 -1315 0 - -1270 -1331 0 - -1300 -1310 0 - -1300 -1315 0 - -1300 -1331 0 - -1310 -1315 0 - -1310 -1331 0 - -1315 -1331 0 - -1204 -1227 0 - -1204 -1271 0 - -1204 -1293 0 - -1204 -1311 0 - -1204 -1324 0 - -1227 -1271 0 - -1227 -1293 0 - -1227 -1311 0 - -1227 -1324 0 - -1271 -1293 0 - -1271 -1311 0 - -1271 -1324 0 - -1293 -1311 0 - -1293 -1324 0 - -1311 -1324 0 - -1215 -1242 0 - -1215 -1262 0 - -1215 -1277 0 - -1215 -1301 0 - -1215 -1316 0 - -1215 -1332 0 - -1242 -1262 0 - -1242 -1277 0 - -1242 -1301 0 - -1242 -1316 0 - -1242 -1332 0 - -1262 -1277 0 - -1262 -1301 0 - -1262 -1316 0 - -1262 -1332 0 - -1277 -1301 0 - -1277 -1316 0 - -1277 -1332 0 - -1301 -1316 0 - -1301 -1332 0 - -1316 -1332 0 - -1197 -1216 0 - -1197 -1243 0 - -1197 -1252 0 - -1197 -1278 0 - -1197 -1317 0 - -1197 -1325 0 - -1216 -1243 0 - -1216 -1252 0 - -1216 -1278 0 - -1216 -1317 0 - -1216 -1325 0 - -1243 -1252 0 - -1243 -1278 0 - -1243 -1317 0 - -1243 -1325 0 - -1252 -1278 0 - -1252 -1317 0 - -1252 -1325 0 - -1278 -1317 0 - -1278 -1325 0 - -1317 -1325 0 - -1210 -1217 0 - -1210 -1263 0 - -1210 -1279 0 - -1210 -1318 0 - -1217 -1263 0 - -1217 -1279 0 - -1217 -1318 0 - -1263 -1279 0 - -1263 -1318 0 - -1279 -1318 0 - -1211 -1237 0 - -1211 -1264 0 - -1211 -1272 0 - -1211 -1280 0 - -1211 -1302 0 - -1237 -1264 0 - -1237 -1272 0 - -1237 -1280 0 - -1237 -1302 0 - -1264 -1272 0 - -1264 -1280 0 - -1264 -1302 0 - -1272 -1280 0 - -1272 -1302 0 - -1280 -1302 0 - -1198 -1212 0 - -1198 -1218 0 - -1198 -1228 0 - -1198 -1238 0 - -1198 -1244 0 - -1198 -1253 0 - -1198 -1265 0 - -1198 -1281 0 - -1198 -1286 0 - -1198 -1312 0 - -1198 -1326 0 - -1198 -1333 0 - -1212 -1218 0 - -1212 -1228 0 - -1212 -1238 0 - -1212 -1244 0 - -1212 -1253 0 - -1212 -1265 0 - -1212 -1281 0 - -1212 -1286 0 - -1212 -1312 0 - -1212 -1326 0 - -1212 -1333 0 - -1218 -1228 0 - -1218 -1238 0 - -1218 -1244 0 - -1218 -1253 0 - -1218 -1265 0 - -1218 -1281 0 - -1218 -1286 0 - -1218 -1312 0 - -1218 -1326 0 - -1218 -1333 0 - -1228 -1238 0 - -1228 -1244 0 - -1228 -1253 0 - -1228 -1265 0 - -1228 -1281 0 - -1228 -1286 0 - -1228 -1312 0 - -1228 -1326 0 - -1228 -1333 0 - -1238 -1244 0 - -1238 -1253 0 - -1238 -1265 0 - -1238 -1281 0 - -1238 -1286 0 - -1238 -1312 0 - -1238 -1326 0 - -1238 -1333 0 - -1244 -1253 0 - -1244 -1265 0 - -1244 -1281 0 - -1244 -1286 0 - -1244 -1312 0 - -1244 -1326 0 - -1244 -1333 0 - -1253 -1265 0 - -1253 -1281 0 - -1253 -1286 0 - -1253 -1312 0 - -1253 -1326 0 - -1253 -1333 0 - -1265 -1281 0 - -1265 -1286 0 - -1265 -1312 0 - -1265 -1326 0 - -1265 -1333 0 - -1281 -1286 0 - -1281 -1312 0 - -1281 -1326 0 - -1281 -1333 0 - -1286 -1312 0 - -1286 -1326 0 - -1286 -1333 0 - -1312 -1326 0 - -1312 -1333 0 - -1326 -1333 0 - -1199 -1205 0 - -1199 -1219 0 - -1199 -1229 0 - -1199 -1245 0 - -1199 -1266 0 - -1205 -1219 0 - -1205 -1229 0 - -1205 -1245 0 - -1205 -1266 0 - -1219 -1229 0 - -1219 -1245 0 - -1219 -1266 0 - -1229 -1245 0 - -1229 -1266 0 - -1245 -1266 0 - -1334 -1341 0 - -1334 -1347 0 - -1334 -1352 0 - -1334 -1359 0 - -1334 -1365 0 - -1334 -1379 0 - -1334 -1387 0 - -1334 -1399 0 - -1334 -1413 0 - -1334 -1417 0 - -1334 -1434 0 - -1341 -1347 0 - -1341 -1352 0 - -1341 -1359 0 - -1341 -1365 0 - -1341 -1379 0 - -1341 -1387 0 - -1341 -1399 0 - -1341 -1413 0 - -1341 -1417 0 - -1341 -1434 0 - -1347 -1352 0 - -1347 -1359 0 - -1347 -1365 0 - -1347 -1379 0 - -1347 -1387 0 - -1347 -1399 0 - -1347 -1413 0 - -1347 -1417 0 - -1347 -1434 0 - -1352 -1359 0 - -1352 -1365 0 - -1352 -1379 0 - -1352 -1387 0 - -1352 -1399 0 - -1352 -1413 0 - -1352 -1417 0 - -1352 -1434 0 - -1359 -1365 0 - -1359 -1379 0 - -1359 -1387 0 - -1359 -1399 0 - -1359 -1413 0 - -1359 -1417 0 - -1359 -1434 0 - -1365 -1379 0 - -1365 -1387 0 - -1365 -1399 0 - -1365 -1413 0 - -1365 -1417 0 - -1365 -1434 0 - -1379 -1387 0 - -1379 -1399 0 - -1379 -1413 0 - -1379 -1417 0 - -1379 -1434 0 - -1387 -1399 0 - -1387 -1413 0 - -1387 -1417 0 - -1387 -1434 0 - -1399 -1413 0 - -1399 -1417 0 - -1399 -1434 0 - -1413 -1417 0 - -1413 -1434 0 - -1417 -1434 0 - -1342 -1353 0 - -1342 -1360 0 - -1342 -1371 0 - -1342 -1380 0 - -1342 -1418 0 - -1342 -1428 0 - -1342 -1435 0 - -1342 -1442 0 - -1353 -1360 0 - -1353 -1371 0 - -1353 -1380 0 - -1353 -1418 0 - -1353 -1428 0 - -1353 -1435 0 - -1353 -1442 0 - -1360 -1371 0 - -1360 -1380 0 - -1360 -1418 0 - -1360 -1428 0 - -1360 -1435 0 - -1360 -1442 0 - -1371 -1380 0 - -1371 -1418 0 - -1371 -1428 0 - -1371 -1435 0 - -1371 -1442 0 - -1380 -1418 0 - -1380 -1428 0 - -1380 -1435 0 - -1380 -1442 0 - -1418 -1428 0 - -1418 -1435 0 - -1418 -1442 0 - -1428 -1435 0 - -1428 -1442 0 - -1435 -1442 0 - -1366 -1372 0 - -1366 -1388 0 - -1366 -1407 0 - -1366 -1414 0 - -1366 -1419 0 - -1372 -1388 0 - -1372 -1407 0 - -1372 -1414 0 - -1372 -1419 0 - -1388 -1407 0 - -1388 -1414 0 - -1388 -1419 0 - -1407 -1414 0 - -1407 -1419 0 - -1414 -1419 0 - -1389 -1394 0 - -1389 -1400 0 - -1389 -1420 0 - -1389 -1429 0 - -1389 -1443 0 - -1394 -1400 0 - -1394 -1420 0 - -1394 -1429 0 - -1394 -1443 0 - -1400 -1420 0 - -1400 -1429 0 - -1400 -1443 0 - -1420 -1429 0 - -1420 -1443 0 - -1429 -1443 0 - -1348 -1354 0 - -1348 -1381 0 - -1348 -1421 0 - -1348 -1444 0 - -1354 -1381 0 - -1354 -1421 0 - -1354 -1444 0 - -1381 -1421 0 - -1381 -1444 0 - -1421 -1444 0 - -1349 -1390 0 - -1349 -1408 0 - -1349 -1445 0 - -1390 -1408 0 - -1390 -1445 0 - -1408 -1445 0 - -1350 -1361 0 - -1350 -1373 0 - -1350 -1401 0 - -1350 -1422 0 - -1350 -1430 0 - -1350 -1436 0 - -1350 -1446 0 - -1361 -1373 0 - -1361 -1401 0 - -1361 -1422 0 - -1361 -1430 0 - -1361 -1436 0 - -1361 -1446 0 - -1373 -1401 0 - -1373 -1422 0 - -1373 -1430 0 - -1373 -1436 0 - -1373 -1446 0 - -1401 -1422 0 - -1401 -1430 0 - -1401 -1436 0 - -1401 -1446 0 - -1422 -1430 0 - -1422 -1436 0 - -1422 -1446 0 - -1430 -1436 0 - -1430 -1446 0 - -1436 -1446 0 - -1335 -1367 0 - -1335 -1374 0 - -1335 -1391 0 - -1335 -1402 0 - -1335 -1409 0 - -1335 -1423 0 - -1335 -1447 0 - -1367 -1374 0 - -1367 -1391 0 - -1367 -1402 0 - -1367 -1409 0 - -1367 -1423 0 - -1367 -1447 0 - -1374 -1391 0 - -1374 -1402 0 - -1374 -1409 0 - -1374 -1423 0 - -1374 -1447 0 - -1391 -1402 0 - -1391 -1409 0 - -1391 -1423 0 - -1391 -1447 0 - -1402 -1409 0 - -1402 -1423 0 - -1402 -1447 0 - -1409 -1423 0 - -1409 -1447 0 - -1423 -1447 0 - -1343 -1355 0 - -1343 -1368 0 - -1343 -1382 0 - -1343 -1415 0 - -1343 -1431 0 - -1343 -1437 0 - -1355 -1368 0 - -1355 -1382 0 - -1355 -1415 0 - -1355 -1431 0 - -1355 -1437 0 - -1368 -1382 0 - -1368 -1415 0 - -1368 -1431 0 - -1368 -1437 0 - -1382 -1415 0 - -1382 -1431 0 - -1382 -1437 0 - -1415 -1431 0 - -1415 -1437 0 - -1431 -1437 0 - -1351 -1383 0 - -1351 -1395 0 - -1351 -1403 0 - -1351 -1424 0 - -1383 -1395 0 - -1383 -1403 0 - -1383 -1424 0 - -1395 -1403 0 - -1395 -1424 0 - -1403 -1424 0 - -1336 -1356 0 - -1336 -1410 0 - -1336 -1425 0 - -1336 -1448 0 - -1356 -1410 0 - -1356 -1425 0 - -1356 -1448 0 - -1410 -1425 0 - -1410 -1448 0 - -1425 -1448 0 - -1337 -1369 0 - -1337 -1375 0 - -1337 -1384 0 - -1337 -1432 0 - -1337 -1438 0 - -1369 -1375 0 - -1369 -1384 0 - -1369 -1432 0 - -1369 -1438 0 - -1375 -1384 0 - -1375 -1432 0 - -1375 -1438 0 - -1384 -1432 0 - -1384 -1438 0 - -1432 -1438 0 - -1362 -1370 0 - -1362 -1392 0 - -1362 -1411 0 - -1362 -1439 0 - -1370 -1392 0 - -1370 -1411 0 - -1370 -1439 0 - -1392 -1411 0 - -1392 -1439 0 - -1411 -1439 0 - -1344 -1363 0 - -1344 -1396 0 - -1344 -1404 0 - -1363 -1396 0 - -1363 -1404 0 - -1396 -1404 0 - -1338 -1345 0 - -1338 -1357 0 - -1338 -1376 0 - -1338 -1385 0 - -1338 -1397 0 - -1338 -1405 0 - -1338 -1412 0 - -1338 -1416 0 - -1338 -1440 0 - -1338 -1449 0 - -1345 -1357 0 - -1345 -1376 0 - -1345 -1385 0 - -1345 -1397 0 - -1345 -1405 0 - -1345 -1412 0 - -1345 -1416 0 - -1345 -1440 0 - -1345 -1449 0 - -1357 -1376 0 - -1357 -1385 0 - -1357 -1397 0 - -1357 -1405 0 - -1357 -1412 0 - -1357 -1416 0 - -1357 -1440 0 - -1357 -1449 0 - -1376 -1385 0 - -1376 -1397 0 - -1376 -1405 0 - -1376 -1412 0 - -1376 -1416 0 - -1376 -1440 0 - -1376 -1449 0 - -1385 -1397 0 - -1385 -1405 0 - -1385 -1412 0 - -1385 -1416 0 - -1385 -1440 0 - -1385 -1449 0 - -1397 -1405 0 - -1397 -1412 0 - -1397 -1416 0 - -1397 -1440 0 - -1397 -1449 0 - -1405 -1412 0 - -1405 -1416 0 - -1405 -1440 0 - -1405 -1449 0 - -1412 -1416 0 - -1412 -1440 0 - -1412 -1449 0 - -1416 -1440 0 - -1416 -1449 0 - -1440 -1449 0 - -1339 -1364 0 - -1339 -1377 0 - -1339 -1426 0 - -1339 -1433 0 - -1339 -1450 0 - -1364 -1377 0 - -1364 -1426 0 - -1364 -1433 0 - -1364 -1450 0 - -1377 -1426 0 - -1377 -1433 0 - -1377 -1450 0 - -1426 -1433 0 - -1426 -1450 0 - -1433 -1450 0 - -1340 -1346 0 - -1340 -1358 0 - -1340 -1378 0 - -1340 -1386 0 - -1340 -1393 0 - -1340 -1398 0 - -1340 -1406 0 - -1340 -1427 0 - -1340 -1441 0 - -1340 -1451 0 - -1346 -1358 0 - -1346 -1378 0 - -1346 -1386 0 - -1346 -1393 0 - -1346 -1398 0 - -1346 -1406 0 - -1346 -1427 0 - -1346 -1441 0 - -1346 -1451 0 - -1358 -1378 0 - -1358 -1386 0 - -1358 -1393 0 - -1358 -1398 0 - -1358 -1406 0 - -1358 -1427 0 - -1358 -1441 0 - -1358 -1451 0 - -1378 -1386 0 - -1378 -1393 0 - -1378 -1398 0 - -1378 -1406 0 - -1378 -1427 0 - -1378 -1441 0 - -1378 -1451 0 - -1386 -1393 0 - -1386 -1398 0 - -1386 -1406 0 - -1386 -1427 0 - -1386 -1441 0 - -1386 -1451 0 - -1393 -1398 0 - -1393 -1406 0 - -1393 -1427 0 - -1393 -1441 0 - -1393 -1451 0 - -1398 -1406 0 - -1398 -1427 0 - -1398 -1441 0 - -1398 -1451 0 - -1406 -1427 0 - -1406 -1441 0 - -1406 -1451 0 - -1427 -1441 0 - -1427 -1451 0 - -1441 -1451 0 - -1452 -1459 0 - -1452 -1463 0 - -1452 -1469 0 - -1452 -1502 0 - -1459 -1463 0 - -1459 -1469 0 - -1459 -1502 0 - -1463 -1469 0 - -1463 -1502 0 - -1469 -1502 0 - -1453 -1464 0 - -1453 -1489 0 - -1453 -1503 0 - -1453 -1510 0 - -1464 -1489 0 - -1464 -1503 0 - -1464 -1510 0 - -1489 -1503 0 - -1489 -1510 0 - -1503 -1510 0 - -1454 -1470 0 - -1454 -1476 0 - -1454 -1490 0 - -1454 -1497 0 - -1454 -1504 0 - -1470 -1476 0 - -1470 -1490 0 - -1470 -1497 0 - -1470 -1504 0 - -1476 -1490 0 - -1476 -1497 0 - -1476 -1504 0 - -1490 -1497 0 - -1490 -1504 0 - -1497 -1504 0 - -1471 -1477 0 - -1471 -1491 0 - -1471 -1511 0 - -1477 -1491 0 - -1477 -1511 0 - -1491 -1511 0 - -1455 -1465 0 - -1455 -1478 0 - -1455 -1483 0 - -1455 -1486 0 - -1455 -1505 0 - -1465 -1478 0 - -1465 -1483 0 - -1465 -1486 0 - -1465 -1505 0 - -1478 -1483 0 - -1478 -1486 0 - -1478 -1505 0 - -1483 -1486 0 - -1483 -1505 0 - -1486 -1505 0 - -1460 -1466 0 - -1460 -1473 0 - -1460 -1479 0 - -1460 -1487 0 - -1460 -1492 0 - -1460 -1498 0 - -1460 -1506 0 - -1460 -1512 0 - -1466 -1473 0 - -1466 -1479 0 - -1466 -1487 0 - -1466 -1492 0 - -1466 -1498 0 - -1466 -1506 0 - -1466 -1512 0 - -1473 -1479 0 - -1473 -1487 0 - -1473 -1492 0 - -1473 -1498 0 - -1473 -1506 0 - -1473 -1512 0 - -1479 -1487 0 - -1479 -1492 0 - -1479 -1498 0 - -1479 -1506 0 - -1479 -1512 0 - -1487 -1492 0 - -1487 -1498 0 - -1487 -1506 0 - -1487 -1512 0 - -1492 -1498 0 - -1492 -1506 0 - -1492 -1512 0 - -1498 -1506 0 - -1498 -1512 0 - -1506 -1512 0 - -1472 -1499 0 - -1472 -1507 0 - -1472 -1513 0 - -1499 -1507 0 - -1499 -1513 0 - -1507 -1513 0 - -1456 -1484 0 - -1456 -1488 0 - -1456 -1493 0 - -1456 -1508 0 - -1484 -1488 0 - -1484 -1493 0 - -1484 -1508 0 - -1488 -1493 0 - -1488 -1508 0 - -1493 -1508 0 - -1461 -1480 0 - -1461 -1485 0 - -1480 -1485 0 - -1457 -1462 0 - -1457 -1494 0 - -1457 -1509 0 - -1462 -1494 0 - -1462 -1509 0 - -1494 -1509 0 - -1467 -1474 0 - -1467 -1481 0 - -1467 -1495 0 - -1467 -1500 0 - -1467 -1514 0 - -1474 -1481 0 - -1474 -1495 0 - -1474 -1500 0 - -1474 -1514 0 - -1481 -1495 0 - -1481 -1500 0 - -1481 -1514 0 - -1495 -1500 0 - -1495 -1514 0 - -1500 -1514 0 - -1458 -1468 0 - -1458 -1475 0 - -1458 -1482 0 - -1458 -1496 0 - -1458 -1501 0 - -1468 -1475 0 - -1468 -1482 0 - -1468 -1496 0 - -1468 -1501 0 - -1475 -1482 0 - -1475 -1496 0 - -1475 -1501 0 - -1482 -1496 0 - -1482 -1501 0 - -1496 -1501 0 - -1554 -1572 0 - -1554 -1589 0 - -1554 -1595 0 - -1572 -1589 0 - -1572 -1595 0 - -1589 -1595 0 - -1522 -1530 0 - -1522 -1544 0 - -1522 -1558 0 - -1522 -1605 0 - -1522 -1618 0 - -1522 -1624 0 - -1530 -1544 0 - -1530 -1558 0 - -1530 -1605 0 - -1530 -1618 0 - -1530 -1624 0 - -1544 -1558 0 - -1544 -1605 0 - -1544 -1618 0 - -1544 -1624 0 - -1558 -1605 0 - -1558 -1618 0 - -1558 -1624 0 - -1605 -1618 0 - -1605 -1624 0 - -1618 -1624 0 - -1515 -1523 0 - -1515 -1545 0 - -1515 -1559 0 - -1515 -1573 0 - -1515 -1581 0 - -1515 -1590 0 - -1515 -1614 0 - -1515 -1619 0 - -1523 -1545 0 - -1523 -1559 0 - -1523 -1573 0 - -1523 -1581 0 - -1523 -1590 0 - -1523 -1614 0 - -1523 -1619 0 - -1545 -1559 0 - -1545 -1573 0 - -1545 -1581 0 - -1545 -1590 0 - -1545 -1614 0 - -1545 -1619 0 - -1559 -1573 0 - -1559 -1581 0 - -1559 -1590 0 - -1559 -1614 0 - -1559 -1619 0 - -1573 -1581 0 - -1573 -1590 0 - -1573 -1614 0 - -1573 -1619 0 - -1581 -1590 0 - -1581 -1614 0 - -1581 -1619 0 - -1590 -1614 0 - -1590 -1619 0 - -1614 -1619 0 - -1524 -1531 0 - -1524 -1574 0 - -1524 -1606 0 - -1531 -1574 0 - -1531 -1606 0 - -1574 -1606 0 - -1516 -1546 0 - -1516 -1566 0 - -1516 -1575 0 - -1516 -1582 0 - -1516 -1596 0 - -1516 -1607 0 - -1516 -1615 0 - -1516 -1625 0 - -1546 -1566 0 - -1546 -1575 0 - -1546 -1582 0 - -1546 -1596 0 - -1546 -1607 0 - -1546 -1615 0 - -1546 -1625 0 - -1566 -1575 0 - -1566 -1582 0 - -1566 -1596 0 - -1566 -1607 0 - -1566 -1615 0 - -1566 -1625 0 - -1575 -1582 0 - -1575 -1596 0 - -1575 -1607 0 - -1575 -1615 0 - -1575 -1625 0 - -1582 -1596 0 - -1582 -1607 0 - -1582 -1615 0 - -1582 -1625 0 - -1596 -1607 0 - -1596 -1615 0 - -1596 -1625 0 - -1607 -1615 0 - -1607 -1625 0 - -1615 -1625 0 - -1517 -1525 0 - -1517 -1547 0 - -1517 -1560 0 - -1517 -1576 0 - -1517 -1583 0 - -1517 -1597 0 - -1517 -1608 0 - -1517 -1620 0 - -1525 -1547 0 - -1525 -1560 0 - -1525 -1576 0 - -1525 -1583 0 - -1525 -1597 0 - -1525 -1608 0 - -1525 -1620 0 - -1547 -1560 0 - -1547 -1576 0 - -1547 -1583 0 - -1547 -1597 0 - -1547 -1608 0 - -1547 -1620 0 - -1560 -1576 0 - -1560 -1583 0 - -1560 -1597 0 - -1560 -1608 0 - -1560 -1620 0 - -1576 -1583 0 - -1576 -1597 0 - -1576 -1608 0 - -1576 -1620 0 - -1583 -1597 0 - -1583 -1608 0 - -1583 -1620 0 - -1597 -1608 0 - -1597 -1620 0 - -1608 -1620 0 - -1538 -1548 0 - -1538 -1561 0 - -1538 -1567 0 - -1538 -1598 0 - -1538 -1609 0 - -1538 -1626 0 - -1548 -1561 0 - -1548 -1567 0 - -1548 -1598 0 - -1548 -1609 0 - -1548 -1626 0 - -1561 -1567 0 - -1561 -1598 0 - -1561 -1609 0 - -1561 -1626 0 - -1567 -1598 0 - -1567 -1609 0 - -1567 -1626 0 - -1598 -1609 0 - -1598 -1626 0 - -1609 -1626 0 - -1526 -1532 0 - -1526 -1539 0 - -1526 -1549 0 - -1526 -1562 0 - -1526 -1591 0 - -1526 -1599 0 - -1526 -1621 0 - -1532 -1539 0 - -1532 -1549 0 - -1532 -1562 0 - -1532 -1591 0 - -1532 -1599 0 - -1532 -1621 0 - -1539 -1549 0 - -1539 -1562 0 - -1539 -1591 0 - -1539 -1599 0 - -1539 -1621 0 - -1549 -1562 0 - -1549 -1591 0 - -1549 -1599 0 - -1549 -1621 0 - -1562 -1591 0 - -1562 -1599 0 - -1562 -1621 0 - -1591 -1599 0 - -1591 -1621 0 - -1599 -1621 0 - -1533 -1540 0 - -1533 -1563 0 - -1533 -1584 0 - -1533 -1600 0 - -1533 -1610 0 - -1540 -1563 0 - -1540 -1584 0 - -1540 -1600 0 - -1540 -1610 0 - -1563 -1584 0 - -1563 -1600 0 - -1563 -1610 0 - -1584 -1600 0 - -1584 -1610 0 - -1600 -1610 0 - -1518 -1550 0 - -1518 -1555 0 - -1518 -1568 0 - -1518 -1577 0 - -1518 -1601 0 - -1518 -1611 0 - -1518 -1627 0 - -1550 -1555 0 - -1550 -1568 0 - -1550 -1577 0 - -1550 -1601 0 - -1550 -1611 0 - -1550 -1627 0 - -1555 -1568 0 - -1555 -1577 0 - -1555 -1601 0 - -1555 -1611 0 - -1555 -1627 0 - -1568 -1577 0 - -1568 -1601 0 - -1568 -1611 0 - -1568 -1627 0 - -1577 -1601 0 - -1577 -1611 0 - -1577 -1627 0 - -1601 -1611 0 - -1601 -1627 0 - -1611 -1627 0 - -1519 -1541 0 - -1519 -1564 0 - -1519 -1585 0 - -1519 -1592 0 - -1519 -1622 0 - -1541 -1564 0 - -1541 -1585 0 - -1541 -1592 0 - -1541 -1622 0 - -1564 -1585 0 - -1564 -1592 0 - -1564 -1622 0 - -1585 -1592 0 - -1585 -1622 0 - -1592 -1622 0 - -1527 -1534 0 - -1527 -1542 0 - -1527 -1556 0 - -1527 -1569 0 - -1527 -1586 0 - -1527 -1602 0 - -1527 -1616 0 - -1534 -1542 0 - -1534 -1556 0 - -1534 -1569 0 - -1534 -1586 0 - -1534 -1602 0 - -1534 -1616 0 - -1542 -1556 0 - -1542 -1569 0 - -1542 -1586 0 - -1542 -1602 0 - -1542 -1616 0 - -1556 -1569 0 - -1556 -1586 0 - -1556 -1602 0 - -1556 -1616 0 - -1569 -1586 0 - -1569 -1602 0 - -1569 -1616 0 - -1586 -1602 0 - -1586 -1616 0 - -1602 -1616 0 - -1528 -1535 0 - -1528 -1570 0 - -1528 -1578 0 - -1528 -1587 0 - -1528 -1593 0 - -1528 -1612 0 - -1528 -1617 0 - -1535 -1570 0 - -1535 -1578 0 - -1535 -1587 0 - -1535 -1593 0 - -1535 -1612 0 - -1535 -1617 0 - -1570 -1578 0 - -1570 -1587 0 - -1570 -1593 0 - -1570 -1612 0 - -1570 -1617 0 - -1578 -1587 0 - -1578 -1593 0 - -1578 -1612 0 - -1578 -1617 0 - -1587 -1593 0 - -1587 -1612 0 - -1587 -1617 0 - -1593 -1612 0 - -1593 -1617 0 - -1612 -1617 0 - -1520 -1536 0 - -1520 -1551 0 - -1520 -1565 0 - -1520 -1579 0 - -1520 -1588 0 - -1520 -1603 0 - -1536 -1551 0 - -1536 -1565 0 - -1536 -1579 0 - -1536 -1588 0 - -1536 -1603 0 - -1551 -1565 0 - -1551 -1579 0 - -1551 -1588 0 - -1551 -1603 0 - -1565 -1579 0 - -1565 -1588 0 - -1565 -1603 0 - -1579 -1588 0 - -1579 -1603 0 - -1588 -1603 0 - -1537 -1552 0 - -1537 -1557 0 - -1537 -1571 0 - -1537 -1580 0 - -1537 -1594 0 - -1537 -1604 0 - -1537 -1613 0 - -1537 -1623 0 - -1537 -1628 0 - -1552 -1557 0 - -1552 -1571 0 - -1552 -1580 0 - -1552 -1594 0 - -1552 -1604 0 - -1552 -1613 0 - -1552 -1623 0 - -1552 -1628 0 - -1557 -1571 0 - -1557 -1580 0 - -1557 -1594 0 - -1557 -1604 0 - -1557 -1613 0 - -1557 -1623 0 - -1557 -1628 0 - -1571 -1580 0 - -1571 -1594 0 - -1571 -1604 0 - -1571 -1613 0 - -1571 -1623 0 - -1571 -1628 0 - -1580 -1594 0 - -1580 -1604 0 - -1580 -1613 0 - -1580 -1623 0 - -1580 -1628 0 - -1594 -1604 0 - -1594 -1613 0 - -1594 -1623 0 - -1594 -1628 0 - -1604 -1613 0 - -1604 -1623 0 - -1604 -1628 0 - -1613 -1623 0 - -1613 -1628 0 - -1623 -1628 0 - -1521 -1529 0 - -1521 -1543 0 - -1521 -1553 0 - -1529 -1543 0 - -1529 -1553 0 - -1543 -1553 0 - -1645 -1657 0 - -1629 -1635 0 - -1629 -1642 0 - -1629 -1646 0 - -1629 -1658 0 - -1635 -1642 0 - -1635 -1646 0 - -1635 -1658 0 - -1642 -1646 0 - -1642 -1658 0 - -1646 -1658 0 - -1630 -1643 0 - -1630 -1647 0 - -1630 -1652 0 - -1630 -1659 0 - -1630 -1663 0 - -1630 -1669 0 - -1643 -1647 0 - -1643 -1652 0 - -1643 -1659 0 - -1643 -1663 0 - -1643 -1669 0 - -1647 -1652 0 - -1647 -1659 0 - -1647 -1663 0 - -1647 -1669 0 - -1652 -1659 0 - -1652 -1663 0 - -1652 -1669 0 - -1659 -1663 0 - -1659 -1669 0 - -1663 -1669 0 - -1636 -1660 0 - -1636 -1666 0 - -1660 -1666 0 - -1631 -1637 0 - -1631 -1653 0 - -1631 -1661 0 - -1631 -1667 0 - -1637 -1653 0 - -1637 -1661 0 - -1637 -1667 0 - -1653 -1661 0 - -1653 -1667 0 - -1661 -1667 0 - -1632 -1638 0 - -1632 -1662 0 - -1632 -1664 0 - -1632 -1668 0 - -1638 -1662 0 - -1638 -1664 0 - -1638 -1668 0 - -1662 -1664 0 - -1662 -1668 0 - -1664 -1668 0 - -1649 -1654 0 - -1639 -1650 0 - -1639 -1655 0 - -1650 -1655 0 - -1633 -1640 0 - -1633 -1648 0 - -1633 -1651 0 - -1633 -1656 0 - -1633 -1665 0 - -1633 -1670 0 - -1640 -1648 0 - -1640 -1651 0 - -1640 -1656 0 - -1640 -1665 0 - -1640 -1670 0 - -1648 -1651 0 - -1648 -1656 0 - -1648 -1665 0 - -1648 -1670 0 - -1651 -1656 0 - -1651 -1665 0 - -1651 -1670 0 - -1656 -1665 0 - -1656 -1670 0 - -1665 -1670 0 - -1634 -1641 0 - -1634 -1644 0 - -1634 -1671 0 - -1641 -1644 0 - -1641 -1671 0 - -1644 -1671 0 - -1678 -1683 0 - -1678 -1688 0 - -1678 -1692 0 - -1678 -1695 0 - -1683 -1688 0 - -1683 -1692 0 - -1683 -1695 0 - -1688 -1692 0 - -1688 -1695 0 - -1692 -1695 0 - -1679 -1684 0 - -1679 -1693 0 - -1679 -1696 0 - -1684 -1693 0 - -1684 -1696 0 - -1693 -1696 0 - -1675 -1681 0 - -1675 -1697 0 - -1681 -1697 0 - -1685 -1689 0 - -1685 -1698 0 - -1689 -1698 0 - -1686 -1699 0 - -1672 -1676 0 - -1672 -1680 0 - -1676 -1680 0 - -1673 -1690 0 - -1673 -1700 0 - -1690 -1700 0 - -1674 -1677 0 - -1674 -1682 0 - -1674 -1687 0 - -1674 -1691 0 - -1674 -1694 0 - -1677 -1682 0 - -1677 -1687 0 - -1677 -1691 0 - -1677 -1694 0 - -1682 -1687 0 - -1682 -1691 0 - -1682 -1694 0 - -1687 -1691 0 - -1687 -1694 0 - -1691 -1694 0 - -1701 -1707 0 - -1701 -1722 0 - -1701 -1738 0 - -1701 -1745 0 - -1701 -1749 0 - -1701 -1764 0 - -1701 -1790 0 - -1707 -1722 0 - -1707 -1738 0 - -1707 -1745 0 - -1707 -1749 0 - -1707 -1764 0 - -1707 -1790 0 - -1722 -1738 0 - -1722 -1745 0 - -1722 -1749 0 - -1722 -1764 0 - -1722 -1790 0 - -1738 -1745 0 - -1738 -1749 0 - -1738 -1764 0 - -1738 -1790 0 - -1745 -1749 0 - -1745 -1764 0 - -1745 -1790 0 - -1749 -1764 0 - -1749 -1790 0 - -1764 -1790 0 - -1708 -1754 0 - -1708 -1791 0 - -1708 -1798 0 - -1754 -1791 0 - -1754 -1798 0 - -1791 -1798 0 - -1702 -1723 0 - -1702 -1730 0 - -1702 -1755 0 - -1702 -1765 0 - -1702 -1778 0 - -1702 -1803 0 - -1723 -1730 0 - -1723 -1755 0 - -1723 -1765 0 - -1723 -1778 0 - -1723 -1803 0 - -1730 -1755 0 - -1730 -1765 0 - -1730 -1778 0 - -1730 -1803 0 - -1755 -1765 0 - -1755 -1778 0 - -1755 -1803 0 - -1765 -1778 0 - -1765 -1803 0 - -1778 -1803 0 - -1731 -1750 0 - -1731 -1756 0 - -1731 -1766 0 - -1731 -1779 0 - -1731 -1799 0 - -1750 -1756 0 - -1750 -1766 0 - -1750 -1779 0 - -1750 -1799 0 - -1756 -1766 0 - -1756 -1779 0 - -1756 -1799 0 - -1766 -1779 0 - -1766 -1799 0 - -1779 -1799 0 - -1739 -1751 0 - -1739 -1767 0 - -1739 -1773 0 - -1739 -1780 0 - -1739 -1787 0 - -1739 -1792 0 - -1739 -1800 0 - -1751 -1767 0 - -1751 -1773 0 - -1751 -1780 0 - -1751 -1787 0 - -1751 -1792 0 - -1751 -1800 0 - -1767 -1773 0 - -1767 -1780 0 - -1767 -1787 0 - -1767 -1792 0 - -1767 -1800 0 - -1773 -1780 0 - -1773 -1787 0 - -1773 -1792 0 - -1773 -1800 0 - -1780 -1787 0 - -1780 -1792 0 - -1780 -1800 0 - -1787 -1792 0 - -1787 -1800 0 - -1792 -1800 0 - -1709 -1715 0 - -1709 -1724 0 - -1709 -1746 0 - -1709 -1768 0 - -1709 -1793 0 - -1715 -1724 0 - -1715 -1746 0 - -1715 -1768 0 - -1715 -1793 0 - -1724 -1746 0 - -1724 -1768 0 - -1724 -1793 0 - -1746 -1768 0 - -1746 -1793 0 - -1768 -1793 0 - -1710 -1716 0 - -1710 -1740 0 - -1710 -1752 0 - -1710 -1757 0 - -1710 -1774 0 - -1710 -1788 0 - -1710 -1794 0 - -1716 -1740 0 - -1716 -1752 0 - -1716 -1757 0 - -1716 -1774 0 - -1716 -1788 0 - -1716 -1794 0 - -1740 -1752 0 - -1740 -1757 0 - -1740 -1774 0 - -1740 -1788 0 - -1740 -1794 0 - -1752 -1757 0 - -1752 -1774 0 - -1752 -1788 0 - -1752 -1794 0 - -1757 -1774 0 - -1757 -1788 0 - -1757 -1794 0 - -1774 -1788 0 - -1774 -1794 0 - -1788 -1794 0 - -1711 -1717 0 - -1711 -1732 0 - -1711 -1781 0 - -1711 -1804 0 - -1717 -1732 0 - -1717 -1781 0 - -1717 -1804 0 - -1732 -1781 0 - -1732 -1804 0 - -1781 -1804 0 - -1703 -1718 0 - -1703 -1733 0 - -1703 -1753 0 - -1703 -1758 0 - -1718 -1733 0 - -1718 -1753 0 - -1718 -1758 0 - -1733 -1753 0 - -1733 -1758 0 - -1753 -1758 0 - -1704 -1712 0 - -1704 -1725 0 - -1704 -1734 0 - -1704 -1741 0 - -1704 -1759 0 - -1704 -1795 0 - -1704 -1801 0 - -1712 -1725 0 - -1712 -1734 0 - -1712 -1741 0 - -1712 -1759 0 - -1712 -1795 0 - -1712 -1801 0 - -1725 -1734 0 - -1725 -1741 0 - -1725 -1759 0 - -1725 -1795 0 - -1725 -1801 0 - -1734 -1741 0 - -1734 -1759 0 - -1734 -1795 0 - -1734 -1801 0 - -1741 -1759 0 - -1741 -1795 0 - -1741 -1801 0 - -1759 -1795 0 - -1759 -1801 0 - -1795 -1801 0 - -1713 -1726 0 - -1713 -1760 0 - -1713 -1775 0 - -1713 -1789 0 - -1713 -1796 0 - -1726 -1760 0 - -1726 -1775 0 - -1726 -1789 0 - -1726 -1796 0 - -1760 -1775 0 - -1760 -1789 0 - -1760 -1796 0 - -1775 -1789 0 - -1775 -1796 0 - -1789 -1796 0 - -1719 -1735 0 - -1719 -1742 0 - -1719 -1769 0 - -1719 -1782 0 - -1719 -1802 0 - -1719 -1805 0 - -1735 -1742 0 - -1735 -1769 0 - -1735 -1782 0 - -1735 -1802 0 - -1735 -1805 0 - -1742 -1769 0 - -1742 -1782 0 - -1742 -1802 0 - -1742 -1805 0 - -1769 -1782 0 - -1769 -1802 0 - -1769 -1805 0 - -1782 -1802 0 - -1782 -1805 0 - -1802 -1805 0 - -1761 -1770 0 - -1761 -1776 0 - -1761 -1783 0 - -1761 -1806 0 - -1770 -1776 0 - -1770 -1783 0 - -1770 -1806 0 - -1776 -1783 0 - -1776 -1806 0 - -1783 -1806 0 - -1705 -1720 0 - -1705 -1727 0 - -1705 -1736 0 - -1705 -1743 0 - -1705 -1747 0 - -1705 -1762 0 - -1705 -1771 0 - -1705 -1784 0 - -1705 -1797 0 - -1720 -1727 0 - -1720 -1736 0 - -1720 -1743 0 - -1720 -1747 0 - -1720 -1762 0 - -1720 -1771 0 - -1720 -1784 0 - -1720 -1797 0 - -1727 -1736 0 - -1727 -1743 0 - -1727 -1747 0 - -1727 -1762 0 - -1727 -1771 0 - -1727 -1784 0 - -1727 -1797 0 - -1736 -1743 0 - -1736 -1747 0 - -1736 -1762 0 - -1736 -1771 0 - -1736 -1784 0 - -1736 -1797 0 - -1743 -1747 0 - -1743 -1762 0 - -1743 -1771 0 - -1743 -1784 0 - -1743 -1797 0 - -1747 -1762 0 - -1747 -1771 0 - -1747 -1784 0 - -1747 -1797 0 - -1762 -1771 0 - -1762 -1784 0 - -1762 -1797 0 - -1771 -1784 0 - -1771 -1797 0 - -1784 -1797 0 - -1706 -1714 0 - -1706 -1728 0 - -1706 -1737 0 - -1706 -1748 0 - -1706 -1763 0 - -1706 -1785 0 - -1714 -1728 0 - -1714 -1737 0 - -1714 -1748 0 - -1714 -1763 0 - -1714 -1785 0 - -1728 -1737 0 - -1728 -1748 0 - -1728 -1763 0 - -1728 -1785 0 - -1737 -1748 0 - -1737 -1763 0 - -1737 -1785 0 - -1748 -1763 0 - -1748 -1785 0 - -1763 -1785 0 - -1721 -1729 0 - -1721 -1744 0 - -1721 -1772 0 - -1721 -1777 0 - -1721 -1786 0 - -1729 -1744 0 - -1729 -1772 0 - -1729 -1777 0 - -1729 -1786 0 - -1744 -1772 0 - -1744 -1777 0 - -1744 -1786 0 - -1772 -1777 0 - -1772 -1786 0 - -1777 -1786 0 - -1807 -1813 0 - -1807 -1823 0 - -1807 -1829 0 - -1807 -1834 0 - -1807 -1840 0 - -1807 -1845 0 - -1813 -1823 0 - -1813 -1829 0 - -1813 -1834 0 - -1813 -1840 0 - -1813 -1845 0 - -1823 -1829 0 - -1823 -1834 0 - -1823 -1840 0 - -1823 -1845 0 - -1829 -1834 0 - -1829 -1840 0 - -1829 -1845 0 - -1834 -1840 0 - -1834 -1845 0 - -1840 -1845 0 - -1808 -1824 0 - -1808 -1830 0 - -1808 -1835 0 - -1824 -1830 0 - -1824 -1835 0 - -1830 -1835 0 - -1818 -1825 0 - -1818 -1831 0 - -1818 -1841 0 - -1818 -1850 0 - -1825 -1831 0 - -1825 -1841 0 - -1825 -1850 0 - -1831 -1841 0 - -1831 -1850 0 - -1841 -1850 0 - -1814 -1826 0 - -1814 -1837 0 - -1814 -1846 0 - -1826 -1837 0 - -1826 -1846 0 - -1837 -1846 0 - -1815 -1819 0 - -1815 -1827 0 - -1815 -1842 0 - -1819 -1827 0 - -1819 -1842 0 - -1827 -1842 0 - -1809 -1820 0 - -1809 -1838 0 - -1809 -1847 0 - -1809 -1851 0 - -1820 -1838 0 - -1820 -1847 0 - -1820 -1851 0 - -1838 -1847 0 - -1838 -1851 0 - -1847 -1851 0 - -1810 -1848 0 - -1816 -1832 0 - -1816 -1836 0 - -1816 -1843 0 - -1832 -1836 0 - -1832 -1843 0 - -1836 -1843 0 - -1811 -1817 0 - -1811 -1821 0 - -1811 -1833 0 - -1811 -1849 0 - -1817 -1821 0 - -1817 -1833 0 - -1817 -1849 0 - -1821 -1833 0 - -1821 -1849 0 - -1833 -1849 0 - -1812 -1822 0 - -1812 -1828 0 - -1812 -1839 0 - -1812 -1844 0 - -1822 -1828 0 - -1822 -1839 0 - -1822 -1844 0 - -1828 -1839 0 - -1828 -1844 0 - -1839 -1844 0 - -1852 -1857 0 - -1852 -1863 0 - -1852 -1865 0 - -1852 -1869 0 - -1852 -1876 0 - -1857 -1863 0 - -1857 -1865 0 - -1857 -1869 0 - -1857 -1876 0 - -1863 -1865 0 - -1863 -1869 0 - -1863 -1876 0 - -1865 -1869 0 - -1865 -1876 0 - -1869 -1876 0 - -1853 -1858 0 - -1853 -1870 0 - -1853 -1877 0 - -1858 -1870 0 - -1858 -1877 0 - -1870 -1877 0 - -1854 -1859 0 - -1854 -1866 0 - -1854 -1874 0 - -1859 -1866 0 - -1859 -1874 0 - -1866 -1874 0 - -1855 -1860 0 - -1855 -1871 0 - -1855 -1878 0 - -1860 -1871 0 - -1860 -1878 0 - -1871 -1878 0 - -1872 -1879 0 - -1856 -1861 0 - -1856 -1864 0 - -1856 -1867 0 - -1856 -1873 0 - -1856 -1880 0 - -1861 -1864 0 - -1861 -1867 0 - -1861 -1873 0 - -1861 -1880 0 - -1864 -1867 0 - -1864 -1873 0 - -1864 -1880 0 - -1867 -1873 0 - -1867 -1880 0 - -1873 -1880 0 - -1862 -1868 0 - -1862 -1875 0 - -1862 -1881 0 - -1868 -1875 0 - -1868 -1881 0 - -1875 -1881 0 - -1882 -1891 0 - -1882 -1906 0 - -1882 -1931 0 - -1882 -1956 0 - -1882 -1964 0 - -1882 -1973 0 - -1891 -1906 0 - -1891 -1931 0 - -1891 -1956 0 - -1891 -1964 0 - -1891 -1973 0 - -1906 -1931 0 - -1906 -1956 0 - -1906 -1964 0 - -1906 -1973 0 - -1931 -1956 0 - -1931 -1964 0 - -1931 -1973 0 - -1956 -1964 0 - -1956 -1973 0 - -1964 -1973 0 - -1883 -1896 0 - -1883 -1907 0 - -1883 -1914 0 - -1883 -1932 0 - -1883 -1939 0 - -1896 -1907 0 - -1896 -1914 0 - -1896 -1932 0 - -1896 -1939 0 - -1907 -1914 0 - -1907 -1932 0 - -1907 -1939 0 - -1914 -1932 0 - -1914 -1939 0 - -1932 -1939 0 - -1884 -1924 0 - -1884 -1933 0 - -1884 -1940 0 - -1884 -1949 0 - -1884 -1965 0 - -1924 -1933 0 - -1924 -1940 0 - -1924 -1949 0 - -1924 -1965 0 - -1933 -1940 0 - -1933 -1949 0 - -1933 -1965 0 - -1940 -1949 0 - -1940 -1965 0 - -1949 -1965 0 - -1900 -1915 0 - -1900 -1919 0 - -1900 -1925 0 - -1900 -1934 0 - -1900 -1941 0 - -1900 -1957 0 - -1900 -1966 0 - -1900 -1974 0 - -1915 -1919 0 - -1915 -1925 0 - -1915 -1934 0 - -1915 -1941 0 - -1915 -1957 0 - -1915 -1966 0 - -1915 -1974 0 - -1919 -1925 0 - -1919 -1934 0 - -1919 -1941 0 - -1919 -1957 0 - -1919 -1966 0 - -1919 -1974 0 - -1925 -1934 0 - -1925 -1941 0 - -1925 -1957 0 - -1925 -1966 0 - -1925 -1974 0 - -1934 -1941 0 - -1934 -1957 0 - -1934 -1966 0 - -1934 -1974 0 - -1941 -1957 0 - -1941 -1966 0 - -1941 -1974 0 - -1957 -1966 0 - -1957 -1974 0 - -1966 -1974 0 - -1885 -1892 0 - -1885 -1897 0 - -1885 -1908 0 - -1885 -1926 0 - -1885 -1942 0 - -1885 -1950 0 - -1885 -1958 0 - -1885 -1967 0 - -1892 -1897 0 - -1892 -1908 0 - -1892 -1926 0 - -1892 -1942 0 - -1892 -1950 0 - -1892 -1958 0 - -1892 -1967 0 - -1897 -1908 0 - -1897 -1926 0 - -1897 -1942 0 - -1897 -1950 0 - -1897 -1958 0 - -1897 -1967 0 - -1908 -1926 0 - -1908 -1942 0 - -1908 -1950 0 - -1908 -1958 0 - -1908 -1967 0 - -1926 -1942 0 - -1926 -1950 0 - -1926 -1958 0 - -1926 -1967 0 - -1942 -1950 0 - -1942 -1958 0 - -1942 -1967 0 - -1950 -1958 0 - -1950 -1967 0 - -1958 -1967 0 - -1901 -1916 0 - -1901 -1951 0 - -1901 -1959 0 - -1901 -1975 0 - -1916 -1951 0 - -1916 -1959 0 - -1916 -1975 0 - -1951 -1959 0 - -1951 -1975 0 - -1959 -1975 0 - -1893 -1927 0 - -1893 -1943 0 - -1893 -1968 0 - -1927 -1943 0 - -1927 -1968 0 - -1943 -1968 0 - -1886 -1894 0 - -1886 -1909 0 - -1886 -1935 0 - -1886 -1944 0 - -1886 -1960 0 - -1886 -1976 0 - -1894 -1909 0 - -1894 -1935 0 - -1894 -1944 0 - -1894 -1960 0 - -1894 -1976 0 - -1909 -1935 0 - -1909 -1944 0 - -1909 -1960 0 - -1909 -1976 0 - -1935 -1944 0 - -1935 -1960 0 - -1935 -1976 0 - -1944 -1960 0 - -1944 -1976 0 - -1960 -1976 0 - -1887 -1898 0 - -1887 -1910 0 - -1887 -1936 0 - -1887 -1952 0 - -1887 -1977 0 - -1898 -1910 0 - -1898 -1936 0 - -1898 -1952 0 - -1898 -1977 0 - -1910 -1936 0 - -1910 -1952 0 - -1910 -1977 0 - -1936 -1952 0 - -1936 -1977 0 - -1952 -1977 0 - -1911 -1920 0 - -1911 -1928 0 - -1911 -1945 0 - -1911 -1953 0 - -1911 -1961 0 - -1911 -1969 0 - -1920 -1928 0 - -1920 -1945 0 - -1920 -1953 0 - -1920 -1961 0 - -1920 -1969 0 - -1928 -1945 0 - -1928 -1953 0 - -1928 -1961 0 - -1928 -1969 0 - -1945 -1953 0 - -1945 -1961 0 - -1945 -1969 0 - -1953 -1961 0 - -1953 -1969 0 - -1961 -1969 0 - -1895 -1902 0 - -1895 -1937 0 - -1895 -1946 0 - -1895 -1970 0 - -1902 -1937 0 - -1902 -1946 0 - -1902 -1970 0 - -1937 -1946 0 - -1937 -1970 0 - -1946 -1970 0 - -1888 -1903 0 - -1888 -1912 0 - -1888 -1921 0 - -1888 -1929 0 - -1888 -1938 0 - -1888 -1947 0 - -1888 -1978 0 - -1903 -1912 0 - -1903 -1921 0 - -1903 -1929 0 - -1903 -1938 0 - -1903 -1947 0 - -1903 -1978 0 - -1912 -1921 0 - -1912 -1929 0 - -1912 -1938 0 - -1912 -1947 0 - -1912 -1978 0 - -1921 -1929 0 - -1921 -1938 0 - -1921 -1947 0 - -1921 -1978 0 - -1929 -1938 0 - -1929 -1947 0 - -1929 -1978 0 - -1938 -1947 0 - -1938 -1978 0 - -1947 -1978 0 - -1889 -1899 0 - -1889 -1904 0 - -1889 -1917 0 - -1889 -1922 0 - -1889 -1954 0 - -1889 -1962 0 - -1889 -1971 0 - -1889 -1979 0 - -1899 -1904 0 - -1899 -1917 0 - -1899 -1922 0 - -1899 -1954 0 - -1899 -1962 0 - -1899 -1971 0 - -1899 -1979 0 - -1904 -1917 0 - -1904 -1922 0 - -1904 -1954 0 - -1904 -1962 0 - -1904 -1971 0 - -1904 -1979 0 - -1917 -1922 0 - -1917 -1954 0 - -1917 -1962 0 - -1917 -1971 0 - -1917 -1979 0 - -1922 -1954 0 - -1922 -1962 0 - -1922 -1971 0 - -1922 -1979 0 - -1954 -1962 0 - -1954 -1971 0 - -1954 -1979 0 - -1962 -1971 0 - -1962 -1979 0 - -1971 -1979 0 - -1890 -1905 0 - -1890 -1913 0 - -1890 -1918 0 - -1890 -1923 0 - -1890 -1930 0 - -1890 -1948 0 - -1890 -1955 0 - -1890 -1963 0 - -1890 -1972 0 - -1905 -1913 0 - -1905 -1918 0 - -1905 -1923 0 - -1905 -1930 0 - -1905 -1948 0 - -1905 -1955 0 - -1905 -1963 0 - -1905 -1972 0 - -1913 -1918 0 - -1913 -1923 0 - -1913 -1930 0 - -1913 -1948 0 - -1913 -1955 0 - -1913 -1963 0 - -1913 -1972 0 - -1918 -1923 0 - -1918 -1930 0 - -1918 -1948 0 - -1918 -1955 0 - -1918 -1963 0 - -1918 -1972 0 - -1923 -1930 0 - -1923 -1948 0 - -1923 -1955 0 - -1923 -1963 0 - -1923 -1972 0 - -1930 -1948 0 - -1930 -1955 0 - -1930 -1963 0 - -1930 -1972 0 - -1948 -1955 0 - -1948 -1963 0 - -1948 -1972 0 - -1955 -1963 0 - -1955 -1972 0 - -1963 -1972 0 - -1989 -1993 0 - -1989 -1998 0 - -1989 -2010 0 - -1989 -2025 0 - -1989 -2033 0 - -1993 -1998 0 - -1993 -2010 0 - -1993 -2025 0 - -1993 -2033 0 - -1998 -2010 0 - -1998 -2025 0 - -1998 -2033 0 - -2010 -2025 0 - -2010 -2033 0 - -2025 -2033 0 - -1999 -2004 0 - -1999 -2014 0 - -1999 -2020 0 - -1999 -2026 0 - -1999 -2036 0 - -1999 -2040 0 - -2004 -2014 0 - -2004 -2020 0 - -2004 -2026 0 - -2004 -2036 0 - -2004 -2040 0 - -2014 -2020 0 - -2014 -2026 0 - -2014 -2036 0 - -2014 -2040 0 - -2020 -2026 0 - -2020 -2036 0 - -2020 -2040 0 - -2026 -2036 0 - -2026 -2040 0 - -2036 -2040 0 - -1980 -2000 0 - -1980 -2011 0 - -1980 -2015 0 - -1980 -2027 0 - -1980 -2037 0 - -1980 -2041 0 - -2000 -2011 0 - -2000 -2015 0 - -2000 -2027 0 - -2000 -2037 0 - -2000 -2041 0 - -2011 -2015 0 - -2011 -2027 0 - -2011 -2037 0 - -2011 -2041 0 - -2015 -2027 0 - -2015 -2037 0 - -2015 -2041 0 - -2027 -2037 0 - -2027 -2041 0 - -2037 -2041 0 - -1981 -1994 0 - -1981 -2001 0 - -1981 -2005 0 - -1981 -2021 0 - -1981 -2038 0 - -1981 -2042 0 - -1994 -2001 0 - -1994 -2005 0 - -1994 -2021 0 - -1994 -2038 0 - -1994 -2042 0 - -2001 -2005 0 - -2001 -2021 0 - -2001 -2038 0 - -2001 -2042 0 - -2005 -2021 0 - -2005 -2038 0 - -2005 -2042 0 - -2021 -2038 0 - -2021 -2042 0 - -2038 -2042 0 - -1982 -1995 0 - -1982 -2012 0 - -1982 -2028 0 - -1982 -2034 0 - -1982 -2039 0 - -1995 -2012 0 - -1995 -2028 0 - -1995 -2034 0 - -1995 -2039 0 - -2012 -2028 0 - -2012 -2034 0 - -2012 -2039 0 - -2028 -2034 0 - -2028 -2039 0 - -2034 -2039 0 - -1983 -1990 0 - -1983 -2006 0 - -1983 -2016 0 - -1983 -2029 0 - -1983 -2043 0 - -1990 -2006 0 - -1990 -2016 0 - -1990 -2029 0 - -1990 -2043 0 - -2006 -2016 0 - -2006 -2029 0 - -2006 -2043 0 - -2016 -2029 0 - -2016 -2043 0 - -2029 -2043 0 - -1984 -1991 0 - -1984 -1996 0 - -1984 -2017 0 - -1984 -2022 0 - -1991 -1996 0 - -1991 -2017 0 - -1991 -2022 0 - -1996 -2017 0 - -1996 -2022 0 - -2017 -2022 0 - -1985 -1992 0 - -1985 -2018 0 - -1985 -2030 0 - -1992 -2018 0 - -1992 -2030 0 - -2018 -2030 0 - -1986 -2002 0 - -1986 -2007 0 - -2002 -2007 0 - -1987 -2003 0 - -1987 -2008 0 - -1987 -2013 0 - -1987 -2023 0 - -1987 -2031 0 - -1987 -2044 0 - -2003 -2008 0 - -2003 -2013 0 - -2003 -2023 0 - -2003 -2031 0 - -2003 -2044 0 - -2008 -2013 0 - -2008 -2023 0 - -2008 -2031 0 - -2008 -2044 0 - -2013 -2023 0 - -2013 -2031 0 - -2013 -2044 0 - -2023 -2031 0 - -2023 -2044 0 - -2031 -2044 0 - -1988 -2032 0 - -1988 -2045 0 - -2032 -2045 0 - -1997 -2009 0 - -1997 -2019 0 - -1997 -2024 0 - -1997 -2035 0 - -1997 -2046 0 - -2009 -2019 0 - -2009 -2024 0 - -2009 -2035 0 - -2009 -2046 0 - -2019 -2024 0 - -2019 -2035 0 - -2019 -2046 0 - -2024 -2035 0 - -2024 -2046 0 - -2035 -2046 0 - -2078 -2082 0 - -2078 -2089 0 - -2082 -2089 0 - -2047 -2053 0 - -2047 -2061 0 - -2047 -2067 0 - -2047 -2072 0 - -2047 -2083 0 - -2047 -2096 0 - -2053 -2061 0 - -2053 -2067 0 - -2053 -2072 0 - -2053 -2083 0 - -2053 -2096 0 - -2061 -2067 0 - -2061 -2072 0 - -2061 -2083 0 - -2061 -2096 0 - -2067 -2072 0 - -2067 -2083 0 - -2067 -2096 0 - -2072 -2083 0 - -2072 -2096 0 - -2083 -2096 0 - -2068 -2079 0 - -2068 -2084 0 - -2079 -2084 0 - -2048 -2057 0 - -2048 -2069 0 - -2048 -2080 0 - -2048 -2102 0 - -2057 -2069 0 - -2057 -2080 0 - -2057 -2102 0 - -2069 -2080 0 - -2069 -2102 0 - -2080 -2102 0 - -2062 -2073 0 - -2062 -2085 0 - -2062 -2090 0 - -2062 -2094 0 - -2062 -2097 0 - -2073 -2085 0 - -2073 -2090 0 - -2073 -2094 0 - -2073 -2097 0 - -2085 -2090 0 - -2085 -2094 0 - -2085 -2097 0 - -2090 -2094 0 - -2090 -2097 0 - -2094 -2097 0 - -2049 -2074 0 - -2049 -2086 0 - -2049 -2098 0 - -2049 -2103 0 - -2074 -2086 0 - -2074 -2098 0 - -2074 -2103 0 - -2086 -2098 0 - -2086 -2103 0 - -2098 -2103 0 - -2054 -2063 0 - -2054 -2075 0 - -2054 -2081 0 - -2054 -2091 0 - -2054 -2099 0 - -2063 -2075 0 - -2063 -2081 0 - -2063 -2091 0 - -2063 -2099 0 - -2075 -2081 0 - -2075 -2091 0 - -2075 -2099 0 - -2081 -2091 0 - -2081 -2099 0 - -2091 -2099 0 - -2055 -2058 0 - -2055 -2087 0 - -2058 -2087 0 - -2050 -2059 0 - -2050 -2064 0 - -2050 -2070 0 - -2050 -2076 0 - -2059 -2064 0 - -2059 -2070 0 - -2059 -2076 0 - -2064 -2070 0 - -2064 -2076 0 - -2070 -2076 0 - -2051 -2088 0 - -2051 -2104 0 - -2088 -2104 0 - -2060 -2065 0 - -2060 -2071 0 - -2060 -2092 0 - -2060 -2095 0 - -2060 -2100 0 - -2060 -2105 0 - -2065 -2071 0 - -2065 -2092 0 - -2065 -2095 0 - -2065 -2100 0 - -2065 -2105 0 - -2071 -2092 0 - -2071 -2095 0 - -2071 -2100 0 - -2071 -2105 0 - -2092 -2095 0 - -2092 -2100 0 - -2092 -2105 0 - -2095 -2100 0 - -2095 -2105 0 - -2100 -2105 0 - -2052 -2056 0 - -2052 -2066 0 - -2052 -2077 0 - -2052 -2093 0 - -2052 -2101 0 - -2052 -2106 0 - -2056 -2066 0 - -2056 -2077 0 - -2056 -2093 0 - -2056 -2101 0 - -2056 -2106 0 - -2066 -2077 0 - -2066 -2093 0 - -2066 -2101 0 - -2066 -2106 0 - -2077 -2093 0 - -2077 -2101 0 - -2077 -2106 0 - -2093 -2101 0 - -2093 -2106 0 - -2101 -2106 0 - -2114 -2127 0 - -2114 -2132 0 - -2114 -2138 0 - -2114 -2144 0 - -2114 -2149 0 - -2114 -2162 0 - -2114 -2168 0 - -2127 -2132 0 - -2127 -2138 0 - -2127 -2144 0 - -2127 -2149 0 - -2127 -2162 0 - -2127 -2168 0 - -2132 -2138 0 - -2132 -2144 0 - -2132 -2149 0 - -2132 -2162 0 - -2132 -2168 0 - -2138 -2144 0 - -2138 -2149 0 - -2138 -2162 0 - -2138 -2168 0 - -2144 -2149 0 - -2144 -2162 0 - -2144 -2168 0 - -2149 -2162 0 - -2149 -2168 0 - -2162 -2168 0 - -2112 -2125 0 - -2112 -2128 0 - -2112 -2139 0 - -2112 -2150 0 - -2112 -2157 0 - -2112 -2169 0 - -2125 -2128 0 - -2125 -2139 0 - -2125 -2150 0 - -2125 -2157 0 - -2125 -2169 0 - -2128 -2139 0 - -2128 -2150 0 - -2128 -2157 0 - -2128 -2169 0 - -2139 -2150 0 - -2139 -2157 0 - -2139 -2169 0 - -2150 -2157 0 - -2150 -2169 0 - -2157 -2169 0 - -2115 -2140 0 - -2115 -2145 0 - -2115 -2151 0 - -2115 -2163 0 - -2140 -2145 0 - -2140 -2151 0 - -2140 -2163 0 - -2145 -2151 0 - -2145 -2163 0 - -2151 -2163 0 - -2113 -2133 0 - -2113 -2146 0 - -2133 -2146 0 - -2122 -2129 0 - -2122 -2152 0 - -2122 -2164 0 - -2122 -2170 0 - -2122 -2176 0 - -2129 -2152 0 - -2129 -2164 0 - -2129 -2170 0 - -2129 -2176 0 - -2152 -2164 0 - -2152 -2170 0 - -2152 -2176 0 - -2164 -2170 0 - -2164 -2176 0 - -2170 -2176 0 - -2116 -2134 0 - -2116 -2141 0 - -2116 -2171 0 - -2116 -2177 0 - -2134 -2141 0 - -2134 -2171 0 - -2134 -2177 0 - -2141 -2171 0 - -2141 -2177 0 - -2171 -2177 0 - -2107 -2117 0 - -2107 -2130 0 - -2107 -2135 0 - -2107 -2147 0 - -2107 -2158 0 - -2117 -2130 0 - -2117 -2135 0 - -2117 -2147 0 - -2117 -2158 0 - -2130 -2135 0 - -2130 -2147 0 - -2130 -2158 0 - -2135 -2147 0 - -2135 -2158 0 - -2147 -2158 0 - -2108 -2118 0 - -2108 -2123 0 - -2108 -2136 0 - -2108 -2153 0 - -2108 -2159 0 - -2108 -2165 0 - -2108 -2172 0 - -2108 -2178 0 - -2118 -2123 0 - -2118 -2136 0 - -2118 -2153 0 - -2118 -2159 0 - -2118 -2165 0 - -2118 -2172 0 - -2118 -2178 0 - -2123 -2136 0 - -2123 -2153 0 - -2123 -2159 0 - -2123 -2165 0 - -2123 -2172 0 - -2123 -2178 0 - -2136 -2153 0 - -2136 -2159 0 - -2136 -2165 0 - -2136 -2172 0 - -2136 -2178 0 - -2153 -2159 0 - -2153 -2165 0 - -2153 -2172 0 - -2153 -2178 0 - -2159 -2165 0 - -2159 -2172 0 - -2159 -2178 0 - -2165 -2172 0 - -2165 -2178 0 - -2172 -2178 0 - -2109 -2119 0 - -2109 -2137 0 - -2109 -2173 0 - -2109 -2179 0 - -2119 -2137 0 - -2119 -2173 0 - -2119 -2179 0 - -2137 -2173 0 - -2137 -2179 0 - -2173 -2179 0 - -2124 -2160 0 - -2124 -2174 0 - -2124 -2180 0 - -2160 -2174 0 - -2160 -2180 0 - -2174 -2180 0 - -2154 -2161 0 - -2142 -2155 0 - -2142 -2175 0 - -2155 -2175 0 - -2110 -2120 0 - -2110 -2126 0 - -2110 -2131 0 - -2110 -2148 0 - -2110 -2156 0 - -2110 -2166 0 - -2110 -2181 0 - -2120 -2126 0 - -2120 -2131 0 - -2120 -2148 0 - -2120 -2156 0 - -2120 -2166 0 - -2120 -2181 0 - -2126 -2131 0 - -2126 -2148 0 - -2126 -2156 0 - -2126 -2166 0 - -2126 -2181 0 - -2131 -2148 0 - -2131 -2156 0 - -2131 -2166 0 - -2131 -2181 0 - -2148 -2156 0 - -2148 -2166 0 - -2148 -2181 0 - -2156 -2166 0 - -2156 -2181 0 - -2166 -2181 0 - -2111 -2121 0 - -2111 -2143 0 - -2111 -2167 0 - -2121 -2143 0 - -2121 -2167 0 - -2143 -2167 0 - -2182 -2191 0 - -2182 -2208 0 - -2182 -2217 0 - -2182 -2236 0 - -2182 -2252 0 - -2182 -2277 0 - -2182 -2289 0 - -2182 -2302 0 - -2182 -2325 0 - -2191 -2208 0 - -2191 -2217 0 - -2191 -2236 0 - -2191 -2252 0 - -2191 -2277 0 - -2191 -2289 0 - -2191 -2302 0 - -2191 -2325 0 - -2208 -2217 0 - -2208 -2236 0 - -2208 -2252 0 - -2208 -2277 0 - -2208 -2289 0 - -2208 -2302 0 - -2208 -2325 0 - -2217 -2236 0 - -2217 -2252 0 - -2217 -2277 0 - -2217 -2289 0 - -2217 -2302 0 - -2217 -2325 0 - -2236 -2252 0 - -2236 -2277 0 - -2236 -2289 0 - -2236 -2302 0 - -2236 -2325 0 - -2252 -2277 0 - -2252 -2289 0 - -2252 -2302 0 - -2252 -2325 0 - -2277 -2289 0 - -2277 -2302 0 - -2277 -2325 0 - -2289 -2302 0 - -2289 -2325 0 - -2302 -2325 0 - -2192 -2209 0 - -2192 -2222 0 - -2192 -2253 0 - -2192 -2290 0 - -2192 -2303 0 - -2192 -2308 0 - -2192 -2326 0 - -2209 -2222 0 - -2209 -2253 0 - -2209 -2290 0 - -2209 -2303 0 - -2209 -2308 0 - -2209 -2326 0 - -2222 -2253 0 - -2222 -2290 0 - -2222 -2303 0 - -2222 -2308 0 - -2222 -2326 0 - -2253 -2290 0 - -2253 -2303 0 - -2253 -2308 0 - -2253 -2326 0 - -2290 -2303 0 - -2290 -2308 0 - -2290 -2326 0 - -2303 -2308 0 - -2303 -2326 0 - -2308 -2326 0 - -2183 -2223 0 - -2183 -2237 0 - -2183 -2245 0 - -2183 -2254 0 - -2183 -2267 0 - -2183 -2282 0 - -2183 -2291 0 - -2183 -2309 0 - -2183 -2318 0 - -2223 -2237 0 - -2223 -2245 0 - -2223 -2254 0 - -2223 -2267 0 - -2223 -2282 0 - -2223 -2291 0 - -2223 -2309 0 - -2223 -2318 0 - -2237 -2245 0 - -2237 -2254 0 - -2237 -2267 0 - -2237 -2282 0 - -2237 -2291 0 - -2237 -2309 0 - -2237 -2318 0 - -2245 -2254 0 - -2245 -2267 0 - -2245 -2282 0 - -2245 -2291 0 - -2245 -2309 0 - -2245 -2318 0 - -2254 -2267 0 - -2254 -2282 0 - -2254 -2291 0 - -2254 -2309 0 - -2254 -2318 0 - -2267 -2282 0 - -2267 -2291 0 - -2267 -2309 0 - -2267 -2318 0 - -2282 -2291 0 - -2282 -2309 0 - -2282 -2318 0 - -2291 -2309 0 - -2291 -2318 0 - -2309 -2318 0 - -2184 -2193 0 - -2184 -2210 0 - -2184 -2218 0 - -2184 -2238 0 - -2184 -2268 0 - -2184 -2283 0 - -2184 -2292 0 - -2184 -2327 0 - -2193 -2210 0 - -2193 -2218 0 - -2193 -2238 0 - -2193 -2268 0 - -2193 -2283 0 - -2193 -2292 0 - -2193 -2327 0 - -2210 -2218 0 - -2210 -2238 0 - -2210 -2268 0 - -2210 -2283 0 - -2210 -2292 0 - -2210 -2327 0 - -2218 -2238 0 - -2218 -2268 0 - -2218 -2283 0 - -2218 -2292 0 - -2218 -2327 0 - -2238 -2268 0 - -2238 -2283 0 - -2238 -2292 0 - -2238 -2327 0 - -2268 -2283 0 - -2268 -2292 0 - -2268 -2327 0 - -2283 -2292 0 - -2283 -2327 0 - -2292 -2327 0 - -2200 -2246 0 - -2200 -2255 0 - -2200 -2284 0 - -2200 -2293 0 - -2200 -2304 0 - -2246 -2255 0 - -2246 -2284 0 - -2246 -2293 0 - -2246 -2304 0 - -2255 -2284 0 - -2255 -2293 0 - -2255 -2304 0 - -2284 -2293 0 - -2284 -2304 0 - -2293 -2304 0 - -2201 -2211 0 - -2201 -2224 0 - -2201 -2239 0 - -2201 -2247 0 - -2201 -2269 0 - -2201 -2294 0 - -2201 -2310 0 - -2201 -2319 0 - -2201 -2328 0 - -2211 -2224 0 - -2211 -2239 0 - -2211 -2247 0 - -2211 -2269 0 - -2211 -2294 0 - -2211 -2310 0 - -2211 -2319 0 - -2211 -2328 0 - -2224 -2239 0 - -2224 -2247 0 - -2224 -2269 0 - -2224 -2294 0 - -2224 -2310 0 - -2224 -2319 0 - -2224 -2328 0 - -2239 -2247 0 - -2239 -2269 0 - -2239 -2294 0 - -2239 -2310 0 - -2239 -2319 0 - -2239 -2328 0 - -2247 -2269 0 - -2247 -2294 0 - -2247 -2310 0 - -2247 -2319 0 - -2247 -2328 0 - -2269 -2294 0 - -2269 -2310 0 - -2269 -2319 0 - -2269 -2328 0 - -2294 -2310 0 - -2294 -2319 0 - -2294 -2328 0 - -2310 -2319 0 - -2310 -2328 0 - -2319 -2328 0 - -2212 -2225 0 - -2212 -2256 0 - -2212 -2270 0 - -2212 -2305 0 - -2212 -2311 0 - -2212 -2320 0 - -2212 -2329 0 - -2225 -2256 0 - -2225 -2270 0 - -2225 -2305 0 - -2225 -2311 0 - -2225 -2320 0 - -2225 -2329 0 - -2256 -2270 0 - -2256 -2305 0 - -2256 -2311 0 - -2256 -2320 0 - -2256 -2329 0 - -2270 -2305 0 - -2270 -2311 0 - -2270 -2320 0 - -2270 -2329 0 - -2305 -2311 0 - -2305 -2320 0 - -2305 -2329 0 - -2311 -2320 0 - -2311 -2329 0 - -2320 -2329 0 - -2185 -2257 0 - -2185 -2295 0 - -2185 -2321 0 - -2185 -2330 0 - -2257 -2295 0 - -2257 -2321 0 - -2257 -2330 0 - -2295 -2321 0 - -2295 -2330 0 - -2321 -2330 0 - -2186 -2194 0 - -2186 -2213 0 - -2186 -2226 0 - -2186 -2240 0 - -2186 -2248 0 - -2186 -2285 0 - -2186 -2306 0 - -2194 -2213 0 - -2194 -2226 0 - -2194 -2240 0 - -2194 -2248 0 - -2194 -2285 0 - -2194 -2306 0 - -2213 -2226 0 - -2213 -2240 0 - -2213 -2248 0 - -2213 -2285 0 - -2213 -2306 0 - -2226 -2240 0 - -2226 -2248 0 - -2226 -2285 0 - -2226 -2306 0 - -2240 -2248 0 - -2240 -2285 0 - -2240 -2306 0 - -2248 -2285 0 - -2248 -2306 0 - -2285 -2306 0 - -2195 -2202 0 - -2195 -2219 0 - -2195 -2258 0 - -2195 -2278 0 - -2195 -2286 0 - -2195 -2307 0 - -2195 -2312 0 - -2195 -2331 0 - -2202 -2219 0 - -2202 -2258 0 - -2202 -2278 0 - -2202 -2286 0 - -2202 -2307 0 - -2202 -2312 0 - -2202 -2331 0 - -2219 -2258 0 - -2219 -2278 0 - -2219 -2286 0 - -2219 -2307 0 - -2219 -2312 0 - -2219 -2331 0 - -2258 -2278 0 - -2258 -2286 0 - -2258 -2307 0 - -2258 -2312 0 - -2258 -2331 0 - -2278 -2286 0 - -2278 -2307 0 - -2278 -2312 0 - -2278 -2331 0 - -2286 -2307 0 - -2286 -2312 0 - -2286 -2331 0 - -2307 -2312 0 - -2307 -2331 0 - -2312 -2331 0 - -2196 -2279 0 - -2196 -2296 0 - -2196 -2313 0 - -2196 -2322 0 - -2196 -2332 0 - -2279 -2296 0 - -2279 -2313 0 - -2279 -2322 0 - -2279 -2332 0 - -2296 -2313 0 - -2296 -2322 0 - -2296 -2332 0 - -2313 -2322 0 - -2313 -2332 0 - -2322 -2332 0 - -2203 -2232 0 - -2203 -2241 0 - -2203 -2271 0 - -2203 -2287 0 - -2203 -2297 0 - -2232 -2241 0 - -2232 -2271 0 - -2232 -2287 0 - -2232 -2297 0 - -2241 -2271 0 - -2241 -2287 0 - -2241 -2297 0 - -2271 -2287 0 - -2271 -2297 0 - -2287 -2297 0 - -2204 -2214 0 - -2204 -2227 0 - -2204 -2249 0 - -2204 -2259 0 - -2204 -2272 0 - -2204 -2298 0 - -2204 -2314 0 - -2204 -2323 0 - -2214 -2227 0 - -2214 -2249 0 - -2214 -2259 0 - -2214 -2272 0 - -2214 -2298 0 - -2214 -2314 0 - -2214 -2323 0 - -2227 -2249 0 - -2227 -2259 0 - -2227 -2272 0 - -2227 -2298 0 - -2227 -2314 0 - -2227 -2323 0 - -2249 -2259 0 - -2249 -2272 0 - -2249 -2298 0 - -2249 -2314 0 - -2249 -2323 0 - -2259 -2272 0 - -2259 -2298 0 - -2259 -2314 0 - -2259 -2323 0 - -2272 -2298 0 - -2272 -2314 0 - -2272 -2323 0 - -2298 -2314 0 - -2298 -2323 0 - -2314 -2323 0 - -2187 -2205 0 - -2187 -2220 0 - -2187 -2228 0 - -2187 -2273 0 - -2187 -2280 0 - -2187 -2315 0 - -2187 -2333 0 - -2205 -2220 0 - -2205 -2228 0 - -2205 -2273 0 - -2205 -2280 0 - -2205 -2315 0 - -2205 -2333 0 - -2220 -2228 0 - -2220 -2273 0 - -2220 -2280 0 - -2220 -2315 0 - -2220 -2333 0 - -2228 -2273 0 - -2228 -2280 0 - -2228 -2315 0 - -2228 -2333 0 - -2273 -2280 0 - -2273 -2315 0 - -2273 -2333 0 - -2280 -2315 0 - -2280 -2333 0 - -2315 -2333 0 - -2197 -2215 0 - -2197 -2233 0 - -2197 -2242 0 - -2197 -2250 0 - -2197 -2260 0 - -2197 -2264 0 - -2197 -2274 0 - -2197 -2281 0 - -2197 -2299 0 - -2197 -2316 0 - -2197 -2324 0 - -2215 -2233 0 - -2215 -2242 0 - -2215 -2250 0 - -2215 -2260 0 - -2215 -2264 0 - -2215 -2274 0 - -2215 -2281 0 - -2215 -2299 0 - -2215 -2316 0 - -2215 -2324 0 - -2233 -2242 0 - -2233 -2250 0 - -2233 -2260 0 - -2233 -2264 0 - -2233 -2274 0 - -2233 -2281 0 - -2233 -2299 0 - -2233 -2316 0 - -2233 -2324 0 - -2242 -2250 0 - -2242 -2260 0 - -2242 -2264 0 - -2242 -2274 0 - -2242 -2281 0 - -2242 -2299 0 - -2242 -2316 0 - -2242 -2324 0 - -2250 -2260 0 - -2250 -2264 0 - -2250 -2274 0 - -2250 -2281 0 - -2250 -2299 0 - -2250 -2316 0 - -2250 -2324 0 - -2260 -2264 0 - -2260 -2274 0 - -2260 -2281 0 - -2260 -2299 0 - -2260 -2316 0 - -2260 -2324 0 - -2264 -2274 0 - -2264 -2281 0 - -2264 -2299 0 - -2264 -2316 0 - -2264 -2324 0 - -2274 -2281 0 - -2274 -2299 0 - -2274 -2316 0 - -2274 -2324 0 - -2281 -2299 0 - -2281 -2316 0 - -2281 -2324 0 - -2299 -2316 0 - -2299 -2324 0 - -2316 -2324 0 - -2188 -2216 0 - -2188 -2229 0 - -2188 -2251 0 - -2188 -2261 0 - -2188 -2300 0 - -2188 -2317 0 - -2216 -2229 0 - -2216 -2251 0 - -2216 -2261 0 - -2216 -2300 0 - -2216 -2317 0 - -2229 -2251 0 - -2229 -2261 0 - -2229 -2300 0 - -2229 -2317 0 - -2251 -2261 0 - -2251 -2300 0 - -2251 -2317 0 - -2261 -2300 0 - -2261 -2317 0 - -2300 -2317 0 - -2206 -2234 0 - -2206 -2243 0 - -2206 -2262 0 - -2206 -2265 0 - -2206 -2275 0 - -2234 -2243 0 - -2234 -2262 0 - -2234 -2265 0 - -2234 -2275 0 - -2243 -2262 0 - -2243 -2265 0 - -2243 -2275 0 - -2262 -2265 0 - -2262 -2275 0 - -2265 -2275 0 - -2189 -2198 0 - -2189 -2230 0 - -2189 -2235 0 - -2189 -2244 0 - -2189 -2266 0 - -2189 -2276 0 - -2189 -2288 0 - -2189 -2301 0 - -2189 -2334 0 - -2198 -2230 0 - -2198 -2235 0 - -2198 -2244 0 - -2198 -2266 0 - -2198 -2276 0 - -2198 -2288 0 - -2198 -2301 0 - -2198 -2334 0 - -2230 -2235 0 - -2230 -2244 0 - -2230 -2266 0 - -2230 -2276 0 - -2230 -2288 0 - -2230 -2301 0 - -2230 -2334 0 - -2235 -2244 0 - -2235 -2266 0 - -2235 -2276 0 - -2235 -2288 0 - -2235 -2301 0 - -2235 -2334 0 - -2244 -2266 0 - -2244 -2276 0 - -2244 -2288 0 - -2244 -2301 0 - -2244 -2334 0 - -2266 -2276 0 - -2266 -2288 0 - -2266 -2301 0 - -2266 -2334 0 - -2276 -2288 0 - -2276 -2301 0 - -2276 -2334 0 - -2288 -2301 0 - -2288 -2334 0 - -2301 -2334 0 - -2190 -2199 0 - -2190 -2207 0 - -2190 -2221 0 - -2190 -2231 0 - -2190 -2263 0 - -2199 -2207 0 - -2199 -2221 0 - -2199 -2231 0 - -2199 -2263 0 - -2207 -2221 0 - -2207 -2231 0 - -2207 -2263 0 - -2221 -2231 0 - -2221 -2263 0 - -2231 -2263 0 - -2343 -2357 0 - -2343 -2376 0 - -2343 -2382 0 - -2357 -2376 0 - -2357 -2382 0 - -2376 -2382 0 - -2335 -2352 0 - -2335 -2358 0 - -2335 -2365 0 - -2335 -2377 0 - -2335 -2383 0 - -2352 -2358 0 - -2352 -2365 0 - -2352 -2377 0 - -2352 -2383 0 - -2358 -2365 0 - -2358 -2377 0 - -2358 -2383 0 - -2365 -2377 0 - -2365 -2383 0 - -2377 -2383 0 - -2348 -2359 0 - -2348 -2366 0 - -2348 -2378 0 - -2348 -2384 0 - -2359 -2366 0 - -2359 -2378 0 - -2359 -2384 0 - -2366 -2378 0 - -2366 -2384 0 - -2378 -2384 0 - -2336 -2344 0 - -2336 -2353 0 - -2336 -2367 0 - -2336 -2373 0 - -2336 -2379 0 - -2344 -2353 0 - -2344 -2367 0 - -2344 -2373 0 - -2344 -2379 0 - -2353 -2367 0 - -2353 -2373 0 - -2353 -2379 0 - -2367 -2373 0 - -2367 -2379 0 - -2373 -2379 0 - -2345 -2354 0 - -2345 -2360 0 - -2345 -2368 0 - -2345 -2374 0 - -2354 -2360 0 - -2354 -2368 0 - -2354 -2374 0 - -2360 -2368 0 - -2360 -2374 0 - -2368 -2374 0 - -2340 -2346 0 - -2340 -2349 0 - -2340 -2361 0 - -2340 -2369 0 - -2346 -2349 0 - -2346 -2361 0 - -2346 -2369 0 - -2349 -2361 0 - -2349 -2369 0 - -2361 -2369 0 - -2347 -2355 0 - -2347 -2362 0 - -2347 -2370 0 - -2347 -2380 0 - -2355 -2362 0 - -2355 -2370 0 - -2355 -2380 0 - -2362 -2370 0 - -2362 -2380 0 - -2370 -2380 0 - -2337 -2341 0 - -2337 -2350 0 - -2337 -2356 0 - -2337 -2363 0 - -2337 -2371 0 - -2337 -2375 0 - -2337 -2385 0 - -2341 -2350 0 - -2341 -2356 0 - -2341 -2363 0 - -2341 -2371 0 - -2341 -2375 0 - -2341 -2385 0 - -2350 -2356 0 - -2350 -2363 0 - -2350 -2371 0 - -2350 -2375 0 - -2350 -2385 0 - -2356 -2363 0 - -2356 -2371 0 - -2356 -2375 0 - -2356 -2385 0 - -2363 -2371 0 - -2363 -2375 0 - -2363 -2385 0 - -2371 -2375 0 - -2371 -2385 0 - -2375 -2385 0 - -2338 -2351 0 - -2338 -2372 0 - -2338 -2381 0 - -2338 -2386 0 - -2351 -2372 0 - -2351 -2381 0 - -2351 -2386 0 - -2372 -2381 0 - -2372 -2386 0 - -2381 -2386 0 - -2339 -2342 0 - -2339 -2364 0 - -2342 -2364 0 - -2392 -2402 0 - -2392 -2409 0 - -2392 -2414 0 - -2402 -2409 0 - -2402 -2414 0 - -2409 -2414 0 - -2387 -2393 0 - -2387 -2398 0 - -2387 -2403 0 - -2387 -2410 0 - -2393 -2398 0 - -2393 -2403 0 - -2393 -2410 0 - -2398 -2403 0 - -2398 -2410 0 - -2403 -2410 0 - -2388 -2395 0 - -2388 -2405 0 - -2395 -2405 0 - -2389 -2396 0 - -2389 -2406 0 - -2389 -2415 0 - -2396 -2406 0 - -2396 -2415 0 - -2406 -2415 0 - -2394 -2399 0 - -2394 -2411 0 - -2399 -2411 0 - -2390 -2400 0 - -2390 -2404 0 - -2400 -2404 0 - -2391 -2407 0 - -2391 -2412 0 - -2391 -2416 0 - -2407 -2412 0 - -2407 -2416 0 - -2412 -2416 0 - -2397 -2401 0 - -2397 -2408 0 - -2397 -2413 0 - -2397 -2417 0 - -2401 -2408 0 - -2401 -2413 0 - -2401 -2417 0 - -2408 -2413 0 - -2408 -2417 0 - -2413 -2417 0 - -2446 -2455 0 - -2446 -2491 0 - -2446 -2512 0 - -2455 -2491 0 - -2455 -2512 0 - -2491 -2512 0 - -2425 -2434 0 - -2425 -2460 0 - -2425 -2492 0 - -2425 -2501 0 - -2425 -2525 0 - -2425 -2531 0 - -2434 -2460 0 - -2434 -2492 0 - -2434 -2501 0 - -2434 -2525 0 - -2434 -2531 0 - -2460 -2492 0 - -2460 -2501 0 - -2460 -2525 0 - -2460 -2531 0 - -2492 -2501 0 - -2492 -2525 0 - -2492 -2531 0 - -2501 -2525 0 - -2501 -2531 0 - -2525 -2531 0 - -2426 -2440 0 - -2426 -2447 0 - -2426 -2476 0 - -2426 -2485 0 - -2426 -2493 0 - -2426 -2502 0 - -2426 -2513 0 - -2426 -2526 0 - -2426 -2532 0 - -2440 -2447 0 - -2440 -2476 0 - -2440 -2485 0 - -2440 -2493 0 - -2440 -2502 0 - -2440 -2513 0 - -2440 -2526 0 - -2440 -2532 0 - -2447 -2476 0 - -2447 -2485 0 - -2447 -2493 0 - -2447 -2502 0 - -2447 -2513 0 - -2447 -2526 0 - -2447 -2532 0 - -2476 -2485 0 - -2476 -2493 0 - -2476 -2502 0 - -2476 -2513 0 - -2476 -2526 0 - -2476 -2532 0 - -2485 -2493 0 - -2485 -2502 0 - -2485 -2513 0 - -2485 -2526 0 - -2485 -2532 0 - -2493 -2502 0 - -2493 -2513 0 - -2493 -2526 0 - -2493 -2532 0 - -2502 -2513 0 - -2502 -2526 0 - -2502 -2532 0 - -2513 -2526 0 - -2513 -2532 0 - -2526 -2532 0 - -2427 -2435 0 - -2427 -2461 0 - -2427 -2477 0 - -2427 -2494 0 - -2427 -2503 0 - -2427 -2514 0 - -2435 -2461 0 - -2435 -2477 0 - -2435 -2494 0 - -2435 -2503 0 - -2435 -2514 0 - -2461 -2477 0 - -2461 -2494 0 - -2461 -2503 0 - -2461 -2514 0 - -2477 -2494 0 - -2477 -2503 0 - -2477 -2514 0 - -2494 -2503 0 - -2494 -2514 0 - -2503 -2514 0 - -2428 -2441 0 - -2428 -2462 0 - -2428 -2486 0 - -2428 -2515 0 - -2441 -2462 0 - -2441 -2486 0 - -2441 -2515 0 - -2462 -2486 0 - -2462 -2515 0 - -2486 -2515 0 - -2448 -2478 0 - -2448 -2487 0 - -2448 -2504 0 - -2448 -2527 0 - -2448 -2533 0 - -2478 -2487 0 - -2478 -2504 0 - -2478 -2527 0 - -2478 -2533 0 - -2487 -2504 0 - -2487 -2527 0 - -2487 -2533 0 - -2504 -2527 0 - -2504 -2533 0 - -2527 -2533 0 - -2418 -2436 0 - -2418 -2442 0 - -2418 -2449 0 - -2418 -2479 0 - -2418 -2495 0 - -2436 -2442 0 - -2436 -2449 0 - -2436 -2479 0 - -2436 -2495 0 - -2442 -2449 0 - -2442 -2479 0 - -2442 -2495 0 - -2449 -2479 0 - -2449 -2495 0 - -2479 -2495 0 - -2429 -2463 0 - -2429 -2496 0 - -2429 -2521 0 - -2429 -2528 0 - -2429 -2534 0 - -2463 -2496 0 - -2463 -2521 0 - -2463 -2528 0 - -2463 -2534 0 - -2496 -2521 0 - -2496 -2528 0 - -2496 -2534 0 - -2521 -2528 0 - -2521 -2534 0 - -2528 -2534 0 - -2419 -2443 0 - -2419 -2464 0 - -2419 -2469 0 - -2419 -2480 0 - -2419 -2505 0 - -2419 -2516 0 - -2419 -2522 0 - -2443 -2464 0 - -2443 -2469 0 - -2443 -2480 0 - -2443 -2505 0 - -2443 -2516 0 - -2443 -2522 0 - -2464 -2469 0 - -2464 -2480 0 - -2464 -2505 0 - -2464 -2516 0 - -2464 -2522 0 - -2469 -2480 0 - -2469 -2505 0 - -2469 -2516 0 - -2469 -2522 0 - -2480 -2505 0 - -2480 -2516 0 - -2480 -2522 0 - -2505 -2516 0 - -2505 -2522 0 - -2516 -2522 0 - -2437 -2450 0 - -2437 -2465 0 - -2437 -2506 0 - -2437 -2523 0 - -2437 -2529 0 - -2450 -2465 0 - -2450 -2506 0 - -2450 -2523 0 - -2450 -2529 0 - -2465 -2506 0 - -2465 -2523 0 - -2465 -2529 0 - -2506 -2523 0 - -2506 -2529 0 - -2523 -2529 0 - -2420 -2456 0 - -2420 -2470 0 - -2420 -2481 0 - -2420 -2488 0 - -2420 -2507 0 - -2420 -2517 0 - -2420 -2524 0 - -2420 -2535 0 - -2456 -2470 0 - -2456 -2481 0 - -2456 -2488 0 - -2456 -2507 0 - -2456 -2517 0 - -2456 -2524 0 - -2456 -2535 0 - -2470 -2481 0 - -2470 -2488 0 - -2470 -2507 0 - -2470 -2517 0 - -2470 -2524 0 - -2470 -2535 0 - -2481 -2488 0 - -2481 -2507 0 - -2481 -2517 0 - -2481 -2524 0 - -2481 -2535 0 - -2488 -2507 0 - -2488 -2517 0 - -2488 -2524 0 - -2488 -2535 0 - -2507 -2517 0 - -2507 -2524 0 - -2507 -2535 0 - -2517 -2524 0 - -2517 -2535 0 - -2524 -2535 0 - -2421 -2444 0 - -2421 -2471 0 - -2421 -2482 0 - -2421 -2489 0 - -2421 -2497 0 - -2421 -2508 0 - -2421 -2530 0 - -2421 -2536 0 - -2444 -2471 0 - -2444 -2482 0 - -2444 -2489 0 - -2444 -2497 0 - -2444 -2508 0 - -2444 -2530 0 - -2444 -2536 0 - -2471 -2482 0 - -2471 -2489 0 - -2471 -2497 0 - -2471 -2508 0 - -2471 -2530 0 - -2471 -2536 0 - -2482 -2489 0 - -2482 -2497 0 - -2482 -2508 0 - -2482 -2530 0 - -2482 -2536 0 - -2489 -2497 0 - -2489 -2508 0 - -2489 -2530 0 - -2489 -2536 0 - -2497 -2508 0 - -2497 -2530 0 - -2497 -2536 0 - -2508 -2530 0 - -2508 -2536 0 - -2530 -2536 0 - -2422 -2430 0 - -2422 -2445 0 - -2422 -2451 0 - -2422 -2457 0 - -2422 -2466 0 - -2422 -2472 0 - -2422 -2483 0 - -2422 -2498 0 - -2422 -2509 0 - -2430 -2445 0 - -2430 -2451 0 - -2430 -2457 0 - -2430 -2466 0 - -2430 -2472 0 - -2430 -2483 0 - -2430 -2498 0 - -2430 -2509 0 - -2445 -2451 0 - -2445 -2457 0 - -2445 -2466 0 - -2445 -2472 0 - -2445 -2483 0 - -2445 -2498 0 - -2445 -2509 0 - -2451 -2457 0 - -2451 -2466 0 - -2451 -2472 0 - -2451 -2483 0 - -2451 -2498 0 - -2451 -2509 0 - -2457 -2466 0 - -2457 -2472 0 - -2457 -2483 0 - -2457 -2498 0 - -2457 -2509 0 - -2466 -2472 0 - -2466 -2483 0 - -2466 -2498 0 - -2466 -2509 0 - -2472 -2483 0 - -2472 -2498 0 - -2472 -2509 0 - -2483 -2498 0 - -2483 -2509 0 - -2498 -2509 0 - -2423 -2431 0 - -2423 -2438 0 - -2423 -2452 0 - -2423 -2467 0 - -2423 -2473 0 - -2423 -2499 0 - -2423 -2510 0 - -2423 -2518 0 - -2431 -2438 0 - -2431 -2452 0 - -2431 -2467 0 - -2431 -2473 0 - -2431 -2499 0 - -2431 -2510 0 - -2431 -2518 0 - -2438 -2452 0 - -2438 -2467 0 - -2438 -2473 0 - -2438 -2499 0 - -2438 -2510 0 - -2438 -2518 0 - -2452 -2467 0 - -2452 -2473 0 - -2452 -2499 0 - -2452 -2510 0 - -2452 -2518 0 - -2467 -2473 0 - -2467 -2499 0 - -2467 -2510 0 - -2467 -2518 0 - -2473 -2499 0 - -2473 -2510 0 - -2473 -2518 0 - -2499 -2510 0 - -2499 -2518 0 - -2510 -2518 0 - -2424 -2432 0 - -2424 -2439 0 - -2424 -2453 0 - -2424 -2458 0 - -2424 -2474 0 - -2424 -2490 0 - -2424 -2500 0 - -2424 -2519 0 - -2432 -2439 0 - -2432 -2453 0 - -2432 -2458 0 - -2432 -2474 0 - -2432 -2490 0 - -2432 -2500 0 - -2432 -2519 0 - -2439 -2453 0 - -2439 -2458 0 - -2439 -2474 0 - -2439 -2490 0 - -2439 -2500 0 - -2439 -2519 0 - -2453 -2458 0 - -2453 -2474 0 - -2453 -2490 0 - -2453 -2500 0 - -2453 -2519 0 - -2458 -2474 0 - -2458 -2490 0 - -2458 -2500 0 - -2458 -2519 0 - -2474 -2490 0 - -2474 -2500 0 - -2474 -2519 0 - -2490 -2500 0 - -2490 -2519 0 - -2500 -2519 0 - -2433 -2454 0 - -2433 -2459 0 - -2433 -2468 0 - -2433 -2475 0 - -2433 -2484 0 - -2433 -2511 0 - -2433 -2520 0 - -2454 -2459 0 - -2454 -2468 0 - -2454 -2475 0 - -2454 -2484 0 - -2454 -2511 0 - -2454 -2520 0 - -2459 -2468 0 - -2459 -2475 0 - -2459 -2484 0 - -2459 -2511 0 - -2459 -2520 0 - -2468 -2475 0 - -2468 -2484 0 - -2468 -2511 0 - -2468 -2520 0 - -2475 -2484 0 - -2475 -2511 0 - -2475 -2520 0 - -2484 -2511 0 - -2484 -2520 0 - -2511 -2520 0 - -2537 -2542 0 - -2537 -2548 0 - -2537 -2565 0 - -2537 -2572 0 - -2537 -2593 0 - -2537 -2598 0 - -2542 -2548 0 - -2542 -2565 0 - -2542 -2572 0 - -2542 -2593 0 - -2542 -2598 0 - -2548 -2565 0 - -2548 -2572 0 - -2548 -2593 0 - -2548 -2598 0 - -2565 -2572 0 - -2565 -2593 0 - -2565 -2598 0 - -2572 -2593 0 - -2572 -2598 0 - -2593 -2598 0 - -2538 -2549 0 - -2538 -2553 0 - -2538 -2566 0 - -2538 -2585 0 - -2538 -2588 0 - -2549 -2553 0 - -2549 -2566 0 - -2549 -2585 0 - -2549 -2588 0 - -2553 -2566 0 - -2553 -2585 0 - -2553 -2588 0 - -2566 -2585 0 - -2566 -2588 0 - -2585 -2588 0 - -2550 -2562 0 - -2550 -2567 0 - -2550 -2573 0 - -2550 -2589 0 - -2562 -2567 0 - -2562 -2573 0 - -2562 -2589 0 - -2567 -2573 0 - -2567 -2589 0 - -2573 -2589 0 - -2551 -2554 0 - -2551 -2563 0 - -2551 -2568 0 - -2554 -2563 0 - -2554 -2568 0 - -2563 -2568 0 - -2543 -2555 0 - -2543 -2569 0 - -2543 -2574 0 - -2543 -2586 0 - -2543 -2594 0 - -2543 -2599 0 - -2555 -2569 0 - -2555 -2574 0 - -2555 -2586 0 - -2555 -2594 0 - -2555 -2599 0 - -2569 -2574 0 - -2569 -2586 0 - -2569 -2594 0 - -2569 -2599 0 - -2574 -2586 0 - -2574 -2594 0 - -2574 -2599 0 - -2586 -2594 0 - -2586 -2599 0 - -2594 -2599 0 - -2556 -2575 0 - -2556 -2581 0 - -2556 -2590 0 - -2575 -2581 0 - -2575 -2590 0 - -2581 -2590 0 - -2539 -2544 0 - -2539 -2570 0 - -2539 -2587 0 - -2539 -2595 0 - -2539 -2600 0 - -2544 -2570 0 - -2544 -2587 0 - -2544 -2595 0 - -2544 -2600 0 - -2570 -2587 0 - -2570 -2595 0 - -2570 -2600 0 - -2587 -2595 0 - -2587 -2600 0 - -2595 -2600 0 - -2558 -2564 0 - -2558 -2576 0 - -2558 -2591 0 - -2558 -2596 0 - -2564 -2576 0 - -2564 -2591 0 - -2564 -2596 0 - -2576 -2591 0 - -2576 -2596 0 - -2591 -2596 0 - -2540 -2559 0 - -2540 -2577 0 - -2540 -2582 0 - -2540 -2592 0 - -2559 -2577 0 - -2559 -2582 0 - -2559 -2592 0 - -2577 -2582 0 - -2577 -2592 0 - -2582 -2592 0 - -2545 -2552 0 - -2545 -2571 0 - -2545 -2578 0 - -2545 -2583 0 - -2552 -2571 0 - -2552 -2578 0 - -2552 -2583 0 - -2571 -2578 0 - -2571 -2583 0 - -2578 -2583 0 - -2557 -2597 0 - -2546 -2560 0 - -2546 -2579 0 - -2560 -2579 0 - -2541 -2547 0 - -2541 -2561 0 - -2541 -2580 0 - -2541 -2584 0 - -2541 -2601 0 - -2547 -2561 0 - -2547 -2580 0 - -2547 -2584 0 - -2547 -2601 0 - -2561 -2580 0 - -2561 -2584 0 - -2561 -2601 0 - -2580 -2584 0 - -2580 -2601 0 - -2584 -2601 0 - 1 2 3 4 5 6 0 - 7 8 9 10 11 12 0 - 13 14 15 16 0 - 17 18 19 20 0 - 21 22 23 0 - 24 25 26 27 28 0 - 29 30 31 32 33 34 35 36 0 - 37 38 39 40 0 - 41 42 43 0 - 44 45 0 - 46 47 48 49 50 51 0 - 52 53 54 55 0 - 56 57 58 0 - 59 60 61 62 63 0 - 64 65 66 67 68 0 - 69 70 71 72 73 74 0 - 75 76 77 0 - 78 79 0 - 80 81 82 83 84 85 86 0 - 87 88 89 0 - 90 91 92 0 - 93 94 95 96 97 0 - 98 99 100 101 102 0 - 103 104 105 106 0 - 107 108 109 110 111 0 - 112 113 114 115 116 117 118 0 - 119 120 121 122 123 124 125 0 - 126 127 128 129 130 131 0 - 132 133 134 135 136 0 - 137 138 139 140 141 0 - 142 143 144 145 146 147 148 0 - 149 150 151 152 0 - 153 154 0 - 155 156 157 158 159 160 161 0 - 162 163 164 165 0 - 166 167 168 169 170 171 172 0 - 173 174 175 0 - 176 177 178 0 - 179 180 181 182 183 184 0 - 185 186 187 188 189 190 191 0 - 192 193 194 195 0 - 196 197 198 0 - 199 200 201 202 203 204 205 0 - 206 207 208 209 0 - 210 211 212 213 214 215 216 0 - 217 218 219 220 0 - 221 222 223 224 225 226 0 - 227 228 229 230 231 232 0 - 233 234 235 236 237 238 239 0 - 240 241 242 243 244 245 246 247 248 0 - 249 250 251 252 0 - 253 254 255 256 0 - 257 258 259 260 0 - 261 262 263 264 265 266 267 268 0 - 269 270 271 272 273 0 - 274 275 276 277 278 279 280 0 - 281 282 283 284 0 - 285 286 287 288 0 - 289 290 291 292 293 294 0 - 295 296 297 298 0 - 299 300 301 302 303 304 0 - 305 306 307 308 309 310 0 - 311 312 313 314 315 316 317 318 319 0 - 320 321 322 323 324 325 326 327 0 - 328 329 330 331 332 0 - 333 334 0 - 335 336 337 338 339 340 341 342 0 - 343 344 345 346 347 348 349 350 351 0 - 352 353 354 355 356 0 - 357 358 359 360 361 362 0 - 363 364 365 366 367 368 369 370 371 372 0 - 373 374 375 376 377 378 379 380 0 - 381 382 383 384 0 - 385 386 387 388 389 390 391 392 0 - 393 394 395 396 397 398 399 0 - 400 401 402 403 404 405 406 407 408 409 0 - 410 411 412 413 414 415 0 - 416 417 418 419 420 421 422 423 0 - 424 425 426 427 428 429 430 431 0 - 432 433 434 435 436 437 438 439 440 0 - 441 442 443 444 445 446 447 448 449 450 0 - 451 452 453 454 455 456 457 458 0 - 459 460 461 462 463 464 0 - 465 466 467 468 469 470 471 472 473 474 475 0 - 476 477 478 479 480 0 - 481 482 483 0 - 484 485 486 487 488 489 490 491 0 - 492 493 494 495 496 497 498 499 0 - 500 501 502 503 504 505 506 507 508 509 0 - 510 511 512 513 0 - 514 515 516 517 518 519 520 521 0 - 522 523 524 525 526 527 528 529 530 0 - 531 532 533 534 535 536 0 - 537 538 539 540 541 542 543 544 545 546 547 548 0 - 549 550 551 552 553 554 0 - 555 556 557 558 559 0 - 560 561 562 563 0 - 564 565 566 567 568 569 0 - 570 571 572 573 0 - 574 575 576 577 578 579 580 0 - 581 582 583 584 585 586 587 0 - 588 589 590 591 592 0 - 593 594 595 596 0 - 597 598 599 600 601 602 0 - 603 604 605 606 607 608 0 - 609 610 611 612 0 - 613 614 615 616 617 0 - 618 619 620 0 - 621 622 623 624 625 626 627 628 629 0 - 630 631 632 633 634 635 636 0 - 637 638 639 640 641 0 - 642 643 644 645 646 647 648 0 - 649 650 651 652 653 654 655 0 - 656 657 658 659 660 661 662 663 664 0 - 665 666 667 668 669 670 671 0 - 672 673 674 675 676 677 0 - 678 679 680 681 682 683 684 685 686 687 688 0 - 689 690 691 692 693 0 - 694 695 696 697 698 0 - 699 700 701 702 0 - 703 704 705 706 707 708 0 - 709 710 711 712 713 0 - 714 715 716 0 - 717 718 719 720 721 0 - 722 723 724 725 726 727 728 729 0 - 730 731 732 733 734 0 - 735 736 737 738 739 740 741 0 - 742 743 744 745 746 0 - 747 748 749 750 751 752 0 - 753 754 755 0 - 756 757 758 759 760 761 0 - 762 763 764 0 - 765 766 767 768 769 0 - 770 771 772 773 774 0 - 775 776 777 778 779 780 781 782 0 - 783 784 785 786 787 0 - 788 789 790 791 0 - 792 793 794 795 796 797 798 0 - 799 800 801 802 803 804 805 806 0 - 807 808 809 810 811 812 813 814 815 0 - 816 817 818 819 820 821 822 0 - 823 824 825 826 827 828 829 830 831 832 0 - 833 834 835 836 837 838 839 0 - 840 841 842 843 0 - 844 845 846 847 848 0 - 849 850 851 852 0 - 853 854 855 856 857 858 859 0 - 860 861 862 863 864 865 866 867 868 0 - 869 870 871 872 0 - 873 874 875 876 877 878 879 880 881 0 - 882 883 884 0 - 885 886 887 888 889 0 - 890 891 892 893 894 0 - 895 896 897 0 - 898 899 900 901 0 - 902 903 904 905 906 0 - 907 908 909 910 0 - 911 912 0 - 913 914 915 916 917 0 - 918 919 920 921 922 923 0 - 924 925 926 927 928 929 930 0 - 931 932 933 934 935 0 - 936 937 938 939 940 0 - 941 942 943 944 945 946 0 - 947 948 949 950 951 952 953 0 - 954 955 956 957 958 959 0 - 960 961 962 0 - 963 964 965 0 - 966 967 968 969 970 971 972 973 974 975 0 - 976 977 0 - 978 979 980 981 982 983 0 - 984 985 986 987 988 989 0 - 990 991 992 993 994 995 996 997 998 0 - 999 1000 1001 1002 1003 1004 0 - 1005 1006 1007 1008 1009 1010 1011 0 - 1012 1013 1014 1015 1016 1017 1018 1019 1020 0 - 1021 1022 1023 1024 1025 1026 1027 1028 1029 0 - 1030 1031 1032 1033 1034 1035 1036 1037 1038 0 - 1039 1040 1041 1042 1043 1044 1045 0 - 1046 1047 1048 1049 1050 0 - 1051 1052 1053 0 - 1054 1055 1056 1057 1058 1059 1060 1061 0 - 1062 1063 1064 1065 1066 1067 1068 1069 1070 0 - 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 0 - 1082 1083 1084 0 - 1085 1086 1087 1088 1089 1090 1091 0 - 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 0 - 1103 1104 1105 1106 1107 1108 0 - 1109 1110 1111 1112 1113 1114 1115 1116 0 - 1117 1118 1119 1120 0 - 1121 1122 1123 0 - 1124 1125 1126 1127 1128 0 - 1129 1130 1131 0 - 1132 1133 0 - 1134 1135 1136 1137 0 - 1138 1139 1140 1141 1142 1143 0 - 1144 1145 0 - 1146 1147 1148 1149 1150 0 - 1151 1152 1153 1154 0 - 1155 1156 0 - 1157 1158 1159 1160 1161 1162 1163 0 - 1164 1165 1166 1167 1168 0 - 1169 1170 1171 0 - 1172 1173 1174 1175 0 - 1176 1177 0 - 1178 1179 1180 1181 1182 0 - 1183 1184 1185 1186 1187 1188 1189 0 - 1190 1191 1192 0 - 1193 1194 1195 1196 1197 1198 1199 0 - 1200 1201 1202 1203 1204 1205 0 - 1206 1207 1208 1209 1210 1211 1212 0 - 1213 1214 1215 1216 1217 1218 1219 0 - 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 0 - 1230 1231 1232 1233 1234 1235 1236 1237 1238 0 - 1239 1240 1241 1242 1243 1244 1245 0 - 1246 1247 1248 1249 1250 1251 1252 1253 0 - 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 0 - 1267 1268 1269 1270 1271 1272 0 - 1273 1274 1275 1276 1277 1278 1279 1280 1281 0 - 1282 1283 1284 1285 1286 0 - 1287 1288 1289 1290 1291 1292 1293 0 - 1294 1295 1296 1297 1298 1299 1300 1301 1302 0 - 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 0 - 1313 1314 1315 1316 1317 1318 0 - 1319 1320 1321 1322 1323 1324 1325 1326 0 - 1327 1328 1329 1330 1331 1332 1333 0 - 1334 1335 1336 1337 1338 1339 1340 0 - 1341 1342 1343 1344 1345 1346 0 - 1347 1348 1349 1350 1351 0 - 1352 1353 1354 1355 1356 1357 1358 0 - 1359 1360 1361 1362 1363 1364 0 - 1365 1366 1367 1368 1369 1370 0 - 1371 1372 1373 1374 1375 1376 1377 1378 0 - 1379 1380 1381 1382 1383 1384 1385 1386 0 - 1387 1388 1389 1390 1391 1392 1393 0 - 1394 1395 1396 1397 1398 0 - 1399 1400 1401 1402 1403 1404 1405 1406 0 - 1407 1408 1409 1410 1411 1412 0 - 1413 1414 1415 1416 0 - 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 0 - 1428 1429 1430 1431 1432 1433 0 - 1434 1435 1436 1437 1438 1439 1440 1441 0 - 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 0 - 1452 1453 1454 1455 1456 1457 1458 0 - 1459 1460 1461 1462 0 - 1463 1464 1465 1466 1467 1468 0 - 1469 1470 1471 1472 0 - 1473 1474 1475 0 - 1476 1477 1478 1479 1480 1481 1482 0 - 1483 1484 1485 0 - 1486 1487 1488 0 - 1489 1490 1491 1492 1493 1494 1495 1496 0 - 1497 1498 1499 1500 1501 0 - 1502 1503 1504 1505 1506 1507 1508 1509 0 - 1510 1511 1512 1513 1514 0 - 1515 1516 1517 1518 1519 1520 1521 0 - 1522 1523 1524 1525 1526 1527 1528 1529 0 - 1530 1531 1532 1533 1534 1535 1536 1537 0 - 1538 1539 1540 1541 1542 1543 0 - 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 0 - 1554 1555 1556 1557 0 - 1558 1559 1560 1561 1562 1563 1564 1565 0 - 1566 1567 1568 1569 1570 1571 0 - 1572 1573 1574 1575 1576 1577 1578 1579 1580 0 - 1581 1582 1583 1584 1585 1586 1587 1588 0 - 1589 1590 1591 1592 1593 1594 0 - 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 0 - 1605 1606 1607 1608 1609 1610 1611 1612 1613 0 - 1614 1615 1616 1617 0 - 1618 1619 1620 1621 1622 1623 0 - 1624 1625 1626 1627 1628 0 - 1629 1630 1631 1632 1633 1634 0 - 1635 1636 1637 1638 1639 1640 1641 0 - 1642 1643 1644 0 - 1645 1646 1647 1648 0 - 1649 1650 1651 0 - 1652 1653 1654 1655 1656 0 - 1657 1658 1659 1660 1661 1662 0 - 1663 1664 1665 0 - 1666 1667 1668 0 - 1669 1670 1671 0 - 1672 1673 1674 0 - 1675 1676 1677 0 - 1678 1679 1680 0 - 1681 1682 0 - 1683 1684 1685 1686 1687 0 - 1688 1689 1690 1691 0 - 1692 1693 1694 0 - 1695 1696 1697 1698 1699 1700 0 - 1701 1702 1703 1704 1705 1706 0 - 1707 1708 1709 1710 1711 1712 1713 1714 0 - 1715 1716 1717 1718 1719 1720 1721 0 - 1722 1723 1724 1725 1726 1727 1728 1729 0 - 1730 1731 1732 1733 1734 1735 1736 1737 0 - 1738 1739 1740 1741 1742 1743 1744 0 - 1745 1746 1747 1748 0 - 1749 1750 1751 1752 1753 0 - 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 0 - 1764 1765 1766 1767 1768 1769 1770 1771 1772 0 - 1773 1774 1775 1776 1777 0 - 1778 1779 1780 1781 1782 1783 1784 1785 1786 0 - 1787 1788 1789 0 - 1790 1791 1792 1793 1794 1795 1796 1797 0 - 1798 1799 1800 1801 1802 0 - 1803 1804 1805 1806 0 - 1807 1808 1809 1810 1811 1812 0 - 1813 1814 1815 1816 1817 0 - 1818 1819 1820 1821 1822 0 - 1823 1824 1825 1826 1827 1828 0 - 1829 1830 1831 1832 1833 0 - 1834 1835 1836 0 - 1837 1838 1839 0 - 1840 1841 1842 1843 1844 0 - 1845 1846 1847 1848 1849 0 - 1850 1851 0 - 1852 1853 1854 1855 1856 0 - 1857 1858 1859 1860 1861 1862 0 - 1863 1864 0 - 1865 1866 1867 1868 0 - 1869 1870 1871 1872 1873 0 - 1874 1875 0 - 1876 1877 1878 1879 1880 1881 0 - 1882 1883 1884 1885 1886 1887 1888 1889 1890 0 - 1891 1892 1893 1894 1895 0 - 1896 1897 1898 1899 0 - 1900 1901 1902 1903 1904 1905 0 - 1906 1907 1908 1909 1910 1911 1912 1913 0 - 1914 1915 1916 1917 1918 0 - 1919 1920 1921 1922 1923 0 - 1924 1925 1926 1927 1928 1929 1930 0 - 1931 1932 1933 1934 1935 1936 1937 1938 0 - 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 0 - 1949 1950 1951 1952 1953 1954 1955 0 - 1956 1957 1958 1959 1960 1961 1962 1963 0 - 1964 1965 1966 1967 1968 1969 1970 1971 1972 0 - 1973 1974 1975 1976 1977 1978 1979 0 - 1980 1981 1982 1983 1984 1985 1986 1987 1988 0 - 1989 1990 1991 1992 0 - 1993 1994 1995 1996 1997 0 - 1998 1999 2000 2001 2002 2003 0 - 2004 2005 2006 2007 2008 2009 0 - 2010 2011 2012 2013 0 - 2014 2015 2016 2017 2018 2019 0 - 2020 2021 2022 2023 2024 0 - 2025 2026 2027 2028 2029 2030 2031 2032 0 - 2033 2034 2035 0 - 2036 2037 2038 2039 0 - 2040 2041 2042 2043 2044 2045 2046 0 - 2047 2048 2049 2050 2051 2052 0 - 2053 2054 2055 2056 0 - 2057 2058 2059 2060 0 - 2061 2062 2063 2064 2065 2066 0 - 2067 2068 2069 2070 2071 0 - 2072 2073 2074 2075 2076 2077 0 - 2078 2079 2080 2081 0 - 2082 2083 2084 2085 2086 2087 2088 0 - 2089 2090 2091 2092 2093 0 - 2094 2095 0 - 2096 2097 2098 2099 2100 2101 0 - 2102 2103 2104 2105 2106 0 - 2107 2108 2109 2110 2111 0 - 2112 2113 0 - 2114 2115 2116 2117 2118 2119 2120 2121 0 - 2122 2123 2124 0 - 2125 2126 0 - 2127 2128 2129 2130 2131 0 - 2132 2133 2134 2135 2136 2137 0 - 2138 2139 2140 2141 2142 2143 0 - 2144 2145 2146 2147 2148 0 - 2149 2150 2151 2152 2153 2154 2155 2156 0 - 2157 2158 2159 2160 2161 0 - 2162 2163 2164 2165 2166 2167 0 - 2168 2169 2170 2171 2172 2173 2174 2175 0 - 2176 2177 2178 2179 2180 2181 0 - 2182 2183 2184 2185 2186 2187 2188 2189 2190 0 - 2191 2192 2193 2194 2195 2196 2197 2198 2199 0 - 2200 2201 2202 2203 2204 2205 2206 2207 0 - 2208 2209 2210 2211 2212 2213 2214 2215 2216 0 - 2217 2218 2219 2220 2221 0 - 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 0 - 2232 2233 2234 2235 0 - 2236 2237 2238 2239 2240 2241 2242 2243 2244 0 - 2245 2246 2247 2248 2249 2250 2251 0 - 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 0 - 2264 2265 2266 0 - 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 0 - 2277 2278 2279 2280 2281 0 - 2282 2283 2284 2285 2286 2287 2288 0 - 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 0 - 2302 2303 2304 2305 2306 2307 0 - 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 0 - 2318 2319 2320 2321 2322 2323 2324 0 - 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 0 - 2335 2336 2337 2338 2339 0 - 2340 2341 2342 0 - 2343 2344 2345 2346 2347 0 - 2348 2349 2350 2351 0 - 2352 2353 2354 2355 2356 0 - 2357 2358 2359 2360 2361 2362 2363 2364 0 - 2365 2366 2367 2368 2369 2370 2371 2372 0 - 2373 2374 2375 0 - 2376 2377 2378 2379 2380 2381 0 - 2382 2383 2384 2385 2386 0 - 2387 2388 2389 2390 2391 0 - 2392 2393 2394 0 - 2395 2396 2397 0 - 2398 2399 2400 2401 0 - 2402 2403 2404 0 - 2405 2406 2407 2408 0 - 2409 2410 2411 2412 2413 0 - 2414 2415 2416 2417 0 - 2418 2419 2420 2421 2422 2423 2424 0 - 2425 2426 2427 2428 2429 2430 2431 2432 2433 0 - 2434 2435 2436 2437 2438 2439 0 - 2440 2441 2442 2443 2444 2445 0 - 2446 2447 2448 2449 2450 2451 2452 2453 2454 0 - 2455 2456 2457 2458 2459 0 - 2460 2461 2462 2463 2464 2465 2466 2467 2468 0 - 2469 2470 2471 2472 2473 2474 2475 0 - 2476 2477 2478 2479 2480 2481 2482 2483 2484 0 - 2485 2486 2487 2488 2489 2490 0 - 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 0 - 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 0 - 2512 2513 2514 2515 2516 2517 2518 2519 2520 0 - 2521 2522 2523 2524 0 - 2525 2526 2527 2528 2529 2530 0 - 2531 2532 2533 2534 2535 2536 0 - 2537 2538 2539 2540 2541 0 - 2542 2543 2544 2545 2546 2547 0 - 2548 2549 2550 2551 2552 0 - 2553 2554 2555 2556 2557 0 - 2558 2559 2560 2561 0 - 2562 2563 2564 0 - 2565 2566 2567 2568 2569 2570 2571 0 - 2572 2573 2574 2575 2576 2577 2578 2579 2580 0 - 2581 2582 2583 2584 0 - 2585 2586 2587 0 - 2588 2589 2590 2591 2592 0 - 2593 2594 2595 2596 2597 0 - 2598 2599 2600 2601 0 - 393 703 990 1193 1334 1701 2182 0 - 173 394 704 1117 1194 1515 1629 2047 0 - 395 705 1118 1516 1702 2183 2335 0 - 98 396 706 991 1195 1517 2184 2336 0 - 992 1119 1335 1630 1703 2048 0 - 174 707 993 2049 2185 0 - 994 1631 1704 2186 0 - 99 708 995 1196 1336 1518 1632 0 - 100 996 1197 1337 1519 2187 0 - 175 397 997 1198 1338 1705 2337 0 - 398 1120 1520 1706 2050 0 - 101 998 1339 2051 2188 0 - 102 399 1340 1633 2052 2189 2338 0 - 1199 1521 1634 2190 2339 0 - 221 400 549 1341 1452 1852 2191 0 - 222 401 775 1342 1453 1522 2192 0 - 402 1454 1523 1853 2053 2537 0 - 103 223 295 403 1524 2538 0 - 104 296 404 776 1455 1525 2193 0 - 297 550 777 1854 2107 2194 0 - 405 778 1343 1456 1526 1855 2195 0 - 105 224 406 551 779 2054 2108 2196 0 - 552 1457 2109 2539 0 - 1344 1527 2055 2540 0 - 407 553 1458 1528 2197 0 - 225 298 408 554 780 1345 1856 0 - 106 409 781 1346 2056 2110 2198 2541 0 - 226 782 1529 2111 2199 0 - 410 613 709 1200 1347 1459 1707 0 - 107 176 411 614 710 1201 1708 0 - 177 412 711 1202 1348 1709 0 - 108 712 783 1121 1203 1349 1710 0 - 109 413 615 713 1350 1460 1711 0 - 784 1712 0 - 110 178 414 616 785 1122 0 - 111 1351 1461 0 - 1204 1462 1713 0 - 415 617 786 1123 1714 0 - 787 1205 0 - 1 227 1124 1206 1530 0 - 2 228 714 1207 1531 0 - 59 999 1208 2112 0 - 715 1125 1209 2113 0 - 3 229 1000 1126 2057 2418 0 - 4 60 1001 1532 0 - 1533 2419 0 - 5 882 1002 1672 0 - 6 61 716 1210 1534 2058 2420 0 - 62 883 1003 1211 1535 1673 2421 0 - 63 230 1004 1212 1674 2422 0 - 231 1127 1536 2059 2423 0 - 232 884 1128 1537 2060 2424 0 - 299 717 1213 1538 1715 1980 2200 0 - 300 718 1214 1716 0 - 618 719 913 1675 1717 2201 0 - 7 1718 1981 0 - 8 914 1539 1982 2202 0 - 301 555 915 1155 1540 2203 0 - 302 916 1215 1719 1983 2204 0 - 556 917 1216 1541 2205 0 - 9 557 1676 1984 0 - 10 619 720 1217 1542 1985 2340 0 - 11 303 721 1986 0 - 304 558 1218 1677 1720 1987 2341 0 - 559 620 1156 1721 1988 2206 0 - 12 1219 1543 2207 2342 0 - 621 885 1005 1220 1352 1722 1807 1857 0 - 622 1157 1221 1353 1544 1882 0 - 179 1222 1545 1635 1808 1858 1883 2061 2425 2542 0 - 886 1158 1546 1723 1884 2114 2426 0 - 623 1006 1223 1547 1885 2427 0 - 180 887 1224 1354 1548 1724 2115 2428 0 - 181 1007 1225 1636 1886 2062 2116 2543 0 - 888 1008 1159 1637 1725 1809 1859 2117 0 - 64 182 1009 1355 1549 1860 1887 0 - 183 624 1160 2063 2118 2429 0 - 625 1010 1226 1356 1550 1638 0 - 1227 1726 1810 2119 2544 0 - 65 184 1011 1228 1357 1727 1811 1861 1888 2430 0 - 626 1551 1728 2064 2431 2545 0 - 66 627 1161 1639 1729 2546 0 - 628 889 1162 1552 1862 1889 2065 2432 0 - 67 629 1163 1358 1640 1812 1890 2066 2120 2433 2547 0 - 68 1229 1553 1641 2121 0 - 416 560 630 1012 1359 1463 1989 2208 0 - 417 631 788 1360 1464 1891 2209 2343 0 - 305 418 632 0 - 306 419 633 789 1013 1465 1892 2210 2344 0 - 420 634 1014 1361 1466 1893 2211 0 - 307 421 1015 1894 2212 2345 0 - 308 561 790 1016 2213 0 - 309 1017 1467 1895 1990 2214 0 - 422 562 791 1018 1362 1991 0 - 635 1363 1992 2346 0 - 423 563 636 1019 1468 2215 2347 0 - 310 1020 1364 2216 0 - 424 637 1365 1678 1993 2217 0 - 425 1642 1896 2067 2434 2548 0 - 426 638 2549 0 - 1366 2068 2550 0 - 427 639 792 1679 1897 2218 2435 0 - 793 1367 1643 1994 2069 2436 2551 0 - 428 794 1368 1898 1995 2219 0 - 1369 2220 2437 0 - 429 795 1370 1680 1996 0 - 430 640 796 2070 2438 2552 0 - 431 641 797 1899 1997 2071 2439 0 - 798 1644 2221 0 - 233 432 564 722 890 918 1021 1230 1813 1863 1998 0 - 13 112 234 433 723 1231 2553 0 - 434 565 724 891 1999 2440 0 - 113 435 566 919 1022 1232 0 - 436 725 892 1233 2000 2441 0 - 114 726 1234 1814 0 - 14 235 1023 1815 2001 2442 2554 0 - 236 437 1024 1235 2555 0 - 727 893 920 1025 0 - 115 567 921 2443 2556 0 - 15 116 237 728 922 1026 1236 0 - 16 117 438 729 2002 0 - 439 568 894 923 1027 1237 1816 2444 0 - 238 440 569 1028 1238 1817 1864 2003 2445 0 - 118 239 1029 2557 0 - 119 311 441 730 1239 2446 0 - 240 442 799 1240 1371 2222 0 - 312 443 731 895 1730 2004 2223 2447 0 - 241 732 924 1030 1372 1731 1818 0 - 120 444 733 925 1031 1373 1681 1732 2224 2448 0 - 242 800 1032 1374 1733 1819 2005 2449 0 - 243 313 445 1033 1241 2225 0 - 314 801 896 926 1034 1734 1820 2226 0 - 121 315 927 1035 1242 1735 2006 2227 0 - 122 928 1036 1243 1375 2228 2450 0 - 123 316 446 734 2007 0 - 244 317 447 802 1037 1244 1376 1682 1736 1821 2008 2451 0 - 245 448 803 929 1737 2452 0 - 124 246 318 1038 1377 2229 0 - 247 319 449 804 897 930 2009 2453 0 - 125 450 805 1378 1822 2230 2454 0 - 248 806 1245 2231 0 - 320 451 735 1554 2455 0 - 249 452 642 736 1738 1865 0 - 453 643 807 1739 1900 2348 0 - 321 737 808 1129 1740 1901 0 - 738 809 1164 0 - 322 810 1165 1741 1866 0 - 250 323 644 739 811 1555 0 - 324 1742 1902 2558 0 - 454 812 0 - 645 740 1556 2349 2456 2559 0 - 325 455 741 1130 0 - 251 326 456 813 1743 1867 1903 2350 2457 0 - 646 1166 1744 2560 0 - 252 327 457 647 814 1131 1167 1557 1868 1904 2458 0 - 458 648 815 1168 1905 2351 2459 2561 0 - 742 1039 1246 1379 1683 1745 1869 2010 0 - 1247 1380 1558 1906 0 - 185 743 1248 1559 1870 1907 2072 2460 0 - 328 744 1040 1249 1560 1684 1908 2461 0 - 186 329 745 1250 1381 1561 1746 2011 2462 0 - 187 330 1041 1251 1909 2073 0 - 188 746 1042 1685 2074 0 - 189 1043 1382 1562 1871 1910 2012 0 - 190 1686 2075 2463 0 - 331 1383 1563 1911 2464 0 - 1044 1252 1384 1564 1872 2465 0 - 191 332 1045 1253 1385 1687 1747 1873 1912 2013 2466 0 - 1565 1748 2076 2467 0 - 1386 1913 2077 2468 0 - 747 1645 0 - 748 931 1387 1469 1749 1823 0 - 17 749 1470 1646 1824 1914 0 - 18 750 932 1388 1750 1825 0 - 816 933 1389 1471 1751 1915 0 - 751 817 1390 1752 1826 1916 0 - 19 818 1391 1647 1753 1827 0 - 752 819 934 1169 1472 0 - 20 820 1392 0 - 821 935 1170 1917 0 - 822 1171 1393 1648 1828 1918 0 - 333 753 2078 0 - 21 253 0 - 22 254 754 1046 2079 2562 0 - 755 1047 2122 0 - 23 255 1048 2080 2563 0 - 256 2081 2123 0 - 334 1049 2564 0 - 1050 2124 0 - 649 936 1051 1394 1919 2125 0 - 937 1395 1920 2232 2469 0 - 650 1396 1649 2470 0 - 651 938 1052 2233 2471 0 - 1053 1397 1921 2472 0 - 652 939 2473 0 - 653 1650 2234 0 - 654 940 1922 2474 0 - 655 1398 1651 1923 2126 2235 2475 0 - 257 656 756 898 941 1399 2236 0 - 126 258 657 757 0 - 758 899 1132 1924 2127 2237 2476 0 - 69 127 658 942 1400 1925 2128 0 - 128 659 759 1926 2238 2477 0 - 129 660 760 943 1401 1927 2129 2239 2478 0 - 259 1133 1402 1652 2479 0 - 900 944 1653 2130 2240 0 - 130 945 1403 1928 2241 2480 0 - 70 661 761 1404 1654 2481 0 - 71 662 901 946 2242 2482 0 - 72 260 1405 1929 2483 0 - 73 663 1655 2243 0 - 74 131 664 1406 1656 1930 2131 2244 2484 0 - 459 570 1566 2014 2245 2485 0 - 24 947 1054 0 - 460 1567 2015 2246 2486 0 - 461 948 1055 1473 2247 2487 0 - 25 571 949 1056 1874 2248 0 - 26 950 1057 1568 0 - 75 951 1058 1474 2016 2249 0 - 27 462 572 1059 2017 0 - 28 76 1569 2018 2488 0 - 77 463 573 952 1060 1475 1570 2250 2489 0 - 1061 2251 0 - 464 953 1571 1875 2019 2490 0 - 1572 1657 2491 0 - 29 1573 1658 2492 2565 0 - 30 665 1574 1754 2566 0 - 574 902 1575 1755 2020 2132 2352 2493 0 - 31 954 1062 1407 1756 2387 2567 0 - 666 823 1063 1576 2353 2494 0 - 824 1408 1757 2133 2388 0 - 32 825 1064 1409 1659 1758 2021 2495 2568 0 - 1065 1660 2134 2354 2569 0 - 33 575 826 903 955 1066 1661 1759 2135 0 - 576 667 827 2136 2496 0 - 34 668 828 956 1067 1410 1577 1662 0 - 35 577 1760 2137 2389 2570 0 - 36 578 829 904 1068 1411 2022 0 - 78 579 669 905 957 1069 1578 1761 2355 2497 0 - 79 580 830 1070 1412 1762 2023 2356 2498 0 - 670 831 958 1579 1763 2390 2499 2571 0 - 671 832 906 959 1580 2024 2391 2500 0 - 132 335 465 1254 2082 0 - 261 466 1071 1255 1688 1764 1829 2025 2252 2392 0 - 262 467 833 1134 1172 1256 1931 2253 2357 0 - 468 1135 1257 1830 1932 2083 0 - 336 469 1136 1173 1765 1933 2026 2138 2254 2358 0 - 263 1072 1766 1831 2084 2393 0 - 133 470 834 1073 1258 1767 1934 2139 2359 0 - 337 471 1259 1768 2027 2140 2255 0 - 264 338 472 1074 1137 1260 1935 2085 2141 2256 2360 0 - 835 1075 1174 1689 2086 2257 0 - 473 836 1076 1936 2028 2258 0 - 134 265 339 837 1077 1261 0 - 135 340 1078 1262 1769 1937 2029 2259 2394 0 - 1263 2030 2087 2361 0 - 474 1079 1264 1690 1770 1832 2260 2362 0 - 266 341 475 838 1080 1265 1691 1771 1833 1938 2031 2363 0 - 136 267 342 1081 2088 2142 2261 0 - 1175 1772 2032 2262 0 - 268 839 1266 2143 2263 2364 0 - 37 476 1267 0 - 137 477 840 1268 1773 0 - 138 841 1269 1774 2395 0 - 38 139 842 1270 0 - 39 1271 1775 2396 0 - 40 140 478 0 - 479 1272 1776 2264 0 - 1176 1777 2265 0 - 141 480 843 1177 2266 2397 0 - 481 762 907 960 1082 0 - 482 763 908 0 - 764 909 961 1083 0 - 483 910 962 1084 0 - 41 1273 1476 1581 1939 2501 2572 0 - 581 911 1582 1778 1940 2267 2365 2502 0 - 42 1779 2398 2573 0 - 80 142 582 844 1274 1477 1780 1941 2366 0 - 143 845 1275 1478 1583 1942 2268 2367 2503 0 - 144 1479 1781 1943 2269 2504 0 - 1276 1944 2270 2368 2574 0 - 145 583 1480 1584 1945 2271 2505 2575 0 - 81 146 1277 1481 1782 1946 2272 2399 2576 0 - 147 584 1278 1585 2273 2506 0 - 43 82 1279 1586 2369 2507 2577 0 - 83 585 912 1280 1482 1587 1783 2274 2370 2508 0 - 84 586 846 1281 1784 1947 2371 2509 0 - 847 1588 1785 2400 2510 2578 0 - 85 587 1786 2275 2579 0 - 86 148 848 1948 2276 2372 2401 2511 2580 0 - 149 1589 0 - 588 1834 2033 2277 0 - 44 192 1590 1835 0 - 150 193 589 849 0 - 45 194 850 1591 2034 2278 0 - 151 195 590 851 2279 0 - 152 591 1592 2280 0 - 592 1593 1836 2281 0 - 852 1594 2035 0 - 343 484 1595 2512 0 - 344 485 1178 1596 1949 2144 2282 2513 0 - 345 486 672 1483 1597 1950 2283 2514 0 - 346 487 1598 2145 2284 2515 0 - 347 1837 1951 2146 0 - 348 1179 1838 2147 2285 0 - 87 488 1484 1599 1952 2286 0 - 349 1180 1485 1600 1953 2287 2516 2581 0 - 350 673 1601 0 - 88 674 1602 2517 2582 0 - 489 675 1603 2518 2583 0 - 351 490 676 1181 1604 1954 2519 0 - 89 491 677 1182 1839 1955 2148 2288 2520 2584 0 - 269 492 1085 1282 1413 1692 2402 0 - 493 1283 0 - 270 1086 1414 2403 0 - 153 352 494 1087 1284 1693 2373 0 - 271 353 495 1088 1285 2374 0 - 354 1089 0 - 496 1090 1415 0 - 154 355 497 0 - 272 356 498 1091 1286 1416 1694 2375 0 - 273 499 2404 0 - 155 357 1287 2089 0 - 853 1288 1956 0 - 156 196 593 854 963 1289 1787 1957 0 - 157 358 855 1290 1958 0 - 158 359 856 1291 1788 1959 2405 0 - 197 360 1292 1960 2090 0 - 159 198 594 857 2091 0 - 160 361 595 964 1961 0 - 596 1293 1789 2406 0 - 362 858 965 1962 2092 2407 0 - 161 859 1963 2093 2408 0 - 597 678 966 1092 1294 1417 1695 1840 2289 2409 0 - 46 679 860 1138 1183 1295 1418 1605 1964 2290 2376 0 - 47 363 680 1296 1606 0 - 364 598 1139 1184 1607 1965 2149 2291 2377 0 - 48 967 1093 1419 1841 2410 0 - 599 681 861 968 1094 1297 1420 1966 2150 2378 0 - 365 682 862 1095 1298 1608 1696 1967 2292 2379 0 - 366 1299 1421 1609 2151 2293 0 - 683 969 1096 1422 1697 1968 2152 2294 0 - 49 863 1097 1140 1423 1663 1842 0 - 864 970 1098 1185 1698 2295 0 - 600 684 865 1141 1186 1699 2153 2296 0 - 367 601 971 1187 1424 1610 1969 2297 0 - 50 368 685 866 972 1099 1300 1425 1611 1664 0 - 369 973 1100 1301 1970 2298 2411 0 - 51 370 1142 2154 0 - 602 686 974 1101 1302 1612 1700 1843 2299 2380 0 - 371 1102 1426 2155 2300 0 - 372 687 867 975 1143 1188 1613 1971 2412 0 - 688 868 1189 1427 1665 1844 1972 2156 2301 2381 2413 0 - 274 500 1303 1790 1845 2302 0 - 275 501 1190 1304 1973 2303 0 - 162 199 276 373 502 1305 1791 2585 0 - 163 200 503 1306 1792 1974 0 - 201 374 504 1307 1793 2304 0 - 164 375 1308 1794 1846 1975 0 - 202 277 376 505 1309 1666 1976 2094 2305 2586 0 - 377 1191 1667 1795 1847 2306 0 - 203 506 1977 2307 0 - 165 278 378 1310 1668 0 - 1311 1796 1848 2587 0 - 204 507 0 - 205 279 379 508 1312 1797 1849 1978 0 - 280 380 509 1192 1979 2095 0 - 281 510 765 1313 1798 2588 0 - 282 766 1103 1799 1850 2589 0 - 511 1104 1314 1800 2157 0 - 1105 1801 1851 2158 0 - 283 512 1144 2159 2521 0 - 2522 2590 0 - 284 767 1106 1315 0 - 1107 1316 1802 2591 0 - 1108 1317 2160 2523 0 - 768 1318 2524 2592 0 - 513 769 1145 2161 0 - 52 514 689 770 0 - 515 771 2036 2162 0 - 516 690 772 1486 0 - 517 773 2037 2163 0 - 518 691 774 976 1487 2164 0 - 53 1669 2038 0 - 54 90 519 977 1488 2039 0 - 520 692 2165 0 - 91 521 693 1670 2166 0 - 55 92 1671 2167 0 - 285 522 694 869 1146 1428 1489 2308 0 - 523 1147 1490 2525 2593 0 - 524 1148 2168 2309 2526 0 - 525 695 870 978 1109 1429 1491 2169 0 - 526 696 979 1110 1430 1492 2170 2310 2527 0 - 286 527 1111 1149 2171 2311 2594 0 - 528 871 980 1112 1431 1493 2312 0 - 287 529 697 872 1150 2172 2313 2528 0 - 1494 2173 2595 0 - 981 1113 1495 2314 2596 0 - 982 1114 1432 2174 2315 2529 0 - 530 698 983 1115 1496 2316 2530 0 - 288 1116 1433 2175 2317 2597 0 - 206 531 1151 1497 1614 2531 0 - 532 1152 1615 1803 2318 2532 0 - 533 699 1498 1804 2319 2533 0 - 207 534 1153 2320 0 - 208 1499 2321 0 - 209 535 700 1154 2322 2534 0 - 1500 1805 2323 0 - 701 1616 2535 0 - 536 702 1501 1617 1806 2324 2536 0 - 289 537 603 1319 1434 1502 1876 2325 2414 0 - 290 538 1320 1435 1503 1618 2326 0 - 210 539 1321 1504 1619 1877 2096 2598 0 - 381 540 1322 1505 1620 2327 0 - 541 1436 1506 2176 2328 0 - 211 291 382 542 1323 2097 2177 2329 2599 0 - 212 1507 2098 2330 0 - 213 543 1437 1508 1621 1878 2331 0 - 214 292 544 604 2099 2178 2332 0 - 605 1324 1509 2179 2415 2600 0 - 606 1325 1438 1622 1879 2180 2333 0 - 215 545 607 1439 0 - 216 293 383 546 608 1326 1440 1880 0 - 294 384 547 1623 1881 2100 2416 0 - 548 1441 2101 2181 2334 2417 2601 0 - 56 873 1327 1442 1510 1624 2382 0 - 385 609 1625 2040 2383 0 - 93 166 217 610 874 984 1328 1443 1511 2384 0 - 218 386 1329 1444 1626 2041 0 - 167 387 875 1330 1445 0 - 168 985 1446 1512 0 - 57 876 1447 2042 2102 0 - 219 877 986 1513 2103 0 - 58 169 388 878 987 1331 1448 1627 0 - 94 170 389 988 1332 1514 2043 0 - 95 220 390 611 879 1333 1449 2044 2385 0 - 171 391 1450 2104 0 - 96 612 2045 0 - 392 880 989 1628 2046 2105 0 - 97 172 881 1451 2106 2386 0 - 1 21 46 56 0 - 17 29 37 41 44 0 - 2 13 30 47 52 0 - 18 22 24 31 42 48 0 - 3 7 14 19 23 32 49 53 57 0 - 25 33 0 - 4 8 45 54 0 - 15 26 34 38 50 58 0 - 35 39 0 - 5 9 20 27 36 0 - 6 10 28 43 0 - 11 16 40 51 0 - 12 55 0 - 59 69 80 93 0 - 60 64 87 90 0 - 75 81 94 0 - 61 70 76 82 88 0 - 62 71 77 78 83 0 - 63 65 72 79 84 95 0 - 66 73 85 96 0 - 67 74 86 89 91 97 0 - 68 92 0 - 119 132 149 155 0 - 103 107 112 126 162 0 - 113 127 133 137 142 150 156 163 166 0 - 98 104 128 143 153 157 0 - 108 114 138 158 164 167 0 - 109 120 129 144 168 0 - 105 110 151 159 0 - 111 115 130 145 160 0 - 99 116 134 139 165 169 0 - 121 135 146 170 0 - 100 122 147 152 0 - 117 123 140 154 0 - 101 118 124 136 171 0 - 102 106 125 131 141 148 161 172 0 - 173 179 185 192 206 210 0 - 176 199 0 - 193 196 200 217 0 - 177 180 186 201 218 0 - 181 187 197 202 207 211 0 - 174 188 208 212 219 0 - 182 189 194 203 213 0 - 178 183 190 195 198 209 214 0 - 204 215 0 - 175 184 191 205 216 220 0 - 221 233 249 257 261 269 274 289 0 - 222 227 240 253 262 275 285 290 0 - 223 228 234 258 276 281 0 - 241 254 263 270 282 0 - 229 235 242 255 259 0 - 236 243 264 271 277 286 291 0 - 224 256 283 287 292 0 - 237 250 265 278 284 0 - 225 230 238 244 251 260 266 272 279 293 0 - 231 245 273 0 - 239 246 267 288 0 - 232 247 252 280 294 0 - 226 248 268 0 - 311 320 333 335 343 357 0 - 295 305 363 373 0 - 312 336 344 364 385 0 - 296 306 328 345 352 358 365 381 0 - 299 329 337 346 366 374 386 0 - 300 321 347 359 375 387 0 - 307 313 330 338 353 360 376 382 0 - 297 308 314 322 348 354 377 0 - 301 331 349 361 367 0 - 323 339 350 368 378 388 0 - 302 309 315 324 334 340 369 389 0 - 303 316 325 355 370 0 - 298 304 317 326 332 341 356 379 383 390 0 - 310 318 342 371 391 0 - 319 327 351 362 372 380 384 392 0 - 441 451 465 484 0 - 393 400 410 416 424 432 452 466 481 492 500 537 0 - 401 417 442 467 501 522 538 0 - 394 402 425 468 476 493 523 531 539 0 - 403 411 418 426 433 502 510 514 0 - 395 434 443 459 469 485 515 524 532 0 - 435 453 470 477 503 511 525 0 - 396 404 419 427 486 494 516 540 0 - 412 436 460 471 482 487 504 517 0 - 413 420 444 461 518 526 533 541 0 - 421 437 445 472 495 505 527 534 542 0 - 405 428 473 488 496 506 519 528 543 0 - 406 414 512 520 529 535 544 0 - 422 429 454 462 507 545 0 - 438 446 455 478 497 513 0 - 407 423 439 463 474 479 483 530 536 0 - 397 408 440 447 456 475 498 508 546 0 - 398 415 430 448 489 499 0 - 431 449 457 464 490 509 547 0 - 399 409 450 458 480 491 521 548 0 - 549 560 564 588 597 603 0 - 565 570 574 581 598 609 0 - 566 582 589 593 599 610 0 - 550 561 571 575 0 - 551 576 590 594 600 604 0 - 555 567 583 595 601 0 - 552 577 596 605 0 - 556 584 591 606 0 - 557 562 572 578 607 0 - 553 563 568 573 579 585 592 602 0 - 554 558 569 580 586 608 611 0 - 559 587 612 0 - 613 621 630 637 642 656 678 0 - 622 631 679 694 0 - 614 632 638 657 665 680 689 0 - 643 649 658 681 695 0 - 623 633 639 659 666 672 682 690 0 - 615 618 634 660 683 691 696 699 0 - 616 624 667 684 692 697 700 0 - 625 644 668 673 685 0 - 619 635 645 650 661 674 701 0 - 636 651 662 669 686 698 702 0 - 617 626 640 652 670 675 0 - 620 627 646 653 663 0 - 628 641 647 654 671 676 687 0 - 629 648 655 664 677 688 693 0 - 730 735 747 753 0 - 703 709 722 736 742 748 756 762 0 - 704 743 749 0 - 710 714 723 757 765 770 0 - 705 724 731 758 771 0 - 732 750 754 766 0 - 706 744 759 772 0 - 711 717 725 745 763 773 0 - 712 715 718 726 737 751 0 - 713 719 733 755 760 774 0 - 707 727 738 746 752 764 0 - 708 728 739 767 0 - 716 720 740 761 768 0 - 721 729 734 741 769 0 - 775 788 799 833 853 860 869 873 0 - 807 816 834 840 844 849 854 861 870 874 0 - 776 789 792 823 845 855 862 0 - 783 808 817 824 841 856 875 0 - 793 800 818 825 863 876 0 - 809 819 835 864 877 0 - 777 784 790 801 810 826 0 - 778 794 836 850 871 0 - 779 785 827 851 857 865 872 0 - 811 828 837 842 866 878 0 - 791 795 812 820 829 0 - 780 802 813 830 838 846 879 0 - 786 796 803 831 847 0 - 797 804 814 821 832 852 858 867 880 0 - 781 805 815 822 843 848 859 868 881 0 - 782 787 798 806 839 0 - 885 890 898 907 0 - 886 891 895 899 902 911 0 - 887 892 908 0 - 893 909 0 - 888 896 900 903 0 - 882 904 0 - 883 894 901 905 910 912 0 - 884 889 897 906 0 - 918 931 941 960 966 0 - 924 932 947 954 967 0 - 919 933 936 942 963 968 978 984 0 - 913 925 943 948 969 976 979 985 0 - 920 934 961 970 986 0 - 926 944 949 955 0 - 914 977 980 0 - 915 921 937 945 964 971 0 - 922 950 956 972 987 0 - 916 927 951 973 981 988 0 - 917 928 982 0 - 923 938 946 952 957 962 974 983 0 - 929 939 958 0 - 930 935 940 953 959 965 975 989 0 - 990 1005 1012 1021 1039 1071 1082 1085 1092 0 - 1030 1046 1054 1062 1072 1086 1093 1103 0 - 999 1022 1051 1073 1094 1104 1109 0 - 991 1006 1013 1040 1063 1087 1095 0 - 1014 1031 1047 1055 1096 1110 0 - 992 1000 1023 1032 1048 1064 1097 0 - 1007 1015 1024 1033 1041 1065 1074 1088 1111 0 - 993 1025 1042 1075 1083 1098 0 - 994 1008 1016 1034 1056 1066 1089 1105 0 - 1001 1009 1043 1076 1090 1112 0 - 995 1010 1026 1057 1067 1077 1099 1106 0 - 1017 1035 1049 1058 1078 1100 1107 1113 0 - 996 1036 1044 1050 1108 1114 0 - 1002 1018 1059 1068 0 - 1003 1019 1027 1052 1060 1069 1079 1084 1101 1115 0 - 997 1004 1011 1028 1037 1045 1053 1070 1080 1091 0 - 998 1020 1029 1038 1061 1081 1102 1116 0 - 1124 1134 1138 1146 0 - 1117 1135 1147 1151 0 - 1118 1132 1136 1139 1148 1152 0 - 1121 1125 1129 0 - 1119 1126 1133 1140 0 - 1137 1149 1153 0 - 1122 1141 1144 1150 1154 0 - 1130 1142 1145 0 - 1120 1123 1127 0 - 1128 1131 1143 0 - 1157 1172 1183 1190 0 - 1158 1173 1178 1184 0 - 1164 1169 1174 1185 0 - 1159 1165 1179 1191 0 - 1160 1186 0 - 1155 1180 1187 0 - 1156 1161 1166 1175 1176 0 - 1162 1167 1170 1181 1188 1192 0 - 1163 1168 1171 1177 1182 1189 0 - 1239 1254 1287 0 - 1193 1200 1220 1230 1246 1255 1282 1294 1303 1319 0 - 1206 1221 1240 1247 1256 1288 1295 1304 1320 1327 0 - 1194 1222 1248 1257 1267 1273 1283 1321 0 - 1201 1207 1231 1296 1305 1313 0 - 1208 1232 1258 1268 1274 1289 1297 1306 1314 1328 0 - 1195 1223 1249 1275 1284 1290 1298 1322 0 - 1202 1213 1224 1233 1250 1259 1299 1307 1329 0 - 1203 1209 1214 1234 1269 1291 1308 1330 0 - 1225 1235 1241 1251 1260 1276 1285 1292 1309 1323 0 - 1196 1226 1236 1261 1270 1300 1310 1315 1331 0 - 1204 1227 1271 1293 1311 1324 0 - 1215 1242 1262 1277 1301 1316 1332 0 - 1197 1216 1243 1252 1278 1317 1325 0 - 1210 1217 1263 1279 1318 0 - 1211 1237 1264 1272 1280 1302 0 - 1198 1212 1218 1228 1238 1244 1253 1265 1281 1286 1312 1326 1333 0 - 1199 1205 1219 1229 1245 1266 0 - 1334 1341 1347 1352 1359 1365 1379 1387 1399 1413 1417 1434 0 - 1342 1353 1360 1371 1380 1418 1428 1435 1442 0 - 1366 1372 1388 1407 1414 1419 0 - 1389 1394 1400 1420 1429 1443 0 - 1348 1354 1381 1421 1444 0 - 1349 1390 1408 1445 0 - 1350 1361 1373 1401 1422 1430 1436 1446 0 - 1335 1367 1374 1391 1402 1409 1423 1447 0 - 1343 1355 1368 1382 1415 1431 1437 0 - 1351 1383 1395 1403 1424 0 - 1336 1356 1410 1425 1448 0 - 1337 1369 1375 1384 1432 1438 0 - 1362 1370 1392 1411 1439 0 - 1344 1363 1396 1404 0 - 1338 1345 1357 1376 1385 1397 1405 1412 1416 1440 1449 0 - 1339 1364 1377 1426 1433 1450 0 - 1340 1346 1358 1378 1386 1393 1398 1406 1427 1441 1451 0 - 1452 1459 1463 1469 1502 0 - 1453 1464 1489 1503 1510 0 - 1454 1470 1476 1490 1497 1504 0 - 1471 1477 1491 1511 0 - 1455 1465 1478 1483 1486 1505 0 - 1460 1466 1473 1479 1487 1492 1498 1506 1512 0 - 1472 1499 1507 1513 0 - 1456 1484 1488 1493 1508 0 - 1461 1480 1485 0 - 1457 1462 1494 1509 0 - 1467 1474 1481 1495 1500 1514 0 - 1458 1468 1475 1482 1496 1501 0 - 1554 1572 1589 1595 0 - 1522 1530 1544 1558 1605 1618 1624 0 - 1515 1523 1545 1559 1573 1581 1590 1614 1619 0 - 1524 1531 1574 1606 0 - 1516 1546 1566 1575 1582 1596 1607 1615 1625 0 - 1517 1525 1547 1560 1576 1583 1597 1608 1620 0 - 1538 1548 1561 1567 1598 1609 1626 0 - 1526 1532 1539 1549 1562 1591 1599 1621 0 - 1533 1540 1563 1584 1600 1610 0 - 1518 1550 1555 1568 1577 1601 1611 1627 0 - 1519 1541 1564 1585 1592 1622 0 - 1527 1534 1542 1556 1569 1586 1602 1616 0 - 1528 1535 1570 1578 1587 1593 1612 1617 0 - 1520 1536 1551 1565 1579 1588 1603 0 - 1537 1552 1557 1571 1580 1594 1604 1613 1623 1628 0 - 1521 1529 1543 1553 0 - 1645 1657 0 - 1629 1635 1642 1646 1658 0 - 1630 1643 1647 1652 1659 1663 1669 0 - 1636 1660 1666 0 - 1631 1637 1653 1661 1667 0 - 1632 1638 1662 1664 1668 0 - 1649 1654 0 - 1639 1650 1655 0 - 1633 1640 1648 1651 1656 1665 1670 0 - 1634 1641 1644 1671 0 - 1678 1683 1688 1692 1695 0 - 1679 1684 1693 1696 0 - 1675 1681 1697 0 - 1685 1689 1698 0 - 1686 1699 0 - 1672 1676 1680 0 - 1673 1690 1700 0 - 1674 1677 1682 1687 1691 1694 0 - 1701 1707 1722 1738 1745 1749 1764 1790 0 - 1708 1754 1791 1798 0 - 1702 1723 1730 1755 1765 1778 1803 0 - 1731 1750 1756 1766 1779 1799 0 - 1739 1751 1767 1773 1780 1787 1792 1800 0 - 1709 1715 1724 1746 1768 1793 0 - 1710 1716 1740 1752 1757 1774 1788 1794 0 - 1711 1717 1732 1781 1804 0 - 1703 1718 1733 1753 1758 0 - 1704 1712 1725 1734 1741 1759 1795 1801 0 - 1713 1726 1760 1775 1789 1796 0 - 1719 1735 1742 1769 1782 1802 1805 0 - 1761 1770 1776 1783 1806 0 - 1705 1720 1727 1736 1743 1747 1762 1771 1784 1797 0 - 1706 1714 1728 1737 1748 1763 1785 0 - 1721 1729 1744 1772 1777 1786 0 - 1807 1813 1823 1829 1834 1840 1845 0 - 1808 1824 1830 1835 0 - 1818 1825 1831 1841 1850 0 - 1814 1826 1837 1846 0 - 1815 1819 1827 1842 0 - 1809 1820 1838 1847 1851 0 - 1810 1848 0 - 1816 1832 1836 1843 0 - 1811 1817 1821 1833 1849 0 - 1812 1822 1828 1839 1844 0 - 1852 1857 1863 1865 1869 1876 0 - 1853 1858 1870 1877 0 - 1854 1859 1866 1874 0 - 1855 1860 1871 1878 0 - 1872 1879 0 - 1856 1861 1864 1867 1873 1880 0 - 1862 1868 1875 1881 0 - 1882 1891 1906 1931 1956 1964 1973 0 - 1883 1896 1907 1914 1932 1939 0 - 1884 1924 1933 1940 1949 1965 0 - 1900 1915 1919 1925 1934 1941 1957 1966 1974 0 - 1885 1892 1897 1908 1926 1942 1950 1958 1967 0 - 1901 1916 1951 1959 1975 0 - 1893 1927 1943 1968 0 - 1886 1894 1909 1935 1944 1960 1976 0 - 1887 1898 1910 1936 1952 1977 0 - 1911 1920 1928 1945 1953 1961 1969 0 - 1895 1902 1937 1946 1970 0 - 1888 1903 1912 1921 1929 1938 1947 1978 0 - 1889 1899 1904 1917 1922 1954 1962 1971 1979 0 - 1890 1905 1913 1918 1923 1930 1948 1955 1963 1972 0 - 1989 1993 1998 2010 2025 2033 0 - 1999 2004 2014 2020 2026 2036 2040 0 - 1980 2000 2011 2015 2027 2037 2041 0 - 1981 1994 2001 2005 2021 2038 2042 0 - 1982 1995 2012 2028 2034 2039 0 - 1983 1990 2006 2016 2029 2043 0 - 1984 1991 1996 2017 2022 0 - 1985 1992 2018 2030 0 - 1986 2002 2007 0 - 1987 2003 2008 2013 2023 2031 2044 0 - 1988 2032 2045 0 - 1997 2009 2019 2024 2035 2046 0 - 2078 2082 2089 0 - 2047 2053 2061 2067 2072 2083 2096 0 - 2068 2079 2084 0 - 2048 2057 2069 2080 2102 0 - 2062 2073 2085 2090 2094 2097 0 - 2049 2074 2086 2098 2103 0 - 2054 2063 2075 2081 2091 2099 0 - 2055 2058 2087 0 - 2050 2059 2064 2070 2076 0 - 2051 2088 2104 0 - 2060 2065 2071 2092 2095 2100 2105 0 - 2052 2056 2066 2077 2093 2101 2106 0 - 2114 2127 2132 2138 2144 2149 2162 2168 0 - 2112 2125 2128 2139 2150 2157 2169 0 - 2115 2140 2145 2151 2163 0 - 2113 2133 2146 0 - 2122 2129 2152 2164 2170 2176 0 - 2116 2134 2141 2171 2177 0 - 2107 2117 2130 2135 2147 2158 0 - 2108 2118 2123 2136 2153 2159 2165 2172 2178 0 - 2109 2119 2137 2173 2179 0 - 2124 2160 2174 2180 0 - 2154 2161 0 - 2142 2155 2175 0 - 2110 2120 2126 2131 2148 2156 2166 2181 0 - 2111 2121 2143 2167 0 - 2182 2191 2208 2217 2236 2252 2277 2289 2302 2325 0 - 2192 2209 2222 2253 2290 2303 2308 2326 0 - 2183 2223 2237 2245 2254 2267 2282 2291 2309 2318 0 - 2184 2193 2210 2218 2238 2268 2283 2292 2327 0 - 2200 2246 2255 2284 2293 2304 0 - 2201 2211 2224 2239 2247 2269 2294 2310 2319 2328 0 - 2212 2225 2256 2270 2305 2311 2320 2329 0 - 2185 2257 2295 2321 2330 0 - 2186 2194 2213 2226 2240 2248 2285 2306 0 - 2195 2202 2219 2258 2278 2286 2307 2312 2331 0 - 2196 2279 2296 2313 2322 2332 0 - 2203 2232 2241 2271 2287 2297 0 - 2204 2214 2227 2249 2259 2272 2298 2314 2323 0 - 2187 2205 2220 2228 2273 2280 2315 2333 0 - 2197 2215 2233 2242 2250 2260 2264 2274 2281 2299 2316 2324 0 - 2188 2216 2229 2251 2261 2300 2317 0 - 2206 2234 2243 2262 2265 2275 0 - 2189 2198 2230 2235 2244 2266 2276 2288 2301 2334 0 - 2190 2199 2207 2221 2231 2263 0 - 2343 2357 2376 2382 0 - 2335 2352 2358 2365 2377 2383 0 - 2348 2359 2366 2378 2384 0 - 2336 2344 2353 2367 2373 2379 0 - 2345 2354 2360 2368 2374 0 - 2340 2346 2349 2361 2369 0 - 2347 2355 2362 2370 2380 0 - 2337 2341 2350 2356 2363 2371 2375 2385 0 - 2338 2351 2372 2381 2386 0 - 2339 2342 2364 0 - 2392 2402 2409 2414 0 - 2387 2393 2398 2403 2410 0 - 2388 2395 2405 0 - 2389 2396 2406 2415 0 - 2394 2399 2411 0 - 2390 2400 2404 0 - 2391 2407 2412 2416 0 - 2397 2401 2408 2413 2417 0 - 2446 2455 2491 2512 0 - 2425 2434 2460 2492 2501 2525 2531 0 - 2426 2440 2447 2476 2485 2493 2502 2513 2526 2532 0 - 2427 2435 2461 2477 2494 2503 2514 0 - 2428 2441 2462 2486 2515 0 - 2448 2478 2487 2504 2527 2533 0 - 2418 2436 2442 2449 2479 2495 0 - 2429 2463 2496 2521 2528 2534 0 - 2419 2443 2464 2469 2480 2505 2516 2522 0 - 2437 2450 2465 2506 2523 2529 0 - 2420 2456 2470 2481 2488 2507 2517 2524 2535 0 - 2421 2444 2471 2482 2489 2497 2508 2530 2536 0 - 2422 2430 2445 2451 2457 2466 2472 2483 2498 2509 0 - 2423 2431 2438 2452 2467 2473 2499 2510 2518 0 - 2424 2432 2439 2453 2458 2474 2490 2500 2519 0 - 2433 2454 2459 2468 2475 2484 2511 2520 0 - 2537 2542 2548 2565 2572 2593 2598 0 - 2538 2549 2553 2566 2585 2588 0 - 2550 2562 2567 2573 2589 0 - 2551 2554 2563 2568 0 - 2543 2555 2569 2574 2586 2594 2599 0 - 2556 2575 2581 2590 0 - 2539 2544 2570 2587 2595 2600 0 - 2558 2564 2576 2591 2596 0 - 2540 2559 2577 2582 2592 0 - 2545 2552 2571 2578 2583 0 - 2557 2597 0 - 2546 2560 2579 0 - 2541 2547 2561 2580 2584 2601 0 diff --git a/examples/commandline/spear_qcp/scenario.txt b/examples/commandline/spear_qcp/scenario.txt deleted file mode 100644 index 13abddb8d..000000000 --- a/examples/commandline/spear_qcp/scenario.txt +++ /dev/null @@ -1,10 +0,0 @@ -algo = python -u examples/commandline/spear_qcp/target_algorithm/scripts/SATCSSCWrapper.py --mem-limit 1024 --script examples/commandline/spear_qcp/target_algorithm/spear-python/spearCSSCWrapper.py -paramfile = examples/commandline/spear_qcp/target_algorithm/spear-python/spear-params-mixed.pcs -execdir = . -deterministic = 0 -run_obj = runtime -overall_obj = PAR10 -cutoff_time = 5 -wallclock-limit = 60 -instance_file = examples/commandline/spear_qcp/instances.txt -feature_file = examples/commandline/spear_qcp/features.txt \ No newline at end of file diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver deleted file mode 100755 index bca96711f1470ad8839b4be7e8a611fd09c72d4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 124213 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

    `_D{R30M80$KHA_cY5O#%Z+>T`mRYW+o_KL-Pb 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. - */ - - - -#ifndef _CircularBufferFilter_hh_ -#define _CircularBufferFilter_hh_ - -#include -#include - -using namespace std; - - -class AbstractFilter -{ -public: - virtual void write(const char *buffer, int len)=0; - - /** - * handle partial writes and EINTR - */ - void systemWrite(int fd, const char *buf, size_t len) - { - const char *p=buf; - int n; - - do - { - n=::write(fd,p,len); - if(n<0) - { - if(errno==EINTR) - continue; - - perror("write failed: "); - break; - } - - len-=n; - p+=n; - } - while(len); - } -}; - -class NullFilter : public AbstractFilter -{ -private: - int fd; -public: - NullFilter(int fd): fd(fd) {} - - virtual void write(const char *buffer, int len) - { - systemWrite(fd,buffer,len); - } -}; - -/** - * This filter enforces a limit on an output file size. When the file - * exceeds the limit, only the first and last bytes of output are - * saved in the file. - * - * This filter transmits directly to file descriptor fd the first - * activateSize bytes and then it only transmits the last - * maxSize-activateSize bytes. - * - * TODO ??? - * - * - we currently use a circular buffer in memory to do the job. We - may instead work directly on the file, using lseek to rewind to - position activateSize as soon as the file size exceeds - maxSize. At the end, we have to reorder the bytes at the end of - the file. This may save memory but requires to be able to lseek - on the file. - * - * - the last part of the file to which we output doesn't necessarily - start at the beginning of a line. This may cause problem in some - applications. - */ -class CircularBufferFilter : public AbstractFilter -{ -private: - unsigned long long int total; // total number of bytes sent to this filter - unsigned long long int activateSize,maxSize,bufferSize; - - char *data; // circular buffer - unsigned int w; // position where to write in the circular buffer - - int fd; // file descriptor to write to - -public: - CircularBufferFilter() - { - data=NULL; - } - - CircularBufferFilter(int fd, - unsigned long long int activateSize, - unsigned long long int maxSize) - { - data=NULL; - setup(fd,activateSize,maxSize); - } - - void setup(int fd, - unsigned long long int activateSize, - unsigned long long int maxSize) - { - this->fd=fd; - this->activateSize=activateSize; - this->maxSize=maxSize; - bufferSize=maxSize-activateSize; - total=0; - } - - ~CircularBufferFilter() - { - flush(); - } - - virtual void write(const char *buffer, int len) - { - total+=len; - - if (totalbufferSize-w) - n=bufferSize-w; - - memcpy(data+w,buffer+r,n); - len-=n; - r+=n; - w+=n; - if (w>=bufferSize) - w=0; - } while(len>0); - } - } - - /** - * normally, this should only be called by the destructor. - * - * remember that the destructor is not called if we are an auto - * object (local variable) and if we call exit() - */ - void flush() - { - if (!data) - return; - - char msg[512]; - - if (total<=maxSize) - systemWrite(fd,data,total-activateSize); - else - { - snprintf(msg,sizeof(msg), - "\n" - "###########################################################\n" - "# A total of %llu bytes were output by the program.\n" - "# This exceeds the hard limit that is enforced.\n" - "# Only the %llu first bytes are saved in this file before\n" - "# this point and only the %llu last bytes are saved after\n" - "# this point. A total of %llu bytes are lost.\n" - "###########################################################\n", - total,activateSize,bufferSize,total-maxSize); - systemWrite(fd,msg,strlen(msg)); - systemWrite(fd,data+w,bufferSize-w); - systemWrite(fd,data,w); - } - } -}; - -// Local Variables: -// mode: C++ -// End: -#endif diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Cores.hh b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Cores.hh deleted file mode 100644 index 6cb934d64..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Cores.hh +++ /dev/null @@ -1,203 +0,0 @@ -#ifndef _Cores_hh_ -#define _Cores_hh_ - -#include -#include - -using namespace std; - -/** - * get the list of cores available on the system - * - * if physicalView is true, "list" will contain the id of existing - * cores in increasing order (from 0 to n-1). - * - * if physicalView is false, "list" will contain the id of existing - * cores ordered by the processor to which they belong. For example, - * list could contain 0,2,4,6,1,3,5,7 if the first processor contains - * cores 0,2,4,6 and the second processor contains cores 1,3,5,7. - */ -void getExistingCores(vector &list, bool physicalView) -{ - char fname[128]; - string buffer; - ifstream f; - cpu_set_t cores; - - CPU_ZERO(&cores); - - list.clear(); - - for(unsigned int cpu=0;cpu0) - --len; - - int id=0,mask; - for(int i=len;i>=0;--i,id+=4) - if(buffer[i]!='0' && buffer[i]!=',') - { - if(buffer[i]>='0' && buffer[i]<='9') - mask=buffer[i]-'0'; - else - if(buffer[i]>='a' && buffer[i]<='f') - mask=10+buffer[i]-'a'; - else - if(buffer[i]>='A' && buffer[i]<='F') - mask=10+buffer[i]-'A'; - else - throw runtime_error("invalid character in cpu mask"); - - for(int j=0;j<4;++j) - { - if((mask & 1) && !CPU_ISSET(id+j,&cores)) - { - list.push_back(id+j); - CPU_SET(id+j,&cores); // don't count it twice! - } - - mask>>=1; - } - } - } // if(CPU_ISET(...)) -} - - - -/** - * return the list of cores allocated to this process, ordered by - * physical cpu - */ -void getAllocatedCoresByProcessorOrder(vector &allocatedCores) -{ - char fname[128]; - string buffer; - ifstream f; - cpu_set_t affinityMask; - - allocatedCores.clear(); - - sched_getaffinity(0,sizeof(cpu_set_t),&affinityMask); - - for(unsigned int cpu=0;cpu0) - --len; - - int id=0,mask; - for(int i=len;i>=0;--i,id+=4) - if(buffer[i]!='0' && buffer[i]!=',') - { - if(buffer[i]>='0' && buffer[i]<='9') - mask=buffer[i]-'0'; - else - if(buffer[i]>='a' && buffer[i]<='f') - mask=10+buffer[i]-'a'; - else - if(buffer[i]>='A' && buffer[i]<='F') - mask=10+buffer[i]-'A'; - else - throw runtime_error("invalid character in cpu mask"); - - for(int j=0;j<4;++j) - { - if((mask & 1) && CPU_ISSET(id+j,&affinityMask)) - { - allocatedCores.push_back(id+j); - CPU_CLR(id+j,&affinityMask); // don't count it twice! - } - - mask>>=1; - } - } - } // if(CPU_ISET(...)) -} - -/** - * get the list of cores allocated to this process - */ -void getAllocatedCores(vector &list, pid_t pid=0) -{ - cpu_set_t mask; - list.clear(); - - sched_getaffinity(pid,sizeof(cpu_set_t),&mask); - - for(unsigned int i=0;i &list) -{ - size_t end; - - for(size_t beg=0;beg &cores) -{ - cpu_set_t mask; - - CPU_ZERO(&mask); - for(size_t i=0;i. - */ - - - -#include -#include -#include - -#include - -using namespace std; - -map list; - -int main() -{ -#include "tmpSyscallList.cc" - - int max=0; - map::iterator it; - - for(it=list.begin();it!=list.end();++it) - if ((*it).first>max) - max=(*it).first; - - ofstream Hfile("SyscallNames.hh"); - - Hfile << "#ifndef _SyscallNames_hh_" << endl - << "#define _SyscallNames_hh_" << endl - << endl; - - Hfile << "const int nbSyscallNames=" << max+1 << ";" << endl; - - Hfile << endl; - - Hfile << "const char *getSyscallName(int n);" << endl; - - Hfile << endl; - - Hfile << "#endif" << endl; - - Hfile.close(); - - ofstream CCfile("SyscallNames.cc"); - - CCfile << "#include \"SyscallNames.hh\"\n\n"; - - CCfile << "const char *syscallNames[nbSyscallNames]={\n"; - - for(int i=0;i<=max;++i) - { - string name; - it=list.find(i); - if (it==list.end()) - name="???"; - else - name=(*it).second; - - CCfile << "\t\"" << name << "\""; - if (i!=max) - CCfile << ","; - - CCfile << "\n"; - } - - CCfile << "};\n\n"; - - CCfile << "const char *getSyscallName(int n)\n" - << "{\n" - << " if (n>0 && n<=nbSyscallNames)\n" - << " return syscallNames[n];\n" - << " else\n" - << " return \"???\";\n" - << "}\n"; - - CCfile.close(); -} diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/LICENSE-GPL-3.0.txt b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/LICENSE-GPL-3.0.txt deleted file mode 100644 index 94a9ed024..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/LICENSE-GPL-3.0.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Makefile b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Makefile deleted file mode 100644 index 9dadd8b5d..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Makefile +++ /dev/null @@ -1,69 +0,0 @@ -VERSION=3.3.4 -#SVNVERSION=`svnversion .` -SVNVERSION=$(word 2,$$Rev: 1649 $$) -#DEBUG=-g -DEBUG=-O3 - -STATIC= -#STATIC=-static - -WSIZE:=$(shell if [ `uname -m` = 'x86_64' ] ; then echo 64; else echo 32; fi ) -SYSCALLINCFILE:=$(shell for f in /usr/include/asm/unistd_$(WSIZE).h /usr/include/`uname -m`-linux-gnu/asm/unistd_$(WSIZE).h /usr/include/i386-linux-gnu/asm/unistd_$(WSIZE).h; do if [ -e $$f ] ; then echo $$f ; break ; fi ; done ) - -CFLAGS=-Dtmpdebug -Wall -DVERSION=\"$(VERSION)\" -DSVNVERSION=\"$(SVNVERSION)\" -DWSIZE=$(WSIZE) - -SRC=runsolver.cc SignalNames.cc SyscallNames.cc -OBJ=$(SRC:.cc=.o) - -all:runsolver - -install: runsolver - cp runsolver $(INSTROOT)/usr/bin - -include $(SRC:.cc=.d) - -.cc.o: - g++ $(CFLAGS) $(DEBUG) -c $*.cc - -runsolver: $(OBJ) - g++ $(STATIC) $(DEBUG) -o $@ $^ -lpthread - -testlimit: testlimit.cc - g++ -o testlimit testlimit.cc - -testthread: testthread.cc - g++ -o testthread testthread.cc -lpthread - -SyscallNames.cc SyscallNames.hh: CreateSyscallsNames.cc $(SYSCALLINCFILE) - grep '#define[[:space:]]*__NR' $(SYSCALLINCFILE) | grep -v '^/' | awk '{print $$2}' | sed -e 's/^__NR_//' | awk '{printf "list[__NR_%s]=\"%s\";\n",$$1,$$1}' > tmpSyscallList.cc - g++ -o CreateSyscallsNames CreateSyscallsNames.cc - ./CreateSyscallsNames - rm tmpSyscallList.cc CreateSyscallsNames - -.PHONY: clean realclean archive - -tar: /tmp/runsolver-$(VERSION).tar.bz2 -archive: /tmp/runsolver-$(VERSION).tar.bz2 - -/tmp/runsolver-$(VERSION).tar.bz2: realclean $(SRC) Makefile - sed -i -e 's/^Version:.*/Version:\t'$(VERSION)'/' runsolver.spec - tar cvjf /tmp/runsolver-$(VERSION).tar.bz2 -C ../.. runsolver/src --exclude .svn - -rpm: /tmp/runsolver-$(VERSION).tar.bz2 - rpmbuild -tb /tmp/runsolver-$(VERSION).tar.bz2 - -srpm: /tmp/runsolver-$(VERSION).tar.bz2 - rpmbuild -ts /tmp/runsolver-$(VERSION).tar.bz2 - -clean: - rm -f runsolver $(OBJ) *.class testlimit testtimestamper vlineSplitter testProcessTree runtestlimit testthread - -realclean: clean - rm -f *.d *~ SyscallNames.* - - -%.d: %.cc - $(SHELL) -ec '$(CC) -MM $(CFLAGS) $< \ - | sed -e '\''s/$*\.o[ :]*/$@ &/g'\'' > $@' - - diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Makefile.back b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Makefile.back deleted file mode 100644 index df47d1574..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Makefile.back +++ /dev/null @@ -1,69 +0,0 @@ -VERSION=3.3.4 -#SVNVERSION=`svnversion .` -SVNVERSION=$(word 2,$$Rev: 1649 $$) -#DEBUG=-g -DEBUG=-O3 - -STATIC= -#STATIC=-static - -WSIZE:=$(shell if [ `uname -m` = 'x86_64' ] ; then echo 64; else echo 32; fi ) -SYSCALLINCFILE:=$(shell for f in /usr/include/asm/unistd_$(WSIZE).h /usr/include/`uname -m`-linux-gnu/asm/unistd_$(WSIZE).h ; do if [ -e $$f ] ; then echo $$f ; break ; fi ; done ) - -CFLAGS=-Dtmpdebug -Wall -DVERSION=\"$(VERSION)\" -DSVNVERSION=\"$(SVNVERSION)\" -DWSIZE=$(WSIZE) - -SRC=runsolver.cc SignalNames.cc SyscallNames.cc -OBJ=$(SRC:.cc=.o) - -all:runsolver - -install: runsolver - cp runsolver $(INSTROOT)/usr/bin - -include $(SRC:.cc=.d) - -.cc.o: - g++ $(CFLAGS) $(DEBUG) -c $*.cc - -runsolver: $(OBJ) - g++ $(STATIC) $(DEBUG) -o $@ $^ -lpthread - -testlimit: testlimit.cc - g++ -o testlimit testlimit.cc - -testthread: testthread.cc - g++ -o testthread testthread.cc -lpthread - -SyscallNames.cc SyscallNames.hh: CreateSyscallsNames.cc $(SYSCALLINCFILE) - grep '#define[[:space:]]*__NR' $(SYSCALLINCFILE) | grep -v '^/' | awk '{print $$2}' | sed -e 's/^__NR_//' | awk '{printf "list[__NR_%s]=\"%s\";\n",$$1,$$1}' > tmpSyscallList.cc - g++ -o CreateSyscallsNames CreateSyscallsNames.cc - ./CreateSyscallsNames - rm tmpSyscallList.cc CreateSyscallsNames - -.PHONY: clean realclean archive - -tar: /tmp/runsolver-$(VERSION).tar.bz2 -archive: /tmp/runsolver-$(VERSION).tar.bz2 - -/tmp/runsolver-$(VERSION).tar.bz2: realclean $(SRC) Makefile - sed -i -e 's/^Version:.*/Version:\t'$(VERSION)'/' runsolver.spec - tar cvjf /tmp/runsolver-$(VERSION).tar.bz2 -C ../.. runsolver/src --exclude .svn - -rpm: /tmp/runsolver-$(VERSION).tar.bz2 - rpmbuild -tb /tmp/runsolver-$(VERSION).tar.bz2 - -srpm: /tmp/runsolver-$(VERSION).tar.bz2 - rpmbuild -ts /tmp/runsolver-$(VERSION).tar.bz2 - -clean: - rm -f runsolver $(OBJ) *.class testlimit testtimestamper vlineSplitter testProcessTree runtestlimit testthread - -realclean: clean - rm -f *.d *~ SyscallNames.* - - -%.d: %.cc - $(SHELL) -ec '$(CC) -MM $(CFLAGS) $< \ - | sed -e '\''s/$*\.o[ :]*/$@ &/g'\'' > $@' - - diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Observer.hh b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Observer.hh deleted file mode 100644 index cce1bca40..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/Observer.hh +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2010 Olivier ROUSSEL - * - * This file is part of runsolver. - * - * runsolver is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * runsolver is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with runsolver. If not, see . - */ - - - -#ifndef _Observer_hh_ -#define _Observer_hh_ -#endif - -/** - * a class to watch file descriptors and wait for data to become - * available on one of them - * - */ -class Observer -{ -private: - vector desc; - fd_set readable; -public: - /** - * add a file descriptor to watch - */ - void add(int fd) - { - desc.push_back(fd); - } - - /** - * remove a file descriptor from the list of file descriptors to watch - * - * doit être appele avant de fermer la socket - */ - void remove(int fd) - { - vector::iterator i=find(desc.begin(),desc.end(),fd); - if (i!=desc.end()) - desc.erase(i); - } - - /** - * tell if there is some file descriptor left to watch - */ - bool empty() - { - return desc.empty(); - } - - /** - * wait for data to become available on one of the file descriptor - * that is watched - * - * this is a blocking method - */ - void waitForData() - { - int max=0; - - FD_ZERO(&readable); - - for (int i=0;i. - */ - - - -#ifndef _ProcessData_hh_ -#define _ProcessData_hh_ - -#include -using namespace std; - -// see man proc (/proc/[number]/stat) -struct statData -{ - int pid; - char comm[64]; // ??? - char state; - int ppid; - int pgrp; - int session; - int tty_nr; - int tpgid; - unsigned long int flags; - unsigned long int minflt; - unsigned long int cminflt; - unsigned long int majflt; - unsigned long int cmajflt; - unsigned long int utime; - unsigned long int stime; - long int cutime; - long int cstime; - long int priority; - long int nice; - long int zero; - long int itrealvalue; - unsigned long int starttime; - unsigned long int vsize; - long int rss; - unsigned long int rlim; - unsigned long int startcode; - unsigned long int endcode; - unsigned long int startstack; - unsigned long int kstkesp; - unsigned long int kstkeip; - unsigned long int signal; - unsigned long int blocked; - unsigned long int sigignore; - unsigned long int sigcatch; - unsigned long int wchan; - unsigned long int nswap; - unsigned long int cnswap; - int exit_signal; - int processor; - unsigned long int rtprio; - unsigned long int sched; - - // extra - bool valid; -}; - -/** - * The information we keep on a process - */ -class ProcessData -{ -private: - bool valid; // did we collect meaningful data? - - static const unsigned long int clockTicksPerSecond; - - uid_t uid; - gid_t gid; - - pid_t pid,tid,ppid,pgrp; - unsigned long int utime,stime,cutime,cstime; - unsigned long int starttime; - unsigned long int vsize; - - vector children; - - char statLine[1024]; // ??? - char statmLine[1024]; // ??? - - vector allocatedCores; - - bool selected; // a flag to select/unselect processes - - inline void init() - { - valid=false; - pid=-1; - tid=-1; - ppid=-1; - utime=stime=cutime=cstime=0; - vsize=0; - } - -public: - ProcessData() {init();} - - ProcessData(pid_t pid, pid_t tid=0) - { - init(); - read(pid,tid); - } - - ProcessData(const ProcessData &pd) - { - uid=pd.uid; - gid=pd.gid; - - pid=pd.pid; - tid=pd.tid; - ppid=pd.ppid; - utime=pd.utime; - stime=pd.stime; - cutime=pd.cutime; - cstime=pd.cstime; - vsize=pd.vsize; - - for(size_t i=0;ipid=pid; - this->tid=tid; - - - if (tid) - snprintf(fileName,sizeof(fileName),"/proc/%d/task/%d/stat",pid,tid); - else - snprintf(fileName,sizeof(fileName),"/proc/%d/stat",pid); - - if ((file=fopen(fileName,"r"))!=NULL) - { - struct stat info; - - fstat(fileno(file),&info); - - uid=info.st_uid; - gid=info.st_gid; - - if (fgets(statLine,sizeof(statLine),file)==NULL) - { -#ifdef debug - perror("failed to read stat file"); -#endif - - strcpy(statLine,"-- couldn't read stat file --"); - fclose(file); - - return false; - } - - fclose(file); - } - else - { -#ifdef debug - perror("failed to open stat file"); -#endif - - strcpy(statLine,"-- couldn't open stat file --"); - return false; - } - - nbFields=sscanf(statLine, -#if WSIZE==32 - "%*d " - "%*s " - "%*c " - "%d %d %*d %*d %*d " - "%*u %*u %*u %*u %*u " // lu lu lu lu lu - "%Lu %Lu %Lu %Lu " /* utime stime cutime cstime */ - "%*d %*d " // ld ld - "%*d " - "%*d " // ld - "%Lu " /* start_time */ - "%lu ", -#else - "%*d " - "%*s " - "%*c " - "%d %d %*d %*d %*d " - "%*u %*u %*u %*u %*u " // lu lu lu lu lu - "%lu %lu %lu %lu " /* utime stime cutime cstime */ - "%*d %*d " // ld ld - "%*d " - "%*d " // ld - "%lu " /* start_time */ - "%lu ", -#endif - &ppid,&pgrp, - &utime, &stime, &cutime, &cstime, - &starttime, - &vsize - ); - - valid=(nbFields==8); - -#ifdef debug - if(!valid) - cout << "FAILED TO READ EACH FIELD\n"; -#endif - - if (!tid) - { - snprintf(fileName,sizeof(fileName),"/proc/%d/statm",pid); - - if ((file=fopen(fileName,"r"))!=NULL) - { - if (fgets(statmLine,sizeof(statmLine),file)==NULL) - { -#ifdef debug - perror("failed to read statm file"); -#endif - - strcpy(statmLine,"-- couldn't read statm file --"); - fclose(file); - - return false; - } - - fclose(file); - } - else - { -#ifdef debug - perror("failed to open statm file"); -#endif - - strcpy(statmLine,"-- couldn't open statm file --"); - } - } - - getAllocatedCores(); - - return true; - } - - /** - * update data on this process - * - * return false iff the process doesn't exit any more - */ - bool update() - { - return read(pid,tid); - } - - /** - * return the % of CPU used by this process (and its children when - * withChildren is true). The result is between 0 and 1 - */ - float percentageCPU(float uptime, bool withChildren=false) const - { - float cputime=stime+utime; - - if (withChildren) - cputime+=cstime+cutime; - - cputime/=clockTicksPerSecond; - - float wctime=uptime-static_cast(starttime)/clockTicksPerSecond; - - if (wctime==0) - return 0; - else - return cputime/wctime; - } - - - void readProcessData(statData &s) - { - int nbFields; - - memset(&s,0,sizeof(statData)); - - nbFields=sscanf(statLine, -#if WSIZE==32 - "%d " - "%s " - "%c " - "%d %d %d %d %d " - "%lu %lu %lu %lu %lu " - "%Lu %Lu %Lu %Lu " /* utime stime cutime cstime */ - "%ld %ld " - "%d " - "%ld " - "%Lu " /* start_time */ - "%lu " - "%ld " - "%lu %lu %lu %lu %lu %lu " - "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */ - "%lu %*u %*u " // lu lu lu - "%d %d " - "%lu %lu", -#else - "%d " - "%s " - "%c " - "%d %d %d %d %d " - "%lu %lu %lu %lu %lu " - "%lu %lu %lu %lu " /* utime stime cutime cstime */ - "%ld %ld " - "%ld " - "%ld " - "%lu " /* start_time */ - "%lu " - "%ld " - "%lu %lu %lu %lu %lu %lu " - "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */ - "%lu %*u %*u " // lu lu lu - "%d %d " - "%lu %lu", -#endif - &s.pid, - s.comm, - &s.state, - &s.ppid, &s.pgrp, &s.session, &s.tty_nr, &s.tpgid, - &s.flags, &s.minflt, &s.cminflt, - &s.majflt, &s.cmajflt, - &s.utime, &s.stime, &s.cutime, &s.cstime, - &s.priority, &s.nice, - &s.zero, - &s.itrealvalue, - &s.starttime, - &s.vsize, - &s.rss, - &s.rlim, &s.startcode, &s.endcode, - &s.startstack, &s.kstkesp, &s.kstkeip, - /* s.signal, s.blocked, s.sigignore, s.sigcatch, */ /* can't use */ - &s.wchan, - /* &s.nswap, &s.cnswap, */ - /* nswap and cnswap dead for 2.4.xx and up */ - /* -- Linux 2.0.35 ends here -- */ - &s.exit_signal, &s.processor, - /* 2.2.1 ends with "exit_signal" */ - /* -- Linux 2.2.8 to 2.5.17 end here -- */ - &s.rtprio, &s.sched /* both added to 2.5.18 */ - ); - - s.valid=(nbFields==35); - -#ifdef debug - if(!s.valid) - cout << "FAILED TO READ EACH FIELD OF STAT DATA\n"; -#endif - } - - bool isTask() const - { - return tid!=0; - } - - void addChild(pid_t child) - { - children.push_back(child); - } - - int getNbChildren() const - { - return children.size(); - } - - pid_t getPIDChild(int i) const - { - return children[i]; - } - - pid_t getppid() const - { - return ppid; - } - - pid_t getProcessGroupId() const - { - return pgrp; - } - - uid_t getUid() const - { - return uid; - } - - void select() - { - selected=true; - } - - void unselect() - { - selected=false; - } - - bool isSelected() const - { - return selected; - } - - float getOverallCPUTime() const - { - // note that cstime and cutime (child system and user time) are - // only updated by the wait call in the parent (this is to say, - // only once the child has terminated). Therefore, we can't rely - // on these variables to limit the global cpu use of a process - // and its children - - return (stime+(float)utime - +cstime+(float)cutime - )/clockTicksPerSecond; - } - - float getOverallUserTime() const - { - // note that cstime and cutime (child system and user time) are - // only updated by the wait call in the parent (this is to say, - // only once the child has terminated). Therefore, we can't rely - // on these variables to limit the global cpu use of a process - // and its children - - return (utime+(float)cutime)/clockTicksPerSecond; - } - - float getOverallSystemTime() const - { - // note that cstime and cutime (child system and user time) are - // only updated by the wait call in the parent (this is to say, - // only once the child has terminated). Therefore, we can't rely - // on these variables to limit the global cpu use of a process - // and its children - - return (stime+(float)cstime)/clockTicksPerSecond; - } - - /** - * return the current vsize in Kb - */ - long getVSize() const - { - return vsize/1024; - } - - /** - * get the list of cores allocated to this process - */ - void getAllocatedCores() - { - cpu_set_t mask; - allocatedCores.clear(); - - sched_getaffinity(pid,sizeof(cpu_set_t),&mask); - - for(int i=0;i. - */ - - - -#ifndef _ProcessHistory_hh_ -#define _ProcessHistory_hh_ - -#include -#include "ProcessTree.hh" - -using namespace std; - -/** - * maintains a history of process trees - */ -class ProcessHistory -{ -private: - int nbcell; - vector nbitem; - vector history; - -public: - ProcessHistory(int n) - { - nbcell=n; - nbitem.resize(nbcell); - history.resize(2*nbcell); - for(int i=0;i0;++cell) - for(int i=0;i0;++cell) - { - for(int i=0;i=0;--cell) - { - if (nbitem[cell]==0 || - history[2*cell]->getElapsedTime()<=elapsedLimit) - continue; - - history[2*cell]->dumpProcessTree(s); - history[2*cell]->dumpCPUTimeAndVSize(s); - } - - if (nbitem[0]==2) - { - // also dump the most recent - history[1]->dumpProcessTree(s); - history[1]->dumpCPUTimeAndVSize(s); - } - } - -protected: - void drop(ProcessTree *elem) - { - delete elem; - } -}; - -// Local Variables: -// mode: C++ -// End: -#endif diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/ProcessList.hh b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/ProcessList.hh deleted file mode 100644 index 956c76207..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/ProcessList.hh +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2010 Olivier ROUSSEL - * - * This file is part of runsolver. - * - * runsolver is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * runsolver is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with runsolver. If not, see . - */ - - - -#ifndef _ProcessList_hh_ -#define _ProcessList_hh_ - -#include - -using namespace std; - -/** - * a class to store a list of process IDs - * - * ??? should be optimized !!! - */ -class ProcessList -{ -private: - set s; -public: - typedef set::iterator iterator; - - inline void add(pid_t pid) - { - s.insert(pid); - } - - inline void remove(pid_t pid) - { - s.erase(pid); - } - - inline bool contains(pid_t pid) const - { - return s.find(pid)!=s.end(); - } - - iterator begin() const {return s.begin();} - iterator end() const {return s.end();} -}; - - -#endif - -// Local Variables: -// mode: C++ -// End: - diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/ProcessTree.hh b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/ProcessTree.hh deleted file mode 100644 index 05b5b356d..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/ProcessTree.hh +++ /dev/null @@ -1,711 +0,0 @@ -/* - * Copyright (C) 2010 Olivier ROUSSEL - * - * This file is part of runsolver. - * - * runsolver is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * runsolver is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with runsolver. If not, see . - */ - - - -#ifndef _ProcessTree_hh_ -#define _ProcessTree_hh_ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "ProcessData.hh" - -using namespace std; - -class ProcessTree -{ -private: - typedef map ProcMap; - - ProcMap tree; - pid_t currentRootPID; - - char loadavgLine[1024]; // ??? - long memTotal,memFree,swapTotal,swapFree; // data from /proc/meminfo - - float elapsed; // number of seconds elapsed since the start of the program - - float uptime; // up time of the host (in seconds) - - bool treeHasAllProcesses; - - // process groups of the solver - set solverProcessGroups; - - // process group id of runsolver itself - pid_t runsolverGroupId; - -public: - ProcessTree() - { - currentRootPID=0; - treeHasAllProcesses=false; - - runsolverGroupId=getpgrp(); - } - - ProcessTree(const ProcessTree &pt) - { - currentRootPID=pt.currentRootPID; - treeHasAllProcesses=false; - - elapsed=pt.elapsed; - memTotal=pt.memTotal; - memFree=pt.memFree; - swapTotal=pt.swapTotal; - swapFree=pt.swapFree; - - clone(pt,currentRootPID); - strncpy(loadavgLine,pt.loadavgLine,sizeof(pt.loadavgLine)); - } - - ~ProcessTree() - { - clear(); - } - - void clear() - { - for(ProcMap::iterator it=tree.begin();it!=tree.end();++it) - delete (*it).second; - - tree.clear(); - } - - void setDefaultRootPID(pid_t pid) - { - currentRootPID=pid; - } - - - void setElapsedTime(float timeSinceStartOfProgram) - { - elapsed=timeSinceStartOfProgram; - } - - float getElapsedTime() const - { - return elapsed; - } - - /** - * gather informations on all processes (to determine all children - * of the watched process) - */ - void readProcesses() - { - readProcesses(currentRootPID); - } - - /** - * update informations on processes which are a child of the current - * root. - * - * doesn't attempt to identify new children processes - */ - void updateProcessesData() - { - updateProcessesData(currentRootPID); - } - - /** - * return true when the main process has ended - */ - bool rootProcessEnded() - { - ProcMap::iterator it=tree.find(currentRootPID); - - if (it!=tree.end() && (*it).second!=NULL) - return false; // no, still running - -#if 0 - // the main process terminated, but some children may still be - // running. Look for a new root process (with init as parent and - // the process group id given to the solver) - - // get a fresh list of processes - readProcesses(); - - for(ProcMap::iterator it=tree.begin();it!=tree.end();++it) - { - ProcessData *data=(*it).second; - - if (!data) - { - //cout << "Ooops ! No data available on process " << (*it).first << endl; - continue; - } - - if (data->getppid()==1 && data->getProcessGroupId()==currentRootPID) - { - // we have found the new root - currentRootPID=(*it).first; - cout << "New process root " << currentRootPID << endl; - return false; - } - } -#endif - - return true; - } - - /** - * gather informations on all processes (to determine all children - * of the watched process) - * - */ - void readProcesses(pid_t root) - { - DIR *procfs=opendir("/proc"); - struct dirent *dirEntry; - pid_t pid; - - clear(); - solverProcessGroups.clear(); - - currentRootPID=root; - - readGlobalData(); - - if (!procfs) - throw runtime_error("unable to read /proc filesystem"); - - while((dirEntry=readdir(procfs))) - { - // we only care about process ID - if (!isdigit(*dirEntry->d_name)) - continue; - - //cout << "process " << dirEntry->d_name << endl; - - pid=atoi(dirEntry->d_name); - tree[pid]=new ProcessData(pid); - } - - closedir(procfs); - - treeHasAllProcesses=true; - - identifyChildren(); - - readTasksRec(root); - } - - /** - * update informations on processes which are a child of root. - * - * doesn't attempt to identify new children processes - */ - void updateProcessesData(pid_t root) - { - ProcessData *data=tree[root]; - - if (!data) // no data on this process - return; - - if (!data->update()) - { - // this process doesn't exist any more - tree.erase(root); - return; - } - - for(int i=0;igetNbChildren();++i) - { - pid_t childpid=data->getPIDChild(i); - updateProcessesData(childpid); - } - - } - /** - * - * list heavy processes running on the system. must be called right - * after readProcesses. We only consider processes which are run - * by another user. - * - * threshold is the percentage of CPU (between 0 and 1) above which - * a process is considered as heavy - */ - void dumpHeavyProcesses(ostream &s, float threshold) - { - uid_t myUid=getuid(); - - // we need all processes in the tree. Reread if necessary. - if (!treeHasAllProcesses) - readProcesses(); - - cout << "heavy processes:\n"; - - for(ProcMap::iterator it=tree.begin();it!=tree.end();++it) - { - ProcessData *data=(*it).second; - - if (!data) - { - cout << "Ooops ! No data available on process " << (*it).first << endl; - continue; - } - - float pcpu=data->percentageCPU(uptime); - - // is this someone else process which uses a significant - // proportion of the CPU? - if (data->getUid()!=myUid && pcpu>threshold) - { - pid_t pid=(*it).first; - - s << " %CPU=" << static_cast(pcpu*100) - << " pid=" << pid - << " uid=" << data->getUid() - << " cmd="; - - dumpCmdLine(s,pid); - - s << endl; - } - } - } - - float currentCPUTime() - { - float userTime=0,systemTime=0; - currentCPUTimeRec(currentRootPID,userTime,systemTime); - - return userTime+systemTime; - } - - void currentCPUTime(float &userTime, float &systemTime) - { - userTime=0; - systemTime=0; - currentCPUTimeRec(currentRootPID,userTime,systemTime); - } - - long currentVSize() - { - return currentVSizeRec(currentRootPID); - } - - /** - * add the pid of each solver task to "list" - */ - void listProcesses(set &list) - { - listProcessesRec(list,currentRootPID); - } - - void dumpProcessTree(ostream &out) - { - cout << "\n[startup+" << elapsed << " s]" << endl; - - dumpGlobalData(out); - - dumpProcessTreeRec(out,currentRootPID); - } - - void dumpCPUTimeAndVSize(ostream &out) - { - float userTime,systemTime,VSize; - VSize=currentVSize(); - currentCPUTime(userTime,systemTime); - dumpCPUTimeAndVSize(out,userTime+systemTime,VSize); - } - - void dumpCPUTimeAndVSize(ostream &out, - float currentCPUTime, float currentVSize) - { - cout << "Current children cumulated CPU time (s) " - << currentCPUTime << endl; - - cout << "Current children cumulated vsize (KiB) " - << static_cast(currentVSize+0.5) << endl; - } - - /** - * send a signal to the whole process tree without delay - */ - void sendSignalNow(int sig) - { - if(currentRootPID) - sendSignalNowRec(currentRootPID,sig); - } - - /** - * send a signal to the whole process tree without delay - */ - void sendSignalToProcessGroups(int sig) - { - for(set::iterator it=solverProcessGroups.begin(); - it!=solverProcessGroups.end();++it) - kill(-(*it),sig); - } - - void sendSignalBottomUp(int sig) - { - if(currentRootPID) - sendSignalBottomUpRec(currentRootPID,sig); - } - - void sendSignalBottomUp(pid_t pid, int sig) - { - sendSignalBottomUpRec(pid,sig); - } - -protected: - void readGlobalData() - { - FILE *file; - - if ((file=fopen("/proc/loadavg","r"))!=NULL) - { - fgets(loadavgLine,sizeof(loadavgLine),file); - fclose(file); - } - - if ((file=fopen("/proc/meminfo","r"))!=NULL) - { - fscanf(file, -#if WSIZE==32 - "%*s%d%*s" // MemTotal: 1033624 kB - "%*s%d%*s" // MemFree: 13196 kB - "%*s%*d%*s" // Buffers: 8084 kB - "%*s%*d%*s" // Cached: 343436 kB - "%*s%*d%*s" // SwapCached: 0 kB - "%*s%*d%*s" // Active: 803400 kB - "%*s%*d%*s" // Inactive: 154436 kB - "%*s%*d%*s" // HighTotal: 130240 kB - "%*s%*d%*s" // HighFree: 120 kB - "%*s%*d%*s" // LowTotal: 903384 kB - "%*s%*d%*s" // LowFree: 13076 kB - "%*s%d%*s" // SwapTotal: 2048248 kB - "%*s%d%*s" // SwapFree: 2041076 kB -#else - "%*s%ld%*s" // MemTotal: 1033624 kB - "%*s%ld%*s" // MemFree: 13196 kB - "%*s%*d%*s" // Buffers: 8084 kB - "%*s%*d%*s" // Cached: 343436 kB - "%*s%*d%*s" // SwapCached: 0 kB - "%*s%*d%*s" // Active: 803400 kB - "%*s%*d%*s" // Inactive: 154436 kB - "%*s%*d%*s" // HighTotal: 130240 kB - "%*s%*d%*s" // HighFree: 120 kB - "%*s%*d%*s" // LowTotal: 903384 kB - "%*s%*d%*s" // LowFree: 13076 kB - "%*s%ld%*s" // SwapTotal: 2048248 kB - "%*s%ld%*s" // SwapFree: 2041076 kB -#endif - ,&memTotal,&memFree,&swapTotal,&swapFree); - - fclose(file); - } - - if ((file=fopen("/proc/uptime","r"))!=NULL) - { - fscanf(file, - "%g" // uptime - ,&uptime); - - fclose(file); - } - } - - void identifyChildren() - { - // get links from fathers to children - for(ProcMap::iterator it=tree.begin();it!=tree.end();++it) - { - ProcessData *data=(*it).second; - - if (!data) - { - cout << "Ooops ! No data available on process " << (*it).first << endl; - continue; - } - - pid_t parent=data->getppid(); - - if (parent==-1) - continue; // we just have no data on this process - - ProcMap::iterator itParent=tree.find(parent); - if (itParent!=tree.end()) - (*itParent).second->addChild((*it).first); -#ifdef debug - else - if ((*it).first!=1) // init has no father - { - cout << "Ooops! Can't find parent pid " << parent - << " of child pid " << (*it).first << endl; - dumpProcessTree(cout); - } -#endif - } - } - - - void dumpGlobalData(ostream &out) - { - out << "/proc/loadavg: " << loadavgLine; - out << "/proc/meminfo: memFree=" << memFree << "/" << memTotal - << " swapFree=" << swapFree << "/" << swapTotal << endl; - } - - void currentCPUTimeRec(pid_t pid, float &userTime, float &systemTime) - { - ProcessData *data=tree[pid]; - - if (!data) // no data on this process - return; - - userTime+=data->getOverallUserTime(); - systemTime+=data->getOverallSystemTime(); - - for(int i=0;igetNbChildren();++i) - { - pid_t childpid=data->getPIDChild(i); - if (tree[childpid] && !tree[childpid]->isTask()) - currentCPUTimeRec(childpid,userTime,systemTime); - } - } - - long currentVSizeRec(pid_t pid) - { - ProcessData *data=tree[pid]; - - if (!data) // no data on this process - return 0; - - long size=data->getVSize(); - - for(int i=0;igetNbChildren();++i) - { - pid_t childpid=data->getPIDChild(i); - if (tree[childpid] && !tree[childpid]->isTask()) - size+=currentVSizeRec(childpid); - } - - return size; - } - - void sendSignalNowRec(pid_t pid, int sig) - { - ProcessData *data=tree[pid]; - - if (!data) // no data on this process - return; - - if (data->getNbChildren()!=0) - { - for(int i=0;igetNbChildren();++i) - { - pid_t childpid=data->getPIDChild(i); - if (tree[childpid] && !tree[childpid]->isTask()) - sendSignalNowRec(childpid,sig); - } - } - - kill(pid,sig); - } - - void sendSignalBottomUpRec(pid_t pid, int sig) - { - ProcessData *data=tree[pid]; - - if (!data) // no data on this process - return; - - if (data->getNbChildren()!=0) - { - for(int i=0;igetNbChildren();++i) - { - pid_t childpid=data->getPIDChild(i); - if (tree[childpid] && !tree[childpid]->isTask()) - sendSignalBottomUpRec(childpid,sig); - } - - // give some time to the father to wait for its children - struct timespec delay={0,020000000}; // 20 ms - - // use a loop in case of an interrupt - while(nanosleep(&delay,&delay)==-1 && errno==EINTR); - } - - kill(pid,sig); - } - - void readTasksRec(pid_t pid) - { - ProcessData *data=tree[pid]; - - if (!data) // no data on this process - return; - - pid_t groupId=data->getProcessGroupId(); - - if(groupId!=runsolverGroupId) - solverProcessGroups.insert(groupId); - - readProcessTasks(pid); - - int nb=data->getNbChildren(); - for(int i=0;igetPIDChild(i); - if (tree[childpid] && !tree[childpid]->isTask()) - readTasksRec(childpid); - } - } - - void readProcessTasks(pid_t pid) - { - char processdir[64]; // ??? - DIR *procfs; - struct dirent *dirEntry; - pid_t tid; - ProcessData *data; - - data=tree[pid]; - - if (!data) - return; - - snprintf(processdir,sizeof(processdir),"/proc/%d/task",pid); - - procfs=opendir(processdir); - if (!procfs) - { - if (errno==ENOENT) - { - // process "pid" is probably gone. Don't make a fuss about it - return; - } - - cout << "!!! unable to read " << processdir << " filesystem (" - << strerror(errno) << ") !!!" << endl; - return; - } - - while((dirEntry=readdir(procfs))) - { - // we only care about process ID - if (!isdigit(*dirEntry->d_name)) - continue; - - tid=atoi(dirEntry->d_name); - if (tid==pid) - continue; - - //cout << "task " << dirEntry->d_name - // << " (pid=" << pid << ")" << endl; - - tree[tid]=new ProcessData(pid,tid); - - // add a link from the father to the task - data->addChild(tid); - } - - closedir(procfs); - } - - void listProcessesRec(set &list,pid_t pid) - { - ProcessData *data=tree[pid]; - - if (!data) - return; - - list.insert(pid); - - for(int i=0;igetNbChildren();++i) - listProcessesRec(list,data->getPIDChild(i)); - } - - void dumpProcessTreeRec(ostream &out,pid_t pid) - { - ProcessData *data=tree[pid]; - - if (!data) - return; - - out << *data; - for(int i=0;igetNbChildren();++i) - dumpProcessTreeRec(out,data->getPIDChild(i)); - } - - void clone(const ProcessTree &pt, pid_t pid) - { - treeHasAllProcesses=false; // we only copy the solver processes - - ProcMap::const_iterator it=pt.tree.find(pid); - if (it==pt.tree.end()) - return; - - ProcessData *data=(*it).second; - - if (!data) - return; - - tree[pid]=new ProcessData(*data); - - for(int i=0;igetNbChildren();++i) - clone(pt,data->getPIDChild(i)); - } - - void dumpCmdLine(ostream &s, pid_t pid) - { - char buffer[128]; - char fileName[64]; // ??? - int fd; - - snprintf(fileName,sizeof(fileName),"/proc/%d/cmdline",pid); - - fd=open(fileName,O_RDONLY); - - if(fd>0) - { - unsigned int size=0,r; - - while(size0) - size+=r; - - for(unsigned int i=0;i. - */ - - - -#include "SignalNames.hh" - -const char *signalNames[]={ - "???", - "SIGHUP", - "SIGINT", - "SIGQUIT", - "SIGILL", - "SIGTRAP", - "SIGABRT", - "SIGBUS", - "SIGFPE", - "SIGKILL", - "SIGUSR1", - "SIGSEGV", - "SIGUSR2", - "SIGPIPE", - "SIGALRM", - "SIGTERM", - "SIGSTKFLT", - "SIGCHLD", - "SIGCONT", - "SIGSTOP", - "SIGTSTP", - "SIGTTIN", - "SIGTTOU", - "SIGURG", - "SIGXCPU", - "SIGXFSZ", - "SIGVTALRM", - "SIGPROF", - "SIGWINCH", - "SIGIO", - "SIGPWR", - "SIGSYS"}; - - -const char *getSignalName(int sig) -{ - if (sig>0 && sig<=static_cast(sizeof(signalNames)/sizeof(char *))) - return signalNames[sig]; - else - return "???"; -} diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SignalNames.d b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SignalNames.d deleted file mode 100644 index 789fb4005..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SignalNames.d +++ /dev/null @@ -1 +0,0 @@ -SignalNames.d SignalNames.o: SignalNames.cc SignalNames.hh diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SignalNames.hh b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SignalNames.hh deleted file mode 100644 index f2a11167b..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SignalNames.hh +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2010 Olivier ROUSSEL - * - * This file is part of runsolver. - * - * runsolver is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * runsolver is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with runsolver. If not, see . - */ - - - -#ifndef _SignalNames_hh_ -#define _SignalNames_hh_ - -const char *getSignalName(int sig); - -#endif diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SignalNames.o b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SignalNames.o deleted file mode 100644 index 5531670dd57f3b908c2f37152e786a1d7986b3ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2856 zcmd^mQW`~)1l*lAo0s%oS zAg+prA}U0Gu#v}{G$xLU4)Hv-_hh$kC6q4hrJuF_fu0XteKE1^WRCH_{)tAT!q8G_ zD-S`-nhx?vW7DP)=L70r^9XXjara(`aR3fkE zl0XVtHlBt=%fWBHphGZj8bYMv@hjneYj(M6FNY(_h!PnbUUroFP>m=7mJErUbThdq_>N9Q7E(Z zy3{ogFh*5Wp_FQsO1Ub1)tX&aQdw)TZWl{;aa?(R`p#0ZQudbp*E}o-!o51kCWxVK z`Zmcz%-1V*D`Oyc+FUqio1s;B*D8MPH9-`?eg6fwBj~#lSljmgb>d zZXOnNcA%Mo3JUyKPZ!+t9?v@I9XKm_w|AYiA;!X7wYZp-&r)3}mBjRvdTFj~H|T<> zHylEAVJ$c&hcSn60&`$~Uk&BFoW7JeokdtbG~#$11)V$MJ+4iJF%mzCx}WhgsOesi ze*pOi^TYcBE#~|-)I{t{sQ%D#$Tb{Wc&^4EymxX-e&wA>i3M3?<3>n`@}f;zA*k7^$z1-QGaLr W8|t5o|3JOVIQhcJ;jQz&cYguuF}*+l diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.cc b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.cc deleted file mode 100644 index 36a788ae4..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.cc +++ /dev/null @@ -1,326 +0,0 @@ -#include "SyscallNames.hh" - -const char *syscallNames[nbSyscallNames]={ - "read", - "write", - "open", - "close", - "stat", - "fstat", - "lstat", - "poll", - "lseek", - "mmap", - "mprotect", - "munmap", - "brk", - "rt_sigaction", - "rt_sigprocmask", - "rt_sigreturn", - "ioctl", - "pread64", - "pwrite64", - "readv", - "writev", - "access", - "pipe", - "select", - "sched_yield", - "mremap", - "msync", - "mincore", - "madvise", - "shmget", - "shmat", - "shmctl", - "dup", - "dup2", - "pause", - "nanosleep", - "getitimer", - "alarm", - "setitimer", - "getpid", - "sendfile", - "socket", - "connect", - "accept", - "sendto", - "recvfrom", - "sendmsg", - "recvmsg", - "shutdown", - "bind", - "listen", - "getsockname", - "getpeername", - "socketpair", - "setsockopt", - "getsockopt", - "clone", - "fork", - "vfork", - "execve", - "exit", - "wait4", - "kill", - "uname", - "semget", - "semop", - "semctl", - "shmdt", - "msgget", - "msgsnd", - "msgrcv", - "msgctl", - "fcntl", - "flock", - "fsync", - "fdatasync", - "truncate", - "ftruncate", - "getdents", - "getcwd", - "chdir", - "fchdir", - "rename", - "mkdir", - "rmdir", - "creat", - "link", - "unlink", - "symlink", - "readlink", - "chmod", - "fchmod", - "chown", - "fchown", - "lchown", - "umask", - "gettimeofday", - "getrlimit", - "getrusage", - "sysinfo", - "times", - "ptrace", - "getuid", - "syslog", - "getgid", - "setuid", - "setgid", - "geteuid", - "getegid", - "setpgid", - "getppid", - "getpgrp", - "setsid", - "setreuid", - "setregid", - "getgroups", - "setgroups", - "setresuid", - "getresuid", - "setresgid", - "getresgid", - "getpgid", - "setfsuid", - "setfsgid", - "getsid", - "capget", - "capset", - "rt_sigpending", - "rt_sigtimedwait", - "rt_sigqueueinfo", - "rt_sigsuspend", - "sigaltstack", - "utime", - "mknod", - "uselib", - "personality", - "ustat", - "statfs", - "fstatfs", - "sysfs", - "getpriority", - "setpriority", - "sched_setparam", - "sched_getparam", - "sched_setscheduler", - "sched_getscheduler", - "sched_get_priority_max", - "sched_get_priority_min", - "sched_rr_get_interval", - "mlock", - "munlock", - "mlockall", - "munlockall", - "vhangup", - "modify_ldt", - "pivot_root", - "_sysctl", - "prctl", - "arch_prctl", - "adjtimex", - "setrlimit", - "chroot", - "sync", - "acct", - "settimeofday", - "mount", - "umount2", - "swapon", - "swapoff", - "reboot", - "sethostname", - "setdomainname", - "iopl", - "ioperm", - "create_module", - "init_module", - "delete_module", - "get_kernel_syms", - "query_module", - "quotactl", - "nfsservctl", - "getpmsg", - "putpmsg", - "afs_syscall", - "tuxcall", - "security", - "gettid", - "readahead", - "setxattr", - "lsetxattr", - "fsetxattr", - "getxattr", - "lgetxattr", - "fgetxattr", - "listxattr", - "llistxattr", - "flistxattr", - "removexattr", - "lremovexattr", - "fremovexattr", - "tkill", - "time", - "futex", - "sched_setaffinity", - "sched_getaffinity", - "set_thread_area", - "io_setup", - "io_destroy", - "io_getevents", - "io_submit", - "io_cancel", - "get_thread_area", - "lookup_dcookie", - "epoll_create", - "epoll_ctl_old", - "epoll_wait_old", - "remap_file_pages", - "getdents64", - "set_tid_address", - "restart_syscall", - "semtimedop", - "fadvise64", - "timer_create", - "timer_settime", - "timer_gettime", - "timer_getoverrun", - "timer_delete", - "clock_settime", - "clock_gettime", - "clock_getres", - "clock_nanosleep", - "exit_group", - "epoll_wait", - "epoll_ctl", - "tgkill", - "utimes", - "vserver", - "mbind", - "set_mempolicy", - "get_mempolicy", - "mq_open", - "mq_unlink", - "mq_timedsend", - "mq_timedreceive", - "mq_notify", - "mq_getsetattr", - "kexec_load", - "waitid", - "add_key", - "request_key", - "keyctl", - "ioprio_set", - "ioprio_get", - "inotify_init", - "inotify_add_watch", - "inotify_rm_watch", - "migrate_pages", - "openat", - "mkdirat", - "mknodat", - "fchownat", - "futimesat", - "newfstatat", - "unlinkat", - "renameat", - "linkat", - "symlinkat", - "readlinkat", - "fchmodat", - "faccessat", - "pselect6", - "ppoll", - "unshare", - "set_robust_list", - "get_robust_list", - "splice", - "tee", - "sync_file_range", - "vmsplice", - "move_pages", - "utimensat", - "epoll_pwait", - "signalfd", - "timerfd_create", - "eventfd", - "fallocate", - "timerfd_settime", - "timerfd_gettime", - "accept4", - "signalfd4", - "eventfd2", - "epoll_create1", - "dup3", - "pipe2", - "inotify_init1", - "preadv", - "pwritev", - "rt_tgsigqueueinfo", - "perf_event_open", - "recvmmsg", - "fanotify_init", - "fanotify_mark", - "prlimit64", - "name_to_handle_at", - "open_by_handle_at", - "clock_adjtime", - "syncfs", - "sendmmsg", - "setns", - "getcpu", - "process_vm_readv", - "process_vm_writev", - "kcmp", - "finit_module" -}; - -const char *getSyscallName(int n) -{ - if (n>0 && n<=nbSyscallNames) - return syscallNames[n]; - else - return "???"; -} diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.d b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.d deleted file mode 100644 index de4081e69..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.d +++ /dev/null @@ -1 +0,0 @@ -SyscallNames.d SyscallNames.o: SyscallNames.cc SyscallNames.hh diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.hh b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.hh deleted file mode 100644 index 126d465a4..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.hh +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _SyscallNames_hh_ -#define _SyscallNames_hh_ - -const int nbSyscallNames=314; - -const char *getSyscallName(int n); - -#endif diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.o b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallNames.o deleted file mode 100644 index 9dac1d0c4f02a6a155a24b8ec2519678f01f79e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14688 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%0AC~i0q}L=Q{WrKo8X(oJK$Ty?*-o` zz6icUd-{ezEq8#}(jv*ZuN C=NO>? diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallsTracer.hh b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallsTracer.hh deleted file mode 100644 index 5bf84222f..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/SyscallsTracer.hh +++ /dev/null @@ -1,1439 +0,0 @@ -/* - * Copyright (C) 2010 Olivier ROUSSEL - * - * This file is part of runsolver. - * - * runsolver is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * runsolver is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with runsolver. If not, see . - */ - - - -#ifndef _SyscallsTracer_hh_ -#define _SyscallsTracer_hh_ - -/* TODO ??? - * check alignment when using PTRACE_PEEKDATA - * - * check return value of ptrace - * - * investigate the syscall number -1 we get after a sigreturn - */ - - -#define USE_CLONE_PTRACE - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include "ProcessList.hh" -#include "SyscallNames.hh" - -using namespace std; - - -#ifndef __i386__ -#error Sorry, this code is i386 specific -#endif - -#ifndef __linux__ -#error Sorry, this code is Linux specific -#endif - - -/** - * a callback - */ -class SyscallCallback -{ -public: - virtual void operator() ()=0; -}; - -/** - * a class which represent an action to perform when a system call is - * intercepted - */ -class SyscallAction -{ -public: - typedef - struct user_regs_struct Registers; - - /** - * list of system calls that this action should be registered to - * - * must return NULL if we don't want to define a list of syscalls to - * which this action should be registered or otherwise a static array of - * system calls numbers terminated by -1 - */ - virtual const int *syscallsToRegisterTo() - { - return NULL; - } - - virtual void action(int syscall, bool entering, - pid_t pid, Registers ®s)=0; - - /** - * helper functions - */ - - // On the i386 architecture, the system call number is put in - // the register %eax. The arguments to this system call are - // put into registers %ebx, %ecx, %edx, %esi and %edi, in that - // order. - - template inline T &parm1(Registers ®s) - { - return reinterpret_cast(regs.ebx); - } - - template inline T &parm2(Registers ®s) - { - return reinterpret_cast(regs.ecx); - } - - template inline T &parm3(Registers ®s) - { - return reinterpret_cast(regs.edx); - } - - template inline T &parm4(Registers ®s) - { - return reinterpret_cast(regs.esi); - } - - template inline T &parm5(Registers ®s) - { - return reinterpret_cast(regs.edi); - } - - template inline T returnValue(Registers ®s) - { - return static_cast(regs.eax); - } - - /** - * import a string from a process - * - * maxlen==0 means no limit - */ - void importString(string &s, pid_t pid, void *addr, int maxlen=0) - { - char tab[5]={0,0,0,0,0}; - long &dst=*(long *)tab; - - char *p=static_cast(addr); - - s.clear(); - - do - { - dst=ptrace(PTRACE_PEEKDATA,pid,p,NULL); - s+=tab; - p+=sizeof(long); - } - while(strlen(tab)==4 && (maxlen==0 || s.length() actionList; - - // list of the processes which are currently calling a system call - // used to determine whether we are entering or exiting the system call - ProcessList procList; - - struct ExecveInfo - { - long from; // from where the call was made - int n; // number of spurious execve calls - - ExecveInfo() - { - from=0; - n=0; - } - }; - - // special data for execve - map execveData; - -public: - /** - * append an action to this action list - */ - void addAction(SyscallAction *action) - { - actionList.push_back(action); - } - - /** - * run all actions registered for a given system call - */ - void run(int syscall, pid_t pid, struct user_regs_struct ®s) - { - bool entering=!procList.contains(pid); - /* - * ??? if it succeeds execve does not return, we have to find a - * way to handle this correctly - */ - if (syscall==SYS_execve) - { - // this is a special case !! - if (entering) - { - execveData[pid].from=regs.eip; // remember from where tjis is called - } - else - { - if (execveData[pid].from!=regs.eip) - { - // expect 2spurious calls, ignore them - // ??? is this always the case ? - - if ((++execveData[pid].n)==2) - execveData.erase(pid); - - return; - } - } - - } - - if (entering) - procList.add(pid); - - for(int i=0;iaction(syscall,entering,pid,regs); - - if (!entering) - procList.remove(pid); - } -}; - -/** - * class which let us intercept system calls - */ -class SyscallsTracer -{ -private: - struct user_regs_struct regs; - - // actions to take when a system call is intercepted - SyscallActionList *syscallActionLists[nbSyscallNames]; - - int nbsyscalls; //??? -public: - SyscallsTracer() - { - // no action registered yet - for(int i=0;inbSyscallNames) - throw runtime_error("invalid system call number"); - - if (syscallActionLists[syscallNumber]==NULL) - { - syscallActionLists[syscallNumber]=new SyscallActionList; - } - - syscallActionLists[syscallNumber]->addAction(action); - } - - - /** - * register an action to the list of system calls defined by the action - */ - void registerAction(SyscallAction *action) - { - const int *list=action->syscallsToRegisterTo(); - - if (list==NULL) - throw runtime_error("no list of system calls defined inside the action"); - - int i; - for(i=0;list[i]>=0;++i) - registerAction(list[i],action); - - if(i==0) - throw runtime_error("list of system calls defined inside the action is empty"); - } - - void childTraceKernelCalls() - { - ptrace(PTRACE_TRACEME,0,NULL,NULL); - } - - void parentTraceKernelCalls(pid_t childpid) - { - int status,result; - - result=waitpid(childpid,&status,0); - - if (result!=childpid) - throw runtime_error("wait for traced child failed"); - - if (WIFEXITED(status)) - { - cout << "\n\nChild to trace (pid=" << childpid << ") exited with status: " - << WEXITSTATUS(status) - << " before we could trace it" << endl; - exit(2); - } - else - if (WIFSIGNALED(status)) - { - int sig=WTERMSIG(status); - - cout << "\n\nChild to trace (pid=" << childpid - << ") ended because it received signal " - << sig << " (" << getSignalName(sig) << ")" - << " before we could trace it" << endl; - exit(2); - } - - if (!WIFSTOPPED(status)) - throw runtime_error("traced child is not stopped"); - -#ifdef debug - cout << "traced child is first stopped by signal " - << getSignalName(WSTOPSIG(status)) << endl; -#endif - - ptrace(PTRACE_SYSCALL,childpid,NULL,NULL); - } - - /** - * trace the system calls of childpid until it exits - * - * has almost the same parameters as wait4() - * - */ - void watchKernelCalls(pid_t childpid, - int &childstatus, - struct rusage &childrusage) - { - long err; - - while(true) - { - int pid,retry; - - /* - The wait4() system call below is particularly subject to be - interrupted by a signal (SIGALRM dor example). The best way to - avoid it is to use the SA_RESTART flag for sigaction() or the - siginterrupt() call so that the system call will be - automatically (and transparently) restarted when interrupted. - - However, we cannot be sure that this will be used so we use a - loop to relaunch wait4 when it's interrupted. - */ - - retry=0; - do - { - // wait for SIGTRAP from some of our children. Note that we - // MUST use the __WALL option to get informations from threads - pid=wait4(-1,&childstatus,__WALL,&childrusage); - -#ifdef debug - if (pid==-1 && errno==EINTR) - cout << "WARNING: the internal wait4() system call was interrupted, " - << "retrying. Better use sigaction() with " - << "SA_NOCLDSTOP|SA_RESTART flags;" << endl; -#endif - } while(pid==-1 && errno==EINTR - && ++retry<=3); // accept up to 3 interrupts - - if (pid==-1) - { - if (errno==ECHILD) - { - // no child any more, our job is finished ! -#ifdef tmpdebug - cout << "All traced children have exited ! Game is over." << endl; - - cout << "Intercepted " << nbsyscalls/2 << " system calls" << endl; // ??? -#endif - return; - } - else - throw runtime_error(string("wait4 on traced child failed") - +strerror(errno)); - } - - if (WIFSTOPPED(childstatus)) - { - // was it stopped by a system call ? - if (WSTOPSIG(childstatus)==SIGTRAP) - { - err=ptrace(PTRACE_GETREGS,pid,NULL,®s); - if (err<0) - { - perror("ptrace(PTRACE_GETREGS,...) failed"); - continue; - } - - nbsyscalls++; //??? - -#ifdef debug - cout << "[pid=" << pid << "] syscall " - << getSyscallName(regs.orig_eax) - << " (orig_eax=" <=nbSyscallNames) - { - if (regs.orig_eax!=-1) - { - cout << "Ooops ! Got an invalid system call number !" - << " (orig_eax=" << regs.orig_eax - << " eax=" << regs.eax << ")" << endl - << "Trying to continue..." << endl; - } - /* - else ... - - we get regs.orig_eax==-1 on the "return" of a sigreturn - we'll assume for now that we don't have to care about - that (after all, strace gets the same value) but this - needs to be investigated ??? - */ - - // restart the process up to the next system call - ptrace(PTRACE_SYSCALL,pid,NULL,NULL); - - continue; - } - - // On the i386 architecture, the system call number is put in - // the register %eax. The arguments to this system call are - // put into registers %ebx, %ecx, %edx, %esi and %edi, in that - // order. - - if (syscallActionLists[regs.orig_eax]) - syscallActionLists[regs.orig_eax]->run(regs.orig_eax,pid,regs); - - // restart the process up to the next system call - ptrace(PTRACE_SYSCALL,pid,NULL,NULL); - } - else - { - // it wasn't the SIGTRAP, child is not stopped by a system - // call. We just have to let the program run until the next - // system call -#ifdef debug - cout << "Traced child got signal " - << getSignalName(WSTOPSIG(childstatus)) - << " just continuing" - << endl; -#endif - - // whatever the signal was, we must restart the process up - // to the next system call and transmit that signal - ptrace(PTRACE_SYSCALL,pid,NULL,WSTOPSIG(childstatus)); - } - } - else - { - if (WIFEXITED(childstatus)) - cout << "One traced child (pid=" << pid << ") exited with status: " - << WEXITSTATUS(childstatus) << endl; - else - if (WIFSIGNALED(childstatus)) - { - int sig=WTERMSIG(childstatus); - - cout << "One traced child (pid=" << pid - << ") ended because it received signal " - << sig << " (" << getSignalName(sig) << ")" << endl; - } - } - } - } -}; - - -class HeapSizeWatcher : public SyscallAction -{ -private: - // system calls that we should register to - static const int listOfSyscalls[]; - - struct ProcInfo - { - pid_t process; // pid of the process which owns the thread (0 for - // a plain process) - int vsize; // virtual size of this process in Kb (read from /proc/pid/stat) - - int baseBrk; // value of brk to compute the increase of the heap - int lastBrk; // last value of brk - long memIncr; // estimated memory increment (bytes) - - ProcInfo() - { - process=0; - - vsize=0; - - baseBrk=0; - lastBrk=0; - memIncr=0; - } - }; - - map data; - - // estimated memory increment (bytes) of all the processes - long long globalMemIncr; - - // cumulated size of all processes currentBrkSize (in Kb) - int cumulatedSize; - - // maximum allowed cumulatedSize (if any). must be zero if inactive - int maximumCumulatedSize; - - // function to be called when cumulatedSize gets over maximumCumulatedSize - void (*overQuotaHandlerFn)(); - - // functional object to be called when cumulatedSize gets over quota - SyscallCallback *overQuotaHandlerObj; - - void init() - { - cumulatedSize=0; - maximumCumulatedSize=0; - globalMemIncr=0; - - overQuotaHandlerFn=NULL; - overQuotaHandlerObj=NULL; - } - - /** - * update the vsize of a process by reading from /proc/pid/stat - * - * pid is the id of the process whose vsize must be read again - * info must be data[pid] - */ - inline void updateProcessVsize(pid_t pid, ProcInfo &info) - { - cumulatedSize-=info.vsize; - info.vsize=readProcessVSIZE(pid); - globalMemIncr-=info.memIncr; - info.memIncr=0; - cumulatedSize+=info.vsize; - info.baseBrk=info.lastBrk; - } - - /** - * check if we have exceeded our memory quota - * - */ - inline void checkOverQuota() - { - if (maximumCumulatedSize) - { - if (cumulatedSize+(globalMemIncr>>10)<=maximumCumulatedSize) - return; // we're clearly below our limit - -#ifdef debug - cout << "HeapSizeWatcher::checkOverQuota() is updating processes vsize" - << endl; -#endif - - // update the vsize of all processes with fresh values - for(map::iterator it=data.begin(); - it!=data.end();++it) - if ((*it).second.memIncr!=0 && (*it).second.process==0) - updateProcessVsize((*it).first,(*it).second); - - // now that data is updated, check the limit - if (cumulatedSize>maximumCumulatedSize) - { -#ifdef debug - cout << "memory is over quota" << endl; -#endif - - if (overQuotaHandlerObj) - (*overQuotaHandlerObj)(); - - if (overQuotaHandlerFn) - overQuotaHandlerFn(); - } - } - } - -public: - HeapSizeWatcher() - { - init(); - } - - HeapSizeWatcher(void (*f)()) - { - init(); - overQuotaHandlerFn=f; - } - - HeapSizeWatcher(SyscallCallback *obj) - { - init(); - overQuotaHandlerObj=obj; - } - - /** - * return the estimated cumulated size of all process (in Kb) - * - */ - int getCumulatedSize() - { - return cumulatedSize+(globalMemIncr>>10); - } - - /** - * max is expressed in Kb - */ - void setMaximumCumulatedSize(int max) - { - maximumCumulatedSize=max; - } - - void setOverQuotaHandler(void (*f)()) - { - overQuotaHandlerFn=f; - } - - void setOverQuotaHandler(SyscallCallback *obj) - { - overQuotaHandlerObj=obj; - } - - virtual const int *syscallsToRegisterTo() - { - return listOfSyscalls; - } - - virtual void action(int syscall, bool entering, pid_t pid, Registers ®s) - { -#ifdef debug - cout << "[pid=" << pid << "] "; - - if (entering) - cout << "entering "; - else - cout << "exiting "; - - cout << getSyscallName(syscall) - << " syscall" << endl; -#endif - - pid_t processid=pid; - - // is this a thread ? - if (data[processid].process) - processid=data[processid].process; - - ProcInfo &info=data[processid]; - - if (!entering) - { - switch(syscall) - { - case SYS_brk: - info.lastBrk=returnValue(regs); - - if (info.baseBrk==0) - { - // we have a process pid and we don't know its initial brk address -#ifdef debug - cout << "brk(0)=" - << hex << showbase << returnValue(regs) - << noshowbase << dec - << endl; -#endif - - updateProcessVsize(processid,info); - } - else - { - info.memIncr+= - info.lastBrk-info.baseBrk; - globalMemIncr+= - info.lastBrk-info.baseBrk; - -#ifdef debug - cout << "brk()=" - << hex << showbase << returnValue(regs) - << noshowbase << dec - <<" -> brkSize=" - << ((returnValue(regs)-info.baseBrk)>>10) - << endl; -#endif - } - - checkOverQuota(); - break; - - case SYS_clone: - if (parm1(regs) & CLONE_VM) - { - // if we have a thread, store the pid of the process which - // owns this thread - pid_t newpid=returnValue(regs); - - if (data[pid].process) - data[newpid].process=data[pid].process; - else - data[newpid].process=pid; - } - break; - - case SYS_exit: - case SYS_exit_group: - if (pid==processid) // if this is a process - { - cumulatedSize-=info.vsize; - globalMemIncr-=info.memIncr; - } - - data.erase(pid); - break; - - case SYS_mmap: - { - long p=parm1(regs); - long len; - long flags; - - len=ptrace(PTRACE_PEEKDATA,pid,p+4,NULL); - flags=ptrace(PTRACE_PEEKDATA,pid,p+12,NULL); - -#ifdef debug - cout << "old_mmap(len=" << len - << endl; -#endif - info.memIncr+=len; - globalMemIncr+=len; - } - checkOverQuota(); - break; - - case SYS_mmap2: - { - size_t len=parm2(regs); - -#ifdef debug - cout << "mmap2(len=" << len - << endl; -#endif - - info.memIncr+=len; - globalMemIncr+=len; - } - checkOverQuota(); - break; - - case SYS_mremap: - { - size_t oldlen=parm2(regs); - size_t newlen=parm3(regs); -#ifdef debug - cout << "mremap(oldlen=" << oldlen - << ",newlen=" << newlen - << endl; -#endif - - info.memIncr+=newlen-oldlen; - globalMemIncr+=newlen-oldlen; - - if (newlen>oldlen) - checkOverQuota(); - } - break; - - case SYS_munmap: - { - size_t len=parm2(regs); -#ifdef debug - cout << "munmap(len=" << len - << endl; -#endif - - // check if munmap is successfull before updating the - // counter for otherwise it would be easy to fool us - if (returnValue(regs)==0) - { - info.memIncr+=len; - globalMemIncr+=len; - } - } - break; - } - } -#ifdef debug - cout << "pid=" << processid - << " estimated vsize=" << info.vsize+(info.memIncr>>10) - << " /proc/stat vsize=" << readProcessVSIZE(processid) - << endl; -#endif - } - -private: - /** - * return the process vsize in Kb - */ - int readProcessVSIZE(int pid) - { - char statFileName[64]; // ??? - FILE *file; - int vsize=0; - - snprintf(statFileName,sizeof(statFileName),"/proc/%d/stat",pid); - - if ((file=fopen(statFileName,"r"))!=NULL) - { - - fscanf(file, - "%*d " - "%*s " - "%*c " - "%*d %*d %*d %*d %*d " - "%*lu %*lu %*lu %*lu %*lu " - "%*Lu %*Lu %*Lu %*Lu " /* utime stime cu- & cstime */ - "%*ld %*ld " - "%*d " - "%*ld " - "%*Lu " /* start_time */ - "%lu ",&vsize - ); - - fclose(file); - - vsize >>=10; - } - - return vsize; - } -}; - -const int HeapSizeWatcher::listOfSyscalls[]= - {SYS_brk, - SYS_mmap,SYS_mmap2,SYS_munmap,SYS_mremap, - SYS_clone, - SYS_exit,SYS_exit_group, - -1}; - -/** - * a class to keep a list of processes created by the traced child - * - * - * - */ -class ProcessWatcher : public SyscallAction -{ -private: - // system calls that we should register to - static const int listOfSyscalls[]; - - // list of processes that we must maintain (to be provided by the - // user of this class) - ProcessList &procList; - -public: - ProcessWatcher(ProcessList &list) : procList(list) {} - - virtual const int *syscallsToRegisterTo() - { - return listOfSyscalls; - } - - virtual void action(int syscall, bool entering, pid_t pid, Registers ®s) - { -#ifdef debug - cout << "[pid=" << pid << "] "; - - if (entering) - cout << "entering "; - else - cout << "exiting "; - - cout << getSyscallName(syscall) << " syscall" << endl; -#endif - - if (!entering && syscall==SYS_clone) - { - // trace the new child - pid_t newpid=returnValue(regs); - - if (parm1(regs) & CLONE_VM) - { - cout << "New thread pid=" << newpid << endl; - } - else - { - // add new pid to process list -- if only it is a process - // and not a thread - procList.add(newpid); - - cout << "New process pid=" << newpid << endl; - } - - if (newpid>0) - { -#ifndef USE_CLONE_PTRACE - ptrace(PTRACE_ATTACH,newpid,NULL,NULL); - - int status,result; - - result=waitpid(newpid,&status,__WALL); - - if (result!=newpid) - { - throw runtime_error(string("wait for traced child after a clone() failed (")+strerror(errno)+")"); - } - - if (!WIFSTOPPED(status)) - throw runtime_error("trace child after a clone() is not stopped"); - - cout << "traced child after a clone() is first stopped by signal " - << getSignalName(WSTOPSIG(status)) << endl; - - ptrace(PTRACE_SYSCALL,newpid,NULL,NULL); -#endif - } - } - else - if (entering) - { - switch(syscall) - { - case SYS_exit: - case SYS_exit_group: - { - // remove pid of the caller from process list - procList.remove(pid); - -#ifdef debug - cout << "unregistering process " << pid << endl; -#endif - } - break; - - case SYS_execve: - { - string exec; - - importString(exec,pid,parm1(regs),200); - // do we need that limit ??? - - cout << "execve syscall for " - << exec - << " executable" - << endl; - } - break; - -#ifdef USE_CLONE_PTRACE - case SYS_clone: - { -#ifdef debug - cout << "forcing CLONE_PTRACE flag" << endl; -#endif - // modify the clone parameters to add the CLONE_PTRACE flag - parm1(regs)|=CLONE_PTRACE; - ptrace(PTRACE_SETREGS,pid,NULL,®s); - } - break; -#endif - } - } - } - -}; - -const int ProcessWatcher::listOfSyscalls[]= - {SYS_clone, - SYS_exit,SYS_exit_group, - SYS_execve, - -1}; - -/** - * a class to watch file accesses - */ -class FileWatcher : public SyscallAction -{ -private: - // system calls that we should register to - static const int listOfSyscalls[]; - -public: - FileWatcher() - { - } - - virtual const int *syscallsToRegisterTo() - { - return listOfSyscalls; - } - - virtual void action(int syscall, bool entering, pid_t pid, Registers ®s) - { -#ifdef debug - cout << "[pid=" << pid << "] "; - - if (entering) - cout << "entering "; - else - cout << "exiting "; - - cout << getSyscallName(syscall) << " syscall: parm1=" - << hex << showbase << parm1(regs) - << dec << endl; -#endif - - if (entering) - { - string filename; - importString(filename,pid,(void *)parm1(regs)); - - cout << "open syscall for file " << filename << endl; - } - else - { - //if (true) - // changeResultValue(regs,EACCES); - } - } -}; - -const int FileWatcher::listOfSyscalls[]={SYS_open,-1}; - - -/** - * a class to watch network activity - */ -class NetworkWatcher : public SyscallAction -{ -private: - // system calls that we should register to - static const int listOfSyscalls[]; - - // number of parameters for each subcall - static const int nbSubcallParms[]; - static const int maxNbParms; - -public: - NetworkWatcher() - { - } - - virtual const int *syscallsToRegisterTo() - { - return listOfSyscalls; - } - - virtual void action(int syscall, bool entering, pid_t pid, Registers ®s) - { -#ifdef debug - cout << "[pid=" << pid << "] "; - - if (entering) - cout << "entering "; - else - cout << "exiting "; - - cout << getSyscallName(syscall) << " syscall: parm1=" - << hex << showbase << parm1(regs) - << dec << endl; -#endif - - // parameters of the subcall - long parms[maxNbParms]; - - int subcall=parm1(regs); - - if (entering) - { - cout << "socket syscall " << flush; - - getparms(parms,pid,subcall,regs); - - switch(subcall) - { - case SYS_SOCKET: - decodeSocket(parms); - break; - case SYS_BIND: - decodeSockSockaddrSocklen("bind",pid,parms); - cout << endl; - break; - case SYS_CONNECT: - decodeSockSockaddrSocklen("connect",pid,parms); - cout << endl; - break; - case SYS_LISTEN: - cout << "listen(" << parms[0] << "," << parms[1] << ")" << endl; - break; - case SYS_ACCEPT: - cout << "accept(" << parms[0] << ",...)" << endl; - break; - case SYS_GETSOCKNAME: - cout << "getsockname(...)" << endl; - break; - case SYS_GETPEERNAME: - cout << "getpeername(...)" << endl; - break; - case SYS_SOCKETPAIR: - cout << "socketpair(...)" << endl; - break; - case SYS_SEND: - cout << "send(...)" << endl; - break; - case SYS_RECV: - cout << "recv(...)" << endl; - break; - case SYS_SENDTO: - cout << "sendto(" << parms[0] << ",buf=...,len=" << parms[2] - << ",flags=" << parms[3] << ","; - if (parms[4]!=0) - decodeSockAddr(pid,parms[4],parms[5]); - else - cout << "NULL"; - cout << ",len=" << parms[5] << ")" << endl; - break; - case SYS_RECVFROM: - cout << "recvfrom(...)" << endl; - break; - case SYS_SHUTDOWN: - cout << "shutdown(...)" << endl; - break; - case SYS_SETSOCKOPT: - cout << "setsockopt(...)" << endl; - break; - case SYS_GETSOCKOPT: - cout << "getsockopt(...)" << endl; - break; - case SYS_SENDMSG: - cout << "sendmsg(...)" << endl; - break; - case SYS_RECVMSG: - cout << "recvmsg(...)" << endl; - break; - default: - cout << "unknown syscall(...)" << endl; - break; - } - } - else - { - switch(subcall) - { - case SYS_SOCKET: - cout << "=" << returnValue(regs) << endl; - break; - } - } - } - -private: - void getparms(long *parms, pid_t pid, int subcall, Registers ®s) - { - long p=parm2(regs); - for(int i=0;iSYS_RECVMSG) -// return -EINVAL; -// -// /* copy_from_user should be SMP safe. */ -// if (copy_from_user(a, args, nargs[call])) -// return -EFAULT; -// -// a0=a[0]; -// a1=a[1]; -// -// switch(call) -// { -// case SYS_SOCKET: -// err = sys_socket(a0,a1,a[2]); -// break; -// case SYS_BIND: -// err = sys_bind(a0,(struct sockaddr __user *)a1, a[2]); -// break; -// case SYS_CONNECT: -// err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]); -// break; -// case SYS_LISTEN: -// err = sys_listen(a0,a1); -// break; -// case SYS_ACCEPT: -// err = sys_accept(a0,(struct sockaddr __user *)a1, (int __user *)a[2]); -// break; -// case SYS_GETSOCKNAME: -// err = sys_getsockname(a0,(struct sockaddr __user *)a1, (int __user *)a[2]); -// break; -// case SYS_GETPEERNAME: -// err = sys_getpeername(a0, (struct sockaddr __user *)a1, (int __user *)a[2]); -// break; -// case SYS_SOCKETPAIR: -// err = sys_socketpair(a0,a1, a[2], (int __user *)a[3]); -// break; -// case SYS_SEND: -// err = sys_send(a0, (void __user *)a1, a[2], a[3]); -// break; -// case SYS_SENDTO: -// err = sys_sendto(a0,(void __user *)a1, a[2], a[3], -// (struct sockaddr __user *)a[4], a[5]); -// break; -// case SYS_RECV: -// err = sys_recv(a0, (void __user *)a1, a[2], a[3]); -// break; -// case SYS_RECVFROM: -// err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3], -// (struct sockaddr __user *)a[4], (int __user *)a[5]); -// break; -// case SYS_SHUTDOWN: -// err = sys_shutdown(a0,a1); -// break; -// case SYS_SETSOCKOPT: -// err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]); -// break; -// case SYS_GETSOCKOPT: -// err = sys_getsockopt(a0, a1, a[2], (char __user *)a[3], (int __user *)a[4]); -// break; -// case SYS_SENDMSG: -// err = sys_sendmsg(a0, (struct msghdr __user *) a1, a[2]); -// break; -// case SYS_RECVMSG: -// err = sys_recvmsg(a0, (struct msghdr __user *) a1, a[2]); -// break; -// default: -// err = -EINVAL; -// break; -// } -// return err; -//} - -#endif - -// Local Variables: -// mode: C++ -// End: - diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/TimeStamper.hh b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/TimeStamper.hh deleted file mode 100644 index a4534d51d..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/TimeStamper.hh +++ /dev/null @@ -1,442 +0,0 @@ -/* - * Copyright (C) 2010 Olivier ROUSSEL - * - * This file is part of runsolver. - * - * runsolver is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * runsolver is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with runsolver. If not, see . - */ - - - -#ifndef _TimeStamper_hh_ -#define _TimeStamper_hh_ - -#include -#include -#include -#include -#include -#include -#include -#include - -// if we use the TimeStamper in a threaded program, we may have to use -// mutexes -#include - -#include "CircularBufferFilter.hh" - -using namespace std; - -/** - * a class that intercepts the data on some watched file descriptors - * and adds a timestamp at the beginning of each line. - */ -class TimeStamper -{ -public: - TimeStamper(bool withCPUtime=true) : withCPUtime(withCPUtime) - { - // initialize select() data - FD_ZERO(&watcher); - max=1; - - // in case the user forgets to call it - resetTimeStamp(); - - lastKnownCPUtime=0; - pthread_mutex_init(&cputimeMutex,NULL); - - addEOFLine=false; - - incompleteLineSent=false; - } - - /** - * specifies if a special 'EOF' line should be added at the end of - * output. This line allows to get the value of the timestamps when - * the process ends. - * - * @parm add: true iff the 'EOF' line must be added - */ - void addEOF(bool add) - { - addEOFLine=add; - } - - /** - * add a file descriptor (inputfd) to the list of timestamped files - * - * @parm inputfd: file descriptor of the file that must be timestamped - * @parm letter: one letter name of the timestamped stream (0 if unused) - * @parm outputfd: file descriptor to use to output the - * timestamped stream (stdout by default) - */ - void watch(int inputfd, char letter=0, int outputfd=STDOUT_FILENO) - { - FD_SET(inputfd,&watcher); - if(inputfd>=max) - max=inputfd+1; - - watched.push_back(info(inputfd,outputfd,letter,true)); - } - - /** - * add a file descriptor (inputfd) to the list of timestamped files - * - * @parm inputfd: file descriptor of the file that must be timestamped - * @parm letter: one letter name of the timestamped stream (0 if unused) - * @parm outputfilter: filter to use to output the timestamped stream - */ - void watch(int inputfd, AbstractFilter *outputfilter, char letter=0) - { - FD_SET(inputfd,&watcher); - if(inputfd>=max) - max=inputfd+1; - - watched.push_back(info(inputfd,outputfilter,letter,true)); - } - - /** - * reset the time stamp - */ - void resetTimeStamp() - { - gettimeofday(&tvStart,NULL); - } - - /** - * obtain the current time stamp - * - * @return the current timestamp (for external use) - */ - struct timeval getTimeStamp() - { - gettimeofday(&tv,NULL); - tv.tv_usec-=tvStart.tv_usec; - tv.tv_sec-=tvStart.tv_sec; - if(tv.tv_usec<0) - { - tv.tv_sec-=1; - tv.tv_usec+=1000000; - } - - return tv; - } - - /** - * loop that waits for data on one of the watched files - * and timestamps each line read from these files - * - * the loop ends when all watched files are closed - * - * should be called from a separate thread of process (in most cases) - */ - void timeStampLines() - { - fd_set tmp; - int result,watchDog=0; - - while (watched.size()) - { - tmp=watcher; - - // wait for data to become available on one of the watched files - result=select(max,&tmp,NULL,NULL,NULL); - - if(result<0) - { - if(++watchDog<10) - continue; - else - { - cout << "Error in TimeStamper::timeStampLines(), select() keeps returning errors, exiting." << endl; - break; - } - } - - watchDog=0; - - for(size_t i=0;i=max) - max=watched[i].inputdescr+1; - } - else - ++i; - } - } - - - if(addEOFLine) - { - const char *lastLine="EOF\n"; - - if(incompleteLineSent) - watched[0].write("\n",strlen("\n")); - - prepareTimeStamp(0); - watched[0].write(tstampbuffer,tstampsize); - watched[0].write(lastLine,strlen(lastLine)); - } - } - - /** - * communicate the current CPU time to the time stamper - * - * must only be called from another thread than the one running - * timeStampLines() !! - */ - void setCPUtimeFromAnotherThread(float cputime) - { - pthread_mutex_lock(&cputimeMutex); - lastKnownCPUtime=cputime; - pthread_mutex_unlock(&cputimeMutex); - } - - protected: - - /** - * get the current time and store the ascii representation of the - * time stamp in tstampbuffer (tstampsize will contain the number of - * characters of the representation) - * - * @parm name=one letter name of the watched file (0 if unused) - */ - void prepareTimeStamp(char name) - { - float cputimeCopy; - - getTimeStamp(); - pthread_mutex_lock(&cputimeMutex); - cputimeCopy=lastKnownCPUtime; - pthread_mutex_unlock(&cputimeMutex); - - // store time stamp in tstampbuffer - if(withCPUtime) - { - // CPU time+Wall Clock time - if(name) - tstampsize=snprintf(tstampbuffer,sizeof(tstampbuffer), -#if WSIZE==32 - "%c%.2f/%d.%02d\t", -#else - "%c%.2f/%ld.%02ld\t", -#endif - name,cputimeCopy,tv.tv_sec,tv.tv_usec/10000); - else - tstampsize=snprintf(tstampbuffer,sizeof(tstampbuffer), -#if WSIZE==32 - "%.2f/%d.%02d\t", -#else - "%.2f/%ld.%02ld\t", -#endif - cputimeCopy, - tv.tv_sec,tv.tv_usec/10000); - } - else - { - // no CPU time - if(name) - tstampsize=snprintf(tstampbuffer,sizeof(tstampbuffer), -#if WSIZE==32 - "%c%d.%02d\t", -#else - "%c%ld.%02ld\t", -#endif - name,tv.tv_sec,tv.tv_usec/10000); - else - tstampsize=snprintf(tstampbuffer,sizeof(tstampbuffer), -#if WSIZE==32 - "%d.%02d\t", -#else - "%ld.%02ld\t", -#endif - tv.tv_sec,tv.tv_usec/10000); - } - - } - - private: - /** - * read data available on watched file with index id (in the watched - * vector) and output the timestamp stream - * - * @return false on EOF - */ - bool readFrom(int id) - { - char buffer[1024]; - - int size=read(watched[id].inputdescr,buffer,sizeof(buffer)); - - // PTY seem to be special (this is a quick fix that must be cleaned ???) - if(size<0) - return false; // ??? - - if(size<0) - throw runtime_error(string("TimeStamper::readFrom(): read failed: ") - +strerror(errno)); - - if(size==0) - return false; // indicate EOF - - // create the time stamp once for all the lines we read - prepareTimeStamp(watched[id].name); - - if(watched[id].EOLBefore) - { - watched[id].write(tstampbuffer,tstampsize); - watched[id].EOLBefore=false; - } - - char *s=buffer; - char *eol; - - // split into lines - while (size>0 && (eol=(char*)memchr(s,'\n',size))!=NULL) - { - // output up to EOL included - watched[id].write(s,eol-s+1); - size-=eol-s+1; - s=eol+1; - - if(size>0) - watched[id].write(tstampbuffer,tstampsize); - else - watched[id].EOLBefore=true; - } - - // output the last incomplete line - if(size>0) - { - watched[id].write(s,size); - incompleteLineSent=true; - } - else - incompleteLineSent=false; - - return true; - } - -private: - bool withCPUtime; // do we display CPU time in the timestamp ? - - float lastKnownCPUtime; // current CPU time (provided by an external source) - pthread_mutex_t cputimeMutex; // a mutex to protect access to cputime - - struct timeval tvStart,tv; // first timestamp and current timestamp - - int max; // max(fd)+1 for select - fd_set watcher; // set of watched file descriptors for use by select - - // buffer that contains the ascii representation of the timestamp - char tstampbuffer[64]; // a buffer to output the time stamps - int tstampsize; // size of the timestamp - - bool incompleteLineSent; // true iff the last line we sent didn't have an EOL - - bool addEOFLine; // true if we must add an 'EOF' line at the end of output - - /** - * internal information kept on each file to be watched - */ - struct info - { - bool filteredOutput; - int inputdescr; // file descriptor to watch - int outputdescr; // file descriptor to use to output the timestamped stream (when filteredOutput is false) - AbstractFilter *outputfilter; // filter to write to (when filteredOutput is true) - char name; // a letter that identifies the watched file - bool EOLBefore; // true iff the first character we read is the start of - // a new line - - info(int inputfd, int outputfd, char letter, bool lineStart) - { - filteredOutput=false; - inputdescr=inputfd; - outputdescr=outputfd; - name=letter; - EOLBefore=lineStart; - } - - info(int inputfd, AbstractFilter *filter, char letter, bool lineStart) - { - filteredOutput=true; - inputdescr=inputfd; - outputfilter=filter; - name=letter; - EOLBefore=lineStart; - } - - void write(const char *buffer, int len) - { - if(filteredOutput) - outputfilter->write(buffer,len); - else - systemWrite(outputdescr,buffer,len); - } - - /** - * handle partial writes and EINTR - */ - void systemWrite(int fd, const char *buf, size_t len) - { - const char *p=buf; - int n; - - do - { - n=::write(fd,p,len); - if(n<0) - { - if(errno==EINTR) - continue; - - perror("write failed: "); - break; - } - - len-=n; - p+=n; - } - while(len); - } - }; - - vector watched; // list of files we watch -}; - -#endif diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/aeatk.c b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/aeatk.c deleted file mode 100644 index ce070358f..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/aeatk.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * aeatk.c - * - * Created on: May 7, 2014 - * Author: sjr - */ - -#include -#include - -#include -#include - - -/** - * This function is called by the timerThread at the end of updating runsolvers internal - * time measure and will send a message to the port on the local machine. - * - * Written by Stephen Ramage - */ -void writeCPUTimeToSocket(double cpuTime) { - - static char* portstr = getenv("AEATK_PORT"); - - static int noMessageDisplay = 0; - - if (portstr == NULL) { - if (noMessageDisplay == 0) { - noMessageDisplay++; - printf( - "[AEATK] No environment variable \"AEATK_PORT\" detected not sending updates\n\n"); - } - return; - } - - //Safely convert port to int - int port = strtol(portstr, NULL, 10); - - if (port < 1024 || port > 65535) { - //Invalid port, probably nothing - - if (noMessageDisplay == 0) { - noMessageDisplay++; - printf( - "[AEATK] Invalid port set in \"AEATK_PORT\" must be in [1024, 65535]\n\n"); - } - return; - - } - - static struct sockaddr_in servaddr; - - static double updateFreqDbl = 1; - static int lastUpdate = -1; - - static int sockfd = -1; - - static char* updateFreq = getenv("AEATK_CPU_TIME_FREQUENCY"); - - if (sockfd == -1) { - sockfd = socket(AF_INET, SOCK_DGRAM, 0); - - bzero(&servaddr, sizeof(servaddr)); - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); - servaddr.sin_port = htons(port); - - } - - if (updateFreq != NULL) { - //Runsolver won't actually call this function faster than every 2 seconds - //But we set it to 1 second anyway - updateFreqDbl = strtod(updateFreq, NULL); - - if (updateFreqDbl < 1) { - updateFreqDbl = 1; - } - } - - if ((time(NULL) - lastUpdate) > updateFreqDbl) { - lastUpdate = time(NULL); - - static char buf[100]; - - int length = sprintf(buf, "%f\n", cpuTime); - - //Not sure if MSG_DONTWAIT flag is appropriate but it didn't seem to matter when I tried a few times - - if (noMessageDisplay >= 2) { - noMessageDisplay++; - printf( - "[AEATK] Sending CPUTime updates to 127.0.0.1 on port %d every %.3f seconds, last time %.3f \n\n", - port, updateFreqDbl, cpuTime); - } - - sendto(sockfd, buf, length, 0, (struct sockaddr*) &servaddr, - sizeof(servaddr)); - return; - } - - return; -} - -void updateCores() { - - char* set_affinity = getenv("AEATK_SET_TASK_AFFINITY"); - - if ((set_affinity == NULL) || strcmp(set_affinity, "1") != 0) { - printf( - "[AEATK] No environment variable \"AEATK_SET_TASK_AFFINITY\" detected, cores will be treated normally\n\n"); - return; - - } - - char* taskstr = getenv("AEATK_CONCURRENT_TASK_ID"); - - if (taskstr == NULL) { - printf( - "[AEATK] No environment variable \"AEATK_CONCURRENT_TASK_ID\" detected, cores are treated normally\n\n"); - return; - - } - - unsigned int taskid = strtoul(taskstr, NULL, 10); - - printf( - "[AEATK] This version of runsolver restricts subprocesses to only one core when \"AEATK_CONCURRENT_TASK_ID\" is set.\n"); - - vector cores2; - - getAllocatedCoresByProcessorOrder(cores2); - - if (taskid >= cores2.size()) { - cout << "[AEATK] taskid: " << taskid - << " is greater than the number of cores we have available cores: " - << cores2.size() << " affinity: "; - - printAllocatedCores(cout, cores2); - - cout << " something is wrong, exiting" << endl; - exit(1); - } - - vector cores; - cores.push_back(cores2[taskid]); - - cpu_set_t mask = affinityMask(cores); - if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) != 0) { - perror("sched_setaffinity failed: "); - cout << "[AEATK] Couldn't set affinity " << endl; - exit(1); - } - - return; -} - - diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/runsolver b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/runsolver deleted file mode 100755 index bca96711f1470ad8839b4be7e8a611fd09c72d4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 124213 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

    CiCg+*dA||a>KCBF^66|8>;%CYa76W^Z0At&g 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 z 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
      `F0VUe!aSK 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 z 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

      FHQau z?PdN2d2j_h#QGiVk1qqSKlh>LPuKXr2|P>wl3*`+H+c9e_CtI7bnzXO7ww-s06zH7 z7g>Hh_0L`4^~j(0dhm2-caq=p9lC#gnqU7D;8QQ&o!W!J8w*zqz0cF@H}yRGGp_ge zk-tnw`u~RcyI1%nX}-Q^fH!_2jW?dtedLoT^Q`*+2sqxaaTI*=_J^9hsrJ7ZeBwhF z4Ly(VsXd<+{L z9?MmmE3M^Px3;)y7SG{M4PRE3HnhyuZBMtR5*B zrYK^6d9`7vI9kXZYByI~<;LpDLam)E*9(&k56bP@TD#t@rSBFB`RL_BVKSdn>E&)^ zVY#-qke@6TbDeIjl`q>zb!C({GMd7jkqLWtG(20#7sqpr)#YWoTnLx*lM@BIQjD&+ z&x}-}OC!Cd@!Ggu8Ci%P8p|E7&U8wk8hzkIWm%~ zcgl;MZjNR`>(JZcYAd%?UtX>^%I#LAdpK8JZLclvog5!y0IHQ1kK|S=$LOYZ1Bzd+ zHFBNf)MG_gI`Qwa?o}2S%X+TdZI<1%$n2NNaYme~bla7`oy&E`IrEi?!c_K+Ddw!PwDQ7g^+>I|cXEnG)*E#Qrqb!c1UjqBUC6M~rEYK< z*kG+zK2kegUa76*_?v4ss~rS3`zOogdegpJe$Fj76^hBjx0DOT;-OlDVXBsE?KUjA zTItpfHQUEWMR%p66fp2c!ISK!FZ6vO`}ymNuJpc#H>pl|dYiSMSgCb7l|!|XdSIIk z=zXWJTAQ>XzoRx}KE1y<>iaWveYp++R~Y${D&Jd?{l&2`URjU#Q^76s-NI<^IPKe6 zzZa~92X5=D$7W=cQRU($eYaW3*^jH>12a3Q#o)tR6g0|cYDOP6=^^dS%3td* ze{GI%e zy^X0IE?1Umka~&*u(n*z7t2Q)&9z2ZvS_7ULu6?zF4xMHmFyL61bNZF>Ui*4v%T0? zRwnBonP9#5h1;i%>%&_qrrua;(yZq&H7D!MPI;lysf{1AnwE~1#>?|8Pc0s};kN#c z)ST<8?<>^VEmE#OkCqCPgO}|iz1(BxCG^dU!gy2#|MASaW<6Lc6n!o2&5ZhO@2y*Q zNRvpP{arcby6`I{W6N$z$2~#_e8KkrUPK@fL%Y^))Fd)uY|an}u<- zht_JN%59TMvPvlA(}%J?z}>QjE$iJW-E6IHaseQPQS2)u_yyfs(RrW>C^vCY{8Tl>NjYC9P%B zNl!_cPehcIS?7q7GVc#iQsz?;C1u`EDQSz_n{|32rKHTaN2*~lsbiYSAgIakfbOA3 z6vvEzm**A`TF_M5O;nc}n(9(_7I9-TxzT{7*}^tApT~M+?~Z4_+pZrve2$kT!;Y~^ zI{nSbQJLb?SO?myB0`xYQZ%-o^=_7E)cfuTdVhx6u(aA;ZPzwel=>FiLgw>clNpq# zvmehGmSXmk*Ln4#Uq#A(x<}i|kTu53WncY4J>zI;jJq|oPQ;kLMsM@I{@1cAd7al0 z2IT(i$JJK{2Bq`NVX%Hp1qtNQq@7yDJ9a;dda?xfUg{}g50H2_NVVIbJS<4jsd+6$JHi?O3(e>eidc+RL>{W3^S7DnGliR;L1U zwMvKU``{6?g^6&r6keb@`wK<%C91kyTfojXd(+|BWnC^7BrKPkOJ$S^UdJk1SzfIj zo-K@pV)iwW^p9bw;ps^$r03D{TAglCDTnV=v3J_nCSVwpyWClgmFG z^axtmQZv%l#;ho`^KQ9o!XElc`*_AXE740D?fu2P(=O6J>tHC$cyktQZ_3Knw`Y$o zMG=F4b$8S)r`eg-*BLT@{7O0M}TowB1#1R$5Xw z%TmrknA~J!tRCSv!ZN0OXK2iWO^-x2ch|XqCAVb~GZa->;XtZjrCwN&UA?kcY0*@D0!4y9zG2A+EA2y_T(z~libcA%QfVBb zkcHLyau-vyUQw$T(0SqNXcL;4z93Ab7$Q6B2(`1lVwTRuGK#)gD%_X)N)9(w>S`l& zZBi?hS;p+VQfVLO37djpfK;=d2mSYauR9a1s$y1aKb8*kRNak2p^!Yf(p*e_SK95$ zaciM}3`?vp(dveV9WW!HO9`%Gn)NS@I39=lB0S7D%D%oyFNZOTCSyTUjjHia-X`I= zE{u)Xzhjz=V`G}D6BC-J_lqV`tLQEUHbL3ia1;$Qqm`d6imPN-I+I0E_CS~O}c8nqUU;%d=# z)nwFKG=^QC{=ku=fA!WxUf-IquA9Jjqu+XKBCoe5@_K&4dVeCXFHYq3?u6BC!s-PFM{mtnCx_tqE)UgpJ9BwOxjOO0c$1SlcJ8?GuQax^7>@)283{MdP>=#&Rd% zxm-uE)W23++(-ItZ{feuZ>!;meFg6xS8yWgUwa;(iGJI+M(wRp`_`zvi?ML;{wXVG z%F3Cta;B`DDJ!SohLAt{0+xS$fFY!m8;?GnrUZow_ zWVLnnuf?@WdkMG4p>hYml=E%oGB4JS)vA~Z%hja{)3$j?%rsnsUBeKW!K-WK)z+ff zCz`hEmkWc}bj5j>`og&0E!t!*+GH+T=Q-+i#Or9ch)0qqC+ztNE5}%G zabhgQ9$*(n_QQQ^p$PifX}0msiBkI|&YVQ0Y8_RufgGxpZ3%^UwGpU4*q2RQH6N-E zYVe-WD*nF?p3&fDRk#6eRu*Z=1olG(b|{%;Jbc878Wiy5vI*Azrq`cFb z*;oe_yGO%MT-f1fXSs;7g34~`Rw?9K3?%&6R1=~#iLmUyu%%V#_>4(;px^uhA}eT3ZHF^k?gaQ&8N z-&~rT*_UzWz|75AFYLQ@|Nd(ZWZk*>)>}#ku9>?dqpX`t^Yho-P@2!UHG9o$X)fdX zEyMt2T)yVI>*gpdE&82JRpFLN)xpd2cjJq?}aee0cnSIwNWGB}~ z!mU-2^A*&f5yFF{pA0+>s+_bq(SR=1k4l`bbPX&S31p7R#CWzJe_|4OfFOl(qUGgI?XH!D@N2D_!^?{7x_4?4#v+w_0lk6w>xQ`FB*}aZ0liJANR5GKR$xPu zr@!57c5}D^8y%37Fa2}NEEI&4*gdtjx!N_mF{+8aVY7>PiYX+YtXsNMRCLM_7g3Q0 zvk)Vr&+B{QaNLIL`YQq$&yr9kSUSpW3dfKbVhBeL1s4l2{`4MjiptYWSBEhKiE_EN zyktg+GEH3|C}VX&A4Un`4mDyThMpnl%U+~?j1BX`jdXdjxl+MN?proSkB17dMx>ud z>NW`l#jnyqkz?cJj!cVDf zQR90f_!PXDOc{yVDMCt1f#fKVNU4mJ9^F8{i4fvY@|gYfgV_^wSQxj7>I#czf|R|q zFp2!3FxNC+7}8+;R9YC~R(b=ODS10C7L_hsmkBg!ea{RaG|)mQ8FiNX5N4S^>=}qP zL8FifG>+fWZt$L zX|>RX(n680Q{c#c!3bWF~TMtn;(WDeZUG8z7F4G$&4P5R_EI}4&O_DyX(G<(!$esCa^e!=ZYhe+I#^;`XYL+D6>ZTT5+t0 zCQ&oeC8{{--n9NMvO>r2P00kB_T?1%Px#`q4GXFI!y9WY zfco4^Z!|!9gZDe#MWd>sA5Jco_m0Kh!W9}s9DAApH|Nk6%{PH0*%=1)pGZ+NheAx_>deymO{=nSKiZ`Z) z?lw~{!g7u6e(8aKr5P3aWS;Qk!^2PCJU045>_M*58rXwVH83t_roO^!NoBz$`mvsO~XC}yjMz0`pxtgtg- zO6FUCi8*r&XI)cFha1?9lNZM|>b~F6hN90+I*uGZH#v@Tpx#l(NpdijbDz#x3IBD zPA(M2$9wxho{qTi38%IAewe^W5Z|4Y{2YB+yL6(O$<)w;oo?s~E3$^fchLxY1{9x! z-S55Z5VqJlf)5%{L$@hqL z!W#NMsmibG8FmfFzk1hwlgrSCFz=M>6-^*Hf2*zI4j~M21$yea-%9Sgr}0YY{&G|T z+l_iI%4BT}55tu=9GMwATmFLgquPdw%qmEC7@>wZ=BDWpM+I*J`V!F)>4y`JXDwUAJK_ zy@Y+8Ud0B5p~b@3m=$IAhae6uxy7@r;E>Gkk56pcLy%QfJ4%*A?1Z+OEjJH&VLX|| z6SnC}H6*pGw%x2$>&x}-as57^P=%`Y zl`Z#`cypx_SI z41-EPamRuKi23kJ>!%xF^yJnO#1?e>n^l&f@J%sJZl=#P^joJ0~WvyI7n~`a(iB zc&+1QjJ3i@$`1!ReJ_-iF&RX(`gBz)j`s9@aSU0BaGx$-BJH=7%1!r)=*onN&>QB= z^FI$UUyl0gSbuRw;OH|uaWUveq7`(l(a1VG*|Ydh%1)EPoOlO;I|Tc<4|Wo4AHlQ8 z^(v-USdu=XwK=0GLL6v+#tmHJ;zSwTcZ67z%o|Qp$#`Q-N>o-+V=|#;-5AF?k$J=R zxS0Y>>K8985{z3%Wl$+F(}Tq?mAZlTU8w+2riGnj3+4ByO!nOiRO%%x-x3X z{DxyHn9fnCHst5rv~Z$^{cGN+_%Xj5*Xa#-_PnnTPSIw@v7E}#=y^`4WOTE07+Vk= z&OPb^2Q&K$9+KU=+{n95u$4s{qE++IKNuCb(p^|iuOK1xJOS4Ivepj79|BFfDZuh| zR5L%}S7-;eJDBVBHd0c`jS7e}j=7rKxD(wP=}~a76eh)u_c-Ld8fD9pLXXnI6<8Do zmuRQ-hNR<+;Uq-?jLMc#d@t#%y*7JdQIbM2Ayu3NYLSQ07vg$Il}-3>p+`yZdY4tc zmuvU1$q~kDVY$*^bM)P{b`#c@RH4__+T;2tGX0^O5Eh}|m1U$;*1)ECw=T#4WvV#$`l8)${wySb(4!Dx`+{{74@&1)aOgaZUdguVu1o(J0Z-8q~oDag1WWfXFwgdq^iNcXckz^;3$cqCIZ}TEiCdeA@ksjhNC`vCH ze+A?vqc_=O^(ZLKMS8DS@14REX)}~hVo2muX2PyFn&IbS?P&P9qV|&?gJ~?G$G>9O z0%!FL{?Vcsu;w%&Fg+FA#e5^$hh?H#!-U_qTZ;y5Gi<=STbkS#yZ$Sbwu#9j@FuC_Q;%^D6&UZJ4AUjB`P;pw0P)m z$K4?4fN@1Fwg9KhdMG_u z8b1_Yjl@rIx-LkFa9ylKAm%l(urxwxV+q@0Qj=L+l(#XXqajykQZ@qbHF>e%ETYJv z>?}4}h!&SuOnxztupeSbVF0i~6M%EAYT$4D>L_L-*XZCZ6ypkii5g>r`d-3!iLGH6 z^((uVvCU>zdrD*`K_3agZ>JE=sYBRFPPAQSW2c;BZ;;#|e+9Ixpfa*G#z(bfbr&If ztTi-Os<*{HVc#Cx#I!LrCa-2jr%#O|L%V?tUF!<;{j4GPKaVEAHL0YDSdIYY#8JWk zlPHw~D(f?oBeaSkz;>m~C|QpXT0M?_52ImKTv?Ewol##{P`2(eF>1VyFHkujQ3Z`s z6j6m1Un(x)EHbulfZZQl(3*?mOdAQ@1@=h{JcMLfuD4+6_J~hFaO?Hg&zBCA4_tHY z{*pX(QArpSO#16BD_l)V3m2mA`AfF3Y=m%60v?xGjM)#FwF#^=!HO$Nv-BPF-D18h zeyGgVQXx+Nmht8;#0K+>%cy?wN1IN zE2{;}&etJhTb%7zE9J}cO2VRjdDMYC5|xGeQM`?Gi$zu=H)&f~$)2I~QC~Wdmzy?@ zXM6Y8Q4h=+Ssmea!;MgwcF<{?TcJE(EbDVho-t+{^VwHd$U_oc@^#@KEX`KcGwMFp zY4@?v=w>JDE2UA5R7tyF2ezu}i}ty+>(sWi>o(oPx3AD!X9^f8&yX|XR%(V8C3C6C zd+Iv|u@LA21D(1fV@yIt3M^3}=a213MOr&*wr1VsD;v>{ zXt`5|yAecfLAK1@rDhn>O)0rB!U(4#yvu77lYX_I^vnFmNOiU8hi`amuC_C?n=wqM zv>)AUn(r=WlgL~;K!LX55&3dyo}?dLw$aYl5LP=&%N2HVsnJ{T8l!s+-gZ`eL=(otb5O>HKKd*_Dd5h~F#T!bFv$tgbo>VPC8PsTJXm26Y>(xO;_YL)IxEKDNP*R_fe zBO&EtvkAu}o}H8)cm2%%ghr)Opk^5>*?wh4c%?KyQihU})G=#f%WMlp+?0Ez)`g-c z5lEH&%908#FhpIq`JQ=YGt;x!=2q*+s4X76+sW$L1M|IK!LRLtSr|j-Xt4Fe6xP1@ zI$wwU0vmj#IMo;Z#&soZie`D%3PA+$J_XA61%oqLj^@h^$W*I>=hVGCc2m*`ACC$M zQwwt6Y<2Z)jIXiZ-4hcWLdM_BjkNNwNsy`sp5byk@>Bx~Dk>1~k#Gv8|SbXz>JOeQ`Ms(+aBeLD@7&i z@*zadxsaV{f$-VGHFP7*NYAIojFXd4WuBQHtY&3xQWV{mk4=tS8V8IhkFd;MGKy?iY;_xRfG8j(Kf(ae^V;1w(VS z40p`FrLyW{WZ+vqWc179omz`=Q*gRZ=AhPi6^6};VM{H~u+ge^MAltL-o;pdb(r7gYRlJJScY|0-fKQw88gx~Ud7y}FDx0FbVW^r zWL%uK=xC)wU((~_>8wR>h86G<#H#!)Njq@SvtQ7yy*bl$6s)c~Y|c%6?jW8yhdMge zUPKDs7>!k~m?$jYs5yD-uNC9z-zYuA<%n(Skw zX~QH?B}$KRH#*IWTYPHL#Cff~nmCzjZ`O=*3aon9W!4enW>fh>7!~+P7?^=Yz!tH7 z2|H*NP6TK|(N4TcWswYtvs5beO5-6sG$j{jDczC`=nkL4b-uaWDM>Ulos<6PPbiup zc2*CdaE>7#RdbaEZE$AI#4F-vmltiMnW*}inn`B6k{!a=%fWm7bw=jWM9y=#R3C{c z1JYyG;QEJ?3F@VedeTkFq8t>StTw>X9K+NcX0l2L7r_N$Ov!LlxeMLaRSu3*?o^ggWEU_9xU9xov1cbBYHIfDdRe8r1%QWpmp@;@3=tF( zv}9vJA4|6m8xN(jPL0vhOTSI{-A|=se4}Vf>^w|ewzW0R9B3+@PDYh9fn2>&#n6qj zNhyz_>q%lcU$;rD5{u@rW1nrCl-J8OvSDHxXKy_wWm|3BCR6qrl1+b^Q6-44)3C4l zAudd$YepjZ*-&ED>nbrG%&&DALhxVRhLXm_oV{L}8M++>NNja>dud+;0<2k!)Qt4V_%0g(m+A4u@<_Up z+A$|TXrhlfY-YL&sgetM_qJ>-GyqjeF)-}&b=qx2lL|u&R)GTtG^pn`LRiz2CxeKc zL8&FOn>R?xs23zf12hOwz}B8w+$* zV*!}67+NzCB&%*hOXpx=;;N}V0W)+-8_0>YaQ-Q&5J=wyNY#n2Y#&rAaeq_E>ZQ!XQzsbj_Nhp4w_TleI$1 zztajsL>oGUQR9E|)C$YOHaLM+x|@2Y_s*qrR)d=r6>F9w*4&w8jCdnjJNaKc-V$h? z0D&7!9?CgcJ`qD?55ODkTvXZu>u4{r_>InBM@iPpD7_|1iF8!Qp#EdbL@E>U`(%XD z{G{SOO|FC%V>f<2gh?Kx(JYE$k~?WM)xrBcvrYtad`aX`E)r+dGw2DEMq9=So1&Z_cnH#j~%a=A=mFs!-reYV-9y21wawdV-`x9Aw&JaD)%X@0~&h0eB<8B7rd3=#_p)FzF^VC~dV>oH&SR^y5Ank2b|$(!Yt>mKIp!-WO-aUEG@F@7cK_?;<2tQN9d>RdtOB=b#GV*zQlIzLsO zGro~}LTY!RhuRdu{K%O-Qp)>Xr!EwwWKL6~u+;FiR^mhy%5u5p zz;(=xl|+x|EXXnoeKoa*!$Ulu9hJGzxQ9A5>kHk(&D9Rcqws3hT3$rzjJw>yX0^N1 zmM&iZ&k|{l??|LEW0R~#UKmxMiGI_;$P66GqUCXy@`nXk!)dGRplv*OSQB#E**ta< zF)E|CK>M7M7Tr&+&&}7WN8wFgQVq7o`c{H%kinx4>6@Eb%e|9z<49!La}x6ifuUr3 zfswmJ?h3?LU_N$5pKm}eV;_-Xyd-ez?U}p0q|L((yieFllmgbu$*_$QEEf)9h^@zK zOb?}ZqC#vH)!a=uOibraT(?j)a!)EmlDJnrN^@OAx*4ocQf-TiS5oVvSQB9{PGDfv zd{`qNbzzMb!RbUh(UP>}O$`G%9MU*z74Ah%*R&^3ZB10ELcT-l(*`!yx-4T<%+6(0 zvMD{dg`9zm;;mpZcd-jX%$}F})}$mRGi#LzGkfPg3N*6B)*~G=nb2;cFB@lh-iLqjtcgIrk5MCrZzgWvn6lQS&8g(=bdyKer^~WYh>6$BHr&VM)O=3Nt5S-DW!xZYkR<5{=0ua{)H&-c~goN3&w* zI|v~Re8-00dyXY-_j~iR%>oQ(GcZkLs$;TQ_C*_|=~jKf5pIcIwTuvBJ262-#=J|T zG;rx)H%lS}sb*Ng;lXUBsa5&E8!nPNrd!-qt1)-(16}R4n}k;}@#7ejM63;O!^?h4ui$46#mKZn5(&n#~iggQE*Lb88Y8z3i8c*yrJ zIS^6e5jMfsS3=&OBoH@C0N|t#C{D^b*1DQE4#tdIpYE}w%$ic~l_jrkPU^u{z#vFsdBzQcY>aRFolkuu^l%oK~BvHnol+6}isPKi0J>S@et^xF39g1y`) z&f8P(XvwlF%G`>eWc&$H!|~VNe37pEQWEzuoLPK(^bmm`i6}S_MG=BH%F3KvlR1ic z7CZ!_fkik57}VK{QUZs6Ek62p*JQ{h(OCM%FMje%p47g#OW^CA*VW!`h*NK1aM!_2|us~X|xbO?tbQ=HB`0gLBa@WOJMEO>MH zu6Vb9TLVlDt**wFmo#P$iBCrj)Q>4n1lOfj{U{+adHkOZ5oXB^3J*p^vVg^U@D>9# zQSFOX-CpC6E|oFdj=OIts@QUqYZJR_vBmesoQFYrHA zf@?F&&x#(wC5yTDhy2+9ZpV~~G@Q+bDM4|65Ci&ABC^}DC2BnECK`iLZ9}IZZH%z4 z!C1!8BkZ?=j z3YEn>S36w}t6O&aVr8%C3w$Qb)=}<}+VL{Ma!AFrxzCak5kiJ7MHIlX>?G={CNV}w z6(E+@H(9%wVLROIQiy%}MEwMry?MOgb7ox|X+vkS(K;s~pI-z~q_M^F ziVFjg06=eKt>f+i22#`z+r)i=o36h*lOnU$-z;%9gZx}9pr4<#B1^eUXlu&6xJpA< z%2ldWqM=;NGG*sYKNf`UMC~6l2Wgnru^gKb0O;RlVT`Pb6H6SfLXJ9?LYsHnTLz>+ zNJ5)RT!~s2qWGs|1_XUdY@t(_i0o53+Qr*`?Ay-yDt&T5@5$&8)z|E)zQSACzE!+t z6clWmdI^o4WJ_)2Sefszfm%iaUur5i?rhOfFC>yg+@C>8$y%z$Sr(8?e01>^yfZOP zM&_(6pHjLVk0xpTK?XUxsctCxVWVQpcorCTkVykk|q0z2eH| z%N2r&$$Nln--a^r46%~h*I{F%@U$;(7VR127CU!N?i+&F$S`K_q$3xea$_Iwrq{?J zTO|$|;{#dJLKZRkYD<{LTq#S(oxvcPSj8n6WLOls3u01cyc*awj-6gxd&nzlHi{6o z9f@y5OScYd)l}f=PUOO94^lI*g-CD5V-*`Yeb&Wjwbw9u?;kQdBWz*{i%0oTV^uy* zBN9Yj?N#SJv%-2N5f4Yvwx*?<<-#1x=qo0N9R4JC(6Wc0S(2b7DsdU&WhiyxZ8%2D zXl+2|v60=j0ZVI)+et_Vudy{72D8O}jWAIYb;9V^h46+5-hP9wJHIR5KAP$0%9#7k zB$CQ8S>7QRM&+J?ePXh3>V-FaZ4r`0s$k6@W^a$p(p$>&c#R`R7cz`^iG^b@v6Uqg zJDD`NAn~+GNP?|=t*t-SElJ?O(l$5hXLLats0^1fgMstEs^(N_aF1g$poxi2XLZF! zx}c~9gG<(sPqmc1O^e^T`L3}zrV}FSYL%$bHKt2}`I2lRHiKd~o^G(ioh(zx_j(r9 z_eU_TA_!W(R_C7dSr|I0zN7=q(v9KHod7*Hw66lUz3-=V%{-?$+-5*Fl~T{colyu!}diV|tCss-sflBfnO;&bM6?6ERtNM{h4OpJ>} z?IU%U>bGD@lR(#5T__)c1mI#^!QiOqToQoTeb~HzK}jx2aR-NHCTQlmQAq7lJ8NDd zWuHe<6i9Id;mjgnv&Q$k=I}Oz;?xeW z9hM9?>}$jVsH>F z#1<8a1=8O5RD~2AURWN~W;2$r(ga?mo()^k0+vNX(d?~{Pmb>uk&;uft0S29R7t^hY~QmqsahWA2<7dp zn!%6E7Y1aTj2?7dzWI(0E`UV2eU)JH=nMhA zu$nn?%y6^E7e05>pvI{0@LUE}JJ(f2)jT%P^bBIKnOmCb9B#75uyI_Q{vu!Rrsg%b zWqM1aJ%(zfRw|{DQ2()M6FIAFpKk^oWKNv&^QfNACQNv{ zoej|iB|h;oq>_4boPgwR`*;$;YQm?9`zFW48O?MJYgWWr6~Pd8 zzq@*V2gG}e3#CiG1D}$U#CDGNc7r!i;8-R}U^pbfvHy%Xoy4xu$8Rz?erb%bSGQ+6 z81Xbv-3OW@RjMiAFTBR~BR+LaT9+8tHr(XHO7kH(2TCs8!ZlvvV0|2T}y1W+HwT z(TVBybwy$&Wx6YmsvH8!u(9a!_Zn;PI^dSO6U>^ZV+|A!;3=nv8+n_htS*CFQO?Ix zjrDmO^DG#mm|6XT;TC(1TqfQ`>%2AOv?&ajvNgWjm!E@qL~>yg72+*se(ytK)c2|E z#^BW=Mmw;VlwlGp+zG-K+7%|LSN8_8P}?fv57=O>#Qs#Zo#BeGNgOq1$CrmeAwF^# z=^lqtAj$X*A&IeM8*4L;dh}DR7&=+c?fH;pm?Xs(J&D=VFIi?D?z6sR(d|~Tb4YfMVpkWRH#O{BnQk1dgh zkE4E4d-wJz30{J@l=_&kZk#)P3_9G+mTD3EUpwqg4B4sFcbZX)U0J8>R0%PXEqXod zl9LE#);kQn&U)7yee2D4^x_}|pcDqP)qqBru|_y$ID`evchuQF{D`gBvTS-E64^T< z;{aV142sT@uSDnVMpojLEdr8leUh<^v@c8io$0ZeDgLQn5odbSqdFDPn;Knm1sx>r zT&35ZleEV;xWn)5itUeq!f6INpQqmpdUPtZ0$4vqP5?!D- zAPT~Cv(&yBmin!9FTaTO*rBwat=?@`@FeNP1JhERk@CFN>1eIo2CE4AyH-}m>>RD& z{myh4Psv<|xT9Q;em~6?9Dr`A9`Oo&9HXO9h@+em-xoQ4{=w#^cAclz4cSC7*BX(* z<;dAx`yH7~->^IGnB@#sQ7C|6@@?XSg9};2q1>R3W}_D}=IDQpk;No)?GU$J)$*mC zBhyV{Szyx|!jwCClEe*eJhw|QSR8t&<-9}FgG8!*YT8|H_Iox71^c}X+WFd`%s{)- zjXq-NZq5XMdls+hKNMlB6?0!GZ^V@c)AV6gA zot6R9<@olqck^JR{-ng3yj#co_H9cIiEbo6I?7S5lB_8owrg*X#*#^9JNKm?^k>8j z1F2ZVfl9eOsHU@SgP>ij&wG8cjjCcsJGTE3ePmITc8;5~zGXWT{E|&CCsx5R^yj(~ zhsjGd3z@*Xk-Lr{cd0#TH*d9qyC8U;HfO74@tXDC@I)*MVWqCa`AW#`Utd}xc%F>v z5U%p6>vg9!x+6&asxxXHZEm;}jO47jYn4;ZNCQA<1k8?!=${CQNU1+pm z)a?Bu^~^2Vi8aIb#kNF&Q8;@GDS&k(yk;wK#B5C3^F4a*jSF6EXZm`fIBRA&fpv3> z%mmJ;g&A`45Gry&>KZ2&aX_VN253j3n82eXLe)Gq)zBn>un5b{A(bgT-v_Nym7VtYYbNshZmx zo;134*d>S1jPbVd2@~ST%;4njI)bs6#LfP!j@=m5anp;3d)HQl9eFJi=U4Ce7Cin zwKdJD-poB?CYpM7&J1m|yj}B(pU7D0c08ZWuHjFcM~AcSOGr2b!)JU*Cu>ldj_7@0 zH+;NQOtDmOW(Y^u`Q@T2CZDG8pP8LW!h$-ZwhG9$=r@P0tS*;1-q3kPdi(HP9D@o< zK=4$7P}j4?494H@o$NU+q({F}_}qYc<@hU0l9?3!8NR`$=q6Yi1gls)n#I%66b#bA z77T@nMC^@Y0q3ysXAM%4g9zpFx*yp~vdq2MBaU&x5D5mG4LnNSVonn|t zJ)8En=ToWDwAf@*zg7!|LBUbVC_&fZ`KaJt_o=(hA_dWDV~m|5%q9_PnItaE3RTXS z+>X^JOLpoP%(yg0#5q~KeZ>y5EjcXJ5->7_*o-gozB3DibC!rs3pPcz1$wd@aB4cr z^zUwq{G+HepX6TKp>&w!l1`!NU!{EHWmb(XK{zoE-FEZ16aO^H#k5tB7Yr9k07XJ8 z{J*kXcX1ys*2$y>|8ViDTI14LxLFis*nTA@Vw~t)7SMP~ri?Z=4V)_k-)6hMhy3Wd zx7$mllc&ObywV#%EiaA|LNeGv)XxykgD&D2v(n-@>Xbym z@VfJh>4L2!(&3p32p^){C{Y+ae+TwXqP%{io!4BlynvL&Z> z`GL!1ycsm*xL#Q1)b;VIFU=xd^)tTR%ZvJkgQY9;U{k(fJGlMfFX3!CS93QtcCMGX z84n%TS6ZCo!;uf}e2Wf!yLhxNog)>J_g5N+Ryh%Lo~($<+jpIgok#D|2_SC%ciir~ zFb{g7Pn}>3`DK{ws?H%O;tXDwt)%{xGiwGC65PRo!LhHa;UtnEu54ngcWV>xR$Hrx z9)##aQdmAb6$FWRi&*-e_*A${bq&5P8eLxP9M)ztW=KP)xynAIkREtruQ|zLX3SgQ zsMtFTDI?j3=B(@@E(SjJ!-tcJP!7`MzT`}wo39hLYs*J)c$(Xfd~6}8C=zIP#zGlp z0`6>kXUI<#`Ih_&u_>jq(e&o>Y74cBEm0xE6Wy;=*)&pV2BwvL8VF_T^Ac9Lxv?nJsV zfeEj*+m@$hZPGbf4+cWq)yr)=#)!Cz{UKn;(5GMl3TEjj-XdyYaJFJNAxC%2AcMf9%;Td>r8(GYr zC@*v@zjJ@b_{D8xpjViVVNYVbbYvLtFon9L;J)L(1M!z3vqxdv8fc%s z+f2+BHAMZLbtY#PL+J4vCOPaRHxC>)6ZEG|Xj+p!BNH$s^)H|qT)C8HAmoJs%bCeC z8LFy_-}=r?1R40X^hM0aRxMdooIfPBz-0BdDH}9!P{1okJOUJY&ARJeD(|p)EP(8Zf1ZD;*Bp^2}1Br+zkTshQ+)LMQ9sSAZ^<_gh z2dAB|I@9lxbfh3NL}Wa)nVHq{nhVd5sc&tl;q5$Q|Rr(V5!7%v3%Zti-7@ zsRqqv3)B6_Cgy|KFpwI&qPzqD$8q} z)~!HQvR5X`^5EWAM~}}u%1LD|Bl13$@{U9*g#4qOhD%}NC)q$-vk0fBt;VIZmTpFK zZI!@@Fk=Qp;EZ7`B{q7CHfUI`ZTE%2bI_)NmE&!n&um}?L~41Z#O=;n4PW0cSAqF$eKfm+`A(MAoO#Fx6WteK_+RW8-=i*Yx_W_oBCL#wx!7# zl-JHVB#jY^jWDgG@dq|7ho0}|jQN#)Ov)B^H+i&Buzchu9iJ`WVSNhg=)9~se!B;C zj~2BoS|9iB)v!AviPEvH1j=?G>$#KMXciS<=P_Gr+&MH@5XfUT%NeKmiJRSxt~$ZLv7=&mpNIoYST^-#SN@dhAHnV~sB3T%r=pK=LWN;@|)JN{`jA}0tiyCxnfqE?4QLblvHz*ut?zO2JADLM0G?&Z}e;xzS zn+DDHbl=Tf_1iI=c{yyyphmN|&d=O#W=`Mop1Ft(%n@FOzhcc)1q-g-9$89EtM?3^ zJB`m-?2RURxX~u&9SKYk-%NxH&o9cvDX~LWiOg({rd_!CIWb8Yy^__wo~(sgYQ|O> zHH#$A#HWHG7nbiAtv>Fa2=Q86wl7f+t^nRYbsdLvlj$-LuC@Q1LJN_&;RT)A?1bqZ zMLr;%Q~&WIpG{3ziPxo^Wozt8+>9B%?U2RoEYW#N1LYI!WnFINT((&vfMN=p81twrdg;*yce9 zr{3PuQ%(|J#(!}AHv35bfdDY6zKvH-I*HfKb5UO^&2={MJy7K|efG}B)9)&_ShcKK zwyB(HU4aEr_!BL z*z_9Wb+TKue{;&D+daoAoe0Hs)BvS=T_xk-V%_AcUeBW!Dqt+laM~h+?2fm~AmNN& zbpxP9skK06Y%g1u0w^QN___6gY9EG&K$0`f;qlJvLm0NCBlsak=p74&$EbmT7 zfrlf(O98?Ee<%ooI#dFRmCKTfnB>s2NV;*yrNyjr$!f_$ZL#Z#;O|9z3*AjlZ18!o zrBj%mv*bh7#Nf6kGLK2Jua;0IoEVbWVewPg8jqFJX9v@!2TdyV<^0Y)gJh}j7HD#N z)?3Km3TJnP^&CnTbLOm^_S-N}ji&kM(%H6Uw3$B(aTDu+3K@tn=VCnJY%Db4AY&Kv zv4uSb>p-nFrR^lBiI$?9j&(Z+Vj>kqt~P4N(D*p}*XQtT`&pgH0YN5DlzJ*l$%VVOFk&om3RyQ|ZgK7+Csm2ge01w#`e1KR4mo?%hX+~|!U?c#_GFvJu7#so z$L&~3aC-fi8M|%6PN3Dzmn(A9r_E29vG(i0wqRcHFilP#+P);&<7w`H**Q-$MYb`= zJ?$tl4j1t$^I~my(;x|P5Qf&hnyheP&SkAGQ0dJkO}RiRpS!wOPwJ+DP1Xh5k+rsV zZ(B!ht>d~dw!H1**>W|eHnthQ>aDdR?VX7!Ze%?fJDJ5kOF6}!P`~7`0P4Tlu>;|J zJZ7*(ky%{5ryWm-DS`Dxb&wh+N@KF3ZZDSQHSARkeL#CS?bAubE;!)k>N&>7Pv)(qbK~D;%QAI< zl~E~FoV0}Cc%J*@HglSjDccWYKmBLTE6>BkRr=SgJ}o>7tx!Rk8E>+_0Cn`o&IGIH zqC45n)T8o(L@!_S!4Q{ZS7F51Ja5X7@Urv7_39(mh;8rebArB6koMF?3$3cGQA~5o z>a5!SW3UFdWG8M^%cIe!q;0-KUNa|NWWBDQ?7unh@Lua-qFEZ1M#%M&$al`bHQ-uw zHf-d(?QRJ~ZW%AnPkLtAms$)VQo*0LiP(j~vxf5tPZQ~aw#B&n)!KM#+)GXrF|l`z z(TOGBOtme8kiDvCmh!&2V!YF5emmTlENhJV_*WK@MoKjHV9Rvx6L+(GhrSPq8^WOh z@E;wFpp)c0<(;?3`MeLzazes(*h{({uzbTz*b`%~Z$Hg~k7=^~tiD%V%eN4j{T4r` z>~3Me%Jk$UW9PlvS!0)-h>~&N7Mb?WeX);oxJ)P7i)Kb*(O3+@AW#CCDBZ3K=e6)N zrN=f+Ed8doX%|b+>X#@3E_B%vL@xXcGSH`eA%R!QEBb+F{E&E7NytWh)CuNiO z4iPuYwjqlJ6(zOd7y1IKGMZ8Wlxsw{u-?<4Ur%o@m09L|OjnaijYv+_n{>=A4-Mg8)HLf=m)L z0g_Tb{rOgA?n_l|6bi6=B6@mS5(!l8otKrFD>Iv^`qrb&$QkAS1j*F9uc^*=Fnuf7 zX%4vK6(dKjCV+nQSsFTb)9%e&E5&1`IaqqbwJh^lo_niHQ8&?JI1A7oAr0N~BnoUw zsQu|9{lONzD<52_qJ< zOoG5R*6DVA$3MQ;c4oa&k#dZNPe{B{mi_mlpKV-XEh#-*qU2oJ_Y+r`ksyFZeK*D0 zh~n^q$Fc>%D)#d*UhHQ`2P|J(NN5)qRcKn7SG?goIvEHv8j~XUg(6Tnm@oz0cq*YY zxT10JY25YM>+bZKY^IB*Br#}xN(692bNqDuoSc>d^ZbmGrRi}*%C+-LlbkG4NhAbP zXR3T~9D5TR;UFh`w!0 z^*6Y7iE@F37J-Qi7e&NK;5E(cK;ptuV$EQuq04THQRv1acWmfX!ppg_sZ1d?DTOg9 zA^6?QRi?&syyV4sJlGHren8dD3JCw+S#?*;~i)0;WzN~%gqB1z!+-vB|!+Yj~a2w-8n=@mJ|~XY#GRw z9dLV{>Qu!T9;feamYo638`G)a`KO@jkrGUVIwE8z;GHBvZz+eH9VhpTrrB^_tk*U- z3<}5`KP0<0h6n1YIQCn3>e`WNvtxJ)G4Je7*HLgNNk?jc$~ijeu$qW(nBh=2jj-mO z1g8qHgnTAh$WF(x%Wj*4)OBN~ zTa9!F(kagoPw`g;7B?pZ786oAHE8{bV?EB!QY}t<<|?|Mp`*zON>zh{^3qw(B?duv zHH1A|W&;$^>|T%9E*KPQdc>?*{3e!V_ZL2~u6pj7ZhaL&a_A zxC)D()7gGJUd@JeuW*9_s^QiXmBmnF3IodqDAJ+wd{duY6y=@bk}qSzFu!L z;~_P55YrFC@@LKbNwX%$UaqkX9(&42K-ZRj80KiiU%K${QexO4i244sC^F@sBSSkn z0Wz!D*5SkaZy$gAFrWSUw>gOwL>U*Sh08rZG$AV$yQWfDjrbj43-g+sf-0%qGNO&b z=UFTP?7GG5vpA}9wIh}495OJwWA-&0#0#z2T0clJdg(*XT6+(^XcI%CzvJ+dKJICP zeQjHnQ}u0vt%xR*!4sM30x)7l#D50&XnAB*Mqe`M@$QO-cqrFTi-)x>3uXOX9JDEV zIh8}c_yy6>G>&0~7JI>N;owjh?gzRKA`YCKZQSS`Tw(*XtW+s0cnt8WR7(~bMDstL z9h>c9S{D4>z_+x%(xr1>gG`1evMkNKXT#*!;JE~F5@hHa`^Im%p{fA~NvI((Z2YF+ zC<;xl*~(YUsDGg|E(9;BOq8-}wch3wO6=UR=*Ylf$zO_zq-?8VQ-IsW{QdiyqK)Eyj>pONYk8B$aZKWZtyizg6@qv zV)pKNK`zUO*CR-3#poy1N+i;@Gdz4>(o&yIl7uWz$+|S&ObyN)q=uoX!rcIvio1{vb(rvDl1=Mf{emuCm6L)-bBkM|rEKiR2md&ed1fOMcd3 zME@&8PfY3B(z&4B^435%^GO1HFOI=ifo7tcW9<$Xre8;DomNtgUWiHIA~+L^ZAt!? z?EC&UljO+WnF8%k_3MlAc0^7Lsswr2)-0Jc-j2AO1e2_tw#u8iD-$HktcayG#t}K$ zzMD@J3l?{gs67CMG04s6d4gT7?mRZ91T=CqRr%*){@#dAwmx z)3Tt{Hj&jPrEToZ#lEspUKDIN=tz8aytQB=WtJrr*2g<_=(#*plb+H|g2?*ul$Fa` zGn}TN&tCwasZ3OP!Zgy~0nk?B#ee+xP7Q1^7|HQh(5R(euRm=zv5TwEtjCNPwVpXy zMq#!S4hy>dc6+(Gfz=+k853~lV@O9VGEeoJjE&i?k)JDL^h?Y#!K783!Ef&8ym)(Z zMJOda`v>z@=aM@P5R=MO|6p>fI-X_Z^|sPTc1U|Mke9C$B2!2%=`j$;W>6ZJt$!X2 zatqK~Jz(Hc+wIr_qDtuW4m-Dm=$|yTr(QU3?2=R4szfrJo>(G6smY{I*TwMjWN(LY zi>o%KA`e5<@vAPLQ}f-H?5dV$mF0o;YN^m0-hdcK#`mPmGxt|;YUg>X{S1m(sWX4t zFhb9&G-7cmWYe`jth~&diKphL$BD%_6=M;~iQ%mI(_*;rBk^0og=QfqX`X_Q+^YiyENu{U0eInG zD7gfxnv{sxJzZ~EA!!2vs}mi8_lN>gB%;I@!3~CFOQtRo#^awEt*MS$L>LMv2|#)c z)WeW!sXDspCGSQ@OL+$vff0zAfoqQ;N}+7DP+sVTbZ|#;=@W z8trsOJPDd{f5REgYF#X_<$;bjg&WOlGWQTE=<)%FC7L_qf-nIP{&ONo-){ zfJS6D?KeeK_H^D_0OO%vy3p49*{)nfA@$AyrI~-exQXqQIJ)iYyzYU|%sHB~bKyQl zcke#qB^cv5i-~!_2{#9Qa+-x?TynLPe(V+9+*zjQL0E+U=`I^1B!f z`(Te7p#9hfW{CnYj6+)d)bD$Q5y~Kx)x!&tPrg5bb4bA<2+*f_c$PU5>AJth)|K!& zwS(;2!lvGq9)|FzNhn%BJudX}A|;1_^1I@;(qo>eDGD3?L-Yk*#11NeO2#g!v8sRp z5@xrLcbcJY2BVbGj&o%h)^_~j$(q~njAxxjzOtVc1ChHQ%cT1h|E8;m13c*}iWwd$ zLU^MX@dnE^oC27}^vDa$mHrCPHRh=eUuHe~YB z9R`Qj|5IB4sdBe2ZNXp`iD@rlKZH(NFDM^x9(08r9NQwLIR#L}fn+eaXNvhE*w?}SbQ7q!eQfdu+e z2OxX48!IaX;A2P?pQqq`vgEe^bSK5w?W&32pi1C}#Q zh_7XF>ne)yoqrFdspdEMvQ%{!*~paS$(3tla_VNe6_eol#XV~@ff?M_@mjFI@49C# z^)}N(-HNm`ZX3TU>sTh5R{>ui;StE}Ekz87?Nmm8(I(Q>-mbeO8KE$(%Q90qwX&Ym z!a}ZG1;>R%hklU{W{Z&%jz`AGCvD7WFWWTenT`>>9*RxR1QSYvc7KOV;3gm~;wbKR zmKImS1sX>}ggkLxvys<)3zGw~3mwqJO5FgbX4=e*K<^(;^6zrsjxX`;gWzdo%7iQK zbv%`{OusT5eYYH)?-z1BkwAGj>CiZo$fua`7!g^VyLvrsufyhA1xamQ zBh{$Cc2g{Gp@{-G1HB-RI|)9f$c~@y$1bfqits^zvu=0d~9cLI)v z9zmh8ODZADUuPf9rf_LFNWc*M;(OAp&ZE=2}pE$kKZbnZUM4WN!{0EpA8 z10c%dA|sqo9?u#pp*l`1_c&d~{=VGZvdJ&aSun6+%!SqK`AeRjlUIXMr*Oc*`q#I@ z@aBmQXm}7uw)Z63(@L%sE!+2ePq4gUHT^7XMSb0-{*#tva^>~0!*1r-EA<>Yrq-i( zigl8rR+_8vi|$czYy3{vVPlonZ{EK74~ggf66E)I_xpcbsxnR$_;<@ZhScMyj@d9OM@BK8u`vF{zt;?Uk(_^$8#J>^aMG zymXr-jKR!m*UnuP^|#toW}D>B!INEmiGq_v?2>ywzWYT|XPJyYSvQPVI@QM#wwE6C zr}ZLMaVAe&C9emb?>#zgp+gLq{kwwqJ2hzoa{kUDTak73czg(6&KO)U$dhiI5WFMg z!Y_S4V~FMfjWD~ND#$Z zHs?@&YroIi(_v@MXM)z|c^g*QhTMI0wnSw?9gy5Ev~j{+PQ{eCX`mXW!}IVi1&P2h z=Ai%dWHQ7{(8?aNHxfkC;!-w2Ofx+d&aPv>-EE#xv0SbhTF1UXG~GEAV;8%g=HeMV48`~y+iu2>Gk_K*P`^x z*$=&CUg>E5I@~JH=ENKN1C-kEiO=bLy7^+gX6*fr@jS6=J54Zt+**Ty*euR3Tl`DQ z*1D8ELZCcpp*MIkiFyj{8@J&hMp@i-#AqOc!lcx#V~G_ZDF&{Ah|?o6ef#ULXzqw&33XN7rp~ zSG-zazE?oy!=8e$>Nz=uj1-OsqLUE`(q#uxPf7jsu(<#1kD8iI-+a2+Tv8h6(Nx6o zUTyxc^_BELRY!?vQ;uu$7bt3AFH#yOO#^fD@#jJrbdTs{eoxEJh7$gI_ig_49e;_z zWj1FNNUoPVa?*PIx^7^#t~9SC^OpWM&OY_xCm|d;IpiIr(-3*yuwDw65yEi>b5Gc$ zmB|IK{u`wbuMY0B4MdA?mpQkGm^J-cN&UQj;I~L`F2Hpf`*Ja7?u_qs@AlK*U$Hb9+P+-u zl-DD7&`mfP8{K+=+=~PH5!^!9p_-w2^(`N%v?=L_2_HsdWFoTTfb^CTxKArL4!?1s510-@at1> zgg}Q2%7cx0NIjr!HmAGNa#I%_L${|uYML^dG3=$oc@N{Crz95Z7dlC-D%(Fc8zq&- z;RAH(2K{-{$vL-T8%8-Xx-wbsIYVs#bdr`o9@op?x2&7uq||YCv;DmGtu?T^?li+j zrM>&opwz@4jju%5;T-~Nbe#=@?69I+et*d%an+2wqq16z9NF08)V{~aQL9pwaRTO^ z%=M|9LMH>g1T*wn`An>D7Z1O)Df()4v(r{aI~!z@&J+3*nZEn(`P*#$-VCE}j!OXU z@3*__o5iQCuf)kue7wKW6Z2b3F6sNhxUKJ)MQ;_;&R^d_J2csHlhbklXJk%fbvs{e+Qfu)b5&UOmmfk3MjpZkWWGf6SpvO9vNDGS zlC3v4nkz|{bG#r?94|>JTY)wuAN9f3E&y5*MfD3{bACe`zi5_5HB*H6MB!f-#TB-v zPN~>nNrZ%yOC#`Y4Udct{%6Y`xb~}+Ewtx(1~trv0i2Zq^eI>cA6Qkp!^hdfvOx@U zvvM_uF|{qrV0la?+DYr{apOi=lC0$}!%QSCaP|1e{*`=3Z}R0cFGi|{)NM)ova;0?$kz}eu*s(w#V4TJI zUCfPFr}iA9vHYF*K_(qxdx9_&eHq+Coj%z!Ek!NIzJ)@8Rarng@h$h#TRi2^i|kr>{}e8)$)kwCJTbDq$!^p=KKdSBxe4j7l_y1CA%J4DuiJhzyq zNWM0q%w%q_6r=D67peTkoM`01vUh1qo1Uhh+uzqa&;xj$<3jDM(;yXo`*bvuNz~qv5veFK7ASIL@9(wm8}20!)f`X@3U^srL3R?g*1%AKs5R8wwFEe0q`& zb;=I^< zw0^Mi9N)s*SB*GN?3;Z;1aMJ9*#7g7X6YxCn_S+ep-JiJ;$9|u$haBvkevO9v$u>> z;Ui+*6>BEOP=BA{*ay9w|7e>FKd;G;C}|#9V7vlD6@or6@&PHkQtu-&&7L5XJoYCY zf1WLX%@RcQ6vJ}+SbSn|`SJNe4~t9fbH3ebPqtw1F}sMk^|1 zzJ8`k#1E9Q6G7gvG>gIkGCw7@DU{ITwE3tg(xii2l_el`42J=1wN#*EH^%ZDf%oi2 zUpj#+7m4+iN0v{jF-F{5Z}EJo(ti1(2&O~doH}^+_(l!KJiRK)ex|e5M7 zqWgM(L96dBg9(M?m59O&Bzpxzmo7eVg9X?XWj(Wxx7=nG|?xVh>FOq-nLxW{+P`-XnO%`Do#%kgGXn!fP zMGtE?jRFC=;Bd6>FMWJ<@ROb@i0{(e4kev5f0;=8{49$r(_ICM-V-+NO zz)ch|C;36g@sDg&-k~>!>93r2q+K6bF4ANZ89SS)a?#L%x801^#o9uR_cmY1-nL^a9eMEgBYt%K zHASQq^a(~o19Eu`l0SI2Z%SaY-y*8w_PXs4h(Yms%9vUkGWWHwD;MCH^>>5OQoS`N z&~(SjH?LqnZ7r{AH@~s$#`1+CBfdcCjQh`5f@x-+da*ev&ybf1;Hf(^D|(@2U#?0G zCM#4?gEwEPi2HmHVx=^qg%rszvmY=1>%;7q*{>f>9gOs!b6|R|PNGRZV7u6N zezCaD5u#i7?Rz35Z-A|{RR0(^chI5 zRdAAS?B=Q5L{otgW06Mde$gG9^Bqu2gB&<0amlS{;-3*E9V3qtljkw39x|oJ9si*gLH{1hUu$ zE=?;`WUZNf^KVxRPti^MWVSf54rPZ0^LZf=LZ{V1rT}I?PA|ZEs>YnW3~Io2fqG9H3Vf}^a71T^;>JI^u;_5 zeuU%-7T`(J5S&)qvM1WA_@`$Z7)V5T$FaveBK{XrX;HS0pZ|Sz!;nrLINy3|i|QT& z_?+6zKpF~LWfr_NWa)6>qe-%NzWT(5Y(o*F!D>ple#ro|OJ=d0K|deV&wX(eHE3*A zf*Se}m@z3ued#SN=eh2`UEl6>*EZ>N?<|PD-{s_CUE8*-?L`Y1r10%#{pquqUdjyL z+b#&d^6SNJAzydWw}Fj7A4YKxlz#oNxrN!k{JhoxLhW9fKW`okP=E#_3L|SfnzWzA|A0 zv2O{Vz=MAFE@KbDKjl$W!jj_UP@Km$24yVcrHK0+&wA8W(0y#A(GY!Z*MD z-B3fx7WohP9x&uKmG(n)?-x6wVHqUVR_Hs%>!?A6zkBk@Y}%Z|`eprFo($%s$mHQlINZQzw(1<;k!9S`cK_$Y?fzB3 z`pV`4Qa8I<@nlVWn=@h~XK_xVj(khSsBOXxpUTNq8WvmLQ?OetsQGI)h2c@9;zZYr zO?ZhQXcQaLXZ6k>1V#DB*^+K)$;;-R?)PPy!4-lS6rLCX8>=>)M) z*(y(YPs#w+e%imxcNR|Wz-ly{!|cuWj)H3*bcCmA&tRN#-if%Ui61W>7FK9fn%+o($i>PUgG&rKZ@W(7k>PXk7g` zI9uGS6CooW{8{B)CCN?i+7j_<=a-Gz`Lxe|r!@@{B6!aY)P`bb(ApaunJ*Ola(>viP@G ziBq+Yhanu5PWieLPs2VNIKm(1)QANJRm8Ik8=WgY3Lc3Rha!bU5D^Q)1n3rT^TP*M>uY1hvUm| zU}g=Hprtsu_1u7WVW;|STVY`1S3MlJ_s*f0Cuf|*jUGfYdhai5#=yO1N(%YE*6Iv> zK)%A)?=TdexFU3?*ZASgkE0Gb8^Sny6ZK=^7!**y87DHO1Rh5sk5q?LjlO(7Tej?) zo-5aF=>J>XkL_*oZ%k8PIx|uO38YZZ-JT&bEn!^jHn&=&$fsWcAwNZHxqn_#$UTD@ zwSDPkW_8B`NjsgA`i5)l0V~5a5A)Uh13<$53qXc;k$TGsropD@)?6*!Zj%MwxORpD z{n?ox_HMeGw15ttcp2PK<&(T#Ld;12h33D81hk~PAwzD^5Cz_@PpRx;0w-91wc?e1 z$EGfdYuapQRg!Dwp6HZdytvG138SMoEqdpMde`+}G)$A<&P?` z^eO6Rl(^CyFE^4Qso9CZJcrRYN~hgg z8cxdp+r0xf0Ka+&>{*R0<}$J|DB1e--I7}=c+*BTG=Eml->VOAi1*&hbs9IS0-MKu zm+>&3QuB=q5OLil9UUxDBaX)hEFlIDGL3tJ&1^n49`rKVjpo4+rl!od%ye(&R%(E6 z{isf*Cri0jhcx3H^XAz2u`xIj4-Izkd9bRmWx8do9JwbiL4IRLZH_Ovx=Aa|%7dif zrS-gMDo$dnAMP>bJwrxVm{?e!)Lk0$yFUFVv1PBYv>N}fIfsJI72#%(V*bX7KQ?4BpcBp;qA|$*uU!{1x$4=##2A( zc3@xtq6kkIsxA%JLIA=qs^I+ZxTXoJPO_JfjTuD#E7b+Gu5?gEl6vKYU!P-e4u_Q8 zkK#W71Mzwv_&-XsjCAM!%xdJs)8U&)oyA&|Jul1dtgrTFhXNmWOyuXN?fxC~JWRUt&m%O@31=AA6Z>&ZIs^4-02UvKDt z)WA??+)Vw7cpZ~(q+dJ5hs8SYwP=M*?o6=ke2DqJj5Z}n)03>N_HU;&g>LwM4Xd}_ zTzWg=QMW=;$y&jYo6QiFf0s~s()T_6?;#bsj)osJ(hSF!TL^02bnF}^?^I`J!lE=J zu*&WTVUN6Qf;?ZoU{q|PB^p+}NaMrJW42zx(hs`I(}Paaa$odoAC#i;R0EPiq`B96 zw5E`>o_QvRtSsa68cUv&RF-D^ASd?mLY{VH{YnbG26xx`I#t9!6@9id8DvZQ8qHk< zw@%8K-DCg9{DcRf;N`Wo&=FPJn1N8D0H`YFWOTmkd8L6$43)im1g7KoZkgyg zzd8Hv?po;GkFWLv1_y30AHd-Lk12v23*20R`SjJ-doT(y!FpjnRffb+7|)hyl*gI> zfG5bgribZ6t*|((XX#(mKo{n1!)s5cj!dC5_)Cgj$;)W;gt%nRk@u=+;>gQQ%0;SU zP$$0qB&D4%88?uQv{PaY^JhMK%7gfaluI-Ep*l(c%K=t-H^Mcf=Zb&sUYsts$`*}puh9oe%RX$F< zkCsjUA$w#4TKmFXryKvYKV7)D{Uelfxd3MUEG-T2g_0ynEJQ zb$ghaiA1Ey?$gY1YRP)`h#DBMxz-5IPpc}0P-#r$lp3GnC{$nnf2*%=FMZJ$a&<_L z#Y3*xXkxp?;J_#{edR^Jln%JJGr5w}5{8Cjj)5MS9rInT}ZS z^G{Z;2JZ9PjB#2)2TnNIUb(uO_?T8sH#!}z4G(AMM9WT43^l? z$R+v-ruj&;R<9&*xvc^qnwf+OuJP!T(@7@MIkoWD5mGAz<^yz|k9w zy;9vNZJ5a}@7C0k?icHacR#P!f&k?CS2s|j&bGUE7zO_7_04L#eaAm8>@PIhIadOS zZuy=3X=JTp`sKxcDmUlt1+}Y~qEk2yl(|gI+HP)8YP$JYNV5L=n6T1dSBlJkv#8kg z3kqok7pvi!n8*>3d-h7DGadnQ2@cfBg#5nP1irsRXAxL_l&@FMbdZr6`O@>HEOH2Z zX0wDs%1U;28H79M?<@FY$Fj-p_W;W>eT3enE;$5xd4&Xb=iXIRrA&q#xcM7#Uu_anF)X~^kt7bw?g1ix zDn@d&;({Y;ytp@_de)?u0T3YZ>VOb$ur)%ZYS^-$oF?Q9H4%;2!th8O;c6}`vOaQ& zsbj%90LxL4IP#-v+29rs3(A>8ajJ7kK|>h&nGYB?&Kcz^+mz!9*@Yw}MQwe3-W0_s zAd3bb|L|{4$y#gjvR090)W11-vs_tQtC>OKe9A_oe8iqoN@v<9FVczl{o_ZDK1VZZ zYA{CP*hP7-i!FYJw(_v~CSNYAX$?9;?_S;aY-VNth-BH)zTDl!TwNma`-c^vf`e=3_MW#R}t?0@cQi;>@sEO0 ziSh(!>tJ{=?&A_L&(6k>nHpt8^>w^fbJlqAWI%AN2h_B~Lh8|Oi13Av*1I*OfPeWy zb<0>ZYDf0;wC~TpERoS@%Gbcowf<}S$HV;2_}7At{-#~)qcXb`b!m=Hu8%0E>I-9^ zt2LmwVwLpFNDiPzz6Yp42@IVMMD}#8Tb@yNygc!tgi^rZ2Cu2;U!Bi4Uy)XaM@+o? zm*9dr|3a!J0FERx7sNxe59mVD46;yk1p5aakfwdrNwY(&GSP=rwxc!8uB9jW(KyF> znH2(Y@!{;%>g#fKuWgk2qcq;bkd#7+2In8d%4Zfk5(X^(w0T&sd`*cK1;-(E`rNXQ zl~60N%wl~Y!qddhGAuQv&`$sjr57M$R(X(SJ-y{xX8UB)ecOl!u{X}E(hN24>-;n^ zVN~LRh~Af!cy+J7=3X+p`K+?hjA)E%n<@VL9}~5E%MfHx$NUk9?_gk0&Z+UyW{bp! zkn0?HA#UEw(!+GD7s~loBX9XaHP1Q4l+yUslpq*4^o9reB*8NEDJC3Q$#s6E3#xH- zPCm4hqsG;x&Sbbc_a{4(a2)M&L>$j>Zg5_tnuzl<)AE+saYi(-mZ029t}@#0pvraF z`T@&&XsyA`M#FpvS#d45YV;LMSCEq$+r4nP1FC2yF)cr0sb4ew%fyWbi01v1tcj%i zt*qPM-~Ds;;&1=&KR|r{;g{KIr9Z8Dn7Z|Apd~MFb>;B#var@SaKM~GJ4irqf&ovo zr|Ls3r3o*}KBb=l@*Kw5D;55BO0!#I71ooZ!C+IHVqA9IEjWU-MPA{3GMl0|a9`ge zUPGiVlE*R>^Rmg{vl`pRWGBgyK7 zilZK@Qq)6a8dU=6>GaH{yBc0eiKNtTvsK1{F``fh?-!pmZNMV3eh!aRm{k+uOUn*{B*!JIPR<^<|8{DkT$S6U*uWdba*o8w#&^*2v|12vNt5OTEw`UFjbETzLi+UG{Emc%aD6hi7_uq69LIFsj9l8ucd}7+$uwH3 z628~sd||LPE7Tw_h0GHXB-;70HwRrWoi2%&#z?j&u#`7VC6v+ZppKP1>)WY&{k=LE75%OhF}SZ`TqRatr~CO-=?3rT8v zjsEX3u~X=_tA@wr;j*F`1lYKS0A>zU>H4K8)}%P+ z6tYwnHt)u?cx6}!k`*xk(X2#Kbh?rMv8bY_zAYPTK}8apcnj}cmjdj$gn_#^a!LA> zBzi5abgiuMGxn>(X^EX=wn%4d4H{}@qYzf~pE%sqsOX@3a!%PoOj8?mxfeB=PxqIF$ z9o5HqpIjwxk_4e$+iO*ZqNOXK$6pl8T1>1Mx>xGxCNh1Rf%SZOq(v=Gi4*(7bE2Ys z6+PLx+ap)(d79J~f@OZHD5EH83CF*~0YwT`!z*OaQxRY59doJus#2z*jj^h&FlwsT zb@rqU;kxHm{-jX&9h`MXWmp{O{R-H64Jk8foGKO8$UV{Jr6ivW_r45Qm*b-`D!pDu zvxl_<(;7IC7iHE?H*y%lQ5dw}2cusjl3>bW)8_JCZ|->WK;&q;nLLxMt9hn?=!ZR1 z)&EfYBK~(*&Ob2W+Z_EBGcWEwi91C~@9gEGr?NcAd0YJiTaYC zwZ+>pCYPVs@_Rp4+Y@=ZQs6av(%E@hjA4#i&du;AFVunP9!hmbDf3V{hLWI6kL**! zqi)RGM~*MLS>uz@$=(?ozY&G$^)344;TqH(O9X^>xxm}L*ComQCVbI{NtPvD-SN*j z8w{O7i@QxdFYv@dgspK%9W-OAF}&hb`)fg0V*rHYO@cHA>N%`IGZNA@j#~y9{Kw_? z*kIY6d1?iMV z@3`3K+0i@-tY5vcH$b6%?MVwgXr^T&MQ@jYpS`7HDS)Y^$8t5InbYU!yx#p%Ys!Y< ze_)8V+EK90DR$wDQuU_n4LoFZmjQNwFrhF7QR9}BS{RJ?*)y=9n(&vCWT$n0E;yzdRYS<+~d zr+Qr$G?e=r19wrG>Er(s6U2j90T%Vn)zLIibHREIaC1gEHM8Icn&3VJ%P{*&O9OJd z&$jzuJ6qcijCv2HmNntqiGum4PZY1F0PqlkUV=G(_4u;Xb|$zos7rYnYb0FF%Qf(& zDZt5x@cko{Gb{*@L1`5@0Wd4CXtN2!K)$7n;RWtmQjK!W{cU8`&(6+RBi#)DY94i9W8(AG$hh5>T7 z)H`C>z!v^ocUEG^IvqAT4VpoR5x|tLGDsUt!^7tcROZ^8ZmXKYq?RqrNq?WLq;y?eEe_Vw=rne=3lA=W+DEx%#tjmL{PYrf5U7!y=p{h=y} zxW(8#k;jbF;k428{O^`&s<>j-Dtu^}^pNVTu*ZSkeHobwRgJzj-{9s5T|V>Qm>}D0zl10l z-U@T&@)C5rCfm*2HZx+$oQR?96g107C*@bHfTe8uUS;}qfy4Wg0lmGnboD$QYUJY1 z$-gIpNAbr~@Kj+;bSpDKJW@~T^}}Mx&P%PObl1f#4fcFiqad6Ba$i!=DbC)eRU0Hq_X76JHmC0ns+QPmf9i#AZX8 zf@0anF4ugX7e*NHUA53gK%e4pjCUE~V3wto_-*(i)l{ue#SW&GND;mPs9Ldxm9#N5 z$PqfR`kmLa6##%$6&{aq6Od6ny+%%UOD9wNk6U*m)TtyT%=V zjZgF(>h^ifaCWgx1>St)liB}iIk)pwRyX#I6>;4^Gh-jY*t6si*0ml_GChMfAMj+`_y@tu#EBAoYlIp-6fQwX2sF%Is$Bby?QKgVgRqc*VqRSU$^PrM#z17akNvoYPwIxO>Pu8$}UN%9- zc`Y3=YqM-rLL+C%O(pa-midSk1 z3EG|wH`kjHLNu>Y6EL7Ql z+=Fm&K$)-uq@?x9C|}&Z6Q_%ro~-?a+{*b0r}Ci=nbApqzwP(ShcYCs(l*}*vUJt4 z+R81n#K_Q)(lCwbb{Yjdm3hsa`#}X+a@V6biTE~T7M57%`0~fTXp-#@q)=>JMl#2A zoSBe3st=?ETN}2d+9P5*{SGSlZ{(Yk6VCOh6gd=K8_TV}zE_xI=ATnaI=`n{w|sgZ zQ+WB=Optkt!7XJxdF-5yinr@XT}sN>dw2eYXv09%|8!Sf_a}at(8>?cFkD7B4&7_R zRw+%5S}SeVO>6!7U%$*gy!(m$aVcZM8~(+}jO4@D+d#JM%-Se7Z^Y6Yf(vl4ze`!0ou>#OtuJjW#D_4fYpve8z- zl?nPSZgw7P=V?R#SnWPAKYw#~VWX$aM`<9Y7r@-ySce2AFikljDqMKEKhHSf@meX) zy(e46g9m9XcCI;3$u|u<)Y9QsRbPGe^f=3($8!s@8)TuNGHny@`0V4}G{e$q7wDzF zloFo;G}QIg5|5Z>g__-MUld+oUiR@IVEsEqJCtEVWK?&)BP^Cun%=^cfi{owD&_NB za@Z%iG~Dun{xYS?Zx>rF!(~u+6|Pz*$j19X$eC8B1#sJw;&L_BN6GB;oTC)%yUt`I z(bi42vF>G&a{Rj4Nxip)n$wdcD#xV_OepcY+eF+JMy~9^AhhzHm(i4|j^~!7ijiLv ziaXHuk}ZY?9C$>uwVL6>iq5j-YQA{<3YFIQGS-HY`yfnZS})#EYjD6X~~CAL}hp{mQy1s;`Xv1H~x|m)3ByyJCATo)ZLC3B-lZvLwGe0i_?4LYFn_RxVAr+ zmr6p0+MSw@IDSB^t9gm;pJ%05pw`vJQk_~Z5F@v}M*K_UsRX&lE4}J*$DDY=8r1c% z-wcIn%|G2e=12^SO|J(tfoYLeRk5LOVjB)^z5YqXoOa>k87h#kl2jmdk>9>07rt1ocg8B}#tcreE&CjIgZ8vzV@F2a3uc%^w~4VMoLH9I z@&pZr)QAqL2ehN_h7twP&)~=;Yat2YttAX)1pHFBk-YSrJzi zcP7*$<}0wLs-_HFpaH)~>B)#EYHlkj>iyWMG&3S6WzypOYPsf!MJ93$@zDI=d+PWF zmA*i}OG&8;a3@{>9~7&Tp+?-T#CCCXIaJ=38L8>rge0j=sTxc<-!l9DYW{7#x)DVr z?Iesa#Ygh~xr3Xm77n#XZk2H~k%2+bCK~Z`Bh(~F&d#eUU)apf8za)zVqu%oNUf6G z`}YdY*B3&NHm3!Uz?uhMvsW0XEo!LEJH~9z7&o#oXA@X``B^huM8@*@{(k~3PrgB@ zA?)k=Jx0bv8(IlVeZxeg4Q$+%b(JgYmw>Uyfs@Svl6i5;FuMVrU_Re3 zcAqsA=V@V)4wo>YmL~kwlFG;kxX7j=c`i)bgbA}~O28%hn#4O*j?*Q#^gAMgtDFAx zEgL{!jcGT_xlI9L#GB`k+axVcqoQ|6>};`tO_NjBumk&} zgLrNG8IIS23`g`5%g+nuC$esB$GU`Ohfe6udz8nnsOnKVxBMy)F5#$Oe*1Oy@y8GU zZ9i%PEQ$ASoT^5&gyUTRCuzpm@;PGK`t2(}69v4d@%+M8C=w*^ZC(jd#6MQMkNCI` ztDFBM%6MUd_~ngn0wZBqH7ZJFE8$<<7PtdY&@pGG=}+%PO} zxMvT8;bk`)FLT?SUPo7B=bb>tpsSCzJujzr=5L%DCJ!SpMH2kSns%L}e>}+^k@pUk z5kXM$iNRZJG2#CE7YFHd@)$ zshg?Oo%J)i_#VykindDK`vshZ+B z3rC2jV(CbhPZTGbb5wE>a>V>sH1=PA%ry4-4XFjk$n$6Ih68&&aW4}3es0`}?fdpt zw-2@F!0-tu(y*N{MrBrLRnqPp455d@!Ds~dUM}|k`y&w%Lm=-9~%8{GHF?e`>MdsNE`p*AkpqxGV;Gn6{1XSH~(EI=|Y}*U+h`^HR87= z9F0#9VAr#y=5XC}?*m(cnN$D+VMzheMa(@(RlS^3k{~VnC?k-X(Lh(`hMw!9SJjhd zk4<4;qG`?mngB&-?fb(sWFZ-GuvN;9x{fr#=)QfrK{BqqRb49}1Yz(ENei1K`mnjR zea`rPyTz@YgZHI6OMW#g47*Ks>1At!3Ic3R=A-b6&fWt%D|3&0#z> zA(52(BCThZZ@JjSgN1bnMQ^r`xJn9{)6n*y9U7oLP!OMxG1L*6ft>Y`m60`SvNKv{ z$_YfQ@G#9~3+L|LRVuRA#>RHMFpcYLnY7!x;S*DzJt;t}%+RVu^LMX6^E%xQrQnBM zZrRnGMyHR!3BN2W+R}Q>rufC(a%BpKdZQ0t7nkd&GnIa}Yai1v&utV{h|qGQPBQn) zyuP1*HZ4pRAH_eC=W$HRKL#-+($yY5-mBeu1=^Bi9-m^dsq}iAy~7nW&xG;hnm~m7 ze>Z=>G-8IbZZa$DH>3$z;A%FA9hl>8iU1-F!6sP^dq%TP{DVp?V8Y$vQ8a~na!Qkg zL)yo2?a-q}$d?5l>mB+iuZQjsMS8EFd}6swm+yaOe{9-HQ+$5nXbMZ46UZ#{ljdm{>zxCz3B=2-O8$QL z5*ZJ1^euqHX2HlfI10_ejqubx|S91@dWVp+u_>Fl}HAP)f<(jGP$$h@L$ z6jWeTs3aVgvyf|cdf(Hv1lWHKn|Nz_M3bUAiXT8%=!aB8(%4Tn=WokiL6+#>q&QhY zla1(ej?Otd#y*>Mpwogx4*v&qeZ&8F<1E{15s4SCZTFA8-tpPDUok@hfj05tTq}~4nfWCIok8-0P9lz*OjGC^lbMHHzmfs!tx@Fq_gV68wpK5A+;J=W?mLec*}C; zusZ3Cic1?#|0~pH?sUJAaA@OshvQ=Z^M@)#6oz3vQkfhbPq`*Z@bdWMwYj-tOqexGvkDcRw&ngaXM6%u&|6cFNB z3TJ6$e0m3=iyI^K8-DQ5!)XZI=!NQ)e9#f4sRXwgp;EIEg-<_DWL_aA%5unMc@LIr zcFCU~=Oo-n&Mz4kph`oY;sQP7c)Z+ZzVj(cJABx(}xCZd%s*` zhW>@R6vlHAoO285Y-GM~(bRDm$)y^Y=B;};Q%MzepFz8zqxsEZyPI#WXa8$9&a99; zhli_UB?|4Mv|&#yh&hl=~{@WhfQp9OTub z;LJY7*o*c%|Ge2m?ykDG*6lhn6~arODA!%5QM%<>5NR))VkYR7nV@SBquC%wT72rW zs_p*%TRw04;h6F=tSK=cB$DdaVKlYJd))T5w3@I$C4_cj^=V(DMo}{6_y{LrZw++it5}bMY*Zlu?om_a6Bw;!GmgXtl z5my^#(kInuf;&h>GY=I<=vJ!i=?^M=WKx(k_kg5ZnMy_SR8q#A{PD=xZYP5B;zN3Q zAU%@}mn!I6yBLqsIjdlfG~Ukxt&|suD6M+d#;^DU3c$6!U#uJ*5h=PE5G5DeZ5Vxn z0dhru!W`DMOZLGklej_(iw%yHK?|{^t88g$`ZYPazTk5~#S}?1z8^rvfL!{=UT`9A zV`3wa;dWnx>zzVd3%EUaBo=JsA#S#M!V|M{PM6+>pjVvE2z5D55`PFarEF%Yx7oMF z>59mdyPR4lJ!{q5ushV-keG}A>?ga$=C%hMk+x8Z-M{=`_2VbNiZ@=&li)hBCL4iN zgloOeF+;k7i-0u!*X7PBRL@lbj?a(Sqkk0^r4ViPDKN2 z9=JUu{_$%4bhRdkRJ`v!J4p8dBO$Rw@*4I!GW2~k6PQCG+;H`sqq8ovhpVl??D5gK zi+V12y<%F5p-F<4?$DR8it3_L2Em3u<}k+?-}>u+k(yxTyG1?4tR1I)L}_K*gU?@| zkR|w#vuhKB9gAZV7FA~WIIWF$jI3~8>f%i#@2QuAo#qL2_-b&&eSY9Akr=x0^DP%j zYrqoqYc-y{tO-LPZNyDUx@w6V{&*uOWKt|Z*Hh#}wk?!#+T${Q|1ysWmFb_Lh_8i( z?Wy(i!vrVZr{2;KhK*xcJ}X&lZvX1WitSzE_D{e4^PfL{%p=HooK8&^UwM%^skvF| zL$in@g}vC_P8q^KmP4+A7&R9 zc|yI4bVC?}vdX&JHUcAqvHzUbucfzIQ2Bph*=t$8(CgZfxG^W}-rZmlxi1YNtBTcp zW&PvMB1=CmByB=>LeVOATuW^C@iF=farAgkg}0f*8q&yjkN41Cwx88YVpKeXSQD(Q zA4#a)_vqUO#+^9_f&@WEnMFg2i-%ADvO3gu1?_6y{i8ByBUq=GTQkx!R?mS0u_TSP zkUpSS2K|$REeq+*9QI`>oUo?;b!Hn`HX}2nYed!9EC&b9H_Q1v7=6ctBdeKm(!fRw zhD$g$z$17K8MnC%Tg&j5q1|I=Ml|x94YwmwrSG_@-AlHfOt3oSNO{y?xa?v7SDObImTe%opTncs=8tpZJ1hjz8EV0h)FQ(K6B2*ar`d zWUs;3g1$RrEBO{$N#Fz7t_A$x2i&3k*N4?Tz!j93>*vWB@1c0U{C3_=ve1qa z6;$kc#M-zfy{QLnB7CjjtRX5+MB!p2;tda8C0g~{O-^#-W36!|6$6X7=(L-WTklz9e8jFzHZ*yc9(8(^>Tk)O>{d%Dno$y%H{?CxUFT(cKk#M@44}`| z>tj~P7>4`(5aYNz;5j((*8&`mERkiZ7^@fF|NZHS<8fh4oFM!a$nIu+w`v7#o~5bm zQi2zI)-FOYw6E@@O0z@k*Z&7OQ=T~t_9gx5X(k>Pn!rtvHzwS+Ijao0SVAF2f?SeY% zrD$Gd>y3D@&xqr#vwK{zm-?%ICo#!3ue= zWLmm%C!{Xp^uZe81r_>4;?^xAt^TrGBR42ABz(BDHfr@{V{Tm|sf6Xtu_E8n7eYGR zDpVi&uH=i5kO_G^d%Lsmit;DXsmX~vn%?4^Ith$U(&>o&-EMpKo##7*zC&H|U7ugC zzp}(d5-^GoU&U&b`Sb1SR_4x$e^R}sk851`AVZ8p@a_c8X|7#~jK$l&UI644FA{^q{MfEw?nXrv1DBC7(7qEk|41^G?nDG(uhvNNDHyiYdr)9RK73Va+ zhCj`3zRJLw9uhk2oA%m}i}dCv@x!r8O1;SvgZi0buw!HjyZ3LEJRM6g|}e@ z56l=~vCudGb3vm0?LsYs5qW&!h?8NQiAvQ{|L$+IUq8P4`@5fh{P^y-UpaoIfOPu4 z50lWMJMHnMQs{JJlX&%EkA_nIf)sx@#~HF#eK?VKsvqd;o4~at6B4ky`yHen?)G7G z2}YEH@Dbe|Hwbu;0xhC$WL9H((j>)lwU##cxXnm3tehR#5sQi*IA5w{&*K^QJeA5f zBd)(@oEJn<@dpaDK2&jwQyH|bP+PTZK1P;#x;uQ}Ee)IT(|vf_F%z8}U;Yr;=qvm? zpMV_KJygKKv=3xYt#v}sF6@-@s#8voCl1IF3oMkkyansK= z&_zA`1uy^U%WZ0>pZHRe3q{yHxlMZ+5gLe3(Lw&qj zSfs9(GKcw~yAX;&fSH&-R|AR`cLE#Z3nsj}EmXBF(XJZO++Lzi{8il3UjAzDH*jd6 zeSX8@Ssm!KH-SBp-0Uy2A20sv!|WGqCvT#I&xCZnS!LFzvW9-h9S+u(>MlQ%h`V9ekcCUt`&8WuVPI_E&SHN^##Oyp>CM#wVEnX;h6j)K4|iRay{`s4!K#5kL-6xp$tJtC zn&_U+8>>;SnoXUflOc9L2aLBlLo_zX7}8 zaw*kYljE<%QtuLegrsl_zMtoHJAzU%rz6@z^vTP-bW|GepEVY|g?1X0jh`NA$9}12 zPJCN=# zVaImj%bfg#&M+oaJmD!*qq?QIbW#;wXyL$?+#W^PaxJZ0kC(0Y?OB?HIo@If7Eg=y zjk{ZJ`$W10II&QNsJp*o%cW<~_-`r)Id!NiLMN4;3NosgdqkL;bDSVHP5+_VVo@1B z%g2wj{3h!Zg$vC;!0PAl?8KK(L%E8zO5f9R4d`Ao+g+N+N1#%!O=;TuGd*UD-A=Pp z7_DG|9KNMBFv?&pQffrDNs^%-Ht3pT74N5vxTs$bIb-1|^a z1d@92G*_U9cylB)shA$5xk*pPI!I%z3s|3O@`~ZFAb;dy8t;Foe!FiBg~!0S7*Du9 zb=T-tI(*z^B#iMVY`#sh1#K8p=b6kZNWOlBMBX> zB4Sul-8Rz2PB7L_hQ9yt{p>?xA>rqIqk@+f$a#I*efEtC_B0#IOx&B5v{kZuZxixI zUL`Bj=Zj=*Zr+2g4jP` zx+Sgs!A{2LF8gG|Kk-i9LP8UcP4d#$iRRT`(b}&B$I-Ngueh&^@2F;9->i4GxKj`< zQuFpHy-~=sFNzi%%b9RZso6P0QjC_NEJ@tz23H%ztZzn;a%UN39M!!(YkKG7<-KYL z_6MFo&-bGWeRSNgfgYgqzW;<>x^ZL{^|g_a<(uAY;e9ew!aCRp{hr&wh)iiu-YN%( z3@C(#ab>$Z?527@-H`U#i`(CWeP;>mqbMtu$sQ(bgPJ|YpF#q-$g&4BrJZfFxCfx$ z%`NXDf$J@N;u`3qRba$Yz(iu|Cm2ax^)~Ct6L$BVg7pUN9v%mcxb6u{F`Q6hF)FdT z$EbjEatlPJqV1^|>sk_edc4auuo6v#N@C0|7`^0;Cq(W>g-fkd*O7 zZHng|$=6o1?{Oq6C7a`fE76qcY@)#yPM=kr?ZZ$IfqG6e){T~YO&Akz-K|t1L%EiCkT(=rG3jxQb4yvyqj@ zYEueA-}hFeGa5VK0f>CUI3ff_?G{f46KVZfR!81j(p9uFp1MP2Jo{{(F#RxxPzW~- zf9P1=5LJ@OFm(c1PgUFAXs#8*XPfD9>E`DgqCKx+sTpkw2ei8~alRQk}$UF^PT(BZ|#k03u-ql(&l<5?BSj#yiYn^Ps z@x2Nur(PR?Wip`Ea)~!Sad@VAHqK_%4_R)YS5JO1p9RbOPehP8CnqbLg2u7@QANT^ z*EfGUcr>W{?G)ADND>IL|{QjHA+jfB?vqOO1-0Z??gEbM5Wn z+~v$RD{BS~yh_9cP7gob?v)yrOW25qmB!>x7TnSD7gY}lp0TZM)WT`0DpVYDLlb<;B1Ugo|w z4XFp%ikOwVOyY5R<^z+TYyklv>zw+Qg27qe%1>0->^R7PjdG7L(vxMDG&eBClyAWY zqF!xu3YdX|-e5cplObCx2Z3_wkET}AH=8B#ZSYS(@VfniY?JNZd_;W99LL zIx{3XzTv%v)O<`ujD_F+U|pgn=Xm?r*+QEjm{_+cpIM4)3^;`S6=8-3U09wt>tj9q zS(*^S*G={KTcs9}JgY8xL@}@uFrFc9@+1ho*s0sTE~&qr9n~h*a#aa zPan+Us##WK0FV9+Fj%UaOPfEiPOQ3a2fgLZ^ct(jwG40$49qvqFSj;gjmw%MqY+py zpDtZviVd2)y^wY#MrkIV)m|c>TMK$Wl?L!BaNWkb>$jjJ1DPWtx%0(M*Tmn6Zj@P596rslKQ)}r9EsGwT& zY?5w?C6d3n81sE_jvMh?)7%NgiMaM$+qg>qiXfFFtWx2q{o}nlzxkbRJ{xD`ga577 zUbHm=ha?y!pcw&NxhbS+=>e%{uXdj|k6Wg=ZUG=xNmF=?PzfD!n#`)R6*y$^tiP&O z{l6%>HP0_&c$zus7OQKu=YRJ|4P?3m5um?4Yv&a&V`-_z7%lt3oXL!}cmSwQDyjZ! z1Dk_OX*lHB+q75!ny(EItvVC@dBPb+4Ao2zCm98GRqM?ok}o5X-$9pN`8^`ZXoq5+B2g4YRwp#U4RI=hC4TABcEEaQ6gJu@0(GRK@D=p zn`efd2(r~FRS8{5sg9nZ;TK@y6CAWQZ_NrQi`vd+2kZ=+;4#H9Na(a&g-4n@J`EvcDG_CC`DmjbF6)@Mx?SCm=V7w z6**BKOf&10QQdEFlsKnn`H9y%>KBt^{J9fU4!us_OKBFq`yrG$r^c}x5~U%gA2VbA z?2aLSL%kwZfVf_ggBvSxFp6 z%r|&O4)cJ6gbJma3Th_@MzfPUAQvf!;-YZf7XLEhS-=!Y91=sHbJWtJlDOHl4s1b8@d;8D z!QNvM{O)9ggUlRE#@f|A`x4Q-w7!~aS^@S~-Tsn>DO6df-Cttq#3YfFl})rLj5#@M1ADxf<-p^3r=u{#ZiBk2 zhS}h-l1wS$$q_kUO3~2JUpUA*Fg7RZfiZkIvU*Ys1@h~~4INPrR@t}i3-g(KrO~cZ zS(R8W*Wj--q-JI#9+wdtj9^>>yNmhi_I~$`6STOsdR=P^u>-hUJv+fkz4E3Rl5q9D zUT70lO9mA+_H;MX^4?=W^(p(kMC+ZhVX8R}t*;K(IZ$02e8sg{ii5~Nk09t(I4&vl+!WEw>VpN?mv@eZ#%Xo-&9 zCswN;)OL%`_E7q%UchRZpnk%i>FrE*gx(MrnX^!C)RV-0oc(k5{{Q^;Ye-ZM=U-Yk zGA@M0!k(Y3#lLl;Rtok6qq7bNAW$Bhr*GRMm^jZS^0I246|@ATjrg~B7_ld5DMJpJ z$D)QeS~q$}jyX85Ffbk#;jw}?nYO-JQI7z-zW)ipQimzgLOOSlyAs~Flu2cLS}jej zlRaVfJy0V9PYK*~zMzQ*mOp@2OFxEjIEnKe;S)$)S$a9O1ExtoC79Jq;vBl?oExbx zq@|E$id;+He3MOe6BF(f$YJ+4nu|YsV{0jFGY;$Yv>nH#%wDVx@c3&c8+YA2#9^Jw z=exI7RBL4e8sbqyHKpnARR@uW8PS&<_7{cOrG(4q>LWh|oON-{sT(=sga{ub{*l`( z;xAlkrDB6ULo0^j15B?{^*_4?li)~19A>oyYBdIZK!H@aZQ0&t9z~^#;o|guDkW;p zgEaroPal4TgP{6|7YD?3c)Uq&JDqe+ZM7bYla`Edj(x(OM>H+zXnc<6Op$MkP{HQ3 z&}$mY$|lKfcbkXBr>bi1x-KiE|qm3O)MEWhx5eSKQ4D2-?!h?lAU-nXAD5JSSZYc-15v|jXX;NlJlo{ zp9bg}Iu%`=ZJfWqBtI2XRk}IDo18QBbLzO0v@%>Xh3|Z*Fkf$`=Vcvw9`j5;XvyR) z?5`B$)WH|aTt8f-;45F+C@DhME#c3TLiZYYVbykE^F6)5m+vlKYX-wdYxhCTeZK4U zl^CXJKuhXOGh7vOGEy?f51|kV^&HdDIFW|L%E*Y>Y9IFzkqP2!&jAzWUp`aVyjeZO zaMfB7#?G`9&8KeNTbdtc{F0 z|5vi2!6^9$42<{LmO?{+yjgAMS2o~jKSF7UiKTAe>W=^Z&f@;A-*C0BN*eSISbI#wjowL!*jeur8ilt{8Rk7qY$x6{Hz^;^?PoUu`M*!Bj zO*x&q{ZTFR2M}8+n#M(@Y?N^XQ#dW*vrZatpruEq<9?eg!8J_hhjSMw4^MvL93bX9 zuXbW9(R2n~;qEc9z1vwSk#fcMc~qg%(u;$X$@ZQ~EDvNheoDqtKRXCR#Br<t78Jad13)N%g6eHz#CbnQ;qGbtk% znTmze;#=4Tb^)go&@-*W(*=J~!u;zCRD~HR-5CmqRJh&1*D~}8(~Q-o`k<@2z`ada zh47X0AcjX;NW3L1X~vns0>ngCwdM$W3W4Wu7^~b@A1dQvl8&Ty@7~CS5C=KfuQDg# zULoojqG0stA4e@fBl94JjWjls>nzA4#`9GrQ7raPt_o^QGW_(gdAtW6NCgO8WB33Y zGU=8rbIXlU=B4XiB8LfiR@d`1UMCYd`VwV=mmPbJHoU3WT(Lu2;aAyuSO_5xOkqO1J2glnVgV+&J@dCjeM2|xc7_(qMWbpj5Jl8S(@2C1H_UoIA1+M zZX`;WuPvUj*Bo;7<8e+z(oJe};%uJynIs`{pAR9!J3<|=MiZ9g_kQ*@5S;Ld-_m^Y zL@>;^X2o0LQ!Mw+`ZqOQZWh3?%2yu3w>IEJLztnAbwL%N)59`e8-cfPkr#fq{5D&^ zw|Ww@|9o_?%|rS7?ar2+UlMMiAOIO4=1+((TeAlPP)t1ddq4)?O37LAd zaDkP_nNfhtM@?NQaS!egqX$`@_IvxD6Na}nDtS|r>B$aUtQpUWP1a3wS+dSPz)#AT zANSmfy|%6^5&aBslSvVe>SuI10$0P4DeKJ(B7OWfc&k;W^sL;A+ZbMm1uGEhkk}Ri zs*F3>ne>Jp|5`gz`18F+s2GBkPqi`QyploUUV6mew2oGlB5Fii0KaR#;aN2SLqr{e zi@O7x+c8`V-))@w*tg5>6`}C;#zF`LG|#Q!mN~aB>#ypmJzp|`MC0&veH9`)G681| z1?CO15jzg_84;BhhUL1pjn>0pbG18Ny_;K+E9bD_jex3kRH54nOx3X@+?{S zc&~78zWM}n-|XG57gwI&H()mOz5$@kY4xW;=n?ui?wT>f{2BwluGJ};Nga?{;tpLotyPfp)>fGS7 z-X5#yQ<~Dwc(FQE^({d8@^O7bqGcYH*|tTd?|8>2;o6qi@%#+-KhFIxlUSzkaKA~O zRk+Tw0#q{b3;kx*ht&-uS@?&~&&!2(sg8tmuOC!s7M755Br6A9qb0Oz9g3NeEI)n8 zPCEzJqVWu9P3DC+{yLqKSi@?nQKSpszKy`QA)MCW@m&4f^;<{TY4gY9noWI%Y|REZ z8$%+;A>!}N5tVspx^DR{o<21(d!rV)1yXJRu3#Kg7A+DNt#dp7RMiJO-)JkElty;Z zrd^wL_5e6dd2k-9p0nc)VU?Vo(gVh zNRs@v@qx&Y@?RB>_MNDau|sR^wY=xem|QtV0-u3Z!+bVo$YAFXdeQ{snvl&Mg2A3 z*|JG3ttI&BM+S~^`dbrt9CM`Ufr?=t%ZMJANQ*pLD$I{kAVww_qCU3rl|$V!xrUA{ z$^dih;)D0cYg+uU-Q0n3VlAi+UqdfP*BpWY?+l8-!4Y_XF{atcH(S|BD_mmI=(3d^ zD=rs;=pq%K%sQlo+r6+eNj8bS3iULfr(sJVi=uZ+YhmIQ0C>L44ldRwP^migK_eYu zv{49!&FUL?&Aajeje$N1&34K}d#DM21-Iiutkl-4?l0cb@T@k+j-{Jle=S~cKk#jR zQM|DiK!QV2me1H>j<}RG`>w~$Z6bS(bZqSu#<5!9goQq`0(DF)=>A69maTaM&jlu< z0WtG;!}b~;It-t^VPMp z_W@5;)>aSK99r#L@c8wrjhQ4myBKUzgl5d@n5=L6)1&AcizV>xVdaWg@p3?@!(2f` z4jJ7jMA6~tGXc$MX+u5YrBBf5X8ZFM_>2^uEYE%Ob$9=xrgO4vWCPcLsE0f!0LWiA zfLlthT8)<+-^X{p@J}Wv#tBVBLA-wy<@QdSNfpEU1%9Dp$viw^U, - "specifics" : , - "cutoff" : , - "runlength" : , - "seed" : - } - config: a mapping from parameter name to parameter value - Returns: - A command call list to execute the target algorithm. - """ - - ext_script = self.args.cssc_script - if not os.path.isfile(ext_script): - self._ta_status = "ABORT" - self._ta_misc = "cssc script is missing - should have been at %s." % (ext_script) - self._exit_code = 1 - sys.exit(1) - - # loaded_script = importlib.load_source("cssc", ext_script - loaded_script = importlib.machinery.SourceFileLoader("cssc", ext_script).load_module() - - cmd = loaded_script.get_command_line_cmd(runargs, config) - - # remember instance and cmd to verify the result later on - self._instance = runargs["instance"] - self.__cmd = cmd - - return cmd - - def save_failed_cmd(self): - # save command line call - failed_file = os.path.join(self._tmp_dir, self._FAILED_FILE) - with open(failed_file, "a") as fp: - fp.write(self.__cmd + "\n") - fp.flush() - - def process_results(self, filepointer, exit_code): - """ - Parse a results file to extract the run's status (SUCCESS/CRASHED/etc) and other optional results. - - Args: - filepointer: a pointer to the file containing the solver execution standard out. - exit_code : exit code of target algorithm - Returns: - A map containing the standard AClib run results. The current standard result map as of AClib 2.06 is: - { - "status" : <"SAT"/"UNSAT"/"TIMEOUT"/"CRASHED"/"ABORT">, - "runtime" : , - "quality" : , - "misc" : - } - ATTENTION: The return values will overwrite the measured results of the runsolver (if runsolver was used). - """ - self.print_d("reading solver results from %s" % (filepointer.name)) - data = str(filepointer.read()) - resultMap = {} - - # Make sure self._specific has an entry - try: - self._set_true_solubility() - except ValueError: - resultMap["status"] = "ABORT" - resultMap[ - "misc" - ] = f"""SCENARIO BUG: Solubility of instance {self._instance} specified in both, - instance specifics and true solubility file, but with different values""" - return resultMap - - print("INFO: True solubility look-up yielded '%s'" % self._specifics) - - if self._ta_status == "TIMEOUT": - resultMap["status"] = "TIMEOUT" - resultMap["misc"] = "Runsolver returned TIMEOUT; disregard the rest of the output" - return resultMap - - if re.search("s SATISFIABLE", data): - # Solver returned "SATISFIABLE", trying to verify this - resultMap["status"] = "SAT" - - if not self.args.sat_checker: - resultMap["misc"] = "SAT checker was not given; could not verify SAT" - elif not os.path.isfile(self.args.sat_checker): - resultMap["misc"] = "have not found %s; could not verify SAT" % (self.args.sat_checker) - else: - sat_checked = self._verify_SAT(filepointer) - if sat_checked: - if self._specifics in ("UNSATISFIABLE", "20"): - # Solver managed to solve unsatisfiable instance - resultMap["status"] = "ABORT" - resultMap[ - "misc" - ] = f"""SCENARIO BUG: True solubility of instance {self._instance} was supposed - to be UNSATISFIABLE, but verifiably solved the instance as SATISFIABLE - """ - self.save_failed_cmd() - return resultMap - else: - # SAT checker returned false - resultMap["status"] = "CRASHED" - resultMap["misc"] = "SOLVER BUG: solver returned a wrong model" - self.save_failed_cmd() - return resultMap - - # Could not use SAT checker, so we only compare to true solubility. - if self._specifics in ("UNSATISFIABLE", "20"): - resultMap["status"] = "CRASHED" - resultMap["misc"] = "SOLVER BUG: instance is UNSATISFIABLE but solver claimed it is SATISFIABLE" - self.save_failed_cmd() - - elif re.search("s UNSATISFIABLE", data): - # Solver returned 'UNSAT', verify this via true solubility - resultMap["status"] = "UNSAT" - - if self._specifics in ("SATISFIABLE", "10"): - resultMap["status"] = "CRASHED" - resultMap["misc"] += "SOLVER BUG: instance is SATISFIABLE but solver claimed it is UNSATISFIABLE" - self.save_failed_cmd() - - elif re.search("s UNKNOWN", data): - resultMap["status"] = "TIMEOUT" - resultMap["misc"] = "Found s UNKNOWN line - interpreting as TIMEOUT" - return resultMap - elif re.search("INDETERMINATE", data): - resultMap["status"] = "TIMEOUT" - resultMap["misc"] = "Found INDETERMINATE line - interpreting as TIMEOUT" - return resultMap - else: - print(self._ta_status) - resultMap["status"] = "CRASHED" - resultMap["misc"] = "Could not find usual SAT competition-formatted result string in %s" % data - return resultMap - - def _verify_SAT(self, solver_output): - """ - verifies the model for self._instance - Args: - solver_output: filepointer to solver output - Returns: - True if model was correct - False if model was not correct - """ - cmd = [self.args.sat_checker, self._instance, solver_output.name] - io = Popen(cmd, stdout=PIPE) - out_, err_ = io.communicate() - for line in out_.split("\n"): - if "Solution verified" in line: - self.print_d("Solution verified") - return True - elif "Wrong solution" in line: - return False - raise ValueError("%s did not work" % " ".join(cmd)) - - def _set_true_solubility(self): - """ - Gets solubility from and from instance specifics. - """ - sol_status = None - if self.args.solubility_file and os.path.isfile(self.args.solubility_file): - with open(self.args.solubility_file) as fp: - for line in fp: - if line.startswith(self._instance): - line = line.strip("\n") - sol_status = line.split(" ")[1] - break - if sol_status is None: - # There is nothing in the solubility file we can confirm/reject - if self.args.solubility_file is not None: - print( - "INFO: solubility file %s was specified, but does not contain solubility of instance %s" - % (self.args.solubility_file, self._instance) - ) - return - - if sol_status == self._specifics: - # Solubility file and specifics agree on solubility - pass - elif sol_status in ("20", "UNSATISFIABLE") and self._specifics in ( - "20", - "UNSATISFIABLE", - ): - # Solubility file and specifics agree - self._specifics = "UNSATISFIABLE" - elif sol_status in ("10", "SATISFIABLE") and self._specifics in ( - "10", - "SATISFIABLE", - ): - # Solubility file and specifics agree - self._specifics = "SATISFIABLE" - elif sol_status in ("20", "UNSATISFIABLE") and self._specifics in ( - "10", - "SATISFIABLE", - ): - # Solubility file and specifics don't agree - raise ValueError("self.specifics says 'SATISFIABLE', solubility says 'UNSATISFIABLE'") - elif sol_status in ("10", "SATISFIABLE") and self._specifics in ( - "20", - "UNSATISFIABLE", - ): - # Solubility file and specifics don't agree - raise ValueError("self.specifics says 'UNSATISFIABLE', solubility says 'SATISFIABLE'") - elif self._specifics not in ( - "20", - "UNSATISFIABLE", - "10", - "SATISFIABLE", - ) and sol_status in ("20", "UNSATISFIABLE", "10", "SATISFIABLE"): - self._specifics = sol_status - elif self._specifics in ("20", "UNSATISFIABLE", "10", "SATISFIABLE"): - pass - else: - self._specifics = "UNKNOWN" - - return - - -if __name__ == "__main__": - wrapper = SatCSSCWrapper() - wrapper.main() diff --git a/examples/commandline/spear_qcp/target_algorithm/scripts/genericWrapper.py b/examples/commandline/spear_qcp/target_algorithm/scripts/genericWrapper.py deleted file mode 100755 index 41fd27ba4..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/scripts/genericWrapper.py +++ /dev/null @@ -1,778 +0,0 @@ -#!/usr/bin/env python2.7 -# encoding: utf-8 - -""" -genericWrapper -- template for an AClib target algorithm wrapper -abstract methods for generation of callstring and parsing of solver output -@author: Marius Lindauer, Chris Fawcett, Alex Fréchette, Frank Hutter -@copyright: 2014 AClib. All rights reserved. -@license: GPL -@contact: lindauer@informatik.uni-freiburg.de, fawcettc@cs.ubc.ca, afrechet@cs.ubc.ca, fh@informatik.uni-freiburg.de - -@note: example call: python src/generic_wrapper/spearWrapper.py \ - --runsolver ./target_algorithms/runsolver/runsolver-3.3.4/src/runsolver \ - -- -@warning: use "--" after the last additional argument of the wrapper to deactivate prefix matching! -""" - -import json -import logging -import os -import random -import re -import shutil -import signal -import sys -import time -import traceback -from subprocess import PIPE, Popen -from tempfile import NamedTemporaryFile, mkdtemp - -__all__ = [] -__version__ = 0.1 -__authors__ = "Marius Lindauer, Chris Fawcett, Alex Fréchette, Frank Hutter" -__date__ = "2014-03-06" -__updated__ = "2014-03-21" - - -def signalHandler(signum, frame): - sys.exit(2) - - -class AbstractWrapper(object): - """ - abstract solver wrapper - """ - - def __init__(self): - """ - Constructor - """ - # program_name = os.path.basename(sys.argv[0]) - program_version = "v%s" % __version__ - program_build_date = str(__updated__) - program_version_message = "%%(prog)s %s (%s)" % ( - program_version, - program_build_date, - ) - program_shortdesc = __import__("__main__").__doc__.split("\n")[1] - self._program_license = """%s - - Created by %s on %s. - Copyright 2014 - AClib. All rights reserved. - - Licensed under the GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html - - Distributed on an "AS IS" basis without warranties - or conditions of any kind, either express or implied. - - Version: %s - - USAGE - """ % ( - program_shortdesc, - str(__authors__), - str(__date__), - program_version_message, - ) - self.parser = OArgumentParser() - self.args = None - - self.RESULT_MAPPING = {"SUCCESS": "SAT"} - self._watcher_file = None - self._solver_file = None - - self._instance = "" - self._specifics = "" - self._cutoff = 0.0 - self._runlength = 0 - self._seed = 0 - self._config_dict = {} - - self._exit_code = None - - self._runsolver = None - self._mem_limit = 2048 - self._tmp_dir = None - self._tmp_dir_algo = None - - self._crashed_if_non_zero_status = True - - self._subprocesses = [] - - self._DEBUG = True - self._DELAY2KILL = 2 - - self._ta_status = "EXTERNALKILL" - self._ta_runtime = 999999999.0 - self._ta_runlength = -1 - self._ta_quality = -1 - self._ta_exit_code = None - self._ta_misc = "" - - def print_d(self, str_): - if self._DEBUG: - print(str_) - - def main(self, argv=None): - """parse command line""" - if argv is None: - argv = sys.argv - else: - sys.argv.extend(argv) - - try: - signal.signal(signal.SIGTERM, signalHandler) - signal.signal(signal.SIGQUIT, signalHandler) - signal.signal(signal.SIGINT, signalHandler) - - # Setup argument parser - - self.parser.add_argument( - "--runsolver-path", - dest="runsolver", - default="examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver", - help="path to runsolver binary (if None, the runsolver is deactivated)", - ) - self.parser.add_argument( - "--temp-file-dir", - dest="tmp_dir", - default=None, - help="""directory for temporary files (relative to -exec-dir in SMAC scenario). - If 'NONE' use $TMPDIR if available, otherwise './'""", - ) - self.parser.add_argument( - "--temp-file-dir-algo", - dest="tmp_dir_algo", - default=False, - type=bool, - help="create a directory for temporary files from target algo", - ) - self.parser.add_argument( - "--mem-limit", - dest="mem_limit", - default=self._mem_limit, - type=int, - help="memory limit in MB", - ) - self.parser.add_argument( - "--internal", - dest="internal", - default=False, - type=bool, - help="skip calling an external target algorithm", - ) - self.parser.add_argument( - "--log", - dest="log", - default=True, - type=bool, - help='logs all runs in "target_algo_runs.json" in --temp-file-dir', - ) - self.parser.add_argument( - "--ext-callstring", - dest="ext_callstring", - default=None, - help="""Command to get call string via external program; - your programm gets a file with - first line: instance name, - second line: seed - further lines: paramter name, paramater value; - output: one line with callstring for target algorithm""", - ) - self.parser.add_argument( - "--ext-parsing", - dest="ext_parsing", - default=None, - help="""Command to use an external program to parse the output of your target algorihm; - only paramter: name of output file; - output of your progam: - status: SAT|UNSAT|TIMEOUT|CRASHED - quality: - misc: """, - ) - self.parser.add_argument("--help", dest="show_help", default=False, type=bool, help="shows help") - - # Process arguments - self.args, target_args = self.parser.parse_cmd(sys.argv[1:]) - args = self.args - - if args.show_help: - self.parser.print_help() - self._ta_status = "ABORT" - self._ta_misc = "help was requested..." - self._exit_code = 1 - sys.exit(1) - - if args.runsolver != "None" and not os.path.isfile(args.runsolver) and not args.internal: - self._ta_status = "ABORT" - self._ta_misc = "runsolver is missing - should have been at %s." % (args.runsolver) - self._exit_code = 1 - sys.exit(1) - else: - self._runsolver = args.runsolver - self._mem_limit = args.mem_limit - - if args.tmp_dir is None: - if "TMPDIR" in os.environ: - args.tmp_dir = os.environ["TMPDIR"] - else: - args.tmp_dir = "." - - if not os.path.isdir(args.tmp_dir): - self._ta_status = "ABORT" - self._ta_misc = "temp directory is missing - should have been at %s." % (args.tmp_dir) - self._exit_code = 1 - sys.exit(1) - else: - self._tmp_dir = args.tmp_dir - - if len(target_args) < 5: - self._ta_status = "ABORT" - self._ta_misc = f"""some required TA parameters (instance, specifics, cutoff, runlength, seed) missing - - was {' '.join(target_args)}""" - self._exit_code = 1 - sys.exit(1) - - self._config_dict = self.build_parameter_dict(target_args) - - if args.tmp_dir_algo: - try: - self._tmp_dir_algo = mkdtemp(dir="/tmp/") - except OSError: - sys.stderr.write("Creating directory for temporary files failed") - pass - - runargs = { - "instance": self._instance, - "specifics": self._specifics, - "cutoff": self._cutoff, - "runlength": self._runlength, - "seed": self._seed, - "tmp": self._tmp_dir_algo, - } - - if args.ext_callstring: - target_cmd = self.get_command_line_args_ext( - runargs=runargs, - config=self._config_dict, - ext_call=args.ext_callstring, - ) - else: - target_cmd = self.get_command_line_args(runargs=runargs, config=self._config_dict) - - target_cmd = target_cmd.split(" ") - target_cmd = filter(lambda x: x != "", target_cmd) - - if not args.internal: - self.call_target(target_cmd) - self.read_runsolver_output() - - try: - if "core" in os.listdir("."): - os.remove("core") - except Exception: - traceback.print_exc() - - if args.ext_parsing: - resultMap = self.process_results_ext( - self._solver_file, - {"exit_code": self._ta_exit_code}, - ext_call=args.ext_parsing, - ) - else: - resultMap = self.process_results(self._solver_file, {"exit_code": self._ta_exit_code}) - - if "status" in resultMap: - self._ta_status = self.RESULT_MAPPING.get(resultMap["status"], resultMap["status"]) - if "runtime" in resultMap: - self._ta_runtime = resultMap["runtime"] - if "quality" in resultMap: - self._ta_quality = resultMap["quality"] - if "misc" in resultMap and not self._ta_misc: - self._ta_misc = resultMap["misc"] - if "misc" in resultMap and self._ta_misc: - self._ta_misc += " - " + resultMap["misc"] - - # if still no status was determined, something went wrong and output files should be kept - if self._ta_status == "EXTERNALKILL": - self._ta_status = "CRASHED" - sys.exit() - except (KeyboardInterrupt, SystemExit): - self.cleanup() - self.print_result_string() - # traceback.print_exc() - if self._ta_exit_code: - sys.exit(self._ta_exit_code) - elif self._exit_code: - sys.exit(self._exit_code) - else: - sys.exit(0) - - def build_parameter_dict(self, arg_list): - """ - Reads all arguments which were not parsed by ArgumentParser, - extracts all meta information - and builds a mapping: parameter name -> parameter value - Format Assumption: - Args: - list of all options not parsed by ArgumentParser - """ - self._instance = arg_list[0] - self._specifics = arg_list[1] - self._cutoff = int(float(arg_list[2]) + 1) # runsolver only rounds down to integer - self._ta_runtime = self._cutoff - self._runlength = int(arg_list[3]) - self._seed = int(arg_list[4]) - - params = arg_list[5:] - if (len(params) / 2) * 2 != len(params): - self._ta_status = "ABORT" - self._ta_misc = "target algorithm parameter list MUST have even length, found %d arguments." % (len(params)) - self.print_d(" ".join(params)) - self._exit_code = 1 - sys.exit(1) - - return dict((name, value.strip("'")) for name, value in zip(params[::2], params[1::2])) - - def call_target(self, target_cmd): - """ - extends the target algorithm command line call with the runsolver - and executes it - Args: - list of target cmd (from getCommandLineArgs) - """ - logging.warning("genericWrapper: falling back to non-deterministic behaviour") - random_id = random.randint(0, 1000000) - self._watcher_file = NamedTemporaryFile( - suffix=".log", - prefix="watcher-%d-" % (random_id), - dir=self._tmp_dir, - delete=False, - ) - self._solver_file = NamedTemporaryFile( - suffix=".log", - prefix="solver-%d-" % (random_id), - dir=self._tmp_dir, - delete=False, - ) - - runsolver_cmd = [] - if self._runsolver != "None": - runsolver_cmd = [ - self._runsolver, - "-M", - self._mem_limit, - "-C", - self._cutoff, - "-w", - self._watcher_file.name, - "-o", - self._solver_file.name, - ] - - runsolver_cmd.extend(target_cmd) - # for debugging - self.print_d("Calling runsolver. Command-line:") - self.print_d(" ".join(map(str, runsolver_cmd))) - - # run - try: - if self._runsolver != "None": - # if there are quotes in the call, we cannot split it individual list elements. - # We have to call it via shell as a string; problematic solver: SparrowToRiss - if '"' in runsolver_cmd: - runsolver_cmd = " ".join(map(str, runsolver_cmd)) - io = Popen( - runsolver_cmd, - shell=True, - preexec_fn=os.setpgrp, - universal_newlines=True, - ) - else: - io = Popen( - map(str, runsolver_cmd), - shell=False, - preexec_fn=os.setpgrp, - universal_newlines=True, - ) - else: - io = Popen( - map(str, runsolver_cmd), - stdout=self._solver_file, - shell=False, - preexec_fn=os.setpgrp, - universal_newlines=True, - ) - self._subprocesses.append(io) - io.wait() - self._subprocesses.remove(io) - if io.stdout: - io.stdout.flush() - except OSError: - self._ta_status = "ABORT" - self._ta_misc = "execution failed: %s" % (" ".join(map(str, runsolver_cmd))) - self._exit_code = 1 - sys.exit(1) - - self._solver_file.seek(0) - - def float_regex(self): - return "[+-]?\d+(?:\.\d+)?(?:[eE][+-]\d+)?" - - def read_runsolver_output(self): - """ - reads self._watcher_file, - extracts runtime - and returns if memout or timeout found - """ - if self._runsolver == "None": - self._ta_exit_code = 0 - return - - self.print_d("Reading runsolver output from %s" % (self._watcher_file.name)) - data = str(self._watcher_file.read()) - - if re.search("runsolver_max_cpu_time_exceeded", data) or re.search("Maximum CPU time exceeded", data): - self._ta_status = "TIMEOUT" - - if re.search("runsolver_max_memory_limit_exceeded", data) or re.search("Maximum VSize exceeded", data): - self._ta_status = "TIMEOUT" - self._ta_misc = "memory limit was exceeded" - - cpu_pattern1 = re.compile("runsolver_cputime: (%s)" % (self.float_regex())) - cpu_match1 = re.search(cpu_pattern1, data) - - cpu_pattern2 = re.compile("CPU time \\(s\\): (%s)" % (self.float_regex())) - cpu_match2 = re.search(cpu_pattern2, data) - - if cpu_match1: - self._ta_runtime = float(cpu_match1.group(1)) - if cpu_match2: - self._ta_runtime = float(cpu_match2.group(1)) - - exitcode_pattern = re.compile("Child status: ([0-9]+)") - exitcode_match = re.search(exitcode_pattern, data) - - if exitcode_match: - self._ta_exit_code = int(exitcode_match.group(1)) - - def print_result_string(self): - - if self.args and self.args.log: - # if not os.path.isfile("target_algo_runs.csv"): - # with open("target_algo_runs.csv", "a") as fp: - # fp.write("instance,seed,status,performance,config,[misc]\n") - with open("target_algo_runs.json", "a") as fp: - out_dict = { - "instance": self._instance, - "seed": self._seed, - "status": self._ta_status, - "time": self._ta_runtime, - "config": self._config_dict, - "misc": self._ta_misc, - } - json.dump(out_dict, fp) - fp.write("\n") - fp.flush() - - sys.stdout.write( - "Result for ParamILS: %s, %s, %s, %s, %s" - % ( - self._ta_status, - str(self._ta_runtime), - str(self._ta_runlength), - str(self._ta_quality), - str(self._seed), - ) - ) - if len(self._ta_misc) > 0: - sys.stdout.write(", %s" % (self._ta_misc)) - print("") - sys.stdout.flush() - - def cleanup(self): - """ - cleanup if error occurred or external signal handled - """ - if len(self._subprocesses) > 0: - print("killing the target run!") - try: - for sub in self._subprocesses: - # sub.terminate() - Popen(["pkill", "-TERM", "-P", str(sub.pid)]) - self.print_d("Wait %d seconds ..." % (self._DELAY2KILL)) - time.sleep(self._DELAY2KILL) - if sub.returncode is None: # still running - sub.kill() - - self.print_d( - "done... If anything in the subprocess tree fork'd a new process group" - ", we may not have caught everything..." - ) - self._ta_misc = "forced to exit by signal or keyboard interrupt." - self._ta_runtime = self._cutoff - except (OSError, KeyboardInterrupt, SystemExit): - self._ta_misc = "forced to exit by multiple signals/interrupts." - self._ta_runtime = self._cutoff - - if self._ta_status == "ABORT" or self._ta_status == "CRASHED": - if len(self._ta_misc) == 0: - if self._ta_exit_code: - self._ta_misc = "Problem with run. Exit code was %d." % (self._ta_exit_code) - else: - self._ta_misc = "Problem with run. Exit code was N/A." - - if self._watcher_file and self._solver_file: - self._ta_misc = f"""{self._ta_misc}; - Preserving runsolver output at {self._watcher_file.name or "None"} - Preserving target algorithm output at {self._solver_file.name or "None"} - """ - - try: - if self._watcher_file: - self._watcher_file.close() - if self._solver_file: - self._solver_file.close() - - if self._ta_status != "ABORT" and self._ta_status != "CRASHED": - os.remove(self._watcher_file.name) - os.remove(self._solver_file.name) - - if self._tmp_dir_algo: - shutil.rmtree(self._tmp_dir_algo) - - except (OSError, KeyboardInterrupt, SystemExit): - self._ta_misc = "problems removing temporary files during cleanup." - except AttributeError: - pass # in internal mode, these files are not generated - - if self._ta_status == "EXTERNALKILL": - self._ta_status = "CRASHED" - self._exit_code = 3 - - def get_command_line_args(self, runargs, config): - """ - Returns the command call list containing arguments to execute the implementing subclass' solver. - The default implementation delegates to get_command_line_args_ext. If this is not implemented, a - NotImplementedError will be raised. - - Args: - runargs: a map of any non-configuration arguments required for the execution of the solver. - config: a mapping from parameter name (with prefix) to parameter value. - Returns: - A command call list to execute a target algorithm. - """ - raise NotImplementedError() - - def get_command_line_args_ext(self, runargs, config, ext_call): - """ - When production of the target algorithm is done from a source other than python, - override this method to return a command call list to execute whatever you need to produce the command line. - - Args: - runargs: a map of any non-configuration arguments required for the execution of the solver. - config: a mapping from parameter name (with prefix) to parameter value. - ext_call: string to call external program to get callstring of target algorithm - Returns: - A command call list to execute the command producing a single line of output containing the solver command - string - """ - callstring_in = NamedTemporaryFile(suffix=".csv", prefix="callstring", dir=self._tmp_dir, delete=False) - callstring_in.write("%s\n" % (runargs["instance"])) - callstring_in.write("%d\n" % (runargs["seed"])) - for name, value in config.items(): - callstring_in.write("%s,%s\n" % (name, value)) - callstring_in.flush() - - cmd = ext_call.split(" ") - cmd.append(callstring_in.name) - self.print_d(" ".join(cmd)) - try: - io = Popen( - cmd, - shell=False, - preexec_fn=os.setpgrp, - stdout=PIPE, - universal_newlines=True, - ) - self._subprocesses.append(io) - out_, _ = io.communicate() - self._subprocesses.remove(io) - except OSError: - self._ta_misc = "failed to run external program for output parsing : %s" % (" ".join(cmd)) - self._ta_runtime = self._cutoff - self._exit_code = 2 - sys.exit(2) - if not out_: - self._ta_misc = "external program for output parsing yielded empty output: %s" % (" ".join(cmd)) - self._ta_runtime = self._cutoff - self._exit_code = 2 - sys.exit(2) - callstring_in.close() - os.remove(callstring_in.name) - self._instance = runargs["instance"] - return out_.strip("\n\r\b") - - def process_results(self, filepointer, out_args): - """ - Parse a results file to extract the run's status (SUCCESS/CRASHED/etc) and other optional results. - - Args: - filepointer: a pointer to the file containing the solver execution standard out. - exit_code : exit code of target algorithm - Returns: - A map containing the standard AClib run results. The current standard result map as of AClib 2.06 is: - { - "status" : <"SAT"/"UNSAT"/"TIMEOUT"/"CRASHED"/"ABORT">, - "runtime" : , - "quality" : , - "misc" : - } - ATTENTION: The return values will overwrite the measured results of the runsolver (if runsolver was used). - """ - raise NotImplementedError() - - def process_results_ext(self, filepointer, out_args, ext_call): - """ - Args: - filepointer: a pointer to the file containing the solver execution standard out. - exit_code : exit code of target algorithm - Returns: - A map containing the standard AClib run results. The current standard result map as of AClib 2.06 is: - { - "status" : <"SAT"/"UNSAT"/"TIMEOUT"/"CRASHED"/"ABORT">, - "quality" : , - "misc" : - } - """ - - cmd = ext_call.split(" ") - cmd.append(filepointer.name) - self.print_d(" ".join(cmd)) - try: - io = Popen( - cmd, - shell=False, - preexec_fn=os.setpgrp, - stdout=PIPE, - universal_newlines=True, - ) - self._subprocesses.append(io) - out_, _ = io.communicate() - self._subprocesses.remove(io) - except OSError: - self._ta_misc = "failed to run external program for output parsing" - self._ta_runtime = self._cutoff - self._exit_code = 2 - sys.exit(2) - - result_map = {} - for line in out_.split("\n"): - if line.startswith("status:"): - result_map["status"] = line.split(":")[1].strip(" ") - elif line.startswith("quality:"): - result_map["quality"] = line.split(":")[1].strip(" ") - elif line.startswith("misc:"): - result_map["misc"] = line.split(":")[1] - - return result_map - - -class Arguments: - """ - parsed arguments - """ - - def __init__(self): - """ - Constructor - """ - - -class OArgumentParser(object): - """ - my own argument parser... - problem with the standard argument parser is the prefix-matching when using parse_known_args() - """ - - def __init__(self): - """ - Constructor - """ - self.options = {} - self.required = [] - self.args = Arguments() - - def add_argument( - self, parameter_name, dest, default=None, help="", type=str, required=False - ): # pylint: disable=built-in - """ - adds arguments to parse from command line - Args: - parameter_name: name of parameter - dest: destination in returned Argument() object - default: default value - help: help output if --help - """ - setattr(self.args, dest, default) - self.options[parameter_name] = { - "dest": dest, - "default": default, - "help": help, - "type": str, - } - if required: - self.required.append(parameter_name) - - def print_help(self): - """ - print help message - """ - print("") - print("Help:") - for name_, dict_ in self.options.items(): - print("\t %-20s \t %s (default: %s)" % (name_, str(dict_["help"]), str(dict_["default"]))) - print("") - sys.exit(0) - - def parse_cmd(self, args): - """ - parse command line - """ - unknown_args = [] - iterator_args = iter(args) - while True: - try: - name = next(iterator_args) - except StopIteration: - break - # for name, value in zip(args[::2], args[1::2]): - # if name in ["--help"]: - # self.print_help() - if self.options.get(name): - try: - value = next(iterator_args) - except StopIteration: - sys.stderr.write("%s is missing some value\n" % (name)) - sys.exit(2) - dict_ = self.options.get(name) - setattr(self.args, dict_["dest"], dict_["type"](value)) - if name in self.required: - self.required.remove(name) - else: - unknown_args.append(name) - - if self.required: - print("The following arguments are required:") - for name_ in self.required: - print("\t%s" % (name_)) - - return self.args, unknown_args - - -# =============================================================================== -# if __name__ == "__main__": -# sys.exit(main()) -# =============================================================================== diff --git a/examples/commandline/spear_qcp/target_algorithm/scripts/generic_solver_wrapper.rb b/examples/commandline/spear_qcp/target_algorithm/scripts/generic_solver_wrapper.rb deleted file mode 100755 index abcd9c06f..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/scripts/generic_solver_wrapper.rb +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/env ruby - -require 'fileutils' - -def float_regexp() - return '[+-]?\d+(?:\.\d+)?(?:[eE][+-]\d+)?'; -end - -def get_true_solubility - File.open($true_solubility_file){|file| - while line = file.gets - entries = line.split - if entries[0].eql?($orig_input_file) - return entries[1] - end - end - } -end - -if ARGV.length < 6 - puts "Usage: ruby generic_solver_wrapper - #{instance_filename}" - created_instance_file=1 - instance_created = instance_filename -else - instance_filename=input_file -end - -Signal.trap("TERM") { - #=== Respond to termination by deleting temporary file and crashing. - begin - puts "Result for ParamILS: CRASHED, 0, 0, 0, #{$seed}" - File.delete(tmp_checking_outfilename) - File.delete(tmp_file) - File.delete(tmp_runsolve) - if created_instance_file - File.delete(instance_created) - end - ensure - Process.exit 1 - end -} - - -#=== Go into solver directory and call the solver. -memout="3000" -solver_callstring = get_solver_callstring(instance_filename, seed, param_hashmap) -cmd = "#{runsolver_executable} --timestamp -w #{tmp_runsolve} -o #{tmp_file} -C #{timeout} -M #{memout} #{solver_callstring}" -exec_cmd = "#{cmd}" -STDERR.puts "Call to runsolver: #{exec_cmd}" -system exec_cmd -#runsolver_exitcode = $?.exitstatus - - -#=== Parse algorithm output to extract relevant information for ParamILS. -solved = "CRASHED" -runtime = nil -witness=nil -File.open(tmp_file){|file| - while line = file.gets - if line =~ /s UNSATISFIABLE/ - solved = "UNSAT" - end - if line =~ /s SATISFIABLE/ - solved = "SAT" - end - end -} - -#=== Parse runsolver output to get runtime and to count successes past the cutoff as timeouts. -File.open(tmp_runsolve){|file| - while line = file.gets - if line =~ /runsolver_max_cpu_time_exceeded/ or line =~ /Maximum CPU time exceeded/ - solved = "TIMEOUT" - end - if line =~ /CPU time \(s\): (#{float_regexp})/ - runtime = $1.to_f - end - end -} - -#=== Check correctness of solution (solubility status and witness) -if solved == "SAT" or solved == "UNSAT" - # Check the solubility status against the correct one, call differences crashed. - true_solubility = get_true_solubility - true_solubility = instance_specifics - puts "#{true_solubility} #{solved}" - solved = "CRASHED" unless ((true_solubility == "SATISFIABLE" && solved == "SAT") || true_solubility == "UNSATISFIABLE" && solved == "UNSAT") || true_solubility == "UNKNOWN" -end - - -if solved == "SAT" # check the witness - #solved = "CRASHED" # if we can't verify it we'll call it crashed. -=begin -# Can't easily get the actual exitcode, so using the manual parsing below instead. - checking_cmd = "#{SAT_executable} #{instance_filename} #{tmp_file}" - puts "Checking the solution with cmd: #{checking_cmd}" - system checking_cmd - - p $? - exitcode = $?.exitstatus - p exitcode - if exitcode == 10 || exitcode == 11 - solved = "SAT" - end -=end - - checking_cmd = "#{SAT_executable} #{instance_filename} #{tmp_file} > #{tmp_checking_outfilename}" - puts "Checking the solution with cmd: #{checking_cmd}" - system checking_cmd - File.open(tmp_checking_outfilename){|file| - while line = file.gets - if line =~ /Solution verified./ - solved = "SAT" - end - end - } - -end - -#=== Output for configurators. -puts "Result for ParamILS: #{solved}, #{runtime}, 0, 0, #{seed}" - - -unless solved == "CRASHED" - #=== Tidy up. - File.delete(tmp_checking_outfilename) if solved == "SAT" - File.delete(tmp_file) - File.delete(tmp_runsolve) - if created_instance_file - File.delete(instance_created) - end -end diff --git a/examples/commandline/spear_qcp/target_algorithm/spear-python/README.md b/examples/commandline/spear_qcp/target_algorithm/spear-python/README.md deleted file mode 100755 index 3795304a5..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/spear-python/README.md +++ /dev/null @@ -1,10 +0,0 @@ -Author: Domagoj Babic, babic.domagoj@gmail.com -Please contact the author concerning licensing information. - -Reference: -@misc{BabHut07, - author = {Domagoj Babi\'c and Frank Hutter}, - title = {SPEAR Theorem Prover}, - note = {Solver description, {SAT} competition}, - year={2007} -} diff --git a/examples/commandline/spear_qcp/target_algorithm/spear-python/Spear-32_1.2.1 b/examples/commandline/spear_qcp/target_algorithm/spear-python/Spear-32_1.2.1 deleted file mode 100755 index 3c13aaf51b2188def41cdc63ab261be0ea61c60c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1456308 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⩔0;EOHaKKxS}0k}{k zz6a@$wk0!HosnxFRerUc$6>*+E^YYpW0Sk*#~^dQJDo`;niMJAm`t#c}*{zYT)7F)BN7UEmEofNO+ z#>dIugQVbYq}-gN2K3zQkZo?%Y^S>iojq2jwz!fSqq>B+VX$;_+BAHCld9%HkD!1t zH^{%eZ_=^_5dX9vFi9Pz3*_pbXYK@PnxmdIbL--((l}gA68e7#bh$UOcj&S|Bvv@v zK5+v|RHO)KWD<4*dE?wy_}2kDt+w8Mt^nPn02~IB(nGHh0JD++zV_~eTYMY;1${x^4@S?mnZ_dV2(mP7l9Q z%9*MYy3LCL5-4KH)X4iHQavD()jjxw0QhYZz}Ke6bm8iPB*0q) z;0^x{K;1(MS83EZJ@r6S<4^Fwrp77ucTnR5DcI9ZXeY#%BsE@$BKGjh9@RsyrF=8@ z_xyVjN~DlwMCP0z2elNT7&bza z@tu`#{U<6F&w-;8iU28{;N2Mz2h||)wbWN1Ao+^VzsLw(fJ)J7ic>lv37o66PD#oZ zpve3$_|m@!o6(%Z{E9H~>_L>O{)S)CYD!XSfyh(-6{7NlapHb-Tnl84Msx#9Kz2dJ z>$8#V7#W-qwO4`k7pDIp4UW+9K2PuU;+5R-o)Y|vgtr|s7z1Kz;qhH;Wh8;C!&UC+ zNLX?XB71K1XGEEQRX6fQ7gy{oKd4F$Mza^fX`_OCl0DwbMKOJW=4} z9)OV4ucMFlPJiGO%@imkU5DKTU4$DX`^P})=gktduu`F?aIO+{a`qg0Vqwt56;Lqc zDDOfO0?1;H9S$K()UN$OAeKg9FNZAtcsruby{Xg%(vZ$&gQ87>v642hPL0B zC5Ma!{||fL9v)S7^__$P1|2;SB1T1_N-MNhjiQo*3V{iDiQ;7dEm3Q2t+$FgBZvlw z$q>#Q2C)Ti6{{Al?Mp2xXe3lah)Gnc;Zh9{FjdqUCu#%=C}_UlZ|!rPBpBMh-}C+P z@jOgs&RKiy+uCcdz4p3nh2VysGq6Y~K~s|q)nim?8h_wCE*3t;NI`wR0w> znCoPMxd1#ngxX@fhNL&zHGBrvsl`6wry%I|4#7KLr^&fC!p8*RgEqo0?I38py0sTr zDhSkMs$3t}gL#U1@KP=!M4Ab~HT^y;2fnPIJqIU-k0C@T3}*S%s}8-FVCl=_ zXkQf4VgY`>T!?5l7sHJGa`9;x}KD z)U-P_yl);^v}jmr_;p$h9t5h6_hr1S<@~sf;0!_VV;jLlbnim1gTk0j9TZ@y=bFKZ zHp0zIwYz%6l}%Xd*9KRpuX33#%=nUafal>ZbVXNw3qoYQWMs2YHgB+tU^ z5o0V5jUPD=)BqfV2N-~D^w2om`i=p}50$Q%;yo&;o^EnWDldF8f^B^iduZ?FH;WDqRnKy6$4O@gtquQe@bWzD410c2?l}A zgef$_iiKdj@)RKA_p1|fv#xGDkc$fuisDmq;!|J`uoFfQh;SyakZW6SlIs|x=gND7 z1}cZ;t#^gOz*V*t_<}a2X7CGMeBHYkO_3`X2o<D;q zVy{qReyADtzF5zU`z4JmlRjsPkX7~&d!CW}*=_>N#?HnayJ&%&;AivXU87=l!D+h& zyPCSwG?g8dR5-&YFG>{hObi+qU~|LhsNlkp2&NI@f`?W~bt9pwOa^@YBOlU%(SN0sY1Layp4foV&OhdZ^Pw`+|1* zNo7O-z{yu1v5cyDAbzcM;VjH!lbr3Q{P;+KBH_OOdm$+6US;5t3ZWi zUm-&dQX5d)1%K{%uzG9hR1|L3r{i;LsCrXkYp7vU^_EY!Zq^EGwr0dbP4M*H`Urux zmePlyStsFa4K;3hV{37fDUB4?_`B2 zvT@arvVy6ZWtW9js<-i4`SIAejDq21a(|Uswuo(nl5*zYNAl(S9mnE+1ue`M0If`V zV&{8+-*fSDug#WeqbRl{bZr5fJoE2h`80vS_eW|l22skKD}gmb6p}PMp@ti+URjBm zme;hB!Y+23Ncqc+IjsPP%m+@0ET6B(wByk@Up0(NsY`kO2i#KYJPA^(a@O zG4?dZ?!F$Qjnk(w#y0{^=H)lyWGYt!*%GYDw%rul#>^f1qQ_7awkF0RnjK*CSVUak zN1JE&*{hH3FOi$>3>b?EC)>w(9kHCKgRc}r^{t~W%73$Y`~W!nZ5=hXdHi7H>(v{= zeO>lkG;3sjkl=E5l}ap*!Nek5ZT=HK5EKm~XeynrU5&(Vpj4VsiOLvN&8~ccgQ%6C z189|ggn=$Hs(7r8i!G|Y!iVN7Gdq1zBl{Gu#;5WxhSmBa{&Jmu^P?=Fm3@l+oeKyZ zf}dhtBZq&1d=~mj*T@lcaRbrG6|W#Li^k7H^+tRohfSS^8BW2vV`w0v-^rP{O*G#5 zHM*Ds=qoCLPK)mWxyF5q(1Q%b>J;k(IA@Es2QLuB$`kRA`r&mWkmC4gh|z*+FClGO zBnA?T_}zF(wUx}CP8PUrSkc0Ezpd<6_2^RmTEwgL3k@-qUAHeDz|M|#4m(_r)64|N za52#`Rw%?Y*HYG&nQvRiXk!r(!OI($xmv}i>?YORxgXMp094^e3;|^-Y+Nt|i&1m{ zSb_5hscyp;4gVahG`lj(}0tIo9mdlc%jWEZX zPr=qiwL(yZIC(H6(xc62n=uo~w|vGS>LzWr6pT**m-kuvh_WA$R|Fn7Kui|c#7S5d zO;3_={_D1kd~$Iv+C}8=|kNnLrY)0@;6;Gk)^(ZECS#l1R1H^sP1AX!lj==PH zoL-F;LSBpIQ%DJhwfYJnofQF2s@O;yLd80+{N;8YcT0w{e_g=?F+|vrJ&a+m>TS9h z=<=G337zy63BHYTdBm?c4}}S>FB%WL*2Kjh1G&a0Wva`v_(gc7-TyjCSQe?VLY9Kq z;u&H{jk~kEd`MQOFP6oyLKZ=EtyIJ(#v=I+^9=rVXq-*4G$$OCMTj04F@bxsj4Jt5x`kkE^wss}+e3XHwAsG|4_y))4kgt{1`kD@#OAqmAEtGZimHma8Zw zK^FP*w&>z- zPI6C+k7XOC7 zEWQbav^g=h{Alb-@xd&0x*>lV?ITNHOapYah*hf=x3X0$masY+u5IL(C=tm$?;^g? z<5!@R+B@K;9w2@(Q!9uM>x)#i_utHoV=;l?uc4^Gkdjy{1C9Zk+>F{yHKv+cT?|%v z5Q?$^oN08GwrS6CHPALK2l-MlsK|(bz-y9}3+@7dP5k)!LpU>x;t6&u^D^{Jswy|+ zt>3fLkmLfo45KMnP{GInL=z10_Au3p!g=n6<43!u4*}+~!U>sm(Sd*jy<7Q;D)t~enT5pWiviCG1+SJ@N zvv+19{@`L(8O}9U2pJ$6c`q$WKW5dIjKs*FgKoieLAo~43c1dvn5W3~k08Oa$|3mO zORIRE^f;S8Fpj^%I64ddF1%8Ql^`nBV(0PGb$oIi0pzShiRW0rC?ZmJ#>OH}wV{Yc zC`K1@5Yw@T^>sb1eXhfqBI1q9;@PgJjH`{@f;~Gcbf;NM&*v+w1?&eYB<(ln6wyO&=2yscmoi6 zj*GlYGasnqVh|6k<6^$`9mmBVA&a#`);Zn7to$sPK{Pdg!-^Y_q z?sFOA)1xd;zQiXyBCfy}P;%L*$(dynreq|3cI_d^Kc0zjY2U*|SDdS~Ps8lL4a8qu znzU%q0?|7qpWB5U9s@T!wHfi*6|2EM6BqWhXac~=d3;C1x!v#`!Aluv?qNZpqUHDu z#q1!p55m!l&czeCUoB=qeFdpG8N>sz+u#Xk0OEVWY(aSXax=N694wMEaG14E)~347 zlr}!K_2UcCXe~CMpSCvI6I8`sS~mg45SbOxR^L6_PM+lLdn7W)@JJ8zg;0oP$bEGp3|j>d6Ur7f>RHTamuObtw@i#8nWHs95RJ+7h(BB}{{>-kntLui8D}Yd1qz!3C zGVJNs7`uXAQb9&q1sxcJ_kIQGzs+@AFQd9zMz!lu`^nv0d$w%4%GSmI61SaN>{@=x zq)-*ztzbdIW^IHw+X#;ogxbL%RO53Km#K~L7#m?N$u0Kkfrw5K4oz5`8T5AWza~W`Sc>As-|~p=Hq!Zw4{(KmAWsTn2bqz2paT5= zQyw^11uB@+Id0zbDnL;Ew>0K?!TOHspTT#zFmnZD61Q-BUJ(z~oR-!doY#-}**QC? zxE9#7Xe}Wa{K>vn7-iRD&C4l#V&BNNK`v5Le+3$?#V+QjOU&rF=Tp!a0x0Y3A!i3U zAsGYC(Botfy0DLA7NDG(=GZlTD!th*z3Frr3Q47GOK7d)0=w*UQuf)d%c=!3$-_Vn zwTs^-#c#BWcU$q>%Y$&GEh!x@2y+hxp=#j@*9Nau+-M{G`l%%A$d8 zKBcy}F7={_bEDBq`EtTu^UFEpZAzo31Q{suqT4a+N_Cf9 z_&L4;v9XA{9#`Ce`eSjQro(dD5NBnVn;UI;-x$Ya=N`3F@&y88h}$I`2Gl{@VenfC z=9ZD1^(kLfLSyUkPZ9m?1{C9vKX^9uw{`r10IQvWDi62Y0LD?MUrxI&am%;jJuO95 zm^9>~3K*pecuu%+x;}ciG5TIGv$?0i7zeb4pyr5E-aLOi-ft?YgKK!5=^9>ZT%Eh= z*4iyskKA-?UD-a^VE-D8FJG@k?}liB-m;aA(7&OMEu)9)HO=}cFOssavwn$KyF_y3 za3I+-dStUcfiQeRv|)72ABC^F+c(v6Mx8*dB?+g-n4N)Ahu-JIr&eBrXQ;EZhO|wU zLujt?2*1N-Y(Qlr?saH0_EQ+$U|?t|cF&=F7x|Ls@xqghnL`Z8kSChU@y49J#JVtg zC#f~Gj43~_Weu+qB?7e~U<>?`5rS2GE{*ed8F%eP@X-7(kq`=jrce!SbK(@<3|NuD zn>L~u69T0l;K3}@bSP47;5N4xV_(Lv#dC4C{4#v5-hlH76cB4ViFF?L^;VIqab4kx zTs<0M4`a;NMe6gaXa9|@#tp@E{E752kT_ulzZ%LLJX>F9rXy|8-?kKi#+0+OksK_# zeA5tlZl~wsgk7?0w$i2lbOu6Mc%+u`g+}qKrvRf;WpkrBnq*9Cii6w$i-cf;6yd5fi zV|=a|%@^kRMJ-N1-xepJuNlo(#qu>q=jsy9mm_*27sBWP2=H^|AOvC?x&{$N5UFwg zn;hx7RDTJ5)(Zj3#v$%mo$o>frsXhB@{O3}N3@7AZqCR|M-EP8jQO@b)l61aw<6C^ zfaiUDKopROzARzq?L0jU%V(EcaSPfdMHA41mR;-r2KP8xIWNPkOB_NV#ukS(zhtRa z{tU~X&7P_BK#>G;<3R1+NroVA&C!d2sk89q&&)rRIbDEK?;+9LI|C|JK!7-pO_q(H zO|xRhW5iE625T45z|hL*Rd`hvT!LUgOY)1J)#NV4Pzz)#EJyR}^flp=oGhgqPDTEm zH6YhW%Ji7dOd$e=ET~>;a`XaU@;S&P+~m0$I$sJgARG8;y9tL%3es_*iy{)8xklcZ)s2k+|-)FUb zBiV)_(9 zdX{7KlCMJ_fyF*xaQlLHAZ?)&Vo0-?;(BB*Zg!gFjM86aY}k6TTi(a1WUet>og3ER zjV6mS}@=QRp)%UZtprCyL*?0GBYu>cWPyC|@g zF9{#?|FV8mPDTbk`)6c$OGtf8VT(DC?HxXcNS1<~3b{Sf?G_Q%()(;=ChQC7q0mRJ z@lFFlWBCFZiH_cjt;K7$_L8}^?&x8M5ZXKQj?B>@{{^gHX^*~!K(57h@zXW9)E=2R2P!D_XwmhA@Ms#T0k-s_(do)c_DsTOAPBl@{1z4 zQXoI*4^PKxV3;QTa8eg-U|`UA`g|-227i>R+I&X z{Kk@}i5e9~+T>IME(PtE>VLo#3>!T0`VFnZF-izBlV2%2eW@6 z#9->1H3Ipy4^2aB) z!@EB|@=;^JeTIfB{|0qJ7ZE4F=FV5xm12LY8huYmoAY|S{KjQ@xJ|f@A$}A5~nU6 zBICp0x#R|e4eGE5ypPa}b$VTVAkeZIP6Wm^stM@}BX?-Wo+;3Y1yLZ~B;cY^Cuon-uffn_C z$S(y#c+N`GL$@gDnzeJ$oy6&a)!vb8FNUUW=r#mmDEpT5qRnpvVHqX(z8!WK9xSSx=XpCck0?+9q+ZxAkoC5*#3f>C1uZO0-GB`}dFVGfw0 zFBtJF{YgF=(Q?OS;nDGn_ow=D88_L>I0plXWMFUWO^V?6PWwE(HNPlv=gAl?gfvHN zi)x>K1c*GCNd1Ki@J(|K*OV+P3nrlLNTe1?lxcYB$>p_(#W?>jCF3O$zcuej=s15a z-PQbt&4XUTE}2aAA=!D3b&RL#hfSzQx52 zG!5^Ag8&{@MUC<4`?=uv0T?l}ARvJ}Chjl$+(&pE@M#FlTXHmQH40xiYh79GNMTG| z^;WO6ID-R_H%{1Z(N-PunQHe%@jQIgh!T9Ajjs#Br?9%B=Hwmtg|@4lg{!rBQ}8?W z9-dhI;=Lr!A?^GL*118Ohr@cizB^UTB6zY+n@8LDy#;}$IUg2AWpV+_iX)F5$BGm2xt>0KfBB>P|^mz;>xE7xT$oMjBK zagg~Qskat1y))Iv4Ew6rN4JEbR8-RYw*wJfaohkTo{HZ&K;IepXLL6*$%ZwI1J1#O zN^AiL#*S$aBCGHc+A4rUb*q)%Py8hTWdG6krTnV@`um7&7!a-eu)&GPQ5Yu}6wWWM z1RMh6VUVFMq0${2WqznqZw&YI*juADm65$Xo3dZ61EpUE@}Lw|GpXBJoa*rl2bcoC zHJmFsKFNX|Y!-;!s zerQ$rP^g)qDa=O%-MxvuKg9brJl%YL7$F++L$!pc1;mJ81N^azYcYy^LNw>2iHTa4 z!H-oaksqmrYqo$h_h;f+!I#Lys|DEV)+F`>l*-{YG{i-#{A%&flZ|3(0?Ng+3PS}I z$SA+qursLV+Z_3#9P-$F{mY@NvGSOyOAj_{_tk(v^vyrQ0sqXj9G&TrZaM5)?%Zl1 z(}#mYT>$x@7)*sM5&5Wmk7KB57K8AN=$f3bN*;jXM*wgH&qSP{NFYpBujL}<`S?Nj zKZ;2{fps>p;?EQKW3G(X@Rg6M@ki>z;Ix(1o@|OWe*H`1v`TED=MDW5(VK@hpg%=z zTE+e7PySnR2AXcu#??R;U@YHt+MLzEvy$+z?L+KIQp@9m6t!TBuScvNPsGfc1rjln zJq+c*Tz!*qdiV^z=725NcOzuGwD)ufednw3p$zaahyF_6PzlJ1@78K5MkOTiEKAXP z@^NSpO8AcFJ}}xB_Z_9@>!U%ss^47Q>;tY2vAQK%YGQJ64=iypZ`XGWt&y!jR--@h z&HWtzYz!3w9LWrD@{#&N(*9z>`r%uFl%oL0Svm1G!I2##n2iJl@Y61rd=3o<46E#o z5AB(+#z>movZa^i*MF&V{oXft_)qtMsiGcuzg8s4-o+G1Hz8pkgaZN;a6Rt@Y zd`tfIj&Bq(buxrpT3N+y$76DYR(g49>`M+6MyQL_@gFK_FIs zHTS~Auc+wkWzG0cjs;Tee2@^%Mda~9nesxvq$n10P2Pj1Ta4-CUxG2+@KbvnvHt%{ zWqKhXZ&B_pU0sR5C3dE3N1QF{+T9!?goSMM#~B63XndOtPSgq>ROr{=M`BUohxzpgObPC06skm_`SsJz z;7KEAu+68cbSO_%xX!_zdZH98uKm1eqa`b58!tb5U|F*zsDNl8ltNlMg}CjN zpV6Px*QTG#O%fb)5^y9goah-N_xSv0`^yV+Necg~{1N+X=lt<3)ZKml=x0mqQQDjX zq}|?M)?Net*gIs8p~WAeObD2gN5vB)8_1Z$_0be=$PW!d@? zKf#L3-vdev_t$GuJ+Li`uDl2CkR((!5>)V{W%v=kK38u@^LH`*DH$%FXUN0YnWEOA z-(u01eY`)js10Fn4ngkV%r*+27uZd}etA4aj{R5LU6CbY^#>>IK>Uh~ETsDlWtS8p z%RqgqC+Jy!m)zs3cBG0Rl^VVW@CVB$7v$G|-xzv8SceNTQU zd)Djvm-rK&3XK$!Bu@b3%;``|A%l^rm0Gtib^HRPmI#ST$+xm(cpS>8_vJOzQKm8q zX^=xX5E!ve{8&SB@b)DZ9hC)Bbcq}p9|rAMj^4|X2$_|s*nffH`>pOX;81ZdRskNw zhZ(8&&=4w)xOC1y=4;P+LG+Vaxw7T3bd)XOnQ*!zM+NU-e1%Z93b-*lNb~Qol_?R$+1Px);FgRSl{k&!Ts#8 zGvDY_5Mr;mL1k<65xxP@qPDZ~(2H4_a>;2R=)DV}bwn|f=yCWEn3P~pmNns+2fakI z!ETschwDMAw_$Y~eJ0riTi9WoKsfXdA8B_RFRUi^Lia)4IC)3@gc?|p)p^{RAtfN| zRLBD6;3ufjz#BKLiO$5p>dv3vhdr|<{wMNN%JA)v@YC8n{wwq_8O`+214+Mo^zd4a zJuuy*2i(Rfdf3|uJ!F?1h#pRlcZVL>_-@bx!8)M_KK^ccXy|8m`(WrnkPC4g96jXE z^U%W&<)=*#$Kq!f^gzu$_+y0pvEDr9Vff=$<3c*u@I5NT2`;F3RQr)nu=hL295;{w zh5J(PFMO#=e-Hd|9e!H;@nOLJ7x`n)zW*tI?1v=s|CRN?QSht>cC~x!;k+Yk!uZbh zKz7yw9t;OqSrJ?hAPyIgOk~VH(0Vu_)}8gh#&=^q5Ui8+z{lUc9>!?`-bR?My-=-vn;Q*d_PrAf#90Z0UB{00wnZS^PeHH+m`uMO% zATX$HIx!g6NT9;i*c{`-U^bhBn?p-=2GSY3azA3J#V>46-G^I-4eI_1OM6%>wZYn; zuvvTyi(1!Uc-9AxIeyCc5McIZji31o{jK;Nav8A4xO_j38Q7A@vanzAkC6$y!otSv z=e{_0a5k<77`&MG{Malh-dzo=6)Y88A6#PZYcezD_BQJYs}r5R=Kgk}x5?K2KT@JC zV@Gdc8$@D-H#BaCW?rDzlzoaQ*6QjIcOb)lpUS)tX;?zsgr&uZz$|AFTxl4%PZ}wQ zoy7C7yg^H&$e$@~jJyeH33sokgM06t(~L7Gv*V!vl-)>+7RtWsdgO-Mz`265QN+p| zG`aoV^6Kz5<0zvCc5T*Y4OBnMP^g<%PwF;>!-U#tWpxH>S6J<#kS%{25~e1kiXd7d zGoTvmON@f_VBNl$Z8qD=f$KYm%R=N3{_G(?MVfL66Q4!X2*Mg)=$`|OSE>Si^`HHHO%(7&}Iltcp^X=hN}+|foh;3 zBZy7ov*msGr}`dPD*`*J5957r4lM@(D2fFnG9`Zbe)E0~uHQ&{DyAx?7@03HfQWpM z|Nfn4!FuH=y*I23^?s?pqFOXss+ActTh>dVor@|{WdKdh0ZCJc6$;6=2UkYZnohJ` zxHAs7b(jg_MPyeQVM{EXIUM>!eV<;^f(WS5zXM7Mrd(fRL1M9;qNVNy% z52(Ex{6P>2L;EiNu%pz&A3l_y>HOjEcxsNU`eTjnzri2aG>bn-Gt>D)Hj;cEh(EL; z_&-?df%wDU1?0f|;ZFi{Q2gO8g|^%L!Rp?B$R7mFzr`P3N8&>aWtaTnY4iO7_`~{H z9{#XKe%kzDIewBN9D3*ue;6**E+=8(5Bz-~{&4YsEmy6AEd>d z^M@zDTYeA$@}T4gHDaQZ(GSQUPVAoiAnorKe^_7Q;SX!%XF7lQ3!a)ItNvKy`)}|E zHqGJ>(#&-JFvxdc{*WQ>56B<3!s-)Z^a16ER|Mvu_`@89w%h!{>fV3I9|X<6#UK8W zbzuIm!hC-K{_w&*9{%tL`Dycqh4@K|u;}3;NIWoN7%>&TDus~$U?p-(eBv%d{?S7X z#>8EnWy2IHO5O8TJPv;cU87ne)RnUOcsrAg0*~=*0rC;qgt`cz?;-b2kPrN-< z?yQ(46V-^5#}`=3Am`OopIb|GB<@peAw_?Sdl%CX1!sJ;E- zNJ>fk2t`Z9P$G$kwDdC5E^glMNBpXbl+{G8nAhD{N7UzpLZKsJl>@tG>qgwNBT|;q zrC$q#vLm_7W{+Oz#WUXcWgj3AXJjGy!zJ(bfQUat@zELQX z)%Y}bYkXctgIx{W_6^6U`8&pE@2$@GY_!H_V?KOiOEH0BURBL6n8WJuaqxZuJt&9f z$G`T&zZHhe)sciiSTCY@Ax=`bpR0xHeYy{Z79l_E7ieXrh;1dQC&>UmiI;wY8&>g$ zPW%tPCZ6HPllTC!Bq#GQRI)Znr;`N<15Yhuhx38}n}G*BfBYul&lpj%DttUluiS0? z`YX^k9J}5R`}C&NCyASSD$A;ZY52P)QS}Mt5FFrg6vs^F2b;nJ4bZHsmz4^`h~l+^ z;$tJyggH4B{o`_a6mJtpnn(`=mi&^Y@G)s9A^_q*?TsW8Ngpdt#ZdXVX5}y4#YbvU ze!2&N$y4 z_`wyoL7WCm^Q=`TarJ;820+<+Nqj&RypW2UvEDuJjUvzm&B65&+JDC{Ixj1~D0}8% z$nXRY*~nTkg2svW9X!>_KO_GLHq+!f*rLUGHx(ZPZF8%5AI3+bcR9EoTZc%2aMcAb zQYMXW>zFDeBZJaeH3N##wI~8Rui!>=G!ht%U=w=(lbmTkl)!*fJ-r{=)9n3Wnz4Y) zeEAR9Kcjd%*m8Wp!=k>(K~@j_h_c!t!cCpPA}6EW%6TCGBGzII)zQTeD*uSrLGw=W z&%hc~aUS^*dV`-`h2Bt(8|5AZOKk-61i_pGAy6%VZ?UTdH`@qkucgHp6HK+B8{tV* zi*{h4J6$@)M(B3uIH-6&PXeJQ7LGV;sNgs%7LMtxv6J2;bk?vUhuk0~osc*x&*trT zYDW$kiyu~Nr%hFZKLO9Q7{mFv2j4~h6_Pq4I)o6OuqC@;)2_%qEtYz+8E?E1O5WrX zI+47NFW{N+ak#E+jw7eV5wL64`ynm!S9}9v4wTWd1`oeVZWTORd#U6tbb?ZCo+53Y zXt%i=(IJE&~xaWP%8U9Vr)o_yTXg0JoQ zO-~QeA#jqAM{2$`+FAtz8mrb5rxtsdpDuEDjkuy!Nqgb~=ok?wc`{aobc&WR zTclscg}3GPTWFsN*B3E3g5wjmFs=MDj3Ow!%=CZK%17gY*IpE+8}#+*1ABuvxra4y z3oCB6wlKX2?yxd+YCrai^g7WWEug$QI>V zgCS_skIF9!D3x$o?JcGWG#aIRBh8k*fPKsKjeKOrL%^Eeyhf{ejb`Yf+pugfZVHP% zcrbpqT@;ICLmNjp;=9b#-L)3JwP3;8OV!DN3Lp9cWHSDs=R?%Pv`TuqDxrI$7W;s7 z?^L30mImh443)vvUjAwSp^)2F?;DucG_(os4r-l=$4iGU{1F_|^0JHxcJ7!5gI#s# zVwUQF{d^bplLh+^`1x`H7C;03^v79K`zxA#d4GAJQvcwUYVucDIUSKQq=(gHra9DN z>-Z^1YZcO38z~-`NNdfZU!st5is0h`^r}d8rc62#Q?W!QNOP6h3UP15q9P>1`NBg( z8eVA?SZ)%E?35%;0Mm9J-l=b7T# z+)==_i_IKqliXv(hi{TZDm+ z%`eKMw25FvO!t;@Xn($DZlBR+ zZlBZ;i{G(_&eo#x3Vi~X0&8obVDKJ(^Hv)tj$%ZP##fkN(ZGPlS2VZhRThp$zrO~@ zUT|m{V72LfeN%E0DprGoEac0rVM_g(!Cgknz{((|9!VSN)LcMV9s16Nua@Rz7Ee+v z_&ZCfwKssJSl8_`c-mjvxJoT5tTX3@@i%2*+ySV9{vDtcE=DsKmZDmaHW4PO{~?gm zxpW|fi2^$?Ih#y7NJwq#7a)*?78}dYm#3mNqxvUrRSebH?bg}Q0|dIiKwpOAN>lRB z>8O|3z+WKxh-Op)z}7D%w=g1!vy)D{TeYH=ps`o^l6~Pe0Lr^7BPo%3oR&{JU%@XF z`1$-ax5#9cYPEN7h%26g%qs50Z^^sxSk^_vvkgEww4nrr>^oKbR zp^Z>(eXxnk5sEn2|J4WSAO!^;!yw_0ggTR)6^xDUNwW#>S7^FsPVtxrK$@Y2z@xa) zSMt(@B0VQ?`lKHn&Y-=pb%XoM741cXF$1;gQkZwY+4PR$@xND__)*pFTo;J^!KEw>a=&UpO5W}}Szqs6Y~XXoRhc+6)YE2lVfy({vj->w!Lm{#0# ztWXP^(usrM85;q^9BQ#`{B+gtL7-Z2vj@R1Yy^K01dDA1-9DEb<3X5XBfLxyUUV=B zRSVzX5_QL{t#lr!E%T3fU>$URXMM+pdA|TUyOBDl6Q8vkC2zqGYy76&qz3<4Jkw$f z=HMPX*Qv4MCwv!`+}oam&hdf`tOn<3!A0rRer7${i+x;}&rWT>2gWZ z*t>zeMAErI%fAXLs`}tsKB^B^^AA(0hE`D_-cTQ`l_y2bmc@rQzX%5eWM|{PtMO~e zD%vz7B};L0dr{@kui^I#ZC4}A9$=wF|MpezZ+{2Q=9Pde)LA80k zUbHOj%v4wQRnD~UwGWP0lTq}x6bjEHDkHnktf)7#6+MjNCN~GdiKt{*&a@Fm(J~ay z`9unv$Bt)Rg{LBtr9d8G1ebxWFsT56abP6pU@F{=h}DM%7bUQhI92Fx(UP$_J`!RF zm`B>xgy9M9QgJ32qE=pn_;VKi zNUK+0S-8LRo)@Z~XD8e}--#oh4BLkqf02js z5mhbHs=;^&qYbA3jK*@*46le9=+gDWaez)rGCGy2*%Ab&1Kd2~y)~19qJr_mN@P6bv$PE;{M;{Dc#Y8?PSMs(u?!qe?(b#HX zPR=k3yHv-A)#eG{@s4?i7Myg5Z<`Ay8BDBM$-w>{AGK%DL(@IagU~ z>hFlGuXgy&m{+GoUx2?Wgii^=#|{ReYN4ekM96m)c8GdHZm@O?c;@r~S?wcP{nYvn zvbsb7o&8jWf<{7rU6RXm);R`Ao_ z7d#cI#eig~PBT2xpCYb1NUioReW;CoilCopqbL8q*qh|JFfGZmA}$Ky4#JgSjcQ;H z6@)!)gxtr{@?t0iEhHAZMR(f>-h^qU7OUl_bg3IHQeC_s2#s-)13{7dOsT=c=QttH z05zr`8sTImzA8>=5^QQ4nq*2N$Jk8@OOt+WH>tzkEDcCK<&@oKbI0LQ_VljHs?lId zTBrE$?c!g-5KW8i; zgt*%+QuD*GJLdeT4P_gcEV$1ea-a3qcQB1QeCMXN9%o1-W`?&hr|kvUnc3WjIldj@ z8EG@$IDoWR1wUQu!7b6>j*?pR7lfchZ}TPQ1b50nk@HvRU%ev1YtD|S2<$oMf$9!OUJ&YzLI?W#Tow^r=uQasmU zv`%*y?_@Fuc@R8pBghs6ylGGfx}Br_9g?>@NBgT>Kz0+y*flVbvle3_qHfoq7SOF8 zggcO@L-n~>5KgiYszmLcKC40Fo^3W!(d)5z0D6_I@tXA=^x8uJ)9LkJASinUQo?k4 z_Y1o*jikodoBXtcE_&v?;vB$gQ8{~ivc}J68HQmTW625)SRhUvQ*9Qs52-tFmC8>$ zn&fvdMHxZT^Xn*;T!{|{Fp)uN! znD!OIwrp0HRP^j(Wl1I@7cTc3ph-I~k&^Dgv_e(48;R=c#PV*oj=TiupWTRhiZ}eSC z3SV%F>=rPDtU~aRAb7w=&~4p-I(u9D2v02vVWA)#bub9k7!?5pMUOGoNeIECtp|!K ze}o6t&OOlj4)W_Kfa%owH}}rnMmn*(PtK=W`!ImC*l+mR#m=3NL@-)(Dj_^O_v64} z=?7uIOn*3P3E_JgtTjCbU{NgELQ_Mi7j37_!2CGXghgxP*No!P3AYI51p*B3FM@|8 z{r!HsjS)L*weoKO%Og<}w1QBM*TJG0i5oJgA6J?(ByVL}Tv##S-n2O_o(5D+#Z){w zinpOKuf@rYQJAljD9nK*4IXNFutL+}RL7{(V$zvB9F4%q%eiQtp%1j|AATVA4@OCA@+MF&4u)95a@#4B@Q|{Sh-_v~ilQdDL8#R= zEqWM$Eww%j1=H*wp2d#fvVRCQYtozoIpC>FKHQL6s-inp#mX$fzG=PQ#-T+o1%xG) zoDai8;KN;@w|k0GPa-b1$IXc-%cEy$6}aO_w}DKx*r)u&b(*^W?9Psud>KVL=|A=q z%YtB$puV4QMQFFWDyAjHh5g~{y4NnCt3)^%@uceEz7?~^U6jj}nWhit^Us!wjgL;J?T;wQLl;GG!* zFBA`mk3=exY$QjTBN3c3#eD#FRJi|YyLjE_5>(c+z#Dj{*h8*BVX%!L%FB%29igCG zPvlJdF*pZw>(>ewXoY4gLzz7xu9Apbk-$BJoclsXyMm?SNW&=3(Fc~*W{YVOHHt7= zmg`0mMFHmOGSEkjwfOA&W}FpQ8NFc_kE=UIiS|8k=Zbh?yAFqlx^JFK zkybruBw<{POD%y0R^Uo6UEGRaJZs;E7q&FU+zJZQ<+urNrFjGw|933?TZkLMoS|+k zuy)0TkCx!mjv$@5!?wnPF$P8 zHvsDQT-?IzFm9(pbR*K6#qaH#rLP-N(x{cC&?}^8L*(1Oyj;V*BW@Dmi_>CWa7jE> z7@5ScX^-cugxZzn`nYKGJ!}-?dU88FYl^ma!WE3njt}d~N?LNJT?RKB=s=f)RbYlp z9q2!-6*_Fj>DKg8m&mDcD_K>mkj?o>Py68NP=aPb>$@G23a|K)p{7~)JsQ8aPgZ_O zBf6$loP{~UcjR{CEu{e`gYyjpzk+)J4Bu)cjTAfxa?x2mGytk-y%-J>uu07^!yhD{ zhJ};Uq~lbhC3z!nSx-S zz1Wngh6jPO{+$*$CIQtf8e3^2=(b4!&vp(%$0UIKM2p>cFbGu(e}Ez$lYk?g2TE`^ zZ^OiNpSNMyU3cONFWHLkZr-Mph#%vr&DdxkV71mV33wgPv=}vgUCdp@Qof5))91B) zz6VdzO`PdJpq`nVMw0RJWtsj0U+m2pU##^CdEd4-YQaLZ?TxnchiQXkS|4>~Z`6u& z!d*qQj9F8b%37DAoe?6Dz#0d)A_r$+v;tZ|1ZuYpj8tN!0P;!T(xjfZ%^5}?S=O|$ z?r3YYvBQ#wr4d>Ji)*cdq0_|b+W)D0;s3}I5bl|AcNeoDOx;}Ww*Qc_H`<7}9a!f_ zQS^chlxcEgT2O%tfRl;Y5iU)|1Q9n&iFdGy)6O^Xamv_;`z?))?waB;Hd?^`yMxtR z_C>8?UnBuMEc+r}W`PTJVqY`~^&$!9v=^+Sh((TCg=1lKhb(8_S9UQNYN>F&JH;J# zBCWVkf=$gqrwFd~vfAo1YAVybm`Dwf}47 zI%Fp>2FH_PIv@0a%MktkHoD|-+U#yLBG3q6OYg9XfdkH>5G1lXyn<_7Ni+Q^g3Z{| zr+Ak3?@ZGgt$ZoIdG_m)Hu(23RRnw%Vc&u z){Hm0Kn=~q7e070KrZ1i*M+*v1Z>Jzk>taul}cfxJvcf>>mxAu}^*Zn<6`@AP8G?(LaQWtdqyTKtKw?F9pH12ST7)u+W2m$pI9C?+b!~X$V|L7&RiHbov{M2Nod> zcU~%5BvE_mj$(1T@R=#a9M!dwfEPmKu(vD{wMC-Lf+plA&O#n**AGwz`z=3Ri`vzR z3K55+!wKOjSpuYLTXM;_ZhiJzN1yGsq({mlL^sgpJc)sXUQD0LyLG^0#}8X&K=8j)7$iH$y#&{S?E_f{fy zLD0WoQ#H(JxHACRbjOlXjACkk(R z$6MB`mV_&VkhCr8zu=yR0i?p)cIRgH?pN^VW zaAhTHbEb{N-5<>BmnsYf3^?J>jBvpu}sMCz{@gPm01rOXW7>;H|^^^ z73pu87lTbOvL{okCw6TXjQ)l+| zo@Uv0{pi)D=oNbOEbFwdX1_759u@z!%q6{dj9UStl~>}kbN(Btu@PuCr|hdHVC{k|g}x4AuS2esIY1L0Ba znbZ-F+uWX_lomTZ4G%XtkGcI3005&BbNimwcknFnx9-H;{+}*}^$+qjx2-`f9;BI% zB&iYbn2jWP=WiBMLLMC&o%Q0F+tZgwi+yHG-IQrP>qZ8Ya{n8EQRepGH;@;}*QS+~`R$&${r3gIzyl#r6Z<0)u+2UkbNlTgU~jVpY@QkZ zBXj%T3c~ooAXF{XK`1;j{oLIGWM4K4K_G+OgvQ(47Cw-+vt4@ z>B2_1)JC{XtiPIU>#uGgR4rtTi!^RUUeX9mka%b`{~^A5c$eE~{(E){9+wt8Y`5UM zjpjeHbsN`7*(qI@Rc-Ye%|BxoKT(PwXBY3b;Ta8W9C;yRBNQXJ*xG|PJX@`%#}vR2 zCe)noQlg68Vu8oP#wzX&=OR?<*-A#>fwi5TYJJCccA@|}8Ul6s z|B>6cjfyd6M8;sDDT1vQ2LkIm`c)@@PQR4qpTe)10iDb8kAzuL|+6Tg>0>2HTx0*hgviN5&eZBKgSMJbw7x$rMnXD zrW7N_bvxWskZb`bl}hnZLR31K&*A>w?W6R@hgPlTkfmalrL0zQ2I^Mbnn9^97UpN? z*0M?hqvfbme3xymK0=D0*>!QX76YE*E9~ObJ7}@p{B+gc2^qgdO0&)7!#08{L9oI` z&~3A1N{b$YeBM7hHT1P>pp}{yyZm5kP(zmMLAZK{HR66jm}MjE%8WcI_fg7cKv6XB zqd?JOkiXLU4vOZYfggxqZEd78NlwsDpzbW+V83#__Rce!9legFsQni5>*!+X$W%1iwo|;F=6o zKZ80r^&esvzfOuz>AJY8-8RSv<rkgz#Gij~jAxT=&$3Enz ztK+VASNxstqO1AoA)=#!QS_-DsCN-yZR(Yvk8V#QmqEUB;j$JKDLnHUl`M91TC zGK78KE1z^Q$rpFL*vYnl(1mQ$!6g4{*|vA3ZQHJ4P}6riXWv#kkg=*!tdim@(u#LN zKBsvQI41cs1VQ*f2viHG9(7vq27aj)Mg z8Z^PCrmjr#eNUl3+#>x0cDV$CHRJaIKD~T>(0}uyWBkR*)Bcr}f%j!WzppsCX6v`@3-sNV zKJ%jSVLLD~^Jr#dMA{JMm<6kbV7(@GqVOw&W5-~#kSIY&lZy9)p6l&T41c9hMm+c? zuTLufaQRV8N5glsQ8ux3K`*>}tLh}+5P;hKxYr8eeNg!KxEX zDCB)&FCHeq$M>@r>t6kW0N=9!!UFIZ0bUn?^{-61G1p%;$O3>bIWRmyfF%O3Vde1{ zpQ?ctfEvUl1Sk`LjaA2+?<(WYNIpw|>jdB*RmWKXJDhGVCBSF_c(-bR1u(YkpO$*9EX;wz7~My05lQcT>04oIGgQ{!`puYpqLV$$=@L`o^0UYB1Y$w2-0m7H@bN1r5CAs|3P6oy8v(Kf;FFan5dcbj1)xT97XiMQ$8kth zA!d$RnP&6n2HIg|5_{88iw7n7{0E9>dphhy60CfWJ<;p7w0I^B|sF55%fTsmuN8T10 zged?shj9?!R^V+Vc`M(-yic*mWiNrZHow@%&W@nhRgx7CZHoJ%WUlA%L$8Oy#Wnkp zbfI+Lgg+ePj~odDq0)A~ihP1(4vEc3+MKGb#Ma+V#PKl_;q8iiKVqZL+7J)!gQk4yV~ zcG~Zg(taP6_Pcl5?>&$h-0lA)?KjVU?)UU%aQ}We?e`zkem|74J4@*HA1=Dp?*-k~l5W2^5=mM*$U~DRN<~3`&xKgK@HaqGa9HG>jT{I) z7U=toF_NwXt^!0cAnkCMLBjmm+QORkeo zGKWkFx7|HYPd5(t7FLIH;o9^rk)2;8@aT1Qwu6eNxA%4)s?Z+L7194jH5U zR|!^Q9E^1Ud!PNp8b7vJVecu(Cj^Y~e*Hb9IOWK-qMAKw)cT|bt16E3Q~^7IqE>BD zby=-nYQo>VYWbE%U#|XmYn&cn@_Fge9#@A>Qnd~8)Mi}jH;yq1d{+)_`YDplAW{%q zUeT{g+5O&uT}#nwP5trPn-u1wm&DT{tWQ;XEZVeWgDXc*LZ7jKU7@dpzHgd zs_&Te`m~ou<(Nca^~rhU>|g)Q`i{KP+dI@}K62G3)L!Mop{A@AP8b0zH)dIFPpmV) zG5ZOVDPe^@jIRjI4wbD|eiOt9t~ydLmwFH^hq0=VIaHF5_L$oX?_2WL835?!OvH>^cu74=V^0c2{tW%RW2fy6F6LM^ETNMQE#BVnVEEA`d_uZauclh zZe%FA?|h2{#Nl3Lb(v-FF>^f}StIYVp8^vg%oTs?nXyt1G?1$JV9c7H8ImQH>3Q+{ zx9Lq%H1!kAb*KYe;rX+!JcSwm1zYO^CIQj0bn`(h<(8q%qYG z*9FD|@G=-rRvM8?Bp-qyOeNxFA{gj?V`?_W#khLch+@RgY(q|9ZCs5GpHF}O8#|wY zwGy4SAF3HrGjyN!Y7P21c1^VUZdiX+f{1t>L4=XNBEwRCNsCtgXEjN1FWhfT&!(Ma zGjrEov&*<(7d!@$WLG=`v8m}rtO-_8Fyt($&9By1?|jCtt9YYXS#V>tdirGBpBL6t z&XQ59%Odn9wP>0YogbF^5yd9OZPK{)XsAj?sJx@jHB0 z{qS9$#i;Gc3JHJldaJL3wvwo zez$yQ?%w}C{S;S5w;TlhL>&6r**X0XA?c?jzhtdNKLO*C?ERP-Sn6K63(;tcYK;lI z5d4;zy+NArEe&nVFNOgy=(Sa-sN3`dv%h9Ffmh`y(lPGH;qkW8Yn3o6t-4>B5KNeHyCcPLjg>ieB1ENGA4%RPnVh}4q=qMSxnOqlmD^qLf zxgA)UnxDQumbNbCvPb`5LMaljzKA!Apk+Ne6<_q7rU+aRfE28U=IK#ksyPsV_0a6R z^?O#xZxMw1C$Go8T~uv~*bQ!lzDX1eflAQ`G1(?JS?Map_In@VY^}mZAiL^02D8#NfNPh3h{m&n zKg1(Ts<%!RyW;?p(b3nalSPL^MP}WoBQp&(c9+AjeSnU07wm!5BeO6!PSsjal5^r1 z2c;Bv2c5EPZt-`_aOJ*v>1}wbch~$MX`Z})zeVy-zyDFk$gT;>E%V4e7X}CVv!VZv9 z=qSZNAc+4{ZG@`x-1-M&z}H4mgAxAPhgLN0xgGPoN;M#mx`^_joL>vP{0XOtQ1b|e zZfy&Xj;;@9=~?6QOKRgtwi;}{3m(Ye8x4?Hu${h%I?=n-L#zb>21W;XI+W7Fq1qy^ zaSyE%0l+N$s&d0=5l>(w|*Xu zpO$r+NJy(VXl%jxNKdd9Vd6kbB!j-jF-(GUhZ==Qr9BGSN#@EhS9u-3dZcs&UAWsn zd*$rU`};G(SInN3lM%ic^&^_)c%OmPpHl<+1m4h$59qnf2MQMA#{HFp`}1_%`qx06 zItvqKT;(_J2*~`nN(WMt?D;8jx2hGV*V!|2G9m??QiQT@#Z*9I5+mbDz~WNiffrl)Qz`uzchvzkM!3djO!DjW z5WwC>jYx~Ng^yB8B;Pv)Tq21Pa9qL?Ia4i>c1X@#AqiMv<`*?#6GyO)BT|b+X##YZ z3zjgK#as5W*lsP0QBdmxi7Fmh9S?3-`fGKH6-|$uOOM9AzH2RHMY$G~I*h|;k8`qI zjq~U%XzQ%=$SqKIL6?JpSq8!cJEX5$#@yf^uv^_X5`)CQqwv>Oh3TOs^}%Nq@KXMP zwftMr^++3tZPth^(cgzGTo^FuM6t@2|3?(B3g2nm>BkwSztPKDw-ebUoUty(U*o3% z$dm6O@Vzk*U*6XDYPpyA@P9D=kH!D%wcPu8l*FGU_~WGqV=_SJ*5WxfcnG>*phCSq z5;uZNjL;HjM2s0wKH`8CJ|k~8*OTvMxbhyF$~Dm-Y}#a@=QU+G6mE%pQST$kWEAE{ zDkJNSq9wCS{*X~Wn)gZ>@V>%;K#f*f&s_|$ZPA~>P-|xCJE_$))1qGql`eYBE#%ik zx*OZ*IG;W)V5S__$07N!XfFj7b2oN{YUUyv)rlr5MpqJSf<{z`CN|_`Vx2P@p;3+a zfg97+Hl-*2sOg$RA*-~WpP2P!f9ZucGV9z!o@6To;uY*RI94NtEO@c};4TMB#Qh6a zW&34*^>Ca|!83bf2B5>xMRGf|!R*{REdW53+xbVQ)-hqKGu72g94_lv?I-*OeI18w zqS2d;$7FK6ahr_|*c7l-QEn!mB0?|s-r|jx;OCJohay2-R&>kEe7!D}qwmI2^%=e3 zAscM)!GHF`@b|#EMugTzK8_k184~ldEe+Ahsu{7RQ<=YX~1@zUlZ=5 z?*`T7*F?U;Z;Q5PlyZw|Hi}z1ZI51OwrE!W)R_lt4>U|xdoVmb-0i{fiqq4r_Ix@U z?LokxU?W=G+iq{8)!sMdeG8iGdEack|1;j(DF%!ErE93wH=s=!pv1tdc5(du^h{uk?XiMAySiQg707&-dg15d0sH|G(05^|_c}{CO6CaKLsg z>h<9-kOTEml(WR&J@@&cK8Fwi(9p7aXwdPmK4Ze)p^cjC!;WkeJ*$t;)`gz27Lq66 z(gZy50HaVli=))_C@jHyuCD#ao=!|A!6CyKx~NLf>cpC75VRl&m{BIz%vKo|0w*w z7EE(C!13n^{Lyz~T&wX#9U4@9t@T+M;BP#bT^5M0#~D3*0VXtWHw2v2QMlGvzXwOe z(cqFV#&aHmCmFsTVgz2qeZdEXBO+~)ogA35nH)e!`2l@~UoU|Btf|((v#b26=0we_ z*#FVwmHYe|9s75Y$pU^EN}gU_fH;NRtpjhH1%Bac7K1%NK)yWi+_8EZ08Zr!9} z1L{pf8{(4@3Mf9)H){iO@C-zoE{6=N8r5{jCQN1S7EEPPHO3`#Q^_{%<>0no9+naK zC1n3iw>H0D(zxl?Z9Myo_9rP`Z*6KiKZnKsxmk*RN0E6v-MtmvqTPQfs9BP{Ll}qf zi?b-kWY118U;Vv=4=Z-{Cs{i3z(2?mAe<*}QbNPg_aXuN!V0d##Ai3+59;9ckoXoGQ70@@za2b?aYeZVvaR44Dai z#nnEEJ)8)H1$ajKEZ>%3B0v;wIQ3W_k{|hK5foA;R0y%@*alwM~a3eToeO96Y z`wR$j!tc5B;gk&Bo5Y*0WWL+O zVe>#kZLselY{NySh&o8R5I$FLN`0m(D6Ns9bf#r1Mx5GBG^)9@M&`zOc^YiXQ{XCz zdS3;`En`%Ea65#t$b+c5zR(_lSEhSLKr25TRAY@ol8(*}`?k*$BPjw1k(jssjq27v zU&LeQ(x1r!6DPHDj7OXCviZ+HP znQ}94N`#6|GpT6vUF>N1dy0x!L5Jad2*V623N|@}g4mKxkr%y$vH+M|L!p&l;wBWv zKV<8JYdcKi9}zDnHOJxC)G<)`6WO1)TQAtFR6_TuOTfNKfxmH=#c#cSA$ zJsSYm6X4GRuyG~rd2Q=nb0mL709^n&H|)KI0KXD|&JBB~5@4JFbZ*!iCcqB_pmW3C z=>+I00G%85-b;Y(*Kr&++cv$GwD2`Yay9|h2*8%tyoSAMB+Ci#lmIlp>NV_DBRQ7< zr2??^6|Z5h8p(MCxK;qRt)xA#ZQW~*H|$+SfQM=-jZkfdID)K<9?NYY8wx06w>EdMjz+YmQ_i0ZtWwFJAK+_NtNG zNPxoypzT$!VXqp=CIWnR4aecjSG z5+FJSB7{<)Xw_D$wpgTLsup3OJfh_hEYR>!L_mj*Dp3Li zZ6VMGQd&t0RYU28LJJfuGQ(7flv@ha=J)-swf8w^9%-lE`~Ta;GHQ~16>aWE5-cks)dn8tvQ&I007k5SOk!ZFayX@V4E5{DFWwsS zb(cE~rj8U{EZ|5r1MR6CZ9c*6if5Nsqz}bbUssaEWQjkTI04m(`2yR>#e5-4?zw5o z{WMF$X|VQLi}R^Y@w-exi<59X(oTA@jco?nkLagzjd4F4}jLG-96oiD7osy z5UXp7CN7QG2KJcBCBJw%NP^G!36_Twuyt6*OR#mlaaw@2k-hQxa!+Wka?8EiM9}O$ zbb}4UqC8f}LT!5O1}cH`Vs-PQiA7k*SBSycLssSH*-w}HpH>B*v>h$1v^Ty$>L##y z;|onY49wQBF)acp9I|cNk-MCcK3CeYAXc|9n&1JF+Kzrx8N3|y6Jz!1r#1EyQGWqc zpd>vL0crKQfoICn%kaXkWW=$OaR_qCItKd2RSHeo@L4xSx13mt=^HnAQc!pmq5FEX zs_{G~zUQ$XbR)Eupjp6KpE~(gW z7gk$7Ra(2)s+SNK6o6_K<9QCG2r+itO%j`7PJ2q0etvRjmQD;MfUBTcw_x{$ECTHs zTx`|}rdgcYxdRw9ixeSf)?wKbg7u$Wk7Gf@{^#0~8iqwxw(VZH5PNkkG&wHfZEXVc zCw&`l1yoGa%%>Dv$YhOp8Wdue`4IL?55eVXTR7Lq6qvaI4UmYk9xTCl$Kv@JdC%pi zgJHk*;;UFyDA!l{>6RV*)Tg=hGje^BpQg*F<(|g@3}@V%U;m6HAWxziMZA=s0(sv= zScJ?i@^y()+;HC2{JK?o!BdBkS9)n$dmq0(gs*c}kq3w?CLF4j>K`p95!dwo=ygz0 zZ)CR+1CoVByz7m(@j^1%8(%E9k!nLI)(Lfh;Of;b3aF@Ey-Xj2THU&~Sl!}i;%OL< zl@P2mgrJR9Usl6)092#vx1c=jX1U(=>p|phtd*3RVF9P6TLHS3ey)Kvbj0d9alXA@ z!+KLg2T4uG5O^AI{|`t_0g9f929!8=_JO}V8`L&bgBAs|=0BgzD-e*mga)0=+0b}2 zXG}ZM`3m03Af_+GZH7VO;I^db+b)QPRF|Oe%)OLdK|NGZ zg`#Nf?AXM03{(E#1bz8SeW~@og_qe^n3qJDja$z*ui;I65g%8ZTo)skmy$#)+3<&$ z>@g;5XR>>kY+H5#Ug-3~EWQQrWJe)h!j%!3W-$|M1J^;OtN6HC8HKZw6aRd^c`aPe zDSY$S>=r-MB))i%j|rABmX9tTeOhw}-bi%CU_bmUc%t^k4FeYDPp^cVz^l1Ra{&Bd zW!0r7K0n&L1i}R7qgy}U%s@N)%2)giXVyTVka>eiq5(Yp$+FIkjK~)oym%WMx%e7R zm&LKV_Gn^)FRa)JNnaBA^r0_2SbmOAIA#lV$|SY(bC_|h2v5{Ez#y0a1ytEID7-@T5{c6+`X`_X^ydUBf|~mZE_+~e!5OR_47Sx20x)6SMt;QB6_UzYTLrO&fuq& z{4{$(hVhS^SIrXf0;&|9jY4wKrmF1GOZlBp;@{gL0FMyBlVPywdlk0mYXKn`vZH^8V4&he5O}Gk^L{b;1AfM zaYDpBDBur|l0OK^EZ`8s$RX-N9Kzr!l{v5r(j;H4G@RjQ-w!y$WS=vH$^V3Hxzyj% zepR=_5+SOP$zYj)A^)F|DwO;`?vsDZe1!Z%!VJ@XOZnLuVJnaWMY*@NZIlj?6F>$n z!U>jK5paU>ew6_yC}0wn8SNpHs0vL@&PzCgQw3R_UC*k4KO(b5z_b`7b){bkX9mOWG z?#TFsl+nW7QTt=^1^nTw${+TCJD3S*^%k`+872|E?0?N9Mv%M=%_Memsa?gD943*1 znUzdpJ!g<5)S#@h@!l|rjVk8*{NvmFBup0?fG~*;%Op#y6O=g-U}X+rmVZXu=~s zEE4yEx{)-(h|$i=iBFJ8D8(*Pv*T+Khi7Ysuvf;1s5!Cgn&VP4gzbgKjGf!( zpyq?Kh5E}7RUWYGD%T#xd2Xg`)Vi7Y{KD54-a2#F{NQQ%z7YHGa%8>*R-gDiz7NWj z$t(p4(*>hE#pWbwOR-6<{NhrnqbSJ`Pr8#>N4&{Q)BOxTX^J*KLw-_6eWSi9R|1kL zYQ-Rxs|Ay$L$TXa zTs|5_??T|&ijWc9s6~CeNX-F89iCx8+>n^%#OJF9bcQfDnMFCwt?MGg+?0c+GO8I1%o38;~!B5Si0U70Jfz9~KO@~c0SnYBTuz{`IeK_nj{B*%7Ys{nmNau>sEWoFK{%mwIU_IBi+Jeu-c`Y_2- zbgC>R6N$0H`>^5RH~~(E5(=KEIx#=}$0iJNMfXbR;f9}Z)$(F`k>;Ap+M>;$A@k#z z(QUbVq-nE;Dr6>w@HeRHBScy7d4Y$0TxgMV0VhyTtZr2_vD`NrSyjCU)6<)cby>`? zKAmA}c%fa|Y~%?T&t|bt33^N}FissM^{k22^+ywv{dzVVlfW8oIjvt~3~WZlG9t1;Hz8>tL+3i2rq>pF%n%dk`r;cTi;W( z?qDKTXOAsh&xwa?uMKL4RbfIYwQo6$6M&rubq?(}+Q*XT{cB0yd(c88sA%!&zrUsY ztPg~86>GZ6SOEieUi$RcC2((hsG02>q^-s;+q*$m{M|}x&g;mWnVNa!v@pR zPvK=|82})rm*u`xjZwIdpC*ameWeg45~Ss%J=cs-g0$~y3d%H(T!=S5tb<)X0Ci(s z_I$6dQ;EbRNoK~(PsR}wZ=Z8d!wm28Q|~9KhX(v-k(_k#r+uW0*k{DH{r91uLmBv1 z{}Qy}azMlKfAud_X*gF7ZlSmfgPz?V3xggQ?CpF@TRPZl*b3_Uw)8*(#!9?UioT*| zbP<8;&m*m%`8S%_;mfCX4IUz}e&V4^7}veQTFvyPbS!4{HpQUDykI{1t6);IEWft} z^u(BLd@luSS5bsZ|Mgt9MlJV~{3=7H8~rrQ|72IAOA+?Ht4DW%2{ouF!DvK>j%#{M#Wa|If1JH6L60~rd#uW*D4$@8&oSUX#ZM25_-YnzeWCY0erl3W z@NsAh*VV#LJjT}danR4GtC63U%O|nX&;npY<@5OI3-YPYt6dN-@N4|Eot_8~eh)@V z?WV_yaH>NQ);%OBb7Sey(5j3pcevz9GN~c#K97^{zJ4N7Y|H+Esfx^M-F?t&HIc9eH@v$=+_oCWzM`f%f@=)|~a8Vl%g;FgD-nOGP`tQtnD$R+SrtTf4Q6;!SErbf(`ln#eyCw1afaWi>s1E4?x z#@HLr2DFhP_o_#T_2DojeuX90R+u)lId$!Bx%Lni+adZBX3N&|#1!{WjJ_#rfm650 zse}1pmDV1X#o_l17FU*jLX(5vV>hh_>mrr_xkG1_gC#AUN9+jjJB3qKD0I{wP>!DT zYmqrJo+ zK9D8D>juNcGwpGfA~)u(v`JiJ8*oZ2nG zl}eHSb-2<`WTh=$eMkBdG>49>GjQbqK>=lORmMT#yoR9o!#9u^7=Ij681(%jGXYAU z;VTd#0B$61?K&S~O1bcwLf)qy$J~QQ1+sq_r8`L*-wxoG+0%Tbi*+yK7RGay^mCI3 z33{1u;%K&WIc~F$Xy;I3R{9A~9^BrIvhV@7On5sJ-U2sQ{WxE*E!kU`^tOWdgxK$C z*LvYLw{uv-1V&^S=qnpXn9+|c|sLhEcy@p?@0#*rvuxQqgquowy<6n!F| z*yY!G#lONdVn67NW~u(%-4${HYUw6KAGWxjHW&q&Y!^8E4auPxxse{rI1d{+b}^qu zN)D17#+{PM#2mYr)NdWnR#iz39Q}s_toxWFEGgzu!7k>gO~}kLWRNo}Ea~PE!kZ<> zJjv1F)Ga}rFXm|D;1?%!XG@Oxl7nOp(PNln7qsjXlA}d(VCo{%BCj4)ZJ{Mjm0m4Y zWYQogWS8Q}{V|X!BsCvEjF}_!{gpTI9ijkekT@eMjUzf?Ca&N|uqMuP;(;miXRk3& zfAKu6IAe)Z_Lxc~7oW$~u(wa}7QzZ9_960Ccx9!$?dRKadApIbVB7voo95drZV_}Y z%coRw^Gv_+_K*_QZ~Sr zz1Qi5si_w-L2vd^r6bp70C@~O(mh+1|iIk z($siIVZfzZj~pE2E{0oK9yS)FBOo}yumf25@evvkP|PVfxCzwD>?A-K35YM4${4#Z z;tR*<%`WcqwEIgX+V%{@R~n5#NVtqbOf2MghvR-ZC@5NuyB@%Vjh4XexZzfdT~P+7 zH(mBs<@RujN90$;s#>>8;`8iR0>Tg?KD%I`cS#P*K$UxfaSo^aOIVYSZV5BcTX0Yl z0r2U46-kf46_zMrn+zs~UUKB(BXPl?Rck4f2P{9>fm1;#4$8jpS1ZUoXE^KC5ChnI zk8mEC&pr4|1ioHgvfc;y29x8rxMp8O!p4o+w~NLDJe?x|9ySxJfLIEYg+K^_(Zu1f z+(#eo!*&mbvdiv8`wqPU5UAd6iBVR;-7SVy>}I6m%hPp}Nn)1^G3z&u!#ZS+PMS@a z03e+*XXK|6j)APsjc5#v{r6domtpONFJ5$H5zd@px%(9gnrR zn(^qx?GWQ}=^bb>@JaGW^iJ zz+7*l2!5PdPa>ks#POVCQ!5%`6MCZ6`$ely9fkiX_`fJx{j(c&2+43pk+gz2`8enbnM)nW2&0Vx zd4Cl_|IH}~>Dp@%bm%F*ZpLdiH}*#~7nJ2oxQ6zp?Lq(VVHZOEp9IvXt55)(TG8;8 z32UR(2yQhMy`PHSPet$NH6af!ZOpnv0#O+I3i{vDgSSp)%hzjOY8-(3SzdPu4uOTz zP!b{4OH3q|^S1k4ziY2h*UxOUU7v^7?D~{HXxAs5;NoY#Ocd&YMFowaC<>9u zgZ6}2!f4Vdzum9Qi+rhR_JhzN(?w%`!e^aj0)~hi4cR5$WtRI8!vpIiHCWJyZ;U1$ z=W2RwI}R+l1o~C`gbm*8f$V`W>tIjk3AFUpwCW$Pr?61(dt{8P&qDbxKKXDe7U&?_fFN#2w&6Q_;&Q++U1W$@CJC} z!2VFb?m4)aW6G=n%9lSqR(2;5Ez;$`L6{`COV%!@Nqim?#XFH}Y@gE$p|s&F-55In z8K8EvQE&l2i>8o9ed)6k1wtHJV+GI1eu|$W=$?YJ2PsuPied!*8vzzV2l1sKM5J$6 zjPcX}F8{>YJ%bPmYnRv5E{QgO1MHRr4#>I=Ukqxl#;qTK4 zCj!Pg4x3s(w#5vA0f1m$7uht7J2oQ-!PQVXU{ zmrk3`T}&I0a*SW6gwLY%oE|uuga|mqX~WQ2GHuYGEu?k_{uigs5#cGb31|YLYdJ3% z8Q?mXO(_NT%FO7*%=jK=MyJk~|VEIA%y=kr_hMkl1`cxtI7e zghrtXH*#=jhJ5BNfkFqO9itkOsEru0&=ffb?Jy|#%#L7+bVZxv!4!E2U(6KwEpGiO zf`!Ck?s>r!`MFM!!}HfmFq%3~u0a9~S&exDVrJ(Fh}l&KaGs<;@`KQ%sF8YQQhXt3 z!Y6UdNwMMgMJC0ipXa0ydvHmfzh=GI%#*kS!_>^g|Y>@s6XdGH1TV!H%=Jj zVK9(My2W*%^=!}*2f+>p2OF-oVhIZLncXW+l1UoQEXM9;<+Ho>uB491CWgi?;t2sN zdt@z4zZNA-Uuk>*@bXe@1wEvpHB-EH+?FF%A+}AB6D+L?t&> zV+_8Jr#Y(#ZV`;&4A(jZ1?75rB>Cz`2-QB^m9M#5rNmdjSOeY2UdEHI-;ErE{o-Ue ze$-wszK|_B_BgIRFN)Q*MH9p?bp^2N@{5E?LmOGFv!k6CZg%UCo5)^tl!Z*Vmsh_S zGcM0@&n*3~t6>%GvAPaDc0#MLE9&jsZQAZ6Peiu};i=|&I(r^p>~mLccY=#*H`8Sg z+fS9k)7PK$Mru0-xgDUu}u#1-y;!S%u$)cTbNsc6xZT*PzF7-6_SC!r*$!K zUe;P0QEKK9(qgRo)Gn0QM(jS1pKAC?z?Rt?eIQm%W?!CO@xic~6mO%=H=|_wS@8TS z`Knqlp^7avLLqUtQVhZqvNyJBM_c21**^JJZAoV!~;F2hv#oy+B{TVSrT@vg9JL@qOME`8 zd#y6J>st_BL-5jB${Fj=^n=kEf2J2k*Vx@G|L!rnoMoSWSLb-bNh*nHny{L&G)Pw* zAsxXiKQtTL<4OMaiGKCPyh`rfD%H0VS8}NgiJ!D6N&F2Oab)&*)q?l9Ddz`GDNgT3 zn8sXL&=^;ucjQyO{{$Y-f60eO5u)d{l4hiDdxxZNK1turysc{@>AQ)y9-%=w=qfx> zs{1akkU<8MBwdg}rbRO2t&7L}F!h^(nllJ*3BtIBywAhYNrq+~&rfH{C+N&g;as2Q zrzdtW*DB4WpV6NE`N@$_U8Y?u&k);x9F4F4mX<7+jCv@LsqwG;x=g-ydY%x-u9Q?5r901b-Tilu!CasU7#*JeI$hJ^$ybv^Ij%)Uc@tYHUa z5Le)JOBPy_7jI&L56SbLc=qeK6R$GYaBT5XdW&=pUUH0&(gvxB(qLXz>dV^2Y}18! z$zE?|GAxOv9?Tf09s#V(T$J(f~scBNjik7D%M&5evjz1ffer zA;_Ny2#^-h6N67_6n##;3mBl!SG^6q4#&4n9|lKqwV0lPZydsX??iP^K?{WJ{5CT$ z90hIW4W$p(J~mD|CEef}3%HQ!nV1pL6*SWCO?2)b+1;Q=pDyT;H#;5fRQz@-rj;;* zplR8?B18lr`XOCl z0PRz8a*vH7mj3sJfR2p+m++bp%r4?!grIZdk%Ug6eRVcs#bZSb8?*#6?u8L7;JJrj zEiczhIQBV2EwZ+DSX#oi=AB2uIyrH?vjQq0Ml!j9Ch}<&4K=h3?2CgGM&W-7{x8Bw z3Qfq1OB;*u&l89JY)%i}I8}4h-X7=C#*JzB*!Q-wN$W+`pI7&4^x?H|1Rvcr`_;0B zS}&49p0cqk$8Pl^_U`S}riCT3oZHx|&wo*v+%0k+^Dj_U*VAcQfs>*IWQ zZ(^q~cvE(a{SZL+aHuj*bE|0XL9BnSVsE$OfK40Wakv&U0OOF{fbqjv;H0#4!!f%X z16lPbg#UWNe>0#D6?b~XorEFSj%go#_??=k8g5LiZ9G~wC;`5k2wwqKC1*MfDxi!{ z4=Bv;$p(1pMIIR|(d3Z{giGkcuojHeYN(;02s%u}buHs&Q^p0MG9W04Lxt2z#WfhF zZ<*v@3?)y$K0}A;r912}4aa8=Qy<>gVG714UfH~TWWxzR9^btE@Mr?jv&x#cf2yVN zvt~ZRM-3V?_P7wE3^u(AD?@hQ=ItY*i5HmOoOT4!+)k=P-D@KL@aoXlZyftdnDm?i z88Jk9?mGg_h5c})5K~#*M(l0U9um_(-y+16iZ2vytW4l^u)W;VB52px6*vpv%pGow z_Wx(F;3~s5c4fn4uW=xIXh4tPn_rq!!$FICcWicoS*O}eykB(*z2{20<0iZfB6Njc8RGR_{f)! zhF@-Z6C%aBfIjQ|n6m}GLKHZg)0iduM&}*_q#YccobomfVZe6CL25DMAsB!(ES*Ss zrgaj2!?wd{^1TWSMT)7=_u*c$j45PVffFeqfuRm@K7nTGOW+FWrXolJ8=iQix{?8! z6HmAW|JS`y=0qs2_YgbP=d~B0N{&WeUdNd0I9#pO>ZHQ}hbNn#jA$|^#gEP;C&k3p zMK;#~Eut=~m3~e{v4YoGH4c=t&S|+mZYINq5vo}L|f2TDyXCvt!NM-C7JZTTYYF_SomqtqE$&KsC zX)H7*1-aIFF!SSF=Q_w}YY-|QFli86rR5Iip;S=p9Anzo^M^NGeimB0WE1a}ItC9o40shB_BPHjFf(AUY3L_Cg z4$C9I&D8f}&ilRJ%>+#B#2mK0Khn~}p#MZL>HqVb>!mEYZg!K*6iaflv;Uj5|1!sI z|F=v3Es@yW2$g}b*ft*KuXkZBSSPZyZ)2(LgN>Y+5R8wpk+TtmJICiRZ6D564e+ch zD#N7XBj_o3KLG7B69dKpI1@GqQQ`_Vw}nwEpkbS4bxVcBb}s!Lf(rEe1^v+dP`Z4*UADM4LGS*Au)s# z!-dBLVs+o26v7w13R(bpeLAjgD`zr$dA+&L!nHTvg!yL~89CsUQac-5*8~nIsdEE5 zD-)Ce5;iD?!Qb&g-ud2A*5lO86IPhOIo(BB;md#1rY?O&n|eR40Y=}2r_2vQr!7T0 zv_;qZgr0E}gr2>L4OS)5+b#L@c1t0>J#8!Ltyrz0UBx$I4RS3PA!Omv^Uh|kbSW}5 zgZDseK$VOLoc#+&{u?lMTc`7j*cJ(KF1GP{BCfjX=$om>f&RhWM!-?Lug378P#SC)loa7#GjuCnIkvaj!JVG4VHSB(rRDa*llt1SzL(J=z+g@uoo?s(=DmKWf@2`@GPh}dk)}Bf;^$t0 zQnSP0{Veh>Oa6r^`5P=5s%&pMfj2N+%Ki1Y&or_Ty)BumaP?$G4*Lzzg`4m(uh!cP z5VG@7hfjfzHg&w^pbo(NbzY_XYJLG78IEhv+hKTub+%V$I@$uyU_OypW&5*S%0)Xj z;utXIU59JXpQrfbGp;VC0o-P?M)-X9WdDr9p5<*j4%Fx^)TcCB2E%0B0eJ^{Rc}tL zioDnu+#&CKtgYhAfESs#h+Sq~#107g5x^g#hyEBP_{?pAOI$3zD&&u`9Q0V^qP^g2 z4eHeRh_1Xh4qx&2VeoKBZbMXy=S+ko)p4QUegmokqgpUn2BTW^i8y2?_@n>aImk4P zuZpw;6|n(3wczz8);S9P;*da_G{_Lz&0$})1!#bsLxEOs=31FP&Ql_IO&C;15TgZ( z!yk=8q5zse4&3{=LW;+~< zj#v@&g@tme9wStXp+oq3_3}09oeOj=NBJhC%C;A9iz7+__$+UYxMySN?!r{?YN5NgONI5=htt73yV*A0#wYhckcH-z z_}IzEUc)SBjINZ(qoAJR8ibG*Crh`C+4cKr9LHVP6{}kwO;n+gImRl~jB)M&eU&)v zmHM4}g*d)@Uq@RJ@&KTc(DUW%-TGD7rDWNyoM{~r1tQ2S|d4eZfS0 zAjDljTUqpY)fMq%#cRVHS?Z)W#S8!o0TyzFH+vArlpRGi5fxhPe7$I20-f}nzTtyV zON=&B0VRQgiyk}^eR#l;15C09Azn`4j|Dn9uS7bUcH{G6@%f~qlvUu@mcYOY9bJ|& zbQEriEI(&&*@A|f;MnVWl&O&q%P|S--h2A!k?PG02lNHHxoo(rGP(G=EwR47@kO{J z)kM7n>V8j?PV!6-n2{TAg-J8-sD!bmMM)Nn!A^6q8)nM5m8lm;ykk(jzMAiEK+7Vr zZVtw^-$pOtY{3EV7m9#*?-)>E3n%)~*TXoAonXL$@xU#-cQk`xM-oGjcvoHYzw& z6ffXMdsXz|r4*~^O1mZd@z6M{v>RW5Q>IJgnG0g^g^k^85bchHJ80h8H#yK!SmVPe z$uK*$UE1IPv=VT}tDO@3DWP+Bo?hn5C8F%+?vH?6;EGy?5kiYu_A`Kq=^^Hp6DZ`uwFU8Txx1kkA*NrM5IzwHETZvh-& z0|N>qJaAzkgJ1#x`Dh15X)TW+06(MprE&T-IUtAE*ml#x5(&$62I?XV6$U9jhnASL zMMkkvv#a3D|5e~Qb~r$CzO4DJh$DVjuBf7Pq(Z~m(J^6fKyH#aSlfhNR-`}tZ;%f? z*q|Q(H-|9%iMNQofaX5NTirMDAaAXy<rpB+!fhFKoTYrpG zsQ-DqeqCa}&c;^{J2^lX4n*U!{|)Ou=r11kk{-0P2iyP39xUNR`v-2bf8hf$)lALp zn|BUoUgyTh+$p<>@BgVACiMuH2bld%f?uLS-Ni?7NDwN}!pElU+h~I%r@ut{CcdN( z1F-38+!LF=$9Ef$Z1$S$+r{}mXA0OP#JFB7IxBmIOYWim{`4Ns5T}3adWj*BCk-ll z^?7S2)8?{R!&4Dvj^0=je_(sg_F!xwT2*{|ib`KH(a!)&T@{0L1#--_7qIp)y8jB( zflj)QftCOpv35q|-Z4KXX-r=6<4*-;aZ=&5D;G4CGoA4YQOo>Kj z&rS|dXeWgJ(ZO8-D?IEBttBt;$!YWJ3YJH>;0HEAKkwQloM={G2w2{m|HFn=Vs3K3 z5rh>qpSUYi7#*x8NYZz&W~wSSe<`@XG~XZ7oaVO`XZr=sA3$)|SbQnkPtRJcP3+@SbneyrpaAi;*Ls+_5&&^aC_RFPDEiVS0`GVs}rsG_dZyRHrfz2 zor$`FDy&eI-gK35vVsN%va#8tD}9*XOVWWjhC@oeRm~GzUQhk=I;=mii$ZK z=`iD9$~G=`u}}7kyV)n3vorQN>cH+QdANoB44@#JzUCBbOkBg{jq0jZNr!Lq>GZV5gtful>CxW zU{RRdL$+;&C4`Usmrm>l;BL_@m{(p@4v*eE29^{>yQ!+IMH3(AE9ue`Aj@>Ak_|H= zsEHCN$_)02MicGe!=k5@8$E>zjCVXtqS0g{zH!-yn{Jw~>VO=&3jb|b{EjS^B&We(fyjn3>%;bHsc-8yXV{fX=_nkc7IY&iM|rD7qzzEwfh@Xcas zQcjo2+6P=&Vu{6@8kg}fRxmpw$dk!~(%kcscKsp9Jq)-WfK`<7hU@H>R~bL67R#PK z6LW=%ul6wEE5q%%%JAnO<`m#?>e;1~!EUFQ4~Ie5qRdDfWSRpN6x^tN9+`B!O*;V} z4-S}e38_(45=s^O19q;oSh5h{S}s09Jdl2?`taH+uoV4)1zOLuk1h%b%w+-L;j2zBs0Wn@|YpmEkZq<8V*JQ zJ%K7=2B9*fc<5*_|-I-<=)}4W6i#PPf0-F?ckKh^n@K3~3O z&O?($-#N{sh5{E&e3uw?JN?e>Nq6?G)k~cW|F!V@31(pSkW!gQLC6 z;BloUg)%^vZU-XQ6Nc<0M{QB30wXG{G`cet$c!SX(UT*D#dBQf@Dib`*k z-8(T|ph{;~yAZb1LniB0jXQe}>O|{M1;kE>Hf2s@1#82gZmG|l@gB(TYP=k(o#Ww* zE5|RH6C;cFWCWb=G%-FO(C`#+#Y(dNJ5S2rXYnF-r=X}B4fuwyXM z6HP+fFH078yKA@(4wVN9LBnFxNiaayyhru&B8TU4i{D26(cB`I9Rqob^W_E2?@pN zejMb%SbSmviRe!cAg<~zCfQ1*oyK&Z_nyeybT(-oDBoX_L=!73$UrFB?XLwSRt_W0 zSyNoPBOPR`Ks9CpexMY+Y5;+%eM9~lhv*wed;E7s&pO5aT^ zkM%fdA{Jjx#admqpx~KKe}65-2ChpY3Oy87vE|h~t>-{T6Nf>B(LIz{us;*Rp9o2+ z(C88J>9L;&*yJ4a$G-;5bHpq>Ru@Z-uV~(09!(6Ql;|z5>;-8Sp6`j*F(A#`_agnq zaqM^tVvjWx%qkN@19orWKNs&;)+`*pQY)yb?QghI5@U9D$x7btw$hghBA8Arh$vLT z_TYnP>a!L^3LJ6Li6-A5ff7U`E;jE?ahdc1Iq76b{tFlz(I5h1MY}9G@{7xWF||EZ zw*P`#7w)0)HSkNo;(w){6}g7I3_MlV-S_(kco{Wdu_1_6jT>aJ%b^1=p@)hdCmS5> zZieebG4%Db_O~J3V#yLxB;vnxln+o%>vV6ZXgJ#GpRjtDv7C-aRHGhL<4nS`M9qth zZ+nr0;hY@rphN>aBTpr&kszwK+=w3bRQ3PIg4rys538I;+bn4iY5yu^EdlX9jQtDC z$ufvaaxl4@|LPASHED=&we2)WG$_lH>3|$&xIVQ8ov2*`<_Kns-f+ASAi;~zBe$~S zY9>I}18Oi=1Ih=FVnOC};?)RJvb!E%Ze5v*&hJwDy(#GTro4W;f3f|R8EN`LodGOk z3KCZe*20b=G;rKAil7NV+WWA}lOTu!Z!%o^DH<{;(kz+rf$g~n;`6mhZ6C-TKz>@!&pytLXy_hoxc5D-d;|k{knqA+? zwHJfFc0eC3Vt37F#ReM22C*m`dwlqt=BzQXCMKJJ$FkPH+O#6@Q8881B4Lh>6akHdPGy5)L^rU4?hr zOmj#6nfoxj@kR7xMGR$Bsp<-yCos!qpAT!6sy*+3X0b3(yf<+>C~K{*M6Re2ucv79 zfuNw7GI#;2E+*~~m~DbH#1*zSz?a+l1`VQ=XCDg7KkvQN{A}5KFJ}G)0a^IB3@?08 zTv$!oIMiktLe|wSk*osJgu=WvzXiq(z%IfVD1}@MJaw=CPl+TIvUnScJl$Wp+fqxhfU5^25ivcz+{K%Tw&B$e}t2g1cI7`v> ztz~~lKVf$~=n`meu%O5UJ_(ula_4uj==J`mgC#LJvS=Q%WbdQ+CMSd+Y7#%o#A_sR z*5ly!@WnE`;IPYr)jzSWA$pi!(?-5I31~^AmExYp8`F#@_>syep(F*|&o>OGV$HTz ze%>fjg9y*bsX}r13n3c65C-?{k0zdH670&{$Gt8x_9?(KIS=?Yp$A@9PIA87!nTI= zWlpjMkI|cdLOPf%9|;}#CW!PJ(SRa-P2)&N^_s51iuEYIP^`zxK&*FOfMD;YSUSY}eS4!P zz8Fif4v6$pcUY=FwvSSMeGr+;$@`Hw|FhRMpD~ zT;R2h{7z~zezTXrKV)^o0d5PwxJ^vPm__BHz!1)^q;@5BFmy5+9>H&i?qEdi)^XDu zI(m8eBnNCv%!l!2Uys0$3%^Pp!VbvTdXbk&l42fl&TC<#qRBcUy#R&NU9w|BUHCrs z=u=t=;|8|kVF&SEUgX#1J>EIPYWkhl6H#S!?(+aJ@zd#UV%G#YCv4cio5%`NBi=c! z3H(MAf0Oz-1kuDLr>U(HC;a(Kvejy(q$5g_Wnnp0J$#A@VfmKIB=^?2k}sBIeT@%! z9^a8!c-ju5d%XQ8_^8}-Li2}_=-l_vN>`>vXnbGC%gkZOOD*E&b$r^!UiOeR|Aqi8Fdbb)YTXL6AkPwzD zNpj(IFXwM-4}ZN?_>Cs&La3D&po_&eOfLzo79uGS1ivw9L*r7<<2ywwwp6GOrp>~Y z6o6h~08CiI!z?m7?Yl{KEBiXgqj&N9y4@(1jDtZ^{-9pV}@9gaASRZ zr}YU`O?_QYO8>4ylq-C!vhidqmBvo!G5E;PxrUgS9mY>UHJi}$LK*9OCb@$c>9qFoc7x-s;T6d@%O_w|svmbwGR@!aE&OdA;IH>Cexr#sA-oF3 zhp-DDi2ueC8_wA+dbuekjG5{d1L%#9cu8J8kBtL3d_oXgp~&@nAlJb;{*9=Q9W4>P z+GV(zAtq4aaPu8O?8}tM^D+YO=)NOdi_k5&&{!Z|TEfILpB^NRU9m74A?xlzy zUaRy6CD^>23KWT3kx^$u1bx@pP@#924V8Ka6dx{kG8?v|K+c98yka&~$tTW+GTb@I zYW{YQ= zEVHPj>=SCDQjQc;k$oCu&j)oAG<5-Wa-t-pX9V;Oxu^Uynbmbq;cx4S{Pm{dH=1~Z z6&TOBybdyRi5=`iQfMFejY&=HAm3>R*&>U(3m>x`EFj=)R*ChkK~gu#3|NT4FYDtw ztxtMs06NC6Z}qbM*;mTMDW>qeG0f3`(6U%q&67s{wHXPLO+=k;Hs5BM#VkNwBo|97 zvI;G|yPXZCiVe9+C_A!{O z{YflxQzj51GGVZbVvFZVg|u4|fr&6cUXCHmAjd;Bg-JX)lSR|Snj%(d7Ozz%N)h6D zzE-1+L@{0~86+>o1VdMtwP{XmI&49 zseG3l$@D1eI6kF@^9f~*pl&JoU1O!ztg zOax2lSVVX~$N37v7po{q_+r9Xiv5M2gkK6@yn51V;Il7$J+ee+4t0<`QIDW0sWv8z zrGykEH<{Xa^-3jaSd($c6j1;g8a{>#TkiII;nj z9F~&mW5Spjvo!8mAFpOwgkbsoy!vwZ2hpcQIy zHLMjA2u5`d4tBxZP1_hqDCQ?ODRf_gvW?vkA}0u|Z=QJjL7Bxl>Yq@-Vm1&`oX3&j z2{?_EL3E=pcBR)ni!BFv_hjy)Mz*X1-|RBw&sd3(mpP1yax+@81UYm>6k!n7Eml3h?vC0!A&q_ryqF^dsayqN?jg4Y>0h37|jp|f7 z9gdb!B7xs7%qxKcX;jAu8{S&MU#}9syA)8Xkf21)za2^Ql`Fne3}K@zM!xspker{* zDzUyDQXew}$|K)tebP%a1LpemtzNbtM&Dv2+vItIyR(XlsJGk?NJ8nKrXa_Vuvq1e ze#QbpwH=;bIuhiwp^*@k9*)yavW)F4V$wp~zcMS(R?|Nw*L*38d};D*8J7)2^N&ew zl#BRIp(6r-fzIU*3^^JDXn%>L5s~_sA+Y!Goz@q&_q>lFTPpXh_5+fUKCn$j)F?h0 zWgR*iQ;|?7h!}BlGZu`-2oJfy)MYjR_G%Ssr6tVq()ew-&O*Gvp2tKb_Mhuju9z+Z z`x@VA|7k0-*#CZq?SH=h$y6j=0tmZk=SR;i}2hFvHU-LZhEja~6djd2~=r8>gAktC4;s}#p_~_(xpU#Qg zI*q@gk5cHQaLj9uxvnzTPIFx%SE~(n;mPsNMhVfmfBqsgKwTCBw;EUtzb)tvD-gq7 zDv)Z{tLn!L&;z51tB_~6+Mlz{?03r5|K73spR*MkTi67u(&c@0PmraSKr5=mk#D3& zIr3N&vXmrkGNCT6fgfQIDUb2$FID)`=7!(tU|RVvM;;?YOGKY)K_6xamQ(mn(T781 z=i}w3#v<|k7RV0qKQS6T12+Orc=){uT(iR$=YwLXJ{FN*T%VT9J5*Q@WxHoN;`3 zWv+@aYJsnWs>fEQ54jyEM>UXyD!nhCs0ME5(}zE&GAGj$0?j}8KnI<^}uP9c1 zk59gOd@i3MJ%Xft+dy-#YKFd-#`O}z0Q+@pE zC8lxhV$#?#Xp>qAbOCW_8r!|V4uBF-Ux-y&$}u1{9g@o#TVS4*bq~nK7=cMH+2A{c zNtkRL{hJ}jU=O(5IH@m}%kiDo7v^$LF1GdQ7{JH345_e8rl3+M9K6XNgVoso0Qo98 zihzS;Wk`+kJn`JY+X8O)w`QFHcBPh96N*Ea6tKml^$tvrBmfZ&xsJPzw=kZp=M~8Z zqbL5zcaw0V+O& zx$(agj$|+&H}M1Ia7@@2_CVg7C&>#3t>)RfxZ>(RRB4)63B?tj|R?xT*F~_$F zI-3q#)&){IyYz@`vLU)9bbBspqC5LM&tlSb>cF{`ORzwy5 zTNCOEs|~`8LTdAni&lG}!cGw&=c=%Lr$Ck(4HB(t%boqfq7_r^V$tfEI}MP{8nY@5 zRg#J1v>JECESkGmv|{EGSOx5G=Uz zoz^FszNzm=w!TOA4Ji!*a#0#2ZK2XI8BXh7vVsWI&E|V4CLEffZK3ul>qL}tC{Z}F zNQpa8$no+{tTYql)>^EsaxfWhi!3S5{oNe&DYUA-E@s5ygN=b0W)BS3c2t0yQBbODIrF&KjDiHz9XPM4bt()^IvTkwl$W zU)=DC`fpkTqMpb6$Fa83Y$nqLdzc}xsq&p-4+qd<>oKOjA~byuY{q9xeasLn>GGY{ zM~txb{b`Y<=|*SZa2T6nt4t#zZL*9cuFw*B$gE;I1IyPUis=luL)#5>2F?o=%|T~D zakuY~ii+tBzrRT;b3P1~px(S0vi0?E*qOk#>r5aX=}aJ&QfJPc2E;X;0s{Py%YgAJ z^reOzHIc=ZIONl12$&&Q<>osb0=Co+!PkF1B!S`xmRR46B16u1g+spfS3}lE3@wq3 zUnKSAqBP$XqV%`5KHmcc^Vi6iI$r*y%a<=NU&x^w!mH3AagJBvBxW#Pg#jLufqTgT zB32crqGyj?iSMwTky1Sz9#mclN`0U*yYs%w6KZT_S*F zhJds1odS}rvWRl%9YYQr+f`zHmq~rh5O7Yu)B3`k^LGnveff%k!pb;d6v|(nSX!oH zkXD#OG2kpSe9fB03lxK=uhRi=k`w}U_EJp59TbBylz_Wj#UR3$Pz)+~%~1?0@y03! zBJG9jveCpdAs%AmXXK~{EU+X_!i2Gu)^;VJNKV45u{Vd*gY$o>&@5_yVn0h9znLG*}^=gcqc_!US#d!QdQNPW5b0pDqTVg29+t*@AVKy{`>!g9UTm#hEqoz|D5 z|M>Og>j#1t&<_Ng3-NRK0)tDr`hle8N|>sWA^qUh+lgORKVa#Yli*gHh~Sj*3kk~( zz=&QPSmar+seJu_eeitdYS}8vkfpkn$e5!aP}y->*Ynrgh~FXg112dEiDn6qnIW*b z^PK`&YBb{P;sq9o6v$jgMXHb!tqe&aknj7s0kYWal(MShG(-G2VtOy;)c<0Ltma8v zYuLfkPTg*D#Yfv%_;KoXlXgwCGSjuyw`U;h!uSNPW4KFO?{vcgkv5Dq9pr)K6W92nTFO0)MXA_cMTUu=`InI2jtwn2_6{_uOt?jx8tXa|+1Hu-}Wy0>B zF#5K?XM&AZlO}+aBi)>LD@J#%^PvdJ>R>L8UMk=8k;qU&r=cBdJ7(8JBUl5-knaWM zbravx58MPaq;Z=KOSj<({>mH}7_NVAzmd+R6MUlioYXOWEmQiwSeNlh9Ny{CbsnG8 z#TBKT&U~z43iExHPcWZ+@X4Eqec? zqgd?0yjqw)uND^0tG932O?{+qCF1*{eEPOX>kHGjpJ;v8JpfIMJT(yUb-acY@o%2b z;e5g{6@Ld@q}D0W!zob6iGPTbDinE$>_i?T;**_(c@bwun9)WP zFaMwLqPBcq)TR&&^P;bK!H5;1Pr}+uqfd)n*)3%FI_T44txbAoINx9A+uBykN*_rL zJtf#8LkNm{N<9BFg5OJa5!6)Jg`60}znWu?5d3Hu^`D;qch z#g%bpE|EjD=QvORChZCn5nT*Smbn%tMfP7&{4KGr4p-JEj4>T`b!c5-N%@<8U8|Q> zu!jczY>$x$io^dLN8#V;!=J@k2LtI&5%|x%!oq($QitHbLmnxOQN41+2`SfuYZvfm z<`QAjnd2}7m~<*k*c|j-ICSi1yMVvwW2NE$lAL`%sEcuy!2pfaE_MCYv30FpHarA> zw#RtM6o>!wShptrxBKvCv3Ap!bf*aX2cTc)i1eaqgp3IdR}X>XRMSg@aaWFu9QrL` zkFYwyku@-WVV>=`(&<2$XDUjg0;NJp^p%?hZ*t5ej_g0u={uU06l}kVCN>UDKu49{ zHH4gHYGWTk*Ou#$hK(aT=Gv}p$R6UKM`+jI#+6=KPtIl^R7Ow%f5xzj29CGd%DRH( z5`HHH1>VKw`RRWEt|Z%lJJto0nTD3&hZE7b5;3_iXLk^4>eHH}f2O97+L}Td z6~A)?1TBh>!1J9<@`Tyb>B}3;X{^wBLiZb7tv2!oM^^V%sJyX|M4(XK7=~bxB63T6 zBM^bvpmb~|geT6V>{Bc{`xvb`H!!g~*q0Qa@NM1bbjof1@eDf0-q! zu8B##WeEKLHt|E&9*Ou$wnoXF1VmMfI@e^B>8K;Q(F$8>TL+odGqg4ePsH-iY@}`)P*h+!Y#yp9wZlh7{Lr`*>0w{?T8-kM1%2sv@ZOn&K%Z~#niOeKW zYAOz;KVIlViL$OjiSqb_9)nKr%7gVMQfC`a{D+)Y(JS)M>8LLXlzwEPR7Br0xfI zu9ruO`5SRp%*SnWNKPxZ-<2hl$h|k`>z|vIeueeVZ~oZAV?(wmy`*YhVtwg+?K-XX zg|+KfXPf#$@*2NWS`e@nmloRF!SIta0d1@2GSkrV+H|b^D1I)N!i5I*$SoJHR>_?~ zTAD$Pr$cKO?se;<)4c@uD!BQR8=G*uGYGLc_@4!Ta z3Uk)e@K;+h1>)A&?h%y+&NdlsGqrZWlQxa6kf0E=|`Sjjz| zPmnvuO1?|@1iAAt&6hriZ*}(^YTp}Axk25|`h~_BypM-KugqMHRV&>BsDb^~UBbqg zLcMO*mF~ei4pSGu`Oe$F=aWB7izFYJI~#Kgp9CwIuSxO|W7vrwNOsD#l1~T>@*OAnWa1#-ILRkO3i*zZe54wzZ@A<$sGS}r`6$g3XzzXp2(*z8 zD>GGGv0E9~T@@8+-T7Zlw^kh~g zu1D;A4WInwj?eMQ-)(R>pZrclC7+-e@|8~F(UMuCv`#g}Pho*|2B;*pgvRGsr@!~&a6b7%8s(F&!lPH^eDdk?){B@= zCKXWO4LEgAU`u9}T(h0F ze;|pHTn6?BQna0+7fI0qS`>XI3;P#)xiY&UoT(-!)8}$B9nL2UzI2pNqWE)E%O#&7 zBc2fE^gk{{3V-SCRX+Js{aHLQ-)g>9`yb%FOmYbH*?-Al$ep3%cbI&)Nj^)noAH$S z{s#gZ#ReJg%nZec?-+c@rqJE{byKlY;!~vxGtW$vh%cKezQ95U1S7H=q&$tk%Ja7C zaJp)hQ@0pV7M*yT`@@JkY%EUgXhV2aMsSQTjwTrJ9dCGCvDr{m`|O8uL;-_UxwRu9 zg{^%yvn_noK~3G_=-fAl@tlM8_~j`>{bl&`k6Sax`K;2HeR^5LOEkeJ>+5CC7YeW6 z#n%;&BTm63XR#2`mDk1M>qFP zAfodsmo}6vi7g}a8Tke7a1FOUo(I>*5J)yh^!r)q2Scw;4L%8GPu#XXJO=dj9R>tBq-ei z_hy9H#@S~5UIby4*GpTmVxZ?er}ru6QMnafRm`bd*W5#WuF@HBXLq1$k+ItqFcO2M z`v*8RC*JSYuiNz*%Y8g!xi40?hNrKuLI5N6G)7}$*Vr?b5eB~j0Icl~b7c+naxj3$ z;Q~;y=L1{<#PlZKn%JZ@@wVvAM`C7S;Bt?WGsje5T5jPar7dUo?9M)0a zmLJl$c5!|@-?~0Om@m4rgk7N_L~%Vm6QZAl@qG|IfVO~E5^zLxA0;9NYu3TQYPxY~ zs2l31Zcj3dZ{jpGXSYY^&clO=c1kJys9VKLJ-N*rS?zwaqG950PEQkhlXFVk^Nj-z z&U~-m?A32{;u}>mVvk&$LI5KacP;|t3oir}M1?cY3*eppB%(#8kH8NO9GW;fU4^r( zi9S!IPerQi793s#;Bb7INr!GczpVA@Rv`{X=Y z|2`L4?qXs6EbO0r+?;oQ;kV8?YtEJqA&r>b)rjH*9`4GolbVHlkWa?1{@#J0kvZ|J z*MVD^_|@y>wz-?>%QRk$jbAOrNkNwC9=rNOD66ysVtI@4tK}A9Zx-Mcsc(_^)l!Pb z;?Z{!oAd#heGtETB*JL(WokZtZTxD!aZ_xbE5Vl}gs&v<<5$yELt|pvTqzj=_W{G= z@vE7x!Njj-Tk?@znwF36DdzG~UidEKSF`Rg;$L@_AHSNbMhM``6grKG`;~4^4Bz$h zfcq&a1ZXf~I))|5zWU?ugyUDUu?6v~z2VG6Fq!z(3Y^WDA&6hy!|>EqFRx;J>ZGXB zsVk8*h+oYwZWEIQ@vDge2z-TfIG+W-=!mvGC8Oc720L`;ya{DYs@EJkdU^RI2O>=~ zpP}d%q>+#3EhO8cXsL{?AHSN~jn~3P+4$823c%;bufFR}KYn$98%5$*H=s&Ce)R?z zzSuQE&IxPx?-u)Y>l&7w>c?+1(SXLpB&t|6u{B;+CXOr+D4B#5jO02n_P#g*ufgJ7 zL3@(7+~Iu`8y4Io+rVR2_CCocT0qxUt^zvAMflB5wKJ%$)10N(L^DhtA2MNi1 z%xCpMLNc#BmPa2jzuU^+npWv-;!ZSMvB>R~uQ@T88y7+{LcR0RBQ+3ei24aVc4N>2 z_EiFXYfx{;n@7;h{k)Vbtp@#h0GNpwav+PkO$XdbLUOyZy4n0>g_By$rrCIMQbyvn z6VQSGD4v|e6JX^UPcFem7Kf)6FSz-~fPSK{->B(gZeK=2*uy(a(?hK0`FeYr|b z@FF3yOxU0?y*;mqMc@cy9N+bIIK&dDpaxa&s^c9cD{;j^Ir*FIUzsZK#~K>7Z?gz+ zncTt>W}=UAu??J5sM`#^dJVW53*tq%^)$4e2KQHY7S`mFz1*b$9@qv28psc>QJ-_UC%Tfy2GGkD$7H?CyBy^=Lk3|Ngk z)^5q-y_B>@#=W1Mq0nymcbrh67UC17J;5p}hy8SYhxs{4?r1-j5Fo$=@z7=R$?^%t ze2qJx=B(~MiafzgnX3+@uYM1H=K=xCGBru!kDRP3+}nUfmhF)hF?H} ztU*Tmrb`w2MfW#MMdA|2djLssRtA1!0uYWj-|2W0Y<9f&H8mF5D@P!exZmJiBy|O< zF@YuP<2$X717YjC5;j&%_xx!ftlFlAg~PK^ZQ)5#V!Z-+0)Q7vdck?l)&mD z_~jWS7IsC)kNlB^@DVSL+=n!brWtM5vHE znFynJi-|CrS0t~LPn-y2aHp%uPWKKrLl;NIoYe3mI|=dv8nT#@rT`PhOdUIL&ndvG z=dneAu0I8?|BjggLZLl%@UrwcSjD;fKv~7P!8WYO`Xl>R;(RHC0mc5Qpt52OW-){FfNT##^8$?XH{DB2aA2I08e$wZzCapUhShAvxDCWO zFU5iN14kpV`XVrchVb|qm%kBWhO1v_B78~NSj}f(Tf~=c@$m-Z1lJ_ogD1Gg8jQ(S z%`V|%#eRTXIUaaodun}`Rg2xM;U?>WS9Cp+2}>N0h>Qm_#AG8I$Aj;5JfvPTzfZZ? zjEA7hV^G}a2wDZ{5SoG~aoW9|?_PC9kz8nZKWrc}nbmh4h~pVo6rZ{_~yopAEA3c*;el{}Wa(`>1vv zLABUOP<)OI*B56%b~2K`!J7kJB#=`gylxg}v&?7FMKI>cZ*YS2;j2(8S4HpCVkTFC z4uERM69Lv?uASz(#9X^@C7_o67`BFP%C?N2#Z=g0!(X#0p6T2pasJ`%#M~nL;YGeB ziRdfcsj?JMm5z@BF!dE)H{tFIZCa9+DEs z78%Und_{&)ymFMTR4Xb_1tYW1UWm@fs$|s;f^y&S=S=HgPPkWcNFtXEU3VqM=6D)S0+gu@qxv& z%s3WYq3=`eyu$QI`yYf}ncox_$%%8tMOjJJb+PU$fR7!kqk%M&MJ~`5~jaS3rLy(B&wN0ec!nR$qkF;%; z9U}jHrW{UJN!XPZn`IPQlP^E>oz4$pm*sSKe9O!aVLU3RlDyef93wwJ`4Pl|hVorD zVS|d%5J$1z&VneSwi*=34+nh{j_P|1%Tr~_@F4<1*iLb>p#ETYa3DXQiVRvoSi!^8 zfV8N*3F0ubhg7_SZ5~w%RlF1zdjAk5Jz>CzTqOUmpUny6si@p$yC-1&VXzZK5!*N2(;twE+kuDk@c*J_H)mqLFHhaoG4O35O7UZ&G0 z-!=U4qmdY*fgf9X8r#x}0R(`i;}`m>;Za5PRVFMEKH~*G%n*~)E{Lyur|^+_jeKy& znFc;Wp$ha>c1rD1$eO?LpmmzXgc(TYyKY}35B3NSdjN-?VWaY{In~B80+2t_qzlS( zIaI#BdL_y&vHugL|IFaqvpv4k{$Gao7C-JdBbYzIaYXEov4$4cSHC?A2{cx}n32^? zZ0PMQft0R|&rs9Yfm6tk`cQ0+zS_W+`1GerL!r)zz~B)Sz-L2L551$0nE#j2aWqK1m3 zj3^Z3Zc>W$Ieh18jB)e}QvdK{mq)K)Ry5lpGk1ySK}KKV-RFg|(X zkc>*{yOT^QqJ00IPb_62OIeR6#86~Pl=3v6d!JnE2 zU~f;g5onraVXIvM!4E%WP`9#8Sgl!S&<18k3q(VxjGE8nXfaNT%v69_?Pg+n@`M(7 zvIY&}0ug?S(h>Cngw+IsKbn~?`>c!-*cFLFNbEaX9x3)s$6XPzZ>aMcnXyFtM5BS9 z%n-;ke5d%y23cyn`ji}SrzpS>l6m^vJKJ}vKDX>61a(N>6V`7R%JSvC|Hs_Bz(-YG z`{PL%V1UsRG-5;wXk#@6mH1=~Y6uPw1w}D{LQrpgR_#TdQG7(e$w-ce(X<6cE4@%{ zOBF3z)IbmtLNfs=gohXeAu7rlCu$JnUGw{X*FI$z0rRbPV8#=R1Fy>W2OP#N4tES!I9B$J zq#b<#TWg<=4&f1V$^r=;htJfU_Q!L3PQM>!&1q|$iU?|_j}{s)+GBx)4##JzJx+q% zUP!gqmQOgl{q2pH_E;cae4pg0e5TryIkaT(Q|$H<3?S~IlO2;jVT+JcW$@FWSxYg? zC!B3@4r}A(idKrT!(i|QI2C=u9CKW?vA^v=S%E?ZWCaJ0&iI8TxvKp9qAiN6)N<{q zPnfnLXRJO8Z8?=5*~#a6h;B)%kg!Ol%KL%aM&`~mN? za1h1n44QJ?!V=OjFje?`wa zZYp5b)=rAMTd>0d`O-9JgU=LpEE}xM#-&4!IUDR;JKMWY+GBx)2bRxNdviEGi^3y? z$82vcz}m?zCrf)QkkEhlOtn|%Xm6Zq?~w(z&sIny*}eM;wnE`jty1F=Q%cfD|C1uI zu7m(;8w|NHaq5Y!%8r|jQf+XliDw9x!gtpRNe>?>%ADPwqQA@uJ$$UpoX%%))!`xB zi5fGI@9y9o6%g`Va~vThy#UpS5xf)5N#9yN@8W9AxhzLz$j@?pc*k0%@vb`|MRZ~< z9eAfA%TUYr{aDN00HZRr8+TM=cJc~|?IXTb3Iw<_=6iuiqBzA4a zgwvs_%oZmYn%Hv))USCn5q(94234Gt^Dr^f4AUU&g$FximXND z|Ke&*)mMJP>hlYACa*p|YrXoGTxqQarLQRWi3;X4Mjtg4rBFSWElm-LkXR;3thKUo zsJG38NajksBag%{FrkC;OI%)x0k+;ANPW6v|I+b z^{)so=TVts2b zmytd4WC{1rOqfZxO?x!^*3S0Ik_2zGSEkzQ%i&p*r^VY_U1^0)Wm=vLcFPha z+V*6ed>JRg`lNw)GFY=6b|xg!hCp%d>GoXo2ee}@sFbK2Xcys^{yujFeJ=WkxhTM2 zt#m8rqV4osq5`!q{npGR{Re2VlH9U8Hd+o|mY9Z=)GzE%J0}1MZV7F9OVCoengAJ@ zH384YTS_D_v?hQZiihvDBtPHQC z9W9*@Zz++%(3${t$Wp}HUIE=NRTI!0p8(bz7Ar-(V5fueuYXXC%1mPMY^P>MKr(MZS8L{d@+Cq(kn!&p zK&6@8C%;KWlkrOzd%Qk(3V%z2gygu8R^M}F@pg`HUee(SjBlQzBlgCoqw_CK&{3QG zlpSkld#~aIAfWTbVj4huud4R^j`mhuV&gL&CoZ-=qEYbPwpLbl8^}h!@}L|Twt-N# z%{G|mKq&aivlS;KT{#uzma{i;@76TjC7ei{l`_5$&%)p1<|khZfOg`=I?ZFLEWpbG zg_kvW&5@2A^P&l`&yKT?+oLg+U$0C!s+{40_e<(#YdzG5FmE4D<US(h)r?}AS;{-sLZUVwCS+W}yPJ0^Dh0LN%sYwNW-TLG z4yxHzyh1e_#J9GxX`Y9>=;d{a$~nwqd65_xolwm#y&L zui)Qgf0BHkE1{nW(tol)seOVC^7V2pStaQ((x$Zz`&?kHLt8WB=pCAwAcG;q zN;8w9B3U<(!dh!)w{$1QX;3E@q%eWQ(&`s9vnS-2Xl5ilPR)ICO>7Us6dik?R(gX& zGi$y7{RDoEJif{N+REeWTG@}R!%`_s${JUaa@KM# zrrBartUl=^U)xNomu8dDXcTbBqc_SeNo72Kwf1pY@@PE5G)akP;daj3s#GbFNfvN& zmEE!DZTESJc}v;9hPS_^x>U)xPPOEed`sdjB{Ire@+doGi4kqfqtp8n`^Zikmq$f} zl_PHPg01AyKj1)tTEhd;fMWs%$t@?K0KeLb_4^Y1&X!e=6JORo{Xv-0X^~&TdnD_pU_P)i9$j3NY9H zrLf~P>#oOIRQ%x>_Byezx34|Z5YL+B+CqD#YEQ6W?dP}rSbJ+R`|Xq;nv@Kf(A!j^ zD{<%9boTApGCs%;-N^mKVXJ1~xL(Ez1UW0wAyvO&VWk3)l|pB6bC8tpSJ7#0i4JO= z){)>v`dq*j+*-f+6bA$#U0Ug5sNZmnm_3n}$2#vx{f3p9o%k%SQylD$!B8{d{%C&1 zJ8CA}V9o8ggNGD9=`>o$J9R7;nn@M!;`+_oEXP`=vD~Y?V=Xgz_dFrh5pmQ~#yb^& zh*}=wU0m0?kL57_+j)h4GZEM5y_H-J)Ek)Cv=lW)>y|kmRpAlk=|$Fcv30E=Xb4|< zQl)7g(BEfyeQp{4vbJ&yl)a9=wlxmAQJ3UAD$MO=1*8o3;%tqc|D_F@T1&#hpzI6L zKkfynWPI`%vn4xBt5a)V;DiAr6*NOM-zC|1Hsg-zQ%6*wXCv{))Sg04sIL6e5q~7# zlzC)2ZujF)qWC!|9_@kx%(p7*6No$+4{^Yp+yO0gB9zwZ7%8_cMoDj_GR=HQCoJikjZ?=@Xn~<3D8B)%#%9)SyPF$31?_McK zs$oBi<(=RK@2^1e6loRB0eR77(JEgSSLeO~Jp*?lR)x26t%d`SPgQT>K0OL7N%FVIL z^;PA}Y$?IDL6mlt63cjp5=WuXM_=NNjkYMvxV#&Qi)DL}+>{#u8IQ{~SMe?$k^3%d z`A}+!vmbN5fJjHt(C>L?vmDCAQ1M$NgW|g~A z$_f2pV0luGM96{tT*?Xku^ea6)U0xyrJT?o${q4>C|v(67vr4@b_Pj&g1hMBTr@S& zD+Dj`&|iy{Rtmg5Rd3*qBl7>d8kJ9YFO4KCna*rGzq!Zi!e zh?tpD&ZgccaTk3}Dvhl`e;!VY)uL-V(9=OAKb8APmg*R(R7MDV`FN+S>8SKlsZ?-+ z&gAkg9@%iZloO1S4oEC!XFOwPTDk&KlHzb-?BSg~OXlaiQ>i#uj_cE0k7q0wk#aKU zD7Qq)31`LNUzc(+K=gAi?^J>WlzUFf31U!gI_{!x<`0rV<K;ljengR&LfuPzVNDcYg=}FyuxMY@Uk*fD^Q-cs zPH$RX5k~SC=~8>*Tz2jbq)Y84pCZ;uOA)w={A`4lGC(2(6qlL( zgi4yq&mkWpX{x|$GaiLNqy_K7U*=7nhdv-1p+ba$k+ z%g{5ePcr#lZD&eTq)dG^-?zY#GIgFU=DB+-F_tM)Q)-{n&f1YMbxywTWk*T5XyD8BTU?BFAdFB@|;Iqw7aE+g)ceZFH+zUT){1mDr ztF-W-5HBFtgVhH!E=Py2g!ha6E}0hbRq$QJSH+7qV=Js{l1a6cOY)ETIDUl4fbVo) zMefI_VD=Og3%lI9uZq$#zI4E9kQWnTv$^Q?E}&s|*p;vQmW4|yBww6Fj4?%Gm_jB`Zu7R&7>Xb9Eo93P-y(HctS)VkU7G}|mF74sv?rA(<-_tV@()9Yj12AFO$K9^ z-Ae(?SZ_`*kK5p5pGMK5{G!E34;rWm`?f1DPCU-J`JNi z#fklNtA)1n13x_CNBgg@)P1`W`GJ*Mn8*)Y*;DCE?{Q{EEi?*AsZ?`3BtxX?1nrug zaQ4v%Of!57!Cpf9pS%K_fE8b2rIQU8x`H*XYjxioX1bk|U-U9F-Ogf+u!TzVdbC8C zz)oW}XFXgZtHGLhG>SC|nif*I<5c9;Wryb39pVx~Z_qDu3QhqsbwqImUh+UmNP@k5 z%W@k)zc^Kh%KhVzL;?A3I{-@GqrF>=ab$y5B0voY6FX`EWIVGj8Rkw1zT25+sR!pD9jvGyOIjC5Gy5IG>a8n z%AaumVZ-T(e55kOp2nYc8ed^E+S6eq-j+J7z8%-SEat=y)6vy%EU{y`lui?b@1g@6{E`z0&GIIlu{ zjy)(lsc*enx|`LdffsE?5Puy>eMerJoYeRHA4%%lI-l=mMA;QLgp2Eu-I+{P!qk)U z`PLzq0F{z?a4y(=G{^X%r(KIboHNPKI~}i?pV#{qbQekTdp>@J!T$L=T(Z!-g%zmr z-bi4~>qtH-s^F{-mbXlIZC-+(vG^*#b0=K)v|p6{i2Kz`RP5-+M0XOfjoA z_8u8$t$7GMDK@QKW&MRb5NtZf@Okv&eN6TnIEWowFF9%ivVsS*jgm~GxbGufTXI5f zS9zdQ_I%pw}3Wf|YDH2D__|Wa!1sxfBe%HQLkiW#l^jkTC$}iM_f#Ls$gT6% z=%bpn8Fiox?+4+c=CH3x8@5tkV`Qg)?A@R}{Q_PrC4BW~8G2D8>Hc;QIkzq(My@q7 zu*`C6xKP8rdXw>*p;sU(jM`4C4Ay|!kX+_}@@dg_4%p`j4?5YniQkFKt>JRuM#GZV z2n_agAV5T{o&zYOK_%!I)njoWN_St)i-ER?) zUEiEO*u|Avl!?}Ik4V=kZh%pQ0TU(X4!h=(KkA2bSF$h~#pvAT{j9^+j0q@mv!y~; zZna*AhH{ZL-VYXQ&%{;TpcEcxB9~GLLxI7iCRP z?~Zu)K_!)40a$euNvko@0LURg>}Ld6Tghm10hqWm4sZ?u)(F7AD$lY3zO0M`^e4dI z1Yq5YD4tj5SWsNyXkq{X$^>Bj@^Al}$QNn0QaNJGXeN`$>;9_z&fZV>A!)f zTM2OVbPjXd(j%+!TPqk2tThVY>;%9p0$e8mW+eLNMP3469s!z8BOYRv(1TSImnHySCBO#) z(6r)mJXa@q6PR2;fY$`z>*YfT(9x;}n=;qtLIUUl@XeAD1c0nA?SPpQU=acC7JzS; zUPl1Py$V2Kaxnph2|#ltWIff%B0yT0tRTQI1Yl1k_DKbBQvzTa0Xhl*&ZH|Amv~#n zx4W}Dw!vFR=YqEm8thK9`LbSHK~|jG5O#(7qTJ>DqeuLw8Mhn_#yr!HjDDr3J8(+y zTi5g!z6$K7_Uyk>85F zEcQ&SEcSH5o)tHA+4jr3P%jl{`+i)?J&sc+y{Glt34eCV{i!MUom1`)V25*-rzGgS z|0v}?l5+oE%Kcwc?q5i`pOJF^NXq?PDfi=2?uVt^Uz&1%cFO&!DfgXI?hn8|=^XFw zl>3iT?jtGp@1@-THRb+=l=~Se_m8C9--Uapy%TaG*1#7cDi=rR9^hL?ydvd|6{OR# zdHW%D^f&&<6#(k>?hKq9{Khr?hl!Z*fEirXUt|%Aoh@O|ES4il^JtCG?S~G!jdlUu zuHwtQcJEYRty9qS7-_~;oe;!Yb|B zm5BKCZHL3YWvVbxF;_5_W}-4=Kxbm7H-J=)qWb)z&06W}r~;r-D&<}$eipFlryXNgTI#I#;CR4HGWO6Q?qXAWBRE2Xu37wnU!;kWp-gwrOP>K|$2Fp5nqXo>`2)xph$L?x}k3{%!AuC%1U_`p2y}i?eM> z86<%xG1Ed;K+nc|U1>|I2w+X}ZG7oh6aEffF1^x0D_8L)O3l2Uw6ZI-ZOR?^R0&Y$ zFJ3OKpdj{7A+&5qD+`NVX_Y1`A~mCeBR8Oe*igr)kSq+N4)?CrNK9 zr5T|=_9pD=#HKIyWdO1Yke+by%!&ela6)7dnBh);%Sc->i(Pgg_YAAWd(h(YUVQ1m ziHk2S6dc63Dj)kOfs@^M5WNn_TyafGo&Vw+381Jw9ws`S0h1Q`HR+Ggjf)%fT3O5f zwOGe1P_1%UU|8#x3%A6FzP=G8fL&C&z%5^i{gtoSMSs7)9bJvuGDMu zR%_W^bGK;OSNFyL0{p*K%YJ4WaE8k~Tp*_1v+d-2?*b{iy2G;Cy$TNp=hb-EK@@D5 zFbQK|{V=`z+|c^k9rczvh7P6r zM&Fyh=fEa?UH2n;owl%{hyF2obzr%SsJVJ~*SzZL!#(tMT@L4Ms{Zl>eW&*7`V;hK z{qO;x`oPBSAHzV5acK)Txb=_ha(k+GXXn*bA93r|U5?~^tZ%CRw1f6)O^5XA?q~_y z*2ic`OKPV(+tKWHPM3C0PibcpThOc1Tcmf@NBVR*f_D4leLSJdp1e8^x3xU8;;*%L zT}z`4cP}H`cxWHe1vYzY{5R${jdWdAKCk2TJL! zAJz|5f7THM)4K`)L#v}1>1)#K^CI3rb702={h&x<@%1$>uf@^$7&cyy#z(ZL@$y7_ zof7SRn#uNZ528Ii64||8 zjaq+L9BA$VHrJfD9}SMxBdWpm$8J!`Kg(U+^zY(re&z~al%an&{WEl++22jCKd`ad z%+9MlW6KZ-yc;Lz&5EL(@dEZ}2k+C@q=QL$H=y|~?H%t*V7QyJeR#xHTJ3kO{wh1K zo`RIdFw#xJ5t8Y_=06zz^&Ecv(vC2+00V{XLHWb*EX3Px>=<@sIIz9T!gi{B4zWh~ zrH184=2+J?Z)G@ZBY$;L{IT1Cjlv<tGC|9%yBt$Jl+Shb8_|S(}ARu zNt8qKs=a#yUro^KNQb0%=tqwAHNY6UFI0u~jE{d}KqqS1*Mi)y1-V}fa({Ljs=#F) zF0r#M`tw|4U+K7bE^Q+rjFkEC! zNafce4-EHrUaj_1%htRHHLijpXg1xKp0<=`W|)|1p;r>#qDx^4$s6EKW2t2NNzylC z^MF6tw^>zjbl$xQ`!)>r9&8Op=kPdBWGGEyY2is8y^}G_r4Mx(!`%8%yo5=f9%!KZ zN__)3diJ&biT1AeF;d9sy8c@Bbt4`6w~;G2TU`H!_T7ue8W8 z8F%hpIE6yn^kMGsFpu8n$qV#qi?YIFU5dEe7^RG~+$cN>Ib$NnNZlnJgS-axs<@U# zuWC2=U&C)c@jEGjUtsosf4m+_QJ8H{f@H>kNw*liWx>cQNm(9PKEr~wq7;4UODvSj zCb`loKK&kYv{FYYPS@hSx1>4n7Rs!+ZW&w&&^!3z4Hm@p`yo)3pt`Q|2p$slInaNw z3B+bEQCXL?!C~d_pX{f3s|i4oNQ)FG(kS40BispvL<%z1U6#7gkdJqCEa1{tUFd1S z9W9c$^jDVw>T;2~3{;mv>QbOCSEHbb-7(#?pBxk)Mc`| z_|;{Kx;(*)UOo+fAw86p^150tFSG7vTKDDF{VeN#j&=XCbwAI#f7QBQVBIgY?iX42 zi>><#>wcMaUuE5|!c{M?$6w0dlxGD0tvmQrJLGI2Z}tDed?w?U_|VIU4UEfX%R+zI zZSwaz@^{Z&mKzZ>LIqpdtI972f4}~h?dI>SsyJG8ht1!`GC#d(B5fnsq%w^M_oX)* z!(GAQ9+y#wKnu4XMsNdCcxJf5Aqihs8w)z0W)A#?d@g*+qg7ah$~dx3fIgW^7wfjr zMflrr@glt59^w=A-0X`GAuh(dv4)nq_M(!{_31axDMW!~F*C3yoFl-n3T z(3m`^;#IaSu~#nhzLY}CtU|A{&?ryEO15`Xf$_jq4t8{DN}(B6q5otdbZ(T#cyOd~ z^YGxxf{1S#O)n82S6!MuI}7MC1{Z{jrWs-W?l#z^*Nq*cUA1y&{~YXF_{M43voQE= zo!cL8w}$w);e>O5%$?^L1umEe%U*={OTEV2u4qID71)kL7FXOlw;pqA{uU^6;^WgB zVQ2b*W48=mlL4?*K8po(Z3X7c9WdQ2&Dg7EjyN2}I&htRMI487=3yYlB65L>js&$Z zd1gv;52861y_rRU5#Z%5E)L^GE|lrjoWO{$OksySD9w0^&%#Azt&xJnnLvbA7Ct^| zCdFTa##a0sy>p^w#b9>Vle2a1X5!}o%$5^B)iB#7;b$}ew#t{RZTu|1E(Jg5vJMO~ za5-mQgqmz)vPAY_hgv~wn_W`cxydpm4wf=7zc&yEMxood!L#-K-Wca6vZ}1_vdvzM z&W4M&Ywv~l?#>g^Ja-~v7JhDhgMYWp^~i6&H00xEIKhbDq17-FhkXs&bt{du7s*`I z=gP1saV&_HqT^hl=6ELc-ok}iQ*jGXgyqLIJDM{m`NNH{zylbxBa3Sm?RnNFd=B88 zh@66VOG;RgMQEJ~04!ZMh@{|_Pc9hgM!`P)#&qcHFbtO)le6@QF+MXGaYuaRiYU0a z(~JUlH^`(1Q`X4saMB)Uja(dmO=c5x5&{QD8fX9p%=C=Mr9`jfD|wjKp!@KDvx^R=?; z;;%{0^PQt0b*&(ZIh5@j%AbH-4y97oFL>Z`S?lPSiB)YIwx0mh)({KWh$`Z16fzy(*O;6G{{_gaXczo2LS77NqE)cE`cNNCx`)(ervDmb z#eROme#&7pFf3iZcg9Bc0AEhr4@NhWa9>Y^hS#F6hDwBKMINzm7CDvB7>N=OUSVyx z#=N94k8x99#1TD^6RbKX;+v=Dfh;S{xc(eO;dUGC(ypo&?ZfUnW8B#7tM5U#)#uU> zhBbQ?o9EVXjxo7Ec^yZj?Zdjxv-)3ogyVA}{1LDT8{yqJh}l)FCCs|G;_O9q9pNx# z?DywDIj{Xx|HDN%AWH1RJ12O*X@K#-MQSRbEroq%XtfjdvcS2t&1deG_%KR^FF&mI=d{Tvws3y(yGq0di>}>V%#kseM(-{3yeji zeVC@_P?!u>4~X~{sJY-yfHg4yte$IPO=3-4jnIcr=JqAw4R(DJ;*XuV-1lJPw_jj2 zoQo+?Z>@dLlk>^kM)cWy5g2sRRTK+0iLRyq;FAzxf$&d&4|B^kpr&h3qs$Iwp@?Q^|d;#}JC?fbI$3$msOXf(b@*{q2U zL~|q1>Kw+Isl#{+09!+ZMNeh>?gM+n(nlS`c$Rfw7+Wr}hEbuU4rBbS9mbD0Cf|PY zxcqv}17g#wJGg60` z1(wysA$|+&I)`{ASW(hUtpmVT`B1k##6iPThWI+`z!3LdtcEzMDlCUKFgT17Nodf? zZgOk`bmoiG84iG`Iixm{rj_|!c9)$p7bp;%I9Dr!n&}wPpW7NyGe`8Zlo8zz!b%#^ zQOv~F5Mk{R{TjxIB$;a~Kscf^)QE)n+-*$0ucC%CYe`#+2}IUmtzs7%VxgvsxEMVZ zp90pnX|nNvpSwBvE?!PeDYX7-Rp>nyD)Lmcun;0TZ=Pa2_=GK1U!79uC9BX=ECjK7 zwgf4Qd}b`-iZJFZ!rzFG;j_4`#osg|BV1J1nwpeQ*8qRFMJ^#ean3+|*}Ref0!{r4 z_JO2jLz>#h8TbRR=0r>kYhp=@L+X7D09!+ZwP(P@>?X~?IMxAt{nv$xrr?8J#1&!8 zp-zjC2sN_JGBR2IDf{}D0C27hCBet{buKk)z~E&0&apg!Ddm=akYia76g$Vl^_?`9 zHvzCUL|A()y*QSeXg#s^MT2!mM~CZJ zY7r@^;-8KV|2?H>3ziZ)ygQE_c2|5(SjAM$AYd)I+G6^jTDGGlga%qfBQ`Q9mX6GD zO*Y zr>ER6N64|WzUNZzD-R{V|6R)c=7Y)aUr)LJ%lFCeA4$2d+L!$Pw<-58029vs-JNnj z5ue#r_BG`n&IG3@o{rBAwM;eAByz>OQu|$%_HI?1{)-gb4VgkqM49C7j1*+0rQt6E zV(~XSoz|8ldmounk^4@)FdK2=WraNrUshS+>4Z}DS<%ql(6>Ix_~bMM&KrdueXz$U z%+v>G8im<+>VvbjcME%#4ep7{>1BgYHwv@xFiU%{um`R^TD3=w|9`SRR958C-n;7h ztOD&lgt6bL<7c4roe^AGT3Y+(67Yx&=`>uxZ7A5E6h4YkVPO{i=@Rx;zL)|lHNTGL2V0U z@bdNaUnZW0V>$O@-8rB6y`DnTPqAr#*8*>K;F}3~RmC+E4j|H?4`vGEr8yq-=Z0Qx zWP5k`Pp`tA zcUWoqUYPM={7)y^Fqr5iJk#Nhz$A}0k4hepM&$1ic!mb+%!>;N2 z7?;ghRG(CnaJ8`mW*)l3%GX+z+x%i2Lo@TXNqB)Loz6$j{Xn=^i<*Ot>FIEy7ld6i zKn=rP2$ehkxKxX?x!KQDp*eVuI%Ip-1il%%C_VT?M&NA(q^Q-hm`6YC_+e}Pf_^iL z^D}X(g)Fl*zq8*fmg;;DzoA69jLHmNmhtduL2rf#`hIO|p)0Ry>tNT;=qC-R*XEBZ zzQD89+**Ybp$!G_&)tx>akSC^jPxCcwytg%?7CreUX^-OyyK8ngYB;czuAgn=rAq- zQ}tU>1;T%DPzZJsS0N|?xGOeJGOl{c9p}&DAM8sif&L9-64}@NJia>zHwBk(bU{`Qe4jjQ@-$8U(Nop{Ir|s})*KihxhkDP#wosi$F96Y_e3Kjd^h29NVBe5BwjWRz| z_F;7j1bOxGs4r`}_aoVU zUnIWttQUtAcxwYDA_B1k2Yt=>);$)|F0ZdS5R_p^#F#U_M%G(@N(rODV6(`(jninv zZkF@s_f)<9+H0>tTzmRoXdo9;ZsH0ps7YO#dBLg-JQ?K?KlJ=p@XH&cOq&2LY7{b* z7~RVu&0VR@cpA{*ULh%tK+H1_cS=L(L3)iI(L3pr&}q{sZc+yYUFtNYdBv~0q~YKr zBNX85EpT~SMm73>$qr+0ALY(}z^#QIN9{0=$;zTl5-aOHCaD|M7}$b3?cmu>`#T{X zLj~2Fzqm9V=^`9&-@#jm0OkpPXYeNd#Ujr}(iVN)!wr_H;s?2(6}oua=6m{<1wJ|n z)rwdg|C3Z-^orxZx7th8cj8Zk-|_u%+iz9>o5z4JHVQK1{2J)5Eyzrd$LmnOhP`2Q z7LI32Jg9Y_E%!^)4npQ({7eo>uGk$kdj}78e8ht@-t!Lia4#K(_hH|WIA_Exe6$Pl zQiCwfl~8X_1Vn86(&huWe7Lk9@768d&P!zJLSCxK{jDIQ|_06K04Fu8L*4axy1CV5;{}U`pl}MQz5{u&Ux{!Elk6F!*A0GcGUU ze7hCz%Sc}j`S(M;yg&HQGluj6HJ)K)VtdF659!N((6Yc?3?pdS7#Y%wtHbCVstTNt z%$&!%5UbOfN`82piv4CHM*SZ6J_4>0` zqT%4d4FA;NXD2SX4|EeeoZ;^-TX}KK{*?okWa3$1OW5~)7(tZWW$BATh`8~Y#KRL` z=C>cBW%7s`qHOwe>1hT|`F5Ch6xDs>nf`;EAIsseGv{{^@)(I%4bpw=J;tfe(3B?!r6b|YEq)~7qT&~7{8k%K! zb0KX-ld$d(dM+6h?yNY_q4EHv^VoLLkFE7-Zwfdd^fjO9hd3D`^g-Oiy+pkm|%lFv-paLV^6YS)4oR!IDXNEB` z#b%^a!zxUSeXvgpy^1mzX?T)5(rM9Qsb0o79!-abGR|GJtr57zk&Oz@CeH;l1cpNX zi*%a9B4ab{O+eU6GzW+m&Jyj(Ut}&(njeI}M3FwCMOaLf(Gw|C^}w?->907BVolnN zsrp?C+z5fwo@2@^OJ*fvE&yy43A|g3)0eWVdW4<*SPT80sFAUgb}Hc<#c1Ma2xJlY zciaz(io_c5|1l>4J9_nhO*lYw$k z*<+Il^&tV(p1VOtwa0E|BEhe~v*;c8gZyDPM*GB>a7#;;*bh@;@kLGyHfjO9u@<)IVaWp^BqMh6v-bJ2~Tc z?IrPDCXwg_rr6hF+Czo+CNpi3cgVBjP{Z1Qf-?)$JH%xmc~qnKpudN4siM$tJl^KO z8PGz!C5?fvj1H(rR>Ya#fK}jJ6c)!L_ku+xbDVvK%y6Aee4vz1H(-X}L&}Fl9Xq~e z9SyAGLex=i?T`Pr_#1Y~apvp)75shH6vyAC@Jy%RZ}Bs&@HfA{75@GxbqN0U!cDC3 zC#-TJ6JP|~;o@Cw>t7%>i^79dy@bf$zJlCq7tIO%C$MpO*g<*YNCU9l;G619dcpW&9A5}uHz->R(lSB~I2^I9~`di5B4@%nrQ;KSrO#+UP^3ejw0<{;R&< zENZOEGDD3ac`nX)T4;HVgO*QR3o=^5>{@PP3a!~Y{bv#@W4Tm8o;w43V%;HQ;?WR1 zf>;>a3}_l$ZHn8r8x)5Xc;Oum{x!X6CI-lU!B#O%N5EKc%D(i6J5C#Tu0IEgb%c|X z7D3pfT->~%4_2}C^mF}NkSrf8OJI^&7LJi-up}_ex3b!ym#^PSS#TQS#?hMg z^vRGF5wHd*2EMcqnx$;sFdxuSgv+MsdrDVprRU%s6BriFi}>a#ZuzPd^DST-$H=o- zq2AE|gUPK@GA3s3B9k1mF5kCSEBy!B0J=1O)69xsn_U0|`|6;wHt?t{G&Zd?2Jr;Z zX`!kq-NML^gz@a8r0GMhwq4rt0b$=)q%2a892vo!Xpr0$hi~K*E&3R@->Cz+0!J+K z#S2jo1Gaivpg?qUcB9(jMv)3ZO_T|nF!&^vrG?z=4mSIOqmCYpqA09T$`KQ#Y=oWS zJ!}8vHq~k1vEV2X2rbg=$W=EAv25E}L_i_t8-uf0!hC|qiJ(`k)Jp$^caXdG7o3o; zmC}h6MwU8i1b`bbg750FRY)Qbqhzze%xNt*xbyalMvLAK--kG+*(>!eA$$?-=in-p z(uIa*t$LUXYP%LX5f7p14$5g6?bhWv6cRk&51Wql+!KxBhMDXzE@mO5DGwdE@+vIH z5h(vZM<<>yiLHu!-!iSVGbVv_V&KE9;7%YF^OFYdrxjS&m$_Sv*Sc!vc0zIhzXgM(2~QC+_xWNLI*AI`XErT5v-2e!2`FONLX ztFtyEj|-%iIQB?XE5C&N18)@#{z|JckDNzEBPo; zXB4l37w!~gA7Rht6sY>(e?#LGh33H>&M}|&d8f4K?{SmD8aqu_o82K0Y#6B(*fCV} zZ7n#&7krk?Nk5vVz&{c2c>0Dag0IFiE(@I`RDPbt7k+6<*VYN1(V2F2Usgy648 zY5rPie(HROWR#DlW98h^F!M?%8c0hJwy5$+P-<(z<`SIGA55w7=#vTql4C9*_I$Al z1G;dDin)oJ2l9m(Ta3 zBAbbN&IS`rHk~YaO!c9^^g-F%wBohiq3&?+-UIg^4LCOf0M1@{}I9r3X|0>QM8( zUjZ)qd@Pka?s!xU?NRA#-1Zj%w6e$5Mjw+dN5{z}>{~{xbw&@91L0Jf=Ewd*W~$a-?61(-#ZwzUKkm>M+dPCxgNd&K>PDon7Z*ox zB|NRms9gFQd2b!IgcNG&Zu%A+D-FAz(tWGM?Mt41rRBENW_*Jp4!7g$p8)S5Bph#O zfS(rfsyDDo_tp6?MV6(#OVLVS2}fg22y# zGEsw_YUx$%5t8hK`d%E{$`;Z?4GsCG<1C^BhxQIJ9Z4v+KQv%6!6wF{3Oo-q((~P{ zQ>ps_6p6#gwiDpvz*X!yyvUG+jxJ3@><3hsymo-aTXe=3zAK%X? zV&@{J3;7A<9-p{Aw!P*5hxKzW2=c$Xep(=aw`=`;2{!&evwpV2;dYGm^OnEW`uXEh zSwEdmO<6zB({1t{n_tMzl9>Zf!4oX&oxuAfc{&bfZ}RvdTztU?CAHrLPb{hjs4_IDOSgm?HmKd)=s z- z^A9N3mcKJ7psoC!_YwRG2pq@ucTR6A!)9uwePaqZYBfe5>^t}u8LCto6}2ZSMuxJ( zo}xFWsx;UiXm|}E#fxOjgvDDF49jFk`hrR)CPhE6t@~yMt6-)(=ulpcz({2HW|AcF zORSSYlkv3pc#rrR`;p9xc#zoE47$Lji+D;Ur+nq{*6T&(T4*^df(5Gs0u%B_@6%?? z#{fkJ}FOlO_5w9tC#+-6BmNY$~HnpROm*tZBr z@RTaG7^sFJ-)Yu`F99Qg0eF?V)Tzr_UTUFV+2a_@Hhp&ObdxR1FG4H-qB13JGWqr{ z^_xyGQUra=u=gtq@nZyWxXQi+l0@GUH=0)Zpn4B|YLVUAm|sGw8mhc$^PJwap6B2N zt$d4!h?(b7^xmA1Q?$^Fs0?-%hjLZA2WoSQNU`^RlDziZ7QRY|N!$McdZZP}lD}=G zwF4@JR*HFVt+b*W7gJ-#`QF5=pA7BTD6*m`)U40ZN`FJ?q&`?B8Z)PYW4PPu%Jq1O zZ|B%*3h;O>dvh5cLmi{+Yu^7h*m4m~{_ZnyFUXQ$oZAm8O?uv+Rp5;YMoi_&vkg0x z=zgng-ETy9=y*?|Qibifk8$vM&=tdc4jMxPVPAzk89i9!eAN%Jt$mMkB(26V?>>dn zvw%LMs9YKJ#clfZ+Ze}*cwC+`3-?<^MoP1B``TE-55%+bR*mFaC9flDm^<;(D2iq3g)KM)!HmoG!-jC&u~!!AQ{ z`Rr?in=VWl%)EPri~hq_D1iR(Ti~L9N`adqaN2V`YUz+@Zv$-fc0Nm1phsBXmkD^> zU~~b&e?bz8h!sw0iBbWc^dd0jVH)*te<|;=x*}Yj{#}%X@}1b ztY?Jg%^qm6Tz8-)N+hyVn4>TW)v+$LBZUJ^!OznPfwpLmh1Z4qf_d6VqL%z-9#wvw>qqvr#SgKP}Xc)F4)c=$F7J8O0dr=0=C|*i!23J&g{= z=%b;Hk9MjpNIy5WkN_75KDAi5f>R`zuP*tohk)PGFe_&1!1jXZr@QvB7RYvV|%++QW95o>qGlmGdZ@-N@ zkRH)W4+Eu)d=dn?Oaokx43ap*(wO)Gw_5Jx+;hyi++GmF1kE@TD8%EI8(jFPy06NA zO2W82R6K8`|G@T(J;E+~o0Q!JTWF#z&Wgdx(ni@ExOE2*2gx{a}jadGRL5XBAS zh%XbzIOx81^UE{{%MZIc&~-ntsxI{Fp`cQcn@l&6XiiBN9N8Si%Bvl`H{^dLXS%@fG8AFnI*mry`4gO6Xeg75KVd?@w5)1mgp3DjrrJX7*N`xZiw@Q zKB~N4L&aY_uM)aZCxJ!l1FVqS%?#57LQg{9aLVPPxF2|^@&iB5jZ;1MMD@gtau!=a z7bl*38&y z7zO=dZ?z1oSV4U&Zhff>VjldblB+53TUOv za5L@l287C|MMC4z4c4!fGQ}Q44e(vVUO86g2?+F=zv|-FM^(x9z)Rsr?@BP3ctf{Z zB_pAU_(d>PTO%q~-%5lck&mM|rJe8{83tR|WSd5;wWy#za>!vi*e~=ugbU93KcBkE~iNJs&xTFp#8C6qKk@oPw@!6#M|V zjAC+p6!+m5!6NdhL^TSAtJ$OYD~fOw`~tQ;3O347FjH9SC|F+`qpYEVprAw)%Ylm-G4U6 z{{J4&Qv3fz+x@qDi2mcicCfKi@L(scbO9>2pLE677qrrE;2E?hM@0f@rCsq1P)Re> z_fCwdsLFgCDCC%6#^mflv6M0S77_sIYGgE{7Tcql4KU6|2v=(~z6Bi0cs#I%VxQ>n zEo?mxs!Mccu$h#|I2noTH~m4b5y^c+DEV?+Blk*|R~~A?YZ&bVRA9;8fFqi@EAb0K zA^PlIKwr419vp=%+z{~UMpt?rDcT&>6U73XxvK>b5emOR8jkaBYg1Q_oyB7Xu9I01 zwTDNolKludUf3Q37 zwpA0?E>a(%U2{-hySCVS@t}q*I~aU z0Cs=zt6Ash`%Pia6a-Hu(1{X3QYlg>dl3E`|Ff>Yc)mJPZBa!0sy$t?&$NOJU8 zAI9M1=%H*h62q5cPqRhMP6*c_ifcKU`S0-kgN;&qHt4$a8PK1QNGJzyNR6>O!WW$u zeA3n8ABzQJ49;ekZrllxO+d6@XGryi{6;lGJ%s|31{fg-40u%)pdwi+U#oa5Xr9Lu*dm8);5Wq6wZ`uI4 z*Pf*<%Yi^={317?xffx8ke?&sJx1@CBQ&iI1S<-)^MgF@cALaYNh3PkG|T{o=JN{P#_?zBGm+= zPyJ&6F--MZ3~Q+F8+<{|S~Z+O%uYsl_5f6AiOB4q`L|Pm6&1eS8*454^bv3j8GNip zW($asTid{Luoou%?|c;&oN!@r&g)x_v_!exDCuYec?){SPq*8P{YS9-Sbo=+0=?L+7Ak|0<^^^E|o`-{JFs$>?sl2DxOYwh%v) zQFrt!-bBR5m?E_ezFF7+!o=m=I@>;P9#liSqCh41Z}c^mmrwQ(|GtH&*zheNwUdTg za1MDvv=nFE!-yOs^@&s_;`D8R;M-s0EjBWOM=$1YR}O_R^9(EkcK5S`@n?~ayYQwQ z0$ZdbH@@;e;27i*OiJ{90C9la>q;f!O`Tdi_8u3#5Ix7iA^J$rG4$9~ZYdL;Ux%RY zQtH6Hf5hx?&DIiS{~l+#J_>jS&lY&K&}m>P9P>0wDexRoO2NP37LD)rb2vn;RBR(D za@gd_N`eU^#F)GNhk(SoL*y`iK-PVk07%%VWS3XfZtuSx+T{d$m!AXLy+G~oTIoyN z%Pb7M9A`b;cSL6NNmrtNKsn~_WtqD<&bgZtdn1gmgeOlN1b%dGPV9Li9L;K_=F#>v zw-Qho14k64am}9SfuKXyvKtd(J!^BK>+wCo=*lE|6VZ;{7Rk=hbGeTe-~+_yq};Ft z1`ylQ^b_m>29_dCPr+m9VHSI9k>c}cndRT#7k}1f56@!{=MX5aKtxYWlrgWh>RCwr zR@L2RJzp+GJu?7geTXU<|L1^6)R>~bLS7`hlY3Bzo!pUVu+7SKR=z|!86WJoxQ)|k z8ON`c(iR35jdh>Udg|{|bovG4Khh~xF`-lL2%yuM1VGkYMW?fDsq_WBbkb^>lUBE* zZJSob(UUwQKIu;MT|hZ#b*9j2nPbL%WwAF%kf>WL9S8ILkJp(8sIadrg}hf&boG@* zsmNg{99Z;12f6A?6rP!&@pzMX2H>KVWHQP2+vtr_{<*c>*AkwPjW0pOs=zazoQ-Iq z>%j{3{pP(dg8zuh{pm~C3LIydw9@m)SCcEBX;pqQUnR_g0ao5Z9vNrVA=w$Zbf|hc z5GnUjA2Mcf>F^~n+7T7nONS-fSULn1Y;8?$Xd<4=8Xa$yBM^25StHRNNW~SFpDFV& z7&K~CggqSIL~`owzJrw4Fv_lb?P(Ilf~gBKMKMXvh_9kMf{A1c2A$A{7IlVU)DCwQ=fe=JzQnTXoYL{g?szyxz<;IonVsz?0jH~lHl*8E@3 zzn$ADb}yq7a*T5@ANKBoHaaskj(JowAR?l^^mOA)QHo;tm%E!t>q+DWdIv5 zvZ8qW6DTG_F!i4$q~1fX!~C_Xn@pHBK*HS>+K4mps|HEh88UDd(7O|VAcdr+4BswP zyh$qlm-RA<``XozhDOmFtGdalz#%tOiw<`O&Z67N=Wtk~t29%%FAhq8=quc`tM&SR z=`ayo4nowl(vQ$ksA?&j^LIm97i4gG>M`8O_t7t@cmU=6)x}_b3|rjH zBhDH5K95#PXdK!>#kKm8SeEGP;cGMVp({O}sUPuQrmw@NTE14KEGYamj1|@aXC$!1 z!pMJQkB50F93=y_A*5H@?mro4DwOkzrM1u(#4nWo2%^2~0bo4p$}d4j4#{))N4DcY zN^xUMLtRwr*5l@<_rfk2sHFn>s7qgCWOyTfug?4+SS>p+W%l`QMLNVG@y^-UsD>M! zl!?g94b*qyN9(37qV@uv7M6oXJ-q{r+t(x8Q=C?oa$=%ooU6mZl`g%rLX^G+H%X|9 zQiA~IqaX5Gv>n-XNCP}rXg=NyIlz^3V>wQQoPPxUs-fZ#f)ouak$2Q)|E2H@>pSGk z_m7SO%5i>RJHGw5H-@;?%@}w$T*v?x|GiV1c|OD`-kgovy(OFdm;AJ-fZ+=f*#cV1QazY6q&tIb1VJ}&KF6e-=jF%`?~bw zzW?FcHr^xufeWlV(t&lb!wrDK$0LKQT=;xszc-Vb1iy8(_h!55D|6Pj?Tys>TD3pk z3IBR4_+ZmOay@)#Qj#qg-C++6m12n`dyZD|JM|b%&$;kGB2@@=yg@nUgyG^UitBcP zN3kDFS()$;-CR!p&>YJ@WD37i{-J5}=^qkUFqZlW9Yv`uMOk`GzN;WTSUY_fpVbg1 zN<#%)oZOudWQA?KB(dYCh<0yXt>i{ZUkKuoZtu?3u8eA4C{ z=MZ;r>30%t_ObD>|LPPx{Q2xwc=+yjt?;0!mYjGv8di9)$)tVs(I!4_ zL#K|1kKYb!g^wrMdMZBdQE*Otj3wN0@bM+Ga3#^j>|eCP$7jE7g^!k@(jzB6nr60% z50!aKSQZx!_)v#akSdXBfN0l6?zaqM^ggT(%im$TV4@QH=M!LCSTz1OV6TRhZJqoG z{2i}T<1vvj!V#vWnpt~RtB{Zps@I=7=;adjx}MDBWx(5W$*V12_h+&}h|l9(kP7kn z+Z=sMZmr5Zdm5gH$Ge2VqK03?>kE-tZ$R=G9-&5>03BD`mKHdPyby^K?8HZm^h?%p z7x)CNA2~WW+dv1!LU$x5se_8%e#(B(^@XJU;F`i8*$=LM=2-i|$Xw!)@&c_W3Dh zy%s4o3*WDRJ%S#906kcEjvyQy^$xCdBQQ|M3GyOOKb-e2a>KPnb>YfLp(q|k(W*!x zOsx$hFp?o&zH{)TQ$g61C?X5+85^7`E z-A?82!_ijjBsun#H(vxT40U;tGvRTUekAZIlCJ89VV+0Fb$l9z62wByvmE{?+Prz0L~OI(nU@!`Z-IBa9hxJ9?evt@V$FPXIFF zfz+e#|DJ=z#j{@9!%w<+^j(L3gbuG|Z;`b7-NL@;Us+*a`-}-RfoOjU4@H|}4bU&l zN+`ePyZFP^z79OzTTE=X$-{7*C2@_K8r|!Jz+rVX{v|l zgJ4n-%qldIKz?j+%5vU1Se7&N-MF8jb_Pi4Wz=de1Xs0+DhFoeD|i6Bvm+|pB=C?? zR7D*zjbB;!RmB=p8kiytXwN+;f>S*FQ-W=F%RzT!UY2uWmCgS_-4^LZ7q=0`}AVY>U6%WDP1kl(Yh;%PL$j+g^1wj+E zK&)w{Ja)~O%l-r%r5raF6Sn!U*f+3_sH-j7-kbLEoa;GPGvh7JOYDg%B_o70f zW0aMoEjMHFG@|TjvL2k}Z;6+0OqB0um3Pd?f5fc@$p=}RuT%^xAoCDE$htKIB;Cem z$>7xjKJ^(JN!aDyBtZV7TJU4@TM^JLpnek2irzTe z3VVYS{2GFnVEsYucVojc9;EQ-ZAL=0-i`ogeks%&*p5Ao$uM7t&BbKVer4W^I#YUU zULmuOfyHU@!_u|~_Pzo|Ua9!ncnzSj-=dGPS&sd8BInSuZ|wndfr&{A7pnLMj4VKV zIBWqh+7zN=U?wyd;Kf9~kfOn_hQmOED2;;|_cYTN4>Q*YeN*`H?Q$9b+;9=}8)FC! zhT$Q*MDKyY4m4?+M=&AM!LW+{+=3_CyF-p*pD!EI7mRZWn}pE?N-#^@$(m?}@PN}< zL3R>(&X+H;I)rtKe&iu*#lupm5!tqjJm6johs47cxM+f_CW@h}UuHb#h{`Wg3YpC| zFlnWTpHEXprr-(qxs)kL!mf?X&F)*wx`vRBxp7qSFt0K&?c>%^QYGdkw~9~Gn_*It zpaJTRy2~U~$Uf{y@7?1v0Kc60_o6kI+VqD1u0iUa|-;u{_0QG>>(|vmU4b z9}g^x+4U!7GFgto!I~U2;oS)!WHP}V;>;@TL8r_5L}7G(KV1n!a=ekiw}gn(`m;2< zaFeRiVcA?JTUHeyI$+F&P~a8nrXQAsEKfIqt(cWNWXw^Ti;+l1P_ir*woN({qiSbZ zJd@~J)Jtw;OG;k_!4zXJ zaOO|Oy(e+c$PEN}Wa{Y|{t0o>6uQF?x>cBa_wdcgKLjrf915IS_!lgbF|ID0>JWoP1#pM3>%fOF zCT?h5zbrrx#t-Dg^`$}d1QmP2*Sd}CT}aBWl@`L?1+IM(EW8=;hXAjI7GA@JZs0gP zG|L$6&L53D?VI(YX8*`zu9dlU=(KOU_U!X0qkn)vd_=~Ns<-k&>Z6(vQwIZ6 zmma|*ef=Ym)-gR;vbL}P2JPKZYb9kq0;?eKY-)rD)C==_5no7e)P&RGkUo? zsh9t>d)cgdxkCI~R!?D`lLLVKggExawRF#o#=2qPUN<0-`ERP`kmqh@;>=MdtoPQ$ z>R5?g?fDj)05)2}54aAV>whBp4!XdAjgltVC@~d6NhkUbk|x-!Yv9Ln)UvhZ$&uK? za(&{%;%X+jBNi6T%c+gp5||f!@GBwOcrzxO8yN^0Cs;InCBX(C)z@`+fC!%@QPE6@ zJimeo1y8(1S;)u(XmUk-=OYV;NAca1863R9WPO_c5)q2Gml?y3>WBE`VLa(DVjuiT zqv{{JrPjAT9fYAD(SOK2==Ehi(g9zPN{>vN+R|4)AEM8pd(#d zJ_I{0&*o5HB}&Q=;Q+}j@Wr%}^=bZ2=v8`ACUhPx^cVEPKBR#zL{yeUM?aqyLoroj6~Q81LJK(#>o##x=|)7!vWYpFEPDPhj1-dI zA+T|8jaE98M0r@RX0&itKVRks6HdV(b!B~hG#4*rIY<8s#7pu(Y&E4D4(Xk`4v+>i zAq|{v6!RdLt1n-w9@eeIcw^}SlVc3_LOnoX8u|)-R|~y`+QP%qkx=7d_q3_$ef=4F zgzuws@PfVk+bjyJAn@oEJh3F$S$G)b={fyS^f_EWhzmij=E$AsEL7p*#{Waz+rURr zB>m$FSs-F$q69=iK@JrJJy1LipayV3!Q7!3K{247Z+E_&$}ZwdB)Gefaah3@lvD6f z@#GX05fGJ^yqS%dJHv|*1u=T4yKdB|5yQ*L@B6KunVp?o(7St{=kx#jxg$F>-PKjy zUDegq)m4f8>1o&-6UnQN;^JHlgF9wkC-`+>SbX{BOs36m`kAo9RJ#`IH%}M!0d9Eq z-sbsfSkYdHjz!cFz>6>9KdY5~s`z(C6NL#bgoaC&21@b}P;?2+h=HO!1|SmNDtR5N zBiihS?L^qWqy~QFHQrajS1T~t@N>2Jv*D-kzO|OF1vjxT(C+LkB%<>{(s*(6^ik4R zS_V+?7NbcLkGJCyskD}%l``)F{Cc&_yU>8<>0N|})n(qr(Vei6u`{;fFMt@ub2&W_ z<;U>T!6$l}2~l2_^G$aJbU}D6y-fskHJ~jl^S*6((Gj(HSX}0(OI?|Gk|9&iPDJGe z>z8>k+Xb&Yp+KS&9p)lNwkp>O9`vLGd2)hzi9%vQ83JGl0XSd)0Xq}{joji0()8UW z<-G)gT=_QLT=DV<1*Ab=NXFC`iWf}iTKrZNT%-{HphjIwDGL?i1rIWmBkwA2W0^Sp znmGLyW;t_jH3C1iRrBZs6~ui44`xN_^zIp^y-ZwihK$PdA8p9Blp&+KLY^b{6U0?0 z%DH!EPlOqj`2hv|dKfpr(jvei4Y)nRG&Ffvr)QBf{=F7L5UdPiR995DE>?y|<*_@W z3&x<=M|F2#_BC3AE1Fu)4~f?xpOOo$dfg8yJ40yipvuA0$lKcmsw&)zK;Ur z#=qk)SqKm+<)<*#>-agNnk+3_T?>cU$wVO85;FTVN(*hyI$-NC8u)z>S9H}??ru3@ zXHGjJTr zk>D{b-x1|qh@BybX>;hLYZ(z4J}=c{pX*C^bXi)akeW-Y}&8CJkRC5+mO0+H^K zVREVx8HQ^gKoMc-@SicFdq23`nTJEedh(r#DVEi&$v9P7)^7p~mpv+?ZAl`www+1M zpb(2vlnjH{gHgCFoHszf7}m0-W;k*EgNi*pDq`JmGJ{)5`sTo=Ju3FK4R@cf*Y~Z} z%3gvRfLhGZ>RrT6xZGn5RGZ`WdU35W+0z#Z7^~ENa`AmrsZU@_i5;iz4JL0agV+xT zd-{f9V>zT`v2P4Emci8RZ(X{LWl*-ib=X)2d-~U_?Jr;lTAnanFbQ(!a1@n*bVk zI1#AoV~p_(83UuqV3fjyLf`LzFI)40;T}CRJS-a%8@qPqPeoi5-TNqd@=?peu(|QD zHimZrhNu8T1CsM2THskU7S7v?dFkCH6H{5V#QF&bS}{hGkIwm;N)scKk8?Gh$z}rOJp*4t zeSQ>gWC;`d3^ThjlDGio4RZhUun6&8PJAJ@73rGp2&H94HARB*`AmfYe>)S#7B(CA z#4dnr;bk1?VRHEj>|8zsDnK=4Wpq_7x0Ta549Cc1S8z1~>|MS76Gl z$S+>)e?Jjv^@Ev!Q?xA6O|^VMskp|N=rNECr#gQh(jG`prviQt(?EK=+P{P2L)T&1 zBhMh1q~6v-B|_U$(sm6#M?d0mD=tKNXNR_HLEa5>cSlHkK2wGR3M~p-LlRq-cyLT* zEwm!>J}35k;vuQfXNmVYv4+G$QlVqOL3VRFWJL@LlNt%F^I-JGKqZLhg=o$_bO^ld z@^P~|fA2wO>qXLk<(RGL^(+hKFoC($I3}^Pzb%@BNPmb=#_^xghTHqu@iu8TLp#_rYK5WZzzG2Dyx~VXa zVhv~uO1SSWH%+d+VOWseJ0^A?U6p|{AZ2P;2JbUih6?6ToX-LdHt{xVK(wHAN|Yy< zig@9Y2$*|nc?3$eWiEIfMji`rKLMP`;N){3n?2D(q&*^&+4iwn*Fo9`TL zl+)(WZe+O>pxucmD?z((T%lJbuH9ciq+b3B+|3=E{oWUK?GL)C<~LfKZ77o2Y`z@( zi8a_yw7Sa2j~8_96ay&Va4pKuWsjbV_EOHL)cqB;tA@=ahn8^jaCs7T7aTpAh8T(d z>~oC9N!Q~pZ_1dpFu(;Q5U6KDPgl863q#f&?wJ3)Pu2 z<}wvD@g0H2iy#syOKw`z+=sq;2G9^@o(B+|NC?IxT6Mqk2q2pWbubpfIgssKFYP47 zV}F4SC0l`N-o;O3L?0o{IG> zE+N`sf-xf*(gdT0K44Gy8|+!Ag?NjWy`Hn5GqlmU@Ag%?rrb1XZu*N3o5FQyD*gnf z=-=WGb3oqSM=-Fd*iHE6xS*VFB28PUrCDL}b|ccX62@Pp{u+M~VbzoO@O!!`YH513 zm=qO1y&4s1m~j=~U>4_^?qI6dSiBWoALA*(Tx(lwomYwf6SQw!$64ji#X)JH3P;oo zRd%Uyo^=9=q#@$!f)Dh0$2l6peNAY=sYE^wor-r&&4Q|ruwPpdiDQ5MJKz7+pMwwn z_NWg%3DdaFI%X@qx6Of5d{0T28Fhh}6w+=gK*ei0KQUkE@8Mh=C+9`lpN;)xx_wK5 zF*!$kA0dSDzt=)fu^%VP-G!b0KR^ZN`?av*k^1ZGi zsg_$@0{&hz379|aqVRcDuGF!NJ&JOo?YWBHD@C_?0L0rol;=cQ)Q=Lm`DzgH1w zI#v!>1e#9_$QWJlBl^7tuA<3`XlI4dM-Ca=hM2o$@mEI+h0j}z4OBkFj72Dcn)o6Y z%nB*1&0#tX%ld}$FQB|!HABi6VV4yyf0KV_ds#sV@%6YPAuQ9qD!QxHfLP&m_Eef=lus`TKhxVF&(Bb za*pX~#N0im$Eb47F>S|k>BjV{&WhVzW4d*sYfO{#MgMYs{ofzYenP=?=XiDn_9^3; zrOG+Sv-yhjI>j}f6)gUrjA!fjX~*++X=b`}JSU-vl<~Y!m2-~gZ&)tfcphh> z?i$Z_M4i_`EgnrC&kG#4)^U|~KW;$7Um3F;D`pr0MZJxX50NYiWmuqP$2B6Gf1rm! z1D2CO^K#pj(6#j`ynAi!UX?X5I%OI>LR?nFvkzdeaa)c)0v?8pYeBUx`a}y?81wF( zIC;SV2X~CSI#27Kk@d5|`O<&AxlZZdLGHuAjQgqS3_ayi6`u16c)q{5IC|?*A|%Qj z%wIJDT`0O%vYdEP6%9D#$$S<~la#hpifVI4U?3cV`424qYrK`I9H19CiX*1KkE-=yDNGkx_v4p zXB7ug`X82C^DH<=MuQ>5P?T1M=rR-ykM$t>h1@STM-ygX?7yjxAxh^@U9#zUsKaeb zB`<>o7fvTo3?6CVu+m#M#Zo);Eno|G=tfD`*oMYiTq}kZa^X-?~YX9u=z8G9e1F&Rq=r+4hrxA zh5xpWI48`_{`qMT30jE6?j(UJbZ8-7fv{*%8315)zu+pn&9&d)_N}t~+V&#Ze{%e| z&5U;Q)6*)u4Z@>V)quaOam~Ve?)`9Y*}8^?u~mcG9_L@yplEbysOz@= ze{N8XuhpPFJ2{DOJkf{mRf7tpsr~JC2VaTe|0jbwWs==y^eYbP3Q?0p|NS47zdN8= zrXg4-Su~CO&5)ug@;7$L zq2%wx;-q=hGddcsjj6n@Nw_zg-xKopo-5t*H_&v@@m6cBES|E=s`WRb7Td^(99eUx zSW7F`Ws8eDV@Cg?(H;7gdvqZH?*xuXYs~d`IfgJrUSEW@-no2gp{0;St~DBY?2yye zvB>(*FxThUmt@2((W@sSJk5&KuJj+=9DnZNGA~v+7TbOoLlt_?wSISza@w4`AYL6S z?SZivX&&{tiht38V6{17{YYE^t7q{OQ9S0wKR_Ahmn!1`)@Ww2Q~vX|JeKv;_!FqZ zddf5kNnJJ5q|08Pwif_jK*A&gd z|AIBE>DYLP<@CsylDvJdKyNyi)2`wdDa&d2CR+-A8pY55b~&BvO7biof~IbuQbAxn zZJl5@dJ7tLuc!YT{203B7xLrMSQ>u(0thGZ<5n!@wn+V-=EuvwQ2e-Y{J+hQFSk$O z$0rNZ@Z(G=m%@*KLfLfuINmJo;>Q9O|9O7IjE`ib0%eZj{w;#^{=ZnGxt~l+(?YLTbwJ09Om1dQIyaz0jxMmYBVLo{=4GNA!k?;3~ zebCb9!~Hz3T~d|KstT5cA>LgGY>r0|!Hgery3mUKIPWLJO6>^l`Hp$ebjo=j+0CS_G!G7Ky_*TlDsY-=w-%w9ViL1?8gYpjN zqFsLwG0nTEYxIvLA>o!x==Kd%_KQ|9VnUkx=cj=ZxqrUGzJI<2LY*g<(M=bUjTWg? z1!3Iq#%2~SNC9=i(t;g0o{SWx7JL)+a48R7c|XS6tNV#b_Zew#;3eYX z)%NxDa($0cv{)Z45vIzI>L4>O0oxk;sRnGLr`)p8S%f z{?FB=A9Wswv=8P~3>VfC?v;oh8yD6+eH?E~u0ee1!Zl)A5XXf41}*d*x`^4phLo)F ze;Vy@8|Fz3cDn{R3^z9S;%a*j`%Fy>&0uLZz0CgwCLkL>qxXgYqCs{>AJqdMSX z^pZo*Ir;*0z&Usab5E}P;r*1&mg7XWg9^oEkAcg0`Qa>@lrQ`w#e`pzsoJ?#+Of}d zUxAxiY%Ci1w_CE@jdh)hqd`;g7_Hk^qT0{i@8qohLv>o)xS@4 zHm>gEAzuWXbX36En)qjQi(w*lWewMY;!T}wo6*cRc_GAMv2GoSHt%7Z=+}GDC_JGs z0gB2cJG)BW`T>)-mSf1u{hvq2-wGZ60#?`^8z#8wn~lOnkxYreg;{VJ5nG5E7wg5Q zlCjV;WRYCvFeXg}21wqDxfz`}7DN{<7Vn}LiO?Tm@QFWdElcWr1a}Y+u(5?6eiB1V ztsiny%qp|?$f|Kf(zXrFAte(_@_0w`vS5_fKwTEOjNgrLOJSrflkZ-IkF_La*_9+} z5faL~%bAcRhEOAkge-D_$h_O7UBQz7AwVc=M~$DUUW^^9Xo57 zXCH@hPj0_wna-{L5H1F*8k}28idO>&YX*15$|H~Z$^#xZyDk7 zEyk!8eI7$G8l7kJV~EFW{2uXg%Y;A6_(y-VZ3aGsN46N9A4NGmBwkh{TX6eF67ElS+uS#||--XAvU|rYeF*vT#dA^kIX_xP9{xT7WNG73+q!3B< z$uG8amHeU^F?%5JgS^i|ki~FF9KA4qRM$l0x$HDj+8<@PA&P{W(X`l%Lj&+mwy>D9 zRrg_Ghwt-n$8BOQh?jgD_ODSNI5o|C#q_i&E?2uMPjvAPs8q;;7$SC@;Eg{hCzyBV zWT^>`HrD&I#6hMf@F1V`co^S!MGC*4#W$n4JYfXZLUXa`!o$qD4)owFNzV}RRugp| z)oz8KvN4ahP4ertSziLUv6B5A3Gv20OXYv(yMl?#PJkPazOC<%VedaJMnq|G3HjBz zV7{?AfdiRXo#(M4l0*S8yeF~+N65SBo3qgnzeP;vuOT&_!9a+w!bm%g=u*5|CAvoR zX_$VD;yMTVLE`3OxqbUM@wGX0OALFz5a!2l?t;bU~Ch$EmNB8Y19NlW?=myZFOt>7S=&E?y=GfL+W1GiP*4WM@vnh%ZU_?eV&!+P$ zH=P#`g=nq9fQAclbeZ*9a1rLSO($mEOB+F_d~?uwHd-W;SlO*)O~wW9=zBO?P0r*Q zT15PcYZIg!XK0~z@-nyY_sETsd|j$oH?K>U^*FUm zN?WM~#s4RHc|B#1n$S$-=TIyOAdC$adjROlVe;vWliTW|Xj92t8z75c|ND%@W~2Zg<# zS|KOkFqxF zYq|LHQ}P;Zt;(|MN95n;E?E39kJ~-Z#)u@<(ym*flnBZYISJ16pOoOJ=h|Z`+ZDce zA1dD8Rh9UMuI{>GHwR%l+~Z=_0E~iUi7&UBs7**U-$GaA<5cCD4z;EOD#v)v6n_xo z7uT^wC`bt~T2PK8lc(CIAJk;%gQ{>WvT%6|Y#yV=0Rfx}XCJ!6h0+5i91cy``?&>Y z3gOh@U95uLxZBjXVz1!|OfTK2;M1k&LoJv^Jd#oTwFz1liGrZ3;?3epUAp0v}z(++RtZy$JLB5>c*}|2`Ziz{wLWr zph`Hq_#00DWc>eRBEidMVecna*GypZhB- zL&wF|vk9Onu7TDxP3qs@u|zU{?|rX2X%IhozqFvP&Ii<X0>F9 zB__n*@KeH{{(3^}MPMr+W@1O-S5m&y8BKI=#qA$(sj^a?O2rIooworgjG$y%nlbGv zq7%lVTzhsKBXeQNG9%OsRS{|!(Smvc#B%W_Q;no1>{KIzp@bSa?WSZkvSAj!(aRQ+ z;7WYMMU@2CYoV)THQr&nso`$=@puIu3eO`mF$e{#BFqb_Mxn)Gh8tN!5dIt1vzae* zr{UeDBK&3K?}SZC3&~5#DaM-Pvmi}`%774_$>22;JjP|nVX2IR@>&ach>H9UujM{H zyzqSdYYeKSou%R#eC({YFV)zJl%vD2ad{iD^|238ve4ctcx(9;ZiM^;$=IE6I;$NI zBriX0kw2pN^Or#3ZYCm&s`|)Xh5B{2`3tbv_eN*6mluvxLvS^SjnjKAeKfbpx;;zefhh=8}Z zsOPfyYkZds{Uoz^NQyUGjHg zHpk76JiA#VyB_4q++}&VGFLIJScL#;47KqyCYiC4AaPRT)~ zwPt+BH4nUzP!|xhJ?{qtsCBuWG0CF~Cb8=QC{V$xc=0CzdEoBw-F@`>cptzqx_^hX zb2bP-{b&YQ(R3Wz2>k~l!5G^Ix4sk`)ZFoot;hORcC*IqB=!nx zLomn|HX*nD6XAdFcC7NT72ZU8kw&xk)6Z;5|rtmMo!R|K}1nBH(0&;L#f85l|R(ps`kM?MPU zOmdl0j|^BF#AIP}Gno}{^_9YqXHqiQ+(~gD-^ln8*)@@mErx6_nh}_yiAvl`qC}hE zPjxVy4a0-Wr+n&HiW?Xxz~Q?-`3z7iL1&~RlkvX;*AHStP{DnE=eWOQ9yml`4?|iN zb<4>N%$bPKi8+F_=$KB$gL74ojH*N#*xNAa5cdm9O)(;B9rz?EoeblJCB48i>_fcH zi(tzoc}5p~bHA?0SB>j>50;{Xv{mf~4?p0$GH_a^(M3J@PmfOwUwlBvkz z{5!&PIq0&(0k9}Lxz|$4r{?eBGM+IPo@2$aTMLtt4XAj4iSHOEj*ng5em2G?wcj%_ zYDwi|9sfP}v7NAKC*}r!CBd`XxA1>CU3u{jkoyoYcOd~hOz{YBX2u5XkJUc3>5W#* zYeX;|-Wnd=ig0tooZ;r6XonGT4zU5+@p88}iuBR`UAz#DTjPaw6??J}TdJ_(zN_?Y z&?5?W;3jwAmWE8<*uXd0#`yg>H#SDK>P0*9Kh)d%KEpW4y=jAixxvaE~oTF}S-b7if$4DTddUJq>+k9#UU z&4R}wcWPf_`~iUurSU{kW;c8F3jlKc6GY~?S3WPAi4EBd82!X2XA%%7R zkMs{E9$-5R&`9S1VRR(>EFuUr<2Gyl2EC}scZ$A2ufV`;K16hdq9y<>Y#vh58GSCh% zP$aI=4HZH2zsU~VvLn-12-czFMSH}4<2{>3Fi^RQMev_6P$4e@>F?nVy0A}F&jbU3 zq07NQMNx)JNY6lpQ5}vvEN3xL^gm;uxmMOE6;{N|z2)McOC9_($>N_~z$@`wiOMq5DHI zzXFlwjWI1SrDCw&LUkzVatl^=0EJCFDnFs0@(?h=A+jd)7JsubEQg0G=K`yIf36lh zp6C@qMLXBXlq=G>k%xv#@5;{5LN{?k>&-5mmYbmk>2`u>9_5Iyj%BgQ7{U0Kp=%UY zBt#4SSst(|<__uLn--pw8P8WA77!3{x9kaH@LYN~n~*Q?M`4-_oZbi7q`ose5us3l z)IHqfhz-KG3a&isDv}75+kw%uPe~%w_c$=FDaeHnPhLOJn+f9s;@LFwQXe33J0q$z zGLWOwq95X@B~aa6_E*XRkFh!(U4y>TLZ4EQTOGi=#3(wjT*{${?^`2NTh+z1k-SD4 zG3>~YR_6W9sUT0#f>*Q*ZLpymfhx(zxmzd{SLcj&W#4mjSX zAH!UgM7jZGxzaAVj;}OUPdbb8q}$q{!?v@m606f_CV!L8U1%fNAhpQ|}1IA%2W%7xiA!*VlYzXb;RUTs|^3%=hm zx!^Qau=K9n3?GuZ30u^lT87>x)|nOEQ-f6Y?BnX0T>{0PYN2_=ttLjt7#qbW9WBRR z;TH?1VU92HW$0BRdb8tY>^@*(zRz>Me}bJ0=Bu&UFEoR8w>K^D&S53%UVpL!^B9f1 zDFkw1L;FLWCCS(K0Wc&4_U%l7IUsf`pHGi9j*Mu$(i^Dbh*3z&rfo+k7*dF zr2bXZqy=vT%BEPV(KBOzw!k_en5aGN1QI(!`bGw~HOJBX(@E+F^?lx7r0)guXBj16 z!&Y2r&WU8vaD>acI9>1F87xIFO-TQalg2a_Vcv;*RLcl_pv|y*@92&2d0`Z5 z1h{TNelZm&+vuj(IP44(4R`(86u*U`JH?jZOa{~!@{VlL0GVnvVPY4&V|+!+OF^ZHx*D-+h1ws$xx0w^UveT z2rokfCs%J~@(tq^Ge-iPiW!6p4pGyx^Y;RW^94$yZ>Zql`*t1o434WdjcGOsb(RXO z>iUtg25Bj+_PVI{oPmvyQLL5|cHg=15~;pOB&f?CQ@=y{8Yu#Uf(L z+TdRY3|?JN#7)vd&tTx0?*@F|`WETO?!Zwc$SO&Fa)){osD$?>qN5?`bS)LFyR+e) z)>HLFis$dKLTv*A@uz!YLOFQm*EXH zkRVwu5|hlDU;R11kgL^lZj`Jia7AS1l00Y9lA-iB{FwP?8WSBj^&EB}YKvD7Mx`yY z9;j%+SKym<70hKu`n8}t9hqv>01bk_C{RVFn$1+>YZ47~b~mv2BP^ZpcoRMw|0a|x zRDR{egYwTWrf*<6`W_qVqVGgDZ{BZn5p~6fGV4}5h#J)sL_MEERDm1ndr1Fn6LtR^ zil`5JJBh-0NkpCL~i!u$vfWRvP*qC#L32h};PC>?CE%+{fBG@)Jr)Dw4^71x^ zaJ+mNRiOJk_!(s2QAl|d8)_B9!~!tiqowzGGJJQb7VkJ%i{G>UJpzg+Qf^QdVt28{ zQCQWq;H~_L_B_&F^jP7$W^5{qY$(@?xXQt^rquaybBUP#94SmG5ida#ur@w;?7TVc=_ zy`(wzHQxYRs=4sG^XYpr(vIgo1xrl`yJl?`G*bBepJe2WWObYp|1s?%Kb0o_6 zGXZBV;dIp_#N-xyit)CIpc5=66xH}80?;15iFV_>&7itSL}^5#eOU8%DD;ccbD~K5 z4HV$gNXZ44Fsjl~96y?;BKb&jeC1qYBKC*~R9DoP zUsUV+y|l(mhd9z#9pHMj}OvC20`i*AL zhJ6S;>$Ae6^ROpYrf2xD*Tz4FqcaNjZ3va$wK@J(xTq=q0xV4Ij_3>d;(Hv7qsPs6 zk+uYeX_8UXwuQeUZClM3k+%EIU(tc6NHwtc1Bei2Zn6MiQP&RH(G!2yK0~|%(ElnQ zJTsvm$GYh(Fnx{AY!T**PB33VzDZ7W91lQuqa6*|RjI~7o~2oKtL$a>4FyK80wC;% zpc~*u8b#aI*RT{@_p??06ZCv|w4`IgO$`+r&6L*>741yFAV(SbL-d8}C(fWu5?0P6Vfi6e(gH9+z~P9+vgWZecyu0* z_4IImJ}u5pYf>r@Z~7IONi=^@XgiL{ndiCi!BFhRCVUuhqjm zSJn&brXW3ReSEPgpSBtE*mV9p*7eJem*%*Ej>{&UI&M8XM=&#A#)F`~l%>htJ5 z0r7OgI{bBPh_v9*wDg4eIf8-0?}cnN>|IPH0*9i}_vZnLqQ&qv(N?J`jM!ZMCFl;G z*9%oyS+YOP!(NvD5ZMPVX8MlKV$K(1^GJ1CLr-`ktl%#teBRpf1AcfiY>LYmSZ<*# z^mxS8nw|QnY+qkMlJhEH-+4VyJD|5(fqKn=t|8XZ2c&IsF-e-Inp|P&MQuoucLmx* z&NkCyBL_zQNE)190swta@ExC+K_S00a89EO=jh`673;>alu-&xYKHK=}Xnz3RYI{4<68UBXP9~RrA4HmM>mzh?=YOle~wAKCCbYy!y8 zeHuE$1HYozurp@jM>GN#Gj5^LiCW+ua?8A2R5zttA5Q7Q2GCjQ(dS)Cxbuec^DxK| zdS>e(dLzXP;HGHFH@;3U-xw6HpHxutO(2QtrQ{nyCFC1Rr;~5QB44o3f@Q+egd;MJ zO&gGL7q*CyYY_-3Lhc|4Ldda#2)Q$i5P{}P$`Rj*=p(rSMTg4|w9vEYm4FOcrm?Uq z%Ei2FScU=VTug^Aiyfytgm2z8m<(aqBK+l!-CN#4xN;d9AXjR^qc{LXTL$ii0PJ8Y zMu>xEx~>&B=>A77v1#D4rCv3Vi3Y8hI2cwv%PBf*MtCyA_vA#>r3q1Kecjf9f+V4N z4;qcMm+4sasI$67Vzouv<8wjVLQWWxR|wNxtrf3ze!YN-1i#01O)=>*f+C~ga;eu)QoBSOfVa*%r-yAXF@B7o%n zz8zX!3*F4xmSmcocz{b9*9#@%suJ&WVyp1r>VmE&8QJ^U1-mF4C?_&WM$0}lfGj~u zyvqTCC%SDmGMt+C9b~Q^>00%CC!mhV7?r05V&+-Y;kkJ?B)G=8aZ75)_}{hYjaQ(= zT4){|(1hlX!Aw|uQ(U<(sTDgAT)AyW{PQ}30`9a0$yZ%SB5p>?(&C?O<8VLXHqG0Q z-U_VcziPHf$Ay}Z&W6bp~dOe(KqgS7a*Vy7kAP=ldmOrHS_6rd;X+Gp9x#uLk z{m2o(wFd8uqDQf1#ytLs59X#P_AN`(ADIVQBuC)}dqFX`yaWo;rJfij%azY;}hS-cN2(N{@moFWcwKRXRvQUMFJD6 z%lnA2ffW+7V(s?_4q&g9_)qqiXEPj#hD>z6Ck$^_oH`o0 zfr?z?juzeDNTZQIk3Oyi$e7m%+dHfPcfyL$j3De=jqEf_8jJ|MWj z><;ldk~|n4NaN}9lM%1})HCo<6`k1`f537-Eq2--*(;X4<09Bq%+>c}iaNNd!GL}k zSKPvCqp%iPO1yRPPIK`SnIO?&m(YR^*8?0BG#MA?((G{sED&{iaWlij!Z@aHGyK(Q z@NbJav3(o#%8D<01ULC#7Kk_q!7p!Za1$*X-Yx#?=qrLWBWN7n&zH;Y10R}c;J8_V zhmGYR;On`XYGpXb=YW9A5r`{8&oxB~hC|;NsK7CM1Botd$RC~KJB}NKztm#>&>1P0 z3EZkxY)SO%!|=}wV{k5V#E8BWXT~^{Hudf2sI<|}+E_CjDJ|OIegjq}CBo32)ZYPa zMx6$GTLN$nYZh-DL7#=?rDhmFz!NYDggJ$+@fr#wxe;6d8f(1U8D^|EXYPVN80iQ! zr%0}E6lR;H7#;uJs1ZgcZ=M!pyhEapA@(a6 zuNpaZit@yM)g<;S1R0KE_7p{x32R6C*&}u-5S^jvJj<3Oog6lmq>>`u#dccsX6q~)>?EpEt$eSaBhMGFxkkWn1KSCvnbcdY(*@XHqoe!&5f z$ueY(Bz~D-6xNZ6YT@01DIciJHHPJaXfRF^*C4IsFW{ObHrFh1a?K6o8mN22atU87 zlHeIw`=p`fe7KPnH)*qY*BSXpPfyE0J}=bd(Z7^$ZYJN{0MoUTZ4mm`!P3aZ; zTxf^%$Q?2^M+?3L3b+Y93n-Hp78wQhM154O-+7P@G-Trm_oh@IiRvx0H4VaAkR!>J zP*nJ$MaJkHW-IiwvxslK!Z$Y!zRUK?G)>@}5ZIh?G(pTYymM7|!U zcAfqN?G2v~NS~k1R&D1x3%})P)S4kOybR`#<3XW0O zTWwe5OiQ$vDHWlN*cE4~iecnXO{V5D8QEoyqE}i)%~d!D$_2Q`ZVE;-;v8MjN}_Q1 zk!>#G5<$d)UBZ(t8N1(0#d1fJF$gtty_nG?A~(IfF)qo~3xhEJa!(`sAY8503rmgg zy_h)qylI?15FJ#4%D~W&9u(rAV%}qRDbcZ(Mbk@VmC5jZxIuVjJtDqR_n8b#vz#SrWSU1 zeWKqI6IJm~Q|uUafMk|6ib^3Mjzvzegb4jm`hg;CB|E_=38o@QjsaH^L&-vrkfPb_ z2T0NL*MUChB1b`rf-QA~)f$U_h$0K1^h1<}_t>d0gOxqI<9U|+IDurP$QiYN68R*SZ}m)hb1COKCRa%8i6Jt@Wmws z+;tL49>l$7-zrhRUva44(fwH&@uw()VE~5rl@0e#gbSx8<>~?qPJ4{o+j!h2&(_bc zL^;%?0U3GuidJC9dXsrrFWZkbdeQ3mAli>1Ie1*ZCe1X*PP@FHcQy3kK6>#AbtZPA zbzEOA3d`zzFMdRMTpx|E#Cfxv*KbFDx_l(Ky9~=0&O|6x>U3Bov6ZFY863%?vg?V8 zvIA!V$n$q7pylK~&8}8$^eDg7rWS{wTQCID+kJDm!CV1V2e+>Q1pC1qxc=M7pRz8Z z$Gp4b=}Ii*rW?!ml)Fesh2xWw`gCXP=Nm;0jy<9WK>%#Sli#bC)L|QM%IZLQ9{eC` zDGu(6#U_DiAyLFZ&9oglFUKsg?1}NGTz10d6*3hTJMLwEv=>w15nw`-mD5s}z(or& zbPnxiJs+ikY!@9uQiGa%4?&Kv!iCB83X>PCNr|a8j+p#84Ly$Zldvc{ItfvA*b~V& zgL4G3Hiz38M^K0#_Tb9v*?6Z%VgplaZGBDI^twnb`#$k{)@q|_#Y8?xKVdn>JKMB( zBOc<9W6Xq$`hO zXqXzno+w%Hy$hf~Ku3kkuNotgyOV#2!Y;TnhFa@C_(qpV|;Z)ru37OMI%047PFZznpX z8Z2zf9)k3#iN|p;0_WCaZK8c2xEecJ8k=6}7;q07ioc4Q5WVcbwbpROUyHve3;&vv z$+GdMoTDY+-uelAuXPsV)Qwa4WCygh7U>VhuKHe#8z z{Rab0kbbRHDBxNFzZ%ngKm1&E)QHoU{qVR^UekduGxZwf#QE@C^bTbI3kdob_Wp=! z;`Os4Lm~%6tJ-)nqF*Bv9}Mv|(a-Qh;$gN9ohr;X-Xlc9-tWU+o-qo< zy?_Yw7io#!0;pV4T|P#03*ci^aZ3A)cf=|GN91L;{BPbQJpD7d-ccOq`hossFedbJ zY-cC*#)^|T5Q+G6=)c~^ak*$tM!Xqr=l*tq>fT7<=oGwKzg>oJF*_RRoYA$_+Ky|IGcu4MFn=R$AFEljwWKy648YRg=xohqp9N}%?t ztILI3DNxhRVWe6BeV2P$FhxMJNJ#$SBc0e3z6*EIV+G>*54 zGh>g|c=G-A{x9O|8DUc${>Z4*x=4TZB_aJ)SjVk#-Z>pjFOoIUhU%N^s&|*v)8+lsr>$+QGF;iomPP^_p$+6aANaB5Fq1`$HJ<%uOlrlPPz`{DdeGiR?BvcUA%eEJDHI z>6EErP@E6WmtMpG_IVNCMyjCIBPD1x;-p8~MNu??@ez@Btu=7_tL`bT(8;TQ7lrr^ zB?sj^E{ll2%Yf^NpsD_jJh-{s03o=T8{g~d`T7@y{Ow`yZfk(oehVlY+~;99--0U~ zj+a__MFz`@-yMqdyD+lAm_5^(Bx4a6_^&JpKgysDn471=@0WU`N2F^u`X@RaVNKFU z8htJk-Plgb0&4wrDok+>>XM?Pq5gA^fg==%m%AQNPsL4k@A-DQNd4>uA@%PT8v(sd z%^-D+9ZmFmun>Lgy8uTk_UL5z6~Qmx+Zg}S3L6?7^G#dGdgpwT`#1gJpB_d=QxN(N zH77YhgwKnz20YdWf8mCX2@!qZK?qr#NboMRrqkOhF!*FGcp-QASL}!HvCccWfx*3e zzsD*h{dBa(h^!MxeO0(X5_^*H0zxvC6lB3}pQJXa_t3Hva#srtBxLJaf~?BM=>0Hn z>id8wb`ybn@893cUk1d2XR>INSMxk7%xvOB6eIANfsr$@yQ^bb?4Ak#671BNQT}K_ za=UBXPW>ClO`CHjKP4i7U3L$I1I-w7IUOdbtej>+F7Z?p^sNMd;mAP*(MB z*)#agTD?>)b)byrqs(U>f%AJC^&EfGh0+sC6iW2Oa!jYHyMfXoyhFE0meb{zV!mhf ztiUCT8nVM3NJ8Np4Zqx)Uo%UX^K(}tXG$YUxx=nO12~bbLFn9s>~&|q``7a_&mVWl z>{D28rW2FsVbGFR<09fc=oJ7orwX!cz#jdl#5;;vI54XOpyql9eJ0Q}0g?bAQF!Ey zgKvn0E->kb5HL0aF4J(;tEKB#n?P&kR3AetVGCu7hEq(@&;=WfFqSBa26Xk&f2C+> zxUp?QG+f(ci-xK&lz3_sI4Z}ie*j`7k|kvUwbT?1KGbC|??-~P{Eb?O`%|phVcfem z+qS|wJpoPGn!4)_=hThbjCC*YK@%sV2}F81K8c}BM&fdUd&i^|<)lihtv$#ER~hBGi(;@lTOISjzIMBX8!Y$tBIM? z#PltW4apRMk5|R(;u}zbh5^gT#OI!~7plsbv)__@V(}YRo~ds`RX&c#Fd>+guNx+b z$Y#u$s^^t#E{}gHi9pQx8GSL<77ZM=V$AHq3zcp24?KqF6}4vdAb?yNE{G zo7x4Eryi$91HXnu+Ed3!p2*3X#?og+pwu}A=g%<=&Reby3#xK2J7&P`EVqzKrb8nc z>6{9<^A7soxF0hMGwfCTbwNE%LH)oXXS)%qTUBkR%0254d*4SEYq<6#i0dpxG9a?bJiJ14gsqD zKhfZQ%kk?exY?^rA_&bhBf8Z?6eAfoR>`f<1^5G%J~A=~AJJ90yy{*vGA~0Pi9cO3 zYDON7w+x|Zuau7hLL{RPS6QYZB*Px)JP41*WjQ*$dz=J!)xKy^_3$R&~SJwea4<;UI9uQ zK zf3yc?A5w%kRT${&B=1^6{e^o2rPAb6;WzgN?5JK0f2fcMQBhP>ny54hRxM1+ezNau z^|I_Ls*b1`<(8N=dugE}yff?6ChFkBP<-G%<6FEv_&$UCQA=4nGCietfB#D;P3#S| zpoi2XPpG#SaOkx;w0b+1x|cu2djCGxdysXcA}hw~e9knV>1OK!H`N5kdmoP3G@0P$ zFbA0A56BTg(ZsRklD zeHs&Fd}oN6fy&7ydJ!RZoVgd%vn6v!QuHu;e$)1h01J#Sho zz{pcj3J#OdJ;)CbH@j%)vn`Z53py;6UCx6k-l5?89&mNbb5E#l;qJ&`3Vg5;-7*pH zAWiA!pyHy!jT7!4w3$FiUXm~a9*u5thDx|S)e=~^ziq-2m^?*$nJCcB+A&4P+>Cwa zmv~1@WMMAM{-zTZb_G2AXVA6Ywiq2L+Y=xxxpCh6v!aS;@76TMtXusts1jy&+8CGb zgJKOOv%_5wpm22;8)H#DG6%rMn7CKspA2768W_KfS5o(j*owCQ4(Cw%*1EcQxF9nf z_W*5m`%gDva?OKpeT{AZiw9lR4p-G4aLoSJoxqB;6(gkU>VeS{Sz z|W%P9H0cqi;Tp!GREv`&?>!VLztZtED z)63l?rrrl%Lxw?f$1NpU0Y_T)F$h?$ecU&q*m4wu;|hiMo!29llbpt4nXcztje1Zx zP@^&eVTk7KBdavZO3&qPpB5?scvG|g*gr|?e;|4CU2Vc@FR++iL|~03 ztnb0V9GN3N0skjgJH@QVTV0Mx-ig&D=6-VVee={5e@%+pth8%8iu2~7IMpTOwFFp5 z)^9|Ak@H(j#r@1*sf?M_;5a+q!Ih{mqCuL@8E@W2>)S5)m`OO6DFbnj4naeWk^6Oj zJzO0+@`?_sEf1m`q;&VSfFm;8b3EUFSdJkr!n{8g@rT2gwdA)>g$p;X1WbD)T)2zw zz|&AOz7#9P(Re|2n_mES!v_tr503tqSsKDOduU9tmDEgtHCsnJ? ztW~6dbu-&oWVUgxsu5xrSwtP1Oq4_OwunMTMzmlZ_@*-feU6he$~~G)9sI3bkF|q| zZW$)ZHW(-R!w;v@I?~={3S>U~&p&m|%?AbUbnFLhJ^ZcfFj%!Fxl2uQum7X89~4zd zjo}Kjlg5~zhz6Wz90?>xvjn72Y!{4ZaKAFQ312>hzJV(}zT!-iI~Vw-$R9I}ecuM# zq*kMrWRID@?02vosB@O^Bpm06Rgo>6h4;LVfng1gq(@8iM3#iizUr>FBH){{fD8C@ zAH_F;)VnkVttCh?Mm=srB3oXN|aN!n|6GIfHN9H`2xC@q&XOr2v z|HEqJcuCM9aXU9q0O1|NifvPI!o78D{6l}QNIT2a^%n$hg|eiDE<@)=AKHqOi`bc| zZPY%*Hl}0WV+*B~+aNDjxo}w|SDiD z(+>~m-dmAMCdMmB_@Zyapj+LAA=&hZ5QAGNaCrjb-G3XGf*mgMeH&a@#ODfeuQ`^W z1|;~3Kg0VI7=4ufyMI+TqlhwoyLB8xAIJ}mPU;cMfk2Eeo z{GVynEdN&?!@U{Wg!pWE6gCATF#Yg;(g#@>sf57$spUTrc+R`iuX0+NN6&1jm`sP3 zS}?&N?H=R1#dEvLyGHfye!jcfELSJ*v>^S{`TkGl`xUBeSH8R6e78uIO_FXylPO3m z-6Zmlb+J%Rf|7Y8dye9fLBPQw-M-y|iPlplQ+YzVC5VS~D=g;}IQv#gxAGL}whZTM z;0vd8TL_r(S`ziUHL6qI#dM}-$Wv;*|AR^FYv%h-L`sxF5hQXR+`V_yH%in*r~bD~ ziJTKC`vIuIJYensBQR6Inc^>w`w2+Xp~T-7CH@+m;_sBrPVpz&qbdHzvIRM27V#&C z4HSQ8%<=heJg!WP?Pe*a~R7%^YgHGrCE$asVK**#E19IGj7j z6o=lSUA zIeM+Y)aJ-pLn04YA>hW^sW}>xon7+OKGm$YUg-VC(iyw3+KZ{(^OK>vNAANWr1=*c zQTz`mE(BmQSp$Kgyyro)Q@+_xF;~vvorRSGzID=zjvLH1F3~zyTAL16TYD!}f|gSJ zFmE)ml3Tcm-QC5+sspf!@s!1MOZx@+1d{VJtX{#>J3DDZ(dq#_adkX5lpX7Q=J_S< zcXF`44u|>JH*2dd@hH8qY^*2gdavof70IZrWd4Geo3Ml?{ipo9%1dAlY`ZZ9AJspT4vLR_xA5u1 z;9cC0w<3*9q#ZF4Zllib@9Ll*;1WIKnysIf30H4)_{?Y%!N%ogea!n`C-AfeSu|5uixP8A7~%2(Pc-nL{_5v8W4V8FNQ0SXJT=z z--&*NrOlzAF_!=pU z{9n?P!rKg3tZ%|w%SfR1Y)lbh?b&eO0@+ZkH0xS$A-=iOM=%(Pq>ngHo2BD-#it1Q z)9_;e(+k z5q_12O?-?({!ai201%oVMo(W4+7r9J!Cp{@9v}EeFnfK(A7i;p=x?%fZT*5|;Tk#& zgpFq22auTsb7gYy`txE8D&yjt7ec}}u*=zdylvw#f9JW%-7n>|Ikyu;VnuinWn+wE zf=-Nm#J`L)*ogKeS`s9m$sj*ON>{XH=&< z$PkHfA+hi`jYS{BdW>!augsPzI}r5H*9|vvl56?LpvDmI4*ys9gx)#ayyCs>^R^k$ zO9%svjs}GMk}XIWjtf2MdpCL#%DTu8z(@gQ(V6Oc*=6XaFv2B_-=gH;d&qe1s&;~U*mzQWe1?5R=lY^;q2BACJYSBuw3i_@L*_jm6= zTX4wVSFW+;pe2%izWtw*zhA;bT50jN{M}Y7TD)2}G2~|@+LiXs)>iSJ4P$!HuhHKs z-V}k#0m^ZsEEk-e^YmDZM8w-9y7&3Lb1@%9Z^s{=M2qR->#vlFmy&N2c0cCv=1%{S z$p7N7%2qO>hwKl~>tdQ8q_A#q>}O6O*0>SUBsO)pZ!lzS^q#B1D~M0ys^2n0sQ$>o zwV%M7C|+lEtoax8s`Gb-C(VY>EHYa8A)Xiq!my%vi7S^Mpy=TU(zrdjVGYzQ%(F32 zy}`tc-5-6W2J@T;F5(qGtP~wGLbv}6Xe3JPkLtxeAWk+`K4Lmzs^} z!3%4k2k#*t;yeuGG4B^#|C8(XHtTK_e*J+ae*JEPNn?W#>Jj~M^@}@Q>@pc^a|Jg60G|Xa3>}${5Iqd z_da+Mo^-=E_Y11TFDh^PJHVV$oD=v7*R#hCT zKuZhf4al#bIv%f~gc%u`mHFGymA(@Y_GNRt9r*!y6b6WW6N_K|cmCVdk!cJy`js>W zzS!S)TGDy8|2}-Nycqgc0tFRmfcm3c%ZPTv-D$YdVuBty2B&PassKUO!W|uq=olCW z9WY#MxbR-Z!hVq(%Km^UIkOUMC|AFdrTDF8ak;pc$TBdV#m8|RYdE1-PlP`%5>P1D z6=g*5xjlk^C7_Z{`Evm#zRt;AiTii`;hzglvM1&i(mZ^uR8l!o^h7U!Xros7b*BHH zR$r0yv2|@ThLmI@$vmdi@rjMUAK5)T`9J#O(bKS5aLsMw9^p1DG`45yvojKKjK z@(s>HHilcvf#C*VxORiW@JtIs3%`r7Z(-bz-(q=UONirK1h&Uy2p|sP2ya#)I{+)KTfR_8SZ_Ko{(WGHa%DVSrlDf?L{j zk|;*|vk;#?za&TQ2{*te15L=)WgXV@ns}S!CivpQu|aW%!_G5Ro%-jD^dk+057#)0?{2+U-iBCeox zxCcy{tMBPom{YN*P5wr`uw0wjOkv`|Q3;U#sc%jGkG}r;KHL_LXUqLQ1pbu{GTl6G z0T@hHV)Uc`pA2%q4zO!y!iD_%e#JSGz!Q5;J3B|J{26eqgf11?DR*StD+X9wb{iV+^=%cXJjc$J%{kQbR9$hgXEpV z8Klkp9OFfK7k%;AjEsIo<>A7p3@MyBEdTBb8tBl+-rRoPa*EN39?YdoY~s}D)@#96 z*h@8XH6{o5*xO^jijZ(gTgoy2P|O96{+NTytNjEs3=S)-gJ`g5Tej<<^i&)hGuB+h zAB`PJ3X-*w2k%tG?Irf+;s$|s=UNa)iYRlvJ7N|5SyD~+HotI(?|7x}Ad`%}k1Yf_!qPjikNw9#L5l2St69n0 zkngSb`4MkR)vHkg?_>X9%<&=q2HzEWGp-Axjd>`Z53#YE+tzA=cZl!tgx?DAe*$>d z^sMs>g?6|E^w%420ds^)sD@1kJ7h_5vvM1D$O8nss19W?_AK?#akqTiK=KPKmfnSy5lkajqMKISi?$_`p1boU;l+R=Np@>fzX)8Z@ z`zCd-l*^i1ziNl8&g|x*r2EaWTQHh#{2buC*@Rpin~GNK{)~9DiE44IXWtCwcLDvq z)lwDlwg_(C=CLImAURs0no$mei-{Ru;;%R@bC(hQ9hO=sh{gM1F?|O^rp|AHf=y5`W8q93**bv_`<>;TukjJaeqqar5w+Rcw5;&F0#3TrxFap zb7h#W;PUMtIz`|AFX8WnK>B(3qFN*nFGx3De3>*}TH9pMX~*k5Pcrp| z4F9Buuf@NS44S}XInO?HmcZ{)65DF6sBL|N}%ed$m zNccs@Lh&v zgnZGwu6P7CB4MoC+=dSA9bKp9zMM^7^O}8*Ulm4(A51gz24op*2bf;jxnHVl(M^Zr z1QZiDV+WQ)DL*AwVe!-NTd-Wf%^Iskt#yPQ7fo8Am-NZATO#hDPZLdPHVd!%6 zBkG)rI^#@rEupM~FeoZ;ZN zY_x_Mq%zM4zvZxf$(9nyplvgi-(ZMp%wmkz9kCeZooO{Hh8+xPoWK?yKmflbu^)JE zrEFSC8ek?M$K|q}qQy3|>5@)>EGIZgA{1dc7PMK8&Z(H8U^&JfPseh^KPyBSX;}{O zwS?tXQ*so7<*=njCfzh#m(v@Y1=x|__RWzEXpGozY4dPv@T2pYPB z_l9Grp%n@5eg2A*DV)5Q1KUPA-V4I%Fg@>`BF)&mxBbRMGt-lraq`}CkPGRWIS$R} zlEsGG@~*3S8f5a`ZuXqYhy&ic7U|y2f({>R-#f0k`u9Hw>n(S%-kYz0^`5rsMgiTl zNVy%nACkJ-oI5b4*5(iVbT{gYQmsIc^S-zaocCIcStrSP8T^ZqrSDmsxBDgZo>)0! z$Md5qf|@U(W^6ECDJL06A(5901%TYGL){Pzv!gTv<2YY4cT(fqhy@?MbOKd9eg zkM2Ii-;<{3V%Ssm`)(dmoaehiTQwv+s#M=Q^t?My)M4T6&G795Rzn5L&^sg>p;w66 zwRG>l?%W#OG;LIPOy-cnJnhkP{ajkl3iEuu^8G!1$7!qbO^8#MvG9xfyYctXmi__C z0=kOA1mH!z_0!`E!ov%O_K0|!F_|-__6-m3IaI?F+n?GkJUn-3dpz-C!PFzd!?TBa zj@cBbY|AumSO&&qzR6mqM%!;_=-zfmXA_Z&v{mCW!^2C5;sCra`qnI=w+1RQD;nDc zyiJ+mdvi;TqNeqoQt`oz<3YNDS+GA?>@UWiCvxaI@ zJB5ekF82&=Kea=6m`PweW#^XpBK|_qpaOfR-`5|f`t36^mSx`8ONDh>*5kuPvbZSL(&x`S%pOI0;21p+N7xoAm+MfMVb>rIq*~qquunCp3lP zoX-9{AHsLBrWQID<1W>@bIuMJSdOoH2ec8W&X&}bYA~jES)T&}R%hU-5Y%%D$9NS& z4bXkZtHGJxe?9M=av8`(DehHcpJ2kQKT5sD?O$&bCw%N(zCB*O%_zWIY%VflEAiGS z?5XEr|q+=j(;HSy6d^C+1(b34*ZhdTTp(sz8V>uN*91|MnX&UkJa4L7Dj|C<9-Mw1YjvAI|naqkXVl zl#Gc*q~JMd3GFWEiA&IkSBEnb@yLt4(;wXruRMOB9pDWzrwuVsf{gOT zt>6`XB<>^f4Ag{Oy%-DqNCI~F`B@NeFd1;4!szsJwR&q?5~kdJm? z<%41VQa{xfZx>kWFUQZ(AF1%(WR-)#D>mWzZFml7=vf)BC1#(_eU{@@dzKoOUZ|Qh zZW}9Ud`%R{G+`fO!DdhYT>n~adC*o0)>hNc!$`*&vHrY&d;Bc;ixCInpzueT@F)K~ z{G|RS@aSk(cK5f1`B!}v=4~y=?*2%Hx0h8825+GWZzYnW%R~l0+Oxque)Whdp@t`E z+%6{!$GBZ+!n~R=4+XC@J-owQ@b>gkG#_~|cu{kH4G?%1lUAZy5}vo0tNCRWc-n;f z26C6%Oq>p13w}MqKcta2mlKXd-dt$Hy_#?pJnU<@l14j{12Vb>C1Gy%fDyUk@)E=| zro8{-4<>#i9e_53enDEb2@Q31=@UKcKf1i}EH{tiZRF!Wr z;SW&d?e(p!JvbL^vt$wow$`_9>(j1pEL$AA4<+rcwdR)%uiS(;!3po(b!p+TY;o+g zL&9q|;blAF-IE?3%VPa4J`{d^vK74zPI&#&!(&LaeyJmww%OkF!n+ZMw&q33(B|o&mX613v0Y6* z{0hRQ8#XMbcjN7gu#qk&)NT3VfW`d=&KVCwi}s`xelz1MDqllvK3QjTm}r0NM7#0D z1ls2(p*?c~(N4s;P-rLF2cd{G;sDfz_TMkJ(6;SVZ~jHlj!<>kiB^)~ayy?5a_DkM zpR$PmtLWKJrNH&uaBYj+H3Ijb^!^j!HeK{KzmTB!QnZm6rm2^uqj&CHHB48M4}pC% z4n|}Z`SeODgWW%-Ty+hz0l-}=+6nZlp^j?cL67+sv zl#bp;lio*7dXX*NP48FmzE;!H9^VfaCg}YMt)$Z1=d5IUCoD*TJI{t|(|f=>2j*g5Hj4={@^5$@Jd% zWD4BpY`8YP4-4Fb(%T79NnP`~=aK}yL9~&ecV^o8e49z{Vw2vU)hT!#VdG_w@7^a9 z^d661O&#A0&P=9v8X}9h`f(-vz*Rrm^gbhS4@&Q^^b~prJfEQVQM8eucj~xw<2&FX zHNJJ`d>&Ahg4c02UN*fQ9Q2-sUQMNU!k}b&%l@1K_w7ql=w1E<`|+Uko>7)UZ^2&^ z^e#pl33|Jv<=?qqD|+jBeuMd3P?>_)sWx6Vy~he($jOHq=b~3r>AjIj8XfX^{`?fU z?~hBNxAM=2q;~`YmAdBh*yj@Tu0R_JdLO$a-T0Q8N)4SyH6u6&Vl0xsM`G=%;+{_euuUed-w;F9E=xvvl-g=YXK_>rRg$;m< z&kAk4YW6X4le3BV%D?tX@S?B@ccQ(WZr>4+8B`y z6C3>_pl@X#80Rs%g|_=+f$Hv3llsj+u~~|qi+|$(gdTw_PoCb?kD@OQ0CD)91<{2T zg#Y9gx!AHjy>EL4ejHtZ(K|5WBA-j9y5rmC$V^{oW;!H%GvDG4P03UQAHk{MWR1M zI-KoPQa~?SA34Rf7*y?duZL6Hut2xrgtTpV8C#n1HgGwLZ6jr->L$xvUH!*;&3IB$ z^~vk!)cQYzKOb>$KYjdCa4^2}`mbS?cK3t2{zp$v>^|$Cgsm>zA_Clprxj@*_HfnU zNQqZaJ7%^fUZjMFP%CUsGDGm=7M^D}X0QGIECaImotyAOTwGE!Qo zKE@d77ixC0vsj^HY$vy9!3tJyL{((9cT%N;_x>1M7P}gEA+o?iDG=Kk?gE?pSsNfz zBrq5X*#V(!y@F7$(q>ioR`?nIfr==M3?~d0P6Yjtp&Fs)luW=g;`t`T2jh|CRx~NT zXgrbw;yw)T#2v!K72~<%L_{NIvLpJ>x(On>86oLqa;JO%x*<%k=DK+h#3Fv%AnUbg zk{@Xg&L{7KKY$amOtptgU3h*HNmK8Gi;C;KnN!DqFg~3+hUhvPTy=D<{5{J$DgSPF z`5V8j`Y<55{4U}1Tojeihoq^r+c_!t{IJoD&tC4flj^DB5M;atG$;RsAocYHL7Z{g{3w2#!_&1OZ2) z0gyg9p*8uKt*-pojL$=!Xu-l3SPv&#WdphU9AIM6r;s&b&o4m8b% zJVnz1TKmf|FZ7=Q(Cj}Fm8jCK6IuF9ycMcs*8);C)&WvAHaO4{2WoVnn;qyj8}bz0 z1<<-)YFrA?>|ZW5vUFAB1AtVGO@LI5YXGSl*E-O82ioXBTO4Ss4M7S5v-UlD84fhlfo3@nd3EqW zVvmKE5Q3O0VU3<5sptB&XpN_64D0-SFY9~;psI5Pp_c5w@CPl{?<3i39B8crt#_aj zK!f|TP8M1sP`RCo+G|zqQln4RUWW!-wNm?T-S(F3eS})FKjBX#d%u1UqZ-DUqJYPN zybi<>p?b_h?H&``=%v(i8;<7zF5+%{F{|2Lf#?ts0V(Aad81mq);^;y^s>UKfs>T5hG}wWLIM6T$8g4_LqLBctN~y5~ zpgo3Ix~j1pkg9P8AXVc`K&r-B4m8_=<~UHmfog2XQ?wAEwdE{gjU}=cqkk=+NOm3m z>M=Bv0{G;Q$(f{~=EvL<1w zwV=QC=iRJ!xbyW$4Yg#K5Q3a^lEygDI0ve7peB^ob~LMHxfwE&))2x!j$>jO4UkG# z|5X~`;RN$tX+Qv=>UWKXTCx`sie%S1Np%j?;6O_ph*Ov9H_NsAO|+w0sp=e6?E^@* z{<@3(&c{$w)fQ-|CA%M?NOqxLu{H5rsR66OY&YNGdOlLZonL25HtDUJkGO_tA8qE@E^jo`G$b@$yAa2y} z_#xhrxvfQs@5n&bkMWKS@oxx`IG3n18*ktphf(kt45K!-jYL9!DPw;{8+-iU*C)5E`1OG-^~pWoVT~maX!aN?1pMFBC+kq-!S%^EKOl9#7JYKppM;S`a$hFZe$D#i<*ETP zO}vMwp38fgdCCzmqEXj^4W(kDYq>`b4$l!>-JiG3WHXpKDCwSj(_`25UL zI_)@4zDTD%iRZ1e5J~xH7mgcfSzGa4;Ik>y$TG7;O@|MBd^1Sx2Vg&j##Z=8qf+~O zZum6f+jX)f)e}Znyop!Yt8pWOlyEx+82l_sIR`9&gzMOe|F+=2`nbxoV z(b4e?mI)|EAM5QXs-KCJb>b!swWfL9GgxWzhgzet2LMg@_@SyP&-AQs{#_IBiu}QQ zZA+fggNPq)r#yi|;eUo*%oP17o{pX;|IzBw;Ve4A>OKa zB$Kf*mb;9=x@PNXG*80`4G{cvg5M$ENrT@7xCoIZ?#)-l@jHN|;Yl9ex+gK)a1ng_ zGr$XshH{|Acr7>{ryW+k-_0!bnZDum>jLLs;}T2(Z4$Qv7$4Pb3ieJkH_Lew9A007 z9=KDY`zk-)3k>kR)dmDxJZW)6tkeN#?N*&-4NX!Bb&x4t>@G@js@tl5=yqArum5${Te@Dj&JBFye%)!PX2n zY;b;+{iy{4xe2isTgm2;`^w??-j}_Ipj6ommv6n+d2mrMb0s1=DjG4(}B!fqa72+1wbAkR_72shJ#y3J{M zW)DV6+Qf&;g$@06FQ`^|7_s&C&vQBK>KSU@ECXNq>o}Cc6_)r2fX`3l-Z8E~!#NfP zj9J4lDmv7TPmmlE)MRWP(ClBi9bO2y4rk+_>h)JITFXdw!MV6``R3-U7d6S# z_h5r?kL~86O|cJgLLFCJJ^d{-S`93J{aK)woF@^v99xelHXI;$+YxFKXXm*?1h;qV|D&&0hhhZ}qF4mU~^FZRwGX!d&s@Bgi39lOp77NA;8n!(p>Q5sthNpv`p z1Yg#O8zBpEVPI2Wf;j;f9t6%p!9DQiz{M7&D;D#c^8R?Q5rt00#pHog@k6QC7Zvni zdtOpWA;dfTY)J4uWbqr=eO8_*(MOT$_DUI#S;Tr6VmJB|k`mq}eOXAKo@$agnaKu| z<-jxgWLm@kiTTsO zFvdL@kUqVB%~_7M8x?K!dyIc0klZ5g&`st=9@Fz{b5Wb&`3*2y{M!8X5y72vkHY!V zu;)qaL*O4yo}%l;<9E*gkh2=1FJDER8H?Jj=Wdc>apl+#7|QNV97V724qb;n(O<~) zTyJ=8z~{xU%-_Xg^TVD$>oPINq0DNQ2^`?Yq8c9@d{8ZYekv?-h`GpO(;xg5>R8lX zkKl#Bp!h^M7!a%xZ=)B zng6<~!RB9hZy=kvbag`s#~Yi~d`m4s4ospa5_F@C!PcNsyccr2g*iH%U7lN>$guCK zJQ+0T!15%v1PIphWGYq6uE~=v-%eAkBTsIXLNS0-<;emjP0i~>o>U{%El>UfA!|*; zpG0}m(p_=Kn@lcdvLjD=1k=hBoUO&Mz>OuoNU56hyP2cl-Xd;mky7z|LNsrLMj-cG zdjlF7^x{yVIghfSa_Dw@{+fb#cz1KiVKz6%dd%}YW_~x^G?SkhI67RKptd#*B=367 zIbO^=#c$4U4tqpou!bVmfF}LGYn1vRKq`iA7x|7-!}+TGpw}GjGbi{=PoClFWqNK0 zI(uV8a-O30!eXjb6)b<6cm8T{;}E1+EgX+f1xoXVZi;xMbD`Cq(5FL!{~o%B4IiCn zdWN99(I~{&WmWFYWz>kQ^Yg+rnH*&Gx_~SORxQml^M{zD_zCnap8d6>4t|dF;?Tc> zkGN@eHY7T_&E}nn5n>g?T}7$vy*Ne>^MZEGRtQTS#34hb^XIxK7U%abLa*1ts1l=X zzSkJ-`O@^t8z2o&ORoTHK$9SxL9ac55pj9}aU|445b8E8Av|}aQ0dPnO2L{+>uo9( zsxs44$>z6IV%`3660x>&NP*f1A=X6bktxKw3j$OV>&DMOtR*)m5UV;zi1pe$n^=$h z(nTytDbP#o0bp~-%Z5~b`dcC&Y!4G~_#VDK33bTPB)FYKmo`XUN(I@Kt%meDi4}BK z{^a!Kzl>-FPWdJ7@_((%kIhNw0Ll-w4Ujp2i(TacW{)s4m;Cw3H-VyKZ0(S}cpYz` zH6V&{u93sbJ9cAUP?JJ4dKzV(GPVT2&tC`Bz0P^9I|^&GdgyjstlBZ{?>@J5 zr@C{Rp0bQhQ>jE!4x9}uz04D-^e_)Doi2Qr8$R{?!F!rpBD7gxka>z%&3_l4mM~*B z|IWdG>wDNLfXwy(Im0~SQpA#&nkPmN&X2ZqX!3;iK{}w9_R7dHP_SsQ>{3)`opcBM zVKIgAffy-7@l=K@_FyX1atV2@_mQ_(v%K^2foVn9@MPYjnKygqKjCY{lCxU@&FaVP z6bq2lmffbImh5&yk?h@0(mn^;??4&^TbBW7 z4Ehww_5xyCi8W~@`lPYXOjdQV!ag6^-zMGPcf}K7CcaetodKvu`a4Tk8_Axnp_c49 zgj%wHseUf!53JofIg(xDKnoqH)`99AsKJ4jI8dVl-K?RO?E48tviCVO+zqHf`nw+x z+nT1o%w*j?LiLvq?C%=g-;d!;0IvsNLG{-Ms6lfZVdh<{^RAP;1y0_4Ky!3n!pyr~ z=e=9<7J)#j+(JMbrKJN%G~t`U4m8AphB?r12O8->B@R^XKw}(eoC8%k&}0Xi=0MXO zXodsLbf8%dG~0pZI8eZWY8+^x1JycEodY#E&=Ln~bfB9Z=r#ws%Yl|U&;t(iume5f zKu2E1FdkNCI?#MKx-Xny#sA@pe+ux)q!?6P^*SovUd_9^9i}Z2!(h70o@OL zVYOCgN*W)?Bs(;d)U=cNhT*MZk|BU*Y4Jsvd3Wl(<0S7$C+~1T59_>ynYUHvEo5Fi zOR1is5+qwoW~nytfoYb`yZgh0yfc`$5T|*N$IA!iEz*d&?2P$%)0UBz_F8a3=F$3*T z*qjci0Wmb-ssxDjvwd0!GSM1dsrtzWrpqf(+Xkc4d}d2 zOWuH!cMhO2Ixk`7t~R`u$*v*< zCk-cQngdODpcxJ{(}89=&};{q<3Iris&Sx&8fwXYgis{A0WDW;tOMi~j#~nVEl$&G zX0m#op*W8Z?Db6D>r2H8VkzFL67K@!B_4r2!pu8M=RIBWKJ4Uu08qQ;Ji^R7Tj%X5 zdCNg2jZHvR($X;`o$$~&2dZ+Q$qqElfu=jq3T za6A-gd?1r7)Jzf;uYomqtC*yTnK|T;moW3z>AZiDyz8C3YXJ?=%tx4cYjxfmnHSGe zs;6iplCAz*R2%rfv<98`66EC_JIIC!%G%S4Pd%Yl4qIyd$ zP`%|eYdx*raynLTDdeqt)LTvp>Mg~N)u7A8z*TQKEUnAcTMlRSmiV%!>$kvbNRCN1 z>wOQsr-W(5*n<^mH1@vOn_{g@N54cJoq+xG*e3lZgLCdkzG>2LxC*hp%{PzfH?m|Z z;G4T*x9WFQM5KrB8uS~P)jmSYqI0n;Af`M!o_DmG`yQ$ZlseW+%r-9mk^lA(b=TI@@4qWO1otTkz%_Jd>*?nuZ<_V=lOS5B>*;#+6~?HRF@XLz ztfvo$nI3Y?%dt#(X0NAj*-4E4?^{ogAFTSDyq^B;JM3@Q*3(x=-ma~u$4lNKNO8qC zl;{6%J#DLQ|Cg<&8*$kc2V3%bdI09auCAx|bNw0=8HY!O2s&nKudp+&Ltc>$>P+}Dg{&%gX zSHca1y{2Z%Ufb*GK5wwsU0Y9ou!DKKww_)odCQ^vYit7gf9q*`I{LqCJ^jPy(yym) z-$sV&+IsqDlDBK?=~2uZpN_bMwq9P7em#98@~TyTS|` zdZv1tw4R=z-X^W5hwE~km9wm;k5_M#*3&-y_5kbY;`cjUPk)ZD6W7yie3P)A-o`fx z>*=+8leC_Gn(q?U)A!*`7uM4Ym@RodeJNk2Ur)a@F>yW3{c(|9++v2EU;sCiZ`GE3 zKH%L*{$vMW*N_+4H5#%W+tAUG$e?n{AV)~YZLzj?e87*SywR^t%~q0WC(vF;;&zd z$7O7=&APn(F^Bv9nK{_^&tXtehO6&abU$-h&jWSvre*HwxPy^f{NC?pp~PT$LZq?j z5<`a;qX+m|KKeoLaD1lSEpB=RLjX}$FWId<#FhU~MH@bm81=|%opcx++8(kC*>Kdy zd!ye+CzAY-M`w$xYS?y^MVp451Gm>JkX3o!T!3~e|50-%J?5lwF}vLPC^zLAeIW%)452!CsM~Jj1aVi09mpLg2#8VVMfBM|a(qD0{3zhqQ}ni=^D6ko zy7L|7DYy&!`pRD=F&>z-=hB0>XIEBN+cVJBp8hHA>7mK6sMxW2tnzNk96$WpqCT7XcP*dL$t(43cL+8jV!X(k53r>^zYI5((#c% z!rK^c5!|VRBNd+yH-3ja>Rj==vy?+-e-!JCRRXp@Pc@xfz?3Y<`VIIrlEskI>S({~ zi?sPK`-1r?=^|MSLEW|TbH8Bz*FR_Z3`yO!@)_^0^ZY6JCh=YPUUyz9zJr%3d_UTn zitmG!cSV}=zNyQrPE($J{wtY&458h%{ww-*J-?zKLuz-ed_}*m=U4P&i0!VGujtqH z{PTt047uI4@_7U$lGR!MRCxh^=KVZU2anb2)bV-ylvIAHxJ2>GqBqm=OKSWs4*4Yc z(+x!&Nd7$exhGlv+*|IJKdWZC8ZYc4C7>{&cB5m!r4?wdeTM_V^RpGt}Ll znYukI#(atPNPM%mFGi0rNj`Vcf!lTce*{nN_-6CVfF~!u+3hIEk<;sUC_EMEc6EZE zH1ox|DCR);>89G@o-aNub3lH3`pX^V;&v|T@ zgP$u=viKDevCT0F{UtPZ|rycpZ$%t$D&I{Fai2mMBupNS&92=q8&j0e$MN3n+7SU&U* zTys>#y7wZ=+9+RjqpH)3I&&r7hOW;W*rS4`rYV0CJAiPIa4=@mjZwZpmNCYI+HiCE zh#t6BjP-g0M;TQJb+cX(u6R+R6QhiH{JM}#Zo)0O9Ad0qqPamuqn zRKxOox;)XZafCRZj^)*P3`rBl3D7C+#zmgUXtj$!TKU=MQrimrsCcWf!0WGj0}msn z0r&?1v&uvLvH11CcX;3hN8*Bm&AA@4(p%gd_%W6UBTbxk3VzqvTl{(?OUxS?@>ev< z;7gHie6kNw7z=zOrul-uN2xfq68qhV1>V2~IF_QnlyA6=NbTcxNLwVCd9iDFK8r`I za#&a3<`D~gyvsD@E1m>>%Kj>PHn;(qjJK%HFo{=X<8t}f4aTR&0uP!J{T8#ZDcJjwi6alR$A{*l(=%~PT;{6cZNU#OFxJKc?1G+x zf1GTfMrBU)@(5H#dp$0an+=SZSNQ5Xj74ohZ*&G88*vP1wD5oN26Mj8U-y0H!4l_6 zk8z&2_^rS(Kt9eMZ4KlY7kbRmI8fMT%<&rM`M|wK4c0F0BS5fs_?2*-zb*$0L*Upv z3aWRc`K?6+=!#=>x^!S7XmqvOeDjBzTKu@!~TvA)SeT`VM1t4o;t+K zhNY|fd}K$pu`os`I{#Yz9^eo|3izASKH_x6=RoC%8qMm)93PKWRQM1UR$ku2`t=*% zh#H^2{&_ORXs_`R`rI@60n%Oa2?#}qr(V%pnMp92jcx^xi^zbGR^o#U`;f-7@VN(W zkn zU*42eHp`ggG0s6G>sdybx20@O2Blj3AL@gBft&#&0LK_kf}LNor`a1AUcYDYFmvMc z&^viy D%6TOhl!J&~+DuD7R@!*e)Dj+dTed}Et<^lOee=yM(*nlA7FPWoeg!X05 zYYvxp>o6wf8x!-4Q3bA#>=xPm57_+=p#QU_|8u06{OZ{?~YSa{LCX`JlH=$m%WFcB6DJM$GLZ(DQwfw=qNT^N$fEqO9YN%$Y z1Jw5_?oLRkug0$L&yrFjwUl*QZE9tze^`%#B%!9^M8po9OJ0Y87>`8%!K={VkWva? zM8QWw{zp>sKd18_5zmh^s?crnMwI4Xo-=gQI%;~|APs3_f>i40s2cVhe{gUn*cxlq zm{328-@oGb!?6ql2VqsVr>w*q=ux}i(u}~7GXFipisROjMhwZ~GLL`x+`!z);i~-x zNWY-6>7DYVQR z#$ZNjg|DUT65JXayT$CyH~}8^J)eRIzj>V1@T(%1;lK}@#DEDM=OU(mn~6XIA9aM< zY31y`+Fv)84OU@2ibDHvK-Mp}v`0_ndrY}ErQ+|)^ncFW^TSmg=)Zq?Rfk5v>k0uI z1pzNmhWqOqT?l~eFPj}MyUdt0D~H&P z_($}yaiD)Bi$M^>bNfZg#J@YdV0ffV>BD78A1+fPLi7q-;h;p^`=5Hprw)QPeDzVYd32!yhmz5^B_&PNhX$NWm&i zZ++{=KZ7lC9J}8L96BH4nLE(*-3WE!csu=dh1MB6B*b3UIe`eLYKHl$xu;x6A=Ah0hrmsh`4vX6a6Vla!C-#-!F*ony-lOn*V9|el`qz3$AdL#FxbP6mWZ2@vY?*j!&N_ zxSRuycJuCC;4pJ^P57d5U`tO?$GL?@^$uJP=zqK-FI;s@=Ker z@aW;ZypItY!1I`%tXhOJKmsD?+}wn1GiVL!GK>l-7OMn)g1lUVPwph|ly+vS*gEhd z#JXYmvbhHiJ%Mo&Vwr(#gmj9n9Z}I5++%*fhP}a*c(hTyh1Vgb>DK6cmNF1`h3^l0 zx*-Q5KkPu*9=5`)-qJA%ed`t|!lj6;Y2YsO1Pk@8928H$In&rC!D*~DCYDE@qJ6Nl zS~b6l4+)G1q0dODOk8_ZiFlD!J5?GHFTurJn2ZY8=_K^YBe~4#9XQ~hkG6U<+lh7dh!xp1DiIoOCXL2ZLV07kpb2q+7P5<4vD*xX z9|RGq$SZD1CRaPuD~2u_xG%ICv}iS^Y$acy7VB<&1uEc{m2B*m#+nr!#`7*pHh5wH zZI)JYLqu4Njb-!8@)b9Mui97|*^3g8?d}HaK{aaKDz$z_60ou%(nMox^n8lB((B_C z*t5i@fQ^4OpEE4-mzmG^yshVR?-Bpq`TS=^hjcz~DZ4CbPUi|af(ae}x-h4Eig@VT zioki$J(GW1P1PrW;U)bcm2S`Ey;U;Sq9eR#A!A;NK|HYx_ zTg@r-9E|m!R?X7T^ULbnQ{VcU^zD}m4=FtlslI*OBZ*WUsD`1VZ)d7>o2u#b?a%8R zqRu^zL?uBuNdNY{L)61orx5i#XoqoqJAOYDBQr^qrl|q*!V>CvMNM*hVU=<@IQYq`k*gMu$A$!_#G-_Mk4Q5yo z*`JA2?BvI_i(wHx17XpA^&R50(mSHkMicwU9^B6s zc%6g^zG_bOh9?Zij*KzBb>Kz>N(-C`0{QE?X!0-b){qNAvVIc_rV1-_f*0jwEo#R% zAkp~NSRU3xcr~!uUw1XKWk%?LMS8Ib(7$GYMPbqK*R4Y;Z0eiqw>JiUU{+2~i(dqT zLcfhlUrXgB%IpoKNo+~~a_TG%xro3Qs`O?Ov%n!+=C9jIgEsO#Ccmk44jz-XGSrb7 zZIy3?Gq)vhL97-1C9h*)i@hGIJJ#Igxf#)q@C7E-AwFo4r(!HxKf5}KBft-ccSBXp zj_+y)^CbLDek23D*bym_Rbxcm4rt%&2#>Ol;PVGN)DcufU2aDRZE98&h!)K;2yKze zK4AClW1y{%_eMr}fryOAC?9`}Ol%QN^kCza2BwcXx~~pAZkE;1et^8ngdn_ai1pGC zYbI=<5!+HCtZs;{OAK9z>s4r!jWZ@yVIR|&=#%q=4)@FYUGB?(IR>`4Jb&GtXhrRU zOJGU55fJTc=A?XRsKt}=j23@r30`7>Tel6rW?3E<&jVLex#f_zeu1bfSi@gnmX*MQ zt_Cc24<>nyN!Uu`N*v~4EM@%HJ_=Y!HYwjg!DR)pdEtt^SZ0f3HVZ?^*0EBazy2ZS ze)?PZn<4hW(3(QI$OHSKp#?KC=Uo8|n3HBW-S%H=AgckDw=6GQp4Y)^II-#n`Z6KQ zV3}Ax#n+G*D$CDoRF*XK4^P~CX&-Co#ti6uG@oR*ZBTh^MMRmRVyl>uMK@IJ z_1B$_e8xwx9H_EbU36k+-)cWi;kPkPWF>Igp2`w$@CfOrn~$1h%v&5h+I%7+=^!F# zM5WJv4G9SWB(jL65K19zKWibB4^bXp&IabgXfd>dLk6KZNeBvZuFvF|>oyaR;8*WPjZ%KoAZ}4rFeHB*4J#bo4?fMEe0b9Q;$|*Iu9H!~67CgRyIh z6sT_1V|Ylfe|?hIRsCz#zHVJs!739~uvMSY){?A+ zJyviaE$qi+okT5cytGw(st=}xVPCRqTG%IWyLM)vbo!SCMuPr@9oZ5}nC}v&TK_r~ zaHK*qi2gOl(Z9G;TRGcS!DfpJ7QlCDRj|#Ap@NAq@5%&%;bvD`|A7j&)JGM};)c(f z!D31?_K5zqO=R(wMEwiIGAeck&VW?%*YD?8=%)2ABbSEY@jEleTWDn_RG`SfmncJE z!Rd(#sDRbtgYd}f%<*vz456*xJW6X|w3kBz+l1(O=XXs3t0XZYTVLU@qy7ce%LCM* zzZnKpF%J>!uiMV#FR6dMBj4Kkmy70<7QePGw(8SvK)h1NN)VI->e%G;>ey2GN~vQv0}hvif{r>i z8*({W9cvd1`|H{BRQ*JqPZ53Wq9lE6-bJ=9*0>+KSlv3&#JWQhgNo%bhA0``9(&x_ zBQH*+*7a@5}xQ;K#!5Wi5??0iXJ2HI?-dQh$_)zFh$Yy96-$cISDGvSt7c+ zsKV6YGAhN5u}kQGl3shgzh0OkP9NR?2ekq67-K*r@w(TAvn*jh3wi`{7JU(14!h197A;5@e0shUZEO2?7q z!c&z_CEP)^YGKr3c~cjf*Ao4XTJ=Lwt3(82LZf;GHD_rH)vAS5t4N_JM2hH+{`y}5 z5*6srV%2LBdfJb*f>b=@83d6(A3D-~Q?*HAW4u}PxV^^XTp)Ulwo z_5Ls8s@I)?xYl(8U`JC>fQ)D5A5+futnelK$GrF$bR47{-#0taKSuH|Ny&eQ&i~QT zN%=>zl<6t?f1>lRPMd#lO8(Pz{+p8Xm$3Z(J(Amhn9l#BwE5SkhG!X{KG*) zi6$GjVR$jESQq^U6h+8=?GbRAnSUdkuLkSfR!L%Vcd>68k>KH0B{tcWd>`7O7e8fa z2qu?v#c%A`{{SypQk`-Stf6tfTA zZpgxQ;KtzD941r8B(BFYot5ZxwG7o}N@Hr3vqm#3%ehL0oUfc5eHUXChsUrsnew8Mt88rHF{IGx6>W*^0p{K4;42TS`n<8>BSEmWSgrH<-U zRUlHah0O$q_qLf428@a={)Q7d2aVr?TV?KPGX9O-*6yG9Z)rXb4$QttH=-j_s*HT4 z3MDEEzTUVU>lLiz)NK#s!yi7?+E?vUo>;e{a+t);(sJ|MF-FB&vtkRckXgOz(_@W_ zCUbm~ftzKj*X(`I|M&*~<8NmE-DnOE>xD0Buf%9MEVQTq_ilt)$aoxn*=kN%OLo)y z{o{%X%<%=BiD6n)_YuJ(#yTTLtZzlEcP=`15b^L22Bk*>@jH#tzUVpVUYAfFEkVvk zxqn7uGP)5gXqMUwZ8+}WEC<}OM|lVFBnbMI+1%^3=y=uuQ~G%UcxVqo^>la0Gq0;f zR{$ZnevR6S_8R*_P4>Xa8eKE!I#C?#f&_Py&2y&#cR*b9V@^^w7wmjkaly!ZVRL7e zQ0oVmKCM{7HoAs!W*LiwUXVHc>i0ljSe#=lm~PCSVQl4I2>3>>u4snK@jv0e1wP4$ z?PR5NvXJ{SeyS^OhSsFUmmMFSCS!2np3zV#riCvW&Z)e=)E78gkl{1u;{pj7qyyt^ zhJ>vGo))jwH`NSc_Iq;Ct7dbwjp9OM8@Xkbv32h!nJuwgW6w$;w<9T}y7kacT$p+B zA%&UarGZKKz$_4+JDjpKlr5O>4{b^iwxWM*qnL$F)QD)`H|}aPLtSoY|&#AOc?6FM9t-lK1L%@5Z z8uNP|RDGHNHO?Wm0O+4SZy7EljjSFh4&~?SlZ5ch+?G|f+bpH~Luf6{cOL7yu~<*Z zCE41a7*Y|gp`s+c1TH(+_oLH)FXGAU!uPoe-|O?I`fwt5%!#Kml%uEGF(6gAvR@=e zw)D}*hwcR#s`nY#vI@#gEAs}BZxZr^kcT9D{uu!987JPan4Sxk^Y#3|tIfHFmk< zqc_o3o`*C%BHL3B07U>%%08erwnOnzr}${bsTGZnwOO6v>>P)Ql;i^~Tp zVrqS#D3AV=`Bml7_WRY)IL+^_Pu*b^;O{Nc#wxIEgG(WyN0zMd+N0!kij z_{#EV0WgwQ9^D7|33>DxL{5r4y7WGU^p%Gv3@%0QFD{QB0t?%L;{Pm)|@P#t@7ve-U zbfn}dmRcs%prO~^`SL=b;%ISOM=pU94#^Mt0uDcfq z@OXgEE_=fIP853~LGuvS5|gon2<95?QJ7jg%syeLf$#x^TOjp#7=+}g1Ld)MH6%wJ zEU1jGqYlC6*`yJWi$j~lIv1axr{1f^!()df5Z5&xaJ3W!Aa@m^s$&k;v-MvfiZS|* zj#S91!_3N>w%fU%rm+mtJ3fEoRz1w$+@s~?ICGJhTz+V+@^rPUkO}<<2dd(v167fS zMDVAKXK8fPD%^>a+_lmmZ)CPask8KUNW;Cb3Vnedb9z@AK*13b*HCHKy&E zZu>_{{ug!rQu6;q=U<(iU+fu!Q}Um#^WT&<|Nc*t z`+Jzq|D$+*+B4*W0#2j$HAJvy+&~{Ww>@LzjZo}q&yWKWa}twX_KbNKVRDyvN{&5a zE*jE_Jp(4+PV5;>>4H6@CP6=;twh-~PT6SNGvxHg*JaPZB7AC%*fSPl)5Ci7~&$#$qNY5`~&p6m2Qd#&o>)CwW&DqfP(M{){ z$cI_!>>D+m+cyT%zEQCSeiUEAzVVa2f~5o7H+a zz!AqhQvRON2sjdAa%&$%Ub1{x+T+G|^T;JkP{&ClaKXhty;)%7XN5;2!_CM*huqP- zTPkais196;L7%ye$ywfIbu2`-tfTqbY|IYEWy)z*4jIA=e5#!RmRaVV!H-53Su-*9+g6HwL-4geqy^O#J`*%j-3O#VuPlUGE|_zd zKW{h}jQ!N{uDFIAw{T5yXF!M&Ok4}RxK>?tpM4ZOBBnGM=NB3E9&ShTjTd%a2mji___-NG|CRxsI(6B8*lLfb-)M${@K}^NPM2r}gesB{JBZj3@{tZ5UF+m`Sygu-F6RTG#SSoJyvIp8d zFQyK(7f(xRs2XS(WiWJR8XkLuHQnfpux#wc#Qv`2*TKgB0owD}%c<=-{p7B;XNapk zaF$MKk6E1$`#J4}ix8p&OFax}C79a6^PN`tVXMj&5d|#yNxA~I3R`#(HynXSaRfea zl8mX)OFXqFM;|z*CZJl3sc$=DiUoyH3cfi0J4yYJ{Y-33ewt!G-a8BozZq6BIMI%8 zllCagAI0Htb|cW<%!VC{xhu9U4L_(pDidk=Ui$IiL-f)5*$**39_dLz$G;KS-SJN! zD07LqCy>~JGvkru@DfITZo0r0k=!LL?mAYr6cAhYkCznM*yiBiq|QS`s&+s`5ES<>%d?7!8FE{ujFZLcGgv{{OP{y$y%alj*xg$;>4BzKN?-JEQMS z$rDuP^t}vOJE!lDe`VA6aX3UJ(boiBJx+Tj-2Z+1lHZA{{?w$P1ioY0Uwjv4}~4IsVnXE^#bc$y9BIL z2#8~4`TJ<^U_MahEE^)P(O%|JIomOVB$(2Av+m}Df5hV7uU75sSmLzP<|yGOg->iT zFp3C*5p(9{F&Iy8y@~T6w?X0bq7pb;qb+I=qZyt|MsIWMZg_7R`HX^V-R7#MEBHvI z-*-{p!AX#Sk1Y1Fi*$P(kq=0>56$%WaQhE(f8pw85`tg!M=gSL2ASY#6D3eu%7QZA z3gI^BgR1ZeO%*4eUM_KxDV%V%!wDndztW7RakmAP4H22^3%!zu(Ml~x^q8?qk2#UG zV@#l78D^$gvO9Avalb!#_F!n4ZYdJ?Z^#yV-Dr`%Fw1x^?rC;duNR`lQe{CS{%`>r z1VPk%J;p@~)s^m8xyM>G19gpmUfcOa_-7ibO5o2_dT_9_bz!tl$yJ9ITDY?_GVV7} zt{RkA2MTN=AE-cseycVPzTnkcyoPNE6&@*MarbyW^sgpWzJ`3(+% z)qIvXf9t`<`TJ3Pp?LI2iboaNWe$1XHFzdFm`YfSs&ksy9bF*$JCu-)FLC%@m9wH6XfD^R{yZbs|10V{08)vp-FQ9+2{u&HJ zE4GLcGnuYYj=i{BFoKPh979x&*iKtm;Mc}C*Sk2(dv&udDK0*bl0x%Ps^oqGDaI?d zlvkA(y;+vE=lW$#Rsu!pq8P3QB-p~7gUSz%nQBbm1TsU(&j^5gYb-F#KmfZ1|*Hg zwEUKhT(Pb36$BYAfvLzw8SHj55m5Oiy1kj*PA7jf%Y^59t&@N;h2A4=-qBOOOa72| zx-Nh8ay(a9M=c;U>tw8mv(B(@CUsjD5Xk6=A=VmEg~>X?mPVgWa-@+zWxMufOk;&~9)N7@gYTCO z=7U-&Zl$OT^lW*}^Iy3liq$t3YcJu|J`#aYU@hn**)4guV~{JwadA2H50f z7ZzPuc}>M!C9{!Uef34nae>~o`dT0zTMTCY>`fyI_@ z)nZohvxqhqKT{y9WqZmp*!O+mX9{P<&j%0VYCaBH%8I%QIXdE7=+g;3d+yBzdUwTM#{`widQI#A?4(`|gdQCb+rI zJv6uBD4VTJ|NQ7A;mKJ{4JBKdW~*GYm_3e$jdy0Tvlps~0>l28?P?aYd*9e4`la$) zjN*e|1bhYk8cYvC;)FK ztPjEn6#<=EuM*VqA#s&}5DirQ`*pHP*wPP_>2#Ga?;TM-qiakGG2xiaX;mZbHEthJP4B``Il$R^v;Utdu_5O z=0l3+*_KKyJF(fqNiaWmS)P2!zbV@-^1UVB=QG{ebulLAdY5_dDhwKz>}DW|BMb0I zr7m|phldQW-7-+Tt@vSQxBO92h)TTo8!4k~ClLWFvo1(AY>LHW3wX;0NVU>aY-fo@ z+SM)Aog)e*ivQi&9E$XEvsTs++dE~x-;AMnJM{tjkH8y^%iCm*0vW@!Bci>VPzYk7 zlaCc=BD@eqHV&<-)veNH61Y61Zj?7Cd1zmpRAA)yG$-Y88k*|Gkyc|eoSYeGez(yF z0r71OC|{Prz0p+A9PM-Me`-owQ_-a)GMi@z*+R_1p1bsWK}^CPD9LVbfC@5Y15`6Y z3h8AJloWOmU3NZ&I@tN_M|z~+`5X)h8p$FX$j&EQC!A2GI02f*i;sg7pyrYjJ}=-H zbvDuU@~S|oLK2Ufoa)~BT;DgY2=P3IYqLAP`}vS76EA3Rc0Uy(VfRxplHUDv;Y)Tu zZAO|gnv7(T9fI@7NV5C+9CmJn|G^B)Y>dc2!Wyp3yG-gR@2%^=yR2pI3Ov1IcXO+B zg&N>$eAB;W5~ix58sYs@4?HgC2}kRlS+{A|oM5ML|DOBh(jT~I)eJ|(; zJs*T#>@i!qCC`Vw8bdGD6%NEO~~R>+sLe{Y~cX-WXgDsPOAXNHYd?s6%HOi`H6+|U5=u8=Y^xinJgA$ zI6B~(U(JO5ql4f>91MsLXK%Yw?QNs<`ys%@k%4TgTo(Gs{<+47+Aaq^=m?agP1`@BCVu`W0eGothM-U;uDG- zyd12%Dz@9^*e^t%1M%zJ^@o~Yfx^d1fx@88w=;H&&PPd(&T3ip)%o^pRs#I~@Ry*| z7sAJZ_;nY@Z#tIx()1$c))Sx z&rF&d>AlXQw6F+BR5XO@^{=e>OqGOSFl&&vF?JuW-+IpGPSGDy>Nh>NsgG!%?X>>E z@%s05R=-)X&#C?eRB!A_Q-7$!JG?qt_OeIoGwS)Yq~~A1l9(QHp3#2Z4?>i_axXlM zveq8Sivf9Sm)$=*{jr0jpZq(1@1=T__WPdc(mOf-0&fX@9_$%(#W~xOV6Eyo3#^cM z9_6#k6nC8VbCGHy&*J-oqS$i_1E(^r+J~uc_*8>wgolbGTN~GH1mi+o+VBz`JcEZP z+V`-=Sx{n>%^JypHAiOgN0i8OGGN(V^JOgqpUZSrHX!SSKFN#3$nH$nlUKpS#vM_?(3*9DK6i z2;BV=tPi(-$dQ|n=YHg2JGakv%@(%3*nPf?AyO573m@tPzb9@``2EF;2gC0fw|`mu zo`!Y;RSteHUZe2)Q_wU4g)d%W<9EjEDfoSqi7EK)gr6Gk(*5NCa`V&c>r{WQ!ta6k z>G0dWY=6ss%Kpk}E8(ZCwW_}hfMb2}hvKJga0yiWH0L$dUx`wz692-)uJ(87ot^ae z{k5vUH{kcc{q2ts7+-GuO!`S;e|tw%e@);xp}!woZ1=bJZz=tKnTcKP?-LyClo!eL zJr4tlWB1<3!TNjBtzWjk7ymf1zbDY;0CnC894GYm(;wOWZQPR5-#3`p)&4ed@TKeT zWf()O`|*~8_4kZhzHEQz&Pwd>X<9!10XR~Q-uD@~rFr4wW&wpc& z-#-71Dn#}<=f6=(f_$9!0cuk6(asY>9?Hk(FH4n=>FV43SEs(MK%-M%!u)nJ3r(!& z8@irJs-8~eeHP@M?oW+fJ_mY2`LE1y%D2y!KF?R^$F57`^JlvD_l~!}z%HM-NGSj3 z@$zjvU7*YVwX6J24d_<~00WyOUxU?>SYra%K1mws10p&xbXB9Xg;9rh=l5j|1&O%&5JJ!GaQ{z?t z^1AQ!$Dv0=ty#G-0OS5{|H{?zh#+Pk|H|$Dm8-;uhdecmw=^MZ@#eth2K8y|MQg?h z9UXD}C_Z7krVWMYsa8wKXpeVf-D)!c@}qGPIfhgnJsCOe6!n{!-ZNdg=AXFRlQqOu z-<@x#t&cRTj;=(`^!2Iqd(x%5*JG5ZpCA*}DmmPedM+nrdmuiJMe}?s6aR}>w^t06ZZ$CxV|3_S;&ia$&->3JIZvvHU;$m8e zDi;#GAD0f}bvfb7?gN90RO3^zN;!t`;gV7Z2DXKtI#3o{5yw(&r%NKjr77W`#z(UJ z!zudo@{b;+YQQH7znXNy4>hI1cX!}}8-+r18T;y>oNI6J05rEL4P$KzrB69oVS02m zFs=Tjsoy}ZuU>!i=c)BSev+y`GTmLjn!ocyt(lkt(H{N$s>L_)XPlP4nNj7{JZTdt zo_^r(tMYOEO8W1We1r>IcVw!{3wB%AY*j#EmnsB5;yaD|-^SKJb{=k&<|ABHeQ91W zcU^O6ZD#CW2x!oR@orspT=H;de;_{)gAZl+%xN`7RShgCl&av#4C`)C$=Ir6%dkTB z2N@wxt6%|#$F8qBT7yjP{z9Lr@by5%tO_Jv;7U~Avp9J}zq?cGoqFIZQQ)ZT?g z6%YOY*V`-TapC_3D~^BRhpPP0+u5N{GmqYM+(tJC5MR*O4yGR-EFGxsw0uoJPHWBt zdaQqCv)Kz{O&oD0tRn8T93q7DuWV|zp6;hmi}qo^`0BQPa^zpl!qXNaY=BVRljGMxuvXMruiq9WlH}2yB-X9@Uokk~^ z(q8(MDh2z`c3(lNJQj$tXT5+@nCm|iSsE6h@%Ut`F~#8IAmeOUZz7q~NS|rgWfD){ z^>`EeN4!&@@4@;rFttCgVdqEpXMwc9@si)akNv4h?aw>d)d5yI>CeY(DEf1fl-6l~ zdNLdOQ+b};#W~tQvX1^HafyI-vx@fx`d|x4_-+}@<=}-#$t;Ry%>nD77L!4s z*-k4ke=xIt`{LuxD`yVfZjPNL{tdm%QJ&B$aLb!U`J0|lb7t{dSA9Scj`ISgGi$G` z$uQ>PO6QqoDeeQrDwK{HI5rEDKUbt>bNqGczEBDh<~FAV{B_N207k>K0N(QA&VEIq zCRuJ-+e^?OxX@#Cgj!~EM11mnMbx4AWvhAc1f6{H-+06&&Ok*sq5@?9K0AY^mEuMp zxp5LhgQFqx3Ep%2Se;m3)tW`bjoJ=;V3TapOx}a(4SkRq`m`flQ{=B}gn+=sJlLH0 zB;siywj$-Q>EIvaRq@8)2gZizY|18eXL%tKthu|SbuFc!cWEGc3}gkH#x%BeGLx%u z?YFQ)v<+Vi6Qt08ii`fj3kRitAeH_nNL!NW5A!zqnMD8NnUxbVs16pu6~bSP{&4I$ z2>tf}1>{E3KZuLLTnx~Vh5@#ni54X=z&`jklDtFk>tcXEQZyzqzy#f!WCrN77g!k0{xucw0&_L8M`T%}gc($ovtFX$AoOiF@7$#bCr* zKg6@)JY&6tJ3Ru~>zc8DdiAHglRCHyD%pzraCPU3fx>6rqU-xQA^>UaSN8B)8UI9V zZ?`f*dG#E>H{O+~Gu2V*XK`V~bmQGfg_lKFM`sL>`4EknB%@s)fp;7fdSJ}UL3lV& z^?;)sj=M5)2aa1Ir2ujOJEkZP=MxhA2j}}ZsazCV`csu&5h#3+zElzPedWoAg)aAn zuT+6a9ba{#^uWkgerz7Mv)ZvKo#|r++vxuXFTkUW$sihBt9tQ8Z?lh0S9K^kOSQOD z`jw$`%N{5o{rFcFRQ(?TluE;g`Bu@;wZ^k)_ykhBOvBvSQkL&#`SI5BTTQuBWj^SP za#9N^Cr!2~=W1Dk3+H>sI+Xitg0q(6Kmk6~`W2xeIgdb8(dh~h`=BQK* zoO(vC&oW{AP#t6>|3w^2AHNlLgGVop?2}U~6~k5v!-kZx^K0d&4Id^Cj;{05Q{x4X zXzy2&Cnl&Jk8hC1KW!O#kd^Sjol)vtCX_EfOi><(w#V#VCS)%ani!tL!iWf5>Z2h7 zfrhK2AM|&NL|1$HsV&{0>!<^LO7ubBQL@i9Ud3IcEB;B^{wAEjVjaT6hm&$fq6jEH zQckt-AEORf<`Y2!P3C)OvST3pKsmB#sh-NO*CVrx0@vYST69>iAjW1^@zS;r2YdPm zV$)f6`JqBGIaLN|r5d1>GC(z_ssJUS$)gkl=BE;j;gThT15W3-eSoqELqqYf?uz`dJ@_8CCpzC# zf5|geWx>IBim&@bU&0=g%0CGyrg!fq^ZeamU%_z`eNMqD-K3K`@)(o7-h|IZANv18 z|DE=GQraJCi|-fXFyt8B ztbcxSeiQx#kM%Z$OD1`61+MUCKJ;dIpZmc%U3%i0LrV5?!8cA@o_-N^ZN%Axs| zqtIGyLjNMh^GMcUQn}1qB0ma^T7UWo92h6O(HuX}vbB%2+G?-JmUzUJ7hR;s-6&M# zhW%&Cvn3QQ5F)aufSDt3L}=e(f9w|MF?3yMU!MQwRjdAy<(#VUgx4}Urp?=~&X*|ut2V5emvE*849$;DNT2E$)2rW}0 zRO<+ORNeJf@~E;`hu8F0qw0=Q_W~nLe{K9qB@TY27~=PIl#|rI;k1(2mpRh^6l=|H z40*JX`vlmZ1_vjsajR;=I9^kcZ}x@FD&NZyT3)20D8UdfqDV98%*Y{q|j$U2ij>8*P75%u~Al(3t$pJtcXW!NaWMkr2Hc_6O@3y1zE7c-U4C zyymhhG|FBtgxY3FXSfAVLjsTR=8mVZE$F|ZIP7S#IcM9$;D&PsFni>$; zn9^j7Utv@{&DpBr8N^$dhOH|`mO&r$!&BDa^eE2jAr*Ki5Io>kBiVxBJ*+<1dw>{N z6FArTIyU1@B*DiT6{f!XYJdGt(WA5>4{pNLBzF@LD=+*JSkWDE;iM;CM`6+FupWRG z4~snRe|SRSV{S4^>M6yr>6ao)_T-Z7y@s=Ulo6Hgf8@7p# z--J8L@GK>wvpms)p#11<>^XM8BOS<^vpX~a6s-hBbFC^gHFlR3g#v6utWq|7B<@E! zOB$|OROYiU&YuepF@OH)9h*N#pqawMGCo|ixaM)2KMU9)n?HYw*W}N4&WzLJknrci z|HJ%w-`g(!{3d#thChG)n9ZLay7-bmFTg9}WAhntd>m5#T==Ex!{hv^&zDc}hJ!p4j3dUwiZVCa`x7Hz6s4hXvp6>ry8XKtWR!x) zYEJJ~Be$FREXjR;V+r@Sh& zF!(a0vvUeONw~_L+SZs>ROrgYEx?YdqS({2dDc-iz*r60D-l5d^BpzGd}pK{gj%0e zlLO`eY{D$sFI&GoVT8k8htZDUk=QP--xNF&UWV8_I*jA}=C?C5%$~S&Zq(3CS--?7 zwtVB?I0NLt1qA&KWTB<9MznZRkFNMrL;F zHaD0(qhl!rdp^fqBDqw~MpJB+hKgbl>z)7Lf@6(Iv!}6IrY60906#O*W4K~39ETtj zMcM)@+@!L84@-XSqP-bGtij>?2NfE9Z^kMK+%P$-20LOSN_}%n{mZ?fQZ)FJ4tS?w zf#FTeop4K74h63D!#6=YcaiE|_W?K#_$*0s^&i0EI_R@YZoT+>jL~1B%MxGIx&Z$0 zMvL_{-jZ1dpYE2%di{dcgsnn=KdPg-3D30OC-qGT;)QLJxtDj2#6 zGzy*q)Vl9M%ak5o^#%x2y$@Rw_~i{>#>VPq^#m+}pLpq4Gy8fAnlG2LgwDZqQ-E;$FmLhDczTZ9Gs2A^ zrNntUCKhs&y37Nrk!DK&gv^hZ8>%1n3QERTqP$ZJI%i=M-cJh=>?>C{lEClhy^&z-0BtV+?ddZ=>e&h{^7nL2M^XmRz9 z03@|}UcP_j+7ag!%ssi`F#qyZq0#w5r={?63D|g%$q-*sdw{%)^72OH#r7#>=144x zhCyKD0L4o2x=zDIMsv)J!%?V>a?R*V_k(&`TZC2_Uk@LFgy|4dPkl%5l+gGcZcZ4J-+lZJa|&Y(2`Ak3w**dTG=n>Ex9uy5tdPP7&kF7HSPQ(5 z;B!-vw|EZq!G-?i+l#ju)vb{VX?3tytQwRJ%q*^M6?qt#&~OwBk7Npmgi1V_nhD0I zWsOR(2C6Z(=>nblu6mf7I=DdUiDrU)fJKMoMZiK?@Pi`yqj5sV{^-D~$uGk--8V1m zgzkNj5WNpYDt+w_D@lCIgSRC6!XZL-TRhR;d?5NGPM1UfWRrZRiw)f69b7+(z-n19zEd3tEv6iX4kAD{lPNgb!VOdK9c3*adEv^6!gHon8Jo^ z(n7Go8@oZvcdEVHD0r`5nnwKjtj+g&GKOx7Rs-GEWq$$kTC66#qUalbN*srrZ&xBu z$3d0CO&>X(9-=yZf^-_&H$4zbNqJpbD>kVf^D=Sz=b*nkV*g6h%ONrgeBPmY`Is%` z+kqU>7;WXgMfLIlv^_=0_p<(ZkDkcT)yvq`g)5}1f5Urrb^OWkt_qFyd?xqpa{WR@ zULrbh%#&=;6Rr2*IwAURDRjf5sTD(e!}6s(htAyA!$vn;Q3|y%UF72DUj@s#CUcbV zy8D?0ypGnn^g$0SfF9V33h_GDY(4ut+TLs(!7hN~uRqD%1!?~*d;R;J!%3 zMmAqxKo+T3tDZP}z*A2z&lakAx`*`b!wi32Em9HW?6XLP1t%EPv6k8v0%U|S@rhxuFbq$j@*?Wxx*wgFRuY$jq;PX+t=&Ryy@YjgHfg1#WNBm3S?+3t48vNaV zr^4TeX9a&ZD;G;_Z>kG`J!QaJqDExi)XTjfO zJQ9EF2OeVlor8_pug-t45q)j=`yFHk#_vsV@g)AX-JTYIC*U>5@AJzAfBASM{+0|l z#Q5uhE$6R}zrwE(e?LZ~8_K^AwEVj*E&ks5jh26Z68vq!qsYG#4>A5;`ruIDuiw{* zzk#^43-~)Cs^uRrlV<$hf2)>%PYeET#-qr;ZyjR%nbAXmzk;t3f6=u{{=Ki|A25>! ze;41Pbtl33x5?4;zWVUp^j1 z{^90!XK@1HtLuO8K>6zOFaK-A-;WW=hVt)SE&qO<7Ju*Dq~#yCS(~jbcog|}{2|8Q zOW4U*_w?@kl<$>okH->%&*V9X4AWN0DA9h|D5{;^6SVJ;nzcZ z6u(|bO7-Q}`(lb;$Kw_Z z-@nq|dHKNh6Q2yixPYc=9_KB2@Z)Mj(5EeHid_@>@Q%zWQH-V-jbn@n9)) zFGE7J7cNt&q#a{Le$Sva1q>fH1dhT)g^Sft26?lJq3D91enu`_MKwdUj}zRCW)=57 zMe-*PEs{F@UpXq>-H8S#yn*A{oCW=mvbDIKR)=7rO-Hy`2;)!aQN+jE>7@o|E*f5S zpryqgT3XC4fIj?im!-vpe*))du(X&=_^`IPewTqK%s#TLEslo78PW+m3m92g7h27K zK%;4$9~%Viv2VIUzeHn;!7#RHGy;Pi(*BI3E#!G{cyKMka?lyL0h)CE<=_d4b1Z_M z=ce+hR^*vUD&zG&c;-$aG>^MU==D598Ek0gc?h`jhjAzq(Y2@JOrO7voaEG7Yj+(AAXZ5?_ZFhCQ5bs9 z6Y14!wrIEV8}!w}8$s`<@B#IuI(Snj42kpAc`iD(rb{mbvE={*hgw=_C33sF;tcPLt~j72?eTy0(YQ;tP+9ou$pY=(S~y} z)jl~}j9d&~7yOche-FYMHQgXeWF>-T8~!BGXUW96r_fN0wBNbVGXBVHzq7!y-*GI zG2m$ix_xx~L*ql(G#=!Ny}(^Ozio@}x=A%{&X=90x%K-4*yr5v2?qs?TO9kG1^a8C za~sTS_Rl`&pFl7D(0wA}005KmbMe37a1*UK(Q3s5huN(_|Lptx=?TmDA#j>)pF_tl zg_eCzS*&s|>~oCDy*y;^$2@1_ldhAg%;q^|(uTBi^}+EQB3LMFwBG}W4^u6fjaaf; zcY)m!wQEgn4;BsmSVC;GEFl8lHil6SpP$2CITGAfUg^Dt)>Aa#;b65)a)!ZvD%~XK zy|u1JsrhmI+`YjKp(}IYeIeJt=?cZ-OyJsw&j!cu4WnVr@l$!g+!p^izSy8)sSQeP zPd|+rVK-(nsu8m{3>(_xwCu?aR4S94GcA*x6O&o?ZIW|s7VUBVYz-XC9;cDr{BlNH zidUWhw>`x+&qi}HwE2+`+I)9>3Qt#+JoCPp9 zIuLuD5@7C}ld=K#0j~@V*uEGISQ3xm5EKo7JCQ;UW z4-fu)x78t;?Qv{5?sMdLOMeDAzg&|lw(Xf6g&!5F>hWT4 zLsOs;2A4NInDBuRmSo=ebF0ZxqxC&%b>r(niRWDrgE>@Pi^4Ui2`d&X8?GPk&$o~X zRwgd7*CL^*Vq{~b%$w_8k_@$0hd&euR+hk+a({l{7~aXKEEr!|IB&FGD6?n*uB&1Y zKVTW!^q}S}v5-MQF|jBD(O8UY7bOPALJT;|io}xSk^&Y2*1D1Q=;j2JWS9J)7F}YI z#--s|c_X@{32P49-f}#SSGoT~JQp5$S|9!!z}{lvBdAR7KRU)XqX64BwsEarM`Qge z#x~N{{*A3R!_-2Surrmh&7k(dB#yF#K?lO#23_u~hz#1>xI4Z3aS$%KiK)ZWzbMs@ zy^XUUSj{R@&24^6bDN)zuP(3|VT7zt6SK%5KPdMXsQD%^9kt0yRUZ**_qyzD&dS6d zmDqJCYa5|M(%R;?mbHyUn_1{Yn-%B;%+>Qr^O{x|yxTp$!Th$>?^i8EzdQ7C_59;C z==lc$EIy5Ay|#VTU5IAKf9YU*JPy#`w?1t5ce-&+L29?F#enMneXnm`gpGn@Uvrih zwsQU0*L=FlHA@U=Ut`O=uR8MXBg^#UZnuGro}0MUwJa3_n~|VMpP45we$4_gu;G3; zK8UBiZShMGMOpSD-ZZyb%=xnkS@vyT^ZE{_(F54mf3|0Q;I@kAdY}^J^yin(KgE2K$=k8?aVrjeidZsE}}v0Gaqg#m%>qnXUx^KU|lQemRIH{qEwyXls1ewTgao27S)-yC}g0$7dbC zu&?>T==afYg?=Y>DEi$1%48V7twBYwfB6sgHD4J0 zo?M~iAC!0^|3H}x^a}+n`NxB0$Ul6F{LA^A>9=)2`-c6cAM9(sF#28pppt)ovgF^q zjP#p?Xpw(B2!;H^r^vtT&zXLA4QSu6zx0EB%@;<$@&}as+i1zZYckR=2hk$`aQ;d8 z_Y+0GIiAm%eiyySguYFBJvm)x7KgvMVq45(LE%r4V4I%4Mh#*<( z|7DT&Gw(+XxH0(b>lxVBWT5B2$KHSBy~2-|J*7zZ(A618H#oj?j^an-79rd=e3Edp z_I}R%c$Q=Dk;%Ts=Ewc8ugO5a{pQF0u&>#9Pai(qPy3pW)(fF6`ef9I;SJ>BNp!dIrU$4Jg`1SF1ieEpzG6Mkz$0s3L z);~Ox#rg-IB;U%9QpiX9nt|;j#1nf-5{G{cR zY^1`f1Nv;w#om^lY!A*i<#kzdRIAu=p3dRUJJxM z=2Rr&s#c=5w&vBWz+faXH*JS@2Y!IuS_FGb+%EQ>#`wS>oK@&}yc@M!H7*l`yFwMY zUbBIk-zi>C`A4D#zxG<@8;sk2yto=T*eNyd45Lu*vh5>2$iY+KmIDcOul=ic!LVWm z>qU9{5N)@4S89y8IQ1K69$n^jDRHHVn;ezyZu8TBK~AE3&-)>UX}V8_q48yxLo5C= z^4RAgqj(P78V@6$<8|*pGmqENS|i+r;n@LYbQ1M#B}-MN9$VoQid9E(_AX}{-^Gn{ zJTNc_tO5HcYU$6UvKzO_Q|=I_+v&{wWy+ZbZWqH?(tVp8xNHS9Smdai#0zkvd}{9a zsd?92XpRKGCOPew=s5Bi9pz^CSV zF7yJb7TN+ZxVV~ZKVCeD&G3|0=P$e-1|nKGh79FZJ%PD=(&D<@ksFMud3ay|M#v*~ z2>WZXA1WqPL*O=kp;BR?C;^k_;7n&KyvE?vZED`UYpnarh=I-9Fx9|$SK$7#{*pMW zp=C&YvyVw~Z%Abg88P0ckgWx`;wlq=C&%wi&> zKIu=$aFSY4U5+c!{2B_9Snto~l-b8tXJ~vJ5=`Dc=6UrqJpXKsmmrS(wQOfU`~@5> z-s=Cb>>gZq7sk3!9hMCN1>4a|#alPfx z9M!wK^?u zq|JTbN20}j=zsHEd|Lbmn99K51WHSD98(yOO#h3GtbLZn9wR`d;AuNQt(Tm|n09-N_j zI^^iR+km@49KBD+)OA6<#Z|UtA+KELv0p?$;X?$ax;2~||L*P_IpHNVV}WOZ8kt&X z8|pV`XqdFMBaDQw38q_2oe$v~80O+hA55Uuygzu5u*zUcG|Zf25YDTd=)ovw9E8W+ zybXlpnJQAv-z8FQvR&Pxnl2Dcv$|5d`9)+6)$~BFd6}iEr@Sg(3v;AFw*3LUph3Ki zs_q)OL(T5haZK=9>;j9s1k>C2#dk_Qp?Dq`uGKSsruRD7Amn1LaVX_2Px?XRGqcf~ z*I`)Yg?wF}@z=o!AO^?lGj{1!TTrz=bC;e6H;-G|C;v()Dk8JJGMDYqLiZA>L7}N? z_8n@;xP?Zc)uyED2<;|1T4b{VWf+riF=4ar*Dcnyj34vKcD`=dK}LokXda6{7DMa1 z6EC9*K@=i-D(g*jIHJqC-avw{W_$REI+@b zA0~hKVleqR$R+Es_*FcUZH;e4EZKa*yP4%H9H{x=&NI!XHtKuPy)v}s?~dPDY=2VP zM4j&J!~lCe`0wdC*L&jNot64hn5@$p6A?+XD<@i*MgkiWMfmi#>{(GP#i`hn{-$IjZe z`%TL8_a3zv8}{N`;3`(U8aX(KRPQpX#H$gddgraAkHg;RLr1D-V(pg8(6O>Y7KTv{ zcn#_Q92%$7vV0q77q(;D->{p;?*dL#%CdaoHf;GUY%nWWOIi(LpB)d-SO#n194zZ( z-<2ic+ z83Y(_+O50@kvb5zKCcGCJkDeA`Gqkt4o$EN8_wQN3$3tNs=CW+v#hIoz((cOomz}K z$~s}L|B~9fWoS6e6P&xdY`LdA@{~DU$PyeC_XefHZfH2S~TzD}$Wz z&?)2L?*NZcjIE`@*J^^cgx*TYC*jAC!-~cHU~QNwMV!kS{!Ul}4Kb>=U=uwkacrn+ zi`z7i+vqJ6Y?eLf@LN*ES8|k;kO`};y(gDUAz!C(_kU;PZn(7Ll@kvX4-*wDbRk2Hxw%F~-pJ?lr}ZjZO|T$9791Dq#ZsU9I?-D! zz*^o~>DP2w3!V0nA41-EKK1<6JN_qm`cHDT5LfN0F0FCUg4>n8+t!SIg|!#4_7TdU z8k(^;Jg}d=7@Y;yNZe9e^9J^}(2j{4acai`SBh>7H??}<5bHGdy3&oojV3NuS-P=% zSuLkGyn~o)KE}*d-A^HNZwQ_Zf!w?x@CmhffFv>n4neXpkyM&DO=}#d?@=N!)nBYq zk)3@rB)p#xQJtkCGapI&{$=Vn?ilVd^^~~M#Lc#Xtj;_HNnC%Nd_MTh@@VE-eJnT+ zT=uq0KQ@d9(QWbBh$nw_zut$x&_~HPa3C*JzbT_El{Xi>9yqBt=HvAe+*jKXo7!8B zt9{$Gn{hEXD^|VTIO&hz8(7c}{dy!h@o8t8aOl$NcHvi(CJjHlz?3@G|CyMfm~c=lpyi@QeD-k0qg>4}ui) zk)|Ymv1*)><{IjKIw)MuDKUI|^qe#}1LQC5C%@EJ0TpJ&@K)BB{65tEZB&d#KOkM+ z%jQ#c$VeT4aW1Z*UNc5(ys%7bJQH5C&pkOh(-WPR9i5S*e-O<#kdvx{?R=23-|}No z{+lR&3d({$;gQ z=Hvfx{693Sy|RE&?UjYuPOtDiCIXDol(Dko*R9;K?zQ2+cdvVM-w zY2x2d{0rc*68Pq?D#C&YuZ!g4(2PppS$Kp5s$J~Vro%=WN-(}rGTOlQ5q<}!V>vLn z1f#nY`5;E~@KtR1{GiD(xdf5EFRqz}~0+t{00Q%y0hr9o?4T^hv`HdUj!ibC7Z2MMaTfi@*j zkr$E-hOY>*aMlf8(NHw%gxvXfv7g z??CwUKOX;g@WD{(M?U=T;Dh&_{-f|I{13wiocSL`AB>y-D10!l{zu_c`X7eR=>IT$ z#(cr}WSEaSjvC>-Vr#7rrtyS_ix^qH1Pib}z~Z0?hr2Jw{5UP8o(Kz-0<2K@pexpQ z`i>|x`It4ZUZ8><6NiDd3eF)A%}!2e1f2)hC&fWeylP`4_-Y%3^xi@yMiM4>MZ(@NlCiS=QbOgf%lQY1~a=!q4xO;c#S zsKPj~C7{gDc+YOY`a#TpTmAc3f1-c??khHC7X2M=%gI1Clc{c&&XsQ7&vmD@9vNUg zjv8e>ULx!9Qe!S8dNIka*5j(-yp+3M}EWll-k-Ev_I56aCD+i zOg{Py`nb~bnO@L;dLh$`($ZbKQ_$)LAp^nR$StY(oTQrJovDAMtGMVJH^~1k5kh%~SCfsQ#u+xh z<2<1R6FBrk)YlKi@(O)e$W<8apJenE;0GCaJdxXb4j+2Vbauc|r{t-e2jpAh$yK{b z?^{1CsX4Cf`lTwT{sC)rwMW&b#(Vw+kUr;?zc6ov{@1ciBX?>|1!XuMYUwz<{H2&T zJNO>ooU!ng^H2z!JR?|0-B(Qkj-aBxooFx4e?b17y-bS*qH{QSZx_{NitIFGE~ zF!IHm>im&iBp9UEPMj^|%lZ|jU3@hfx~zBuj{UGKGlBpzJT}cU@&%lcZpRseA5dU2 zvRiBPo&+roagMGk#3 z<)_q^{LA|0C%({B*5H-#T}E{=X95WS#;O85YI+&*{B-0CC@u)d!E>J0)Z(nmNVOmG zOtNLAH2Pl;rKLIszRhvC>N6edp4442>k*&mSHG(C2XU?FM8Z7cq zPb`(Ol5A8mvMbS~=xqN5z0qE%J#*W0rWKFiW+dJP8%7<+h`AuV5`gt7YF?sW zx_|svSoL)vmqU-AX0DHU{pQ`7;7NTdePO0_hyI<%sP@ft*EcM4ea!1OPs{{Q>Qm{5 zWJ-7H>#*7vhc%GXzPqMmq$l(G%_ijRLocaMrQe+?-Kj74Xw|;??)pY$u8(>B=65o| zlloNpiJ8)!`sP~o<+|(pqc3ConAdN<0^@?r?NjMZnbMv5qE>x(B9}wYvohDmyngdX znczwLRQh)^rK7%rc8IQDACVjX*Tbj^R}6B?wi;REu|=8Yoj0UxYiPplI9-D^nG$TY z7R{x!I~pgv{Jr+;HWAc#CA@8f_sD_4d$vWvJ3a%v9>Vh<7`%lRycb6I#jl(24n8n= zCtC1s$^fr}@Ydrs=L4YMD}1RD^gAU3yv>An^?|{=)q?ldsJ`uOCcL8#4BqJ$ygM?$ z+d_DM{_O#^_uZ#dd#7Z8*F|_W2L|tM3*PP%`nI=|@VbvEqH@6z$@Mcc&8m0yo)V(jU!$3gT03>4&H^oJ7H%>7-)v~ zc321HZnFK~C2CVdoUFH4UWWgp`!4@*Kjme(|7hRkuj;3~4DS>7U4B$Q)Og?-&Q(VHUiv_kqX#VraXCA+2!GY8v89@v4T$k6_U$Ni16xhOHzm zTDtW~Tl5HuYW<-04;|Ke|4!UbPOg6@q~}-bMOp9v2s!PRs$al!rGF<=I`&)KRR#B8 z*Y(svKJ*nH3B?Wv<=AUC%tvqea-XDOGB(9~+p~z*&If|nZo9Tk_WKFJyr6KJPm%p zfq$h1f3XeU>7PB;c#1-?;q=cZ#s-3~S^6eF z{)>BTTH*a~)i>v(TH^xTWeecKH;k!zXLw^%-$>Y`nG+mzxsYvD5SLN8BORSLceO&bE?#1QvvZ+a@c)735k{PC&qN2kI+Di!`AZumdR1V5TiudQQK_+_(vI{2@bPY3@;@kx4p8K9kZ zY(iwe^y*XJRRYnW*DU#T>Z_7Zr@qti>C|)YvJCX<1Aka5{Jkfq_P=~O@L$2F1An3g zpYsW3a;nesS*(=rb&c7u8 zK>um!Z}xnYldRPM3QlfY-w*w#=x?4yPFEs=fQQN5PJgs-`atWiTuid8n!EbMb3?&I+qN)BfRV&mT`pDPZHvyngd&vORs;HRkQU?V9x~)vmx}3Qc$a#I5#3-*mMnQI@ei%!~ODIWxCMrJvq6 zeW3PC+edpqo%DWMg6m1_r~4mu+GF{@sxAco;>{7gGt_ftXir7piqM{1Ega&qwT25& zyd}l!8(e=5MI?U3Ha@~68%9p6-Z`X8e_Ps+n?pT2 zV&~`Dr8J!5WTT|F>|Fq0nrQGVzz3 zEeyba`$~`QgMasx&NW(J{{7i=)@l#9Z@^p%pmRVc_4*$ zoS6n}FE)Xx?fvw&OzmA;>a_RZWP8uW8CBo*=6$yI9yKImdrv{MtGzG2tJ?e6Z~C@3 z!~A;%&z1*}e=AbbpO+Q3x<9x9ZtdE$c-C#X&S|fZHBx&;KD(p6LR}rKP*(>l)YZX? zVwF~+eoEEPX!WD64pzw3!S;%ADsqDQsZc+Y)z38bGhO}6P(SCZpPA}smioC={mfQB zSE!#k>SwO{38>Suxc)YkF`|JrNADu7Vb2`zC#bxvrh6S~t0EptN4ozVSGXoVA6 z>4a7}q18@ktrJ@3gqp2TTkS@MqzkLd{;z*XjP~09)cz6uzp_}uE!+aAJ+VjL#Up^J zP@(6y52hBdeQK_>VrriH$>$HH=*qf5S+ThuD*WF68n+l!#nUmNr5#`JOUlzc{gQm! zkKqxrymM*ZA@?WXJ?JHBJ~8eE6ac>OP)L@kj=pi*vEHosZQo!G@%V5^muJ*E-kNXz z$M|o*&B4Dc6aMS&OvnF1S>5%)|M!N%|C!|q|2Kf73;z=0|CbX5|I)trJLfOy>w6*o z6rnmN&4X{@q?zwtZ9cuL?<(YY3z^e_OosiAYrV6N`WoOlBdxv*Gt}2ev@oGPd(Xb= zOIZ)7#wpHHZH$lZTcNqLZv%Z+h5qyk2V8$-sBc}gPXn*dP~YE(78>}@-DwTXw0;-c zCwu-9ePjt%2r=I$Sh`>*n+M}>qoTwfA#dkri^XBdMfA5o){nX>QTmPy<%{g{5A`WO zZGXxa*yXS6Q$BZp$~z72miMZ>C^uGDevI_P@nSC_NTmq|CN2p=cbky{pGCtj>r{%AaEL^zZt;# z%|f;(zY+W~`xtU4`xB|O&Dp0Yy;|2LB zR3E>Sk7D(4t9+F5BQUS_v@sGmr}ngQ5;&#yvG&T2{IU%2{TBRSfACZ0Qy1|wHK6nPz7*QKST&%)svsTz^!|0JMAG`V@-Ka%-)%u} z{KfwEZ~yRLf3`3D(=GTH^b0?o{)FF8|1QceH#3M z*0aTZ*5e$h$3~om>=i{^W~ME~cgB#;g}CTJd_2;E^JljOj9P~$YE_GK6h;<595_Bd z4?FmD!2+cZK0ipoXU(4e@VUssXI{PZKstY>@INUNzmf&8RDOLSfBWc{hFS2Ab-@$; zf4<{?s4hjnV4n}{e|qUY%BQ4T&fsR@t~c%suREtb^4!vGpp*@O4npnj7XlolQgsI?#a^cS=+T_o3je75bnGW-<2 zanb`2aEv|;l79=TD%P4d@EQy~5jFY#f_o{2JZ-XWS>XmSl~Quc%D%yar80f)_CCjl zL#n&w1(CR3y&d;b&`O=xl6XO>K#SyK-UOeE$DRVqV^8eAcJZhPYiZ?wIv)<{^fu$1 zg7N1RD*xcoY`Dk`O)T(06R1U+F(tP73Owc$oEug10}lxm;lQyIgdFD(GB94>p*8K$ zgIml3Ry_b!weX98XOUIC`Y}N6z=K9rXEa>Sni6*z!xz1USv!j-dGMy&d+u1};M<%YW_@tEsA9wAoD-qF|XfzcR>btvi?))&t^(@ z>LX69Zv}EW^<9*?KIZkC3;V)T>x*$b(Z^w44dl0IeW{O__WXu>+GKm)dRgIqF{17E9BQ>^sMDUF z8&rGt;OuyR+avS0(?5Os>8SR;?TX{L>VMi#fBrz)<#eB=b!u!=?OTZC#Dl7LF8h;; zJAo(R;u<;2YG2{!*S^zGaeBXfmiE2#e#Z8_V729LB-`yu@H?W=%gFLTXA%;(g;M>6o| zXKCNp(EyCQbpD)zYKZwO?xmWAVo?&CJOeIodoa~&kNJAW z`%1OjxaZIDb(Q;eXtym!824rmhgWKGn|(QIA#zjkCd4-4fP#Pc;pLZK9`bd2jNrBp z-$q2s$Js4=jv0xk8M-3CtdM0sMSvcZ_24#m_mk}~jaNyMcyC}1{1I#6;ef~H@)!OU z%YEI@XZ(ZMR*ek}fulq$X}0O7!#3V|Qw_@uBe(_aFuu%AK7loGc{P^Hs|hDy@dX$C z02d9POmdhXVo`wl;W|WKQ{%x4ynthgqY%*|^N?AvYxiwOVwQOsk+E7ImF#2Kn8?Y9 zFN4Qzp@Lb8FIn$M&Urr(pxdNpBA27iGFOi&C;6+7CwP#f2}OI&e-JSroF?|@`R1XB zv0ZYTIRC{R?(J|@jHL*k4f4g!vyd>O~HD`BdKEPaghn69}vpYYj)tMcNngvx1$r^Mhts1 zLOoLWdG#Br>Gq0$eWjl={BdF6CNeO5s9P+&8qgFAzYoXoVBs&jSa=U>`fF?wSopf{ z!}$`bEHdECV@mG$DRA(ejr#>;Oy~|Q#>A5$O|8O~H9NqWu>#%Ub+#(P7dagAkHH7~ zj|h{xKsX!z1hw#=*c@Q5g?adf%iWO>HjkVZN%a zX!sCe^yl!2{R-N!=<1$1gE`_$>A7?o?xM$=hz08gim1{l`wGQMVE*JxCOA@jw5!xo4y zy}DTscIuy41MI839#W0%)2f)a{%ABE7JMT&#-^8y2)(vLuUd%>2wu?$A8`;;@S;=L zi^a9>-w0W53(GvpsAeQRfms6^rC!r~}Ri}P~SMSt^CU;@T({1=V zo6rt+`5~d2jlEi9aHAI93ou4aDa899^&MqfwWdAR$WMI5s9J?>8(beymXgch?z*|W zJut%BpB)bm2zW8a`zv(RxQ<{arW2!jrCzicy{58YP6@t@*PT4tO(&p9Mpd&B+}Q4u z6KuVz1G&YuIvp%z8{S3SZuiWC?+4%ZM1Bkd2zyxUy?gLIZ!|J9pZ8Z*#wU<*yl*?k z*)?0>pWl`Dnk|Mr%5a2H-D$HVY)VBkROSi|LcY}=!?zmcwa{P)4bV*3D6%OqOtDsY zB%{;l-T}J5ijj)Sv`9MwoT&I~_*b_3G|)`F5a0VaTJo)Svl+y;h~ zP^3y~=KB*zpaHfBGOVUfR82*N`iCGMe0zZF;piSjELy}ZV(^+lS(sjR-#RU%mIEC* z{?)o~v+nQGcUr6%qDcJ&?S7Dmy_!yu%}DtdQiLLWDlOcs?+*R|vP%ENYUEr{S~c<` z@^sU4s)YW&YUDRns=KqUw#0a){n`>%6~1mhx>OEaZ{}nSOnlw^ig-D~L1|2z!!c`q zf)$mk-~SGjC`a%rYE!4)rbU*6dV*NHuamOJq_UpfSdL%)LvxLk-(vI4dR1Qcb#UCF z|07w*XRQsk2!QuQ#5kOGx0Jw}s=fLvTI3H%Y+`5I)%{j-JMiDN;Wu03I#LP*Qs9qB zZP8yG_*&#OJeJk&+vtY>9HLz9pW@1WIxv6Ao%b7vaOLHUE@FG5BOauN6#P2@pX53M zWlgO8Wq!dEC)QeuvzEZv%=UmgBCn&tDeZYqDB#emPL;5Fs8x%+f%KFfdL9w_t1>># z1qe$>ui0q6`7yh`(-z(4m)RcYyVM*`%HhE}XVKJQo|2LUkNcSCrNoq)I?;20lyE&@ zUIMDQ;!p}M4^lyIHixBU@@+I%GZ%!AJsVw>j!?6CkW)QYt612znqQSdZb@0)VGcvd zwE1Sy_R-Q|^jZWZa?wbCA=XXB(Rx`$&d6Kz`YMn%^l`Bk{y8#+KF$xEQPx|wTWg9i z$H-5xZxPW>r;IQdf$;r(kcJj;gp{8HALw|l(6d8w6u`gBb%U}NZDfsN10LQK_`Nl! zeFw^=Sp6=5)a$wa&?DQ~f*AjnHP>EWl%++kz=)29OPL9i!)aLn6MWC8XO>viE@Qov z$W~B7?MI30BDz$E-JSv@%ibbGKtIXid)q3CJjNPUh#R6q6|A<(qHLAl@L-MP{W=IB zB?~!`;RVp9;pLFAZ=ifEMr5GqiV=h=B)Eplkg7FJoL1i17TJk3sCOS9pg`byWUeCE zJCUn)y;TGjlwgnozO9ti#6KXa*64|5!|#V)oo`-?bW#|3==;*@falo($f&vq9pri0 z?jpV#`K3TgCw##a1tm$;F=$Nh40x#`IYC#G2dDo#sEkTSWg4i8?l{xb5K!IcF)I<4 zC$29+K=riZH1xi3Ec-oBqcz=L&wc{`LnL~6X9+P6LP*usqh8UkNr3wSlNT4(>&;l??fUo zGX;q!L2yVptfwu1WBy|0Y>XGa3uP2xZJk500JS_l!C@m)gdd1N$}-5Y0?M(im>Bev zlUB`+=KCNkTJ=5_JF%)AhLoni zN>C@amXzcXPK1?O?#kjVFF>I+p(L(fF$l7WO~EN(@ED_>&69bFLWsG0XN-}3YI$oA z*LtMYJknXujL~zvc!;4qxu1x7$2f9)rx9b*V%T6eFo4HpTPeYF^;fO46I7AcI|I_2 z$w}!Aee+q0EJikY;W)N{x0nm9))>k%e?skZ^xz8JzuM~Q_z9N* zepNBLd7{(JyPa;XzF!N+@kK&c9E=^F?|uw--5(Wac}w7>RWF&`c16=4&EA9|<8FyM2me32S4-uSHd(JQ_ZqiVBJ-C>N;pTQxX{vx)Uk=mW+r51h$ zqSM+aXyNamcF`ZPEkH21!pH@7-;da9;6N@`-GOUm!)zh{JXBK6qF20`{fcdG;5ZOrO73jG*(^MyWaoFZ2v6Lo~LOjnx3>ecpoxlA%zdJb83NBqcd*5Hqk%yzcQru$ZK zS&goU1W~26td{Mx2`SWTLxnY?gb;UuKc3B9AbMfB7CI8; za{SBCDMl68e=Vr2SFh8nR#Ag{GCQ9;!Mn+KuK+v6AI~fPScMeoc7;FgQT*{k!@oA7 z7V}!;kx_rEzSF(e<%Su@(XH6(egg`K5w?a^V@;x87F0vNb=dzc=Vn-Td_P7*P``g& zxqrD9<|d4ah@D1oRcNT!e3$EYOI5pv6^a75TuY*pEl+ZVnpTBh1;9)qy{ilVoOMhmZYyN;C(t49t4-gdbm zY(v#5J-AY@T45f{FYEnL^KEn+bqLm;M)=KKpUiZ3b)jRv5s_zYg>$%D@2|{LlG0S8i;zbS$CP^zyqs#mzA`VvZY6S*nXi$NNZ>hEJr>HZ}~)zTEH9>u7=pTmtB zt#SmYztJKOu?ZsE*HN~gb*aeq$O4iO1g3bO4ZRWljDv)gb9B(JaxSzaMr8mBB%^Y^ z2>%5X{>sldX%=P^NHfm0oCTDhang))u~AR>YC2e#G94`8bg)DVceA5KZ?r)RpABp+ z{X6G_B}OosoDY@*5>(jMe6ZZ8wwj$jA1wbY^Fh8nAB;LVbv|g}z>u+S%?CFEaa$1C z^MRmf%?C?u$vsgrI`hGDh#x;%V$BCmwo6nt-4~^hLMKMB0J4RYENILVh=Zk-As5)y ze~5w3`CthtWNycol26pra6Y)?V$289)4H!#3zed1PJ?v3bPtv>y8lkDO)zs;)k*s} zJA9K&`=$qp$DzBMfb<7U>~!Cv@fw9{A1J7nIGy%dbBITV?S$tP^2G$ z2)mL#^TS9a+H(FVgzfp^X@_UD(9?n$hRBjojm7(-=d{c1!H$vZ!r&6UDr)|VU-taK z-6aNj>iqBsTWv{l&JTgK?~ho>H9!27=}919GjnNrR9%MoLGg(%Y5*}mmK3OoknNg% zOU%(|vD)ADcs^)Gu&MuWqOQ8A;^SH|JPK^0Hs-2VNH8xt&Pe#?s=P zC}P2Bu;JLdhJoO%f`x4gyl*@3zG=a$>H|;NQaQmwPnJl4=y96YJY^SP|ahMMe{Lu%zLl3qTD_oxi-wHhB0k zW02!!#8?Dz>`(i#2NV3SL%Gk4|G6~J?Ti0ah)%_Sq=kP=wZi|UD{cHAV+`biNas=7gp@p$aE7*$ItvLKB?O7$-E=36(mb(N3t? z36(gZLMK$@gz}wGffLGgLU~Ro%L#d%5QT+m?_MXg!wKzjLUAXw-3j$Lp>0m6%L#Qm zp-v~X#R+vdq0LUH*$HiQLTjDSIw!Qs39WWQE1b|uC$!uN-S33%bVAFVP@NN6>V%?B zXo(Z5bwXh$G~Wp=a6)sPP{0XY;e_Tmp-ZihVs}t;@ zd1jptu!vOv;!O@1a%BWYQ%NwL<=eF>nZ5b`wJ1s*QE&LUUIomQJ zi%*;|?z%%@JKkFo>4A?>vA0qNIh(N7koB6;Kw7}R-S|l`3JM?2Wgbm(BBUjjj#}hh zWK~u<5(i9s+aoN*_L)@5VlzS*)&lxGfuYQEmLRoV2&alMpA_SESk*6G&W<{TkQxM% zh0Re%sMRaxb_a%>R;X@lB1}^bW>XOON1-$VYKGuXg_qi;4ym`9t+Lo<-y{y4p?8{V z1TX0Y7!-XerDuz1Ph|D%HOLoLylq~NFb~lMZSyQrM-71iAOwDHl}4QtQ9ka00?H7Y zV~;B!ggAIEBf^=Dr}*{k2R&BAzYUxWjRqz`mps~7Be9dA9ECovKA(v({*BeIU>52| zr3f;^tSK+lTp%jZ0v=qIV3^TiSMd+jZxM;d-8PEJo9ywD7XU9pNJjli!V?zfVFa7P z^VfmrxzVf4H9JYM%xrT%lPsQD$uNu>8&)vTz$C>sOwj9>Gd9M*vHBiT3yegqxv&k3 z*fggaJ(XvueF5(jK^CR*%#ObTW`xj;`fW}RK!!VPV`S0La5RpjU@HmZFJ9iQ%r}o@ zgkmQ5NVouu!g zasoVsQz8z`SV5MOO~$0c-IKFD#h9apV`f$CwMIs*h@BE^&n3X^o6_91S(h0tBOhaL~NbZ2X#;j8YX$2aRI<21)V_$sB6tohx!0 z;`l~2NK!%uBWZ@HU&U|%yiljqe%|K%!O=2}&@DkW$kRCG2FPla#R2 ziA}>aMm8`>4Hb#CC2R!~WBePd&u3-QpDbbxm1G-;ye(mmaU<{U-wmBao{)_Cf(*zH zBJu-C*a<{P(VP-?tYB71col_uYz$K=VaG`}W(LNPu*?TqCnYR%+M`|C#1hESN5VGq z>?|TR)XR}c+MICyZXzUeLi#Y?ONj=$H*m!(XMa$Y;_MHq(ix&EJzrF%IQzqy;4C>O zyVR0o7*S*bIpKrmR3x8X&s8!e2mFoIj}z)~wJf8N0X8t^YsL?UUZ|NT;yez_H`Wjp z)px{&9H%Bo#Hp#T{BHmhAyA`!LWYjH`kzcs4m|f9@@Rg_z>_(|n1X?q?|~_J+eav= zNFxHq663;^dTatmE39uprus1m^OE9p^q3^*k+DpU+=;(>!#E~4FcXl6z+oCJ0ZNQ| zrl3?FpO(ewBIORDVB|Z#u&u#&(WSTGyq?1d+U%+=08~$)Mr;|2idc|SQL4=Uh31IL z{BR90UNP2lJ))H6y#WnMKUrR5@qGs~Qg7}@MmbKxFD6N27;JbWGx$**2gHbvLB%&~ z<^xz;<4DD=|#g|P&k!L3H|m!VM4FU1 z@dKs{@@^?Xp1IH9MKKB!^hhUTt4m{%4uv#EGc)S3F3HYh#Vh=F^GdQy*c}tx-QjN3MNb310Ec~v z)2QS!;+`Eyx7NB-iY7ox9Xw178fM!gCF;ffZ_7||r?nGae<>R&TVW$V7MYBpX4@*_ zwMA@>fwQ;w+#&_pvU8bIz6u$(a8YsN6B}qb0e|VJC}U?=$#?bf&S4 zVs%;0AQuj}u-2$$TVfkYB$_B~Wq5HwG3mOL}Ql*<^VRMjTj4AoE&QQA{CoDW|iqIby=P=Al)4By$*nm z2wL^s8IW&)H^hDhvL2~r8t5)Uni=(2RI)Qc z(VO3)W=@%D0>xh9xn*;TKzX$dz$2{EsNaQV=v z3^+C13^fFYe_q3m!;Qmk!~P7r07=AQw}1g_*zHlI#js;6bT~}2haF4k_46e=6F}OO zVaJp{!|v>V078UtjrzSA==TPX{UdXjt`rYS6`|I|v~wsX)P~Wl6^Ha9rg%;--jH}y zADPZ=Bu?uiGb9kuBj-zCJ|;;CEYKsf7*HqokxL~khxd`$5|;D(h}bfyBmBr5iLZk| zHtHz`pd~_#US9$gybV+~V56zqqHu1sfwFG8|=oDR7cV=77x6-JD zHQOpa>+XXwSB36di)Y=fwgYWWPipnPPUccCfvn-*Sp68HCR6wQRzvNzx{yNZj2|f#bk;!Qz=4~NLCX{CDc?|WFFeaXmmNqJ2 zaY&I2mS!v@K%pTS_4-_V$Ld!xR;CrgK~BgzrX*!W2@!Wo5B)u4!vEHM{IVq&I4|#WKHkQ@rC`|3fF&536>UI8#_DT&m}UzGmeA|tlAQ^5 z58LxGQ~C&oQ{DiC2;my_8#55`X$p=n<5CqD6c~s~PcL_%Elu7n+o{b}cc2#{+|L{` zn|N^!nZtlu=+|eF(PXX9WE`e2&7MP;QIB0A*_oi2g5T5+^gD;xYJ#Ce*3B<>H3PWZ zv@+_w894maw~#(yQ_q5ww43^EhX3vBIc38vH%`QcnKvWO!m>YhZB}aKpYMF8s^vup z;Zt68F^^@n{#VEcmKsUTa5xY9$)rJ9<_K)b)6#{}k$w)-^TAzK`XB)23@Hm!q?a-M zQYr&dPBosuvcNW_*@VS)6NEnwNx1v~fGqO~F1Xe9-!d~X=dcfjxole%T*ketPoy~n zY|YK}eE^}(Nb1a2DNmHRvJPmD5(IFbHy2gf+U>|Y=y_2Tx=@k%Q`KwT+5V=K&E=jy zaxbt=FL44Fx+%34s@iDu8L35yq;lO=QI3dVlE6MidJ@V#br~@@I%AOqab#X2MFzo! zp?5~9`NErMc8k{7Wu3cx4_UOv7HBxtmfc)JzuvA4O9(@e+M-R3s#YC;CG{7P3baev zEhUe>Mk7vt_K)m7Gdr!_^KJz4N>`yr=9A7mN;?a=+T}B~&n+c~If_tR%rb8}?*~c& zXln(GrM3`7gcdnN0NSi$X*tZ32_}VI%%7mb6l!5NO?Zh1Q!Ipv7k{vbaAfdEV@{ zxR1Xy1m}dfLM<#jZxlz3xB>u#U{hj?6<@41j>DZ|{gdGB!m?$%6H)j#uqmB*#xZJZ zPrPVbciHyIq&IGY_zU5kg>=nK>{mf*bEV$`Vb56Prk4nd+Laf=c&y za}&&?Jy5;6%SCQ#r_lwN{77{yn=C?eRR#KU$w!-C&^&(1R-4$kp)Z}{(44X>@ZaEy zh#Jt!cLOMXDlUrm295z~kI6)G>;40hU&AUD7a|fb@jN|#5snv>0pq-rlcWcz;F`qq z@c*EC<*Q2KM}EO@A~pk0)_h z0O2YUnwuN{8t$PO!R@%FTAdr4aHKJ1S9>-c;NnWEuP0H3SR*?W&oc_4f*n88d);6- zyNhgEsNpg!%6qBkUA+k)@WfJ^$H+G-ceQ!5p^;U^tfvX|!nVnUd0DZkM~fo{uy|}% z$uXffb^r}7TpUpUeN|3YdHZ!EWAi8iW$3qMz0_c0`X*A~M**Q2!ys)HNRX!hBDq zJD2w)eH+uihIF`yfpZdL(k@RI?Z&6Kg-MDKVoc&r!{^D zLgoJM7_3Z|HkZQ@%+d{D^ikpGahcP<4U}5+3279(2(`GVRYJEyd(a0%7*Q)Z}>Z0N*8=u$pc^@2oIFixetg#n!UD)T-2HDUpOQnQq{R-yJ6}zr(6dnwOttj8N_`1?h7kis1v(L0YdY6nN!bTI6yCECvZ*SxBts((7GhrHyr= z9=S6W>jE4-F-G$AgY_uEy4#KQMXX4{#oSq0Kn#0Op~4W8NKXoe#SVt&*cjq!VKFg$ zmVWCZt85I5^~kbR3=55ViIF`0V5mPvx^8o0I7KiNxWur?GW&+BpxkI>iS?ByU&m1t z0F^Iofpy+tJv^JMUk)Tf8*mJ`76EysC)9kr9$Aeqt#Ohl3V#XygHm836oqRxBR=&O zYEJB@N0HT(m3xqub|#c`1sW`XBJKB+oUzz{SL2Uab* zw;PB7Up;a^a)+9a2yHl17gwcv{W=#~$0@WbZM2}jbl;BMPrO!va}9y(!mAn8upu~` zQ6b`GO%bi})!3BB{996ngl`LaOgS8wu@{Gr`f9?(tmofSJ1$H5OS`8spA$uw7B0ZL zG)+bwb1`L^cH2kTy~lj-P-1f+<>Ho*`6lD8&1#!elx?z`7{IqV8M4t3*DCP$Cg_sB zQWVqSzs6$E_!uG#A>>FQp;0ew19T%oVzL-nqLZ1A06bQj191xnw&K+(4bAz`5E0EA z1x-#fg68+{1Dd>0pO*a$D|?P)PuST{V)plNB%YT22rK(7lAX&Ffu6(c*Y<^;n5B^6 z`G8BdZ}~N{*Y?f6&dPo;EB2W-^xrW1M=%RYLndZrzek`uv;VcsZXizoKyM8{AM}x8 z&yDwD)zmG^0hvz9KM4FIZ4x+5;N94%W?z{EP5`XLQSTH;S<87fNU|d8P?|`e!*+{4}FJ^Lbelx$5_I-NV_larWyf^Qf67BWP0y+-B zhRTn1eIfln;*NF)R^ixQ(Km{(&YmR`$8we|R(ItLe{;(l**P=wjFWLwFC2iH7}_@D z$addys_pRhy8cqA#Rd>Q>BybpCB3;@{ugP&dz4&g%f1FBjrb-C z=OAPRJFt}y<5o-xbZ7;ie7u;|2oGG!h0%5jELb}5R}XG(^F5H&=DP>i13SeXSDWua zY=%|<7vx`_-|kxpgTAc5AfswIU6civ!ZepF@!ExO#~o?Iyj@%Kfamb%7rl*awYWxI zy}IoA4};5%>N`Ulyy2~S^;+Cy!lgpCw5qdgcbo6`_HIjudmi6X-0r)lc7A?V;9&S( zxM$onm`>wuJY+`7#1a}tKLpT*ZzUzO0dQDNVo4Ft`8w_;Hf!M}NYz_LRjmzuq%F(| znS(F{%7aU^&^S!#sLd0>N_%q?dbKxjl6^C)mt>QBWOmHQh_QXk1&68~CFLFb5ZYT= zxy-f&)NH_PN1N&hCPGX~TA05&D!~sh-OjB^KAc;g7rT(#6Yb`ioE|IiK|aUfnie;8UjC(+neky z8T^8+5^Cle!OeO(TxqVz;L-<4Hw@%<0fLb zyh2(ehs$618AN|h@pANxN5ceJr4p(_!o=+I&Ojc1bC4l2L(tjCRj`JRg(%7g1XryX zK*7y2A_8SL&5uW+)Byx{x9B3}`z$!5N%b@I1<9V4@zaUkJPjyeZxuW_+*}ttN%gVi z8-Se~j$0_B{aPfK471hz5$8*YaSVrNMN%Qr{GLprG~$&#e>0S{NB$Ua@%Xt(r3B|o7&j5@F#wx7`0bHD7bAa+EqTo|*sj&b0~yL6yD5WIQy|j! z^y+YyHVVrHb&r3SuVU$d7kXj`}vz?;2)rN)^DtfF0}xp{$T(706=QwuSBhJ}))qKhBF z6F&Imfdi=O?be$fc&7$iO9Sw0_UhaDwuikWR_ozK+n0djM1eAi`k{?*krjWxu#bM| zj}((iKeUR$!LeYo=yZ7C1^o~Pilb_A>4!e#6cvPiXd_q17(Rp6P>->c9f61lCrk7Q z)yI}{NMhD7L%I@!q8QZPcsG?88)cUvno}+EXJUliau9dp1T?fmQ=lv2%s7~7RDVG~ zq`F?|HilbYu$0P;PC3;2DmF&wQ%1+&P1RP?V56|vR($DGk~*aIs!q{-i4JM>YVZm$ zxHgmaNc2hW1_iJdw)Uqx8`z*u_d=tXWK$X^s7+^x-e`-15)U6xQxdDB-snRJRZt=Z z)mNqHjqdopg;gevk?4^6!OAaKb-A$;#h}tM5vw9o)ED(c%>Y^-oJ zR7x($vay2w#qv}ZfWtAy^n+FVGSaHYja3OdLNq|=W_?Bpuo4}ZgBWbhMZXoy)t}Ql zTTD1=e5GYiA!>+DIXNcUx#)LJie6k6y?c6b!sj+c-9;w;5zUtcyCaHKO#XuVW)N4AbkWuEeC@SRvZ5_b?&Z+OTG%WYRiu zVDTKIUv1dE0Affza80Z#fKH4S8*5mtXk}2`t#vvb9+qPMj+C+H>p+u|TBqx;C;R$4 zk{YKew#I1#M0J{Ed*m!NkvzyGpW4J@bl!10CX$~cu1@q#7hvjFNLgy64{YQ}wO@V} zYNPLQb%2taS8|EfVP1#tw7Mo*brnioJoOYwa02V{vMx~*q@KJSY?a|+NS$a%nceX9 zZoVvKQuF`)Ox2!J$?xcz+L*r}L;gps{3V#8T-u{Mn4hK?b!ly=vGTu*89e5D%WlKv zf)C=-^H*8!^Jo;%;D;r+QEzn>OxI6crV2to z^$#Hn3ZBh^VI$a;s!aMZc_Z}@|5r!zo!S5#CNlxre>{9x+cM^$&2}Ol{ ze)IRlR`U*)R{Mv4~Je;$4 zp@&mnkGyEB1S`O1gIc8QYaqzML^-|CbdA4#t z82|ml5Cl_4Bjd=H$R@pN^}-(j_2@H1{kwU*?6W}TUYygZ17JwLe4GG(E$00)4}s}l zbSjrfd03+32M+M39tt*Qx5lSj3@6H6xOBn<*?#81zG}JD7-?R3HP%lc!*DsHj#m9z z9aC#J@8Kdz>4Dyn^%8Pb|2n5)S2Q0bkURBg8eZy&^52OkTe6z9#k_|Q1?wcW>Jf^& z!m{TL=oAIS?)GdWE0AM+w=m|-&H<&Xx8WrTp^dN8)6B9|pDo2nFEp2n-r(6o=#zYC zex5SU$SsGFg1Kh1z@__*M~=v?t$8Xda1@*~-5aag z1|PD-$?&ZmC5|Y)@#!Etnj8AW8-y$IRi0~)F?_3d?Nzd^q|QLF-$u!On+WS|vTH66 zuD!M#d(~nsd@Op^ssNjEwfjOLAWEmTC=Y6_9my`ri!US5gKM?M8n~|BX87~j3s0}P^8#UKLJaLQkfO-PY>OUTO!)>Yur!{(qrP^VKS$c{>oSvJ#knR{Fr+`nsiQ)1s z43)7QBr;ksJUCjuTVkAD8d|suUZYorUg?E1MspK{D!V|7Rz-MH1Ww?{m^~TH_E^f3)hp>%J$|-*~R7`aV>h2w|MW@NSQ_C4LBViP|`5-Y~4A7QWdtoiV{WD#jUcl_2Nv*ob2E2gDJ+nc&_E-Ty=6a zap}b)N4T7>#rUwEy~LYzg15jyLADXR6mq@|p3c*KBou%+-M_&9jhx+$k5T9?6`>ck zKa`jc`YMMC9d=C@reNN^9k=wQ169oJ_+aIukfD9;~Z9~aybu;OMU~< z5aTI_^n)YMkxu~>8f#R^F6>YEBET;THk=;j?1@msP&$f{#8Dcs#->Fk9VthJPx3FQOw9 zc^P=lMv!;!#G`N^fyCE=#ItKK`Gu?XyR#!(1B0Rd;9IdQmIE z)FHZElza#uX1^5CC9KXH=3lhMsv?sRQahj*SML!F!D{v?enAUB8CI0JWt%s80$*XY z?NGb|@_spLj4T1vo3Q9-YTrVYh|(g#USRa>YEWyT!=^gTewJxD`pH)qh(4gFw#_G=th0RBW11L2H?H zAQcrpfzj2WOTv3ZPnhZe5up*#_X7dIHKDRvn5CfvJ`*^)X7HgwY%rEeM(4!^N5{YY z3a5iD^@b0{HEY6bwjtKVh6*a)@EMs5kCONlDJIzSaAVH~~peNeoTZ?He(q2Z^pI2i0}y`5x1-`Ho3yxb6|fYnuNoc4Qt{!NM_rgj^!J)Km7yn;vD#w zqnm}If0d|&&i-|}ayf<9MPoaafoXo57gcGo;@|d?` ztgiW$;IT42dLuXB(Q^Ecvr8OP%@MeU`FLwCPv{)6qOR_{ju0yZ>8fBaBf0}puvUd< z5Knv9o7mj@C0~4SkV}-?UtM7jhqv{L9$rEG8Fj@(*Vl- zBAB+5Kx&3*G#z~oURW?~3&@ok|0Z(Mezlbrqva+Hsf)H*h(ftHJdWIR{#-JW7#n>v zg{`RQ(gB>D1_H1GFZ_Wzmp=X}D9eh#S?jhgo zR5*@Fb*30mnokr19t_GT;W)7b+Gef|OC)|$ zWCC$B?F(+svS*`_E|@nP2MkbQ2SXtnngXd1!--tALms9$#QM>ZxFZA_1lv1tG=2$@ z4v$nK7(Nc2mwNAL9>^|fgWMtc&lh1!C!)Zg8X+Iy1+qyftLYg?RA#vy=Dg{hcp9FN z$fFIO==fZ`e)~anLQ)&J)#^2)ZgvHEi2lOH~nszN793*aj-(~g0U2~8|>)_N|CSuaR6 z>HMn?_J$7^YA~q?Y!^zU0&UK}<`8e>ZCr5R%x}E{*HAWK+~JM_Oflb|2dda_EUmWI zc85Vn*%ORkJjKPP%f@VOcu2eM)jF?o;CY#_+|HJvbhPmNMPQi_)q6mSBkO`)pe~`5 zHN_jgLFBdywSzFP)#h-HC^q8{>={?kqzS4W8H(28W}Jf?uD$bw`hp0GByCmjp}_Mc z#0z^-`{RZ{F7k;GjoIM;usLPWn(rC&ax5YDXJ-IV!^r|ah%PX}X?wSK8}KKH;SOBr!NySxKW-3(FYW;)hIs;h2??0^274@cnY`YsxTK208-5Zk z;fnr@-pC>7r>JnE#~c0)UMMP@nG)!P-{2}H;CGdehN~2_bPFgF&MT&lEWzSh;GANP zvqz#r$*g01E$*@@Zv(yKYQJJ8^h6kqyU2LDLexu=W95B%Z-@nXQdPllehxx&5H1{6ON1 z%%?D1&_@xpA*J0#YxesX8h4si#%+o@K#;)p9~cN+FzTML6bqIAEXBeyTa{G!8HNWv z19by9C!qF%d@<_#@0-l)k)cKV*o4IF-F+2EO$`pziGCbN=0&9bF=!kni>nHu^h6JFNO~x4 zXw~K_tR~M9B)S>_!BF#0_l75OG_hs|q8Wu+mMCUEDfH5z8j}R-XE=2eBeB*y*tpb`Fqm3xHrUy$!Unuqn{EZ zUHDgS{y}zzy_~(w*<8#R1klg142uO)*)XUwp-=>hD%G6w0RJ|ahd{cR)eHe)plpAF zd`w+kf`TpkG*QGk0bx+E9Rxp61cb#K%eI{=?>PZsc<&3iq1I1Bo6pog8$VYvd9In6 z#}VGHd6b*$us$SPeIJ0v(6@|YjGn*D(HI{2R5}K0+*61@7<+vtz1(de^h7c}(~QHs zUS=q6r*eD0ldzbt5+trhD$RlM!{n*UYz8s?p$H3b01^L|Qu?*(-ukx`{+i4D1NpaP+oRfB z|CX=r=cxYu{9BIwJ(l_3*}vtdtM;>h%UiwF8I<7PvJiD5+w9-+deZ?h{}y=Ufk-Cx zZ@IzhE5W~IDEn&3zvWcxn*{%s!}v`y|CaAhR`W^lZ~5{Lyr0a!@)PU*1pk(k_PK6Z~7Y-j4SZ z{ac22Xp4W#(v#%ybo;kFht~G&-|~?9(Cy!HCqHbHe~V8(_D;S6lAp`J)X&qTm4(!I8otGf`7}iY{T|%Iq-me_HThdDtWa%+WQ_wcmV8uJkC+* z$hZjZZLwIb{b%nx8Jjk_z3(7yXXw@cW%j;?|JnO~aC_gjD1pq-JQ1c?RW;7u7c)jV zX4L;8dta~)Hs=4Y_P)Y8Mq&Qf*!wKoY)mil|Hj@|yXk;-+xu<}k>~oKz3;!m-naB; z`)2RE1R9h5Ztq)&$h&`{z3=_*|IPNk`yb?D|Igkh%0`cJp_-HZe2fA&7xYwth6-ZzzI zuK(Zmz8ktJ-BN%75N;Yy!y2Dcm?rHi2Wb#QBnu%7;2Kc4OVwd2Z?_}r3{m=Gi`vJ#DhKpHom{>onf{pM?|Y6- zt2a1%xUaB974jctG&N2Cy6+S`l(ckD-U@l3zzTR#kz%XZhdW`b?suRymo2DRaT{84 zYXL*J1zPiY5vk#O0xdp3XMh%;#6gQ8=Q3~?w2mMTAfF4gYVnE=1FE(A7s-QCr{t&aJ;QwZ6%P1#iQnTEh@~0BV{$W3sM6kSro7AATD2p`n6d7 zQ4Uxtu+bZS7B}JN2iH?Zi4`nEwv8x!3<`VbL8h0@W-biY1Iu3w$Ei$~KSHo49`t~Z zr&C^$eoSwz!t@Xhgl*touT^UA8&AQBRP!N#jh?-TvFwFEZ~;99e}E(s##lWJB&tAV zy^|I3M#f`6dO?PEmR6^Y^TZPSPSJfmB=o2!V{7xF6YhlwTMk;XL04%0(kNV-C0#UQ zHM4P_xg!|lCW)e}?FrZwK{~VagRe$LT+0!g7dMCKpiBkPUkF4UNSr$8m zf#?TFA_AX|3H5*|NYwrKn}8u(I}22ibCyYb#ZTt*;ecwjPEdsw;)@4o^5gMz2JyEmzWB@IlEfDujJ#|5b*DtuwH{y0 zRM_6he-nR|J;WFL=OYff)%fDSfnv8SzW8idkoP*iI0Z7(9^#7|&@Lhv+7@4Yx{Q9G z;)^ffqW{z4i+`UX`^p_(tY0P&*qiv`#kA`FKZ-9d22A%~eDP2Ky@mMVHP_=&LVU4K z)ALzE7oGUx`*5%2_~Ku1d6UE!zjIp~;*0mITEK}fzVZVe0txZO!{32gU{CSIGceWW z@x|ib(AN0ktGR-$#urx|ppKt}_~Mr@!G~>)FW$Tnr;#hZIQ>dW52eamFcx3jaCfu# z;)|vC2i@_-=c4Ck@x{lf_uTQtT{yz{_~QC2Fb;4-H)1?0zBtq{1ev$`Zbd+7R#9R1 z6eQq6I)ZE@-?F^Ut?Cki8?|qdpr9DB4`fu}IL!*YaVuJUH*^ z)7{m$co~6|4+513RUM4Q$<4=PfB$1tq`$ z)!98*e2}U#!J42Rqd&bQeH)lZAzwP808l^8;?&e1C!Si)7pA~PTny57AeK5p2M!Fr zc-nGYR7tWbVb&x;6t9>W7wiJEmsdrYVn{tDOUkR_=ww_KKm92FCEqa|Q9DsT=U{w! zUwTTYVOaFio~MUCZB+7(7A4$66cdC8b2y;xje>m6_@IsDJ= z#Ark&M1~Pv21|kIX-Z_hE7GGB3Y<=gIp-+3eLLa#W+f{9ilPz)GAlkJUS5*a;7Z5~ ztvFg!sm(<~)en|&NV*J^*I18r$zXj_`9fqRN>90zp5~)<3Ql(uq%u5x8pnfbocuQ( zLQ1CoG#A@SMQ*X401O4Er}-|jh3hgS^ut2T9%RJ_0y7J{u^n7y(Hn$q3PNoF4%Gf3 zhN#r*dVUYQ-@RDLUr&pCB5AL@B@tpm)E3^lqRb|K6QwQO#a@vL+wZ#7b2+b&jGRdmqp%zy1)CK(l`!>vlHMATPve=gPs-L4lbb|=3+g0bI6X$3Pv z8OgAj(;D$Q;gtxLl9wYHhyY+t zYyQ>!rIo%p2nK~{h4`munJ&}LtE%Or{=^JdVg}^mMck5J@Jyv|9x@`l1kTdK^v7Dk z%ZSPq3WxL>HTcL|5}d=BLdjA+=e(gvyIaRk`6RbzPNO#hM@razUqS%c8emva;XGXZ z1y?Fskolm!5oosXNiS8r#>x}xo%kV#Em5J9*YL8Sm)=d4tT_#ZGMdF@`j8R$CFCK7 z=LwvO#G@#1x}8RJq7{EVIK#ek3+~v#V!@6jKnuyg?8Lu|%MnAX-(#E&lPny=<%Img zzlg)&BE}{WxRH|#4&(%b7per;v7kw$y0xg^*)XGv5!L^k6(#KsnpCBC^!ynr#1tv8 zjT?9DmKe2lvX0L(=eU$5O2}yNnuMKa==s&!`A9cD6v>lsw-OpiJO=S#8^V2@eAjcD z0yv@y5NA`06t{D{CGVoBIzgBJjFoG7YFQZBEq=_U_sviVuXkfD^a8*(uP1Xv^yV{v zucGa#N308do)#+a69+mev^8hn!1?!*;zPx#`|R%}kdp5rO^KnlrBDULvXhU& z92xNx1LAEG94*O}3#yq%Te9IIk9N?4<5rb{gCYKFxrqqJ7qLMYeYY8L3{0{-r%CcL z05Cv^0tqDIf+8 z?b(*3Mn`mq#P!vfP;xjga+-#N+P<0;uvE`Mvu1 zpYbll>ZLN?+D@!IUN*~vL8?a&#zx`t@`Z#&EvT`xfl5q@5MIj36_g@{7PxUYQNa8WSuOODn>QV zNJ2H3j+e{fq8aDdF#jWZiAT4mamS$<2U#@3ihoLyoJ0r2fN{aW%;!C*IZ`aaZURP_ z+j+hf^bB?Om`7qcWxrzU+gm2}r&v#BP=|(i2`H;Yd))LoJPbbIOqh>jF&o5{0s3zi z;Is3@HyrSFZNXRgU?cmCMc&QA&X>r$I!4~rAvW$ngaXAy-fcruija4}BSpw};U2Oy zMb;vI`&hzAh1)wMyk`uXjlM$MVMzx<4(Xki4%s%$lnScbP<;9 zwBjx+NWLv`!fXP&KE(#&cC4{XY?(R}m*ZhyyjsdH%tiQR!Oz<0ro69$CX2bn2LU41 z7Vn2kJD>Myg3&f5jBLeN%_DiwYUfkC-%=0^MP$)h36i-yg;$-d-XI)0e!HS;yIl+D zyi4fhFGD_V4pYl7H4mZ;f?Z#ddkAWfAW400cb=}}um%bgcgYo!?}vYmE7!qY|U?YLuYnSxqU z=vvOGIfdp)dmD}uq0lRVE2Pl;n|NT=GF!cUVDSt4z^d``zzSX>bOIUt0|WH@S}-uF zI77_gh}d+YJ4~DkE1W>nD*)R#bkD;L>)Z<<$04$~*92a&&%T<#tk%yxtL|Rx+`HhK z#B)!Myad{e${#FjAY&$V?u%jbP#J;%RLQ_y~>{n_fOyF4DItF z)d&?o>bIQ#ViYV9%)qgvGPHL?wPo{K#`s#8J`6Bm`U>X6ksZ?;khHpx6yj!cI@ya#NNt?uN9;HHTK>MpD& zOS_nvoIs5HF-@<0>Lk+$pzXL%Z**;LIKrvdYq7Z%LRy>qPQ2yb+}H6dJ12U%`6#

      173 z#v*CW`LS;i*gnPV1W>u2GthGbw}3rbjomxVPce(#zkQ|Vq5R%@8T^#FGxi&irc=xv z{9VusClnIlVArPW{kQPA;txn)l*5gahhm=3Q82VSsx)VF1!Lm|@i&hqb+q3=g!O$w zEuFw(rnWyP?v-l9QxC*=$(@wH1L+Qb*CDr`UNB$f;hXP7)mB``+YND6pTj8?>in+U z@Zd8Fi{tS#0eIWLCGZ|x>x!xV^2ZPl%cvo;?Rl3*3z2cIhO-7ORDUE3sdIE+(9`f< z3G9dE)W?#kD9Eb$A!{MY1gJxI7U2^!SS@UH*SeQ*dL-a);Kbmrz!~3|!ilyDsO5-n1{dtA@mXSQcOpZliSdQLm1#r6F9ZMB}C;cvovUZ)yzuji#~sJ-iXCoI+bYdy>pAN0&DPU*c8lxzySLPOK8C*u>*-Yuxz}^oS;^OPJnNXT(lpf9 zDDm85>!3{J7kW-@{JHASGwRQ5{?Q65G!{fgI+$8-off8T3jYPiCt~pR!df|DB@r7~WM}w$&xmZSs@gIzM`fo$)? z6FG!IJe+IB7X+h>{K4T-e3D;NBF(Q1oC}Uj z&&%kS2Q9!rJR*CWhs=>E$*K}@U4)y;;ZfVH^thC#k;F$$&XohS4<}cd8oUkiVBnWX ztR-(q-{-RLV6Li>#d+T^XBw+ynZLs26yDpydj%DJ6=TAcux1)gm$@SWbqQQKa_;XQ zT&M?oRdz_NpO5mskE5q4=9~CfpkSRo<~5igMrlt2>6#T!Q%wmFu!sckg~bBNL1o^^ z{)0$I*O(4UCKGb7Z$>YOWrM-7z_xYt^VZvE+gey^(=Q^Wnuv)Z2l;qx zG-yD;Nr1cf+k9}z--CbZTWTJK;o#3O_2&qVGgxEg*y&}yD*R22#egu!kh)Xr+(A_P zFZfY@?4@$EJg%9ZWQ)Lm<0aR3tIfA@i^KVQ4$dFTDCjS>GNX8m_hBmpPjd#IlBapD zT1EG+%=%%SpD0RxUs+!?@m$KDM2MGsZjldG2=Q zUk0(`@oTvt^z9QbzTOqeXDl6lP63SO%SLU1@>vrThnQ7tyHqv?&P}=i3kK#^j6V7T z8eXiyfycG|^)Llki29Zmjq8yTT|oClZ}}scce>8EbF;c z=>r34#ohHN-(%G~Z}?%%IkFZXql}+SiJKrgFh%9?NB;iPy$e&3zyE?$l|7*E*1jt* z=!5*C=vMh~!PU>|2;PuvIJFwxBU}Cvmi-kvhZAupQ_q!U1mRF{@E9|jb~mNM-|}fL z3o+%&jDCJBF3p#KWYHVM5|DhOYJj9{44$YWz`2hnCtd&3@lE`?ckVpH>?{;_DT1p)DsgN2k!L~yVTu<0zR zpqO_qDzeB%xV?bA_~kJ=48^sqs>y8?9hIsqu`t70CSP&*G`hU(-UDcmx?{orMwPcb z9{)#>t1wItNsl1(>%anR^A1w3rZ_!xhmmT2iKivd>7|&rgN}u^rFkEHRz4tR0Xb8P zcIJ2|ldO9eUY1t09Y0d>qiE;p-bZO^DB6yTZ*ZaVLH&~7$~NsSmPfLSI!8S_9FuL%tuq*iF9Ge%)6hE}fub(RQ5r5J$%~cIdd&qdRdP==hZoT}efs zI~!C~K4X$VeE~;l(Q%KV4;Nax@hnA4uPu+!QcM1WKLoN}ic18wv6~+8XTcYOr5S{2 z>rVfOlENO^d&--c7yuD469dY^8fDv#5p;n7Zji;p_Pi9UWL!>JzRjh{;^;f;*C)_- zmZc_r{|LMwN&Fi8{^;pvxHdxZilZ;_dI7EAHlaUyZc;+ujrByj#}Rt2ag5OScN2P{ zdLyJ&K^?c7l2Q|JH zX?-o;bkq8cx}29WS}$F4=D@e}7Px6W7cQRGZ}f&QlPPiBEztS{@PbY2w+pS`h;;$2 zw>ko?XTxo$^{>HNks92nJ^1|ruDUZ zru9E-jn)$si3iBIqltJtKw6>lD1M-*JjtC<`ROq#&(#H$=i;?9IA@zGg-|6`U5)Z!!__J^6yY3@Ue3YE*UM%${3wMDq_0S6`k_&C8&W@x?o%bZ@ zTnjl4ud#?aicNOZwrN*+lwTXYWeO9DO@gA^`Y>eEhOz-(^ibGAV~Aw(-Jw!|UAsrI z{}ULJ;A&P}unO8;n{wZXL)~G~&sH)#>RBDbOu(>NU?-!NUw<9d_)2wug+4BmXbr9< zdf7>sgeRKUi~0(1witYt41PQ`4P=l&!4@6_pRRHiMJJkG(knPd+omeGd&3bl&id_P z9zj%&3(^@$A}6gD(54XOM-J;jG_m%K0YDkD5z6pg$2RRIGmo2-Sv=7XSATnG2 zX0aXLfOo+}`c}z)XShm!cblu@GGLK-ij()pikywCQfTQYv}EglEnChD{Vl{t86H;K zUA?aij;|J-%0Y7Z{XlvDYQcB#hRTpL6o5C6!S4I6Dg}pxH#`>CP%m^YC>4JQ%ABEX z{R1aJ0aEr&m(W+K-h~7Er4Dc9IsXK* zg{6^F)KSKAsv>B!xq0%du?+!WXAAQ5BEkWw1?Yf8pHAG0*nYr$pU z$HMfh3>D5#@lNWCt0~?`kHnwv5Nm|}2Yd+nF>rk(x9Ed6Wb@ISd2|Ek`mJJpYo3PcebICo!9PV3cpQdXskR3LPdsQ}nh1&es z@`}%nkpZj`Tz~18NgTrdZ%M%I(kT1?9bmP!|B)6w#XE65Z>apYzWJSfFT-0*c7vLf z+99dt3f%kdD@F~x>mTS2p_^9&K-EApP=RjEsldl*OZTgJ zJ5*pLZjuVziEM#TD2q=zRE9Pc7&TC+z$?g~2fASNUg)9%a-QzD{eN1!_y47e1W`n? zcDP1Q^6n@&Rh++uj@!N(>TwYQAoKSiI=J&I@s>Jt1taA<?kfSZkW|qXWa0#DFa@ziyvcGo;eMwIH%;p4xiU$Wk!f9iQ|FF*3 z&M-jezFIkBVbxQ~?>)vAFcu&f`A}K$jQz_?7Qu)+U-xN0KDL+pQPo$5$dE={Vi36kgyDBZ zXxiS2_>dKK58t5XSP8rY(MAg8Qh_j2qG^ZdgVkt;CVrqU131BL&@b09ps7ZPlE)m*+QbYOE^02hu}k2R&Gu6P)dxcqb){(AD@CU8jnCL5KTN@ka=pZF`cw z!4;149-e_aj>COs38&rH(l7c~$N5*#dE^w@jv3)W2NT+2`4=lMxCrir!4il!3XD{K zr{0NQVMMyh3$UJ-tWKUkRa=ohY)f$`CZ_Jciy+cQZK?Eh$KxV>SSEraj5U>v$q7OR zr?-sEr-((YB7@k9e=51aqohGdaF_N*9w%DoFJ|RM&I7xI;)ttP7tGGEVfFw%iig=XxZ}Vq!-g3s zbsW6t2gJ}Yf!7=hUi>Tj0~0ns2Z<*T`+=G;b^;5f3Ud|vbnHfq5O4L}YUc3(+zRnB z$M=l)A#idM&!Wp#@Xo{XtE~|o1LUcadYkAaMVodle#8I8J8v4dPOAL=?&ccpJ?%|y zta2>tOl?|#Z$<--cI$Dg=9mx}?E;9`nca zMqcO6q@Px)cA+-NGogZ%(s9+AlJl;2B98>~I{btFfX$H9#JBnt_#sJ=0l-AJoS4?4 zHF2>ozt2s6Ej}|;eCxyoXz=PNYn0O-vm`2}q=5bJ_le~E&H=}tMB4Iox z46BHaWob!IHb;&_r8@`K4IkmTUNDs!=zPvn?OZ#|siW*zRd|$V4?g_lNAO3C+r0M9 z9fO^FhD~bCiELY)|FRPzF9H=1euYbV^n^N_862kgb2)1T&tI+;=>4`P zph=_)6!mftRH@87=J)i(w!Gu#tCyj&Q-?wYo>_asT1`yHM~7+?GPY_u>K@H{!+g)W!uoz2WoGANEwK1RP=u zM;Iqyce=%%Qz;lj^$Hh(kw(@c?8GVqnL2qR18{SSlAdLemA)!;oiq_!!4d?P(#hR% z$Jd)wv8bROS7V;fpc)HMXC!_X)T}K`m|r9KyHakFg8H%OyZW$72omnZ0`1 zX0LvaddE=k>eJY)>|64_;*s#%N5Ns8gE$5(Mn<4Ni3{=7ybe<(xqK3jxW694Hgy7> z;Qu}pzrnG@yvi!fH!v+XuJ=F_E<8hm72ug&%knsUA4deFxIbnf;-6S)MPA_ zdXw{ig)h73TP^g642ao?HFAbtX-?#BiM?tb&)@dUe9HvC%%8Y#B3@!DH>Ld&ewQcw zemnMlFOFwkd?y(EEcXH;f+7tqkeUrR1e`KUae`A0zU(-d{OEMyltTwAPI>1ud@DZT znE2Iyh6ilsL;Oux=c~%Z0ov`iJg}qkTUPob>J*gL2tNAM$7E4I(+g@PO;j{WM9-%4 zF?HqvFrSo0L@$`F6!6gAC=u;^#&NXSDMjgPCNwO==1Riv&)4!RY<{0-s?G1u(JX#{ zuJHSF-TeL^V*Gws`27WA@g-UQ1>@LSFpJ>oN8m*vQGkO^!u5Yd zHi)l9E&&}0Gahm%E+9};`X0g27Xs_Q%wqj@o-?E+p~5g&|0y=>AJ+1x=rYDQ?%xDI zSXB53K9b;O95NXEgMlvi%|_IrQ>6RCKw%{lOd}6MsQ?Ojp(qp*BG>aKxb;mB0&#?1 zg~})zbdN4b2E5kmAr5wxua& z$On^Jln)|YAWKA;3r`M+2;k2sB4}Hg`reWcAWmWxC?624fll8cBHLe^@*V7>zBqY7 z5)MFKfR}wtUI@kHg^Tp~A%o z77TeKQ*pzF?nTlGaBVS|0QHI=uiS-Kco2u0%v*4&^m#n$Xn9r*JCZ@j8-4-Y3I++u zU?@p4<$~$nhu}JcWF_1fis$EU@XmuVO6`jUlmV(D*WzDvk&^l@B!ScqVerzXU4eVr zCOx>CQh`VneV*pqM=hYRowGomM~2}V6MKR(!82$9md{w=oT15#F(eDJi4c;ZhnH1l!}zGMKg zC;TS*$MS((QDI?GDjRK@?ftLOsQpk$oprn;1lATj2MDHPI$^=#a6WX}YBK|DQRG8GOC^dNW^cEzoz}HOSS>cV`i&k7uJ5^eh3Cth4M&|LJ=P7SPjT(YPfYkU0c}6 z;eK}?N)ZZ{=JC934Q)a~mlPOY!;Bq)0R(3jeq-q&bqvIp=6DPoJOHAwIR(W>#$bH3 zJ=bM~F)%FhdwdiJ<0x*Bbp0J1sQDEdw`0L9v0@JNpl-23c%A;hYV3)WTlYG3er5lg zfO5~-V#Q!ThVPrJi&i6EthnGIh!t;>XSP;n3f7om-AW(@KsLnyLMLskN(KVDK)VP6 z^IF_;!{So>cHsVX3KEGdi8&Mx5*XZj z7b-y_UAB^wrRh4T@yu@sS_l#tY{(WQnuhf29~|NJ**xo%kb)Kji+CCq88L;=Pn7LJi|BVFLNM@)?Ij;{i%& zZ$N?(8V`I^ZAoHup(RO_Qj#brb*JRI9*ha&IFG9G%_h;&;UM@ysBEZKFx|5L=Ffon zC~zqK8K;)QNy}wCm`yR_E0GVVq|PI|CPnWoF#_qk9OJ=O>~s};_ck^Rb;bOPc*jcL zEJzSFmIU!44#>Hr38BK-kRV>RC5YKt{>yrBx}6zDj0f95t^2NBOnUWQ5H^7smO)^# z=9}V;%;U`RYsm61LZj>1C^C9L#}7t-mSYT}m@!~|PH>7hGy+r-y^+5!SS?3EjPdM-;l!Wlo3LbRb@=-R`&6{`|I=;#KSC6m z>HimjUt5R{L14+>Pm)XU^Xs*)CcX3s%`>q@BTWJfKs3h5&+?YwlPI7sNsopLC-xMU z8=wc+D}4^cW&HseTLhaWBG@W-utXetmH_46G3!Q9dr1 zOMdY@&MGcx6SA?WDMc$U;h(8W|2>f z6g->yNdfC6oXYCj8tIdB!jsMuf)M>5N?1*y7cSVb;Ee#$2Q>r{G@Pg z(KtuH4xi_QeRH6UO$pblkt>dy)bDH;Tdw932z6tqH4r7z0H%pT%Phjv8?FUlA%U(A z6|RP8jX9R%hP_O+_MG6p=~u7#@yauJ#af1GxCAnR(_M$FB>HuqG+{G@pjb%6cA0$d z&@=Nw{KnyCsj5t7=(0y}Xu{_aTfYtvDE&I6Lr1?p18PpU@50o5R<19zx!?f^s)X4L zc3Q!2N3))DA6Vc+!De9@G6ENpco0*~CZG{F=6F0Zb!;oD)v1JQGX3}kFTEJESYsls zC*k47@e<-;48M@cgk;jSaNwIz!8a>m_{U={3s$jPksL*TQH^{jz3=tZ*}qP`5%AY7XBRPQF8wL^I!kx z^XI3-w6+iY`4ZChME)EL>hR}EQ~EFC&q2Oz0nlRA|Hq&21%Lhu&C36d{CRf{tUdhs zCotmf5B}W6v{6EXKM;TZhYQI_H0RHsyzM`O zKYwRtQvQ6&7yl*v`3=ZMn1nwcnkoGGEacXs+WDvlTHw#q@PPdJj>2aA`F+1{#-HD` z(Z!$dSAO?=^58!lzq{&|e=C0X9V#85!Q|h4)<=UNp_LjK)W0iqtE6wyd<&s8is_^m zq})>F`xx)G#Pa?HsuNr10uujcXb_sKq#N&4EbnAI)yn%6EDr6Y<4Ju7@1p#VZ^_AD z=w0Od*c-YS8j4Ea0=Ht?sUOj)nPS>>E2cXxCCi8;g)y~_)Q_N6`e8IHdYd?}ljd6K z%WSW7nx!qRG+jO84|XPbPw+~&wbD7$h3i%IAyBVl0TghH04_@*{g_<#@-Fg!d<#_4 zvn+s~4k_AkMvqrXgXMjM`a#LVFDmKjSnf8eq#M{>OdIwQf5ueOcVJ>lC0!x!HB(7{ z8B<9=k2fqn*Twm|Sl+X29n-32D(M&8D(TK83QCr~G0uL_c73$%b&^~k?Wg(&`e2~e zZ1244#l-a9b>2nqz_(&%Y3c_Vz6De)-J<1xZ2lJSDz)@YRAso-(pT{Jo;1=^37g~^ zX>F@ZA6VDI$No!$EAZ!CSZeNLOFG*?G|HdV2^gpErbz3M}3{+|$DN*!GmuZ|wdG1}^A z&{OkAxUnaF^nz6L#eYA2^j#a1^1Iih3cvf>=ZfF$b59HW?jd+Uezz0;Cg`Il{d z3X?>Z8|R~eP(`*@H~Oaq<Rx6ohW?@&W8MCFRge;Xo=S=KtZOYe&=8LjMO^FS^@6~nC3R$bXu ztJCpb!8QggHx#V!uw16HAHa7W@x|G+*@EX`%|`d&u^Nxh@T9Di0g1fjvwVG3F@`jNRIb2aZHsd50z~uP1ufYA_1)Uv9<6=k+9Wa@8C_szpn8S<5yt1dpV8NO{{Z*C>H4rO zQBOAx=Cm$wd+)O=g|{#S>}_we#?H<2+R)XJh^rofyy(lUN`LO;!!xQBG_|~J*ZLuzy6?JB z12IG|%tRpeDX2@|#o~|Phmo^6aHQ_f&~u4L9^EsOGoKAGz#}L4NvI-|TkpEqd_xT| zHlekQ(2k~nm!R=yXuiyqW=*9B0*pZM=4n7!WS2jp^t&3hPzqeudJ&&wzC@9|o9{YC zesI1<^7-~HWkx=Rt{|@z&2hmDn&VJa19M-e=Vw)QAi`G-@~A)b>q@B_B#R7xEI2Br zGNA_o;jpRb;{=k0L=-ZsnJc-Wg2z}tuYc2nFWn7j9keSwT4O>bT5Ia^$Nxi)uc!Z@%!?7XhZ;K{S({o7CouQ9Q2V) zt0PW8GeC1)WsrPt{oBeM2^4%#%{ zhhHb_so_)V5l?_b&c9FXPD9z@4S3F9g*Q^WrpBiS(#9j#W4}SD@DQOk0*Tex4&31+ z115ZikW?KH;P;VaS?aGRzQMv@m8#8%G~h!twp6fJ-cFT<;UuGcvkQK=W5Lje514hnsCNveH32)@r=rSt( z3;70V1ibi&fcjfuT1wwiYM@fgEAe8ikGM}H$rWBN!Rx@qKwo=}ss^SB?A17#-{5en zC}gDYc8o9>RB~6Th5^r%`f|(lqL;S9JP>_aKf%kQ_`a$BZDZR%0P;%3-x!zR6}D0S zbICBW&*$yv(4Nj)?7w|D-*!IK`uQgOXjKXi&QNI&;fkJvz-ZGb%nw_uDuuk7{k%B- zIsCI#&MoSS5aAiz#*H~18}plksGVy_30$uqjzXgPz>YZBk@YT9M`_J1Iq)~}ZFul! ze3HKb569}Plde+UAVmo&hV|O+^t-p%8M;vtq7U*%6+I9{R70INJRNAp+F+=g zj>JLuk%Dnhw&)7C7<4Fg0N@215Mr<`J$DQD!aHA(?9gpSKO7S=&jz|pF?RknO}y&v zC-PaN(y!<`mbiX1@2QMR-N6xfBUjijp-B)ov>JLdRcPA419u}l8;`kBvpFK*+?|~f z)ITVoA6_{qm}&gz8AtFN`?(rJ;1gH>LDYG`vNh|wI%9WJ z{X&HSfPc&Iy$C$yUch&Jo8h|^BWMM_;}hYVuizW?M32EzsXT)=jK?Y%KaK}`gt49g z;$P*_ARYrTQY1AQM?mRBzsdV--~sQjy5kAq8+p$S;Su&r%~tsk%+Z9(CJw>}rD5WS zYl8xngT|(sm*Qzs2oJgu5dO`(97i04F@`ol`0HxQ#NeJC8>G7lV{X-NhS z{^9vZaxEF(t<|sI63y&>-0;kV_OSQmYwd~*Pu%Cy-@kHxI<~K#kv?DxNk@IEHUH=^ zPgTk(y&wSU01HX*s@FRY-`26+$ zm?e=)kPd+}fkQ&uI=zwx*$V<=R}^zC`!pS6|42|_`azsPa$8PLcTm8U!Fp~P?3#jAqV+F4)S#n^&`QaWq}K#Fw44#Dvt?e_spJ6Baw98AoGseFq@h} zjZFuxK;92Zi!O%NLxODhn;>aSCs(Os{cLF|wZ@T6IvHzMK{i1S;U?o&6C#MEZ#9;D^dvl ziq1jKSS0Nn(G|0;U&0B@$FTGc>0`EpE`zi=2-0RpBL(;BXBbZ+eV|rlY+8vw7xXlK zd9@7OB9In6cJ%WscdTEL4sjxIkc-3zz@RB>09EnMap#tqHtoW(?s|!%I)I)JJ4t0%uu=au#%;2cgPjjIf2O!GUfghXM8kdlQuJEDRs=J}WL6R2if@^^i#) zxIX*0zw(_N#Gw~H%rXhRc(l@{7d>H26?!oax40gg{jv4fuk->^Z#(Ek7{f}W z7d3wodhrWddaH~T_(ghgz8ZM*{Reut3!7W$1&{o_q!)K@Z%!}%jy{vniyw1f7QI;Y z8HQD9^yKJ3FJ@fvFQON(ZFBbTKZRc8#rkv83%$AgXX*2*L@JBsd2sHC=mmOwSx>CF zZ3az!4`xl!*FDM8HvJ>KEqU=_v{MNw5nIxd8KE(Pu&7w*R%FI>VWIPcg|;Qd*S91} ziXyTCxHA!O$-T2^gahOx^9A=L@~+4M{ncD4@7{o~_c*^K?K$hmv8wfx0b%{Ra6tC@ zl|fI#_T%`+4%+7QQCo_}1yh2DV_PcK8m5p1@IlN-EzS$T*(ei#$SHv(6x+77xzu@= zw7szj?G0>Xdt*`KIY7GDQga-i@;`t96UsQG%Y*I|0pP}Ka6l<4Y)lCrt^2mP1ivd| zJ_cf+vKa#O0*06zC}u^iKBS4tKyp^#D?-%WYTyMUuT1#%KiKyzIr29yCIcC+`B5`D zayptbsoTWZja6u;8Hwm*WZ|}XCf+e#_?QI3?nX&p2a{CXHHx*(Ue_1_MMIN4gCf<`6hJG$2o7Fa{l7;(1A3_sKrx^d+cZq6f7vYLQ76ARkx zNE~5|!+6Xc;HWJ7Mn5&azt}BV<6GbwU+_73*bEP*Y{IfnGFGk!fXj{J(Gc>5BEh@O z8xkP0A0ZGZ0~7@(4DBj(uiR1i+>$T3I}`mUeg(i{PRjqmV;qa`V6hNYh1og#-d5Cq%re(w`M<#*)K?47L=!(x-mzH8&ug36o!WceukKwK4V^{!kR{sj1dMVnde^R!V zy}uId1Hyri$o-zxt2$LCPG2mG{*dgZdqilH;p`amf2<2&+$> z`<0ftCUplY?pezGU(;Yy3-L541-i3|KUu&ZARbIe#RCHRk1GcRQ|X`#J5zowa7dYR zP{2(0Zvc>0nMAl?ph%Z(&KV@;oV3+Y4KND2R{t?1VGa=gDyx*L21kQDtx~@AEtsVy zBO`k&W{bQ8vjyo3@aV&&CayfT<+f@C%J{zV)KWntNaV`mW$n+|ZXiC9+^*C3chHWp z7M{56WkM%54OMia(=s4gN0EXLcVxm#jgjcrJOv*cN2n33N*KXX_Xu7}K7wCi1oe*@ zf1(Yw+>$Sspk0(Yv4{lre}VZYn5iukliw%?;rcE94;SFPEB0*wA>q!YIG@Gyx!5k=+9Q%Ym7ek;oObdE6W&$MBjt^Ky zOSBhank-mvn*q@?2q1f1xsr&Jq3u+?LBB)1NlRwU6u&69_D7u*SN)Cn3yXDv|R@T9X_Ae}i8^{c8^6gH!h?;2CX-`581S>Ia)l zP>5wv`E!u;rWX+g)7*q!lPaci@IVAF!OvCTS`%$n2a^sxHU#)pZgj@mINoa)a}L_p zAe2XOh-{afky#+mRx$(S$(+TDHvK_)6XZY0EHLTa0DN(N*Qnp22INzKGJA`65p@LH zpc7L#V7Z?s$o(X|VxbI!Q8HNU?&83C;rm5>>^TF=vM(`F0@ZAG_BRR)-UZkR__>DsgFW_^+5q-U9Clmq;%*! z@@rUs3rL96qc~PTziPp^bQk3s^i8;xNhRaEP%#x1u1|p@6h<4bNK@Fg{cBQ^1{V)` z6gzes$i0i!F$of_k;eIp1iN4jSD#n}X0>~J;~2xkW(>e&^i)5~7<(X2X-`kJzn7x9 zBxrMjk%kB6O++qmE$-3DsddvJoLUcpgEwA4E zdHru=XBV%4FTfA7{1~M2UMNN}Ig}_X+?9YHmPgi+f3s2KS;C&`T7SwkFfBLKX49R#3T0=26y1bELz zfEI|$4kHo#&GgyEpartVUnQ*Z`)-)OnH=U%q51lkj4*0L6XwP$G?f(Ihv0#EPfPF) z3(E|Ocpr}kaZ{kDJ$Sc-MR;@P0kj4ey<3FDblp@t|dR z&%O}wUJhHP@%2mXhWAAr5aInE+Gqvdw(d68b(41&G?Y-)q1? zgTBAsw97@`ccQ)I@W+Ez;E#Hd#&Vb!jj!jnAO0MWz#nb20)LCY-ve)d!{;{qW9NB1 z{N+4v#<*|Cvd6(+7`G3=F*>|^51V&)^KCB19U)}h9iENgj%VDHp69v9V%LWATz|3M z@ZNx8+kp4V@7?h3jHONr?-A&zWq8j={Y7I9Y>~!}7upT)%Q+yz`%AR32Y7!3p|4=e zN2sg-iB41iNmzF5`rAIWp*{z6KxU@JL!Cu6oA3H`;9hg6f8?u9fI2~&$jP}NFPmE` zY>LDd6W3bU(if?6>bl={x<|9fLniWrvf?ZX&!~8P73dzp%?S6miYCG z^8oKpV5BrUsnN7YKD&;iA-uOf*DAbkRYcWd*->o{qMEO|H6bndi4FBv33Yn2Y9GhZ z`s70TLBPO7sN>^~`la2*qGBrwc|=wsL$|%%TM^Z{;{!c=B<^~Dg-zsps8K1uM7N{H z#i-U{bLYw=(HsS){i)q3@b!m+0`G1Y6flm$(kF*Mx^S!tZlOZt^NMo`{|dri4W(W1 z=THRx&+c#df7DL+10!38Kf*Hq8Sww04e+1Se)uD(3MAm&?=1LZ>661BU9py$HP3QZIvjo!Z zk>66#1I~BzAMW$L50*M9yzfRwEuZhTr~z$!0~4Zgq#96r;C&}5RS3L0pp8A8?|atI z{N{u9!MiuGZcp(30p#_&|Jo*aFa1Ni;oTKI0N&d-yW!pMk1fFaA#~I-yf>Z!csIe` zX#7YGs6Ft0u-1b2VQ8Z@cq4IEO#h1PU2nH`wm&bt zC!vkj;GI-nuK}k9d0o{B#7sVIaep()fQA-D&*6F#r)Xcf8tV-XF)u&|99z+@i=KVF z*jmQy3pqbpsHe+aS*c(1Fn9Q;#xrh7?CZqyyLEVf@a7)jy)Wdv z1$d8yb+Ik*u79fC@NQfq@b3M0H@t6~(E_|*MMo{eJF5@ieL8Hf#x-g{?Sc1o91!6> z2yL_oZ`(faw#*;;9%(k+KrW~IM*&i2c*B?Q5}}3g;P4%B0DQlqo=7Zyb39^nziNzZ z4fO7dG#fk~yd|E_6!o#CnGJx{?xSkzDrYj^!NaOip$`GB4F5j4q$R179s^KF1Z-|-}&1&*;EtXIoS9x7rK1kLgW=yxDPM#Tr9 zU;}Kf@IQchXCP7oG!Je@Eh&aczS)eJY<){mS|;j*%)6aIg8k5k?GS*~iL4BMlAJER z$fn$%2L57P^`zkBGW$MkK-}_k-8P_^$xZLqqPhA|qJjT38u(x7r5s3BqIH=Vp;Ya- zkPI`r;(^388}Ci$0O&6egrk{b|K}hQ9nF&2|KA6Nhojl`u<8OCUq@pmYiK(5+_-r3 zcz>ix@7jpisz1_CV*W@EQ-R>Gi+ZLxxC!5&=%qle&k_3k)+a-y()JB)2 zxvMaYmLui+C%7a{fj`nmN&S&-WJ7UyR3ZKzou3d~T4X=7aOqEOT!OP>GB9>Uljb-} z!=#Wv={v+9>1QV?bow-tlV%s0- zHntn*kCfowMt!4|-{7KmY-D218F!8Y)NS)VdLrptve5Kvfjjg%8Vh>(8Nc*7^u_Ty zVj={fMigpJL=}tc?QGZd*p8g zq;7w2E=ODZy+@%r$KQJ$+Oe0wq4k}Np17_2y_djNo7CTX0_#Io8ao~n0F~oUqQ5r> z)E0m5D$v?Qe{b=p=SHP7{o`+0iwjZCwG6=#jx#-`paNy|+^l}N-R~*Hp&H7~Lg5>& zn*MZENn`3RBawHv7vAWn1dm123=Y_Bx^1q&-#YryX?iv*~=r)k*P zCUf#6uHh;4ng7Xgib(pgdd;A4nels!MRMjCH_52rZwKEFwF1_-UU!dcZt`*c9OJ5= zVl=`E3xHkBM#o@8&Uu^Y&wXE8{ke}p{WRP8I@j&Y{nu5N$EUMj68*W$@kR^&+~e&g zmm@xXKlpRsT9F9lY9nikKzVt>YPQ9n`v^3r{JCeM5tmE8?aysY#sZHd$P@heMe{!N zSa9+;!}w}U!FxFPz{$r!G*NQH!)BN^4K!Fd`SEC@nLl^i^sNKlBn0Ryxp(qf?>DfV zt;^^8;y%%WyfzD@xh?WqzeiSJ=2Cct}K!0kdYUhkk>D8zRHx;Js0fEz40AIp7$eiP$G?gIU; zcDqj?kOxdr2IHM#?h{8PmxTUMig>d<>Sy{*YCrtZ0pb5~9Q@H$Y~ z0)ZP*U!C|&h~IM!&TwYq$`oTdw@Lp}%@0UE%Zg>wv7l606-VN_n zXfKKMdxCKY9<&Vaz!8A=WSCNoC0e`Ty&RNP;QcJx*aN)V>OV2M!iM^6N-E+trL1bU zR_<@OU({^Z=Phah>dJ*;U!mTD`@|c|&}*CvY;)WB)y1;FZubci)V4S~{}jzb*Z#zZ z3gV2%S%=n=!uxnU5H=~f|0}9U8Z%)!HQt=qZg{T&WfgcYL>sNZd++@xZeC$S{WU^e z)Ksg)$=!zfuB1@UUywlSp>>D5R7b6#g6}NfF>U zJZRbA5;_zJ@O#)njg^shBf#4rt%3jx(1yfY)K9ep&6vE&^Ie=0_ribX=F)b;|FzcP zpY>0H|Kc{lKiq!!qj|#rJq!M5DLMS{pcVLcBm7|(HCBe&4}TC@1%I^BGW^?izTZ`1 z!+h_~_p4t?fVucb{?PWecTtI^E$91f56SAbho2vVE^xj-U+X^K>(O3Pe(p>>X!(4f ziaONBJeWm|4=1!6+@FBD3b-rLMl1BbZSbD3tbOqQ#k@Vj`v}= z3&8vJwQhLtM0-i$or?!8!+W+D@LmqHsPXlK?S}U^pr!)v_s~Xb@ZKx`32liD_n1d) zJVz%UvCTNTyZ*dKxc?CD6JNi&XKMcN1G2{LVd-=<4{BafBS4>GbjDIAh4%>b(Q^FS z{DT1RH86{!o?yG--2_T1@ct5Qv<7b_ueTwd^=YKPS2~(^Ej}*Dpy>FJ);c_Lg|x2Z zo3JkSNY$6x+#U+UCgJg~dl8_CNp=S_q!sOV6xx5-czsMJj(ZW_;PY-VdaG?l531RC zCXWJM9D{d4OA)Q_zYJQS^#Ae9{&5(0Twv=IE3%b=>na{~^mqZ!d-jKx8nn`_27O%~ zHdC5ik8bOqHlBRjO&`{wy`;zg@pxbsSX#Vh{Yd5~E0VNiES%@}T z!5|f*rJ6 zkCvOE^Tgko3D0A{t^7{ytY6c)C-DLGTry6P`Zc>| z9SPNwyMB$k{k^Xj^Wq6s`^Be8``0#WAN4?JfPiX}kbos8HzOz4b5Fm}_NHLBkU=Qu zJ$m*n)v3Hhw#I%#c16eR(yz%5^a_36HPB5PvP-Mh?&eGOE4Y+8CiZXjRI9ds$7DX( zOOVVM2q1m-4HafK1v|qUjZXWPB5Ss{q#*OWQBA;(K#$O;T?5|GFEdi{b}=-oOo=>N z`Y9-v+j1w?Hm@XJ9)1gqpGGoP7{jfssROdLO`5N<%t#MyNGKbVZ42WzU`x6+RB_$`SyhKs2Y6L04vshUa}()_Umi6H3TxE znXmV4Ldw;m!g^W75)J9W^D?wc)GESi(xBawrQOq2%fk?}$~L5>=3zzaQ?Qnoc(hCS zX2M$T^ZJeJl*at0|J?QCF%QSF7z$<)1M;)0`pf!P^_SsP(YK%pDc1Ri<)~JKKFf%_ zqxlW*v=!mC+WUd4nf`W!NAox6{s!m(e;nG-HBzl@^G@?^4woWB6dn)FLdMbq`VGyz zFAoTHC~xIv_VG28ZA;TulaiI9pWFiF{z;s~0;hElv6L2e3*ge%7eu58!rFvlney3^Q zYCFB*&A6vNy#QB{bz^U_Ay@4{?%fB6d!uQpUuYpUq<-RBMY)#gzUtUoVVwjdHB~+` z%kWZO&p(7ttw5(vptBy(DJoop;l7BcktX!Xg#ot2`h1^#KFa}PuKCDEvq3A|h}s`f z&jL0X>sA|{C4FwF@)>+mQTNeKfKhDTv3=6X% zEVN-Btd%w_(;K-BA0U2}3y@(gK)F^{w)5EZ{L*d(6@C2GWjoTe_qF%RnvP8mmUb(w z5O76?b3{77yV#@UTbK~p3QrpPo9X_F&i+zwn*YRp=-;fzE!nR*mEMU@k;4s`eAUh*)fw!MI)$_gLKHCH@TVjb<>C9AkXa0x!Cm;af>9tut#8>C!l^HiG@NE^W{`7$ZCxk zqW+u^LqCe`S=8{ofM35VMhed*DfA%|qD2b_>4Vk424~)vi|urnKA2Qck-&E!80^Sn zaRL}A;Z;HgpSST3Rlere!x7SmtOfl7Upm;M4bIR8XVyQD--Tyto5E{@Lr9&(KlC&$ z-zJ9z$;siQoU;3h>#J4!y00O%S{s}d_ghHa;Ecfh`VBUHyVRj?^bhsDtoi4Y#Klv! zyzDrdr&W@^eQD9RO_KlVTI^(!xaHUw=Lw0^*2EFH_Z5-z&+|q|lF*wz8VTkX>%PS( zcx92Pc_10R`4rfDO;%(bDBLxma5v+YqHyEo;e6Dj7Yc{O>+fiw%c#y=gC|9WY~VOt zMOKa-&Mxu*o5fmX+3sU=zJ2f_Z&b>rBY`_I+{2s#r_~xD97>2Gwa}hPnzlU*S}f+j z{0j6iI@W_6fc}aHhj^o^Eidy|)jHs<`@qvt)%8R(H`=qDC7vt%*b% z5S2AxL&65CM)4zR)mYnFYpb;ub^)s(xLL{VWffb}`m|!P^|7>~r7cjZh~cAIP_z-P zq9R5_-E~tNB}Ei!-rw)cy?5{K-hALk+xLB+{E?k|@64Gq=bSln=FH3)9D{B(l$X}9 z6BU{&EYzWgI-C?4&$~mFonxpn$OE*Q_E+k&7f9o_(C-RTe?|}Fcj5n zqRK+2$#mRQ zWd_S*2MN68!y?Okv4g0__CeVgYV7e8mh`YYM%fsvvZ^9X3CO>}7YhJJsuG2@%e*O^ z7wrkIGxgWG{KR|Il>Q2}`sV!_Pp1C*3cqCjsK1ozi3~B6ld^b60+6e}0yh0M)YM-nq}0~CR(wwkepnCW_IOy~ZtmfLdCq;GI<6Oi zF}$QOT&lxh$Ne&xOCex}-L_6n-vx@vrK!j>Y71;D^AGKj{h7VO-|NEsy+8ODclQn7 zx{r<5qV3;(qt_kw+0IXgnecm^pPKZ6jp>PB=8dka)Nwr#b{cZSAsCVLOd9|Tb|KcH z0}E{QSMc4@b!9p5mrNZ1{2vbxK5jv*$bmn3An;EaAbbb>SM^}$b$=ps=z9aBj|2Xf z4g~%m2Z+A|fA@Se0Q@iP6~1x42itQ zzT4HSoq9n$vJJ+!ICaKQTHVPHJ?PLE`Y|mi(rb%En~W(fZ>Nz@VIIcDV$7qn1yl0k z;q3l_k+m=?j64`EJtJ$~MgjPNjcCB*PxKvcuq$%8YR$BeHkLPV)&_7} z=Z0}|w6f+bI?g^PPCjF;)orFZj zciH~leVAOyeg`i|<*tA1^*uI8wgqG}FgE@_LcCy-q`kqHFsvg(c+K<68X$de!hU?}WY z*e=F+OO(fdlJpX=3o zd`+A2MY{UFo}MrHV)x7?STe&V; zi}3D7{~oW}Q4Z>;?)|1IzwF;8_Va2;P4w^a_|*?ZjCO;mh|r#1p3}vLA0mQ#yD2Z| z!5>{B^ps@;baAasVTfL|Rdw)#Uq#i@V)MgZjX?;in3Z~gnte>n8- zuPH?A{rg-Z(r5p!P=)OM`v?~5vwy$ubL`(84Z!%*@87)L-nw7y%F@JOuAD$y&E3mk zAaZ=Xt~fE6hAuVV{3I5_AgNb>p9&u1h1`#kqn}O}{BLcGhu$~jKY8_QP--i9%_JcK zB!PX2z@R2)yo23K>QCggPkfHb0W%>(& z6Lls0tmA|qM|e#5GBcq%nF;;=?-)<7EQ!8e7#^wD$y5l6Fn22SAfi}i`Sg($Got6r zCQ>23@TO{?@3U>+0?&T6@0relwC~9``)}X-Z>aWtvD2ABHuP3FYGkPSYD#na^cugRtRx@e8~6ao%?C_z@SXSmYD2h!#>}OONnRNaCVAqM$`W-- zzoG9eaFafMe3rHMx>A{@)iLI&+dRt4K6Y*FUvM21%e)CIp6qT1d>@ZZ`#;U$FYHJ7xHyi@HH_T{v1^*rbHL0+BP_uv~JuYI2Q46sZ(sKGz8x7uwB zgMUs2hMkS*q8^it9~Q8gp1#@mx4DkZykU2ynRLYt(OrfTF}V5I&aBoN{)B!;pc zldVHld;4bV8&E+GVtb`cY#rPC)a&`}6@64e{TqQxEFe9z1BzHj{1>Hn6CN^bGpFd>!J>0ftTa)lbZ#ANi?T$m(%`j94r-O5S!*fcA&B5iMO>#8 zacv)#W+m0^q}O4I-CPoVt1$8<%;HCFp_NI?-Ez94QRF-*QnFa5?m71+WG`X4& zqc{kcUZW)vg<|Q~ilyIxGjgB@R0FkqvhAlh_YGA)dk3bo391D*JnrdNM1Fdqr_yxW za3gv2ul{U$86C!nDniPHK^k7wg>ew?;&o7HmZp0F5s-ymqU;iV(W{Kj9d(VTva??^N%jmp%;}d*L&x|v$H#nmwCLxF{xxUK z!l0jFGGvj_pkJ1R*~tjOv^33veFhmt+4M=q3X-uMb1_5gg1w5}lP~3y@q}3!G8zK6 zAxGeLzL+5(d-7mtF0&g79Sb{5I%@EAr=RVYexG`O7KJsS{(L&ZHLLzChDKlVDgF7a zDX={I>(8UN<`eemIT^y{_h%xEU72KE42R!r{dwhGsy{!x-6U!ND|G+%{%j5bLq0U< z$|2;u3dyaulSZ1ie)AtR32{rTosOp?7uIs9+v&n6jL_ZVdK^h-wR z&o`cHk+Jvw{OI5E$#{uPE!z9Djer51wlfu?V}Cwnn@Pw2!Mcq@f1U-4V+;nM_*cEj zYaZIyb6+zITV9fDyCD~l@0-tk!=~*ulRlb+x-b_e&wUH{@%A}xf^c*p3|uz=P<{lF=6@fTqQqVlO-o1YBRQ4VT#os zOD2gXc;y_pm_mN+RIM$EoMI_iYh35j2u9y8Jg1mK)BTBCGCBbJG1PeegcjC~mfg!_x9NtjxjY#lW{eNHk@sEkyg7WtUt*rcAH%XX`QzlU z(UW8 zN^Z}t2N^~7qDkx#gH7M9A135`GP>SvyHQ&UxU`K47wH~tElEre(kCXk*;x$b(TCD- zR2GRmqkc5PI?J9>XqOU7x44c`3pGKG2bnReWMq3~vZazw?X~xpwP$+&=9!t^FZ(T3 z|KCXS%BGHEI4-9M#eIi}Vkmp`T>3#m9`B%xD!BRAU>F4X$InBc#MJ-bZK#2D8GDQC z4{GN=JP46J@tZ3|3%HZ5C~9X(Mi&S_;QcW8LDi7R)&+sWICk$jX@WxO9<_=`wFZxr zI&snpInEOFr{BE0uP0jXWej1cebK{t{X8}!cYcYjl2ESyN?}4R0;V0RH*{f^rvxfc znCbFget?YbWY@j&Z)U$T<+tDdg>^kI1R_yaSwgvB|3{toRq#Rjf2Dkt24hMQud75> zQw==*zd34P)+M3_SQ@%R8+>11r5B(ywZW3-GFX2Md|yls2;VHVa2TszFRkn!&*c3I z&jR47{7>2PS1I!6Nmc{seM?88>4)*ZF%BglhWN9KAU?K1{1R#$%1E6?DJ->(Li3tg z6o2OYwQxQG*H$i;ADKxR(-cSdUA{0D|3ick8#y72YTlLQcV*Yx^F3!_T(DDw5mje| zQSea0L7D7093KH<#jh-xd}2$6yN16acVajN7Q zL%#ds4==S&UPJg9{^T+SmEy$k#R#({9NE~Ok$}v2O8dG}>}yYQFp#iwB4cISMpEHK z9W-}cmX#eC5}#D+I9wV=`4Hj6B*h6hHpvNtNls>W_DCA7_lrdCOp{D7r2C!QOp>|H z!U;A*dQ`7N=_`nlX6})g`~A#NYM9ZR5ydylj2^!UXeh>xV6 zR&oI#+wIaVa1qCA$0TaUn4`twUjQSRW$5!T(K;Jb=bbNS!PKOnjp!}PD|8(SaU_gB zjW)EUcVtkPl%8eZeiHnDgoQOb{)c3tk~Mm~aIbALBY3|7M@V*K|A;)q`^l%W@V1Q~ znf-Iv*(W}`S}d3y4%TC%eVpnL<3DaAi`R0$vuu-_jcT&p&295^7H$=CQJ8Oqf}txkEf!qpNDUG;QG4Af}W9|{P_=15pV*)YlG%C=02pEn){G!n3d4^-`Cvdc1y?xth>pK3T$P8s1UvE zS&*!6W$&Z0WGu^W@!BXCodF$1#c5AM9pp8uerjU~O>pRBQW`xCPuhn`(Xh9zo6$e|h(xAw=J z`4#q*WM)C6zPUoI7Qor!TfdpB-qb8;1bq66b{TU9=!hL-TAa22Lq^Li=+cd5?-)S; z#r$VJn^q?_HAUt(xJO3Jc*Fl8U!|$m{KxFZPv4!>jc1)D-FPX=sCmV*K1we@X|i`o z;uGQf0UYxKz&C3IJB(GsEixcHlRs5>764CkJgLCuk&OLT2XOY8kMe>Tz20WRks4G2 zc^w^=i&$}%D7w61kvwIEAG*+XH)^sLm7q~E`d~roVm&#ew^xn^w&&7PDI6K$eH@!+dm=FV#v+@ z3&cwqa)nr4fFbuW#-~lVTcxjoCQDE{TSQPQh-mM_SsQaflDBWjNV1q`c7LPu_#{K- zX+X&~J{gS7omM!{_r8-=XEr?sW)J4u8dL+n#KME!VyL;vH1W*d-@XuxvD>q^)+u{d zj0~|056Ua_a}=Uo_{a5N<)>rMI^(|sK6*R;x4~1VOjP2Z#pEHt$;v8tH)LfdIbQ#< z!h7$!EWB;w(Ln6kdLU)9Sl`KWd)e*Tc6{Zq(99}x>{+FM%=MWzAgAovEX_kzW7)HY z9#LyEnfW4DX~dq*tj%O}k1{m~LhM;5MI);;V$WuIU^DitTBQ*lO;YykBxTPUyW&9G zd9N61so(tG^-Gr3f2G^S4Kc*G_Kp!<~RT%$47(2l!^ zq^!}r-wK8xi5`E;7N{ZH8jZ-qSXf-yhlAiSi%&L>2r(vuuSqW2{R$2vO+VW>BE+*n z2(hA1@B=cZJjrv{!4NZ!5PW?L8f_gRpf#9(L})rrhrELV9v%#$ahCyAXahAuKpE!; z{+zQ3{I}R;fKA}Pw4D!n;SbmZewUmNindmmld)%>!SR`yJiw8CKIq#5z;|b(ob9mA z2mJ^@VDVsQ3R$P`${<54Y#Z$DfwjEsK5l=^K|Wjx5xabxOho#WkIgvQ59zkc#|OXbPdO(%hvn@|Pe)_OYKFUn_xC>C6 zGo(;$4xBM=lF7o6v~s2 zYGV9ZoKMTvAC`QWM;He_z8qYUVd^2{K9PK^ADID_sOm#LPTZO&A0v*j$%kcs%moxD z`AEG$0D1DU<>=n!;|qUskdONW&aHO+aR*@ZEg#pbLU#H13JdiqAEz4_=bTS2A;$kk z`GEbgpZepS@(feo3w|Q`_>~RRuLV>u`eW$xdGhi8Q8xLo?2oSjij#b7`kSMC`~g6E z(;tIZILOCbfs-X4HxQ9N$Cq7-3+XBOe9C_|umU#J6eGhw5MU_Pl1S zSC=s)4`S(Iy}BX5y;E4h8iipT0gD(w!prFjD% z2%UqgKyj1N3*1vQ@iZb%nbgN%mlAT$FCpg;kkASuYvNPw&x+@&R|j&Qi!#p!{D27`CnrLP85hu| zU>A{Jn^MFN#vkC@o(y~hGnJuXRBKj_yk|u(3n20RKY92V^1xuCN+e9y$nR$+A5x!) zNM3!Psl2R$#nqD$DMP-q$B=f>ur4i|re>#vrNW<>SITw^w+w%Vodu`)Fft_U;UDP%VL9J}sF>4+>Vu-|7}FU6;atd7*7F*_TjdW>0p{!g zvc4aP@6j5M3&oiS#R(t$W<0j6^JZ^wKOh8pgZuKv)T)y+s~CjG{!4zFTwI0B3fpY{ zncwfu{%+Mr8XD(eBKG~1 z2NAx)2&5{qQ)~#RDPR1a_fg5OaUK!zka4~~;!T7);H$=rZ1yy+2cGBAFY%T?fmpV& zEim)KL0F6~s@18b1 zc-g*)H(s+3;pO$3eGp|5^2Ujd=LbG>#Qg-$2>;R>BB5 z+Y>z7J?)UO=l4Voicjc4BzE7*2|cW_(6LP4VRN(izsZo-fPP6)Kk~{rT3(*i z^&~exmM5j0j8FzBlt<}9d@FNMsU#{C#*jZ4>X>z`ex7Q#68ooZVt?yBh`pBnnH|v#IjKL) z^9d?aKW1=y;?s)FSYuwUqM@r%nJP+P^5n;YFb%2XG1#-I$qfQ#d;}O%h>(EAxLWRm zgrRlw|7Hd!)Z1Tu_blkEzK03axxQz)e-ZT!%d5}Sr?sA{TJN+{4gsw$2y=9uO9q?p zFep}jI*h|FM^p$nKtWY$>(VbG-RQxHFJE%Imu-ADfE=e6P)VZ-8F*yWA@D#(ZJW^3 zFXh7qeMG*C<@^qV@>bP(ebEFo8?Z}+x~Q9}LC-MGuU_oYuk@Bb8{O-geG0S@WSVRe zO3=TRT!QdV>YfNu5QXnSAlk_U{L+XoAbflkC zdpA>)`_1Nl*b}zmZ=WmeeRA#IL2Poh(PVlvIJi`zz41VdL~>p1LCbw+bqG`aVGm;H z+uAPrdQzZjQqi9M-za7N$@f0R3XNi0IkX=2e2Q5jA8&>xgVPfvzW7JWb(1y9`V zdGM+|Ym=Wt1`~d@2QKPgv_i;PD)Xnl!(;?kdjQdTrU+WkV2NQ>YW}u~elNeMKLdWW zHRGSM`a@O38~&0W;usJ<9a8FrF?w3pzZ zW8ok|CBD(l06oa{h?vOEH{-nElhaQ}VB_HWX@j)&@e7Zh`(Xi`2=Us3rXL@CaoXqR z?&$Fq6s+RA@^;sHRciVHkFqc_kuYx@2kKwyR*XuxOP6E%nDm2M{5W}0h9#(9`U734 zI!r!tCHTy=GjMyK_O$jw8dC=sMf&~l3FHl2*}gS}ltj>ru!`?;V=5DQF}lHP^n*1J zHk?s5A1%sR^kzIN_-k?H#H;`h&hZkHJ$fZrs$bmg+7nzi^-J;M!=XasZXar5`HR%AHq*)cY}Y?PP*gmvFJfe@%powpwZn3?aa9b{{)|Y@^8(~FZ{tO z0J;p9a;v+mLV>AcM}`9EGtd;%$HhUpf_+-ZpLQ?6fA9%6Yw3{=-siE_wLRFn>w)k= z@!B35C$C};m;@HrwyLN*uwBiwv<7}k9=V8-d`E-1NqTedR92g=R#2>dB#j$j`^r$( zMh~EK_( z#lDq;qHA5zw_Rt1c17NXt!ZlQC(~ODi8WuDO_5*l%eRIa+1r?!1H2o=>J%Zl(6arAhT<2`Vg6)V@BxGb^-OV>m!FlxdvZ|bS6T6U;^#$R|609 zmys0tYD^6&wmP_yH|x9>1|LI@55-axH>;w{c8M$xO^{&g)7v&j?ZTc&aWa>Pl5KIb+zQ*1CBo%Sl|59do99C$K44EhdQFWA@> zPAF^#+Z5hN!8ZJIdKuYi(W9^apM#(DFUWWUSdNhjaj&~!Bz~d19kqZoYgt*`eJC}@ z8l9vG4=q=o^!3_qXmrtTX2-M3=VH)7_>GI)xB~`_Cn2~)99n}LG!gNSnhFzE{j%;L z;%PN@PA}vX*`vLHc-H%Zn|3|m`%$a!r|ZhrVcdjbKLGRE@~v4KR@!a;Lw%QAEG$Q} zWDuDZkU<3T^ef|2h3%u`#g+MN-|<6f+b|p3GouFCziFWKote#loBldE)|>tce_&p( zV75g43jJh!veS=Rm;3C6eq_wwnm7*+BE&ZAFX&)JM5S|`skqIVim1|N7VTTK?+Z%bWA5{K*uW3i_Dedz){W{T(V5 ziaN4q4d+G>|5y(Ohlnpn6+uTHHZfCZra(@)5)M1p zD$!T|l3w$p7n_ZMB_nNNL;e9F$*DlW?M6ALY-+*0F{zUNE8DG{L za0b{PdZmwOwJ{!Sk(d>5l6`F}CM8)KB;HVj3*c4?-p|@Wy_x{)l0QM;g0{&scpD?ggEJi=_PJ@W0GBm$oTx69tj0=DG`lTa~f{YT$z>KIZg3 zQ0vG>4ffDe+WPdvCLZZ4vn)j5ld+dEK-L!lnM=BZ&(D}myQ`pTa`%id(RwfVtk&2C zk|9b*V=-nGAfeE%rmC?^Yb*xQrjFKj1Go4%(9Vf)@TE}kdzGQ4?w^V`6sMomp0!|1 zTa)et6Vi8^_*UiN%l<*)2}|Kr2K@#@p9R+``n6~1C&obs_niDN!+pq3~+ ze4qBb!^l9s<`572?d!YJPuS`AU}G-*FrE_!%;#naO0X+2P;sL_u8)JOr*H`Q_&TB! z;wI!F2OQH&x%V3{c?OWE{GeV6&iiB&0U#u3Shl&C1PFGdi5dc z7uo5+Kcz1@;e!Vr*O3i3ETR%?W(xyLWE1LyYk&bv8z-VK=1h3+lg-tf@cMwT;V($0 z?^N^N+PyB$d(HkQ_ENpfcVLx+9hI2u1fQ8v2U*eD%Qu1vBXMtA5u9#u_rc*ZaWqo- zz}0){JZR;uSy#z|!p|7?VL|hd^lRY$F7}%7S7cFJ=}$CNrnu#B(PhtYKe|14#tM8% zm|pTf?UY~HXF-|LJ#Oe;v@l$cNiCXmmDUEGJ2E17rkJXVI@N`Eb&oLlewM@{?7{|glVK{^<~Z~^%-FFC6l^T9JI5Ao36@)s2c0`R(iA?_aJ+DaD3 z-A7X4j5Zk^-B1ea+UU>P#`N9vX8tgXAJQI7 ztf0f-RdZEM>7H>c?E`ce8e(c=rZ>s8h0Kl~@c6ntP$K&-&Q_qHPapbl4UCOm`a@N& zKE!3rSu1PnMSu6E9CzY|A**v7m!ljfq1vp@k-k5p5AD{dIsSZ1eVDJWjy5}qO`gsE zR)jk-ut*ncoq41&qMio4;}?20Y^V?x@*1hoURbUkV;qp+k*W8=2;3;`vGS?&bEacC z_!~HuSCG+nHV-PmcpKTphaI>MRoeGS>;HB?_5XQ^S-;O-|JTj><@}mhGT6DRrrfiX zJ0N~mcYI6{te6Szkr99S-jOhw+hz>WzYMcu_P62SG9b5$=s(eIWcy z+f`1%Sw*)Rq1N*Bk0R5Yq;Kp^Lc?q^_YOKJg5gY5^1pPY|<2 zUG>E3AoApfq_8GU1NrrZLEu{-w6tx6;)-DD?$ADf=J2 zC-yQbX|D15ntXUI*V9Qg9OS%+--V$o6<~qvW0i;eT5Vf@2YN`6Qlx1wqCuu!!aK^y zmA7X!gPL-Q{w?F=1~Y?3*>4TQ80ILfGTN0KU9tFn3r7B90R+S=);=se9K^tVqEI4S z{|%_c`L(lPmSseM*ma(DBRj>I=iu&(t`={mwfYnMSdO%kNat;(+}k& z7x@&In$tTXm7IjuW7R*~1T^MP?S(bn@u}Uo!+4E89pmk-f6{RAcrY>JL~tWqExa%o z<_E%hp-o!GwoM%CEl58A1N$xJKE$4T#}C{E>2}qaO;C@=atM5iW32tp96zu_24>2d zX_o&0R+LP7_kxOQ1d?Jxf3$_|}O3V4cVcJ2!;*pEu>nis3&Po_-7XpGM-| zXy6Y}YVd*^0b6a*@R7qboGhsKVj^-jWWmM&lb)h~WF-Y&X_;SPyuq})0R3G}-h<^& z_|~iszCcKOv`AnEF4=_RxC>x2`T{%yb>9MwOnad{y%qRfDE7~JLSJ(}$;mh{Vp&2ko%q^i{iHWE z2hABcSdAe@dmcSzzR`7Jm%^+J=>UA>T6S}6D_`6oZKdCBI2h2oMV z;^b#?D1rCOM}vH%zu054zNwRB&{SXKa1T$C2|w6AqX;BrATF{r`D-{UO)6^b>78Ke z(tR+_zFME7eX{f`cUHk~%vO9(zpy)gc6YD=m!Q7`q*W(w$LhXs-rIN%Zl4C*1m|hI zoNtDq^C1})YsiY{&`Jbb%C^`rKEg4NAsiz*?3`Oy14j9FJ68b>G!tg-fLVcCi|W1c zKxMob;v9&(hhj;0c<`mEqocC|uE_UjIu@pXCJ)$xHV6;6?K{BYiWHvY6*DmU@zPN& zWIYdqF!C+Q2j?O(mcNGyecU}P?mZ=PhS;I0vyj#71C{3>euL2;0wA5?ktAps!bulF zA4$+Oiw#B9IUO$#;7d-%6(Y$O@G?E$lo!QM_QT}U-xYn&(hyBj5&lk5`&U~%(v$@C ztNH^oaWIsVPHGg!_)G7)-y8)-S);)5#weh)6-+CR1pyfgL|c8pK8KBDW>bS_+oZ&- zk8{Bq-jL^j>ndBfGxO71A+nEh7i1KLTaj)sJjO~<*yNid>v-;w2>Wq73gU67)d!6! z80=pYtSM~xRlaCYbJ;&(BR!c%FR}6T>V2`+o3Ll8F{Ob!Rwjk76~CJ*VleOs7EM1| zkeJDVDtR%A6duFXJS^of)S|Tj?mf#!EI@UFCv_`82ju~>Fi{DkJR`Zn&4nm*9(4LC z`(j&W4yC|6#X+DD8=kbmG@a02xdLfBA>vqm@;=!)W6WcO-XFB(m=2F$PO(sw@Z8(~iUd1-d_xiJf@fsU9>ds3Xd zzoKM~@U5xeg>^0}`p-8H)rSD>Y_xJlqy=0rrlzL)#&j$GJ!tBU(1f#T1buE&cIP(n zHR88&_=|u<5o|x6`yuBe*vl~Eg5Wb#D`(iY2b4d9o@?qYjL$MGBn#oawL0*6t&7%W zmj+PQmH|MAE%`Fq1$=%?eD1941s`-4yNDZO2-nMU%rQeh(F`Pu$y>-D<}cqt8NEno zt;T*B>>JS;hr_Z3z6SF)Ltql5Kifb{`zXQ!# z$B~7j(@~RY-wD}?1o=|qt;}bxye4mcMSpYle8!gl0RkD;!-lmAGC&-7;Jd9aowhr4U79M;JJspm4PJI)z|Pud`-$5S~Y20^y!)^AGqm zroNRKp%s22taLLt>Z4PXKC0+bALR@RrbfE{LujNnvv#2e_zzcw-E9;Z990gu?BSxl zMTU#l;gd55md9QOxGmJF$45k`D)}22ygB*szH7mIHf+L^`HYN@jWF5wnN|ORcdErXhy%)en!?bVp};3amHUq1wSZO0k<`__81hzsIsWus?CV zkGwC^&+d+2*d4?Ky`jT5?^;ZxI8)g;66+>yGtSYTP!p8*sI-cF^De^o;H%Tda&?)D zanw{^XkNcv)sM!tEWFnwx9_y2mtjf(2hdsu1V4)--JfI@G7R9br4o~^s`6L0y&2#f zKj3&WyIDCIfypZFj>Fug$h9^)tH>2OQuzoM#NDHViK!5XqViYEyW+(|(koTe13B_T zb@!X^U<#o^GrS9!m>3j^!oxRgH6k14?If!^vGR{9j5cxa5X^JBz+`*ph4AC)jh$$r z#dGB#%;ER2A1gOtH{>NAde4-m?yYbGcHkY2I_+XL zl*9Z9c%R;$Q_(MfB!cy-%>ua-m874xc+Uj;3mV0V45qvdf%1l;@{Mq`z=I?1n{60t z>;?MEzjQzIY*^F!$i<frp)^#uk+#Y~H}mzni+bp97m4C-nr|LERa%;|-nq=tt?y!SZY+ zRui~ac1U`doM(93)%0@sD7|r;Z)Hu-8SW#rJ-&tO%QtfKY;sWW>Bx2%TN9848yB`g z!T45sPjRiou?HzPbnH;%#?IHsKbkhp9tzNBczlamk6j<#P~=;=UTtK-Zk0KD1K@i{ zz~YI{@)r17S`jrUF{o$^ z;<4ArnsK5=oN{e7{(?k}il|W|5j7Gu#n5MXS)zVqQ)Hs1Og&YopGx&JM*WOcKM3Q7 zn+R&IS7*i~{!dQ*o_}Zk>_3ymcofG4_E}oYOHaupKW)jQ%1Nel;Y z1Y=|OPCZnCXoXyxsS)H z1W_=$84H7>vFVnR{@fQnFm?X@_;9{Q`=J8F8MWJ3v>2ql_e1!FNug3Kk2I8l5~cC_ zB&a}w6f=(2MW6)A(&LXc7YU?QjF)aCv2F`P731uH=puua8eh2nZ{Wk7eR=qRLcoX2 zA|h(BL68w%v1(ZP44m_EFfjEub~^pPMPFLuQEATjQ^se9{=esxPuBn2ZY!m+BeS%A z<+=C%RhHGij2>n5FX1OWc?0cNS@QGo`qzI3AK0|L^!NV^K9~;tSK))c{9lC+B=f%t zAM~65Dts_b{#W5s_Fsli#eW$-n6Lg@`Q1?;;SmVHVtWbK z4!|DKx|-LD(Z$_enB#Lkk2QE>+^4?_V?VuQ`)hpA{^-AcoWyQkOXKG=sD2>z`#;nA zh5DxbtbbENzfDSwfazf{U!A?2^?tNhh0f31|iUdrDn<-<~b zrj)-0<@MXE4#vi$L3GN-HD5wquq*asF#a=5_x?=`bZu|4Keoca!x!Eel|sJRp)y$1f?%(8+H0kz{mg>TJp?Oknb{8IPY9fQi%c~$HkS&MtzaR)5clHQAM zz`Ba83fbr<`^&L&uv8YqN^p7_(cQQ*TwEG=-=^3@PJl|ZYX!lmJ|8i zg17N>1#csAS@3RlhR3{!|J)}$>S^LP(++Q>Gd$7hsdJs+VLVS1sP88`eYf!ExW)QB z_WB;hT?-EMkTEW`J$HH4qL=!<#_79N-xPyhcO#cYud|)&W8TKp4NmZ+ed_zUPT#Hi zCL8T5v)A|XbB^s}-p15N;~e-c^{MaMoxWT3H5>JiZ2& z->v$(jQS?q>)ZRRWBZu5F?Ea+JgHB8|ES8L{Z@UR)0DhyMJ}s-x^sQZ+n9RT37*ua zzTe^Wo%(>AX|QV%E`M2Xhe~KDL8Py1wU5f5U;ELZxcd)EFHi+Z7t#|-d z%(b;RFZ-lkUy9h4`f%Bj;w~+Jk18zr`ETK96y}P9u%*Rah&-?&?MwC0MurknfT4)g zZhA~uMD0-#C4Yg(zeY4Ti>m+t*= z|K%U-rMz_K8~QJQT`%RO`<~o?`H{Vpm+tz`DgC$K>)igUDXtIqU;e>f%1igYq5tyN z^-^BC^U3{}AK6QJ>AvqEh^7ojeei#nbNi*cKHPu#2YV?m-SdY2%U|bE9(y8qvIz5o zcpWbByNY%sMxkX7;z~|z-<%ApaMv()(aG_m)ca51!+L%?i3Ed*8=7pK7?J+e3%sic z4?xVxMCuH!4vvXdt_nW|J8g#2QkO{UK%^)Pa( zcAEdQzu)2XUG_K9Y=af|_yHmC3Y(%cTSOlj0&ayoxRH{ZqP_bt*Fs&p9*AH%Y&&IP zoCiRT$5$UY!t>|B$d0(CFJt-|T`$v?-y=>0tTF2#iI{)xKf!U zIi-G0eyQ*GI(@gK4U(1^ycmW%BH$!vf=y1sE}KP6N9VK|Me^V$q`mt{3dUn`wKe5#W=$d#t|m$3hSBTj@L=V<{v(w zSalrR5kYt}M3HSwF~4fxP@{dv)sjz+_5HfSq@7jY)xFktBH zM;`LqcY1z(-5Yvq-*5ldUf(#YzR^Z~XXVys@NdqevP&pY$n8eqlO&RjlJjQPRU0r# zdy_mCn~&eYBipeBNLpNv;iYg4^y?RXSvLG5vf&RUd{aAr7nNiQ%s}98T%U#ipXJfQ z|F?Lw;2+cn{F|>YNECV?x8ZR%;#`*v?FxA;HHlL%j}_+QnXC)6dSIlDR$swOt4+4{ zIM=s#9UEg|-zkq)eXq!)Ro~NiwAwZbl~SHfemKMbc{cp{+3;s&!@t1}|CtjV;B)V4 z7QL#n;a6nCKRO%!!GsTbO$TVJ9p!l0E4}jSdva|Sy;jSkMX!hC(W2MAc(m$q4X8cW zWy8NB8-9H@{4?$F?<>!u7w%s({bje?u&6>2tF;;Z$h`2EA*byllHjk{E9(0V`QQ7p zr<$Qp6Lo5%F$@%Od}C0%b{YP2iUUlN7a8tu9Jd$#uc9M?YB zP&V7cyzti|r|qMSKkECr`QQ6$&qe^v@F%Z5)j!B@&$ts5pDr4w_yqi9{R0j}tbE{z zKJTRII4ioRI(&U}kH^<^JIzo8CM_Gj4@c*Fy=W&$xDparhxG6fSOD%5k#HV zr^VNewJQF7obR9Ne&HN>^g}-y^DoXd2HR8yPiJu*Dj3Zd!xg1U44(>Ss|(dW%i_k_ z^Zwz#cw0XF1_S ztLEktXpB+8IXM+L$(O8s^dW!!efLoJ^-;dx@30yEljmJ%L;PFo??e8n@|Aq%sr&Q8 z`E0H~R>|o%Pc!65`0ETm&i-rqjxPEijA{8^lz){dc!%vTJhwBvhYWasIkgx3vgBL* zJCCqPK4#xAAl+<2vgu1(d$ZtiE}#64f|uNJ4B_JZf_aWqP0NNEaF17TF@6~S8#&1i z7mm$!b0?Sj8$5|YoXzT&d6RblChp`p1TV1<&b0a`!~4x;GWZD=9q%r{HCs&Xz#0#? z4y;!`A&IQyPTq4I7UFE)Anj5wTtNqAxq@pr({sQO8v)X&_(N68XbfwkahtM3Oz3*X-C%?T*kNrgLee5TW z?R_4vZS8&k9o63RPRVaC$2YXee%?;@3iP4B%4mCA-?xaxt;(;{a7%ds*W!)935ja4 znG#NyU?-~8k-h2?vMNzss(#AUPlftXNA{}Kk-chlWUpF|>?NwJRhEhBXR`V^NBvAu zKNqQ=OV!U6>gOu;bG7=pR{dPBer{AhVf8ap{oEoybLQ{|e~CFwDuGn9m6~s*7Fel; zR_bmmb&r)=Y^CnEQcJAVQY*F0N-eiitE|*&E7fYG+U9IvN?6n|>i_sdVkGALxAI5) z`)Y>+dSL**vORE+)5Sl@4Zn0^2)%rXNgip%B(M7M^M_jWoMR9e{zeyluXnA$5N5<4 z;`~L7(c%-RU%c8^d5xgF`N7Rl8FtS)^!~WgJ7+*g&{_~Hv@PB5x1O8WhAs7EgWR_=J8Bq9Nc!a|Lx|0?DQ6Oo< ze*-RSNPcg%;J@+SJp2t0biVy9#IF;oV-QY0G=hU><^~y%{X~aZXIqg%X8TFG3bzk? zMCKDY^^MQ3?~$9xzwAN#Vxztr`mHa^9#EAj$x>}hZazG(fnRp0@D1XH5%gabIStHh zUw`#okze17GxHl5G3tvu)|WGWO}39u;_oYkg`lDwVYi?cF{||IQnQB)t$+IFv6o%RezPw|sJBhA6$3uQ1F1Ft7ZQft0uKKP#{NwF4<{;XgF5 zeA#}Lf8xZv_MbR__Dei|PM3d}SANMr%3Jh1E3f>u_VNfuqP-D@y`uJ0%*7V1f`Ulb zobd~=fM2k{*Yq8n4W2XpZa(>%YUN?EdMJ^HCFH)8>qCm&EyV#&4CxX>-PRNTO)Y_#_j)*f(H9H?|$XCl~H_Gv2oyzw1+ zH%he~Kq#)r`t*wiz_tTefh)2kz0Fu3HL7eofGD^kx2OL$XS`t3b^tMS`C<%CmfkvN zydd0m02_g~llk^J)-SDUJAf_4`{Vh3i_!451D2?_1(KWbV&;tJtAe%z7OO8W!bDED z&l%50yuDk!UCFo4sr-C}FALO{J0-uGyNJK%5%NcS0~>^3!KB9HbLJybx%ws7U(&~X z@z`tf^~%wQDm`{;nIW>d@U!$6f%oBmZ2e|_1*;EH6+Cx>svs9W#`}yv$)6Z(bJ;3? z5*3z=+oK_plCR zPB?xW{i`o~lwOc*dzv^G9%>n)lYdJyk$XbI<{3$M^4X*CorbjdF+PtaA#D;i-Q?M! zPadoU;`QSN$&ZcCv_mX>R$+%o9zL~M__T1z%n_d|1D^}C@UiUOEcqAz&{-@1Q+J)0 zzj^+l^g*h9g$BId>?4bRC${ABZzC?JHU(e+{9l+`zS&;h@?ZF^ctL)<{r54xFM&5J zTRG@(g?nt)sLU^%E2mIVVgwuJ(<i!~RO0R*&P|MOwp7eTo;i9O4SzlDRwRVJUCyihfw) zYubWz^h1Am@~9phWyTeZ433H=uTFADM>rwGvp)QEwri(e--TNz5iALM0JoFt2=N2O z8QBwe|Imm>_|$3cf~HO3<%V3`%PDS~&b37MNvH=yK+lviy?$qv%h%LGwhbQD!;d2~ z9~*X74GQ0a8yE1pGV_{c!r#+AXxij!x`0G!*ol?cf`T<}f!!6KAAclpBZ~d z5A7sViDm%zodGoo!U=kGF`6P!pS3UU{u$LZD5nA{Rv?$am3w&*$anU9g$i9`2KvCT z#vR5fEuwg7<~>lH;P%BXHL&9bzcrj}8rbPIZk!B`uJwQ=sUI7Lp5h;DBE7Lhc2RP! zUBrfAXbIyGm`E(6CE@rN<=3>2cpwcuZy%ymYNupIX>)uKpJnRg19P^|SmTHIJW~fd zeYe*0nTM0!Bz8GkyCi-NKZ#8Q&H5C)HBS2>EqKI<@b0$5JKGta!vBVRcxrueteh`~ z{|NSyh&la7weCcrERm^;YsnqZQ8tvc&=$s4N^ZxLotOL6_KN}d+7Wp$(gj%JHc&Q< zJNeByfQKlYhS%vdfXAogF?gN+9h4MWx#Mc}?g?B@uHG!>R!8JomAS6mwD0$~J%6F; z{VBGm<<_kBTm$gL^jy3)+jEG~o)3>Q+tWT+wI_);rbrm$f3NKklhfjla>dyAH2%u` zc9p??`M7*~Y__z^VxPgU=Nnb~ZoudCuN8ORJ5Rkk(rRDV0jhnTPp^HAs5qBzpQL@S z%yMks?kiMV?!afWeTNwBJL5=`e}1EVWq31C{)xTaJO3WKB)@&veggje>lVlM)u9^7 z=fn7Hw(q^O)w|1%Fxyv&OT3wDGTwYj?R)CtPq%$XJGbu|R71?4hVR;J-yuf(eo$n# zZ%UzR-?eySwa>86uAqI^P>jh6qjHDZ5iU_{Xw@Q{wY|pJJm?`b1wjKt2%p_@4D@8Q zqpN&F{G8Ge(bsTE)>6HGa|~l`Cv<2O_k}kdS_oD8s@~8pt_p0l^^R(d&T7S%zu*h5 znur|N)VcuIeS|8b9t^rLM>cCea3i4c(2Q}^)Ea(O4{ct9A+iU{rNaPO`^eYyDj*QNALA9MeO0Qq0yJ&^L1nHSl6l@CzPz{W+@bNsmh>_bLd_IDZx*dAMQmyzl zm|5z(7V3z+5}noI^4*>U`g(n<_E91vi%DAj24t3Cz&Q1YfI05D97bamaD|3C(|%lT z4;VNe(ug3Fozp6infXwc%6J7bRv{pBsB7wTi0*C6JM}p<3@tcPZ`h2;C{?)NFS?18 zX?1Hg9%*qjwA`hKmZQ8cdMV5Ueb(kjg%UlW#7Cgyb`1R5F1_BM@g_y%4fs-t?5NBa z1Fs93TGcOL>5WShAz2(-+c8KBJ(nI4{cZ=YN$k+ULHAu+A*d4!t(u0AjSRg}|D4W~ zoSXG=o>fxw7O5HLW)s4F7X%ZL)@W!I$cUvFmYcd!$T)8!%oRz+FkBOF97a7748~m$ zRTI$A?;8!Bpc<;zFW24&=@3O3c3J&4lCXF#N5$fixUM_|>w2{>`T)4hCNyk_7e}?m zt=cZF0eiDH$9DUgzRmXTMZ>YLfMSF>xAY>S*~vM7W3<*W!Sn&1&uL_^yU2Yy>LwACG~U+tUsT~GVHqNMhIYC7seUo{1BiYgo0z&;CkiY-Gn z>BxytyCC=rys#B`-YC#&bn@6y0GL%^sMKJI`dAiFjpwC9S&csvFKi7N&8%Zp*NRDr_R5y+;vmqy=khVKDSIQauw<&K)Q!LChRbag^=6zCq#KL+mOJQ$4p4-juT4+P+mswd#wYC&5)MT^O zd4#gm&xw}>q*VBux*EvXUTW)8s|1h@_QurkoJ@(-njF|M;f77mHEc+oEl9EvoOYE0 z^i_g^4RmX&7=X;d1HZ86Wxr?zC%D-3$#@yXS^`R4dtR&W)Ebk<@Ui{@S3z`E#h^rJ z8+VIpp{?N~$dL-SZ(%DIAwG}w$({p}&PC=~ros7=@yU@<6P?_(}AK`l;`+b-1 zPHvKg#v7qe@>ETNx_cMTR2q|%(m=6CzIDwt*WmJ%P>1XNxADC8!$E8Je6D;QT8Yr_ zu_J+J7+Vd9M)&PRodu}}Sq1LDl@?9AK)NnS7yAp| zk5c4J{TXTMx`v&aKdr;VIgZo!l!p!A1Ikmb+EP_~Q{S2C4$1WGMs|o@`u>c7Im;LC zKtRki_$Rbi4f*=U{jP#+nGreajJ5ijHlYE=`j3cC0?!bgP#3mXAvG&W(*p5a67VwY z#TyYG`Z=1m_+sMknAPF-HH{D{=@ik4ZL);tYkVEqhWH0#@M0x}U&-1Fmw=nuB&WISMS9;Ce*o`PR@xEA3Xq6fRvXY+pEE^~}Nj_?YwKPH4e zjNbGnxGMxpgpz=Op^EOhg4EM^uf00=yF0HR!bZ|vqfLg}taA->k+ke(+l{T`ja{n0 zz8v-_SqnFU+>vu&=?xY%xC@-#q?vBscWp{5-k4+4&qzy-927tBQwg8h(y4fX!Tk{h zEByz(q012Pn{=uXXq)sM7PM($p_ul*FUI@MA1B5pr1O!^2x(dBQb1-wrJgCq3*=3g z`6kuQXPa1Ky)t!?K>}55er+X zYpvAvMk<=PUW+tqjd$bxyqrHjdG}r*RDvVS6Rua$G<;E924A2D?uu>E8k&4fx8RA} zTT1Wa#Hooc!9@fNU#+%+a1pmpi?e!NMUM-uHp0h+T8;2=p$$g(xX?x;d|YU=5k3y{ z2o*jq^qdhs4!%+qJ`PG=g^z53-i7vrN@#SO@)FDxV&pN&%OHqX!*KT@oT4IssuB>{@fmjo!bfYr4SK8U7Yv1qkeD+nlsk)&lNs~vM_7{0*y(LKQT zakMFjQS?hdSJ{iOw9&Lkvwk2{+Sg)`{)H&i6ZW!$H7@iuNd*l?)3=0&px3s!!dFCR zE$j(@9zE?K5Hji+WS&39WlWcG5_d+{Bo4lDW=dn`1=U@qP4TA}O8va(=pzDjvDdM+!C=;?>e;ShVV#I{?gH!cLezSxC;GO8za4u13o1Bfq* zs1=f}nHmRdi+vfD7^BIq&Pd7 zjooEIVXhXMNx*rUi%6}mq%I!5|sY>vUKC1Z0$LyIv{ znG+4&gNe#wUySrd-HnTbjdOfWPXY*7gh4T~0JHH_HyUUXnHK~b^f(WN){qvUafkqR z)84oWsq|XYp0KFSengvM0jZ8g#YQ4u7S(Cxn6*5uax1I50v|@H=B7%J*0w9UKSMSd zHq}}K{Cq>UW2L|ucpXjD8y0Ah`C3DBs+dovA+n3b)kCQkTFCiTY8^Aw-)-sVA7=`{ z6j_jR6Gv0!<9TOxel66TdLZ}Jg4BU5Wz;0sgX(cn0gcT%@Ty;|H7-ca$--uSY7EL4 z_VnCN@;#&fL+pewZ)Cr0B1A2KC+dI#(fLcX##N@yKcg#O=kJCr7&`xcUsDoK)cKV^ zr_R4$mVuh7@FVl7?duol_04+CWKl%tP!ZKkfw`{NTtpRAb1A$fdd(HIerv9xuBLv5 zh$H@;UcZ1!2&P%X!g#|*QNoQ2sexe`Py<6{x7$=U^)a_^FyOGLba(Q4+}b9Zd9#kW zeMU1crDo>B9X0a{Xn<&DEc`+}= zLavaOR!U39NK3~`OUFq|tEHtA;dM|7w%w*+p<er=u zWJyN9E(f1Y{kq)LuSNsx`gM6v_;}H;^`+4RE&YmRr%T-k8fdK_!f<*U{t{$K?|}Vg z=+`1szn-1Q*02Ah)HSTi=vRcivgz05)UQj;K6b2p)95x@p08gGAigN}14>U{!k!M| zi&bXybn!K%XqB1z^&L>h&?nV+P5ru*BuSl$C)LkKB1Qdr%n?|Ft6!o=mIgx`wEAVx zqrT{3Fto7oZmn^(-naq!Rog|SI+FaRQeA}4RH_z5MX4^s7tY{BsWJ;FdyU?>+NM-n zZAz6`SA|+5U$oES@-*r_@XjrchE}_Sp?iF>>1cx3uEFV#aL&#_8i3yjK%bi*<&vQ%qWk~)!3&Kh+&?A*?c5y8QeHO8RHrA}9<5qIGJh#&iS%*e*e;&{AD@u>Yq%Zn<7pA#$X1xT~?2LpFtQbCNnRh8|i0qy@$PPW{ zhW3Nccqe{flWijwKZK^DtdBn2jeQWW!(kUW0&V5i8g=zCc09TB+-;)KymM zYAbcAmAb-8O|eoJS*giJ%JLF&W6kr4*f~~?Y9pn+3VF2Nj3y8e^r(tW<@S zDzQ?fR;tKK6E)X;KYx$ZMaq*3zpAAp{ST)nM zcFtp2<(0cgGO{83OGsl_BsBq#roCu2aUT(g3+IhRvkHJxOHT()yt&yTn;FU?`7382UoQ90ZRwc#9fQ}K)V5BjsjSGcGjyo@W zLNqyH+}IgIeJfjgN@L4$fju|)4Rfbz07?@KQ|0?xMf$hN|5**Wj!|}Xk#~KO@Lsi% z-YE7YdbJj@bLortsI6d^6H|T}Z*+jgiW?n5u$*AZ!`lK=9^Mw1^6<7y)LY0#Og`wY zRZ|oAUoiV^Vb9c8@PE98E>jHCW%1B5PFdi!h_-S=RS|||zpm%uTg^=SY9fNEBIBBh3E>^1Yd)2B()xAF_TCE>;(0bnB;($*pY%J>Av+U zK*v~_-op8JF6rc^;1@ZCetV`sF|iFw%^(-1Vx{^D_Eas- z!Agfo7~@E$x@U z}qTa2FqG{ui%a#Why=X0EsEMr#AeYPzFfplz_&tQA#c&e z*npOq{21r|c*{oBN+8c7$c$a1UbcbVFB^FZ4;oR70U;atufI;@2uW|*?120|9}pR$ z7jId`TuN5#qr#%Qi4(`&dze7f>lODiA^)}5Vy1yQ9UzOC1H^;Kv5ICC@W5toL0lom&Tb4Rte$o5*-UlmQGFU;D^v#Orh%i~P znF)gxi-i>%`Nm`g^C?y^qqd@*|AiIImcLVFjr! zp}GgIa9Wf^Eznciif$&rnRq7?Ez-=_N(lv+hI4X}pNU2Mgi4`@_(5y&@NJy` z<1KUeMh5>*g&~V%Fr*T~dk!_SXBfXFz7v!pvifouhc{o@PRs~RZ)qY3QU79Vut)Ny zjiT7gzL<|Kz^Zmb*-2bQ)oKE%eceQdG&L8TeHeFG=tCIwS3>XUs_XOe4;7#YIuQN8 zXNrgZqo-lNiC@s2`aMCdxCmu+yPm!nwiM?bu(!2EoQjCk7i&;(`eF@gia12!^xY~> zU$}nZ^o8s9DnqHGtFhhU+J!S%=&!YK`l84AKi*O#V>hQU910dO0iwbQycs`si;BME z6+}f5o4Am|O7D}W-hsd(dQ56)*@t>^ zNB<(xijD(l1wh&c&@rrGQ6*n4;wMxO!Iih>09wY6asH3Dbg?qgdMsk#8f|qN{Xb_Y zzPthS*lk3fko1;r2jnk*kKZ!_+KLHXMnK;_5aOrQVxc9VB@obJLqMBuJODYw)=`Qd z<||vE45Ss~kj4z|xa`@~*kNi4RO87^fzFMz7^Gc zCBVVTGCj^`5DEr8GiNA-cO1|l;H4xCeoN4xjHO{WSNFU`w59r%P6tAbruuU3er}?p z7S`1{5OpeqaLAPtr>yeZ8cYO8Q1gFOzgw(%i|a#b!#H zo^4}2+E{jiNgcuOn!WmohpVj{aA)@Jusz>E=^-m<}xfhdES`FS9!u9oC_$1AVcFVKKwK(U*qZNz$tNsbRMXjqfH^sxjDXTWAPI2vG#4X*2`Pe z3}`hPolMC%jM5e=ZLw0NiWV(x5u+jof=N_rqoPJZMWs5YL5)fQFO~oIS^J#1C8)Lf zUVi`b`H-18XPczoJJO!DBnfoplDzF>Vc=! zIMtM#IZd?`7>Dny)1B|>zQ2OvO;LZGSICbwC4%VJx;Rqu7ZiwL@TZvjuiSW9qLcqeMu2aHWmV! zEBOnbr1H34nVh*+FD@X(>pk3Y;KdQdjj8o8ks6`~v!@RrGqXZ<)XHYBNZO4=e6ViE zFHyBOROqmqD#c=!sCL(`!d~XTLF?T~mC6zcYthr#_=1J{>sH*O*KgcnRd&ZAjBpmN{J!4*! z% zDn3cyXO7@+a;C0O7Ke#`Q`QVe`Ap8%Cmo%&{+~`Tf=K+muj^DP8YvPK(@tL^)WUBODN>hgucRyt5 z64fD>uM0mjgCVDmd*$lLd*IaU13!kx3m;go2b`Aieb34Q)hGVq;sO>4bU68ULCdG>HK#+AtU$$o zPa(G`o^i^1ztV$uok>9#Eqigbb%xYf?csPEHAB2VALhHBfQ_iQvBf62Rxrcc1>?LEGiw`q~A*MFYfpC4=PDQ-_K zk3zRBD-^Pe79z98YSR>GEh&)np7PPuvQc{-A>bNGV#5{FspB0`!u3B$)U6@X1GX%3 zVaIDX*MF1H=gsxI5k9jUo6lU(;=b7xb$>mE+b@FR6ubD0;>FVM-AqSa5d41dkOdU0 zrs$%cCsM@6JHFh*28iF$_{a@@n{0=85XI2*piR}&9oe7g6Tk4!(*MwhKN0;&0Ptzh z|CSg2+vtDUO`k3O-G1?^E$tUSll(pD!$j=7-PDQkCFHYb|NDn;Jbs@Od0tFiT!Or% z_ZX+>yXCs&7xxZteUXInq&!|Iz@Slh#iFB?6`wDH1YOkm{F_^~{3I=5DNeR{s7-LnH?=~A>#Tk+_VLsPaw zwZ>bo2vY_H8KScEzU+dcT&z9BF4l?bQ-`nbIKnW{%Rw3pkIhq++LsCr5Q|n&u6BOEd z)A{BYV-S?At$gt%8THW7?`^jiJS+_P|hSkaOa7D~<1;jOnsfrFSE`~ZJ+SyB; zee{iiQYBD2-Nx7I=;~Byj2$u6HEY5%mND0jXnLH}SnAe7udCh@rJ~j=YD<6=h5MFsE7*$9z zObzHPDEE{rce4}6kVrRB<$l_8i+?UdFsgz4o)53Jk3GK_JY**yE?kC=B767JZl_eFl_8RpZ$1v;Z)KMrbWaWhxLoz#M2Js%DYgQJC|g^S zsv~s4a%(+w2K|f7=7q^grQiu^2{~2Se>s~+>2Ilo*hJO2iqbznTq3pnUR7$M5RMnA zHBp_jOI62|;vZu7e*6M8ELze&p^U6bOg5|~NMgs8`@dL+rBxOFugc_3i3$75ogZ`q zWVRyP2aQl-LS;C1?a4bFO>vxRQ_fk>&j||aQt+2 zBQl3(sV(dUTA68!&|^^4h+6$@VnS6o&JhjjW2&w@IibpLOtelkookJ$%4rP2qjR^= zm~qya?%)m8Y(~3LHB*ag%#f4?c)IZgOs@`#W*Rdh94`$TvnDxVgx{F->1xb8Ys`q8 z#&9Ofye%~5E)aRLZcXq8HRd8$bCQan%y5OVh*LsCHXk=c(Y%i(x|Vun;U&*6-BdxX z%@anaBJ-P%`DXJO)y*f4;+dc{*`nt6DSgaA&1W3gJmKKx6UT_n%gJA({EORl%R};3 zY}weP?D^8vvD*gz6TSKP;l3JgOaE!sdx}R&MLYc-$^RJHGg-H{}ua5n%IB*q0v14n69DU3Ld$`e`LPBQ5xOe_*4hlY8Ka~7nE=C z%fDf-=n2Z-qVlV_R#?8qFaM0ypP6DNtNerg)c<9_Jk}UpcqOQRkh*X;*9z-@ZN2OA z1gl@z!`i>2>fg7Y`tSG4C#?Oi1?699r+l=Z_NV;v7hCbvsMRKyG3z1KH2;b<#Bd(MON3my!)2WO@tuLcjkSw9~1ej+kGE0>a#mMXO`J z>UdxFJqbke+VLtE3+g!7uVaO7Wr4{#s-uN#Qme_w!QUIeGYj|BEYQ=_Q-M!q{-rP_%S-(J|HGMH5Dc7oGUc@S-zD zHP^h>9C@R;_Vs2qOHQat9y5es{?uXQR-2csnUgF^em)hN%2<`6h}`lvXjSPIjw>I! zY3U}Cm&|4c)-W9#K!kYuvgrw5l5iew=B3UDt%=w4V|z*GbUT^*$KPgit}lI z^P_72iKX)2UZ#nqb~ZU>QlTd^;5HSyj>^gbxL>7NWgPY?U@RQR`m%7`gbFl{-k1L4 zW&UCJx-NBcx@q6^vv^c^HR$}mk`u~p8&Wz4x`(Ge#5fsz4_~)LU**?e?q8oMy_yrM z(xv{<5eZ`FX4y=ij4V*&pH_>Rj`4$|-if}|dqdY-V`l!9dy$Hfk0(zCa_W5>ItC<* z>IWuItmN3Kfl$77Ex8tqK9Md*GHLbqU*f@)#E0j(Jf8ZskCC{X-Aw43L!uhz=1Bu?njaMUdfFKPgItW~mBQU*pV z*v&5#E&Qa3X+(8fIKEmlYTa6!WD}d3BWn`T)g*B>KxK&@AG8ALdxU#*^%=5FNb%*H z7MEg(X_gwBuM=-t3VDWe4FNd_VL1?CIT%41BM~uKK{zBU$BamxI9v&rzO|nHILf)S z0?ixi2wBn?5jPyyE)}0HqeyEo`jSw1>T)rY%);<>_vowgGY;;DuSRto+G2d=MbQ&* z&4#ht4G1^MO`e=ksWHFl#}iK zR~~EcCeUPk&(%nVtI~Mn%kZi90Yrd>VPwM~7Drx^zUfJB$yKZ3!X~iP8Ed$Lb$l;3Kc!s)DtR0?HITce?zT|lhgs-K(;&VtQs_RV=;W$t$} z#+XZToFyqIYWhpm;9WymGuphJ`*Li@I>)h<#<3Zc(2IKVN;O!r$=n?JdTDhp8|$KJ z2Sxe$R&*bMu(uy!esAmfEB|}d?tupa7ukQes(#yK&0>GMSSY>j6gy)$*JwM!-hSo1 z!Hfrjo6MVgZYwa_c6a4sfz?R4W7I-T=sivBubMN`i~db zZ(Hl}E!c0bz3SdrV888nFM6Xt`|WrOkf&DgN z?=9GGd;8@J?6(tDe}DGdKVEVDEwJDARQdkww?F&k3+%TIEtJo<-*#w60zcAzYq0wA z?6-4=e;oVmQ1@W2{Wek$ek}WKE58>7_S*>c;^W$HZ6Fgtrvm$J-QCpjaqYJ&{W=Ql zw|S}~u-_h8{%PB9M=+&=b2P2Ve%txZ{|D^1vp8YqGq&H}x}bmiZRbbq)&ITr+mlc8 z(obT)jR7_P)Arl^e(kq6SHh+rZNELt8(D?=c=p>rG>-q0{WeU)@!x8{-2=An$9{W> z3<3Wq?YB#1Sba+NTOEA51^ex#4sI3LZ@+$=YX$b(HC)@G{dU0c@a=G@~wUjHc--eMrOZMB%_XqY{ zY~A7H^dGr_3?Jo<4He{b^~*8H$6;HQWXbq2KgfleUek5!Dz ziSu)7r#j>#lzv0ek0dlAr%p`?>k_yYj}6J-$vM`B&Em~USd2f51-MG9!$Ou;vvDSc zkI3ro#^mKq7F+7alctZ|q}tb*FCM5f^rk0sbJYbn zVAtXo!1gv@2GvxoV$EMJv|tn4SSr*K%7bK3OLVjp74yQ)2VYT>I&3!=DnOm7Zx^f1 zJ%T#Z5PC~>n!khkqE=rjq|7{YVOJ7DTYdN}@U`xPlQmhE3LQjoZDDb%+j0f7to54e zND&a`yNbmePu-_8Wo|60pUfdF4=95U+d+sYYmUrKMLH9;b4Yd0a1e<$R|7-fDT|JU z%79Zj@ItG zNGse%#;M?~JheI}{67g=V&JCN5txpfocD#CG2dvon$Ph5q+ zXuF>^ak05Wv_38mrB>xpEQ`0$#3FEi&a$u;ZR@ON)so;znxkP;U7L?%IeSe`a}wh! z&ErPWjQB>^g%t51ndSAH4-&~*tv$KR>#S&tx%wm7q(y3v^KZ+2O?&=fHLI4y#KH6` z8xb(X|5Zwx662~Am#rBDq%*ne#ViurnCk^~E5kRAb^iHq z({6Zu3wZqv(29^;NViZV=uK&l?k{6)_sZAl){956P{<)seb$Q?_>a#sw`gWD_ZNCx zJB|8TFK+N3Sz?c5B=K*@fvs^1I|K*^()t^}}o}l`l z?Wg|Nz~7>PZHBVgGo!$cDqr7E`TPCyX{$f8p8E zz>VqtdYE^*N-2=Qt;brb(%j!;3huut-pnaaC;R5}imkg`@PnNXbp(kh3p=UKCY(>y zbE39|^At$J8EENUe$5oX(%6Lav6_~?JYXOTg4$`)G=FQPf0x8FS1pw=l0CL=Mxtg` zxal`rF(Ws$5Vh1hNNTSoQM*`L3(bIKxhB4fBQh*|_EK`}Os9wyj=#hGrpKp;n>I;> zL>7-EBUvX?T6^TC;`YeRgp?aPNielWW{_Csbd+o3B^)9bzw&dccvgGlcSY@y+lyjL z%NiSQDq_Ja99M=Nj%3jG=`#`S6z#IzsQ<3I89hW@jE+v7s=&|bM1D>x ziEp?foFe%?z=&1T^f?@G#$#Z|xrt>1C$4Yareo~;P^2hvGM%e{GBYThoT!7J)2qlo zs8{|85~2S@dJ=Qj4vB3ufauA40#SH|tq1B2u&6rO&`CDBOUW&Nxu08}G)qk-FmBxC zb0q(8-D0I7Y#OYAip-*=L>=liI;SLAGo$fRjr>~r&A^U6bx630GYq*=HKzDW8)B~v zh_w$(7TfT}mKBel7~*UidU`k$_!VE^xE8l>(863VLu`4D^ETcz3NwJsr2{m66jO{W zDUO{jm?&vKw1h!q5iZhMKQb7kL3AsB*oxIrrEp>hqFZ-FI{P0v9lLV5ghfXW9E%_{ zSw83z)S^!Z=oxNeaR9~GDRRVhoO1Bq33>}tc4u%?hZ2x|aW_6hDeXxN=xMos-wroj zD?Vm(M6@$e)8)CmNZO!h!+zn{(LLYbDb~-0;cd^ISON|CmG*(?J)MoXk}8JP&iak! zVh$k)FB)vbvS?yoGN$~^vtDYtmJWJL<826wevkVva#XjqUfRG8so8yLI|s&Czp_*;kuB%imjYN z7-1#zJFi}=_c^*C!&G92e5N{D4e@osHoh)^KR8RqsRKXB_zIZn6ov?O5Iv0ZlA;p| zd7v4gfdTB41@gaYuS&>^KtWsY)ch#=G3MXF(c(2Bm83qs7kZFV+Se)4yS@qzH|YfM zRP8!ACR8aMQ9dO7RXzCwuB(1G9@O`FT}@LTZ4S;1I8pKq6L%{^X*V4HbrznjL;e;b zZwMEhdfwN*Nj+EbjU{dZaoED#Wh%mSb!H$eRjOm24`|beF;6nm6-+IWZtr`xcn(Z= z=PPZJg0UkFmfMjA$FkAPWpaxwmrw_dtme9(&)QerciNNKAZ~X1JGQ$L*Dlsm-fR2{ z_|-4}Yn3;h@pd?V3ZoGm)ltLu(c(%ia{0KPSm}yOM#yjW;%Ai$) zgJ$uejI1+I3naqFmN7IDz)8A*`Z$=aSfp0m} z$u>4DO+S+2Etng#`w6n*_yyh#H{7R}2J_@u!Gl>P8z=@e)XJ=q{fk&SZ*F{8s?6k} zZl#p$O4ZBs81?00gFD+w)8nzU(5A;fB9y4cxIP21V5(VZQ_bn>M|P@-9%gEzek@2H zXi<#X@a|UXp4tp|yrU;z-W6hA1TQ|7vC3m!J0;22EuUi)>p5OR(^dWL=mw^$Rk4=` z#M=C+D$t((JjGs&jOaYY9*01iNSR9Q~adL)jPS{gSq78n_4lfH$9!>xkeS zvj`TQcaz>>1bCaY=j|P1;T^6QZNiK1SNe(Ep3c079t&iSNrP;DLqon$uMq<>r?82Q z>$lFt&)Ld$&VYTtE=mU6|81B%;Qr(Jt`R#%JX^~^2n;e9wVmF7(80t82V*k)h9WW) zgA%%lrHB3n-=P@qFcYZEXtqjY)B4O2R^LM^@<#J`F3^G^??-BSmJgIwTu*k14`8_X zb<~{qrLGtUkpH*HDFD$S`zWb1~L~f^C5b1&~ zdUTlK{<)r|eL)&!6x(bmwxGIs5@h{IY;c=Pe;ZuT&q#}R7-W~04vwdKH~X|sAgRs2 zjRbh_!1|f`-wxXD>TmaYg9}#wV(&y%5VU5m?4v{I_)z_d7byzI{OW);wp`FGP2e9XndjwWSww_>X)8!}(b-a<^e! z;4ynVXIIP5x5E@&n&&S|H4tCfWs>R6p(ff=Hcw`r9Wx~T0CB_G7R%A`?6Z$@E;Dvc zrP46fRmd}CyJT154yr^VapZI%8%j5*%!%??m&pBQ{>)JNl6LGXE>dgAYgZSj5KMp;ANUHEpi=IF0 zEu`lm(?61)cb0q-dY=4XA9_Z%-6B1IIoHwir+gOBGwdqLrRV$G{0sElO+M96i9g5N zT$0P5Z(jS6^jxylC!uG_@;>zZ`_@~e=aDx%dXD3>fSwPuSw*?@oUe-d@aI+QrXlGW zXN)%a;p=9fJBMP(8`B*lX*1g7&Tr&|);FiGoF)%C0#B~m^OFs&OJ5x@@DjFo$vrb~ zJ1RW5J#r%kV^yMI8o9ByPEHv?(w~`;8S;W zYPiFRBpjha70H@uvG%dC)BQoP7aeyH$@0UCjwPA?AqO_sz7$?`1QE&P&yW1I zJ#sxOOH-R`Ud65OQgiJq9g#Usxy{l?a}CuU)U4^RC((Lf*rJJxa6ZJnL0 zPwLXKsM9zH;*b%dUL#HLt36WH$`>&Oit3<H>`l7 zV4>SFAr};Ly2HTmb=PAFv66JV08=aTTC8CfC)8fYRcAoWrc;81&&rgHR4E6GZ|Y4T zG)e?(=)TXI&2b~XSD|S;k*6Oc^BH-o)aQB3%zC3fwf8gd_;h1BKo{-I4KA@juv(XJ zwbynhCUIcfRm;Wrz89#m;rgO*)65b|Fv7=ipl}PPwOW)htx*byXt$D9{gg*+L=L7X zBT~@1m5kMMkjYvnFQr>V9q?6$mAQm4}RD5GQ3 zR$?M`0%xt2%y3|wJL-skoUV5!ZH6X6KPT%!F=#k`DtD~2KA(C=S<%W3;}qk+EluTc zQLT_olUI*@C$RZ(mGOrXwF?`@aXbc;rP{VKp9C+49;}%Cf}QSNXY}V7-WbJE!LLR z^Z1qcz%F{{x;watVK5pU=?XX5*_)Dgc7XLG?=mR0bH=z!N7HD8?1-9(FFYC_8j8E^ zO4+D2`OY+yX!jojXW9c47*;#|H`)bh&a^R{W|+IXCu`VbHv~PzngXQ~?+zThz8UbR z@_{i}(SB$}X_352EJhM5zcW1QGT4M;P2wvCeL~3+x-E)u5)K5xV#qM>_C7)UEIw5t zt;;o}l?i(;V1$>s!0`Bz(21x7-HvN~8l1O{e$YQ&J7O7zlAZ8*IV0s!?KEM_yRpw&5&N0I@-wqVE@&wpe^wW-nE;Ifv=XY zl_ShlF{(K{^+J9*$I0T;hB@J;FR4b~pZk_ZZ7jQ} z*YU3F%yW;cG2mgj?*bkUJV4Wu$Cff>CZ0)80hddmErEfo_ND`6u%dIuu`e!s?LPeC zNIQ;f&}ErIog8B8?X6!~w)iixW#D8u3vS5MYbS6){?x2lf1q?`u35h|Me@x0O~y*o zMxRpdANE>1O^ZyNvkv==KD8noSB5TYar_aPr;)idx9Kre>{gfj%~n}4uUqFw0;FI1 zvK}z6w*;LnZ@EMo)qd!x(qK`{3#;*qG&bhvwxld(*6 z=-LXeR?W)x+2#puWSk&pQdeG|zr}?dqMm0&f;GuKvn(7xi@W!VAp$LN0v8+sUNC(z zSMHmR$Xw&3$Q*MesLg{JD|QHiYrJ7{?D%IHKPS&*b_n0dyscetE8?NNx4og_+1{*I z@lkGCjo-$-yq?VB!t|WVuN8kdWp^u0$P@orT+ozr#E?Wyi+47^q`$^8{yr_e?WoP9 zd}@yxQMZ6qfTlHeaRCdRKbG!mSTG-8E$RqQ>Qu18ANdUmW=<&b zisbP@-{?Neq?$i{)OUN4x3%0U*>1K5RG|tA>IB{u-p?R>&UCn!%DCkNjtb*mUiXgS z0`(`OEVr^g8C~Js!uut1FRiDVh_9l36_1L3T#N)>ep|rikRd?U`2ALLnxjW?a%@#1 zGK(k0@TbUAlO4rM+TtdnT>ZR1h1~g&C#ZWhD}ih3bIl4I{_wZ8Z>8EM-?ltG{v_3= zSr(pMDfb)TFFugx^G5nDy*~>Lu67T&gU+^5;arY=M}@P*og=+Pb9E4~x2@gF;G{{q z7ZCMn`kzPCb}pdq3}a(dePL&ORwe$7hZq8*YJ{tx%7XvWZ32zmov_L7U7aIhuuypL603uMYZROOu@ z=E}CAOo6;#DQ6_7F|u$L-|klJS$cVs zkZ<)SU>1C}``D!{|Fn7kDgLR=T=AwFJ#$FBocY@upGV~(i4Jd=8fDDtb@5))sPPF9 zzp>#ZwC))D_A)wpU%sJ#>1vVJh`Xu4oAV~&&%!Qy(c2)=4|4tN#t$}oJ95pfpXSF4 z-S0oMwLO!Gf4W7_{5_8dRo;bS(F}jcrTrsa$?5UHg1+gz@GGQIa$NPt<^QJt@57wL z)+zohY#C6lw~cjpa^#q&~VZ7fbZs!YDFxXA0|T5h+0pC_+FSP&Gy zE$%~%4o0Bh)r)@bUabX)V6g`McH`f9^<=IU`l~;#Kk(Q8k+W5+IsL;+J{y1FNjuQ& ze*J+5a_4{4AGq@0{=f_}{Pg^R2UKWSJDEXEUn6n({=f#`X(i*{O*l>e_6JJc{o5ai zS@6HXA1F`p{}q2==db@If8agLb%4=~FXi9?*Z)p`FQ@oa|V5gKkzf%tnT{*x4KKJr@$Zh(s}>w{=gSl3;P%Rfya%&6^KQz zbdCS^2jY?XpYsQPvFQK4Kk!91pZDVre3*stE&2oR@SiF02mV~o{IBo_UVi!i9e-f& zI-Jsd{ejQ0`{U#I19vALsvm#gHZNuUfv@*Cf8g1deY*a@TYlBAKk&L2x8M)7{hC9< zi|Q&nBCT2nN=8ZRu-v{i3?_vGn>q9&$Wj@E(k*ls=edvh`j8{_`)pKdTIGlL;j9fe zu_cvA49?RdewS#$`9y6h|3R{DK6^h23P?nicz`o^=5d*g?h8qQn<9vcBX+pXfjbMh zj*m#0y)8zVnIm2Kw5AO|WHU%j;4hnSif#P_@;B_N7uTl!!V|c9vFlB%x?vq%?)S#}rcRyqH+8DZ;-pswo$~gSyE)(Wzw8%ojL+At zY=H9bSO@n`mWfp7yR4OWq52t@)%qWo5KP+lbcxAU*Oir$(7>cSKg`o6j&lw-oY;g_M(rnSv~A6FQ+hpLNC<+dv}2`4vQwU$R;acA?*X9)ZiP z4<(8H65WZ*Lb>A^8Em1kq@=G%)O2cKBg@rj8@}c2^JKW);I^veZWLEIwuDbRgAVwO zPA4sEhd-Wbv3Eq@vD|RI8_OTqSZ2oHh36Q{S{uu?Zu!9GIv?a1QedU?*_p^7K@ZZ$n z`!(E|?b$6_v5QnBcS&9nO4PM-ibV67x>Qp^Dwpt*$bAxDBurUdkT@3UAXiCxOnGrp z`uguMW|t39GUU(!VvYTWShkmSZuh+Q7AVn+J3SJ|6ukhK@Re|v=(<#eMKt7B9ppUo*Y-jM`@DNG{m~&N*t|1=>vg~*K!Pe?KOX~v_wSxT7Yw!mo}ASo|dqHW>3x-A+aO8N;D zSMx;Ecx73JPn}#GYjwGD(nSn+FkFld^(fi2jZCjwkjxvIV+RmU7fE~PYSAe)(==_( za+h|;`b;YH3K2%pqwEAxW^!`eP_0B8QO#s*z8fB=mmd8EGcF{v6T^8yx;R;UkD?M1 z1HHSoh^2Uy2Z-$`@_x?3UhKUA;cJx<6F5|QD z9lqSd7hAvO(^q0JzHL=I=N*xB@n^ABt&<+xR<#rEQP-4S+1QXpdgjT^XJF#o%>ti( zT)kETWpR$l+D?C|I+Q+-MM+i;m%j`huv(~|zu>HTV%9VBYY z^0driX_<2aEi)D^b9btCWj|WxDF8@%WSwal($Wuc=Z|utnQb?EE;3I<;B1!X{QOPn zzi$2qT4p#2rvfe0A}#Z4EMsYzwSBeB+KiT&o2Y61I9g`0cKnkPCtu49o0_X-7H750 zv=7xXhvsORQKn_q(kHacT&HCge{3ytWTmtWvvO;oWuSo~t4+)NMKfoPmSILDZGnR? zC1UOZE%TQ^%XB8Tpk;n}TCSE6A42zhqPehcb+23un_-I=~wyHNV3C12lMcPjek4qxAVVU6_7a*K8#vzH5v z38mkAAL(yPpFoN}>6;b*)koi)c>Ks!v2`OHfOwA6HKHZR(O{02BK_MFsoGwK)J<$? znQ~Jpa8nU2)4XbmM%v|Z(=NTq+VzP+iQdGCA@oY>EROQZQz`*oIbGHRnPkW+L+Ug^ zY@o^u>|8?lw0Ws+MUYVD;gfRI$^H(sa3096ikGA_@N>mg>$DY!Q$g6+MAt9V0ddOd zmO5rQEexe%bJSjrl<>DPqCv{JQUo>ajE3)zgpHQLvMp6y9op4+MOhICmqjm1Y>KTp zGPY?fTN8LwY*Pu_F!V})`d$~l?nWK}QZ+WbKttAbjI+>ZjyCnfUra0am8ut{gX_AR zj}5W&@k`m+oBhe5C--z!I(cv%iM4UU$2yu1q}EVqE-yF5Fu`Gs3A)Om2^p-hKP&Qy zXS~q@piabm`C3OoXt)>XvU? z*UHgNTxZnkcPByH(l@q`y~?Jibpw)z-dB=1~3xsJa?n;=hWN&u-j2=|&y}x+g~`)O6K}?r4o^gvv)xXnRb+)PZJ1Y1 zS_rKT&s1z}NqFY6-MhkzS_!I*EQXYkg*Z@}=ShTWS2(f6#L9@H;z!*f-F7IC24cnD z`J#zfa!^6zWlHC>28K&U7i){x28n0G!lLlBVSEc;y9<9eV$dRvQB+Mu@_5kpI`sFI z=kQC?+A-cl6`}pa(klF_ZErpUjc05s62^>fTo^qs#-Ek(ozCH+mxa1dD zU=<;_u{O=xrWT|U#5r3sKXxak5|tn%O=Njdc;^6 z_O@tt%}%3}veW3bl2c8ialOT#Mtj58;pTSJ=*vh{Z_YI8Vti4T{ejb$HH}7AYZ@(P z5>0ga8$&absDHk!B_8Z6G}P)Orq3jbH&2k}jWUy%1N%&(z1;rTljv;|td=iROM%eKV6@N{3xA+$gXzzYq=5x3K`M;*~p(K+gBPy4#!Cjjv(XaV4s?VyP=Zlmk8^kt6s6$ zw>Bf?SANfc@~0x<0d5oHA^e$lTroR;+Wn^lwP?Hv>WSHD*Dy2V%KVx$;mUWW)7^X= zu6zmde;p>~Q!YmFpjZ*I-9dU0=3LFR&i>r^jQ2Bsgq!|q;^WXKz0idFIF}0W^$o8` zkUm>{ow!B%A&aj~KkN_3dB0^6+}Q4>*RQL+4V5}wSW-^*HR0>puBESUAA1FSEd}w% zJOzQHH-oPO1@VSIt)BImxMrm@R##?>)ni^a1#xSaQxLnI8BD7gl>M*EqcYhiBOfl0 zSFO|9ms1lAJ&88(nWKDa@j$OgkfIxZdi)ajBmmK0IYI(jNmdJG)`-r}J+$(DZNWVN zne2!(33Ddyx|}DkYG17H(Z=M3rLk9zjD0XR5nWJUPI&YOC2SqzD8ks9cVn$(iAa-Z z9@A#*hJ}19#$%d{E@Z^a8_fKtD47;Zk;<;KrrT}jASL6{nwBY3+A~Uq)MT`!J(2{& zP2<&ETJBsW4NYYok?A{fiA{NrNGTJe<-mv^V0DnQg%k_+l|>L1F;^za5{HMpUAZJa*o5%K-R4EN zcfOZ=6WYXD+n78x#0iv5%9@pEqv3rx#SMXZGfDYZMH)xF#|Eof=~V4B=$gjrQ_8|k z%5&G)kSxLwSD|QQ^%*7grPXJYVt<9>4dUOL1r1XNVmkzu^#df$$55^+XikoY$PjFapbfve8Yp;z%yoVn?8%j>%t8~-%my+2a zlz6l0A!J{hEy=>0vpmk3<0kj47%WbVN=yu0wOpDpz}b_Fs-Fl?RW=jv+x&q(S{;*M zGWQ*;mFqq9W^yq58Bt7&FlL?F27=_o+abOUSchAF_U`gWqpqLPNcoJ#h=k+kQZwUn z5N^j98Y~lQ%$rGGJ4*LR;d zX}>R-B&U0FQyGu>fMvD`$5|lV?41Coxksd*V&B$NMk%&Ez6h;HI+f`?DY{1&c0?Ac zQ{H8pg2%$K(V1t+#(HWFb&uhDhdWP&nbTaR56iWf5_tG5tiE}H)%T~_G!Hm3)7~VJ z6gV~C>B-~3cIUFW79y0?*GO-OL2W(OyMkUaaNfI6Sow9DIw|ODDVFiwO*FP5{h*E=Kf~6IjS2S?qm|eq~-3Y4sM-;mq4+@xw2tJ<55V z?eq?&7J&$F_iPKH!b14RyIA6prHy9}oS;wb)4fY|Kj-()B!4--?=JZLVzzvh>X$s` z)UWKD^K;2Ydfx4*LJGL39fH@*vm9=A#(XgK zpg8%E%(Gdk#Yl6nIAi|Y^>p}SuUb2M`l@vT*Zt%7WdC!{-=7XI23aucUsU?H? zrAc7pks!(o1x2mN0MhD-fJ$4{tC{W=!9%>=ToDg(x`L3dB4-k9!|LWPeQ!YQJ%Rlm z4iZ>dP1g0PwU8_vJD9%#*l)RyCU36K;K_&kfc^FyuwSx7rALzQrFMB9o&-fU)d3m| z?f=QmMos!;fC|z6@d4Ve5Xx}r#kX^S@K+D~k7cz*&NBd4OM zunS)`qV?Nstm}%^t}NQfdR3%f{4dN(;_OJH`c_{GX2$#k^c&qT*E?$)$;>h%a*Zks zH;vR=Y^HL@Cv>arHgF8t=pz{*CgBPy+uW7S|DW=-MuIo?cU#4@(yNur>jnxxiDI7zA zT{#Y0QueyDB^9vjRE;dk3rj$r7nX+O=cs+ooGgRvplq3vIj@yD=?i~`KM&%ed>lGP z)%ceFw!ZM&co)e3Vi;8Cd?EkQ{d8W!zWs2~2EWmeARPZ3lo`3oI~_0tKOxJHn(F4B zcet))4W>#QfZhvW3k5Ow2Gb(pV>r51ph+eyA`*b6<#e>jyNC9?W>hWQbB@^vZB5} zzov_Kq8GUITJaw{24Q9rHL%DWPSwFx$w=eqLzskg&~=`Uiw}mr-afkQE193l6T z&AxLXznp1tEKe}r93Y)B8N^TCR%(5Ad`-tPzKb=!FjxF$p7TMmBg;>@AdOYRgXC{{ zn>Q3#6qn#GO`7lEGqotbGhJlDq7ZO0BR^wN3_1XdqFX@Or_nE=baq^N^Ty>RewpR5 zU0`{5Cvz!xL=NY74v|h*Z53{Mh`$J%O{jHqz9D@i*U!FCPTD>u% zfqVW8o;UNME7t^R_r5MB%RXgi%4fO~S?KJubs|l+kdEV6s^@|(w|oDY{aH2LnW>qZ z*dMv{!5%r8i7>amHgATm1W#aIEYK4L2Esu^tfU(ccLu_&dl;U+cE(jt37@5(MRCi{ zILNWIiQnafw=X1QujiN9c-T+Ro_JPidHzXz!ax6vcs0&H)8S{f7|^Nn z&uY433a+0KSQ5L8^eqX?v0c~dAAz=?qErqPJmCvT1< zv8U_5s8#YAt;EjctOU%}O2AUH59Dc;V6_Q6IAPm8p95do&CN$?_dxV=ZBlJl+ zypG5m3S&Za`6k5gm^9_oqxf7*hz0&aKy)q(5*%<6B>p!0;U(1a$j4hy05Hw$jnTP2 z><4X4565Sye;jK8=D(Xtadhw=9W*j4K>BkG>ECU$4O=Vc7|g$oVmd1Uh;afFjSur* zI?ciSq$JVe>=wc(e_oQ=T{fNv=4h^mcTs`_u;= zkJg=s@5u+)bUC#ewEVp&hw9ZQp-w)&4~}`O&*1xSXhv}Oez?9`7S8z@Z~nSqmJE(V zo8^Q1zdUK+p13H1dz0_RhPyG-4B|%S3*WQ9jtar^s}p733^jjK{U1cq&h5;1vIuhjds@Sh)xg7a-avPp@$Sdjn_e)Dk>PcfpPTCLqi7mgaGS@ z!@rNL-uQTRSNQs6#hoR!cgG_0IlpJMt$(^?q(72QbSYS6d&bY@w~d!&1}^ZQ`-#)o zrPUpd|5Rfl{Jg^Ob8PwLi2ytc59A!9w>t^MyjGh7GFhqb($kV5!2@_R{ypydv!4Z$ zc*k==)KHePepuore+c0N@}E)$C|b@GB3Tc|kD~e<-e5lS4x>!L@m$RvG;9zLai1N2 z@|%w^PRl(+iTwXZmM6hr1V7@Q}k`VaKP49RIs&0oD9l4GF>z4DT_ziTxSC_OD z%;p}MqalQT3bf6G${BpQnF-s+myS}eb0LE-f8paxKcC@U+*{%_f;XS<0ggFV#f&-O zptz2V5!N%Y^A_+6|3K#bd(u*kRpwY9Ie+uU>Od|aYR+#y`dEaoPO~|APbIMk-!c4@ zJ?-nVjNg11&B&LUt<^ARPp=3!{mR5MBQt>|eK(g)k)7v6CcYJ2%Pwczx)4h3;p`71;DfsPW%bUOa5&h0-Oq=(|T#Au-a%XCgb^F+z z(7A50bC)m9xXbT-gjuvLy#sdzW0MEUUGC1Pa0fa0=MG)O4OMe4p9SQ7wa)M@+cz^A zJy+gr?s69|ezM?X-FgL(;4-f>mpLaRT~eyR$-82mC5dswz75vaO$9P3$PgH(^LRDK zWpWaQsXRZ%=D3Ltk6)vPfyd|P;_+yAb+kDg`x^RNnnHMd`4VZoEb`MSGWm1ZJ^kYG zm3eqv$nSh=wFyYDK)O|*(2PD2`Imv?Cj{=tLEQ%$Q5S;~$uRQJPbCNU1rs`}dL)fX`ikLr8PjOq)4;#$yfx!pw`oi;m3 zID8T~+zvFYX%P-rTuVn}mc!whm)L0d(`|M<7Q(Ih`!pc>q;JN*-Vx|iT|Cu@;<&(0pn0B993FKeSL9trx#@r(!{6OG`1^OU z$Q-r3;c;3uHHbPlkkb3)%etPTTRc=&|!|`$o0KSJYA4#Y= z4S>3HSiz71@haJqwTL{~PHoyvQ&`;`;qJZE9>?;1{j@H1JX6rqti|SYeIf3igAf;a zwN&irYXGvYMSxrGO?XK9kk$dX=kTq_)4H_qKc_1B$<)k>w|JFSTWHbAyFTDDZxhp4fm-mQ5g^NJsYobt_nJ{DD7xHCn8QdDkm?BGb7X5N-MmADqDDTggj;%5+Vl@3om3-OI+TcR!Gb{BR zzS#LhL+DrBH6Hqfo8A*SqRUx*3&-EcA~;+$UYUw2mwm!kBXre#FO4$bd50+zLvEn`4h0z z@1ID_;JxMQ5BTyV(Tf>5bB5;%?=d(cm$g?@g^Kv+Lm1wMr7}w{!>J;VrFK!B84PNG z+W%SQ?FN~f>t0|MM2ei@d2(OC{p9fuaQ`PA$pPF;eu20jqcH?<)6Wlb0XJayyK1R> zag`V0hhOexmCNm9mvVh z`x@X*v}aF>a|eAEb3@gpH+N{F!iT`Ut4qh$oX|Udp9eSeizH* z8yjYyorv5CF7Pqzf^htD!?1WUg<{-j;=ntI(ldL?#SwJOb1N!Dg&~eOQcHbzaA*h7yAHN zJ5a~G9dAdszfYw{2L3E?qN$D`T3r6+44&nzUxQEjgoghVo}Ig8JUhe3vy&d^56|9# z$uNr-`iD!Ae}u19;k#>BpPhqfha$6uh_+VX15dF-w3k3`AzIcSK;pS$&LHF<+HSp| zc7`F^75ZK?%Mk7HkhrnoW@bdq!Hjnn(f(PCShHA&_BY(?h|G3~HuDl8S{z8`Q>ouO z7??lsSpJ-?vn&lAXy+%0c0gZ;zYGj>_}eik19+Wg6MfP(2WUs3uXIsikN!+f{5&Wh zKs%UTxS=cZE}&fa?wZ-*-*++tvjeg_eD`D3%fdId7C%!GxkIPyoz3yA-mR3gp2q-R??+C~k75N&54L`(jFES`Pr4;eh0@loEQXPMhF{^T4y>n1DVCw2CN zemd*$?9J+MIIjJ+;MrZ!_Y(4O{9x+NVFa6b3-RoMYLiYMOV-U5o*k<8d?cRTil-c& z4YrgmripUrvB$O+V5)@T@C)5B)Sen>p{$g<2^7db#1*a%NB;&l;Er zrt|kT3Hw0mBIz7q1*a5slA167o?OaVJo^I`4DhVNZvs5~0N>@G@}1ZLp3OLs`*rx| zOPIs6PQn5_+iA`Cn0U4`z_aokKxfwNPiBVbuwRoXZI6j1C*&S9mBK1yz8CP7tRhk!ok_I=tmBm zedAiB{a6hlaF%Yqkq2j2cxS7j!HX0O<(FG&a5krpE4`zsMH8~O3c6(QDBv#p z=KGI{|M+z9Y~~ax#k%L?+0G1}ojwcBs$1yr?7|G5y%aD4o_!qDAv}BJSGRy?M{+}W zb|{|(4&+-7>w{;1#=?Tj|Mw~TV^96m@N8~;L=xo9<_OQW z8=h5sM6YK5EyYLNEU;isEKhSjNLHULy|qVf5#o@_$i_(A$|bo)d1MZFc76~eF((n7 zk1`JMti?$Do;j_t;g;erJv8$1t2~}*OmuFH&LIkd42IeVxr&3T6On0tyhM$~OZ=&!d~a)$i`+`4!P-kTRM@yBm|M7+ebz(4z^e&Z!x?E`E7%9W23 zFOgzyGykmIkJ4TG{IGb5!!;SYc!>k`)f}|*wg_k=GyHgo8<9K$DD&eb{sf+N{@Ho* z&tAa0p+1mvb^U;i&)S-XryvUF&(%QhWYC5&*0kbBPs|418I@U!oozwTx2TqI(`7vB z+ZkXU6{(#;=!=Cv%g2+mT)ST|_5HdJDSw$EWpqV!TOYZ){fe16H(0eDDb7^tG96oGLUDnMfB!kA>>3+xWdst`my2XC<+od(mwz_# zR@38K@Xy{Pl9I-QW#(#*+|2#9kcJ!6Zjs0j%e*AC&Cu$VbTnpMLS4FEl#r=W@a^i4|rUuK6#l*9%=>1TS>(Hjqs@Y@s*E zST5O~YjT1lMybtND`6MEcNr_;4j#xG)!AH-oLBRp_W%_agiahfmv($MC^!>3F@miU zNrg@npy2MULBZw1aXwKjr(T9AeMbSJl++U~JP)*dYq56$1CvXC3!=#q=NM`1AYS56 zFhw3=ZsJ0KIfbvoqHN&)Bp!AgEFR|l64)2Z^qek9hu{|Vcw;3bI+lOny zu{88AS-e~2J<0XVi`qO{LpiurK@z9XFaOD#c#<|0Pxlt*;$86UL@`rNyoALA2kpm2 zT$+Dh_QBJ-6clOquFU=xdNEj-;j8&S`BAHmHx=}J%?@e|Ya1m_m#I%q9E z_{_l$&|Z>aK>7w&eCKvhLRZJ1%wTlC9@eLs+^2qY!VT3v?_g%#U z1&i)LXEiq5SCkxBe_ZnL^5jV+$*~pHUF5vH{J8L_nsV7puZV|8Hnhe!vxq_B@gVJL)2+tb&m|ml z*M7;*WE13jx?Fzynlke4D|b=t;0+T>gZab{aRi1K0U~H>Dydtp1g7l4B)MYTVlX0e z6arK`SMfb{^Kqy_=1PSF)!_G@Ll-JYM5Fz+%+cH;|Cvr(z*K>{+P`6Wis)q-MN8BB z>Fi)mz*f$zE+*;36EI+GQ*rpmm6~iK3W+eUV^`vgP-2kMtp#zc1RweQ7h481E==z< z02`0At{urk`sA6PVzqT8cOfTaoxd+@yI6yKP0OXYiI8aUmsS1KLS@P+fKkh;O{IL% zyrxg)NBp^p16jhv@PN*tPe!}$Rrff|(LA)zhMSa%p|N2}QT>kW1FOEGGW_FdYQluU z)#WYhbzIWe@PO_`TB)LurLpk*@ZRqD`C(+%eX)V8_@}U2Pv*MuBpD?HF6(J_fsw zO7}6ueN^eAy>SQ)f>N!lWGkVo^!nbX(Lc^DkRnB=unLVn=`KQ_Y|>=k>X{e4)naEy zwhE7YUTuzc`QD={?{=HdyE5z7?cQy6C1*vu(>qfS)np5`d7?$O8r+1-J;ypZ&#my3 zOhR<7bkH z?fPMn((10s0}@^KF4f@QuOa`Eu}1a7Q?^fzTb~MDUELY}sal^YYA>v9>AiX9lEhzw ztM2)ZqW8I~YuWbvEA6HKwAT8sfeIJC&+cyu=+lRuC0Rmc+5=r&ef_n+4sXlS^lpCp zRiAlZZY&(VfL_RfR01{n{daPa`OT2{qLL`9Vx5-2FV#vnbnbUQZ&{dl6X6?_c(LMPWK3HuC2h>9T#g0Ro9h9Urk0! z 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

    1. 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 z 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 F`w2, - "specifics" : , - "cutoff" : , - "runlength" : , - "seed" : - } - config: a mapping from parameter name to parameter value - Returns: - A command call list to execute the target algorithm. - """ - solver_binary = "examples/commandline/spear_qcp/target_algorithm/spear-python/Spear-32_1.2.1" - cmd = "%s --seed %d --model-stdout --dimacs %s" % ( - solver_binary, - runargs["seed"], - runargs["instance"], - ) - for name, value in config.items(): - cmd += " -%s %s" % (name, value) - - return cmd diff --git a/examples/commandline/spear_qcp_roar.sh b/examples/commandline/spear_qcp_roar.sh deleted file mode 100644 index 6047ef967..000000000 --- a/examples/commandline/spear_qcp_roar.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -# An example showing how to use commandline to optimization with ROAR facade -python ./scripts/smac.py --scenario examples/commandline/spear_qcp/scenario.txt --verbose DEBUG --mode ROAR diff --git a/examples/commandline/spear_qcp_smac.sh b/examples/commandline/spear_qcp_smac.sh deleted file mode 100755 index b0054760c..000000000 --- a/examples/commandline/spear_qcp_smac.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -# An example showing how to use commandline to optimization with SMAC4AC facade -python ./scripts/smac.py --scenario examples/commandline/spear_qcp/scenario.txt --verbose DEBUG --mode SMAC4AC diff --git a/examples/python/README.rst b/examples/python/README.rst deleted file mode 100644 index 6d742b6a3..000000000 --- a/examples/python/README.rst +++ /dev/null @@ -1,2 +0,0 @@ -Python -~~~~~~ \ No newline at end of file diff --git a/examples/python/plot_gb_non_deterministic.py b/examples/python/plot_gb_non_deterministic.py deleted file mode 100644 index 2cc1beb4a..000000000 --- a/examples/python/plot_gb_non_deterministic.py +++ /dev/null @@ -1,130 +0,0 @@ -""" -Non-Deterministic Gradient-Boosting -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -We optimize a GradientBoosting on an artificially created binary classification dataset. -The results are not deterministic so we need to evaluate each configuration -multiple times. To ensure fair comparison, SMAC will only sample from a fixed set of random seeds and apply them to -control the randomness of the function to be evaluated. - -To evaluate undeterministic functions, we need to set "deterministic" as "false". -Additional to the configuration, the function should make use of the seed parameter as well. -""" - -import logging - -logging.basicConfig(level=logging.INFO) - -import numpy as np -from ConfigSpace.hyperparameters import ( - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) -from sklearn.datasets import make_hastie_10_2 -from sklearn.ensemble import GradientBoostingClassifier -from sklearn.model_selection import KFold, cross_val_score - -from smac.configspace import ConfigurationSpace -from smac.facade.smac_hpo_facade import SMAC4HPO -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -# load data and split it into training and test dataset -X, y = make_hastie_10_2(random_state=0) -X_train, X_test = X[:8400], X[8400:] -y_train, y_test = y[:8400], y[8400:] - - -# Gradient Boosting scored with cross validation -def xgboost_from_cfg(cfg, seed=0): - # use random seed to control the randomness of the model and cross validator - clf = GradientBoostingClassifier(**cfg, random_state=seed).fit(X_train, y_train) - cv = KFold(n_splits=5, shuffle=True, random_state=seed) - scores = cross_val_score(clf, X_train, y_train, cv=cv) - - return 1 - np.mean(scores) - - -def eval_undeterministic_model(cfg, seeds): - # Evaluate an undeterminstic model with the given configuration and a seed pool - cfg_cv_scores = [0.0] * len(run_seeds) - cfg_test_scores = [0.0] * len(run_seeds) - for i, seed in enumerate(seeds): - cfg_cv_scores[i] = xgboost_from_cfg(cfg, seed=seed) - clf = GradientBoostingClassifier(**cfg, random_state=seed).fit(X_train, y_train) - cfg_test_scores[i] = 1 - clf.score(X_test, y_test) - return cfg_cv_scores, cfg_test_scores - - -if __name__ == "__main__": - # creating a Configuration Space with every parameter over which SMAC is going to optimize - cs = ConfigurationSpace() - - max_depth = UniformIntegerHyperparameter("max_depth", 1, 10, default_value=3) - cs.add_hyperparameter(max_depth) - - learning_rate = UniformFloatHyperparameter("learning_rate", 0.01, 1.0, default_value=1.0, log=True) - cs.add_hyperparameter(learning_rate) - - min_samples_split = UniformFloatHyperparameter("min_samples_split", 0.01, 1.0, default_value=0.1, log=True) - max_features = UniformIntegerHyperparameter("max_features", 2, 10, default_value=4) - cs.add_hyperparameters([min_samples_split, max_features]) - - subsample = UniformFloatHyperparameter("subsample", 0.5, 1, default_value=0.8) - cs.add_hyperparameter(subsample) - - cfg = cs.get_default_configuration() - clf = GradientBoostingClassifier(**cfg, random_state=0).fit(X_train, y_train) - def_test_score = 1 - clf.score(X_test, y_test) - - print("Default cross validation score: %.2f" % (xgboost_from_cfg(cfg))) - print("Default test score: %.2f" % def_test_score) - - # scenario object - scenario = Scenario( - { - "run_obj": "quality", - "runcount-limit": 100, - "cs": cs, - # the evaluations are not deterministic, we need to repeat each - # configuration several times and take the mean value of these repetitions - "deterministic": "false", - "wallclock_limit": 120, - "maxR": 3, # Each configuration will be evaluated maximal 3 times with various seeds - "minR": 1, # Each configuration will be repeated at least 1 time with different seeds - } - ) - - intensifier_kwargs = { - "maxR": 3, # Each configuration will be evaluated maximal 3 times with various seeds - "minR": 1, # Each configuration will be repeated at least 1 time with different seeds - } - - smac = SMAC4HPO( - scenario=scenario, - rng=np.random.RandomState(0), - intensifier_kwargs=intensifier_kwargs, - tae_runner=xgboost_from_cfg, - ) - - incumbent = smac.optimize() - - # get all the seeds applied to incumbent - run_seeds = [] - for inst_seed_budget in smac.get_runhistory().get_runs_for_config(incumbent, only_max_observed_budget=True): - run_seeds.append(inst_seed_budget.seed) - - cfg_default = cs.get_default_configuration() - - cfg_default_cv_scores, cfg_default_test_scores = eval_undeterministic_model(cfg_default, seeds=run_seeds) - - print("Default cross validation score: %.2f" % (np.mean(cfg_default_cv_scores))) - print("Default test score: %.2f" % np.mean(cfg_default_test_scores)) - - # the optimization process is called - cfg_inc_cv_scores, cfg_inc_test_scores = eval_undeterministic_model(cfg_default, seeds=run_seeds) - # a classifier is trained with the hyperparameters returned from the optimizer - print("Score on test set: %.2f" % np.mean(cfg_inc_test_scores)) diff --git a/examples/python/plot_mlp_mf.py b/examples/python/plot_mlp_mf.py deleted file mode 100644 index 676d475c4..000000000 --- a/examples/python/plot_mlp_mf.py +++ /dev/null @@ -1,182 +0,0 @@ -""" -MLP with Multi-Fidelity -^^^^^^^^^^^^^^^^^^^^^^^ - -Example for optimizing a Multi-Layer Perceptron (MLP) using multiple budgets. -Since we want to take advantage of Multi-Fidelity, the SMAC4MF facade is a good choice. By default, -SMAC4MF internally runs with `hyperband `_, which is a combination of an -aggressive racing mechanism and successive halving. - -MLP is a deep neural network, and therefore, we choose epochs as fidelity type. The digits dataset -is chosen to optimize the average accuracy on 5-fold cross validation. - -.. note:: - - This example uses the ``SMAC4MF`` facade, which is the closest implementation to - `BOHB `_. -""" - -import logging - -logging.basicConfig(level=logging.INFO) - -import warnings - -import ConfigSpace as CS -import numpy as np -from ConfigSpace.hyperparameters import ( - CategoricalHyperparameter, - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) -from sklearn.datasets import load_digits -from sklearn.exceptions import ConvergenceWarning -from sklearn.model_selection import StratifiedKFold, cross_val_score -from sklearn.neural_network import MLPClassifier - -from smac.configspace import ConfigurationSpace -from smac.facade.smac_mf_facade import SMAC4MF -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -digits = load_digits() - - -# Target Algorithm -def mlp_from_cfg(cfg, seed, budget): - """ - Creates a MLP classifier from sklearn and fits the given data on it. - - Parameters - ---------- - cfg: Configuration - configuration chosen by smac - seed: int or RandomState - used to initialize the rf's random generator - budget: float - used to set max iterations for the MLP - - Returns - ------- - float - """ - - # For deactivated parameters, the configuration stores None-values. - # This is not accepted by the MLP, so we replace them with placeholder values. - lr = cfg["learning_rate"] if cfg["learning_rate"] else "constant" - lr_init = cfg["learning_rate_init"] if cfg["learning_rate_init"] else 0.001 - batch_size = cfg["batch_size"] if cfg["batch_size"] else 200 - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=ConvergenceWarning) - - mlp = MLPClassifier( - hidden_layer_sizes=[cfg["n_neurons"]] * cfg["n_layer"], - solver=cfg["solver"], - batch_size=batch_size, - activation=cfg["activation"], - learning_rate=lr, - learning_rate_init=lr_init, - max_iter=int(np.ceil(budget)), - random_state=seed, - ) - - # returns the cross validation accuracy - cv = StratifiedKFold(n_splits=5, random_state=seed, shuffle=True) # to make CV splits consistent - score = cross_val_score(mlp, digits.data, digits.target, cv=cv, error_score="raise") - - return 1 - np.mean(score) - - -if __name__ == "__main__": - # Build Configuration Space which defines all parameters and their ranges. - # To illustrate different parameter types, - # we use continuous, integer and categorical parameters. - cs = ConfigurationSpace() - - n_layer = UniformIntegerHyperparameter("n_layer", 1, 5, default_value=1) - n_neurons = UniformIntegerHyperparameter("n_neurons", 8, 1024, log=True, default_value=10) - activation = CategoricalHyperparameter("activation", ["logistic", "tanh", "relu"], default_value="tanh") - solver = CategoricalHyperparameter("solver", ["lbfgs", "sgd", "adam"], default_value="adam") - batch_size = UniformIntegerHyperparameter("batch_size", 30, 300, default_value=200) - learning_rate = CategoricalHyperparameter( - "learning_rate", - ["constant", "invscaling", "adaptive"], - default_value="constant", - ) - learning_rate_init = UniformFloatHyperparameter("learning_rate_init", 0.0001, 1.0, default_value=0.001, log=True) - - # Add all hyperparameters at once: - cs.add_hyperparameters( - [ - n_layer, - n_neurons, - activation, - solver, - batch_size, - learning_rate, - learning_rate_init, - ] - ) - - # Adding conditions to restrict the hyperparameter space - # Since learning rate is used when solver is 'sgd' - use_lr = CS.conditions.EqualsCondition(child=learning_rate, parent=solver, value="sgd") - # Since learning rate initialization will only be accounted for when using 'sgd' or 'adam' - use_lr_init = CS.conditions.InCondition(child=learning_rate_init, parent=solver, values=["sgd", "adam"]) - # Since batch size will not be considered when optimizer is 'lbfgs' - use_batch_size = CS.conditions.InCondition(child=batch_size, parent=solver, values=["sgd", "adam"]) - - # We can also add multiple conditions on hyperparameters at once: - cs.add_conditions([use_lr, use_batch_size, use_lr_init]) - - # SMAC scenario object - scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternative to runtime) - "wallclock-limit": 100, # max duration to run the optimization (in seconds) - "cs": cs, # configuration space - "deterministic": True, - # Uses pynisher to limit memory and runtime - # Alternatively, you can also disable this. - # Then you should handle runtime and memory yourself in the TA - "limit_resources": False, - "cutoff": 30, # runtime limit for target algorithm - "memory_limit": 3072, # adapt this to reasonable value for your hardware - } - ) - - # Max budget for hyperband can be anything. Here, we set it to maximum no. of epochs to train the MLP for - max_epochs = 50 - - # Intensifier parameters - intensifier_kwargs = {"initial_budget": 5, "max_budget": max_epochs, "eta": 3} - - # To optimize, we pass the function to the SMAC-object - smac = SMAC4MF( - scenario=scenario, - rng=np.random.RandomState(42), - tae_runner=mlp_from_cfg, - intensifier_kwargs=intensifier_kwargs, - ) - - tae = smac.get_tae_runner() - - # Example call of the function with default values - # It returns: Status, Cost, Runtime, Additional Infos - def_value = tae.run(config=cs.get_default_configuration(), budget=max_epochs, seed=0)[1] - - print("Value for default configuration: %.4f" % def_value) - - # Start optimization - try: - incumbent = smac.optimize() - finally: - incumbent = smac.solver.incumbent - - inc_value = tae.run(config=incumbent, budget=max_epochs, seed=0)[1] - - print("Optimized Value: %.4f" % inc_value) diff --git a/examples/python/plot_scalarized_multi_objective.py b/examples/python/plot_scalarized_multi_objective.py deleted file mode 100644 index c971e957c..000000000 --- a/examples/python/plot_scalarized_multi_objective.py +++ /dev/null @@ -1,204 +0,0 @@ -""" -Scalarized Multi-Objective Using ParEGO -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This example builds on :ref:`SVM with Cross-Validation`. - -Optimize both the final performance and the time used for training. -""" - -import logging - -from smac.multi_objective.parego import ParEGO - -logging.basicConfig(level=logging.INFO) - -import time - -import matplotlib.pyplot as plt -import numpy as np -from ConfigSpace.conditions import InCondition -from ConfigSpace.hyperparameters import ( - CategoricalHyperparameter, - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) -from sklearn import datasets, svm -from sklearn.model_selection import cross_val_score - -from smac.configspace import ConfigurationSpace -from smac.facade.smac_hpo_facade import SMAC4HPO -from smac.scenario.scenario import Scenario -from smac.utils.constants import MAXINT - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - -# We load the iris-dataset (a widely used benchmark) -iris = datasets.load_iris() - - -def is_pareto_efficient_simple(costs): - """ - Plot the Pareto Front in our 2d example. - - source from: https://stackoverflow.com/a/40239615 - Find the pareto-efficient points - :param costs: An (n_points, n_costs) array - :return: A (n_points, ) boolean array, indicating whether each point is Pareto efficient - """ - - is_efficient = np.ones(costs.shape[0], dtype=bool) - for i, c in enumerate(costs): - if is_efficient[i]: - # Keep any point with a lower cost - is_efficient[is_efficient] = np.any(costs[is_efficient] < c, axis=1) - - # And keep self - is_efficient[i] = True - return is_efficient - - -def plot_pareto_from_runhistory(observations): - """ - This is only an example function for 2d plotting, when both objectives - are to be minimized - """ - - # find the pareto front - efficient_mask = is_pareto_efficient_simple(observations) - front = observations[efficient_mask] - # observations = observations[np.invert(efficient_mask)] - - obs1, obs2 = observations[:, 0], observations[:, 1] - front = front[front[:, 0].argsort()] - - # add the bounds - x_upper = np.max(obs1) - y_upper = np.max(obs2) - front = np.vstack([[front[0][0], y_upper], front, [x_upper, np.min(front[:, 1])]]) - - x_front, y_front = front[:, 0], front[:, 1] - - plt.scatter(obs1, obs2) - plt.step(x_front, y_front, where="post", linestyle=":") - plt.title("Pareto-Front") - - plt.xlabel("Cost") - plt.ylabel("Time") - plt.show() - - -def svm_from_cfg(cfg): - """Creates a SVM based on a configuration and evaluates it on the - iris-dataset using cross-validation. Note here random seed is fixed. - - It is a multi-objective tae, because we wish to trade-off the time to train - and the algorithm's final performance. - - Parameters: - ----------- - cfg: Configuration (ConfigSpace.ConfigurationSpace.Configuration) - Configuration containing the parameters. - Configurations are indexable! - - Returns: - -------- - Dict: A crossvalidated mean score (cost) for the svm on the loaded data-set and the - second objective; runtime - """ - - # For deactivated parameters, the configuration stores None-values. - # This is not accepted by the SVM, so we remove them. - cfg = {k: cfg[k] for k in cfg if cfg[k]} - # And for gamma, we set it to a fixed value or to "auto" (if used) - if "gamma" in cfg: - cfg["gamma"] = cfg["gamma_value"] if cfg["gamma"] == "value" else "auto" - cfg.pop("gamma_value", None) # Remove "gamma_value" - - t0 = time.time() - clf = svm.SVC(**cfg, random_state=42) - t1 = time.time() - - scores = cross_val_score(clf, iris.data, iris.target, cv=5) - cost_value = 1 - np.mean(scores) # Minimize! - - # Return a dictionary with all of the objectives. - # Alternatively you can return a list in the same order - # as `multi_objectives`. - return {"cost": cost_value, "time": t1 - t0} - - -if __name__ == "__main__": - # Build Configuration Space which defines all parameters and their ranges - cs = ConfigurationSpace() - - # We define a few possible types of SVM-kernels and add them as "kernel" to our cs - kernel = CategoricalHyperparameter( - name="kernel", - choices=["linear", "rbf", "poly", "sigmoid"], - default_value="poly", - ) - cs.add_hyperparameter(kernel) - - # There are some hyperparameters shared by all kernels - C = UniformFloatHyperparameter("C", 0.001, 1000.0, default_value=1.0, log=True) - shrinking = CategoricalHyperparameter("shrinking", [True, False], default_value=True) - cs.add_hyperparameters([C, shrinking]) - - # Others are kernel-specific, so we can add conditions to limit the searchspace - degree = UniformIntegerHyperparameter("degree", 1, 5, default_value=3) # Only used by kernel poly - coef0 = UniformFloatHyperparameter("coef0", 0.0, 10.0, default_value=0.0) # poly, sigmoid - cs.add_hyperparameters([degree, coef0]) - - use_degree = InCondition(child=degree, parent=kernel, values=["poly"]) - use_coef0 = InCondition(child=coef0, parent=kernel, values=["poly", "sigmoid"]) - cs.add_conditions([use_degree, use_coef0]) - - # This also works for parameters that are a mix of categorical and values - # from a range of numbers - # For example, gamma can be either "auto" or a fixed float - gamma = CategoricalHyperparameter("gamma", ["auto", "value"], default_value="auto") # only rbf, poly, sigmoid - gamma_value = UniformFloatHyperparameter("gamma_value", 0.0001, 8, default_value=1, log=True) - cs.add_hyperparameters([gamma, gamma_value]) - # We only activate gamma_value if gamma is set to "value" - cs.add_condition(InCondition(child=gamma_value, parent=gamma, values=["value"])) - # And again we can restrict the use of gamma in general to the choice of the kernel - cs.add_condition(InCondition(child=gamma, parent=kernel, values=["rbf", "poly", "sigmoid"])) - - # Scenario object - scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternatively runtime) - "runcount-limit": 50, # max. number of function evaluations - "cs": cs, # configuration space - "deterministic": True, - "multi_objectives": ["cost", "time"], - # You can define individual crash costs for each objective - "cost_for_crash": [1, float(MAXINT)], - } - ) - - # Example call of the function - # It returns: Status, Cost, Runtime, Additional Infos - def_value = svm_from_cfg(cs.get_default_configuration()) - print("Default config's cost: {cost:2f}, training time: {time:2f} seconds".format(**def_value)) - - # Optimize, using a SMAC-object - print("Optimizing! Depending on your machine, this might take a few minutes.") - # Pass the multi objective algorithm and its hyperparameters - smac = SMAC4HPO( - scenario=scenario, - rng=np.random.RandomState(42), - tae_runner=svm_from_cfg, - multi_objective_algorithm=ParEGO, - multi_objective_kwargs={ - "rho": 0.05, - }, - ) - - incumbent = smac.optimize() - - # pareto front based on smac.runhistory.data - cost = np.vstack([v[0] for v in smac.runhistory.data.values()]) - plot_pareto_from_runhistory(cost) diff --git a/examples/python/plot_sgd_instances.py b/examples/python/plot_sgd_instances.py deleted file mode 100644 index 65b4c3ba8..000000000 --- a/examples/python/plot_sgd_instances.py +++ /dev/null @@ -1,176 +0,0 @@ -""" -SGD on Instances -^^^^^^^^^^^^^^^^ - -Example for optimizing a Multi-Layer Perceptron (MLP) using multiple instances. - -Alternative to budgets, here we consider instances as a fidelity type. An instance represents a specific -scenario/condition (e.g. different datasets, subsets, transformations) for the algorithm to run. SMAC then returns the -algorithm that had the best performance across all the instances. In this case, an instance is a binary dataset i.e., -digit-2 vs digit-3. - -If we use instance as our fidelity, we need to initialize scenario with argument instance. In this case the argument -budget is no longer required by the target function. -""" - -import logging - -logging.basicConfig(level=logging.INFO) - -import itertools -import warnings - -import numpy as np -from ConfigSpace.hyperparameters import ( - CategoricalHyperparameter, - UniformFloatHyperparameter, -) -from sklearn import datasets -from sklearn.exceptions import ConvergenceWarning -from sklearn.linear_model import SGDClassifier -from sklearn.model_selection import StratifiedKFold, cross_val_score - -# Import ConfigSpace and different types of parameters -from smac.configspace import ConfigurationSpace -from smac.facade.smac_mf_facade import SMAC4MF - -# Import SMAC-utilities -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -# We load the MNIST-dataset (a widely used benchmark) and split it into a list of binary datasets -digits = datasets.load_digits() -instances = [[str(a) + str(b)] for a, b in itertools.combinations(digits.target_names, 2)] - - -def generate_instances(a: int, b: int): - """ - Function to select data for binary classification from the digits dataset - a & b are the two classes - """ - # get indices of both classes - indices = np.where(np.logical_or(a == digits.target, b == digits.target)) - - # get data - data = digits.data[indices] - target = digits.target[indices] - - return data, target - - -# Target Algorithm -def sgd_from_cfg(cfg, seed, instance): - """Creates a SGD classifier based on a configuration and evaluates it on the - digits dataset using cross-validation. - - Parameters: - ----------- - cfg: Configuration (ConfigSpace.ConfigurationSpace.Configuration) - Configuration containing the parameters. - Configurations are indexable! - seed: int or RandomState - used to initialize the svm's random generator - instance: str - used to represent the instance to use (the 2 classes to consider in this case) - - Returns: - -------- - float - A crossvalidated mean score for the SGD classifier on the loaded data-set. - """ - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=ConvergenceWarning) - - # SGD classifier using given configuration - clf = SGDClassifier( - loss="log", - penalty="elasticnet", - alpha=cfg["alpha"], - l1_ratio=cfg["l1_ratio"], - learning_rate=cfg["learning_rate"], - eta0=cfg["eta0"], - max_iter=30, - early_stopping=True, - random_state=seed, - ) - - # get instance - data, target = generate_instances(int(instance[0]), int(instance[1])) - - cv = StratifiedKFold(n_splits=4, random_state=seed, shuffle=True) # to make CV splits consistent - scores = cross_val_score(clf, data, target, cv=cv) - - return 1 - np.mean(scores) - - -if __name__ == "__main__": - # Build Configuration Space which defines all parameters and their ranges - cs = ConfigurationSpace() - - # We define a few possible parameters for the SGD classifier - alpha = UniformFloatHyperparameter("alpha", 0, 1, default_value=1.0) - l1_ratio = UniformFloatHyperparameter("l1_ratio", 0, 1, default_value=0.5) - learning_rate = CategoricalHyperparameter( - "learning_rate", choices=["constant", "invscaling", "adaptive"], default_value="constant" - ) - eta0 = UniformFloatHyperparameter("eta0", 0.00001, 1, default_value=0.1, log=True) - # Add the parameters to configuration space - cs.add_hyperparameters([alpha, l1_ratio, learning_rate, eta0]) - - # SMAC scenario object - scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternative to runtime) - "wallclock-limit": 100, # max duration to run the optimization (in seconds) - "cs": cs, # configuration space - "deterministic": True, - "limit_resources": True, # Uses pynisher to limit memory and runtime - "memory_limit": 3072, # adapt this to reasonable value for your hardware - "cutoff": 3, # runtime limit for the target algorithm - "instances": instances, # Optimize across all given instances - } - ) - - # intensifier parameters - # if no argument provided for budgets, hyperband decides them based on the number of instances available - intensifier_kwargs = { - "initial_budget": 1, - "max_budget": 45, - "eta": 3, - # You can also shuffle the order of using instances by this parameter. - # 'shuffle' will shuffle instances before each SH run and 'shuffle_once' - # will shuffle instances once before the 1st SH iteration begins - "instance_order": None, - } - - # To optimize, we pass the function to the SMAC-object - smac = SMAC4MF( - scenario=scenario, - rng=np.random.RandomState(42), - tae_runner=sgd_from_cfg, - intensifier_kwargs=intensifier_kwargs, - ) - - # Example call of the function - # It returns: Status, Cost, Runtime, Additional Infos - def_costs = [] - for i in instances: - cost = smac.get_tae_runner().run(cs.get_default_configuration(), i[0])[1] - def_costs.append(cost) - print("Value for default configuration: %.4f" % (np.mean(def_costs))) - - # Start optimization - try: - incumbent = smac.optimize() - finally: - incumbent = smac.solver.incumbent - - inc_costs = [] - for i in instances: - cost = smac.get_tae_runner().run(incumbent, i[0])[1] - inc_costs.append(cost) - print("Optimized Value: %.4f" % (np.mean(inc_costs))) diff --git a/examples/python/plot_simple_multi_objective.py b/examples/python/plot_simple_multi_objective.py deleted file mode 100644 index af5476610..000000000 --- a/examples/python/plot_simple_multi_objective.py +++ /dev/null @@ -1,80 +0,0 @@ -""" -Simple Multi-Objective -^^^^^^^^^^^^^^^^^^^^^^ - -""" - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - -import numpy as np -from ConfigSpace.hyperparameters import UniformFloatHyperparameter -from matplotlib import pyplot as plt - -from smac.configspace import ConfigurationSpace -from smac.facade.smac_bb_facade import SMAC4BB -from smac.scenario.scenario import Scenario - - -def schaffer(x): - f1 = np.square(x) - f2 = np.square(np.sqrt(f1) - 2) - - return f1, f2 - - -def plot(all_x): - plt.figure() - for x in all_x: - f1, f2 = schaffer(x) - plt.scatter(f1, f2, c="blue", alpha=0.1, zorder=3000) - - plt.vlines([1], 0, 4, linestyles="dashed", colors=["red"]) - plt.hlines([1], 0, 4, linestyles="dashed", colors=["red"]) - - plt.show() - - -def plot_from_smac(smac): - rh = smac.get_runhistory() - all_x = [] - for (config_id, _, _, _) in rh.data.keys(): - config = rh.ids_config[config_id] - all_x.append(config["x"]) - - plot(all_x) - - -def tae(cfg): - f1, f2 = schaffer(cfg["x"]) - return {"metric1": f1, "metric2": f2} - - -if __name__ == "__main__": - MIN_V = -2 - MAX_V = 2 - - # Simple configspace - cs = ConfigurationSpace() - cs.add_hyperparameter(UniformFloatHyperparameter("x", lower=MIN_V, upper=MAX_V)) - - # Scenario object - scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternatively runtime) - "runcount-limit": 50, # max. number of function evaluations - "cs": cs, # configuration space - "multi_objectives": "metric1, metric2", - "limit_resources": False, - } - ) - - smac = SMAC4BB( - scenario=scenario, - rng=np.random.RandomState(5), - tae_runner=tae, - ) - incumbent = smac.optimize() - - # Plot the evaluated points - plot_from_smac(smac) diff --git a/examples/python/plot_svm_cv.py b/examples/python/plot_svm_cv.py deleted file mode 100644 index 0e45ad33f..000000000 --- a/examples/python/plot_svm_cv.py +++ /dev/null @@ -1,131 +0,0 @@ -""" -SVM with Cross-Validation -^^^^^^^^^^^^^^^^^^^^^^^^^ - -An example to optimize a simple SVM on the IRIS-benchmark. SMAC4HPO is designed -for hyperparameter optimization (HPO) problems and uses an RF as its surrogate model. -It is able to scale to higher evaluation budgets and higher number of -dimensions. Also, you can use mixed data types as well as conditional hyperparameters. - -SMAC4HPO by default only contains single fidelity approach. Therefore, only the configuration is -processed by the :term:`TAE`. -""" - -import logging - -logging.basicConfig(level=logging.INFO) - -import numpy as np -from ConfigSpace.conditions import InCondition -from ConfigSpace.hyperparameters import ( - CategoricalHyperparameter, - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) -from sklearn import datasets, svm -from sklearn.model_selection import cross_val_score - -from smac.configspace import ConfigurationSpace -from smac.facade.smac_hpo_facade import SMAC4HPO -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -# We load the iris-dataset (a widely used benchmark) -iris = datasets.load_iris() - - -def svm_from_cfg(cfg): - """Creates a SVM based on a configuration and evaluates it on the - iris-dataset using cross-validation. Note here random seed is fixed - - Parameters: - ----------- - cfg: Configuration (ConfigSpace.ConfigurationSpace.Configuration) - Configuration containing the parameters. - Configurations are indexable! - - Returns: - -------- - A crossvalidated mean score for the svm on the loaded data-set. - """ - # For deactivated parameters, the configuration stores None-values. - # This is not accepted by the SVM, so we remove them. - cfg = {k: cfg[k] for k in cfg if cfg[k]} - # And for gamma, we set it to a fixed value or to "auto" (if used) - if "gamma" in cfg: - cfg["gamma"] = cfg["gamma_value"] if cfg["gamma"] == "value" else "auto" - cfg.pop("gamma_value", None) # Remove "gamma_value" - - clf = svm.SVC(**cfg, random_state=42) - - scores = cross_val_score(clf, iris.data, iris.target, cv=5) - return 1 - np.mean(scores) # Minimize! - - -if __name__ == "__main__": - # Build Configuration Space which defines all parameters and their ranges - cs = ConfigurationSpace() - - # We define a few possible types of SVM-kernels and add them as "kernel" to our cs - kernel = CategoricalHyperparameter("kernel", ["linear", "rbf", "poly", "sigmoid"], default_value="poly") - cs.add_hyperparameter(kernel) - - # There are some hyperparameters shared by all kernels - C = UniformFloatHyperparameter("C", 0.001, 1000.0, default_value=1.0, log=True) - shrinking = CategoricalHyperparameter("shrinking", [True, False], default_value=True) - cs.add_hyperparameters([C, shrinking]) - - # Others are kernel-specific, so we can add conditions to limit the searchspace - degree = UniformIntegerHyperparameter("degree", 1, 5, default_value=3) # Only used by kernel poly - coef0 = UniformFloatHyperparameter("coef0", 0.0, 10.0, default_value=0.0) # poly, sigmoid - cs.add_hyperparameters([degree, coef0]) - - use_degree = InCondition(child=degree, parent=kernel, values=["poly"]) - use_coef0 = InCondition(child=coef0, parent=kernel, values=["poly", "sigmoid"]) - cs.add_conditions([use_degree, use_coef0]) - - # This also works for parameters that are a mix of categorical and values - # from a range of numbers - # For example, gamma can be either "auto" or a fixed float - gamma = CategoricalHyperparameter("gamma", ["auto", "value"], default_value="auto") # only rbf, poly, sigmoid - gamma_value = UniformFloatHyperparameter("gamma_value", 0.0001, 8, default_value=1, log=True) - cs.add_hyperparameters([gamma, gamma_value]) - # We only activate gamma_value if gamma is set to "value" - cs.add_condition(InCondition(child=gamma_value, parent=gamma, values=["value"])) - # And again we can restrict the use of gamma in general to the choice of the kernel - cs.add_condition(InCondition(child=gamma, parent=kernel, values=["rbf", "poly", "sigmoid"])) - - # Scenario object - scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternatively runtime) - "runcount-limit": 50, # max. number of function evaluations - "cs": cs, # configuration space - "deterministic": True, - } - ) - - # Example call of the function - # It returns: Status, Cost, Runtime, Additional Infos - def_value = svm_from_cfg(cs.get_default_configuration()) - print("Default Value: %.2f" % (def_value)) - - # Optimize, using a SMAC-object - print("Optimizing! Depending on your machine, this might take a few minutes.") - smac = SMAC4HPO(scenario=scenario, rng=np.random.RandomState(42), tae_runner=svm_from_cfg) - - incumbent = smac.optimize() - - inc_value = svm_from_cfg(incumbent) - print("Optimized Value: %.2f" % (inc_value)) - - # We can also validate our results (though this makes a lot more sense with instances) - smac.validate( - config_mode="inc", # We can choose which configurations to evaluate - # instance_mode='train+test', # Defines what instances to validate - repetitions=100, # Ignored, unless you set "deterministic" to "false" in line 95 - n_jobs=1, - ) # How many cores to use in parallel for optimization diff --git a/examples/python/plot_svm_eips.py b/examples/python/plot_svm_eips.py deleted file mode 100644 index 400c68973..000000000 --- a/examples/python/plot_svm_eips.py +++ /dev/null @@ -1,124 +0,0 @@ -""" -SVM with EIPS as acquisition functions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -An example to optimize a simple SVM on the IRIS-benchmark with EIPS (EI per seconds) -acquisition function. Since EIPS requires two types of objections: EI values and the predicted -time used for the configurations. We need to fit the data -with a multi-objective model -""" - -import logging - -logging.basicConfig(level=logging.INFO) - -import numpy as np -from ConfigSpace.hyperparameters import ( - CategoricalHyperparameter, - UniformFloatHyperparameter, -) -from sklearn import datasets, svm -from sklearn.model_selection import cross_val_score - -from smac.configspace import ConfigurationSpace -from smac.epm.random_forest.rf_mo import MultiObjectiveRandomForest -from smac.facade.smac_ac_facade import SMAC4AC - -# EIPS related -from smac.optimizer.acquisition import EIPS -from smac.runhistory.runhistory2epm import RunHistory2EPM4EIPS - -# Import SMAC-utilities -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - -iris = datasets.load_iris() - - -# Target Algorithm -def svm_from_cfg(cfg): - """Creates a SVM based on a configuration and evaluates it on the - iris-dataset using cross-validation. Note here random seed is fixed - - Parameters: - ----------- - cfg: Configuration (ConfigSpace.ConfigurationSpace.Configuration) - Configuration containing the parameters. - Configurations are indexable! - - Returns: - -------- - A crossvalidated mean score for the svm on the loaded data-set. - """ - # For deactivated parameters, the configuration stores None-values. - # This is not accepted by the SVM, so we remove them. - cfg = {k: cfg[k] for k in cfg if cfg[k]} - # And for gamma, we set it to a fixed value or to "auto" (if used) - if "gamma" in cfg: - cfg["gamma"] = cfg["gamma_value"] if cfg["gamma"] == "value" else "auto" - cfg.pop("gamma_value", None) # Remove "gamma_value" - - clf = svm.SVC(**cfg, random_state=42) - - scores = cross_val_score(clf, iris.data, iris.target, cv=5) - return 1 - np.mean(scores) # Minimize! - - -if __name__ == "__main__": - # Build Configuration Space which defines all parameters and their ranges - cs = ConfigurationSpace() - - # We define a few possible types of SVM-kernels and add them as "kernel" to our cs - kernel = CategoricalHyperparameter("kernel", ["linear", "rbf", "poly", "sigmoid"], default_value="poly") - cs.add_hyperparameter(kernel) - - # There are some hyperparameters shared by all kernels - C = UniformFloatHyperparameter("C", 0.001, 1000.0, default_value=1.0, log=True) - shrinking = CategoricalHyperparameter("shrinking", [True, False], default_value=True) - cs.add_hyperparameters([C, shrinking]) - - # Scenario object - scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternatively runtime) - "runcount-limit": 50, # max. number of function evaluations - "cs": cs, # configuration space - "deterministic": True, - } - ) - - # Example call of the function - # It returns: Status, Cost, Runtime, Additional Infos - def_value = svm_from_cfg(cs.get_default_configuration()) - print("Default Value: %.2f" % def_value) - - # Optimize, using a SMAC-object - print("Optimizing! Depending on your machine, this might take a few minutes.") - - # Besides the kwargs used for initializing UncorrelatedMultiObjectiveRandomForestWithInstances, - # we also need kwargs for initializing the model insides UncorrelatedMultiObjectiveModel - model_kwargs = {"target_names": ["loss", "time"], "model_kwargs": {"seed": 1}} - smac = SMAC4AC( - scenario=scenario, - model=MultiObjectiveRandomForest, - rng=np.random.RandomState(42), - model_kwargs=model_kwargs, - tae_runner=svm_from_cfg, - acquisition_function=EIPS, - runhistory2epm=RunHistory2EPM4EIPS, - ) - - incumbent = smac.optimize() - - inc_value = svm_from_cfg(incumbent) - print("Optimized Value: %.2f" % (inc_value)) - - # We can also validate our results (though this makes a lot more sense with instances) - smac.validate( - config_mode="inc", # We can choose which configurations to evaluate - # instance_mode='train+test', # Defines what instances to validate - repetitions=100, # Ignored, unless you set "deterministic" to "false" in line 95 - n_jobs=1, - ) # How many cores to use in parallel for optimization diff --git a/examples/python/plot_synthetic_function.py b/examples/python/plot_synthetic_function.py deleted file mode 100644 index 8f45c864d..000000000 --- a/examples/python/plot_synthetic_function.py +++ /dev/null @@ -1,84 +0,0 @@ -""" -Synthetic Function with few Hyperparameters -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -An example of applying SMAC to optimize a synthetic function (2d rosenbrock function). - -We use the SMAC4BB facade because it is designed for black-box function optimization. -SMAC4BB uses a :term:`Gaussian Process` or a set of Gaussian Processes whose -hyperparameters are integrated by Markov-Chain Monte-Carlo as its surrogate model. -SMAC4BB works best on numerical hyperparameter configuration space and should not -be applied to the problems with large evaluation budgets (up to 1000 evaluations). -""" - -import logging - -logging.basicConfig(level=logging.INFO) - -import numpy as np -from ConfigSpace.hyperparameters import UniformFloatHyperparameter - -# Import ConfigSpace and different types of parameters -from smac.configspace import ConfigurationSpace -from smac.facade.smac_bb_facade import SMAC4BB -from smac.optimizer.acquisition import EI - -# Import SMAC-utilities -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def rosenbrock_2d(x): - """The 2 dimensional Rosenbrock function as a toy model - The Rosenbrock function is well know in the optimization community and - often serves as a toy problem. It can be defined for arbitrary - dimensions. The minimium is always at x_i = 1 with a function value of - zero. All input parameters are continuous. The search domain for - all x's is the interval [-5, 10]. - """ - - x1 = x["x0"] - x2 = x["x1"] - - val = 100.0 * (x2 - x1**2.0) ** 2.0 + (1 - x1) ** 2.0 - return val - - -if __name__ == "__main__": - # Build Configuration Space which defines all parameters and their ranges - cs = ConfigurationSpace() - x0 = UniformFloatHyperparameter("x0", -5, 10, default_value=-3) - x1 = UniformFloatHyperparameter("x1", -5, 10, default_value=-4) - cs.add_hyperparameters([x0, x1]) - - # Scenario object - scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternatively runtime) - "runcount-limit": 10, # max. number of function evaluations - "cs": cs, # configuration space - "deterministic": True, - } - ) - - # Use 'gp' or 'gp_mcmc' here - model_type = "gp" - - # Example call of the function - # It returns: Status, Cost, Runtime, Additional Infos - def_value = rosenbrock_2d(cs.get_default_configuration()) - print("Default Value: %.2f" % def_value) - - # Optimize, using a SMAC-object - print("Optimizing! Depending on your machine, this might take a few minutes.") - smac = SMAC4BB( - scenario=scenario, - model_type=model_type, - rng=np.random.RandomState(42), - acquisition_function=EI, # or others like PI, LCB as acquisition functions - tae_runner=rosenbrock_2d, - ) - - smac.optimize() diff --git a/examples/python/plot_synthetic_function_boing.py b/examples/python/plot_synthetic_function_boing.py deleted file mode 100644 index e093229d8..000000000 --- a/examples/python/plot_synthetic_function_boing.py +++ /dev/null @@ -1,74 +0,0 @@ -""" -Synthetic Function with BOinG as optimizer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -An example of applying SMAC with BO inside Grove (BOinG) to optimize a -synthetic function (2d rosenbrock function). - -BOinG optimizer requires a SMAC4BOING wrapper to optimize the target algorithm. It is a two stage BO algorithm. -In the first stage, BOinG constructs an RF to capture the global loss landscape. Then in the second stage, it only -optimizes inside a subregion near the candidate suggested by the RF model with a GP model to focus only on the most -promising region. -""" - -import logging - -import numpy as np -from ConfigSpace import ConfigurationSpace -from ConfigSpace.hyperparameters import UniformFloatHyperparameter - -from smac.facade.smac_boing_facade import SMAC4BOING - -# Import SMAC-utilities -from smac.scenario.scenario import Scenario - - -def rosenbrock_2d(x): - """The 2 dimensional Rosenbrock function as a toy model - The Rosenbrock function is well know in the optimization community and - often serves as a toy problem. It can be defined for arbitrary - dimensions. The minimium is always at x_i = 1 with a function value of - zero. All input parameters are continuous. The search domain for - all x's is the interval [-5, 10]. - """ - x1 = x["x0"] - x2 = x["x1"] - - val = 100.0 * (x2 - x1**2.0) ** 2.0 + (1 - x1) ** 2.0 - return val - - -if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) # logging.DEBUG for debug output - - # Build Configuration Space which defines all parameters and their ranges - cs = ConfigurationSpace() - x0 = UniformFloatHyperparameter("x0", -5, 10, default_value=-3) - x1 = UniformFloatHyperparameter("x1", -5, 10, default_value=-4) - cs.add_hyperparameters([x0, x1]) - # Scenario object - scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternatively runtime) - "runcount-limit": 20, - # max. number of function evaluations; for this example set to a low number - "cs": cs, # configuration space - "deterministic": "true", - } - ) - - # Example call of the function - # It returns: Status, Cost, Runtime, Additional Infos - def_value = rosenbrock_2d(cs.get_default_configuration()) - print("Default Value: %.2f" % def_value) - - # Optimize, using a SMAC-object - print("Optimizing! Depending on your machine, this might take a few minutes.") - - smac = SMAC4BOING( - scenario=scenario, - rng=np.random.RandomState(42), - tae_runner=rosenbrock_2d, - ) - - smac.optimize() diff --git a/examples/python/plot_synthetic_function_parallel.py b/examples/python/plot_synthetic_function_parallel.py deleted file mode 100644 index 93ea864e3..000000000 --- a/examples/python/plot_synthetic_function_parallel.py +++ /dev/null @@ -1,109 +0,0 @@ -""" -Synthetic Function with few Hyperparameters -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -An example of applying SMAC to optimize a synthetic function (2d rosenbrock function). - -We use the pSMAC [1]_ facade to demonstrate the parallelization of SMAC. -Other than that, we use a :term:`Gaussian Process` to optimize our black-box -function. - - -.. [1] Ramage, S. E. A. (2015). Advances in meta-algorithmic software libraries for - distributed automated algorithm configuration (T). University of British - Columbia. Retrieved from - https://open.library.ubc.ca/collections/ubctheses/24/items/1.0167184. -""" -import importlib - -import logging - -logging.basicConfig(level=logging.INFO) - -import numpy as np -from ConfigSpace.hyperparameters import UniformFloatHyperparameter - -# Import ConfigSpace and different types of parameters -from smac.configspace import ConfigurationSpace -from smac.facade.psmac_facade import PSMAC -from smac.facade.smac_bb_facade import SMAC4BB -import smac - -importlib.reload(smac.facade.psmac_facade) -from smac.facade.psmac_facade import PSMAC - -from smac.optimizer.acquisition import EI - -# Import SMAC-utilities -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def rosenbrock_2d(x): - """The 2 dimensional Rosenbrock function as a toy model - The Rosenbrock function is well know in the optimization community and - often serves as a toy problem. It can be defined for arbitrary - dimensions. The minimium is always at x_i = 1 with a function value of - zero. All input parameters are continuous. The search domain for - all x's is the interval [-5, 10]. - """ - - x1 = x["x0"] - x2 = x["x1"] - - val = 100.0 * (x2 - x1**2.0) ** 2.0 + (1 - x1) ** 2.0 - return val - - -if __name__ == "__main__": - # Build Configuration Space which defines all parameters and their ranges - cs = ConfigurationSpace() - x0 = UniformFloatHyperparameter("x0", -5, 10, default_value=-3) - x1 = UniformFloatHyperparameter("x1", -5, 10, default_value=-4) - cs.add_hyperparameters([x0, x1]) - - # Scenario object - scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternatively runtime) - "runcount-limit": 20, # max. number of function evaluations PER WORKER - "cs": cs, # configuration space - "deterministic": True, - } - ) - - # Use 'gp' or 'gp_mcmc' here - model_type = "gp" - - # Example call of the function - # It returns: Status, Cost, Runtime, Additional Infos - def_value = rosenbrock_2d(cs.get_default_configuration()) - print("Default Value: %.2f" % def_value) - - # Optimize, using a SMAC-object - print("Optimizing! Depending on your machine, this might take a few minutes.") - smac = PSMAC( - scenario=scenario, - facade_class=SMAC4BB, - model_type=model_type, - rng=np.random.RandomState(42), - acquisition_function=EI, # or others like PI, LCB as acquisition functions - tae_runner=rosenbrock_2d, - n_workers=2, # 2 parallel workers - ) - - incumbent = smac.optimize() - # Get trajectory of optimization (incumbent over time) - trajectory_json = smac.get_trajectory() # trajectory in json format - - # Plot trajectory: cost of incumbent against number of evaluations - # import matplotlib.pyplot as plt - # X = [t["evaluations"] for t in trajectory_json] - # Y = [t["cost"] for t in trajectory_json] - # plt.plot(X, Y) - # plt.yscale("log") - # plt.xlabel("Number of Evaluations") - # plt.ylabel("Cost of Incumbent") - # plt.show() diff --git a/examples/python/plot_synthetic_function_turbo.py b/examples/python/plot_synthetic_function_turbo.py deleted file mode 100644 index 753cae7ef..000000000 --- a/examples/python/plot_synthetic_function_turbo.py +++ /dev/null @@ -1,80 +0,0 @@ -""" -Synthetic Function with TuRBO as optimizer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -An example of applying SMAC with trust region BO (TuRBO) to optimize a -synthetic function (2d rosenbrock function). - -Eriksson et al. Scalable Global Optimization via Local {Bayesian} Optimization, -http://papers.nips.cc/paper/8788-scalable-global-optimization-via-local-bayesian-optimization.pdf - -TurBO gradually shrinks its search space to the vicinity of the optimum configuration that is ever optimized. -TuRBO optimizer requires EPMChooserTurBO to suggest the next configuration. Currently, it only supports pure numerical -hyperparameters. -""" - -import logging - -import numpy as np -from ConfigSpace.hyperparameters import UniformFloatHyperparameter - -# Import ConfigSpace and different types of parameters -from smac.configspace import ConfigurationSpace -from smac.facade.smac_bb_facade import SMAC4BB -from smac.optimizer.configuration_chooser.turbo_chooser import TurBOChooser - -# Import SMAC-utilities -from smac.scenario.scenario import Scenario - - -def rosenbrock_2d(x): - """The 2 dimensional Rosenbrock function as a toy model - The Rosenbrock function is well know in the optimization community and - often serves as a toy problem. It can be defined for arbitrary - dimensions. The minimium is always at x_i = 1 with a function value of - zero. All input parameters are continuous. The search domain for - all x's is the interval [-5, 10]. - """ - x1 = x["x0"] - x2 = x["x1"] - - val = 100.0 * (x2 - x1**2.0) ** 2.0 + (1 - x1) ** 2.0 - return val - - -if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) # logging.DEBUG for debug output - - # Build Configuration Space which defines all parameters and their ranges - cs = ConfigurationSpace() - x0 = UniformFloatHyperparameter("x0", -5, 10, default_value=-3) - x1 = UniformFloatHyperparameter("x1", -5, 10, default_value=-4) - cs.add_hyperparameters([x0, x1]) - - # Scenario object - scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternatively runtime) - "runcount-limit": 100, - "cs": cs, # configuration space - "deterministic": "true", - } - ) - - # Example call of the function - # It returns: Status, Cost, Runtime, Additional Infos - def_value = rosenbrock_2d(cs.get_default_configuration()) - print("Default Value: %.2f" % def_value) - - # Optimize, using a SMAC-object - print("Optimizing! Depending on your machine, this might take a few minutes.") - smac = SMAC4BB( - scenario=scenario, - rng=np.random.RandomState(42), - model_type="gp", - smbo_kwargs={"epm_chooser": TurBOChooser}, - initial_design_kwargs={"init_budget": 0}, - tae_runner=rosenbrock_2d, - ) - - smac.optimize() diff --git a/examples/python/plot_user_prior_mlp.py b/examples/python/plot_user_prior_mlp.py deleted file mode 100644 index 42bcae3e5..000000000 --- a/examples/python/plot_user_prior_mlp.py +++ /dev/null @@ -1,166 +0,0 @@ -""" -HPO with User Priors over the Optimum -^^^^^^^^^^^^^^^^^^^^^^^ - -Example for optimizing a Multi-Layer Perceptron (MLP) setting priors over the optimum on the -hyperparameters. These priors are derived from user knowledge - from previous runs on similar -tasks, common knowledge or intuition gained from manual tuning. To create the priors, we make -use of the Normal and Beta Hyperparameters, as well as the "weights" property of the -CategoricalHyperparameter. This can be integrated into the optimiztion for any SMAC facade, -but we stick with SMAC4HPO here. To incorporate user priors into the optimization, -πBO (nolinkexistsyet) is used to bias the point selection strategy. - -MLP is used as the deep neural network. -The digits datasetis chosen to optimize the average accuracy on 5-fold cross validation. -""" - -import logging - -logging.basicConfig(level=logging.INFO) - -import warnings - -import numpy as np -from ConfigSpace.hyperparameters import ( - BetaIntegerHyperparameter, - CategoricalHyperparameter, - NormalFloatHyperparameter, - UniformIntegerHyperparameter, -) -from sklearn.datasets import load_digits -from sklearn.exceptions import ConvergenceWarning -from sklearn.model_selection import StratifiedKFold, cross_val_score -from sklearn.neural_network import MLPClassifier - -from smac.configspace import ConfigurationSpace -from smac.facade.smac_hpo_facade import SMAC4HPO -from smac.initial_design.random_configuration_design import RandomConfigurations -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -digits = load_digits() - - -# Target Algorithm -def mlp_from_cfg(cfg, seed): - """ - Creates a MLP classifier from sklearn and fits the given data on it. - - Parameters - ---------- - cfg: Configuration - configuration chosen by smac - seed: int or RandomState - used to initialize the rf's random generator - budget: float - used to set max iterations for the MLP - - Returns - ------- - float - """ - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=ConvergenceWarning) - - mlp = MLPClassifier( - hidden_layer_sizes=[cfg["n_neurons"]] * cfg["n_layer"], - solver=cfg["optimizer"], - batch_size=cfg["batch_size"], - activation=cfg["activation"], - learning_rate_init=cfg["learning_rate_init"], - random_state=seed, - ) - - # returns the cross validation accuracy - cv = StratifiedKFold(n_splits=5, random_state=seed, shuffle=True) # to make CV splits consistent - score = cross_val_score(mlp, digits.data, digits.target, cv=cv, error_score="raise") - - return 1 - np.mean(score) - - -if __name__ == "__main__": - # Build Configuration Space which defines all parameters and their ranges. - # To illustrate different parameter types, - # we use continuous, integer and categorical parameters. - cs = ConfigurationSpace() - - # We do not have an educated belief on the number of layers beforehand - # As such, the prior on the HP is uniform - n_layer = UniformIntegerHyperparameter("n_layer", lower=1, upper=5) - - # We believe the optimal network is likely going to be relatively wide, - # And place a Beta Prior skewed towards wider networks in log space - n_neurons = BetaIntegerHyperparameter("n_neurons", lower=8, upper=1024, alpha=4, beta=2, log=True) - - # We believe that ReLU is likely going to be the optimal activation function about - # 60% of the time, and thus place weight on that accordingly - activation = CategoricalHyperparameter( - "activation", ["logistic", "tanh", "relu"], weights=[1, 1, 3], default_value="relu" - ) - - # Moreover, we believe ADAM is the most likely optimizer - optimizer = CategoricalHyperparameter("optimizer", ["sgd", "adam"], weights=[1, 2], default_value="adam") - - # We do not have an educated opinion on the batch size, and thus leave it as-is - batch_size = UniformIntegerHyperparameter("batch_size", 16, 512, default_value=128) - - # We place a log-normal prior on the learning rate, so that it is centered on 10^-3, - # with one unit of standard deviation per multiple of 10 (in log space) - learning_rate_init = NormalFloatHyperparameter( - "learning_rate_init", lower=1e-5, upper=1.0, mu=np.log(1e-3), sigma=np.log(10), log=True - ) - - # Add all hyperparameters at once: - cs.add_hyperparameters([n_layer, n_neurons, activation, optimizer, batch_size, learning_rate_init]) - - # SMAC scenario object - scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternative to runtime) - "runcount-limit": 20, # max duration to run the optimization (in seconds) - "cs": cs, # configuration space - "deterministic": "true", - "limit_resources": True, # Uses pynisher to limit memory and runtime - # Alternatively, you can also disable this. - # Then you should handle runtime and memory yourself in the TA - "cutoff": 30, # runtime limit for target algorithm - "memory_limit": 3072, # adapt this to reasonable value for your hardware - } - ) - - # The rate at which SMAC forgets the prior. - # The higher the value, the more the prior is considered. - # Defaults to # n_iterations / 10 - user_prior_kwargs = {"decay_beta": 1.5} - - # To optimize, we pass the function to the SMAC-object - smac = SMAC4HPO( - scenario=scenario, - rng=np.random.RandomState(42), - tae_runner=mlp_from_cfg, - # This flag is required to conduct the optimisation using priors over the optimum - user_priors=True, - user_prior_kwargs=user_prior_kwargs, - # Using random configurations will cause the initialization to be samples drawn from the prior - initial_design=RandomConfigurations, - ) - - # Example call of the function with default values - # It returns: Status, Cost, Runtime, Additional Infos - def_value = smac.get_tae_runner().run(config=cs.get_default_configuration(), seed=0)[1] - - print("Value for default configuration: %.4f" % def_value) - - # Start optimization - try: - incumbent = smac.optimize() - finally: - incumbent = smac.solver.incumbent - - inc_value = smac.get_tae_runner().run(config=incumbent, seed=0)[1] - - print("Optimized Value: %.4f" % inc_value) diff --git a/examples/python/spear_mf_instances.py b/examples/python/spear_mf_instances.py deleted file mode 100644 index b67f9f7ab..000000000 --- a/examples/python/spear_mf_instances.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -SPEAR-QCP with Multi-Fidelity on Instances -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -We optimize the SPEAR algorithm on QCP to demonstrate the powerful SMAC4AC facade. Algorithm and -instance definition is done inside scenario file. - -Moreover, we present you an alternative :term:`intensification` procedure "Successive Halving". -""" - -import logging - -logging.basicConfig(level=logging.INFO) - -from smac.facade.smac_ac_facade import SMAC4AC -from smac.intensification.successive_halving import SuccessiveHalving -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -if __name__ == "__main__": - scenario = Scenario("examples/commandline/spear_qcp/scenario.txt") - - # provide arguments for the intensifier like this - intensifier_kwargs = { - "n_seeds": 2, # specify the number of seeds to evaluate for a non-deterministic target algorithm - "initial_budget": 1, - "eta": 3, - "min_chall": 1, # because successive halving cannot handle min_chall > 1 - } - - smac = SMAC4AC( - scenario=scenario, # scenario object - intensifier_kwargs=intensifier_kwargs, # arguments for Successive Halving - # change intensifier to successive halving by passing the class. - # it must implement `AbstractRacer`. - intensifier=SuccessiveHalving, - ) - - # Start optimization - try: - incumbent = smac.optimize() - finally: - incumbent = smac.solver.incumbent - - print("Optimized configuration %s" % str(incumbent)) diff --git a/pyproject.toml b/pyproject.toml index a2d47729e..5e63e895d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,7 +76,10 @@ disallow_untyped_decorators = true # ... even decorators disallow_incomplete_defs = true # ... all types [[tool.mypy.overrides]] -module = ["setuptools.*"] # Add modules that give import errors here +module = [ + "setuptools.*", + "yaml.*" +] # Add modules that give import errors here ignore_missing_imports = true [[tool.mypy.overrides]] diff --git a/scripts/check-deterministic.sh b/scripts/check-deterministic.sh deleted file mode 100755 index 5c47056b0..000000000 --- a/scripts/check-deterministic.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -cd $(dirname $0)/../examples/quickstart/branin - -cat << EOF > /tmp/scenario.txt -algo = python cmdline_wrapper.py -paramfile = param_config_space.pcs -run_obj = quality -runcount_limit = 100 -deterministic = 1 -EOF - -INITIAL_GIT_REVISION=$(git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e "s/* \(.*\)/\1/") - -function test_deterministic () { - python ../../scripts/smac --scenario /tmp/scenario.txt --seed 1 --verbose_level INFO --mode SMAC | tee "$1" - python ../../scripts/smac --scenario /tmp/scenario.txt --seed 1 --verbose_level INFO --mode SMAC | tee "$2" - GIT_REVISION=$(git rev-parse HEAD~1) - echo ============================== - echo === diff between two runs on $(git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e "s/* \(.*\)/\1/") === - echo ============================== - diff "$1" "$2" -} - -test_deterministic /tmp/smac_det_1.log /tmp/smac_det_2.log - -if [[ $# -ge 1 ]] ; then - git checkout $1 || (echo "argument provided must be a git revision" ; exit 1) - test_deterministic /tmp/smac_det_3.log /tmp/smac_det_4.log - - echo ============================== - echo === diff between runs on $INITIAL_GIT_REVISION and $1 === - echo ============================== - diff /tmp/smac_det_1.log /tmp/smac_det_3.log -fi diff --git a/scripts/plot_traj_perf.py b/scripts/plot_traj_perf.py deleted file mode 100644 index 391cedc2a..000000000 --- a/scripts/plot_traj_perf.py +++ /dev/null @@ -1,145 +0,0 @@ -import os -import typing -import logging - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger("PlotTraj") - -import matplotlib.pyplot as plt - -from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter - -from smac.utils.io.traj_logging import TrajLogger -from smac.scenario.scenario import Scenario -from smac.facade.smac_ac_facade import SMAC4AC -from smac.configspace import convert_configurations_to_array - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2017, ML4AAD" -__license__ = "3-clause BSD" - - -def setup_SMAC_from_file(smac_out_dn: str, add_dn: typing.List[str]): - """ - read all files from disk - and initialize SMAC data structures with it - - Arguments - --------- - smac_out_dn: str - output directory name of a SMAC run - add_dn: typing.List[str] - additional output directories of SMAC runs - to extend runhistory - (Assumption: All output directories correspond - to the same scenario) - - Returns - ------- - smac: SMAC() - SMAC Facade object - traj: typing.List - list of trajectory entries (dictionaries) - """ - - scenario_fn = os.path.join(smac_out_dn, "scenario.txt") - scenario = Scenario(scenario_fn, {"output_dir": ""}) - smac = SMAC4AC(scenario=scenario) - - rh = smac.solver.runhistory - rh.load_json(os.path.join(smac_out_dn, "runhistory.json"), cs=scenario.cs) - - for dn in add_dn: - rh.update_from_json(fn=os.path.join(dn, "runhistory.json"), cs=scenario.cs) - - logger.info("Fit EPM on %d observations." % (len(rh.data))) - X, Y = smac.solver.rh2EPM.transform(rh) - smac.solver.model.train(X, Y) - - traj = TrajLogger.read_traj_aclib_format(fn=os.path.join(smac_out_dn, "traj_aclib2.json"), cs=scenario.cs) - - return smac, traj - - -def predict_perf_of_traj(traj, smac: SMAC4AC): - """ - predict the performance of all entries in the trajectory - marginalized across all instances - - Arguments - --------- - smac: SMAC() - SMAC Facade object - traj: typing.List - list of trajectory entries (dictionaries) - - Returns - ------- - perfs: typing.List[float] - list of performance values - time_stamps: typing.List[float] - list of time stamps -- in the same order as perfs - """ - - logger.info("Predict performance of %d entries in trajectory." % (len(traj))) - time_stamps = [] - perfs = [] - for entry in traj: - config = entry["incumbent"] - wc_time = entry["wallclock_time"] - config_array = convert_configurations_to_array([config]) - m, v = smac.solver.model.predict_marginalized_over_instances(X=config_array) - - if smac.solver.scenario.run_obj == "runtime": - p = 10 ** m[0, 0] - else: - p = m[0, 0] - - perfs.append(p) - time_stamps.append(wc_time) - - return perfs, time_stamps - - -def plot(x: typing.List[float], y: typing.List[float], out_dir: str): - """ - plot x vs y and save in out_dir - - Arguments - --------- - x: typing.List[float] - time stamps - y:typing.List[float] - predicted performance values - out_dir: str - output directory to save plot - """ - plt.plot(x, y) - - plt.semilogx() - plt.ylabel("Average Cost") - plt.xlabel("Configuration Time") - plt.title("Predicted Performance of Incumbents over Time") - - out_fn = os.path.join(out_dir, "pred_perf_over_time.png") - - logger.info("Plot average performance and save at %s" % (out_fn)) - - plt.savefig(out_fn) - - -if __name__ == "__main__": - parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter) - parser.add_argument("--smac_output_dir", required=True, help="Output directory of SMAC") - parser.add_argument( - "--additional_data", - nargs="*", - help="Further output directory of SMAC which is used to extend the runhistory", - ) - args_ = parser.parse_args() - - smac, traj = setup_SMAC_from_file(smac_out_dn=args_.smac_output_dir, add_dn=args_.additional_data) - - perfs, times = predict_perf_of_traj(traj=traj, smac=smac) - - plot(x=times, y=perfs, out_dir=args_.smac_output_dir) diff --git a/scripts/smac-validate.py b/scripts/smac-validate.py deleted file mode 100644 index a21b44fd9..000000000 --- a/scripts/smac-validate.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env python - -import inspect -import logging -import os -import sys -from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser - -cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile(inspect.currentframe()))[0])) -cmd_folder = os.path.realpath(os.path.join(cmd_folder, "..")) -if cmd_folder not in sys.path: - sys.path.insert(0, cmd_folder) - -from smac.runhistory.runhistory import RunHistory # noqa: E402 -from smac.scenario.scenario import Scenario # noqa: E402 -from smac.stats.stats import Stats # noqa: E402 -from smac.tae.execute_ta_run_aclib import ExecuteTARunAClib # noqa: E402 -from smac.tae.execute_ta_run_old import ExecuteTARunOld # noqa: E402 -from smac.utils.io.traj_logging import TrajLogger # noqa: E402 -from smac.utils.validate import Validator # noqa: E402 - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -if __name__ == "__main__": - parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter) - req_opts = parser.add_argument_group("Required Options") - req_opts.add_argument("--scenario", required=True, help="path to SMAC scenario") - req_opts.add_argument("--trajectory", required=True, help="path to SMAC trajectory") - req_opts.add_argument("--output", required=True, help="path to save runhistory to") - - req_opts = parser.add_argument_group("Optional Options") - req_opts.add_argument( - "--configs", - default="def+inc", - type=str, - choices=["def", "inc", "def+inc", "wallclock_time", "cpu_time", "all"], - help="what configurations to evaluate. " - "def=default; inc=incumbent; " - "all=all configurations in the trajectory; " - "wallclock_time/cpu_time=evaluates at cpu- or " - "wallclock-timesteps of: [max_time/2^0, " - "max_time/2^1, max_time/2^3, ..., default] " - "with max_time being the highest recorded time", - ) - req_opts.add_argument( - "--instances", - default="test", - type=str, - choices=["train", "test", "train+test"], - help="what instances to evaluate", - ) - req_opts.add_argument("--epm", dest="epm", action="store_true", help="Use EPM to validate") - req_opts.add_argument("--no-epm", dest="epm", action="store_false", help="Don't use EPM to validate") - req_opts.set_defaults(epm=False) - req_opts.add_argument( - "--runhistory", - default=None, - type=str, - nargs="*", - help="path to one or more runhistories to take runs " - "from to either avoid recalculation or to train" - " the epm", - ) - req_opts.add_argument("--seed", type=int, help="random seed") - req_opts.add_argument( - "--repetitions", - default=1, - type=int, - help="number of repetitions for nondeterministic " "algorithms", - ) - req_opts.add_argument("--n_jobs", default=1, type=int, help="number of cpu-cores to use (-1 to use all)") - req_opts.add_argument( - "--tae", - default="old", - type=str, - help="what tae to use (if not using epm)", - choices=["aclib", "old"], - ) - req_opts.add_argument("--verbose_level", default="INFO", choices=["INFO", "DEBUG"], help="verbose level") - - args_, misc = parser.parse_known_args() - - # remove leading '-' in option names - misc = dict((k.lstrip("-"), v.strip("'")) for k, v in zip(misc[::2], misc[1::2])) - - if args_.verbose_level == "INFO": - logging.basicConfig(level=logging.INFO) - else: - logging.basicConfig(level=logging.DEBUG) - - scenario = Scenario(args_.scenario, cmd_options={"output_dir": ""}) - traj_logger = TrajLogger(None, Stats(scenario)) - trajectory = traj_logger.read_traj_aclib_format(args_.trajectory, scenario.cs) - stats = Stats(scenario) - if args_.tae == "old": - tae = ExecuteTARunOld( - ta=scenario.ta, - stats=stats, - run_obj=scenario.run_obj, - par_factor=scenario.par_factor, - cost_for_crash=scenario.cost_for_crash, - ) - if args_.tae == "aclib": - tae = ExecuteTARunAClib( - ta=scenario.ta, - stats=stats, - run_obj=scenario.run_obj, - par_factor=scenario.par_factor, - cost_for_crash=scenario.cost_for_crash, - ) - - validator = Validator(scenario, trajectory, args_.seed) - - # Load runhistory - if args_.runhistory: - runhistory = RunHistory() - for rh_path in args_.runhistory: - runhistory.update_from_json(rh_path, scenario.cs) - else: - runhistory = None - - if args_.epm: - validator.validate_epm( - config_mode=args_.configs, - instance_mode=args_.instances, - repetitions=args_.repetitions, - runhistory=runhistory, - output_fn=args_.output, - ) - else: - validator.validate( - config_mode=args_.configs, - instance_mode=args_.instances, - repetitions=args_.repetitions, - n_jobs=args_.n_jobs, - runhistory=runhistory, - tae=tae, - output_fn=args_.output, - ) diff --git a/scripts/smac.py b/scripts/smac.py deleted file mode 100644 index 46bbeb17d..000000000 --- a/scripts/smac.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python - -import inspect -import logging -import os -import sys - -cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile(inspect.currentframe()))[0])) -cmd_folder = os.path.realpath(os.path.join(cmd_folder, "..")) -if cmd_folder not in sys.path: - sys.path.insert(0, cmd_folder) - -from smac.smac_cli import SMACCLI # noqa: E402 - -if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) - - smac = SMACCLI() - smac.main_cli() diff --git a/scripts/test_no_files_left.sh b/scripts/test_no_files_left.sh deleted file mode 100644 index c7a3f0592..000000000 --- a/scripts/test_no_files_left.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# check if test cases left any files after execution by looking for files not tracked by git -CHECK_CMD=(git ls-files '*output*' 'run_*' 'test/' --others --exclude-standard) -if [[ ! -z "$(${CHECK_CMD[@]})" ]] -then - echo 'ERROR: tests did not clean up all files they generated:' - "${CHECK_CMD[@]}" | sed 's/^/ /' - exit 1 -fi diff --git a/scripts/update_files.py b/scripts/update_files.py new file mode 100644 index 000000000..3c21f01f7 --- /dev/null +++ b/scripts/update_files.py @@ -0,0 +1,76 @@ +from pathlib import Path + + +def replace(file: str, find: str, replace: str) -> None: + new_lines = [] + with open(file) as f: + for line in f.readlines(): + line = line.replace(find, replace) + new_lines += [line] + + # Empty file + with open(file, "w") as f: + f.write("") + + # Save file with new lines + with open(file, "a") as f: + for line in new_lines: + f.write(line) + + +def replace_if_starts_with(file: str, starts_with: str, replace: str = "") -> None: + new_lines = [] + with open(file) as f: + for line in f.readlines(): + if line.startswith(starts_with): + line = replace + if len(replace) > 0: + line = line + "\n" + + new_lines += [line] + + # Empty file + with open(file, "w") as f: + f.write("") + + # Save file with new lines + with open(file, "a") as f: + for line in new_lines: + f.write(line) + + +def check(file: str) -> None: + has_copyright = False + has_license = False + + copyright = "__copyright__" + license = "__license__" + + with open(file) as f: + for line in f.readlines(): + if line.startswith(copyright): + has_copyright = True + + if line.startswith(license): + has_license = True + + if not has_copyright: + print(f"{file} does not have copyright.") + + if not has_license: + print(f"{file} does not have license.") + + +if __name__ == "__main__": + + for path in Path("smac").rglob("*.py"): + filename = str(path) + if filename == "smac/__init__.py": + continue + + replace_if_starts_with(filename, "__copyright__ =", '__copyright__ = "Copyright 2022, automl.org"') + replace_if_starts_with(filename, "__author__ =") + replace_if_starts_with(filename, "__maintainer__ =") + replace_if_starts_with(filename, "__version__ =") + replace_if_starts_with(filename, "__email__ =") + check(filename) diff --git a/setup.py b/setup.py index b92f04ded..e74cb12f5 100644 --- a/setup.py +++ b/setup.py @@ -20,13 +20,9 @@ def read_file(filepath: str) -> str: return fh.read() +torch_requirements = ["torch>=1.9.0", "gpytorch>=1.5.0", "pyro-ppl>=1.7.0", "botorch>=0.5.0"] extras_require = { - "gpytorch": [ - "torch>=1.9.0", - "gpytorch>=1.5.0", - "pyro-ppl>=1.7.0", - "botorch>=0.5.0" - ], + "gpytorch": torch_requirements, "dev": [ "setuptools", "types-setuptools", @@ -36,7 +32,7 @@ def read_file(filepath: str) -> str: "pytest-xdist", "pytest-timeout", # Docs - "automl-sphinx-theme>=0.1.9", + "automl-sphinx-theme>=0.1.18", # Others "mypy", "isort", @@ -44,6 +40,7 @@ def read_file(filepath: str) -> str: "pydocstyle", "flake8", "pre-commit", + *torch_requirements, ], } @@ -60,20 +57,21 @@ def read_file(filepath: str) -> str: version=version, packages=setuptools.find_packages(exclude=["tests"]), include_package_data=True, - python_requires=">=3.7", + python_requires=">=3.8", install_requires=[ - "numpy>=1.7.1", - "scipy>=1.7.0", + "numpy>=1.23.3", + "scipy>=1.9.2", "psutil", - "pynisher<1.0.0", - "ConfigSpace>=0.5.0", + "pynisher>=1.0.0", + "ConfigSpace>=0.6.0", "joblib", - "scikit-learn>=0.22.0", + "scikit-learn>=1.1.2", "pyrfr>=0.8.3", "dask", "distributed", "emcee>=3.0.0", "regex", + "pyyaml", ], extras_require=extras_require, test_suite="pytest", @@ -82,7 +80,6 @@ def read_file(filepath: str) -> str: "console_scripts": ["smac = smac.smac_cli:cmd_line_call"], }, classifiers=[ - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", diff --git a/smac/__init__.py b/smac/__init__.py index fa90f8557..c39700f34 100644 --- a/smac/__init__.py +++ b/smac/__init__.py @@ -1,7 +1,5 @@ import datetime -import os -import sys -import warnings +import traceback name = "SMAC3" package_name = "smac" @@ -20,13 +18,37 @@ copyright = f""" Copyright {datetime.date.today().strftime('%Y')}, Marius Lindauer, Katharina Eggensperger, Matthias Feurer, André Biedenkapp, Difan Deng, Carolin Benjamins, Tim Ruhkopf, René Sass - and Frank Hutter -""" -version = "1.4.0" + and Frank Hutter""" +version = "2.0.0a1" -if os.name != "posix": - warnings.warn( - f"Detected unsupported operating system: {sys.platform}." - "Please be aware, that SMAC might not run on this system." +try: + from smac.callback import Callback + from smac.facade import ( + AlgorithmConfigurationFacade, + BlackBoxFacade, + HyperbandFacade, + HyperparameterOptimizationFacade, + MultiFidelityFacade, + RandomFacade, ) + from smac.runhistory.runhistory import RunHistory + from smac.scenario import Scenario + from smac.utils.logging import setup_logging + + setup_logging(0) + + __all__ = [ + "Scenario", + "RunHistory", + "BlackBoxFacade", + "HyperparameterOptimizationFacade", + "MultiFidelityFacade", + "AlgorithmConfigurationFacade", + "RandomFacade", + "HyperbandFacade", + "Callback", + ] +except ModuleNotFoundError as e: + print(e) + traceback.print_exc() diff --git a/smac/epm/__init__.py b/smac/acquisition/__init__.py similarity index 100% rename from smac/epm/__init__.py rename to smac/acquisition/__init__.py diff --git a/smac/acquisition/function/__init__.py b/smac/acquisition/function/__init__.py new file mode 100644 index 000000000..5ac88a721 --- /dev/null +++ b/smac/acquisition/function/__init__.py @@ -0,0 +1,22 @@ +from smac.acquisition.function.abstract_acquisition_function import ( + AbstractAcquisitionFunction, +) +from smac.acquisition.function.confidence_bound import LCB +from smac.acquisition.function.expected_improvement import EI, EIPS +from smac.acquisition.function.integrated_acquisition_function import ( + IntegratedAcquisitionFunction, +) +from smac.acquisition.function.prior_acqusition_function import PriorAcquisitionFunction +from smac.acquisition.function.probability_improvement import PI +from smac.acquisition.function.thompson import TS + +__all__ = [ + "AbstractAcquisitionFunction", + "LCB", + "PI", + "EI", + "EIPS", + "TS", + "PriorAcquisitionFunction", + "IntegratedAcquisitionFunction", +] diff --git a/smac/acquisition/function/abstract_acquisition_function.py b/smac/acquisition/function/abstract_acquisition_function.py new file mode 100644 index 000000000..eca9288de --- /dev/null +++ b/smac/acquisition/function/abstract_acquisition_function.py @@ -0,0 +1,110 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import Any + +import numpy as np +from ConfigSpace import Configuration + +from smac.model.abstract_model import AbstractModel +from smac.utils.configspace import convert_configurations_to_array +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class AbstractAcquisitionFunction: + """Abstract base class for acquisition function.""" + + def __init__(self) -> None: + self._model: AbstractModel | None = None + + @property + def name(self) -> str: + """Returns the full name of the acquisition function.""" + raise NotImplementedError + + @property + def meta(self) -> dict[str, Any]: + """Returns the meta data of the created object.""" + return { + "name": self.__class__.__name__, + } + + @property + def model(self) -> AbstractModel | None: + """Returns the used surrogate model in the acquisition function.""" + return self._model + + @model.setter + def model(self, model: AbstractModel) -> None: + """Updates the surrogate model.""" + self._model = model + + def update(self, model: AbstractModel, **kwargs: Any) -> None: + """Updates the acquisition function attributes required for calculation. + + This method will be called after fitting the model, but before maximizing the acquisition + function. As an examples, EI uses it to update the current fmin. The default implementation only updates the + attributes of the acqusition function which are already present. + + Calls `_update` to update the acquisition function attributes. + + Parameters + ---------- + model : AbstractModel + The model which was used to fit the data. + kwargs : Any + Additional arguments to update the specific acquisition function. + """ + self.model = model + self._update(**kwargs) + + def _update(self, **kwargs: Any) -> None: + pass + + def __call__(self, configurations: list[Configuration]) -> np.ndarray: + """Compute the acquisition value for a given X. + + Parameters + ---------- + configurations : list[Configuration] + The configurations where the acquisition function should be evaluated. + + Returns + ------- + np.ndarray [N, 1] + Acquisition values for X + """ + X = convert_configurations_to_array(configurations) + if len(X.shape) == 1: + X = X[np.newaxis, :] + + acq = self._compute(X) + if np.any(np.isnan(acq)): + idx = np.where(np.isnan(acq))[0] + acq[idx, :] = -np.finfo(float).max + + return acq + + @abstractmethod + def _compute(self, X: np.ndarray) -> np.ndarray: + """Compute the acquisition value for a given point X. This function has to be overwritten + in a derived class. + + Parameters + ---------- + X : np.ndarray [N, D] + The input points where the acquisition function should be evaluated. The dimensionality of X is (N, D), + with N as the number of points to evaluate at and D is the number of dimensions of one X. + + Returns + ------- + np.ndarray [N,1] + Acquisition function values wrt X. + """ + raise NotImplementedError diff --git a/smac/acquisition/function/confidence_bound.py b/smac/acquisition/function/confidence_bound.py new file mode 100644 index 000000000..b72e76a69 --- /dev/null +++ b/smac/acquisition/function/confidence_bound.py @@ -0,0 +1,77 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np + +from smac.acquisition.function.abstract_acquisition_function import ( + AbstractAcquisitionFunction, +) +from smac.model.abstract_model import AbstractModel +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class LCB(AbstractAcquisitionFunction): + r"""Computes the lower confidence bound for a given x over the best so far value as acquisition value. + + :math:`LCB(X) = \mu(\mathbf{X}) - \sqrt(\beta_t)\sigma(\mathbf{X})` [SKKS10]_ + + with + + :math:`\beta_t = 2 \log( |D| t^2 / \beta)` + + :math:`\text{Input space} D` + :math:`\text{Number of input dimensions} |D|` + :math:`\text{Number of data points} t` + :math:`\text{Exploration/exploitation tradeoff} \beta` + + Returns -LCB(X) as the acquisition_function optimizer maximizes the acquisition value. + + Parameters + ---------- + beta : float, defaults to 1.0 + Controls the balance between exploration and exploitation of the acquisition function. + """ + + def __init__(self, beta: float = 1.0) -> None: + super(LCB, self).__init__() + self._model: AbstractModel | None = None + self._beta: float = beta + self._num_data: int | None = None + + @property + def name(self) -> str: # noqa: D102 + return "Lower Confidence Bound" + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update({"beta": self._beta}) + + return meta + + def _update(self, **kwargs: Any) -> None: + assert "num_data" in kwargs + self._num_data = kwargs["num_data"] + + def _compute(self, X: np.ndarray) -> np.ndarray: + """Computes the LCB value.""" + assert self._model is not None + if self._num_data is None: + raise ValueError( + "No current number of data points specified. Call `update` to inform the acqusition function." + ) + + if len(X.shape) == 1: + X = X[:, np.newaxis] + + m, var_ = self._model.predict_marginalized(X) + std = np.sqrt(var_) + beta_t = 2 * np.log((X.shape[1] * self._num_data**2) / self._beta) + + return -(m - np.sqrt(beta_t) * std) diff --git a/smac/acquisition/function/expected_improvement.py b/smac/acquisition/function/expected_improvement.py new file mode 100644 index 000000000..08d4b250c --- /dev/null +++ b/smac/acquisition/function/expected_improvement.py @@ -0,0 +1,206 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np +from scipy.stats import norm + +from smac.acquisition.function.abstract_acquisition_function import ( + AbstractAcquisitionFunction, +) +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class EI(AbstractAcquisitionFunction): + r"""Computes the expected improvement for a given x. + + :math:`EI(X) := \mathbb{E}\left[ \max\{0, f(\mathbf{X^+}) - f_{t+1}(\mathbf{X}) - \xi \} \right]`, + with :math:`f(X^+)` as the best location. + + Parameters + ---------- + xi : float, defaults to 0.0 + Controls the balance between exploration and exploitation of the + acquisition function. + log : bool, defaults to False + Whether the function values are in log-space. + + """ + + def __init__( + self, + xi: float = 0.0, + log: bool = False, + ) -> None: + super(EI, self).__init__() + + self._xi: float = xi + self._log: bool = log + self._eta: float | None = None + + @property + def name(self) -> str: # noqa: D102 + return "Expected Improvement" + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update( + { + "xi": self._xi, + "log": self._log, + } + ) + + return meta + + def _update(self, **kwargs: Any) -> None: + assert "eta" in kwargs + self._eta = kwargs["eta"] + + if "xi" in kwargs and kwargs["xi"] is not None: + self._xi = kwargs["xi"] + + def _compute(self, X: np.ndarray) -> np.ndarray: + """Computess the EI value and its derivatives.""" + assert self._model is not None + assert self._xi is not None + if self._eta is None: + raise ValueError("No `eta` specified. Call `update` to inform the acqusition function.") + + if not self._log: + if len(X.shape) == 1: + X = X[:, np.newaxis] + + m, v = self._model.predict_marginalized(X) + s = np.sqrt(v) + + def calculate_f() -> np.ndarray: + z = (self._eta - m - self._xi) / s + return (self._eta - m - self._xi) * norm.cdf(z) + s * norm.pdf(z) + + if np.any(s == 0.0): + # if std is zero, we have observed x on all instances + # using a RF, std should be never exactly 0.0 + # Avoid zero division by setting all zeros in s to one. + # Consider the corresponding results in f to be zero. + logger.warning("Predicted std is 0.0 for at least one sample.") + s_copy = np.copy(s) + s[s_copy == 0.0] = 1.0 + f = calculate_f() + f[s_copy == 0.0] = 0.0 + else: + f = calculate_f() + if (f < 0).any(): + raise ValueError("Expected Improvement is smaller than 0 for at least one " "sample.") + + return f + else: + if len(X.shape) == 1: + X = X[:, np.newaxis] + + m, var_ = self._model.predict_marginalized(X) + std = np.sqrt(var_) + + def calculate_log_ei() -> np.ndarray: + # we expect that f_min is in log-space + assert self._eta is not None + assert self._xi is not None + + f_min = self._eta - self._xi + v = (f_min - m) / std + return (np.exp(f_min) * norm.cdf(v)) - (np.exp(0.5 * var_ + m) * norm.cdf(v - std)) + + if np.any(std == 0.0): + # if std is zero, we have observed x on all instances + # using a RF, std should be never exactly 0.0 + # Avoid zero division by setting all zeros in s to one. + # Consider the corresponding results in f to be zero. + logger.warning("Predicted std is 0.0 for at least one sample.") + std_copy = np.copy(std) + std[std_copy == 0.0] = 1.0 + log_ei = calculate_log_ei() + log_ei[std_copy == 0.0] = 0.0 + else: + log_ei = calculate_log_ei() + + if (log_ei < 0).any(): + raise ValueError("Expected Improvement is smaller than 0 for at least one sample.") + + return log_ei.reshape((-1, 1)) + + +class EIPS(EI): + r"""Computess for a given x the expected improvement as acquisition value. + + :math:`EI(X) := \frac{\mathbb{E}\left[\max\{0,f(\mathbf{X^+})-f_{t+1}(\mathbf{X})-\xi\right]\}]}{np.log(r(x))}`, + with :math:`f(X^+)` as the best location and :math:`r(x)` as runtime. + + Parameters + ---------- + xi : float, defaults to 0.0 + Controls the balance between exploration and exploitation of the acquisition function. + """ + + def __init__(self, xi: float = 0.0) -> None: + super(EIPS, self).__init__(xi=xi) + + @property + def name(self) -> str: # noqa: D102 + return "Expected Improvement per Second" + + def _compute(self, X: np.ndarray) -> np.ndarray: + """Computess the EIPS value.""" + assert self._model is not None + if len(X.shape) == 1: + X = X[:, np.newaxis] + + m, v = self._model.predict_marginalized(X) + if m.shape[1] != 2: + raise ValueError("m has wrong shape: %s != (-1, 2)" % str(m.shape)) + if v.shape[1] != 2: + raise ValueError("v has wrong shape: %s != (-1, 2)" % str(v.shape)) + + m_cost = m[:, 0] + v_cost = v[:, 0] + + # The model already predicts log(runtime) + m_runtime = m[:, 1] + s = np.sqrt(v_cost) + + if self._eta is None: + raise ValueError( + "No current best specified. Call update(" + "eta=) to inform the acquisition function " + "about the current best value." + ) + + def calculate_f() -> np.ndarray: + z = (self._eta - m_cost - self._xi) / s + f = (self._eta - m_cost - self._xi) * norm.cdf(z) + s * norm.pdf(z) + f = f / m_runtime + + return f + + if np.any(s == 0.0): + # if std is zero, we have observed x on all instances + # using a RF, std should be never exactly 0.0 + # Avoid zero division by setting all zeros in s to one. + # Consider the corresponding results in f to be zero. + logger.warning("Predicted std is 0.0 for at least one sample.") + s_copy = np.copy(s) + s[s_copy == 0.0] = 1.0 + f = calculate_f() + f[s_copy == 0.0] = 0.0 + else: + f = calculate_f() + + if (f < 0).any(): + raise ValueError("Expected Improvement per Second is smaller than 0 " "for at least one sample.") + + return f.reshape((-1, 1)) diff --git a/smac/acquisition/function/integrated_acquisition_function.py b/smac/acquisition/function/integrated_acquisition_function.py new file mode 100644 index 000000000..c1e9d920f --- /dev/null +++ b/smac/acquisition/function/integrated_acquisition_function.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +from typing import Any + +import copy + +import numpy as np + +from smac.acquisition.function.abstract_acquisition_function import ( + AbstractAcquisitionFunction, +) +from smac.model.abstract_model import AbstractModel +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class IntegratedAcquisitionFunction(AbstractAcquisitionFunction): + r"""Marginalizes over model hyperparameters to compute the integrated acquisition function. + + See "Practical Bayesian Optimization of Machine Learning Algorithms" by Jasper Snoek et al. + (https://papers.nips.cc/paper/4522-practical-bayesian-optimization-of-machine-learning-algorithms.pdf) + for further details. + + Parameters + ---------- + acquisition_function : AbstractAcquisitionFunction + The acquisition function, which should be integrated. + """ + + def __init__(self, acquisition_function: AbstractAcquisitionFunction) -> None: + super().__init__() + self._acquisition_function: AbstractAcquisitionFunction = acquisition_function + self._functions: list[AbstractAcquisitionFunction] = [] + self._eta: float | None = None + + @property + def name(self) -> str: # noqa: D102 + return f"Integrated Acquisition Function ({self._acquisition_function.__class__.__name__})" + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update({"acquisition_function": self._acquisition_function.meta}) + + return meta + + def _update(self, **kwargs: Any) -> None: + """Updates the acquisition functions values. + + This method will be called if the model is updated. For example, entropy search uses it to update its + approximation of P(x=x_min) and EI uses it to update the current fmin. + + This implementation creates an acquisition function object for each model to integrate over and sets the + respective attributes for each acquisition function object. + + Parameters + ---------- + kwargs : Any + Keyword arguments for the model. + """ + model = self.model + models: list[AbstractModel] | None = None + if hasattr(model, "models"): + models = model.models # type: ignore + + if models is None or len(models) == 0: + raise ValueError("IntegratedAcquisitionFunction requires at least one model to integrate!") + + if len(self._functions) == 0 or len(self._functions) != len(models): + self._functions = [copy.deepcopy(self._acquisition_function) for _ in models] + + for submodel, func in zip(models, self._functions): + func.update(model=submodel, **kwargs) + + def _compute(self, X: np.ndarray) -> np.ndarray: + """Computess the EI value and its derivatives.""" + if self._functions is None: + raise ValueError("Need to call `update` first!") + + return np.array([func._compute(X) for func in self._functions]).mean(axis=0) diff --git a/smac/acquisition/function/prior_acqusition_function.py b/smac/acquisition/function/prior_acqusition_function.py new file mode 100644 index 000000000..3586487c2 --- /dev/null +++ b/smac/acquisition/function/prior_acqusition_function.py @@ -0,0 +1,226 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np +from ConfigSpace import Configuration +from ConfigSpace.hyperparameters import FloatHyperparameter + +from smac.acquisition.function.abstract_acquisition_function import ( + AbstractAcquisitionFunction, +) +from smac.acquisition.function.confidence_bound import LCB +from smac.acquisition.function.integrated_acquisition_function import ( + IntegratedAcquisitionFunction, +) +from smac.acquisition.function.thompson import TS +from smac.model.abstract_model import AbstractModel +from smac.model.random_forest.abstract_random_forest import AbstractRandomForest +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class PriorAcquisitionFunction(AbstractAcquisitionFunction): + r"""Weights the acquisition function with a user-defined prior over the optimum. + + See "piBO: Augmenting Acquisition Functions with User Beliefs for Bayesian Optimization" by Carl + Hvarfner et al. [HSSL22]_ for further details. + + Parameters + ---------- + decay_beta: float + Decay factor on the user prior. A solid default value for decay_beta (empirically founded) is + ``scenario.n_trials`` / 10. + prior_floor : float, defaults to 1e-12 + Lowest possible value of the prior to ensure non-negativity for all values in the search space. + discretize : bool, defaults to False + Whether to discretize (bin) the densities for continous parameters. Triggered for Random Forest models and + continous hyperparameters to avoid a pathological case where all Random Forest randomness is removed + (RF surrogates require piecewise constant acquisition functions to be well-behaved). + discrete_bins_factor : float, defaults to 10.0 + If discretizing, the multiple on the number of allowed bins for each parameter. + """ + + def __init__( + self, + acquisition_function: AbstractAcquisitionFunction, + decay_beta: float, + prior_floor: float = 1e-12, + discretize: bool = False, + discrete_bins_factor: float = 10.0, + ): + super().__init__() + self._acquisition_function: AbstractAcquisitionFunction = acquisition_function + self._functions: list[AbstractAcquisitionFunction] = [] + self._eta: float | None = None + + self._hyperparameters: dict[Any, Configuration] | None = None + self._decay_beta = decay_beta + self._prior_floor = prior_floor + self._discretize = discretize + self._discrete_bins_factor = discrete_bins_factor + + # check if the acquisition function is LCB or TS - then the acquisition function values + # need to be rescaled to assure positiveness & correct magnitude + if isinstance(self._acquisition_function, IntegratedAcquisitionFunction): + acquisition_type = self._acquisition_function._acquisition_function + else: + acquisition_type = self._acquisition_function + + self._rescale = isinstance(acquisition_type, (LCB, TS)) + self._iteration_number = 0 + + @property + def name(self) -> str: # noqa: D102 + return f"Prior Acquisition Function ({self._acquisition_function.__class__.__name__})" + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update( + { + "acquisition_function": self._acquisition_function.meta, + "decay_beta": self._decay_beta, + "prior_floor": self._prior_floor, + "discretize": self._discretize, + "discrete_bins_factor": self._discrete_bins_factor, + } + ) + + return meta + + @property + def model(self) -> AbstractModel | None: # noqa: D102 + return self._model + + @model.setter + def model(self, model: AbstractModel) -> None: + self._model = model + self._hyperparameters = model._configspace.get_hyperparameters_dict() + + if isinstance(model, AbstractRandomForest): + if not self._discretize: + logger.warning("Discretizing the prior for random forest models.") + self._discretize = True + + def _update(self, **kwargs: Any) -> None: + """Update the acquisition function attributes required for calculation. + + Parameters + ---------- + eta : float + Current incumbent value. + """ + assert "eta" in kwargs + self._iteration_number += 1 + self._eta = kwargs["eta"] + + assert self.model is not None + self._acquisition_function.update(model=self.model, **kwargs) + + def _compute_prior(self, X: np.ndarray) -> np.ndarray: + """Computes the prior-weighted acquisition function values, where the prior on each + parameter is multiplied by a decay factor controlled by the parameter decay_beta and + the iteration number. Multivariate priors are not supported, for now. + + Parameters + ---------- + X: np.ndarray [N, D] + The input points where the user-specified prior should be evaluated. The dimensionality of X is (N, D), + with N as the number of points to evaluate at and D is the number of dimensions of one X. + + Returns + ------- + np.ndarray [N, 1] + The user prior over the optimum for values of X. + """ + assert self._hyperparameters is not None + + prior_values = np.ones((len(X), 1)) + # iterate over the hyperparmeters (alphabetically sorted) and the columns, which come + # in the same order + for parameter, X_col in zip(self._hyperparameters.values(), X.T): + if self._discretize and isinstance(parameter, FloatHyperparameter): + assert self._discrete_bins_factor is not None + number_of_bins = int(np.ceil(self._discrete_bins_factor * self._decay_beta / self._iteration_number)) + prior_values *= self._compute_discretized_pdf(parameter, X_col, number_of_bins) + self._prior_floor + else: + prior_values *= parameter._pdf(X_col[:, np.newaxis]) + + return prior_values + + def _compute_discretized_pdf( + self, + hyperparameter: FloatHyperparameter, + X_col: np.ndarray, + number_of_bins: int, + ) -> np.ndarray: + """Discretizes (bins) prior values on continous a specific continous parameter + to an increasingly coarse discretization determined by the prior decay parameter. + + Parameters + ---------- + hyperparameter : FloatHyperparameter + A float hyperparameter that, due to using a random forest surrogate, must have its prior discretized. + X_col: np.ndarray [N, ] + The input points where the acquisition function should be evaluated. The dimensionality of X is (N, ), + with N as the number of points to evaluate for the specific hyperparameter. + number_of_bins : int + The number of unique values allowed on the discretized version of the pdf. + + Returns + ------- + np.ndarray [N, 1] + The user prior over the optimum for the parameter at hand. + """ + # Evaluates the actual pdf on all the relevant points + pdf_values = hyperparameter._pdf(X_col[:, np.newaxis]) + + # Retrieves the largest value of the pdf in the domain + lower, upper = (0, hyperparameter.get_max_density()) + + # Creates the bins (the possible discrete options of the pdf) + bin_values = np.linspace(lower, upper, number_of_bins) + + # Generates an index (bin) for each evaluated point + bin_indices = np.clip( + np.round((pdf_values - lower) * number_of_bins / (upper - lower)), 0, number_of_bins - 1 + ).astype(int) + + # Gets the actual value for each point + prior_values = bin_values[bin_indices] + + return prior_values + + def _compute(self, X: np.ndarray) -> np.ndarray: + """Computes the prior-weighted acquisition function values, where the prior on each + parameter is multiplied by a decay factor controlled by the parameter decay_beta and + the iteration number. Multivariate priors are not supported, for now. + + Parameters + ---------- + X: np.ndarray [N, D] + The input points where the acquisition function should be evaluated. The dimensionality of X is (N, D), + with N as the number of points to evaluate at and D is the number of dimensions of one X. + + Returns + ------- + np.ndarray [N, 1] + Prior-weighted acquisition function values of X + """ + if self._rescale: + # for TS and UCB, we need to scale the function values to not run into issues + # of negative values or issues of varying magnitudes (here, they are both) + # negative by design and just flipping the sign leads to picking the worst point) + acq_values = np.clip(self._acquisition_function._compute(X) + self._eta, 0, np.inf) + else: + acq_values = self._acquisition_function._compute(X) + + prior_values = self._compute_prior(X) + self._prior_floor + decayed_prior_values = np.power(prior_values, self._decay_beta / self._iteration_number) + + return acq_values * decayed_prior_values diff --git a/smac/acquisition/function/probability_improvement.py b/smac/acquisition/function/probability_improvement.py new file mode 100644 index 000000000..cf95e6815 --- /dev/null +++ b/smac/acquisition/function/probability_improvement.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np +from scipy.stats import norm + +from smac.acquisition.function.abstract_acquisition_function import ( + AbstractAcquisitionFunction, +) +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class PI(AbstractAcquisitionFunction): + r"""Computes the probability of improvement for a given x over the best so far value as acquisition value. + + :math:`P(f_{t+1}(\mathbf{X})\geq f(\mathbf{X^+}))` :math:`:= \Phi(\\frac{ \mu(\mathbf{X})-f(\mathbf{X^+}) } + { \sigma(\mathbf{X}) })` with :math:`f(X^+)` as the incumbent and :math:`\Phi` the cdf of the standard normal. + + Parameters + ---------- + xi : float, defaults to 0.0 + Controls the balance between exploration and exploitation of the acquisition function. + """ + + def __init__(self, xi: float = 0.0): + super(PI, self).__init__() + self._xi: float = xi + self._eta: float | None = None + + @property + def name(self) -> str: # noqa: D102 + return "Probability of Improvement" + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update({"xi": self._xi}) + + return meta + + def _update(self, **kwargs: Any) -> None: + assert "eta" in kwargs + self._eta = kwargs["eta"] + + if "xi" in kwargs and kwargs["xi"] is not None: + self._xi = kwargs["xi"] + + def _compute(self, X: np.ndarray) -> np.ndarray: + """Computes the PI value. + + Parameters + ---------- + X: np.ndarray [N, D] + Points to evaluate PI. N is the number of points and D the dimension for the points. + + Returns + ------- + np.ndarray [N, 1] + Expected Improvement of X. + """ + assert self._model is not None + if self._eta is None: + raise ValueError( + "No current best specified. Call update(" + "eta=) to inform the acquisition function " + "about the current best value." + ) + + if len(X.shape) == 1: + X = X[:, np.newaxis] + m, var_ = self._model.predict_marginalized(X) + std = np.sqrt(var_) + + return norm.cdf((self._eta - m - self._xi) / std) diff --git a/smac/acquisition/function/thompson.py b/smac/acquisition/function/thompson.py new file mode 100644 index 000000000..5d732ebbc --- /dev/null +++ b/smac/acquisition/function/thompson.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import numpy as np + +from smac.acquisition.function.abstract_acquisition_function import ( + AbstractAcquisitionFunction, +) +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class TS(AbstractAcquisitionFunction): + r"""Do a Thompson Sampling for a given x over the best so far value as acquisition value. + + Warning + ------- + Thompson Sampling can only be used together with `RandomSearch`. Please do not use `LocalAndSortedRandomSearch` to + optimize the TS acquisition function! + + :math:`TS(X) ~ \mathcal{N}(\mu(\mathbf{X}),\sigma(\mathbf{X}))' + Returns -TS(X) as the acquisition_function optimizer maximizes the acquisition value. + + Parameters + ---------- + xi : float, defaults to 0.0 + TS does not require xi here, we only wants to make it consistent with other acquisition functions. + """ + + @property + def name(self) -> str: # noqa: D102 + return "Thompson Sampling" + + def _compute(self, X: np.ndarray) -> np.ndarray: + """Sample a new value from a gaussian distribution whose mean and covariance values are given by model. + + Parameters + ---------- + X: np.ndarray [N, D] + Points to be evaluated where we could sample a value. N is the number of points and D the dimension + for the points. + + Returns + ------- + np.ndarray [N, 1] + Negative sample value of X. + """ + assert self._model + + if len(X.shape) == 1: + X = X[:, np.newaxis] + + sample_function = getattr(self._model, "sample_functions", None) + if callable(sample_function): + return -sample_function(X, n_funcs=1) + + m, var_ = self._model.predict_marginalized(X) + rng = self._model._rng + m = m.flatten() + var_ = np.diag(var_.flatten()) + + return -rng.multivariate_normal(m, var_, 1).T diff --git a/smac/acquisition/maximizer/__init__.py b/smac/acquisition/maximizer/__init__.py new file mode 100644 index 000000000..6fea59b45 --- /dev/null +++ b/smac/acquisition/maximizer/__init__.py @@ -0,0 +1,19 @@ +from smac.acquisition.maximizer.abstract_acqusition_maximizer import ( + AbstractAcquisitionMaximizer, +) +from smac.acquisition.maximizer.differential_evolution import DifferentialEvolution +from smac.acquisition.maximizer.local_and_random_search import ( + LocalAndSortedPriorRandomSearch, + LocalAndSortedRandomSearch, +) +from smac.acquisition.maximizer.local_search import LocalSearch +from smac.acquisition.maximizer.random_search import RandomSearch + +__all__ = [ + "AbstractAcquisitionMaximizer", + "DifferentialEvolution", + "LocalAndSortedRandomSearch", + "LocalAndSortedPriorRandomSearch", + "LocalSearch", + "RandomSearch", +] diff --git a/smac/acquisition/maximizer/abstract_acqusition_maximizer.py b/smac/acquisition/maximizer/abstract_acqusition_maximizer.py new file mode 100644 index 000000000..463f56785 --- /dev/null +++ b/smac/acquisition/maximizer/abstract_acqusition_maximizer.py @@ -0,0 +1,160 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import Any, Iterator + +import numpy as np +from ConfigSpace import Configuration, ConfigurationSpace + +from smac.acquisition.function.abstract_acquisition_function import ( + AbstractAcquisitionFunction, +) +from smac.acquisition.maximizer.helpers import ChallengerList +from smac.random_design.abstract_random_design import AbstractRandomDesign +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class AbstractAcquisitionMaximizer: + """Abstract class for the acquisition maximization. In order to use this class it has to be subclassed and the + method `_maximize` must be implemented. + + Parameters + ---------- + configspace : ConfigurationSpace + acquisition_function : AbstractAcquisitionFunction + challengers : int, defaults to 5000 + seed : int, defaults to 0 + """ + + def __init__( + self, + configspace: ConfigurationSpace, + acquisition_function: AbstractAcquisitionFunction | None = None, + challengers: int = 5000, + seed: int = 0, + ): + self._configspace = configspace + self._acquisition_function = acquisition_function + self._challengers = challengers + self._seed = seed + self._rng = np.random.RandomState(seed=seed) + + @property + def acquisition_function(self) -> AbstractAcquisitionFunction | None: + """The acquisition function used for maximization.""" + return self._acquisition_function + + @acquisition_function.setter + def acquisition_function(self, acquisition_function: AbstractAcquisitionFunction) -> None: + self._acquisition_function = acquisition_function + + @property + def meta(self) -> dict[str, Any]: + """Returns the meta data of the created object.""" + acquisition_function_meta = None + if self._acquisition_function is not None: + acquisition_function_meta = self._acquisition_function.meta + + return { + "name": self.__class__.__name__, + "acquisition_function": acquisition_function_meta, + "challengers": self._challengers, + "seed": self._seed, + } + + def maximize( + self, + previous_configs: list[Configuration], + n_points: int | None = None, + random_design: AbstractRandomDesign | None = None, + ) -> Iterator[Configuration]: + """Maximize acquisition function using `_maximize`, implemented by a subclass. + + Parameters + ---------- + previous_configs: list[Configuration] + Previous evaluated configurations. + n_points: int, defaults to None + Number of points to be sampled. If `n_points` is not specified, `self._challengers` is used. + random_design: AbstractRandomDesign, defaults to None + Part of the returned ChallengerList such that we can interleave random configurations by a scheme defined by + the random design. The method `random_design.next_iteration()` is called at the end of this function. + + Returns + ------- + challengers : Iterator[Configuration] + An iterable consisting of configurations. + """ + if n_points is None: + n_points = self._challengers + + def next_configs_by_acquisition_value() -> list[Configuration]: + assert n_points is not None + return [t[1] for t in self._maximize(previous_configs, n_points)] + + challengers = ChallengerList( + self._configspace, + next_configs_by_acquisition_value, + random_design, + ) + + if random_design is not None: + random_design.next_iteration() + + return challengers + + @abstractmethod + def _maximize( + self, + previous_configs: list[Configuration], + n_points: int, + ) -> list[tuple[float, Configuration]]: + """Implements acquisition function maximization. + + In contrast to `maximize`, this method returns an iterable of tuples, consisting of the acquisition function + value and the configuration. This allows to plug together different acquisition function maximizers. + + Parameters + ---------- + previous_configs: list[Configuration] + Previously evaluated configurations. + n_points: int + Number of points to be sampled. + + Returns + ------- + challengers : list[tuple[float, Configuration]] + A list consisting of tuples of acquisition_value and its configuration. + """ + raise NotImplementedError() + + def _sort_by_acquisition_value(self, configs: list[Configuration]) -> list[tuple[float, Configuration]]: + """Sort the given configurations by the acquisition value. + + Parameters + ---------- + configs : list[Configuration] + + Returns + ------- + challengers : list[tuple[float, Configuration]] + Candidates ordered by their acquisition value. + """ + assert self._acquisition_function is not None + acq_values = self._acquisition_function(configs) + + # From here + # http://stackoverflow.com/questions/20197990/how-to-make-argsort-result-to-be-random-between-equal-values + random = self._rng.rand(len(acq_values)) + + # Last column is primary sort key! + indices = np.lexsort((random.flatten(), acq_values.flatten())) + + # Cannot use zip here because the indices array cannot index the + # rand_configs list, because the second is a pure python list + return [(acq_values[ind][0], configs[ind]) for ind in indices[::-1]] diff --git a/smac/acquisition/maximizer/differential_evolution.py b/smac/acquisition/maximizer/differential_evolution.py new file mode 100644 index 000000000..b011fbe64 --- /dev/null +++ b/smac/acquisition/maximizer/differential_evolution.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +import numpy as np +from ConfigSpace import Configuration +from scipy.optimize._differentialevolution import DifferentialEvolutionSolver + +from smac.acquisition.maximizer import AbstractAcquisitionMaximizer + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class DifferentialEvolution(AbstractAcquisitionMaximizer): + """Get candidate solutions via `DifferentialEvolutionSolvers` from scipy.""" + + def _maximize( + self, + previous_configs: list[Configuration], + n_points: int, + ) -> list[tuple[float, Configuration]]: + + configs: list[tuple[float, Configuration]] = [] + + def func(x: np.ndarray) -> np.ndarray: + assert self._acquisition_function is not None + return -self._acquisition_function([Configuration(self._configspace, vector=x)]) + + ds = DifferentialEvolutionSolver( + func, + bounds=[[0, 1] for _ in range(len(self._configspace))], + args=(), + strategy="best1bin", + maxiter=1000, + popsize=50, + tol=0.01, + mutation=(0.5, 1), + recombination=0.7, + seed=self._rng.randint(1000), + polish=True, + callback=None, + disp=False, + init="latinhypercube", + atol=0, + ) + + _ = ds.solve() + for pop, val in zip(ds.population, ds.population_energies): + rc = Configuration(self._configspace, vector=pop) + rc.origin = "Differential Evolution" + configs.append((-val, rc)) + + configs.sort(key=lambda t: t[0]) + configs.reverse() + + return configs diff --git a/smac/acquisition/maximizer/helpers.py b/smac/acquisition/maximizer/helpers.py new file mode 100644 index 000000000..ed3353f31 --- /dev/null +++ b/smac/acquisition/maximizer/helpers.py @@ -0,0 +1,112 @@ +from __future__ import annotations + +from typing import Callable, Iterator + +from ConfigSpace import Configuration, ConfigurationSpace + +from smac.random_design.abstract_random_design import AbstractRandomDesign +from smac.random_design.modulus_design import ModulusRandomDesign + + +class ChallengerList(Iterator): + """Helper class to interleave random configurations in a list of challengers. + + Provides an iterator which returns a random configuration in each second + iteration. Reduces time necessary to generate a list of new challengers + as one does not need to sample several hundreds of random configurations + in each iteration which are never looked at. + + Parameters + ---------- + configspace : ConfigurationSpace + challenger_callback : Callable + Callback function which returns a list of challengers (without interleaved random configurations, must a be a + closure: https://www.programiz.com/python-programming/closure) + random_design : AbstractRandomDesign | None, defaults to ModulusRandomDesign(modulus=2.0) + Which random design should be used. + """ + + def __init__( + self, + configspace: ConfigurationSpace, + challenger_callback: Callable, + random_design: AbstractRandomDesign | None = ModulusRandomDesign(modulus=2.0), + ): + self._challengers_callback = challenger_callback + self._challengers: list[Configuration] | None = None + self._configspace = configspace + self._index = 0 + self._iteration = 1 # 1-based to prevent from starting with a random configuration + self._random_design = random_design + + def __next__(self) -> Configuration: + if self._challengers is not None and self._index == len(self._challengers): + raise StopIteration + elif self._random_design is None: + if self._challengers is None: + self._challengers = self._challengers_callback() + + config = self._challengers[self._index] + self._index += 1 + + return config + else: + if self._random_design.check(self._iteration): + config = self._configspace.sample_configuration() + config.origin = "Random Search" + else: + if self._challengers is None: + self._challengers = self._challengers_callback() + + config = self._challengers[self._index] + self._index += 1 + self._iteration += 1 + + return config + + def __len__(self) -> int: + if self._challengers is None: + self._challengers = self._challengers_callback() + + return len(self._challengers) - self._index + + +''' +class FixedSet(AbstractAcquisitionMaximizer): + def __init__( + self, + configurations: list[Configuration], + acquisition_function: AbstractAcquisitionFunction, + configspace: ConfigurationSpace, + challengers: int = 5000, + seed: int = 0, + ): + """Maximize the acquisition function over a finite list of configurations. + + Parameters + ---------- + configurations : list[~smac._configspace.Configuration] + Candidate configurations + acquisition_function : ~smac.acquisition.AbstractAcquisitionFunction + + configspace : ~smac._configspace.ConfigurationSpace + + rng : np.random.RandomState or int, optional + """ + super().__init__( + acquisition_function=acquisition_function, configspace=configspace, challengers=challengers, seed=seed + ) + self.configurations = configurations + + def _maximize( + self, + runhistory: RunHistory, + stats: Stats, + n_points: int, + ) -> list[tuple[float, Configuration]]: + configurations = copy.deepcopy(self.configurations) + for config in configurations: + config.origin = "Fixed Set" + + return self._sort_by_acquisition_value(configurations) +''' diff --git a/smac/acquisition/maximizer/local_and_random_search.py b/smac/acquisition/maximizer/local_and_random_search.py new file mode 100644 index 000000000..eaf6f96bf --- /dev/null +++ b/smac/acquisition/maximizer/local_and_random_search.py @@ -0,0 +1,251 @@ +from __future__ import annotations + +from typing import Any + +from ConfigSpace import Configuration, ConfigurationSpace + +from smac.acquisition.function import AbstractAcquisitionFunction +from smac.acquisition.maximizer.abstract_acqusition_maximizer import ( + AbstractAcquisitionMaximizer, +) +from smac.acquisition.maximizer.local_search import LocalSearch +from smac.acquisition.maximizer.random_search import RandomSearch +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class LocalAndSortedRandomSearch(AbstractAcquisitionMaximizer): + """Implements SMAC's default acquisition function optimization. + + This optimizer performs local search from the previous best points according, to the acquisition function, uses the + acquisition function to sort randomly sampled configurations. Random configurations are interleaved by the main SMAC + code. + + Parameters + ---------- + configspace : ConfigurationSpace + acquisition_function : AbstractAcquisitionFunction | None, defaults to None + challengers : int, defaults to 5000 + Number of challengers. + max_steps: int | None, defaults to None + [LocalSearch] Maximum number of steps that the local search will perform. + n_steps_plateau_walk: int, defaults to 10 + [LocalSearch] number of steps during a plateau walk before local search terminates + local_search_iterations: int, defauts to 10 + [Local Search] number of local search iterations + seed : int, defaults to 0 + """ + + def __init__( + self, + configspace: ConfigurationSpace, + acquisition_function: AbstractAcquisitionFunction | None = None, + challengers: int = 5000, + max_steps: int | None = None, + n_steps_plateau_walk: int = 10, + local_search_iterations: int = 10, + seed: int = 0, + ) -> None: + super().__init__( + configspace, + acquisition_function=acquisition_function, + challengers=challengers, + seed=seed, + ) + + self._random_search = RandomSearch( + configspace=configspace, + acquisition_function=acquisition_function, + seed=seed, + ) + + self._local_search = LocalSearch( + configspace=configspace, + acquisition_function=acquisition_function, + max_steps=max_steps, + n_steps_plateau_walk=n_steps_plateau_walk, + seed=seed, + ) + + self._local_search_iterations = local_search_iterations + + @property + def acquisition_function(self) -> AbstractAcquisitionFunction | None: # noqa: D102 + """Returns the used acquisition function.""" + return self._acquisition_function + + @acquisition_function.setter + def acquisition_function(self, acquisition_function: AbstractAcquisitionFunction) -> None: + self._acquisition_function = acquisition_function + self._random_search._acquisition_function = acquisition_function + self._local_search._acquisition_function = acquisition_function + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update( + { + "random_search": self._random_search.meta, + "local_search": self._local_search.meta, + } + ) + + return meta + + def _maximize( + self, + previous_configs: list[Configuration], + n_points: int, + ) -> list[tuple[float, Configuration]]: + + # Get configurations sorted by EI + next_configs_by_random_search_sorted = self._random_search._maximize( + previous_configs=previous_configs, + n_points=n_points, + _sorted=True, + ) + + next_configs_by_local_search = self._local_search._maximize( + previous_configs=previous_configs, + n_points=self._local_search_iterations, + additional_start_points=next_configs_by_random_search_sorted, + ) + + # Having the configurations from random search, sorted by their + # acquisition function value is important for the first few iterations + # of SMAC. As long as the random forest predicts constant value, we + # want to use only random configurations. Having them at the begging of + # the list ensures this (even after adding the configurations by local + # search, and then sorting them) + next_configs_by_acq_value = next_configs_by_random_search_sorted + next_configs_by_local_search + next_configs_by_acq_value.sort(reverse=True, key=lambda x: x[0]) + first_five = [f"{_[0]} ({_[1].origin})" for _ in next_configs_by_acq_value[:5]] + + logger.debug( + "First 5 acquisition function values of selected configurations:\n%s", + ", ".join(first_five), + ) + + return next_configs_by_acq_value + + +class LocalAndSortedPriorRandomSearch(AbstractAcquisitionMaximizer): + """Implements SMAC's default acquisition function optimization. + + This optimizer performs local search from the previous best points according to the acquisition function, uses the + acquisition function to sort randomly sampled configurations. Random configurations are interleaved by the main SMAC + code. The random configurations are retrieved from two different ConfigurationSpaces - one which uses priors + (e.g. NormalFloatHP) and is defined by the user, and one that is a uniform version of the same space, i.e. with the + priors removed. + + Parameters + ---------- + configspace : ConfigurationSpace + The original ConfigurationSpace specified by the user. + uniform_configspace : ConfigurationSpace + A version of the user-defined ConfigurationSpace where all parameters are uniform (or have their weights removed + in the case of a categorical hyperparameter). + acquisition_function : AbstractAcquisitionFunction | None, defaults to None + challengers : int, defaults to 5000 + Number of challengers. + max_steps: int, defaults to None + [LocalSearch] Maximum number of steps that the local search will perform. + n_steps_plateau_walk: int, defaults to 10 + [LocalSearch] number of steps during a plateau walk before local search terminates. + local_search_iterations: int, defaults to 10 + [Local Search] number of local search iterations. + prior_sampling_fraction: float, defaults to 0.5 + The ratio of random samples that are taken from the user-defined ConfigurationSpace, as opposed to the uniform + version. + seed : int, defaults to 0 + """ + + def __init__( + self, + configspace: ConfigurationSpace, + uniform_configspace: ConfigurationSpace, + acquisition_function: AbstractAcquisitionFunction | None = None, + challengers: int = 5000, + max_steps: int | None = None, + n_steps_plateau_walk: int = 10, + local_search_iterations: int = 10, + prior_sampling_fraction: float = 0.5, + seed: int = 0, + ) -> None: + super().__init__( + acquisition_function, + configspace, + challengers=challengers, + seed=seed, + ) + + self._prior_random_search = RandomSearch( + acquisition_function=acquisition_function, + configspace=configspace, + seed=seed, + ) + + self._uniform_random_search = RandomSearch( + acquisition_function=acquisition_function, + configspace=uniform_configspace, + seed=seed, + ) + + self._local_search = LocalSearch( + acquisition_function=acquisition_function, + configspace=configspace, + max_steps=max_steps, + n_steps_plateau_walk=n_steps_plateau_walk, + seed=seed, + ) + + self._local_search_iterations = local_search_iterations + self._prior_sampling_fraction = prior_sampling_fraction + + def _maximize( + self, + previous_configs: list[Configuration], + n_points: int, + ) -> list[tuple[float, Configuration]]: + + # Get configurations sorted by EI + next_configs_by_prior_random_search_sorted = self._prior_random_search._maximize( + previous_configs, + round(n_points * self._prior_sampling_fraction), + _sorted=True, + ) + + # Get configurations sorted by EI + next_configs_by_uniform_random_search_sorted = self._uniform_random_search._maximize( + previous_configs, + round(n_points * (1 - self._prior_sampling_fraction)), + _sorted=True, + ) + next_configs_by_random_search_sorted = [] + next_configs_by_random_search_sorted.extend(next_configs_by_prior_random_search_sorted) + next_configs_by_random_search_sorted.extend(next_configs_by_uniform_random_search_sorted) + + next_configs_by_local_search = self._local_search._maximize( + previous_configs, + self._local_search_iterations, + additional_start_points=next_configs_by_random_search_sorted, + ) + + # Having the configurations from random search, sorted by their + # acquisition function value is important for the first few iterations + # of SMAC. As long as the random forest predicts constant value, we + # want to use only random configurations. Having them at the begging of + # the list ensures this (even after adding the configurations by local + # search, and then sorting them) + next_configs_by_acq_value = next_configs_by_random_search_sorted + next_configs_by_local_search + next_configs_by_acq_value.sort(reverse=True, key=lambda x: x[0]) + logger.debug( + "First 5 acq func (origin) values of selected configurations: %s", + str([[_[0], _[1].origin] for _ in next_configs_by_acq_value[:5]]), + ) + + return next_configs_by_acq_value diff --git a/smac/acquisition/maximizer/local_search.py b/smac/acquisition/maximizer/local_search.py new file mode 100644 index 000000000..db5e66828 --- /dev/null +++ b/smac/acquisition/maximizer/local_search.py @@ -0,0 +1,370 @@ +from __future__ import annotations + +from typing import Any + +import itertools +import time + +import numpy as np +from ConfigSpace import Configuration, ConfigurationSpace +from ConfigSpace.exceptions import ForbiddenValueError + +from smac.acquisition.function import AbstractAcquisitionFunction +from smac.acquisition.maximizer.abstract_acqusition_maximizer import ( + AbstractAcquisitionMaximizer, +) +from smac.utils.configspace import ( + convert_configurations_to_array, + get_one_exchange_neighbourhood, +) +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class LocalSearch(AbstractAcquisitionMaximizer): + """Implementation of SMAC's local search. + + Parameters + ---------- + configspace : ConfigurationSpace + acquisition_function : AbstractAcquisitionFunction + challengers : int, defaults to 5000 + Number of challengers. + max_steps: int | None, defaults to None + Maximum number of iterations that the local search will perform. + n_steps_plateau_walk: int, defaults to 10 + Number of steps during a plateau walk before local search terminates. + vectorization_min_obtain : int, defaults to 2 + Minimal number of neighbors to obtain at once for each local search for vectorized calls. Can be tuned to + reduce the overhead of SMAC. + vectorization_max_obtain : int, defaults to 64 + Maximal number of neighbors to obtain at once for each local search for vectorized calls. Can be tuned to + reduce the overhead of SMAC. + seed : int, defaults to 0 + """ + + def __init__( + self, + configspace: ConfigurationSpace, + acquisition_function: AbstractAcquisitionFunction | None = None, + challengers: int = 5000, + max_steps: int | None = None, + n_steps_plateau_walk: int = 10, + vectorization_min_obtain: int = 2, + vectorization_max_obtain: int = 64, + seed: int = 0, + ) -> None: + super().__init__( + configspace, + acquisition_function, + challengers=challengers, + seed=seed, + ) + + self._max_steps = max_steps + self._n_steps_plateau_walk = n_steps_plateau_walk + self._vectorization_min_obtain = vectorization_min_obtain + self._vectorization_max_obtain = vectorization_max_obtain + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update( + { + "max_steps": self._max_steps, + "n_steps_plateau_walk": self._n_steps_plateau_walk, + "vectorization_min_obtain": self._vectorization_min_obtain, + "vectorization_max_obtain": self._vectorization_max_obtain, + } + ) + + return meta + + def _maximize( + self, + previous_configs: list[Configuration], + n_points: int, + additional_start_points: list[tuple[float, Configuration]] | None = None, + ) -> list[tuple[float, Configuration]]: + """Starts a local search from the given startpoint and quits if either the max number of + steps is reached or no neighbor with an higher improvement was found. + """ + init_points = self._get_initial_points(previous_configs, n_points, additional_start_points) + configs_acq = self._search(init_points) + + # Shuffle for random tie-break + self._rng.shuffle(configs_acq) + + # Sort according to acq value + configs_acq.sort(reverse=True, key=lambda x: x[0]) + for a, inc in configs_acq: + inc.origin = "Local Search" + + return configs_acq + + def _get_initial_points( + self, + previous_configs: list[Configuration], + n_points: int, + additional_start_points: list[tuple[float, Configuration]] | None, + ) -> list[Configuration]: + if len(previous_configs) == 0: + init_points = self._configspace.sample_configuration(size=n_points) + else: + init_points = self._get_init_points_from_previous_configs( + previous_configs, + n_points, + additional_start_points, + ) + + return init_points + + def _get_init_points_from_previous_configs( + self, + previous_configs: list[Configuration], + n_points: int, + additional_start_points: list[tuple[float, Configuration]] | None, + ) -> list[Configuration]: + """ + A function that generates a set of initial points from the previous configurations and additional points (if + applicable). The idea is to decouple runhistory from the local search model and replace it with a more genreal + form (list[Configuration]). + + Parameters + ---------- + previous_configs: list[Configuration] + Previous configuration (e.g., from the runhistory). + n_points: int + Number of initial points to be generated. + additional_start_points: list[tuple[float, Configuration]] | None + if we want to specify another set of points as initial points + + Returns + ------- + init_points: list[Configuration] + A list of initial points. + """ + assert self._acquisition_function is not None + + # configurations with the highest previous EI + configs_previous_runs_sorted = self._sort_by_acquisition_value(previous_configs) + configs_previous_runs_sorted = [conf[1] for conf in configs_previous_runs_sorted[:n_points]] + + # configurations with the lowest predictive cost, check for None to make unit tests work + if self._acquisition_function.model is not None: + conf_array = convert_configurations_to_array(previous_configs) + costs = self._acquisition_function.model.predict_marginalized(conf_array)[0] + assert len(conf_array) == len(costs), (conf_array.shape, costs.shape) + + # In case of the predictive model returning the prediction for more than one objective per configuration + # (for example multi-objective or EIPS) it is not immediately clear how to sort according to the cost + # of a configuration. Therefore, we simply follow the ParEGO approach and use a random scalarization. + if len(costs.shape) == 2 and costs.shape[1] > 1: + weights = np.array([self._rng.rand() for _ in range(costs.shape[1])]) + weights = weights / np.sum(weights) + costs = costs @ weights + + # From here + # http://stackoverflow.com/questions/20197990/how-to-make-argsort-result-to-be-random-between-equal-values + random = self._rng.rand(len(costs)) + + # Last column is primary sort key! + indices = np.lexsort((random.flatten(), costs.flatten())) + + # Cannot use zip here because the indices array cannot index the + # rand_configs list, because the second is a pure python list + previous_configs_sorted_by_cost = [previous_configs[ind] for ind in indices][:n_points] + else: + previous_configs_sorted_by_cost = [] + + if additional_start_points is not None: + additional_start_points = [asp[1] for asp in additional_start_points[:n_points]] + else: + additional_start_points = [] + + init_points = [] + init_points_as_set: set[Configuration] = set() + for cand in itertools.chain( + configs_previous_runs_sorted, + previous_configs_sorted_by_cost, + additional_start_points, + ): + if cand not in init_points_as_set: + init_points.append(cand) + init_points_as_set.add(cand) + + return init_points + + def _search( + self, + start_points: list[Configuration], + ) -> list[tuple[float, Configuration]]: + assert self._acquisition_function is not None + + # Gather data structure for starting points + if isinstance(start_points, Configuration): + start_points = [start_points] + + candidates = start_points + # Compute the acquisition value of the candidates + num_candidates = len(candidates) + acq_val_candidates_ = self._acquisition_function(candidates) + + if num_candidates == 1: + acq_val_candidates = [acq_val_candidates_[0][0]] + else: + acq_val_candidates = [a[0] for a in acq_val_candidates_] + + # Set up additional variables required to do vectorized local search: + # whether the i-th local search is still running + active = [True] * num_candidates + # number of plateau walks of the i-th local search. Reaching the maximum number is the stopping criterion of + # the local search. + n_no_plateau_walk = [0] * num_candidates + # tracking the number of steps for logging purposes + local_search_steps = [0] * num_candidates + # tracking the number of neighbors looked at for logging purposes + neighbors_looked_at = [0] * num_candidates + # tracking the number of neighbors generated for logging purposse + neighbors_generated = [0] * num_candidates + # how many neighbors were obtained for the i-th local search. Important to map the individual acquisition + # function values to the correct local search run + obtain_n = [self._vectorization_min_obtain] * num_candidates + # Tracking the time it takes to compute the acquisition function + times = [] + + # Set up the neighborhood generators + neighborhood_iterators = [] + for i, inc in enumerate(candidates): + neighborhood_iterators.append( + get_one_exchange_neighbourhood(inc, seed=self._rng.randint(low=0, high=100000)) + ) + local_search_steps[i] += 1 + + # Keeping track of configurations with equal acquisition value for plateau walking + neighbors_w_equal_acq: list[list[Configuration]] = [[] for _ in range(num_candidates)] + + num_iters = 0 + while np.any(active): + num_iters += 1 + # Whether the i-th local search improved. When a new neighborhood is generated, this is used to determine + # whether a step was made (improvement) or not (iterator exhausted) + improved = [False] * num_candidates + # Used to request a new neighborhood for the candidates of the i-th local search + new_neighborhood = [False] * num_candidates + + # gather all neighbors + neighbors = [] + for i, neighborhood_iterator in enumerate(neighborhood_iterators): + if active[i]: + neighbors_for_i = [] + for j in range(obtain_n[i]): + try: + n = next(neighborhood_iterator) + neighbors_generated[i] += 1 + neighbors_for_i.append(n) + except ValueError as e: + # `neighborhood_iterator` raises `ValueError` with some probability when it reaches + # an invalid configuration. + logger.debug(e) + new_neighborhood[i] = True + except StopIteration: + new_neighborhood[i] = True + break + obtain_n[i] = len(neighbors_for_i) + neighbors.extend(neighbors_for_i) + + if len(neighbors) != 0: + start_time = time.time() + acq_val = self._acquisition_function(neighbors) + end_time = time.time() + times.append(end_time - start_time) + if np.ndim(acq_val.shape) == 0: + acq_val = np.asarray([acq_val]) + + # Comparing the acquisition function of the neighbors with the acquisition value of the candidate + acq_index = 0 + # Iterating the all i local searches + for i in range(num_candidates): + if not active[i]: + continue + + # And for each local search we know how many neighbors we obtained + for j in range(obtain_n[i]): + # The next line is only true if there was an improvement and we basically need to iterate to + # the i+1-th local search + if improved[i]: + acq_index += 1 + else: + neighbors_looked_at[i] += 1 + + # Found a better configuration + if acq_val[acq_index] > acq_val_candidates[i]: + is_valid = False + try: + neighbors[acq_index].is_valid_configuration() + is_valid = True + except (ValueError, ForbiddenValueError) as e: + logger.debug("Local search %d: %s", i, e) + + if is_valid: + # We comment this as it just spams the log + # logger.debug( + # "Local search %d: Switch to one of the neighbors (after %d configurations).", + # i, + # neighbors_looked_at[i], + # ) + candidates[i] = neighbors[acq_index] + acq_val_candidates[i] = acq_val[acq_index] + new_neighborhood[i] = True + improved[i] = True + local_search_steps[i] += 1 + neighbors_w_equal_acq[i] = [] + obtain_n[i] = 1 + # Found an equally well performing configuration, keeping it for plateau walking + elif acq_val[acq_index] == acq_val_candidates[i]: + neighbors_w_equal_acq[i].append(neighbors[acq_index]) + + acq_index += 1 + + # Now we check whether we need to create new neighborhoods and whether we need to increase the number of + # plateau walks for one of the local searches. Also disables local searches if the number of plateau walks + # is reached (and all being switched off is the termination criterion). + for i in range(num_candidates): + if not active[i]: + continue + + if obtain_n[i] == 0 or improved[i]: + obtain_n[i] = 2 + else: + obtain_n[i] = obtain_n[i] * 2 + obtain_n[i] = min(obtain_n[i], self._vectorization_max_obtain) + + if new_neighborhood[i]: + if not improved[i] and n_no_plateau_walk[i] < self._n_steps_plateau_walk: + if len(neighbors_w_equal_acq[i]) != 0: + candidates[i] = neighbors_w_equal_acq[i][0] + neighbors_w_equal_acq[i] = [] + n_no_plateau_walk[i] += 1 + if n_no_plateau_walk[i] >= self._n_steps_plateau_walk: + active[i] = False + continue + + neighborhood_iterators[i] = get_one_exchange_neighbourhood( + candidates[i], + seed=self._rng.randint(low=0, high=100000), + ) + + logger.debug( + "Local searches took %s steps and looked at %s configurations. Computing the acquisition function in " + "vectorized for took %f seconds on average.", + local_search_steps, + neighbors_looked_at, + np.mean(times), + ) + + return [(a, i) for a, i in zip(acq_val_candidates, candidates)] diff --git a/smac/acquisition/maximizer/random_search.py b/smac/acquisition/maximizer/random_search.py new file mode 100644 index 000000000..bde06e8c3 --- /dev/null +++ b/smac/acquisition/maximizer/random_search.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +from ConfigSpace import Configuration + +from smac.acquisition.maximizer.abstract_acqusition_maximizer import ( + AbstractAcquisitionMaximizer, +) +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class RandomSearch(AbstractAcquisitionMaximizer): + """Get candidate solutions via random sampling of configurations.""" + + def _maximize( + self, + previous_configs: list[Configuration], + n_points: int, + _sorted: bool = False, + ) -> list[tuple[float, Configuration]]: + """Randomly sampled configurations.""" + if n_points > 1: + rand_configs = self._configspace.sample_configuration(size=n_points) + else: + rand_configs = [self._configspace.sample_configuration(size=1)] + + if _sorted: + for i in range(len(rand_configs)): + rand_configs[i].origin = "Random Search (sorted)" + + return self._sort_by_acquisition_value(rand_configs) + else: + for i in range(len(rand_configs)): + rand_configs[i].origin = "Random Search" + + return [(0, rand_configs[i]) for i in range(len(rand_configs))] diff --git a/smac/callback.py b/smac/callback.py new file mode 100644 index 000000000..3901cf38d --- /dev/null +++ b/smac/callback.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from ConfigSpace import Configuration + +import smac +from smac.runhistory import TrialInfo, TrialInfoIntent, TrialValue + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class Callback: + """Callback interface with several methods that are called at different stages of the optimization process.""" + + def __init__(self) -> None: + pass + + def on_start(self, smbo: smac.main.BaseSMBO) -> None: + """Called before the optimization starts.""" + pass + + def on_end(self, smbo: smac.main.BaseSMBO) -> None: + """Called after the optimization finished.""" + pass + + def on_iteration_start(self, smbo: smac.main.BaseSMBO) -> None: + """Called before the next run is sampled.""" + pass + + def on_iteration_end(self, smbo: smac.main.BaseSMBO) -> None: + """Called after an iteration ended.""" + pass + + def on_next_configurations_start(self, smbo: smac.main.BaseSMBO) -> None: + """Called before the intensification asks for new configurations. Essentially, this callback is called + before the surrogate model is trained and before the acquisition function is called. + """ + pass + + def on_next_configurations_end(self, smbo: smac.main.BaseSMBO, configurations: list[Configuration]) -> None: + """Called after the intensification asks for new configurations. Essentially, this callback is called + before the surrogate model is trained and before the acquisition function is called. + """ + pass + + def on_ask_start(self, smbo: smac.main.BaseSMBO) -> None: + """Called before the intensifier is asked for the next trial.""" + pass + + def on_ask_end(self, smbo: smac.main.BaseSMBO, intent: TrialInfoIntent, info: TrialInfo) -> None: + """Called after the intensifier is asked for the next trial.""" + pass + + def on_tell_start(self, smbo: smac.main.BaseSMBO, info: TrialInfo, value: TrialValue) -> bool | None: + """Called before the stats are updated and the trial is added to the runhistory. Optionally, returns false + to gracefully stop the optimization. + """ + pass + + def on_tell_end(self, smbo: smac.main.BaseSMBO, info: TrialInfo, value: TrialValue) -> bool | None: + """Called after the stats are updated and the trial is added to the runhistory. Optionally, returns false + to gracefully stop the optimization. + """ + pass diff --git a/smac/callbacks.py b/smac/callbacks.py deleted file mode 100644 index 87ecd1d37..000000000 --- a/smac/callbacks.py +++ /dev/null @@ -1,26 +0,0 @@ -from typing import TYPE_CHECKING, Optional - -if TYPE_CHECKING: - from smac.optimizer.smbo import SMBO -from smac.runhistory.runhistory import RunInfo, RunValue - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class IncorporateRunResultCallback: - """Callback to react on a new run result. - - Called after the finished run is added to the runhistory. Optionally - return `False` to (gracefully) stop the optimization. - """ - - def __call__( - self, - smbo: "SMBO", - run_info: RunInfo, - result: RunValue, - time_left: float, - ) -> Optional[bool]: - """Calls the callback.""" - ... diff --git a/smac/configspace/__init__.py b/smac/configspace/__init__.py deleted file mode 100644 index c1af4df4f..000000000 --- a/smac/configspace/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -from functools import partial - -from ConfigSpace import ( - CategoricalHyperparameter, - Configuration, - ConfigurationSpace, - Constant, - InCondition, - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) -from ConfigSpace.exceptions import ForbiddenValueError -from ConfigSpace.read_and_write import json, pcs, pcs_new -from ConfigSpace.util import get_one_exchange_neighbourhood - -from smac.configspace.util import convert_configurations_to_array - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -__all__ = [ - "ConfigurationSpace", - "Configuration", - "Constant", - "CategoricalHyperparameter", - "UniformFloatHyperparameter", - "UniformIntegerHyperparameter", - "InCondition", - "pcs", - "pcs_new", - "json", - "get_one_exchange_neighbourhood", - "convert_configurations_to_array", - "ForbiddenValueError", -] - -get_one_exchange_neighbourhood = partial(get_one_exchange_neighbourhood, stdev=0.05, num_neighbors=8) diff --git a/smac/configspace/util.py b/smac/configspace/util.py deleted file mode 100644 index 621e9e753..000000000 --- a/smac/configspace/util.py +++ /dev/null @@ -1,25 +0,0 @@ -from typing import List - -import numpy as np - -from smac.configspace import Configuration - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def convert_configurations_to_array(configs: List[Configuration]) -> np.ndarray: - """Impute inactive hyperparameters in configurations with their default. - - Necessary to apply an EPM to the data. - - Parameters - ---------- - configs : List[Configuration] - List of configuration objects. - - Returns - ------- - np.ndarray - """ - return np.array([config.get_array() for config in configs], dtype=np.float64) diff --git a/smac/constants.py b/smac/constants.py new file mode 100644 index 000000000..7db48ebe8 --- /dev/null +++ b/smac/constants.py @@ -0,0 +1,9 @@ +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +MAXINT = 2**31 - 1 +MINIMAL_COST_FOR_LOG = 0.00001 +MAX_CUTOFF = 65535 +VERY_SMALL_NUMBER = 1e-10 # The very small number is used to avoid numerical instabilities or dividing by zero +N_TREES = 10 diff --git a/smac/epm/base_epm.py b/smac/epm/base_epm.py deleted file mode 100644 index aa7dafc16..000000000 --- a/smac/epm/base_epm.py +++ /dev/null @@ -1,313 +0,0 @@ -from typing import List, Optional, Tuple - -import copy -import warnings - -import numpy as np -from sklearn.decomposition import PCA -from sklearn.exceptions import NotFittedError -from sklearn.preprocessing import MinMaxScaler - -from smac.configspace import ConfigurationSpace -from smac.utils.constants import VERY_SMALL_NUMBER -from smac.utils.logging import PickableLoggerAdapter - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2016, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Marius Lindauer" -__email__ = "lindauer@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -class BaseEPM: - """Abstract implementation of the EPM API. - - **Note:** The input dimensionality of Y for training and the output dimensions - of all predictions (also called ``n_objectives``) depends on the concrete - implementation of this abstract class. - - Parameters - ---------- - configspace : ConfigurationSpace - Configuration space to tune for. - types : List[int] - Specifies the number of categorical values of an input dimension where - the i-th entry corresponds to the i-th input dimension. Let's say we - have 2 dimension where the first dimension consists of 3 different - categorical choices and the second dimension is continuous than we - have to pass [3, 0]. Note that we count starting from 0. - bounds : List[Tuple[float, float]] - bounds of input dimensions: (lower, uppper) for continuous dims; (n_cat, np.nan) for categorical dims - seed : int - The seed that is passed to the model library. - instance_features : np.ndarray (I, K) - Contains the K dimensional instance features - of the I different instances - pca_components : float - Number of components to keep when using PCA to reduce - dimensionality of instance features. Requires to - set n_feats (> pca_dims). - - Attributes - ---------- - instance_features : np.ndarray(I, K) - Contains the K dimensional instance features - of the I different instances - pca : sklearn.decomposition.PCA - Object to perform PCA - pca_components : float - Number of components to keep or None - n_feats : int - Number of instance features - n_params : int - Number of parameters in a configuration (only available after train has - been called) - scaler : sklearn.preprocessing.MinMaxScaler - Object to scale data to be withing [0, 1] - var_threshold : float - Lower bound vor variance. If estimated variance < var_threshold, the set - to var_threshold - types : list - If set, contains a list with feature types (cat,const) of input vector - """ - - def __init__( - self, - configspace: ConfigurationSpace, - types: List[int], - bounds: List[Tuple[float, float]], - seed: int, - instance_features: Optional[np.ndarray] = None, - pca_components: Optional[int] = 7, - ) -> None: - self.configspace = configspace - self.seed = seed - self.instance_features = instance_features - self.pca_components = pca_components - - if instance_features is not None: - self.n_feats = instance_features.shape[1] - else: - self.n_feats = 0 - - self.n_params = len(self.configspace.get_hyperparameters()) - - self.pca = PCA(n_components=self.pca_components) - self.scaler = MinMaxScaler() - self._apply_pca = False - - # Never use a lower variance than this - self.var_threshold = VERY_SMALL_NUMBER - - self.bounds = bounds - self.types = types - # Initial types array which is used to reset the type array at every call to train() - self._initial_types = copy.deepcopy(types) - - self.logger = PickableLoggerAdapter(self.__module__ + "." + self.__class__.__name__) - - def train(self, X: np.ndarray, Y: np.ndarray) -> "BaseEPM": - """Trains the EPM on X and Y. - - Parameters - ---------- - X : np.ndarray [n_samples, n_features (config + instance features)] - Input data points. - Y : np.ndarray [n_samples, n_objectives] - The corresponding target values. n_objectives must match the - number of target names specified in the constructor. - - Returns - ------- - self : BaseEPM - """ - if len(X.shape) != 2: - raise ValueError("Expected 2d array, got %dd array!" % len(X.shape)) - if X.shape[1] != self.n_params + self.n_feats: - raise ValueError("Feature mismatch: X should have %d features, but has %d" % (self.n_params, X.shape[1])) - if X.shape[0] != Y.shape[0]: - raise ValueError("X.shape[0] (%s) != y.shape[0] (%s)" % (X.shape[0], Y.shape[0])) - - # reduce dimensionality of features of larger than PCA_DIM - if self.pca_components and X.shape[0] > self.pca.n_components and self.n_feats >= self.pca_components: - X_feats = X[:, -self.n_feats :] - # scale features - X_feats = self.scaler.fit_transform(X_feats) - X_feats = np.nan_to_num(X_feats) # if features with max == min - # PCA - X_feats = self.pca.fit_transform(X_feats) - X = np.hstack((X[:, : self.n_params], X_feats)) - if hasattr(self, "types"): - # for RF, adapt types list - # if X_feats.shape[0] < self.pca, X_feats.shape[1] == - # X_feats.shape[0] - self.types = np.array( - np.hstack((self.types[: self.n_params], np.zeros((X_feats.shape[1])))), - dtype=np.uint, - ) # type: ignore - self._apply_pca = True - else: - self._apply_pca = False - if hasattr(self, "types"): - self.types = copy.deepcopy(self._initial_types) - - return self._train(X, Y) - - def _train(self, X: np.ndarray, Y: np.ndarray) -> "BaseEPM": - """Trains the random forest on X and y. - - Parameters - ---------- - X : np.ndarray [n_samples, n_features (config + instance features)] - Input data points. - Y : np.ndarray [n_samples, n_objectives] - The corresponding target values. n_objectives must match the - number of target names specified in the constructor. - - Returns - ------- - self - """ - raise NotImplementedError - - def predict( - self, X: np.ndarray, cov_return_type: Optional[str] = "diagonal_cov" - ) -> Tuple[np.ndarray, Optional[np.ndarray]]: - """Predict means and variances for given X. - - Parameters - ---------- - X : np.ndarray of shape = [n_samples, n_features (config + instance features)] - Training samples - cov_return_type: Optional[str] - Specifies what to return along with the mean. (Applies to only Gaussian Process for now) - Can take 4 values: [None, diagonal_std, diagonal_cov, full_cov] - * None - only mean is returned - * diagonal_std - standard deviation at test points is returned - * diagonal_cov - diagonal of the covariance matrix is returned - * full_cov - whole covariance matrix between the test points is returned - - Returns - ------- - means : np.ndarray of shape = [n_samples, n_objectives] - Predictive mean - vars : None or np.ndarray of shape = [n_samples, n_objectives] or [n_samples, n_samples] - Predictive variance or standard deviation - """ - if len(X.shape) != 2: - raise ValueError("Expected 2d array, got %dd array!" % len(X.shape)) - if X.shape[1] != self.n_params + self.n_feats: - raise ValueError( - "Rows in X should have %d entries but have %d!" % (self.n_params + self.n_feats, X.shape[1]) - ) - - if self._apply_pca: - try: - X_feats = X[:, -self.n_feats :] - X_feats = self.scaler.transform(X_feats) - X_feats = self.pca.transform(X_feats) - X = np.hstack((X[:, : self.n_params], X_feats)) - except NotFittedError: - pass # PCA not fitted if only one training sample - - if X.shape[1] != len(self.types): - raise ValueError("Rows in X should have %d entries but have %d!" % (len(self.types), X.shape[1])) - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", "Predicted variances smaller than 0. Setting those variances to 0.") - mean, var = self._predict(X, cov_return_type) - - if len(mean.shape) == 1: - mean = mean.reshape((-1, 1)) - if var is not None and len(var.shape) == 1: - var = var.reshape((-1, 1)) - - return mean, var - - def _predict( - self, X: np.ndarray, cov_return_type: Optional[str] = "diagonal_cov" - ) -> Tuple[np.ndarray, Optional[np.ndarray]]: - """Predict means and variances for given X. - - Parameters - ---------- - X : np.ndarray - [n_samples, n_features (config + instance features)] - cov_return_type: Optional[str] - Specifies what to return along with the mean. Refer ``predict()`` for more information. - - Returns - ------- - means : np.ndarray of shape = [n_samples, n_objectives] - Predictive mean - vars : None or np.ndarray of shape = [n_samples, n_objectives] or [n_samples, n_samples] - Predictive variance or standard deviation - """ - raise NotImplementedError() - - def predict_marginalized_over_instances(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: - """Predict mean and variance marginalized over all instances. - - Returns the predictive mean and variance marginalised over all - instances for a set of configurations. - - Parameters - ---------- - X : np.ndarray - [n_samples, n_features (config)] - - Returns - ------- - means : np.ndarray of shape = [n_samples, 1] - Predictive mean - vars : np.ndarray of shape = [n_samples, 1] - Predictive variance - """ - if len(X.shape) != 2: - raise ValueError("Expected 2d array, got %dd array!" % len(X.shape)) - if X.shape[1] != len(self.bounds): - raise ValueError("Rows in X should have %d entries but have %d!" % (len(self.bounds), X.shape[1])) - - if self.instance_features is None or len(self.instance_features) == 0: - mean, var = self.predict(X) - assert var is not None # please mypy - - var[var < self.var_threshold] = self.var_threshold - var[np.isnan(var)] = self.var_threshold - return mean, var - - n_instances = len(self.instance_features) - - mean = np.zeros(X.shape[0]) - var = np.zeros(X.shape[0]) - for i, x in enumerate(X): - X_ = np.hstack((np.tile(x, (n_instances, 1)), self.instance_features)) - means, vars = self.predict(X_) - assert vars is not None # please mypy - # VAR[1/n (X_1 + ... + X_n)] = - # 1/n^2 * ( VAR(X_1) + ... + VAR(X_n)) - # for independent X_1 ... X_n - var_x = np.sum(vars) / (len(vars) ** 2) - if var_x < self.var_threshold: - var_x = self.var_threshold - - var[i] = var_x - mean[i] = np.mean(means) - - if len(mean.shape) == 1: - mean = mean.reshape((-1, 1)) - if len(var.shape) == 1: - var = var.reshape((-1, 1)) - - return mean, var - - def get_configspace(self) -> ConfigurationSpace: - """ - Retrieves the ConfigurationSpace used for the model. - - Returns - ------- - self.configspace: The ConfigurationSpace of the model - """ - return self.configspace diff --git a/smac/epm/base_imputor.py b/smac/epm/base_imputor.py deleted file mode 100644 index be7bddbda..000000000 --- a/smac/epm/base_imputor.py +++ /dev/null @@ -1,45 +0,0 @@ -from abc import abstractmethod -from typing import Optional - -import numpy as np - -__author__ = "Katharina Eggensperger" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Katharina Eggensperger" -__email__ = "eggenspk@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -class BaseImputor(object): - """Abstract implementation of the Imputation API.""" - - def __init__(self) -> None: - pass - - @abstractmethod - def impute( - self, - censored_X: np.ndarray, - censored_y: np.ndarray, - uncensored_X: np.ndarray, - uncensored_y: np.ndarray, - ) -> Optional[np.ndarray]: - """Imputes censored runs and returns new y values. - - Parameters - ---------- - censored_X : np.ndarray [N, M] - Feature array of all censored runs. - censored_y : np.ndarray [N, 1] - Target values for all runs censored runs. - uncensored_X : np.ndarray [N, M] - Feature array of all non-censored runs. - uncensored_y : np.ndarray [N, 1] - Target values for all non-censored runs. - - Returns - ------- - imputed_y: np.ndarray - Same shape as censored_y [N, 1] - """ diff --git a/smac/epm/gaussian_process/augmented.py b/smac/epm/gaussian_process/augmented.py deleted file mode 100644 index 183000c9a..000000000 --- a/smac/epm/gaussian_process/augmented.py +++ /dev/null @@ -1,496 +0,0 @@ -from typing import Dict, List, Optional, Tuple, Union - -from collections import OrderedDict - -import gpytorch -import numpy as np -import torch -from botorch.optim.numpy_converter import module_to_array, set_params_with_array -from botorch.optim.utils import _get_extra_mll_args -from gpytorch.constraints.constraints import Interval -from gpytorch.distributions import MultivariateNormal -from gpytorch.kernels import Kernel -from gpytorch.likelihoods import GaussianLikelihood -from gpytorch.means import ZeroMean -from gpytorch.mlls import ExactMarginalLogLikelihood -from gpytorch.models import ExactGP -from gpytorch.utils.errors import NanError -from scipy import optimize -from scipy.stats.qmc import LatinHypercube - -from smac.configspace import ConfigurationSpace -from smac.epm.gaussian_process.gpytorch import ExactGPModel, GPyTorchGaussianProcess -from smac.epm.gaussian_process.kernels.boing import FITCKernel, FITCMean -from smac.epm.utils import check_subspace_points - -gpytorch.settings.debug.off() - - -class AugmentedLocalGaussianProcess(ExactGP): - def __init__( - self, - X_in: torch.Tensor, - y_in: torch.Tensor, - X_out: torch.Tensor, - y_out: torch.Tensor, - likelihood: GaussianLikelihood, - base_covar_kernel: Kernel, - ): - """ - An Augmented Local GP, it is trained with the points inside a subregion while its prior is augemented by the - points outside the subregion (global configurations) - - Parameters - ---------- - X_in: torch.Tensor (N_in, D), - feature vector of the points inside the subregion - y_in: torch.Tensor (N_in, 1), - observation inside the subregion - X_out: torch.Tensor (N_out, D), - feature vector of the points outside the subregion - y_out:torch.Tensor (N_out, 1), - observation inside the subregion - likelihood: GaussianLikelihood, - likelihood of the GP (noise) - base_covar_kernel: Kernel, - Covariance Kernel - """ - X_in = X_in.unsqueeze(-1) if X_in.ndimension() == 1 else X_in - X_out = X_out.unsqueeze(-1) if X_out.ndimension() == 1 else X_out - assert X_in.shape[-1] == X_out.shape[-1] - - super(AugmentedLocalGaussianProcess, self).__init__(X_in, y_in, likelihood) - - self._mean_module = ZeroMean() - self.base_covar = base_covar_kernel - - self.X_out = X_out - self.y_out = y_out - self.augmented = False - - def set_augment_module(self, X_inducing: torch.Tensor) -> None: - """ - Set an augmentation module, which will be used later for inference - - Parameters - ---------- - X_inducing: torch.Tensor(N_inducing, D) - inducing points, it needs to have the same number of dimensions as X_in - """ - X_inducing = X_inducing.unsqueeze(-1) if X_inducing.ndimension() == 1 else X_inducing - # assert X_inducing.shape[-1] == self.X_out.shape[-1] - self.covar_module = FITCKernel( - self.base_covar, X_inducing=X_inducing, X_out=self.X_out, y_out=self.y_out, likelihood=self.likelihood - ) - self.mean_module = FITCMean(covar_module=self.covar_module) - self.augmented = True - - def forward(self, x: torch.Tensor) -> MultivariateNormal: - """ - Compute the prior values. If optimize_kernel_hps is set True in the training phases, this model degenerates to - a vanilla GP model with ZeroMean and base_covar as covariance matrix. Otherwise, we apply partial sparse GP - mean and kernels here. - """ - if not self.augmented: - # we only optimize for kernel hyperparameters - covar_x = self.base_covar(x) - mean_x = self._mean_module(x) - else: - covar_x = self.covar_module(x) - mean_x = self.mean_module(x) - return MultivariateNormal(mean_x, covar_x) - - -class VariationalGaussianProcess(gpytorch.models.ApproximateGP): - """ - A variational GP to compute the position of the inducing points. - We only optimize for the position of the continuous dimensions and keep the categorical dimensions constant. - """ - - def __init__(self, kernel: Kernel, X_inducing: torch.Tensor): - """ - Initialize a Variational GP - we set the lower bound and upper bounds of inducing points for numerical hyperparameters between 0 and 1, - that is, we constrain the inducing points to lay inside the subregion. - - Parameters - ---------- - kernel: Kernel - kernel of the variational GP, its hyperparameter needs to be fixed when it is by LGPGA - X_inducing: torch.tensor (N_inducing, D) - inducing points - """ - variational_distribution = gpytorch.variational.TrilNaturalVariationalDistribution(X_inducing.size(0)) - variational_strategy = gpytorch.variational.VariationalStrategy( - self, X_inducing, variational_distribution, learn_inducing_locations=True - ) - super(VariationalGaussianProcess, self).__init__(variational_strategy) - self.mean_module = gpytorch.means.ZeroMean() - self.covar_module = kernel - - shape_X_inducing = X_inducing.shape - lower_X_inducing = torch.zeros([shape_X_inducing[-1]]).repeat(shape_X_inducing[0]) - upper_X_inducing = torch.ones([shape_X_inducing[-1]]).repeat(shape_X_inducing[0]) - - self.variational_strategy.register_constraint( - param_name="inducing_points", - constraint=Interval(lower_X_inducing, upper_X_inducing, transform=None), - ) - self.double() - - for p_name, t in self.named_hyperparameters(): - if p_name != "variational_strategy.inducing_points": - t.requires_grad = False - - def forward(self, x: torch.Tensor) -> MultivariateNormal: - """ - Pass the posterior mean and variance given input X - - Parameters - ---------- - x: torch.Tensor - Input data - Returns - ------- - """ - mean_x = self.mean_module(x) - covar_x = self.covar_module(x, cont_only=True) - return MultivariateNormal(mean_x, covar_x) - - -class GloballyAugmentedLocalGaussianProcess(GPyTorchGaussianProcess): - def __init__( - self, - configspace: ConfigurationSpace, - types: List[int], - bounds: List[Tuple[float, float]], - bounds_cont: np.ndarray, - bounds_cat: List[Tuple], - seed: int, - kernel: Kernel, - num_inducing_points: int = 2, - likelihood: Optional[GaussianLikelihood] = None, - normalize_y: bool = True, - n_opt_restarts: int = 10, - instance_features: Optional[np.ndarray] = None, - pca_components: Optional[int] = None, - ): - """ - The GP hyperparameters are obtained by optimizing the marginal log-likelihood and optimized with botorch - We train an LGPGA in two stages: - In the first stage, we only train the kernel hyperparameter and thus deactivate the gradient w.r.t the position - of the inducing points. - In the second stage, we use the kernel hyperparameter acquired in the first stage to initialize a new - variational Gaussian process and only optimize its inducing points' position with natural gradients. - Finally, we update the position of the inducing points and use it for evaluation. - - - Parameters - ---------- - bounds_cont: np.ndarray(N_cont, 2), - bounds of the continuous hyperparameters, store as [[0,1] * N_cont] - bounds_cat: List[Tuple], - bounds of categorical hyperparameters - kernel : gpytorch kernel object - Specifies the kernel that is used for all Gaussian Process - num_inducing_points: int - Number of inducing points - likelihood: Optional[GaussianLikelihood] - Likelihood values - normalize_y : bool - Zero mean unit variance normalization of the output values when the model is a partial sparse GP model. - """ - super(GloballyAugmentedLocalGaussianProcess, self).__init__( - configspace=configspace, - types=types, - bounds=bounds, - seed=seed, - kernel=kernel, - likelihood=likelihood, - normalize_y=normalize_y, - n_opt_restarts=n_opt_restarts, - instance_features=instance_features, - pca_components=pca_components, - ) - self.cont_dims = np.where(np.array(types) == 0)[0] - self.cat_dims = np.where(np.array(types) != 0)[0] - self.bounds_cont = bounds_cont - self.bounds_cat = bounds_cat - self.num_inducing_points = num_inducing_points - - def update_attribute(self, **kwargs: Dict) -> None: - """We update the class attribute (for instance, number of inducing points)""" - for key in kwargs: - if not hasattr(self, key): - raise AttributeError(f"{self.__class__.__name__} has no attribute named {key}") - setattr(self, key, kwargs[key]) - - def _train( - self, X: np.ndarray, y: np.ndarray, do_optimize: bool = True - ) -> Union[AugmentedLocalGaussianProcess, GPyTorchGaussianProcess]: - """ - Update the hyperparameters of the partial sparse kernel. Depending on the number of inputs inside and - outside the subregion, we initialize a PartialSparseGaussianProcess or a GaussianProcessGPyTorch - - Parameters - ---------- - X: np.ndarray (N, D) - Input data points. The dimensionality of X is (N, D), - with N as the number of points and D is the number of features., N = N_in + N_out - y: np.ndarray (N,) - The corresponding target values. - do_optimize: boolean - If set to true, the hyperparameters are optimized otherwise, - the default hyperparameters of the kernel are used. - """ - X = self._impute_inactive(X) - if len(y.shape) == 1: - self.n_objectives_ = 1 - else: - self.n_objectives_ = y.shape[1] - if self.n_objectives_ == 1: - y = y.flatten() - - ss_data_indices = check_subspace_points( - X, - cont_dims=self.cont_dims, - cat_dims=self.cat_dims, - bounds_cont=self.bounds_cont, - bounds_cat=self.bounds_cat, - expand_bound=True, - ) - - if np.sum(ss_data_indices) > np.shape(y)[0] - self.num_inducing_points: - # we initialize a vanilla GaussianProcessGPyTorch - if self.normalize_y: - y = self._normalize_y(y) - self.num_points = np.shape(y)[0] - get_gp_kwargs = {"X_in": X, "y_in": y, "X_out": None, "y_out": None} - else: - # we initialize a PartialSparseGaussianProcess object - X_in = X[ss_data_indices] - y_in = y[ss_data_indices] - X_out = X[~ss_data_indices] - y_out = y[~ss_data_indices] - self.num_points = np.shape(y_in)[0] - if self.normalize_y: - y_in = self._normalize_y(y_in) - y_out = (y_out - self.mean_y_) / self.std_y_ - get_gp_kwargs = {"X_in": X_in, "y_in": y_in, "X_out": X_out, "y_out": y_out} - - n_tries = 10 - - for i in range(n_tries): - try: - self.gp = self._get_gp(**get_gp_kwargs) - break - except Exception as e: - if i == n_tries - 1: - raise RuntimeError(f"Fails to initialize a GP model, {e}") - - if do_optimize: - self.hypers = self._optimize() - self.gp = set_params_with_array(self.gp, self.hypers, self.property_dict) - if isinstance(self.gp.model, AugmentedLocalGaussianProcess): - # we optimize the position of the inducing points and thus needs to deactivate the gradient of kernel - # hyperparameters - lhd = LatinHypercube(d=X.shape[-1], seed=self.rng.randint(0, 1000000)) - - inducing_points = torch.from_numpy(lhd.random(n=self.num_inducing_points)) - - kernel = self.gp.model.base_covar - var_gp = VariationalGaussianProcess(kernel, X_inducing=inducing_points) - - X_out_ = torch.from_numpy(X_out) - y_out_ = torch.from_numpy(y_out) - - variational_ngd_optimizer = gpytorch.optim.NGD( - var_gp.variational_parameters(), num_data=y_out_.size(0), lr=0.1 - ) - - var_gp.train() - likelihood = GaussianLikelihood().double() - likelihood.train() - - mll_func = gpytorch.mlls.PredictiveLogLikelihood - - var_mll = mll_func(likelihood, var_gp, num_data=y_out_.size(0)) - - for t in var_gp.variational_parameters(): - t.requires_grad = False - - x0, property_dict, bounds = module_to_array(module=var_mll) - for t in var_gp.variational_parameters(): - t.requires_grad = True - bounds = np.asarray(bounds).transpose().tolist() - - start_points = [x0] - - inducing_idx = 0 - - inducing_size = X_out.shape[-1] * self.num_inducing_points - for p_name, attrs in property_dict.items(): - if p_name != "model.variational_strategy.inducing_points": - # Construct the new tensor - if len(attrs.shape) == 0: # deal with scalar tensors - inducing_idx = inducing_idx + 1 - else: - inducing_idx = inducing_idx + np.prod(attrs.shape) - else: - break - while len(start_points) < 3: - new_start_point = np.random.rand(*x0.shape) - new_inducing_points = torch.from_numpy(lhd.random(n=self.num_inducing_points)).flatten() - new_start_point[inducing_idx : inducing_idx + inducing_size] = new_inducing_points - start_points.append(new_start_point) - - def sci_opi_wrapper( - x: np.ndarray, - mll: gpytorch.module, - property_dict: Dict, - train_inputs: torch.Tensor, - train_targets: torch.Tensor, - ) -> Tuple[float, np.ndarray]: - """ - A modification of from botorch.optim.utils._scipy_objective_and_grad, the key difference is that - we do an additional natural gradient update before computing the gradient values - Parameters - ---------- - x: np.ndarray - optimizer input - mll: gpytorch.module - a gpytorch module whose hyperparameters are defined by x - property_dict: Dict - a dict describing how x is mapped to initialize mll - train_inputs: torch.Tensor (N_input, D) - input points of the GP model - train_targets: torch.Tensor (N_input, 1) - target value of the GP model - Returns - ---------- - loss: np.ndarray - loss value - grad: np.ndarray - gradient w.r.t. the inputs - ---------- - """ - # A modification of from botorch.optim.utils._scipy_objective_and_grad: - # https://botorch.org/api/_modules/botorch/optim/utils.html - # The key difference is that we do an additional natural gradient update here - variational_ngd_optimizer.zero_grad() - - mll = set_params_with_array(mll, x, property_dict) - mll.zero_grad() - try: # catch linear algebra errors in gpytorch - output = mll.model(train_inputs) - args = [output, train_targets] + _get_extra_mll_args(mll) - loss = -mll(*args).sum() - except RuntimeError as e: - if isinstance(e, NanError) or "singular" in e.args[0]: - return float("nan"), np.full_like(x, "nan") - else: - raise e # pragma: nocover - loss.backward() - variational_ngd_optimizer.step() - param_dict = OrderedDict(mll.named_parameters()) - grad = [] - for p_name in property_dict: - t = param_dict[p_name].grad - if t is None: - # this deals with parameters that do not affect the loss - grad.append(np.zeros(property_dict[p_name].shape.numel())) - else: - grad.append(t.detach().view(-1).cpu().double().clone().numpy()) - mll.zero_grad() - return loss.item(), np.concatenate(grad) - - theta_star = x0 - f_opt_star = np.inf - for start_point in start_points: - try: - theta, f_opt, res_dict = optimize.fmin_l_bfgs_b( - sci_opi_wrapper, - start_point, - args=(var_mll, property_dict, X_out_, y_out_), - bounds=bounds, - maxiter=50, - ) - if f_opt < f_opt_star: - f_opt_star = f_opt - theta_star = theta - except Exception as e: - self.logger.warning(f"An exception {e} occurs during the optimizaiton") - - start_idx = 0 - # modification on botorch.optim.numpy_converter.set_params_with_array as we only need to extract the - # positions of inducing points - for p_name, attrs in property_dict.items(): - if p_name != "model.variational_strategy.inducing_points": - # Construct the new tensor - if len(attrs.shape) == 0: # deal with scalar tensors - start_idx = start_idx + 1 - else: - start_idx = start_idx + np.prod(attrs.shape) - else: - end_idx = start_idx + np.prod(attrs.shape) - X_inducing = torch.tensor( - theta_star[start_idx:end_idx], dtype=attrs.dtype, device=attrs.device - ).view(*attrs.shape) - break - # set inducing points for covariance module here - self.gp_model.set_augment_module(X_inducing) - else: - self.hypers, self.property_dict, _ = module_to_array(module=self.gp) - - self.is_trained = True - return self - - def _get_gp( - self, - X_in: Optional[np.ndarray] = None, - y_in: Optional[np.ndarray] = None, - X_out: Optional[np.ndarray] = None, - y_out: Optional[np.ndarray] = None, - ) -> Optional[ExactMarginalLogLikelihood]: - """ - Construct a new GP model based on the inputs - If both in and out are None: return an empty model - If only in_x and in_y are given: return a vanilla GP model - If in_x, in_y, out_x, out_y are given: return a partial sparse GP model. - - Parameters - ---------- - X_in: Optional[np.ndarray (N_in, D)] - Input data points inside the subregion. The dimensionality of X_in is (N_in, D), - with N_in as the number of points inside the subregion and D is the number of features. If it is not given, - this function will return None to be compatible with the implementation of its parent class - y_in: Optional[np.ndarray (N_in,)] - The corresponding target values inside the subregion. - X_out: Optional[np.ndarray (N_out, D). - Input data points outside the subregion. The dimensionality of X_out is (N_out, D). If it is not given, this - function will return a vanilla Gaussian Process - y_out: Optional[np.ndarray (N_out)] - The corresponding target values outside the subregion. - - Returns - ------- - mll: ExactMarginalLogLikelihood - a gp module - """ - if X_in is None: - return None - - X_in = torch.from_numpy(X_in) - y_in = torch.from_numpy(y_in) - if X_out is None: - self.gp_model = ExactGPModel(X_in, y_in, likelihood=self.likelihood, base_covar_kernel=self.kernel).double() - else: - X_out = torch.from_numpy(X_out) - y_out = torch.from_numpy(y_out) - - self.gp_model = AugmentedLocalGaussianProcess( - X_in, y_in, X_out, y_out, likelihood=self.likelihood, base_covar_kernel=self.kernel # type:ignore - ).double() - mll = ExactMarginalLogLikelihood(self.likelihood, self.gp_model) - mll.double() - return mll diff --git a/smac/epm/gaussian_process/gp.py b/smac/epm/gaussian_process/gp.py deleted file mode 100644 index cdfcf99f1..000000000 --- a/smac/epm/gaussian_process/gp.py +++ /dev/null @@ -1,316 +0,0 @@ -from typing import List, Optional, Tuple, Union, cast - -import logging - -import numpy as np -from scipy import optimize -from sklearn.gaussian_process import GaussianProcessRegressor -from sklearn.gaussian_process.kernels import Kernel - -from smac.configspace import ConfigurationSpace -from smac.epm.gaussian_process import BaseModel -from smac.epm.gaussian_process.utils.prior import Prior -from smac.utils.constants import VERY_SMALL_NUMBER - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -logger = logging.getLogger(__name__) - - -class GaussianProcess(BaseModel): - """Gaussian process model. - - The GP hyperparameterŝ are obtained by optimizing the marginal log likelihood. - - This code is based on the implementation of RoBO: - - Klein, A. and Falkner, S. and Mansur, N. and Hutter, F. - RoBO: A Flexible and Robust Bayesian Optimization Framework in Python - In: NIPS 2017 Bayesian Optimization Workshop - - Parameters - ---------- - types : List[int] - Specifies the number of categorical values of an input dimension where - the i-th entry corresponds to the i-th input dimension. Let's say we - have 2 dimension where the first dimension consists of 3 different - categorical choices and the second dimension is continuous than we - have to pass [3, 0]. Note that we count starting from 0. - bounds : List[Tuple[float, float]] - bounds of input dimensions: (lower, uppper) for continuous dims; (n_cat, np.nan) for categorical dims - seed : int - Model seed. - kernel : george kernel object - Specifies the kernel that is used for all Gaussian Process - prior : prior object - Defines a prior for the hyperparameters of the GP. Make sure that - it implements the Prior interface. - normalize_y : bool - Zero mean unit variance normalization of the output values - n_opt_restart : int - Number of restarts for GP hyperparameter optimization - instance_features : np.ndarray (I, K) - Contains the K dimensional instance features of the I different instances - pca_components : float - Number of components to keep when using PCA to reduce dimensionality of instance features. Requires to - set n_feats (> pca_dims). - """ - - def __init__( - self, - configspace: ConfigurationSpace, - types: List[int], - bounds: List[Tuple[float, float]], - seed: int, - kernel: Kernel, - normalize_y: bool = True, - n_opt_restarts: int = 10, - instance_features: Optional[np.ndarray] = None, - pca_components: Optional[int] = None, - ): - super().__init__( - configspace=configspace, - types=types, - bounds=bounds, - seed=seed, - kernel=kernel, - instance_features=instance_features, - pca_components=pca_components, - ) - - self.normalize_y = normalize_y - self.n_opt_restarts = n_opt_restarts - - self.hypers = np.empty((0,)) - self.is_trained = False - self._n_ll_evals = 0 - - self._set_has_conditions() - - def _train(self, X: np.ndarray, y: np.ndarray, do_optimize: bool = True) -> "GaussianProcess": - """Computes the Cholesky decomposition of the covariance of X and estimates the GP - hyperparameters by optimizing the marginal loglikelihood. The prior mean of the GP is set to - the empirical mean of X. - - Parameters - ---------- - X: np.ndarray (N, D) - Input data points. The dimensionality of X is (N, D), - with N as the number of points and D is the number of features. - y: np.ndarray (N,) - The corresponding target values. - do_optimize: boolean - If set to true the hyperparameters are optimized otherwise - the default hyperparameters of the kernel are used. - """ - X = self._impute_inactive(X) - if self.normalize_y: - y = self._normalize_y(y) - y = y.flatten() - - n_tries = 10 - for i in range(n_tries): - try: - self.gp = self._get_gp() - self.gp.fit(X, y) - break - except np.linalg.LinAlgError as e: - if i == n_tries: - raise e - # Assume that the last entry of theta is the noise - theta = np.exp(self.kernel.theta) - theta[-1] += 1 - self.kernel.theta = np.log(theta) - - if do_optimize: - self._all_priors = self._get_all_priors(add_bound_priors=False) - self.hypers = self._optimize() - self.gp.kernel.theta = self.hypers - self.gp.fit(X, y) - else: - self.hypers = self.gp.kernel.theta - - self.is_trained = True - return self - - def _get_gp(self) -> GaussianProcessRegressor: - return GaussianProcessRegressor( - kernel=self.kernel, - normalize_y=False, - optimizer=None, - n_restarts_optimizer=-1, # Do not use scikit-learn's optimization routine - alpha=0, # Governed by the kernel - random_state=self.rng, - ) - - def _nll(self, theta: np.ndarray) -> Tuple[float, np.ndarray]: - """Returns the negative marginal log likelihood (+ the prior) for a hyperparameter - configuration theta. (negative because we use scipy minimize for optimization) - - Parameters - ---------- - theta : np.ndarray(H) - Hyperparameter vector. Note that all hyperparameter are - on a log scale. - - Returns - ------- - float - lnlikelihood + prior - """ - self._n_ll_evals += 1 - - try: - lml, grad = self.gp.log_marginal_likelihood(theta, eval_gradient=True) - except np.linalg.LinAlgError: - return 1e25, np.zeros(theta.shape) - - for dim, priors in enumerate(self._all_priors): - for prior in priors: - lml += prior.lnprob(theta[dim]) - grad[dim] += prior.gradient(theta[dim]) - - # We add a minus here because scipy is minimizing - if not np.isfinite(lml).all() or not np.all(np.isfinite(grad)): - return 1e25, np.zeros(theta.shape) - else: - return -lml, -grad - - def _optimize(self) -> np.ndarray: - """Optimizes the marginal log likelihood and returns the best found hyperparameter - configuration theta. - - Returns - ------- - theta : np.ndarray(H) - Hyperparameter vector that maximizes the marginal log likelihood - """ - log_bounds = [(b[0], b[1]) for b in self.gp.kernel.bounds] - - # Start optimization from the previous hyperparameter configuration - p0 = [self.gp.kernel.theta] - if self.n_opt_restarts > 0: - dim_samples = [] - - prior = None # type: Optional[Union[List[Prior], Prior]] - for dim, hp_bound in enumerate(log_bounds): - prior = self._all_priors[dim] - # Always sample from the first prior - if isinstance(prior, list): - if len(prior) == 0: - prior = None - else: - prior = prior[0] - prior = cast(Optional[Prior], prior) - if prior is None: - try: - sample = self.rng.uniform( - low=hp_bound[0], - high=hp_bound[1], - size=(self.n_opt_restarts,), - ) - except OverflowError: - raise ValueError("OverflowError while sampling from (%f, %f)" % (hp_bound[0], hp_bound[1])) - dim_samples.append(sample.flatten()) - else: - dim_samples.append(prior.sample_from_prior(self.n_opt_restarts).flatten()) - p0 += list(np.vstack(dim_samples).transpose()) - - theta_star: Optional[np.ndarray] = None - f_opt_star = np.inf - for i, start_point in enumerate(p0): - theta, f_opt, _ = optimize.fmin_l_bfgs_b(self._nll, start_point, bounds=log_bounds) - if f_opt < f_opt_star: - f_opt_star = f_opt - theta_star = theta - - if theta_star is None: - raise RuntimeError - - return theta_star - - def _predict( - self, X_test: np.ndarray, cov_return_type: Optional[str] = "diagonal_cov" - ) -> Tuple[np.ndarray, Optional[np.ndarray]]: - r""" - Returns the predictive mean and variance of the objective function at - the given test points. - - Parameters - ---------- - X_test: np.ndarray (N, D) - Input test points - cov_return_type: Optional[str] - Specifies what to return along with the mean. Refer ``predict()`` for more information. - - Returns - ------- - np.array(N,) - predictive mean - np.array(N,) or np.array(N, N) or None - predictive variance or standard deviation - """ - if not self.is_trained: - raise Exception("Model has to be trained first!") - - X_test = self._impute_inactive(X_test) - - if cov_return_type is None: - mu = self.gp.predict(X_test) - var = None - - if self.normalize_y: - mu = self._untransform_y(mu) - - else: - predict_kwargs = {"return_cov": False, "return_std": True} - if cov_return_type == "full_cov": - predict_kwargs = {"return_cov": True, "return_std": False} - - mu, var = self.gp.predict(X_test, **predict_kwargs) - - if cov_return_type != "full_cov": - var = var**2 # since we get standard deviation for faster computation - - # Clip negative variances and set them to the smallest - # positive float value - var = np.clip(var, VERY_SMALL_NUMBER, np.inf) - - if self.normalize_y: - mu, var = self._untransform_y(mu, var) - - if cov_return_type == "diagonal_std": - var = np.sqrt(var) # converting variance to std deviation if specified - - return mu, var - - def sample_functions(self, X_test: np.ndarray, n_funcs: int = 1) -> np.ndarray: - """Samples F function values from the current posterior at the N specified test points. - - Parameters - ---------- - X_test: np.ndarray (N, D) - Input test points - n_funcs: int - Number of function values that are drawn at each test point. - - Returns - ------- - function_samples: np.array(N, F) - The F function values drawn at the N test points. - """ - if not self.is_trained: - raise Exception("Model has to be trained first!") - - X_test = self._impute_inactive(X_test) - funcs = self.gp.sample_y(X_test, n_samples=n_funcs, random_state=self.rng) - - if self.normalize_y: - funcs = self._untransform_y(funcs) - - if len(funcs.shape) == 1: - return funcs[None, :] - else: - return funcs diff --git a/smac/epm/gaussian_process/gpytorch.py b/smac/epm/gaussian_process/gpytorch.py deleted file mode 100644 index 2cf0f0539..000000000 --- a/smac/epm/gaussian_process/gpytorch.py +++ /dev/null @@ -1,365 +0,0 @@ -from typing import List, Optional, Tuple - -import warnings -from collections import OrderedDict - -import gpytorch -import numpy as np -import torch -from botorch.optim.numpy_converter import module_to_array, set_params_with_array -from botorch.optim.utils import _scipy_objective_and_grad -from gpytorch.constraints.constraints import Interval -from gpytorch.distributions.multivariate_normal import MultivariateNormal -from gpytorch.kernels import Kernel -from gpytorch.likelihoods import FixedNoiseGaussianLikelihood, GaussianLikelihood -from gpytorch.means import ZeroMean -from gpytorch.mlls import ExactMarginalLogLikelihood -from gpytorch.models import ExactGP -from gpytorch.priors import HorseshoePrior -from gpytorch.utils.errors import NotPSDError -from scipy import optimize - -from smac.configspace import ConfigurationSpace -from smac.epm.gaussian_process import BaseModel -from smac.utils.constants import VERY_SMALL_NUMBER - -warnings.filterwarnings("ignore", module="gpytorch") - - -class ExactGPModel(ExactGP): - """Exact GP model serves as a backbone of the class GaussianProcessGPyTorch""" - - def __init__( - self, train_X: torch.Tensor, train_y: torch.Tensor, base_covar_kernel: Kernel, likelihood: GaussianLikelihood - ): - """ - Initialization function - - Parameters - ---------- - train_X: torch.tenor - input feature - train_y: torch.tensor - input observations - base_covar_kernel: Kernel - covariance kernel used to compute covariance matrix - likelihood: GaussianLikelihood - GP likelihood - """ - super(ExactGPModel, self).__init__(train_X, train_y, likelihood) - # in our experiments we find that ZeroMean more robust than ConstantMean when y is normalized - self.mean_module = ZeroMean() - self.covar_module = base_covar_kernel - - def forward(self, x: torch.Tensor) -> MultivariateNormal: - """Compute the posterior mean and variance""" - mean_x = self.mean_module(x) - covar_x = self.covar_module(x) - return MultivariateNormal(mean_x, covar_x) - - -class GPyTorchGaussianProcess(BaseModel): - def __init__( - self, - configspace: ConfigurationSpace, - types: List[int], - bounds: List[Tuple[float, float]], - seed: int, - kernel: Kernel, - normalize_y: bool = True, - n_opt_restarts: int = 10, - likelihood: Optional[FixedNoiseGaussianLikelihood] = None, - instance_features: Optional[np.ndarray] = None, - pca_components: Optional[int] = None, - ): - """ - A Gaussian Process written with GPyTorch, its interface is written to be compatible with partial sparse gaussian - process - - Parameters - ---------- - configspace: ConfigurationSpace - Configuration space - types : List[int] - Specifies the number of categorical values of an input dimension where - the i-th entry corresponds to the i-th input dimension. Let's say we - have 2 dimensions where the first dimension consists of 3 different - categorical choices, and the second dimension is continuous than we - have to pass [3, 0]. Note that we count starting from 0. - bounds : List[Tuple[float, float]] - bounds of input dimensions: (lower, uppper) for continuous dims; (n_cat, np.nan) for categorical dims - seed : int - Model seed. - kernel : Kernel - Specifies the kernel that is used for all Gaussian Process - normalize_y : bool - Zero mean unit variance normalization of the output values - n_opt_restarts : int - Number of restarts for GP hyperparameter optimization - likelihood: Optional[FixedNoiseGaussianLikelihood] = None, - Gaussian Likelihood (or noise) - instance_features : np.ndarray (I, K) - Contains the K dimensional instance features of the I different instances - pca_components : float - The number of components to keep when using PCA to reduce dimensionality of instance features. Requires to - set n_feats (> pca_dims). - """ - super(GPyTorchGaussianProcess, self).__init__( - configspace, - types, - bounds, - seed, - kernel, - instance_features, - pca_components, - ) - if likelihood is None: - noise_prior = HorseshoePrior(0.1) - likelihood = GaussianLikelihood( - noise_prior=noise_prior, noise_constraint=Interval(np.exp(-25), np.exp(2), transform=None) - ).double() - self.likelihood = likelihood - - self.normalize_y = normalize_y - - n_opt_restarts = int(n_opt_restarts) - if n_opt_restarts <= 0: - raise ValueError(f"n_opt_restarts needs to be positive, however, it get {n_opt_restarts}") - self.n_opt_restarts = n_opt_restarts - - self.hypers = np.empty((0,)) - self.property_dict = OrderedDict() # type: OrderedDict - self.is_trained = False - - def _train(self, X: np.ndarray, y: np.ndarray, do_optimize: bool = True) -> "GPyTorchGaussianProcess": - """ - Computes the Cholesky decomposition of the covariance of X and - estimates the GP hyperparameters by optimizing the marginal - loglikelihood. The prior mean of the GP is set to the empirical - mean of X. - - Parameters - ---------- - X: np.ndarray (N, D) - Input data points. The dimensionality of X is (N, D), - with N as the number of points and D is the number of features. - y: np.ndarray (N,) - The corresponding target values, N as the number of points - do_optimize: boolean - If set to true, the hyperparameters are optimized otherwise - the default hyperparameters of the kernel are used. - """ - X = self._impute_inactive(X) - if len(y.shape) == 1: - self.n_objectives_ = 1 - else: - self.n_objectives_ = y.shape[1] - if self.n_objectives_ == 1: - y = y.flatten() - - if self.normalize_y: - y = self._normalize_y(y) - - n_tries = 10 - for i in range(n_tries): - try: - self.gp = self._get_gp(X, y) - break - except Exception as e: - if i == n_tries - 1: - # To avoid Endless loop, we need to stop it when we have n_tries unsuccessful tries. - raise e - - if do_optimize: - self.hypers = self._optimize() - self.gp = set_params_with_array(self.gp, self.hypers, self.property_dict) - else: - self.hypers, self.property_dict, _ = module_to_array(module=self.gp) - self.is_trained = True - return self - - def _get_gp( - self, X: Optional[np.ndarray] = None, y: Optional[np.ndarray] = None - ) -> Optional[ExactMarginalLogLikelihood]: - """ - Get the GP model with the given X and y values. As GPyTorch requires the input data to initialize a new - model, we also pass X and y here. X and y are set optional to ensure compatibility. - - Parameters - ---------- - X: Optional[np.ndarray(N, D)] - input feature vectors, N is number of data points, and D is number of feature dimensions - y: Optional[np.ndarray(N,)] - input observations, N is number of data points - Returns - ------- - mll : Optional[ExactMarginalLogLikelihood] - a GPyTorch model with Zero Mean and user specified covariance - """ - if X is None: - # To be compatible with the base model - return None - - X = torch.from_numpy(X) - y = torch.from_numpy(y) - self.gp_model = ExactGPModel(X, y, likelihood=self.likelihood, base_covar_kernel=self.kernel).double() - - mll = ExactMarginalLogLikelihood(self.likelihood, self.gp_model) - mll.double() - return mll - - def _optimize(self) -> np.ndarray: - """ - Optimizes the marginal log likelihood and returns the best found - hyperparameter configuration theta. - - Returns - ------- - theta : np.ndarray(H) - Hyperparameter vector that maximizes the marginal log likelihood - """ - x0, property_dict, bounds = module_to_array(module=self.gp) - - bounds = np.asarray(bounds).transpose().tolist() - - self.property_dict = property_dict - - p0 = [x0] - - # Avoid infinite sampling - n_tries = 5000 - for i in range(n_tries): - try: - gp_model = self.gp.pyro_sample_from_prior() - x_out = [] - for key in property_dict.keys(): - param = gp_model - param_names = key.split(".") - for name in param_names: - param = getattr(param, name) - x_out.append(param.detach().view(-1).cpu().double().clone().numpy()) - sample = np.concatenate(x_out) - p0.append(sample.astype(np.float64)) - except Exception as e: - if i == n_tries - 1: - self.logger.debug(f"Fails to sample new hyperparameters because of {e}") - raise e - continue - if len(p0) == self.n_opt_restarts: - break - - self.gp_model.train() - self.likelihood.train() - - theta_star = x0 - f_opt_star = np.inf - for i, start_point in enumerate(p0): - try: - theta, f_opt, _ = optimize.fmin_l_bfgs_b( - _scipy_objective_and_grad, - start_point, - args=(self.gp, property_dict), - bounds=bounds, - ) - except NotPSDError as e: - self.logger.warning(f"Fail to optimize the GP hyperparameters as an Error occurs: {e}") - f_opt = np.inf - theta = start_point - if f_opt < f_opt_star: - f_opt_star = f_opt - theta_star = theta - return theta_star - - def _predict( - self, X_test: np.ndarray, cov_return_type: Optional[str] = "diagonal_cov" - ) -> Tuple[np.ndarray, Optional[np.ndarray]]: - r""" - Returns the predictive mean and variance of the objective function at - the given test points. - - Parameters - ---------- - X_test: np.ndarray (N, D) - Input test points - cov_return_type: Optional[str] - Specifies what to return along with the mean. Refer ``predict()`` for more information. - - Returns - ------- - np.array(N,) - predictive mean - np.array(N,) or np.array(N, N) or None - predictive variance or standard deviation - - """ - if not self.is_trained: - raise Exception("Model has to be trained first!") - - X_test = torch.from_numpy(self._impute_inactive(X_test)) - self.likelihood.eval() - self.gp_model.eval() - - with torch.no_grad(), gpytorch.settings.fast_pred_var(): - - observed_pred = self.likelihood(self.gp_model(X_test)) - - mu = observed_pred.mean.numpy() - if cov_return_type is None: - var = None - - if self.normalize_y: - mu = self._untransform_y(mu) - - else: - if cov_return_type != "full_cov": - var = observed_pred.stddev.numpy() - var = var**2 # since we get standard deviation for faster computation - else: - # output full covariance - var = observed_pred.covariance_matrix.numpy() - - # Clip negative variances and set them to the smallest - # positive float value - var = np.clip(var, VERY_SMALL_NUMBER, np.inf) - - if self.normalize_y: - mu, var = self._untransform_y(mu, var) - - if cov_return_type == "diagonal_std": - var = np.sqrt(var) # converting variance to std deviation if specified - - return mu, var - - def sample_functions(self, X_test: np.ndarray, n_funcs: int = 1) -> np.ndarray: - """ - Samples F function values from the current posterior at the N - specified test points. - - Parameters - ---------- - X_test: np.ndarray (N, D) - Input test points - n_funcs: int - The number of function values that are drawn at each test point. - - Returns - ------- - function_samples: np.array(F, N) - The F function values drawn at the N test points. - """ - if not self.is_trained: - raise Exception("Model has to be trained first!") - self.likelihood.eval() - self.gp_model.eval() - - X_test = torch.from_numpy(self._impute_inactive(X_test)) - with torch.no_grad(): - funcs = self.likelihood(self.gp_model(X_test)).sample(torch.Size([n_funcs])).t().cpu().numpy() - - if self.normalize_y: - funcs = self._untransform_y(funcs) - - if len(funcs.shape) == 1: - return funcs[None, :] - else: - return funcs diff --git a/smac/epm/gaussian_process/kernels/__init__.py b/smac/epm/gaussian_process/kernels/__init__.py deleted file mode 100644 index 9f9bea9c6..000000000 --- a/smac/epm/gaussian_process/kernels/__init__.py +++ /dev/null @@ -1,763 +0,0 @@ -from typing import Any, Callable, Dict, List, Optional, Tuple, Union - -import math -from inspect import Signature, signature - -import numpy as np -import scipy.optimize -import scipy.spatial.distance -import scipy.special -import sklearn.gaussian_process.kernels as kernels - -from smac.epm.gaussian_process.utils.prior import Prior - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def get_conditional_hyperparameters(X: np.ndarray, Y: Optional[np.ndarray] = None) -> np.ndarray: - """Returns conditional hyperparameters.""" - # Taking care of conditional hyperparameters according to Levesque et al. - X_cond = X <= -1 - if Y is not None: - Y_cond = Y <= -1 - else: - Y_cond = X <= -1 - active = ~((np.expand_dims(X_cond, axis=1) != Y_cond).any(axis=2)) - return active - - -class MagicMixin: - # This is a mixin for a kernel to override functions of the kernel. - # Because it overrides functions of the kernel, it needs to be placed first in the inheritance - # hierarchy. For this reason it is not possible to subclass the - # Mixin from the kernel class because this will prevent it from being instantiatable. - # Therefore, mypy won't know about anything related to the superclass and I had - # to add a few type:ignore statements when accessing a member that is declared in the - # superclass such as self.has_conditions, self._call, super().get_params etc. - - prior = None # type: Optional[Prior] - - def __call__( - self, - X: np.ndarray, - Y: Optional[np.ndarray] = None, - eval_gradient: bool = False, - active: Optional[np.ndarray] = None, - ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: - """Call the kernel function.""" - if active is None and self.has_conditions: # type: ignore[attr-defined] # noqa F821 - if self.operate_on is None: - active = get_conditional_hyperparameters(X, Y) - else: - if Y is None: - active = get_conditional_hyperparameters(X[:, self.operate_on], None) - else: - active = get_conditional_hyperparameters(X[:, self.operate_on], Y[:, self.operate_on]) - - if self.operate_on is None: - rval = self._call(X, Y, eval_gradient, active) # type: ignore[attr-defined] # noqa F821 - else: - if self.len_active is None: - raise RuntimeError("len_active is not set.") - - if Y is None: - rval = self._call( # type: ignore[attr-defined] # noqa F821 - X=X[:, self.operate_on].reshape([-1, self.len_active]), - Y=None, - eval_gradient=eval_gradient, - active=active, - ) - X = X[:, self.operate_on].reshape((-1, self.len_active)) - else: - rval = self._call( # type: ignore[attr-defined] # noqa F821 - X=X[:, self.operate_on].reshape([-1, self.len_active]), - Y=Y[:, self.operate_on].reshape([-1, self.len_active]), - eval_gradient=eval_gradient, - active=active, - ) - X = X[:, self.operate_on].reshape((-1, self.len_active)) - Y = Y[:, self.operate_on].reshape((-1, self.len_active)) - - return rval - - def __add__(self, b: Union[kernels.Kernel, float]) -> kernels.Sum: - if not isinstance(b, kernels.Kernel): - return Sum(self, ConstantKernel(b)) - return Sum(self, b) - - def __radd__(self, b: Union[kernels.Kernel, float]) -> kernels.Sum: - if not isinstance(b, kernels.Kernel): - return Sum(ConstantKernel(b), self) - return Sum(b, self) - - def __mul__(self, b: Union[kernels.Kernel, float]) -> kernels.Product: - if not isinstance(b, kernels.Kernel): - return Product(self, ConstantKernel(b)) - return Product(self, b) - - def __rmul__(self, b: Union[kernels.Kernel, float]) -> kernels.Product: - if not isinstance(b, kernels.Kernel): - return Product(ConstantKernel(b), self) - return Product(b, self) - - def _signature(self, func: Callable) -> Signature: - try: - sig_ = self._signature_cache.get(func) # type: Optional[Signature] - except AttributeError: - self._signature_cache = {} # type: Dict[Callable, Signature] - sig_ = None - if sig_ is None: - sig = signature(func) - self._signature_cache[func] = sig - return sig - else: - return sig_ - - def get_params(self, deep: bool = True) -> Dict[str, Any]: - """Get parameters of this kernel. - - Parameters - ---------- - deep : boolean, optional - If True, will return the parameters for this estimator and - contained subobjects that are estimators. - - Returns - ------- - params : mapping of string to any - Parameter names mapped to their values. - """ - params = dict() - - try: - args = self._args_cache - except AttributeError: - # ignore[misc] looks like it catches all kinds of errors, but misc is actually a category from mypy: - # https://mypy.readthedocs.io/en/latest/error_code_list.html#miscellaneous-checks-misc - tmp = super().get_params(deep) # type: ignore[misc] # noqa F821 - args = list(tmp.keys()) - # Sum and Product do not clone the 'has_conditions' attribute by default. Instead of changing their - # get_params() method, we simply add the attribute here! - if "has_conditions" not in args: - args.append("has_conditions") - self._args_cache = args # type: List[Union[str, Any]] - - for arg in args: - params[arg] = getattr(self, arg, None) - return params - - @property - def hyperparameters(self) -> List[kernels.Hyperparameter]: - """Returns a list of all hyperparameter specifications.""" - try: - return self._hyperparameters_cache - except AttributeError: - pass - - r = super().hyperparameters # type: ignore[misc] # noqa F821 - self._hyperparameters_cache = r # type: List[kernels.Hyperparameter] - - return r - - @property - def n_dims(self) -> int: - """Returns the number of non-fixed hyperparameters of the kernel.""" - try: - return self._n_dims_cache - except AttributeError: - pass - - self._n_dims_cache = -1 # type: int - self._n_dims_cache = super().n_dims # type: ignore[misc] # noqa F821 - return self._n_dims_cache - - def clone_with_theta(self, theta: np.ndarray) -> kernels.Kernel: - """Returns a clone of self with given hyperparameters theta. - - Parameters - ---------- - theta : array, shape (n_dims,) - The hyperparameters - """ - self.theta = theta - return self - - def set_active_dims(self, operate_on: Optional[np.ndarray] = None) -> None: - """Sets dimensions this kernel should work on. - - Parameters - ---------- - operate_on : None, list or array, shape (n_dims,) - """ - if operate_on is not None and type(operate_on) in (list, np.ndarray): - if not isinstance(operate_on, np.ndarray): - raise TypeError("argument operate_on needs to be of type np.ndarray, but is %s" % type(operate_on)) - if operate_on.dtype != int: - raise ValueError("dtype of argument operate_on needs to be int, but is %s" % operate_on.dtype) - self.operate_on = operate_on # type: Optional[np.ndarray] - self.len_active = len(operate_on) # type: Optional[int] - else: - self.operate_on = None - self.len_active = None - - -class Sum(MagicMixin, kernels.Sum): - def __init__( - self, - k1: kernels.Kernel, - k2: kernels.Kernel, - operate_on: np.ndarray = None, - has_conditions: bool = False, - ) -> None: - super(Sum, self).__init__(k1=k1, k2=k2) - self.set_active_dims(operate_on) - self.has_conditions = has_conditions - - def _call( - self, - X: np.ndarray, - Y: Optional[np.ndarray] = None, - eval_gradient: bool = False, - active: np.ndarray = None, - ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: - """Return the kernel k(X, Y) and optionally its gradient. - - Parameters - ---------- - X : array, shape (n_samples_X, n_features) - Left argument of the returned kernel k(X, Y) - - Y : array, shape (n_samples_Y, n_features), (optional, default=None) - Right argument of the returned kernel k(X, Y). If None, k(X, X) - if evaluated instead. - - eval_gradient : bool (optional, default=False) - Determines whether the gradient with respect to the kernel - hyperparameter is determined. - - active : np.ndarray (n_samples_X, n_features) (optional) - Boolean array specifying which hyperparameters are active. - - Returns - ------- - K : array, shape (n_samples_X, n_samples_Y) - Kernel k(X, Y) - - K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) - The gradient of the kernel k(X, X) with respect to the - hyperparameter of the kernel. Only returned when eval_gradient - is True. - """ - if eval_gradient: - K1, K1_gradient = self.k1(X, Y, eval_gradient=True, active=active) - K2, K2_gradient = self.k2(X, Y, eval_gradient=True, active=active) - return K1 + K2, np.dstack((K1_gradient, K2_gradient)) - else: - return self.k1(X, Y, active=active) + self.k2(X, Y, active=active) - - -class Product(MagicMixin, kernels.Product): - def __init__( - self, - k1: kernels.Kernel, - k2: kernels.Kernel, - operate_on: np.ndarray = None, - has_conditions: bool = False, - ) -> None: - super(Product, self).__init__(k1=k1, k2=k2) - self.set_active_dims(operate_on) - self.has_conditions = has_conditions - - def _call( - self, - X: np.ndarray, - Y: Optional[np.ndarray] = None, - eval_gradient: bool = False, - active: np.ndarray = None, - ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: - """Return the kernel k(X, Y) and optionally its gradient. - - Parameters - ---------- - X : array, shape (n_samples_X, n_features) - Left argument of the returned kernel k(X, Y) - - Y : array, shape (n_samples_Y, n_features), (optional, default=None) - Right argument of the returned kernel k(X, Y). If None, k(X, X) - if evaluated instead. - - eval_gradient : bool (optional, default=False) - Determines whether the gradient with respect to the kernel - hyperparameter is determined. - - active : np.ndarray (n_samples_X, n_features) (optional) - Boolean array specifying which hyperparameters are active. - - Returns - ------- - K : array, shape (n_samples_X, n_samples_Y) - Kernel k(X, Y) - - K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) - The gradient of the kernel k(X, X) with respect to the - hyperparameter of the kernel. Only returned when eval_gradient - is True. - """ - if eval_gradient: - K1, K1_gradient = self.k1(X, Y, eval_gradient=True, active=active) - K2, K2_gradient = self.k2(X, Y, eval_gradient=True, active=active) - return K1 * K2, np.dstack((K1_gradient * K2[:, :, np.newaxis], K2_gradient * K1[:, :, np.newaxis])) - else: - return self.k1(X, Y, active=active) * self.k2(X, Y, active=active) - - -class ConstantKernel(MagicMixin, kernels.ConstantKernel): - def __init__( - self, - constant_value: float = 1.0, - constant_value_bounds: Tuple[float, float] = (1e-5, 1e5), - operate_on: Optional[np.ndarray] = None, - prior: Optional[Prior] = None, - has_conditions: bool = False, - ) -> None: - - super(ConstantKernel, self).__init__(constant_value=constant_value, constant_value_bounds=constant_value_bounds) - self.set_active_dims(operate_on) - self.prior = prior - self.has_conditions = has_conditions - - def _call( - self, - X: np.ndarray, - Y: Optional[np.ndarray] = None, - eval_gradient: bool = False, - active: Optional[np.ndarray] = None, - ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: - """Return the kernel k(X, Y) and optionally its gradient. - - Parameters - ---------- - X : array, shape (n_samples_X, n_features) - Left argument of the returned kernel k(X, Y) - - Y : array, shape (n_samples_Y, n_features), (optional, default=None) - Right argument of the returned kernel k(X, Y). If None, k(X, X) - if evaluated instead. - - eval_gradient : bool (optional, default=False) - Determines whether the gradient with respect to the kernel - hyperparameter is determined. Only supported when Y is None. - - active : np.ndarray (n_samples_X, n_features) (optional) - Boolean array specifying which hyperparameters are active. - - Returns - ------- - K : array, shape (n_samples_X, n_samples_Y) - Kernel k(X, Y) - - K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) - The gradient of the kernel k(X, X) with respect to the - hyperparameter of the kernel. Only returned when eval_gradient - is True. - """ - X = np.atleast_2d(X) - if Y is None: - Y = X - elif eval_gradient: - raise ValueError("Gradient can only be evaluated when Y is None.") - - K = np.full( - (X.shape[0], Y.shape[0]), - self.constant_value, - dtype=np.array(self.constant_value).dtype, - ) - if eval_gradient: - if not self.hyperparameter_constant_value.fixed: - return ( - K, - np.full( - (X.shape[0], X.shape[0], 1), - self.constant_value, - dtype=np.array(self.constant_value).dtype, - ), - ) - else: - return K, np.empty((X.shape[0], X.shape[0], 0)) - else: - return K - - -class Matern(MagicMixin, kernels.Matern): - def __init__( - self, - length_scale: Union[float, Tuple[float, ...], np.ndarray] = 1.0, - length_scale_bounds: Union[Tuple[float, float], List[Tuple[float, float]], np.ndarray] = ( - 1e-5, - 1e5, - ), - nu: float = 1.5, - operate_on: Optional[np.ndarray] = None, - prior: Optional[Prior] = None, - has_conditions: bool = False, - ) -> None: - - super(Matern, self).__init__(length_scale=length_scale, length_scale_bounds=length_scale_bounds, nu=nu) - self.set_active_dims(operate_on) - self.prior = prior - self.has_conditions = has_conditions - - def _call( - self, - X: np.ndarray, - Y: Optional[np.ndarray] = None, - eval_gradient: bool = False, - active: Optional[np.ndarray] = None, - ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: - """Return the kernel k(X, Y) and optionally its gradient. - - Parameters - ---------- - X : array, shape (n_samples_X, n_features) - Left argument of the returned kernel k(X, Y) - Y : array, shape (n_samples_Y, n_features), (optional, default=None) - Right argument of the returned kernel k(X, Y). If None, k(X, X) - if evaluated instead. - eval_gradient : bool (optional, default=False) - Determines whether the gradient with respect to the kernel - hyperparameter is determined. Only supported when Y is None. - active : np.ndarray (n_samples_X, n_features) (optional) - Boolean array specifying which hyperparameters are active. - - Returns - ------- - K : array, shape (n_samples_X, n_samples_Y) - Kernel k(X, Y) - K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) - The gradient of the kernel k(X, X) with respect to the - hyperparameter of the kernel. Only returned when eval_gradient - is True. - """ - X = np.atleast_2d(X) - length_scale = kernels._check_length_scale(X, self.length_scale) - - if Y is None: - dists = scipy.spatial.distance.pdist(X / length_scale, metric="euclidean") - else: - if eval_gradient: - raise ValueError("Gradient can only be evaluated when Y is None.") - dists = scipy.spatial.distance.cdist(X / length_scale, Y / length_scale, metric="euclidean") - - if self.nu == 0.5: - K = np.exp(-dists) - elif self.nu == 1.5: - K = dists * math.sqrt(3) - K = (1.0 + K) * np.exp(-K) - elif self.nu == 2.5: - K = dists * math.sqrt(5) - K = (1.0 + K + K**2 / 3.0) * np.exp(-K) - else: # general case; expensive to evaluate - K = dists - K[K == 0.0] += np.finfo(float).eps # strict zeros result in nan - tmp = math.sqrt(2 * self.nu) * K - K.fill((2 ** (1.0 - self.nu)) / scipy.special.gamma(self.nu)) - K *= tmp**self.nu - K *= scipy.special.kv(self.nu, tmp) - - if Y is None: - # convert from upper-triangular matrix to square matrix - K = scipy.spatial.distance.squareform(K) - np.fill_diagonal(K, 1) - - if active is not None: - K = K * active - - if eval_gradient: - if self.hyperparameter_length_scale.fixed: - # Hyperparameter l kept fixed - K_gradient = np.empty((X.shape[0], X.shape[0], 0)) - return K, K_gradient - - # We need to recompute the pairwise dimension-wise distances - if self.anisotropic: - D = (X[:, np.newaxis, :] - X[np.newaxis, :, :]) ** 2 / (length_scale**2) - else: - D = scipy.spatial.distance.squareform(dists**2)[:, :, np.newaxis] - - if self.nu == 0.5: - K_gradient = K[..., np.newaxis] * D / np.sqrt(D.sum(2))[:, :, np.newaxis] - K_gradient[~np.isfinite(K_gradient)] = 0 - elif self.nu == 1.5: - K_gradient = 3 * D * np.exp(-np.sqrt(3 * D.sum(-1)))[..., np.newaxis] - elif self.nu == 2.5: - tmp = np.sqrt(5 * D.sum(-1))[..., np.newaxis] - K_gradient = 5.0 / 3.0 * D * (tmp + 1) * np.exp(-tmp) - else: - # original sklearn code would approximate gradient numerically, but this would violate our assumption - # that the kernel hyperparameters are not changed within __call__ - raise ValueError(self.nu) - - if not self.anisotropic: - return K, K_gradient[:, :].sum(-1)[:, :, np.newaxis] - else: - return K, K_gradient - else: - return K - - -class RBF(MagicMixin, kernels.RBF): - def __init__( - self, - length_scale: Union[float, Tuple[float, ...]] = 1.0, - length_scale_bounds: Union[Tuple[float, float], List[Tuple[float, float]]] = ( - 1e-5, - 1e5, - ), - operate_on: Optional[np.ndarray] = None, - prior: Optional[Prior] = None, - has_conditions: bool = False, - ) -> None: - - super(RBF, self).__init__(length_scale=length_scale, length_scale_bounds=length_scale_bounds) - self.set_active_dims(operate_on) - self.prior = prior - self.has_conditions = has_conditions - - def _call( - self, - X: np.ndarray, - Y: Optional[np.ndarray] = None, - eval_gradient: bool = False, - active: Optional[np.ndarray] = None, - ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: - """Return the kernel k(X, Y) and optionally its gradient. - - Parameters - ---------- - X : array, shape (n_samples_X, n_features) - Left argument of the returned kernel k(X, Y) - Y : array, shape (n_samples_Y, n_features), (optional, default=None) - Right argument of the returned kernel k(X, Y). If None, k(X, X) - if evaluated instead. - eval_gradient : bool (optional, default=False) - Determines whether the gradient with respect to the kernel - hyperparameter is determined. Only supported when Y is None. - active : np.ndarray (n_samples_X, n_features) (optional) - Boolean array specifying which hyperparameters are active. - - Returns - ------- - K : array, shape (n_samples_X, n_samples_Y) - Kernel k(X, Y) - K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) - The gradient of the kernel k(X, X) with respect to the - hyperparameter of the kernel. Only returned when eval_gradient - is True. - """ - X = np.atleast_2d(X) - length_scale = kernels._check_length_scale(X, self.length_scale) - - if Y is None: - dists = scipy.spatial.distance.pdist(X / length_scale, metric="sqeuclidean") - K = np.exp(-0.5 * dists) - # convert from upper-triangular matrix to square matrix - K = scipy.spatial.distance.squareform(K) - np.fill_diagonal(K, 1) - else: - if eval_gradient: - raise ValueError("Gradient can only be evaluated when Y is None.") - dists = scipy.spatial.distance.cdist(X / length_scale, Y / length_scale, metric="sqeuclidean") - K = np.exp(-0.5 * dists) - - if active is not None: - K = K * active - - if eval_gradient: - if self.hyperparameter_length_scale.fixed: - # Hyperparameter l kept fixed - return K, np.empty((X.shape[0], X.shape[0], 0)) - elif not self.anisotropic or length_scale.shape[0] == 1: - K_gradient = (K * scipy.spatial.distance.squareform(dists))[:, :, np.newaxis] - return K, K_gradient - elif self.anisotropic: - # We need to recompute the pairwise dimension-wise distances - K_gradient = (X[:, np.newaxis, :] - X[np.newaxis, :, :]) ** 2 / (length_scale**2) - K_gradient *= K[..., np.newaxis] - return K, K_gradient - - return K - - -class WhiteKernel(MagicMixin, kernels.WhiteKernel): - def __init__( - self, - noise_level: Union[float, Tuple[float, ...]] = 1.0, - noise_level_bounds: Union[Tuple[float, float], List[Tuple[float, float]]] = ( - 1e-5, - 1e5, - ), - operate_on: Optional[np.ndarray] = None, - prior: Optional[Prior] = None, - has_conditions: bool = False, - ) -> None: - - super(WhiteKernel, self).__init__(noise_level=noise_level, noise_level_bounds=noise_level_bounds) - self.set_active_dims(operate_on) - self.prior = prior - self.has_conditions = has_conditions - - def _call( - self, - X: np.ndarray, - Y: Optional[np.ndarray] = None, - eval_gradient: bool = False, - active: Optional[np.ndarray] = None, - ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: - """Return the kernel k(X, Y) and optionally its gradient. - - Parameters - ---------- - X : array, shape (n_samples_X, n_features) - Left argument of the returned kernel k(X, Y) - Y : array, shape (n_samples_Y, n_features), (optional, default=None) - Right argument of the returned kernel k(X, Y). If None, k(X, X) - if evaluated instead. - eval_gradient : bool (optional, default=False) - Determines whether the gradient with respect to the kernel - hyperparameter is determined. Only supported when Y is None. - active : np.ndarray (n_samples_X, n_features) (optional) - Boolean array specifying which hyperparameters are active. - - Returns - ------- - K : array, shape (n_samples_X, n_samples_Y) - Kernel k(X, Y) - K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) - The gradient of the kernel k(X, X) with respect to the - hyperparameter of the kernel. Only returned when eval_gradient - is True. - """ - X = np.atleast_2d(X) - - if Y is not None and eval_gradient: - raise ValueError("Gradient can only be evaluated when Y is None.") - - if Y is None: - K = self.noise_level * np.eye(X.shape[0]) - - if active is not None: - K = K * active - - if eval_gradient: - if not self.hyperparameter_noise_level.fixed: - return (K, self.noise_level * np.eye(X.shape[0])[:, :, np.newaxis]) - else: - return K, np.empty((X.shape[0], X.shape[0], 0)) - else: - return K - else: - return np.zeros((X.shape[0], Y.shape[0])) - - -class HammingKernel( - MagicMixin, - kernels.StationaryKernelMixin, - kernels.NormalizedKernelMixin, - kernels.Kernel, -): - def __init__( - self, - length_scale: Union[float, Tuple[float, ...], np.ndarray] = 1.0, - length_scale_bounds: Union[Tuple[float, float], List[Tuple[float, float]], np.ndarray] = ( - 1e-5, - 1e5, - ), - operate_on: Optional[np.ndarray] = None, - prior: Optional[Prior] = None, - has_conditions: bool = False, - ) -> None: - self.length_scale = length_scale - self.length_scale_bounds = length_scale_bounds - self.set_active_dims(operate_on) - self.prior = prior - self.has_conditions = has_conditions - - @property - def hyperparameter_length_scale(self) -> kernels.Hyperparameter: - """Hyperparameter of the length scale.""" - length_scale = self.length_scale - anisotropic = np.iterable(length_scale) and len(length_scale) > 1 # type: ignore - if anisotropic: - return kernels.Hyperparameter("length_scale", "numeric", self.length_scale_bounds, len(length_scale)) # type: ignore # noqa: E501 - return kernels.Hyperparameter("length_scale", "numeric", self.length_scale_bounds) - - def _call( - self, - X: np.ndarray, - Y: Optional[np.ndarray] = None, - eval_gradient: bool = False, - active: Optional[np.ndarray] = None, - ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: - """Return the kernel k(X, Y) and optionally its gradient. - - Parameters - ---------- - X : [array-like, shape=(n_samples_X, n_features)] - Left argument of the returned kernel k(X, Y) - Y : [array-like, shape=(n_samples_Y, n_features) or None(default)] - Right argument of the returned kernel k(X, Y). If None, k(X, X) - if evaluated instead. - eval_gradient : [bool, False(default)] - Determines whether the gradient with respect to the kernel - hyperparameter is determined. Only supported when Y is None. - active : np.ndarray (n_samples_X, n_features) (optional) - Boolean array specifying which hyperparameters are active. - - Returns - ------- - K : [array-like, shape=(n_samples_X, n_samples_Y)] - Kernel k(X, Y) - - K_gradient : [array-like, shape=(n_samples_X, n_samples_X, n_dims)] - The gradient of the kernel k(X, X) with respect to the - hyperparameter of the kernel. Only returned when eval_gradient - is True. - - Note - ---- - Code partially copied from skopt (https://github.com/scikit-optimize). - Made small changes to only compute necessary values and use scikit-learn helper functions. - """ - X = np.atleast_2d(X) - length_scale = kernels._check_length_scale(X, self.length_scale) - - if Y is None: - Y = X - elif eval_gradient: - raise ValueError("gradient can be evaluated only when Y != X") - else: - Y = np.atleast_2d(Y) - - indicator = np.expand_dims(X, axis=1) != Y - K = (-1 / (2 * length_scale**2) * indicator).sum(axis=2) - K = np.exp(K) - - if active is not None: - K = K * active - - if eval_gradient: - # dK / d theta = (dK / dl) * (dl / d theta) - # theta = log(l) => dl / d (theta) = e^theta = l - # dK / d theta = l * dK / dl - - # dK / dL computation - if np.iterable(length_scale) and length_scale.shape[0] > 1: # type: ignore - grad = np.expand_dims(K, axis=-1) * np.array(indicator, dtype=np.float32) - else: - grad = np.expand_dims(K * np.sum(indicator, axis=2), axis=-1) - - grad *= 1 / length_scale**3 - - return K, grad - return K diff --git a/smac/epm/gaussian_process/kernels/boing.py b/smac/epm/gaussian_process/kernels/boing.py deleted file mode 100644 index 33d955e1a..000000000 --- a/smac/epm/gaussian_process/kernels/boing.py +++ /dev/null @@ -1,560 +0,0 @@ -from typing import Any, Dict, Optional, Tuple, Union - -import copy -import math - -import gpytorch -import numpy as np -import torch -from gpytorch import settings -from gpytorch.kernels import Kernel, MaternKernel, ProductKernel, ScaleKernel -from gpytorch.lazy import ( - DiagLazyTensor, - MatmulLazyTensor, - PsdSumLazyTensor, - RootLazyTensor, - delazify, -) -from gpytorch.likelihoods import GaussianLikelihood -from gpytorch.means.mean import Mean -from gpytorch.utils.cholesky import psd_safe_cholesky -from sklearn.gaussian_process.kernels import Kernel as SKLKernels - -from smac.epm.gaussian_process.kernels import ConstantKernel, WhiteKernel - - -class MixedKernel(ProductKernel): - """ - A special form of ProductKernel. It is composed of a cont_kernel and a cat_kernel that work with continuous and - categorical parameters, respectively. Its forward pass allows an additional parameter to determine if only - cont_kernel is applied to the input. - """ - - def __init__(self, cont_kernel: Kernel, cat_kernel: Kernel): - kernels = cont_kernel.kernels if isinstance(cont_kernel, ProductKernel) else [cont_kernel] - kernels += cat_kernel.kernels if isinstance(cat_kernel, ProductKernel) else [cat_kernel] - super().__init__(*kernels) - self.cont_kernel = cont_kernel - self.cat_kernel = cat_kernel - - def forward( - self, x1: torch.Tensor, x2: torch.Tensor, diag: bool = False, cont_only: bool = False, **params: Any - ) -> gpytorch.lazy.LazyTensor: - """Compute kernel values, if cont_only is True, then the categorical kernel is omitted""" - if not cont_only: - return super().forward(x1, x2, diag, **params) - else: - return self.cont_kernel(x1, x2, diag, **params) - - -def construct_gp_kernel( - kernel_kwargs: Dict[str, Any], cont_dims: np.ndarray, cat_dims: np.ndarray -) -> Union[Kernel, SKLKernels]: - """ - Construct a GP kernel with the given kernel init argument, the cont_dims, and cat_dims of the problem. Since the - subspace might not have the same number of dimensions as the global search space. - We need to reconstruct the kernel every time when a new subspace is generated. - - Parameters - ---------- - kernel_kwargs: Dict[str, Any] - kernel kwargs. Arguments to initialize the kernels. It needs to contain the following items: - cont_kernel: type of continuous kernels - cont_kernel_kwargs: additional arguments for continuous kernels, for instance, length constraints and prior - cat_kernel: type of categorical kernels - cat_kernel_kwargs: additional arguments for categorical kernels, for instance, length constraints and prior - scale_kernel: type of scale kernels - scale_kernel_kwargs: additional arguments for scale kernels, for instance, length constraints and prior - cont_dims: np.ndarray - dimensions of continuous hyperparameters - cat_dims: np.ndarray - dimensions of categorical hyperparameters - Returns - ------- - kernel: Union[Kernel, SKLKernels] - constructed kernels - - """ - if len(cont_dims) > 0: - cont_kernel_class = kernel_kwargs.get("cont_kernel", MaternKernel) - cont_kernel_kwargs = kernel_kwargs.get("cont_kernel_kwargs", {}) - cont_kernel = cont_kernel_class( - ard_num_dims=cont_dims.shape[-1], active_dims=tuple(cont_dims), **cont_kernel_kwargs - ).double() - - if len(cat_dims) > 0: - cat_kernel_class = kernel_kwargs.get("cat_kernel", MaternKernel) - cat_kernel_kwargs = kernel_kwargs.get("cat_kernel_kwargs", {}) - cat_kernel = cat_kernel_class( - ard_num_dims=cat_dims.shape[-1], active_dims=tuple(cat_dims), **cat_kernel_kwargs - ).double() - - if len(cont_dims) > 0 and len(cat_dims) > 0: - if isinstance(cont_kernel, SKLKernels): - base_kernel = cont_kernel * cat_kernel - else: - base_kernel = MixedKernel(cont_kernel=cont_kernel, cat_kernel=cat_kernel) - elif len(cont_dims) > 0 and len(cat_dims) == 0: - base_kernel = cont_kernel - elif len(cont_dims) == 0 and len(cat_dims) > 0: - base_kernel = cat_kernel - else: - raise ValueError("Either cont_dims or cat_dims must exist!") - if isinstance(base_kernel, SKLKernels): - scale_kernel_class = kernel_kwargs.get("scale_kernel", ConstantKernel) - scale_kernel_kwargs = kernel_kwargs.get("scale_kernel_kwargs", {}) - scale_kernel = scale_kernel_class(**scale_kernel_kwargs) - - noise_kernel_class = kernel_kwargs.get("noise_kernel", WhiteKernel) - noise_kernel_kwargs = kernel_kwargs.get("noise_kernel_kwargs", {}) - noise_kernel = noise_kernel_class(**noise_kernel_kwargs) - - gp_kernel = scale_kernel * base_kernel + noise_kernel - else: - scale_kernel_class = kernel_kwargs.get("scale_kernel", ScaleKernel) - scale_kernel_kwargs = kernel_kwargs.get("scale_kernel_kwargs", {}) - gp_kernel = scale_kernel_class(base_kernel=base_kernel, **scale_kernel_kwargs) - return gp_kernel - - -class FITCKernel(Kernel): - def __init__( - self, - base_kernel: Kernel, - X_inducing: torch.Tensor, - likelihood: GaussianLikelihood, - X_out: torch.Tensor, - y_out: torch.Tensor, - active_dims: Optional[Tuple[int]] = None, - ): - r"""A reimplementation of FITC Kernel that computes the posterior explicitly for globally augmented local GP. - This should work exactly the same as a gpytorch.kernel.InducingPointKernel. - However, it takes much less time when combined with LGPGA. - References: Edward Snelson and Zoubin Ghahramani. Sparse Gaussian processes using pseudo-inputs. Advances in - Neural Information Processing Systems 18, Cambridge, Massachusetts, 2006. The MIT Press. - https://papers.nips.cc/paper/2005/hash/4491777b1aa8b5b32c2e8666dbe1a495-Abstract.html - - Mean value is computed with: - \mathbf{\mu_{l'}} = \mathbf{K_{l',u} \Sigma K_{u,1} \Lambda}^{-1}\mathbf{y_g} \label{eq:mean_sgp} - and variance value: - \mathbf{\sigma}^2_{l'} = \mathbf{K_{l',l'}} - \mathbf{Q_{l', l'} + \mathbf{K_{l', u}\Sigma K_{u, l'}}} - \mathbf{\Sigma} = (\mathbf{K_{u,u}} + \mathbf{K_{u, g} \Lambda}^{-1}\mathbf{K_{g,u}})^{-1} - \mathbf{\Lambda} = diag[\mathbf{K_{g,g}-Q_{g,g}} + \sigma^2_{noise}\idenmat] - ---------- - base_kernel: Kernel - base kernel function - X_inducing: torch.Tensor (N_inducing, D) - inducing points, a torch tensor with shape (N_inducing, D), N_inducing is the number of the inducing points - likelihood: GaussianLikelihood - GP likelihood - X_out: torch.Tensor (N_out,D) - data features outside the subregion, it needs to be of size (N_out, D), N_out is the number of points - outside the subspace - y_out: torch.Tensor - data observations outside the subregion - active_dims: typing.Optional[typing.Tuple[int]] = None - Set this if you want to compute the covariance of only a few input dimensions. The ints - corresponds to the indices of the dimensions. Default: `None`. - """ - super(FITCKernel, self).__init__(active_dims=active_dims) - self.has_lengthscale = base_kernel.has_lengthscale - self.base_kernel = base_kernel - self.likelihood = likelihood - - if X_inducing.ndimension() == 1: - X_inducing = X_inducing.unsqueeze(-1) - - self.X_out = X_out - self.y_out = y_out - self.register_parameter(name="X_inducing", parameter=torch.nn.Parameter(X_inducing)) - - def _clear_cache(self) -> None: - if hasattr(self, "_cached_kernel_mat"): - del self._cached_kernel_mat - if hasattr(self, "_cached_inducing_sigma"): - del self._cached_inducing_sigma - if hasattr(self, "_cached_poster_mean_mat"): - del self._cached_poster_mean_mat - if hasattr(self, "_train_cached_k_u1"): - del self._train_cached_k_u1 - if hasattr(self, "_train_cached_lambda_diag_inv"): - del self._train_cached_lambda_diag_inv - if hasattr(self, "_train_cached_posterior_mean"): - del self._train_cached_posterior_mean - if hasattr(self, "_cached_kernel_inv_root"): - del self._cached_kernel_inv_root - - @property - def _inducing_mat(self) -> torch.Tensor: - """ - Computes inducing matrix, K(X_inducing, X_inducing) - - Returns - ------- - res: torch.Tensor (N_inducing, N_inducing) - K(X_inducing, X_inducing) - """ - if not self.training and hasattr(self, "_cached_kernel_mat"): - return self._cached_kernel_mat - else: - res = delazify(self.base_kernel(self.X_inducing, self.X_inducing)) - if not self.training: - self._cached_kernel_mat = res # type: torch.Tensor - return res - - @property - def _inducing_inv_root(self) -> torch.Tensor: - """ - Computes the inverse of the inducing matrix: K_inv(X_inducing, X_inducing) = K(X_inducing, X_inducing)^(-1) - - Returns - ------- - res: torch.Tensor (N_inducing, N_inducing) - K_inv(X_inducing, X_inducing) - """ - if not self.training and hasattr(self, "_cached_kernel_inv_root"): - return self._cached_kernel_inv_root - else: - chol = psd_safe_cholesky(self._inducing_mat, upper=True, jitter=settings.cholesky_jitter.value()) - eye = torch.eye(chol.size(-1), device=chol.device, dtype=chol.dtype) - inv_root = torch.triangular_solve(eye, chol)[0] - - res = inv_root - if not self.training: - self._cached_kernel_inv_root = res # type: torch.Tensor - return res - - @property - def _k_u1(self) -> torch.Tensor: - """ - Computes the covariance matrix between the X_inducing and X_out : K(X_inducing, X_out) - - Returns - ------- - res: torch.Tensor (N_inducing, N_out) - K(X_inducing, X_out) - """ - if not self.training and hasattr(self, "_cached_k_u1"): - return self._cached_k_u1 - else: - res = delazify(self.base_kernel(self.X_inducing, self.X_out)) - if not self.training: - self._cached_k_u1 = res # type: torch.Tensor - else: - self._train_cached_k_u1 = res # type: torch.Tensor - return res - - @property - def _lambda_diag_inv(self) -> torch.Tensor: - r"""Computes the inverse of lambda matrix, it is computed by - \Lambda = diag[\mathbf{K_{X_out,X_out}-Q_{X_out,X_out}} + \sigma^2_{noise}\idenmat] and - Q{X_out, X_out} = K(X_out, X_inducing) K^{-1}(X_inducing,X_inducing) K(X_inducing, X_out) - - Returns - ------- - res: torch.Tensor (N_out, N_out) - inverse of the diagonal matrix lambda - """ - if not self.training and hasattr(self, "_cached_lambda_diag_inv"): - return self._cached_lambda_diag_inv - else: - diag_k11 = delazify(self.base_kernel(self.X_out, diag=True)) - - diag_q11 = delazify(RootLazyTensor(self._k_u1.transpose(-1, -2).matmul(self._inducing_inv_root))).diag() - - # Diagonal correction for predictive posterior - correction = (diag_k11 - diag_q11).clamp(0, math.inf) - - sigma = self.likelihood._shaped_noise_covar(correction.shape).diag() - - res = delazify(DiagLazyTensor((correction + sigma).reciprocal())) - - if not self.training: - self._cached_lambda_diag_inv = res # type: torch.Tensor - else: - self._train_cached_lambda_diag_inv = res # type: torch.Tensor - return res - - @property - def _inducing_sigma(self) -> torch.Tensor: - r"""Computes the inverse of lambda matrix, it is computed by - \mathbf{\Sigma} = (\mathbf{K_{X_inducing,X_inducing}} + - \mathbf{K_{X_inducing, X_out} \Lambda}^{-1}\mathbf{K_{X_out,X_inducing}}) - - Returns - ------- - res: torch.Tensor (N_inducing, N_inducing) - \Sigma - """ - if not self.training and hasattr(self, "_cached_inducing_sigma"): - return self._cached_inducing_sigma - else: - k_u1 = self._k_u1 - res = PsdSumLazyTensor( - self._inducing_mat, - MatmulLazyTensor(k_u1, MatmulLazyTensor(self._lambda_diag_inv, k_u1.transpose(-1, -2))), - ) - res = delazify(res) - if not self.training: - self._cached_inducing_sigma = res # type: torch.Tensor - - return res - - @property - def _inducing_sigma_inv_root(self) -> torch.Tensor: - r"""Inverse of Sigma matrix: - - Returns - ------- - res: torch.Tensor (N_inducing, N_inducing) - \Sigma ^{-1} - """ - if not self.training and hasattr(self, "_cached_inducing_sigma_inv_root"): - return self._cached_inducing_sigma_inv_root - else: - chol = psd_safe_cholesky(self._inducing_sigma, upper=True, jitter=settings.cholesky_jitter.value()) - - eye = torch.eye(chol.size(-1), device=chol.device, dtype=chol.dtype) - inv_root = torch.triangular_solve(eye, chol)[0] - res = inv_root - if not self.training: - self._cached_inducing_sigma_inv_root = res # type: torch.Tensor - return res - - @property - def _poster_mean_mat(self) -> torch.Tensor: - r"""A cached value for computing the posterior mean of a sparse kernel it is defined by - \Sigma K_{u, 1} \Lambda}^{-1}\mathbf{y_out} - - Returns - ------- - res: torch.Tensor (N_inducing, 1) - cached posterior mean - """ - if not self.training and hasattr(self, "_cached_poster_mean_mat"): - return self._cached_poster_mean_mat - else: - inducing_sigma_inv_root = self._inducing_sigma_inv_root - sigma = RootLazyTensor(inducing_sigma_inv_root) - - k_u1 = self._k_u1 - lambda_diag_inv = self._lambda_diag_inv - - res_mat = delazify(MatmulLazyTensor(sigma, MatmulLazyTensor(k_u1, lambda_diag_inv))) - - res = torch.matmul(res_mat, self.y_out) - - if not self.training: - self._cached_poster_mean_mat = res # type: torch.Tensor - return res - - def _get_covariance(self, x1: torch.Tensor, x2: torch.Tensor) -> gpytorch.lazy.LazyTensor: - r"""Compute the posterior covariance matrix of a sparse kernel explicitly - - Parameters - ---------- - x1: torch.Tensor(N_x1, D) - first input of the FITC kernel - x2: torch.Tensor(N_x2, D) - second input of the FITC kernel - - Returns - ------- - res: Optional[torch.Tensor (N_x1, 1), PsdSumLazyTensor] - a cached value for computing the posterior mean, it - is defined by \Sigma K_{u, 1} \Lambda}^{-1}\mathbf{y_out} - """ - k_x1x2 = self.base_kernel(x1, x2) - k_x1u = delazify(self.base_kernel(x1, self.X_inducing)) - inducing_inv_root = self._inducing_inv_root - inducing_sigma_inv_root = self._inducing_sigma_inv_root - if torch.equal(x1, x2): - q_x1x2 = RootLazyTensor(k_x1u.matmul(inducing_inv_root)) - - s_x1x2 = RootLazyTensor(k_x1u.matmul(inducing_sigma_inv_root)) - else: - k_x2u = delazify(self.base_kernel(x2, self.X_inducing)) - q_x1x2 = MatmulLazyTensor( - k_x1u.matmul(inducing_inv_root), k_x2u.matmul(inducing_inv_root).transpose(-1, -2) - ) - s_x1x2 = MatmulLazyTensor( - k_x1u.matmul(inducing_sigma_inv_root), k_x2u.matmul(inducing_sigma_inv_root).transpose(-1, -2) - ) - covar = PsdSumLazyTensor(k_x1x2, -1.0 * q_x1x2, s_x1x2) - - if self.training: - k_iu = self.base_kernel(x1, self.X_inducing) - sigma = RootLazyTensor(inducing_sigma_inv_root) - - k_u1 = self._train_cached_k_u1 if hasattr(self, "_train_cached_k_u1") else self._k_u1 - lambda_diag_inv = ( - self._train_cached_lambda_diag_inv - if hasattr(self, "_train_cached_lambda_diag_inv") - else self._lambda_diag_inv - ) - - mean = torch.matmul( - delazify(MatmulLazyTensor(k_iu, MatmulLazyTensor(sigma, MatmulLazyTensor(k_u1, lambda_diag_inv)))), - self.y_out, - ) - - self._train_cached_posterior_mean = mean # type: torch.Tensor - return covar - - def posterior_mean(self, inputs: torch.Tensor) -> torch.Tensor: - """ - The posterior mean of the FITC kernel, will serve as the prior mean of the dense kernel. - - Parameters - ---------- - inputs: torch.Tensor(N_inputs, D) - input of the FITC kernel - - Returns - ------- - res: Torch.Tensor (N_inputs, 1) - The posterior mean of the FITC Kernel - """ - if self.training and hasattr(self, "_train_cached_posterior_mean"): - return self._train_cached_posterior_mean - if inputs.ndimension() == 1: - inputs = inputs.unsqueeze(1) - - k_iu = delazify(self.base_kernel(inputs, self.X_inducing, cont_only=True)) - poster_mean = self._poster_mean_mat - res = torch.matmul(k_iu, poster_mean) - return res - - def forward( - self, x1: torch.Tensor, x2: torch.Tensor, diag: bool = False, **kwargs: Dict - ) -> gpytorch.lazy.LazyTensor: - """Compute the kernel function""" - covar = self._get_covariance(x1, x2) - if self.training: - if not torch.equal(x1, x2): - raise RuntimeError("x1 should equal x2 in training mode") - - if diag: - return covar.diag() - else: - return covar - - def num_outputs_per_input(self, x1: torch.Tensor, x2: torch.Tensor) -> int: - """ - Number of outputs given the inputs - if x1 is of size `n x d` and x2 is size `m x d`, then the size of the kernel - will be `(n * num_outputs_per_input) x (m * num_outputs_per_input)` - - Parameters - ---------- - x1: torch.Tensor - the first input of the kernel - x2: torch.Tensor - the second input of the kernel - Returns - ------- - res: int - for base kernels such as matern or RBF kernels, this value needs to be 1. - """ - return self.base_kernel.num_outputs_per_input(x1, x2) - - def __deepcopy__(self, memo: Dict) -> "FITCKernel": - replace_inv_root = False - replace_kernel_mat = False - replace_k_u1 = False - replace_lambda_diag_inv = False - replace_inducing_sigma = False - replace_inducing_sigma_inv_root = False - replace_poster_mean = False - - if hasattr(self, "_cached_kernel_inv_root"): - replace_inv_root = True - kernel_inv_root = self._cached_kernel_inv_root - if hasattr(self, "_cached_kernel_mat"): - replace_kernel_mat = True - kernel_mat = self._cached_kernel_mat - if hasattr(self, "_cached_k_u1"): - replace_k_u1 = True - k_u1 = self._cached_k_u1 - if hasattr(self, "_cached_lambda_diag_inv"): - replace_lambda_diag_inv = True - lambda_diag_inv = self._cached_lambda_diag_inv - if hasattr(self, "_cached_inducing_sigma"): - replace_inducing_sigma = True - inducing_sigma = self._cached_inducing_sigma - if hasattr(self, "_cached_inducing_sigma_inv_root"): - replace_inducing_sigma_inv_root = True - inducing_sigma_inv_root = self._cached_inducing_sigma_inv_root - if hasattr(self, "_cached_poster_mean_mat"): - replace_poster_mean = True - poster_mean_mat = self._cached_poster_mean_mat - - cp = self.__class__( - base_kernel=copy.deepcopy(self.base_kernel), - X_inducing=copy.deepcopy(self.X_inducing), - X_out=self.X_out, - y_out=self.y_out, - likelihood=copy.deepcopy(self.likelihood), - active_dims=self.active_dims, - ) - - if replace_inv_root: - cp._cached_kernel_inv_root = kernel_inv_root - - if replace_kernel_mat: - cp._cached_kernel_mat = kernel_mat - - if replace_k_u1: - cp._cached_k_u1 = k_u1 - - if replace_lambda_diag_inv: - cp._cached_lambda_diag_inv = lambda_diag_inv - - if replace_inducing_sigma: - cp._cached_inducing_sigma = inducing_sigma - - if replace_inducing_sigma_inv_root: - cp._cached_inducing_sigma_inv_root = inducing_sigma_inv_root - - if replace_poster_mean: - cp._cached_poster_mean_mat = poster_mean_mat - - return cp - - -class FITCMean(Mean): - def __init__(self, covar_module: FITCKernel, batch_shape: torch.Size = torch.Size(), **kwargs: Dict): - """ - Read the posterior mean value of the given fitc kernel and serve as a prior mean value for the - second stage - - Parameters - ---------- - covar_module: FITCKernel - a FITC kernel - batch_shape: torch.size - batch size - """ - super(FITCMean, self).__init__() - self.covar_module = covar_module - self.batch_shape = batch_shape - self.covar_module = covar_module - - def forward(self, input: torch.Tensor) -> torch.Tensor: - """ - Compute the posterior mean from the cached value of FITC kernels - - Parameters - ---------- - input: torch.Tensor(N_xin, D) - input torch Tensor - - Returns - ------- - res: torch.Tensor(N_xin) - posterior mean value of FITC GP model - """ - # detach is applied here to avoid updating the same parameter twice in the same iteration - # which might result in an error - res = self.covar_module.posterior_mean(input).detach() - return res diff --git a/smac/epm/gaussian_process/utils/prior.py b/smac/epm/gaussian_process/utils/prior.py deleted file mode 100644 index b271f19b4..000000000 --- a/smac/epm/gaussian_process/utils/prior.py +++ /dev/null @@ -1,545 +0,0 @@ -import math -import warnings - -import numpy as np -import scipy.stats as sps - -from smac.utils.constants import VERY_SMALL_NUMBER - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class Prior(object): - """Abstract base class to define the interface for priors of GP hyperparameter. - - This class is adapted from RoBO: - - Klein, A. and Falkner, S. and Mansur, N. and Hutter, F. - RoBO: A Flexible and Robust Bayesian Optimization Framework in Python - In: NIPS 2017 Bayesian Optimization Workshop - - [16.04.2019]: Whenever lnprob or the gradient is computed for a scalar input, we use math.* rather than np.* - - Parameters - ---------- - rng: np.random.RandomState - Random number generator - """ - - def __init__(self, rng: np.random.RandomState): - if rng is None: - raise ValueError("Argument rng must not be `None`.") - self.rng = rng - - def lnprob(self, theta: float) -> float: - """Return the log probability of theta. - - Theta must be on a log scale! This method exponentiates theta and calls ``self._lnprob``. - - Parameters - ---------- - theta : float - Hyperparameter configuration in log space. - - Returns - ------- - float - The log probability of theta - """ - return self._lnprob(np.exp(theta)) - - def _lnprob(self, theta: float) -> float: - """Return the log probability of theta. - - Theta must be on the original scale. - - Parameters - ---------- - theta : float - Hyperparameter configuration on the original scale. - - Returns - ------- - float - The log probability of theta - """ - raise NotImplementedError() - - def sample_from_prior(self, n_samples: int) -> np.ndarray: - """Returns ``n_samples`` from the prior. - - All samples are on a log scale. This method calls ``self._sample_from_prior`` and applies a log transformation - to the obtained values. - - Parameters - ---------- - n_samples : int - The number of samples that will be drawn. - - Returns - ------- - np.ndarray - """ - if np.ndim(n_samples) != 0: - raise ValueError("argument n_samples needs to be a scalar (is %s)" % n_samples) - if n_samples <= 0: - raise ValueError("argument n_samples needs to be positive (is %d)" % n_samples) - - sample = np.log(self._sample_from_prior(n_samples=n_samples)) - - if np.any(~np.isfinite(sample)): - raise ValueError("Sample %s from prior %s contains infinite values!" % (sample, self)) - - return sample - - def _sample_from_prior(self, n_samples: int) -> np.ndarray: - """Returns ``n_samples`` from the prior. - - All samples are on a original scale. - - Parameters - ---------- - n_samples : int - The number of samples that will be drawn. - - Returns - ------- - np.ndarray - """ - raise NotImplementedError() - - def gradient(self, theta: float) -> float: - """Computes the gradient of the prior with respect to theta. - - Theta must be on the original scale. - - Parameters - ---------- - theta : float - Hyperparameter configuration in log space - - Returns - ------- - float - The gradient of the prior at theta. - """ - return self._gradient(np.exp(theta)) - - def _gradient(self, theta: float) -> float: - """Computes the gradient of the prior with respect to theta. - - Parameters - ---------- - theta : float - Hyperparameter configuration in the original space space - - Returns - ------- - float - The gradient of the prior at theta. - """ - raise NotImplementedError() - - -class TophatPrior(Prior): - def __init__(self, lower_bound: float, upper_bound: float, rng: np.random.RandomState): - """Tophat prior as it used in the original spearmint code. - - This class is adapted from RoBO: - - Klein, A. and Falkner, S. and Mansur, N. and Hutter, F. - RoBO: A Flexible and Robust Bayesian Optimization Framework in Python - In: NIPS 2017 Bayesian Optimization Workshop - - Parameters - ---------- - lower_bound : float - Lower bound of the prior. In original scale. - upper_bound : float - Upper bound of the prior. In original scale. - rng: np.random.RandomState - Random number generator - """ - super().__init__(rng) - self.min = lower_bound - self._log_min = np.log(lower_bound) - self.max = upper_bound - self._log_max = np.log(upper_bound) - if not (self.max > self.min): - raise Exception("Upper bound of Tophat prior must be greater than the lower bound!") - - def _lnprob(self, theta: float) -> float: - """Return the log probability of theta. - - Parameters - ---------- - theta : float - A hyperparameter configuration - - Returns - ------- - float - """ - if theta < self.min or theta > self.max: - return -np.inf - else: - return 0 - - def _sample_from_prior(self, n_samples: int) -> np.ndarray: - """Return ``n_samples`` from the prior. - - Parameters - ---------- - n_samples : int - The number of samples that will be drawn. - - Returns - ------- - np.ndarray - """ - if np.ndim(n_samples) != 0: - raise ValueError("argument n_samples needs to be a scalar (is %s)" % n_samples) - if n_samples <= 0: - raise ValueError("argument n_samples needs to be positive (is %d)" % n_samples) - - p0 = np.exp(self.rng.uniform(low=self._log_min, high=self._log_max, size=(n_samples,))) - return p0 - - def gradient(self, theta: float) -> float: - """Computes the gradient of the prior with respect to theta. - - Parameters - ---------- - theta : float - Hyperparameter configuration in log space - - Returns - ------- - (D) np.array - - The gradient of the prior at theta. - """ - return 0 - - -class HorseshoePrior(Prior): - def __init__(self, scale: float, rng: np.random.RandomState): - """Horseshoe Prior as it is used in spearmint. - - This class is adapted from RoBO: - - Klein, A. and Falkner, S. and Mansur, N. and Hutter, F. - RoBO: A Flexible and Robust Bayesian Optimization Framework in Python - In: NIPS 2017 Bayesian Optimization Workshop - - Parameters - ---------- - scale: float - Scaling parameter. See below how it is influencing the distribution. - rng: np.random.RandomState - Random number generator - """ - super().__init__(rng) - self.scale = scale - self.scale_square = scale**2 - - def _lnprob(self, theta: float) -> float: - """Return the log probability of theta. - - Parameters - ---------- - theta : (D,) numpy array - A hyperparameter configuration - - Returns - ------- - float - """ - # We computed it exactly as in the original spearmint code, they basically say that there's no analytical form - # of the horseshoe prior, but that the multiplier is bounded between 2 and 4 and that they used the middle - # See "The horseshoe estimator for sparse signals" by Carvalho, Poloson and Scott (2010), Equation 1. - # https://www.jstor.org/stable/25734098 - # Compared to the paper by Carvalho, there's a constant multiplicator missing - # Compared to Spearmint we first have to undo the log space transformation of the theta - # Note: "undo log space transformation" is done in parent class - if theta == 0: - return np.inf # POSITIVE infinity (this is the "spike") - else: - a = math.log(1 + 3.0 * (self.scale_square / theta**2)) - return math.log(a + VERY_SMALL_NUMBER) - - def _sample_from_prior(self, n_samples: int) -> np.ndarray: - """Returns N samples from the prior. - - Parameters - ---------- - n_samples : int - The number of samples that will be drawn. - - Returns - ------- - np.ndarray - """ - # This is copied from RoBO - scale is most likely the tau parameter - lamda = np.abs(self.rng.standard_cauchy(size=n_samples)) - p0 = np.abs(self.rng.randn() * lamda * self.scale) - return p0 - - def _gradient(self, theta: float) -> float: - """Computes the gradient of the prior with respect to theta. - - Parameters - ---------- - theta : (D,) numpy array - Hyperparameter configuration - - Returns - ------- - (D) np.array - The gradient of the prior at theta. - """ - if theta == 0: - return np.inf # POSITIVE infinity (this is the "spike") - else: - a = -(6 * self.scale_square) - b = 3 * self.scale_square + theta**2 - b *= math.log(3 * self.scale_square * theta ** (-2) + 1) - b = max(b, 1e-14) - return a / b - - -class LognormalPrior(Prior): - def __init__( - self, - sigma: float, - rng: np.random.RandomState, - mean: float = 0, - ): - """Log normal prior. - - This class is adapted from RoBO: - - Klein, A. and Falkner, S. and Mansur, N. and Hutter, F. - RoBO: A Flexible and Robust Bayesian Optimization Framework in Python - In: NIPS 2017 Bayesian Optimization Workshop - - Parameters - ---------- - sigma: float - Specifies the standard deviation of the normal - distribution. - rng: np.random.RandomState - Random number generator - mean: float - Specifies the mean of the normal distribution - """ - super().__init__(rng) - - if mean != 0: - raise NotImplementedError(mean) - - self.sigma = sigma - self.sigma_square = sigma**2 - self.mean = mean - self.sqrt_2_pi = np.sqrt(2 * np.pi) - - def _lnprob(self, theta: float) -> float: - """Return the log probability of theta. - - Parameters - ---------- - theta : float - A hyperparameter configuration - - Returns - ------- - float - """ - if theta <= self.mean: - return -1e25 - else: - rval = -((math.log(theta) - self.mean) ** 2) / (2 * self.sigma_square) - math.log( - self.sqrt_2_pi * self.sigma * theta - ) - return rval - - def _sample_from_prior(self, n_samples: int) -> np.ndarray: - """Returns N samples from the prior. - - Parameters - ---------- - n_samples : int - The number of samples that will be drawn. - - Returns - ------- - np.ndarray - """ - return self.rng.lognormal(mean=self.mean, sigma=self.sigma, size=n_samples) - - def _gradient(self, theta: float) -> float: - """Computes the gradient of the prior with respect to theta. - - Parameters - ---------- - theta : (D,) numpy array - Hyperparameter configuration in log space - - Returns - ------- - (D) np.array - The gradient of the prior at theta. - """ - if theta <= 0: - return 0 - else: - # derivative of log(1 / (x * s^2 * sqrt(2 pi)) * exp( - 0.5 * (log(x ) / s^2))^2)) - # This is without the mean!!! - return -(self.sigma_square + math.log(theta)) / (self.sigma_square * (theta)) * theta - - -class SoftTopHatPrior(Prior): - def __init__(self, lower_bound: float, upper_bound: float, exponent: float, rng: np.random.RandomState) -> None: - super().__init__(rng) - - with warnings.catch_warnings(): - warnings.simplefilter("error") - self.lower_bound = lower_bound - try: - self._log_lower_bound = np.log(lower_bound) - except RuntimeWarning as w: - if "invalid value encountered in log" in w.args[0]: - raise ValueError("Invalid lower bound %f (cannot compute log)" % lower_bound) - - raise w - self.upper_bound = upper_bound - try: - self._log_upper_bound = np.log(upper_bound) - except RuntimeWarning as w: - if "invalid value encountered in log" in w.args[0]: - raise ValueError("Invalid lower bound %f (cannot compute log)" % lower_bound) - - raise w - - if exponent <= 0: - raise ValueError("Exponent cannot be less or equal than zero (but is %f)" % exponent) - self.exponent = exponent - - def lnprob(self, theta: float) -> float: - """Return the log probability of theta.""" - # We need to use lnprob here instead of _lnprob to have the squared function work - # in the logarithmic space, too. - if np.ndim(theta) == 0: - if theta < self._log_lower_bound: - return -((theta - self._log_lower_bound) ** self.exponent) - elif theta > self._log_upper_bound: - return -((self._log_upper_bound - theta) ** self.exponent) - else: - return 0 - else: - raise NotImplementedError() - - def _sample_from_prior(self, n_samples: int) -> np.ndarray: - """Returns N samples from the prior. - - Parameters - ---------- - n_samples : int - The number of samples that will be drawn. - - Returns - ------- - np.ndarray - """ - return np.exp(self.rng.uniform(self._log_lower_bound, self._log_upper_bound, size=(n_samples,))) - - def gradient(self, theta: float) -> float: - """Returns the gradient of the prior at theta.""" - if np.ndim(theta) == 0: - if theta < self._log_lower_bound: - return -self.exponent * (theta - self._log_lower_bound) - elif theta > self._log_upper_bound: - return self.exponent * (self._log_upper_bound - theta) - else: - return 0 - else: - raise NotImplementedError() - - def __repr__(self) -> str: - return "SoftTopHatPrior(lower_bound=%f, upper_bound=%f)" % ( - self.lower_bound, - self.upper_bound, - ) - - -class GammaPrior(Prior): - def __init__(self, a: float, scale: float, loc: float, rng: np.random.RandomState): - """Gamma prior. - - f(x) = (x-loc)**(a-1) * e**(-(x-loc)) * (1/scale)**a / gamma(a) - - Parameters - ---------- - a: float > 0 - shape parameter - scale: float > 0 - scale parameter (1/scale corresponds to parameter p in canonical form) - loc: float - mean parameter for the distribution - rng: np.random.RandomState - Random number generator - """ - super().__init__(rng) - - self.a = a - self.loc = loc - self.scale = scale - - def _lnprob(self, theta: float) -> float: - """Returns the logpdf of theta. - - Parameters - ---------- - theta : float - Hyperparameter configuration - - Returns - ------- - float - """ - if np.ndim(theta) != 0: - raise NotImplementedError() - return sps.gamma.logpdf(theta, a=self.a, scale=self.scale, loc=self.loc) - - def _sample_from_prior(self, n_samples: int) -> np.ndarray: - """Returns N samples from the prior. - - Parameters - ---------- - n_samples : int - The number of samples that will be drawn. - - Returns - ------- - np.ndarray - """ - return self.rng.gamma(shape=self.a, scale=self.scale, size=n_samples) - - def _gradient(self, theta: float) -> float: - """As computed by Wolfram Alpha. - - Parameters - ---------- - theta: float - A hyperparameter configuration - - Returns - ------- - float - """ - if np.ndim(theta) == 0: - # Multiply by theta because of the chain rule... - return ((self.a - 1) / theta - (1 / self.scale)) * theta - else: - raise NotImplementedError() diff --git a/smac/epm/multi_objective_epm.py b/smac/epm/multi_objective_epm.py deleted file mode 100644 index f294854ae..000000000 --- a/smac/epm/multi_objective_epm.py +++ /dev/null @@ -1,183 +0,0 @@ -from abc import abstractmethod -from typing import Any, Dict, List, Optional, Tuple - -import numpy as np - -from smac.configspace import ConfigurationSpace -from smac.epm.base_epm import BaseEPM - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class MultiObjectiveEPM(BaseEPM): - """Wrapper for the surrogate models to predict multiple targets. - - Only a list with the target names and the types array for the - underlying model are mandatory. All other hyperparameters to - model can be passed via kwargs. Consult the documentation of - the corresponding model for the hyperparameters and their meanings. - - Parameters - ---------- - target_names : list - List of str, each entry is the name of one target dimension. Length - of the list will be ``n_objectives``. - types : List[int] - Specifies the number of categorical values of an input dimension where - the i-th entry corresponds to the i-th input dimension. Let's say we - have 2 dimension where the first dimension consists of 3 different - categorical choices and the second dimension is continuous than we - have to pass [3, 0]. Note that we count starting from 0. - bounds : List[Tuple[float, float]] - bounds of input dimensions: (lower, uppper) for continuous dims; (n_cat, np.nan) - for categorical dims - instance_features : np.ndarray (I, K) - Contains the K dimensional instance features of I different instances - pca_components : float - Number of components to keep when using PCA to reduce dimensionality of instance features. - Requires to set n_feats (> pca_dims). - model_kwargs: Optional[Dict[str, Any]]: - arguments for initialing estimators - - Attributes - ---------- - target_names: - target names - num_targets: int - number of targets - estimators: List[BaseEPM] - a list of estimators predicting different target values - """ - - def __init__( - self, - target_names: List[str], - configspace: ConfigurationSpace, - types: List[int], - bounds: List[Tuple[float, float]], - seed: int, - instance_features: Optional[np.ndarray] = None, - pca_components: Optional[int] = None, - model_kwargs: Optional[Dict[str, Any]] = None, - ) -> None: - super().__init__( - configspace=configspace, - bounds=bounds, - types=types, - seed=seed, - instance_features=instance_features, - pca_components=pca_components, - ) - if model_kwargs is None: - model_kwargs = {} - self.target_names = target_names - self.num_targets = len(self.target_names) - self.estimators: List[BaseEPM] = self.construct_estimators(configspace, types, bounds, model_kwargs) - - @abstractmethod - def construct_estimators( - self, - configspace: ConfigurationSpace, - types: List[int], - bounds: List[Tuple[float, float]], - model_kwargs: Dict[str, Any], - ) -> List[BaseEPM]: - """ - Construct a list of estimators. The number of the estimators equals 'self.num_targets' - Parameters - ---------- - configspace : ConfigurationSpace - Configuration space to tune for. - types : List[int] - Specifies the number of categorical values of an input dimension where - the i-th entry corresponds to the i-th input dimension. Let's say we - have 2 dimension where the first dimension consists of 3 different - categorical choices and the second dimension is continuous than we - have to pass [3, 0]. Note that we count starting from 0. - bounds : List[Tuple[float, float]] - bounds of input dimensions: (lower, uppper) for continuous dims; (n_cat, np.nan) for categorical dims - model_kwargs : Dict[str, Any] - model kwargs for initializing models - Returns - ------- - estimators: List[BaseEPM] - A list of estimators - """ - raise NotImplementedError - - def _train(self, X: np.ndarray, Y: np.ndarray) -> "MultiObjectiveEPM": - """Trains the models on X and y. - - Parameters - ---------- - X : np.ndarray [n_samples, n_features (config + instance features)] - Input data points. - Y : np.ndarray [n_samples, n_objectives] - The corresponding target values. n_objectives must match the - number of target names specified in the constructor. - - Returns - ------- - self - """ - if len(self.estimators) == 0: - raise ValueError("The list of estimators for this model is empty!") - for i, estimator in enumerate(self.estimators): - estimator.train(X, Y[:, i]) - - return self - - def _predict(self, X: np.ndarray, cov_return_type: Optional[str] = "diagonal_cov") -> Tuple[np.ndarray, np.ndarray]: - """Predict means and variances for given X. - - Parameters - ---------- - X : np.ndarray of shape = [n_samples, n_features (config + instance - features)] - cov_return_type: Optional[str] - Specifies what to return along with the mean. Refer ``predict()`` for more information. - - Returns - ------- - means : np.ndarray of shape = [n_samples, n_objectives] - Predictive mean - vars : np.ndarray of shape = [n_samples, n_objectives] - Predictive variance - """ - if cov_return_type != "diagonal_cov": - raise ValueError("'cov_return_type' can only take 'diagonal_cov' for this model") - - mean = np.zeros((X.shape[0], self.num_targets)) - var = np.zeros((X.shape[0], self.num_targets)) - for i, estimator in enumerate(self.estimators): - m, v = estimator.predict(X) - assert v is not None # please mypy - mean[:, i] = m.flatten() - var[:, i] = v.flatten() - return mean, var - - def predict_marginalized_over_instances(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: - """Predict mean and variance marginalized over all instances. - - Returns the predictive mean and variance marginalised over all - instances for a set of configurations. - - Parameters - ---------- - X : np.ndarray of shape = [n_features (config), ] - - Returns - ------- - means : np.ndarray of shape = [n_samples, n_objectives] - Predictive mean - vars : np.ndarray of shape = [n_samples, n_objectives] - Predictive variance - """ - mean = np.zeros((X.shape[0], self.num_targets)) - var = np.zeros((X.shape[0], self.num_targets)) - for i, estimator in enumerate(self.estimators): - m, v = estimator.predict_marginalized_over_instances(X) - mean[:, i] = m.flatten() - var[:, i] = v.flatten() - return mean, var diff --git a/smac/epm/random_epm.py b/smac/epm/random_epm.py deleted file mode 100644 index d93e8c365..000000000 --- a/smac/epm/random_epm.py +++ /dev/null @@ -1,101 +0,0 @@ -from typing import List, Optional, Tuple - -import numpy as np - -from smac.configspace import ConfigurationSpace -from smac.epm.base_epm import BaseEPM - -__author__ = "Katharina Eggensperger" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Katharina Eggensperger" -__email__ = "eggenspk@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -class RandomEPM(BaseEPM): - """EPM which returns random values on a call to ``fit``. - - Parameters - ---------- - configspace : ConfigurationSpace - Configuration space to tune for. - types : List[int] - Specifies the number of categorical values of an input dimension where - the i-th entry corresponds to the i-th input dimension. Let's say we - have 2 dimension where the first dimension consists of 3 different - categorical choices and the second dimension is continuous than we - have to pass [3, 0]. Note that we count starting from 0. - bounds : List[Tuple[float, float]] - bounds of input dimensions: (lower, uppper) for continuous dims; (n_cat, np.nan) for categorical dims - seed : int - The seed that is passed to the model library. - instance_features : np.ndarray (I, K), optional - Contains the K dimensional instance features - of the I different instances - pca_components : float - Number of components to keep when using PCA to reduce - dimensionality of instance features. Requires to - set n_feats (> pca_dims). - """ - - def __init__( - self, - configspace: ConfigurationSpace, - types: List[int], - bounds: List[Tuple[float, float]], - seed: int, - instance_features: Optional[np.ndarray] = None, - pca_components: Optional[int] = None, - ) -> None: - super().__init__( - configspace=configspace, - types=types, - bounds=bounds, - seed=seed, - instance_features=instance_features, - pca_components=pca_components, - ) - self.rng = np.random.RandomState(self.seed) - - def _train(self, X: np.ndarray, Y: np.ndarray) -> "RandomEPM": - """Pseudo training on X and Y. - - Parameters - ---------- - X : np.ndarray (N, D) - Input data points. The dimensionality of X is (N, D), - with N as the number of points and D is the number of features. - Y : np.ndarray (N, 1) - The corresponding target values. - """ - if not isinstance(X, np.ndarray): - raise NotImplementedError("X has to be of type np.ndarray") - if not isinstance(Y, np.ndarray): - raise NotImplementedError("Y has to be of type np.ndarray") - - self.logger.debug("(Pseudo) Fit model to data") - return self - - def _predict(self, X: np.ndarray, cov_return_type: Optional[str] = "diagonal_cov") -> Tuple[np.ndarray, np.ndarray]: - """Predict means and variances for given X. - - Parameters - ---------- - X : np.ndarray of shape = [n_samples, n_features (config + instance features)] - cov_return_type: Optional[str] - Specifies what to return along with the mean. Refer ``predict()`` for more information. - - Returns - ------- - means : np.ndarray of shape = [n_samples, n_objectives] - Predictive mean - vars : np.ndarray of shape = [n_samples, n_objectives] - Predictive variance - """ - if cov_return_type != "diagonal_cov": - raise ValueError("'cov_return_type' can only take 'diagonal_cov' for this model") - - if not isinstance(X, np.ndarray): - raise NotImplementedError("X has to be of type np.ndarray") - return self.rng.rand(len(X), 1), self.rng.rand(len(X), 1) diff --git a/smac/epm/random_forest/__init__.py b/smac/epm/random_forest/__init__.py deleted file mode 100644 index f106104ff..000000000 --- a/smac/epm/random_forest/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -from typing import Dict, List, Optional, Tuple - -import numpy as np - -from smac.configspace import ( - CategoricalHyperparameter, - ConfigurationSpace, - Constant, - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) -from smac.epm.base_epm import BaseEPM - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class BaseModel(BaseEPM): - def __init__( - self, - configspace: ConfigurationSpace, - types: List[int], - bounds: List[Tuple[float, float]], - seed: int, - instance_features: Optional[np.ndarray] = None, - pca_components: Optional[int] = None, - ) -> None: - """Abstract base class for all random forest models.""" - super().__init__( - configspace=configspace, - types=types, - bounds=bounds, - seed=seed, - instance_features=instance_features, - pca_components=pca_components, - ) - - self.rng = np.random.RandomState(seed) - self.conditional = dict() # type: Dict[int, bool] - self.impute_values = dict() # type: Dict[int, float] - - def _impute_inactive(self, X: np.ndarray) -> np.ndarray: - X = X.copy() - for idx, hp in enumerate(self.configspace.get_hyperparameters()): - if idx not in self.conditional: - parents = self.configspace.get_parents_of(hp.name) - if len(parents) == 0: - self.conditional[idx] = False - else: - self.conditional[idx] = True - if isinstance(hp, CategoricalHyperparameter): - self.impute_values[idx] = len(hp.choices) - elif isinstance(hp, (UniformFloatHyperparameter, UniformIntegerHyperparameter)): - self.impute_values[idx] = -1 - elif isinstance(hp, Constant): - self.impute_values[idx] = 1 - else: - raise ValueError - - if self.conditional[idx] is True: - nonfinite_mask = ~np.isfinite(X[:, idx]) - X[nonfinite_mask, idx] = self.impute_values[idx] - - return X - - -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances # noqa - -__all__ = [ - "BaseModel", - "RandomForestWithInstances", -] diff --git a/smac/epm/random_forest/rf_mo.py b/smac/epm/random_forest/rf_mo.py deleted file mode 100644 index e7cf36f90..000000000 --- a/smac/epm/random_forest/rf_mo.py +++ /dev/null @@ -1,49 +0,0 @@ -from typing import Any, Dict, List, Tuple - -from smac.configspace import ConfigurationSpace -from smac.epm.base_epm import BaseEPM -from smac.epm.multi_objective_epm import MultiObjectiveEPM -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class MultiObjectiveRandomForest(MultiObjectiveEPM): - """Wrapper for the random forest to predict multiple targets. - - Only a list with the target names and the types array for the - underlying forest model are mandatory. All other hyperparameters to - the random forest can be passed via kwargs. Consult the documentation of - the random forest for the hyperparameters and their meanings. - """ - - def construct_estimators( - self, - configspace: ConfigurationSpace, - types: List[int], - bounds: List[Tuple[float, float]], - model_kwargs: Dict[str, Any], - ) -> List[BaseEPM]: - """ - Construct a list of estimators. The number of the estimators equals 'self.num_targets' - Parameters - ---------- - configspace : ConfigurationSpace - Configuration space to tune for. - types : List[int] - Specifies the number of categorical values of an input dimension where - the i-th entry corresponds to the i-th input dimension. Let's say we - have 2 dimension where the first dimension consists of 3 different - categorical choices and the second dimension is continuous than we - have to pass [3, 0]. Note that we count starting from 0. - bounds : List[Tuple[float, float]] - bounds of input dimensions: (lower, uppper) for continuous dims; (n_cat, np.nan) for categorical dims - model_kwargs : Dict[str, Any] - model kwargs for initializing models - Returns - ------- - estimators: List[BaseEPM] - A list of Random Forests - """ - return [RandomForestWithInstances(configspace, types, bounds, **model_kwargs) for _ in range(self.num_targets)] diff --git a/smac/epm/random_forest/rf_with_instances.py b/smac/epm/random_forest/rf_with_instances.py deleted file mode 100644 index 92647ad09..000000000 --- a/smac/epm/random_forest/rf_with_instances.py +++ /dev/null @@ -1,333 +0,0 @@ -from typing import List, Optional, Tuple - -import numpy as np -from pyrfr import regression - -from smac.configspace import ConfigurationSpace -from smac.epm.random_forest import BaseModel -from smac.utils.constants import N_TREES, VERY_SMALL_NUMBER - -__author__ = "Aaron Klein" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Aaron Klein" -__email__ = "kleinaa@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -class RandomForestWithInstances(BaseModel): - """Random forest that takes instance features into account. - - Parameters - ---------- - types : List[int] - Specifies the number of categorical values of an input dimension where - the i-th entry corresponds to the i-th input dimension. Let's say we - have 2 dimension where the first dimension consists of 3 different - categorical choices and the second dimension is continuous than we - have to pass [3, 0]. Note that we count starting from 0. - bounds : List[Tuple[float, float]] - bounds of input dimensions: (lower, uppper) for continuous dims; (n_cat, np.nan) for categorical dims - seed : int - The seed that is passed to the random_forest_run library. - log_y: bool - y values (passed to this RF) are expected to be log(y) transformed; - this will be considered during predicting - num_trees : int - The number of trees in the random forest. - do_bootstrapping : bool - Turns on / off bootstrapping in the random forest. - n_points_per_tree : int - Number of points per tree. If <= 0 X.shape[0] will be used - in _train(X, y) instead - ratio_features : float - The ratio of features that are considered for splitting. - min_samples_split : int - The minimum number of data points to perform a split. - min_samples_leaf : int - The minimum number of data points in a leaf. - max_depth : int - The maximum depth of a single tree. - eps_purity : float - The minimum difference between two target values to be considered - different - max_num_nodes : int - The maxmimum total number of nodes in a tree - instance_features : np.ndarray (I, K) - Contains the K dimensional instance features of the I different instances - pca_components : float - Number of components to keep when using PCA to reduce dimensionality of instance features. Requires to - set n_feats (> pca_dims). - - Attributes - ---------- - rf_opts : regression.rf_opts - Random forest hyperparameter - n_points_per_tree : int - rf : regression.binary_rss_forest - Only available after training - hypers: list - List of random forest hyperparameters - unlog_y: bool - seed : int - types : np.ndarray - bounds : list - rng : np.random.RandomState - logger : logging.logger - """ - - def __init__( - self, - configspace: ConfigurationSpace, - types: List[int], - bounds: List[Tuple[float, float]], - seed: int, - log_y: bool = False, - num_trees: int = N_TREES, - do_bootstrapping: bool = True, - n_points_per_tree: int = -1, - ratio_features: float = 5.0 / 6.0, - min_samples_split: int = 3, - min_samples_leaf: int = 3, - max_depth: int = 2**20, - eps_purity: float = 1e-8, - max_num_nodes: int = 2**20, - instance_features: Optional[np.ndarray] = None, - pca_components: Optional[int] = None, - ) -> None: - super().__init__( - configspace=configspace, - types=types, - bounds=bounds, - seed=seed, - instance_features=instance_features, - pca_components=pca_components, - ) - - self.log_y = log_y - self.rng = regression.default_random_engine(seed) - - self.rf_opts = regression.forest_opts() - self.rf_opts.num_trees = num_trees - self.rf_opts.do_bootstrapping = do_bootstrapping - max_features = 0 if ratio_features > 1.0 else max(1, int(len(types) * ratio_features)) - self.rf_opts.tree_opts.max_features = max_features - self.rf_opts.tree_opts.min_samples_to_split = min_samples_split - self.rf_opts.tree_opts.min_samples_in_leaf = min_samples_leaf - self.rf_opts.tree_opts.max_depth = max_depth - self.rf_opts.tree_opts.epsilon_purity = eps_purity - self.rf_opts.tree_opts.max_num_nodes = max_num_nodes - self.rf_opts.compute_law_of_total_variance = False - - self.n_points_per_tree = n_points_per_tree - self.rf = None # type: regression.binary_rss_forest - - # This list well be read out by save_iteration() in the solver - self.hypers = [ - num_trees, - max_num_nodes, - do_bootstrapping, - n_points_per_tree, - ratio_features, - min_samples_split, - min_samples_leaf, - max_depth, - eps_purity, - self.seed, - ] - - def _train(self, X: np.ndarray, y: np.ndarray) -> "RandomForestWithInstances": - """Trains the random forest on X and y. - - Parameters - ---------- - X : np.ndarray [n_samples, n_features (config + instance features)] - Input data points. - y : np.ndarray [n_samples, ] - The corresponding target values. - - Returns - ------- - self - """ - X = self._impute_inactive(X) - self.X = X - self.y = y.flatten() - - if self.n_points_per_tree <= 0: - self.rf_opts.num_data_points_per_tree = self.X.shape[0] - else: - self.rf_opts.num_data_points_per_tree = self.n_points_per_tree - self.rf = regression.binary_rss_forest() - self.rf.options = self.rf_opts - data = self._init_data_container(self.X, self.y) - self.rf.fit(data, rng=self.rng) - return self - - def _init_data_container(self, X: np.ndarray, y: np.ndarray) -> regression.default_data_container: - """Fills a pyrfr default data container, s.t. the forest knows categoricals and bounds for - continous data. - - Parameters - ---------- - X : np.ndarray [n_samples, n_features] - Input data points - y : np.ndarray [n_samples, ] - Corresponding target values - - Returns - ------- - data : regression.default_data_container - The filled data container that pyrfr can interpret - """ - # retrieve the types and the bounds from the ConfigSpace - data = regression.default_data_container(X.shape[1]) - - for i, (mn, mx) in enumerate(self.bounds): - if np.isnan(mx): - data.set_type_of_feature(i, mn) - else: - data.set_bounds_of_feature(i, mn, mx) - - for row_X, row_y in zip(X, y): - data.add_data_point(row_X, row_y) - return data - - def _predict(self, X: np.ndarray, cov_return_type: Optional[str] = "diagonal_cov") -> Tuple[np.ndarray, np.ndarray]: - """Predict means and variances for given X. - - Parameters - ---------- - X : np.ndarray of shape = [n_samples, - n_features (config + instance features)] - cov_return_type: Optional[str] - Specifies what to return along with the mean. Refer ``predict()`` for more information. - - Returns - ------- - means : np.ndarray of shape = [n_samples, 1] - Predictive mean - vars : np.ndarray of shape = [n_samples, 1] - Predictive variance - """ - if len(X.shape) != 2: - raise ValueError("Expected 2d array, got %dd array!" % len(X.shape)) - if X.shape[1] != len(self.types): - raise ValueError("Rows in X should have %d entries but have %d!" % (len(self.types), X.shape[1])) - if cov_return_type != "diagonal_cov": - raise ValueError("'cov_return_type' can only take 'diagonal_cov' for this model") - - X = self._impute_inactive(X) - - if self.log_y: - all_preds = [] - third_dimension = 0 - - # Gather data in a list of 2d arrays and get statistics about the required size of the 3d array - for row_X in X: - preds_per_tree = self.rf.all_leaf_values(row_X) - all_preds.append(preds_per_tree) - max_num_leaf_data = max(map(len, preds_per_tree)) - third_dimension = max(max_num_leaf_data, third_dimension) - - # Transform list of 2d arrays into a 3d array - preds_as_array = np.zeros((X.shape[0], self.rf_opts.num_trees, third_dimension)) * np.NaN - for i, preds_per_tree in enumerate(all_preds): - for j, pred in enumerate(preds_per_tree): - preds_as_array[i, j, : len(pred)] = pred - - # Do all necessary computation with vectorized functions - preds_as_array = np.log(np.nanmean(np.exp(preds_as_array), axis=2) + VERY_SMALL_NUMBER) - - # Compute the mean and the variance across the different trees - means = preds_as_array.mean(axis=1) - vars_ = preds_as_array.var(axis=1) - else: - means, vars_ = [], [] - for row_X in X: - mean_, var = self.rf.predict_mean_var(row_X) - means.append(mean_) - vars_.append(var) - - means = np.array(means) - vars_ = np.array(vars_) - - return means.reshape((-1, 1)), vars_.reshape((-1, 1)) - - def predict_marginalized_over_instances(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: - """Predict mean and variance marginalized over all instances. - - Returns the predictive mean and variance marginalised over all - instances for a set of configurations. - - Note - ---- - This method overwrites the same method of ~smac.epm.base_epm.AbstractEPM; - the following method is random forest specific - and follows the SMAC2 implementation; - it requires no distribution assumption - to marginalize the uncertainty estimates - - Parameters - ---------- - X : np.ndarray - [n_samples, n_features (config)] - - Returns - ------- - means : np.ndarray of shape = [n_samples, 1] - Predictive mean - vars : np.ndarray of shape = [n_samples, 1] - Predictive variance - """ - if self.instance_features is None or len(self.instance_features) == 0: - mean_, var = self.predict(X) - assert var is not None # please mypy - - var[var < self.var_threshold] = self.var_threshold - var[np.isnan(var)] = self.var_threshold - return mean_, var - - if len(X.shape) != 2: - raise ValueError("Expected 2d array, got %dd array!" % len(X.shape)) - if X.shape[1] != len(self.bounds): - raise ValueError("Rows in X should have %d entries but have %d!" % (len(self.bounds), X.shape[1])) - - X = self._impute_inactive(X) - - dat_ = np.zeros((X.shape[0], self.rf_opts.num_trees)) # marginalized predictions for each tree - for i, x in enumerate(X): - - # marginalize over instances - # 1. get all leaf values for each tree - preds_trees = [[] for i in range(self.rf_opts.num_trees)] # type: List[List[float]] - - for feat in self.instance_features: - x_ = np.concatenate([x, feat]) - preds_per_tree = self.rf.all_leaf_values(x_) - for tree_id, preds in enumerate(preds_per_tree): - preds_trees[tree_id] += preds - - # 2. average in each tree - if self.log_y: - for tree_id in range(self.rf_opts.num_trees): - dat_[i, tree_id] = np.log(np.exp(np.array(preds_trees[tree_id])).mean()) - else: - for tree_id in range(self.rf_opts.num_trees): - dat_[i, tree_id] = np.array(preds_trees[tree_id]).mean() - - # 3. compute statistics across trees - mean_ = dat_.mean(axis=1) - var = dat_.var(axis=1) - - if var is None: - raise RuntimeError("The variance must not be none.") - - var[var < self.var_threshold] = self.var_threshold - - if len(mean_.shape) == 1: - mean_ = mean_.reshape((-1, 1)) - if len(var.shape) == 1: - var = var.reshape((-1, 1)) - - return mean_, var diff --git a/smac/epm/random_forest/rfr_imputator.py b/smac/epm/random_forest/rfr_imputator.py deleted file mode 100644 index 0fe4d1282..000000000 --- a/smac/epm/random_forest/rfr_imputator.py +++ /dev/null @@ -1,192 +0,0 @@ -from typing import Optional - -import logging -import warnings - -import numpy as np -from scipy.stats import truncnorm - -import smac.epm.base_imputor -from smac.epm.base_epm import BaseEPM - -__author__ = "Katharina Eggensperger" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Katharina Eggensperger" -__email__ = "eggenspk@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -class RFRImputator(smac.epm.base_imputor.BaseImputor): - """Imputor using pyrfr's Random Forest regressor. - - **Note:** Sets var_threshold as the lower bound on the variance for the - predictions of the random forest - - - Parameters - ---------- - rng : np.random.RandomState - Will be used to draw a seed (currently not used) - cutoff : float - Cutoff value for this scenario (upper runnning time limit) - threshold : float - Highest possible values (e.g. cutoff * parX). - model : BaseEPM - Predictive model (i.e. RandomForestWithInstances) - change_threshold : float - Stop imputation if change is less than this. - max_iter : int - Maximum number of imputation iterations. - - - Attributes - ---------- - logger : logging.Logger - max_iter : int - change_threshold : float - cutoff : float - threshold : float - seed : int - Created by drawing random int from rng - model : BaseEPM - Predictive model (i.e. RandomForestWithInstances) - var_threshold: float - """ - - def __init__( - self, - rng: np.random.RandomState, - cutoff: float, - threshold: float, - model: BaseEPM, - change_threshold: float = 0.01, - max_iter: int = 2, - ): - super(RFRImputator, self).__init__() - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - self.max_iter = max_iter - self.change_threshold = change_threshold - self.cutoff = cutoff - self.threshold = threshold - self.seed = rng.randint(low=0, high=1000) - - self.model = model - - # Never use a lower variance than this - self.var_threshold = 10**-2 - - def impute( - self, - censored_X: np.ndarray, - censored_y: np.ndarray, - uncensored_X: np.ndarray, - uncensored_y: np.ndarray, - ) -> Optional[np.ndarray]: - """Imputes censored runs and returns new y values. - - Parameters - ---------- - censored_X : np.ndarray [N, M] - Feature array of all censored runs. - censored_y : np.ndarray [N, 1] - Target values for all runs censored runs. - uncensored_X : np.ndarray [N, M] - Feature array of all non-censored runs. - uncensored_y : np.ndarray [N, 1] - Target values for all non-censored runs. - - Returns - ------- - imputed_y : np.ndarray - Same shape as censored_y [N, 1] - """ - if censored_X.shape[0] == 0: - self.logger.critical("Nothing to impute, return None") - return None - - censored_y = censored_y.flatten() - uncensored_y = uncensored_y.flatten() - - # first learn model without censored data - self.model.train(uncensored_X, uncensored_y) - - self.logger.debug("Going to impute %d y-values with %s" % (censored_X.shape[0], str(self.model))) - - imputed_y = None # define this, if imputation fails - - # Define variables - y = np.empty((0,)) # This only defines the type, the actual value will not be used later on. - - it = 1 - change = 0 - - while True: - self.logger.debug("Iteration %d of %d" % (it, self.max_iter)) - - # predict censored y values - y_mean, y_var = self.model.predict(censored_X) - assert y_var is not None # please mypy - - y_var[y_var < self.var_threshold] = self.var_threshold - y_stdev = np.sqrt(y_var)[:, 0] - y_mean = y_mean[:, 0] - - # ignore the warnings of truncnorm.stats - # since we handle them appropriately - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", r"invalid value encountered in (subtract|true_divide|power).*") - warnings.filterwarnings("ignore", r"divide by zero encountered in (true_divide|log).*") - imputed_y = truncnorm.stats( - a=(censored_y - y_mean) / y_stdev, - b=(self.threshold - y_mean) / y_stdev, - loc=y_mean, - scale=y_stdev, - moments="m", - ) - - imputed_y = np.array(imputed_y) - - nans = ~np.isfinite(imputed_y) - n_nans = sum(nans) - if n_nans > 0: - # Replace all nans with maximum of predicted perf and censored value - # this happens if the prediction is far smaller than the - # censored data point - self.logger.debug("Going to replace %d nan-value(s) with " "max(captime, predicted mean)" % n_nans) - imputed_y[nans] = np.max([censored_y[nans], y_mean[nans]], axis=0) - - if it > 1: - # Calc mean difference between imputed values this and last - # iteration, assume imputed values are always concatenated - # after uncensored values - - change = np.mean(np.abs(imputed_y - y[uncensored_y.shape[0] :]) / y[uncensored_y.shape[0] :]) - - # lower all values that are higher than threshold - # should probably never happen - imputed_y[imputed_y >= self.threshold] = self.threshold - - self.logger.debug("Change: %f" % change) - - X = np.concatenate((uncensored_X, censored_X)) - y = np.concatenate((uncensored_y, imputed_y)) - - if change > self.change_threshold or it == 1: - self.model.train(X, y) - else: - break - - it += 1 - if it > self.max_iter: - break - - self.logger.debug("Imputation used %d/%d iterations, last_change=%f" % (it - 1, self.max_iter, change)) - - # replace all y > cutoff with PAR10 values (i.e., threshold) - imputed_y = np.array(imputed_y, dtype=float) - imputed_y[imputed_y >= self.cutoff] = self.threshold - - if not np.isfinite(imputed_y).all(): - self.logger.critical("Imputed values are not finite, %s" % str(imputed_y)) - return np.reshape(imputed_y, [imputed_y.shape[0], 1]) diff --git a/smac/epm/utils.py b/smac/epm/utils.py deleted file mode 100644 index 1f2aa8dca..000000000 --- a/smac/epm/utils.py +++ /dev/null @@ -1,255 +0,0 @@ -import typing - -import logging - -import numpy as np -from ConfigSpace import ConfigurationSpace -from ConfigSpace.hyperparameters import ( - BetaFloatHyperparameter, - BetaIntegerHyperparameter, - CategoricalHyperparameter, - Constant, - NormalFloatHyperparameter, - NormalIntegerHyperparameter, - OrdinalHyperparameter, - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) - -from smac.utils.constants import MAXINT - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def get_types( - config_space: ConfigurationSpace, - instance_features: typing.Optional[np.ndarray] = None, -) -> typing.Tuple[typing.List[int], typing.List[typing.Tuple[float, float]]]: - """Return the types of the hyperparameters and the bounds of the - hyperparameters and instance features. - """ - # Extract types vector for rf from config space and the bounds - types = [0] * len(config_space.get_hyperparameters()) - bounds = [(np.nan, np.nan)] * len(types) - - for i, param in enumerate(config_space.get_hyperparameters()): - parents = config_space.get_parents_of(param.name) - if len(parents) == 0: - can_be_inactive = False - else: - can_be_inactive = True - - if isinstance(param, (CategoricalHyperparameter)): - n_cats = len(param.choices) - if can_be_inactive: - n_cats = len(param.choices) + 1 - types[i] = n_cats - bounds[i] = (int(n_cats), np.nan) - elif isinstance(param, (OrdinalHyperparameter)): - n_cats = len(param.sequence) - types[i] = 0 - if can_be_inactive: - bounds[i] = (0, int(n_cats)) - else: - bounds[i] = (0, int(n_cats) - 1) - elif isinstance(param, Constant): - # for constants we simply set types to 0 which makes it a numerical - # parameter - if can_be_inactive: - bounds[i] = (2, np.nan) - types[i] = 2 - else: - bounds[i] = (0, np.nan) - types[i] = 0 - # and we leave the bounds to be 0 for now - elif isinstance(param, UniformFloatHyperparameter): - # Are sampled on the unit hypercube thus the bounds - # are always 0.0, 1.0 - if can_be_inactive: - bounds[i] = (-1.0, 1.0) - else: - bounds[i] = (0, 1.0) - elif isinstance(param, UniformIntegerHyperparameter): - if can_be_inactive: - bounds[i] = (-1.0, 1.0) - else: - bounds[i] = (0, 1.0) - elif isinstance(param, NormalFloatHyperparameter): - if can_be_inactive: - raise ValueError("Inactive parameters not supported for Beta and Normal Hyperparameters") - - bounds[i] = (param._lower, param._upper) - elif isinstance(param, NormalIntegerHyperparameter): - if can_be_inactive: - raise ValueError("Inactive parameters not supported for Beta and Normal Hyperparameters") - - bounds[i] = (param.nfhp._lower, param.nfhp._upper) - elif isinstance(param, BetaFloatHyperparameter): - if can_be_inactive: - raise ValueError("Inactive parameters not supported for Beta and Normal Hyperparameters") - - bounds[i] = (param._lower, param._upper) - elif isinstance(param, BetaIntegerHyperparameter): - if can_be_inactive: - raise ValueError("Inactive parameters not supported for Beta and Normal Hyperparameters") - - bounds[i] = (param.bfhp._lower, param.bfhp._upper) - elif not isinstance( - param, - ( - UniformFloatHyperparameter, - UniformIntegerHyperparameter, - OrdinalHyperparameter, - CategoricalHyperparameter, - NormalFloatHyperparameter, - NormalIntegerHyperparameter, - BetaFloatHyperparameter, - BetaIntegerHyperparameter, - ), - ): - raise TypeError("Unknown hyperparameter type %s" % type(param)) - - if instance_features is not None: - types = types + [0] * instance_features.shape[1] - - return types, bounds - - -def get_rng( - rng: typing.Optional[typing.Union[int, np.random.RandomState]] = None, - run_id: typing.Optional[int] = None, - logger: typing.Optional[logging.Logger] = None, -) -> typing.Tuple[int, np.random.RandomState]: - """Initialize random number generator and set run_id. - - * If rng and run_id are None, initialize a new generator and sample a run_id - * If rng is None and a run_id is given, use the run_id to initialize the rng - * If rng is an int, a RandomState object is created from that. - * If rng is RandomState, return it - * If only run_id is None, a run_id is sampled from the random state. - - Parameters - ---------- - rng : np.random.RandomState|int|None - run_id : int, optional - logger: logging.Logger, optional - - Returns - ------- - int - np.random.RandomState - """ - if logger is None: - logger = logging.getLogger("GetRNG") - # initialize random number generator - if rng is not None and not isinstance(rng, (int, np.random.RandomState)): - raise TypeError( - "Argument rng accepts only arguments of type None, int or np.random.RandomState, " - "you provided %s." % str(type(rng)) - ) - if run_id is not None and not isinstance(run_id, int): - raise TypeError( - "Argument run_id accepts only arguments of type None, int, " "you provided %s." % str(type(run_id)) - ) - - if rng is None and run_id is None: - # Case that both are None - logger.debug("No rng and no run_id given: using a random value to initialize run_id.") - rng_return = np.random.RandomState() - run_id_return = rng_return.randint(MAXINT) - elif rng is None and isinstance(run_id, int): - logger.debug("No rng and no run_id given: using run_id %d as seed.", run_id) - rng_return = np.random.RandomState(seed=run_id) - run_id_return = run_id - elif isinstance(rng, int) and run_id is None: - run_id_return = rng - rng_return = np.random.RandomState(seed=rng) - elif isinstance(rng, int) and isinstance(run_id, int): - run_id_return = run_id - rng_return = np.random.RandomState(seed=rng) - elif isinstance(rng, np.random.RandomState) and run_id is None: - rng_return = rng - run_id_return = rng.randint(MAXINT) - elif isinstance(rng, np.random.RandomState) and isinstance(run_id, int): - rng_return = rng - run_id_return = run_id - else: - raise ValueError( - "This should not happen! Please contact the developers! Arguments: rng=%s of type %s and " - "run_id=%s of type %s" % (rng, type(rng), str(run_id), type(run_id)) - ) - return run_id_return, rng_return - - -def check_subspace_points( - X: np.ndarray, - cont_dims: typing.Union[np.ndarray, typing.List] = [], - cat_dims: typing.Union[np.ndarray, typing.List] = [], - bounds_cont: typing.Optional[np.ndarray] = None, - bounds_cat: typing.Optional[typing.List[typing.Tuple]] = None, - expand_bound: bool = False, -) -> np.ndarray: - """ - Check which points are placed inside a given subspace - - Parameters - ---------- - X: typing.Optional[np.ndarray(N,D)], - points to be checked, where D = D_cont + D_cat - cont_dims: typing.Union[np.ndarray(D_cont), typing.List] - which dimensions represent continuous hyperparameters - cat_dims: typing.Union[np.ndarray(D_cat), typing.List] - which dimensions represent categorical hyperparameters - bounds_cont: typing.optional[typing.List[typing.Tuple]] - subspaces bounds of categorical hyperparameters, its length is the number of continuous hyperparameters - bounds_cat: typing.Optional[typing.List[typing.Tuple]] - subspaces bounds of continuous hyperparameters, its length is the number of categorical hyperparameters - expand_bound: bool - if the bound needs to be expanded to contain more points rather than the points inside the subregion - - Return - ---------- - indices_in_ss:np.ndarray(N) - indices of data that included in subspaces - """ - if len(X.shape) == 1: - X = X[np.newaxis, :] - if len(cont_dims) == 0 and len(cat_dims) == 0: - return np.ones(X.shape[0], dtype=bool) - - if len(cont_dims) > 0: - if bounds_cont is None: - raise ValueError("bounds_cont must be given if cont_dims provided") - - if len(bounds_cont.shape) != 2 or bounds_cont.shape[1] != 2 or bounds_cont.shape[0] != len(cont_dims): - raise ValueError( - f"bounds_cont (with shape {bounds_cont.shape}) should be an array with shape of" - f"({len(cont_dims)}, 2)" - ) - - data_in_ss = np.all(X[:, cont_dims] <= bounds_cont[:, 1], axis=1) & np.all( - X[:, cont_dims] >= bounds_cont[:, 0], axis=1 - ) - - if expand_bound: - bound_left = bounds_cont[:, 0] - np.min(X[data_in_ss][:, cont_dims] - bounds_cont[:, 0], axis=0) - bound_right = bounds_cont[:, 1] + np.min(bounds_cont[:, 1] - X[data_in_ss][:, cont_dims], axis=0) - data_in_ss = np.all(X[:, cont_dims] <= bound_right, axis=1) & np.all(X[:, cont_dims] >= bound_left, axis=1) - else: - data_in_ss = np.ones(X.shape[0], dtype=bool) - - if len(cat_dims) == 0: - return data_in_ss - if bounds_cat is None: - raise ValueError("bounds_cat must be given if cat_dims provided") - - if len(bounds_cat) != len(cat_dims): - raise ValueError( - f"bounds_cat ({len(bounds_cat)}) and cat_dims ({len(cat_dims)}) must have " f"the same number of elements" - ) - - for bound_cat, cat_dim in zip(bounds_cat, cat_dims): - data_in_ss &= np.in1d(X[:, cat_dim], bound_cat) - - return data_in_ss diff --git a/smac/facade/__init__.py b/smac/facade/__init__.py index e69de29bb..f481cbcc0 100644 --- a/smac/facade/__init__.py +++ b/smac/facade/__init__.py @@ -0,0 +1,19 @@ +from smac.facade.abstract_facade import AbstractFacade +from smac.facade.algorithm_configuration_facade import AlgorithmConfigurationFacade +from smac.facade.blackbox_facade import BlackBoxFacade +from smac.facade.hyperband_facade import HyperbandFacade +from smac.facade.hyperparameter_optimization_facade import ( + HyperparameterOptimizationFacade, +) +from smac.facade.multi_fidelity_facade import MultiFidelityFacade +from smac.facade.random_facade import RandomFacade + +__all__ = [ + "AbstractFacade", + "AlgorithmConfigurationFacade", + "BlackBoxFacade", + "HyperparameterOptimizationFacade", + "MultiFidelityFacade", + "HyperbandFacade", + "RandomFacade", +] diff --git a/smac/facade/abstract_facade.py b/smac/facade/abstract_facade.py new file mode 100644 index 000000000..cb1366e10 --- /dev/null +++ b/smac/facade/abstract_facade.py @@ -0,0 +1,465 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import Any, Callable, Iterator + +from pathlib import Path + +import joblib +from ConfigSpace import Configuration + +import smac +from smac.acquisition.function.abstract_acquisition_function import ( + AbstractAcquisitionFunction, +) +from smac.acquisition.maximizer.abstract_acqusition_maximizer import ( + AbstractAcquisitionMaximizer, +) +from smac.callback import Callback +from smac.initial_design.abstract_initial_design import AbstractInitialDesign +from smac.intensifier.abstract_intensifier import AbstractIntensifier +from smac.main import SMBO +from smac.model.abstract_model import AbstractModel +from smac.multi_objective.abstract_multi_objective_algorithm import ( + AbstractMultiObjectiveAlgorithm, +) +from smac.random_design.abstract_random_design import AbstractRandomDesign +from smac.runhistory.dataclasses import TrialInfo, TrialValue +from smac.runhistory.encoder.abstract_encoder import AbstractRunHistoryEncoder +from smac.runhistory.enumerations import TrialInfoIntent +from smac.runhistory.runhistory import RunHistory +from smac.runner.abstract_runner import AbstractRunner +from smac.runner.dask_runner import DaskParallelRunner +from smac.runner.target_function_runner import TargetFunctionRunner +from smac.runner.target_function_script_runner import TargetFunctionScriptRunner +from smac.scenario import Scenario +from smac.stats import Stats +from smac.utils.logging import get_logger, setup_logging + +logger = get_logger(__name__) + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class AbstractFacade: + """Facade is an abstraction on top of the SMBO backend to organize the components of a Bayesian Optimization loop + in a configurable and separable manner to suit the various needs of different (hyperparameter) optimization + pipelines. + + With the exception to scenario and ``target_function``, which are expected of the user, the parameters ``model``, + ``acquisition_function``, ``acquisition_maximizer``, ``initial_design``, ``random_design``, ``intensifier``, + ``multi_objective_algorithm``, ``runhistory_encoder`` can either be explicitly specified in the subclasses' + ``get_*`` methods (defining a specific BO pipeline) or be instantiated by the user to overwrite a pipelines + components explicitly. + + Parameters + ---------- + scenario : Scenario + The scenario object, holding all environmental information. + target_function : Callable | str | AbstractRunner + This function is called internally to judge a trial's performance. If a string is passed, + it is assumed to be a script. In this case, ``TargetFunctionScriptRunner`` is used to run the script. + model : AbstractModel | None, defaults to None + The surrogate model. + acquisition_function : AbstractAcquisitionFunction | None, defaults to None + The acquisition function. + acquisition_maximizer : AbstractAcquisitionMaximizer | None, defaults to None + The acquisition maximizer, deciding which configuration is most promising based on the surrogate model and + acquisition function. + initial_design : InitialDesign | None, defaults to None + The sampled configurations from the initial design are evaluated before the Bayesian optimization loop starts. + random_design : RandomDesign | None, defaults to None + The random design is used in the acquisition maximier, deciding whether the next configuration should be drawn + from the acquisition function or randomly. + intensifier : AbstractIntensifier | None, defaults to None + The intensifier decides which trial (combination of configuration, seed, budget and instance) should be run + next. + multi_objective_algorithm : AbstractMultiObjectiveAlgorithm | None, defaults to None + In case of multiple objectives, the objectives need to be interpreted so that an optimization is possible. + The multi objective algorithm takes care of that. + runhistory_encoder : RunHistoryEncoder | None, defaults to None + Based on the runhistory, the surrogate model is trained. However, the data first needs to be encoded, which + is done by the runhistory encoder. For example, inactive hyperparameters need to be encoded or cost values + can be log transformed. + logging_level: int | Path | None + The level of logging (the lowest level 0 indicates the debug level). If a path is passed, a yaml file is + expected with the logging configuration. If nothing is passed, the default logging.yml from SMAC is used. + callbacks: list[Callback], defaults to [] + Callbacks, which are incorporated into the optimization loop. + overwrite: bool, defaults to False + When True, overwrites the run results if a previous run is found that is + inconsistent in the meta data with the current setup. If ``overwrite`` is set to False, the user is asked + for the exact behaviour (overwrite completely, save old run, or use old results). + """ + + def __init__( + self, + scenario: Scenario, + target_function: Callable | str | AbstractRunner, + *, + model: AbstractModel | None = None, + acquisition_function: AbstractAcquisitionFunction | None = None, + acquisition_maximizer: AbstractAcquisitionMaximizer | None = None, + initial_design: AbstractInitialDesign | None = None, + random_design: AbstractRandomDesign | None = None, + intensifier: AbstractIntensifier | None = None, + multi_objective_algorithm: AbstractMultiObjectiveAlgorithm | None = None, + runhistory_encoder: AbstractRunHistoryEncoder | None = None, + logging_level: int | Path | None = None, + callbacks: list[Callback] = [], + overwrite: bool = False, + ): + setup_logging(logging_level) + + if model is None: + model = self.get_model(scenario) + + if acquisition_function is None: + acquisition_function = self.get_acquisition_function(scenario) + + if acquisition_maximizer is None: + acquisition_maximizer = self.get_acquisition_maximizer(scenario) + + if initial_design is None: + initial_design = self.get_initial_design(scenario) + + if random_design is None: + random_design = self.get_random_design(scenario) + + if intensifier is None: + intensifier = self.get_intensifier(scenario) + + if multi_objective_algorithm is None and scenario.count_objectives() > 1: + multi_objective_algorithm = self.get_multi_objective_algorithm(scenario=scenario) + + if runhistory_encoder is None: + runhistory_encoder = self.get_runhistory_encoder(scenario) + + # Initialize empty stats and runhistory object + runhistory = RunHistory(multi_objective_algorithm=multi_objective_algorithm) + stats = Stats(scenario) + + # Set the seed for configuration space + scenario.configspace.seed(scenario.seed) + + # Set variables globally + self._scenario = scenario + self._model = model + self._acquisition_function = acquisition_function + self._acquisition_maximizer = acquisition_maximizer + self._initial_design = initial_design + self._random_design = random_design + self._intensifier = intensifier + self._multi_objective_algorithm = multi_objective_algorithm + self._runhistory = runhistory + self._runhistory_encoder = runhistory_encoder + self._stats = stats + self._overwrite = overwrite + + # Prepare the algorithm executer + runner: AbstractRunner + if isinstance(target_function, AbstractRunner): + runner = target_function + elif isinstance(target_function, str): + runner = TargetFunctionScriptRunner( + scenario=scenario, + target_function=target_function, + required_arguments=self._get_signature_arguments(), + ) + else: + runner = TargetFunctionRunner( + scenario=scenario, + target_function=target_function, + required_arguments=self._get_signature_arguments(), + ) + + # In case of multiple jobs, we need to wrap the runner again using DaskParallelRunner + if (n_workers := scenario.n_workers) > 1: + available_workers = joblib.cpu_count() + if n_workers > available_workers: + logger.info(f"Workers are reduced to {n_workers}.") + n_workers = available_workers + + # We use a dask runner for parallelization + runner = DaskParallelRunner(single_worker=runner) + + # Set the runner to access it globally + self._runner = runner + + # Adding dependencies of the components + self._update_dependencies() + + # We have to update our meta data (basically arguments of the components) + self._scenario._set_meta(self.meta) + + # We have to validate if the object compositions are correct and actually make sense + self._validate() + + # Finally we configure our optimizer + self._optimizer = self._get_optimizer() + assert self._optimizer + + # Register callbacks here + for callback in callbacks: + self._optimizer._register_callback(callback) + + @property + def scenario(self) -> Scenario: + """The scenario object.""" + return self._scenario + + @property + def runhistory(self) -> RunHistory: + """The run history, which is filled with all information during the optimization process.""" + return self._optimizer._runhistory + + @property + def stats(self) -> Stats: + """The stats object, which is updated during the optimization and shows relevant information, e.g., how many + trials have been finished and how the trajectory looks like. + """ + return self._optimizer._stats + + @property + def incumbent(self) -> Configuration | None: + """The best configuration so far.""" + return self._optimizer._incumbent + + @property + def meta(self) -> dict[str, Any]: + """Generates a hash based on all components of the facade. This is used for the run name or to determine + whether a run should be continued or not. + """ + multi_objective_algorithm_meta = None + if self._multi_objective_algorithm is not None: + multi_objective_algorithm_meta = self._multi_objective_algorithm.meta + + meta = { + "facade": {"name": self.__class__.__name__}, + "runner": self._runner.meta, + "model": self._model.meta, + "acquisition_maximizer": self._acquisition_maximizer.meta, + "acquisition_function": self._acquisition_function.meta, + "intensifier": self._intensifier.meta, + "initial_design": self._initial_design.meta, + "random_design": self._random_design.meta, + "runhistory_encoder": self._runhistory_encoder.meta, + "multi_objective_algorithm": multi_objective_algorithm_meta, + "version": smac.version, + } + + return meta + + def get_target_function_seeds(self) -> list[int]: + """Which seeds are used to call the target function.""" + return self._intensifier.get_target_function_seeds() + + def get_target_function_budgets(self) -> list[float | None]: + """Which budgets are used to call the target function.""" + return self._intensifier.get_target_function_budgets() + + def get_target_function_instances(self) -> list[str | None]: + """Which instances are used to call the target function.""" + return self._intensifier.get_target_function_instances() + + def get_next_configurations(self) -> Iterator[Configuration]: + """Choose next candidate solution with Bayesian optimization. The suggested configurations + depend on the surrogate model acquisition optimizer/function. This method is used by + the intensifier. + """ + return self._optimizer.get_next_configurations() + + def ask(self) -> TrialInfo: + """Asks the intensifier for the next trial. This method returns only trials with the intend + to run. + """ + counter = 0 + while True: + if counter > 10: + logger.warning("It seems like SMAC only finds trials with the intent to skip/wait.") + counter = 0 + + intend, info = self._optimizer.ask() + # We only accept trials which are intented to run + if intend != TrialInfoIntent.RUN: + counter += 1 + continue + + counter = 0 + return info + + def tell(self, info: TrialInfo, value: TrialValue, save: bool = True) -> None: + """Adds the result of a trial to the runhistory and updates the intensifier. Also, + the stats object is updated. + + Parameters + ---------- + info: TrialInfo + Describes the trial from which to process the results. + value: TrialValue + Contains relevant information regarding the execution of a trial. + save : bool, optional to True + Whether the runhistory should be saved. + """ + return self._optimizer.tell(info, value, time_left=None, save=save) + + def optimize(self) -> Configuration: + """ + Optimizes the algorithm. + + Returns + ------- + incumbent : Configuration + Best found configuration. + """ + incumbent = None + try: + incumbent = self._optimizer.optimize() + finally: + self._optimizer.save() + self._stats.print() + + if incumbent is not None: + cost = self._runhistory.get_cost(incumbent) + logger.info(f"Final Incumbent: {incumbent.get_dictionary()}") + logger.info(f"Estimated cost: {cost}") + + return incumbent + + def validate( + self, + config: Configuration, + *, + instances: list[str] | None = None, + seed: int | None = None, + ) -> float | list[float]: + """Validates a configuration with different seeds than in the optimization process and on the highest + budget (if budget type is real-valued). + + Parameters + ---------- + config : Configuration + Configuration to validate + instances : list[str] | None, defaults to None + Which instances to validate. If None, all instances specified in the scenario are used. + In case that the budget type is real-valued budget, this argument is ignored. + seed : int | None, defaults to None + If None, the seed from the scenario is used. + + Returns + ------- + cost : float | list[float] + The averaged cost of the configuration. In case of multi-fidelity, the cost of each objective is + averaged. + """ + return self._optimizer.validate(config, instances=instances, seed=seed) + + @staticmethod + @abstractmethod + def get_model(scenario: Scenario) -> AbstractModel: + """Returns the surrogate cost model instance used in the BO loop.""" + raise NotImplementedError + + @staticmethod + @abstractmethod + def get_acquisition_function(scenario: Scenario) -> AbstractAcquisitionFunction: + """Returns the acquisition function instance used in the BO loop, + defining the exploration/exploitation trade-off. + """ + raise NotImplementedError + + @staticmethod + @abstractmethod + def get_acquisition_maximizer(scenario: Scenario) -> AbstractAcquisitionMaximizer: + """Returns the acquisition optimizer instance to be used in the BO loop, + specifying how the acquisition function instance is optimized. + """ + raise NotImplementedError + + @staticmethod + @abstractmethod + def get_intensifier(scenario: Scenario) -> AbstractIntensifier: + """Returns the intensifier instance to be used in the BO loop, + specifying how to challenge the incumbent configuration on other problem instances. + """ + raise NotImplementedError + + @staticmethod + @abstractmethod + def get_initial_design(scenario: Scenario) -> AbstractInitialDesign: + """Returns an instance of the initial design class to be used in the BO loop, + specifying how the configurations the BO loop is 'warm-started' with are selected. + """ + raise NotImplementedError + + @staticmethod + @abstractmethod + def get_random_design(scenario: Scenario) -> AbstractRandomDesign: + """Returns an instance of the random design class to be used in the BO loop, + specifying how to interleave the BO iterations with randomly selected configurations. + """ + raise NotImplementedError + + @staticmethod + @abstractmethod + def get_runhistory_encoder(scenario: Scenario) -> AbstractRunHistoryEncoder: + """Returns an instance of the runhistory encoder class to be used in the Bayesian optimization loop, + specifying how the runhistory is to be prepared for the next surrogate model. + """ + raise NotImplementedError + + @staticmethod + @abstractmethod + def get_multi_objective_algorithm(scenario: Scenario) -> AbstractMultiObjectiveAlgorithm: + """Returns the multi-objective algorithm instance to be used in the Bayesian optimization loop, + specifying the scalarization strategy for multiple objectives' costs + """ + raise NotImplementedError + + def _get_optimizer(self) -> SMBO: + """Fills the SMBO with all the pre-initialized components.""" + return SMBO( + scenario=self._scenario, + stats=self._stats, + runner=self._runner, + initial_design=self._initial_design, + runhistory=self._runhistory, + runhistory_encoder=self._runhistory_encoder, + intensifier=self._intensifier, + model=self._model, + acquisition_function=self._acquisition_function, + acquisition_maximizer=self._acquisition_maximizer, + random_design=self._random_design, + overwrite=self._overwrite, + ) + + def _update_dependencies(self) -> None: + """Convenience method to add some more dependencies. And ensure separable instantiation of + the components. This is the easiest way to incorporate dependencies, although + it might be a bit hacky. + """ + self._intensifier._stats = self._stats + self._runhistory_encoder.multi_objective_algorithm = self._multi_objective_algorithm + self._acquisition_function.model = self._model + self._acquisition_maximizer.acquisition_function = self._acquisition_function + + def _validate(self) -> None: + """Checks if the composition is correct if there are dependencies, not necessarily.""" + # Make sure the same acquisition function is used + assert self._acquisition_function == self._acquisition_maximizer._acquisition_function + + def _get_signature_arguments(self) -> list[str]: + """Returns signature arguments, which are required by the intensifier.""" + arguments = [] + + if self._intensifier.uses_seeds: + arguments += ["seed"] + + if self._intensifier.uses_budgets: + arguments += ["budget"] + + if self._intensifier.uses_instances: + arguments += ["instance"] + + return arguments diff --git a/smac/facade/algorithm_configuration_facade.py b/smac/facade/algorithm_configuration_facade.py new file mode 100644 index 000000000..077763b18 --- /dev/null +++ b/smac/facade/algorithm_configuration_facade.py @@ -0,0 +1,191 @@ +from __future__ import annotations + +from ConfigSpace import Configuration + +from smac.acquisition.function.expected_improvement import EI +from smac.acquisition.maximizer.local_and_random_search import ( + LocalAndSortedRandomSearch, +) +from smac.facade.abstract_facade import AbstractFacade +from smac.initial_design.default_design import DefaultInitialDesign +from smac.intensifier.intensifier import Intensifier +from smac.model.random_forest.random_forest import RandomForest +from smac.multi_objective.aggregation_strategy import MeanAggregationStrategy +from smac.random_design.probability_design import ProbabilityRandomDesign +from smac.runhistory.encoder.encoder import RunHistoryEncoder +from smac.scenario import Scenario +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class AlgorithmConfigurationFacade(AbstractFacade): + @staticmethod + def get_model( # type: ignore + scenario: Scenario, + *, + n_trees: int = 10, + ratio_features: float = 5.0 / 6.0, + min_samples_split: int = 3, + min_samples_leaf: int = 3, + max_depth: int = 20, + bootstrapping: bool = True, + pca_components: int = 4, + ) -> RandomForest: + """Returns a random forest as surrogate model. + + Parameters + ---------- + n_trees : int, defaults to 10 + The number of trees in the random forest. + ratio_features : float, defaults to 5.0 / 6.0 + The ratio of features that are considered for splitting. + min_samples_split : int, defaults to 3 + The minimum number of data points to perform a split. + min_samples_leaf : int, defaults to 3 + The minimum number of data points in a leaf. + max_depth : int, defaults to 20 + The maximum depth of a single tree. + bootstrapping : bool, defaults to True + Enables bootstrapping. + pca_components : float, defaults to 4 + Number of components to keep when using PCA to reduce dimensionality of instance features. + """ + return RandomForest( + configspace=scenario.configspace, + n_trees=n_trees, + ratio_features=ratio_features, + min_samples_split=min_samples_split, + min_samples_leaf=min_samples_leaf, + max_depth=max_depth, + bootstrapping=bootstrapping, + log_y=False, + instance_features=scenario.instance_features, + pca_components=pca_components, + seed=scenario.seed, + ) + + @staticmethod + def get_acquisition_function( # type: ignore + scenario: Scenario, + *, + xi: float = 0.0, + ) -> EI: + """Returns an Expected Improvement acquisition function. + + Parameters + ---------- + scenario : Scenario + xi : float, defaults to 0.0 + Controls the balance between exploration and exploitation of the + acquisition function. + """ + return EI(xi=xi) + + @staticmethod + def get_acquisition_maximizer( # type: ignore + scenario: Scenario, + ) -> LocalAndSortedRandomSearch: + """Returns local and sorted random search as acquisition maximizer.""" + optimizer = LocalAndSortedRandomSearch( + scenario.configspace, + seed=scenario.seed, + ) + + return optimizer + + @staticmethod + def get_intensifier( # type: ignore + scenario: Scenario, + *, + min_config_calls=1, + max_config_calls=2000, + min_challenger=1, + intensify_percentage: float = 0.5, + ) -> Intensifier: + """Returns ``Intensifier`` as intensifier. Uses the default configuration for ``race_against``. + + Parameters + ---------- + scenario : Scenario + min_config_calls : int, defaults to 1 + Minimum number of trials per config (summed over all calls to intensify). + max_config_calls : int, defaults to 2000 + Maximum number of trials per config (summed over all calls to intensify). + min_challenger : int, defaults to 2 + Minimal number of challengers to be considered (even if time_bound is exhausted earlier). + intensify_percentage : float, defaults to 0.5 + How much percentage of the time should configurations be intensified (evaluated on higher budgets or + more instances). This parameter is accessed in the SMBO class. + """ + intensifier = Intensifier( + scenario=scenario, + min_challenger=min_challenger, + race_against=scenario.configspace.get_default_configuration(), + min_config_calls=min_config_calls, + max_config_calls=max_config_calls, + intensify_percentage=intensify_percentage, + ) + + return intensifier + + @staticmethod + def get_initial_design( # type: ignore + scenario: Scenario, + *, + additional_configs: list[Configuration] = [], + ) -> DefaultInitialDesign: + """Returns an initial design, which returns the default configuration. + + Parameters + ---------- + additional_configs: list[Configuration], defaults to [] + Adds additional configurations to the initial design. + """ + return DefaultInitialDesign( + scenario=scenario, + additional_configs=additional_configs, + ) + + @staticmethod + def get_random_design( # type: ignore + scenario: Scenario, + *, + probability: float = 0.5, + ) -> ProbabilityRandomDesign: + """Returns ``ProbabilityRandomDesign`` for interleaving configurations. + + Parameters + ---------- + probability : float, defaults to 0.5 + Probability that a configuration will be drawn at random. + """ + return ProbabilityRandomDesign(probability=probability, seed=scenario.seed) + + @staticmethod + def get_multi_objective_algorithm( # type: ignore + scenario: Scenario, + *, + objective_weights: list[float] | None = None, + ) -> MeanAggregationStrategy: + """Returns the mean aggregation strategy for the multi objective algorithm. + + Parameters + ---------- + scenario : Scenario + objective_weights : list[float] | None, defaults to None + Weights for an weighted average. Must be of the same length as the number of objectives. + """ + return MeanAggregationStrategy( + scenario=scenario, + objective_weights=objective_weights, + ) + + @staticmethod + def get_runhistory_encoder(scenario: Scenario) -> RunHistoryEncoder: + """Returns the default runhistory encoder.""" + return RunHistoryEncoder(scenario) diff --git a/smac/facade/blackbox_facade.py b/smac/facade/blackbox_facade.py new file mode 100644 index 000000000..4e5af09f4 --- /dev/null +++ b/smac/facade/blackbox_facade.py @@ -0,0 +1,319 @@ +from __future__ import annotations + +import numpy as np +import sklearn.gaussian_process.kernels as kernels +from ConfigSpace import Configuration + +from smac.acquisition.function.expected_improvement import EI +from smac.acquisition.maximizer.local_and_random_search import ( + LocalAndSortedRandomSearch, +) +from smac.facade.abstract_facade import AbstractFacade +from smac.initial_design.sobol_design import SobolInitialDesign +from smac.intensifier.intensifier import Intensifier +from smac.model.gaussian_process.abstract_gaussian_process import ( + AbstractGaussianProcess, +) +from smac.model.gaussian_process.gaussian_process import GaussianProcess +from smac.model.gaussian_process.kernels import ( + ConstantKernel, + HammingKernel, + MaternKernel, + WhiteKernel, +) +from smac.model.gaussian_process.mcmc_gaussian_process import MCMCGaussianProcess +from smac.model.gaussian_process.priors import HorseshoePrior, LogNormalPrior +from smac.multi_objective.aggregation_strategy import MeanAggregationStrategy +from smac.random_design.probability_design import ProbabilityRandomDesign +from smac.runhistory.encoder.encoder import RunHistoryEncoder +from smac.scenario import Scenario +from smac.utils.configspace import get_types + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class BlackBoxFacade(AbstractFacade): + def _validate(self) -> None: + """Ensure that the SMBO configuration with all its (updated) dependencies is valid.""" + super()._validate() + # Activate predict incumbent + # self.solver.epm_chooser.predict_x_best = True + + if self._scenario.instance_features is not None and len(self._scenario.instance_features) > 0: + raise NotImplementedError("The Black-Box GP cannot handle instances.") + + if not isinstance(self._model, AbstractGaussianProcess): + raise ValueError("The Black-Box facade only works with Gaussian Processes") + + @staticmethod + def get_model( + scenario: Scenario, + *, + model_type: str | None = None, + kernel: kernels.Kernel | None = None, + ) -> AbstractGaussianProcess: + """Returns a Gaussian Process surrogate model. + + Parameters + ---------- + scenario : Scenario + model_type : str | None, defaults to None + Which gaussian process model should be chosen. Choose between `vanilla` and `mcmc`. + kernel : kernels.Kernel | None, defaults to None + The kernel used in the surrogate model. + + Returns + ------- + model : GaussianProcess | MCMCGaussianProcess + The instantiated gaussian process. + """ + available_model_types = [None, "vanilla", "mcmc"] + if model_type not in available_model_types: + types = [str(t) for t in available_model_types] + raise ValueError(f"The model_type `{model_type}` is not supported. Choose one of {', '.join(types)}") + + if kernel is None: + kernel = BlackBoxFacade.get_kernel(scenario=scenario) + + if model_type is None or model_type == "vanilla": + return GaussianProcess( + configspace=scenario.configspace, + kernel=kernel, + normalize_y=True, + seed=scenario.seed, + ) + elif model_type == "mcmc": + n_mcmc_walkers = 3 * len(kernel.theta) + if n_mcmc_walkers % 2 == 1: + n_mcmc_walkers += 1 + + return MCMCGaussianProcess( + configspace=scenario.configspace, + kernel=kernel, + n_mcmc_walkers=n_mcmc_walkers, + chain_length=250, + burning_steps=250, + normalize_y=True, + seed=scenario.seed, + ) + else: + raise ValueError("Unknown model type %s" % model_type) + + @staticmethod + def get_kernel(scenario: Scenario) -> kernels.Kernel: + """Returns a kernel for the Gaussian Process surrogate model. + + The kernel is a composite of kernels depending on the type of hyperparameters: + categorical (HammingKernel), continuous (Matern), and noise kernels (White). + """ + types, _ = get_types(scenario.configspace, instance_features=None) + cont_dims = np.where(np.array(types) == 0)[0] + cat_dims = np.where(np.array(types) != 0)[0] + + if (len(cont_dims) + len(cat_dims)) != len(scenario.configspace.get_hyperparameters()): + raise ValueError( + "The inferred number of continuous and categorical hyperparameters " + "must equal the total number of hyperparameters. Got " + f"{(len(cont_dims) + len(cat_dims))} != {len(scenario.configspace.get_hyperparameters())}." + ) + + # Constant Kernel + cov_amp = ConstantKernel( + 2.0, + constant_value_bounds=(np.exp(-10), np.exp(2)), + prior=LogNormalPrior( + mean=0.0, + sigma=1.0, + seed=scenario.seed, + ), + ) + + # Continuous / Categorical Kernels + exp_kernel, ham_kernel = 0.0, 0.0 + if len(cont_dims) > 0: + exp_kernel = MaternKernel( + np.ones([len(cont_dims)]), + [(np.exp(-6.754111155189306), np.exp(0.0858637988771976)) for _ in range(len(cont_dims))], + nu=2.5, + operate_on=cont_dims, + ) + if len(cat_dims) > 0: + ham_kernel = HammingKernel( + np.ones([len(cat_dims)]), + [(np.exp(-6.754111155189306), np.exp(0.0858637988771976)) for _ in range(len(cat_dims))], + operate_on=cat_dims, + ) + + # Noise Kernel + noise_kernel = WhiteKernel( + noise_level=1e-8, + noise_level_bounds=(np.exp(-25), np.exp(2)), + prior=HorseshoePrior(scale=0.1, seed=scenario.seed), + ) + + # Continuous and categecorical HPs + if len(cont_dims) > 0 and len(cat_dims) > 0: + kernel = cov_amp * (exp_kernel * ham_kernel) + noise_kernel + + # Only continuous HPs + elif len(cont_dims) > 0 and len(cat_dims) == 0: + kernel = cov_amp * exp_kernel + noise_kernel + + # Only categorical HPs + elif len(cont_dims) == 0 and len(cat_dims) > 0: + kernel = cov_amp * ham_kernel + noise_kernel + + else: + raise ValueError("The number of continuous and categorical hyperparameters must be greater than zero.") + + return kernel + + @staticmethod + def get_acquisition_function( # type: ignore + scenario: Scenario, + *, + xi: float = 0.0, + ) -> EI: + """Returns an Expected Improvement acquisition function. + + Parameters + ---------- + scenario : Scenario + xi : float, defaults to 0.0 + Controls the balance between exploration and exploitation of the + acquisition function. + """ + return EI(xi=xi) + + @staticmethod + def get_acquisition_maximizer( # type: ignore + scenario: Scenario, + *, + challengers: int = 1000, + local_search_iterations: int = 10, + ) -> LocalAndSortedRandomSearch: + """Returns local and sorted random search as acquisition maximizer. + + Parameters + ---------- + challengers : int, defaults to 1000 + Number of challengers. + local_search_iterations: int, defauts to 10 + Number of local search iterations. + """ + return LocalAndSortedRandomSearch( + configspace=scenario.configspace, + challengers=challengers, + local_search_iterations=local_search_iterations, + seed=scenario.seed, + ) + + @staticmethod + def get_intensifier( # type: ignore + scenario: Scenario, + *, + min_challenger: int = 1, + min_config_calls: int = 1, + max_config_calls: int = 3, + intensify_percentage: float = 0.5, + ) -> Intensifier: + """Returns ``Intensifier`` as intensifier. Uses the default configuration for ``race_against``. + + Parameters + ---------- + scenario : Scenario + min_config_calls : int, defaults to 1 + Minimum number of trials per config (summed over all calls to intensify). + max_config_calls : int, defaults to 1 + Maximum number of trials per config (summed over all calls to intensify). + min_challenger : int, defaults to 3 + Minimal number of challengers to be considered (even if time_bound is exhausted earlier). + intensify_percentage : float, defaults to 0.5 + How much percentage of the time should configurations be intensified (evaluated on higher budgets or + more instances). This parameter is accessed in the SMBO class. + """ + return Intensifier( + scenario=scenario, + min_challenger=min_challenger, + race_against=scenario.configspace.get_default_configuration(), + min_config_calls=min_config_calls, + max_config_calls=max_config_calls, + intensify_percentage=intensify_percentage, + ) + + @staticmethod + def get_initial_design( # type: ignore + scenario: Scenario, + *, + n_configs: int | None = None, + n_configs_per_hyperparamter: int = 10, + max_ratio: float = 0.1, + additional_configs: list[Configuration] = [], + ) -> SobolInitialDesign: + """Returns a Sobol design instance. + + Parameters + ---------- + scenario : Scenario + n_configs : int | None, defaults to None + Number of initial configurations (disables the arguments ``n_configs_per_hyperparameter``). + n_configs_per_hyperparameter: int, defaults to 10 + Number of initial configurations per hyperparameter. For example, if my configuration space covers five + hyperparameters and ``n_configs_per_hyperparameter`` is set to 10, then 50 initial configurations will be + samples. + max_ratio: float, defaults to 0.1 + Use at most ``scenario.n_trials`` * ``max_ratio`` number of configurations in the initial design. + Additional configurations are not affected by this parameter. + additional_configs: list[Configuration], defaults to [] + Adds additional configurations to the initial design. + """ + return SobolInitialDesign( + scenario=scenario, + n_configs=n_configs, + n_configs_per_hyperparameter=n_configs_per_hyperparamter, + max_ratio=max_ratio, + additional_configs=additional_configs, + seed=scenario.seed, + ) + + @staticmethod + def get_random_design( # type: ignore + scenario: Scenario, + *, + probability: float = 0.08447232371720552, + ) -> ProbabilityRandomDesign: + """Returns ``ProbabilityRandomDesign`` for interleaving configurations. + + Parameters + ---------- + probability : float, defaults to 0.08447232371720552 + Probability that a configuration will be drawn at random. + """ + return ProbabilityRandomDesign(seed=scenario.seed, probability=probability) + + @staticmethod + def get_multi_objective_algorithm( # type: ignore + scenario: Scenario, + *, + objective_weights: list[float] | None = None, + ) -> MeanAggregationStrategy: + """Returns the mean aggregation strategy for the multi objective algorithm. + + Parameters + ---------- + scenario : Scenario + objective_weights : list[float] | None, defaults to None + Weights for an weighted average. Must be of the same length as the number of objectives. + """ + return MeanAggregationStrategy( + scenario=scenario, + objective_weights=objective_weights, + ) + + @staticmethod + def get_runhistory_encoder( + scenario: Scenario, + ) -> RunHistoryEncoder: + """Returns the default runhistory encoder.""" + return RunHistoryEncoder(scenario) diff --git a/smac/facade/boing_facade.py b/smac/facade/boing_facade.py new file mode 100644 index 000000000..76efddb53 --- /dev/null +++ b/smac/facade/boing_facade.py @@ -0,0 +1,264 @@ +# from __future__ import annotations + +# from typing import Callable, Dict, Type + +# import numpy as np +# from botorch.models.kernels.categorical import CategoricalKernel +# from gpytorch.constraints.constraints import Interval +# from gpytorch.kernels import MaternKernel, ScaleKernel +# from gpytorch.likelihoods.gaussian_likelihood import GaussianLikelihood +# from gpytorch.priors import HorseshoePrior, LogNormalPrior + +# from smac.acquisition.maximizer import AbstractAcquisitionMaximizer, LocalAndSortedRandomSearch +# from smac.acquisition.function import EI, AbstractAcquisitionFunction +# from smac.main.boing import BOinGSMBO +# from smac.model.random_forest.random_forest import RandomForest +# from smac.random_design.probability_design import ProbabilityRandomDesign +# from smac.facade.hyperparameter_optimization_facade import HyperparameterOptimizationFacade +# from smac.model.abstract_model import AbstractModel +# from smac.model.gaussian_process.gpytorch_gaussian_process import ( +# GloballyAugmentedLocalGaussianProcess, +# GPyTorchGaussianProcess, +# ) +# from smac.runhistory.encoder.boing_encoder import ( +# RunHistoryRawEncoder, +# RunHistoryRawScaledEncoder, +# ) +# from smac.runner.abstract_runner import AbstractRunner +# from smac.scenario import Scenario + + +# class BOinGFacade(HyperparameterOptimizationFacade): +# """ +# SMAC wrapper for BO inside Grove(BOinG): +# Deng and Lindauer, Searching in the Forest for Local Bayesian Optimization +# https://arxiv.org/abs/2111.05834 + +# BOiNG is a two-stages optimizer: at the first stage, the global optimizer extracts the global optimum with a +# random +# forest (RF) model. Then in the second stage, the optimizer constructs a local model in the vicinity of the +# configuration suggested by the global surrogate model. + +# Its Hyperparameter settings follow the implementation from smac.facade.smac_bb_facade.SMAC4BB: +# Hyperparameters are chosen according to the best configuration for Gaussian process maximum likelihood found in +# "Towards Assessing the Impact of Bayesian Optimization's Own Hyperparameters" by Lindauer et al., presented at the +# DSO workshop 2019 (https://arxiv.org/abs/1908.06674). + +# Parameters +# ---------- +# model_local: Type[AbstractModel], +# local empirical performance model, used in subspace. Since the subspace might have different amount of +# hyperparameters compared to the search space. We only instantiate them under the subspace. +# model_local_kwargs: Optional[Dict] = None, +# parameters for initializing a local model +# acquisition_func_local: AbstractAcquisitionFunction, +# local acquisition function, used in subspace +# acquisition_func_local_kwargs: Optional[Dict] = None, +# parameters for initializing a local acquisition function optimizer +# acq_optimizer_local: Optional[AcquisitionFunctionMaximizer] = None, +# Optimizer of acquisition function of local models, same as above +# acq_optimizer_local_kwargs: typing: Optional[Dict] = None, +# parameters for the optimizer of acquisition function of local models +# max_configs_local_fracs : float +# The maximal number of fractions of samples to be included in the subspace. If the number of samples in the +# subspace is greater than this value and n_min_config_inner, the subspace will be cropped to fit the +# requirement +# min_configs_local: int, +# Minimum number of samples included in the inner loop model +# do_switching: bool +# if we want to switch between turbo and boing or do a pure BOinG search +# turbo_kwargs: Optional[Dict] = None +# parameters for building a turbo optimizer. For details, please refer to smac.utils.subspace.turbo +# """ + +# def __init__( +# self, +# scenario: Scenario, +# target_function: AbstractRunner | Callable, +# *, +# local_model: AbstractModel | None = None, +# local_acquisition_function: AbstractAcquisitionFunction | None = None, +# local_acquisition_maximizer: AbstractAcquisitionMaximizer | None = None, +# # model_local: Type[AbstractModel] = GloballyAugmentedLocalGaussianProcess, +# # model_local_kwargs: Dict | None = None, +# # acquisition_func_local: AbstractAcquisitionFunction | Type[AbstractAcquisitionFunction] = EI, +# # acquisition_func_local_kwargs: Dict | None = None, +# # acq_optimizer_local: AbstractAcquisitionMaximizer | None = None, +# # acq_optimizer_local_kwargs: Dict | None = None, +# # max_configs_local_fracs: float = 0.5, +# # min_configs_local: int | None = None, +# # do_switching: bool = False, +# # turbo_kwargs: Dict | None = None, +# **kwargs, +# ): +# if local_model is None: +# local_model = self.get_local_model(scenario) + +# if local_acquisition_function is None: +# local_acquisition_function = self.get_local_acquisition_function(scenario) + +# if local_acquisition_maximizer is None: +# local_acquisition_maximizer = self.get_local_acquisition_maximizer(scenario) + +# self._local_model = local_model +# self._local_acquisition_function = local_acquisition_function +# self._local_acquisition_maximizer = local_acquisition_maximizer + +# # self.model_local = model_local +# # if model_local_kwargs is None and issubclass(model_local, GPyTorchGaussianProcess): +# # model_local_kwargs = self.get_lgpga_local_components() + +# # self.model_local_kwargs = model_local_kwargs +# # self.acquisition_func_local = acquisition_func_local +# # self.acquisition_func_local_kwargs = acquisition_func_local_kwargs +# # self.acq_optimizer_local = acq_optimizer_local +# # self.acq_optimizer_local_kwargs = acq_optimizer_local_kwargs +# # self.max_configs_local_fracs = max_configs_local_fracs +# # self.min_configs_local = min_configs_local +# # self.do_switching = do_switching +# # self.turbo_kwargs = turbo_kwargs + +# super().__init__(scenario=scenario, target_function=target_function, **kwargs) + +# def _update_dependencies(self) -> None: +# super()._update_dependencies() + +# self._local_acquisition_function.model = self._local_model +# self._local_acquisition_maximizer.acquisition_function = self._local_acquisition_function + +# def _validate(self) -> None: +# super()._validate() + +# if not isinstance(self._model, RandomForest): +# raise ValueError("BOinG only supports RandomForestWithInstances as its global optimizer") + +# if not isinstance(self._runhistory_encoder, RunHistoryRawEncoder): +# raise ValueError("BOinG only supports RunHistory2EPM4CostWithRaw as its rh transformer") + +# @staticmethod +# def get_local_model(scenario: Scenario) -> AbstractGaussianProcess: +# # The lower bound and upper bounds are set to be identical as SMAC4BB +# cont_kernel_kwargs = { +# "lengthscale_constraint": Interval( +# np.exp(-6.754111155189306), np.exp(0.0858637988771976), transform=None, initial_value=1.0 +# ), +# } +# cat_kernel_kwargs = { +# "lengthscale_constraint": Interval( +# np.exp(-6.754111155189306), np.exp(0.0858637988771976), transform=None, initial_value=1.0 +# ), +# } +# scale_kernel_kwargs = { +# "outputscale_constraint": Interval(np.exp(-10.0), np.exp(2.0), transform=None, initial_value=2.0), +# "outputscale_prior": LogNormalPrior(0.0, 1.0), +# } + +# kernel_kwargs = { +# "cont_kernel": MaternKernel, +# "cont_kernel_kwargs": cont_kernel_kwargs, +# "cat_kernel": CategoricalKernel, +# "cat_kernel_kwargs": cat_kernel_kwargs, +# "scale_kernel": ScaleKernel, +# "scale_kernel_kwargs": scale_kernel_kwargs, +# } + +# # by setting lower bound of noise_constraint we could make it more stable +# noise_prior = HorseshoePrior(0.1) +# likelihood = GaussianLikelihood( +# noise_prior=noise_prior, noise_constraint=Interval(1e-5, np.exp(2), transform=None) +# ).double() + +# # ... Instantiate GPyTorchGaussianProcess + +# @staticmethod +# def get_local_acquisition_function( # type: ignore +# scenario: Scenario, +# *, +# xi: float = 0.0, +# ) -> EI: +# """Returns an Expected Improvement acquisition function. + +# Parameters +# ---------- +# scenario : Scenario +# xi : float, defaults to 0.0 +# Controls the balance between exploration and exploitation of the +# acquisition function. +# """ +# return EI(xi=xi) + +# @staticmethod +# def get_local_acquisition_maximizer( # type: ignore +# scenario: Scenario, +# *, +# challengers: int = 1000, +# local_search_iterations: int = 10, +# ) -> LocalAndSortedRandomSearch: +# """Returns local and sorted random search as acquisition maximizer. + +# Parameters +# ---------- +# challengers : int, defaults to 1000 +# Number of challengers. +# local_search_iterations: int, defauts to 10 +# Number of local search iterations. +# """ +# return LocalAndSortedRandomSearch( +# configspace=scenario.configspace, +# challengers=challengers, +# local_search_iterations=local_search_iterations, +# seed=scenario.seed, +# ) + +# @staticmethod +# def get_acquisition_maximizer( +# scenario: Scenario, +# *, +# local_search_iterations: int = 10, +# challengers: int = 1000, +# ) -> AbstractAcquisitionMaximizer: +# """Returns the acquisition optimizer instance for finding the next candidate configuration +# based on the acquisition function.""" +# return LocalAndSortedRandomSearch( +# configspace=scenario.configspace, +# local_search_iterations=local_search_iterations, +# challengers=challengers, +# seed=scenario.seed, +# ) + +# @staticmethod +# def get_runhistory_encoder(scenario: Scenario) -> RunHistoryRawEncoder: +# return RunHistoryRawScaledEncoder(scenario, scale_percentage=5) + +# @staticmethod +# def get_random_design(scenario: Scenario, *, probability: float = 0.08447232371720552) -> ProbabilityRandomDesign: +# return super(BOinGFacade, BOinGFacade).get_random_design(scenario=scenario, probability=probability) + +# def _get_optimizer(self) -> BOinGSMBO: +# """Configure the BOinGSMBO optimizer, that defines the particular BO loop.""" +# return BOinGSMBO( +# scenario=self._scenario, +# stats=self.stats, +# runner=self.runner, +# initial_design=self.initial_design, +# runhistory=self.runhistory, +# runhistory_encoder=self.runhistory_encoder, +# intensifier=self.intensifier, +# model=self.model, +# acquisition_function=self.acquisition_function, +# acquisition_optimizer=self.acquisition_optimizer, +# local_model=self._local_model, +# local_acquisition_function=self._local_acquisition_function, +# local_acquisition_maximizer=self._local_acquisition_maximizer +# random_design=self.random_design, +# seed=self.seed, + +# # model_local=self.model_local, +# # model_local_kwargs=self.model_local_kwargs, +# # acquisition_func_local=self.acquisition_func_local, +# # acq_optimizer_local_kwargs=self.acquisition_func_local_kwargs, +# # max_configs_local_fracs=self.max_configs_local_fracs, +# # min_configs_local=self.min_configs_local, +# # do_switching=self.do_switching, +# # turbo_kwargs=self.turbo_kwargs, +# ) diff --git a/smac/facade/experimental/hydra_facade.py b/smac/facade/experimental/hydra_facade.py deleted file mode 100644 index ae9b0cc3b..000000000 --- a/smac/facade/experimental/hydra_facade.py +++ /dev/null @@ -1,305 +0,0 @@ -# type: ignore -# mypy: ignore-errors - -import typing - -import copy -import datetime -import logging -import os -import pickle -import time -from collections import defaultdict - -import numpy as np -from ConfigSpace.configuration_space import Configuration - -from smac.epm.utils import get_rng -from smac.facade.psmac_facade import PSMAC -from smac.facade.smac_ac_facade import SMAC4AC -from smac.optimizer.pSMAC import read -from smac.runhistory.runhistory import RunHistory -from smac.scenario.scenario import Scenario -from smac.tae.base import BaseRunner -from smac.tae.execute_ta_run_hydra import ExecuteTARunHydra, ExecuteTARunOld -from smac.utils.constants import MAXINT -from smac.utils.io.output_directory import create_output_directory - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2017, ML4AAD" -__license__ = "3-clause BSD" - - -class Hydra(object): - """ - Facade to use Hydra default mode - - Parameters - ---------- - scenario : ~smac.scenario.scenario.Scenario - Scenario object - n_iterations: int, - number of Hydra iterations - val_set: str - Set to validate incumbent(s) on. [train, valX]. - train => whole training set, - valX => train_set * 100/X where X in (0, 100) - incs_per_round: int - Number of incumbents to keep per round - n_optimizers: int - Number of optimizers to run in parallel per round - rng: int/np.random.RandomState - The randomState/seed to pass to each smac run - run_id: int - run_id for this hydra run - tae: BaseRunner - Target Algorithm Runner (supports old and aclib format as well as AbstractTAFunc) - tae_kwargs: Optional[dict] - arguments passed to constructor of '~tae' - - Attributes - ---------- - logger - stats : Stats - loggs information about used resources - solver : SMBO - handles the actual algorithm calls - rh : RunHistory - List with information about previous runs - portfolio : list - List of all incumbents - - """ - - def __init__( - self, - scenario: typing.Type[Scenario], - n_iterations: int, - val_set: str = "train", - incs_per_round: int = 1, - n_optimizers: int = 1, - rng: typing.Optional[typing.Union[np.random.RandomState, int]] = None, - run_id: int = 1, - tae: typing.Type[BaseRunner] = ExecuteTARunOld, - tae_kwargs: typing.Union[dict, None] = None, - **kwargs, - ): - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - self.n_iterations = n_iterations - self.scenario = scenario - self.run_id, self.rng = get_rng(rng, run_id, self.logger) - self.kwargs = kwargs - self.output_dir = None - self.top_dir = None - self.solver = None - self.portfolio = None - self.rh = RunHistory() - self._tae = tae - self._tae_kwargs = tae_kwargs - if incs_per_round <= 0: - self.logger.warning("Invalid value in %s: %d. Setting to 1", "incs_per_round", incs_per_round) - self.incs_per_round = max(incs_per_round, 1) - if n_optimizers <= 0: - self.logger.warning("Invalid value in %s: %d. Setting to 1", "n_optimizers", n_optimizers) - self.n_optimizers = max(n_optimizers, 1) - self.val_set = self._get_validation_set(val_set) - self.cost_per_inst = {} - self.optimizer = None - self.portfolio_cost = None - - def _get_validation_set(self, val_set: str, delete: bool = True) -> typing.List[str]: - """ - Create small validation set for hydra to determine incumbent performance - - Parameters - ---------- - val_set: str - Set to validate incumbent(s) on. [train, valX]. - train => whole training set, - valX => train_set * 100/X where X in (0, 100) - delete: bool - Flag to delete all validation instances from the training set - - Returns - ------- - val: typing.List[str] - List of instance-ids to validate on - - """ - if val_set == "none": - return None - if val_set == "train": - return self.scenario.train_insts - elif val_set[:3] != "val": - self.logger.warning("Can not determine validation set size. Using full training-set!") - return self.scenario.train_insts - - size = int(val_set[3:]) / 100 - if size <= 0 or size >= 1: - raise ValueError("X invalid in valX, should be between 0 and 1") - insts = np.array(self.scenario.train_insts) - # just to make sure this also works with the small example we have to round up to 3 - size = max(np.floor(insts.shape[0] * size).astype(int), 3) - ids = np.random.choice(insts.shape[0], size, replace=False) - val = insts[ids].tolist() - if delete: - self.scenario.train_insts = np.delete(insts, ids).tolist() - - return val - - def optimize(self) -> typing.List[Configuration]: - """ - Optimizes the algorithm provided in scenario (given in constructor) - - Returns - ------- - portfolio : typing.List[Configuration] - Portfolio of found configurations - - """ - # Setup output directory - self.portfolio = [] - portfolio_cost = np.inf - if self.output_dir is None: - self.top_dir = "hydra-output_%s" % ( - datetime.datetime.fromtimestamp(time.time()).strftime("%Y-%m-%d_%H:%M:%S_%f") - ) - self.scenario.output_dir = os.path.join( - self.top_dir, - "psmac3-output_%s" % (datetime.datetime.fromtimestamp(time.time()).strftime("%Y-%m-%d_%H:%M:%S_%f")), - ) - self.output_dir = create_output_directory(self.scenario, run_id=self.run_id, logger=self.logger) - - scen = copy.deepcopy(self.scenario) - scen.output_dir_for_this_run = None - scen.output_dir = None - # parent process SMAC only used for validation purposes - self.solver = SMAC4AC( - scenario=scen, - tae_runner=self._tae, - rng=self.rng, - run_id=self.run_id, - **self.kwargs, - tae_runner_kwargs=self._tae_kwargs, - ) - for i in range(self.n_iterations): - self.logger.info("=" * 120) - self.logger.info("Hydra Iteration: %d", (i + 1)) - - if i == 0: - tae = self._tae - tae_kwargs = self._tae_kwargs - else: - tae = ExecuteTARunHydra - if self._tae_kwargs: - tae_kwargs = self._tae_kwargs - else: - tae_kwargs = {} - tae_kwargs["cost_oracle"] = self.cost_per_inst - self.optimizer = PSMAC( - scenario=self.scenario, - run_id=self.run_id, - rng=self.rng, - tae=tae, - tae_kwargs=tae_kwargs, - shared_model=False, - validate=True if self.val_set else False, - n_optimizers=self.n_optimizers, - val_set=self.val_set, - n_incs=self.n_optimizers, # return all configurations (unvalidated) - **self.kwargs, - ) - self.optimizer.output_dir = self.output_dir - incs = self.optimizer.optimize() - ( - cost_per_conf_v, - val_ids, - cost_per_conf_e, - est_ids, - ) = self.optimizer.get_best_incumbents_ids(incs) - if self.val_set: - to_keep_ids = val_ids[: self.incs_per_round] - else: - to_keep_ids = est_ids[: self.incs_per_round] - config_cost_per_inst = {} - incs = incs[to_keep_ids] - self.logger.info("Kept incumbents") - for inc in incs: - self.logger.info(inc) - config_cost_per_inst[inc] = cost_per_conf_v[inc] if self.val_set else cost_per_conf_e[inc] - - cur_portfolio_cost = self._update_portfolio(incs, config_cost_per_inst) - if portfolio_cost <= cur_portfolio_cost: - self.logger.info("No further progress (%f) --- terminate hydra", portfolio_cost) - break - else: - portfolio_cost = cur_portfolio_cost - self.logger.info("Current pertfolio cost: %f", portfolio_cost) - - self.scenario.output_dir = os.path.join( - self.top_dir, - "psmac3-output_%s" % (datetime.datetime.fromtimestamp(time.time()).strftime("%Y-%m-%d_%H:%M:%S_%f")), - ) - self.output_dir = create_output_directory(self.scenario, run_id=self.run_id, logger=self.logger) - read( - self.rh, - os.path.join(self.top_dir, "psmac3*", "run_" + str(MAXINT)), - self.scenario.cs, - self.logger, - ) - self.rh.save_json(fn=os.path.join(self.top_dir, "all_validated_runs_runhistory.json"), save_external=True) - with open(os.path.join(self.top_dir, "portfolio.pkl"), "wb") as fh: - pickle.dump(self.portfolio, fh) - self.logger.info("~" * 120) - self.logger.info("Resulting Portfolio:") - for configuration in self.portfolio: - self.logger.info(str(configuration)) - self.logger.info("~" * 120) - - return self.portfolio - - def _update_portfolio(self, incs: np.ndarray, config_cost_per_inst: typing.Dict) -> typing.Union[float, float]: - """ - Validates all configurations (in incs) and determines which ones to add to the portfolio - - Parameters - ---------- - incs: np.ndarray - List of Configurations - - Returns - ------- - cur_cost: typing.Union[float, float] - The current cost of the portfolio - - """ - if self.val_set: # we have validated data - for kept in incs: - if kept not in self.portfolio: - self.portfolio.append(kept) - cost_per_inst = config_cost_per_inst[kept] - if self.cost_per_inst: - if len(self.cost_per_inst) != len(cost_per_inst): - raise ValueError("Num validated Instances mismatch!") - else: - for key in cost_per_inst: - self.cost_per_inst[key] = min(self.cost_per_inst[key], cost_per_inst[key]) - else: - self.cost_per_inst = cost_per_inst - cur_cost = np.mean(list(self.cost_per_inst.values())) # type: float - else: # No validated data. Set the mean to the approximated mean - means = [] # can contain nans as not every instance was evaluated thus we should use nanmean to approximate - for kept in incs: - means.append(np.nanmean(list(self.optimizer.rh.get_instance_costs_for_config(kept).values()))) - self.portfolio.append(kept) - if self.portfolio_cost: - new_mean = self.portfolio_cost * (len(self.portfolio) - len(incs)) / len(self.portfolio) - new_mean += np.nansum(means) - else: - new_mean = np.mean(means) - self.cost_per_inst = defaultdict(lambda: new_mean) - cur_cost = new_mean - - self.portfolio_cost = cur_cost - return cur_cost diff --git a/smac/facade/func_facade.py b/smac/facade/func_facade.py deleted file mode 100644 index 3ae4b434e..000000000 --- a/smac/facade/func_facade.py +++ /dev/null @@ -1,115 +0,0 @@ -from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple, Union - -import logging - -import numpy as np -from ConfigSpace.hyperparameters import UniformFloatHyperparameter # type: ignore - -from smac.configspace import Configuration, ConfigurationSpace -from smac.facade.smac_hpo_facade import SMAC4HPO -from smac.runhistory.runhistory import RunKey -from smac.scenario.scenario import Scenario -from smac.tae.execute_func import ExecuteTAFuncArray - -__author__ = "Marius Lindauer, Matthias Feurer" -__copyright__ = "Copyright 2016, ML4AAD" -__license__ = "3-clause BSD" - - -def fmin_smac( - func: Callable, - x0: List[float], - bounds: List[Iterable[float]], - maxfun: int = -1, - rng: Optional[Union[np.random.RandomState, int]] = None, - scenario_args: Optional[Mapping[str, Any]] = None, - tae_runner_kwargs: Optional[Dict[str, Any]] = None, - **kwargs: Any, -) -> Tuple[Configuration, Union[np.ndarray, float], SMAC4HPO]: - """Minimize a function func using the SMAC4HPO facade (i.e., a modified version of SMAC). This - function is a convenience wrapper for the SMAC4HPO class. - - Parameters - ---------- - func : Callable - Function to minimize. - x0 : List[float] - Initial guess/default configuration. - bounds : List[List[float]] - ``(min, max)`` pairs for each element in ``x``, defining the bound on - that parameters. - maxfun : int, optional - Maximum number of function evaluations. - rng : np.random.RandomState, optional - Random number generator used by SMAC. - scenario_args: Mapping[str,Any] - Arguments passed to the scenario - See smac.scenario.scenario.Scenario - **kwargs: - Arguments passed to the optimizer class - See ~smac.facade.smac_facade.SMAC - - Returns - ------- - x : list - Estimated position of the minimum. - f : Union[np.ndarray, float] - Value of `func` at the minimum. Depending on the scenario_args, it could be a scalar value - (for single objective problems) or a np.ndarray (for multi objective problems). - s : :class:`smac.facade.smac_hpo_facade.SMAC4HPO` - SMAC objects which enables the user to get - e.g., the trajectory and runhistory. - """ - # create configuration space - cs = ConfigurationSpace() - - # Adjust zero padding - tmplt = "x{0:0" + str(len(str(len(bounds)))) + "d}" - - for idx, (lower_bound, upper_bound) in enumerate(bounds): - parameter = UniformFloatHyperparameter( - name=tmplt.format(idx + 1), - lower=lower_bound, - upper=upper_bound, - default_value=x0[idx], - ) - cs.add_hyperparameter(parameter) - - # create scenario - scenario_dict = { - "run_obj": "quality", - "cs": cs, - "deterministic": True, - "initial_incumbent": "DEFAULT", - } - - if scenario_args is not None: - scenario_dict.update(scenario_args) - - if maxfun > 0: - scenario_dict["runcount_limit"] = maxfun - scenario = Scenario(scenario_dict) - - # Handle optional tae arguments - if tae_runner_kwargs is not None: - if "ta" not in tae_runner_kwargs: - tae_runner_kwargs.update({"ta": func}) - else: - tae_runner_kwargs = {"ta": func} - - smac = SMAC4HPO( - scenario=scenario, - tae_runner=ExecuteTAFuncArray, - tae_runner_kwargs=tae_runner_kwargs, - rng=rng, - **kwargs, - ) - - smac.logger = logging.getLogger(smac.__module__ + "." + smac.__class__.__name__) - incumbent = smac.optimize() - config_id = smac.solver.runhistory.config_ids[incumbent] - run_key = RunKey(config_id, None, 0) - incumbent_performance = smac.solver.runhistory.data[run_key] - incumbent = np.array([incumbent[tmplt.format(idx + 1)] for idx in range(len(bounds))], dtype=float) - - return incumbent, incumbent_performance.cost, smac diff --git a/smac/facade/hyperband_facade.py b/smac/facade/hyperband_facade.py index ae0b489ba..5fa54c4bd 100644 --- a/smac/facade/hyperband_facade.py +++ b/smac/facade/hyperband_facade.py @@ -1,49 +1,37 @@ -from typing import Any +from __future__ import annotations -from smac.facade.roar_facade import ROAR -from smac.initial_design.random_configuration_design import RandomConfigurations -from smac.intensification.hyperband import Hyperband +from smac.facade.random_facade import RandomFacade +from smac.intensifier.hyperband import Hyperband +from smac.scenario import Scenario -__author__ = "Ashwin Raaghav Narayanan" -__copyright__ = "Copyright 2019, ML4AAD" +__copyright__ = "Copyright 2022, automl.org" __license__ = "3-clause BSD" -class HB4AC(ROAR): - """Facade to use model-free Hyperband for algorithm configuration. - - This facade overwrites options available via the SMAC facade. - - See Also - -------- - :class:`~smac.facade.smac_ac_facade.SMAC4AC` for documentation of parameters. - - Attributes - ---------- - logger - stats : Stats - solver : SMBO - runhistory : RunHistory - List with information about previous runs - trajectory : list - List of all incumbents +class HyperbandFacade(RandomFacade): """ + Facade to use model-free Hyperband [LJDR18]_ for algorithm configuration. - def __init__(self, **kwargs: Any): - kwargs["initial_design"] = kwargs.get("initial_design", RandomConfigurations) - - # Intensification parameters - # select Hyperband as the intensifier ensure respective parameters are provided - kwargs["intensifier"] = Hyperband - - # set Hyperband parameters if not given - intensifier_kwargs = kwargs.get("intensifier_kwargs", dict()) - intensifier_kwargs["min_chall"] = 1 - if intensifier_kwargs.get("eta") is None: - intensifier_kwargs["eta"] = 3 - if intensifier_kwargs.get("instance_order") is None: - intensifier_kwargs["instance_order"] = "shuffle_once" - kwargs["intensifier_kwargs"] = intensifier_kwargs + Uses Random Aggressive Online Racing (ROAR) to compare configurations, a random + initial design and the Hyperband intensifier. + """ - super().__init__(**kwargs) - self.logger.info(self.__class__) + @staticmethod + def get_intensifier( # type: ignore + scenario: Scenario, + *, + min_challenger: int = 1, + eta: int = 3, + ) -> Hyperband: + """Returns a Hyperband intensifier instance. That means that budgets are supported. + + min_challenger : int, defaults to 1 + Minimal number of challengers to be considered (even if time_bound is exhausted earlier). + eta : float, defaults to 3 + The "halving" factor after each iteration in a Successive Halving run. + """ + return Hyperband( + scenario=scenario, + min_challenger=min_challenger, + eta=eta, + ) diff --git a/smac/facade/hyperparameter_optimization_facade.py b/smac/facade/hyperparameter_optimization_facade.py new file mode 100644 index 000000000..f87e01899 --- /dev/null +++ b/smac/facade/hyperparameter_optimization_facade.py @@ -0,0 +1,220 @@ +from __future__ import annotations + +from ConfigSpace import Configuration + +from smac.acquisition.function.expected_improvement import EI +from smac.acquisition.maximizer.local_and_random_search import ( + LocalAndSortedRandomSearch, +) +from smac.facade.abstract_facade import AbstractFacade +from smac.initial_design.sobol_design import SobolInitialDesign +from smac.intensifier.intensifier import Intensifier +from smac.model.random_forest.random_forest import RandomForest +from smac.multi_objective.aggregation_strategy import MeanAggregationStrategy +from smac.random_design.probability_design import ProbabilityRandomDesign +from smac.runhistory.encoder.log_scaled_encoder import RunHistoryLogScaledEncoder +from smac.scenario import Scenario + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class HyperparameterOptimizationFacade(AbstractFacade): + @staticmethod + def get_model( # type: ignore + scenario: Scenario, + *, + n_trees: int = 10, + ratio_features: float = 1.0, + min_samples_split: int = 2, + min_samples_leaf: int = 1, + max_depth: int = 2**20, + bootstrapping: bool = True, + ) -> RandomForest: + """Returns a random forest as surrogate model. + + Parameters + ---------- + n_trees : int, defaults to 10 + The number of trees in the random forest. + ratio_features : float, defaults to 5.0 / 6.0 + The ratio of features that are considered for splitting. + min_samples_split : int, defaults to 3 + The minimum number of data points to perform a split. + min_samples_leaf : int, defaults to 3 + The minimum number of data points in a leaf. + max_depth : int, defaults to 20 + The maximum depth of a single tree. + bootstrapping : bool, defaults to True + Enables bootstrapping. + """ + return RandomForest( + log_y=True, + n_trees=n_trees, + bootstrapping=bootstrapping, + ratio_features=ratio_features, + min_samples_split=min_samples_split, + min_samples_leaf=min_samples_leaf, + max_depth=max_depth, + configspace=scenario.configspace, + instance_features=scenario.instance_features, + seed=scenario.seed, + ) + + @staticmethod + def get_acquisition_function( # type: ignore + scenario: Scenario, + *, + xi: float = 0.0, + ) -> EI: + """Returns an Expected Improvement acquisition function. + + Parameters + ---------- + scenario : Scenario + xi : float, defaults to 0.0 + Controls the balance between exploration and exploitation of the + acquisition function. + """ + return EI(xi=xi, log=True) + + @staticmethod + def get_acquisition_maximizer( # type: ignore + scenario: Scenario, + *, + challengers: int = 10000, + local_search_iterations: int = 10, + ) -> LocalAndSortedRandomSearch: + """Returns local and sorted random search as acquisition maximizer. + + Warning + ------- + If you experience RAM issues, try to reduce the number of challengers. + + Parameters + ---------- + challengers : int, defaults to 10000 + Number of challengers. + local_search_iterations: int, defauts to 10 + Number of local search iterations. + """ + optimizer = LocalAndSortedRandomSearch( + scenario.configspace, + challengers=challengers, + local_search_iterations=local_search_iterations, + seed=scenario.seed, + ) + + return optimizer + + @staticmethod + def get_intensifier( # type: ignore + scenario: Scenario, + *, + min_challenger: int = 1, + min_config_calls: int = 1, + max_config_calls: int = 3, + intensify_percentage: float = 1e-10, + ) -> Intensifier: + """Returns ``Intensifier`` as intensifier. Uses the default configuration for ``race_against``. + + Parameters + ---------- + scenario : Scenario + min_config_calls : int, defaults to 1 + Minimum number of trials per config (summed over all calls to intensify). + max_config_calls : int, defaults to 1 + Maximum number of trials per config (summed over all calls to intensify). + min_challenger : int, defaults to 3 + Minimal number of challengers to be considered (even if time_bound is exhausted earlier). + intensify_percentage : float, defaults to 1e-10 + How much percentage of the time should configurations be intensified (evaluated on higher budgets or + more instances). This parameter is accessed in the SMBO class. + """ + intensifier = Intensifier( + scenario=scenario, + min_challenger=min_challenger, + race_against=scenario.configspace.get_default_configuration(), + min_config_calls=min_config_calls, + max_config_calls=max_config_calls, + intensify_percentage=intensify_percentage, + ) + + return intensifier + + @staticmethod + def get_initial_design( # type: ignore + scenario: Scenario, + *, + n_configs: int | None = None, + n_configs_per_hyperparamter: int = 10, + max_ratio: float = 0.1, + additional_configs: list[Configuration] = [], + ) -> SobolInitialDesign: + """Returns a Sobol design instance. + + Parameters + ---------- + scenario : Scenario + n_configs : int | None, defaults to None + Number of initial configurations (disables the arguments ``n_configs_per_hyperparameter``). + n_configs_per_hyperparameter: int, defaults to 10 + Number of initial configurations per hyperparameter. For example, if my configuration space covers five + hyperparameters and ``n_configs_per_hyperparameter`` is set to 10, then 50 initial configurations will be + samples. + max_ratio: float, defaults to 0.1 + Use at most ``scenario.n_trials`` * ``max_ratio`` number of configurations in the initial design. + Additional configurations are not affected by this parameter. + additional_configs: list[Configuration], defaults to [] + Adds additional configurations to the initial design. + """ + return SobolInitialDesign( + scenario=scenario, + n_configs=n_configs, + n_configs_per_hyperparameter=n_configs_per_hyperparamter, + max_ratio=max_ratio, + additional_configs=additional_configs, + ) + + @staticmethod + def get_random_design( # type: ignore + scenario: Scenario, + *, + probability: float = 0.2, + ) -> ProbabilityRandomDesign: + """Returns ``ProbabilityRandomDesign`` for interleaving configurations. + + Parameters + ---------- + probability : float, defaults to 0.2 + Probability that a configuration will be drawn at random. + """ + return ProbabilityRandomDesign(probability=probability) + + @staticmethod + def get_multi_objective_algorithm( # type: ignore + scenario: Scenario, + *, + objective_weights: list[float] | None = None, + ) -> MeanAggregationStrategy: + """Returns the mean aggregation strategy for the multi objective algorithm. + + Parameters + ---------- + scenario : Scenario + objective_weights : list[float] | None, defaults to None + Weights for an weighted average. Must be of the same length as the number of objectives. + """ + return MeanAggregationStrategy( + scenario=scenario, + objective_weights=objective_weights, + ) + + @staticmethod + def get_runhistory_encoder( # type: ignore + scenario: Scenario, + ) -> RunHistoryLogScaledEncoder: + """Returns a log scaled runhistory encoder. That means that costs are log scaled before + training the surrogate model. + """ + return RunHistoryLogScaledEncoder(scenario) diff --git a/smac/facade/multi_fidelity_facade.py b/smac/facade/multi_fidelity_facade.py new file mode 100644 index 000000000..5ef5e8b6e --- /dev/null +++ b/smac/facade/multi_fidelity_facade.py @@ -0,0 +1,103 @@ +from __future__ import annotations + +from ConfigSpace import Configuration + +from smac.facade.hyperparameter_optimization_facade import ( + HyperparameterOptimizationFacade, +) +from smac.initial_design.random_design import RandomInitialDesign +from smac.intensifier.hyperband import Hyperband +from smac.scenario import Scenario + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class MultiFidelityFacade(HyperparameterOptimizationFacade): + """ + This facade configures SMAC in a multi-fidelity setting. + The way this facade combines the components is the following and exploits + fidelity information in the following form: + + 1. The initial design is a RandomInitialDesign. + 2. The intensification is Hyperband. The configurations from the initial design + are presented as challengers and executed in the Hyperband fashion. + 3. The model is a RandomForest surrogate model. The data to train it is collected by ``SMBO._collect_data``. + Notably, the method searches through the runhistory and collects the data from the highest fidelity level, + that supports at least ``SMBO._min_samples_model`` number of configurations. + 4. The acquisition function is ``EI``, presenting the value of a candidate configuration. + 5. The acquisition optimizer is ``LocalAndSortedRandomSearch``. It optimizes the acquisition + function to present the best configuration as challenger to the intensifier. + From now on 2. works as follows: The intensifier runs the challenger in a Hyperband fashion against the existing + configurations and their observed performances until the challenger does not survive a fidelity level. The + intensifier can inquire about a known configuration on a yet unseen fidelity if necessary. + + The loop 2-5 continues until a termination criterion is reached. + + Note + ---- + For intensification the data acquisition and aggregation strategy in step 2 is changed. + Incumbents are updated by the mean performance over the intersection of instances, that + the challenger and incumbent have in common (``abstract_intensifier._compare_configs``). + The model in step 3 is trained on all the available instance performance values. + The datapoints for a hyperparameter configuration are disambiguated by the instance features + or an index as replacement if no instance features are available. + """ + + @staticmethod + def get_intensifier( # type: ignore + scenario: Scenario, + *, + eta: int = 3, + min_challenger: int = 1, + n_seeds: int = 1, + ) -> Hyperband: + """Returns a Hyperband intensifier instance. That means that budgets are supported. + + min_challenger : int, defaults to 1 + Minimal number of challengers to be considered (even if time_bound is exhausted earlier). + eta : float, defaults to 3 + The "halving" factor after each iteration in a Successive Halving run. + n_seeds : int | None, defaults to None + The number of seeds to use if the target function is non-deterministic. + """ + return Hyperband( + scenario=scenario, + eta=eta, + min_challenger=min_challenger, + n_seeds=n_seeds, + ) + + @staticmethod + def get_initial_design( # type: ignore + scenario: Scenario, + *, + n_configs: int | None = None, + n_configs_per_hyperparamter: int = 10, + max_ratio: float = 0.1, + additional_configs: list[Configuration] = [], + ) -> RandomInitialDesign: + """Returns a random initial design. + + Parameters + ---------- + scenario : Scenario + n_configs : int | None, defaults to None + Number of initial configurations (disables the arguments ``n_configs_per_hyperparameter``). + n_configs_per_hyperparameter: int, defaults to 10 + Number of initial configurations per hyperparameter. For example, if my configuration space covers five + hyperparameters and ``n_configs_per_hyperparameter`` is set to 10, then 50 initial configurations will be + samples. + max_ratio: float, defaults to 0.1 + Use at most ``scenario.n_trials`` * ``max_ratio`` number of configurations in the initial design. + Additional configurations are not affected by this parameter. + additional_configs: list[Configuration], defaults to [] + Adds additional configurations to the initial design. + """ + return RandomInitialDesign( + scenario=scenario, + n_configs=n_configs, + n_configs_per_hyperparameter=n_configs_per_hyperparamter, + max_ratio=max_ratio, + additional_configs=additional_configs, + ) diff --git a/smac/facade/psmac_facade.py b/smac/facade/psmac_facade.py deleted file mode 100644 index cd688ff02..000000000 --- a/smac/facade/psmac_facade.py +++ /dev/null @@ -1,348 +0,0 @@ -# type: ignore -# mypy: ignore-errors - -from typing import Any, Dict, List, Optional, Type, Union - -import copy -import datetime -import logging -import os -import time -from pathlib import Path - -import joblib -import numpy as np -from ConfigSpace.configuration_space import Configuration - -from smac.epm.utils import get_rng -from smac.facade.smac_ac_facade import SMAC4AC -from smac.runhistory.runhistory import RunHistory -from smac.scenario.scenario import Scenario -from smac.tae.base import BaseRunner -from smac.tae.execute_ta_run_hydra import ExecuteTARunOld -from smac.utils.io.output_directory import create_output_directory -from smac.utils.io.result_merging import ResultMerger - -__author__ = "Andre Biedenkapp" -__copyright__ = "Copyright 2018, ML4AAD" -__license__ = "3-clause BSD" - - -def optimize( - scenario: Scenario, - tae_runner: Type[BaseRunner], - tae_runner_kwargs: Dict, - rng: Union[np.random.RandomState, int], - output_dir: str, - facade_class: Optional[Type[SMAC4AC]] = None, - **kwargs, -) -> Configuration: - """ - Unbound method to be called in a subprocess - - Parameters - ---------- - scenario: Scenario - smac.Scenario to initialize SMAC - tae_runner: BaseRunner - Target Algorithm Runner (supports old and aclib format) - tae_runner_kwargs: Optional[dict] - arguments passed to constructor of '~tae' - rng: int/np.random.RandomState - The randomState/seed to pass to each smac run - output_dir: str - The directory in which each smac run should write its results - - Returns - ------- - incumbent: Configuration - The incumbent configuration of this run - - """ - if facade_class is None: - facade_class = SMAC4AC - solver = facade_class( - scenario=scenario, tae_runner=tae_runner, tae_runner_kwargs=tae_runner_kwargs, rng=rng, **kwargs - ) - solver.stats.start_timing() - solver.stats.print_stats() - - incumbent = solver.solver.run() - solver.stats.print_stats() - - if output_dir is not None: - solver.solver.runhistory.save_json(fn=os.path.join(solver.output_dir, "runhistory.json")) - return incumbent - - -class PSMAC(object): - """ - Facade to use pSMAC [1]_ - - With pSMAC you can either run n distinct SMAC optimizations in parallel - (`shared_model=False`) or you can parallelize the target algorithm evaluations - (`shared_model=True`). - In the latter case all SMAC workers share one file directory and communicate via - the logfiles. You can specify the number of SMAC workers/optimizers with the - argument `n_workers`. - - You can pass all other kwargs for the SMAC4AC facade. - In addition, you can access the facade's attributes normally (e.g. smac.stats), - however each time a new SMAC object is built, reading the information from the - file system. - - - .. [1] Ramage, S. E. A. (2015). Advances in meta-algorithmic software libraries for - distributed automated algorithm configuration (T). University of British - Columbia. Retrieved from - https://open.library.ubc.ca/collections/ubctheses/24/items/1.0167184. - - Parameters - ---------- - scenario : ~smac.scenario.scenario.Scenario - Scenario object. Note that the budget/number of evaluations (runcount-limit) is - used for each worker. So if you specify 40 evaluations and 3 workers, 120 - configurations will be evaluated in total. - n_workers: int - Number of optimizers to run in parallel per round - rng: int/np.random.RandomState - The randomState/seed to pass to each smac run - run_id: int - run_id for this hydra run - tae_runner: BaseRunner - Target Algorithm Runner (supports old and aclib format as well as AbstractTAFunc) - tae_runner_kwargs: Optional[dict] - arguments passed to constructor of '~tae_runner' - shared_model: bool - Flag to indicate whether information is shared between SMAC runs or not - validate: bool / None - Flag to indicate whether to validate the found configurations or to use the SMAC estimates - None => neither and return the full portfolio - val_set: List[str] - List of instance-ids to validate on - **kwargs - Keyword arguments for the SMAC4AC facade - - Attributes - ---------- - # TODO update attributes - logger - stats : Stats - loggs information about used resources - solver : SMBO - handles the actual algorithm calls - rh : RunHistory - List with information about previous runs - portfolio : list - List of all incumbents - - """ - - def __init__( - self, - scenario: Scenario, - rng: Optional[Union[np.random.RandomState, int]] = None, - run_id: int = 1, - tae_runner: Type[BaseRunner] = ExecuteTARunOld, - tae_runner_kwargs: Union[dict, None] = None, - shared_model: bool = True, - facade_class: Optional[Type[SMAC4AC]] = None, - validate: bool = True, - n_workers: int = 2, - val_set: Union[List[str], None] = None, - **kwargs, - ): - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - self.scenario = scenario - self.run_id, self.rng = get_rng(rng, run_id, logger=self.logger) - self.kwargs = kwargs - self.output_dir = None - if facade_class is None: - facade_class = SMAC4AC - self.facade_class = facade_class - self.rh = RunHistory() - self._tae_runner = tae_runner - self._tae_runner_kwargs = tae_runner_kwargs - if n_workers <= 1: - self.logger.warning("Invalid value in %s: %d. Setting to 2", "n_workers", n_workers) - self.n_workers = max(n_workers, 2) - self.seeds = np.arange(0, self.n_workers, dtype=int) # seeds for the parallel runs - self.validate = validate - self.shared_model = shared_model - if val_set is None: - self.val_set = scenario.train_insts - else: - self.val_set = val_set - - self.result_merger: Optional[ResultMerger] = None - self.n_incs: int = 1 - - def optimize(self) -> Configuration: - """ - Optimizes the algorithm provided in scenario (given in constructor) - - Returns - ------- - incumbent : Configuration - Best configuration across all workers. - - """ - # Setup output directory - if self.output_dir is None: - self.scenario.output_dir = "psmac3-output_%s" % ( - datetime.datetime.fromtimestamp(time.time()).strftime("%Y-%m-%d_%H:%M:%S_%f") - ) - self.output_dir = create_output_directory(self.scenario, run_id=self.run_id, logger=self.logger) - if self.shared_model: - self.scenario.shared_model = self.shared_model - if self.scenario.input_psmac_dirs is None: - self.scenario.input_psmac_dirs = os.path.sep.join((self.scenario.output_dir, "run_*")) - - scen = copy.deepcopy(self.scenario) - scen.output_dir_for_this_run = None - scen.output_dir = None - self.logger.info("+" * 120) - self.logger.info("PSMAC run") - - incs = joblib.Parallel(n_jobs=self.n_workers)( - joblib.delayed(optimize)( - scenario=self.scenario, # Scenario object - tae_runner=self._tae_runner, # type of tae_runner to run target with - tae_runner_kwargs=self._tae_runner_kwargs, - rng=int(seed), # seed for the rng/run_id - output_dir=self.output_dir, # directory to create outputs in - facade_class=self.facade_class, - **self.kwargs, - ) - for seed in self.seeds - ) - - inc = self.get_best_incumbent(incs=incs) - return inc - - def get_best_incumbent(self, incs: List[Configuration]) -> Configuration: - """ - Determine ID and cost of best configuration (incumbent). - - Parameters - ---------- - incs : List[Configuration] - List of incumbents from the workers. - - Returns - ------- - Configuration - Best incumbent from all workers. - - """ - if self.validate is True: - mean_costs_conf_valid, cost_per_config_valid = self.validate_incs(incs) - val_id = list(map(lambda x: x[0], sorted(enumerate(mean_costs_conf_valid), key=lambda y: y[1])))[0] - inc = incs[val_id] - else: - mean_costs_conf_estimate, cost_per_config_estimate = self._get_mean_costs(incs, self.rh) - est_id = list(map(lambda x: x[0], sorted(enumerate(mean_costs_conf_estimate), key=lambda y: y[1])))[0] - inc = incs[est_id] - - return inc - - def _get_mean_costs(self, incs: List[Configuration], new_rh: RunHistory): - """ - Compute mean cost per instance - - Parameters - ---------- - incs : List[Configuration] - incumbents determined by all parallel SMAC runs - new_rh : RunHistory - runhistory to determine mean performance - - Returns - ------- - List[float] means - Dict(Config -> Dict(inst_id(str) -> float)) - - """ - config_cost_per_inst = {} - results = [] - for incumbent in incs: - cost_per_inst = new_rh.get_instance_costs_for_config(config=incumbent) - config_cost_per_inst[incumbent] = cost_per_inst - values = list(cost_per_inst.values()) - if values: - results.append(np.mean(values)) - else: - results.append(np.nan) - return results, config_cost_per_inst - - def _get_solver(self): - # TODO: specify one output dir or no output dir - solver = self.facade_class(scenario=self.scenario, rng=self.rng, run_id=None, **self.kwargs) - return solver - - def validate_incs(self, incs: List[Configuration]): - """Validation of the incumbents.""" - solver = self._get_solver() - self.logger.info("*" * 120) - self.logger.info("Validating") - new_rh = solver.validate( - config_mode=incs, - instance_mode=self.val_set, - repetitions=1, - use_epm=False, - n_jobs=self.n_workers, - ) - return self._get_mean_costs(incs, new_rh) - - def write_run(self) -> None: - """ - Write all SMAC files to pSMAC dir - - Returns - ------- - None - - """ - # write runhistory - # write configspace file .pcs .json - # write trajectory traj.json - # write scenario .txt - # write stats - raise NotImplementedError - - def _check_result_merger(self): - if self.result_merger is None: - if self.output_dir is None: - raise ValueError( - "Cannot instantiate `ResultMerger` because `output_dir` " - "is None. In pSMAC `output_dir` is set after " - "`optimize()` has been called. If you already have " - "a pSMAC run or rundirs, please directly use " - "`smac.utils.io.result_merging.ResultMerger`." - ) - self.result_merger = ResultMerger(output_dir=Path(self.output_dir).parent) - - def get_runhistory(self) -> Optional[RunHistory]: - """ - Get merged runhistory from pSMAC workers - - Returns - ------- - Optional[RunHistory] - - """ - self._check_result_merger() - return self.result_merger.get_runhistory() - - def get_trajectory(self) -> Optional[List[Dict[str, Any]]]: - """ - Get trajectory from merged runhistory - - Returns - ------- - Optional[List[Dict[str, Any]]] - - """ - self._check_result_merger() - return self.result_merger.get_trajectory() diff --git a/smac/facade/random_facade.py b/smac/facade/random_facade.py new file mode 100644 index 000000000..0c193b60e --- /dev/null +++ b/smac/facade/random_facade.py @@ -0,0 +1,161 @@ +from __future__ import annotations + +import numpy as np +from ConfigSpace import Configuration + +from smac.acquisition.function.abstract_acquisition_function import ( + AbstractAcquisitionFunction, +) +from smac.acquisition.maximizer.random_search import RandomSearch +from smac.facade.abstract_facade import AbstractFacade +from smac.initial_design.default_design import DefaultInitialDesign +from smac.intensifier.intensifier import Intensifier +from smac.model.random_model import RandomModel +from smac.multi_objective.aggregation_strategy import MeanAggregationStrategy +from smac.random_design import AbstractRandomDesign +from smac.runhistory.encoder.encoder import RunHistoryEncoder +from smac.scenario import Scenario + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class RandomFacade(AbstractFacade): + """ + Facade to use Random Online Aggressive Racing (ROAR). + + *Aggressive Racing:* + When we have a new configuration θ, we want to compare it to the current best + configuration, the incumbent θ*. ROAR uses the 'racing' approach, where we run few times for unpromising θ and many + times for promising configurations. Once we are confident enough that θ is better than θ*, we update the + incumbent θ* ⟵ θ. `Aggressive` means rejecting low-performing configurations very early, often after a single run. + This together is called `aggressive racing`. + + *ROAR Loop:* + The main ROAR loop looks as follows: + + 1. Select a configuration θ uniformly at random. + 2. Compare θ to incumbent θ* online (one θ at a time): + - Reject/accept θ with `aggressive racing` + + *Setup:* + Uses a random model and random search for the optimization of the acquisition function. + + Note + ---- + The surrogate model and the acquisition function is not used during the optimization and therefore replaced + by dummies. + """ + + @staticmethod + def get_acquisition_function(scenario: Scenario) -> AbstractAcquisitionFunction: + """The random facade is not using an acquisition function. Therefore, we simply return a dummy function.""" + + class DummyAcquisitionFunction(AbstractAcquisitionFunction): + def _compute(self, X: np.ndarray) -> np.ndarray: + return X + + return DummyAcquisitionFunction() + + @staticmethod + def get_intensifier( + scenario: Scenario, + *, + min_challenger: int = 1, + min_config_calls: int = 1, + max_config_calls: int = 3, + intensify_percentage: float = 0.5, + ) -> Intensifier: + """Returns ``Intensifier`` as intensifier. Uses the default configuration for ``race_against``. + + Note + ---- + Please use ``HyperbandFacade`` if you want to incorporate budgets. + + Warning + ------- + If you are in an algorithm configuration setting, consider increasing ``max_config_calls``. + """ + intensifier = Intensifier( + scenario=scenario, + min_challenger=min_challenger, + race_against=scenario.configspace.get_default_configuration(), + min_config_calls=min_config_calls, + max_config_calls=max_config_calls, + intensify_percentage=intensify_percentage, + ) + + return intensifier + + @staticmethod + def get_initial_design( + scenario: Scenario, + *, + additional_configs: list[Configuration] = [], + ) -> DefaultInitialDesign: + """Returns an initial design, which returns the default configuration. + + Parameters + ---------- + additional_configs: list[Configuration], defaults to [] + Adds additional configurations to the initial design. + """ + return DefaultInitialDesign( + scenario=scenario, + additional_configs=additional_configs, + ) + + @staticmethod + def get_random_design(scenario: Scenario) -> AbstractRandomDesign: + """Just like the acquisition function, we do not use a random design. Therefore, we return a dummy design.""" + + class DummyRandomDesign(AbstractRandomDesign): + def check(self, iteration: int) -> bool: + return True + + return DummyRandomDesign() + + @staticmethod + def get_model(scenario: Scenario) -> RandomModel: + """The model is used in the acquisition function. Since we do not use an acquisition function, we return a + dummy model (returning random values in this case). + """ + return RandomModel( + configspace=scenario.configspace, + instance_features=scenario.instance_features, + seed=scenario.seed, + ) + + @staticmethod + def get_acquisition_maximizer(scenario: Scenario) -> RandomSearch: + """We return ``RandomSearch`` as maximizer which samples configurations randomly from the configuration + space and therefore neither uses the acquisition function nor the model. + """ + return RandomSearch( + scenario.configspace, + seed=scenario.seed, + ) + + @staticmethod + def get_multi_objective_algorithm( # type: ignore + scenario: Scenario, + *, + objective_weights: list[float] | None = None, + ) -> MeanAggregationStrategy: + """Returns the mean aggregation strategy for the multi objective algorithm. + + Parameters + ---------- + scenario : Scenario + objective_weights : list[float] | None, defaults to None + Weights for an weighted average. Must be of the same length as the number of objectives. + """ + return MeanAggregationStrategy( + scenario=scenario, + objective_weights=objective_weights, + ) + + @staticmethod + def get_runhistory_encoder(scenario: Scenario) -> RunHistoryEncoder: + """Returns the default runhistory encoder.""" + return RunHistoryEncoder(scenario) diff --git a/smac/facade/roar_facade.py b/smac/facade/roar_facade.py deleted file mode 100644 index 7657abb86..000000000 --- a/smac/facade/roar_facade.py +++ /dev/null @@ -1,155 +0,0 @@ -from typing import Callable, Dict, List, Optional, Type, Union - -import logging - -import dask.distributed # type: ignore -import numpy as np - -from smac.configspace import Configuration -from smac.epm.random_epm import RandomEPM -from smac.facade.smac_ac_facade import SMAC4AC -from smac.initial_design.initial_design import InitialDesign -from smac.intensification.abstract_racer import AbstractRacer -from smac.multi_objective.abstract_multi_objective_algorithm import ( - AbstractMultiObjectiveAlgorithm, -) -from smac.optimizer.acquisition.maximizer import ( - AcquisitionFunctionMaximizer, - RandomSearch, -) -from smac.runhistory.runhistory import RunHistory -from smac.runhistory.runhistory2epm import ( - AbstractRunHistory2EPM, - RunHistory2EPM4Cost, - RunHistory2EPM4LogCost, -) -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae.base import BaseRunner - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2016, ML4AAD" -__license__ = "3-clause BSD" - - -class ROAR(SMAC4AC): - """Facade to use ROAR mode. - - Parameters - ---------- - scenario: smac.scenario.scenario.Scenario - Scenario object - tae_runner: smac.tae.base.BaseRunner or callable - Callable or implementation of - :class:`~smac.tae.base.BaseRunner`. In case a - callable is passed it will be wrapped by - :class:`~smac.tae.execute_func.ExecuteTAFuncDict`. - If not set, it will be initialized with the - :class:`~smac.tae.execute_ta_run_old.ExecuteTARunOld`. - tae_runner_kwargs: Optional[Dict] - arguments passed to constructor of '~tae_runner' - runhistory: RunHistory - Runhistory to store all algorithm runs - intensifier: AbstractRacer - intensification object to issue a racing to decide the current incumbent - intensifier_kwargs: Optional[Dict] - arguments passed to the constructor of '~intensifier' - acquisition_function_optimizer : ~smac.optimizer.ei_optimization.AcquisitionFunctionMaximizer - Object that implements the :class:`~smac.optimizer.ei_optimization.AcquisitionFunctionMaximizer`. - Will use :class:`smac.optimizer.ei_optimization.RandomSearch` if not set. Can be used - to perform random search over a fixed set of configurations. - acquisition_function_optimizer_kwargs: Optional[dict] - Arguments passed to constructor of `~acquisition_function_optimizer` - multi_objective_algorithm: Optional[Type["AbstractMultiObjectiveAlgorithm"]] - Class that implements multi objective logic. If None, will use: - smac.optimizer.multi_objective.aggregation_strategy.MeanAggregationStrategy - Multi objective only becomes active if the objective - specified in `~scenario.run_obj` is a List[str] with at least two entries. - multi_objective_kwargs: Optional[Dict] - Arguments passed to `~multi_objective_algorithm`. - initial_design : InitialDesign - initial sampling design - initial_design_kwargs: Optional[dict] - arguments passed to constructor of `~initial_design` - initial_configurations: List[Configuration] - list of initial configurations for initial design -- - cannot be used together with initial_design - stats: Stats - optional stats object - rng: Optional[int, np.random.RandomState] - Random number generator - run_id: Optional[int] - Run ID will be used as subfolder for output_dir. - dask_client : dask.distributed.Client - User-created dask client, can be used to start a dask cluster and then attach SMAC to it. - n_jobs : int, optional - Number of jobs. If > 1 or -1, this creates a dask client if ``dask_client`` is ``None``. Will - be ignored if ``dask_client`` is not ``None``. - If ``None``, this value will be set to 1, if ``-1``, this will be set to the number of cpu cores. - - Attributes - ---------- - logger - - See Also - -------- - :class:`~smac.facade.smac_ac_facade.SMAC4AC` - """ - - def __init__( - self, - scenario: Scenario, - tae_runner: Optional[Union[Type[BaseRunner], Callable]] = None, - tae_runner_kwargs: Optional[Dict] = None, - runhistory: RunHistory = None, - intensifier: Optional[Type[AbstractRacer]] = None, - intensifier_kwargs: Optional[Dict] = None, - acquisition_function_optimizer: Optional[Type[AcquisitionFunctionMaximizer]] = None, - acquisition_function_optimizer_kwargs: Optional[dict] = None, - multi_objective_algorithm: Optional[Type[AbstractMultiObjectiveAlgorithm]] = None, - multi_objective_kwargs: Optional[Dict] = None, - initial_design: Optional[Type[InitialDesign]] = None, - initial_design_kwargs: Optional[dict] = None, - initial_configurations: List[Configuration] = None, - stats: Stats = None, - rng: Optional[Union[int, np.random.RandomState]] = None, - run_id: Optional[int] = None, - dask_client: Optional[dask.distributed.Client] = None, - n_jobs: Optional[int] = 1, - ): - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - scenario.acq_opt_challengers = 1 # type: ignore[attr-defined] # noqa F821 - - if acquisition_function_optimizer is None: - acquisition_function_optimizer = RandomSearch - - if scenario.run_obj == "runtime": - # We need to do this to be on the same scale for imputation (although we only impute with a Random EPM) - runhistory2epm = RunHistory2EPM4LogCost # type: Type[AbstractRunHistory2EPM] - else: - runhistory2epm = RunHistory2EPM4Cost - - # use SMAC facade - super().__init__( - scenario=scenario, - tae_runner=tae_runner, - tae_runner_kwargs=tae_runner_kwargs, - runhistory=runhistory, - intensifier=intensifier, - intensifier_kwargs=intensifier_kwargs, - runhistory2epm=runhistory2epm, - multi_objective_algorithm=multi_objective_algorithm, - multi_objective_kwargs=multi_objective_kwargs, - initial_design=initial_design, - initial_design_kwargs=initial_design_kwargs, - initial_configurations=initial_configurations, - run_id=run_id, - acquisition_function_optimizer=acquisition_function_optimizer, - acquisition_function_optimizer_kwargs=acquisition_function_optimizer_kwargs, - model=RandomEPM, - rng=rng, - stats=stats, - dask_client=dask_client, - n_jobs=n_jobs, - ) diff --git a/smac/facade/smac_ac_facade.py b/smac/facade/smac_ac_facade.py deleted file mode 100644 index 557f4d9d0..000000000 --- a/smac/facade/smac_ac_facade.py +++ /dev/null @@ -1,833 +0,0 @@ -from typing import Any, Callable, Dict, List, Optional, Type, Union, cast - -import inspect -import logging - -import dask.distributed # type: ignore -import joblib # type: ignore -import numpy as np - -from smac.configspace import Configuration -from smac.epm.base_epm import BaseEPM -from smac.epm.multi_objective_epm import MultiObjectiveEPM - -# epm -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances -from smac.epm.random_forest.rfr_imputator import RFRImputator -from smac.epm.utils import get_rng, get_types -from smac.initial_design.default_configuration_design import DefaultConfiguration -from smac.initial_design.factorial_design import FactorialInitialDesign - -# Initial designs -from smac.initial_design.initial_design import InitialDesign -from smac.initial_design.latin_hypercube_design import LHDesign -from smac.initial_design.random_configuration_design import RandomConfigurations -from smac.initial_design.sobol_design import SobolDesign -from smac.intensification.abstract_racer import AbstractRacer -from smac.intensification.hyperband import Hyperband - -# intensification -from smac.intensification.intensification import Intensifier -from smac.intensification.successive_halving import SuccessiveHalving -from smac.multi_objective.abstract_multi_objective_algorithm import ( - AbstractMultiObjectiveAlgorithm, -) -from smac.multi_objective.aggregation_strategy import ( - AggregationStrategy, - MeanAggregationStrategy, -) -from smac.optimizer.acquisition import ( - EI, - EIPS, - AbstractAcquisitionFunction, - IntegratedAcquisitionFunction, - LogEI, - PriorAcquisitionFunction, -) -from smac.optimizer.acquisition.maximizer import ( - AcquisitionFunctionMaximizer, - LocalAndSortedPriorRandomSearch, - LocalAndSortedRandomSearch, -) -from smac.optimizer.configuration_chooser.random_chooser import ( - ChooserProb, - RandomChooser, -) - -# optimizer -from smac.optimizer.smbo import SMBO - -# runhistory -from smac.runhistory.runhistory import RunHistory -from smac.runhistory.runhistory2epm import ( - AbstractRunHistory2EPM, - RunHistory2EPM4Cost, - RunHistory2EPM4InvScaledCost, - RunHistory2EPM4LogCost, - RunHistory2EPM4LogScaledCost, -) -from smac.scenario.scenario import Scenario - -# stats and options -from smac.stats.stats import Stats -from smac.tae import StatusType - -# tae -from smac.tae.base import BaseRunner -from smac.tae.dask_runner import DaskParallelRunner -from smac.tae.execute_func import ExecuteTAFuncDict -from smac.tae.execute_ta_run_old import ExecuteTARunOld -from smac.utils.constants import MAXINT -from smac.utils.io.output_directory import create_output_directory -from smac.utils.io.traj_logging import TrajEntry, TrajLogger - -# utils -from smac.utils.logging import format_array - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2018, ML4AAD" -__license__ = "3-clause BSD" - - -class SMAC4AC(object): - """Facade to use SMAC default mode for Algorithm configuration. - - Parameters - ---------- - scenario : ~smac.scenario.scenario.Scenario - Scenario object - tae_runner : ~smac.tae.base.BaseRunner or callable - Callable or implementation of - :class:`~smac.tae.base.BaseRunner`. In case a - callable is passed it will be wrapped by - :class:`~smac.tae.execute_func.ExecuteTAFuncDict`. - If not set, it will be initialized with the - :class:`~smac.tae.execute_ta_run_old.ExecuteTARunOld`. - tae_runner_kwargs: Optional[Dict] - arguments passed to constructor of '~tae_runner' - runhistory : RunHistory - runhistory to store all algorithm runs - runhistory_kwargs : Optional[Dict] - arguments passed to constructor of runhistory. - We strongly advise against changing the aggregation function, - since it will break some code assumptions - intensifier : AbstractRacer - intensification object or class to issue a racing to decide the current - incumbent. Default: class `Intensifier` - intensifier_kwargs: Optional[Dict] - arguments passed to the constructor of '~intensifier' - acquisition_function : `~smac.optimizer.acquisition.AbstractAcquisitionFunction` - Class or object that implements the :class:`~smac.optimizer.acquisition.AbstractAcquisitionFunction`. - Will use :class:`~smac.optimizer.acquisition.EI` or :class:`~smac.optimizer.acquisition.LogEI` if not set. - `~acquisition_function_kwargs` is passed to the class constructor. - acquisition_function_kwargs : Optional[Dict] - dictionary to pass specific arguments to ~acquisition_function - integrate_acquisition_function : bool, default=False - Whether to integrate the acquisition function. Works only with models which can sample their - hyperparameters (i.e. GaussianProcessMCMC). - user_priors : bool, default=False - Whether to make use of user priors in the optimization procedure, using PriorAcquisitionFunction. - user_prior_kwargs : Optional[Dict] - Dictionary to pass specific arguments to optimization with prior, e.g. prior confidence parameter, - and the floor value for the prior (lowest possible value the prior can take). - acquisition_function_optimizer : ~smac.optimizer.ei_optimization.AcquisitionFunctionMaximizer - Object that implements the :class:`~smac.optimizer.ei_optimization.AcquisitionFunctionMaximizer`. - Will use :class:`smac.optimizer.ei_optimization.InterleavedLocalAndRandomSearch` if not set. - acquisition_function_optimizer_kwargs: Optional[dict] - Arguments passed to constructor of `~acquisition_function_optimizer` - model : BaseEPM - Model that implements train() and predict(). Will use a - :class:`~smac.epm.rf_with_instances.RandomForestWithInstances` if not set. - model_kwargs : Optional[dict] - Arguments passed to constructor of `~model` - runhistory2epm : ~smac.runhistory.runhistory2epm.RunHistory2EMP - Object that implements the AbstractRunHistory2EPM. If None, - will use :class:`~smac.runhistory.runhistory2epm.RunHistory2EPM4Cost` - if objective is cost or - :class:`~smac.runhistory.runhistory2epm.RunHistory2EPM4LogCost` - if objective is runtime. - runhistory2epm_kwargs: Optional[dict] - Arguments passed to the constructor of `~runhistory2epm` - multi_objective_algorithm: Optional[Type["AbstractMultiObjectiveAlgorithm"]] - Class that implements multi objective logic. If None, will use: - smac.optimizer.multi_objective.aggregation_strategy.MeanAggregationStrategy - Multi objective only becomes active if the objective - specified in `~scenario.run_obj` is a List[str] with at least two entries. - multi_objective_kwargs: Optional[Dict] - Arguments passed to `~multi_objective_algorithm`. - initial_design : InitialDesign - initial sampling design - initial_design_kwargs: Optional[dict] - arguments passed to constructor of `~initial_design` - initial_configurations : List[Configuration] - list of initial configurations for initial design -- - cannot be used together with initial_design - stats : Stats - optional stats object - rng : np.random.RandomState - Random number generator - restore_incumbent : Configuration - incumbent used if restoring to previous state - smbo_class : ~smac.optimizer.smbo.SMBO - Class implementing the SMBO interface which will be used to - instantiate the optimizer class. - smbo_kwargs : ~ Optional[Dict] - Arguments passed to the constructor of '~smbo' - run_id : int (optional) - Run ID will be used as subfolder for output_dir. If no ``run_id`` is given, a random ``run_id`` will be - chosen. - random_configuration_chooser : ~smac.optimizer.random_configuration_chooser.RandomConfigurationChooser - How often to choose a random configuration during the intensification procedure. - random_configuration_chooser_kwargs : Optional[dict] - arguments of constructor for `~random_configuration_chooser` - dask_client : dask.distributed.Client - User-created dask client, can be used to start a dask cluster and then attach SMAC to it. - n_jobs : int, optional - Number of jobs. If > 1 or -1, this creates a dask client if ``dask_client`` is ``None``. Will - be ignored if ``dask_client`` is not ``None``. - If ``None``, this value will be set to 1, if ``-1``, this will be set to the number of cpu cores. - - Attributes - ---------- - logger - stats : Stats - solver : SMBO - runhistory : RunHistory - List with information about previous runs - trajectory : list - List of all incumbents - """ - - def __init__( - self, - scenario: Scenario, - tae_runner: Optional[Union[Type[BaseRunner], Callable]] = None, - tae_runner_kwargs: Optional[Dict] = None, - runhistory: Optional[Union[Type[RunHistory], RunHistory]] = None, - runhistory_kwargs: Optional[Dict] = None, - intensifier: Optional[Type[AbstractRacer]] = None, - intensifier_kwargs: Optional[Dict] = None, - acquisition_function: Optional[Type[AbstractAcquisitionFunction]] = None, - acquisition_function_kwargs: Optional[Dict] = None, - integrate_acquisition_function: bool = False, - user_priors: bool = False, - user_prior_kwargs: Optional[Dict] = None, - acquisition_function_optimizer: Optional[Type[AcquisitionFunctionMaximizer]] = None, - acquisition_function_optimizer_kwargs: Optional[Dict] = None, - model: Optional[Type[BaseEPM]] = None, - model_kwargs: Optional[Dict] = None, - runhistory2epm: Optional[Type[AbstractRunHistory2EPM]] = None, - runhistory2epm_kwargs: Optional[Dict] = None, - multi_objective_algorithm: Optional[Type[AbstractMultiObjectiveAlgorithm]] = None, - multi_objective_kwargs: Optional[Dict] = None, - initial_design: Optional[Type[InitialDesign]] = None, - initial_design_kwargs: Optional[Dict] = None, - initial_configurations: Optional[List[Configuration]] = None, - stats: Optional[Stats] = None, - restore_incumbent: Optional[Configuration] = None, - rng: Optional[Union[np.random.RandomState, int]] = None, - smbo_class: Optional[Type[SMBO]] = None, - smbo_kwargs: Optional[Dict] = None, - run_id: Optional[int] = None, - random_configuration_chooser: Optional[Type[RandomChooser]] = None, - random_configuration_chooser_kwargs: Optional[Dict] = None, - dask_client: Optional[dask.distributed.Client] = None, - n_jobs: Optional[int] = 1, - ): - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - self.scenario = scenario - self.output_dir = "" - if not restore_incumbent: - # restore_incumbent is used by the CLI interface which provides a method for restoring a SMAC run given an - # output directory. This is the default path. - # initial random number generator - run_id, rng = get_rng(rng=rng, run_id=run_id, logger=self.logger) - self.output_dir = create_output_directory(scenario, run_id) - elif scenario.output_dir is not None: # type: ignore[attr-defined] # noqa F821 - run_id, rng = get_rng(rng=rng, run_id=run_id, logger=self.logger) - # output-directory is created in CLI when restoring from a - # folder. calling the function again in the facade results in two - # folders being created: run_X and run_X.OLD. if we are - # restoring, the output-folder exists already and we omit creating it, - # but set the self-output_dir to the dir. - # necessary because we want to write traj to new output-dir in CLI. - self.output_dir = cast(str, scenario.output_dir_for_this_run) # type: ignore[attr-defined] # noqa F821 - rng = cast(np.random.RandomState, rng) - - if ( - scenario.deterministic is True # type: ignore[attr-defined] # noqa F821 - and getattr(scenario, "tuner_timeout", None) is None - and scenario.run_obj == "quality" # type: ignore[attr-defined] # noqa F821 - ): - self.logger.info( - "Optimizing a deterministic scenario for quality without a tuner timeout - will make " - "SMAC deterministic and only evaluate one configuration per iteration!" - ) - scenario.intensification_percentage = 1e-10 # type: ignore[attr-defined] # noqa F821 - scenario.min_chall = 1 # type: ignore[attr-defined] # noqa F821 - - scenario.write() - - # initialize stats object - if stats: - self.stats = stats - else: - self.stats = Stats(scenario) - - if self.scenario.run_obj == "runtime" and not self.scenario.transform_y == "LOG": # type: ignore[attr-defined] # noqa F821 - self.logger.warning("Runtime as objective automatically activates log(y) transformation") - self.scenario.transform_y = "LOG" # type: ignore[attr-defined] # noqa F821 - - # initialize empty runhistory - num_obj = len(scenario.multi_objectives) # type: ignore[attr-defined] # noqa F821 - runhistory_def_kwargs = {} - if runhistory_kwargs is not None: - runhistory_def_kwargs.update(runhistory_kwargs) - if runhistory is None: - runhistory = RunHistory(**runhistory_def_kwargs) - elif inspect.isclass(runhistory): - runhistory = runhistory(**runhistory_def_kwargs) # type: ignore[operator] # noqa F821 - elif isinstance(runhistory, RunHistory): - pass - else: - raise ValueError("runhistory has to be a class or an object of RunHistory") - - rand_conf_chooser_kwargs = {"rng": rng} - if random_configuration_chooser_kwargs is not None: - rand_conf_chooser_kwargs.update(random_configuration_chooser_kwargs) - if random_configuration_chooser is None: - if "prob" not in rand_conf_chooser_kwargs: - rand_conf_chooser_kwargs["prob"] = scenario.rand_prob # type: ignore[attr-defined] # noqa F821 - random_configuration_chooser_instance = ChooserProb( - **rand_conf_chooser_kwargs # type: ignore[arg-type] # noqa F821 # type: RandomConfigurationChooser - ) - elif inspect.isclass(random_configuration_chooser): - random_configuration_chooser_instance = random_configuration_chooser( # type: ignore # noqa F821 - **rand_conf_chooser_kwargs # type: ignore[arg-type] # noqa F821 - ) - elif not isinstance(random_configuration_chooser, RandomChooser): - raise ValueError( - "random_configuration_chooser has to be" " a class or object of RandomConfigurationChooser" - ) - - # reset random number generator in config space to draw different - # random configurations with each seed given to SMAC - scenario.cs.seed(rng.randint(MAXINT)) # type: ignore[attr-defined] # noqa F821 - - # initial Trajectory Logger - traj_logger = TrajLogger(output_dir=self.output_dir, stats=self.stats) - - # initial EPM - types, bounds = get_types(scenario.cs, scenario.feature_array) # type: ignore[attr-defined] # noqa F821 - model_def_kwargs = { - "types": types, - "bounds": bounds, - "instance_features": scenario.feature_array, - "seed": rng.randint(MAXINT), - "pca_components": scenario.PCA_DIM, - } - if model_kwargs is not None: - model_def_kwargs.update(model_kwargs) - if model is None: - for key, value in { - "log_y": scenario.transform_y in ["LOG", "LOGS"], # type: ignore[attr-defined] # noqa F821 - "num_trees": scenario.rf_num_trees, # type: ignore[attr-defined] # noqa F821 - "do_bootstrapping": scenario.rf_do_bootstrapping, # type: ignore[attr-defined] # noqa F821 - "ratio_features": scenario.rf_ratio_features, # type: ignore[attr-defined] # noqa F821 - "min_samples_split": scenario.rf_min_samples_split, # type: ignore[attr-defined] # noqa F821 - "min_samples_leaf": scenario.rf_min_samples_leaf, # type: ignore[attr-defined] # noqa F821 - "max_depth": scenario.rf_max_depth, # type: ignore[attr-defined] # noqa F821 - }.items(): - if key not in model_def_kwargs: - model_def_kwargs[key] = value - model_def_kwargs["configspace"] = self.scenario.cs # type: ignore[attr-defined] # noqa F821 - model_instance = RandomForestWithInstances( - **model_def_kwargs # type: ignore[arg-type] # noqa F821 # type: BaseEPM - ) - elif inspect.isclass(model): - model_def_kwargs["configspace"] = self.scenario.cs # type: ignore[attr-defined] # noqa F821 - model_instance = model(**model_def_kwargs) # type: ignore # noqa F821 - else: - raise TypeError("Model not recognized: %s" % (type(model))) - - # initial acquisition function - acq_def_kwargs = {"model": model_instance} - if acquisition_function_kwargs is not None: - acq_def_kwargs.update(acquisition_function_kwargs) - - acquisition_function_instance = None # type: Optional[AbstractAcquisitionFunction] - if acquisition_function is None: - if scenario.transform_y in ["LOG", "LOGS"]: # type: ignore[attr-defined] # noqa F821 - acquisition_function_instance = LogEI(**acq_def_kwargs) # type: ignore[arg-type] # noqa F821 - else: - acquisition_function_instance = EI(**acq_def_kwargs) # type: ignore[arg-type] # noqa F821 - elif inspect.isclass(acquisition_function): - acquisition_function_instance = acquisition_function(**acq_def_kwargs) - else: - raise TypeError( - "Argument acquisition_function must be None or an object implementing the " - "AbstractAcquisitionFunction, not %s." % type(acquisition_function) - ) - if isinstance(acquisition_function_instance, EIPS) and not isinstance(model_instance, MultiObjectiveEPM): - raise TypeError( - "If the acquisition function is EIPS, the surrogate model must support multi-objective prediction!" - ) - if integrate_acquisition_function: - acquisition_function_instance = IntegratedAcquisitionFunction( - acquisition_function=acquisition_function_instance, # type: ignore - **acq_def_kwargs, - ) - - if user_priors: - if user_prior_kwargs is None: - user_prior_kwargs = {} - - # a solid default value for decay_beta - empirically founded - default_beta = scenario.ta_run_limit / 10 # type: ignore - discretize = isinstance(model_instance, (RandomForestWithInstances, RFRImputator)) - user_prior_kwargs["decay_beta"] = user_prior_kwargs.get("decay_beta", default_beta) - user_prior_kwargs["discretize"] = discretize - - acquisition_function_instance = PriorAcquisitionFunction( - acquisition_function=acquisition_function_instance, # type: ignore - **user_prior_kwargs, - **acq_def_kwargs, # type: ignore - ) - acquisition_function_optimizer = LocalAndSortedPriorRandomSearch - - # initialize optimizer on acquisition function - acq_func_opt_kwargs = { - "acquisition_function": acquisition_function_instance, - "config_space": scenario.cs, # type: ignore[attr-defined] # noqa F821 - "rng": rng, - } - if user_priors: - acq_func_opt_kwargs["uniform_config_space"] = scenario.cs.remove_hyperparameter_priors() # type: ignore - - if acquisition_function_optimizer_kwargs is not None: - acq_func_opt_kwargs.update(acquisition_function_optimizer_kwargs) - if acquisition_function_optimizer is None: - for key, value in { - "max_steps": scenario.sls_max_steps, # type: ignore[attr-defined] # noqa F821 - "n_steps_plateau_walk": scenario.sls_n_steps_plateau_walk, # type: ignore[attr-defined] # noqa F821 - }.items(): - if key not in acq_func_opt_kwargs: - acq_func_opt_kwargs[key] = value - acquisition_function_optimizer_instance = LocalAndSortedRandomSearch(**acq_func_opt_kwargs) # type: ignore - elif inspect.isclass(acquisition_function_optimizer): - acquisition_function_optimizer_instance = acquisition_function_optimizer( # type: ignore # noqa F821 - **acq_func_opt_kwargs - ) # type: ignore # noqa F821 - else: - raise TypeError( - "Argument acquisition_function_optimizer must be None or an object implementing the " - "AcquisitionFunctionMaximizer, but is '%s'" % type(acquisition_function_optimizer) - ) - - # initialize tae_runner - # First case, if tae_runner is None, the target algorithm is a call - # string in the scenario file - tae_def_kwargs = { - "stats": self.stats, - "run_obj": scenario.run_obj, - "par_factor": scenario.par_factor, # type: ignore[attr-defined] # noqa F821 - "cost_for_crash": scenario.cost_for_crash, # type: ignore[attr-defined] # noqa F821 - "abort_on_first_run_crash": scenario.abort_on_first_run_crash, # type: ignore[attr-defined] # noqa F821 - "multi_objectives": scenario.multi_objectives, # type: ignore[attr-defined] # noqa F821 - } - if tae_runner_kwargs is not None: - tae_def_kwargs.update(tae_runner_kwargs) - - if "ta" not in tae_def_kwargs: - tae_def_kwargs["ta"] = scenario.ta # type: ignore[attr-defined] # noqa F821 - if tae_runner is None: - tae_def_kwargs["ta"] = scenario.ta # type: ignore[attr-defined] # noqa F821 - tae_runner_instance = ExecuteTARunOld( - **tae_def_kwargs - ) # type: ignore[arg-type] # noqa F821 # type: BaseRunner - elif inspect.isclass(tae_runner): - tae_runner_instance = cast(BaseRunner, tae_runner(**tae_def_kwargs)) # type: ignore - elif callable(tae_runner): - tae_def_kwargs["ta"] = tae_runner - tae_def_kwargs["use_pynisher"] = scenario.limit_resources # type: ignore[attr-defined] # noqa F821 - tae_def_kwargs["memory_limit"] = scenario.memory_limit # type: ignore[attr-defined] # noqa F821 - tae_runner_instance = ExecuteTAFuncDict(**tae_def_kwargs) # type: ignore - else: - raise TypeError( - "Argument 'tae_runner' is %s, but must be " - "either None, a callable or an object implementing " - "BaseRunner. Passing 'None' will result in the " - "creation of target algorithm runner based on the " - "call string in the scenario file." % type(tae_runner) - ) - - # In case of a parallel run, wrap the single worker in a parallel - # runner - if n_jobs is None or n_jobs == 1: - _n_jobs = 1 - elif n_jobs == -1: - _n_jobs = joblib.cpu_count() - elif n_jobs > 0: - _n_jobs = n_jobs - else: - raise ValueError("Number of tasks must be positive, None or -1, but is %s" % str(n_jobs)) - if _n_jobs > 1 or dask_client is not None: - tae_runner_instance = DaskParallelRunner( # type: ignore - tae_runner_instance, - n_workers=_n_jobs, - output_directory=self.output_dir, - dask_client=dask_client, - ) - - # Check that overall objective and tae objective are the same - # TODO: remove these two ignores once the scenario object knows all its attributes! - if tae_runner_instance.run_obj != scenario.run_obj: # type: ignore[union-attr] # noqa F821 - raise ValueError( - "Objective for the target algorithm runner and " - "the scenario must be the same, but are '%s' and " - "'%s'" % (tae_runner_instance.run_obj, scenario.run_obj) - ) # type: ignore[union-attr] # noqa F821 - - if intensifier is None: - intensifier = Intensifier - - if isinstance(intensifier, AbstractRacer): - intensifier_instance = intensifier - elif inspect.isclass(intensifier): - # initialize intensification - intensifier_def_kwargs = { - "stats": self.stats, - "traj_logger": traj_logger, - "rng": rng, - "instances": scenario.train_insts, # type: ignore[attr-defined] # noqa F821 - "cutoff": scenario.cutoff, # type: ignore[attr-defined] # noqa F821 - "deterministic": scenario.deterministic, # type: ignore[attr-defined] # noqa F821 - "run_obj_time": scenario.run_obj == "runtime", # type: ignore[attr-defined] # noqa F821 - "instance_specifics": scenario.instance_specific, # type: ignore[attr-defined] # noqa F821 - "adaptive_capping_slackfactor": scenario.intens_adaptive_capping_slackfactor, # type: ignore[attr-defined] # noqa F821 - "min_chall": scenario.intens_min_chall, # type: ignore[attr-defined] # noqa F821 - } - - if issubclass(intensifier, Intensifier): - intensifier_def_kwargs["always_race_against"] = scenario.cs.get_default_configuration() # type: ignore[attr-defined] # noqa F821 - intensifier_def_kwargs["use_ta_time_bound"] = scenario.use_ta_time # type: ignore[attr-defined] # noqa F821 - intensifier_def_kwargs["minR"] = scenario.minR # type: ignore[attr-defined] # noqa F821 - intensifier_def_kwargs["maxR"] = scenario.maxR # type: ignore[attr-defined] # noqa F821 - - if intensifier_kwargs is not None: - intensifier_def_kwargs.update(intensifier_kwargs) - - intensifier_instance = intensifier(**intensifier_def_kwargs) # type: ignore[arg-type] # noqa F821 - else: - raise TypeError( - "Argument intensifier must be None or an object implementing the AbstractRacer, but is '%s'" - % type(intensifier) - ) - - # initialize multi objective - # the multi_objective_algorithm_instance will be passed to the runhistory2epm object - multi_objective_algorithm_instance = None # type: Optional[AbstractMultiObjectiveAlgorithm] - - if scenario.multi_objectives is not None and num_obj > 1: # type: ignore[attr-defined] # noqa F821 - # define any defaults here - _multi_objective_kwargs = {"rng": rng} - - if multi_objective_kwargs is not None: - _multi_objective_kwargs.update(multi_objective_kwargs) - - if multi_objective_algorithm is None: - multi_objective_algorithm_instance = MeanAggregationStrategy( - **_multi_objective_kwargs # type: ignore[arg-type] # noqa F821 - ) - elif inspect.isclass(multi_objective_algorithm): - multi_objective_algorithm_instance = multi_objective_algorithm(**_multi_objective_kwargs) - else: - raise TypeError("Multi-objective algorithm not recognized: %s" % (type(multi_objective_algorithm))) - - # initial design - if initial_design is not None and initial_configurations is not None: - raise ValueError("Either use initial_design or initial_configurations; but not both") - - init_design_def_kwargs = { - "cs": scenario.cs, # type: ignore[attr-defined] # noqa F821 - "traj_logger": traj_logger, - "rng": rng, - "ta_run_limit": scenario.ta_run_limit, # type: ignore[attr-defined] # noqa F821 - "configs": initial_configurations, - "n_configs_x_params": 0, - "max_config_fracs": 0.0, - } - - if initial_design_kwargs is not None: - init_design_def_kwargs.update(initial_design_kwargs) - if initial_configurations is not None: - initial_design_instance = InitialDesign(**init_design_def_kwargs) - elif initial_design is None: - if scenario.initial_incumbent == "DEFAULT": # type: ignore[attr-defined] # noqa F821 - init_design_def_kwargs["max_config_fracs"] = 0.0 - initial_design_instance = DefaultConfiguration(**init_design_def_kwargs) - elif scenario.initial_incumbent == "RANDOM": # type: ignore[attr-defined] # noqa F821 - init_design_def_kwargs["max_config_fracs"] = 0.0 - initial_design_instance = RandomConfigurations(**init_design_def_kwargs) - elif scenario.initial_incumbent == "LHD": # type: ignore[attr-defined] # noqa F821 - initial_design_instance = LHDesign(**init_design_def_kwargs) - elif scenario.initial_incumbent == "FACTORIAL": # type: ignore[attr-defined] # noqa F821 - initial_design_instance = FactorialInitialDesign(**init_design_def_kwargs) - elif scenario.initial_incumbent == "SOBOL": # type: ignore[attr-defined] # noqa F821 - initial_design_instance = SobolDesign(**init_design_def_kwargs) - else: - raise ValueError( - "Don't know what kind of initial_incumbent " "'%s' is" % scenario.initial_incumbent # type: ignore - ) # type: ignore[attr-defined] # noqa F821 - elif inspect.isclass(initial_design): - initial_design_instance = initial_design(**init_design_def_kwargs) - else: - raise TypeError( - "Argument initial_design must be None or an object implementing the InitialDesign, but is '%s'" - % type(initial_design) - ) - - # if we log the performance data, - # the RFRImputator will already get - # log transform data from the runhistory - if scenario.transform_y in ["LOG", "LOGS"]: # type: ignore[attr-defined] # noqa F821 - cutoff = np.log(np.nanmin([np.inf, np.float_(scenario.cutoff)])) # type: ignore[arg-type, attr-defined] # noqa F821 - threshold = cutoff + np.log(scenario.par_factor) # type: ignore[attr-defined] # noqa F821 - else: - cutoff = np.nanmin([np.inf, np.float_(scenario.cutoff)]) # type: ignore[arg-type, attr-defined] # noqa F821 - threshold = cutoff * scenario.par_factor # type: ignore[attr-defined] # noqa F821 - - num_params = len(scenario.cs.get_hyperparameters()) # type: ignore[attr-defined] # noqa F821 - imputor = RFRImputator( - rng=rng, - cutoff=cutoff, - threshold=threshold, - model=model_instance, - change_threshold=0.01, - max_iter=2, - ) - - r2e_def_kwargs = { - "scenario": scenario, - "num_params": num_params, - "success_states": [ - StatusType.SUCCESS, - ], - "impute_censored_data": True, - "impute_state": [ - StatusType.CAPPED, - ], - "imputor": imputor, - "scale_perc": 5, - } - - # TODO: consider other sorts of multi-objective algorithms - if isinstance(multi_objective_algorithm_instance, AggregationStrategy): - r2e_def_kwargs.update({"multi_objective_algorithm": multi_objective_algorithm_instance}) - - if scenario.run_obj == "quality": - r2e_def_kwargs.update( - { - "success_states": [ - StatusType.SUCCESS, - StatusType.CRASHED, - StatusType.MEMOUT, - ], - "impute_censored_data": False, - "impute_state": None, - } - ) - - if isinstance(intensifier_instance, (SuccessiveHalving, Hyperband)) and scenario.run_obj == "quality": - r2e_def_kwargs.update( - { - "success_states": [ - StatusType.SUCCESS, - StatusType.CRASHED, - StatusType.MEMOUT, - StatusType.DONOTADVANCE, - ], - "consider_for_higher_budgets_state": [ - StatusType.DONOTADVANCE, - StatusType.TIMEOUT, - StatusType.CRASHED, - StatusType.MEMOUT, - ], - } - ) - - if runhistory2epm_kwargs is not None: - r2e_def_kwargs.update(runhistory2epm_kwargs) - if runhistory2epm is None: - if scenario.run_obj == "runtime": - rh2epm = RunHistory2EPM4LogCost( - **r2e_def_kwargs # type: ignore - ) # type: ignore[arg-type] # noqa F821 # type: AbstractRunHistory2EPM - elif scenario.run_obj == "quality": - if scenario.transform_y == "NONE": # type: ignore[attr-defined] # noqa F821 - rh2epm = RunHistory2EPM4Cost(**r2e_def_kwargs) # type: ignore # noqa F821 - elif scenario.transform_y == "LOG": # type: ignore[attr-defined] # noqa F821 - rh2epm = RunHistory2EPM4LogCost(**r2e_def_kwargs) # type: ignore # noqa F821 - elif scenario.transform_y == "LOGS": # type: ignore[attr-defined] # noqa F821 - rh2epm = RunHistory2EPM4LogScaledCost(**r2e_def_kwargs) # type: ignore # noqa F821 - elif scenario.transform_y == "INVS": # type: ignore[attr-defined] # noqa F821 - rh2epm = RunHistory2EPM4InvScaledCost(**r2e_def_kwargs) # type: ignore # noqa F821 - else: - raise ValueError( - "Unknown run objective: %s. Should be either " - "quality or runtime." % self.scenario.run_obj # type: ignore # noqa F821 - ) - elif inspect.isclass(runhistory2epm): - rh2epm = runhistory2epm(**r2e_def_kwargs) # type: ignore # noqa F821 - else: - raise TypeError( - "Argument runhistory2epm must be None or an object implementing the RunHistory2EPM, but is '%s'" - % type(runhistory2epm) - ) - - smbo_args = { - "scenario": scenario, - "stats": self.stats, - "initial_design": initial_design_instance, - "runhistory": runhistory, - "runhistory2epm": rh2epm, - "intensifier": intensifier_instance, - "num_run": run_id, - "model": model_instance, - "acq_optimizer": acquisition_function_optimizer_instance, - "acquisition_func": acquisition_function_instance, - "rng": rng, - "restore_incumbent": restore_incumbent, - "random_configuration_chooser": random_configuration_chooser_instance, - "tae_runner": tae_runner_instance, - } # type: Dict[str, Any] - if smbo_kwargs is not None: - smbo_args.update(smbo_kwargs) - - if smbo_class is None: - self.solver = SMBO(**smbo_args) # type: ignore[arg-type] # noqa F821 - else: - self.solver = smbo_class(**smbo_args) # type: ignore[arg-type] # noqa F821 - - def optimize(self) -> Configuration: - """Optimizes the algorithm provided in scenario (given in constructor) - - Returns - ------- - incumbent : Configuration - Best found configuration - """ - incumbent = None - try: - incumbent = self.solver.run() - finally: - self.solver.save() - - self.solver.stats.print_stats() - self.logger.info("Final Incumbent: %s", self.solver.incumbent) - if self.solver.incumbent and self.solver.incumbent in self.solver.runhistory.get_all_configs(): - self.logger.info( - f"Estimated cost of incumbent: " - f"{format_array(self.solver.runhistory.get_cost(self.solver.incumbent))}" - ) - self.runhistory = self.solver.runhistory - self.trajectory = self.solver.intensifier.traj_logger.trajectory - - return incumbent - - def validate( - self, - config_mode: Union[List[Configuration], str] = "inc", - instance_mode: Union[List[str], str] = "train+test", - repetitions: int = 1, - use_epm: bool = False, - n_jobs: int = -1, - backend: str = "threading", - ) -> RunHistory: - """Create validator-object and run validation, using scenario- information, runhistory from - smbo and tae_runner from intensify. - - Parameters - ---------- - config_mode: str or list - string or directly a list of Configuration - str from [def, inc, def+inc, wallclock_time, cpu_time, all] - time evaluates at cpu- or wallclock-timesteps of: - [max_time/2^0, max_time/2^1, max_time/2^3, ..., default] - with max_time being the highest recorded time - instance_mode: string - what instances to use for validation, from [train, test, train+test] - repetitions: int - number of repetitions in nondeterministic algorithms (in - deterministic will be fixed to 1) - use_epm: bool - whether to use an EPM instead of evaluating all runs with the TAE - n_jobs: int - number of parallel processes used by joblib - backend: string - what backend to be used by joblib - - Returns - ------- - runhistory: RunHistory - runhistory containing all specified runs - """ - return self.solver.validate(config_mode, instance_mode, repetitions, use_epm, n_jobs, backend) - - def get_tae_runner(self) -> BaseRunner: - """Returns target algorithm evaluator (TAE) object which can run the target algorithm given - a configuration. - - Returns - ------- - TAE: smac.tae.base.BaseRunner - """ - return self.solver.tae_runner - - def get_runhistory(self) -> RunHistory: - """Returns the runhistory (i.e., all evaluated configurations and the results). - - Returns - ------- - Runhistory: smac.runhistory.runhistory.RunHistory - """ - if not hasattr(self, "runhistory"): - raise ValueError("SMAC was not fitted yet. Call optimize() prior " "to accessing the runhistory.") - return self.runhistory - - def get_trajectory(self) -> List[TrajEntry]: - """Returns the trajectory (i.e., all incumbent configurations over time). - - Returns - ------- - Trajectory : List of :class:`~smac.utils.io.traj_logging.TrajEntry` - """ - if not hasattr(self, "trajectory"): - raise ValueError("SMAC was not fitted yet. Call optimize() prior " "to accessing the runhistory.") - return self.trajectory - - def register_callback(self, callback: Callable) -> None: - """Register a callback function. - - Callbacks must implement a class in ``smac.callbacks`` and be instantiated objects. - They will automatically be registered within SMAC based on which callback class from - ``smac.callbacks`` they implement. - - Parameters - ---------- - callback - Callable - - Returns - ------- - None - """ - types_to_check = callback.__class__.__mro__ - key = None - for type_to_check in types_to_check: - key = self.solver._callback_to_key.get(type_to_check) - if key is not None: - break - if key is None: - raise ValueError("Cannot register callback of type %s" % type(callback)) - self.solver._callbacks[key].append(callback) diff --git a/smac/facade/smac_bb_facade.py b/smac/facade/smac_bb_facade.py deleted file mode 100644 index dc44ae9bc..000000000 --- a/smac/facade/smac_bb_facade.py +++ /dev/null @@ -1,195 +0,0 @@ -from typing import Any, Type - -import numpy as np - -from smac.epm.gaussian_process import BaseModel, GaussianProcess -from smac.epm.gaussian_process.kernels import ( - ConstantKernel, - HammingKernel, - Matern, - WhiteKernel, -) -from smac.epm.gaussian_process.mcmc import MCMCGaussianProcess -from smac.epm.gaussian_process.utils.prior import HorseshoePrior, LognormalPrior -from smac.epm.utils import get_rng, get_types -from smac.facade.smac_ac_facade import SMAC4AC -from smac.initial_design.sobol_design import SobolDesign -from smac.runhistory.runhistory2epm import RunHistory2EPM4Cost - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2018, ML4AAD" -__license__ = "3-clause BSD" - - -class SMAC4BB(SMAC4AC): - """Facade to use SMAC for Black-Box optimization using a GP. - - see smac.facade.smac_Facade for API - This facade overwrites options available via the SMAC facade - - Hyperparameters are chosen according to the best configuration for Gaussian process maximum likelihood found in - "Towards Assessing the Impact of Bayesian Optimization's Own Hyperparameters" by Lindauer et al., presented at the - DSO workshop 2019 (https://arxiv.org/abs/1908.06674). - - Changes are: - - * Instead of having an initial design of size 10*D as suggested by Jones et al. 1998 (actually, they suggested - 10*D+1), we use an initial design of 8*D. - * More restrictive lower and upper bounds on the length scale for the Matern and Hamming Kernel than the ones - suggested by Klein et al. 2017 in the RoBO package. In practice, they are ``np.exp(-6.754111155189306)`` - instead of ``np.exp(-10)`` for the lower bound and ``np.exp(0.0858637988771976)`` instead of - ``np.exp(2)`` for the upper bound. - * The initial design is set to be a Sobol grid - * The random fraction is set to ``0.08447232371720552``, it was ``0.0`` before. - - See Also - -------- - :class:`~smac.facade.smac_ac_facade.SMAC4AC` for documentation of parameters. - - Attributes - ---------- - logger - stats : Stats - solver : SMBO - runhistory : RunHistory - List with information about previous runs - trajectory : list - List of all incumbents - """ - - def __init__(self, model_type: str = "gp_mcmc", **kwargs: Any): - scenario = kwargs["scenario"] - - if len(scenario.cs.get_hyperparameters()) <= 21201: - kwargs["initial_design"] = kwargs.get("initial_design", SobolDesign) - else: - raise ValueError( - 'The default initial design "Sobol sequence" can only handle up to 21201 dimensions. ' - 'Please use a different initial design, such as "the Latin Hypercube design".', - ) - kwargs["runhistory2epm"] = kwargs.get("runhistory2epm", RunHistory2EPM4Cost) - - init_kwargs = kwargs.get("initial_design_kwargs", dict()) or dict() - init_kwargs["n_configs_x_params"] = init_kwargs.get("n_configs_x_params", 8) - init_kwargs["max_config_fracs"] = init_kwargs.get("max_config_fracs", 0.25) - kwargs["initial_design_kwargs"] = init_kwargs - - if kwargs.get("model") is None: - - model_kwargs = kwargs.get("model_kwargs", dict()) or dict() - - _, rng = get_rng( - rng=kwargs.get("rng", None), - run_id=kwargs.get("run_id", None), - logger=None, - ) - - types, bounds = get_types(kwargs["scenario"].cs, instance_features=None) - - cov_amp = ConstantKernel( - 2.0, - constant_value_bounds=(np.exp(-10), np.exp(2)), - prior=LognormalPrior(mean=0.0, sigma=1.0, rng=rng), - ) - - cont_dims = np.where(np.array(types) == 0)[0] - cat_dims = np.where(np.array(types) != 0)[0] - - if len(cont_dims) > 0: - exp_kernel = Matern( - np.ones([len(cont_dims)]), - [(np.exp(-6.754111155189306), np.exp(0.0858637988771976)) for _ in range(len(cont_dims))], - nu=2.5, - operate_on=cont_dims, - ) - - if len(cat_dims) > 0: - ham_kernel = HammingKernel( - np.ones([len(cat_dims)]), - [(np.exp(-6.754111155189306), np.exp(0.0858637988771976)) for _ in range(len(cat_dims))], - operate_on=cat_dims, - ) - - assert (len(cont_dims) + len(cat_dims)) == len(scenario.cs.get_hyperparameters()) - - noise_kernel = WhiteKernel( - noise_level=1e-8, - noise_level_bounds=(np.exp(-25), np.exp(2)), - prior=HorseshoePrior(scale=0.1, rng=rng), - ) - - if len(cont_dims) > 0 and len(cat_dims) > 0: - # both - kernel = cov_amp * (exp_kernel * ham_kernel) + noise_kernel - elif len(cont_dims) > 0 and len(cat_dims) == 0: - # only cont - kernel = cov_amp * exp_kernel + noise_kernel - elif len(cont_dims) == 0 and len(cat_dims) > 0: - # only cont - kernel = cov_amp * ham_kernel + noise_kernel - else: - raise ValueError() - - if model_type == "gp": - model_class = GaussianProcess # type: Type[BaseModel] - kwargs["model"] = model_class - model_kwargs["kernel"] = kernel - model_kwargs["normalize_y"] = True - model_kwargs["seed"] = rng.randint(0, 2**20) - elif model_type == "gp_mcmc": - model_class = MCMCGaussianProcess - kwargs["model"] = model_class - kwargs["integrate_acquisition_function"] = True - - model_kwargs["kernel"] = kernel - - n_mcmc_walkers = 3 * len(kernel.theta) - if n_mcmc_walkers % 2 == 1: - n_mcmc_walkers += 1 - model_kwargs["n_mcmc_walkers"] = n_mcmc_walkers - model_kwargs["chain_length"] = 250 - model_kwargs["burnin_steps"] = 250 - model_kwargs["normalize_y"] = True - model_kwargs["seed"] = rng.randint(0, 2**20) - else: - raise ValueError("Unknown model type %s" % model_type) - kwargs["model_kwargs"] = model_kwargs - - if kwargs.get("random_configuration_chooser") is None: - random_config_chooser_kwargs = ( - kwargs.get( - "random_configuration_chooser_kwargs", - dict(), - ) - or dict() - ) - random_config_chooser_kwargs["prob"] = random_config_chooser_kwargs.get("prob", 0.08447232371720552) - kwargs["random_configuration_chooser_kwargs"] = random_config_chooser_kwargs - - if kwargs.get("acquisition_function_optimizer") is None: - acquisition_function_optimizer_kwargs = ( - kwargs.get( - "acquisition_function_optimizer_kwargs", - dict(), - ) - or dict() - ) - acquisition_function_optimizer_kwargs["n_sls_iterations"] = 10 - kwargs["acquisition_function_optimizer_kwargs"] = acquisition_function_optimizer_kwargs - - # only 1 configuration per SMBO iteration - intensifier_kwargs = kwargs.get("intensifier_kwargs", dict()) or dict() - intensifier_kwargs["min_chall"] = 1 - kwargs["intensifier_kwargs"] = intensifier_kwargs - scenario.intensification_percentage = 1e-10 - - super().__init__(**kwargs) - - if self.solver.scenario.n_features > 0: - raise NotImplementedError("BOGP cannot handle instances") - - self.logger.info(self.__class__) - - self.solver.scenario.acq_opt_challengers = 1000 # type: ignore[attr-defined] # noqa F821 - # activate predict incumbent - self.solver.epm_chooser.predict_x_best = True diff --git a/smac/facade/smac_boing_facade.py b/smac/facade/smac_boing_facade.py deleted file mode 100644 index c9332a02b..000000000 --- a/smac/facade/smac_boing_facade.py +++ /dev/null @@ -1,111 +0,0 @@ -from typing import Any - -import warnings - -import numpy as np -from botorch.models.kernels.categorical import CategoricalKernel -from gpytorch.constraints.constraints import Interval -from gpytorch.kernels import MaternKernel, ScaleKernel -from gpytorch.likelihoods.gaussian_likelihood import GaussianLikelihood -from gpytorch.priors import HorseshoePrior, LogNormalPrior - -from smac.epm.gaussian_process.augmented import GloballyAugmentedLocalGaussianProcess -from smac.facade.smac_hpo_facade import SMAC4HPO -from smac.optimizer.configuration_chooser.boing_chooser import BOinGChooser -from smac.runhistory.runhistory2epm_boing import RunHistory2EPM4ScaledLogCostWithRaw - - -class SMAC4BOING(SMAC4HPO): - """ - SMAC wrapper for BO inside Grove(BOinG): - Deng and Lindauer, Searching in the Forest for Local Bayesian Optimization - https://arxiv.org/abs/2111.05834 - - BOiNG is a two-stages optimizer: at the first stage, the global optimizer extracts the global optimum with a random - forest (RF) model. Then in the second stage, the optimizer constructs a local model in the vicinity of the - configuration suggested by the global surrogate model. - - Its Hyperparameter settings follow the implementation from smac.facade.smac_bb_facade.SMAC4BB: - Hyperparameters are chosen according to the best configuration for Gaussian process maximum likelihood found in - "Towards Assessing the Impact of Bayesian Optimization's Own Hyperparameters" by Lindauer et al., presented at the - DSO workshop 2019 (https://arxiv.org/abs/1908.06674). - """ - - def __init__(self, **kwargs: Any): - kwargs["runhistory2epm"] = kwargs.get("runhistory2epm", RunHistory2EPM4ScaledLogCostWithRaw) - smbo_kwargs = kwargs.get("smbo_kwargs", {}) - if smbo_kwargs is None: - smbo_kwargs = {"epm_chooser", BOinGChooser} - epm_chooser = smbo_kwargs.get("epm_chooser", BOinGChooser) - if epm_chooser != BOinGChooser: - warnings.warn("BOinG must have BOinGChooser as its EPM chooser!") - epm_chooser = BOinGChooser - smbo_kwargs["epm_chooser"] = epm_chooser - epm_chooser_kwargs = smbo_kwargs.get("epm_chooser_kwargs", None) - - if epm_chooser_kwargs is None or epm_chooser_kwargs.get("model_local") is None: - # The lower bound and upper bounds are set to be identical as SMAC4BB - cont_kernel_kwargs = { - "lengthscale_constraint": Interval( - np.exp(-6.754111155189306), np.exp(0.0858637988771976), transform=None, initial_value=1.0 - ), - } - cat_kernel_kwargs = { - "lengthscale_constraint": Interval( - np.exp(-6.754111155189306), np.exp(0.0858637988771976), transform=None, initial_value=1.0 - ), - } - scale_kernel_kwargs = { - "outputscale_constraint": Interval(np.exp(-10.0), np.exp(2.0), transform=None, initial_value=2.0), - "outputscale_prior": LogNormalPrior(0.0, 1.0), - } - - kernel_kwargs = { - "cont_kernel": MaternKernel, - "cont_kernel_kwargs": cont_kernel_kwargs, - "cat_kernel": CategoricalKernel, - "cat_kernel_kwargs": cat_kernel_kwargs, - "scale_kernel": ScaleKernel, - "scale_kernel_kwargs": scale_kernel_kwargs, - } - - # by setting lower bound of noise_constraint we could make it more stable - noise_prior = HorseshoePrior(0.1) - likelihood = GaussianLikelihood( - noise_prior=noise_prior, noise_constraint=Interval(1e-5, np.exp(2), transform=None) - ).double() - - if epm_chooser_kwargs is None: - smbo_kwargs["epm_chooser_kwargs"] = { - "model_local": GloballyAugmentedLocalGaussianProcess, - "model_local_kwargs": dict(kernel_kwargs=kernel_kwargs, likelihood=likelihood), - } - else: - smbo_kwargs["epm_chooser_kwargs"].update( - { - "model_local": GloballyAugmentedLocalGaussianProcess, - "model_local_kwargs": dict(kernel_kwargs=kernel_kwargs, likelihood=likelihood), - } - ) - kwargs["smbo_kwargs"] = smbo_kwargs - - if kwargs.get("random_configuration_chooser") is None: - # follows SMAC4BB - random_config_chooser_kwargs = ( - kwargs.get( - "random_configuration_chooser_kwargs", - dict(), - ) - or dict() - ) - random_config_chooser_kwargs["prob"] = random_config_chooser_kwargs.get("prob", 0.08447232371720552) - kwargs["random_configuration_chooser_kwargs"] = random_config_chooser_kwargs - - super().__init__(**kwargs) - - if self.solver.scenario.n_features > 0: - raise NotImplementedError("BOinG cannot handle instances") - - self.solver.scenario.acq_opt_challengers = 1000 # type: ignore[attr-defined] # noqa F821 - # activate predict incumbent - self.solver.epm_chooser.predict_x_best = True diff --git a/smac/facade/smac_hpo_facade.py b/smac/facade/smac_hpo_facade.py deleted file mode 100644 index edc7cc191..000000000 --- a/smac/facade/smac_hpo_facade.py +++ /dev/null @@ -1,93 +0,0 @@ -from typing import Any - -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances -from smac.facade.smac_ac_facade import SMAC4AC -from smac.initial_design.sobol_design import SobolDesign -from smac.optimizer.acquisition import LogEI -from smac.runhistory.runhistory2epm import RunHistory2EPM4LogScaledCost - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2018, ML4AAD" -__license__ = "3-clause BSD" - - -class SMAC4HPO(SMAC4AC): - """Facade to use SMAC for hyperparameter optimization. - - see smac.facade.smac_Facade for API - This facade overwrites options available via the SMAC facade - - See Also - -------- - :class:`~smac.facade.smac_ac_facade.SMAC4AC` for documentation of parameters. - - Attributes - ---------- - logger - stats : Stats - solver : SMBO - runhistory : RunHistory - List with information about previous runs - trajectory : list - List of all incumbents - """ - - def __init__(self, **kwargs: Any): - scenario = kwargs["scenario"] - - kwargs["initial_design"] = kwargs.get("initial_design", SobolDesign) - if len(scenario.cs.get_hyperparameters()) > 21201 and kwargs["initial_design"] is SobolDesign: - raise ValueError( - 'The default initial design "Sobol sequence" can only handle up to 21201 dimensions. ' - 'Please use a different initial design, such as "the Latin Hypercube design".', - ) - - init_kwargs = kwargs.get("initial_design_kwargs", dict()) - init_kwargs["n_configs_x_params"] = init_kwargs.get("n_configs_x_params", 10) - init_kwargs["max_config_fracs"] = init_kwargs.get("max_config_fracs", 0.25) - kwargs["initial_design_kwargs"] = init_kwargs - - # Intensification parameters - which intensifier to use and respective parameters - intensifier_kwargs = kwargs.get("intensifier_kwargs", dict()) - intensifier_kwargs["min_chall"] = 1 - kwargs["intensifier_kwargs"] = intensifier_kwargs - scenario.intensification_percentage = 1e-10 - - if kwargs.get("model") is None: - model_class = RandomForestWithInstances - kwargs["model"] = model_class - - # == static RF settings - model_kwargs = kwargs.get("model_kwargs", dict()) - model_kwargs["num_trees"] = model_kwargs.get("num_trees", 10) - model_kwargs["do_bootstrapping"] = model_kwargs.get("do_bootstrapping", True) - model_kwargs["ratio_features"] = model_kwargs.get("ratio_features", 1.0) - model_kwargs["min_samples_split"] = model_kwargs.get("min_samples_split", 2) - model_kwargs["min_samples_leaf"] = model_kwargs.get("min_samples_leaf", 1) - model_kwargs["log_y"] = model_kwargs.get("log_y", True) - kwargs["model_kwargs"] = model_kwargs - - # == Acquisition function - kwargs["acquisition_function"] = kwargs.get("acquisition_function", LogEI) - kwargs["runhistory2epm"] = kwargs.get("runhistory2epm", RunHistory2EPM4LogScaledCost) - - # assumes random chooser for random configs - random_config_chooser_kwargs = kwargs.get("random_configuration_chooser_kwargs", dict()) - random_config_chooser_kwargs["prob"] = random_config_chooser_kwargs.get("prob", 0.2) - kwargs["random_configuration_chooser_kwargs"] = random_config_chooser_kwargs - - # better improve acquisition function optimization - # 1. increase number of sls iterations - acquisition_function_optimizer_kwargs = kwargs.get("acquisition_function_optimizer_kwargs", dict()) - acquisition_function_optimizer_kwargs["n_sls_iterations"] = 10 - kwargs["acquisition_function_optimizer_kwargs"] = acquisition_function_optimizer_kwargs - - super().__init__(**kwargs) - self.logger.info(self.__class__) - - # better improve acquisition function optimization - # 2. more randomly sampled configurations - self.solver.scenario.acq_opt_challengers = 10000 # type: ignore[attr-defined] # noqa F821 - - # activate predict incumbent - self.solver.epm_chooser.predict_x_best = True diff --git a/smac/facade/smac_mf_facade.py b/smac/facade/smac_mf_facade.py deleted file mode 100644 index 1d5571b3d..000000000 --- a/smac/facade/smac_mf_facade.py +++ /dev/null @@ -1,66 +0,0 @@ -from typing import Any - -from smac.facade.smac_hpo_facade import SMAC4HPO -from smac.initial_design.random_configuration_design import RandomConfigurations -from smac.intensification.hyperband import Hyperband -from smac.runhistory.runhistory2epm import RunHistory2EPM4LogScaledCost - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2018, ML4AAD" -__license__ = "3-clause BSD" - - -class SMAC4MF(SMAC4HPO): - """Facade to use SMAC with a Hyperband intensifier for hyperparameter optimization using - multiple fidelities. - - see smac.facade.smac_Facade for API - This facade overwrites options available via the SMAC facade - - See Also - -------- - :class:`~smac.facade.smac_ac_facade.SMAC4AC` for documentation of parameters. - - Attributes - ---------- - logger - stats : Stats - solver : SMBO - runhistory : RunHistory - List with information about previous runs - trajectory : list - List of all incumbents - """ - - def __init__(self, **kwargs: Any): - scenario = kwargs["scenario"] - - kwargs["initial_design"] = kwargs.get("initial_design", RandomConfigurations) - kwargs["runhistory2epm"] = kwargs.get("runhistory2epm", RunHistory2EPM4LogScaledCost) - - # Intensification parameters - # select Hyperband as the intensifier ensure respective parameters are provided - if kwargs.get("intensifier") is None: - kwargs["intensifier"] = Hyperband - - # set Hyperband parameters if not given - intensifier_kwargs = kwargs.get("intensifier_kwargs", dict()) - intensifier_kwargs["min_chall"] = 1 - if intensifier_kwargs.get("eta") is None: - intensifier_kwargs["eta"] = 3 - if intensifier_kwargs.get("instance_order") is None: - intensifier_kwargs["instance_order"] = "shuffle_once" - kwargs["intensifier_kwargs"] = intensifier_kwargs - - super().__init__(**kwargs) - self.logger.info(self.__class__) - - # better improve acquisition function optimization - # 2. more randomly sampled configurations - self.solver.scenario.acq_opt_challengers = 10000 # type: ignore[attr-defined] # noqa F821 - - # activate predict incumbent - self.solver.epm_chooser.predict_x_best = True - - # SMAC4MF requires at least D+1 no. of samples to build a model - self.solver.epm_chooser.min_samples_model = len(scenario.cs.get_hyperparameters()) + 1 diff --git a/smac/initial_design/__init__.py b/smac/initial_design/__init__.py index e69de29bb..631789fc5 100644 --- a/smac/initial_design/__init__.py +++ b/smac/initial_design/__init__.py @@ -0,0 +1,15 @@ +from smac.initial_design.abstract_initial_design import AbstractInitialDesign +from smac.initial_design.default_design import DefaultInitialDesign +from smac.initial_design.factorial_design import FactorialInitialDesign +from smac.initial_design.latin_hypercube_design import LatinHypercubeInitialDesign +from smac.initial_design.random_design import RandomInitialDesign +from smac.initial_design.sobol_design import SobolInitialDesign + +__all__ = [ + "AbstractInitialDesign", + "LatinHypercubeInitialDesign", + "FactorialInitialDesign", + "RandomInitialDesign", + "SobolInitialDesign", + "DefaultInitialDesign", +] diff --git a/smac/initial_design/abstract_initial_design.py b/smac/initial_design/abstract_initial_design.py new file mode 100644 index 000000000..0399c8600 --- /dev/null +++ b/smac/initial_design/abstract_initial_design.py @@ -0,0 +1,197 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import Any + +from collections import OrderedDict + +import numpy as np +from ConfigSpace.configuration_space import Configuration, ConfigurationSpace +from ConfigSpace.hyperparameters import ( + CategoricalHyperparameter, + Constant, + NumericalHyperparameter, + OrdinalHyperparameter, +) +from ConfigSpace.util import ForbiddenValueError, deactivate_inactive_hyperparameters + +from smac.scenario import Scenario +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class AbstractInitialDesign: + """Base class for initial design strategies that evaluates multiple configurations. + + Parameters + ---------- + scenario : Scenario + n_configs : int | None, defaults to None + Number of initial configurations (disables the arguments ``n_configs_per_hyperparameter``). + n_configs_per_hyperparameter: int, defaults to 10 + Number of initial configurations per hyperparameter. For example, if my configuration space covers five + hyperparameters and ``n_configs_per_hyperparameter`` is set to 10, then 50 initial configurations will be + samples. + max_ratio: float, defaults to 0.1 + Use at most ``scenario.n_trials`` * ``max_ratio`` number of configurations in the initial design. + Additional configurations are not affected by this parameter. + additional_configs: list[Configuration], defaults to [] + Adds additional configurations to the initial design. + seed : int | None, default to None + """ + + def __init__( + self, + scenario: Scenario, + n_configs: int | None = None, + n_configs_per_hyperparameter: int | None = 10, + max_ratio: float = 0.1, + additional_configs: list[Configuration] = [], + seed: int | None = None, + ): + self._configspace = scenario.configspace + + if seed is None: + seed = scenario.seed + + self._seed = seed + self._rng = np.random.RandomState(seed) + self._n_configs_per_hyperparameter = n_configs_per_hyperparameter + self._additional_configs = additional_configs + + n_params = len(self._configspace.get_hyperparameters()) + if n_configs is not None: + logger.info("Using `n_configs` and ignoring `n_configs_per_hyperparameter`.") + self._n_configs = n_configs + elif n_configs_per_hyperparameter is not None: + self._n_configs = n_configs_per_hyperparameter * n_params + else: + raise ValueError( + "Need to provide either argument `n_configs` or " + "`n_configs_per_hyperparameter` but provided none of them." + ) + + # If the number of configurations is too large, we reduce it + _n_configs = int(max(1, min(self._n_configs, (max_ratio * scenario.n_trials)))) + if self._n_configs != _n_configs: + logger.info( + f"Reducing the number of initial configurations from {self._n_configs} to " + f"{_n_configs} (max_ratio == {max_ratio})." + ) + self._n_configs = _n_configs + + # We allow no configs if we have additional configs + if n_configs is not None and n_configs == 0 and len(additional_configs) > 0: + self._n_configs = 0 + + if self._n_configs + len(additional_configs) > scenario.n_trials: + raise ValueError( + f"Initial budget {self._n_configs} cannot be higher than the number of trials {scenario.n_trials}." + ) + + @property + def meta(self) -> dict[str, Any]: + """Returns the meta data of the created object.""" + return { + "name": self.__class__.__name__, + "n_configs": self._n_configs, + "n_configs_per_hyperparameter": self._n_configs_per_hyperparameter, + "additional_configs": [c.get_dictionary() for c in self._additional_configs], + "seed": self._seed, + } + + def select_configurations(self) -> list[Configuration]: + """Selects the initial configurations. Internally, `_select_configurations` is called, + which has to be implemented by the child class. + + Returns + ------- + configs : list[Configuration] + Configurations from the child class. + """ + configs: list[Configuration] = [] + + if self._n_configs == 0: + logger.info("No initial configurations are used.") + else: + configs += self._select_configurations() + + # Adding additional configs + configs += self._additional_configs + + for config in configs: + if config.origin is None: + config.origin = "Initial design" + + # Removing duplicates + # (Reference: https://stackoverflow.com/questions/7961363/removing-duplicates-in-lists) + configs = list(OrderedDict.fromkeys(configs)) + logger.info( + f"Using {len(configs) - len(self._additional_configs)} initial design " + f"and {len(self._additional_configs)} additional configurations." + ) + + return configs + + @abstractmethod + def _select_configurations(self) -> list[Configuration]: + """Selects the initial configurations, depending on the implementation of the initial design.""" + raise NotImplementedError + + def _transform_continuous_designs( + self, design: np.ndarray, origin: str, configspace: ConfigurationSpace + ) -> list[Configuration]: + """Transforms the continuous designs into a discrete list of configurations. + + Parameters + ---------- + design : np.ndarray + Array of hyperparameters originating from the initial design strategy. + origin : str | None, defaults to None + Label for a configuration where it originated from. + configspace : ConfigurationSpace + + Returns + ------- + configs : list[Configuration] + Continuous transformed configs. + """ + params = configspace.get_hyperparameters() + for idx, param in enumerate(params): + if isinstance(param, NumericalHyperparameter): + continue + elif isinstance(param, Constant): + design_ = np.zeros(np.array(design.shape) + np.array((0, 1))) + design_[:, :idx] = design[:, :idx] + design_[:, idx + 1 :] = design[:, idx:] + design = design_ + elif isinstance(param, CategoricalHyperparameter): + v_design = design[:, idx] + v_design[v_design == 1] = 1 - 10**-10 + design[:, idx] = np.array(v_design * len(param.choices), dtype=int) + elif isinstance(param, OrdinalHyperparameter): + v_design = design[:, idx] + v_design[v_design == 1] = 1 - 10**-10 + design[:, idx] = np.array(v_design * len(param.sequence), dtype=int) + else: + raise ValueError("Hyperparameter not supported in LHD.") + + logger.debug("Initial Design") + configs = [] + for vector in design: + try: + conf = deactivate_inactive_hyperparameters( + configuration=None, configuration_space=configspace, vector=vector + ) + except ForbiddenValueError: + continue + + conf.origin = origin + configs.append(conf) + logger.debug(conf) + + return configs diff --git a/smac/initial_design/default_configuration_design.py b/smac/initial_design/default_configuration_design.py deleted file mode 100644 index f96132355..000000000 --- a/smac/initial_design/default_configuration_design.py +++ /dev/null @@ -1,25 +0,0 @@ -from typing import List - -from ConfigSpace import Configuration - -from smac.initial_design.initial_design import InitialDesign - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2016, ML4AAD" -__license__ = "3-clause BSD" - - -class DefaultConfiguration(InitialDesign): - """Initial design that evaluates default configuration.""" - - def _select_configurations(self) -> List[Configuration]: - """Selects the default configuration. - - Returns - ------- - config: Configuration - Initial incumbent configuration. - """ - config = self.cs.get_default_configuration() - config.origin = "Default" - return [config] diff --git a/smac/initial_design/default_design.py b/smac/initial_design/default_design.py new file mode 100644 index 000000000..cd2184a08 --- /dev/null +++ b/smac/initial_design/default_design.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from ConfigSpace import Configuration + +from smac.initial_design.abstract_initial_design import AbstractInitialDesign + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class DefaultInitialDesign(AbstractInitialDesign): + """Initial design that evaluates only the default configuration.""" + + def _select_configurations(self) -> list[Configuration]: + config = self._configspace.get_default_configuration() + config.origin = "Default" + return [config] diff --git a/smac/initial_design/factorial_design.py b/smac/initial_design/factorial_design.py index 67786ecb8..579d12ff1 100644 --- a/smac/initial_design/factorial_design.py +++ b/smac/initial_design/factorial_design.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import itertools import numpy as np @@ -10,33 +12,17 @@ ) from ConfigSpace.util import deactivate_inactive_hyperparameters -from smac.initial_design.initial_design import InitialDesign +from smac.initial_design.abstract_initial_design import AbstractInitialDesign -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2016, ML4AAD" +__copyright__ = "Copyright 2022, automl.org" __license__ = "3-clause BSD" -class FactorialInitialDesign(InitialDesign): - """Factorial initial design. - - Attributes - ---------- - configs : List[Configuration] - List of configurations to be evaluated - Don't pass configs to the constructor; - otherwise factorial design is overwritten - """ - - def _select_configurations(self) -> Configuration: - """Selects a single configuration to run. +class FactorialInitialDesign(AbstractInitialDesign): + """Factorial initial design to select corner and middle configurations.""" - Returns - ------- - config: Configuration - initial incumbent configuration - """ - params = self.cs.get_hyperparameters() + def _select_configurations(self) -> list[Configuration]: + params = self._configspace.get_hyperparameters() values = [] mid = [] @@ -54,25 +40,22 @@ def _select_configurations(self) -> Configuration: v = [param.sequence[0], param.sequence[-1]] length = len(param.sequence) mid.append(param.sequence[int(length / 2)]) + values.append(v) factorial_design = itertools.product(*values) - self.logger.debug("Initial Design") - configs = [self.cs.get_default_configuration()] + configs = [self._configspace.get_default_configuration()] # add middle point in space conf_dict = dict([(p.name, v) for p, v in zip(params, mid)]) - middle_conf = deactivate_inactive_hyperparameters(conf_dict, self.cs) + middle_conf = deactivate_inactive_hyperparameters(conf_dict, self._configspace) configs.append(middle_conf) - # add corner points + # Add corner points for design in factorial_design: conf_dict = dict([(p.name, v) for p, v in zip(params, design)]) - conf = deactivate_inactive_hyperparameters(conf_dict, self.cs) - conf.origin = "Factorial Design" + conf = deactivate_inactive_hyperparameters(conf_dict, self._configspace) + conf.origin = "Factorial Initial Design" configs.append(conf) - self.logger.debug(conf) - - self.logger.debug("Size of factorial design: %d" % (len(configs))) return configs diff --git a/smac/initial_design/initial_design.py b/smac/initial_design/initial_design.py deleted file mode 100644 index 1fdb211a5..000000000 --- a/smac/initial_design/initial_design.py +++ /dev/null @@ -1,159 +0,0 @@ -from typing import List, Optional - -import logging -from collections import OrderedDict - -import numpy as np -from ConfigSpace.configuration_space import Configuration, ConfigurationSpace -from ConfigSpace.hyperparameters import ( - CategoricalHyperparameter, - Constant, - NumericalHyperparameter, - OrdinalHyperparameter, -) -from ConfigSpace.util import ForbiddenValueError, deactivate_inactive_hyperparameters - -from smac.utils.io.traj_logging import TrajLogger - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2019, AutoML" -__license__ = "3-clause BSD" - - -class InitialDesign: - """Base class for initial design strategies that evaluates multiple configurations. - - Parameters - ---------- - cs: ConfigurationSpace - configuration space object - rng: np.random.RandomState - Random state - traj_logger: TrajLogger - Trajectory logging to add new incumbents found by the initial - design. - ta_run_limit: int - Number of iterations allowed for the target algorithm - configs: Optional[List[Configuration]] - List of initial configurations. Disables the arguments ``n_configs_x_params`` if given. - Either this, or ``n_configs_x_params`` or ``init_budget`` must be provided. - n_configs_x_params: int - how many configurations will be used at most in the initial design (X*D). Either - this, or ``init_budget`` or ``configs`` must be provided. Disables the argument - ``n_configs_x_params`` if given. - max_config_fracs: float - use at most X*budget in the initial design. Not active if a time limit is given. - init_budget : int, optional - Maximal initial budget (disables the arguments ``n_configs_x_params`` and ``configs`` - if both are given). Either this, or ``n_configs_x_params`` or ``configs`` must be - provided. - - Attributes - ---------- - cs : ConfigurationSpace - configs : List[Configuration] - List of configurations to be evaluated - """ - - def __init__( - self, - cs: ConfigurationSpace, - rng: np.random.RandomState, - traj_logger: TrajLogger, - ta_run_limit: int, - configs: Optional[List[Configuration]] = None, - n_configs_x_params: Optional[int] = 10, - max_config_fracs: float = 0.25, - init_budget: Optional[int] = None, - ): - self.cs = cs - self.rng = rng - self.traj_logger = traj_logger - self.configs = configs - - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - n_params = len(self.cs.get_hyperparameters()) - if init_budget is not None: - self.init_budget = init_budget - if n_configs_x_params is not None: - self.logger.debug( - "Ignoring argument `n_configs_x_params` (value %d).", - n_configs_x_params, - ) - elif configs is not None: - self.init_budget = len(configs) - elif n_configs_x_params is not None: - self.init_budget = int(max(1, min(n_configs_x_params * n_params, (max_config_fracs * ta_run_limit)))) - else: - raise ValueError( - "Need to provide either argument `init_budget`, `configs` or " - "`n_configs_x_params`, but provided none of them." - ) - if self.init_budget > ta_run_limit: - raise ValueError( - "Initial budget %d cannot be higher than the run limit %d." % (self.init_budget, ta_run_limit) - ) - self.logger.info("Running initial design for %d configurations" % self.init_budget) - - def select_configurations(self) -> List[Configuration]: - """Selects the initial configurations.""" - if self.init_budget == 0: - return [] - if self.configs is None: - self.configs = self._select_configurations() - - for config in self.configs: - if config.origin is None: - config.origin = "Initial design" - - # add this incumbent right away to have an entry to time point 0 - self.traj_logger.add_entry(train_perf=2**31, incumbent_id=1, incumbent=self.configs[0]) - - # removing duplicates - # (Reference: https://stackoverflow.com/questions/7961363/removing-duplicates-in-lists) - self.configs = list(OrderedDict.fromkeys(self.configs)) - return self.configs - - def _select_configurations(self) -> List[Configuration]: - raise NotImplementedError - - def _transform_continuous_designs( - self, design: np.ndarray, origin: str, cs: ConfigurationSpace - ) -> List[Configuration]: - - params = cs.get_hyperparameters() - for idx, param in enumerate(params): - if isinstance(param, NumericalHyperparameter): - continue - elif isinstance(param, Constant): - # add a vector with zeros - design_ = np.zeros(np.array(design.shape) + np.array((0, 1))) - design_[:, :idx] = design[:, :idx] - design_[:, idx + 1 :] = design[:, idx:] - design = design_ - elif isinstance(param, CategoricalHyperparameter): - v_design = design[:, idx] - v_design[v_design == 1] = 1 - 10**-10 - design[:, idx] = np.array(v_design * len(param.choices), dtype=int) - elif isinstance(param, OrdinalHyperparameter): - v_design = design[:, idx] - v_design[v_design == 1] = 1 - 10**-10 - design[:, idx] = np.array(v_design * len(param.sequence), dtype=int) - else: - raise ValueError("Hyperparameter not supported in LHD") - - self.logger.debug("Initial Design") - configs = [] - for vector in design: - try: - conf = deactivate_inactive_hyperparameters(configuration=None, configuration_space=cs, vector=vector) - except ForbiddenValueError: - continue - conf.origin = origin - configs.append(conf) - self.logger.debug(conf) - - self.logger.debug("Size of initial design: %d" % (len(configs))) - - return configs diff --git a/smac/initial_design/latin_hypercube_design.py b/smac/initial_design/latin_hypercube_design.py index 27c79122e..dacb511ca 100644 --- a/smac/initial_design/latin_hypercube_design.py +++ b/smac/initial_design/latin_hypercube_design.py @@ -1,42 +1,28 @@ -from typing import List +from __future__ import annotations from ConfigSpace.configuration_space import Configuration from ConfigSpace.hyperparameters import Constant from scipy.stats.qmc import LatinHypercube -from smac.initial_design.initial_design import InitialDesign +from smac.initial_design.abstract_initial_design import AbstractInitialDesign -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2019, AutoML" +__copyright__ = "Copyright 2022, automl.org" __license__ = "3-clause BSD" -class LHDesign(InitialDesign): - """Latin Hypercube design. +class LatinHypercubeInitialDesign(AbstractInitialDesign): + """Latin Hypercube design.""" - Attributes - ---------- - configs : List[Configuration] - List of configurations to be evaluated - Don't pass configs to the constructor; - otherwise factorial design is overwritten - """ - - def _select_configurations(self) -> List[Configuration]: - """Selects a single configuration to run. - - Returns - ------- - config: Configuration - initial incumbent configuration - """ - params = self.cs.get_hyperparameters() + def _select_configurations(self) -> list[Configuration]: + params = self._configspace.get_hyperparameters() constants = 0 for p in params: if isinstance(p, Constant): constants += 1 - lhd = LatinHypercube(d=len(params) - constants, seed=self.rng.randint(0, 1000000)).random(n=self.init_budget) + lhd = LatinHypercube(d=len(params) - constants, seed=self._rng.randint(0, 1000000)).random(n=self._n_configs) - return self._transform_continuous_designs(design=lhd, origin="LHD", cs=self.cs) + return self._transform_continuous_designs( + design=lhd, origin="Latin Hypercube Initial Design", configspace=self._configspace + ) diff --git a/smac/initial_design/random_configuration_design.py b/smac/initial_design/random_configuration_design.py deleted file mode 100644 index 08a1f0dc1..000000000 --- a/smac/initial_design/random_configuration_design.py +++ /dev/null @@ -1,28 +0,0 @@ -from typing import List - -from ConfigSpace import Configuration - -from smac.initial_design.initial_design import InitialDesign - -__author__ = "Katharina Eggensperger" -__copyright__ = "Copyright 2016, ML4AAD" -__license__ = "3-clause BSD" - - -class RandomConfigurations(InitialDesign): - """Initial design that evaluates random configurations.""" - - def _select_configurations(self) -> List[Configuration]: - """Select a random configuration. - - Returns - ------- - config: Configuration() - Initial incumbent configuration - """ - configs = self.cs.sample_configuration(size=self.init_budget) - if self.init_budget == 1: - configs = [configs] - for config in configs: - config.origin = "Random initial design." - return configs diff --git a/smac/initial_design/random_design.py b/smac/initial_design/random_design.py new file mode 100644 index 000000000..a5b5d6327 --- /dev/null +++ b/smac/initial_design/random_design.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from ConfigSpace import Configuration + +from smac.initial_design.abstract_initial_design import AbstractInitialDesign + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class RandomInitialDesign(AbstractInitialDesign): + """Initial design that evaluates random configurations.""" + + def _select_configurations(self) -> list[Configuration]: + configs = self._configspace.sample_configuration(size=self._n_configs) + if self._n_configs == 1: + configs = [configs] + for config in configs: + config.origin = "Random Initial Design" + return configs diff --git a/smac/initial_design/sobol_design.py b/smac/initial_design/sobol_design.py index 48ace4b84..c99962e9c 100644 --- a/smac/initial_design/sobol_design.py +++ b/smac/initial_design/sobol_design.py @@ -1,4 +1,6 @@ -from typing import List +from __future__ import annotations + +from typing import Any import warnings @@ -6,35 +8,28 @@ from ConfigSpace.hyperparameters import Constant from scipy.stats.qmc import Sobol -from smac.initial_design.initial_design import InitialDesign +from smac.initial_design.abstract_initial_design import AbstractInitialDesign -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2018, ML4AAD" +__copyright__ = "Copyright 2022, automl.org" __license__ = "3-clause BSD" -class SobolDesign(InitialDesign): - """Sobol sequence design with a scrambled Sobol sequence. - - See https://scipy.github.io/devdocs/reference/generated/scipy.stats.qmc.Sobol.html for further information - - Attributes - ---------- - configs : List[Configuration] - List of configurations to be evaluated - Don't pass configs to the constructor; - otherwise factorial design is overwritten +class SobolInitialDesign(AbstractInitialDesign): + """Sobol sequence design with a scrambled Sobol sequence. See + https://scipy.github.io/devdocs/reference/generated/scipy.stats.qmc.Sobol.html for further information. """ - def _select_configurations(self) -> List[Configuration]: - """Selects a single configuration to run. + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + if len(self._configspace.get_hyperparameters()) > 21201: + raise ValueError( + "The default initial design Sobol sequence can only handle up to 21201 dimensions. " + "Please use a different initial design, such as the Latin Hypercube design." + ) - Returns - ------- - config: Configuration - initial incumbent configuration - """ - params = self.cs.get_hyperparameters() + def _select_configurations(self) -> list[Configuration]: + params = self._configspace.get_hyperparameters() constants = 0 for p in params: @@ -42,10 +37,12 @@ def _select_configurations(self) -> List[Configuration]: constants += 1 dim = len(params) - constants - sobol_gen = Sobol(d=dim, scramble=True, seed=self.rng.randint(low=0, high=10000000)) + sobol_gen = Sobol(d=dim, scramble=True, seed=self._rng.randint(low=0, high=10000000)) with warnings.catch_warnings(): warnings.simplefilter("ignore") - sobol = sobol_gen.random(self.init_budget) + sobol = sobol_gen.random(self._n_configs) - return self._transform_continuous_designs(design=sobol, origin="Sobol", cs=self.cs) + return self._transform_continuous_designs( + design=sobol, origin="Sobol Initial Design", configspace=self._configspace + ) diff --git a/smac/intensification/abstract_racer.py b/smac/intensification/abstract_racer.py deleted file mode 100644 index b1c40bf75..000000000 --- a/smac/intensification/abstract_racer.py +++ /dev/null @@ -1,416 +0,0 @@ -from typing import Iterator, List, Mapping, Optional, Tuple - -import logging -import time -from collections import OrderedDict -from enum import Enum - -import numpy as np - -from smac.configspace import Configuration -from smac.optimizer.configuration_chooser.epm_chooser import EPMChooser -from smac.runhistory.runhistory import RunHistory, RunInfo, RunValue -from smac.stats.stats import Stats -from smac.utils.io.traj_logging import TrajLogger -from smac.utils.logging import format_array - -_config_to_run_type = Iterator[Optional[Configuration]] - -__author__ = "Ashwin Raaghav Narayanan" -__copyright__ = "Copyright 2019, ML4AAD" -__license__ = "3-clause BSD" - - -class RunInfoIntent(Enum): - """Class to define different requests on how to process the runinfo. - - Gives the flexibility to indicate whether a configuration should be skipped (SKIP) or if the - SMBO should simple run a generated run_info. - """ - - RUN = 0 # Normal run execution of a run info - SKIP = 1 # Skip running the run_info - WAIT = 2 # Wait for more configs to be processed - - -class AbstractRacer(object): - """Base class for all racing methods. - - The "intensification" is designed to output a RunInfo object with enough information - to run a given configuration (for example, the run info contains the instance/seed - pair, as well as the associated resources). - - A worker can execute this RunInfo object and produce a RunValue object with the - execution results. Each intensifier process the RunValue object and updates it's - internal state in preparation for the next iteration. - - **Note: Do not use directly** - - Parameters - ---------- - stats: Stats - stats object - traj_logger: smac.utils.io.traj_logging.TrajLogger - TrajLogger object to log all new incumbents - rng : np.random.RandomState - instances : List[str] - list of all instance ids - instance_specifics : Mapping[str, str] - mapping from instance name to instance specific string - cutoff : float - runtime cutoff of TA runs - deterministic: bool - whether the TA is deterministic or not - run_obj_time: bool - whether the run objective is runtime or not (if true, apply adaptive capping) - minR : int - Minimum number of run per config (summed over all calls to - intensify). - maxR : int - Maximum number of runs per config (summed over all calls to - intensifiy). - adaptive_capping_slackfactor: float - slack factor of adpative capping (factor * adpative cutoff) - min_chall: int - minimal number of challengers to be considered (even if time_bound is exhausted earlier) - """ - - def __init__( - self, - stats: Stats, - traj_logger: TrajLogger, - rng: np.random.RandomState, - instances: List[str], - instance_specifics: Optional[Mapping[str, str]] = None, - cutoff: Optional[float] = None, - deterministic: bool = False, - run_obj_time: bool = True, - minR: int = 1, - maxR: int = 2000, - adaptive_capping_slackfactor: float = 1.2, - min_chall: int = 1, - ): - - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - self.stats = stats - self.traj_logger = traj_logger - self.rs = rng - - # scenario info - self.cutoff = cutoff - self.deterministic = deterministic - self.run_obj_time = run_obj_time - - self.minR = minR - self.maxR = maxR - self.adaptive_capping_slackfactor = adaptive_capping_slackfactor - self.min_chall = min_chall - - # instances - if instances is None: - instances = [] - # removing duplicates in the user provided instances - self.instances = list(OrderedDict.fromkeys(instances)) - if instance_specifics is None: - self.instance_specifics = {} # type: Mapping[str, str] - else: - self.instance_specifics = instance_specifics - - # general attributes - self.num_run = 0 # Number of runs done in an iteration so far - self._chall_indx = 0 - self._ta_time = 0.0 - - # attributes for sampling next configuration - # Repeating configurations is discouraged for parallel runs - self.repeat_configs = False - # to mark the end of an iteration - self.iteration_done = False - - def get_next_run( - self, - challengers: Optional[List[Configuration]], - incumbent: Configuration, - chooser: Optional[EPMChooser], - run_history: RunHistory, - repeat_configs: bool = True, - num_workers: int = 1, - ) -> Tuple[RunInfoIntent, RunInfo]: - """Abstract method for choosing the next challenger, to allow for different selections - across intensifiers uses ``_next_challenger()`` by default. - - If no more challengers are available, the method should issue a SKIP via - RunInfoIntent.SKIP, so that a new iteration can sample new configurations. - - Parameters - ---------- - challengers : List[Configuration] - promising configurations - incumbent: Configuration - incumbent configuration - chooser : smac.optimizer.epm_configuration_chooser.EPMChooser - optimizer that generates next configurations to use for racing - run_history : smac.runhistory.runhistory.RunHistory - stores all runs we ran so far - repeat_configs : bool - if False, an evaluated configuration will not be generated again - num_workers: int - the maximum number of workers available - at a given time. - - Returns - ------- - run_info: RunInfo - An object that encapsulates necessary information for a config run - intent: RunInfoIntent - Indicator of how to consume the RunInfo object - """ - raise NotImplementedError() - - def process_results( - self, - run_info: RunInfo, - incumbent: Optional[Configuration], - run_history: RunHistory, - time_bound: float, - result: RunValue, - log_traj: bool = True, - ) -> Tuple[Configuration, float]: - """The intensifier stage will be updated based on the results/status of a configuration - execution. Also, a incumbent will be determined. - - Parameters - ---------- - run_info : RunInfo - A RunInfo containing the configuration that was evaluated - incumbent : Optional[Configuration] - Best configuration seen so far - run_history : RunHistory - stores all runs we ran so far - if False, an evaluated configuration will not be generated again - time_bound : float - time in [sec] available to perform intensify - result: RunValue - Contain the result (status and other methadata) of exercising - a challenger/incumbent. - log_traj: bool - Whether to log changes of incumbents in trajectory - - Returns - ------- - incumbent: Configuration() - current (maybe new) incumbent configuration - inc_perf: float - empirical performance of incumbent configuration - """ - raise NotImplementedError() - - def _next_challenger( - self, - challengers: Optional[List[Configuration]], - chooser: Optional[EPMChooser], - run_history: RunHistory, - repeat_configs: bool = True, - ) -> Optional[Configuration]: - """Retuns the next challenger to use in intensification If challenger is None, then - optimizer will be used to generate the next challenger. - - Parameters - ---------- - challengers : List[Configuration] - promising configurations to evaluate next - chooser : smac.optimizer.epm_configuration_chooser.EPMChooser - a sampler that generates next configurations to use for racing - run_history : smac.runhistory.runhistory.RunHistory - stores all runs we ran so far - repeat_configs : bool - if False, an evaluated configuration will not be generated again - - Returns - ------- - Configuration - next challenger to use - """ - start_time = time.time() - - used_configs = set(run_history.get_all_configs()) - - if challengers: - # iterate over challengers provided - self.logger.debug("Using challengers provided") - chall_gen = (c for c in challengers) # type: _config_to_run_type - elif chooser: - # generating challengers on-the-fly if optimizer is given - self.logger.debug("Generating new challenger from optimizer") - chall_gen = chooser.choose_next() - else: - raise ValueError("No configurations/chooser provided. Cannot generate challenger!") - - self.logger.debug("Time to select next challenger: %.4f" % (time.time() - start_time)) - - # select challenger from the generators - assert chall_gen is not None - for challenger in chall_gen: - # repetitions allowed - if repeat_configs: - return challenger - - # otherwise, select only a unique challenger - if challenger not in used_configs: - return challenger - - self.logger.debug("No valid challenger was generated!") - return None - - def _adapt_cutoff(self, challenger: Configuration, run_history: RunHistory, inc_sum_cost: float) -> float: - """Adaptive capping: Compute cutoff based on time so far used for incumbent and reduce - cutoff for next run of challenger accordingly. - - !Only applicable if self.run_obj_time - - !runs on incumbent should be superset of the runs performed for the - challenger - - Parameters - ---------- - challenger : Configuration - Configuration which challenges incumbent - run_history : smac.runhistory.runhistory.RunHistory - Stores all runs we ran so far - inc_sum_cost: float - Sum of runtimes of all incumbent runs - - Returns - ------- - cutoff: float - Adapted cutoff - """ - if not self.run_obj_time: - raise ValueError("This method only works when the run objective is time") - - curr_cutoff = self.cutoff if self.cutoff is not None else np.inf - - # cost used by challenger for going over all its runs - # should be subset of runs of incumbent (not checked for efficiency - # reasons) - chall_inst_seeds = run_history.get_runs_for_config(challenger, only_max_observed_budget=True) - chal_sum_cost = run_history.sum_cost( - config=challenger, instance_seed_budget_keys=chall_inst_seeds, normalize=True - ) - assert type(chal_sum_cost) == float - - cutoff = min(curr_cutoff, inc_sum_cost * self.adaptive_capping_slackfactor - chal_sum_cost) - return cutoff - - def _compare_configs( - self, - incumbent: Configuration, - challenger: Configuration, - run_history: RunHistory, - log_traj: bool = True, - ) -> Optional[Configuration]: - """Compare two configuration wrt the runhistory and return the one which performs better (or - None if the decision is not safe) - - Decision strategy to return x as being better than y: - 1. x has at least as many runs as y - 2. x performs better than y on the intersection of runs on x and y - - Implicit assumption: - Challenger was evaluated on the same instance-seed pairs as - incumbent - - Parameters - ---------- - incumbent: Configuration - Current incumbent - challenger: Configuration - Challenger configuration - run_history: smac.runhistory.runhistory.RunHistory - Stores all runs we ran so far - log_traj: bool - Whether to log changes of incumbents in trajectory - - Returns - ------- - None or better of the two configurations x,y - """ - inc_runs = run_history.get_runs_for_config(incumbent, only_max_observed_budget=True) - chall_runs = run_history.get_runs_for_config(challenger, only_max_observed_budget=True) - to_compare_runs = set(inc_runs).intersection(chall_runs) - - # performance on challenger runs, the challenger only becomes incumbent - # if it dominates the incumbent - chal_perf = run_history.average_cost(challenger, to_compare_runs, normalize=True) - inc_perf = run_history.average_cost(incumbent, to_compare_runs, normalize=True) - - assert type(chal_perf) == float - assert type(inc_perf) == float - - # Line 15 - if np.any(chal_perf > inc_perf) and len(chall_runs) >= self.minR: - chal_perf_format = format_array(chal_perf) - inc_perf_format = format_array(inc_perf) - # Incumbent beats challenger - self.logger.debug( - f"Incumbent ({inc_perf_format}) is better than challenger " - f"({chal_perf_format}) on {len(chall_runs)} runs." - ) - return incumbent - - # Line 16 - if not set(inc_runs) - set(chall_runs): - # no plateau walks - if np.any(chal_perf >= inc_perf): - chal_perf_format = format_array(chal_perf) - inc_perf_format = format_array(inc_perf) - - self.logger.debug( - f"Incumbent ({inc_perf_format}) is at least as good as the " - f"challenger ({chal_perf_format}) on {len(chall_runs)} runs." - ) - if log_traj and self.stats.inc_changed == 0: - # adding incumbent entry - self.stats.inc_changed += 1 # first incumbent - self.traj_logger.add_entry( - train_perf=chal_perf, - incumbent_id=self.stats.inc_changed, - incumbent=incumbent, - ) - return incumbent - - # Challenger is better than incumbent - # and has at least the same runs as inc - # -> change incumbent - n_samples = len(chall_runs) - chal_perf_format = format_array(chal_perf) - inc_perf_format = format_array(inc_perf) - - self.logger.info( - f"Challenger ({chal_perf_format}) is better than incumbent ({inc_perf_format}) " f"on {n_samples} runs." - ) - self._log_incumbent_changes(incumbent, challenger) - - if log_traj: - self.stats.inc_changed += 1 - self.traj_logger.add_entry( - train_perf=chal_perf, incumbent_id=self.stats.inc_changed, incumbent=challenger - ) - return challenger - - # undecided - return None - - def _log_incumbent_changes( - self, - incumbent: Configuration, - challenger: Configuration, - ) -> None: - params = sorted([(param, incumbent[param], challenger[param]) for param in challenger.keys()]) - self.logger.info("Changes in incumbent:") - for param in params: - if param[1] != param[2]: - self.logger.info(" %s : %r -> %r" % param) - else: - self.logger.debug(" %s remains unchanged: %r", param[0], param[1]) diff --git a/smac/intensification/hyperband.py b/smac/intensification/hyperband.py deleted file mode 100644 index e56a58b88..000000000 --- a/smac/intensification/hyperband.py +++ /dev/null @@ -1,480 +0,0 @@ -from typing import List, Mapping, Optional, Tuple - -import logging - -import numpy as np - -from smac.configspace import Configuration -from smac.intensification.abstract_racer import AbstractRacer, RunInfoIntent -from smac.intensification.parallel_scheduling import ParallelScheduler -from smac.intensification.successive_halving import _SuccessiveHalving -from smac.optimizer.configuration_chooser.epm_chooser import EPMChooser -from smac.runhistory.runhistory import ( # noqa: F401 - RunHistory, - RunInfo, - RunValue, - StatusType, -) -from smac.stats.stats import Stats -from smac.utils.io.traj_logging import TrajLogger - -__author__ = "Ashwin Raaghav Narayanan" -__copyright__ = "Copyright 2019, ML4AAD" -__license__ = "3-clause BSD" - - -class _Hyperband(_SuccessiveHalving): - """Races multiple challengers against an incumbent using Hyperband method. - - This class contains the logic to implement: - "BOHB: Robust and Efficient Hyperparameter Optimization at Scale" (Falkner et al. 2018) - - Objects from this class are meant to run on a single worker. `Hyperband` method, - creates multiple _Hyperband instances to allow parallelism, and for this reason - `Hyperband` should be considered the user interface whereas `_Hyperband` a private - class with the actual implementation of the method. - - Parameters - ---------- - stats: smac.stats.stats.Stats - stats object - traj_logger: smac.utils.io.traj_logging.TrajLogger - TrajLogger object to log all new incumbents - rng : np.random.RandomState - instances : List[str] - list of all instance ids - instance_specifics : Mapping[str, str] - mapping from instance name to instance specific string - cutoff : Optional[int] - runtime cutoff of TA runs - deterministic : bool - whether the TA is deterministic or not - initial_budget : Optional[float] - minimum budget allowed for 1 run of successive halving - max_budget : Optional[float] - maximum budget allowed for 1 run of successive halving - eta : float - 'halving' factor after each iteration in a successive halving run. Defaults to 3 - run_obj_time : bool - whether the run objective is runtime or not (if true, apply adaptive capping) - n_seeds : Optional[int] - Number of seeds to use, if TA is not deterministic. Defaults to None, i.e., seed is set as 0 - instance_order : Optional[str] - how to order instances. Can be set to: [None, shuffle_once, shuffle] - * None - use as is given by the user - * shuffle_once - shuffle once and use across all SH run (default) - * shuffle - shuffle before every SH run - adaptive_capping_slackfactor : float - slack factor of adpative capping (factor * adpative cutoff) - min_chall: int - minimal number of challengers to be considered (even if time_bound is exhausted earlier). This class will - raise an exception if a value larger than 1 is passed. - incumbent_selection: str - How to select incumbent in successive halving. Only active for real-valued budgets. - Can be set to: [highest_executed_budget, highest_budget, any_budget] - * highest_executed_budget - incumbent is the best in the highest budget run so far (default) - * highest_budget - incumbent is selected only based on the highest budget - * any_budget - incumbent is the best on any budget i.e., best performance regardless of budget - identifier: int - Allows to identify the _Hyperband instance in case of multiple ones - """ - - def __init__( - self, - stats: Stats, - traj_logger: TrajLogger, - rng: np.random.RandomState, - instances: List[str], - instance_specifics: Mapping[str, str] = None, - cutoff: Optional[float] = None, - deterministic: bool = False, - initial_budget: Optional[float] = None, - max_budget: Optional[float] = None, - eta: float = 3, - run_obj_time: bool = True, - n_seeds: Optional[int] = None, - instance_order: str = "shuffle_once", - adaptive_capping_slackfactor: float = 1.2, - min_chall: int = 1, - incumbent_selection: str = "highest_executed_budget", - identifier: int = 0, - ) -> None: - - super().__init__( - stats=stats, - traj_logger=traj_logger, - rng=rng, - instances=instances, - instance_specifics=instance_specifics, - cutoff=cutoff, - deterministic=deterministic, - initial_budget=initial_budget, - max_budget=max_budget, - eta=eta, - num_initial_challengers=None, # initial challengers passed as None - run_obj_time=run_obj_time, - n_seeds=n_seeds, - instance_order=instance_order, - adaptive_capping_slackfactor=adaptive_capping_slackfactor, - min_chall=min_chall, - incumbent_selection=incumbent_selection, - ) - - self.identifier = identifier - - self.logger = logging.getLogger(self.__module__ + "." + str(self.identifier) + "." + self.__class__.__name__) - - # to track completed hyperband iterations - self.hb_iters = 0 - self.sh_intensifier = None # type: _SuccessiveHalving # type: ignore[assignment] - - def process_results( - self, - run_info: RunInfo, - incumbent: Optional[Configuration], - run_history: RunHistory, - time_bound: float, - result: RunValue, - log_traj: bool = True, - ) -> Tuple[Configuration, float]: - """The intensifier stage will be updated based on the results/status of a configuration - execution. Also, a incumbent will be determined. - - Parameters - ---------- - run_info : RunInfo - A RunInfo containing the configuration that was evaluated - incumbent : Optional[Configuration] - Best configuration seen so far - run_history : RunHistory - stores all runs we ran so far - if False, an evaluated configuration will not be generated again - time_bound : float - time in [sec] available to perform intensify - result: RunValue - Contain the result (status and other methadata) of exercising - a challenger/incumbent. - log_traj: bool - Whether to log changes of incumbents in trajectory - - Returns - ------- - incumbent: Configuration - current (maybe new) incumbent configuration - inc_perf: float - empirical performance of incumbent configuration - """ - # run 1 iteration of successive halving - incumbent, inc_perf = self.sh_intensifier.process_results( - run_info=run_info, - incumbent=incumbent, - run_history=run_history, - time_bound=time_bound, - result=result, - log_traj=log_traj, - ) - self.num_run += 1 - - # reset if SH iteration is over, else update for next iteration - if self.sh_intensifier.iteration_done: - self._update_stage() - - return incumbent, inc_perf - - def get_next_run( - self, - challengers: Optional[List[Configuration]], - incumbent: Configuration, - chooser: Optional[EPMChooser], - run_history: RunHistory, - repeat_configs: bool = True, - num_workers: int = 1, - ) -> Tuple[RunInfoIntent, RunInfo]: - """Selects which challenger to use based on the iteration stage and set the iteration - parameters. First iteration will choose configurations from the ``chooser`` or input - challengers, while the later iterations pick top configurations from the previously selected - challengers in that iteration. - - If no new run is available, the method returns a configuration of None. - - Parameters - ---------- - challengers : List[Configuration] - promising configurations - incumbent: Configuration - incumbent configuration - chooser : smac.optimizer.epm_configuration_chooser.EPMChooser - optimizer that generates next configurations to use for racing - run_history : smac.runhistory.runhistory.RunHistory - stores all runs we ran so far - repeat_configs : bool - if False, an evaluated configuration will not be generated again - num_workers: int - the maximum number of workers available - at a given time. - - Returns - ------- - intent: RunInfoIntent - Indicator of how to consume the RunInfo object - run_info: RunInfo - An object that encapsulates necessary information for a config run - """ - if num_workers > 1: - raise ValueError( - "HyperBand does not support more than 1 worker, yet " - "the argument num_workers to get_next_run is {}".format(num_workers) - ) - - if not hasattr(self, "s"): - # initialize tracking variables - self._update_stage() - - # sampling from next challenger marks the beginning of a new iteration - self.iteration_done = False - - intent, run_info = self.sh_intensifier.get_next_run( - challengers=challengers, - incumbent=incumbent, - chooser=chooser, - run_history=run_history, - repeat_configs=self.sh_intensifier.repeat_configs, - ) - - # For testing purposes, this attribute highlights whether a - # new challenger is proposed or not. Not required from a functional - # perspective - self.new_challenger = self.sh_intensifier.new_challenger - - return intent, run_info - - def _update_stage(self, run_history: RunHistory = None) -> None: - """Update tracking information for a new stage/iteration and update statistics. This method - is called to initialize stage variables and after all configurations of a successive halving - stage are completed. - - Parameters - ---------- - run_history : smac.runhistory.runhistory.RunHistory - stores all runs we ran so far - """ - if not hasattr(self, "s"): - # setting initial running budget for future iterations (s & s_max from Algorithm 1) - self.s_max = int(np.floor(np.log(self.max_budget / self.initial_budget) / np.log(self.eta))) - self.s = self.s_max - elif self.s == 0: - # reset if HB iteration is over - self.s = self.s_max - self.hb_iters += 1 - self.iteration_done = True - self.num_run = 0 - else: - # update for next iteration - self.s -= 1 - - # compute min budget for new SH run - sh_initial_budget = self.eta**-self.s * self.max_budget - # sample challengers for next iteration (based on HpBandster package) - n_challengers = int(np.floor((self.s_max + 1) / (self.s + 1)) * self.eta**self.s) - - # Compute this for the next round - n_configs_in_stage = n_challengers * np.power(self.eta, -np.linspace(0, self.s, self.s + 1)) - n_configs_in_stage = np.array(np.round(n_configs_in_stage), dtype=int).tolist() - - self.logger.info( - "Hyperband iteration-step: %d-%d with initial budget: %d" - % (self.hb_iters + 1, self.s_max - self.s + 1, sh_initial_budget) - ) - - # creating a new Successive Halving intensifier with the current running budget - self.sh_intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=self.traj_logger, - rng=self.rs, - instances=self.instances, - instance_specifics=self.instance_specifics, - cutoff=self.cutoff, - deterministic=self.deterministic, - initial_budget=sh_initial_budget, - max_budget=self.max_budget, - eta=self.eta, - _all_budgets=self.all_budgets[(-self.s - 1) :], - _n_configs_in_stage=n_configs_in_stage, - num_initial_challengers=n_challengers, - run_obj_time=self.run_obj_time, - n_seeds=self.n_seeds, - instance_order=self.instance_order, - adaptive_capping_slackfactor=self.adaptive_capping_slackfactor, - inst_seed_pairs=self.inst_seed_pairs, # additional argument to avoid - identifier=self.identifier, - ) # processing instances & seeds again - - -class Hyperband(ParallelScheduler): - """Races multiple challengers against an incumbent using Hyperband method. - - Implementation from "BOHB: Robust and Efficient Hyperparameter Optimization at Scale" (Falkner et al. 2018) - - Hyperband is an extension of the Successive Halving intensifier. Please refer to `SuccessiveHalving` documentation - for more detailed information about the different types of budgets possible and the way instances are handled. - - Internally, this class uses the _Hyperband private class which actually implements the hyperband logic. - To allow for parallelism, Hyperband can create multiple _Hyperband instances, based on the number - of idle workers available. - - Parameters - ---------- - stats: smac.stats.stats.Stats - stats object - traj_logger: smac.utils.io.traj_logging.TrajLogger - TrajLogger object to log all new incumbents - rng : np.random.RandomState - instances : List[str] - list of all instance ids - instance_specifics : Mapping[str, str] - mapping from instance name to instance specific string - cutoff : Optional[int] - runtime cutoff of TA runs - deterministic : bool - whether the TA is deterministic or not - initial_budget : Optional[float] - minimum budget allowed for 1 run of successive halving - max_budget : Optional[float] - maximum budget allowed for 1 run of successive halving - eta : float - 'halving' factor after each iteration in a successive halving run. Defaults to 3 - run_obj_time : bool - whether the run objective is runtime or not (if true, apply adaptive capping) - n_seeds : Optional[int] - Number of seeds to use, if TA is not deterministic. Defaults to None, i.e., seed is set as 0 - instance_order : Optional[str] - how to order instances. Can be set to: [None, shuffle_once, shuffle] - * None - use as is given by the user - * shuffle_once - shuffle once and use across all SH run (default) - * shuffle - shuffle before every SH run - adaptive_capping_slackfactor : float - slack factor of adpative capping (factor * adpative cutoff) - min_chall: int - minimal number of challengers to be considered (even if time_bound is exhausted earlier). This class will - raise an exception if a value larger than 1 is passed. - incumbent_selection: str - How to select incumbent in successive halving. Only active for real-valued budgets. - Can be set to: [highest_executed_budget, highest_budget, any_budget] - * highest_executed_budget - incumbent is the best in the highest budget run so far (default) - * highest_budget - incumbent is selected only based on the highest budget - * any_budget - incumbent is the best on any budget i.e., best performance regardless of budget - """ - - def __init__( - self, - stats: Stats, - traj_logger: TrajLogger, - rng: np.random.RandomState, - instances: List[str], - instance_specifics: Mapping[str, str] = None, - cutoff: Optional[float] = None, - deterministic: bool = False, - initial_budget: Optional[float] = None, - max_budget: Optional[float] = None, - eta: float = 3, - run_obj_time: bool = True, - n_seeds: Optional[int] = None, - instance_order: str = "shuffle_once", - adaptive_capping_slackfactor: float = 1.2, - min_chall: int = 1, - incumbent_selection: str = "highest_executed_budget", - ) -> None: - - super().__init__( - stats=stats, - traj_logger=traj_logger, - rng=rng, - instances=instances, - instance_specifics=instance_specifics, - cutoff=cutoff, - deterministic=deterministic, - run_obj_time=run_obj_time, - adaptive_capping_slackfactor=adaptive_capping_slackfactor, - min_chall=min_chall, - ) - - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - # Parameters for a new hyperband - self.n_seeds = n_seeds - self.instance_order = instance_order - self.incumbent_selection = incumbent_selection - self._instances = instances - self._instance_specifics = instance_specifics - self.initial_budget = initial_budget - self.max_budget = max_budget - self.eta = eta - - def _get_intensifier_ranking(self, intensifier: AbstractRacer) -> Tuple[int, int]: - """Given a intensifier, returns how advance it is. This metric will be used to determine - what priority to assign to the intensifier. - - Parameters - ---------- - intensifier: AbstractRacer - Intensifier to rank based on run progress - - Returns - ------- - ranking: int - the higher this number, the faster the intensifier will get - the running resources. For hyperband we can use the - sh_intensifier stage, for example - tie_breaker: int - The configurations that have been launched to break ties. For - example, in the case of Successive Halving it can be the number - of configurations launched - """ - # For mypy -- we expect to work with _Hyperband instances - assert isinstance(intensifier, _Hyperband) - - # For hyperband, we use the internal successive halving as a criteria - # to see how advanced this intensifier is - stage = 0 - if hasattr(intensifier.sh_intensifier, "stage"): - # Newly created SuccessiveHalving objects have no stage - stage = intensifier.sh_intensifier.stage - return stage, len(intensifier.sh_intensifier.run_tracker) - - def _add_new_instance(self, num_workers: int) -> bool: - """Decides if it is possible to add a new intensifier instance, and adds it. If a new - intensifier instance is added, True is returned, else False. - - Parameters - ---------- - num_workers: int - the maximum number of workers available at a given time. - - Returns - ------- - Whether or not a new instance was added. - """ - if len(self.intensifier_instances) >= num_workers: - return False - - self.intensifier_instances[len(self.intensifier_instances)] = _Hyperband( - stats=self.stats, - traj_logger=self.traj_logger, - rng=self.rs, - instances=self._instances, - instance_specifics=self._instance_specifics, - cutoff=self.cutoff, - deterministic=self.deterministic, - initial_budget=self.initial_budget, - max_budget=self.max_budget, - eta=self.eta, - run_obj_time=self.run_obj_time, - n_seeds=self.n_seeds, - instance_order=self.instance_order, - adaptive_capping_slackfactor=self.adaptive_capping_slackfactor, - min_chall=self.min_chall, - incumbent_selection=self.incumbent_selection, - identifier=len(self.intensifier_instances), - ) - - return True diff --git a/smac/intensification/intensification.py b/smac/intensification/intensification.py deleted file mode 100644 index 83322d3a3..000000000 --- a/smac/intensification/intensification.py +++ /dev/null @@ -1,1014 +0,0 @@ -from typing import List, Mapping, Optional, Tuple, cast - -import logging -from collections import Counter -from enum import Enum - -import numpy as np - -from smac.configspace import Configuration -from smac.intensification.abstract_racer import ( - AbstractRacer, - RunInfoIntent, - _config_to_run_type, -) -from smac.optimizer.configuration_chooser.epm_chooser import EPMChooser -from smac.runhistory.runhistory import ( - InstSeedBudgetKey, - RunHistory, - RunInfo, - RunValue, - StatusType, -) -from smac.stats.stats import Stats -from smac.utils.constants import MAXINT -from smac.utils.io.traj_logging import TrajLogger -from smac.utils.logging import format_array - -__author__ = "Katharina Eggensperger, Marius Lindauer" -__copyright__ = "Copyright 2018, ML4AAD" -__license__ = "3-clause BSD" - - -class NoMoreChallengers(Exception): - """Indicates that no more challengers are available for the intensification to proceed.""" - - pass - - -class IntensifierStage(Enum): - """Class to define different stages of intensifier.""" - - RUN_FIRST_CONFIG = 0 # to replicate the old initial design - RUN_INCUMBENT = 1 # Lines 3-7 - RUN_CHALLENGER = 2 # Lines 8-17 - RUN_BASIS = 3 - - # helpers to determine what type of run to process - # A challenger is assumed to be processed if the stage - # is not from first_config or incumbent - PROCESS_FIRST_CONFIG_RUN = 4 - PROCESS_INCUMBENT_RUN = 5 - - -class Intensifier(AbstractRacer): - r"""Races challengers against an incumbent. - SMAC's intensification procedure, in detail: - - Procedure 2: Intensify(Θ_new, θ_inc, M, R, t_intensify, Π, cˆ) - cˆ(θ, Π') denotes the empirical cost of θ on the subset of instances - Π' ⊆ Π, based on the runs in R; maxR is a parameter - where: - Θ_new: Sequence of parameter settings to evaluate, challengers in this class. - θ_inc: incumbent parameter setting, incumbent in this class. - - 1 for i := 1, . . . , length(Θ_new) do - 2 θ_new ← Θ_new[i] - - STAGE-->RUN_INCUMBENT - - 3 if R contains less than maxR runs with configuration θ_inc then - 4 Π' ← {π'∈ Π | R contains less than or equal number of runs using θ_inc and π' - 0 than using θ_inc and any other π''∈ Π} - 5 π ← instance sampled uniformly at random from Π' - 6 s ← seed, drawn uniformly at random - 7 R ← ExecuteRun(R, θ_inc, π, s) - 8 N ← 1 - - STAGE-->RUN_CHALLENGER - - 9 while true do - 10 S_missing ← {instance, seed} pairs for which θ_inc was run before, but not θ_new - 11 S_torun ← random subset of S_missing of size min(N, size(S_missing)) - 12 foreach (π, s) ∈ S_torun do R ← ExecuteRun(R, θ_new, π, s) - 13 S_missing ← S_missing \\ S_torun - 14 Π_common ← instances for which we previously ran both θ_inc and θ_new - 15 if cˆ(θ_new, Π_common) > cˆ(θ_inc, Π_common) then break - 16 else if S_missing = ∅ then θ_inc ← θ_new; break - 17 else N ← 2 · N - 18 if time spent in this call to this procedure exceeds t_intensify and i ≥ 2 then break - 19 return [R, θ_inc] - - Parameters - ---------- - stats: Stats - stats object - traj_logger: TrajLogger - TrajLogger object to log all new incumbents - rng : np.random.RandomState - instances : List[str] - list of all instance ids - instance_specifics : Mapping[str, str] - mapping from instance name to instance specific string - cutoff : int - runtime cutoff of TA runs - deterministic: bool - whether the TA is deterministic or not - run_obj_time: bool - whether the run objective is runtime or not (if true, apply adaptive capping) - always_race_against: Configuration - if incumbent changes race this configuration always against new incumbent; - can sometimes prevent over-tuning - use_ta_time_bound: bool, - if true, trust time reported by the target algorithms instead of - measuring the wallclock time for limiting the time of intensification - run_limit : int - Maximum number of target algorithm runs per call to intensify. - maxR : int - Maximum number of runs per config (summed over all calls to - intensifiy). - minR : int - Minimum number of run per config (summed over all calls to - intensify). - adaptive_capping_slackfactor: float - slack factor of adpative capping (factor * adpative cutoff) - min_chall: int - minimal number of challengers to be considered - (even if time_bound is exhausted earlier) - """ - - def __init__( - self, - stats: Stats, - traj_logger: TrajLogger, - rng: np.random.RandomState, - instances: List[str], - instance_specifics: Mapping[str, str] = None, - cutoff: int = None, - deterministic: bool = False, - run_obj_time: bool = True, - always_race_against: Configuration = None, - run_limit: int = MAXINT, - use_ta_time_bound: bool = False, - minR: int = 1, - maxR: int = 2000, - adaptive_capping_slackfactor: float = 1.2, - min_chall: int = 2, - ): - super().__init__( - stats=stats, - traj_logger=traj_logger, - rng=rng, - instances=instances, - instance_specifics=instance_specifics, - cutoff=cutoff, - deterministic=deterministic, - run_obj_time=run_obj_time, - minR=minR, - maxR=maxR, - adaptive_capping_slackfactor=adaptive_capping_slackfactor, - min_chall=min_chall, - ) - - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - # general attributes - self.run_limit = run_limit - self.always_race_against = always_race_against - - if self.run_limit < 1: - raise ValueError("run_limit must be > 1") - - self.use_ta_time_bound = use_ta_time_bound - self.elapsed_time = 0.0 - - # stage variables - # the intensification procedure is divided into 4 'stages': - # 0. run 1st configuration (only in the 1st run when incumbent=None) - # 1. add incumbent run - # 2. race challenger - # 3. race against configuration for a new incumbent - self.stage = IntensifierStage.RUN_FIRST_CONFIG - self.n_iters = 0 - - # challenger related variables - self._chall_indx = 0 - self.num_chall_run = 0 - self.current_challenger = None - self.continue_challenger = False - self.configs_to_run = iter([]) # type: _config_to_run_type - self.update_configs_to_run = True - - # racing related variables - self.to_run = [] # type: List[InstSeedBudgetKey] - self.inc_sum_cost = np.inf - self.N = -1 - - def get_next_run( - self, - challengers: Optional[List[Configuration]], - incumbent: Configuration, - chooser: Optional[EPMChooser], - run_history: RunHistory, - repeat_configs: bool = True, - num_workers: int = 1, - ) -> Tuple[RunInfoIntent, RunInfo]: - """This procedure is in charge of generating a RunInfo object to comply with lines 7 (in - case stage is stage==RUN_INCUMBENT) or line 12 (In case of stage==RUN_CHALLENGER) - - A RunInfo object encapsulates the necessary information for a worker - to execute the job, nevertheless, a challenger is not always available. - This could happen because no more configurations are available or the new - configuration to try was already executed. - - To circumvent this, a intent is also returned: - - - (intent=RUN) Run the RunInfo object (Normal Execution - - (intent=SKIP) Skip this iteration. No challenger is available, in particular - because challenger is the same as incumbent - - Parameters - ---------- - challengers : List[Configuration] - promising configurations - incumbent: Configuration - incumbent configuration - chooser : smac.optimizer.epm_configuration_chooser.EPMChooser - optimizer that generates next configurations to use for racing - run_history : RunHistory - stores all runs we ran so far - repeat_configs : bool - if False, an evaluated configuration will not be generated again - num_workers: int - the maximum number of workers available - at a given time. - - Returns - ------- - intent: RunInfoIntent - What should the smbo object do with the runinfo. - run_info: RunInfo - An object that encapsulates necessary information for a config run - """ - if num_workers > 1: - raise ValueError( - "Intensifier does not support more than 1 worker, yet " - "the argument num_workers to get_next_run is {}".format(num_workers) - ) - - # If this function is called, it means the iteration is - # not complete (we can be starting a new iteration, or re-running a - # challenger due to line 17). We evaluate if a iteration is complete or not - # via _process_results - self.iteration_done = False - - # In case a crash happens, and FirstRunCrashedException prevents a - # failure, revert back to running the incumbent - # Challenger case is by construction ok, as there is no special - # stage for its processing - if self.stage == IntensifierStage.PROCESS_FIRST_CONFIG_RUN: - self.stage = IntensifierStage.RUN_FIRST_CONFIG - elif self.stage == IntensifierStage.PROCESS_INCUMBENT_RUN: - self.stage = IntensifierStage.RUN_INCUMBENT - - # if first ever run, then assume current challenger to be the incumbent - # Because this is the first ever run, we need to sample a new challenger - # This new challenger is also assumed to be the incumbent - if self.stage == IntensifierStage.RUN_FIRST_CONFIG: - if incumbent is None: - self.logger.info("First run, no incumbent provided;" " challenger is assumed to be the incumbent") - challenger, self.new_challenger = self.get_next_challenger( - challengers=challengers, - chooser=chooser, - ) - incumbent = challenger - else: - inc_runs = run_history.get_runs_for_config(incumbent, only_max_observed_budget=True) - if len(inc_runs) > 0: - self.logger.debug("Skipping RUN_FIRST_CONFIG stage since " "incumbent has already been ran") - self.stage = IntensifierStage.RUN_INCUMBENT - - # LINES 3-7 - if self.stage in [IntensifierStage.RUN_FIRST_CONFIG, IntensifierStage.RUN_INCUMBENT]: - - # Line 3 - # A modified version, that not only checks for maxR - # but also makes sure that there are runnable instances, - # that is, instances has not been exhausted - inc_runs = run_history.get_runs_for_config(incumbent, only_max_observed_budget=True) - - # Line 4 - available_insts = self._get_inc_available_inst(incumbent, run_history) - if available_insts and len(inc_runs) < self.maxR: - # Lines 5-6-7 - instance, seed, cutoff = self._get_next_inc_run(available_insts) - - instance_specific = "0" - if instance is not None: - instance_specific = self.instance_specifics.get(instance, "0") - - return RunInfoIntent.RUN, RunInfo( - config=incumbent, - instance=instance, - instance_specific=instance_specific, - seed=seed, - cutoff=cutoff, - capped=False, - budget=0.0, - ) - else: - # This point marks the transitions from lines 3-7 - # to 8-18. - - self.logger.debug("No further instance-seed pairs for incumbent available.") - - self.stage = IntensifierStage.RUN_CHALLENGER - - # Understand who is the active challenger. - if self.stage == IntensifierStage.RUN_BASIS: - # if in RUN_BASIS stage, - # return the basis configuration (i.e., `always_race_against`) - self.logger.debug("Race against basis configuration after incumbent change.") - challenger = self.always_race_against - elif self.current_challenger and self.continue_challenger: - # if the current challenger could not be rejected, - # it is run again on more instances - challenger = self.current_challenger - else: - # Get a new challenger if all instance/pairs have - # been completed. Else return the currently running - # challenger - challenger, self.new_challenger = self.get_next_challenger( - challengers=challengers, - chooser=chooser, - ) - - # No new challengers are available for this iteration, - # Move to the next iteration. This can only happen - # when all configurations for this iteration are exhausted - # and have been run in all proposed instance/pairs. - if challenger is None: - return RunInfoIntent.SKIP, RunInfo( - config=None, - instance=None, - instance_specific="0", - seed=0, - cutoff=self.cutoff, - capped=False, - budget=0.0, - ) - - # Skip the iteration if the challenger was previously run - if challenger == incumbent and self.stage == IntensifierStage.RUN_CHALLENGER: - self.challenger_same_as_incumbent = True - self.logger.debug("Challenger was the same as the current incumbent; Skipping challenger") - return RunInfoIntent.SKIP, RunInfo( - config=None, - instance=None, - instance_specific="0", - seed=0, - cutoff=self.cutoff, - capped=False, - budget=0.0, - ) - - self.logger.debug("Intensify on %s", challenger) - if hasattr(challenger, "origin"): - self.logger.debug("Configuration origin: %s", challenger.origin) - - if self.stage in [IntensifierStage.RUN_CHALLENGER, IntensifierStage.RUN_BASIS]: - - if not self.to_run: - self.to_run, self.inc_sum_cost = self._get_instances_to_run( - incumbent=incumbent, challenger=challenger, run_history=run_history, N=self.N - ) - - is_there_time_due_to_adaptive_cap = self._is_there_time_due_to_adaptive_cap( - challenger=challenger, - run_history=run_history, - ) - - # If there is no more configs to run in this iteration, or no more - # time to do so, change the current stage base on how the current - # challenger performs as compared to the incumbent. This is done - # via _process_racer_results - if len(self.to_run) == 0 or not is_there_time_due_to_adaptive_cap: - - # If no more time, stage transition is a must - if not is_there_time_due_to_adaptive_cap: - # Since the challenger fails to outperform the incumbent due to adaptive capping, - # we discard all the forthcoming runs. - self.to_run = [] - self.stage = IntensifierStage.RUN_INCUMBENT - self.logger.debug("Stop challenger itensification due " "to adaptive capping.") - - # Nevertheless, if there are no more instances to run, - # we might need to comply with line 17 and keep running the - # same challenger. In this case, if there is not enough information - # to decide if the challenger is better/worst than the incumbent, - # line 17 doubles the number of instances to run. - self.logger.debug("No further runs for challenger possible") - self._process_racer_results( - challenger=challenger, - incumbent=incumbent, - run_history=run_history, - ) - - # Request SMBO to skip this run. This function will - # be called again, after the _process_racer_results - # has updated the intensifier stage - return RunInfoIntent.SKIP, RunInfo( - config=None, - instance=None, - instance_specific="0", - seed=0, - cutoff=self.cutoff, - capped=False, - budget=0.0, - ) - - else: - # Lines 8-11 - incumbent, instance, seed, cutoff = self._get_next_racer( - challenger=challenger, - incumbent=incumbent, - run_history=run_history, - ) - - capped = False - if (self.cutoff is not None) and (cutoff < self.cutoff): # type: ignore[operator] # noqa F821 - capped = True - - instance_specific = "0" - if instance is not None: - instance_specific = self.instance_specifics.get(instance, "0") - - # Line 12 - return RunInfoIntent.RUN, RunInfo( - config=challenger, - instance=instance, - instance_specific=instance_specific, - seed=seed, - cutoff=cutoff, - capped=capped, - budget=0.0, - ) - else: - raise ValueError("No valid stage found!") - - def process_results( - self, - run_info: RunInfo, - incumbent: Optional[Configuration], - run_history: RunHistory, - time_bound: float, - result: RunValue, - log_traj: bool = True, - ) -> Tuple[Configuration, float]: - """The intensifier stage will be updated based on the results/status of a configuration - execution. - - During intensification, the following can happen: - - * Challenger raced against incumbent - * Also, during a challenger run, a capped exception - can be triggered, where no racer post processing is needed - * A run on the incumbent for more confidence needs to - be processed, IntensifierStage.PROCESS_INCUMBENT_RUN - * The first run results need to be processed - (PROCESS_FIRST_CONFIG_RUN) - - At the end of any run, checks are done to move to a new iteration. - - Parameters - ---------- - run_info : RunInfo - A RunInfo containing the configuration that was evaluated - incumbent : Optional[Configuration] - best configuration so far, None in 1st run - run_history : RunHistory - stores all runs we ran so far - if False, an evaluated configuration will not be generated again - time_bound : float - time in [sec] available to perform intensify - result: RunValue - Contain the result (status and other methadata) of exercising - a challenger/incumbent. - log_traj: bool - whether to log changes of incumbents in trajectory - - Returns - ------- - incumbent: Configuration() - current (maybe new) incumbent configuration - inc_perf: float - empirical performance of incumbent configuration - """ - if self.stage == IntensifierStage.PROCESS_FIRST_CONFIG_RUN: - if incumbent is None: - self.logger.info("First run, no incumbent provided;" " challenger is assumed to be the incumbent") - incumbent = run_info.config - - if self.stage in [ - IntensifierStage.PROCESS_INCUMBENT_RUN, - IntensifierStage.PROCESS_FIRST_CONFIG_RUN, - ]: - self._ta_time += result.time - self.num_run += 1 - self._process_inc_run( - incumbent=incumbent, - run_history=run_history, - log_traj=log_traj, - ) - - else: - self.num_run += 1 - self.num_chall_run += 1 - if result.status == StatusType.CAPPED: - # move on to the next iteration - self.logger.debug("Challenger itensification timed out due " "to adaptive capping.") - self.stage = IntensifierStage.RUN_INCUMBENT - else: - - self._ta_time += result.time - incumbent = self._process_racer_results( - challenger=run_info.config, - incumbent=incumbent, - run_history=run_history, - log_traj=log_traj, - ) - - self.elapsed_time += result.endtime - result.starttime - # check if 1 intensification run is complete - line 18 - # this is different to regular SMAC as it requires at least successful challenger run, - # which is necessary to work on a fixed grid of configurations. - if ( - self.stage == IntensifierStage.RUN_INCUMBENT - and self._chall_indx >= self.min_chall - and self.num_chall_run > 0 - ): - if self.num_run > self.run_limit: - self.logger.debug("Maximum #runs for intensification reached") - self._next_iteration() - - if not self.use_ta_time_bound and self.elapsed_time - time_bound >= 0: - self.logger.debug( - "Wallclock time limit for intensification reached " "(used: %f sec, available: %f sec)", - self.elapsed_time, - time_bound, - ) - - self._next_iteration() - - elif self._ta_time - time_bound >= 0: - self.logger.debug( - "TA time limit for intensification reached (used: %f sec, available: %f sec)", - self._ta_time, - time_bound, - ) - - self._next_iteration() - - inc_perf = run_history.get_cost(incumbent) - - return incumbent, inc_perf - - def _get_next_inc_run( - self, - available_insts: List[str], - ) -> Tuple[str, int, Optional[float]]: - """Method to extract the next seed/instance in which a incumbent run most be evaluated. - - Parameters - ---------- - available_insts : List[str] - A list of instances from which to extract the next incumbent run - - Returns - ------- - instance: str - Next instance to evaluate - seed: float - Seed in which to evaluate the instance - cutoff: Optional[float] - Max time for a given instance/seed pair - """ - # Line 5 - and avoid https://github.com/numpy/numpy/issues/10791 - _idx = self.rs.choice(len(available_insts)) - next_instance = available_insts[_idx] - - # Line 6 - if self.deterministic: - next_seed = 0 - else: - next_seed = int(self.rs.randint(low=0, high=MAXINT, size=1)[0]) - - # Line 7 - self.logger.debug("Add run of incumbent for instance={}".format(next_instance)) - if self.stage == IntensifierStage.RUN_FIRST_CONFIG: - self.stage = IntensifierStage.PROCESS_FIRST_CONFIG_RUN - else: - self.stage = IntensifierStage.PROCESS_INCUMBENT_RUN - - return next_instance, next_seed, self.cutoff - - def _get_inc_available_inst( - self, - incumbent: Configuration, - run_history: RunHistory, - log_traj: bool = True, - ) -> List[str]: - """Implementation of line 4 of Intensification. - - This method queries the inc runs in the run history - and return the pending instances if any is available - - Parameters - ---------- - incumbent: Configuration - Either challenger or incumbent - run_history : RunHistory - stores all runs we ran so far - log_traj: bool - Whether to log changes of incumbents in trajectory - """ - # Line 4 - # find all instances that have the most runs on the inc - inc_runs = run_history.get_runs_for_config(incumbent, only_max_observed_budget=True) - inc_inst = [s.instance for s in inc_runs] - inc_inst = list(Counter(inc_inst).items()) - - inc_inst.sort(key=lambda x: x[1], reverse=True) - try: - max_runs = inc_inst[0][1] - except IndexError: - self.logger.debug("No run for incumbent found") - max_runs = 0 - inc_inst = [x[0] for x in inc_inst if x[1] == max_runs] - - available_insts = list(sorted(set(self.instances) - set(inc_inst))) - - # if all instances were used n times, we can pick an instances - # from the complete set again - if not self.deterministic and not available_insts: - available_insts = self.instances - return available_insts - - def _process_inc_run( - self, - incumbent: Configuration, - run_history: RunHistory, - log_traj: bool = True, - ) -> None: - """Method to process the results of a challenger that races an incumbent. - - Parameters - ---------- - incumbent: Configuration - Either challenger or incumbent - run_history : RunHistory - stores all runs we ran so far - log_traj: bool - Whether to log changes of incumbents in trajectory - """ - # output estimated performance of incumbent - inc_runs = run_history.get_runs_for_config(incumbent, only_max_observed_budget=True) - inc_perf = run_history.get_cost(incumbent) - format_value = format_array(inc_perf) - self.logger.info(f"Updated estimated cost of incumbent on {len(inc_runs)} runs: {format_value}") - - # if running first configuration, go to next stage after 1st run - if self.stage in [ - IntensifierStage.RUN_FIRST_CONFIG, - IntensifierStage.PROCESS_FIRST_CONFIG_RUN, - ]: - self.stage = IntensifierStage.RUN_INCUMBENT - self._next_iteration() - else: - # Termination condition; after each run, this checks - # whether further runs are necessary due to minR - if len(inc_runs) >= self.minR or len(inc_runs) >= self.maxR: - self.stage = IntensifierStage.RUN_CHALLENGER - else: - self.stage = IntensifierStage.RUN_INCUMBENT - - self._compare_configs(incumbent=incumbent, challenger=incumbent, run_history=run_history, log_traj=log_traj) - - def _get_next_racer( - self, - challenger: Configuration, - incumbent: Configuration, - run_history: RunHistory, - log_traj: bool = True, - ) -> Tuple[Configuration, str, int, Optional[float]]: - """Method to return the next config setting to aggressively race challenger against - incumbent. - - Parameters - ---------- - challenger : Configuration - Configuration which challenges incumbent - incumbent : Configuration - Best configuration so far - run_history : RunHistory - Stores all runs we ran so far - log_traj: bool - Whether to log changes of incumbents in trajectory - - Returns - ------- - new_incumbent: Configuration - Either challenger or incumbent - instance: str - Next instance to evaluate - seed: int - Seed in which to evaluate the instance - cutoff: Optional[float] - Max time for a given instance/seed pair - """ - # By the time this function is called, the run history might - # have shifted. Re-populate the list if necessary - if not self.to_run: - # Lines 10/11 - self.to_run, self.inc_sum_cost = self._get_instances_to_run( - incumbent=incumbent, challenger=challenger, run_history=run_history, N=self.N - ) - - # Run challenger on all to run - instance, seed, _ = self.to_run.pop() - - cutoff = self.cutoff - if self.run_obj_time: - cutoff = self._adapt_cutoff(challenger=challenger, run_history=run_history, inc_sum_cost=self.inc_sum_cost) - - self.logger.debug("Cutoff for challenger: %s" % str(cutoff)) - - self.logger.debug("Add run of challenger") - - # Line 12 - return incumbent, instance, seed, cutoff - - def _is_there_time_due_to_adaptive_cap( - self, - challenger: Configuration, - run_history: RunHistory, - ) -> bool: - """A check to see if there is no more time for a challenger given the fact, that we are - optimizing time and the incumbent looks more promising Line 18. - - Parameters - ---------- - challenger : Configuration - Configuration which challenges incumbent - run_history : RunHistory - Stores all runs we ran so far - Returns - ------- - bool: - whether or not there is more time for a challenger run - """ - # If time is not objective, then there is always time! - if not self.run_obj_time: - return True - - cutoff = self._adapt_cutoff(challenger=challenger, run_history=run_history, inc_sum_cost=self.inc_sum_cost) - if cutoff is not None and cutoff <= 0: - return False - else: - return True - - def _process_racer_results( - self, - challenger: Configuration, - incumbent: Configuration, - run_history: RunHistory, - log_traj: bool = True, - ) -> Optional[Configuration]: - """Process the result of a racing configuration against the current incumbent. Might propose - a new incumbent. - - Parameters - ---------- - challenger : Configuration - Configuration which challenges incumbent - incumbent : Configuration - Best configuration so far - run_history : RunHistory - Stores all runs we ran so far - - Returns - ------- - new_incumbent: Optional[Configuration] - Either challenger or incumbent - """ - chal_runs = run_history.get_runs_for_config(challenger, only_max_observed_budget=True) - chal_perf = run_history.get_cost(challenger) - - # if all have been run, compare challenger performance - if not self.to_run: - new_incumbent = self._compare_configs( - incumbent=incumbent, - challenger=challenger, - run_history=run_history, - log_traj=log_traj, - ) - - # update intensification stage - if new_incumbent == incumbent: - # move on to the next iteration - self.stage = IntensifierStage.RUN_INCUMBENT - self.continue_challenger = False - self.logger.debug( - "Estimated cost of challenger on %d runs: %.4f, but worse than incumbent", - len(chal_runs), - chal_perf, - ) - - elif new_incumbent == challenger: - # New incumbent found - incumbent = challenger - self.continue_challenger = False - # compare against basis configuration if provided, else go to next iteration - if self.always_race_against and self.always_race_against != challenger: - self.stage = IntensifierStage.RUN_BASIS - else: - self.stage = IntensifierStage.RUN_INCUMBENT - self.logger.debug( - "Estimated cost of challenger on %d runs: %.4f, becomes new incumbent", - len(chal_runs), - chal_perf, - ) - - else: # Line 17 - # challenger is not worse, continue - self.N = 2 * self.N - self.continue_challenger = True - self.logger.debug( - "Estimated cost of challenger on %d runs: %.4f, adding %d runs to the queue", - len(chal_runs), - chal_perf, - self.N / 2, - ) - else: - self.logger.debug( - "Estimated cost of challenger on %d runs: %.4f, still %d runs to go (continue racing)", - len(chal_runs), - chal_perf, - len(self.to_run), - ) - - return incumbent - - def _get_instances_to_run( - self, - challenger: Configuration, - incumbent: Configuration, - N: int, - run_history: RunHistory, - ) -> Tuple[List[InstSeedBudgetKey], float]: - """Returns the minimum list of pairs to run the challenger on before - comparing it with the incumbent. - - Parameters - ---------- - incumbent: Configuration - incumbent configuration - challenger: Configuration - promising configuration that is presently being evaluated - run_history: RunHistory - Stores all runs we ran so far - N: int - number of pairs to select - - Returns - ------- - List[InstSeedBudgetKey] - list of tuples to run - float - total (runtime) cost of running the incumbent on the instances (used for adaptive capping while racing) - """ - # get next instances left for the challenger - # Line 8 - inc_inst_seeds = set(run_history.get_runs_for_config(incumbent, only_max_observed_budget=True)) - chall_inst_seeds = set(run_history.get_runs_for_config(challenger, only_max_observed_budget=True)) - # Line 10 - missing_runs = sorted(inc_inst_seeds - chall_inst_seeds) - - # Line 11 - self.rs.shuffle(missing_runs) - if N < 0: - raise ValueError("Argument N must not be smaller than zero, but is %s" % str(N)) - to_run = missing_runs[: min(N, len(missing_runs))] - missing_runs = missing_runs[min(N, len(missing_runs)) :] - - # for adaptive capping - # because of efficiency computed here - inst_seed_pairs = list(inc_inst_seeds - set(missing_runs)) - # cost used by incumbent for going over all runs in inst_seed_pairs - inc_sum_cost = run_history.sum_cost(config=incumbent, instance_seed_budget_keys=inst_seed_pairs, normalize=True) - assert type(inc_sum_cost) == float - return to_run, inc_sum_cost - - def get_next_challenger( - self, - challengers: Optional[List[Configuration]], - chooser: Optional[EPMChooser], - ) -> Tuple[Optional[Configuration], bool]: - """This function returns the next challenger, that should be exercised though lines 8-17. - - It does so by populating configs_to_run, which is a pool of configuration - from which the racer will sample. Each configuration within configs_to_run, - will be intensified on different instances/seed registered in self.to_run - as stated in line 11. - - A brand new configuration should only be sampled, after all self.to_run - instance seed pairs are exhausted. - - This method triggers a call to _next_iteration if there are no more configurations - to run, for the current intensification loop. This marks the transition to Line 2, - where a new configuration to intensify will be drawn from epm/initial challengers. - - - Parameters - ---------- - challengers : List[Configuration] - promising configurations - chooser : smac.optimizer.epm_configuration_chooser.EPMChooser - optimizer that generates next configurations to use for racing - - Returns - ------- - Optional[Configuration] - next configuration to evaluate - bool - flag telling if the configuration is newly sampled or one currently being tracked - """ - # select new configuration when entering 'race challenger' stage - # or for the first run - if not self.current_challenger or (self.stage == IntensifierStage.RUN_CHALLENGER and not self.to_run): - - # this is a new intensification run, get the next list of configurations to run - if self.update_configs_to_run: - configs_to_run = self._generate_challengers(challengers=challengers, chooser=chooser) - self.configs_to_run = cast(_config_to_run_type, configs_to_run) - self.update_configs_to_run = False - - # pick next configuration from the generator - try: - challenger = next(self.configs_to_run) - except StopIteration: - # out of challengers for the current iteration, start next incumbent iteration - self._next_iteration() - return None, False - - if challenger: - # reset instance index for the new challenger - self._chall_indx += 1 - self.current_challenger = challenger - self.N = max(1, self.minR) - self.to_run = [] - - return challenger, True - - # return currently running challenger - return self.current_challenger, False - - def _generate_challengers( - self, - challengers: Optional[List[Configuration]], - chooser: Optional[EPMChooser], - ) -> _config_to_run_type: - """Retuns a sequence of challengers to use in intensification If challengers are not - provided, then optimizer will be used to generate the challenger list. - - Parameters - ---------- - challengers : List[Configuration] - promising configurations to evaluate next - chooser : smac.optimizer.epm_configuration_chooser.EPMChooser - a sampler that generates next configurations to use for racing - - Returns - ------- - Optional[Generator[Configuration]] - A generator containing the next challengers to use - """ - if challengers: - # iterate over challengers provided - self.logger.debug("Using challengers provided") - chall_gen = iter(challengers) # type: _config_to_run_type - elif chooser: - # generating challengers on-the-fly if optimizer is given - self.logger.debug("Generating new challenger from optimizer") - chall_gen = chooser.choose_next() - else: - raise ValueError("No configurations/chooser provided. Cannot generate challenger!") - - return chall_gen - - def _next_iteration(self) -> None: - """Updates tracking variables at the end of an intensification run.""" - # track iterations - self.n_iters += 1 - self.iteration_done = True - self.configs_to_run = iter([]) - self.update_configs_to_run = True - - # reset for a new iteration - self.num_run = 0 - self.num_chall_run = 0 - self._chall_indx = 0 - self.elapsed_time = 0 - self._ta_time = 0.0 - - self.stats.update_average_configs_per_intensify(n_configs=self._chall_indx) diff --git a/smac/intensification/parallel_scheduling.py b/smac/intensification/parallel_scheduling.py deleted file mode 100644 index ec01143e0..000000000 --- a/smac/intensification/parallel_scheduling.py +++ /dev/null @@ -1,337 +0,0 @@ -from typing import Dict, List, Mapping, Optional, Tuple - -import warnings - -import numpy as np - -from smac.configspace import Configuration -from smac.intensification.abstract_racer import AbstractRacer, RunInfoIntent -from smac.optimizer.configuration_chooser.epm_chooser import EPMChooser -from smac.runhistory.runhistory import RunHistory, RunInfo, RunValue -from smac.stats.stats import Stats -from smac.utils.io.traj_logging import TrajLogger - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class ParallelScheduler(AbstractRacer): - """Common Racer class for Intensifiers that will schedule configurations on a parallel fashion. - - This class instantiates intensifier objects on a need basis, that is, to - prevent workers from being idle. This intensifier objects will give configurations - to run - - Parameters - ---------- - stats: smac.stats.stats.Stats - stats object - traj_logger: smac.utils.io.traj_logging.TrajLogger - TrajLogger object to log all new incumbents - rng : np.random.RandomState - instances : List[str] - list of all instance ids - instance_specifics : Mapping[str, str] - mapping from instance name to instance specific string - cutoff : Optional[int] - cutoff of TA runs - deterministic : bool - whether the TA is deterministic or not - initial_budget : Optional[float] - minimum budget allowed for 1 run of successive halving - max_budget : Optional[float] - maximum budget allowed for 1 run of successive halving - eta : float - 'halving' factor after each iteration in a successive halving run. Defaults to 3 - num_initial_challengers : Optional[int] - number of challengers to consider for the initial budget. If None, calculated internally - run_obj_time : bool - whether the run objective is runtime or not (if true, apply adaptive capping) - n_seeds : Optional[int] - Number of seeds to use, if TA is not deterministic. Defaults to None, i.e., seed is set as 0 - instance_order : Optional[str] - how to order instances. Can be set to: [None, shuffle_once, shuffle] - * None - use as is given by the user - * shuffle_once - shuffle once and use across all SH run (default) - * shuffle - shuffle before every SH run - adaptive_capping_slackfactor : float - slack factor of adpative capping (factor * adaptive cutoff) - inst_seed_pairs : List[Tuple[str, int]], optional - Do not set this argument, it will only be used by hyperband! - min_chall: int - minimal number of challengers to be considered (even if time_bound is exhausted earlier). This class will - raise an exception if a value larger than 1 is passed. - incumbent_selection: str - How to select incumbent in successive halving. Only active for real-valued budgets. - Can be set to: [highest_executed_budget, highest_budget, any_budget] - * highest_executed_budget - incumbent is the best in the highest budget run so far (default) - * highest_budget - incumbent is selected only based on the highest budget - * any_budget - incumbent is the best on any budget i.e., best performance regardless of budget - """ - - def __init__( - self, - stats: Stats, - traj_logger: TrajLogger, - rng: np.random.RandomState, - instances: List[str], - instance_specifics: Mapping[str, str] = None, - cutoff: Optional[float] = None, - deterministic: bool = False, - initial_budget: Optional[float] = None, - max_budget: Optional[float] = None, - eta: float = 3, - num_initial_challengers: Optional[int] = None, - run_obj_time: bool = True, - n_seeds: Optional[int] = None, - instance_order: Optional[str] = "shuffle_once", - adaptive_capping_slackfactor: float = 1.2, - inst_seed_pairs: Optional[List[Tuple[str, int]]] = None, - min_chall: int = 1, - incumbent_selection: str = "highest_executed_budget", - ) -> None: - - super().__init__( - stats=stats, - traj_logger=traj_logger, - rng=rng, - instances=instances, - instance_specifics=instance_specifics, - cutoff=cutoff, - deterministic=deterministic, - run_obj_time=run_obj_time, - adaptive_capping_slackfactor=adaptive_capping_slackfactor, - min_chall=min_chall, - ) - - # We have a pool of instances that yield configurations ot run - self.intensifier_instances = {} # type: Dict[int, AbstractRacer] - self.print_worker_warning = True - - def get_next_run( - self, - challengers: Optional[List[Configuration]], - incumbent: Configuration, - chooser: Optional[EPMChooser], - run_history: RunHistory, - repeat_configs: bool = False, - num_workers: int = 1, - ) -> Tuple[RunInfoIntent, RunInfo]: - """This procedure decides from which instance to pick a config, in order to determine the - next run. - - To prevent having idle workers, this procedure creates new instances - up to the maximum number of workers available. - - If no new intensifier instance can be created and all intensifier - objects need to wait for more data, this procedure sends a wait request to smbo. - - Parameters - ---------- - challengers : List[Configuration] - promising configurations - incumbent: Configuration - incumbent configuration - chooser : smac.optimizer.epm_configuration_chooser.EPMChooser - optimizer that generates next configurations to use for racing - run_history : smac.runhistory.runhistory.RunHistory - stores all runs we ran so far - repeat_configs : bool - if False, an evaluated configuration will not be generated again - num_workers: int - the maximum number of workers available - at a given time. - - Returns - ------- - intent: RunInfoIntent - Indicator of how to consume the RunInfo object - run_info: RunInfo - An object that encapsulates the minimum information to - evaluate a configuration - """ - if num_workers <= 1 and self.print_worker_warning: - warnings.warn( - f"{self.__class__.__name__} is executed with {num_workers} workers only. " - "Consider to use pynisher to use all available workers." - ) - self.print_worker_warning = False - - # If repeat_configs is True, that means that not only self can repeat - # configurations, but also in the context of multiprocessing, N - # intensifier instances will also share configurations. The later - # is not supported - if repeat_configs: - raise ValueError("repeat_configs==True is not supported for parallel execution") - - # First get a config to run from a SH instance - for i in self._sort_instances_by_stage(self.intensifier_instances): - intent, run_info = self.intensifier_instances[i].get_next_run( - challengers=challengers, - incumbent=incumbent, - chooser=chooser, - run_history=run_history, - repeat_configs=repeat_configs, - ) - - # if asked to wait, the intensifier cannot come up - # with a new configuration, so we continue - if intent == RunInfoIntent.WAIT: - continue - - return intent, run_info - - # If gotten to this point, we might look into adding a new - # intensifier - if self._add_new_instance(num_workers): - return self.intensifier_instances[len(self.intensifier_instances) - 1].get_next_run( - challengers=challengers, - incumbent=incumbent, - chooser=chooser, - run_history=run_history, - repeat_configs=repeat_configs, - ) - - # If got to this point, no new instance can be added as - # there are no idle workers and all running instances have to - # wait, so we return a wait intent - return RunInfoIntent.WAIT, RunInfo( - config=None, - instance="0", - instance_specific="0", - seed=0, - cutoff=None, - capped=False, - budget=0.0, - ) - - def process_results( - self, - run_info: RunInfo, - incumbent: Optional[Configuration], - run_history: RunHistory, - time_bound: float, - result: RunValue, - log_traj: bool = True, - ) -> Tuple[Configuration, float]: - """The intensifier stage will be updated based on the results/status of a configuration - execution. - - To do so, this procedures redirects the result argument, to the - respective intensifier object that generated the original config. - - Also, an incumbent will be determined. This determination is done - using the complete run history, so we rely on the current intensifier - choice of incumbent. That is, no need to go over each instance to - get the incumbent, as there is no local runhistory - - Parameters - ---------- - run_info : RunInfo - A RunInfo containing the configuration that was evaluated - incumbent : Optional[Configuration] - Best configuration seen so far - run_history : RunHistory - stores all runs we ran so far - if False, an evaluated configuration will not be generated again - time_bound : float - time in [sec] available to perform intensify - result: RunValue - Contain the result (status and other methadata) of exercising - a challenger/incumbent. - log_traj: bool - Whether to log changes of incumbents in trajectory - - Returns - ------- - incumbent: Configuration - current (maybe new) incumbent configuration - inc_perf: float - empirical performance of incumbent configuration - """ - return self.intensifier_instances[run_info.source_id].process_results( - run_info=run_info, - incumbent=incumbent, - run_history=run_history, - time_bound=time_bound, - result=result, - log_traj=log_traj, - ) - - def _add_new_instance(self, num_workers: int) -> bool: - """Decides if it is possible to add a new intensifier instance, and adds it. If a new - intensifier instance is added, True is returned, else False. - - Parameters - ---------- - num_workers: int - the maximum number of workers available - at a given time. - - Returns - ------- - Whether or not a successive halving instance was added - """ - raise NotImplementedError() - - def _get_intensifier_ranking(self, intensifier: AbstractRacer) -> Tuple[int, int]: - """Given a intensifier, returns how advance it is. This metric will be used to determine - what priority to assign to the intensifier. - - Parameters - ---------- - intensifier: AbstractRacer - Intensifier to rank based on run progress - - Returns - ------- - ranking: int - the higher this number, the faster the intensifier will get - the running resources. For hyperband we can use the - sh_intensifier stage, for example - tie_breaker: int - The configurations that have been launched to break ties. For - example, in the case of Successive Halving it can be the number - of configurations launched - """ - raise NotImplementedError() - - def _sort_instances_by_stage(self, instances: Dict[int, AbstractRacer]) -> List[int]: - """This procedure dictates what SH to prioritize in launching jobs. It prioritizes resource - allocation to SH instances that have higher stages. In case of tie, we prioritize the SH - instance with more launched configs. - - Parameters - ---------- - instances: Dict[int, AbstractRacer] - Dict with the instances to prioritize - - Returns - ------- - List: - The order in which to query for new jobs - """ - # This function might be called when no intensifier instances - # exist (first iteration), so we return an empty list in that case - if len(instances) == 0: - return [] - - # We want to prioritize runs that are close to finishing an iteration. - # In the context of successive halving for example, an iteration has stages - # that are composed of # of configs and each configs has # of instance-seed pairs - # so ranking will be the stage (the higher the stage, the more we want this run - # to be finished earlier). Also, in case of tie (runs at same stage) we need a - # tie breaker, which can be the number of configs already launched - preference = [] - for i, sh in instances.items(): - ranking, tie_breaker = self._get_intensifier_ranking(sh) - preference.append( - (i, ranking, tie_breaker), - ) - - # First we sort by config/instance/seed as the less important criteria - preference.sort(key=lambda x: x[2], reverse=True) - # Second by stage. The more advanced the stage is, the more we want - # this intensifier to finish early - preference.sort(key=lambda x: x[1], reverse=True) - return [i for i, s, c in preference] diff --git a/smac/intensification/simple_intensifier.py b/smac/intensification/simple_intensifier.py deleted file mode 100644 index 491449f88..000000000 --- a/smac/intensification/simple_intensifier.py +++ /dev/null @@ -1,199 +0,0 @@ -from typing import Any, Dict, List, Mapping, Optional, Tuple - -import numpy as np - -from smac.configspace import Configuration -from smac.intensification.abstract_racer import AbstractRacer, RunInfoIntent -from smac.optimizer.configuration_chooser.epm_chooser import EPMChooser -from smac.runhistory.runhistory import RunHistory, RunInfo, RunValue -from smac.stats.stats import Stats -from smac.utils.constants import MAXINT -from smac.utils.io.traj_logging import TrajLogger - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class SimpleIntensifier(AbstractRacer): - """Performs the traditional Bayesian Optimization loop, without instance/seed intensification. - - Parameters - ---------- - stats: smac.stats.stats.Stats - stats object - traj_logger: smac.utils.io.traj_logging.TrajLogger - TrajLogger object to log all new incumbents - rng : np.random.RandomState - instances : List[str] - list of all instance ids - instance_specifics : Mapping[str, str] - mapping from instance name to instance specific string - cutoff : Optional[int] - cutoff of TA runs - deterministic : bool - whether the TA is deterministic or not - run_obj_time : bool - whether the run objective is runtime or not (if true, apply adaptive capping) - """ - - def __init__( - self, - stats: Stats, - traj_logger: TrajLogger, - rng: np.random.RandomState, - instances: List[str], - instance_specifics: Mapping[str, str] = None, - cutoff: Optional[float] = None, - deterministic: bool = False, - run_obj_time: bool = True, - **kwargs: Any, - ) -> None: - - super().__init__( - stats=stats, - traj_logger=traj_logger, - rng=rng, - instances=instances, - instance_specifics=instance_specifics, - cutoff=cutoff, - deterministic=deterministic, - run_obj_time=run_obj_time, - adaptive_capping_slackfactor=1.0, - min_chall=1, - ) - # We want to control the number of runs that are sent to - # the workers. At any time, we want to make sure that if there - # are just W workers, there should be at max W active runs - # Below variable tracks active runs not processed - self.run_tracker = {} # type: Dict[Tuple[Configuration, str, int, float], bool] - - def process_results( - self, - run_info: RunInfo, - incumbent: Optional[Configuration], - run_history: RunHistory, - time_bound: float, - result: RunValue, - log_traj: bool = True, - ) -> Tuple[Configuration, float]: - """The intensifier stage will be updated based on the results/status of a configuration - execution. Also, a incumbent will be determined. - - Parameters - ---------- - run_info : RunInfo - A RunInfo containing the configuration that was evaluated - incumbent : Optional[Configuration] - Best configuration seen so far - run_history : RunHistory - stores all runs we ran so far - if False, an evaluated configuration will not be generated again - time_bound : float - time in [sec] available to perform intensify - result: RunValue - Contain the result (status and other methadata) of exercising - a challenger/incumbent. - log_traj: bool - Whether to log changes of incumbents in trajectory - - Returns - ------- - incumbent: Configuration() - current (maybe new) incumbent configuration - inc_perf: float - empirical performance of incumbent configuration - """ - # Mark the fact that we processed this configuration - self.run_tracker[(run_info.config, run_info.instance, run_info.seed, run_info.budget)] = True - - # If The incumbent is None we use the challenger - if not incumbent: - self.logger.info("First run, no incumbent provided; challenger is assumed to be the incumbent") - incumbent = run_info.config - - self.num_run += 1 - - incumbent = self._compare_configs( - challenger=run_info.config, - incumbent=incumbent, - run_history=run_history, - log_traj=log_traj, - ) - # get incumbent cost - inc_perf = run_history.get_cost(incumbent) - - return incumbent, inc_perf - - def get_next_run( - self, - challengers: Optional[List[Configuration]], - incumbent: Configuration, - chooser: Optional[EPMChooser], - run_history: RunHistory, - repeat_configs: bool = True, - num_workers: int = 1, - ) -> Tuple[RunInfoIntent, RunInfo]: - """Selects which challenger to be used. As in a traditional BO loop, we sample from the EPM, - which is the next configuration based on the acquisition function. The input data is read - from the runhistory. - - Parameters - ---------- - challengers : List[Configuration] - promising configurations - incumbent: Configuration - incumbent configuration - chooser : smac.optimizer.epm_configuration_chooser.EPMChooser - optimizer that generates next configurations to use for racing - run_history : smac.runhistory.runhistory.RunHistory - stores all runs we ran so far - repeat_configs : bool - if False, an evaluated configuration will not be generated again - num_workers: int - the maximum number of workers available - at a given time. - - Returns - ------- - intent: RunInfoIntent - Indicator of how to consume the RunInfo object - run_info: RunInfo - An object that encapsulates the minimum information to - evaluate a configuration - """ - # We always sample from the configs provided or the EPM - challenger = self._next_challenger( - challengers=challengers, - chooser=chooser, - run_history=run_history, - repeat_configs=repeat_configs, - ) - - # Run tracker is a dictionary whose values indicate if a run has been - # processed. If a value in this dict is false, it means that a worker - # should still be processing this configuration. - total_active_runs = len([v for v in self.run_tracker.values() if not v]) - if total_active_runs >= num_workers: - # We only submit jobs if there is an idle worker - return RunInfoIntent.WAIT, RunInfo( - config=None, - instance=None, - instance_specific="0", - seed=0, - cutoff=self.cutoff, - capped=False, - budget=0.0, - ) - - run_info = RunInfo( - config=challenger, - instance=self.instances[-1], - instance_specific="0", - seed=0 if self.deterministic else int(self.rs.randint(low=0, high=MAXINT, size=1)[0]), - cutoff=self.cutoff, - capped=False, - budget=0.0, - ) - - self.run_tracker[(run_info.config, run_info.instance, run_info.seed, run_info.budget)] = False - return RunInfoIntent.RUN, run_info diff --git a/smac/intensification/successive_halving.py b/smac/intensification/successive_halving.py deleted file mode 100644 index 7738d1304..000000000 --- a/smac/intensification/successive_halving.py +++ /dev/null @@ -1,1326 +0,0 @@ -from typing import Dict, List, Mapping, Optional, Set, Tuple, Union - -import logging -import warnings - -import numpy as np - -from smac.configspace import Configuration -from smac.intensification.abstract_racer import AbstractRacer, RunInfoIntent -from smac.intensification.parallel_scheduling import ParallelScheduler -from smac.optimizer.configuration_chooser.epm_chooser import EPMChooser -from smac.runhistory.runhistory import RunHistory, RunInfo, RunValue -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.utils.constants import MAXINT -from smac.utils.io.traj_logging import TrajLogger - -__author__ = "Ashwin Raaghav Narayanan" -__copyright__ = "Copyright 2019, ML4AAD" -__license__ = "3-clause BSD" - - -class _SuccessiveHalving(AbstractRacer): - """Races multiple challengers against an incumbent using Successive Halving method. - - This class contains the logic to implement: - "BOHB: Robust and Efficient Hyperparameter Optimization at Scale" (Falkner et al. 2018) - Supplementary reference: http://proceedings.mlr.press/v80/falkner18a/falkner18a-supp.pdf - - The `SuccessiveHalving` class can create multiple `_SuccessiveHalving` objects, to - allow parallelism in the method (up to the number of workers available). The user interface - is expected to be `SuccessiveHalving`, yet this class (`_SuccessiveHalving`) contains the - actual single worker implementation of the SMAC4MF method. - - Successive Halving intensifier (and Hyperband) can operate on two kinds of budgets: - - 1. **'Instances' as budget**: - - When multiple instances are provided or when run objective is "runtime", - this is the criterion used as budget - for successive halving iterations i.e., the budget determines how many - instances the challengers are evaluated - on at a time. Top challengers for the next iteration are selected based - on the combined performance across all instances used. - - If ``initial_budget`` and ``max_budget`` are not provided, then they are - set to 1 and total number of available instances respectively by default. - - 2. **'Real-valued' budget**: - - This is used when there is only one instance provided and when run - objective is "quality", i.e. budget is a positive, real-valued number - that can be passed to the target algorithm as an argument. - It can be used to control anything by the target algorithm, - Eg: number of epochs for training a neural network. - - ``initial_budget`` and ``max_budget`` are required parameters for - this type of budget. - - Parameters - ---------- - stats: smac.stats.stats.Stats - stats object - traj_logger: smac.utils.io.traj_logging.TrajLogger - TrajLogger object to log all new incumbents - rng : np.random.RandomState - instances : List[str] - list of all instance ids - instance_specifics : Mapping[str, str] - mapping from instance name to instance specific string - cutoff : Optional[int] - cutoff of TA runs - deterministic : bool - whether the TA is deterministic or not - initial_budget : Optional[float] - minimum budget allowed for 1 run of successive halving - max_budget : Optional[float] - maximum budget allowed for 1 run of successive halving - eta : float - 'halving' factor after each iteration in a successive halving run. Defaults to 3 - _all_budgets: Optional[np.ndarray] = None - Used internally when HB uses SH as a subrouting - _n_configs_in_stage: Optional[np.ndarray] = None - Used internally when HB uses SH as a subrouting - num_initial_challengers : Optional[int] - number of challengers to consider for the initial budget. If None, calculated internally - run_obj_time : bool - whether the run objective is runtime or not (if true, apply adaptive capping) - n_seeds : Optional[int] - Number of seeds to use, if TA is not deterministic. Defaults to None, i.e., seed is set as 0 - instance_order : Optional[str] - how to order instances. Can be set to: [None, shuffle_once, shuffle] - - * None - use as is given by the user - * shuffle_once - shuffle once and use across all SH run (default) - * shuffle - shuffle before every SH run - - adaptive_capping_slackfactor : float - slack factor of adpative capping (factor * adaptive cutoff) - inst_seed_pairs : List[Tuple[str, int]], optional - Do not set this argument, it will only be used by hyperband! - min_chall: int - minimal number of challengers to be considered (even if time_bound is exhausted earlier). This class will - raise an exception if a value larger than 1 is passed. - incumbent_selection: str - How to select incumbent in successive halving. Only active for real-valued budgets. - Can be set to: [highest_executed_budget, highest_budget, any_budget] - - * highest_executed_budget - incumbent is the best in the highest budget run so far (default) - * highest_budget - incumbent is selected only based on the highest budget - * any_budget - incumbent is the best on any budget i.e., best performance regardless of budget - identifier: int - Adds a numerical identifier on this SH instance. Used for debug and tagging - logger messages properly - """ - - def __init__( - self, - stats: Stats, - traj_logger: TrajLogger, - rng: np.random.RandomState, - instances: List[str], - instance_specifics: Mapping[str, str] = None, - cutoff: Optional[float] = None, - deterministic: bool = False, - initial_budget: Optional[float] = None, - max_budget: Optional[float] = None, - eta: float = 3, - _all_budgets: Optional[np.ndarray] = None, - _n_configs_in_stage: Optional[np.ndarray] = None, - num_initial_challengers: Optional[int] = None, - run_obj_time: bool = True, - n_seeds: Optional[int] = None, - instance_order: Optional[str] = "shuffle_once", - adaptive_capping_slackfactor: float = 1.2, - inst_seed_pairs: Optional[List[Tuple[str, int]]] = None, - min_chall: int = 1, - incumbent_selection: str = "highest_executed_budget", - identifier: int = 0, - ) -> None: - super().__init__( - stats=stats, - traj_logger=traj_logger, - rng=rng, - instances=instances, - instance_specifics=instance_specifics, - cutoff=cutoff, - deterministic=deterministic, - run_obj_time=run_obj_time, - adaptive_capping_slackfactor=adaptive_capping_slackfactor, - min_chall=min_chall, - ) - - self.identifier = identifier - self.logger = logging.getLogger(self.__module__ + "." + str(self.identifier) + "." + self.__class__.__name__) - - if self.min_chall > 1: - raise ValueError("Successive Halving cannot handle argument `min_chall` > 1.") - self.first_run = True - - # INSTANCES - self.n_seeds = n_seeds if n_seeds else 1 - self.instance_order = instance_order - - # NOTE Remove after solving how to handle multiple seeds and 1 instance - if len(self.instances) == 1 and self.n_seeds > 1: - raise NotImplementedError("This case (multiple seeds and 1 instance) cannot be handled yet!") - - # if instances are coming from Hyperband, skip the instance preprocessing section - # it is already taken care by Hyperband - - if not inst_seed_pairs: - # set seed(s) for all SH runs - # - currently user gives the number of seeds to consider - if self.deterministic: - seeds = [0] - else: - seeds = [int(s) for s in self.rs.randint(low=0, high=MAXINT, size=self.n_seeds)] - if self.n_seeds == 1: - self.logger.warning( - "The target algorithm is specified to be non deterministic, " - "but number of seeds to evaluate are set to 1. " - "Consider setting `n_seeds` > 1." - ) - - # storing instances & seeds as tuples - self.inst_seed_pairs = [(i, s) for s in seeds for i in self.instances] - - # determine instance-seed pair order - if self.instance_order == "shuffle_once": - # randomize once - self.rs.shuffle(self.inst_seed_pairs) # type: ignore - else: - self.inst_seed_pairs = inst_seed_pairs - - # successive halving parameters - self._init_sh_params( - initial_budget=initial_budget, - max_budget=max_budget, - eta=eta, - num_initial_challengers=num_initial_challengers, - _all_budgets=_all_budgets, - _n_configs_in_stage=_n_configs_in_stage, - ) - - # adaptive capping - if self.instance_as_budget and self.instance_order != "shuffle" and self.run_obj_time: - self.adaptive_capping = True - else: - self.adaptive_capping = False - - # challengers can be repeated only if optimizing across multiple seeds or changing instance orders every run - # (this does not include having multiple instances) - if self.n_seeds > 1 or self.instance_order == "shuffle": - self.repeat_configs = True - else: - self.repeat_configs = False - - # incumbent selection design - assert incumbent_selection in ["highest_executed_budget", "highest_budget", "any_budget"] - self.incumbent_selection = incumbent_selection - - # Define state variables to please mypy - - # current instance index tracks two things. Configurations that are to be launched, - # That is config A needs to run in 3 instances/seed pairs, then curr_inst_idx should - # track this. But then, if a new configuration is added in the case of parallelism - # a new separate curr_inst_idx needs to be started. - # The indices normally are of type int, but np.inf is used to indicate to not further - # launch instances for this configuration, hence the type is Union[int, float]. - self.curr_inst_idx = {} # type: Dict[Configuration, Union[int, float]] - self.running_challenger = None - self.success_challengers = set() # type: Set[Configuration] - self.do_not_advance_challengers = set() # type: Set[Configuration] - self.fail_challengers = set() # type: Set[Configuration] - self.fail_chal_offset = 0 - - # Track which configs were launched. This will allow to have an extra check to make sure - # that a successive halver deals only with the configs it launched, - # but also allows querying the status of the configs via the run history. - # In other works, the run history is agnostic of the origin of the configurations, - # that is, which successive halving instance created it. The RunInfo object - # is aware of this information, and for parallel execution, the routing of - # finish results is expected to use this information. - # Nevertheless, the common object among SMBO/intensifier, which is the - # run history, does not have this information and so we track locally. That way, - # when we access the complete list of configs from the run history, we filter - # the ones launched by the current succesive halver using self.run_tracker - self.run_tracker = {} # type: Dict[Tuple[Configuration, str, int, float], bool] - - def _init_sh_params( - self, - initial_budget: Optional[float], - max_budget: Optional[float], - eta: float, - num_initial_challengers: Optional[int] = None, - _all_budgets: Optional[np.ndarray] = None, - _n_configs_in_stage: Optional[np.ndarray] = None, - ) -> None: - """Initialize Successive Halving parameters. - - Parameters - ---------- - initial_budget : Optional[float] - minimum budget allowed for 1 run of successive halving - max_budget : Optional[float] - maximum budget allowed for 1 run of successive halving - eta : float - 'halving' factor after each iteration in a successive halving run - num_initial_challengers : Optional[int] - number of challengers to consider for the initial budget - _all_budgets: Optional[np.ndarray] = None - Used internally when HB uses SH as a subrouting - _n_configs_in_stage: Optional[np.ndarray] = None - Used internally when HB uses SH as a subrouting - """ - if eta <= 1: - raise ValueError("eta must be greater than 1") - self.eta = eta - - # BUDGETS - - if max_budget is not None and initial_budget is not None and max_budget < initial_budget: - raise ValueError("Max budget has to be larger than min budget") - - # - if only 1 instance was provided & quality objective, then use cutoff as budget - # - else, use instances as budget - if not self.run_obj_time and len(self.inst_seed_pairs) <= 1: - # budget with cutoff - if initial_budget is None or max_budget is None: - raise ValueError( - "Successive Halving with real-valued budget (i.e., only 1 instance) " - "requires parameters initial_budget and max_budget for intensification!" - ) - - self.initial_budget = initial_budget - self.max_budget = max_budget - self.instance_as_budget = False - - else: - # budget with instances - if self.run_obj_time and len(self.inst_seed_pairs) <= 1: - self.logger.warning("Successive Halving has objective 'runtime' but only 1 instance-seed pair.") - self.initial_budget = int(initial_budget) if initial_budget else 1 - self.max_budget = int(max_budget) if max_budget else len(self.inst_seed_pairs) - self.instance_as_budget = True - - if self.max_budget > len(self.inst_seed_pairs): - raise ValueError("Max budget cannot be greater than the number of instance-seed pairs") - if self.max_budget < len(self.inst_seed_pairs): - self.logger.warning( - "Max budget (%d) does not include all instance-seed pairs (%d)" - % (self.max_budget, len(self.inst_seed_pairs)) - ) - - budget_type = "INSTANCES" if self.instance_as_budget else "REAL-VALUED" - self.logger.info( - "Successive Halving configuration: budget type = %s, " - "Initial budget = %.2f, Max. budget = %.2f, eta = %.2f" - % (budget_type, self.initial_budget, self.max_budget, self.eta) - ) - - # precomputing stuff for SH - # max. no. of SH iterations possible given the budgets - max_sh_iter = int(np.floor(np.log(self.max_budget / self.initial_budget) / np.log(self.eta))) - # initial number of challengers to sample - if num_initial_challengers is None: - num_initial_challengers = int(self.eta**max_sh_iter) - - if _all_budgets is not None and _n_configs_in_stage is not None: - # Assert we use the given numbers to avoid rounding issues, see #701 - self.all_budgets = _all_budgets - self.n_configs_in_stage = _n_configs_in_stage - else: - # budgets to consider in each stage - linspace = -np.linspace(max_sh_iter, 0, max_sh_iter + 1) - self.all_budgets = self.max_budget * np.power(self.eta, linspace) - # number of challengers to consider in each stage - n_configs_in_stage = num_initial_challengers * np.power( - self.eta, -np.linspace(0, max_sh_iter, max_sh_iter + 1) - ) - self.n_configs_in_stage = np.array(np.round(n_configs_in_stage), dtype=int).tolist() - - def process_results( - self, - run_info: RunInfo, - incumbent: Optional[Configuration], - run_history: RunHistory, - time_bound: float, - result: RunValue, - log_traj: bool = True, - ) -> Tuple[Configuration, float]: - """The intensifier stage will be updated based on the results/status of a configuration - execution. Also, a incumbent will be determined. - - Parameters - ---------- - run_info : RunInfo - A RunInfo containing the configuration that was evaluated - incumbent : Optional[Configuration] - Best configuration seen so far - run_history : RunHistory - stores all runs we ran so far - if False, an evaluated configuration will not be generated again - time_bound : float - time in [sec] available to perform intensify - result: RunValue - Contain the result (status and other methadata) of exercising - a challenger/incumbent. - log_traj: bool - Whether to log changes of incumbents in trajectory - - Returns - ------- - incumbent: Configuration - current (maybe new) incumbent configuration - inc_perf: float - empirical performance of incumbent configuration - """ - # Mark the fact that we processed this configuration - self.run_tracker[(run_info.config, run_info.instance, run_info.seed, run_info.budget)] = True - - # If The incumbent is None and it is the first run, we use the challenger - if not incumbent and self.first_run: - self.logger.info("First run, no incumbent provided; challenger is assumed to be the incumbent") - incumbent = run_info.config - self.first_run = False - - # In a serial run, if we have to CAP a run, then we stop launching - # more configurations for this run. - # In the context of parallelism, we launch the instances proactively - # The fact that self.curr_inst_idx[run_info.config] is np.inf means - # that no more instances will be launched for the current config, so we - # can add a check to make sure that if we are capping, this makes sense - # for the active challenger - if result.status == StatusType.CAPPED and run_info.config == self.running_challenger: - self.curr_inst_idx[run_info.config] = np.inf - else: - self._ta_time = self._ta_time # type: float # make mypy happy - self.num_run = self.num_run # type: int # make mypy happy - self._ta_time += result.time - self.num_run += 1 - - # 0: Before moving to a new stage, we have to complete M x N tasks, where M is the - # total number of configurations evaluated in N instance/seed pairs. - # The last active configuration is stored in self.running challengers is the M - # configuration, and to get to this point, we have already submitted tasks - # for (M - 1) configurations on N instances seed-pairs. The status of such - # (M - 1) * N tasks is tracked in self.run_tracker, that has a value of False - # if not processed and true if such task has been processed. - # This stage is complete only if all tasks have been launched and all of the - # already launched tasks are processed. - - # 1: We first query if we have launched everything already (All M * N tasks) - all_config_inst_seed_launched = self._all_config_inst_seed_pairs_launched( - run_history=run_history, - activate_configuration_being_intensified=self.running_challenger, - ) - - # 2: Then we get the already submitted tasks (that is, proposed by get_next_runs), - # that have not yet been processed process_results - all_config_inst_seeds_processed = len([v for v in self.run_tracker.values() if not v]) <= 0 - - # 3: Then the total number of remaining task before we can conclude this stage - # is calculated by taking into account point 2 and 3 above - is_stage_done = all_config_inst_seed_launched and all_config_inst_seeds_processed - - # adding challengers to the list of evaluated challengers - # - Stop: CAPPED/CRASHED/TIMEOUT/MEMOUT/DONOTADVANCE (!= SUCCESS) - # - Advance to next stage: SUCCESS - # curr_challengers is a set, so "at least 1" success can be counted by set addition (no duplicates) - # If a configuration is successful, it is added to curr_challengers. - # if it fails it is added to fail_challengers. - if np.isfinite(self.curr_inst_idx[run_info.config]) and result.status == StatusType.SUCCESS: - self.success_challengers.add(run_info.config) # successful configs - elif np.isfinite(self.curr_inst_idx[run_info.config]) and result.status == StatusType.DONOTADVANCE: - self.do_not_advance_challengers.add(run_info.config) - else: - self.fail_challengers.add(run_info.config) # capped/crashed/do not advance configs - - # We need to update the incumbent if this config we are processing - # completes all scheduled instance-seed pairs. - # Here, a config/seed/instance is going to be processed for the first time - # (it has been previously scheduled by get_next_run and marked False, indicating - # that it has not been processed yet. Entering process_results() this config/seed/instance - # is marked as TRUE as an indication that it has finished and should be processed) - # so if all configurations runs are marked as TRUE it means that this new config - # was the missing piece to have everything needed to compare against the incumbent - update_incumbent = all([v for k, v in self.run_tracker.items() if k[0] == run_info.config]) - - # get incumbent if all instances have been evaluated - if is_stage_done or update_incumbent: - incumbent = self._compare_configs( - challenger=run_info.config, - incumbent=incumbent, - run_history=run_history, - log_traj=log_traj, - ) - if is_stage_done: - self.logger.info( - "Successive Halving iteration-step: %d-%d with " - "budget [%.2f / %d] - evaluated %d challenger(s)" - % ( - self.sh_iters + 1, - self.stage + 1, - self.all_budgets[self.stage], - self.max_budget, - self.n_configs_in_stage[self.stage], - ) - ) - - self._update_stage(run_history=run_history) - - # get incumbent cost - inc_perf = run_history.get_cost(incumbent) - - return incumbent, inc_perf - - def get_next_run( - self, - challengers: Optional[List[Configuration]], - incumbent: Configuration, - chooser: Optional[EPMChooser], - run_history: RunHistory, - repeat_configs: bool = True, - num_workers: int = 1, - ) -> Tuple[RunInfoIntent, RunInfo]: - """Selects which challenger to use based on the iteration stage and set the iteration - parameters. First iteration will choose configurations from the ``chooser`` or input - challengers, while the later iterations pick top configurations from the previously selected - challengers in that iteration. - - Parameters - ---------- - challengers : List[Configuration] - promising configurations - incumbent: Configuration - incumbent configuration - chooser : smac.optimizer.epm_configuration_chooser.EPMChooser - optimizer that generates next configurations to use for racing - run_history : smac.runhistory.runhistory.RunHistory - stores all runs we ran so far - repeat_configs : bool - if False, an evaluated configuration will not be generated again - num_workers: int - the maximum number of workers available - at a given time. - - Returns - ------- - intent: RunInfoIntent - Indicator of how to consume the RunInfo object - run_info: RunInfo - An object that encapsulates the minimum information to - evaluate a configuration - """ - if num_workers > 1: - warnings.warn( - "Consider using ParallelSuccesiveHalving instead of " - "SuccesiveHalving. The later will halt on each stage " - "transition until all configs for the current stage are completed." - ) - # if this is the first run, then initialize tracking variables - if not hasattr(self, "stage"): - self._update_stage(run_history=run_history) - - # In the case of multiprocessing, we have runs in Running stage, which have not - # been processed via process_results(). get_next_run() is called agnostically by - # smbo. To prevent launching more configs, than the ones needed, we query if - # there is room for more configurations, else we wait for process_results() - # to trigger a new stage - if self._all_config_inst_seed_pairs_launched(run_history, self.running_challenger): - return RunInfoIntent.WAIT, RunInfo( - config=None, - instance=None, - instance_specific="0", - seed=0, - cutoff=self.cutoff, - capped=False, - budget=0.0, - source_id=self.identifier, - ) - - # sampling from next challenger marks the beginning of a new iteration - self.iteration_done = False - - curr_budget = self.all_budgets[self.stage] - - # if all instances have been executed, then reset and move on to next config - if self.instance_as_budget: - prev_budget = int(self.all_budgets[self.stage - 1]) if self.stage > 0 else 0 - n_insts = int(curr_budget) - prev_budget - else: - n_insts = len(self.inst_seed_pairs) - - # The instances remaining tell us, per configuration, how many instances we - # have suggested to SMBO - n_insts_remaining = n_insts - if self.running_challenger is not None: - n_insts_remaining = n_insts - self.curr_inst_idx[self.running_challenger] - - # if there are instances pending, finish running configuration - if self.running_challenger and n_insts_remaining > 0: - challenger = self.running_challenger - new_challenger = False - else: - # select next configuration - if self.stage == 0: - # first stage, so sample from configurations/chooser provided - challenger = self._next_challenger( - challengers=challengers, - chooser=chooser, - run_history=run_history, - repeat_configs=repeat_configs, - ) - if challenger is None: - # If no challenger was sampled from the EPM or - # initial challengers, it might mean that the EPM - # is proposing a configuration that is currently running. - # There is a filtering on the above _next_challenger to return - # None if the proposed config us already in the run history - # To get a new config, we wait for more data - return RunInfoIntent.WAIT, RunInfo( - config=None, - instance=None, - instance_specific="0", - seed=0, - cutoff=self.cutoff, - capped=False, - budget=0.0, - source_id=self.identifier, - ) - - new_challenger = True - else: - # sample top configs from previously sampled configurations - try: - challenger = self.configs_to_run.pop(0) - new_challenger = False - except IndexError: - # self.configs_to_run is populated via update_stage, - # which is triggered after the completion of a run - # If by there are no more configs to run (which is the case - # if we run into a IndexError), - return RunInfoIntent.SKIP, RunInfo( - config=None, - instance=None, - instance_specific="0", - seed=0, - cutoff=self.cutoff, - capped=False, - budget=0.0, - source_id=self.identifier, - ) - - if challenger: - # We see a challenger for the first time, so no - # instance has been launched - self.curr_inst_idx[challenger] = 0 - self._chall_indx = self._chall_indx # type: int # make mypy happy - self._chall_indx += 1 - self.running_challenger = challenger - - # calculating the incumbent's performance for adaptive capping - # this check is required because: - # - there is no incumbent performance for the first ever 'intensify' run (from initial design) - # - during the 1st intensify run, the incumbent shouldn't be capped after being compared against itself - if incumbent and incumbent != challenger: - inc_runs = run_history.get_runs_for_config(incumbent, only_max_observed_budget=True) - inc_sum_cost = run_history.sum_cost(config=incumbent, instance_seed_budget_keys=inc_runs, normalize=True) - else: - inc_sum_cost = np.inf - if self.first_run: - self.logger.info("First run, no incumbent provided; challenger is assumed to be the incumbent") - incumbent = challenger - - assert type(inc_sum_cost) == float - - # Selecting instance-seed subset for this budget, depending on the kind of budget - if self.instance_as_budget: - prev_budget = int(self.all_budgets[self.stage - 1]) if self.stage > 0 else 0 - curr_insts = self.inst_seed_pairs[int(prev_budget) : int(curr_budget)] - else: - curr_insts = self.inst_seed_pairs - - self.logger.debug(" Running challenger - %s" % str(challenger)) - - # run the next instance-seed pair for the given configuration - instance, seed = curr_insts[self.curr_inst_idx[challenger]] # type: ignore[index] - # At this point self.curr_inst_idx[challenger] will still be an integer and might - # be marked LATER with np.inf, so ignore mypy error. - - # selecting cutoff if running adaptive capping - cutoff = self.cutoff - if self.run_obj_time: - cutoff = self._adapt_cutoff(challenger=challenger, run_history=run_history, inc_sum_cost=inc_sum_cost) - if cutoff is not None and cutoff <= 0: - # ran out of time to validate challenger - self.logger.debug("Stop challenger intensification due to adaptive capping.") - self.curr_inst_idx[challenger] = np.inf - - self.logger.debug("Cutoff for challenger: %s" % str(cutoff)) - - # For testing purposes, this attribute highlights whether a - # new challenger is proposed or not. Not required from a functional - # perspective - self.new_challenger = new_challenger - - capped = False - if (self.cutoff is not None) and (cutoff < self.cutoff): # type: ignore[operator] # noqa F821 - capped = True - - budget = 0.0 if self.instance_as_budget else curr_budget - - self.run_tracker[(challenger, instance, seed, budget)] = False - - # self.curr_inst_idx Tell us our current instance to be run. The upcoming return - # will launch a challenger on a given instance/seed/pair. The next time this function - # is called, we will like to run self.curr_inst_idx + 1 for this configuration - self.curr_inst_idx[challenger] += 1 - - return RunInfoIntent.RUN, RunInfo( - config=challenger, - instance=instance, - instance_specific=self.instance_specifics.get(instance, "0"), - seed=seed, - cutoff=cutoff, - capped=capped, - budget=budget, - source_id=self.identifier, - ) - - def _update_stage(self, run_history: RunHistory) -> None: - """Update tracking information for a new stage/iteration and update statistics. This method - is called to initialize stage variables and after all configurations of a successive halving - stage are completed. - - Parameters - ---------- - run_history : smac.runhistory.runhistory.RunHistory - stores all runs we ran so far - """ - if not hasattr(self, "stage"): - # initialize all relevant variables for first run - # (this initialization is not a part of init because hyperband uses the same init method and has a ) - # to track iteration and stage - self.sh_iters = 0 - self.stage = 0 - # to track challengers across stages - self.configs_to_run = [] # type: List[Configuration] - self.curr_inst_idx = {} - self.running_challenger = None - self.success_challengers = set() # successful configs - self.do_not_advance_challengers = set() # configs which are successful, but should not be advanced - self.fail_challengers = set() # capped configs and other failures - self.fail_chal_offset = 0 - - else: - self.stage += 1 - # only uncapped challengers are considered valid for the next iteration - valid_challengers = list( - (self.success_challengers | self.do_not_advance_challengers) - self.fail_challengers - ) - - if self.stage < len(self.all_budgets) and len(valid_challengers) > 0: - # if this is the next stage in same iteration, - # use top 'k' from the evaluated configurations for next iteration - - # determine 'k' for the next iteration - at least 1 - next_n_chal = int(max(1, self.n_configs_in_stage[self.stage])) - # selecting the top 'k' challengers for the next iteration - configs_to_run = self._top_k(configs=valid_challengers, run_history=run_history, k=next_n_chal) - self.configs_to_run = [ - config for config in configs_to_run if config not in self.do_not_advance_challengers - ] - # if some runs were capped, top_k returns less than the required configurations - # to handle that, we keep track of how many configurations are missing - # (since they are technically failed here too) - missing_challengers = int(self.n_configs_in_stage[self.stage]) - len(self.configs_to_run) - if missing_challengers > 0: - self.fail_chal_offset = missing_challengers - else: - self.fail_chal_offset = 0 - if next_n_chal == missing_challengers: - next_stage = True - self.logger.info( - "Successive Halving iteration-step: %d-%d with " - "budget [%.2f / %d] - expected %d new challenger(s), but " - "no configurations propagated to the next budget.", - self.sh_iters + 1, - self.stage + 1, - self.all_budgets[self.stage], - self.max_budget, - self.n_configs_in_stage[self.stage], - ) - else: - next_stage = False - else: - next_stage = True - - if next_stage: - # update stats for the prev iteration - self.stats.update_average_configs_per_intensify(n_configs=self._chall_indx) - - # reset stats for the new iteration - self._ta_time = 0 - self._chall_indx = 0 - self.num_run = 0 - - self.iteration_done = True - self.sh_iters += 1 - self.stage = 0 - self.run_tracker = {} - self.configs_to_run = [] - self.fail_chal_offset = 0 - - # randomize instance-seed pairs per successive halving run, if user specifies - if self.instance_order == "shuffle": - self.rs.shuffle(self.inst_seed_pairs) # type: ignore - - # to track configurations for the next stage - self.success_challengers = set() # successful configs - self.do_not_advance_challengers = set() # successful, but should not be advanced to the next budget/stage - self.fail_challengers = set() # capped/failed configs - self.curr_inst_idx = {} - self.running_challenger = None - - def _compare_configs( - self, - incumbent: Configuration, - challenger: Configuration, - run_history: RunHistory, - log_traj: bool = True, - ) -> Optional[Configuration]: - """Compares the challenger with current incumbent and returns the best configuration, based - on the given incumbent selection design. - - Parameters - ---------- - challenger : Configuration - promising configuration - incumbent : Configuration - best configuration so far - run_history : smac.runhistory.runhistory.RunHistory - stores all runs we ran so far - log_traj : bool - whether to log changes of incumbents in trajectory - - Returns - ------- - Optional[Configuration] - incumbent configuration - """ - if self.instance_as_budget: - new_incumbent = super()._compare_configs(incumbent, challenger, run_history, log_traj) - # if compare config returned none, then it is undecided. So return old incumbent - new_incumbent = incumbent if new_incumbent is None else new_incumbent - return new_incumbent - - # For real-valued budgets, compare configs based on the incumbent selection design - curr_budget = self.all_budgets[self.stage] - - # incumbent selection: best on any budget - if self.incumbent_selection == "any_budget": - new_incumbent = self._compare_configs_across_budgets( - challenger=challenger, - incumbent=incumbent, - run_history=run_history, - log_traj=log_traj, - ) - return new_incumbent - - # get runs for both configurations - inc_runs = run_history.get_runs_for_config(incumbent, only_max_observed_budget=True) - chall_runs = run_history.get_runs_for_config(challenger, only_max_observed_budget=True) - - if len(inc_runs) > 1: - raise ValueError( - "Number of incumbent runs on budget %f must not exceed 1, but is %d", - inc_runs[0].budget, - len(inc_runs), - ) - if len(chall_runs) > 1: - raise ValueError( - "Number of challenger runs on budget %f must not exceed 1, but is %d", - chall_runs[0].budget, - len(chall_runs), - ) - inc_run = inc_runs[0] - chall_run = chall_runs[0] - - # incumbent selection: highest budget only - if self.incumbent_selection == "highest_budget": - if chall_run.budget < self.max_budget: - self.logger.debug( - "Challenger (budget=%.4f) has not been evaluated on the highest budget %.4f yet.", - chall_run.budget, - self.max_budget, - ) - return incumbent - - # incumbent selection: highest budget run so far - if inc_run.budget > chall_run.budget: - self.logger.debug( - "Incumbent evaluated on higher budget than challenger (%.4f > %.4f), " "not changing the incumbent", - inc_run.budget, - chall_run.budget, - ) - return incumbent - if inc_run.budget < chall_run.budget: - self.logger.debug( - "Challenger evaluated on higher budget than incumbent (%.4f > %.4f), " "changing the incumbent", - chall_run.budget, - inc_run.budget, - ) - if log_traj: - # adding incumbent entry - self.stats.inc_changed += 1 - new_inc_cost = run_history.get_cost(challenger) - self.traj_logger.add_entry( - train_perf=new_inc_cost, - incumbent_id=self.stats.inc_changed, - incumbent=challenger, - budget=curr_budget, - ) - return challenger - - # incumbent and challenger were both evaluated on the same budget, compare them based on their cost - chall_cost = run_history.get_cost(challenger) - inc_cost = run_history.get_cost(incumbent) - if chall_cost < inc_cost: - self.logger.info( - "Challenger (%.4f) is better than incumbent (%.4f) on budget %.4f.", - chall_cost, - inc_cost, - chall_run.budget, - ) - self._log_incumbent_changes(incumbent, challenger) - new_incumbent = challenger - if log_traj: - # adding incumbent entry - self.stats.inc_changed += 1 # first incumbent - self.traj_logger.add_entry( - train_perf=chall_cost, - incumbent_id=self.stats.inc_changed, - incumbent=new_incumbent, - budget=curr_budget, - ) - else: - self.logger.debug( - "Incumbent (%.4f) is at least as good as the challenger (%.4f) on budget %.4f.", - inc_cost, - chall_cost, - inc_run.budget, - ) - if log_traj and self.stats.inc_changed == 0: - # adding incumbent entry - self.stats.inc_changed += 1 # first incumbent - self.traj_logger.add_entry( - train_perf=inc_cost, - incumbent_id=self.stats.inc_changed, - incumbent=incumbent, - budget=curr_budget, - ) - new_incumbent = incumbent - - return new_incumbent - - def _compare_configs_across_budgets( - self, - challenger: Configuration, - incumbent: Configuration, - run_history: RunHistory, - log_traj: bool = True, - ) -> Optional[Configuration]: - """Compares challenger with current incumbent on any budget. - - Parameters - ---------- - challenger : Configuration - promising configuration - incumbent : Configuration - best configuration so far - run_history : smac.runhistory.runhistory.RunHistory - stores all runs we ran so far - log_traj : bool - whether to log changes of incumbents in trajectory - - Returns - ------- - Optional[Configuration] - incumbent configuration - """ - curr_budget = self.all_budgets[self.stage] - - # compare challenger and incumbent based on cost - chall_cost = run_history.get_min_cost(challenger) - inc_cost = run_history.get_min_cost(incumbent) - if np.isfinite(chall_cost) and np.isfinite(inc_cost): - if chall_cost < inc_cost: - self.logger.info( - "Challenger (%.4f) is better than incumbent (%.4f) for any budget.", - chall_cost, - inc_cost, - ) - self._log_incumbent_changes(incumbent, challenger) - new_incumbent = challenger - if log_traj: - # adding incumbent entry - self.stats.inc_changed += 1 # first incumbent - self.traj_logger.add_entry( - train_perf=chall_cost, - incumbent_id=self.stats.inc_changed, - incumbent=new_incumbent, - budget=curr_budget, - ) - else: - self.logger.debug( - "Incumbent (%.4f) is at least as good as the challenger (%.4f) for any budget.", - inc_cost, - chall_cost, - ) - if log_traj and self.stats.inc_changed == 0: - # adding incumbent entry - self.stats.inc_changed += 1 # first incumbent - self.traj_logger.add_entry( - train_perf=inc_cost, - incumbent_id=self.stats.inc_changed, - incumbent=incumbent, - budget=curr_budget, - ) - new_incumbent = incumbent - else: - self.logger.debug("Non-finite costs from run history!") - new_incumbent = incumbent - - return new_incumbent - - def _top_k(self, configs: List[Configuration], run_history: RunHistory, k: int) -> List[Configuration]: - """Selects the top 'k' configurations from the given list based on their performance. - - This retrieves the performance for each configuration from the runhistory and checks - that the highest budget they've been evaluated on is the same for each of the configurations. - - Parameters - ---------- - configs: List[Configuration] - list of configurations to filter from - run_history: smac.runhistory.runhistory.RunHistory - stores all runs we ran so far - k: int - number of configurations to select - - Returns - ------- - List[Configuration] - top challenger configurations, sorted in increasing costs - """ - # extracting costs for each given configuration - config_costs = {} - # sample list instance-seed-budget key to act as base - run_key = run_history.get_runs_for_config(configs[0], only_max_observed_budget=True) - for c in configs: - # ensuring that all configurations being compared are run on the same set of instance, seed & budget - cur_run_key = run_history.get_runs_for_config(c, only_max_observed_budget=True) - - # Move to compare set -- get_runs_for_config queries form a dictionary - # which is not an ordered structure. Some queries to that dictionary returned unordered - # list which wrongly trigger the below if - if set(cur_run_key) != set(run_key): - raise ValueError( - "Cannot compare configs that were run on different instances-seeds-budgets: %s vs %s" - % (run_key, cur_run_key) - ) - config_costs[c] = run_history.get_cost(c) - - configs_sorted = [k for k, v in sorted(config_costs.items(), key=lambda item: item[1])] - # select top configurations only - top_configs = configs_sorted[:k] - return top_configs - - def _all_config_inst_seed_pairs_launched( - self, - run_history: RunHistory, - activate_configuration_being_intensified: Optional[Configuration], - ) -> bool: - """When running SH, M configs might require N instances. Before moving to the next stage, we - need to make sure that tasks (each of the MxN jobs) are launched. - - This function returns a true if any M configs are pending or if N instance/seed are - still remaining. - - Parameters - ---------- - run_history : RunHistory - stores all runs we ran so far - activate_configuration_being_intensified: Optional[Configuration] - The last configuration being actively processes by this intensifier - - Returns - ------- - bool: whether a instance/pair of any of the M configurations for the current - stage are pending - """ - # 1: First we count the number of configurations that have been launched - # We only submit a new configuration M if all instance-seed pairs of (M - 1) - # have been proposed - configurations_by_this_intensifier = [c for c, i, s, b in self.run_tracker] - running_configs = set() - for k, v in run_history.data.items(): - if run_history.ids_config[k.config_id] in configurations_by_this_intensifier: - # We get all configurations launched by the current intensifier - # regardless if status is RUNNING or not, to make it more robust - running_configs.add(run_history.ids_config[k.config_id]) - - # The total number of runs for this stage account for finished configurations - # (success + failed + do not advance) + the offset + running but not finished - # configurations. Also we account for the instances not launched for the - # currently running configuration - total_pending_configurations = max( - 0, - self.n_configs_in_stage[self.stage] - - ( - len( - set().union( - self.success_challengers, - self.fail_challengers, - self.do_not_advance_challengers, - running_configs, - ) - ) - + self.fail_chal_offset - ), - ) - - # 2: Second we have to account for the number of pending instances for the active - # configuration. We assume for all (M - 1) configurations, all N instances-seeds - # have been already been launched - curr_budget = self.all_budgets[self.stage] - if self.instance_as_budget: - prev_budget = int(self.all_budgets[self.stage - 1]) if self.stage > 0 else 0 - curr_insts = self.inst_seed_pairs[int(prev_budget) : int(curr_budget)] - else: - curr_insts = self.inst_seed_pairs - - if activate_configuration_being_intensified is None: - # When a new stage begins, there is no active configuration. - # Therefore activate_configuration_being_intensified is empty and all instances are - # remaining - pending_instances_to_launch = len(curr_insts) - else: - # self.curr_inst_idx - 1 is the last proposed instance/seed pair from get_next_run - # But it is zero indexed, so (self.curr_inst_idx - 1) + 1 is the number of - # configurations that we have proposed to run in total for the running - # configuration via get_next_run - pending_instances_to_launch = max( - len(curr_insts) - self.curr_inst_idx[activate_configuration_being_intensified], 0 - ) # type: ignore - - # If the there are any pending configuration, or instances/seed pending for the - # active runner, we return a boolean - return (total_pending_configurations + pending_instances_to_launch) <= 0 - - -class SuccessiveHalving(ParallelScheduler): - """Races multiple challengers against an incumbent using Successive Halving method. - - Implementation following the description in - "BOHB: Robust and Efficient Hyperparameter Optimization at Scale" (Falkner et al. 2018) - Supplementary reference: http://proceedings.mlr.press/v80/falkner18a/falkner18a-supp.pdf - - Successive Halving intensifier (and Hyperband) can operate on two kinds of budgets: - 1. **'Instances' as budget**: - When multiple instances are provided or when run objective is "runtime", this is the criterion used as budget - for successive halving iterations i.e., the budget determines how many instances the challengers are evaluated - on at a time. Top challengers for the next iteration are selected based on the combined performance across - all instances used. - - If ``initial_budget`` and ``max_budget`` are not provided, then they are set to 1 and total number - of available instances respectively by default. - - 2. **'Real-valued' budget**: - This is used when there is only one instance provided and when run objective is "quality", - i.e., budget is a positive, real-valued number that can be passed to the target algorithm as an argument. - It can be used to control anything by the target algorithm, Eg: number of epochs for training a neural network. - - ``initial_budget`` and ``max_budget`` are required parameters for this type of budget. - - Examples for successive halving (and hyperband) can be found here: - * Runtime objective and multiple instances *(instances as budget)*: `examples/spear_qcp/SMAC4AC_SH_spear_qcp.py` - * Quality objective and multiple instances *(instances as budget)*: `examples/SMAC4MF_sgd_instances.py` - * Quality objective and single instance *(real-valued budget)*: `examples/SMAC4MF_mlp.py` - - This class instantiates `_SuccessiveHalving` objects on a need basis, that is, to - prevent workers from being idle. The actual logic that implements the Successive halving method - lies on the _SuccessiveHalving class. - - Parameters - ---------- - stats: smac.stats.stats.Stats - stats object - traj_logger: smac.utils.io.traj_logging.TrajLogger - TrajLogger object to log all new incumbents - rng : np.random.RandomState - instances : List[str] - list of all instance ids - instance_specifics : Mapping[str, str] - mapping from instance name to instance specific string - cutoff : Optional[int] - cutoff of TA runs - deterministic : bool - whether the TA is deterministic or not - initial_budget : Optional[float] - minimum budget allowed for 1 run of successive halving - max_budget : Optional[float] - maximum budget allowed for 1 run of successive halving - eta : float - 'halving' factor after each iteration in a successive halving run. Defaults to 3 - num_initial_challengers : Optional[int] - number of challengers to consider for the initial budget. If None, calculated internally - run_obj_time : bool - whether the run objective is runtime or not (if true, apply adaptive capping) - n_seeds : Optional[int] - Number of seeds to use, if TA is not deterministic. Defaults to None, i.e., seed is set as 0 - instance_order : Optional[str] - how to order instances. Can be set to: [None, shuffle_once, shuffle] - * None - use as is given by the user - * shuffle_once - shuffle once and use across all SH run (default) - * shuffle - shuffle before every SH run - adaptive_capping_slackfactor : float - slack factor of adpative capping (factor * adaptive cutoff) - inst_seed_pairs : List[Tuple[str, int]], optional - Do not set this argument, it will only be used by hyperband! - min_chall: int - minimal number of challengers to be considered (even if time_bound is exhausted earlier). This class will - raise an exception if a value larger than 1 is passed. - incumbent_selection: str - How to select incumbent in successive halving. Only active for real-valued budgets. - Can be set to: [highest_executed_budget, highest_budget, any_budget] - * highest_executed_budget - incumbent is the best in the highest budget run so far (default) - * highest_budget - incumbent is selected only based on the highest budget - * any_budget - incumbent is the best on any budget i.e., best performance regardless of budget - """ - - def __init__( - self, - stats: Stats, - traj_logger: TrajLogger, - rng: np.random.RandomState, - instances: List[str], - instance_specifics: Mapping[str, str] = None, - cutoff: Optional[float] = None, - deterministic: bool = False, - initial_budget: Optional[float] = None, - max_budget: Optional[float] = None, - eta: float = 3, - num_initial_challengers: Optional[int] = None, - run_obj_time: bool = True, - n_seeds: Optional[int] = None, - instance_order: Optional[str] = "shuffle_once", - adaptive_capping_slackfactor: float = 1.2, - inst_seed_pairs: Optional[List[Tuple[str, int]]] = None, - min_chall: int = 1, - incumbent_selection: str = "highest_executed_budget", - ) -> None: - - super().__init__( - stats=stats, - traj_logger=traj_logger, - rng=rng, - instances=instances, - instance_specifics=instance_specifics, - cutoff=cutoff, - deterministic=deterministic, - run_obj_time=run_obj_time, - adaptive_capping_slackfactor=adaptive_capping_slackfactor, - min_chall=min_chall, - ) - - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - # Successive Halving Hyperparameters - self.n_seeds = n_seeds - self.instance_order = instance_order - self.inst_seed_pairs = inst_seed_pairs - self.incumbent_selection = incumbent_selection - self._instances = instances - self._instance_specifics = instance_specifics - self.initial_budget = initial_budget - self.max_budget = max_budget - self.eta = eta - self.num_initial_challengers = num_initial_challengers - - def _get_intensifier_ranking(self, intensifier: AbstractRacer) -> Tuple[int, int]: - """Given a intensifier, returns how advance it is. This metric will be used to determine - what priority to assign to the intensifier. - - Parameters - ---------- - intensifier: AbstractRacer - Intensifier to rank based on run progress - - Returns - ------- - ranking: int - the higher this number, the faster the intensifier will get - the running resources. For hyperband we can use the - sh_intensifier stage, for example - tie_breaker: int - The configurations that have been launched to break ties. For - example, in the case of Successive Halving it can be the number - of configurations launched - """ - # For mypy -- we expect to work with Hyperband instances - assert isinstance(intensifier, _SuccessiveHalving) - - # Each row of this matrix is id, stage, configs+instances for stage - # We use sh.run_tracker as a cheap way to know how advanced the run is - # in case of stage ties among successive halvers. sh.run_tracker is - # also emptied each iteration - stage = 0 - if hasattr(intensifier, "stage"): - # Newly created _SuccessiveHalving objects have no stage - stage = intensifier.stage - return stage, len(intensifier.run_tracker) - - def _add_new_instance(self, num_workers: int) -> bool: - """Decides if it is possible to add a new intensifier instance, and adds it. If a new - intensifier instance is added, True is returned, else False. - - Parameters - ---------- - num_workers: int - the maximum number of workers available - at a given time. - - Returns - ------- - Whether or not a successive halving instance was added - """ - if len(self.intensifier_instances) >= num_workers: - return False - - self.intensifier_instances[len(self.intensifier_instances)] = _SuccessiveHalving( - stats=self.stats, - traj_logger=self.traj_logger, - rng=self.rs, - instances=self._instances, - instance_specifics=self._instance_specifics, - cutoff=self.cutoff, - deterministic=self.deterministic, - initial_budget=self.initial_budget, - max_budget=self.max_budget, - eta=self.eta, - num_initial_challengers=self.num_initial_challengers, - run_obj_time=self.run_obj_time, - n_seeds=self.n_seeds, - instance_order=self.instance_order, - adaptive_capping_slackfactor=self.adaptive_capping_slackfactor, - inst_seed_pairs=self.inst_seed_pairs, - min_chall=self.min_chall, - incumbent_selection=self.incumbent_selection, - identifier=len(self.intensifier_instances), - ) - - return True diff --git a/smac/intensifier/__init__.py b/smac/intensifier/__init__.py new file mode 100644 index 000000000..07f552731 --- /dev/null +++ b/smac/intensifier/__init__.py @@ -0,0 +1,13 @@ +from smac.intensifier.abstract_intensifier import AbstractIntensifier +from smac.intensifier.abstract_parallel_intensifier import AbstractParallelIntensifier +from smac.intensifier.hyperband import Hyperband +from smac.intensifier.intensifier import Intensifier +from smac.intensifier.successive_halving import SuccessiveHalving + +__all__ = [ + "AbstractIntensifier", + "Intensifier", + "AbstractParallelIntensifier", + "SuccessiveHalving", + "Hyperband", +] diff --git a/smac/intensifier/abstract_intensifier.py b/smac/intensifier/abstract_intensifier.py new file mode 100644 index 000000000..ad3c5f6a9 --- /dev/null +++ b/smac/intensifier/abstract_intensifier.py @@ -0,0 +1,369 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import Any, Callable, Iterator, Optional + +import time + +import numpy as np +from ConfigSpace import Configuration + +from smac.runhistory import TrialInfo, TrialInfoIntent, TrialValue +from smac.runhistory.runhistory import RunHistory +from smac.scenario import Scenario +from smac.stats import Stats +from smac.utils.logging import format_array, get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class AbstractIntensifier: + """Base class for all racing methods. + + The intensification is designed to output a TrialInfo object with enough information + to run a given configuration (for example, the trial info contains the instance/seed + pair, as well as the associated resources). + + A worker can execute this TrialInfo object and produce a TrialValue object with the + execution results. Each intensifier process the TrialValue object and updates its + internal state in preparation for the next iteration. + + Parameters + ---------- + scenario : Scenario + min_config_calls : int, defaults to 1 + Minimum number of trials per config (summed over all calls to intensify). + max_config_calls : int, defaults to 2000 + Maximum number of trials per config (summed over all calls to intensify). + min_challenger : int, defaults to 1 + Minimal number of challengers to be considered (even if time_bound is exhausted earlier). + seed : int | None, defaults to none + """ + + def __init__( + self, + scenario: Scenario, + min_config_calls: int = 1, + max_config_calls: int = 2000, + min_challenger: int = 1, + seed: int | None = None, + ): + + if seed is None: + seed = scenario.seed + + self._scenario = scenario + self._seed = seed + self._rng = np.random.RandomState(seed) + self._deterministic = scenario.deterministic + self._min_config_calls = min_config_calls + self._max_config_calls = max_config_calls + self._min_challenger = min_challenger + self._stats: Stats | None = None + + # Set the instances + self._instances: list[str | None] + if scenario.instances is None: + # We need to include None here to tell whether None instance was evaluated or not + self._instances = [None] + else: + # Removing duplicates here + # Fun fact: When using a set here, it always includes randomness + self._instances = list(dict.fromkeys(scenario.instances)) + + # General attributes + self._num_trials = 0 # Number of trials done in an iteration so far + self._challenger_id = 0 + self._repeat_configs = False # Repeating configurations is discouraged for parallel trials. + self._iteration_done = False # Marks the end of an iteration. + self._target_function_time = 0.0 + + @property + def repeat_configs(self) -> bool: + """Whether configs should be repeated or not.""" + return self._repeat_configs + + @property + def iteration_done(self) -> bool: + """Whether an iteration is done or not.""" + return self._iteration_done + + @property + def num_trials(self) -> int: + """How many trials have been done in an iteration so far.""" + return self._num_trials + + @property + @abstractmethod + def uses_seeds(self) -> bool: + """If the intensifier needs to make use of seeds.""" + raise NotImplementedError + + @property + @abstractmethod + def uses_budgets(self) -> bool: + """If the intensifier needs to make use of budgets.""" + raise NotImplementedError + + @property + @abstractmethod + def uses_instances(self) -> bool: + """If the intensifier needs to make use of instances.""" + raise NotImplementedError + + @abstractmethod + def get_target_function_seeds(self) -> list[int]: + """Which seeds are used to call the target function.""" + raise NotImplementedError + + @abstractmethod + def get_target_function_budgets(self) -> list[float | None]: + """Which budgets are used to call the target function.""" + raise NotImplementedError + + @abstractmethod + def get_target_function_instances(self) -> list[str | None]: + """Which instances are used to call the target function.""" + raise NotImplementedError + + @property + def meta(self) -> dict[str, Any]: + """Returns the meta data of the created object.""" + return { + "name": self.__class__.__name__, + "min_config_calls": self._min_config_calls, + "max_config_calls": self._max_config_calls, + "min_challenger": self._min_challenger, + "seed": self._seed, + } + + def get_next_trial( + self, + challengers: list[Configuration] | None, + incumbent: Configuration, + get_next_configurations: Callable[[], Iterator[Configuration]] | None, + runhistory: RunHistory, + repeat_configs: bool = True, + n_workers: int = 1, + ) -> tuple[TrialInfoIntent, TrialInfo]: + """Abstract method for choosing the next challenger. If no more challengers are available, the method should + issue a SKIP via RunInfoIntent.SKIP, so that a new iteration can sample new configurations. + + Parameters + ---------- + challengers : list[Configuration] | None + Promising configurations. + incumbent : Configuration + Incumbent configuration. + get_next_configurations : Callable[[], Iterator[Configuration]] | None, defaults to none + Function that generates next configurations to use for racing. + runhistory : RunHistory + repeat_configs : bool, defaults to true + If false, an evaluated configuration will not be generated again. + n_workers : int, optional, defaults to 1 + The maximum number of workers available. + + Returns + ------- + TrialInfoIntent + Indicator of how to consume the TrialInfo object. + TrialInfo + An object that encapsulates necessary information of the trial. + """ + raise NotImplementedError() + + def process_results( + self, + trial_info: TrialInfo, + trial_value: TrialValue, + incumbent: Configuration | None, + runhistory: RunHistory, + time_bound: float, + log_trajectory: bool = True, + ) -> tuple[Configuration, float | list[float]]: + """The intensifier stage will be updated based on the results/status of a configuration + execution. Also, a incumbent will be determined. + + Parameters + ---------- + trial_info : TrialInfo + trial_value: TrialValue + incumbent : Configuration | None + Best configuration seen so far. + runhistory : RunHistory + time_bound : float + Time [sec] available to perform intensify. + log_trajectory: bool + Whether to log changes of incumbents in the trajectory. + + Returns + ------- + incumbent: Configuration + Current (maybe new) incumbent configuration. + incumbent_costs: float | list[float] + Empirical cost(s) of the incumbent configuration. + """ + raise NotImplementedError() + + def _next_challenger( + self, + challengers: list[Configuration] | None, + get_next_configurations: Callable[[], Iterator[Configuration]] | None, + runhistory: RunHistory, + repeat_configs: bool = True, + ) -> Configuration | None: + """Retuns the next challenger to use in intensification. If challenger is none, then the + optimizer will be used to generate the next challenger. + + Parameters + ---------- + challengers : list[Configuration] | None + Promising configurations to evaluate next. + get_next_configurations : Callable[[], Iterator[Configuration]] | None, defaults to none + Function that generates next configurations to use for racing. + runhistory : RunHistory + repeat_configs : bool, defaults to true + If false, an evaluated configuration will not be generated again. + + Returns + ------- + configuration : Configuration | None + Next challenger to use. If no challenger was found, none is returned. + """ + start_time = time.time() + + chall_gen: Iterator[Optional[Configuration]] + if challengers: + # iterate over challengers provided + logger.debug("Using provied challengers.") + chall_gen = (c for c in challengers) + elif get_next_configurations: + # generating challengers on-the-fly if optimizer is given + logger.debug("Generating new challenger from optimizer.") + chall_gen = get_next_configurations() + else: + raise ValueError("No configurations (function) provided. Can not generate challenger!") + + logger.debug("Time spend to select next challenger: %.4f" % (time.time() - start_time)) + + # Select challenger from the generators + assert chall_gen is not None + for challenger in chall_gen: + # Repetitions allowed + if repeat_configs: + return challenger + + used_configs = runhistory.get_configs() # set(runhistory.get_configs()) + + # Otherwise, select only a unique challenger + if challenger not in used_configs: + return challenger + + logger.debug("No valid challenger was generated!") + return None + + def _compare_configs( + self, + incumbent: Configuration, + challenger: Configuration, + runhistory: RunHistory, + log_trajectory: bool = True, + ) -> Configuration | None: + """Compare two configuration wrt the runhistory and return the one which performs better (or + None if the decision is not safe). + + Decision strategy to return x as being better than y: + * x has at least as many trials as y. + * x performs better than y on the intersection of trials on x and y. + + Note + ---- + Implicit assumption: Challenger was evaluated on the same instance-seed pairs as incumbent. + + Returns + ------- + configuration : Configuration | None + The better configuration. If the decision is not sure, none is returned. + """ + inc_trials = runhistory.get_trials(incumbent, only_max_observed_budget=True) + chall_trials = runhistory.get_trials(challenger, only_max_observed_budget=True) + to_compare_trials = set(inc_trials).intersection(chall_trials) + + # Performance on challenger trials, the challenger only becomes incumbent + # if it dominates the incumbent + chal_perf = runhistory.average_cost(challenger, to_compare_trials, normalize=True) + inc_perf = runhistory.average_cost(incumbent, to_compare_trials, normalize=True) + + assert type(chal_perf) == float + assert type(inc_perf) == float + + # Line 15 + if np.any(chal_perf > inc_perf) and len(chall_trials) >= self._min_config_calls: + chal_perf_format = format_array(chal_perf) + inc_perf_format = format_array(inc_perf) + + # Incumbent beats challenger + logger.debug( + f"Incumbent ({inc_perf_format}) is better than challenger " + f"({chal_perf_format}) on {len(chall_trials)} trials." + ) + + return incumbent + + # Line 16 + # This statement is true if both incumbent trials and challenger trials are the same + if not set(inc_trials) - set(chall_trials): + # No plateau walks + if np.any(chal_perf >= inc_perf): + chal_perf_format = format_array(chal_perf) + inc_perf_format = format_array(inc_perf) + + logger.debug( + f"Incumbent ({inc_perf_format}) is at least as good as the " + f"challenger ({chal_perf_format}) on {len(chall_trials)} trials." + ) + assert self._stats + if log_trajectory and self._stats.incumbent_changed == 0: + self._stats.add_incumbent(cost=chal_perf, incumbent=incumbent) + + return incumbent + + # Challenger is better than incumbent and has at least the same trials as incumbent. + # -> Change incumbent + n_samples = len(chall_trials) + chal_perf_format = format_array(chal_perf) + inc_perf_format = format_array(inc_perf) + + logger.info( + f"Challenger ({chal_perf_format}) is better than incumbent ({inc_perf_format}) " + f"on {n_samples} trials." + ) + self._log_incumbent_changes(incumbent, challenger) + + if log_trajectory: + assert self._stats + self._stats.add_incumbent(cost=chal_perf, incumbent=challenger) + + return challenger + + # Undecided + return None + + def _log_incumbent_changes( + self, + incumbent: Configuration | None, + challenger: Configuration | None, + ) -> None: + if incumbent is None or challenger is None: + return + + params = sorted([(param, incumbent[param], challenger[param]) for param in challenger.keys()]) + logger.info("Changes in incumbent:") + for param in params: + if param[1] != param[2]: + logger.info("--- %s: %r -> %r" % param) + else: + logger.debug("--- %s remains unchanged: %r", param[0], param[1]) diff --git a/smac/intensifier/abstract_parallel_intensifier.py b/smac/intensifier/abstract_parallel_intensifier.py new file mode 100644 index 000000000..3154e07ee --- /dev/null +++ b/smac/intensifier/abstract_parallel_intensifier.py @@ -0,0 +1,276 @@ +from __future__ import annotations + +from typing import Callable, Iterator + +import os + +from ConfigSpace import Configuration + +from smac.intensifier.abstract_intensifier import AbstractIntensifier +from smac.runhistory import TrialInfo, TrialInfoIntent, TrialValue +from smac.runhistory.runhistory import RunHistory +from smac.scenario import Scenario +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class AbstractParallelIntensifier(AbstractIntensifier): + """Common Racer class for Intensifiers that will schedule configurations on a parallel fashion. + + This class instantiates intensifier objects on a need basis, that is, to + prevent workers from being idle. This intensifier objects will give configurations + to run. + + scenario : Scenario + min_challenger : int, optional, defaults to 1 + Minimum number of trials per config (summed over all calls to intensify). + seed : int | None, defaults to None + """ + + def __init__( + self, + scenario: Scenario, + min_challenger: int = 1, + seed: int | None = None, + ) -> None: + + super().__init__( + scenario=scenario, + min_challenger=min_challenger, + seed=seed, + ) + + # We have a pool of instances that yield configurations ot run + self._intensifier_instances: dict[int, AbstractIntensifier] = {} + self._print_worker_warning = True + + @property + def uses_seeds(self) -> bool: # noqa: D102 + return True + + def get_next_trial( + self, + challengers: list[Configuration] | None, + incumbent: Configuration, + get_next_configurations: Callable[[], Iterator[Configuration]] | None, + runhistory: RunHistory, + repeat_configs: bool = False, + n_workers: int = 1, + ) -> tuple[TrialInfoIntent, TrialInfo]: + """This procedure decides from which instance to pick a config, in order to determine the + next run. To prevent having idle workers, this procedure creates new instances + up to the maximum number of workers available. + + If no new intensifier instance can be created and all intensifier + objects need to wait for more data, this procedure sends a wait request to smbo. + + Parameters + ---------- + challengers : list[Configuration] | None + Promising configurations. + incumbent : Configuration + Incumbent configuration. + get_next_configurations : Callable[[], Iterator[Configuration]] | None, defaults to none + Function that generates next configurations to use for racing. + runhistory : RunHistory + repeat_configs : bool, defaults to true + If false, an evaluated configuration will not be generated again. + n_workers : int, optional, defaults to 1 + The maximum number of workers available. + + Returns + ------- + TrialInfoIntent + Indicator of how to consume the TrialInfo object. + TrialInfo + An object that encapsulates necessary information of the trial. + """ + if n_workers <= 1 and self._print_worker_warning: + logger.warning( + f"{self.__class__.__name__} is executed with {n_workers} worker(s) only. " + f"However, your system supports up to {os.cpu_count()} workers. Consider increasing the workers " + "in the scenario." + ) + self._print_worker_warning = False + else: + self._print_worker_warning = False + + # If repeat_configs is True, that means that not only self can repeat + # configurations, but also in the context of multiprocessing, N + # intensifier instances will also share configurations. The later + # is not supported + + # TODO: I'm a bit confused because successive halving itself is a parallel scheduler but + # always repeating configs? + + # if repeat_configs: + # raise ValueError("repeat_configs is not supported for parallel execution") + + # First get a config to run from a SH instance + for i in self._sort_instances_by_stage(self._intensifier_instances): + intent, trial_info = self._intensifier_instances[i].get_next_trial( + challengers=challengers, + incumbent=incumbent, + get_next_configurations=get_next_configurations, + runhistory=runhistory, + repeat_configs=repeat_configs, + ) + + # if asked to wait, the intensifier cannot come up + # with a new configuration, so we continue + if intent == TrialInfoIntent.WAIT: + continue + + return intent, trial_info + + # If gotten to this point, we might look into adding a new intensifier + if self._add_new_instance(n_workers): + return self._intensifier_instances[len(self._intensifier_instances) - 1].get_next_trial( + challengers=challengers, + incumbent=incumbent, + get_next_configurations=get_next_configurations, + runhistory=runhistory, + repeat_configs=repeat_configs, + ) + + # If got to this point, no new instance can be added as + # there are no idle workers and all running instances have to + # wait, so we return a wait intent + return TrialInfoIntent.WAIT, TrialInfo( + config=None, + instance=None, + seed=None, + budget=None, + ) + + def process_results( + self, + trial_info: TrialInfo, + trial_value: TrialValue, + incumbent: Configuration | None, + runhistory: RunHistory, + time_bound: float, + log_trajectory: bool = True, + ) -> tuple[Configuration, float | list[float]]: + """The intensifier stage will be updated based on the results/status of a configuration + execution. To do so, this procedures redirects the result argument, to the + respective intensifier object that generated the original config. + + Also, an incumbent will be determined. This determination is done + using the complete run history, so we rely on the current intensifier + choice of incumbent. That is, no need to go over each instance to + get the incumbent, as there is no local runhistory + + Parameters + ---------- + trial_info : TrialInfo + trial_value: TrialValue + incumbent : Configuration | None + Best configuration seen so far. + runhistory : RunHistory + time_bound : float + Time [sec] available to perform intensify. + log_trajectory: bool + Whether to log changes of incumbents in the trajectory. + + Returns + ------- + incumbent: Configuration + Current (maybe new) incumbent configuration. + incumbent_costs: float | list[float] + Empirical cost(s) of the incumbent configuration. + """ + if len(self._intensifier_instances) == 0: + self._add_new_instance(1) + + return self._intensifier_instances[trial_info.source].process_results( + trial_info=trial_info, + trial_value=trial_value, + incumbent=incumbent, + runhistory=runhistory, + time_bound=time_bound, + log_trajectory=log_trajectory, + ) + + def _add_new_instance(self, n_workers: int) -> bool: + """Decides if it is possible to add a new intensifier instance, and adds it. If a new + intensifier instance is added, true is returned, else false. + + Parameters + ---------- + n_workers : int + The maximum number of workers available at a given time. + + Returns + ------- + successfully_added : bool + Whether or not an instance was added. + """ + raise NotImplementedError() + + def _get_intensifier_ranking(self, intensifier: AbstractIntensifier) -> tuple[int, int]: + """Given a intensifier, returns how advanced it is. This metric will be used to determine + what priority to assign to the intensifier. + + Parameters + ---------- + intensifier : AbstractRacer + Intensifier to rank based on run progress + + Returns + ------- + ranking : int + the higher this number, the faster the intensifier will get + the running resources. For hyperband we can use the + sh_intensifier stage, for example + tie_breaker : int + The configurations that have been launched to break ties. For + example, in the case of Successive Halving it can be the number + of configurations launched + """ + raise NotImplementedError() + + def _sort_instances_by_stage(self, instances: dict[int, AbstractIntensifier]) -> list[int]: + """This procedure dictates what SH to prioritize in launching jobs. It prioritizes resource + allocation to SH instances that have higher stages. In case of tie, we prioritize the SH + instance with more launched configs. + + Parameters + ---------- + instances: dict[int, AbstractRacer] + Dict with the instances to prioritize. + + Returns + ------- + order : list[int] + The order in which to query for new jobs. + """ + # This function might be called when no intensifier instances + # exist (first iteration), so we return an empty list in that case + if len(instances) == 0: + return [] + + # We want to prioritize runs that are close to finishing an iteration. + # In the context of successive halving for example, an iteration has stages + # that are composed of # of configs and each configs has # of instance-seed pairs + # so ranking will be the stage (the higher the stage, the more we want this run + # to be finished earlier). Also, in case of tie (runs at same stage) we need a + # tie breaker, which can be the number of configs already launched + preference = [] + for i, sh in instances.items(): + ranking, tie_breaker = self._get_intensifier_ranking(sh) + preference.append( + (i, ranking, tie_breaker), + ) + + # First we sort by config/instance/seed as the less important criteria + preference.sort(key=lambda x: x[2], reverse=True) + + # Second by stage. The more advanced the stage is, the more we want + # this intensifier to finish early + preference.sort(key=lambda x: x[1], reverse=True) + return [i for i, s, c in preference] diff --git a/smac/intensifier/hyperband.py b/smac/intensifier/hyperband.py new file mode 100644 index 000000000..684de02ab --- /dev/null +++ b/smac/intensifier/hyperband.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +from smac.intensifier.abstract_intensifier import AbstractIntensifier +from smac.intensifier.successive_halving import SuccessiveHalving + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class Hyperband(SuccessiveHalving): + """Races multiple challengers against an incumbent using Hyperband method. + + Implementation from "BOHB: Robust and Efficient Hyperparameter Optimization at Scale" (Falkner et al. 2018) + + Hyperband is an extension of the Successive Halving intensifier. Please refer to `SuccessiveHalving` documentation + for more detailed information about the different types of budgets possible and the way instances are handled. + + Internally, this class uses the `HyperbandWorker` class which actually implements the hyperband logic. + To allow for parallelism, Hyperband can create multiple `HyperbandWorker` instances, based on the number + of idle workers available. + + Parameters + ---------- + scenario : Scenario + instance_seed_pairs : list[tuple[str | None, int]] | None, defaults to None + This argument is used by Hyperband. + instance_order : str | None, defaults to `shuffle_once` + How to order the instances. Can be set to: + * None: Use as is given by the user. + * shuffle_once: Shuffle once and use across all Successive Halving run . + * shuffle: Shuffle before every Successive Halving run + incumbent_selection : str, defaults to `highest_executed_budget` + How to select the incumbent in Successive Halving. Only active for (real-valued) budgets. Can be set to: + * highest_executed_budget: Incumbent is the best in the highest budget run so far. + * highest_budget: Incumbent is selected only based on the highest budget. + * any_budget: Incumbent is the best on any budget i.e., best performance regardless of budget. + n_initial_challengers : int | None, defaults to None + Number of challengers to consider for the initial budget. If not specified, it is calculated internally. + min_challenger : int, defaults to 1 + Minimal number of challengers to be considered (even if time_bound is exhausted earlier). + eta : float, defaults to 3 + The "halving" factor after each iteration in a Successive Halving run. + seed : int | None, defaults to None + n_seeds : int | None, defaults to None + The number of seeds to use if the target function is non-deterministic. + """ + + def _get_intensifier_ranking(self, intensifier: AbstractIntensifier) -> tuple[int, int]: + from smac.intensifier.hyperband_worker import HyperbandWorker + + assert isinstance(intensifier, HyperbandWorker) + assert intensifier._sh_intensifier + + # For hyperband, we use the internal successive halving as a criteria + # to see how advanced this intensifier is + stage = intensifier._sh_intensifier.stage + + return stage, len(intensifier._sh_intensifier._run_tracker) + + def _add_new_instance(self, n_workers: int) -> bool: + from smac.intensifier.hyperband_worker import HyperbandWorker + + if len(self._intensifier_instances) >= n_workers: + return False + + hp = HyperbandWorker( + hyperband=self, + identifier=len(self._intensifier_instances), + ) + hp._stats = self._stats + self._intensifier_instances[len(self._intensifier_instances)] = hp + + return True diff --git a/smac/intensifier/hyperband_worker.py b/smac/intensifier/hyperband_worker.py new file mode 100644 index 000000000..d33775c56 --- /dev/null +++ b/smac/intensifier/hyperband_worker.py @@ -0,0 +1,179 @@ +from __future__ import annotations + +from typing import Callable, Iterator + +import numpy as np +from ConfigSpace import Configuration + +from smac.intensifier.hyperband import Hyperband +from smac.intensifier.successive_halving_worker import SuccessiveHalvingWorker +from smac.runhistory import TrialInfo, TrialInfoIntent, TrialValue +from smac.runhistory.runhistory import RunHistory +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class HyperbandWorker(SuccessiveHalvingWorker): + """This is the worker class for Hyperband. + + Warning + ------- + Do not use this class as stand-alone. + + Parameters + ---------- + hyperband : Hyperband + The controller of the instance. + identifier : int, defaults to 0 + Adds a numerical identifier on the instance. Used for debug and tagging logger messages properly. + """ + + def __init__( + self, + hyperband: Hyperband, + identifier: int = 0, + ) -> None: + + super().__init__( + successive_halving=hyperband, + identifier=identifier, + ) + + min_budget = hyperband._min_budget + max_budget = hyperband._max_budget + eta = hyperband._eta + + # Overwrite logger + self._identifier = identifier + self._logger = get_logger(f"{__name__}.{identifier}") + + # To track completed hyperband iterations + self._hyperband = hyperband + self._hb_iters = 0 + self._sh_intensifier: SuccessiveHalvingWorker | None = None + + # Setting initial running budget for future iterations (s & s_max from Algorithm 1) + self._s_max = int(np.floor(np.log(max_budget / min_budget) / np.log(eta))) + self._s = self._s_max + + # We update our sh intensifier directly + self._update_worker() + + @property + def uses_seeds(self) -> bool: # noqa: D102 + return self._hyperband.uses_seeds + + @property + def uses_budgets(self) -> bool: # noqa: D102 + return self._hyperband.uses_budgets + + @property + def uses_instances(self) -> bool: # noqa: D102 + return self._hyperband.uses_instances + + def process_results( + self, + trial_info: TrialInfo, + trial_value: TrialValue, + incumbent: Configuration | None, + runhistory: RunHistory, + time_bound: float, + log_trajectory: bool = True, + ) -> tuple[Configuration, float]: # noqa: D102 + assert self._sh_intensifier + + # run 1 iteration of successive halving + incumbent, inc_perf = self._sh_intensifier.process_results( + trial_info=trial_info, + trial_value=trial_value, + incumbent=incumbent, + runhistory=runhistory, + time_bound=time_bound, + log_trajectory=log_trajectory, + ) + self._num_trials += 1 + + # reset if SH iteration is over, else update for next iteration + if self._sh_intensifier._iteration_done: + self._update_stage() + + return incumbent, inc_perf + + def get_next_trial( + self, + challengers: list[Configuration] | None, + incumbent: Configuration, + get_next_configurations: Callable[[], Iterator[Configuration]] | None, + runhistory: RunHistory, + repeat_configs: bool = True, + n_workers: int = 1, + ) -> tuple[TrialInfoIntent, TrialInfo]: # noqa: D102 + if n_workers > 1: + raise ValueError( + "HyperBand does not support more than 1 worker, yet " + "the argument n_workers to get_next_trial is {}".format(n_workers) + ) + + # Sampling from next challenger marks the beginning of a new iteration + self._iteration_done = False + + assert self._sh_intensifier + intent, trial_info = self._sh_intensifier.get_next_trial( + challengers=challengers, + incumbent=incumbent, + get_next_configurations=get_next_configurations, + runhistory=runhistory, + repeat_configs=self._sh_intensifier._repeat_configs, + ) + + # For testing purposes, this attribute highlights whether a new challenger is proposed or not + # Not required from a functional perspective + self._new_challenger = self._sh_intensifier._new_challenger + + return intent, trial_info + + def _update_stage(self, runhistory: RunHistory = None) -> None: + if self._s == 0: + # Reset if HB iteration is over + self._s = self._s_max + self._hb_iters += 1 + self._iteration_done = True + self._num_trials = 0 + else: + # Update for next iteration + self._s -= 1 + + self._update_worker() + + def _update_worker(self) -> None: + """Updates the successive halving worker based on `self._s`.""" + max_budget = self._hyperband._max_budget + eta = self._hyperband._eta + + # Compute min budget for new SH run + sh_min_budget = eta**-self._s * max_budget + + # Sample challengers for next iteration (based on HpBandster package) + n_challengers = int(np.floor((self._s_max + 1) / (self._s + 1)) * eta**self._s) + + # Compute this for the next round + n_configs_in_stage = n_challengers * np.power(eta, -np.linspace(0, self._s, self._s + 1)) + n_configs_in_stage = np.array(np.round(n_configs_in_stage), dtype=int).tolist() + + self._logger.info( + "Finished Hyperband iteration-step %d-%d with initial budget %d." + % (self._hb_iters + 1, self._s_max - self._s + 1, sh_min_budget) + ) + + # Creating a new Successive Halving intensifier with the current running budget + self._sh_intensifier = SuccessiveHalvingWorker( + successive_halving=self._hyperband, + identifier=self._identifier, + _all_budgets=self._all_budgets[(-self._s - 1) :], + _n_configs_in_stage=n_configs_in_stage, + _min_budget=sh_min_budget, + _max_budget=max_budget, + ) + self._sh_intensifier._stats = self._stats diff --git a/smac/intensifier/intensifier.py b/smac/intensifier/intensifier.py new file mode 100644 index 000000000..92ab91f5d --- /dev/null +++ b/smac/intensifier/intensifier.py @@ -0,0 +1,809 @@ +from __future__ import annotations + +from typing import Any, Callable, Iterator, Optional, cast + +from collections import Counter + +from ConfigSpace import Configuration + +from smac.constants import MAXINT +from smac.intensifier.abstract_intensifier import AbstractIntensifier +from smac.intensifier.stages import IntensifierStage +from smac.runhistory import ( + InstanceSeedBudgetKey, + TrialInfo, + TrialInfoIntent, + TrialValue, +) +from smac.runhistory.runhistory import RunHistory +from smac.scenario import Scenario +from smac.utils.logging import format_array, get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class Intensifier(AbstractIntensifier): + r"""Races challengers against an incumbent. + + SMAC's intensification procedure, in detail: + Intensify(Θ_new, θ_inc, M, R, t_intensify, Π, c_hat) c_hat(θ, Π') denotes the empirical cost of θ on + the subset of instances Π' ⊆ Π, based on the trials in R; max_config_calls is a parameter where: + Θ_new: Sequence of parameter settings to evaluate, challengers in this class. + θ_inc: incumbent parameter setting, incumbent in this class. + + 1 for i := 1, . . . , length(Θ_new) do + 2 θ_new ← Θ_new[i] + + STAGE -> RUN_INCUMBENT + + 3 if R contains less than max_config_calls trials with configuration θ_inc then + 4 Π' ← {π'∈ Π | R contains less than or equal number of trials using θ_inc and π' + 0 than using θ_inc and any other π''∈ Π} + 5 π ← instance sampled uniformly at random from Π' + 6 s ← seed, drawn uniformly at random + 7 R ← ExecuteRun(R, θ_inc, π, s) + 8 N ← 1 + + STAGE -> RUN_CHALLENGER + + 9 while true do + 10 S_missing ← {instance, seed} pairs for which θ_inc was run before, but not θ_new + 11 S_torun ← random subset of S_missing of size min(N, size(S_missing)) + 12 foreach (π, s) ∈ S_torun do R ← ExecuteRun(R, θ_new, π, s) + 13 S_missing ← S_missing \\ S_torun + 14 Π_common ← instances for which we previously ran both θ_inc and θ_new + 15 if c_hat(θ_new, Π_common) > c_hat(θ_inc, Π_common) then break + 16 else if S_missing = ∅ then θ_inc ← θ_new; break + 17 else N ← 2 · N + 18 if time spent in this call to this procedure exceeds t_intensify and i ≥ 2 then break + 19 return [R, θ_inc] + + Parameters + ---------- + scenario : Scenario + min_config_calls : int, defaults to 1 + Minimum number of trials per config (summed over all calls to intensify). + max_config_calls : int, defaults to 2000 + Maximum number of trials per config (summed over all calls to intensify). + min_challenger : int, defaults to 2 + Minimal number of challengers to be considered (even if time_bound is exhausted earlier). + intensify_percentage : float, defaults to 0.5 + How much percentage of the time should configurations be intensified (evaluated on higher budgets or + more instances). This parameter is accessed in the SMBO class. + race_against : Configuration | None, defaults to none + If incumbent changes, race this configuration always against new incumbent. Prevents sometimes + over-tuning. + seed : int | None, defaults to none + """ + + def __init__( + self, + scenario: Scenario, + min_config_calls: int = 1, + max_config_calls: int = 2000, + min_challenger: int = 2, + intensify_percentage: float = 0.5, + race_against: Configuration | None = None, + seed: int | None = None, + ): + if scenario.deterministic: + if min_challenger != 1: + logger.info("The number of minimal challengers is set to one for deterministic algorithms.") + + min_challenger = 1 + + # Intensify percentage must be between 0 and 1 + assert intensify_percentage >= 0.0 and intensify_percentage <= 1.0 + + super().__init__( + scenario=scenario, + min_config_calls=min_config_calls, + max_config_calls=max_config_calls, + min_challenger=min_challenger, + seed=seed, + ) + + # General attributes + self._race_against = race_against + + if race_against is not None and race_against.origin is None: + assert self._race_against is not None + if race_against == scenario.configspace.get_default_configuration(): + self._race_against.origin = "Default" + else: + logger.warning( + "The passed configuration to the intensifier was not specified with an origin. " + "The origin is set to `Unknown`." + ) + self._race_against.origin = "Unknown" + + self._elapsed_time = 0.0 + + # Stage variables + # the intensification procedure is divided into 4 'stages': + # 0. Run 1st configuration (only in the 1st run when incumbent=None) + # 1. Add incumbent run + # 2. Race challenger + # 3. Race against configuration for a new incumbent + self._stage = IntensifierStage.RUN_FIRST_CONFIG + self._n_iters = 0 + + # Challenger related variables + self._challenger_id = 0 + self._num_challenger_run = 0 + self._current_challenger = None + self._continue_challenger = False + self._configs_to_run: Iterator[Optional[Configuration]] = iter([]) + self._update_configs_to_run = True + self._intensify_percentage = intensify_percentage + self._last_seed_idx = -1 + self._target_function_seeds = [ + int(s) for s in self._rng.randint(low=0, high=MAXINT, size=self._max_config_calls) + ] + + # Racing related variables + self._to_run: list[InstanceSeedBudgetKey] = [] + self._N = -1 + + @property + def intensify_percentage(self) -> float: + """How much percentage of the time should configurations be intensified (evaluated on higher budgets or + more instances). This parameter is accessed in the SMBO class. + """ + return self._intensify_percentage + + @property + def uses_seeds(self) -> bool: # noqa: D102 + return True + + @property + def uses_budgets(self) -> bool: # noqa: D102 + return False + + @property + def uses_instances(self) -> bool: # noqa: D102 + if self._instances == [None]: + return False + + return True + + def get_target_function_seeds(self) -> list[int]: # noqa: D102 + if self._deterministic: + return [0] + else: + return self._target_function_seeds + + def get_target_function_budgets(self) -> list[float | None]: # noqa: D102 + return [None] + + def get_target_function_instances(self) -> list[str | None]: # noqa: D102 + if self._instances == [None] or None in self._instances: + return [None] + + instances: list[str | None] = [] + for instance in self._instances: + if instance is not None: + instances.append(instance) + + return instances + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + race_against: dict | None = None + if self._race_against is not None: + race_against = self._race_against.get_dictionary() + + meta = super().meta + meta.update( + { + "race_against": race_against, + "intensify_percentage": self.intensify_percentage, + } + ) + + return meta + + def get_next_trial( + self, + challengers: list[Configuration] | None, + incumbent: Configuration, + get_next_configurations: Callable[[], Iterator[Configuration]] | None, + runhistory: RunHistory, + repeat_configs: bool = True, + n_workers: int = 1, + ) -> tuple[TrialInfoIntent, TrialInfo]: + """This procedure is in charge of generating a TrialInfo object to comply with lines 7 (in + case stage is stage == RUN_INCUMBENT) or line 12 (In case of stage == RUN_CHALLENGER). + + A TrialInfo object encapsulates the necessary information for a worker to execute the job, nevertheless, a + challenger is not always available. This could happen because no more configurations are available or the new + configuration to try was already executed. + + To circumvent this, an intent is also returned: + + * intent == RUN: Run the TrialInfo object (Normal Execution). + * intent == SKIP: Skip this iteration. No challenger is available. In particular because challenger is the same + as incumbent + + Parameters + ---------- + challengers : list[Configuration] | None + Promising configurations. + incumbent : Configuration + Incumbent configuration. + get_next_configurations : Callable[[], Iterator[Configuration]] | None, defaults to none + Function that generates next configurations to use for racing. + runhistory : RunHistory + repeat_configs : bool, defaults to true + If false, an evaluated configuration will not be generated again. + n_workers : int, optional, defaults to 1 + The maximum number of workers available. + + Returns + ------- + TrialInfoIntent + Indicator of how to consume the TrialInfo object. + TrialInfo + An object that encapsulates necessary information of the trial. + """ + if n_workers > 1: + raise ValueError("The selected intensifier does not support more than 1 worker.") + + # If this function is called, it means the iteration is + # not complete (we can be starting a new iteration, or re-running a + # challenger due to line 17). We evaluate if a iteration is complete or not + # via _process_results + self._iteration_done = False + + # In case a crash happens, and FirstRunCrashedException prevents a + # failure, revert back to running the incumbent + # Challenger case is by construction ok, as there is no special + # stage for its processing + if self._stage == IntensifierStage.PROCESS_FIRST_CONFIG_RUN: + self._stage = IntensifierStage.RUN_FIRST_CONFIG + elif self._stage == IntensifierStage.PROCESS_INCUMBENT_RUN: + self._stage = IntensifierStage.RUN_INCUMBENT + + # if first ever run, then assume current challenger to be the incumbent + # Because this is the first ever run, we need to sample a new challenger + # This new challenger is also assumed to be the incumbent + if self._stage == IntensifierStage.RUN_FIRST_CONFIG: + if incumbent is None: + logger.info("No incumbent provided in the first run. Sampling a new challenger...") + challenger, self._new_challenger = self.get_next_challenger( + challengers=challengers, + get_next_configurations=get_next_configurations, + ) + incumbent = challenger + else: + inc_trials = runhistory.get_trials(incumbent, only_max_observed_budget=True) + if len(inc_trials) > 0: + logger.debug("Skipping RUN_FIRST_CONFIG stage since incumbent has already been ran.") + self._stage = IntensifierStage.RUN_INCUMBENT + + # LINES 3-7 + if self._stage in [IntensifierStage.RUN_FIRST_CONFIG, IntensifierStage.RUN_INCUMBENT]: + # Line 3 + # A modified version, that not only checks for max_config_calls + # but also makes sure that there are runnable instances, that is, instances has not been exhausted + inc_trials = runhistory.get_trials(incumbent, only_max_observed_budget=True) + + # Line 4 + pending_instances = self._get_pending_instances(incumbent, runhistory) + if pending_instances and len(inc_trials) < self._max_config_calls: + # Lines 5-7 + instance, seed = self._get_next_instance(pending_instances) + + return TrialInfoIntent.RUN, TrialInfo( + config=incumbent, + instance=instance, + seed=seed, + budget=None, + ) + else: + # This point marks the transitions from lines 3-7 to 8-18 + logger.debug("No further instance-seed pairs for incumbent available.") + + self._stage = IntensifierStage.RUN_CHALLENGER + + # Understand who is the active challenger. + if self._stage == IntensifierStage.RUN_BASIS: + # if in RUN_BASIS stage, + # return the basis configuration (i.e., `race_against`) + logger.debug("Race against default configuration after incumbent change.") + challenger = self._race_against + elif self._current_challenger and self._continue_challenger: + # if the current challenger could not be rejected, + # it is run again on more instances + challenger = self._current_challenger + else: + # Get a new challenger if all instance/pairs have been completed. Else return the currently running + # challenger + challenger, self._new_challenger = self.get_next_challenger( + challengers=challengers, + get_next_configurations=get_next_configurations, + ) + + # No new challengers are available for this iteration, move to the next iteration. This can only happen + # when all configurations for this iteration are exhausted and have been run in all proposed instance/pairs. + if challenger is None: + return TrialInfoIntent.SKIP, TrialInfo( + config=None, + instance=None, + seed=None, + budget=None, + ) + + # Skip the iteration if the challenger was previously run + if challenger == incumbent and self._stage == IntensifierStage.RUN_CHALLENGER: + self._challenger_same_as_incumbent = True + logger.debug("Challenger was the same as the current incumbent. Challenger is skipped.") + return TrialInfoIntent.SKIP, TrialInfo( + config=None, + instance=None, + seed=None, + budget=None, + ) + + logger.debug("Intensify on %s.", challenger.get_dictionary()) + if hasattr(challenger, "origin"): + logger.debug("Configuration origin: %s", challenger.origin) + + if self._stage in [IntensifierStage.RUN_CHALLENGER, IntensifierStage.RUN_BASIS]: + if not self._to_run: + self._to_run = self._get_missing_instances( + incumbent=incumbent, challenger=challenger, runhistory=runhistory, N=self._N + ) + + # If there is no more configs to run in this iteration, or no more + # time to do so, change the current stage base on how the current + # challenger performs as compared to the incumbent. This is done + # via _process_racer_results + if len(self._to_run) == 0: + # Nevertheless, if there are no more instances to run, + # we might need to comply with line 17 and keep running the + # same challenger. In this case, if there is not enough information + # to decide if the challenger is better/worst than the incumbent, + # line 17 doubles the number of instances to run. + logger.debug("No further trials for challenger possible.") + self._process_racer_results( + challenger=challenger, + incumbent=incumbent, + runhistory=runhistory, + ) + + # Request SMBO to skip this run. This function will + # be called again, after the _process_racer_results + # has updated the intensifier stage + return TrialInfoIntent.SKIP, TrialInfo( + config=None, + instance=None, + seed=None, + budget=None, + ) + + else: + # Lines 8-11 + incumbent, instance, seed = self._get_next_racer( + challenger=challenger, + incumbent=incumbent, + runhistory=runhistory, + ) + + # Line 12 + return TrialInfoIntent.RUN, TrialInfo( + config=challenger, + instance=instance, + seed=seed, + budget=None, + ) + else: + raise ValueError("No valid stage found.") + + def process_results( + self, + trial_info: TrialInfo, + trial_value: TrialValue, + incumbent: Configuration | None, + runhistory: RunHistory, + time_bound: float, + log_trajectory: bool = True, + ) -> tuple[Configuration, float]: + """The intensifier stage will be updated based on the results/status of a configuration execution. + During intensification, the following can happen: + + * Challenger raced against incumbent. + * Also, during a challenger run, a capped exception can be triggered, where no racer post processing is needed. + * A run on the incumbent for more confidence needs to be processed, IntensifierStage.PROCESS_INCUMBENT_RUN. + * The first run results need to be processed (PROCESS_FIRST_CONFIG_RUN). + + At the end of any run, checks are done to move to a new iteration. + + Parameters + ---------- + trial_info : TrialInfo + trial_value: TrialValue + incumbent : Configuration | None + Best configuration seen so far. + runhistory : RunHistory + time_bound : float + Time [sec] available to perform intensify. + log_trajectory: bool + Whether to log changes of incumbents in the trajectory. + + Returns + ------- + incumbent: Configuration + Current (maybe new) incumbent configuration. + incumbent_costs: float | list[float] + Empirical cost(s) of the incumbent configuration. + """ + if self._stage == IntensifierStage.PROCESS_FIRST_CONFIG_RUN: + if incumbent is None: + logger.info("First run and no incumbent provided. Challenger is assumed to be the incumbent.") + incumbent = trial_info.config + + if self._stage in [ + IntensifierStage.PROCESS_INCUMBENT_RUN, + IntensifierStage.PROCESS_FIRST_CONFIG_RUN, + ]: + self._num_trials += 1 + self._process_incumbent( + incumbent=incumbent, + runhistory=runhistory, + log_trajectory=log_trajectory, + ) + else: + self._num_trials += 1 + self._num_challenger_run += 1 + incumbent = self._process_racer_results( + challenger=trial_info.config, + incumbent=incumbent, + runhistory=runhistory, + log_trajectory=log_trajectory, + ) + + self._elapsed_time += trial_value.time + + # check if 1 intensification run is complete - line 18 + # this is different to regular SMAC as it requires at least successful challenger run, + # which is necessary to work on a fixed grid of configurations. + if ( + self._stage == IntensifierStage.RUN_INCUMBENT + and self._challenger_id >= self._min_challenger + and self._num_challenger_run > 0 + ): + # if self._num_trials > self._run_limit: + # logger.debug("Maximum number of trials for intensification reached.") + # self._next_iteration() + if self._elapsed_time - time_bound >= 0: + logger.debug(f"Wallclock time limit for intensification reached ({self._elapsed_time}/{time_bound})") + self._next_iteration() + + inc_perf = runhistory.get_cost(incumbent) + + return incumbent, inc_perf + + def _get_next_instance( + self, + pending_instances: list[str | None], + ) -> tuple[str | None, int | None]: + """Method to extract the next instance and seed instance in which a incumbent run most be evaluated.""" + # Line 5 - and avoid https://github.com/numpy/numpy/issues/10791 + _idx = self._rng.choice(len(pending_instances)) + next_instance = pending_instances[_idx] + + # Line 6 + if self._deterministic: + next_seed = self.get_target_function_seeds()[0] + else: + self._last_seed_idx += 1 + next_seed = self.get_target_function_seeds()[self._last_seed_idx] + + # Line 7 + logger.debug(f"Add run of incumbent for instance = {next_instance}") + if self._stage == IntensifierStage.RUN_FIRST_CONFIG: + self._stage = IntensifierStage.PROCESS_FIRST_CONFIG_RUN + else: + self._stage = IntensifierStage.PROCESS_INCUMBENT_RUN + + return next_instance, next_seed + + def _get_pending_instances( + self, + incumbent: Configuration, + runhistory: RunHistory, + ) -> list[str | None]: + """Implementation of line 4 of Intensification. This method queries the incumbent trials in the runhistory and + return the pending instances if any is available. + """ + # Line 4 + # Find all instances that have the most trials on the inc + inc_trials = runhistory.get_trials(incumbent, only_max_observed_budget=True) + + # Now we select and count the instances and sort them + inc_inst = [s.instance for s in inc_trials] + inc_inst_counter = list(Counter(inc_inst).items()) + inc_inst_counter.sort(key=lambda x: x[1], reverse=True) + + try: + max_trials = inc_inst_counter[0][1] + except IndexError: + logger.debug("No run for incumbent found.") + max_trials = 0 + + # Those are the instances with run the most + inc_inst = [x[0] for x in inc_inst_counter if x[1] == max_trials] + + # We basically sort the instances by their name + available_insts: list[str | None] = list(set(self._instances) - set(inc_inst)) + if None in available_insts: + assert len(available_insts) == 1 + available_insts = [None] + else: + available_insts = sorted(available_insts) # type: ignore + + # If all instances were used n times, we can pick an instances from the complete set again + if not self._deterministic and not available_insts: + available_insts = self._instances + + return available_insts + + def _process_incumbent( + self, + incumbent: Configuration, + runhistory: RunHistory, + log_trajectory: bool = True, + ) -> None: + """Method to process the results of a challenger that races an incumbent.""" + # output estimated performance of incumbent + inc_trials = runhistory.get_trials(incumbent, only_max_observed_budget=True) + inc_perf = runhistory.get_cost(incumbent) + format_value = format_array(inc_perf) + logger.info(f"Updated estimated cost of incumbent on {len(inc_trials)} trials: {format_value}") + + # if running first configuration, go to next stage after 1st run + if self._stage in [ + IntensifierStage.RUN_FIRST_CONFIG, + IntensifierStage.PROCESS_FIRST_CONFIG_RUN, + ]: + self._stage = IntensifierStage.RUN_INCUMBENT + self._next_iteration() + else: + # Termination condition; after each run, this checks + # whether further trials are necessary due to min_config_calls + if len(inc_trials) >= self._min_config_calls or len(inc_trials) >= self._max_config_calls: + self._stage = IntensifierStage.RUN_CHALLENGER + else: + self._stage = IntensifierStage.RUN_INCUMBENT + + self._compare_configs( + incumbent=incumbent, challenger=incumbent, runhistory=runhistory, log_trajectory=log_trajectory + ) + + def _get_next_racer( + self, + challenger: Configuration, + incumbent: Configuration, + runhistory: RunHistory, + log_trajectory: bool = True, + ) -> tuple[Configuration, str | None, int | None]: + """Method to return the next config setting to aggressively race challenger against + incumbent. Returns either the challenger or incumbent as new incumbent alongside with the + instance and seed. + """ + # By the time this function is called, the run history might + # have shifted. Re-populate the list if necessary + if not self._to_run: + # Lines 10/11 + self._to_run = self._get_missing_instances( + incumbent=incumbent, challenger=challenger, runhistory=runhistory, N=self._N + ) + + # Run challenger on all to run + instance_seed_budget_key = self._to_run.pop() + instance = instance_seed_budget_key.instance + seed = instance_seed_budget_key.seed + + # Line 12 + return incumbent, instance, seed + + def _process_racer_results( + self, + challenger: Configuration, + incumbent: Configuration, + runhistory: RunHistory, + log_trajectory: bool = True, + ) -> Configuration | None: + """Process the result of a racing configuration against the current incumbent. Might propose + a new incumbent. + """ + chal_trials = runhistory.get_trials(challenger, only_max_observed_budget=True) + chal_perf = runhistory.get_cost(challenger) + + # If all have been run, compare challenger performance + if not self._to_run: + new_incumbent = self._compare_configs( + incumbent=incumbent, + challenger=challenger, + runhistory=runhistory, + log_trajectory=log_trajectory, + ) + + # Update intensification stage + if new_incumbent == incumbent: + # move on to the next iteration + self._stage = IntensifierStage.RUN_INCUMBENT + self._continue_challenger = False + logger.debug( + "Estimated cost of challenger on %d trials: %.4f, but worse than incumbent.", + len(chal_trials), + chal_perf, + ) + elif new_incumbent == challenger: + # New incumbent found + incumbent = challenger + self._continue_challenger = False + # compare against basis configuration if provided, else go to next iteration + if self._race_against and self._race_against != challenger: + self._stage = IntensifierStage.RUN_BASIS + else: + self._stage = IntensifierStage.RUN_INCUMBENT + logger.debug( + "Estimated cost of challenger on %d trials (%.4f) becomes new incumbent.", + len(chal_trials), + chal_perf, + ) + + else: # Line 17 + # Challenger is not worse, continue + self._N = 2 * self._N + self._continue_challenger = True + logger.debug( + "Estimated cost of challenger on %d trials (%.4f). Adding %d trials to the queue.", + len(chal_trials), + chal_perf, + self._N / 2, + ) + else: + logger.debug( + "Estimated cost of challenger on %d trials (%.4f). Still %d trials to go (continue racing).", + len(chal_trials), + chal_perf, + len(self._to_run), + ) + + return incumbent + + def _get_missing_instances( + self, + challenger: Configuration, + incumbent: Configuration, + N: int, + runhistory: RunHistory, + ) -> list[InstanceSeedBudgetKey]: + """Returns the minimum list of pairs to run the challenger on before + comparing it with the incumbent. + """ + # Get next instances left for the challenger + # Line 8 + inc_inst_seeds = set(runhistory.get_trials(incumbent, only_max_observed_budget=True)) + chall_inst_seeds = set(runhistory.get_trials(challenger, only_max_observed_budget=True)) + + # Line 10 + missing_trials = list(sorted(inc_inst_seeds - chall_inst_seeds)) + + # Line 11 + self._rng.shuffle(missing_trials) # type: ignore + + if N < 0: + raise ValueError("Argument N must not be smaller than zero, but is %s." % str(N)) + + to_run = missing_trials[: min(N, len(missing_trials))] + missing_trials = missing_trials[min(N, len(missing_trials)) :] + + return to_run + + def get_next_challenger( + self, + challengers: list[Configuration] | None, + get_next_configurations: Callable[[], Iterator[Configuration]] | None, + ) -> tuple[Configuration | None, bool]: + """This function returns the next challenger, that should be exercised though lines 8-17. + + It does so by populating `_configs_to_run`, which is a pool of configuration from which the racer will sample. + Each configuration within `_configs_to_run`, will be intensified on different instances/seed registered in + `self._to_run` as stated in line 11. + + A brand new configuration should only be sampled, after all `self._to_run` instance seed pairs are exhausted. + + This method triggers a call to `_next_iteration` if there are no more configurations to run, for the current + intensification loop. This marks the transition to Line 2, where a new configuration to intensify will be drawn + from epm/initial challengers. + + Parameters + ---------- + challengers : list[Configuration] | None, defaults to None + Promising configurations. + get_next_configurations : Callable[[], Iterator[Configuration]] | None + Function that generates next configurations to use for racing. + + Returns + ------- + configuration : Configuration | None + The configuration of the selected challenger. + new_challenger : bool + If the configuration is a new challenger. + """ + # Select new configuration when entering 'race challenger' stage or for the first run + if not self._current_challenger or (self._stage == IntensifierStage.RUN_CHALLENGER and not self._to_run): + # This is a new intensification run, get the next list of configurations to run + if self._update_configs_to_run: + configs_to_run = self._generate_challengers( + challengers=challengers, get_next_configurations=get_next_configurations + ) + self._configs_to_run = cast(Iterator[Optional[Configuration]], configs_to_run) + self._update_configs_to_run = False + + # Pick next configuration from the generator + try: + challenger = next(self._configs_to_run) + except StopIteration: + # Out of challengers for the current iteration, start next incumbent iteration + self._next_iteration() + return None, False + + if challenger: + # Reset instance index for the new challenger + self._challenger_id += 1 + self._current_challenger = challenger + self._N = max(1, self._min_config_calls) + self._to_run = [] + + return challenger, True + + # Return currently running challenger + return self._current_challenger, False + + def _generate_challengers( + self, + challengers: list[Configuration] | None, + get_next_configurations: Callable[[], Iterator[Configuration]] | None, + ) -> Iterator[Optional[Configuration]]: + """Retuns a sequence of challengers to use in intensification. If challengers are not + provided, then optimizer will be used to generate the challenger list. + """ + chall_gen: Iterator[Optional[Configuration]] + if challengers: + # Iterate over challengers provided + logger.debug("Using provided challengers...") + chall_gen = iter(challengers) + elif get_next_configurations: + # Generating challengers on-the-fly if optimizer is given + logger.debug("Generating new challenger from optimizer...") + chall_gen = get_next_configurations() + else: + raise ValueError("No configuration function provided. Can not generate challenger!") + + return chall_gen + + def _next_iteration(self) -> None: + """Updates tracking variables at the end of an intensification run.""" + assert self._stats + + # Track iterations + self._n_iters += 1 + self._iteration_done = True + self._configs_to_run = iter([]) + self._update_configs_to_run = True + + # Reset for a new iteration + self._num_trials = 0 + self._num_challenger_run = 0 + self._challenger_id = 0 + self._elapsed_time = 0.0 + + self._stats.update_average_configs_per_intensify(n_configs=self._challenger_id) diff --git a/smac/intensifier/stages.py b/smac/intensifier/stages.py new file mode 100644 index 000000000..4b0034f90 --- /dev/null +++ b/smac/intensifier/stages.py @@ -0,0 +1,16 @@ +from enum import Enum + + +class IntensifierStage(Enum): + """Class to define different stages of intensifier.""" + + RUN_FIRST_CONFIG = 0 # to replicate the old initial design + RUN_INCUMBENT = 1 # Lines 3-7 + RUN_CHALLENGER = 2 # Lines 8-17 + RUN_BASIS = 3 + + # helpers to determine what type of run to process + # A challenger is assumed to be processed if the stage + # is not from first_config or incumbent + PROCESS_FIRST_CONFIG_RUN = 4 + PROCESS_INCUMBENT_RUN = 5 diff --git a/smac/intensifier/successive_halving.py b/smac/intensifier/successive_halving.py new file mode 100644 index 000000000..8a52c2e0c --- /dev/null +++ b/smac/intensifier/successive_halving.py @@ -0,0 +1,316 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np + +from smac.constants import MAXINT +from smac.intensifier.abstract_intensifier import AbstractIntensifier +from smac.intensifier.abstract_parallel_intensifier import AbstractParallelIntensifier +from smac.scenario import Scenario +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class SuccessiveHalving(AbstractParallelIntensifier): + """Races multiple challengers against an incumbent using Successive Halving method. + + Implementation following the description in + "BOHB: Robust and Efficient Hyperparameter Optimization at Scale" (Falkner et al. 2018) + Supplementary reference: http://proceedings.mlr.press/v80/falkner18a/falkner18a-supp.pdf + + Successive Halving intensifier (and Hyperband) can operate on two kinds of budgets: + + 1. Instances as budget: + When multiple instances are provided or when run objective is "runtime", this is the criterion used as budget + for successive halving iterations i.e., the budget determines how many instances the challengers are evaluated + on at a time. Top challengers for the next iteration are selected based on the combined performance across + all instances used. If `min_budget` and `max_budget` are not provided, then they are set to 1 and total number + of available instances respectively by default. + 2. Real-valued budget: + This is used when there is only one instance provided and when run objective is "quality", + i.e., budget is a positive, real-valued number that can be passed to the target function as an argument. + It can be used to control anything by the target function, Eg: number of epochs for training a neural network. + The parameters `min_budget` and `max_budget` are required for this type of budget. + + This class instantiates ``SuccessiveHalvingWorker`` objects on a need basis, that is, to + prevent workers from being idle. The actual logic that implements the Successive halving method + lies in the worker class. + + Parameters + ---------- + scenario : Scenario + instance_seed_pairs : list[tuple[str | None, int]] | None, defaults to None + This argument is used by Hyperband. + instance_order : str | None, defaults to `shuffle_once` + How to order the instances. Can be set to: + * None: Use as is given by the user. + * shuffle_once: Shuffle once and use across all Successive Halving run . + * shuffle: Shuffle before every Successive Halving run + incumbent_selection : str, defaults to `highest_executed_budget` + How to select the incumbent in Successive Halving. Only active for (real-valued) budgets. Can be set to: + * highest_executed_budget: Incumbent is the best in the highest budget run so far. + * highest_budget: Incumbent is selected only based on the highest budget. + * any_budget: Incumbent is the best on any budget i.e., best performance regardless of budget. + n_initial_challengers : int | None, defaults to None + Number of challengers to consider for the initial budget. If not specified, it is calculated internally. + min_challenger : int, defaults to 1 + Minimal number of challengers to be considered (even if time_bound is exhausted earlier). + eta : float, defaults to 3 + The "halving" factor after each iteration in a Successive Halving run. + seed : int | None, defaults to None + This seed is not the seed used for target function evaluation but rather for sampling next candidates. + n_seeds : int | None, defaults to None + The number of seeds to use if the target function is non-deterministic. + """ + + def __init__( + self, + scenario: Scenario, + instance_seed_pairs: list[tuple[str | None, int]] | None = None, + instance_order: str | None = "shuffle_once", + incumbent_selection: str = "highest_executed_budget", + n_initial_challengers: int | None = None, + min_challenger: int = 1, + eta: float = 3, + seed: int | None = None, + n_seeds: int | None = None, + ) -> None: + + super().__init__( + scenario=scenario, + min_challenger=min_challenger, + seed=seed, + ) + + self._instance_seed_pairs: list[tuple[str | None, int]] + self._instance_order = instance_order + self._incumbent_selection = incumbent_selection + self._n_initial_challengers = n_initial_challengers + self._n_seeds = n_seeds if n_seeds else 1 + self._eta = eta + self._min_budget: float + self._max_budget: float + self._instance_as_budget = False + + available_incumbent_selections = ["highest_executed_budget", "highest_budget", "any_budget"] + if incumbent_selection not in available_incumbent_selections: + raise ValueError(f"The incumbent selection must be one of {available_incumbent_selections}.") + + if self._min_challenger > 1: + raise ValueError("Successive Halving can not handle argument `min_challenger` > 1.") + + if self._instances[0] is None and self._n_seeds > 1: + raise NotImplementedError("The case multiple seeds without using instances can not be handled yet!") + + if eta <= 1: + raise ValueError("The parameter `eta` must be greater than 1.") + + # We declare the instance seed pairs now + if instance_seed_pairs is None: + # Set seed(s) for all SH runs. Currently user gives the number of seeds to consider. + if self._deterministic: + seeds = [0] + logger.info("Only one seed is used as `deterministic` is set to true.") + else: + seeds = [int(s) for s in self._rng.randint(low=0, high=MAXINT, size=self._n_seeds)] + if self._n_seeds == 1: + logger.warning( + "The target function is specified to be non-deterministic, " + "but number of seeds to evaluate are set to 1. " + "Consider increasing `n_seeds` from the intensifier." + ) + + # Storing instances & seeds as tuples + if self._instances is not None: + self._instance_seed_pairs = [(i, s) for s in seeds for i in self._instances] + else: + self._instance_seed_pairs = [(None, s) for s in seeds] + + # Determine instance-seed pair order + if self._instance_order == "shuffle_once": + # Randomize once + self._rng.shuffle(self._instance_seed_pairs) # type: ignore + + self._target_function_seeds = seeds + else: + self._instance_seed_pairs = instance_seed_pairs + + # Get seeds and check whether instances are involved + self._target_function_seeds = [] + self._instance_as_budget = False + for instance, seed in self._instance_seed_pairs: + if seed not in self._target_function_seeds: + self._target_function_seeds += [seed] + + if instance is not None: + self._instance_as_budget = True + + # Budgets + min_budget = scenario.min_budget + max_budget = scenario.max_budget + + if max_budget is not None and min_budget is not None and max_budget < min_budget: + raise ValueError("Max budget has to be larger than min budget.") + + # If only 1 instance was provided + if not self._instance_as_budget: + # budget with algorithm_walltime_limit + if min_budget is None or max_budget is None: + raise ValueError( + "Successive Halving with real-valued budget (i.e., only 1 instance) " + "requires parameters `min_budget` and `max_budget` for intensification!" + ) + + assert len(self._instance_seed_pairs) == 1 + self._min_budget = min_budget + self._max_budget = max_budget + else: + if isinstance(min_budget, float) or isinstance(max_budget, float): + raise ValueError("Successive Halving requires integer budgets when using instances.") + + # Budget with instances + self._min_budget = min_budget if min_budget else 1 + self._max_budget = max_budget if max_budget else len(self._instance_seed_pairs) + + if self._max_budget > len(self._instance_seed_pairs): + raise ValueError( + f"Max budget ({self._max_budget}) can not be greater than the number of " + f"instance-seed pairs ({len(self._instance_seed_pairs)})." + ) + + if self._max_budget < len(self._instance_seed_pairs): + logger.warning( + "Max budget (%d) does not include all instance-seed pairs (%d)." + % (self._max_budget, len(self._instance_seed_pairs)) + ) + + if self._min_budget <= 0: + raise ValueError("Minimum budget must be greater than 0.") + + budget_type = "INSTANCES" if self._instance_as_budget else "BUDGETS" + logger.info( + f"Using successive halving with budget type {budget_type}, min budget {self._min_budget}, " + f"max budget {self._max_budget} and eta {self._eta}." + ) + + # Pre-computing stuff for SH + self._max_sh_iterations = int(np.floor(np.log(self._max_budget / self._min_budget) / np.log(self._eta))) + + # Initial number of challengers to sample + if n_initial_challengers is None: + self._n_initial_challengers = int(self._eta**self._max_sh_iterations) + + # Challengers can be repeated only if optimizing across multiple seeds or changing instance orders every run + # (this does not include having multiple instances) + if self._n_seeds > 1 or self._instance_order == "shuffle": + self._repeat_configs = True + else: + self._repeat_configs = False + + if self._instance_as_budget: + logger.info("The argument `incumbent_selection` is ignored because instances are used as budget type.") + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + return { + "name": self.__class__.__name__, + "instance_seed_pairs": self._instance_seed_pairs, + "instance_order": self._instance_order, + "incumbent_selection": self._incumbent_selection, + "n_initial_challengers": self._n_initial_challengers, + "min_challenger": self._min_challenger, + "eta": self._eta, + "seed": self._seed, + "n_seeds": self._n_seeds, + } + + @property + def uses_budgets(self) -> bool: # noqa: D102 + return not self._instance_as_budget + + @property + def uses_instances(self) -> bool: # noqa: D102 + return self._instance_as_budget + + def get_target_function_seeds(self) -> list[int]: # noqa: D102 + return self._target_function_seeds + + def get_target_function_budgets(self) -> list[float | None]: # noqa: D102 + # If we use instance as budget, then we always use None for the budget + if self._instance_as_budget: + return [None] + + _, _, budgets = self.calculate_budgets(self._min_budget, self._max_budget) + return budgets # type: ignore + + def get_target_function_instances(self) -> list[str | None]: # noqa: D102 + if self._instances == [None] or None in self._instances: + return [None] + + instances: list[str | None] = [] + for instance in self._instances: + if instance is not None: + instances.append(instance) + + return instances + + def calculate_budgets(self, min_budget: float, max_budget: float) -> tuple[int, int, list[float]]: + """Calculates the budgets for successive halving. + + Parameters + ---------- + min_budget : float + The minimal budget. + max_budget : float + The maximal budget. + + Returns + ------- + max_iterations : int + How many iterations will be performed. + n_initial_challengers : int + How many challengers will be sampled in the first iteration. + budgets : list[float] + The budgets for each iteration. + """ + max_iterations = int(np.floor(np.log(max_budget / min_budget) / np.log(self._eta))) + n_initial_challengers = int(self._eta**max_iterations) + + # Budgets to consider in each stage + linspace = -np.linspace(self._max_sh_iterations, 0, self._max_sh_iterations + 1) + _budgets = self._max_budget * np.power(self._eta, linspace) + budgets = _budgets.tolist() + + return max_iterations, n_initial_challengers, budgets + + def _get_intensifier_ranking(self, intensifier: AbstractIntensifier) -> tuple[int, int]: + from smac.intensifier.successive_halving_worker import SuccessiveHalvingWorker + + assert isinstance(intensifier, SuccessiveHalvingWorker) + + # Each row of this matrix is id, stage, configs+instances for stage + # We use sh.run_tracker as a cheap way to know how advanced the run is + # in case of stage ties among successive halvers. sh.run_tracker is + # also emptied each iteration + return intensifier.stage, len(intensifier._run_tracker) + + def _add_new_instance(self, n_workers: int) -> bool: + from smac.intensifier.successive_halving_worker import SuccessiveHalvingWorker + + if len(self._intensifier_instances) >= n_workers: + return False + + sh = SuccessiveHalvingWorker( + successive_halving=self, + identifier=len(self._intensifier_instances), + ) + sh._stats = self._stats + self._intensifier_instances[len(self._intensifier_instances)] = sh + + return True diff --git a/smac/intensifier/successive_halving_worker.py b/smac/intensifier/successive_halving_worker.py new file mode 100644 index 000000000..8cf3718cd --- /dev/null +++ b/smac/intensifier/successive_halving_worker.py @@ -0,0 +1,820 @@ +from __future__ import annotations + +from typing import Callable, Iterator + +import numpy as np +from ConfigSpace import Configuration + +from smac.intensifier.abstract_intensifier import AbstractIntensifier +from smac.intensifier.successive_halving import SuccessiveHalving +from smac.runhistory import StatusType, TrialInfo, TrialInfoIntent, TrialValue +from smac.runhistory.runhistory import RunHistory +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class SuccessiveHalvingWorker(AbstractIntensifier): + """This is the worker class for Successive Halving. + + Warning + ------- + Do not use this class as stand-alone. + + Parameters + ---------- + successive_halving : SuccessiveHalving + The controller of the instance. + identifier : int, defaults to 0 + Adds a numerical identifier on the instance. Used for debug and tagging logger messages properly. + _all_budgets : np.ndarray | None, defaults to None + Used internally when HB uses SH as a sub-routing. + _n_configs_in_stage : np.ndarray | None, defaults to None + Used internally when HB uses SH as a sub-routing. + _min_budget : float | None, defaults to None + Overwrites the budget from the controller instance if it is not none. + _max_budget : float | None, defaults to None + Overwrites the budget from the controller instance if it is not none. + """ + + def __init__( + self, + successive_halving: SuccessiveHalving, + identifier: int = 0, + _all_budgets: list[float] | None = None, + _n_configs_in_stage: np.ndarray | None = None, + _min_budget: float | None = None, + _max_budget: float | None = None, + ) -> None: + super().__init__( + scenario=successive_halving._scenario, + min_challenger=successive_halving._min_challenger, + seed=successive_halving._seed, + ) + + self._successive_halving = successive_halving + self._stats = successive_halving._stats + self._first_run = True + self._identifier = identifier + self._logger = get_logger(f"{__name__}.{identifier}") + + self._min_budget = _min_budget if _min_budget is not None else successive_halving._min_budget + self._max_budget = _max_budget if _max_budget is not None else successive_halving._max_budget + + if _all_budgets is not None and _n_configs_in_stage is not None: + # Assert we use the given numbers to avoid rounding issues, see #701 + self._all_budgets = _all_budgets + self._n_configs_in_stage = _n_configs_in_stage + else: + eta = successive_halving._eta + + # Max sh iterations and n initial challengers are depending on min and max budget + # Since min and max budget can be changed by the user, we need to update + # Pre-computing stuff for SH + ( + self._max_sh_iterations, + self._n_initial_challengers, + self._all_budgets, + ) = self._successive_halving.calculate_budgets(self._min_budget, self._max_budget) + + # Number of challengers to consider in each stage + linspace = -np.linspace(0, self._max_sh_iterations, self._max_sh_iterations + 1) + n_configs_in_stage = self._n_initial_challengers * np.power(eta, linspace) + self._n_configs_in_stage = np.array(np.round(n_configs_in_stage), dtype=int).tolist() + + self._sh_iters = 0 + self._stage = 0 + self._configs_to_run: list[Configuration] = [] + + # Current instance index tracks two things. Configurations that are to be launched, + # That is config A needs to run in 3 instances/seed pairs, then current_instance_indices should + # track this. But then, if a new configuration is added in the case of parallelism + # a new separate current_instance_indices needs to be started. + # The indices normally are of type int, but np.inf is used to indicate to not further + # launch instances for this configuration, hence the type is Union[int, float]. + self._current_instance_indices: dict[Configuration, int | float] = {} + self._running_challenger = None + self._success_challengers: set[Configuration] = set() + self._do_not_advance_challengers: set[Configuration] = set() + self._failed_challengers: set[Configuration] = set() + self._failed_challenger_offset = 0 + self._new_challenger = False + + # Track which configs were launched. This will allow to have an extra check to make sure + # that a successive halver deals only with the configs it launched, + # but also allows querying the status of the configs via the run history. + # In other works, the run history is agnostic of the origin of the configurations, + # that is, which successive halving instance created it. The RunInfo object + # is aware of this information, and for parallel execution, the routing of + # finish results is expected to use this information. + # Nevertheless, the common object among SMBO/intensifier, which is the + # run history, does not have this information and so we track locally. That way, + # when we access the complete list of configs from the run history, we filter + # the ones launched by the current succesive halver using self._run_tracker + self._run_tracker: dict[tuple[Configuration, str | None, int | None, float | None], bool] = {} + + # Those general arguments are initialized in the parent class + # self._num_trials = 0 + # self._challenger_id = 0 + # self._iteration_done = False + # self._target_function_time = 0 + + @property + def stage(self) -> int: + """The current stage of the worker.""" + return self._stage + + @property + def uses_seeds(self) -> bool: # noqa: D102 + return self._successive_halving.uses_seeds + + @property + def uses_budgets(self) -> bool: # noqa: D102 + return self._successive_halving.uses_budgets + + @property + def uses_instances(self) -> bool: # noqa: D102 + return self._successive_halving.uses_instances + + def get_target_function_seeds(self) -> list[int]: # noqa: D102 + return self._successive_halving.get_target_function_seeds() + + def get_target_function_budgets(self) -> list[float | None]: # noqa: D102 + return self._successive_halving.get_target_function_budgets() + + def get_target_function_instances(self) -> list[str | None]: # noqa: D102 + return self._successive_halving.get_target_function_instances() + + def process_results( + self, + trial_info: TrialInfo, + trial_value: TrialValue, + incumbent: Configuration | None, + runhistory: RunHistory, + time_bound: float, + log_trajectory: bool = True, + ) -> tuple[Configuration, float]: # noqa: D102 + # Mark the fact that we processed this configuration + self._run_tracker[(trial_info.config, trial_info.instance, trial_info.seed, trial_info.budget)] = True + + # If The incumbent is None and it is the first run, we use the challenger + if not incumbent and self._first_run: + # We already displayed this message before + # self._logger.info("First run and no incumbent provided. Challenger is assumed to be the incumbent.") + incumbent = trial_info.config + self._first_run = False + + # In a serial run, if we have to CAP a run, then we stop launching + # more configurations for this run. + # In the context of parallelism, we launch the instances proactively + # The fact that self._current_instance_indices[trial_info.config] is np.inf means + # that no more instances will be launched for the current config, so we + # can add a check to make sure that if we are capping, this makes sense + # for the active challenger + # if result.status == StatusType.CAPPED and trial_info.config == self._running_challenger: + # self._current_instance_indices[trial_info.config] = np.inf + # else: + self._target_function_time += trial_value.time + self._num_trials += 1 + + # 0: Before moving to a new stage, we have to complete M x N tasks, where M is the + # total number of configurations evaluated in N instance/seed pairs. + # The last active configuration is stored in self.running challengers is the M + # configuration, and to get to this point, we have already submitted tasks + # for (M - 1) configurations on N instances seed-pairs. The status of such + # (M - 1) * N tasks is tracked in self._run_tracker, that has a value of False + # if not processed and true if such task has been processed. + # This stage is complete only if all tasks have been launched and all of the + # already launched tasks are processed. + + # 1: We first query if we have launched everything already (All M * N tasks) + all_config_inst_seed_launched = self._all_config_instance_seed_pairs_launched( + runhistory=runhistory, + activate_configuration_being_intensified=self._running_challenger, + ) + + # 2: Then we get the already submitted tasks (that is, proposed by get_next_trials), + # that have not yet been processed process_results + all_config_inst_seeds_processed = len([v for v in self._run_tracker.values() if not v]) <= 0 + + # 3: Then the total number of remaining task before we can conclude this stage + # is calculated by taking into account point 2 and 3 above + is_stage_done = all_config_inst_seed_launched and all_config_inst_seeds_processed + + # ADDED IN SMAC 2.0: Makes sure we can not call `tell` method without `ask` first + if trial_info.config not in self._current_instance_indices: + raise RuntimeError("Successive Halving does not support calling `tell` method without calling `ask` first.") + + # adding challengers to the list of evaluated challengers + # - Stop: CAPPED/CRASHED/TIMEOUT/MEMOUT/DONOTADVANCE (!= SUCCESS) + # - Advance to next stage: SUCCESS + # curr_challengers is a set, so "at least 1" success can be counted by set addition (no duplicates) + # If a configuration is successful, it is added to curr_challengers. + # if it fails it is added to fail_challengers. + if np.isfinite(self._current_instance_indices[trial_info.config]) and trial_value.status == StatusType.SUCCESS: + self._success_challengers.add(trial_info.config) # successful configs + elif ( + np.isfinite(self._current_instance_indices[trial_info.config]) + and trial_value.status == StatusType.DONOTADVANCE + ): + self._do_not_advance_challengers.add(trial_info.config) + else: + self._failed_challengers.add(trial_info.config) # capped/crashed/do not advance configs + + # We need to update the incumbent if this config we are processing + # completes all scheduled instance-seed pairs. + # Here, a config/seed/instance is going to be processed for the first time + # (it has been previously scheduled by get_next_trial and marked False, indicating + # that it has not been processed yet. Entering process_results() this config/seed/instance + # is marked as TRUE as an indication that it has finished and should be processed) + # so if all configurations runs are marked as TRUE it means that this new config + # was the missing piece to have everything needed to compare against the incumbent + update_incumbent = all([v for k, v in self._run_tracker.items() if k[0] == trial_info.config]) + + # get incumbent if all instances have been evaluated + if is_stage_done or update_incumbent: + incumbent = self._compare_configs( + challenger=trial_info.config, + incumbent=incumbent, + runhistory=runhistory, + log_trajectory=log_trajectory, + ) + + if is_stage_done: + self._logger.info( + "Finished Successive Halving iteration-step %d-%d with " + "budget [%.2f / %d] and %d evaluated challenger(s)." + % ( + self._sh_iters + 1, + self._stage + 1, + self._all_budgets[self._stage], + self._max_budget, + self._n_configs_in_stage[self._stage], + ) + ) + + self._update_stage(runhistory=runhistory) + + # Get incumbent cost + inc_perf = runhistory.get_cost(incumbent) + + return incumbent, inc_perf + + def get_next_trial( + self, + challengers: list[Configuration] | None, + incumbent: Configuration, + get_next_configurations: Callable[[], Iterator[Configuration]] | None, + runhistory: RunHistory, + repeat_configs: bool = True, + n_workers: int = 1, + ) -> tuple[TrialInfoIntent, TrialInfo]: + """Selects which challenger to use based on the iteration stage and set the iteration + parameters. First iteration will choose configurations from the function ``get_next_configurations`` or input + challengers, while the later iterations pick top configurations from the previously selected + challengers in that iteration. + + Parameters + ---------- + challengers : list[Configuration] | None + Promising configurations. + incumbent : Configuration + Incumbent configuration. + get_next_configurations : Callable[[], Iterator[Configuration]] | None, defaults to none + Function that generates next configurations to use for racing. + runhistory : RunHistory + repeat_configs : bool, defaults to true + If false, an evaluated configuration will not be generated again. + n_workers : int, optional, defaults to 1 + The maximum number of workers available. + + Returns + ------- + TrialInfoIntent + Indicator of how to consume the TrialInfo object. + TrialInfo + An object that encapsulates necessary information of the trial. + """ + if n_workers > 1: + self._logger.warning( + "Consider using ParallelSuccesiveHalving instead of " + "SuccesiveHalving. The later will halt on each stage " + "transition until all configs for the current stage are completed." + ) + + sh = self._successive_halving + + # In the case of multiprocessing, we have runs in Running stage, which have not + # been processed via process_results(). get_next_trial() is called agnostically by + # smbo. To prevent launching more configs, than the ones needed, we query if + # there is room for more configurations, else we wait for process_results() + # to trigger a new stage + if self._all_config_instance_seed_pairs_launched(runhistory, self._running_challenger): + return TrialInfoIntent.WAIT, TrialInfo( + config=None, + instance=None, + seed=None, + budget=None, + source=self._identifier, + ) + + # Sampling from next challenger marks the beginning of a new iteration + self._iteration_done = False + + current_budget = self._all_budgets[self._stage] + + # If all instances have been executed, then reset and move on to next config + if sh._instance_as_budget: + previous_budget = int(self._all_budgets[self._stage - 1]) if self._stage > 0 else 0 + n_insts = int(current_budget) - previous_budget + else: + n_insts = len(sh._instance_seed_pairs) + + # The instances remaining tell us, per configuration, how many instances we + # have suggested to SMBO + n_insts_remaining = n_insts + if self._running_challenger is not None: + n_insts_remaining = n_insts - self._current_instance_indices[self._running_challenger] + + # If there are instances pending, finish running configuration + if self._running_challenger and n_insts_remaining > 0: + challenger = self._running_challenger + new_challenger = False + else: + # Select next configuration + if self._stage == 0: + # first stage, so sample from configurations/chooser provided + challenger = self._next_challenger( + challengers=challengers, + get_next_configurations=get_next_configurations, + runhistory=runhistory, + repeat_configs=repeat_configs, + ) + if challenger is None: + # If no challenger was sampled from the EPM or + # initial challengers, it might mean that the EPM + # is proposing a configuration that is currently running. + # There is a filtering on the above _next_challenger to return + # None if the proposed config us already in the run history + # To get a new config, we wait for more data + return TrialInfoIntent.WAIT, TrialInfo( + config=None, + instance=None, + seed=None, + budget=None, + source=self._identifier, + ) + + new_challenger = True + else: + # Sample top configs from previously sampled configurations + try: + challenger = self._configs_to_run.pop(0) + new_challenger = False + except IndexError: + # self._configs_to_run is populated via update_stage, + # which is triggered after the completion of a run + # If by there are no more configs to run (which is the case + # if we run into a IndexError), + return TrialInfoIntent.SKIP, TrialInfo( + config=None, + instance=None, + seed=None, + budget=None, + source=self._identifier, + ) + + if challenger: + # We see a challenger for the first time, so no + # instance has been launched + self._current_instance_indices[challenger] = 0 + self._challenger_id += 1 + self._running_challenger = challenger + + if (incumbent is None or incumbent == challenger) and self._first_run: + self._logger.info("First run and no incumbent provided. Challenger is assumed to be the incumbent.") + incumbent = challenger + + # Selecting instance-seed subset for this budget, depending on the kind of budget + if sh._instance_as_budget: + previous_budget = int(self._all_budgets[self._stage - 1]) if self._stage > 0 else 0 + current_instances = sh._instance_seed_pairs[int(previous_budget) : int(current_budget)] + else: + current_instances = sh._instance_seed_pairs + + self._logger.debug(f"Running challenger: {challenger}") + + # run the next instance-seed pair for the given configuration + instance, seed = current_instances[self._current_instance_indices[challenger]] # type: ignore[index] + # At this point self._current_instance_indices[challenger] will still be an integer and might + # be marked LATER with np.inf, so ignore mypy error. + + # For testing purposes, this attribute highlights whether a + # new challenger is proposed or not. Not required from a functional + # perspective + self._new_challenger = new_challenger + + budget = None if sh._instance_as_budget else current_budget + self._run_tracker[(challenger, instance, seed, budget)] = False + + # self._current_instance_indices Tell us our current instance to be run. The upcoming return + # will launch a challenger on a given instance/seed/pair. The next time this function + # is called, we will like to run self._current_instance_indices + 1 for this configuration + self._current_instance_indices[challenger] += 1 + + return TrialInfoIntent.RUN, TrialInfo( + config=challenger, + instance=instance, + seed=seed, + budget=budget, + source=self._identifier, + ) + + def _update_stage(self, runhistory: RunHistory) -> None: + """Updates tracking information for a new stage/iteration and update statistics. This method + is called to initialize stage variables and after all configurations of a Successive Halving + stage are completed. + """ + self._stage += 1 + + # Only uncapped challengers are considered valid for the next iteration + valid_challengers = list( + (self._success_challengers | self._do_not_advance_challengers) - self._failed_challengers + ) + + if self._stage < len(self._all_budgets) and len(valid_challengers) > 0: + # If this is the next stage in same iteration, + # use top 'k' from the evaluated configurations for next iteration + + # Determine 'k' for the next iteration - at least 1 + next_n_chal = int(max(1, self._n_configs_in_stage[self._stage])) + + # Selecting the top 'k' challengers for the next iteration + configs_to_run = self._top_k(configs=valid_challengers, runhistory=runhistory, k=next_n_chal) + self._configs_to_run = [ + config for config in configs_to_run if config not in self._do_not_advance_challengers + ] + + # If some runs were capped, top_k returns less than the required configurations + # to handle that, we keep track of how many configurations are missing + # (since they are technically failed here too) + missing_challengers = int(self._n_configs_in_stage[self._stage]) - len(self._configs_to_run) + if missing_challengers > 0: + self._failed_challenger_offset = missing_challengers + else: + self._failed_challenger_offset = 0 + + if next_n_chal == missing_challengers: + next_stage = True + self._logger.info( + "Successive Halving iteration-step: %d-%d with " + "budget [%.2f / %d] - expected %d new challenger(s), but " + "no configurations propagated to the next budget.", + self._sh_iters + 1, + self._stage + 1, + self._all_budgets[self._stage], + self._max_budget, + self._n_configs_in_stage[self._stage], + ) + else: + next_stage = False + else: + next_stage = True + + if next_stage: + # Update stats for the prev iteration + assert self._stats + self._stats.update_average_configs_per_intensify(n_configs=self._challenger_id) + + # Reset stats for the new iteration + self._sh_iters += 1 + self._stage = 0 + self._configs_to_run = [] + self._target_function_time = 0 + self._challenger_id = 0 + self._num_trials = 0 + self._iteration_done = True + + self._failed_challenger_offset = 0 + self._run_tracker = {} + + # Randomize instance-seed pairs per successive halving run, if user specifies + if self._successive_halving._instance_order == "shuffle": + self._rng.shuffle(self._instance_seed_pairs) # type: ignore + + # To track configurations for the next stage + self._running_challenger = None + self._success_challengers = set() + self._do_not_advance_challengers = set() + self._failed_challengers = set() + self._current_instance_indices = {} + + def _compare_configs( + self, + incumbent: Configuration, + challenger: Configuration, + runhistory: RunHistory, + log_trajectory: bool = True, + ) -> Configuration | None: + """Compares the challenger with the current incumbent and returns the best configuration based + on the given incumbent selection design. + + Returns + ------- + configuration : Configuration | None + The better configuration. + """ + assert self._stats + + if self._successive_halving._instance_as_budget: + new_incumbent = super()._compare_configs(incumbent, challenger, runhistory, log_trajectory) + + # If compare config returned none, then it is undecided. So return old incumbent. + return incumbent if new_incumbent is None else new_incumbent + + # For real-valued budgets, compare configs based on the incumbent selection design + current_budget = self._all_budgets[self._stage] + + # incumbent selection: best on any budget + if self._successive_halving._incumbent_selection == "any_budget": + new_incumbent = self._compare_configs_across_budgets( + challenger=challenger, + incumbent=incumbent, + runhistory=runhistory, + log_trajectory=log_trajectory, + ) + return new_incumbent + + # get runs for both configurations + inc_runs = runhistory.get_trials(incumbent, only_max_observed_budget=True) + chall_runs = runhistory.get_trials(challenger, only_max_observed_budget=True) + + if len(inc_runs) > 1: + raise ValueError( + "Number of incumbent runs on budget %f must not exceed 1, but is %d", + inc_runs[0].budget, + len(inc_runs), + ) + + if len(chall_runs) > 1: + raise ValueError( + "Number of challenger runs on budget %f must not exceed 1, but is %d", + chall_runs[0].budget, + len(chall_runs), + ) + + inc_run = inc_runs[0] + chall_run = chall_runs[0] + + if inc_run.budget is None or chall_run.budget is None: + raise RuntimeError("Since budgets are not used for instance optimization, this should not happen.") + + # Incumbent selection: highest budget only + if self._successive_halving._incumbent_selection == "highest_budget": + assert chall_run.budget is not None + if chall_run.budget < self._max_budget: + self._logger.debug( + "Challenger (budget=%.4f) has not been evaluated on the highest budget %.4f yet.", + chall_run.budget, + self._max_budget, + ) + return incumbent + + # Incumbent selection: highest budget run so far + if inc_run.budget > chall_run.budget: + self._logger.debug( + "Incumbent evaluated on higher budget than challenger (%.4f > %.4f), not changing the incumbent", + inc_run.budget, + chall_run.budget, + ) + return incumbent + + if inc_run.budget < chall_run.budget: + self._logger.debug( + "Challenger evaluated on higher budget than incumbent (%.4f > %.4f), changing the incumbent", + chall_run.budget, + inc_run.budget, + ) + if log_trajectory: + assert self._stats + self._stats.add_incumbent( + cost=runhistory.get_cost(challenger), + incumbent=challenger, + budget=current_budget, + ) + + return challenger + + # incumbent and challenger were both evaluated on the same budget, compare them based on their cost + chall_cost = runhistory.get_cost(challenger) + inc_cost = runhistory.get_cost(incumbent) + if chall_cost < inc_cost: + self._logger.info( + "Challenger (%.4f) is better than incumbent (%.4f) on budget %.4f.", + chall_cost, + inc_cost, + chall_run.budget, + ) + self._log_incumbent_changes(incumbent, challenger) + new_incumbent = challenger + if log_trajectory: + assert self._stats + self._stats.add_incumbent(cost=chall_cost, incumbent=new_incumbent, budget=current_budget) + else: + self._logger.debug( + "Incumbent (%.4f) is at least as good as the challenger (%.4f) on budget %.4f.", + inc_cost, + chall_cost, + inc_run.budget, + ) + + if log_trajectory and self._stats.incumbent_changed == 0: + assert self._stats + self._stats.add_incumbent( + cost=inc_cost, + incumbent=incumbent, + budget=current_budget, + ) + new_incumbent = incumbent + + return new_incumbent + + def _compare_configs_across_budgets( + self, + challenger: Configuration, + incumbent: Configuration, + runhistory: RunHistory, + log_trajectory: bool = True, + ) -> Configuration | None: + """Compares challenger with current incumbent on any budget. + + Returns + ------- + configuration : Configuration | None + The better configuration. + """ + assert self._stats + current_budget = self._all_budgets[self._stage] + + # compare challenger and incumbent based on cost + chall_cost = runhistory.get_min_cost(challenger) + inc_cost = runhistory.get_min_cost(incumbent) + if np.isfinite(chall_cost) and np.isfinite(inc_cost): + if chall_cost < inc_cost: + self._logger.info( + "Challenger (%.4f) is better than incumbent (%.4f) for any budget.", + chall_cost, + inc_cost, + ) + self._log_incumbent_changes(incumbent, challenger) + new_incumbent = challenger + if log_trajectory: + assert self._stats + self._stats.add_incumbent( + cost=chall_cost, + incumbent=new_incumbent, + budget=current_budget, + ) + else: + self._logger.debug( + "Incumbent (%.4f) is at least as good as the challenger (%.4f) for any budget.", + inc_cost, + chall_cost, + ) + if log_trajectory and self._stats.incumbent_changed == 0: + self._stats.add_incumbent(cost=inc_cost, incumbent=incumbent, budget=current_budget) + new_incumbent = incumbent + else: + self._logger.debug("Non-finite costs from run history!") + new_incumbent = incumbent + + return new_incumbent + + def _top_k(self, configs: list[Configuration], runhistory: RunHistory, k: int) -> list[Configuration]: + """Selects the top 'k' configurations from the given list based on their performance. + + This retrieves the performance for each configuration from the runhistory and checks + that the highest budget they've been evaluated on is the same for each of the configurations. + + Parameters + ---------- + configs : list[Configuration] + List of configurations to filter from. + runhistory : RunHistory + k : int + Number of configurations to select + + Returns + ------- + list[Configuration] + Top challenger configurations, sorted in increasing costs. + """ + # Extracting costs for each given configuration + config_costs = {} + + # Sample list instance-seed-budget key to act as base + run_key = runhistory.get_trials(configs[0], only_max_observed_budget=True) + for c in configs: + # Ensuring that all configurations being compared are run on the same set of instance, seed & budget + cur_run_key = runhistory.get_trials(c, only_max_observed_budget=True) + + # Move to compare set -- get_trials queries form a dictionary + # which is not an ordered structure. Some queries to that dictionary returned unordered + # list which wrongly trigger the below if + if set(cur_run_key) != set(run_key): + raise ValueError( + "Can not compare configs that were run on different instances-seeds-budgets: \n" + f"{run_key} and\n{cur_run_key}" + ) + config_costs[c] = runhistory.get_cost(c) + + configs_sorted = [k for k, v in sorted(config_costs.items(), key=lambda item: item[1])] + # Select top configurations only + top_configs = configs_sorted[:k] + return top_configs + + def _all_config_instance_seed_pairs_launched( + self, + runhistory: RunHistory, + activate_configuration_being_intensified: Configuration | None, + ) -> bool: + """When running SH, M configs might require N instances. Before moving to the next stage, we + need to make sure that tasks (each of the MxN jobs) are launched. + + This function returns a true if any M configs are pending or if N instance/seed are + still remaining. + + Parameters + ---------- + runhistory : RunHistory + activate_configuration_being_intensified: Configuration | None + The last configuration being actively processes by this intensifier. + + Returns + ------- + pending : bool + Whether a instance/pair of any of the M configurations for the current stage are pending. + """ + # 1: First we count the number of configurations that have been launched + # We only submit a new configuration M if all instance-seed pairs of (M - 1) + # have been proposed + configurations_by_this_intensifier = [ + c for c, i, s, b in self._run_tracker # if b == self._all_budgets[self.stage] # Bugfix closes #880 + ] + running_configs = set() + for k, v in runhistory.items(): + if runhistory.ids_config[k.config_id] in configurations_by_this_intensifier: + # We get all configurations launched by the current intensifier + # regardless if status is RUNNING or not, to make it more robust + running_configs.add(runhistory.ids_config[k.config_id]) + + # The total number of runs for this stage account for finished configurations + # (success + failed + do not advance) + the offset + running but not finished + # configurations. Also we account for the instances not launched for the + # currently running configuration + total_pending_configurations = max( + 0, + self._n_configs_in_stage[self._stage] + - ( + len( + set().union( + self._success_challengers, + self._failed_challengers, + self._do_not_advance_challengers, + running_configs, + ) + ) + + self._failed_challenger_offset + ), + ) + + # 2: Second we have to account for the number of pending instances for the active + # configuration. We assume for all (M - 1) configurations, all N instances-seeds + # have been already been launched + current_budget = self._all_budgets[self._stage] + if self._successive_halving._instance_as_budget: + previous_budget = int(self._all_budgets[self._stage - 1]) if self._stage > 0 else 0 + current_instances = self._successive_halving._instance_seed_pairs[ + int(previous_budget) : int(current_budget) + ] + else: + current_instances = self._successive_halving._instance_seed_pairs + + if activate_configuration_being_intensified is None: + # When a new stage begins, there is no active configuration. + # Therefore activate_configuration_being_intensified is empty and all instances are + # remaining + pending_instances_to_launch = len(current_instances) + else: + # self._current_instance_indices - 1 is the last proposed instance/seed pair from get_next_trial + # But it is zero indexed, so (self._current_instance_indices - 1) + 1 is the number of + # configurations that we have proposed to run in total for the running + # configuration via get_next_trial + pending_instances_to_launch = max( + len(current_instances) - self._current_instance_indices[activate_configuration_being_intensified], 0 + ) # type: ignore + + # If the there are any pending configuration, or instances/seed pending for the + # active runner, we return a boolean + return (total_pending_configurations + pending_instances_to_launch) <= 0 diff --git a/smac/logging.yml b/smac/logging.yml new file mode 100644 index 000000000..1bf110418 --- /dev/null +++ b/smac/logging.yml @@ -0,0 +1,21 @@ +version: 1 +disable_existing_loggers: false +formatters: + simple: + # format: '\x1b[33;20m %(levelname)s: %(message)s (in %(name)s)\x1b[0m' + # format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)' + format: '[%(levelname)s][%(filename)s:%(lineno)d] %(message)s' +handlers: + console: + class: logging.StreamHandler + level: INFO + formatter: simple + stream: ext://sys.stdout +#loggers: +# werkzeug: +# level: ERROR +# handlers: [ console ] +# propagate: no +root: + level: INFO + handlers: [console] diff --git a/smac/main/__init__.py b/smac/main/__init__.py new file mode 100644 index 000000000..b59443e2c --- /dev/null +++ b/smac/main/__init__.py @@ -0,0 +1,7 @@ +from smac.main.base_smbo import BaseSMBO +from smac.main.smbo import SMBO + +__all__ = [ + "BaseSMBO", + "SMBO", +] diff --git a/smac/main/base_smbo.py b/smac/main/base_smbo.py new file mode 100644 index 000000000..a3be170f1 --- /dev/null +++ b/smac/main/base_smbo.py @@ -0,0 +1,544 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import Any, Iterator + +import time +from itertools import product + +import numpy as np +from ConfigSpace import Configuration + +from smac.acquisition.function.abstract_acquisition_function import ( + AbstractAcquisitionFunction, +) +from smac.acquisition.maximizer.abstract_acqusition_maximizer import ( + AbstractAcquisitionMaximizer, +) +from smac.callback import Callback +from smac.constants import MAXINT +from smac.initial_design import AbstractInitialDesign +from smac.intensifier.abstract_intensifier import AbstractIntensifier +from smac.model.abstract_model import AbstractModel +from smac.random_design.abstract_random_design import AbstractRandomDesign +from smac.runhistory import StatusType, TrialInfo, TrialInfoIntent, TrialValue +from smac.runhistory.encoder.encoder import RunHistoryEncoder +from smac.runhistory.runhistory import RunHistory +from smac.runner.abstract_runner import AbstractRunner +from smac.scenario import Scenario +from smac.stats import Stats +from smac.utils.data_structures import recursively_compare_dicts +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class BaseSMBO: + """Implementation that contains the main Bayesian optimization loop. + + Parameters + ---------- + scenario : Scenario + The scenario object, holding all environmental information. + stats : Stats + Stats object to collect statistics about SMAC. + runner : AbstractRunner + The runner (containing the target function) is called internally to judge a trial's performance. + initial_design : InitialDesign + The sampled configurations from the initial design are evaluated before the Bayesian optimization loop starts. + runhistory : Runhistory + The runhistory stores all trials. + runhistory_encoder : RunHistoryEncoder + Based on the runhistory, the surrogate model is trained. However, the data first needs to be encoded, which + is done by the runhistory encoder. For example, inactive hyperparameters need to be encoded or cost values + can be log transformed. + intensifier : AbstractIntensifier + The intensifier decides which trial (combination of configuration, seed, budget and instance) should be run + next. + model : AbstractModel + The surrogate model. + acquisition_maximizer : AbstractAcquisitionMaximizer + The acquisition maximizer, deciding which configuration is most promising based on the surrogate model and + acquisition function. + acquisition_function : AbstractAcquisitionFunction + The acquisition function. + random_design : RandomDesign + The random design is used in the acquisition maximier, deciding whether the next configuration should be drawn + from the acquisition function or randomly. + overwrite: bool, defaults to False + When True, overwrites the run results if a previous run is found that is + inconsistent in the meta data with the current setup. If ``overwrite`` is set to False, the user is asked + for the exact behaviour (overwrite completely, save old run, or use old results). + + Warning + ------- + This model should only be initialized by a facade. + """ + + def __init__( + self, + scenario: Scenario, + stats: Stats, + runner: AbstractRunner, + initial_design: AbstractInitialDesign, + runhistory: RunHistory, + runhistory_encoder: RunHistoryEncoder, + intensifier: AbstractIntensifier, + model: AbstractModel, + acquisition_maximizer: AbstractAcquisitionMaximizer, + acquisition_function: AbstractAcquisitionFunction, + random_design: AbstractRandomDesign, + overwrite: bool = False, + ): + self._scenario = scenario + self._configspace = scenario.configspace + self._stats = stats + self._initial_design = initial_design + self._runhistory = runhistory + self._runhistory_encoder = runhistory_encoder + self._intensifier = intensifier + self._model = model + self._acquisition_maximizer = acquisition_maximizer + self._acquisition_function = acquisition_function + self._random_design = random_design + self._runner = runner + self._overwrite = overwrite + + # Those are the configs sampled from the passed initial design + # Selecting configurations from initial design + self._initial_design_configs = self._initial_design.select_configurations() + if len(self._initial_design_configs) == 0: + raise RuntimeError("SMAC needs initial configurations to work.") + + # Internal variables + self._finished = False + self._allow_optimization = True + self._stop = False # Gracefully stop SMAC + self._min_time = 10**-5 + self._callbacks: list[Callback] = [] + + # We don't restore the incumbent anymore but derive it directly from + # the stats object when the run is started. + self._incumbent = None + + # We initialize the state based on previous data. + # If no previous data is found then we take care of the initial design. + self._initialize_state() + + @property + def runhistory(self) -> RunHistory: + """The run history, which is filled with all information during the optimization process.""" + return self._runhistory + + @property + def stats(self) -> Stats: + """The stats object, which is updated during the optimization and shows relevant information, e.g., how many + trials have been finished and how the trajectory looks like. + """ + return self._stats + + @property + def incumbent(self) -> Configuration | None: + """The best configuration so far.""" + return self._incumbent + + def update_model(self, model: AbstractModel) -> None: + """Updates the model and updates the acquisition function.""" + self._model = model + self._acquisition_function.model = model + + def update_acquisition_function(self, acquisition_function: AbstractAcquisitionFunction) -> None: + """Updates acquisition function and assosiates the current model. Also, the acquisition + optimizer is updated. + """ + self._acquisition_function = acquisition_function + self._acquisition_function.model = self._model + self._acquisition_maximizer.acquisition_function = acquisition_function + + def optimize(self) -> Configuration: + """Runs the Bayesian optimization loop. + + Returns + ------- + incumbent : Configuration + The best found configuration. + """ + if not self._allow_optimization: + raise NotImplementedError("Unfortunately, previous runs can not be continued yet. 😞") + + # We return the incumbent if we already finished the optimization process (we don't want to allow to call + # optimize more than once). + if self._finished: + return self._incumbent + + # Start the timer before we do anything + self._stats.start_timing() + time_left = None + + for callback in self._callbacks: + callback.on_start(self) + + # Main BO loop + while True: + start_time = time.time() + + for callback in self._callbacks: + callback.on_iteration_start(self) + + # Sample next configuration for intensification. + # Initial design runs are also included in the BO loop now. + intent, trial_info = self.ask() + + # Update timebound only if a 'new' configuration is sampled as the challenger + if self._intensifier.num_trials == 0 or time_left is None: + time_spent = time.time() - start_time + time_left = self._get_timebound_for_intensification(time_spent) + logger.debug(f"New intensification time bound: {time_left}") + else: + old_time_left = time_left + time_spent = time_spent + (time.time() - start_time) + time_left = self._get_timebound_for_intensification(time_spent) + logger.debug(f"Updated intensification time bound from {old_time_left} to {time_left}.") + + # Skip starting new runs if the budget is now exhausted + if self._stats.is_budget_exhausted(): + intent = TrialInfoIntent.SKIP + + # Skip the run if there was a request to do so. + # For example, during intensifier intensification, we + # don't want to rerun a config that was previously ran + if intent == TrialInfoIntent.RUN: + n_objectives = self._scenario.count_objectives() + + # Track the fact that a run was launched in the run + # history. It's status is tagged as RUNNING, and once + # completed and processed, it will be updated accordingly + self._runhistory.add( + config=trial_info.config, + cost=float(MAXINT) if n_objectives == 1 else [float(MAXINT) for _ in range(n_objectives)], + time=0.0, + status=StatusType.RUNNING, + instance=trial_info.instance, + seed=trial_info.seed, + budget=trial_info.budget, + ) + + trial_info.config.config_id = self._runhistory._config_ids[trial_info.config] + self._runner.submit_trial(trial_info=trial_info) + elif intent == TrialInfoIntent.SKIP: + # No launch is required + # This marks a transition request from the intensifier + # To a new iteration + pass + elif intent == TrialInfoIntent.WAIT: + # In any other case, we wait for resources + # This likely indicates that no further decision + # can be taken by the intensifier until more data is + # available + self._runner.wait() + else: + raise NotImplementedError("No other RunInfoIntent has been coded.") + + # Check if there is any result, or else continue + for trial_info, trial_value in self._runner.iter_results(): + # Add the results of the run to the run history + # Additionally check for new incumbent + self.tell(trial_info, trial_value, time_left) + + logger.debug( + f"Remaining wallclock time: {self._stats.get_remaing_walltime()}; " + f"Remaining cpu time: {self._stats.get_remaining_cputime()}; " + f"Remaining trials: {self._stats.get_remaining_trials()}" + ) + + if self._stats.is_budget_exhausted() or self._stop: + if self._stats.is_budget_exhausted(): + logger.info("Configuration budget is exhausted.") + else: + logger.info("Shutting down because the stop flag was set.") + + # The budget can be exhausted for 2 reasons: number of ta runs or + # time. If the number of ta runs is reached, but there is still budget, + # wait for the runs to finish. + while self._runner.is_running(): + self._runner.wait() + + for trial_info, trial_value in self._runner.iter_results(): + # Add the results of the run to the run history + # Additionally check for new incumbent + self.tell(trial_info, trial_value, time_left) + + # Break from the intensification loop, as there are no more resources. + break + + for callback in self._callbacks: + callback.on_iteration_end(self) + + # Print stats at the end of each intensification iteration. + if self._intensifier.iteration_done: + self._stats.print() + + for callback in self._callbacks: + callback.on_end(self) + + self._finished = True + return self._incumbent + + @abstractmethod + def get_next_configurations(self, n: int | None = None) -> Iterator[Configuration]: + """Chooses next candidate solution with Bayesian optimization. The suggested configurations + depend on the surrogate model acquisition optimizer/function. This method is used by + the intensifier. + + Parameters + ---------- + n : int | None, defaults to None + Number of configurations to return. If None, uses the number of challengers defined in the acquisition + optimizer. + + Returns + ------- + configurations : Iterator[Configuration] + Iterator over configurations from the acquisition optimizer. + """ + raise NotImplementedError + + @abstractmethod + def ask(self) -> tuple[TrialInfoIntent, TrialInfo]: + """Asks the intensifier for the next trial. + + Returns + ------- + intent : TrialInfoIntent + Intent of the trials (wait/skip/run). + info : TrialInfo + Information about the trial (config, instance, seed, budget). + """ + raise NotImplementedError + + @abstractmethod + def tell(self, info: TrialInfo, value: TrialValue, time_left: float | None = None, save: bool = True) -> None: + """Adds the result of a trial to the runhistory and updates the intensifier. Also, + the stats object is updated. + + Parameters + ---------- + info : TrialInfo + Describes the trial from which to process the results. + value : TrialValue + Contains relevant information regarding the execution of a trial. + time_left : float | None, defaults to None + How much time in seconds is left to perform intensification. + save : bool, optional to True + Whether the runhistory should be saved. + """ + raise NotImplementedError + + def save(self) -> None: + """Saves the current stats and runhistory.""" + self._stats.save() + + path = self._scenario.output_directory + if path is not None: + self._runhistory.save_json(str(path / "runhistory.json")) + + def _register_callback(self, callback: Callback) -> None: + """Registers a callback to be called before, in between, and after the Bayesian optimization loop.""" + self._callbacks += [callback] + + def _initialize_state(self) -> None: + """Detects whether the optimization is restored from a previous state.""" + # Here we actually check whether the run should be continued or not. + # More precisely, we update our stats and runhistory object if all component arguments + # and scenario/stats object are the same. For doing so, we create a specific hash. + # The SMBO object recognizes that stats is not empty and hence does not the run initial design anymore. + # Since the runhistory is already updated, the model uses previous data directly. + + if not self._overwrite: + # First we get the paths from potentially previous data + old_output_directory = self._scenario.output_directory + old_runhistory_filename = self._scenario.output_directory / "runhistory.json" + old_stats_filename = self._scenario.output_directory / "stats.json" + + if old_output_directory.exists() and old_runhistory_filename.exists() and old_stats_filename.exists(): + old_scenario = Scenario.load(old_output_directory) + + if self._scenario == old_scenario: + # TODO: We have to do something different here: + # The intensifier needs to know about what happened. + # Therefore, we read in the runhistory but use the tell method to add everything. + # Update: Not working yet as it's much more complicated. Therefore, we just throw an error. + + logger.info("Continuing from previous run.") + + # We update the runhistory and stats in-place. + # Stats use the output directory from the config directly. + self._runhistory.reset() + self._runhistory.load_json(str(old_runhistory_filename), configspace=self._scenario.configspace) + self._stats.load() + + if self._stats.submitted == 1 and self._stats.finished == 0: + # Reset runhistory and stats if first run was not successful + logger.info("Since the previous run was not successful, SMAC will start from scratch again.") + self._runhistory.reset() + self._stats.reset() + elif self._stats.submitted == 0 and self._stats.finished == 0: + # If the other run did not start, we can just continue + self._runhistory.reset() + self._stats.reset() + else: + self._allow_optimization = False + else: + diff = recursively_compare_dicts( + Scenario.make_serializable(self._scenario), + Scenario.make_serializable(old_scenario), + level="scenario", + ) + logger.info( + f"Found old run in `{self._scenario.output_directory}` but it is not the same as the current " + f"one:\n{diff}" + ) + + feedback = input( + "\nPress one of the following numbers to continue or any other key to abort:\n" + "(1) Overwrite old run completely and start a new run.\n" + "(2) Rename the old run (append an '-old') and start a new run.\n" + "(3) Overwrite old run and re-use previous runhistory data. The configuration space " + "has to be the same for this option. This option is not tested yet.\n" + ) + + if feedback == "1": + # We don't have to do anything here, since we work with a clean runhistory and stats object + pass + elif feedback == "2": + # Rename old run + new_dir = str(old_scenario.output_directory.parent) + while True: + new_dir += "-old" + try: + old_scenario.output_directory.parent.rename(new_dir) + break + except OSError: + pass + elif feedback == "3": + # We overwrite runhistory and stats. + # However, we should ensure that we use the same configspace. + assert self._scenario.configspace == old_scenario.configspace + + self._runhistory.load_json(str(old_runhistory_filename), configspace=self._scenario.configspace) + self._stats.load() + else: + raise RuntimeError("SMAC run was stopped by the user.") + + # And now we save everything + self._scenario.save() + self.save() + + # Make sure we use the current incumbent + self._incumbent = self.stats.get_incumbent() + + # Sanity-checking: We expect an empty runhistory if finished in stats is 0 + # Note: stats.submitted might not be 0 because the user could have provide information via the tell method only + if self.stats.finished == 0 or self._incumbent is None: + assert self.runhistory.empty() + else: + # That's the case when the runhistory is not empty + assert not self.runhistory.empty() + + logger.info(f"Starting optimization with incumbent {self._incumbent.get_dictionary()}.") + self.stats.print() + + def _get_timebound_for_intensification(self, time_spent: float) -> float: + """Calculate time left for intensify from the time spent on choosing challengers using the + fraction of time intended for intensification (which is specified in + ``intensifier.intensification_percentage``). + + Parameters + ---------- + time_spent : float + + Returns + ------- + time_left : float + """ + if not hasattr(self._intensifier, "intensify_percentage"): + return np.inf + + intensify_percentage = self._intensifier.intensify_percentage # type: ignore + total_time = time_spent / (1 - intensify_percentage) + time_left = intensify_percentage * total_time + + logger.debug( + f"\n--- Total time: {round(total_time, 4)}" + f"\n--- Time spent on choosing next configurations: {round(time_spent, 4)} ({(1 - intensify_percentage)})" + f"\n--- Time left for intensification: {round(time_left, 4)} ({intensify_percentage})" + ) + + return time_left + + def validate( + self, + config: Configuration, + *, + instances: list[str] | None = None, + seed: int | None = None, + ) -> float | list[float]: + """Validates a configuration with different seeds than in the optimization process and on the highest + budget (if budget type is real-valued). + + Parameters + ---------- + config : Configuration + Configuration to validate + instances : list[str] | None, defaults to None + Which instances to validate. If None, all instances specified in the scenario are used. + In case that the budget type is real-valued budget, this argument is ignored. + seed : int | None, defaults to None + If None, the seed from the scenario is used. + + Returns + ------- + cost : float | list[float] + The averaged cost of the configuration. In case of multi-fidelity, the cost of each objective is + averaged. + """ + if seed is None: + seed = self._scenario.seed + + rng = np.random.default_rng(seed) + + seeds = [] + for _ in range(len(self._intensifier.get_target_function_seeds())): + seed = int(rng.integers(low=0, high=MAXINT, size=1)[0]) + seeds += [seed] + + used_budgets: list[float | None] = [None] + if self._intensifier.uses_budgets: + # Select last budget + used_budgets = [self._intensifier.get_target_function_budgets()[-1]] + + used_instances: list[str | None] = [None] + if self._intensifier.uses_instances: + if instances is None: + assert self._scenario.instances is not None + used_instances = self._scenario.instances # type: ignore + + costs = [] + for s, b, i in product(seeds, used_budgets, used_instances): + kwargs: dict[str, Any] = {} + if s is not None: + kwargs["seed"] = s + if b is not None: + kwargs["budget"] = b + if i is not None: + kwargs["instance"] = i + + _, cost, _, _ = self._runner.run(config, **kwargs) + costs += [cost] + + np_costs = np.array(costs) + return np.mean(np_costs, axis=0) diff --git a/smac/main/boing.py b/smac/main/boing.py new file mode 100644 index 000000000..e3364ec2c --- /dev/null +++ b/smac/main/boing.py @@ -0,0 +1,627 @@ +# from __future__ import annotations + +# from typing import Dict, Iterator, List, Tuple, Type, Union + +# import copy +# from itertools import chain + +# import numpy as np +# from ConfigSpace.hyperparameters import NumericalHyperparameter + +# from smac.acquisition import AbstractAcquisitionMaximizer +# from smac.acquisition.function import AbstractAcquisitionFunction +# from smac.acquisition.function.expected_improvement import EI +# from smac.acquisition.function.thompson import TS +# from ConfigSpace import Configuration +# from smac.constants import MAXINT +# from smac.main.smbo import SMBO +# from smac.model.abstract_model import AbstractModel +# from smac.model.gaussian_process.gpytorch_gaussian_process import ( +# GloballyAugmentedLocalGaussianProcess, +# ) +# from smac.model.random_forest.random_forest import ( +# RandomForest, +# ) +# from smac.model.utils import get_types +# from smac.runhistory.encoder.boing_encoder import RunHistoryRawEncoder +# from smac.utils.logging import get_logger +# from smac.utils.subspaces.boing_subspace import BOinGSubspace +# from smac.utils.subspaces.turbo_subspace import TuRBOSubSpace + +# __copyright__ = "Copyright 2022, automl.org" +# __license__ = "3-clause BSD" + + +# logger = get_logger(__name__) + + +# class BOinGSMBO(SMBO): +# """ +# Interface to train the EPM and generate next configurations with both global and local models. + +# Parameters +# ---------- +# model_local: Type[AbstractModel], +# local empirical performance model, used in subspace. Since the subspace might have different amount of +# hyperparameters compared to the search space. We only instantiate them under the subspace. +# model_local_kwargs: Optional[Dict] = None, +# parameters for initializing a local model +# acquisition_func_local: AbstractAcquisitionFunction | Type[AbstractAcquisitionFunction], +# local acquisition function, used in subspace +# acquisition_func_local_kwargs: Dict | None = None, +# parameters for initializing a local acquisition function optimizer +# acq_optimizer_local: AbstractAcquisitionMaximizer | None = None, +# Optimizer of acquisition function of local models, same as above, since an acquisition function optimizer +# requires +# acq_optimizer_local_kwargs: Dict | None = None, +# parameters for the optimizer of acquisition function of local models +# max_configs_local_fracs : float +# The maximal number of fractions of samples to be included in the subspace. If the number of samples in the +# subspace is greater than this value and n_min_config_inner, the subspace will be cropped to fit the +# requirement +# min_configs_local: int | None, +# Minimum number of samples included in the inner loop model +# do_switching: bool = False +# if we want to switch between turbo and boing or do a pure BOinG search +# turbo_kwargs: Dict | None = None +# parameters for building a turbo optimizer, for details, please refer to smac.loop.turbo +# args: +# additional arguments for initialize base SMBO object +# kwargs: +# additional arguments for initialize base SMBO object +# """ + +# def __init__( +# self, +# model_local: Type[AbstractModel] = GloballyAugmentedLocalGaussianProcess, +# acquisition_func_local: AbstractAcquisitionFunction | Type[AbstractAcquisitionFunction] = EI, +# model_local_kwargs: Dict | None = None, +# acquisition_func_local_kwargs: Dict | None = None, +# acq_optimizer_local: AbstractAcquisitionMaximizer | None = None, +# acq_optimizer_local_kwargs: Dict | None = None, +# max_configs_local_fracs: float = 0.5, +# min_configs_local: int | None = None, +# do_switching: bool = False, +# turbo_kwargs: Dict | None = None, +# *args, +# **kwargs, +# ): +# super(BOinGSMBO, self).__init__(*args, **kwargs) + +# self.subspace_info = { +# "model_local": model_local, +# "model_local_kwargs": model_local_kwargs, +# "acq_func_local": acquisition_func_local, +# "acq_func_local_kwargs": acquisition_func_local_kwargs, +# "acq_optimizer_local": acq_optimizer_local, +# "acq_optimizer_local_kwargs": acq_optimizer_local_kwargs, +# } + +# self.max_configs_local_fracs = max_configs_local_fracs +# self.min_configs_local = ( +# min_configs_local if min_configs_local is not None else 5 * len(self.configspace.get_hyperparameters()) +# ) + +# types, bounds = get_types(self.configspace, instance_features=None) + +# self.types = types +# self.bounds = bounds +# self.cat_dims = np.where(np.array(types) != 0)[0] +# self.cont_dims = np.where(np.array(types) == 0)[0] + +# self.frac_to_start_bi = 0.8 +# self.split_count = np.zeros(len(types)) +# self.do_switching = do_switching +# self.random_search_upper_log = 1 + +# self.optimal_value = np.inf +# self.optimal_config = None + +# self.ss_threshold = 0.1 ** len(self.configspace.get_hyperparameters()) +# if self.do_switching: +# # If we want to switch between BOinG and TurBO +# self.run_TuRBO = False +# self.failcount_BOinG = 0 +# self.failcount_TuRBO = 0 + +# turbo_model = copy.deepcopy(model_local) +# turbo_acq = TS +# turbo_opt_kwargs = dict( +# config_space=self.configspace, +# bounds=bounds, +# hps_types=types, +# model_local=turbo_model, +# model_local_kwargs=copy.deepcopy(model_local_kwargs), +# acq_func_local=turbo_acq, +# rng=self.rng, +# length_min=2e-4, +# ) +# self.turbo_kwargs = turbo_opt_kwargs +# if turbo_kwargs is not None: +# turbo_opt_kwargs.update(turbo_kwargs) +# self.turbo_optimizer = TuRBOSubSpace(**turbo_opt_kwargs) + +# def restart_TuRBOinG(self, X: np.ndarray, Y: np.ndarray, Y_raw: np.ndarray, train_model: bool = False) -> None: +# """ +# Restart a new TurBO Optimizer, the bounds of the TurBO Optimizer is determined by a RF, we randomly sample 20 +# points and extract subspaces that contain at least self.min_configs_local points, and we select the subspace +# with the largest volume to construct a turbo optimizer +# Parameters +# ---------- +# X: np.ndarray (N, D) +# previous evaluated configurations +# Y: np.ndarray (N,) +# performances of previous evaluated configurations (transformed by rh2epm transformer) +# Y_raw: np.ndarray (N,) +# performances of previous evaluated configurations (raw values, not transformed) +# train_model: bool +# if we retrain the model with the given X and Y +# """ +# if train_model: +# self.model.train(X, Y) +# num_samples = 20 +# union_ss = [] +# union_indices = [] +# rand_samples = self.configspace.sample_configuration(num_samples) +# for sample in rand_samples: +# sample_array = sample.get_array() +# union_bounds_cont, _, ss_data_indices = subspace_extraction( +# X=X, +# challenger=sample_array, +# model=self.model, +# num_min=self.min_configs_local, +# num_max=MAXINT, +# bounds=self.bounds, +# cont_dims=self.cont_dims, +# cat_dims=self.cat_dims, +# ) +# union_ss.append(union_bounds_cont) +# union_indices.append(ss_data_indices) +# union_ss = np.asarray(union_ss) +# volume_ss = np.product(union_ss[:, :, 1] - union_ss[:, :, 0], axis=1) # type: ignore +# ss_idx = np.argmax(volume_ss) +# ss_turbo = union_ss[ss_idx] +# ss_data_indices = union_indices[ss_idx] + +# # we only consider numerical(continuous) hyperparameters here +# self.turbo_optimizer = TuRBOSubSpace( +# **self.turbo_kwargs, # type: ignore +# bounds_ss_cont=ss_turbo, # type: ignore +# initial_data=(X[ss_data_indices], Y_raw[ss_data_indices]), # type: ignore +# ) +# self.turbo_optimizer.add_new_observations(X[ss_data_indices], Y_raw[ss_data_indices]) + +# def ask(self) -> Iterator[Configuration]: +# """ +# Choose next candidate solution with Bayesian optimization. We use TurBO optimizer or BOinG to suggest +# the next configuration. +# If we switch local model between TurBO and BOinG, we gradually increase the probability to switch to another +# optimizer if we cannot make further process. (Or if TurBO find a new incumbent, we will switch to BOinG to do +# further exploitation) +# """ +# incumbent_value: float = None + +# # we also need the untransformed raw y values to used for local models +# X, Y, Y_raw, X_configurations = self._collect_all_data_to_train_model() +# if self.do_switching: +# if self.run_TuRBO: +# X, Y, Y_raw, X_configurations = self._collect_all_data_to_train_model() + +# num_new_observations = 1 # here we only consider batch_size == 1 + +# new_observations = Y_raw[-num_new_observations:] + +# # give new suggestions from initialized values in TurBO +# if len(self.turbo_optimizer.init_configs) > 0: +# self.turbo_optimizer.add_new_observations(X[-num_new_observations:], +# Y_raw[-num_new_observations:]) +# return self.turbo_optimizer.generate_challengers() + +# self.turbo_optimizer.adjust_length(new_observations) + +# # if we need to restart TurBO, we first check if we want to switch to BOinG +# if self.turbo_optimizer.length < self.turbo_optimizer.length_min: +# optimal_turbo = np.min(self.turbo_optimizer.ss_y) + +# logger.debug(f"Best Found value by TuRBO: {optimal_turbo}") + +# increment = optimal_turbo - self.optimal_value + +# if increment < 0: +# min_idx = np.argmin(Y_raw) +# self.optimal_value = Y_raw[min_idx].item() +# # compute the distance between the previous incumbent and new incumbent +# cfg_diff = X[min_idx] - self.optimal_config +# self.optimal_config = X[min_idx] +# # we avoid sticking to a local minimum too often, e.g. either we have a relative much better +# # configuration or the new configuration is a little bit far away from the current incumbent +# if ( +# increment < -1e-3 * np.abs(self.optimal_value) +# or np.abs(np.product(cfg_diff)) >= self.ss_threshold +# ): +# self.failcount_TuRBO -= 1 +# # switch to BOinG as TurBO found a better model and we could do exploration +# # also we halve the failcount of BOinG to avoid switching to TurBO too frequently +# self.failcount_BOinG = self.failcount_BOinG // 2 +# self.run_TuRBO = False +# logger.debug("Optimizer switches to BOinG!") + +# else: +# self.failcount_TuRBO += 1 + +# # The probability is a linear curve. +# prob_to_BOinG = 0.1 * self.failcount_TuRBO +# logger.debug(f"failure_count TuRBO :{self.failcount_TuRBO}") +# rand_value = self.rng.random() + +# if rand_value < prob_to_BOinG: +# self.failcount_BOinG = self.failcount_BOinG // 2 +# self.run_TuRBO = False +# logger.debug("Optimizer switches to BOinG!") +# else: +# self.restart_TuRBOinG(X=X, Y=Y, Y_raw=Y_raw, train_model=True) +# return self.turbo_optimizer.generate_challengers() + +# self.turbo_optimizer.add_new_observations(X[-num_new_observations:], Y_raw[-num_new_observations:]) + +# return self.turbo_optimizer.generate_challengers() +# previous_configs = self.runhistory.get_configs() +# if X.shape[0] == 0: +# # Only return a single point to avoid an overly high number of +# # random search iterations +# return iter([self.configspace.sample_configuration(1)]) +# # if the number of points is not big enough, we simply build one subspace (the raw configuration space) and +# # the local model becomes global model +# if X.shape[0] < (self.min_configs_local / self.frac_to_start_bi): +# if len(self.configspace.get_conditions()) == 0: +# self.model.train(X, Y) +# cs = self.scenario.configspace # type: ignore +# ss = BOinGSubspace( +# config_space=cs, +# bounds=self.bounds, +# hps_types=self.types, +# rng=self.rng, +# initial_data=(X, Y_raw), +# incumbent_array=None, +# model_local=self.subspace_info["model_local"], # type: ignore +# model_local_kwargs=self.subspace_info["model_local_kwargs"], # type: ignore +# acq_func_local=self.subspace_info["acq_func_local"], # type: ignore +# acq_func_local_kwargs=self.subspace_info["acq_func_local_kwargs"], # type: ignore +# acq_optimizer_local=self.acquisition_optimizer, +# ) +# return ss.generate_challengers() + +# # train the outer model +# self.model.train(X, Y) + +# if incumbent_value is not None: +# best_observation = incumbent_value +# x_best_array = None # type: np.ndarray | None +# else: +# if self.runhistory.empty(): +# raise ValueError("Runhistory is empty and the cost value of " "the incumbent is unknown.") +# x_best_array, best_observation = self._get_x_best(self.predict_x_best, X_configurations) + +# self.acquisition_function.update( +# model=self.model, +# eta=best_observation, +# incumbent_array=x_best_array, +# num_data=len(self._get_evaluated_configs()), +# X=X_configurations, +# ) + +# if self.do_switching: +# # check if we need to switch to turbo +# # same as above +# self.failcount_BOinG += 1 +# increment = Y_raw[-1].item() - self.optimal_value +# if increment < 0: +# if self.optimal_config is not None: +# cfg_diff = X[-1] - self.optimal_config +# if ( +# increment < -1e-2 * np.abs(self.optimal_value) +# or np.abs(np.product(cfg_diff)) >= self.ss_threshold +# ): +# self.failcount_BOinG -= X.shape[-1] +# self.optimal_value = Y_raw[-1].item() +# self.optimal_config = X[-1] +# else: +# # restart +# idx_min = np.argmin(Y_raw) +# logger.debug("Better value found by BOinG, continue BOinG") +# self.optimal_value = Y_raw[idx_min].item() +# self.optimal_config = X[idx_min] +# self.failcount_BOinG = 0 + +# # similar to TurBO, we do a judgement every n_dimension times +# amplify_param = self.failcount_BOinG // (X.shape[-1] * 1) + +# if self.failcount_BOinG % (X.shape[-1] * 1) == 0: +# prob_to_TurBO = 0.1 * amplify_param +# rand_value = self.rng.random() + +# if rand_value < prob_to_TurBO: +# self.run_TuRBO = True +# logger.debug("Switch To TuRBO") +# self.failcount_TuRBO = self.failcount_TuRBO // 2 +# self.restart_TuRBOinG(X=X, Y=Y, Y_raw=Y_raw, train_model=False) + +# challengers_global = self.acquisition_optimizer.maximize( +# previous_configs, +# random_design=self.random_design, +# ) + +# if ( +# X.shape[0] < (self.min_configs_local / self.frac_to_start_bi) +# and len(self.configspace.get_conditions()) == 0 +# ): +# return challengers_global + +# cfg_challenger_global_first = next(challengers_global) +# array_challenger_global_first = cfg_challenger_global_first.get_array() # type: np.ndarray + +# num_max_configs = int(X.shape[0] * self.max_configs_local_fracs) + +# # to avoid the case that num_max_configs is only a little larger than self.min_configs_local +# num_max = MAXINT if num_max_configs <= 2 * self.min_configs_local else num_max_configs + +# if len(self.configspace.get_conditions()) > 0: +# challanger_activate_hps = np.isfinite(array_challenger_global_first).astype(int) +# rh_activate_hps = np.isfinite(X).astype(int) +# indices_X_in_same_hierarchy = np.all((challanger_activate_hps - rh_activate_hps) == 0, axis=1) +# num_indices_X_in_same_hierarchy = sum(indices_X_in_same_hierarchy) + +# if num_indices_X_in_same_hierarchy == 0: +# return chain([cfg_challenger_global_first], challengers_global) + +# activate_dims = [] +# hps = self.configspace.get_hyperparameters() +# for idx_hp in np.where(challanger_activate_hps > 0)[0]: +# if isinstance(hps[idx_hp], NumericalHyperparameter): +# activate_dims.append(idx_hp) +# else: +# indices_X_in_same_hierarchy = indices_X_in_same_hierarchy & ( +# X[:, idx_hp] == array_challenger_global_first[idx_hp] +# ) +# num_indices_X_in_same_hierarchy = sum(indices_X_in_same_hierarchy) + +# X = X[indices_X_in_same_hierarchy] +# Y_raw = Y_raw[indices_X_in_same_hierarchy] + +# if len(activate_dims) == 0 or num_indices_X_in_same_hierarchy <= max(5, len(activate_dims)): +# return chain([cfg_challenger_global_first], challengers_global) +# n_min_configs_inner = self.min_configs_local // len(hps) * len(activate_dims) +# else: +# n_min_configs_inner = self.min_configs_local +# activate_dims = np.arange(len(self.configspace.get_hyperparameters())) + +# bounds_ss_cont, bounds_ss_cat, ss_data_indices = subspace_extraction( +# X=X, +# challenger=array_challenger_global_first, +# model=self.model, # type: ignore[arg-type] +# num_min=n_min_configs_inner, +# num_max=num_max, +# bounds=self.bounds, +# cont_dims=self.cont_dims, +# cat_dims=self.cat_dims, +# ) + +# logger.debug("contained {0} data of {1}".format(sum(ss_data_indices), Y_raw.size)) + +# ss = BOinGSubspace( +# config_space=self.configspace, +# bounds=self.bounds, +# hps_types=self.types, +# bounds_ss_cont=bounds_ss_cont, # type: ignore[arg-type] +# bounds_ss_cat=bounds_ss_cat, +# rng=self.rng, +# initial_data=(X, Y_raw), +# incumbent_array=array_challenger_global_first, # type: ignore[arg-type] +# activate_dims=activate_dims, +# **self.subspace_info, # type: ignore[arg-type] +# ) +# return ss.generate_challengers() + +# def _collect_all_data_to_train_model(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: +# """ +# Similar to the implementation of SMBO. However, we also return the raw target values (before transformation). +# """ +# # if we use a float value as a budget, we want to train the model only on the highest budget +# available_budgets = [] +# for run_key in self.runhistory.data.keys(): +# available_budgets.append(run_key.budget) + +# # Sort available budgets from highest to lowest budget +# available_budgets = sorted(list(set(available_budgets)), reverse=True) + +# # Get #points per budget and if there are enough samples, then build a model +# for b in available_budgets: +# X, Y, Y_raw = self.runhistory_encoder.transform_with_raw( # type: ignore[attr-defined] +# self.runhistory, +# budget_subset=[ +# b, +# ], +# ) # type: ignore +# if X.shape[0] >= self.min_samples_model: +# self.currently_considered_budgets = [ +# b, +# ] +# configs_array = self.runhistory_encoder.get_configurations( +# self.runhistory, budget_subset=self.currently_considered_budgets +# ) +# return X, Y, Y_raw, configs_array + +# return ( +# np.empty(shape=[0, 0]), +# np.empty( +# shape=[ +# 0, +# ] +# ), +# np.empty( +# shape=[ +# 0, +# ] +# ), +# np.empty(shape=[0, 0]), +# ) + + +# def subspace_extraction( +# X: np.ndarray, +# challenger: np.ndarray, +# model: RandomForest, +# num_min: int, +# num_max: int, +# bounds: np.ndarray | List[Tuple], +# cat_dims: np.ndarray, +# cont_dims: np.ndarray, +# ) -> Tuple[np.ndarray, List[Tuple], np.ndarray]: +# """ +# Extract a subspace that contains at least num_min points but no more than num_max points + +# Parameters +# ---------- +# X: np.ndarray (N, D) +# points used to train the model +# challenger: np.ndarray (1, D) +# the challenger where the subspace would grow +# model: RandomForestWithInstances +# a rf model +# num_min: int +# minimal number of points to be included in the subspace +# num_max: int +# maximal number of points to be included in the subspace +# bounds: np.ndarray(D, 2) +# bounds of the entire space, D = D_cat + D_cont +# cat_dims: np.ndarray (D_cat) +# categorical dimensions +# cont_dims: np.ndarray(D_cont) +# continuous dimensions + +# Returns +# ------- +# union_bounds_cont: np.ndarray(D_cont, 2), +# the continuous bounds of the subregion +# union_bounds_cat, List[Tuple], +# the categorical bounds of the subregion +# in_ss_dims: +# indices of the points that lie inside the subregion +# """ +# trees = model.rf.get_all_trees() +# trees = [tree for tree in trees] +# num_trees = len(trees) +# node_indices = [0] * num_trees + +# indices_trees = np.arange(num_trees) +# np.random.shuffle(indices_trees) +# ss_indices = np.full(X.shape[0], True) # type: np.ndarray + +# stop_update = [False] * num_trees + +# ss_bounds = np.array(bounds) + +# cont_dims = np.array(cont_dims) +# cat_dims = np.array(cat_dims) + +# if len(cat_dims) == 0: +# ss_bounds_cat = [()] +# else: +# ss_bounds_cat = [() for _ in range(len(cat_dims))] +# for i, cat_dim in enumerate(cat_dims): +# ss_bounds_cat[i] = np.arange(ss_bounds[cat_dim][0]) + +# if len(cont_dims) == 0: +# ss_bounds_cont = np.array([]) # type: np.ndarray +# else: +# ss_bounds_cont = ss_bounds[cont_dims] + +# def traverse_forest(check_num_min: bool = True) -> None: +# nonlocal ss_indices +# np.random.shuffle(indices_trees) +# for i in indices_trees: +# if stop_update[i]: +# continue +# tree = trees[int(i)] +# node_idx = node_indices[i] +# node = tree.get_node(node_idx) + +# if node.is_a_leaf(): +# stop_update[i] = True +# continue + +# feature_idx = node.get_feature_index() +# cont_feature_idx = np.where(feature_idx == cont_dims)[0] +# if cont_feature_idx.size == 0: +# # This node split the subspace w.r.t. the categorical hyperparameters +# cat_feature_idx = np.where(feature_idx == cat_dims)[0][0] +# split_value = node.get_cat_split() +# intersect = np.intersect1d(ss_bounds_cat[cat_feature_idx], split_value, assume_unique=True) + +# if len(intersect) == len(ss_bounds_cat[cat_feature_idx]): +# # will fall into the left child +# temp_child_idx = 0 +# node_indices[i] = node.get_child_index(temp_child_idx) +# elif len(intersect) == 0: +# # will fall into the left child +# temp_child_idx = 1 +# node_indices[i] = node.get_child_index(temp_child_idx) +# else: +# if challenger[feature_idx] in intersect: +# temp_child_idx = 0 +# temp_node_indices = ss_indices & np.in1d(X[:, feature_idx], split_value) +# temp_bound_ss = intersect +# else: +# temp_child_idx = 1 +# temp_node_indices = ss_indices & np.in1d(X[:, feature_idx], split_value, invert=True) +# temp_bound_ss = np.setdiff1d(ss_bounds_cat[cat_feature_idx], split_value) +# if sum(temp_node_indices) > num_min: +# # number of points inside subspace is still greater than num_min, we could go deeper +# ss_bounds_cat[cat_feature_idx] = temp_bound_ss +# ss_indices = temp_node_indices +# node_indices[i] = node.get_child_index(temp_child_idx) +# else: +# if check_num_min: +# stop_update[i] = True +# else: +# # if we don't check the num_min, we will stay go deeper into the child nodes without +# # splitting the subspace +# node_indices[i] = node.get_child_index(temp_child_idx) +# else: +# # This node split the subspace w.r.t. the continuous hyperparameters +# split_value = node.get_num_split_value() +# cont_feature_idx = cont_feature_idx.item() +# if ss_bounds_cont[cont_feature_idx][0] <= split_value <= ss_bounds_cont[cont_feature_idx][1]: +# # the subspace can be further split +# if challenger[feature_idx] >= split_value: +# temp_bound_ss = np.array([split_value, ss_bounds_cont[cont_feature_idx][1]]) +# temp_node_indices = ss_indices & (X[:, feature_idx] >= split_value) +# temp_child_idx = 1 +# else: +# temp_bound_ss = np.array([ss_bounds_cont[cont_feature_idx][0], split_value]) +# temp_node_indices = ss_indices & (X[:, feature_idx] <= split_value) +# temp_child_idx = 0 +# if sum(temp_node_indices) > num_min: +# # number of points inside subspace is still greater than num_min +# ss_bounds_cont[cont_feature_idx] = temp_bound_ss +# ss_indices = temp_node_indices +# node_indices[i] = node.get_child_index(temp_child_idx) +# else: +# if check_num_min: +# stop_update[i] = True +# else: +# node_indices[i] = node.get_child_index(temp_child_idx) +# else: +# temp_child_idx = 1 if challenger[feature_idx] >= split_value else 0 +# node_indices[i] = node.get_child_index(temp_child_idx) + +# while sum(stop_update) < num_trees: +# traverse_forest() + +# if sum(ss_indices) > num_max: +# # number of points inside the subregion have a larger value than num_max +# stop_update = [False] * num_trees +# while sum(stop_update) < num_trees: +# traverse_forest(False) + +# return ss_bounds_cont, ss_bounds_cat, ss_indices # type: ignore[return-value] diff --git a/smac/main/smbo.py b/smac/main/smbo.py new file mode 100644 index 000000000..69a9873b0 --- /dev/null +++ b/smac/main/smbo.py @@ -0,0 +1,313 @@ +from __future__ import annotations + +from typing import Any, Iterator + +import copy + +import numpy as np +from ConfigSpace import Configuration + +from smac.main.base_smbo import BaseSMBO +from smac.runhistory import StatusType, TrialInfo, TrialValue +from smac.runhistory.enumerations import TrialInfoIntent +from smac.runner.exceptions import ( + FirstRunCrashedException, + TargetAlgorithmAbortException, +) +from smac.utils.configspace import convert_configurations_to_array +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class SMBO(BaseSMBO): + """Implements ``get_next_configurations``, ``ask``, and ``tell``.""" + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + self._predict_x_best = True + self._min_samples = 1 + self._considered_budgets: list[float | None] = [None] + + def get_next_configurations(self, n: int | None = None) -> Iterator[Configuration]: # noqa: D102 + for callback in self._callbacks: + callback.on_next_configurations_start(self) + + # Cost value of incumbent configuration (required for acquisition function). + # If not given, it will be inferred from runhistory or predicted. + # If not given and runhistory is empty, it will raise a ValueError. + incumbent_value: float | None = None + + logger.debug("Search for next configuration...") + X, Y, X_configurations = self._collect_data() + previous_configs = self._runhistory.get_configs() + + if X.shape[0] == 0: + # Only return a single point to avoid an overly high number of random search iterations. + # We got rid of random search here and replaced it with a simple configuration sampling from + # the configspace. + return iter([self._scenario.configspace.sample_configuration(1)]) + + self._model.train(X, Y) + + x_best_array: np.ndarray | None = None + if incumbent_value is not None: + best_observation = incumbent_value + else: + if self._runhistory.empty(): + raise ValueError("Runhistory is empty and the cost value of the incumbent is unknown.") + + x_best_array, best_observation = self._get_x_best(self._predict_x_best, X_configurations) + + self._acquisition_function.update( + model=self._model, + eta=best_observation, + incumbent_array=x_best_array, + num_data=len(self._get_evaluated_configs()), + X=X_configurations, + ) + + challengers = self._acquisition_maximizer.maximize( + previous_configs, + n_points=n, + random_design=self._random_design, + ) + + for callback in self._callbacks: + challenger_list = list(copy.deepcopy(challengers)) + callback.on_next_configurations_end(self, challenger_list) + + return challengers + + def ask(self) -> tuple[TrialInfoIntent, TrialInfo]: # noqa: D102 + for callback in self._callbacks: + callback.on_ask_start(self) + + intent, trial_info = self._intensifier.get_next_trial( + challengers=self._initial_design_configs, + incumbent=self._incumbent, + get_next_configurations=self.get_next_configurations, + runhistory=self._runhistory, + repeat_configs=self._intensifier.repeat_configs, + n_workers=self._runner.count_available_workers(), + ) + + if intent == TrialInfoIntent.RUN: + # There are 2 criteria that the stats object uses to know if the budged was exhausted. + # The budget time, which can only be known when the run finishes, + # And the number of ta executions. Because we submit the job at this point, + # we count this submission as a run. This prevent for using more + # runner runs than what the config allows. + self._stats._submitted += 1 + + # Remove config from initial design challengers to not repeat it again + self._initial_design_configs = [c for c in self._initial_design_configs if c != trial_info.config] + + for callback in self._callbacks: + callback.on_ask_end(self, intent, trial_info) + + return intent, trial_info + + def tell( + self, + info: TrialInfo, + value: TrialValue, + time_left: float | None = None, + save: bool = True, + ) -> None: # noqa: D102 + # We first check if budget/instance/seed is supported by the intensifier + if info.seed not in (seeds := self._intensifier.get_target_function_seeds()): + raise ValueError(f"Seed {info.seed} is not supported by the intensifier. Consider using one of {seeds}.") + elif info.budget not in (budgets := self._intensifier.get_target_function_budgets()): + raise ValueError( + f"Budget {info.budget} is not supported by the intensifier. Consider using one of {budgets}." + ) + elif info.instance not in (instances := self._intensifier.get_target_function_instances()): + raise ValueError( + f"Instance {info.instance} is not supported by the intensifier. Consider using one of {instances}." + ) + + if info.config.origin is None: + info.config.origin = "Custom" + + for callback in self._callbacks: + response = callback.on_tell_start(self, info, value) + # If a callback returns False, the optimization loop should be interrupted + # the other callbacks are still being called. + if response is False: + logger.info("An callback returned False. Abort is requested.") + self._stop = True + + # We expect the first run to always succeed. + if self._stats.finished == 0 and value.status == StatusType.CRASHED: + additional_info = "" + if "traceback" in value.additional_info: + additional_info = "\n\n" + value.additional_info["traceback"] + + raise FirstRunCrashedException("The first run crashed. Please check your setup again." + additional_info) + + # Update SMAC stats + self._stats._target_function_walltime_used += float(value.time) + self._stats._finished += 1 + + logger.debug( + f"Status: {value.status}, cost: {value.cost}, time: {value.time}, " f"Additional: {value.additional_info}" + ) + + self._runhistory.add( + config=info.config, + cost=value.cost, + time=value.time, + status=value.status, + instance=info.instance, + seed=info.seed, + budget=info.budget, + starttime=value.starttime, + endtime=value.endtime, + force_update=True, + additional_info=value.additional_info, + ) + self._stats._n_configs = len(self._runhistory._config_ids) + + if value.status == StatusType.ABORT: + raise TargetAlgorithmAbortException( + "The target function was aborted. The last incumbent can be found in the trajectory file." + ) + elif value.status == StatusType.STOP: + logger.debug("Value holds the status stop. Abort is requested.") + self._stop = True + + if time_left is None: + time_left = np.inf + + # Update the intensifier with the result of the runs + self._incumbent, _ = self._intensifier.process_results( + trial_info=info, + trial_value=value, + incumbent=self._incumbent, + runhistory=self._runhistory, + time_bound=max(self._min_time, time_left), + ) + + # Gracefully end optimization if termination cost is reached + if self._scenario.termination_cost_threshold != np.inf: + + cost = self.runhistory.average_cost(info.config) + + if not isinstance(cost, list): + cost = [cost] + + if not isinstance(self._scenario.termination_cost_threshold, list): + cost_threshold = [self._scenario.termination_cost_threshold] + else: + cost_threshold = self._scenario.termination_cost_threshold + + if len(cost) != len(cost_threshold): + raise RuntimeError("You must specify a termination cost threshold for each objective.") + + if all(cost[i] < cost_threshold[i] for i in range(len(cost))): + logger.info("Cost threshold was reached. Abort is requested.") + self._stop = True + + for callback in self._callbacks: + response = callback.on_tell_end(self, info, value) + # If a callback returns False, the optimization loop should be interrupted + # the other callbacks are still being called. + if response is False: + logger.info("An callback returned False. Abort is requested.") + self._stop = True + + if save: + self.save() + + def _collect_data(self) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + """Collects the data from the runhistory to train the surrogate model. The data collection strategy if budgets + are used is as follows: Looking from highest to lowest budget, return those observations + that support at least ``self._min_samples`` points. + + If no budgets are used, this is equivalent to returning all observations. + """ + # if we use a float value as a budget, we want to train the model only on the highest budget + available_budgets = [] + for run_key in self._runhistory: + available_budgets.append(run_key.budget) + + # Sort available budgets from highest to lowest budget + available_budgets = sorted(list(set(available_budgets)), reverse=True) # type: ignore + + # Get #points per budget and if there are enough samples, then build a model + for b in available_budgets: + X, Y = self._runhistory_encoder.transform(self._runhistory, budget_subset=[b]) + + if X.shape[0] >= self._min_samples: + self._considered_budgets = [b] + configs_array = self._runhistory_encoder.get_configurations( + self._runhistory, budget_subset=self._considered_budgets + ) + + return X, Y, configs_array + + return ( + np.empty(shape=[0, 0]), + np.empty( + shape=[ + 0, + ] + ), + np.empty(shape=[0, 0]), + ) + + def _get_evaluated_configs(self) -> list[Configuration]: + return self._runhistory.get_configs_per_budget(budget_subset=self._considered_budgets) + + def _get_x_best(self, predict: bool, X: np.ndarray) -> tuple[np.ndarray, float]: + """Get value, configuration, and array representation of the *best* configuration. + + The definition of best varies depending on the argument ``predict``. If set to `True`, + this function will return the stats of the best configuration as predicted by the model, + otherwise it will return the stats for the best observed configuration. + + Parameters + ---------- + predict : bool + Whether to use the predicted or observed best. + + Returns + ------- + float + np.ndarry + Configuration + """ + if predict: + model = self._model + costs = list( + map( + lambda x: ( + model.predict_marginalized(x.reshape((1, -1)))[0][0][0], # type: ignore + x, + ), + X, + ) + ) + costs = sorted(costs, key=lambda t: t[0]) + x_best_array = costs[0][1] + best_observation = costs[0][0] + # won't need log(y) if EPM was already trained on log(y) + else: + all_configs = self._runhistory.get_configs_per_budget(budget_subset=self._considered_budgets) + x_best = self._incumbent + x_best_array = convert_configurations_to_array(all_configs) + best_observation = self._runhistory.get_cost(x_best) + best_observation_as_array = np.array(best_observation).reshape((1, 1)) + + # It's unclear how to do this for inv scaling and potential future scaling. + # This line should be changed if necessary + best_observation = self._runhistory_encoder.transform_response_values(best_observation_as_array) + best_observation = best_observation[0][0] + + return x_best_array, best_observation diff --git a/smac/main/turbo.py b/smac/main/turbo.py new file mode 100644 index 000000000..9595bb498 --- /dev/null +++ b/smac/main/turbo.py @@ -0,0 +1,105 @@ +# from __future__ import annotations + +# from typing import Iterator + +# from smac.acquisition.function.thompson import TS +# from ConfigSpace import Configuration +# from smac.main.smbo import SMBO +# from smac.model.utils import get_types +# from smac.utils.logging import get_logger +# from smac.utils.subspaces.turbo_subspace import TuRBOSubSpace + + +# logger = get_logger(__name__) + + +# class TuRBOSMBO(SMBO): +# """ +# Interface to train the EPM and generate next configurations with TurBO: +# D. Eriksson et al. Scalable Global Optimization via Local Bayesian Optimization +# https://papers.nips.cc/paper/2019/file/6c990b7aca7bc7058f5e98ea909e924b-Paper.pdf + +# Parameters +# ---------- +# length_init: float +# Initialized length after restarting +# length_min: float +# If the subspace length is smaller than length_min, TurBO will restart +# length_max: float +# The maximum length of subspace +# success_tol: int +# Number of successful suggestions (suggested points become incumbent) required for expanding subspace +# failure_tol_min: int +# The minimum number of failure suggestions (suggested points fails to become incumbent) required for shrinking +# subspace +# n_init_x_params: int +# how many configurations will be used at most in the initial design (X*D). Used for restarting the subspace +# n_candidate_max: int +# Maximal Number of points used as candidates +# args: +# additional arguments for initialize base SMBO object +# kwargs: +# additional arguments for initialize base SMBO object +# """ + +# def __init__( +# self, +# length_init: float = 0.8, +# length_min: float = 0.5**8, +# length_max: float = 1.6, +# success_tol: int = 3, +# failure_tol_min: int = 4, +# n_init_x_params: int = 2, +# n_candidate_max: int = 5000, +# *args, +# **kwargs, +# ): +# super(TuRBOSMBO, self).__init__(*args, **kwargs) + +# types, bounds = get_types(self.configspace, instance_features=None) + +# self.turbo = TuRBOSubSpace( +# config_space=self.configspace, +# bounds=bounds, +# hps_types=types, +# model_local=self.model, +# acq_func_local=TS, +# length_init=length_init, +# length_min=length_min, +# length_max=length_max, +# success_tol=success_tol, +# failure_tol_min=failure_tol_min, +# n_init_x_params=n_init_x_params, +# n_candidate_max=n_candidate_max, +# ) + +# def ask(self) -> Iterator[Configuration]: +# """ +# Choose next candidate solution with TuRBO + +# Parameters +# ---------- +# incumbent_value: float +# Cost value of incumbent configuration (required for acquisition function); +# If not given, it will be inferred from runhistory or predicted; +# if not given and runhistory is empty, it will raise a ValueError. + +# Returns +# ------- +# Iterator +# """ +# logger.debug("Search for next configuration") +# X, Y, X_configurations = self._collect_data() + +# num_new_bservations = 1 # here we only consider batch size = 1 + +# new_observations = Y[-num_new_bservations:] +# if len(self.turbo.init_configs) > 0: +# self.turbo.add_new_observations(X[-num_new_bservations:], Y[-num_new_bservations:]) +# return self.turbo.generate_challengers() +# self.turbo.adjust_length(new_observations) + +# self.turbo.add_new_observations(X[-num_new_bservations:], Y[-num_new_bservations:]) + +# challengers = self.turbo.generate_challengers() +# return challengers diff --git a/smac/model/__init__.py b/smac/model/__init__.py new file mode 100644 index 000000000..fd3396791 --- /dev/null +++ b/smac/model/__init__.py @@ -0,0 +1,9 @@ +from smac.model.abstract_model import AbstractModel +from smac.model.multi_objective_model import MultiObjectiveModel +from smac.model.random_model import RandomModel + +__all__ = [ + "AbstractModel", + "MultiObjectiveModel", + "RandomModel", +] diff --git a/smac/model/abstract_model.py b/smac/model/abstract_model.py new file mode 100644 index 000000000..fdc451f6b --- /dev/null +++ b/smac/model/abstract_model.py @@ -0,0 +1,324 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import Any, TypeVar + +import copy +import warnings + +import numpy as np +from ConfigSpace import ConfigurationSpace +from sklearn.decomposition import PCA +from sklearn.exceptions import NotFittedError +from sklearn.preprocessing import MinMaxScaler + +from smac.constants import VERY_SMALL_NUMBER +from smac.utils.configspace import get_types +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +Self = TypeVar("Self", bound="AbstractModel") + + +class AbstractModel: + """Abstract implementation of the surrogate model. + + Note + ---- + The input dimensionality of Y for training and the output dimensions of all predictions depends on the concrete + implementation of this abstract class. + + Parameters + ---------- + configspace : ConfigurationSpace + instance_features : dict[str, list[int | float]] | None, defaults to None + Features (list of int or floats) of the instances (str). The features are incorporated into the X data, + on which the model is trained on. + pca_components : float, defaults to 7 + Number of components to keep when using PCA to reduce dimensionality of instance features. + seed : int + """ + + def __init__( + self, + configspace: ConfigurationSpace, + instance_features: dict[str, list[int | float]] | None = None, + pca_components: int | None = 7, + seed: int = 0, + ) -> None: + self._configspace = configspace + self._seed = seed + self._rng = np.random.RandomState(self._seed) + self._instance_features = instance_features + self._pca_components = pca_components + + n_features = 0 + if self._instance_features is not None: + for v in self._instance_features.values(): + if n_features == 0: + n_features = len(v) + else: + if len(v) != n_features: + raise RuntimeError("Instances must have the same number of features.") + + self._n_features = n_features + self._n_hps = len(self._configspace.get_hyperparameters()) + + self._pca = PCA(n_components=self._pca_components) + self._scaler = MinMaxScaler() + self._apply_pca = False + + # Never use a lower variance than this. + # If estimated variance < var_threshold, the set to var_threshold + self._var_threshold = VERY_SMALL_NUMBER + self._types, self._bounds = get_types(configspace, instance_features) + + # Initial types array which is used to reset the type array at every call to `self.train()` + self._initial_types = copy.deepcopy(self._types) + + @property + def meta(self) -> dict[str, Any]: + """Returns the meta data of the created object.""" + return { + "name": self.__class__.__name__, + "types": self._types, + "bounds": self._bounds, + "pca_components": self._pca_components, + } + + def train(self: Self, X: np.ndarray, Y: np.ndarray) -> Self: + """Trains the random forest on X and Y. Internally, calls the method `_train`. + + Parameters + ---------- + X : np.ndarray [#samples, #hyperparameter + #features] + Input data points. + Y : np.ndarray [#samples, #objectives] + The corresponding target values. + + Returns + ------- + self : AbstractModel + """ + if len(X.shape) != 2: + raise ValueError("Expected 2d array, got %dd array!" % len(X.shape)) + + if X.shape[1] != self._n_hps + self._n_features: + raise ValueError( + f"Feature mismatch: X should have {self._n_hps} hyperparameters + {self._n_features} features, " + f"but has {X.shape[1]} in total." + ) + + if X.shape[0] != Y.shape[0]: + raise ValueError("X.shape[0] ({}) != y.shape[0] ({})".format(X.shape[0], Y.shape[0])) + + # Reduce dimensionality of features of larger than PCA_DIM + if ( + self._pca_components is not None + and X.shape[0] > self._pca.n_components + and self._n_features >= self._pca_components + ): + X_feats = X[:, -self._n_features :] + + # Scale features + X_feats = self._scaler.fit_transform(X_feats) + X_feats = np.nan_to_num(X_feats) # if features with max == min + + # PCA + X_feats = self._pca.fit_transform(X_feats) + X = np.hstack((X[:, : self._n_hps], X_feats)) + + if hasattr(self, "_types"): + # For RF, adapt types list + # if X_feats.shape[0] < self._pca, X_feats.shape[1] == X_feats.shape[0] + self._types = np.array( + np.hstack((self._types[: self._n_hps], np.zeros(X_feats.shape[1]))), + dtype=np.uint, + ) # type: ignore + + self._apply_pca = True + else: + self._apply_pca = False + + if hasattr(self, "_types"): + self._types = copy.deepcopy(self._initial_types) + + return self._train(X, Y) + + @abstractmethod + def _train(self: Self, X: np.ndarray, Y: np.ndarray) -> Self: + """Trains the random forest on X and Y. + + Parameters + ---------- + X : np.ndarray [#samples, #hyperparameter + #features] + Input data points. + Y : np.ndarray [#samples, #objectives] + The corresponding target values. + + Returns + ------- + self : AbstractModel + """ + raise NotImplementedError() + + def predict( + self, + X: np.ndarray, + covariance_type: str | None = "diagonal", + ) -> tuple[np.ndarray, np.ndarray | None]: + """Predicts mean and variance for a given X. Internally, calls the method `_predict`. + + Parameters + ---------- + X : np.ndarray [#samples, #hyperparameter + #features] + Input data points. + covariance_type: str | None, defaults to "diagonal" + Specifies what to return along with the mean. Applied only to Gaussian Processes. + Takes four valid inputs: + * None: Only the mean is returned. + * "std": Standard deviation at test points is returned. + * "diagonal": Diagonal of the covariance matrix is returned. + * "full": Whole covariance matrix between the test points is returned. + + Returns + ------- + means : np.ndarray [#samples, #objectives] + The predictive mean. + vars : np.ndarray [#samples, #objectives] or [#samples, #samples] | None + Predictive variance or standard deviation. + """ + if len(X.shape) != 2: + raise ValueError("Expected 2d array, got %dd array!" % len(X.shape)) + + if X.shape[1] != self._n_hps + self._n_features: + raise ValueError( + f"Feature mismatch: X should have {self._n_hps} hyperparameters + {self._n_features} features, " + f"but has {X.shape[1]} in total." + ) + + if self._apply_pca: + try: + X_feats = X[:, -self._n_features :] + X_feats = self._scaler.transform(X_feats) + X_feats = self._pca.transform(X_feats) + X = np.hstack((X[:, : self._n_hps], X_feats)) + except NotFittedError: + # PCA not fitted if only one training sample + pass + + if X.shape[1] != len(self._types): + raise ValueError("Rows in X should have %d entries but have %d!" % (len(self._types), X.shape[1])) + + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", "Predicted variances smaller than 0. Setting those variances to 0.") + mean, var = self._predict(X, covariance_type) + + if len(mean.shape) == 1: + mean = mean.reshape((-1, 1)) + + if var is not None and len(var.shape) == 1: + var = var.reshape((-1, 1)) + + return mean, var + + def _predict( + self, + X: np.ndarray, + covariance_type: str | None = "diagonal", + ) -> tuple[np.ndarray, np.ndarray | None]: + """Predicts mean and variance for a given X. + + Parameters + ---------- + X : np.ndarray [#samples, #hyperparameter + #features] + Input data points. + covariance_type : str | None, defaults to "diagonal" + Specifies what to return along with the mean. Applied only to Gaussian Processes. + Takes four valid inputs: + * None: Only the mean is returned. + * "std": Standard deviation at test points is returned. + * "diagonal": Diagonal of the covariance matrix is returned. + * "full": Whole covariance matrix between the test points is returned. + + Returns + ------- + means : np.ndarray [#samples, #objectives] + The predictive mean. + vars : np.ndarray [#samples, #objectives] or [#samples, #samples] | None + Predictive variance or standard deviation. + """ + raise NotImplementedError() + + def predict_marginalized(self, X: np.ndarray) -> tuple[np.ndarray, np.ndarray]: + """Predicts mean and variance marginalized over all instances. + + Warning + ------- + The input data must not include any features. + + Parameters + ---------- + X : np.ndarray [#samples, #hyperparameter] + Input data points. + + Returns + ------- + means : np.ndarray [#samples, 1] + The predictive mean. + vars : np.ndarray [#samples, 1] + The predictive variance. + """ + if len(X.shape) != 2: + raise ValueError("Expected 2d array, got %dd array!" % len(X.shape)) + + if X.shape[1] != self._n_hps: + raise ValueError( + f"Feature mismatch: X should have {self._n_hps} hyperparameters (and no features) for this method, " + f"but has {X.shape[1]} in total." + ) + + if self._instance_features is None: + mean, var = self.predict(X) + assert var is not None + + var[var < self._var_threshold] = self._var_threshold + var[np.isnan(var)] = self._var_threshold + + return mean, var + else: + n_instances = len(self._instance_features) + + mean = np.zeros(X.shape[0]) + var = np.zeros(X.shape[0]) + for i, x in enumerate(X): + features = np.array(list(self._instance_features.values())) + x_tiled = np.tile(x, (n_instances, 1)) + X_ = np.hstack((x_tiled, features)) + + means, vars = self.predict(X_) + assert vars is not None + + # VAR[1/n (X_1 + ... + X_n)] = + # 1/n^2 * ( VAR(X_1) + ... + VAR(X_n)) + # for independent X_1 ... X_n + var_x = np.sum(vars) / (len(vars) ** 2) + if var_x < self._var_threshold: + var_x = self._var_threshold + + var[i] = var_x + mean[i] = np.mean(means) + + if len(mean.shape) == 1: + mean = mean.reshape((-1, 1)) + + if len(var.shape) == 1: + var = var.reshape((-1, 1)) + + return mean, var diff --git a/smac/model/gaussian_process/__init__.py b/smac/model/gaussian_process/__init__.py new file mode 100644 index 000000000..1837bffdf --- /dev/null +++ b/smac/model/gaussian_process/__init__.py @@ -0,0 +1,11 @@ +from smac.model.gaussian_process.abstract_gaussian_process import ( + AbstractGaussianProcess, +) +from smac.model.gaussian_process.gaussian_process import GaussianProcess +from smac.model.gaussian_process.mcmc_gaussian_process import MCMCGaussianProcess + +__all__ = [ + "AbstractGaussianProcess", + "GaussianProcess", + "MCMCGaussianProcess", +] diff --git a/smac/epm/gaussian_process/__init__.py b/smac/model/gaussian_process/abstract_gaussian_process.py similarity index 60% rename from smac/epm/gaussian_process/__init__.py rename to smac/model/gaussian_process/abstract_gaussian_process.py index 8da500d5f..9db465124 100644 --- a/smac/epm/gaussian_process/__init__.py +++ b/smac/model/gaussian_process/abstract_gaussian_process.py @@ -1,73 +1,98 @@ -from typing import List, Optional, Tuple, Union +from __future__ import annotations + +from abc import abstractmethod +from typing import Any import numpy as np import sklearn.gaussian_process +from ConfigSpace import ConfigurationSpace from sklearn.gaussian_process import GaussianProcessRegressor from sklearn.gaussian_process.kernels import Kernel, KernelOperator -import smac.epm.gaussian_process.utils.prior -from smac.configspace import ConfigurationSpace -from smac.epm.base_epm import BaseEPM -from smac.epm.gaussian_process.utils.prior import Prior +from smac.model.abstract_model import AbstractModel +from smac.model.gaussian_process.priors.abstract_prior import AbstractPrior +from smac.model.gaussian_process.priors.tophat_prior import SoftTopHatPrior, TophatPrior -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__copyright__ = "Copyright 2022, automl.org" __license__ = "3-clause BSD" -class BaseModel(BaseEPM): +class AbstractGaussianProcess(AbstractModel): + """Abstract base class for all Gaussian process models. + + Parameters + ---------- + configspace : ConfigurationSpace + kernel : Kernel + Kernel which is used for the Gaussian process. + instance_features : dict[str, list[int | float]] | None, defaults to None + Features (list of int or floats) of the instances (str). The features are incorporated into the X data, + on which the model is trained on. + pca_components : float, defaults to 7 + Number of components to keep when using PCA to reduce dimensionality of instance features. + seed : int + """ + def __init__( self, configspace: ConfigurationSpace, - types: List[int], - bounds: List[Tuple[float, float]], - seed: int, kernel: Kernel, - instance_features: Optional[np.ndarray] = None, - pca_components: Optional[int] = None, + instance_features: dict[str, list[int | float]] | None = None, + pca_components: int | None = 7, + seed: int = 0, ): - """Abstract base class for all Gaussian process models.""" super().__init__( configspace=configspace, - types=types, - bounds=bounds, - seed=seed, instance_features=instance_features, pca_components=pca_components, + seed=seed, ) - self.rng = np.random.RandomState(seed) - self.kernel = kernel - self.gp = self._get_gp() + self._kernel = kernel + self._gp = self._get_gaussian_process() + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update({"kernel": self._kernel.meta}) - def _get_gp(self) -> GaussianProcessRegressor: - """Returns the Gaussian process.""" + return meta + + @abstractmethod + def _get_gaussian_process(self) -> GaussianProcessRegressor: + """Generates a Gaussian process.""" raise NotImplementedError() - def _normalize_y(self, y: np.ndarray) -> np.ndarray: + def _normalize(self, y: np.ndarray) -> np.ndarray: """Normalize data to zero mean unit standard deviation. Parameters ---------- y : np.ndarray - Targets for the Gaussian process + Target values for the Gaussian process. Returns ------- - np.ndarray + normalized_y : np.ndarray + Normalized y values. """ self.mean_y_ = np.mean(y) self.std_y_ = np.std(y) + if self.std_y_ == 0: self.std_y_ = 1 + return (y - self.mean_y_) / self.std_y_ def _untransform_y( self, y: np.ndarray, - var: Optional[np.ndarray] = None, - ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: - """Transform zeromean unit standard deviation data into the regular space. + var: np.ndarray | None = None, + ) -> np.ndarray | tuple[np.ndarray, np.ndarray]: + """Transform zero mean unit standard deviation data into the regular space. + Warning + ------- This function should be used after a prediction with the Gaussian process which was trained on normalized data. @@ -75,30 +100,32 @@ def _untransform_y( ---------- y : np.ndarray Normalized data. - var : np.ndarray (optional) - Normalized variance + var : np.ndarray | None, defaults to None + Normalized variance. Returns ------- - np.ndarray on Tuple[np.ndarray, np.ndarray] + untransformed_y : np.ndarray | tuple[np.ndarray, np.ndarray] """ y = y * self.std_y_ + self.mean_y_ if var is not None: var = var * self.std_y_**2 return y, var # type: ignore + return y def _get_all_priors( self, add_bound_priors: bool = True, add_soft_bounds: bool = False, - ) -> List[List[Prior]]: + ) -> list[list[AbstractPrior]]: """Returns all priors.""" # Obtain a list of all priors for each tunable hyperparameter of the kernel all_priors = [] to_visit = [] - to_visit.append(self.gp.kernel.k1) - to_visit.append(self.gp.kernel.k2) + to_visit.append(self._gp.kernel.k1) + to_visit.append(self._gp.kernel.k2) + while len(to_visit) > 0: current_param = to_visit.pop(0) if isinstance(current_param, KernelOperator): @@ -108,40 +135,46 @@ def _get_all_priors( elif isinstance(current_param, Kernel): hps = current_param.hyperparameters assert len(hps) == 1 + hp = hps[0] if hp.fixed: continue + bounds = hps[0].bounds for i in range(hps[0].n_elements): priors_for_hp = [] + if current_param.prior is not None: priors_for_hp.append(current_param.prior) + if add_bound_priors: if add_soft_bounds: priors_for_hp.append( - smac.epm.gaussian_process.utils.prior.SoftTopHatPrior( + SoftTopHatPrior( lower_bound=bounds[i][0], upper_bound=bounds[i][1], - rng=self.rng, + seed=self._rng.randint(0, 2**20), exponent=2, ) ) else: priors_for_hp.append( - smac.epm.gaussian_process.utils.prior.TophatPrior( + TophatPrior( lower_bound=bounds[i][0], upper_bound=bounds[i][1], - rng=self.rng, + seed=self._rng.randint(0, 2**20), ) ) all_priors.append(priors_for_hp) + return all_priors def _set_has_conditions(self) -> None: """Sets `has_conditions` on `current_param`.""" - has_conditions = len(self.configspace.get_conditions()) > 0 + has_conditions = len(self._configspace.get_conditions()) > 0 to_visit = [] - to_visit.append(self.kernel) + to_visit.append(self._kernel) + while len(to_visit) > 0: current_param = to_visit.pop(0) if isinstance(current_param, sklearn.gaussian_process.kernels.KernelOperator): @@ -157,9 +190,5 @@ def _impute_inactive(self, X: np.ndarray) -> np.ndarray: """Imputes inactives.""" X = X.copy() X[~np.isfinite(X)] = -1 - return X - -from smac.epm.gaussian_process.gp import GaussianProcess # noqa - -__all__ = ["BaseModel", "GaussianProcess"] + return X diff --git a/smac/model/gaussian_process/gaussian_process.py b/smac/model/gaussian_process/gaussian_process.py new file mode 100644 index 000000000..9f6aae9dc --- /dev/null +++ b/smac/model/gaussian_process/gaussian_process.py @@ -0,0 +1,301 @@ +from __future__ import annotations + +from typing import Any, Optional, TypeVar, cast + +import logging + +import numpy as np +from ConfigSpace import ConfigurationSpace +from scipy import optimize +from sklearn.gaussian_process import GaussianProcessRegressor +from sklearn.gaussian_process.kernels import Kernel + +from smac.constants import VERY_SMALL_NUMBER +from smac.model.gaussian_process.abstract_gaussian_process import ( + AbstractGaussianProcess, +) +from smac.model.gaussian_process.priors.abstract_prior import AbstractPrior + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = logging.getLogger(__name__) + + +Self = TypeVar("Self", bound="GaussianProcess") + + +class GaussianProcess(AbstractGaussianProcess): + """Implementation of Gaussian process model. The Gaussian process hyperparameters are obtained by optimizing + the marginal log likelihood. + + This code is based on the implementation of RoBO: + Klein, A. and Falkner, S. and Mansur, N. and Hutter, F. + RoBO: A Flexible and Robust Bayesian Optimization Framework in Python + In: NIPS 2017 Bayesian Optimization Workshop + + Parameters + ---------- + configspace : ConfigurationSpace + kernel : Kernel + Kernel which is used for the Gaussian process. + n_restarts : int, defaults to 10 + Number of restarts for the Gaussian process hyperparameter optimization. + normalize_y : bool, defaults to True + Zero mean unit variance normalization of the output values. + instance_features : dict[str, list[int | float]] | None, defaults to None + Features (list of int or floats) of the instances (str). The features are incorporated into the X data, + on which the model is trained on. + pca_components : float, defaults to 7 + Number of components to keep when using PCA to reduce dimensionality of instance features. + seed : int + """ + + def __init__( + self, + configspace: ConfigurationSpace, + kernel: Kernel, + n_restarts: int = 10, + normalize_y: bool = True, + instance_features: dict[str, list[int | float]] | None = None, + pca_components: int | None = 7, + seed: int = 0, + ): + super().__init__( + configspace=configspace, + seed=seed, + kernel=kernel, + instance_features=instance_features, + pca_components=pca_components, + ) + + self._normalize_y = normalize_y + self._n_restarts = n_restarts + + # Internal variables + self._hypers = np.empty((0,)) + self._is_trained = False + self._n_ll_evals = 0 + + self._set_has_conditions() + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update({"n_restarts": self._n_restarts, "normalize_y": self._normalize_y}) + + return meta + + def _train( + self: Self, + X: np.ndarray, + y: np.ndarray, + optimize_hyperparameters: bool = True, + ) -> Self: + """Computes the Cholesky decomposition of the covariance of X and estimates the GP + hyperparameters by optimizing the marginal loglikelihood. The prior mean of the GP is set to + the empirical mean of X. + + Parameters + ---------- + X : np.ndarray [#samples, #hyperparameter + #features] + Input data points. + Y : np.ndarray [#samples, #objectives] + The corresponding target values. + optimize_hyperparameters: boolean + If set to true the hyperparameters are optimized otherwise the default hyperparameters of the kernel are + used. + """ + if self._normalize_y: + y = self._normalize(y) + + X = self._impute_inactive(X) + y = y.flatten() + + n_tries = 10 + for i in range(n_tries): + try: + self._gp = self._get_gaussian_process() + self._gp.fit(X, y) + break + except np.linalg.LinAlgError as e: + if i == n_tries: + raise e + + # Assume that the last entry of theta is the noise + theta = np.exp(self._kernel.theta) + theta[-1] += 1 + self._kernel.theta = np.log(theta) + + if optimize_hyperparameters: + self._all_priors = self._get_all_priors(add_bound_priors=False) + self._hypers = self._optimize() + self._gp.kernel.theta = self._hypers + self._gp.fit(X, y) + else: + self._hypers = self._gp.kernel.theta + + # Set the flag + self._is_trained = True + + return self + + def _get_gaussian_process(self) -> GaussianProcessRegressor: + return GaussianProcessRegressor( + kernel=self._kernel, + normalize_y=False, # We do not use scikit-learn's normalize routine + optimizer=None, + n_restarts_optimizer=-1, # We do not use scikit-learn's optimization routine + alpha=0, # Governed by the kernel + random_state=self._rng, + ) + + def _nll(self, theta: np.ndarray) -> tuple[float, np.ndarray]: + """Returns the negative marginal log likelihood (+ the prior) for a hyperparameter + configuration theta. Negative because we use scipy minimize for optimization. + + Parameters + ---------- + theta : np.ndarray + Hyperparameter vector. Note that all hyperparameter are on a log scale. + """ + self._n_ll_evals += 1 + + try: + lml, grad = self._gp.log_marginal_likelihood(theta, eval_gradient=True) + except np.linalg.LinAlgError: + return 1e25, np.zeros(theta.shape) + + for dim, priors in enumerate(self._all_priors): + for prior in priors: + lml += prior.get_log_probability(theta[dim]) + grad[dim] += prior.get_gradient(theta[dim]) + + # We add a minus here because scipy is minimizing + if not np.isfinite(lml).all() or not np.all(np.isfinite(grad)): + return 1e25, np.zeros(theta.shape) + else: + return -lml, -grad + + def _optimize(self) -> np.ndarray: + """Optimizes the marginal log likelihood and returns the best found hyperparameter + configuration theta. + + Returns + ------- + theta : np.ndarray + Hyperparameter vector that maximizes the marginal log likelihood. + """ + log_bounds = [(b[0], b[1]) for b in self._gp.kernel.bounds] + + # Start optimization from the previous hyperparameter configuration + p0 = [self._gp.kernel.theta] + if self._n_restarts > 0: + dim_samples = [] + + prior: list[AbstractPrior] | AbstractPrior | None = None + for dim, hp_bound in enumerate(log_bounds): + prior = self._all_priors[dim] + # Always sample from the first prior + if isinstance(prior, list): + if len(prior) == 0: + prior = None + else: + prior = prior[0] + + prior = cast(Optional[AbstractPrior], prior) + if prior is None: + try: + sample = self._rng.uniform( + low=hp_bound[0], + high=hp_bound[1], + size=(self._n_restarts,), + ) + except OverflowError: + raise ValueError("OverflowError while sampling from (%f, %f)" % (hp_bound[0], hp_bound[1])) + + dim_samples.append(sample.flatten()) + else: + dim_samples.append(prior.sample_from_prior(self._n_restarts).flatten()) + p0 += list(np.vstack(dim_samples).transpose()) + + theta_star: np.ndarray | None = None + f_opt_star = np.inf + for i, start_point in enumerate(p0): + theta, f_opt, _ = optimize.fmin_l_bfgs_b(self._nll, start_point, bounds=log_bounds) + if f_opt < f_opt_star: + f_opt_star = f_opt + theta_star = theta + + if theta_star is None: + raise RuntimeError + + return theta_star + + def _predict( + self, + X: np.ndarray, + covariance_type: str | None = "diagonal", + ) -> tuple[np.ndarray, np.ndarray | None]: + if not self._is_trained: + raise Exception("Model has to be trained first!") + + X_test = self._impute_inactive(X) + + if covariance_type is None: + mu = self._gp.predict(X_test) + var = None + + if self._normalize_y: + mu = self._untransform_y(mu) + else: + predict_kwargs = {"return_cov": False, "return_std": True} + if covariance_type == "full": + predict_kwargs = {"return_cov": True, "return_std": False} + + mu, var = self._gp.predict(X_test, **predict_kwargs) + + if covariance_type != "full": + var = var**2 # Since we get standard deviation for faster computation + + # Clip negative variances and set them to the smallest + # positive float value + var = np.clip(var, VERY_SMALL_NUMBER, np.inf) + + if self._normalize_y: + mu, var = self._untransform_y(mu, var) + + if covariance_type == "std": + var = np.sqrt(var) # Converting variance to std deviation if specified + + return mu, var + + def sample_functions(self, X_test: np.ndarray, n_funcs: int = 1) -> np.ndarray: + """Samples F function values from the current posterior at the N specified test points. + + Parameters + ---------- + X : np.ndarray [#samples, #hyperparameter + #features] + Input data points. + n_funcs: int + Number of function values that are drawn at each test point. + + Returns + ------- + function_samples : np.array + The F function values drawn at the N test points. + """ + if not self._is_trained: + raise Exception("Model has to be trained first.") + + X_test = self._impute_inactive(X_test) + funcs = self._gp.sample_y(X_test, n_samples=n_funcs, random_state=self._rng) + + if self._normalize_y: + funcs = self._untransform_y(funcs) + + if len(funcs.shape) == 1: + return funcs[None, :] + else: + return funcs diff --git a/smac/model/gaussian_process/gpytorch_gaussian_process.py b/smac/model/gaussian_process/gpytorch_gaussian_process.py new file mode 100644 index 000000000..6ac60d2c1 --- /dev/null +++ b/smac/model/gaussian_process/gpytorch_gaussian_process.py @@ -0,0 +1,874 @@ +# from __future__ import annotations + +# import logging +# import warnings +# from collections import OrderedDict +# from typing import Any, TypeVar + + +# import gpytorch +# import numpy as np +# from smac.model.gaussian_process.utils.gpytorch import ExactGaussianProcessModel +# import torch +# from botorch.optim.numpy_converter import module_to_array, set_params_with_array # noqa +# from botorch.optim.utils import _get_extra_mll_args, _scipy_objective_and_grad # noqa +# from gpytorch.constraints.constraints import Interval # noqa +# from gpytorch.distributions.multivariate_normal import MultivariateNormal # noqa +# from gpytorch.kernels import Kernel # noqa +# from gpytorch.likelihoods import FixedNoiseGaussianLikelihood, GaussianLikelihood # noqa +# from gpytorch.means import ZeroMean # noqa +# from gpytorch.mlls import ExactMarginalLogLikelihood # noqa +# from gpytorch.models import ExactGP # noqa +# from gpytorch.priors import HorseshoePrior # noqa +# from gpytorch.utils.errors import NanError, NotPSDError # noqa +# from scipy import optimize +# from scipy.stats.qmc import LatinHypercube # noqa +# import logging +# import warnings +# from collections import OrderedDict +# from typing import Any, TypeVar + +# """ +# import gpytorch +# import numpy as np +# import torch +# from botorch.optim.numpy_converter import module_to_array, set_params_with_array +# from botorch.optim.utils import _get_extra_mll_args, _scipy_objective_and_grad +# from gpytorch.constraints.constraints import Interval +# from gpytorch.distributions.multivariate_normal import MultivariateNormal +# from gpytorch.kernels import Kernel +# from gpytorch.likelihoods import FixedNoiseGaussianLikelihood, GaussianLikelihood +# from gpytorch.means import ZeroMean +# from gpytorch.mlls import ExactMarginalLogLikelihood +# from gpytorch.models import ExactGP +# from gpytorch.priors import HorseshoePrior +# from gpytorch.utils.errors import NanError, NotPSDError +# from scipy import optimize +# from scipy.stats.qmc import LatinHypercube +# """ + +# from ConfigSpace import ConfigurationSpace +# from smac.constants import VERY_SMALL_NUMBER +# from smac.model.gaussian_process.abstract_gaussian_process import AbstractGaussianProcess +# from smac.model.utils import check_subspace_points +# from smac.model.gaussian_process.kernels._boing import FITCKernel, FITCMean + + +# # from smac.model.utils import check_subspace_points # noqa +# from smac.model.gaussian_process.kernels._boing import FITCKernel, FITCMean # noqa + +# warnings.filterwarnings("ignore", module="gpytorch") + +# logger = logging.getLogger(__name__) + + +# Self = TypeVar("Self", bound="GPyTorchGaussianProcess") + + +# class GPyTorchGaussianProcess(AbstractGaussianProcess): +# """A Gaussian process written with GPyTorch. The interface is written to be compatible with partial sparse +# gaussian +# process. + +# Parameters +# ---------- +# configspace : ConfigurationSpace +# kernel : Kernel +# Kernel which is used for the Gaussian process. +# n_restarts : int, defaults to 10 +# Number of restarts for the Gaussian process hyperparameter optimization. +# normalize_y : bool, defaults to True +# Zero mean unit variance normalization of the output values. +# likelihood : FixedNoiseGaussianLikelihood | None, defaults to None +# The Gaussian likelihood (or noise). +# instance_features : dict[str, list[int | float]] | None, defaults to None +# Features (list of int or floats) of the instances (str). The features are incorporated into the X data, +# on which the model is trained on. +# pca_components : float, defaults to 7 +# Number of components to keep when using PCA to reduce dimensionality of instance features. +# seed : int +# """ + +# def __init__( +# self, +# configspace: ConfigurationSpace, +# kernel: Kernel, +# n_restarts: int = 10, +# normalize_y: bool = True, +# likelihood: FixedNoiseGaussianLikelihood | None = None, +# instance_features: dict[str, list[int | float]] | None = None, +# pca_components: int | None = 7, +# seed: int = 0, +# ): +# if n_restarts <= 0: +# raise ValueError("The argument `n_restarts` needs to be positive.") + +# super(GPyTorchGaussianProcess, self).__init__( +# configspace=configspace, +# kernel=kernel, +# instance_features=instance_features, +# pca_components=pca_components, +# seed=seed, +# ) + +# if likelihood is None: +# noise_prior = HorseshoePrior(0.1) +# likelihood = GaussianLikelihood( +# noise_prior=noise_prior, +# noise_constraint=Interval( +# np.exp(-25), +# np.exp(2), +# transform=None, +# ), +# ).double() + +# self._likelihood = likelihood +# self._normalize_y = normalize_y +# self._n_restarts = n_restarts +# self._hypers = np.empty((0,)) +# self._property_dict: OrderedDict = OrderedDict() +# self._is_trained = False + +# @property +# def meta(self) -> dict[str, Any]: # noqa: D102 +# meta = super().meta +# meta.update( +# { +# "n_restarts": self._n_restarts, +# "normalize_y": self._normalize_y, +# "likelihood": self._likelihood.__data__, +# } +# ) + +# return meta + +# def _train( +# self: Self, +# X: np.ndarray, +# y: np.ndarray, +# optimize_hyperparameters: bool = True, +# ) -> Self: +# """Computes the Cholesky decomposition of the covariance of X and estimates the GP +# hyperparameters by optimizing the marginal loglikelihood. The prior mean of the GP is set to +# the empirical mean of X. + +# Parameters +# ---------- +# X : np.ndarray [#samples, #hyperparameter + #features] +# Input data points. +# Y : np.ndarray [#samples, #objectives] +# The corresponding target values. +# optimize_hyperparameters: boolean +# If set to true the hyperparameters are optimized otherwise the default hyperparameters of the kernel are +# used. +# """ +# X = self._impute_inactive(X) + +# if len(y.shape) == 1: +# self._n_objectives = 1 +# else: +# self._n_objectives = y.shape[1] + +# if self._n_objectives == 1: +# y = y.flatten() + +# if self._normalize_y: +# y = self._normalize(y) + +# n_tries = 10 +# for i in range(n_tries): +# try: +# self._gp = self._get_gaussian_process(X, y) +# break +# except Exception as e: +# if i == n_tries - 1: +# # To avoid Endless loop, we need to stop it when we have n_tries unsuccessful tries. +# raise e + +# if optimize_hyperparameters: +# self._hypers = self._optimize() +# self._gp = set_params_with_array(self._gp, self._hypers, self._property_dict) +# else: +# self._hypers, self._property_dict, _ = module_to_array(module=self._gp) + +# self._is_trained = True +# return self + +# def _get_gaussian_process( +# self, X: np.ndarray | None = None, y: np.ndarray | None = None +# ) -> ExactMarginalLogLikelihood | None: +# """ +# Get the GP model with the given X and y values. As GPyTorch requires the input data to initialize a new +# model, we also pass X and y here. X and y are set optional to ensure compatibility. + +# Parameters +# ---------- +# X : np.ndarray [#samples, #hyperparameter + #features] +# Input data points. +# Y : np.ndarray [#samples, #objectives] +# The corresponding target values. + +# Returns +# ------- +# mll : ExactMarginalLogLikelihood | None +# A GPyTorch model with zero mean and user specified covariance. +# """ +# if X is None: +# # To be compatible with the base model +# return None + +# X = torch.from_numpy(X) +# y = torch.from_numpy(y) +# self._gp_model = ExactGaussianProcessModel( +# X, y, likelihood=self._likelihood, base_covar_kernel=self._kernel +# ).double() + +# mll = ExactMarginalLogLikelihood(self._likelihood, self._gp_model) +# mll.double() + +# return mll + +# def _optimize(self) -> np.ndarray: +# """Optimizes the marginal log likelihood and returns the best found hyperparameter +# configuration theta. + +# Returns +# ------- +# theta : np.ndarray +# Hyperparameter vector that maximizes the marginal log likelihood. +# """ +# x0, property_dict, bounds = module_to_array(module=self._gp) + +# bounds = np.asarray(bounds).transpose().tolist() + +# self._property_dict = property_dict + +# p0 = [x0] + +# # Avoid infinite sampling +# n_tries = 5000 +# for i in range(n_tries): +# try: +# gp_model = self._gp.pyro_sample_from_prior() +# x_out = [] +# for key in property_dict.keys(): +# param = gp_model +# param_names = key.split(".") +# for name in param_names: +# param = getattr(param, name) +# x_out.append(param.detach().view(-1).cpu().double().clone().numpy()) +# sample = np.concatenate(x_out) +# p0.append(sample.astype(np.float64)) +# except Exception as e: +# if i == n_tries - 1: +# logger.debug(f"Fails to sample new hyperparameters because of {e}.") +# raise e +# continue +# if len(p0) == self._n_restarts: +# break + +# self._gp_model.train() +# self._likelihood.train() + +# theta_star = x0 +# f_opt_star = np.inf +# for i, start_point in enumerate(p0): +# try: +# theta, f_opt, _ = optimize.fmin_l_bfgs_b( +# _scipy_objective_and_grad, +# start_point, +# args=(self._gp, property_dict), +# bounds=bounds, +# ) +# except NotPSDError as e: +# logger.warning(f"Fail to optimize the GP hyperparameters as an Error occurs: {e}.") +# f_opt = np.inf +# theta = start_point + +# if f_opt < f_opt_star: +# f_opt_star = f_opt +# theta_star = theta + +# return theta_star + +# def _predict( +# self, +# X: np.ndarray, +# covariance_type: str | None = "diagonal", +# ) -> tuple[np.ndarray, np.ndarray | None]: +# if not self._is_trained: +# raise Exception("Model has to be trained first.") + +# X_test = torch.from_numpy(self._impute_inactive(X)) +# self._likelihood.eval() +# self._gp_model.eval() + +# with torch.no_grad(), gpytorch.settings.fast_pred_var(): + +# observed_pred = self._likelihood(self._gp_model(X_test)) + +# mu = observed_pred.mean.numpy() +# if covariance_type is None: +# var = None + +# if self._normalize_y: +# mu = self._untransform_y(mu) + +# else: +# if covariance_type != "full": +# var = observed_pred.stddev.numpy() +# var = var**2 # since we get standard deviation for faster computation +# else: +# # output full covariance +# var = observed_pred.covariance_matrix.numpy() + +# # Clip negative variances and set them to the smallest +# # positive float value +# var = np.clip(var, VERY_SMALL_NUMBER, np.inf) + +# if self._normalize_y: +# mu, var = self._untransform_y(mu, var) + +# if covariance_type == "diagonal": +# var = np.sqrt(var) # converting variance to std deviation if specified + +# return mu, var + +# def sample_functions(self, X_test: np.ndarray, n_funcs: int = 1) -> np.ndarray: +# """Samples F function values from the current posterior at the N specified test points. + +# Parameters +# ---------- +# X : np.ndarray [#samples, #hyperparameter + #features] +# Input data points. +# n_funcs: int +# Number of function values that are drawn at each test point. + +# Returns +# ------- +# function_samples : np.array +# The F function values drawn at the N test points. +# """ +# if not self._is_trained: +# raise Exception("Model has to be trained first!") +# self._likelihood.eval() +# self._gp_model.eval() + +# X_test = torch.from_numpy(self._impute_inactive(X_test)) +# with torch.no_grad(): +# funcs = self._likelihood(self._gp_model(X_test)).sample(torch.Size([n_funcs])).t().cpu().numpy() + +# if self._normalize_y: +# funcs = self._untransform_y(funcs) + +# if len(funcs.shape) == 1: +# return funcs[None, :] +# else: +# return funcs + + +# class GloballyAugmentedLocalGaussianProcess(GPyTorchGaussianProcess): +# def __init__( +# self, +# configspace: ConfigurationSpace, +# bounds_cont: np.ndarray, +# bounds_cat: list[tuple], +# kernel: Kernel, +# num_inducing_points: int = 2, +# likelihood: GaussianLikelihood | None = None, +# normalize_y: bool = True, +# n_restarts: int = 10, +# instance_features: dict[str, list[int | float]] | None = None, +# pca_components: int | None = 7, +# seed: int = 0, +# ): +# """ +# The GP hyperparameters are obtained by optimizing the marginal log-likelihood and optimized with botorch +# We train an LGPGA in two stages: +# In the first stage, we only train the kernel hyperparameter and thus deactivate the gradient w.r.t the +# position +# of the inducing points. +# In the second stage, we use the kernel hyperparameter acquired in the first stage to initialize a new +# variational Gaussian process and only optimize its inducing points' position with natural gradients. +# Finally, we update the position of the inducing points and use it for evaluation. + + +# Parameters +# ---------- +# bounds_cont: np.ndarray(N_cont, 2), +# bounds of the continuous hyperparameters, store as [[0,1] * N_cont] +# bounds_cat: List[Tuple], +# bounds of categorical hyperparameters +# kernel : gpytorch kernel object +# Specifies the kernel that is used for all Gaussian Process +# num_inducing_points: int +# Number of inducing points +# likelihood: Optional[GaussianLikelihood] +# Likelihood values +# normalize_y : bool +# Zero mean unit variance normalization of the output values when the model is a partial sparse GP model. +# """ +# super(GloballyAugmentedLocalGaussianProcess, self).__init__( +# configspace=configspace, +# kernel=kernel, +# likelihood=likelihood, +# normalize_y=normalize_y, +# n_restarts=n_restarts, +# instance_features=instance_features, +# pca_components=pca_components, +# seed=seed, +# ) +# self.cont_dims = np.where(np.array(self.types) == 0)[0] +# self.cat_dims = np.where(np.array(self.types) != 0)[0] +# self.bounds_cont = bounds_cont +# self.bounds_cat = bounds_cat +# self.num_inducing_points = num_inducing_points + +# def update_attribute(self, **kwargs: Any) -> None: +# """We update the class attribute (for instance, number of inducing points)""" +# for key in kwargs: +# if not hasattr(self, key): +# raise AttributeError(f"{self.__class__.__name__} has no attribute named {key}") +# setattr(self, key, kwargs[key]) + +# def _train( +# self, X: np.ndarray, y: np.ndarray, do_optimize: bool = True +# ) -> AugmentedLocalGaussianProcess | GPyTorchGaussianProcess: +# """ +# Update the hyperparameters of the partial sparse kernel. Depending on the number of inputs inside and +# outside the subregion, we initialize a PartialSparseGaussianProcess or a GaussianProcessGPyTorch + +# Parameters +# ---------- +# X: np.ndarray (N, D) +# Input data points. The dimensionality of X is (N, D), +# with N as the number of points and D is the number of features., N = N_in + N_out +# y: np.ndarray (N,) +# The corresponding target values. +# do_optimize: boolean +# If set to true, the hyperparameters are optimized otherwise, +# the default hyperparameters of the kernel are used. +# """ +# X = self._impute_inactive(X) +# if len(y.shape) == 1: +# self._n_objectives = 1 +# else: +# self._n_objectives = y.shape[1] +# if self._n_objectives == 1: +# y = y.flatten() + +# ss_data_indices = check_subspace_points( +# X, +# cont_dims=self.cont_dims, +# cat_dims=self.cat_dims, +# bounds_cont=self.bounds_cont, +# bounds_cat=self.bounds_cat, +# expand_bound=True, +# ) + +# if np.sum(ss_data_indices) > np.shape(y)[0] - self.num_inducing_points: +# # we initialize a vanilla GaussianProcessGPyTorch +# if self._normalize_y: +# y = self._normalize_y(y) +# self.num_points = np.shape(y)[0] +# get_gp_kwargs = {"X_in": X, "y_in": y, "X_out": None, "y_out": None} +# else: +# # we initialize a PartialSparseGaussianProcess object +# X_in = X[ss_data_indices] +# y_in = y[ss_data_indices] +# X_out = X[~ss_data_indices] +# y_out = y[~ss_data_indices] +# self.num_points = np.shape(y_in)[0] +# if self._normalize_y: +# y_in = self._normalize_y(y_in) +# y_out = (y_out - self.mean_y_) / self.std_y_ +# get_gp_kwargs = {"X_in": X_in, "y_in": y_in, "X_out": X_out, "y_out": y_out} + +# n_tries = 10 + +# for i in range(n_tries): +# try: +# self._gp = self._get_gaussian_process(**get_gp_kwargs) +# break +# except Exception as e: +# if i == n_tries - 1: +# raise RuntimeError(f"Fails to initialize a GP model, {e}") + +# if do_optimize: +# self._hypers = self._optimize() +# self._gp = set_params_with_array(self._gp, self._hypers, self._property_dict) +# if isinstance(self._gp.model, AugmentedLocalGaussianProcess): +# # we optimize the position of the inducing points and thus needs to deactivate the gradient of kernel +# # hyperparameters +# lhd = LatinHypercube(d=X.shape[-1], seed=self.rng.randint(0, 1000000)) + +# inducing_points = torch.from_numpy(lhd.random(n=self.num_inducing_points)) + +# kernel = self._gp.model.base_covar +# var_gp = VariationalGaussianProcess(kernel, X_inducing=inducing_points) + +# X_out_ = torch.from_numpy(X_out) +# y_out_ = torch.from_numpy(y_out) + +# variational_ngd_optimizer = gpytorch.optim.NGD( +# var_gp.variational_parameters(), num_data=y_out_.size(0), lr=0.1 +# ) + +# var_gp.train() +# likelihood = GaussianLikelihood().double() +# likelihood.train() + +# mll_func = gpytorch.mlls.PredictiveLogLikelihood + +# var_mll = mll_func(likelihood, var_gp, num_data=y_out_.size(0)) + +# for t in var_gp.variational_parameters(): +# t.requires_grad = False + +# x0, property_dict, bounds = module_to_array(module=var_mll) +# for t in var_gp.variational_parameters(): +# t.requires_grad = True +# bounds = np.asarray(bounds).transpose().tolist() + +# start_points = [x0] + +# inducing_idx = 0 + +# inducing_size = X_out.shape[-1] * self.num_inducing_points +# for p_name, attrs in property_dict.items(): +# if p_name != "model.variational_strategy.inducing_points": +# # Construct the new tensor +# if len(attrs.shape) == 0: # deal with scalar tensors +# inducing_idx = inducing_idx + 1 +# else: +# inducing_idx = inducing_idx + np.prod(attrs.shape) +# else: +# break +# while len(start_points) < 3: +# new_start_point = np.random.rand(*x0.shape) +# new_inducing_points = torch.from_numpy(lhd.random(n=self.num_inducing_points)).flatten() +# new_start_point[inducing_idx : inducing_idx + inducing_size] = new_inducing_points +# start_points.append(new_start_point) + +# def sci_opi_wrapper( +# x: np.ndarray, +# mll: gpytorch.module, +# property_dict: dict, +# train_inputs: torch.Tensor, +# train_targets: torch.Tensor, +# ) -> tuple[float, np.ndarray]: +# """ +# A modification of from botorch.optim.utils._scipy_objective_and_grad, the key difference is that +# we do an additional natural gradient update before computing the gradient values +# Parameters +# ---------- +# x: np.ndarray +# optimizer input +# mll: gpytorch.module +# a gpytorch module whose hyperparameters are defined by x +# property_dict: Dict +# a dict describing how x is mapped to initialize mll +# train_inputs: torch.Tensor (N_input, D) +# input points of the GP model +# train_targets: torch.Tensor (N_input, 1) +# target value of the GP model +# Returns +# ---------- +# loss: np.ndarray +# loss value +# grad: np.ndarray +# gradient w.r.t. the inputs +# ---------- +# """ +# # A modification of from botorch.optim.utils._scipy_objective_and_grad: +# # https://botorch.org/api/_modules/botorch/optim/utils.html +# # The key difference is that we do an additional natural gradient update here +# variational_ngd_optimizer.zero_grad() + +# mll = set_params_with_array(mll, x, property_dict) +# mll.zero_grad() +# try: # catch linear algebra errors in gpytorch +# output = mll.model(train_inputs) +# args = [output, train_targets] + _get_extra_mll_args(mll) +# loss = -mll(*args).sum() +# except RuntimeError as e: +# if isinstance(e, NanError) or "singular" in e.args[0]: +# return float("nan"), np.full_like(x, "nan") +# else: +# raise e # pragma: nocover +# loss.backward() +# variational_ngd_optimizer.step() +# param_dict = OrderedDict(mll.named_parameters()) +# grad = [] +# for p_name in property_dict: +# t = param_dict[p_name].grad +# if t is None: +# # this deals with parameters that do not affect the loss +# grad.append(np.zeros(property_dict[p_name].shape.numel())) +# else: +# grad.append(t.detach().view(-1).cpu().double().clone().numpy()) +# mll.zero_grad() +# return loss.item(), np.concatenate(grad) + +# theta_star = x0 +# f_opt_star = np.inf +# for start_point in start_points: +# try: +# theta, f_opt, res_dict = optimize.fmin_l_bfgs_b( +# sci_opi_wrapper, +# start_point, +# args=(var_mll, property_dict, X_out_, y_out_), +# bounds=bounds, +# maxiter=50, +# ) +# if f_opt < f_opt_star: +# f_opt_star = f_opt +# theta_star = theta +# except Exception as e: +# logger.warning(f"An exception {e} occurs during the optimizaiton") + +# start_idx = 0 +# # modification on botorch.optim.numpy_converter.set_params_with_array as we only need to extract the +# # positions of inducing points +# for p_name, attrs in property_dict.items(): +# if p_name != "model.variational_strategy.inducing_points": +# # Construct the new tensor +# if len(attrs.shape) == 0: # deal with scalar tensors +# start_idx = start_idx + 1 +# else: +# start_idx = start_idx + np.prod(attrs.shape) +# else: +# end_idx = start_idx + np.prod(attrs.shape) +# X_inducing = torch.tensor( +# theta_star[start_idx:end_idx], dtype=attrs.dtype, device=attrs.device +# ).view(*attrs.shape) +# break +# # set inducing points for covariance module here +# self._gp_model.set_augment_module(X_inducing) +# else: +# self._hypers, self._property_dict, _ = module_to_array(module=self._gp) + +# self._is_trained = True +# return self + +# def _get_gaussian_process( +# self, +# X_in: np.ndarray | None = None, +# y_in: np.ndarray | None = None, +# X_out: np.ndarray | None = None, +# y_out: np.ndarray | None = None, +# ) -> ExactMarginalLogLikelihood | None: +# """ +# Construct a new GP model based on the inputs +# If both in and out are None: return an empty model +# If only in_x and in_y are given: return a vanilla GP model +# If in_x, in_y, out_x, out_y are given: return a partial sparse GP model. + +# Parameters +# ---------- +# X_in: np.ndarray (N_in, D) | None +# Input data points inside the subregion. The dimensionality of X_in is (N_in, D), +# with N_in as the number of points inside the subregion and D is the number of features. If it is not +# given, +# this function will return None to be compatible with the implementation of its parent class +# y_in: np.ndarray (N_in,) | None +# The corresponding target values inside the subregion. +# X_out: np.ndarray (N_out, D) | None +# Input data points outside the subregion. The dimensionality of X_out is (N_out, D). If it is not given, +# this +# function will return a vanilla Gaussian Process +# y_out: np.ndarray (N_out) | None +# The corresponding target values outside the subregion. + +# Returns +# ------- +# mll: ExactMarginalLogLikelihood +# a gp module +# """ +# if X_in is None: +# return None + +# X_in = torch.from_numpy(X_in) +# y_in = torch.from_numpy(y_in) +# if X_out is None: +# self._gp_model = ExactGaussianProcessModel( +# X_in, y_in, likelihood=self._likelihood, base_covar_kernel=self.kernel +# ).double() +# else: +# X_out = torch.from_numpy(X_out) +# y_out = torch.from_numpy(y_out) + +# self._gp_model = AugmentedLocalGaussianProcess( +# X_in, y_in, X_out, y_out, likelihood=self._likelihood, base_covar_kernel=self.kernel # type:ignore +# ).double() +# mll = ExactMarginalLogLikelihood(self._likelihood, self._gp_model) +# mll.double() +# return mll + + +# class ExactGaussianProcessModel(ExactGP): +# """Exact GP model that serves as a backbone for `GPyTorchGaussianProcess`." + +# Parameters +# ---------- +# train_X: torch.tenor +# input feature +# train_y: torch.tensor +# input observations +# base_covar_kernel: Kernel +# covariance kernel used to compute covariance matrix +# likelihood: GaussianLikelihood +# GP likelihood +# """ + +# def __init__( +# self, +# train_X: torch.Tensor, +# train_y: torch.Tensor, +# base_covar_kernel: Kernel, +# likelihood: GaussianLikelihood, +# ): +# super(ExactGaussianProcessModel, self).__init__(train_X, train_y, likelihood) + +# # In our experiments we find that ZeroMean more robust than ConstantMean when y is normalized +# self._mean_module = ZeroMean() +# self._covar_module = base_covar_kernel + +# def forward(self, x: torch.Tensor) -> MultivariateNormal: +# """Computes the posterior mean and variance.""" +# mean_x = self._mean_module(x) +# covar_x = self._covar_module(x) + +# return MultivariateNormal(mean_x, covar_x) + + +# class AugmentedLocalGaussianProcess(ExactGP): +# def __init__( +# self, +# X_in: torch.Tensor, +# y_in: torch.Tensor, +# X_out: torch.Tensor, +# y_out: torch.Tensor, +# likelihood: GaussianLikelihood, +# base_covar_kernel: Kernel, +# ): +# """ +# An Augmented Local GP, it is trained with the points inside a subregion while its prior is augemented by the +# points outside the subregion (global configurations) + +# Parameters +# ---------- +# X_in: torch.Tensor (N_in, D), +# feature vector of the points inside the subregion +# y_in: torch.Tensor (N_in, 1), +# observation inside the subregion +# X_out: torch.Tensor (N_out, D), +# feature vector of the points outside the subregion +# y_out:torch.Tensor (N_out, 1), +# observation inside the subregion +# likelihood: GaussianLikelihood, +# likelihood of the GP (noise) +# base_covar_kernel: Kernel, +# Covariance Kernel +# """ +# X_in = X_in.unsqueeze(-1) if X_in.ndimension() == 1 else X_in +# X_out = X_out.unsqueeze(-1) if X_out.ndimension() == 1 else X_out +# assert X_in.shape[-1] == X_out.shape[-1] + +# super(AugmentedLocalGaussianProcess, self).__init__(X_in, y_in, likelihood) + +# self._mean_module = ZeroMean() +# self.base_covar = base_covar_kernel + +# self.X_out = X_out +# self.y_out = y_out +# self.augmented = False + +# def set_augment_module(self, X_inducing: torch.Tensor) -> None: +# """ +# Set an augmentation module, which will be used later for inference + +# Parameters +# ---------- +# X_inducing: torch.Tensor(N_inducing, D) +# inducing points, it needs to have the same number of dimensions as X_in +# """ +# X_inducing = X_inducing.unsqueeze(-1) if X_inducing.ndimension() == 1 else X_inducing +# # assert X_inducing.shape[-1] == self.X_out.shape[-1] +# self.covar_module = FITCKernel( +# self.base_covar, X_inducing=X_inducing, X_out=self.X_out, y_out=self.y_out, likelihood=self._likelihood +# ) +# self.mean_module = FITCMean(covar_module=self.covar_module) +# self.augmented = True + +# def forward(self, x: torch.Tensor) -> MultivariateNormal: +# """ +# Compute the prior values. If optimize_kernel_hps is set True in the training phases, this model degenerates to +# a vanilla GP model with ZeroMean and base_covar as covariance matrix. Otherwise, we apply partial sparse GP +# mean and kernels here. +# """ +# if not self.augmented: +# # we only optimize for kernel hyperparameters +# covar_x = self.base_covar(x) +# mean_x = self._mean_module(x) +# else: +# covar_x = self.covar_module(x) +# mean_x = self.mean_module(x) +# return MultivariateNormal(mean_x, covar_x) + + +# class VariationalGaussianProcess(gpytorch.models.ApproximateGP): +# """ +# A variational GP to compute the position of the inducing points. +# We only optimize for the position of the continuous dimensions and keep the categorical dimensions constant. +# """ + +# def __init__(self, kernel: Kernel, X_inducing: torch.Tensor): +# """ +# Initialize a Variational GP +# we set the lower bound and upper bounds of inducing points for numerical hyperparameters between 0 and 1, +# that is, we constrain the inducing points to lay inside the subregion. + +# Parameters +# ---------- +# kernel: Kernel +# kernel of the variational GP, its hyperparameter needs to be fixed when it is by LGPGA +# X_inducing: torch.tensor (N_inducing, D) +# inducing points +# """ +# variational_distribution = gpytorch.variational.TrilNaturalVariationalDistribution(X_inducing.size(0)) +# variational_strategy = gpytorch.variational.VariationalStrategy( +# self, X_inducing, variational_distribution, learn_inducing_locations=True +# ) +# super(VariationalGaussianProcess, self).__init__(variational_strategy) +# self.mean_module = gpytorch.means.ZeroMean() +# self.covar_module = kernel + +# shape_X_inducing = X_inducing.shape +# lower_X_inducing = torch.zeros([shape_X_inducing[-1]]).repeat(shape_X_inducing[0]) +# upper_X_inducing = torch.ones([shape_X_inducing[-1]]).repeat(shape_X_inducing[0]) + +# self.variational_strategy.register_constraint( +# param_name="inducing_points", +# constraint=Interval(lower_X_inducing, upper_X_inducing, transform=None), +# ) +# self.double() + +# for p_name, t in self.named_hyperparameters(): +# if p_name != "variational_strategy.inducing_points": +# t.requires_grad = False + +# def forward(self, x: torch.Tensor) -> MultivariateNormal: +# """ +# Pass the posterior mean and variance given input X + +# Parameters +# ---------- +# x: torch.Tensor +# Input data +# Returns +# ------- +# """ +# mean_x = self.mean_module(x) +# covar_x = self.covar_module(x, cont_only=True) +# return MultivariateNormal(mean_x, covar_x) diff --git a/smac/model/gaussian_process/kernels/__init__.py b/smac/model/gaussian_process/kernels/__init__.py new file mode 100644 index 000000000..2156bd115 --- /dev/null +++ b/smac/model/gaussian_process/kernels/__init__.py @@ -0,0 +1,21 @@ +from smac.model.gaussian_process.kernels.base_kernels import ( + AbstractKernel, + ConstantKernel, + ProductKernel, + SumKernel, +) +from smac.model.gaussian_process.kernels.hamming_kernel import HammingKernel +from smac.model.gaussian_process.kernels.matern_kernel import MaternKernel +from smac.model.gaussian_process.kernels.rbf_kernel import RBFKernel +from smac.model.gaussian_process.kernels.white_kernel import WhiteKernel + +__all__ = [ + "ConstantKernel", + "SumKernel", + "ProductKernel", + "HammingKernel", + "AbstractKernel", + "WhiteKernel", + "MaternKernel", + "RBFKernel", +] diff --git a/smac/model/gaussian_process/kernels/_boing.py b/smac/model/gaussian_process/kernels/_boing.py new file mode 100644 index 000000000..288c28462 --- /dev/null +++ b/smac/model/gaussian_process/kernels/_boing.py @@ -0,0 +1,567 @@ +# from __future__ import annotations + +# from typing import Any, Dict, Tuple + +# import copy +# import math + +# import gpytorch +# import numpy as np +# import torch +# from gpytorch import settings +# from gpytorch.kernels import Kernel, MaternKernel, ProductKernel, ScaleKernel +# from gpytorch.lazy import ( +# DiagLazyTensor, +# MatmulLazyTensor, +# PsdSumLazyTensor, +# RootLazyTensor, +# delazify, +# ) +# from gpytorch.likelihoods import GaussianLikelihood +# from gpytorch.means.mean import Mean +# from gpytorch.utils.cholesky import psd_safe_cholesky +# from sklearn.gaussian_process.kernels import Kernel as SKLKernels + +# from smac.model.gaussian_process.kernels import ConstantKernel, WhiteKernel + + +# class MixedKernel(ProductKernel): +# """ +# A special form of ProductKernel. It is composed of a cont_kernel and a cat_kernel that work with continuous and +# categorical parameters, respectively. Its forward pass allows an additional parameter to determine if only +# cont_kernel is applied to the input. +# """ + +# def __init__(self, cont_kernel: Kernel, cat_kernel: Kernel): +# kernels = cont_kernel.kernels if isinstance(cont_kernel, ProductKernel) else [cont_kernel] +# kernels += cat_kernel.kernels if isinstance(cat_kernel, ProductKernel) else [cat_kernel] +# super().__init__(*kernels) +# self.cont_kernel = cont_kernel +# self.cat_kernel = cat_kernel + +# def forward( +# self, x1: torch.Tensor, x2: torch.Tensor, diag: bool = False, cont_only: bool = False, **params: Any +# ) -> gpytorch.lazy.LazyTensor: +# """Compute kernel values, if cont_only is True, then the categorical kernel is omitted""" +# if not cont_only: +# return super().forward(x1, x2, diag, **params) +# else: +# return self.cont_kernel(x1, x2, diag, **params) + + +# def construct_gp_kernel( +# kernel_kwargs: Dict[str, Any], cont_dims: np.ndarray, cat_dims: np.ndarray +# ) -> Kernel | SKLKernels: +# """ +# Construct a GP kernel with the given kernel init argument, the cont_dims, and cat_dims of the problem. Since the +# subspace might not have the same number of dimensions as the global search space. +# We need to reconstruct the kernel every time when a new subspace is generated. + +# Parameters +# ---------- +# kernel_kwargs: Dict[str, Any] +# kernel kwargs. Arguments to initialize the kernels. It needs to contain the following items: +# cont_kernel: type of continuous kernels +# cont_kernel_kwargs: additional arguments for continuous kernels, for instance, length constraints and +# prior +# cat_kernel: type of categorical kernels +# cat_kernel_kwargs: additional arguments for categorical kernels, for instance, length constraints and +# prior +# scale_kernel: type of scale kernels +# scale_kernel_kwargs: additional arguments for scale kernels, for instance, length constraints and prior +# cont_dims: np.ndarray +# dimensions of continuous hyperparameters +# cat_dims: np.ndarray +# dimensions of categorical hyperparameters +# Returns +# ------- +# kernel: Kernel | SKLKernels +# constructed kernels + +# """ +# if len(cont_dims) > 0: +# cont_kernel_class = kernel_kwargs.get("cont_kernel", MaternKernel) +# cont_kernel_kwargs = kernel_kwargs.get("cont_kernel_kwargs", {}) +# cont_kernel = cont_kernel_class( +# ard_num_dims=cont_dims.shape[-1], active_dims=tuple(cont_dims), **cont_kernel_kwargs +# ).double() + +# if len(cat_dims) > 0: +# cat_kernel_class = kernel_kwargs.get("cat_kernel", MaternKernel) +# cat_kernel_kwargs = kernel_kwargs.get("cat_kernel_kwargs", {}) +# cat_kernel = cat_kernel_class( +# ard_num_dims=cat_dims.shape[-1], active_dims=tuple(cat_dims), **cat_kernel_kwargs +# ).double() + +# if len(cont_dims) > 0 and len(cat_dims) > 0: +# if isinstance(cont_kernel, SKLKernels): +# base_kernel = cont_kernel * cat_kernel +# else: +# base_kernel = MixedKernel(cont_kernel=cont_kernel, cat_kernel=cat_kernel) +# elif len(cont_dims) > 0 and len(cat_dims) == 0: +# base_kernel = cont_kernel +# elif len(cont_dims) == 0 and len(cat_dims) > 0: +# base_kernel = cat_kernel +# else: +# raise ValueError("Either cont_dims or cat_dims must exist!") + +# if isinstance(base_kernel, SKLKernels): +# scale_kernel_class = kernel_kwargs.get("scale_kernel", ConstantKernel) +# scale_kernel_kwargs = kernel_kwargs.get("scale_kernel_kwargs", {}) +# scale_kernel = scale_kernel_class(**scale_kernel_kwargs) + +# noise_kernel_class = kernel_kwargs.get("noise_kernel", WhiteKernel) +# noise_kernel_kwargs = kernel_kwargs.get("noise_kernel_kwargs", {}) +# noise_kernel = noise_kernel_class(**noise_kernel_kwargs) + +# gp_kernel = scale_kernel * base_kernel + noise_kernel +# else: +# scale_kernel_class = kernel_kwargs.get("scale_kernel", ScaleKernel) +# scale_kernel_kwargs = kernel_kwargs.get("scale_kernel_kwargs", {}) +# gp_kernel = scale_kernel_class(base_kernel=base_kernel, **scale_kernel_kwargs) + +# return gp_kernel + + +# class FITCKernel(Kernel): +# def __init__( +# self, +# base_kernel: Kernel, +# X_inducing: torch.Tensor, +# likelihood: GaussianLikelihood, +# X_out: torch.Tensor, +# y_out: torch.Tensor, +# active_dims: Tuple[int] | None = None, +# ): +# r"""A reimplementation of FITC Kernel that computes the posterior explicitly for globally augmented local GP. +# This should work exactly the same as a gpytorch.kernel.InducingPointKernel. +# However, it takes much less time when combined with LGPGA. +# References: Edward Snelson and Zoubin Ghahramani. Sparse Gaussian processes using pseudo-inputs. Advances in +# Neural Information Processing Systems 18, Cambridge, Massachusetts, 2006. The MIT Press. +# https://papers.nips.cc/paper/2005/hash/4491777b1aa8b5b32c2e8666dbe1a495-Abstract.html + +# Mean value is computed with: +# \mathbf{\mu_{l'}} = \mathbf{K_{l',u} \Sigma K_{u,1} \Lambda}^{-1}\mathbf{y_g} \label{eq:mean_sgp} +# and variance value: +# \mathbf{\sigma}^2_{l'} = \mathbf{K_{l',l'}} - \mathbf{Q_{l', l'} + \mathbf{K_{l', u}\Sigma K_{u, l'}}} +# \mathbf{\Sigma} = (\mathbf{K_{u,u}} + \mathbf{K_{u, g} \Lambda}^{-1}\mathbf{K_{g,u}})^{-1} +# \mathbf{\Lambda} = diag[\mathbf{K_{g,g}-Q_{g,g}} + \sigma^2_{noise}\idenmat] +# ---------- +# base_kernel: Kernel +# base kernel function +# X_inducing: torch.Tensor (N_inducing, D) +# inducing points, a torch tensor with shape (N_inducing, D), N_inducing is the number of the inducing +# points +# likelihood: GaussianLikelihood +# GP likelihood +# X_out: torch.Tensor (N_out,D) +# data features outside the subregion, it needs to be of size (N_out, D), N_out is the number of points +# outside the subspace +# y_out: torch.Tensor +# data observations outside the subregion +# active_dims: Tuple[int] | None = None +# Set this if you want to compute the covariance of only a few input dimensions. The ints +# corresponds to the indices of the dimensions. Default: `None`. +# """ +# super(FITCKernel, self).__init__(active_dims=active_dims) +# self.has_lengthscale = base_kernel.has_lengthscale +# self.base_kernel = base_kernel +# self.likelihood = likelihood + +# if X_inducing.ndimension() == 1: +# X_inducing = X_inducing.unsqueeze(-1) + +# self.X_out = X_out +# self.y_out = y_out +# self.register_parameter(name="X_inducing", parameter=torch.nn.Parameter(X_inducing)) + +# def _clear_cache(self) -> None: +# if hasattr(self, "_cached_kernel_mat"): +# del self._cached_kernel_mat +# if hasattr(self, "_cached_inducing_sigma"): +# del self._cached_inducing_sigma +# if hasattr(self, "_cached_poster_mean_mat"): +# del self._cached_poster_mean_mat +# if hasattr(self, "_train_cached_k_u1"): +# del self._train_cached_k_u1 +# if hasattr(self, "_train_cached_lambda_diag_inv"): +# del self._train_cached_lambda_diag_inv +# if hasattr(self, "_train_cached_posterior_mean"): +# del self._train_cached_posterior_mean +# if hasattr(self, "_cached_kernel_inv_root"): +# del self._cached_kernel_inv_root + +# @property +# def _inducing_mat(self) -> torch.Tensor: +# """ +# Computes inducing matrix, K(X_inducing, X_inducing) + +# Returns +# ------- +# res: torch.Tensor (N_inducing, N_inducing) +# K(X_inducing, X_inducing) +# """ +# if not self.training and hasattr(self, "_cached_kernel_mat"): +# return self._cached_kernel_mat +# else: +# res = delazify(self.base_kernel(self.X_inducing, self.X_inducing)) +# if not self.training: +# self._cached_kernel_mat = res # type: torch.Tensor +# return res + +# @property +# def _inducing_inv_root(self) -> torch.Tensor: +# """ +# Computes the inverse of the inducing matrix: K_inv(X_inducing, X_inducing) = K(X_inducing, X_inducing)^(-1) + +# Returns +# ------- +# res: torch.Tensor (N_inducing, N_inducing) +# K_inv(X_inducing, X_inducing) +# """ +# if not self.training and hasattr(self, "_cached_kernel_inv_root"): +# return self._cached_kernel_inv_root +# else: +# chol = psd_safe_cholesky(self._inducing_mat, upper=True, jitter=settings.cholesky_jitter.value()) +# eye = torch.eye(chol.size(-1), device=chol.device, dtype=chol.dtype) +# inv_root = torch.triangular_solve(eye, chol)[0] + +# res = inv_root +# if not self.training: +# self._cached_kernel_inv_root = res # type: torch.Tensor +# return res + +# @property +# def _k_u1(self) -> torch.Tensor: +# """ +# Computes the covariance matrix between the X_inducing and X_out : K(X_inducing, X_out) + +# Returns +# ------- +# res: torch.Tensor (N_inducing, N_out) +# K(X_inducing, X_out) +# """ +# if not self.training and hasattr(self, "_cached_k_u1"): +# return self._cached_k_u1 +# else: +# res = delazify(self.base_kernel(self.X_inducing, self.X_out)) +# if not self.training: +# self._cached_k_u1 = res # type: torch.Tensor +# else: +# self._train_cached_k_u1 = res # type: torch.Tensor +# return res + +# @property +# def _lambda_diag_inv(self) -> torch.Tensor: +# r"""Computes the inverse of lambda matrix, it is computed by +# \Lambda = diag[\mathbf{K_{X_out,X_out}-Q_{X_out,X_out}} + \sigma^2_{noise}\idenmat] and +# Q{X_out, X_out} = K(X_out, X_inducing) K^{-1}(X_inducing,X_inducing) K(X_inducing, X_out) + +# Returns +# ------- +# res: torch.Tensor (N_out, N_out) +# inverse of the diagonal matrix lambda +# """ +# if not self.training and hasattr(self, "_cached_lambda_diag_inv"): +# return self._cached_lambda_diag_inv +# else: +# diag_k11 = delazify(self.base_kernel(self.X_out, diag=True)) + +# diag_q11 = delazify(RootLazyTensor(self._k_u1.transpose(-1, -2).matmul(self._inducing_inv_root))).diag() + +# # Diagonal correction for predictive posterior +# correction = (diag_k11 - diag_q11).clamp(0, math.inf) + +# sigma = self.likelihood._shaped_noise_covar(correction.shape).diag() + +# res = delazify(DiagLazyTensor((correction + sigma).reciprocal())) + +# if not self.training: +# self._cached_lambda_diag_inv = res # type: torch.Tensor +# else: +# self._train_cached_lambda_diag_inv = res # type: torch.Tensor +# return res + +# @property +# def _inducing_sigma(self) -> torch.Tensor: +# r"""Computes the inverse of lambda matrix, it is computed by +# \mathbf{\Sigma} = (\mathbf{K_{X_inducing,X_inducing}} + +# \mathbf{K_{X_inducing, X_out} \Lambda}^{-1}\mathbf{K_{X_out,X_inducing}}) + +# Returns +# ------- +# res: torch.Tensor (N_inducing, N_inducing) +# \Sigma +# """ +# if not self.training and hasattr(self, "_cached_inducing_sigma"): +# return self._cached_inducing_sigma +# else: +# k_u1 = self._k_u1 +# res = PsdSumLazyTensor( +# self._inducing_mat, +# MatmulLazyTensor(k_u1, MatmulLazyTensor(self._lambda_diag_inv, k_u1.transpose(-1, -2))), +# ) +# res = delazify(res) +# if not self.training: +# self._cached_inducing_sigma = res # type: torch.Tensor + +# return res + +# @property +# def _inducing_sigma_inv_root(self) -> torch.Tensor: +# r"""Inverse of Sigma matrix: + +# Returns +# ------- +# res: torch.Tensor (N_inducing, N_inducing) +# \Sigma ^{-1} +# """ +# if not self.training and hasattr(self, "_cached_inducing_sigma_inv_root"): +# return self._cached_inducing_sigma_inv_root +# else: +# chol = psd_safe_cholesky(self._inducing_sigma, upper=True, jitter=settings.cholesky_jitter.value()) + +# eye = torch.eye(chol.size(-1), device=chol.device, dtype=chol.dtype) +# inv_root = torch.triangular_solve(eye, chol)[0] +# res = inv_root +# if not self.training: +# self._cached_inducing_sigma_inv_root = res # type: torch.Tensor +# return res + +# @property +# def _poster_mean_mat(self) -> torch.Tensor: +# r"""A cached value for computing the posterior mean of a sparse kernel it is defined by +# \Sigma K_{u, 1} \Lambda}^{-1}\mathbf{y_out} + +# Returns +# ------- +# res: torch.Tensor (N_inducing, 1) +# cached posterior mean +# """ +# if not self.training and hasattr(self, "_cached_poster_mean_mat"): +# return self._cached_poster_mean_mat +# else: +# inducing_sigma_inv_root = self._inducing_sigma_inv_root +# sigma = RootLazyTensor(inducing_sigma_inv_root) + +# k_u1 = self._k_u1 +# lambda_diag_inv = self._lambda_diag_inv + +# res_mat = delazify(MatmulLazyTensor(sigma, MatmulLazyTensor(k_u1, lambda_diag_inv))) + +# res = torch.matmul(res_mat, self.y_out) + +# if not self.training: +# self._cached_poster_mean_mat = res # type: torch.Tensor +# return res + +# def _get_covariance(self, x1: torch.Tensor, x2: torch.Tensor) -> gpytorch.lazy.LazyTensor: +# r"""Compute the posterior covariance matrix of a sparse kernel explicitly + +# Parameters +# ---------- +# x1: torch.Tensor(N_x1, D) +# first input of the FITC kernel +# x2: torch.Tensor(N_x2, D) +# second input of the FITC kernel + +# Returns +# ------- +# res: Optional[torch.Tensor (N_x1, 1), PsdSumLazyTensor] +# a cached value for computing the posterior mean, it +# is defined by \Sigma K_{u, 1} \Lambda}^{-1}\mathbf{y_out} +# """ +# k_x1x2 = self.base_kernel(x1, x2) +# k_x1u = delazify(self.base_kernel(x1, self.X_inducing)) +# inducing_inv_root = self._inducing_inv_root +# inducing_sigma_inv_root = self._inducing_sigma_inv_root +# if torch.equal(x1, x2): +# q_x1x2 = RootLazyTensor(k_x1u.matmul(inducing_inv_root)) + +# s_x1x2 = RootLazyTensor(k_x1u.matmul(inducing_sigma_inv_root)) +# else: +# k_x2u = delazify(self.base_kernel(x2, self.X_inducing)) +# q_x1x2 = MatmulLazyTensor( +# k_x1u.matmul(inducing_inv_root), k_x2u.matmul(inducing_inv_root).transpose(-1, -2) +# ) +# s_x1x2 = MatmulLazyTensor( +# k_x1u.matmul(inducing_sigma_inv_root), k_x2u.matmul(inducing_sigma_inv_root).transpose(-1, -2) +# ) +# covar = PsdSumLazyTensor(k_x1x2, -1.0 * q_x1x2, s_x1x2) + +# if self.training: +# k_iu = self.base_kernel(x1, self.X_inducing) +# sigma = RootLazyTensor(inducing_sigma_inv_root) + +# k_u1 = self._train_cached_k_u1 if hasattr(self, "_train_cached_k_u1") else self._k_u1 +# lambda_diag_inv = ( +# self._train_cached_lambda_diag_inv +# if hasattr(self, "_train_cached_lambda_diag_inv") +# else self._lambda_diag_inv +# ) + +# mean = torch.matmul( +# delazify(MatmulLazyTensor(k_iu, MatmulLazyTensor(sigma, MatmulLazyTensor(k_u1, lambda_diag_inv)))), +# self.y_out, +# ) + +# self._train_cached_posterior_mean = mean # type: torch.Tensor +# return covar + +# def posterior_mean(self, inputs: torch.Tensor) -> torch.Tensor: +# """ +# The posterior mean of the FITC kernel, will serve as the prior mean of the dense kernel. + +# Parameters +# ---------- +# inputs: torch.Tensor(N_inputs, D) +# input of the FITC kernel + +# Returns +# ------- +# res: Torch.Tensor (N_inputs, 1) +# The posterior mean of the FITC Kernel +# """ +# if self.training and hasattr(self, "_train_cached_posterior_mean"): +# return self._train_cached_posterior_mean +# if inputs.ndimension() == 1: +# inputs = inputs.unsqueeze(1) + +# k_iu = delazify(self.base_kernel(inputs, self.X_inducing, cont_only=True)) +# poster_mean = self._poster_mean_mat +# res = torch.matmul(k_iu, poster_mean) +# return res + +# def forward( +# self, x1: torch.Tensor, x2: torch.Tensor, diag: bool = False, **kwargs: Any +# ) -> gpytorch.lazy.LazyTensor: +# """Compute the kernel function""" +# covar = self._get_covariance(x1, x2) +# if self.training: +# if not torch.equal(x1, x2): +# raise RuntimeError("x1 should equal x2 in training mode") + +# if diag: +# return covar.diag() +# else: +# return covar + +# def num_outputs_per_input(self, x1: torch.Tensor, x2: torch.Tensor) -> int: +# """ +# Number of outputs given the inputs +# if x1 is of size `n x d` and x2 is size `m x d`, then the size of the kernel +# will be `(n * num_outputs_per_input) x (m * num_outputs_per_input)` + +# Parameters +# ---------- +# x1: torch.Tensor +# the first input of the kernel +# x2: torch.Tensor +# the second input of the kernel +# Returns +# ------- +# res: int +# for base kernels such as matern or RBF kernels, this value needs to be 1. +# """ +# return self.base_kernel.num_outputs_per_input(x1, x2) + +# def __deepcopy__(self, memo: Dict) -> "FITCKernel": +# replace_inv_root = False +# replace_kernel_mat = False +# replace_k_u1 = False +# replace_lambda_diag_inv = False +# replace_inducing_sigma = False +# replace_inducing_sigma_inv_root = False +# replace_poster_mean = False + +# if hasattr(self, "_cached_kernel_inv_root"): +# replace_inv_root = True +# kernel_inv_root = self._cached_kernel_inv_root +# if hasattr(self, "_cached_kernel_mat"): +# replace_kernel_mat = True +# kernel_mat = self._cached_kernel_mat +# if hasattr(self, "_cached_k_u1"): +# replace_k_u1 = True +# k_u1 = self._cached_k_u1 +# if hasattr(self, "_cached_lambda_diag_inv"): +# replace_lambda_diag_inv = True +# lambda_diag_inv = self._cached_lambda_diag_inv +# if hasattr(self, "_cached_inducing_sigma"): +# replace_inducing_sigma = True +# inducing_sigma = self._cached_inducing_sigma +# if hasattr(self, "_cached_inducing_sigma_inv_root"): +# replace_inducing_sigma_inv_root = True +# inducing_sigma_inv_root = self._cached_inducing_sigma_inv_root +# if hasattr(self, "_cached_poster_mean_mat"): +# replace_poster_mean = True +# poster_mean_mat = self._cached_poster_mean_mat + +# cp = self.__class__( +# base_kernel=copy.deepcopy(self.base_kernel), +# X_inducing=copy.deepcopy(self.X_inducing), +# X_out=self.X_out, +# y_out=self.y_out, +# likelihood=copy.deepcopy(self.likelihood), +# active_dims=self.active_dims, +# ) + +# if replace_inv_root: +# cp._cached_kernel_inv_root = kernel_inv_root + +# if replace_kernel_mat: +# cp._cached_kernel_mat = kernel_mat + +# if replace_k_u1: +# cp._cached_k_u1 = k_u1 + +# if replace_lambda_diag_inv: +# cp._cached_lambda_diag_inv = lambda_diag_inv + +# if replace_inducing_sigma: +# cp._cached_inducing_sigma = inducing_sigma + +# if replace_inducing_sigma_inv_root: +# cp._cached_inducing_sigma_inv_root = inducing_sigma_inv_root + +# if replace_poster_mean: +# cp._cached_poster_mean_mat = poster_mean_mat + +# return cp + + +# class FITCMean(Mean): +# def __init__(self, covar_module: FITCKernel, batch_shape: torch.Size = torch.Size(), **kwargs: Any): +# """ +# Read the posterior mean value of the given fitc kernel and serve as a prior mean value for the +# second stage + +# Parameters +# ---------- +# covar_module: FITCKernel +# a FITC kernel +# batch_shape: torch.size +# batch size +# """ +# super(FITCMean, self).__init__() +# self.covar_module = covar_module +# self.batch_shape = batch_shape +# self.covar_module = covar_module + +# def forward(self, input: torch.Tensor) -> torch.Tensor: +# """ +# Compute the posterior mean from the cached value of FITC kernels + +# Parameters +# ---------- +# input: torch.Tensor(N_xin, D) +# input torch Tensor + +# Returns +# ------- +# res: torch.Tensor(N_xin) +# posterior mean value of FITC GP model +# """ +# # detach is applied here to avoid updating the same parameter twice in the same iteration +# # which might result in an error +# res = self.covar_module.posterior_mean(input).detach() +# return res diff --git a/smac/model/gaussian_process/kernels/base_kernels.py b/smac/model/gaussian_process/kernels/base_kernels.py new file mode 100644 index 000000000..19fbe6eee --- /dev/null +++ b/smac/model/gaussian_process/kernels/base_kernels.py @@ -0,0 +1,465 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import Any, Callable + +from inspect import Signature, signature + +import numpy as np +import sklearn.gaussian_process.kernels as kernels + +from smac.model.gaussian_process.priors.abstract_prior import AbstractPrior +from smac.utils.configspace import get_conditional_hyperparameters + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class AbstractKernel: + """ + This is a mixin for a kernel to override functions of the kernel. Because it overrides functions of the kernel, + it needs to be placed first in the inheritance hierarchy. For this reason it is not possible to subclass the + Mixin from the kernel class because this will prevent it from being instantiatable. Therefore, mypy won't know about + anything related to the superclass and some type:ignore statements has to be added when accessing a member that is + declared in the superclass such as `self.has_conditions`, `self._call`, `super().get_params`, etc. + + Parameters + ---------- + operate_on : np.ndarray, defaults to None + On which numpy array should be operated on. + has_conditions : bool, defaults to False + Whether the kernel has conditions. + prior : AbstractPrior, defaults to None + Which prior the kernel is using. + + Attributes + ---------- + operate_on : np.ndarray, defaults to None + On which numpy array should be operated on. + has_conditions : bool, defaults to False + Whether the kernel has conditions. Might be changed by the gaussian process. + prior : AbstractPrior, defaults to None + Which prior the kernel is using. Primarily used by sklearn. + """ + + def __init__( + self, + *, + operate_on: np.ndarray | None = None, + has_conditions: bool = False, + prior: AbstractPrior | None = None, + **kwargs: Any, + ) -> None: + self.operate_on = operate_on + self.has_conditions = has_conditions + self.prior = prior + self._set_active_dims(operate_on) + + # Since this class is a mixin, we just pass all the other parameters to the next class. + super().__init__(**kwargs) + + # Get variables from next class: + # We make it explicit here to make sure the next class really has this attributes. + self._hyperparameters: list[kernels.Hyperparameter] = super().hyperparameters # type: ignore + self._n_dims: int = super().n_dims # type: ignore + self._len_active: int | None + + @property + def meta(self) -> dict[str, Any]: + """Returns the meta data of the created object. This method calls the `get_params` method to collect the + parameters of the kernel. + """ + meta: dict[str, Any] = {"name": self.__class__.__name__} + meta.update(self.get_params(deep=False)) + + # We have to handle some special cases to make the meta data serializable + for k in meta: + v = meta[k] + if isinstance(v, AbstractKernel): + meta[k] = v.meta + + if isinstance(v, AbstractPrior): + meta[k] = v.meta + + if isinstance(v, np.ndarray): + meta[k] = v.tolist() + + return meta + + @property + def hyperparameters(self) -> list[kernels.Hyperparameter]: + """Returns a list of all hyperparameter specifications.""" + return self._hyperparameters + + @property + def n_dims(self) -> int: + """Returns the number of non-fixed hyperparameters of the kernel.""" + return self._n_dims + + def get_params(self, deep: bool = True) -> dict[str, Any]: + """Get parameters of this kernel. + + Parameters + ---------- + deep : bool, defaults to True + If True, will return the parameters for this estimator and + contained subobjects that are estimators. + + Returns + ------- + params : dict[str, Any] + Parameter names mapped to their values. + """ + params = {} + + # ignore[misc] looks like it catches all kinds of errors, but misc is actually a category from mypy: + # https://mypy.readthedocs.io/en/latest/error_code_list.html#miscellaneous-checks-misc + tmp = super().get_params(deep) # type: ignore[misc] # noqa F821 + args = list(tmp.keys()) + + # Sum and Product do not clone the 'has_conditions' attribute by default. Instead of changing their + # get_params() method, we simply add the attribute here! + if "has_conditions" not in args: + args.append("has_conditions") + + for arg in args: + params[arg] = getattr(self, arg, None) + + return params + + def __call__( + self, + X: np.ndarray, + Y: np.ndarray | None = None, + eval_gradient: bool = False, + active: np.ndarray | None = None, + ) -> np.ndarray | tuple[np.ndarray, np.ndarray]: + """Call the kernel function. Internally, `self._call` is called, which must be specified by a subclass.""" + if active is None and self.has_conditions: + if self.operate_on is None: + active = get_conditional_hyperparameters(X, Y) + else: + if Y is None: + active = get_conditional_hyperparameters(X[:, self.operate_on], None) + else: + active = get_conditional_hyperparameters(X[:, self.operate_on], Y[:, self.operate_on]) + + if self.operate_on is None: + rval = self._call(X, Y, eval_gradient, active) + else: + if self._len_active is None: + raise RuntimeError("The internal variable `_len_active` is not set.") + + if Y is None: + rval = self._call( + X=X[:, self.operate_on].reshape([-1, self._len_active]), + Y=None, + eval_gradient=eval_gradient, + active=active, + ) + X = X[:, self.operate_on].reshape((-1, self._len_active)) + else: + rval = self._call( + X=X[:, self.operate_on].reshape([-1, self._len_active]), + Y=Y[:, self.operate_on].reshape([-1, self._len_active]), + eval_gradient=eval_gradient, + active=active, + ) + X = X[:, self.operate_on].reshape((-1, self._len_active)) + Y = Y[:, self.operate_on].reshape((-1, self._len_active)) + + return rval + + def __add__(self, b: kernels.Kernel | float) -> kernels.Sum: + if not isinstance(b, kernels.Kernel): + return SumKernel(self, ConstantKernel(b)) + + return SumKernel(self, b) + + def __radd__(self, b: kernels.Kernel | float) -> kernels.Sum: + if not isinstance(b, kernels.Kernel): + return SumKernel(ConstantKernel(b), self) + + return SumKernel(b, self) + + def __mul__(self, b: kernels.Kernel | float) -> kernels.Product: + if not isinstance(b, kernels.Kernel): + return ProductKernel(self, ConstantKernel(b)) + + return ProductKernel(self, b) + + def __rmul__(self, b: kernels.Kernel | float) -> kernels.Product: + if not isinstance(b, kernels.Kernel): + return ProductKernel(ConstantKernel(b), self) + + return ProductKernel(b, self) + + @abstractmethod + def _call( + self, + X: np.ndarray, + Y: np.ndarray | None = None, + eval_gradient: bool = False, + active: np.ndarray | None = None, + ) -> np.ndarray | tuple[np.ndarray, np.ndarray]: + """Return the kernel k(X, Y) and optionally its gradient. + + Note + ---- + Code partially copied from skopt (https://github.com/scikit-optimize). + Made small changes to only compute necessary values and use scikit-learn helper functions. + + Parameters + ---------- + X : np.ndarray [#samples, #features] + Left argument of the returned kernel k(X, Y). + Y : np.ndarray [#samples, #features], defaults to None + Right argument of the returned kernel k(X, Y). If None, k(X, X) is evaluated instead. + eval_gradient : bool, defaults to False + Determines whether the gradient with respect to the kernel hyperparameter is determined. + Only supported when `Y` is None. + active : np.ndarray [#samples, #features], defaults to None + Boolean array specifying which hyperparameters are active. + + Returns + ------- + K : np.ndarray [#X_samples, #Y_samples] + Kernel k(X, Y). + K_gradient : np.ndarray [#X_samples, #X_samples, #dimensions] + The gradient of the kernel k(X, X) with respect to the hyperparameter of the kernel. + Only returned when `eval_gradient` is True. + """ + raise NotImplementedError + + def _signature(self, func: Callable) -> Signature: + sig_: Signature | None + + try: + sig_ = self._signature_cache.get(func) + except AttributeError: + self._signature_cache: dict[Callable, Signature] = {} + sig_ = None + + if sig_ is None: + sig = signature(func) + self._signature_cache[func] = sig + + return sig + else: + return sig_ + + def _set_active_dims(self, operate_on: np.ndarray | None = None) -> None: + """Sets dimensions this kernel should work on.""" + if operate_on is not None and type(operate_on) in (list, np.ndarray): + if not isinstance(operate_on, np.ndarray): + raise TypeError("The argument `operate_on` needs to be of type np.ndarray but is %s" % type(operate_on)) + + if operate_on.dtype != int: + raise ValueError("The dtype of argument `operate_on` needs to be int, but is %s" % operate_on.dtype) + + self.operate_on = operate_on + self._len_active = len(operate_on) + else: + self.operate_on = None + self._len_active = None + + +class SumKernel(AbstractKernel, kernels.Sum): + """Sum kernel implementation.""" + + def __init__( + self, + k1: kernels.Kernel, + k2: kernels.Kernel, + operate_on: np.ndarray = None, + has_conditions: bool = False, + ) -> None: + super().__init__( + operate_on=operate_on, + has_conditions=has_conditions, + k1=k1, + k2=k2, + ) + + def __call__( + self, + X: np.ndarray, + Y: np.ndarray | None = None, + eval_gradient: bool = False, + active: np.ndarray | None = None, + ) -> np.ndarray | tuple[np.ndarray, np.ndarray]: + """Return the kernel k(X, Y) and optionally its gradient. + + Parameters + ---------- + X : array, shape (n_samples_X, n_features) + Left argument of the returned kernel k(X, Y) + + Y : array, shape (n_samples_Y, n_features), (optional, default=None) + Right argument of the returned kernel k(X, Y). If None, k(X, X) + if evaluated instead. + + eval_gradient : bool (optional, default=False) + Determines whether the gradient with respect to the kernel + hyperparameter is determined. + + active : np.ndarray (n_samples_X, n_features) (optional) + Boolean array specifying which hyperparameters are active. + + Returns + ------- + K : array, shape (n_samples_X, n_samples_Y) + Kernel k(X, Y) + + K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) + The gradient of the kernel k(X, X) with respect to the + hyperparameter of the kernel. Only returned when eval_gradient + is True. + """ + if eval_gradient: + K1, K1_gradient = self.k1(X, Y, eval_gradient=True, active=active) + K2, K2_gradient = self.k2(X, Y, eval_gradient=True, active=active) + + return K1 + K2, np.dstack((K1_gradient, K2_gradient)) + else: + return self.k1(X, Y, active=active) + self.k2(X, Y, active=active) + + +class ProductKernel(AbstractKernel, kernels.Product): + """Product kernel implementation.""" + + def __init__( + self, + k1: kernels.Kernel, + k2: kernels.Kernel, + operate_on: np.ndarray = None, + has_conditions: bool = False, + ) -> None: + super().__init__( + operate_on=operate_on, + has_conditions=has_conditions, + k1=k1, + k2=k2, + ) + + def __call__( + self, + X: np.ndarray, + Y: np.ndarray | None = None, + eval_gradient: bool = False, + active: np.ndarray | None = None, + ) -> np.ndarray | tuple[np.ndarray, np.ndarray]: + """Return the kernel k(X, Y) and optionally its gradient. + + Parameters + ---------- + X : array, shape (n_samples_X, n_features) + Left argument of the returned kernel k(X, Y) + + Y : array, shape (n_samples_Y, n_features), (optional, default=None) + Right argument of the returned kernel k(X, Y). If None, k(X, X) + if evaluated instead. + + eval_gradient : bool (optional, default=False) + Determines whether the gradient with respect to the kernel + hyperparameter is determined. + + active : np.ndarray (n_samples_X, n_features) (optional) + Boolean array specifying which hyperparameters are active. + + Returns + ------- + K : array, shape (n_samples_X, n_samples_Y) + Kernel k(X, Y) + + K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) + The gradient of the kernel k(X, X) with respect to the + hyperparameter of the kernel. Only returned when eval_gradient + is True. + """ + if eval_gradient: + K1, K1_gradient = self.k1(X, Y, eval_gradient=True, active=active) + K2, K2_gradient = self.k2(X, Y, eval_gradient=True, active=active) + + return K1 * K2, np.dstack((K1_gradient * K2[:, :, np.newaxis], K2_gradient * K1[:, :, np.newaxis])) + else: + return self.k1(X, Y, active=active) * self.k2(X, Y, active=active) + + +class ConstantKernel(AbstractKernel, kernels.ConstantKernel): + def __init__( + self, + constant_value: float = 1.0, + constant_value_bounds: tuple[float, float] = (1e-5, 1e5), + operate_on: np.ndarray | None = None, + has_conditions: bool = False, + prior: AbstractPrior | None = None, + ) -> None: + super().__init__( + operate_on=operate_on, + has_conditions=has_conditions, + prior=prior, + constant_value=constant_value, + constant_value_bounds=constant_value_bounds, + ) + + def __call__( + self, + X: np.ndarray, + Y: np.ndarray | None = None, + eval_gradient: bool = False, + active: np.ndarray | None = None, + ) -> np.ndarray | tuple[np.ndarray, np.ndarray]: + """Return the kernel k(X, Y) and optionally its gradient. + + Parameters + ---------- + X : array, shape (n_samples_X, n_features) + Left argument of the returned kernel k(X, Y) + + Y : array, shape (n_samples_Y, n_features), (optional, default=None) + Right argument of the returned kernel k(X, Y). If None, k(X, X) + if evaluated instead. + + eval_gradient : bool (optional, default=False) + Determines whether the gradient with respect to the kernel + hyperparameter is determined. Only supported when Y is None. + + active : np.ndarray (n_samples_X, n_features) (optional) + Boolean array specifying which hyperparameters are active. + + Returns + ------- + K : array, shape (n_samples_X, n_samples_Y) + Kernel k(X, Y) + + K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) + The gradient of the kernel k(X, X) with respect to the + hyperparameter of the kernel. Only returned when eval_gradient + is True. + """ + X = np.atleast_2d(X) + if Y is None: + Y = X + elif eval_gradient: + raise ValueError("Gradient can only be evaluated when Y is None.") + + K = np.full( + (X.shape[0], Y.shape[0]), + self.constant_value, + dtype=np.array(self.constant_value).dtype, + ) + if eval_gradient: + if not self.hyperparameter_constant_value.fixed: + return ( + K, + np.full( + (X.shape[0], X.shape[0], 1), + self.constant_value, + dtype=np.array(self.constant_value).dtype, + ), + ) + else: + return K, np.empty((X.shape[0], X.shape[0], 0)) + else: + return K diff --git a/smac/model/gaussian_process/kernels/hamming_kernel.py b/smac/model/gaussian_process/kernels/hamming_kernel.py new file mode 100644 index 000000000..ada6ca372 --- /dev/null +++ b/smac/model/gaussian_process/kernels/hamming_kernel.py @@ -0,0 +1,120 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np +import sklearn.gaussian_process.kernels as kernels + +from smac.model.gaussian_process.kernels.base_kernels import AbstractKernel +from smac.model.gaussian_process.priors.abstract_prior import AbstractPrior + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class HammingKernel( + AbstractKernel, + kernels.StationaryKernelMixin, + kernels.NormalizedKernelMixin, + kernels.Kernel, +): + """Hamming kernel implementation.""" + + def __init__( + self, + length_scale: float | tuple[float, ...] | np.ndarray = 1.0, + length_scale_bounds: tuple[float, float] | list[tuple[float, float]] | np.ndarray = (1e-5, 1e5), + operate_on: np.ndarray | None = None, + has_conditions: bool = False, + prior: AbstractPrior | None = None, + ) -> None: + self.length_scale = length_scale + self.length_scale_bounds = length_scale_bounds + + super().__init__( + operate_on=operate_on, + has_conditions=has_conditions, + prior=prior, + ) + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + + length_scale = self.length_scale + if isinstance(length_scale, np.ndarray): + length_scale = length_scale.tolist() + + length_scale_bounds = self.length_scale_bounds + if isinstance(length_scale_bounds, np.ndarray): + length_scale_bounds = length_scale_bounds.tolist() + + meta.update( + { + "length_scale": length_scale, + "lengthscale_bounds": length_scale_bounds, + } + ) + + return meta + + @property + def hyperparameter_length_scale(self) -> kernels.Hyperparameter: + """Hyperparameter of the length scale.""" + length_scale = self.length_scale + anisotropic = np.iterable(length_scale) and len(length_scale) > 1 # type: ignore + + if anisotropic: + return kernels.Hyperparameter( + "length_scale", + "numeric", + self.length_scale_bounds, + len(length_scale), # type: ignore + ) + + return kernels.Hyperparameter( + "length_scale", + "numeric", + self.length_scale_bounds, + ) + + def _call( + self, + X: np.ndarray, + Y: np.ndarray | None = None, + eval_gradient: bool = False, + active: np.ndarray | None = None, + ) -> np.ndarray | tuple[np.ndarray, np.ndarray]: + X = np.atleast_2d(X) + length_scale = kernels._check_length_scale(X, self.length_scale) + + if Y is None: + Y = X + elif eval_gradient: + raise ValueError("gradient can be evaluated only when Y != X") + else: + Y = np.atleast_2d(Y) + + indicator = np.expand_dims(X, axis=1) != Y + K = (-1 / (2 * length_scale**2) * indicator).sum(axis=2) + K = np.exp(K) + + if active is not None: + K = K * active + + if eval_gradient: + # dK / d theta = (dK / dl) * (dl / d theta) + # theta = log(l) => dl / d (theta) = e^theta = l + # dK / d theta = l * dK / dl + + # dK / dL computation + if np.iterable(length_scale) and length_scale.shape[0] > 1: # type: ignore + grad = np.expand_dims(K, axis=-1) * np.array(indicator, dtype=np.float32) + else: + grad = np.expand_dims(K * np.sum(indicator, axis=2), axis=-1) + + grad *= 1 / length_scale**3 + + return K, grad + + return K diff --git a/smac/model/gaussian_process/kernels/matern_kernel.py b/smac/model/gaussian_process/kernels/matern_kernel.py new file mode 100644 index 000000000..576d5d76b --- /dev/null +++ b/smac/model/gaussian_process/kernels/matern_kernel.py @@ -0,0 +1,110 @@ +from __future__ import annotations + +import math + +import numpy as np +import scipy.optimize +import scipy.spatial.distance +import scipy.special +import sklearn.gaussian_process.kernels as kernels + +from smac.model.gaussian_process.kernels.base_kernels import AbstractKernel +from smac.model.gaussian_process.priors.abstract_prior import AbstractPrior + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class MaternKernel(AbstractKernel, kernels.Matern): + """Matern kernel implementation.""" + + def __init__( + self, + length_scale: float | tuple[float, ...] | np.ndarray = 1.0, + length_scale_bounds: tuple[float, float] | list[tuple[float, float]] | np.ndarray = (1e-5, 1e5), + nu: float = 1.5, + operate_on: np.ndarray | None = None, + has_conditions: bool = False, + prior: AbstractPrior | None = None, + ) -> None: + super().__init__( + operate_on=operate_on, + has_conditions=has_conditions, + prior=prior, + length_scale=length_scale, + length_scale_bounds=length_scale_bounds, + nu=nu, + ) + + def _call( + self, + X: np.ndarray, + Y: np.ndarray | None = None, + eval_gradient: bool = False, + active: np.ndarray | None = None, + ) -> np.ndarray | tuple[np.ndarray, np.ndarray]: + X = np.atleast_2d(X) + length_scale = kernels._check_length_scale(X, self.length_scale) + + if Y is None: + dists = scipy.spatial.distance.pdist(X / length_scale, metric="euclidean") + else: + if eval_gradient: + raise ValueError("Gradient can only be evaluated when Y is None.") + dists = scipy.spatial.distance.cdist(X / length_scale, Y / length_scale, metric="euclidean") + + if self.nu == 0.5: + K = np.exp(-dists) + elif self.nu == 1.5: + K = dists * math.sqrt(3) + K = (1.0 + K) * np.exp(-K) + elif self.nu == 2.5: + K = dists * math.sqrt(5) + K = (1.0 + K + K**2 / 3.0) * np.exp(-K) + else: # general case; expensive to evaluate + K = dists + K[K == 0.0] += np.finfo(float).eps # strict zeros result in nan + tmp = math.sqrt(2 * self.nu) * K + K.fill((2 ** (1.0 - self.nu)) / scipy.special.gamma(self.nu)) + K *= tmp**self.nu + K *= scipy.special.kv(self.nu, tmp) + + if Y is None: + # convert from upper-triangular matrix to square matrix + K = scipy.spatial.distance.squareform(K) + np.fill_diagonal(K, 1) + + if active is not None: + K = K * active + + if eval_gradient: + if self.hyperparameter_length_scale.fixed: + # Hyperparameter l kept fixed + K_gradient = np.empty((X.shape[0], X.shape[0], 0)) + return K, K_gradient + + # We need to recompute the pairwise dimension-wise distances + if self.anisotropic: + D = (X[:, np.newaxis, :] - X[np.newaxis, :, :]) ** 2 / (length_scale**2) + else: + D = scipy.spatial.distance.squareform(dists**2)[:, :, np.newaxis] + + if self.nu == 0.5: + K_gradient = K[..., np.newaxis] * D / np.sqrt(D.sum(2))[:, :, np.newaxis] + K_gradient[~np.isfinite(K_gradient)] = 0 + elif self.nu == 1.5: + K_gradient = 3 * D * np.exp(-np.sqrt(3 * D.sum(-1)))[..., np.newaxis] + elif self.nu == 2.5: + tmp = np.sqrt(5 * D.sum(-1))[..., np.newaxis] + K_gradient = 5.0 / 3.0 * D * (tmp + 1) * np.exp(-tmp) + else: + # original sklearn code would approximate gradient numerically, but this would violate our assumption + # that the kernel hyperparameters are not changed within __call__ + raise ValueError(self.nu) + + if not self.anisotropic: + return K, K_gradient[:, :].sum(-1)[:, :, np.newaxis] + else: + return K, K_gradient + else: + return K diff --git a/smac/model/gaussian_process/kernels/rbf_kernel.py b/smac/model/gaussian_process/kernels/rbf_kernel.py new file mode 100644 index 000000000..5bf207658 --- /dev/null +++ b/smac/model/gaussian_process/kernels/rbf_kernel.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +import numpy as np +import scipy.optimize +import scipy.spatial.distance +import scipy.special +import sklearn.gaussian_process.kernels as kernels + +from smac.model.gaussian_process.kernels.base_kernels import AbstractKernel +from smac.model.gaussian_process.priors.abstract_prior import AbstractPrior + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class RBFKernel(AbstractKernel, kernels.RBF): + """RBF kernel implementation.""" + + def __init__( + self, + length_scale: float | tuple[float, ...] | np.ndarray = 1.0, + length_scale_bounds: tuple[float, float] | list[tuple[float, float]] | np.ndarray = (1e-5, 1e5), + operate_on: np.ndarray | None = None, + has_conditions: bool = False, + prior: AbstractPrior | None = None, + ) -> None: + + super().__init__( + operate_on=operate_on, + has_conditions=has_conditions, + prior=prior, + length_scale=length_scale, + length_scale_bounds=length_scale_bounds, + ) + + def _call( + self, + X: np.ndarray, + Y: np.ndarray | None = None, + eval_gradient: bool = False, + active: np.ndarray | None = None, + ) -> np.ndarray | tuple[np.ndarray, np.ndarray]: + X = np.atleast_2d(X) + length_scale = kernels._check_length_scale(X, self.length_scale) + + if Y is None: + dists = scipy.spatial.distance.pdist(X / length_scale, metric="sqeuclidean") + K = np.exp(-0.5 * dists) + # convert from upper-triangular matrix to square matrix + K = scipy.spatial.distance.squareform(K) + np.fill_diagonal(K, 1) + else: + if eval_gradient: + raise ValueError("Gradient can only be evaluated when Y is None.") + dists = scipy.spatial.distance.cdist(X / length_scale, Y / length_scale, metric="sqeuclidean") + K = np.exp(-0.5 * dists) + + if active is not None: + K = K * active + + if eval_gradient: + if self.hyperparameter_length_scale.fixed: + # Hyperparameter l kept fixed + return K, np.empty((X.shape[0], X.shape[0], 0)) + elif not self.anisotropic or length_scale.shape[0] == 1: + K_gradient = (K * scipy.spatial.distance.squareform(dists))[:, :, np.newaxis] + return K, K_gradient + elif self.anisotropic: + # We need to recompute the pairwise dimension-wise distances + K_gradient = (X[:, np.newaxis, :] - X[np.newaxis, :, :]) ** 2 / (length_scale**2) + K_gradient *= K[..., np.newaxis] + return K, K_gradient + + return K diff --git a/smac/model/gaussian_process/kernels/white_kernel.py b/smac/model/gaussian_process/kernels/white_kernel.py new file mode 100644 index 000000000..a3fa4a61b --- /dev/null +++ b/smac/model/gaussian_process/kernels/white_kernel.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +import numpy as np +import sklearn.gaussian_process.kernels as kernels + +from smac.model.gaussian_process.kernels.base_kernels import AbstractKernel +from smac.model.gaussian_process.priors.abstract_prior import AbstractPrior + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class WhiteKernel(AbstractKernel, kernels.WhiteKernel): + """White kernel implementation.""" + + def __init__( + self, + noise_level: float | tuple[float, ...] = 1.0, + noise_level_bounds: tuple[float, float] | list[tuple[float, float]] = (1e-5, 1e5), + operate_on: np.ndarray | None = None, + has_conditions: bool = False, + prior: AbstractPrior | None = None, + ) -> None: + + super().__init__( + operate_on=operate_on, + has_conditions=has_conditions, + prior=prior, + noise_level=noise_level, + noise_level_bounds=noise_level_bounds, + ) + + def _call( + self, + X: np.ndarray, + Y: np.ndarray | None = None, + eval_gradient: bool = False, + active: np.ndarray | None = None, + ) -> np.ndarray | tuple[np.ndarray, np.ndarray]: + X = np.atleast_2d(X) + + if Y is not None and eval_gradient: + raise ValueError("Gradient can only be evaluated when Y is None.") + + if Y is None: + K = self.noise_level * np.eye(X.shape[0]) + + if active is not None: + K = K * active + + if eval_gradient: + if not self.hyperparameter_noise_level.fixed: + return (K, self.noise_level * np.eye(X.shape[0])[:, :, np.newaxis]) + else: + return K, np.empty((X.shape[0], X.shape[0], 0)) + else: + return K + else: + return np.zeros((X.shape[0], Y.shape[0])) diff --git a/smac/epm/gaussian_process/mcmc.py b/smac/model/gaussian_process/mcmc_gaussian_process.py similarity index 56% rename from smac/epm/gaussian_process/mcmc.py rename to smac/model/gaussian_process/mcmc_gaussian_process.py index e10694c53..7728bf173 100644 --- a/smac/epm/gaussian_process/mcmc.py +++ b/smac/model/gaussian_process/mcmc_gaussian_process.py @@ -1,4 +1,6 @@ -from typing import List, Optional, Tuple, Union, cast +from __future__ import annotations + +from typing import Any, Optional, TypeVar, cast import logging import warnings @@ -6,26 +8,28 @@ import emcee import numpy as np +from ConfigSpace import ConfigurationSpace from sklearn.gaussian_process import GaussianProcessRegressor from sklearn.gaussian_process.kernels import Kernel -from smac.configspace import ConfigurationSpace -from smac.epm.gaussian_process import BaseModel, GaussianProcess -from smac.epm.gaussian_process.utils.prior import Prior +from smac.model.gaussian_process.abstract_gaussian_process import ( + AbstractGaussianProcess, +) +from smac.model.gaussian_process.gaussian_process import GaussianProcess +from smac.model.gaussian_process.priors.abstract_prior import AbstractPrior -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__copyright__ = "Copyright 2022, automl.org" __license__ = "3-clause BSD" logger = logging.getLogger(__name__) +Self = TypeVar("Self", bound="MCMCGaussianProcess") -class MCMCGaussianProcess(BaseModel): - """Gaussian process model. - - The GP hyperparameters are integrated out by Markow-Chain-Monte-Carlo (MCMC) you use this class - to make sure that you also use an integrated acquisition function to - integrate over the GP's hyperparameter as proposed by Snoek et al. +class MCMCGaussianProcess(AbstractGaussianProcess): + """Implementation of a Gaussian process model which out-integrates its hyperparameters by + Markow-Chain-Monte-Carlo (MCMC). If you use this class make sure that you also use an integrated acquisition + function to integrate over the GP's hyperparameter as proposed by Snoek et al. This code is based on the implementation of RoBO: @@ -35,126 +39,136 @@ class MCMCGaussianProcess(BaseModel): Parameters ---------- - types : List[int] - Specifies the number of categorical values of an input dimension where - the i-th entry corresponds to the i-th input dimension. Let's say we - have 2 dimension where the first dimension consists of 3 different - categorical choices and the second dimension is continuous than we - have to pass [3, 0]. Note that we count starting from 0. - bounds : List[Tuple[float, float]] - bounds of input dimensions: (lower, uppper) for continuous dims; (n_cat, np.nan) for categorical dims - seed : int - Model seed. - kernel : george kernel object - Specifies the kernel that is used for all Gaussian Process - n_mcmc_walkers : int - The number of hyperparameter samples. This also determines the - number of walker for MCMC sampling as each walker will - return one hyperparameter sample. - chain_length : int - The length of the MCMC chain. We start n_mcmc_walkers walker for - chain_length steps and we use the last sample - in the chain as a hyperparameter sample. - burnin_steps : int + configspace : ConfigurationSpace + kernel : Kernel + Kernel which is used for the Gaussian process. + n_mcmc_walkers : int, defaults to 20 + The number of hyperparameter samples. This also determines the number of walker for MCMC sampling as each + walker will return one hyperparameter sample. + chain_length : int, defaults to 50 + The length of the MCMC chain. We start `n_mcmc_walkers` walker for `chain_length` steps and we use the last + sample in the chain as a hyperparameter sample. + burning_steps : int, defaults to 50 The number of burnin steps before the actual MCMC sampling starts. - normalize_y : bool - Zero mean unit variance normalization of the output values - mcmc_sampler : str + mcmc_sampler : str, defaults to "emcee" Choose a self-tuning MCMC sampler. Can be either ``emcee`` or ``nuts``. - instance_features : np.ndarray (I, K) - Contains the K dimensional instance features - of the I different instances - pca_components : float - Number of components to keep when using PCA to reduce - dimensionality of instance features. Requires to - set n_feats (> pca_dims). + normalize_y : bool, defaults to True + Zero mean unit variance normalization of the output values. + instance_features : dict[str, list[int | float]] | None, defaults to None + Features (list of int or floats) of the instances (str). The features are incorporated into the X data, + on which the model is trained on. + pca_components : float, defaults to 7 + Number of components to keep when using PCA to reduce dimensionality of instance features. + seed : int """ def __init__( self, configspace: ConfigurationSpace, - types: List[int], - bounds: List[Tuple[float, float]], - seed: int, kernel: Kernel, n_mcmc_walkers: int = 20, chain_length: int = 50, - burnin_steps: int = 50, - normalize_y: bool = True, + burning_steps: int = 50, mcmc_sampler: str = "emcee", average_samples: bool = False, - instance_features: Optional[np.ndarray] = None, - pca_components: Optional[int] = None, + normalize_y: bool = True, + instance_features: dict[str, list[int | float]] | None = None, + pca_components: int | None = 7, + seed: int = 0, ): + if mcmc_sampler not in ["emcee", "nuts"]: + raise ValueError(f"MCMC Gaussian process does not support the sampler `{mcmc_sampler}`.") + super().__init__( configspace=configspace, - types=types, - bounds=bounds, - seed=seed, kernel=kernel, instance_features=instance_features, pca_components=pca_components, + seed=seed, ) - self.n_mcmc_walkers = n_mcmc_walkers - self.chain_length = chain_length - self.burned = False - self.burnin_steps = burnin_steps - self.models = [] # type: List[GaussianProcess] - self.normalize_y = normalize_y - self.mcmc_sampler = mcmc_sampler - self.average_samples = average_samples - - self.is_trained = False - + self._n_mcmc_walkers = n_mcmc_walkers + self._chain_length = chain_length + self._burning_steps = burning_steps + self._models: list[GaussianProcess] = [] + self._normalize_y = normalize_y + self._mcmc_sampler = mcmc_sampler + self._average_samples = average_samples self._set_has_conditions() # Internal statistics self._n_ll_evals = 0 + self._burned = False + self._is_trained = False + self._samples: Any = None # TODO: What type does this variable hold? + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update( + { + "n_mcmc_walkers": self._n_mcmc_walkers, + "chain_length": self._chain_length, + "burning_steps": self._burning_steps, + "mcmc_sampler": self._mcmc_sampler, + "average_samples": self._average_samples, + "normalize_y": self._normalize_y, + } + ) + + return meta + + @property + def models(self) -> list[GaussianProcess]: + """Returns the internally used gaussian processes.""" + return self._models - def _train(self, X: np.ndarray, y: np.ndarray, do_optimize: bool = True) -> "MCMCGaussianProcess": + def _train( + self: Self, + X: np.ndarray, + y: np.ndarray, + optimize_hyperparameters: bool = True, + ) -> Self: """Performs MCMC sampling to sample hyperparameter configurations from the likelihood and - trains for each sample a GP on X and y. + trains for each sample a Gaussian process on X and y. Parameters ---------- - X: np.ndarray (N, D) - Input data points. The dimensionality of X is (N, D), - with N as the number of points and D is the number of features. - y: np.ndarray (N,) + X : np.ndarray [#samples, #hyperparameter + #features] + Input data points. + Y : np.ndarray [#samples, #objectives] The corresponding target values. - do_optimize: boolean - If set to true we perform MCMC sampling otherwise we just use the - hyperparameter specified in the kernel. + optimize_hyperparameters: boolean + If set to true we perform MCMC sampling. Otherwise, we just use the hyperparameter specified in the kernel. """ X = self._impute_inactive(X) - if self.normalize_y: + if self._normalize_y: # A note on normalization for the Gaussian process with MCMC: # Scikit-learn uses a different "normalization" than we use in SMAC3. Scikit-learn normalizes the data to # have zero mean, while we normalize it to have zero mean unit variance. To make sure the scikit-learn GP # behaves the same when we use it directly or indirectly (through the gaussian_process.py file), we # normalize the data here. Then, after the individual GPs are fit, we inject the statistics into them so # they unnormalize the data at prediction time. - y = self._normalize_y(y) + y = self._normalize(y) - self.gp = self._get_gp() + self._gp = self._get_gaussian_process() - if do_optimize: - self.gp.fit(X, y) + if optimize_hyperparameters: + self._gp.fit(X, y) self._all_priors = self._get_all_priors( add_bound_priors=True, - add_soft_bounds=True if self.mcmc_sampler == "nuts" else False, + add_soft_bounds=True if self._mcmc_sampler == "nuts" else False, ) - if self.mcmc_sampler == "emcee": - sampler = emcee.EnsembleSampler(self.n_mcmc_walkers, len(self.kernel.theta), self._ll) - sampler.random_state = self.rng.get_state() + if self._mcmc_sampler == "emcee": + sampler = emcee.EnsembleSampler(self._n_mcmc_walkers, len(self._kernel.theta), self._ll) + sampler.random_state = self._rng.get_state() # Do a burn-in in the first iteration - if not self.burned: + if not self._burned: # Initialize the walkers by sampling from the prior dim_samples = [] - prior = None # type: Optional[Union[List[Prior], Prior]] + prior: AbstractPrior | list[AbstractPrior] | None = None for dim, prior in enumerate(self._all_priors): # Always sample from the first prior if isinstance(prior, list): @@ -162,28 +176,28 @@ def _train(self, X: np.ndarray, y: np.ndarray, do_optimize: bool = True) -> "MCM prior = None else: prior = prior[0] - prior = cast(Optional[Prior], prior) + prior = cast(Optional[AbstractPrior], prior) if prior is None: raise NotImplementedError() else: - dim_samples.append(prior.sample_from_prior(self.n_mcmc_walkers).flatten()) + dim_samples.append(prior.sample_from_prior(self._n_mcmc_walkers).flatten()) self.p0 = np.vstack(dim_samples).transpose() # Run MCMC sampling with warnings.catch_warnings(): warnings.filterwarnings("ignore", r"invalid value encountered in double_scalars.*") - self.p0, _, _ = sampler.run_mcmc(self.p0, self.burnin_steps) + self.p0, _, _ = sampler.run_mcmc(self.p0, self._burning_steps) self.burned = True # Start sampling & save the current position, it will be the start point in the next iteration with warnings.catch_warnings(): warnings.filterwarnings("ignore", r"invalid value encountered in double_scalars.*") - self.p0, _, _ = sampler.run_mcmc(self.p0, self.chain_length) + self.p0, _, _ = sampler.run_mcmc(self.p0, self._chain_length) # Take the last samples from each walker - self.hypers = sampler.get_chain()[-1] - elif self.mcmc_sampler == "nuts": + self._samples = sampler.get_chain()[-1] + elif self._mcmc_sampler == "nuts": # Originally published as: # http://www.stat.columbia.edu/~gelman/research/published/nuts.pdf # A good explanation of HMC: @@ -193,41 +207,41 @@ def _train(self, X: np.ndarray, y: np.ndarray, do_optimize: bool = True) -> "MCM # Do not require the installation of NUTS for SMAC # This requires NUTS from https://github.com/mfeurer/NUTS - import nuts.nuts + import nuts.nuts # type: ignore # Perform initial fit to the data to obtain theta0 if not self.burned: - theta0 = self.gp.kernel.theta - self.burned = True + theta0 = self._gp.kernel.theta + self._burned = True else: theta0 = self.p0 samples, _, _ = nuts.nuts.nuts6( f=self._ll_w_grad, - Madapt=self.burnin_steps, - M=self.chain_length, + Madapt=self._burning_steps, + M=self._chain_length, theta0=theta0, # Increasing this value results in longer running times delta=0.5, adapt_mass=False, # Rather low max depth to keep the number of required gradient steps low max_depth=10, - rng=self.rng, + rng=self._rng, ) indices = [int(np.rint(ind)) for ind in np.linspace(start=0, stop=len(samples) - 1, num=10)] - self.hypers = samples[indices] - self.p0 = self.hypers.mean(axis=0) + self._samples = samples[indices] + self.p0 = self._samples.mean(axis=0) else: - raise ValueError(self.mcmc_sampler) + raise ValueError(self._mcmc_sampler) - if self.average_samples: - self.hypers = [self.hypers.mean(axis=0)] + if self._average_samples: + self._samples = [self._samples.mean(axis=0)] else: - self.hypers = self.gp.kernel.theta - self.hypers = [self.hypers] + self._samples = self._gp.kernel.theta + self._samples = [self._samples] - self.models = [] - for sample in self.hypers: + self._models = [] + for sample in self._samples: if (sample < -50).any(): sample[sample < -50] = -50 @@ -235,54 +249,51 @@ def _train(self, X: np.ndarray, y: np.ndarray, do_optimize: bool = True) -> "MCM sample[sample > 50] = 50 # Instantiate a GP for each hyperparameter configuration - kernel = deepcopy(self.kernel) + kernel = deepcopy(self._kernel) kernel.theta = sample model = GaussianProcess( - configspace=self.configspace, - types=self.types, - bounds=self.bounds, + configspace=self._configspace, kernel=kernel, normalize_y=False, - seed=self.rng.randint(low=0, high=10000), + seed=self._rng.randint(low=0, high=10000), ) try: - model._train(X, y, do_optimize=False) - self.models.append(model) + model._train(X, y, optimize_hyperparameters=False) + self._models.append(model) except np.linalg.LinAlgError: pass - if len(self.models) == 0: - kernel = deepcopy(self.kernel) + if len(self._models) == 0: + kernel = deepcopy(self._kernel) kernel.theta = self.p0 model = GaussianProcess( - configspace=self.configspace, - types=self.types, - bounds=self.bounds, + configspace=self._configspace, kernel=kernel, normalize_y=False, - seed=self.rng.randint(low=0, high=10000), + seed=self._rng.randint(low=0, high=10000), ) - model._train(X, y, do_optimize=False) - self.models.append(model) + model._train(X, y, optimize_hyperparameters=False) + self._models.append(model) - if self.normalize_y: + if self._normalize_y: # Inject the normalization statistics into the individual models. Setting normalize_y to True makes the # individual GPs unnormalize the data at predict time. - for model in self.models: - model.normalize_y = True + for model in self._models: + model._normalize_y = True model.mean_y_ = self.mean_y_ model.std_y_ = self.std_y_ - self.is_trained = True + self._is_trained = True return self - def _get_gp(self) -> GaussianProcessRegressor: + def _get_gaussian_process(self) -> GaussianProcessRegressor: return GaussianProcessRegressor( - kernel=self.kernel, - normalize_y=False, + kernel=self._kernel, + normalize_y=False, # We do not use scikit-learn's normalize routine optimizer=None, - n_restarts_optimizer=-1, # Do not use scikit-learn's optimization routine + n_restarts_optimizer=-1, # We do not use scikit-learn's optimization routine alpha=0, # Governed by the kernel + random_state=self._rng, ) def _ll(self, theta: np.ndarray) -> float: @@ -291,14 +302,8 @@ def _ll(self, theta: np.ndarray) -> float: Parameters ---------- - theta : np.ndarray(H) - Hyperparameter vector. Note that all hyperparameter are - on a log scale. - - Returns - ------- - float - lnlikelihood + prior + theta : np.ndarray + Hyperparameter vector. Note that all hyperparameter are on a log scale. """ self._n_ll_evals += 1 @@ -310,34 +315,28 @@ def _ll(self, theta: np.ndarray) -> float: theta[theta > 50] = 50 try: - lml = self.gp.log_marginal_likelihood(theta) + lml = self._gp.log_marginal_likelihood(theta) except ValueError: return -np.inf # Add prior for dim, priors in enumerate(self._all_priors): for prior in priors: - lml += prior.lnprob(theta[dim]) + lml += prior.get_log_probability(theta[dim]) if not np.isfinite(lml): return -np.inf else: return lml - def _ll_w_grad(self, theta: np.ndarray) -> Tuple[float, np.ndarray]: + def _ll_w_grad(self, theta: np.ndarray) -> tuple[float, np.ndarray]: """Returns the marginal log likelihood (+ the prior) for a hyperparameter configuration theta. Parameters ---------- - theta : np.ndarray(H) - Hyperparameter vector. Note that all hyperparameter are - on a log scale. - - Returns - ------- - float - lnlikelihood + prior + theta : np.ndarray + Hyperparameter vector. Note that all hyperparameter are on a log scale. """ self._n_ll_evals += 1 @@ -353,15 +352,15 @@ def _ll_w_grad(self, theta: np.ndarray) -> Tuple[float, np.ndarray]: # Add prior for dim, priors in enumerate(self._all_priors): for prior in priors: - lml += prior.lnprob(theta[dim]) - grad[dim] += prior.gradient(theta[dim]) + lml += prior.get_log_probability(theta[dim]) + grad[dim] += prior.get_gradient(theta[dim]) # Check if one of the priors is invalid, if so, no need to compute the log marginal likelihood if lml < -1e24: return -1e25, np.zeros(theta.shape) try: - lml_, grad_ = self.gp.log_marginal_likelihood(theta, eval_gradient=True) + lml_, grad_ = self._gp.log_marginal_likelihood(theta, eval_gradient=True) lml += lml_ grad += grad_ except ValueError: @@ -374,43 +373,33 @@ def _ll_w_grad(self, theta: np.ndarray) -> Tuple[float, np.ndarray]: return lml, grad def _predict( - self, X_test: np.ndarray, cov_return_type: Optional[str] = "diagonal_cov" - ) -> Tuple[np.ndarray, np.ndarray]: + self, + X: np.ndarray, + covariance_type: str | None = "diagonal", + ) -> tuple[np.ndarray, np.ndarray | None]: r""" Returns the predictive mean and variance of the objective function at X average over all hyperparameter samples. + The mean is computed by: :math \mu(x) = \frac{1}{M}\sum_{i=1}^{M}\mu_m(x) + And the variance by: :math \sigma^2(x) = (\frac{1}{M}\sum_{i=1}^{M}(\sigma^2_m(x) + \mu_m(x)^2) - \mu^2 - - Parameters - ---------- - X_test: np.ndarray (N, D) - Input test points - cov_return_type: Optional[str] - Specifies what to return along with the mean. Refer ``predict()`` for more information. - - Returns - ------- - np.array(N,) - predictive mean - np.array(N,) - predictive variance """ - if not self.is_trained: + if not self._is_trained: raise Exception("Model has to be trained first!") - if cov_return_type != "diagonal_cov": - raise ValueError("'cov_return_type' can only take 'diagonal_cov' for this model") + if covariance_type != "diagonal": + raise ValueError("`covariance_type` can only take `diagonal` for this model.") - X_test = self._impute_inactive(X_test) + X_test = self._impute_inactive(X) - mu = np.zeros([len(self.models), X_test.shape[0]]) - var = np.zeros([len(self.models), X_test.shape[0]]) - for i, model in enumerate(self.models): + mu = np.zeros([len(self._models), X_test.shape[0]]) + var = np.zeros([len(self._models), X_test.shape[0]]) + for i, model in enumerate(self._models): mu_tmp, var_tmp = model.predict(X_test) - assert var_tmp is not None # please mypy + assert var_tmp is not None mu[i] = mu_tmp.flatten() var[i] = var_tmp.flatten() diff --git a/smac/model/gaussian_process/priors/__init__.py b/smac/model/gaussian_process/priors/__init__.py new file mode 100644 index 000000000..6534f29a8 --- /dev/null +++ b/smac/model/gaussian_process/priors/__init__.py @@ -0,0 +1,12 @@ +from smac.model.gaussian_process.priors.gamma_prior import GammaPrior +from smac.model.gaussian_process.priors.horseshoe_prior import HorseshoePrior +from smac.model.gaussian_process.priors.log_normal_prior import LogNormalPrior +from smac.model.gaussian_process.priors.tophat_prior import SoftTopHatPrior, TophatPrior + +__all__ = [ + "GammaPrior", + "HorseshoePrior", + "LogNormalPrior", + "TophatPrior", + "SoftTopHatPrior", +] diff --git a/smac/model/gaussian_process/priors/abstract_prior.py b/smac/model/gaussian_process/priors/abstract_prior.py new file mode 100644 index 000000000..d121e600e --- /dev/null +++ b/smac/model/gaussian_process/priors/abstract_prior.py @@ -0,0 +1,155 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import Any + +import numpy as np + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class AbstractPrior: + """Abstract base class to define the interface for priors of Gaussian process hyperparameters. + + This class is adapted from RoBO: + + Klein, A. and Falkner, S. and Mansur, N. and Hutter, F. + RoBO: A Flexible and Robust Bayesian Optimization Framework in Python + In: NIPS 2017 Bayesian Optimization Workshop + + Note + ---- + Whenever lnprob or the gradient is computed for a scalar input, we use math.* rather than np.*. + + Parameters + ---------- + seed : int, defaults to 0 + """ + + def __init__(self, seed: int = 0): + self._seed = seed + self._rng = np.random.RandomState(seed) + + @property + def meta(self) -> dict[str, Any]: + """Returns the meta data of the created object.""" + return { + "name": self.__class__.__name__, + "seed": self._seed, + } + + def sample_from_prior(self, n_samples: int) -> np.ndarray: + """Returns `n_samples` from the prior. All samples are on a log scale. This method calls + `self._sample_from_prior` and applies a log transformation to the obtained values. + + Parameters + ---------- + n_samples : int + The number of samples that will be drawn. + + Returns + ------- + samples : np.ndarray + """ + if np.ndim(n_samples) != 0: + raise ValueError("argument n_samples needs to be a scalar (is %s)" % n_samples) + + if n_samples <= 0: + raise ValueError("argument n_samples needs to be positive (is %d)" % n_samples) + + sample = np.log(self._sample_from_prior(n_samples=n_samples)) + + if np.any(~np.isfinite(sample)): + raise ValueError("Sample %s from prior %s contains infinite values!" % (sample, self)) + + return sample + + def get_log_probability(self, theta: float) -> float: + """Returns the log probability of theta. This method exponentiates theta and calls `self._get_log_probability`. + + Warning + ------- + Theta must be on a log scale! + + Parameters + ---------- + theta : float + Hyperparameter configuration in log space. + + Returns + ------- + float + The log probability of theta + """ + return self._get_log_probability(np.exp(theta)) + + def get_gradient(self, theta: float) -> float: + """Computes the gradient of the prior with respect to theta. Internally, his method calls `self._get_gradient`. + + Warning + ------- + Theta must be on the original scale. + + Parameters + ---------- + theta : float + Hyperparameter configuration in log space + + Returns + ------- + gradient : float + The gradient of the prior at theta. + """ + return self._get_gradient(np.exp(theta)) + + @abstractmethod + def _get_log_probability(self, theta: float) -> float: + """Return the log probability of theta. + + Warning + ------- + Theta must be on the original scale. + + Parameters + ---------- + theta : float + Hyperparameter configuration on the original scale. + + Returns + ------- + float + The log probability of theta + """ + raise NotImplementedError() + + @abstractmethod + def _get_gradient(self, theta: float) -> float: + """Computes the gradient of the prior with respect to theta. + + Parameters + ---------- + theta : float + Hyperparameter configuration in the original space space + + Returns + ------- + gradient : float + The gradient of the prior at theta. + """ + raise NotImplementedError() + + @abstractmethod + def _sample_from_prior(self, n_samples: int) -> np.ndarray: + """Returns `n_samples` from the prior. All samples are on a original scale. + + Parameters + ---------- + n_samples : int + The number of samples that will be drawn. + + Returns + ------- + np.ndarray + """ + raise NotImplementedError() diff --git a/smac/model/gaussian_process/priors/gamma_prior.py b/smac/model/gaussian_process/priors/gamma_prior.py new file mode 100644 index 000000000..67ef29345 --- /dev/null +++ b/smac/model/gaussian_process/priors/gamma_prior.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np +import scipy.stats as sps + +from smac.model.gaussian_process.priors.abstract_prior import AbstractPrior + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class GammaPrior(AbstractPrior): + """Implementation of gamma prior. + + f(x) = (x-loc)**(a-1) * e**(-(x-loc)) * (1/scale)**a / gamma(a) + + Parameters + ---------- + a : float + The shape parameter. Must be greater than 0. + scale : float + The scale parameter (1/scale corresponds to parameter p in canonical form). Must be greather than 0. + loc : float + Mean parameter for the distribution. + seed : int, defaults to 0 + """ + + def __init__( + self, + a: float, + scale: float, + loc: float, + seed: int = 0, + ): + super().__init__(seed=seed) + assert a > 0 + assert scale > 0 + + self._a = a + self._loc = loc + self._scale = scale + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update( + { + "a": self._a, + "loc": self._loc, + "scale": self._scale, + } + ) + + return meta + + def _sample_from_prior(self, n_samples: int) -> np.ndarray: + return self._rng.gamma(shape=self._a, scale=self._scale, size=n_samples) + + def _get_log_probability(self, theta: float) -> float: + """Returns the log pdf of theta.""" + if np.ndim(theta) != 0: + raise NotImplementedError() + + return sps.gamma.logpdf(theta, a=self._a, scale=self._scale, loc=self._loc) + + def _get_gradient(self, theta: float) -> float: + """Get gradient as computed by Wolfram Alpha.""" + if np.ndim(theta) == 0: + # Multiply by theta because of the chain rule... + return ((self._a - 1) / theta - (1 / self._scale)) * theta + else: + raise NotImplementedError() diff --git a/smac/model/gaussian_process/priors/horseshoe_prior.py b/smac/model/gaussian_process/priors/horseshoe_prior.py new file mode 100644 index 000000000..d3e298929 --- /dev/null +++ b/smac/model/gaussian_process/priors/horseshoe_prior.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +from typing import Any + +import math + +import numpy as np + +from smac.constants import VERY_SMALL_NUMBER +from smac.model.gaussian_process.priors.abstract_prior import AbstractPrior + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class HorseshoePrior(AbstractPrior): + """Horseshoe Prior as it is used in spearmint. + + Parameters + ---------- + scale: float + Scaling parameter. + seed : int, defaults to 0 + """ + + def __init__(self, scale: float, seed: int = 0): + super().__init__(seed=seed) + self._scale = scale + self._scale_square = scale**2 + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update({"scale": self._scale}) + + return meta + + def _sample_from_prior(self, n_samples: int) -> np.ndarray: + # This is copied from RoBO - scale is most likely the tau parameter + lamda = np.abs(self._rng.standard_cauchy(size=n_samples)) + p0 = np.abs(self._rng.randn() * lamda * self._scale) + return p0 + + def _get_log_probability(self, theta: float) -> float: + # We computed it exactly as in the original spearmint code, they basically say that there's no analytical form + # of the horseshoe prior, but that the multiplier is bounded between 2 and 4 and that they used the middle + # See "The horseshoe estimator for sparse signals" by Carvalho, Poloson and Scott (2010), Equation 1. + # https://www.jstor.org/stable/25734098 + # Compared to the paper by Carvalho, there's a constant multiplicator missing + # Compared to Spearmint we first have to undo the log space transformation of the theta + # Note: "undo log space transformation" is done in parent class + if theta == 0: + return np.inf # POSITIVE infinity (this is the "spike") + else: + a = math.log(1 + 3.0 * (self._scale_square / theta**2)) + return math.log(a + VERY_SMALL_NUMBER) + + def _get_gradient(self, theta: float) -> float: + if theta == 0: + return np.inf # POSITIVE infinity (this is the "spike") + else: + a = -(6 * self._scale_square) + b = 3 * self._scale_square + theta**2 + b *= math.log(3 * self._scale_square * theta ** (-2) + 1) + b = max(b, 1e-14) + + return a / b diff --git a/smac/model/gaussian_process/priors/log_normal_prior.py b/smac/model/gaussian_process/priors/log_normal_prior.py new file mode 100644 index 000000000..6c39c787c --- /dev/null +++ b/smac/model/gaussian_process/priors/log_normal_prior.py @@ -0,0 +1,68 @@ +from __future__ import annotations + +from typing import Any + +import math + +import numpy as np + +from smac.model.gaussian_process.priors.abstract_prior import AbstractPrior + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class LogNormalPrior(AbstractPrior): + """Implements the log normal prior. + + Parameters + ---------- + sigma : float + Specifies the standard deviation of the normal distribution. + mean : float + Specifies the mean of the normal distribution. + seed : int, defaults to 0 + """ + + def __init__( + self, + sigma: float, + mean: float = 0, + seed: int = 0, + ): + super().__init__(seed=seed) + + if mean != 0: + raise NotImplementedError(mean) + + self._sigma = sigma + self._sigma_square = sigma**2 + self._mean = mean + self._sqrt_2_pi = np.sqrt(2 * np.pi) + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update({"sigma": self._sigma, "mean": self._mean}) + + return meta + + def _sample_from_prior(self, n_samples: int) -> np.ndarray: + return self._rng.lognormal(mean=self._mean, sigma=self._sigma, size=n_samples) + + def _get_log_probability(self, theta: float) -> float: + if theta <= self._mean: + return -1e25 + else: + rval = -((math.log(theta) - self._mean) ** 2) / (2 * self._sigma_square) - math.log( + self._sqrt_2_pi * self._sigma * theta + ) + return rval + + def _get_gradient(self, theta: float) -> float: + if theta <= 0: + return 0 + else: + # Derivative of log(1 / (x * s^2 * sqrt(2 pi)) * exp( - 0.5 * (log(x ) / s^2))^2)) + # This is without the mean! + return -(self._sigma_square + math.log(theta)) / (self._sigma_square * (theta)) * theta diff --git a/smac/model/gaussian_process/priors/tophat_prior.py b/smac/model/gaussian_process/priors/tophat_prior.py new file mode 100644 index 000000000..8706003da --- /dev/null +++ b/smac/model/gaussian_process/priors/tophat_prior.py @@ -0,0 +1,164 @@ +from __future__ import annotations + +from typing import Any + +import warnings + +import numpy as np + +from smac.model.gaussian_process.priors.abstract_prior import AbstractPrior + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class TophatPrior(AbstractPrior): + """Tophat prior as it used in the original spearmint code. + + Parameters + ---------- + lower_bound : float + Lower bound of the prior. In original scale. + upper_bound : float + Upper bound of the prior. In original scale. + seed : int, defaults to 0 + """ + + def __init__( + self, + lower_bound: float, + upper_bound: float, + seed: int = 0, + ): + super().__init__(seed=seed) + self._min = lower_bound + self._log_min = np.log(lower_bound) + self._max = upper_bound + self._log_max = np.log(upper_bound) + + if not (self._max > self._min): + raise Exception("Upper bound of Tophat prior must be greater than the lower bound.") + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update({"lower_bound": self._min, "upper_bound": self._max}) + + return meta + + def _get_log_probability(self, theta: float) -> float: + if theta < self._min or theta > self._max: + return -np.inf + else: + return 0 + + def _sample_from_prior(self, n_samples: int) -> np.ndarray: + if np.ndim(n_samples) != 0: + raise ValueError("The argument `n_samples` needs to be a scalar (is %s)." % n_samples) + + if n_samples <= 0: + raise ValueError("The argument `n_samples` needs to be positive (is %d)." % n_samples) + + p0 = np.exp(self._rng.uniform(low=self._log_min, high=self._log_max, size=(n_samples,))) + + return p0 + + def _get_gradient(self, theta: float) -> float: + return 0 + + def get_gradient(self, theta: float) -> float: # noqa: D102 + return 0 + + +class SoftTopHatPrior(AbstractPrior): + """Soft Tophat prior as it used in the original spearmint code. + + Parameters + ---------- + lower_bound : float + Lower bound of the prior. In original scale. + upper_bound : float + Upper bound of the prior. In original scale. + exponent : float + Exponent of the prior. + seed : int, defaults to 0 + """ + + def __init__( + self, + lower_bound: float, + upper_bound: float, + exponent: float, + seed: int = 0, + ) -> None: + super().__init__(seed=seed) + + with warnings.catch_warnings(): + warnings.simplefilter("error") + self._lower_bound = lower_bound + try: + self._log_lower_bound = np.log(lower_bound) + except RuntimeWarning as w: + if "invalid value encountered in log" in w.args[0]: + raise ValueError("Invalid lower bound %f (cannot compute log)" % lower_bound) + + raise w + self._upper_bound = upper_bound + try: + self._log_upper_bound = np.log(upper_bound) + except RuntimeWarning as w: + if "invalid value encountered in log" in w.args[0]: + raise ValueError("Invalid lower bound %f (cannot compute log)" % lower_bound) + + raise w + + if exponent <= 0: + raise ValueError("Exponent cannot be less or equal than zero (but is %f)" % exponent) + + self._exponent = exponent + + def __repr__(self) -> str: + return "SoftTopHatPrior(lower_bound=%f, upper_bound=%f)" % ( + self._lower_bound, + self._upper_bound, + ) + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update({"lower_bound": self._lower_bound, "upper_bound": self._upper_bound, "exponent": self._exponent}) + + return meta + + def get_log_probability(self, theta: float) -> float: # noqa: D102 + # We need to use lnprob here instead of _lnprob to have the squared function work + # in the logarithmic space, too. + if np.ndim(theta) == 0: + if theta < self._log_lower_bound: + return -((theta - self._log_lower_bound) ** self._exponent) + elif theta > self._log_upper_bound: + return -((self._log_upper_bound - theta) ** self._exponent) + else: + return 0 + else: + raise NotImplementedError() + + def get_gradient(self, theta: float) -> float: # noqa: D102 + if np.ndim(theta) == 0: + if theta < self._log_lower_bound: + return -self._exponent * (theta - self._log_lower_bound) + elif theta > self._log_upper_bound: + return self._exponent * (self._log_upper_bound - theta) + else: + return 0 + else: + raise NotImplementedError() + + def _get_log_probability(self, theta: float) -> float: + return 0 + + def _get_gradient(self, theta: float) -> float: + return 0 + + def _sample_from_prior(self, n_samples: int) -> np.ndarray: + return np.exp(self._rng.uniform(self._log_lower_bound, self._log_upper_bound, size=(n_samples,))) diff --git a/smac/model/multi_objective_model.py b/smac/model/multi_objective_model.py new file mode 100644 index 000000000..23a765a8f --- /dev/null +++ b/smac/model/multi_objective_model.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +from typing import TypeVar + +import numpy as np + +from smac.model.abstract_model import AbstractModel + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +Self = TypeVar("Self", bound="MultiObjectiveModel") + + +class MultiObjectiveModel(AbstractModel): + """Wrapper for the surrogate model to predict multiple objectives. + + Parameters + ---------- + models : AbstractModel | list[AbstractModel] + Which model should be used. If it is a list, then it must provide as many models as objectives. + If it is a single model only, the model is used for all objectives. + objectives : list[str] + Which objectives should be used. + seed : int + """ + + def __init__( + self, + models: AbstractModel | list[AbstractModel], + objectives: list[str], + seed: int = 0, + ) -> None: + self._n_objectives = len(objectives) + if isinstance(models, list): + assert len(models) == len(objectives) + + # Make sure the configspace is the same + configspace = models[0]._configspace + for m in models: + assert configspace == m._configspace + + self._models = models + else: + configspace = models._configspace + self._models = [models for _ in range(self._n_objectives)] + + super().__init__( + configspace=configspace, + instance_features=None, + pca_components=None, + seed=seed, + ) + + @property + def models(self) -> list[AbstractModel]: + """The internally used surrogate models.""" + return self._models + + def predict_marginalized(self, X: np.ndarray) -> tuple[np.ndarray, np.ndarray]: # noqa: D102 + mean = np.zeros((X.shape[0], self._n_objectives)) + var = np.zeros((X.shape[0], self._n_objectives)) + + for i, estimator in enumerate(self._models): + m, v = estimator.predict_marginalized(X) + mean[:, i] = m.flatten() + var[:, i] = v.flatten() + + return mean, var + + def _train(self: Self, X: np.ndarray, Y: np.ndarray) -> Self: + if len(self._models) == 0: + raise ValueError("The list of surrogate models is empty.") + + for i, model in enumerate(self._models): + model.train(X, Y[:, i]) + + return self + + def _predict( + self, + X: np.ndarray, + covariance_type: str | None = "diagonal", + ) -> tuple[np.ndarray, np.ndarray | None]: + if covariance_type != "diagonal": + raise ValueError("`covariance_type` can only take `diagonal` for this model.") + + mean = np.zeros((X.shape[0], self._n_objectives)) + var = np.zeros((X.shape[0], self._n_objectives)) + + for i, estimator in enumerate(self._models): + m, v = estimator.predict(X) + assert v is not None + mean[:, i] = m.flatten() + var[:, i] = v.flatten() + + return mean, var diff --git a/smac/model/random_forest/__init__.py b/smac/model/random_forest/__init__.py new file mode 100644 index 000000000..4a1ace1f0 --- /dev/null +++ b/smac/model/random_forest/__init__.py @@ -0,0 +1,7 @@ +from smac.model.random_forest.abstract_random_forest import AbstractRandomForest +from smac.model.random_forest.random_forest import RandomForest + +__all__ = [ + "AbstractRandomForest", + "RandomForest", +] diff --git a/smac/model/random_forest/abstract_random_forest.py b/smac/model/random_forest/abstract_random_forest.py new file mode 100644 index 000000000..d4f1f7ce3 --- /dev/null +++ b/smac/model/random_forest/abstract_random_forest.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np +from ConfigSpace import ( + CategoricalHyperparameter, + Constant, + UniformFloatHyperparameter, + UniformIntegerHyperparameter, +) + +from smac.model.abstract_model import AbstractModel + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class AbstractRandomForest(AbstractModel): + """Abstract base class for all random forest models.""" + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + self._conditional: dict[int, bool] = dict() + self._impute_values: dict[int, float] = dict() + + def _impute_inactive(self, X: np.ndarray) -> np.ndarray: + X = X.copy() + for idx, hp in enumerate(self._configspace.get_hyperparameters()): + if idx not in self._conditional: + parents = self._configspace.get_parents_of(hp.name) + if len(parents) == 0: + self._conditional[idx] = False + else: + self._conditional[idx] = True + if isinstance(hp, CategoricalHyperparameter): + self._impute_values[idx] = len(hp.choices) + elif isinstance(hp, (UniformFloatHyperparameter, UniformIntegerHyperparameter)): + self._impute_values[idx] = -1 + elif isinstance(hp, Constant): + self._impute_values[idx] = 1 + else: + raise ValueError + + if self._conditional[idx] is True: + nonfinite_mask = ~np.isfinite(X[:, idx]) + X[nonfinite_mask, idx] = self._impute_values[idx] + + return X diff --git a/smac/model/random_forest/random_forest.py b/smac/model/random_forest/random_forest.py new file mode 100644 index 000000000..2c47dfdac --- /dev/null +++ b/smac/model/random_forest/random_forest.py @@ -0,0 +1,312 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np +from ConfigSpace import ConfigurationSpace +from pyrfr import regression +from pyrfr.regression import binary_rss_forest as BinaryForest +from pyrfr.regression import default_data_container as DataContainer + +from smac.constants import N_TREES, VERY_SMALL_NUMBER +from smac.model.random_forest import AbstractRandomForest + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class RandomForest(AbstractRandomForest): + """Random forest that takes instance features into account. + + Parameters + ---------- + n_trees : int, defaults to `N_TREES` + The number of trees in the random forest. + n_points_per_tree : int, defaults to -1 + Number of points per tree. If the value is smaller than 0, the number of samples will be used. + ratio_features : float, defaults to 5.0 / 6.0 + The ratio of features that are considered for splitting. + min_samples_split : int, defaults to 3 + The minimum number of data points to perform a split. + min_samples_leaf : int, defaults to 3 + The minimum number of data points in a leaf. + max_depth : int, defaults to 2**20 + The maximum depth of a single tree. + eps_purity : float, defaults to 1e-8 + The minimum difference between two target values to be considered. + max_nodes : int, defaults to 2**20 + The maximum total number of nodes in a tree. + bootstrapping : bool, defaults to True + Enables bootstrapping. + log_y: bool, defaults to False + The y values (passed to this random forest) are expected to be log(y) transformed. + This will be considered during predicting. + instance_features : dict[str, list[int | float]] | None, defaults to None + Features (list of int or floats) of the instances (str). The features are incorporated into the X data, + on which the model is trained on. + pca_components : float, defaults to 7 + Number of components to keep when using PCA to reduce dimensionality of instance features. + seed : int + """ + + def __init__( + self, + configspace: ConfigurationSpace, + n_trees: int = N_TREES, + n_points_per_tree: int = -1, + ratio_features: float = 5.0 / 6.0, + min_samples_split: int = 3, + min_samples_leaf: int = 3, + max_depth: int = 2**20, + eps_purity: float = 1e-8, + max_nodes: int = 2**20, + bootstrapping: bool = True, + log_y: bool = False, + instance_features: dict[str, list[int | float]] | None = None, + pca_components: int | None = 7, + seed: int = 0, + ) -> None: + super().__init__( + configspace=configspace, + instance_features=instance_features, + pca_components=pca_components, + seed=seed, + ) + + max_features = 0 if ratio_features > 1.0 else max(1, int(len(self._types) * ratio_features)) + + self._rf_opts = regression.forest_opts() + self._rf_opts.num_trees = n_trees + self._rf_opts.do_bootstrapping = bootstrapping + self._rf_opts.tree_opts.max_features = max_features + self._rf_opts.tree_opts.min_samples_to_split = min_samples_split + self._rf_opts.tree_opts.min_samples_in_leaf = min_samples_leaf + self._rf_opts.tree_opts.max_depth = max_depth + self._rf_opts.tree_opts.epsilon_purity = eps_purity + self._rf_opts.tree_opts.max_num_nodes = max_nodes + self._rf_opts.compute_law_of_total_variance = False + self._rf: BinaryForest | None = None + self._log_y = log_y + self._rng = regression.default_random_engine(seed) + + self._n_trees = n_trees + self._n_points_per_tree = n_points_per_tree + self._ratio_features = ratio_features + self._min_samples_split = min_samples_split + self._min_samples_leaf = min_samples_leaf + self._max_depth = max_depth + self._eps_purity = eps_purity + self._max_nodes = max_nodes + self._bootstrapping = bootstrapping + + # This list well be read out by save_iteration() in the solver + # self._hypers = [ + # n_trees, + # max_nodes, + # bootstrapping, + # n_points_per_tree, + # ratio_features, + # min_samples_split, + # min_samples_leaf, + # max_depth, + # eps_purity, + # self._seed, + # ] + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update( + { + "n_trees": self._n_trees, + "n_points_per_tree": self._n_points_per_tree, + "ratio_features": self._ratio_features, + "min_samples_split": self._min_samples_split, + "min_samples_leaf": self._min_samples_leaf, + "max_depth": self._max_depth, + "eps_purity": self._eps_purity, + "max_nodes": self._max_nodes, + "bootstrapping": self._bootstrapping, + "pca_components": self._pca_components, + } + ) + + return meta + + def _train(self, X: np.ndarray, y: np.ndarray) -> RandomForest: + X = self._impute_inactive(X) + y = y.flatten() + + # self.X = X + # self.y = y.flatten() + + if self._n_points_per_tree <= 0: + self._rf_opts.num_data_points_per_tree = X.shape[0] + else: + self._rf_opts.num_data_points_per_tree = self._n_points_per_tree + + self._rf = regression.binary_rss_forest() + self._rf.options = self._rf_opts + + data = self._init_data_container(X, y) + self._rf.fit(data, rng=self._rng) + + return self + + def _init_data_container(self, X: np.ndarray, y: np.ndarray) -> DataContainer: + """Fills a pyrfr default data container s.t. the forest knows categoricals and bounds for continous data. + + Parameters + ---------- + X : np.ndarray [#samples, #hyperparameter + #features] + Input data points. + Y : np.ndarray [#samples, #objectives] + The corresponding target values. + + Returns + ------- + data : DataContainer + The filled data container that pyrfr can interpret. + """ + # Retrieve the types and the bounds from the ConfigSpace + data = regression.default_data_container(X.shape[1]) + + for i, (mn, mx) in enumerate(self._bounds): + if np.isnan(mx): + data.set_type_of_feature(i, mn) + else: + data.set_bounds_of_feature(i, mn, mx) + + for row_X, row_y in zip(X, y): + data.add_data_point(row_X, row_y) + + return data + + def _predict( + self, + X: np.ndarray, + covariance_type: str | None = "diagonal", + ) -> tuple[np.ndarray, np.ndarray | None]: + if len(X.shape) != 2: + raise ValueError("Expected 2d array, got %dd array!" % len(X.shape)) + + if X.shape[1] != len(self._types): + raise ValueError("Rows in X should have %d entries but have %d!" % (len(self._types), X.shape[1])) + + if covariance_type != "diagonal": + raise ValueError("`covariance_type` can only take `diagonal` for this model.") + + assert self._rf is not None + X = self._impute_inactive(X) + + if self._log_y: + all_preds = [] + third_dimension = 0 + + # Gather data in a list of 2d arrays and get statistics about the required size of the 3d array + for row_X in X: + preds_per_tree = self._rf.all_leaf_values(row_X) + all_preds.append(preds_per_tree) + max_num_leaf_data = max(map(len, preds_per_tree)) + third_dimension = max(max_num_leaf_data, third_dimension) + + # Transform list of 2d arrays into a 3d array + preds_as_array = np.zeros((X.shape[0], self._rf_opts.num_trees, third_dimension)) * np.NaN + for i, preds_per_tree in enumerate(all_preds): + for j, pred in enumerate(preds_per_tree): + preds_as_array[i, j, : len(pred)] = pred + + # Do all necessary computation with vectorized functions + preds_as_array = np.log(np.nanmean(np.exp(preds_as_array), axis=2) + VERY_SMALL_NUMBER) + + # Compute the mean and the variance across the different trees + means = preds_as_array.mean(axis=1) + vars_ = preds_as_array.var(axis=1) + else: + means, vars_ = [], [] + for row_X in X: + mean_, var = self._rf.predict_mean_var(row_X) + means.append(mean_) + vars_.append(var) + + means = np.array(means) + vars_ = np.array(vars_) + + return means.reshape((-1, 1)), vars_.reshape((-1, 1)) + + def predict_marginalized(self, X: np.ndarray) -> tuple[np.ndarray, np.ndarray]: + """Predicts mean and variance marginalized over all instances. + + Note + ---- + The method is random forest specific and follows the SMAC2 implementation. It requires + no distribution assumption to marginalize the uncertainty estimates. + + Parameters + ---------- + X : np.ndarray [#samples, #hyperparameter + #features] + Input data points. + + Returns + ------- + means : np.ndarray [#samples, 1] + The predictive mean. + vars : np.ndarray [#samples, 1] + The predictive variance. + """ + if self._n_features == 0: + mean_, var = self.predict(X) + assert var is not None + + var[var < self._var_threshold] = self._var_threshold + var[np.isnan(var)] = self._var_threshold + + return mean_, var + + assert self._instance_features is not None + + if len(X.shape) != 2: + raise ValueError("Expected 2d array, got %dd array!" % len(X.shape)) + + if X.shape[1] != len(self._bounds): + raise ValueError("Rows in X should have %d entries but have %d!" % (len(self._bounds), X.shape[1])) + + assert self._rf is not None + X = self._impute_inactive(X) + + dat_ = np.zeros((X.shape[0], self._rf_opts.num_trees)) # Marginalized predictions for each tree + for i, x in enumerate(X): + + # Marginalize over instances + # 1. get all leaf values for each tree + preds_trees: list[list[float]] = [[] for i in range(self._rf_opts.num_trees)] + + for feat in self._instance_features.values(): + x_ = np.concatenate([x, feat]) + preds_per_tree = self._rf.all_leaf_values(x_) + for tree_id, preds in enumerate(preds_per_tree): + preds_trees[tree_id] += preds + + # 2. average in each tree + if self._log_y: + for tree_id in range(self._rf_opts.num_trees): + dat_[i, tree_id] = np.log(np.exp(np.array(preds_trees[tree_id])).mean()) + else: + for tree_id in range(self._rf_opts.num_trees): + dat_[i, tree_id] = np.array(preds_trees[tree_id]).mean() + + # 3. compute statistics across trees + mean_ = dat_.mean(axis=1) + var = dat_.var(axis=1) + + if var is None: + raise RuntimeError("The variance must not be none.") + + var[var < self._var_threshold] = self._var_threshold + + if len(mean_.shape) == 1: + mean_ = mean_.reshape((-1, 1)) + if len(var.shape) == 1: + var = var.reshape((-1, 1)) + + return mean_, var diff --git a/smac/model/random_model.py b/smac/model/random_model.py new file mode 100644 index 000000000..0301e5188 --- /dev/null +++ b/smac/model/random_model.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +import numpy as np + +from smac.model.abstract_model import AbstractModel +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class RandomModel(AbstractModel): + """AbstractModel which returns random values on a call to `fit`.""" + + def _train(self, X: np.ndarray, Y: np.ndarray) -> RandomModel: + if not isinstance(X, np.ndarray): + raise NotImplementedError("X has to be of type np.ndarray.") + if not isinstance(Y, np.ndarray): + raise NotImplementedError("Y has to be of type np.ndarray.") + + logger.debug("(Pseudo) fit model to data.") + return self + + def _predict( + self, + X: np.ndarray, + covariance_type: str | None = "diagonal", + ) -> tuple[np.ndarray, np.ndarray | None]: + if covariance_type != "diagonal": + raise ValueError("`covariance_type` can only take `diagonal` for this model.") + + if not isinstance(X, np.ndarray): + raise NotImplementedError("X has to be of type np.ndarray.") + + return self._rng.rand(len(X), 1), self._rng.rand(len(X), 1) diff --git a/smac/multi_objective/__init__.py b/smac/multi_objective/__init__.py index e69de29bb..a8caa1fdc 100644 --- a/smac/multi_objective/__init__.py +++ b/smac/multi_objective/__init__.py @@ -0,0 +1,11 @@ +from smac.multi_objective.abstract_multi_objective_algorithm import ( + AbstractMultiObjectiveAlgorithm, +) +from smac.multi_objective.aggregation_strategy import MeanAggregationStrategy +from smac.multi_objective.parego import ParEGO + +__all__ = [ + "AbstractMultiObjectiveAlgorithm", + "MeanAggregationStrategy", + "ParEGO", +] diff --git a/smac/multi_objective/abstract_multi_objective_algorithm.py b/smac/multi_objective/abstract_multi_objective_algorithm.py index 3d321772d..f2f56a213 100644 --- a/smac/multi_objective/abstract_multi_objective_algorithm.py +++ b/smac/multi_objective/abstract_multi_objective_algorithm.py @@ -1,19 +1,45 @@ from __future__ import annotations -from abc import ABC -from typing import Optional +from abc import ABC, abstractmethod +from typing import Any -import numpy as np +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" class AbstractMultiObjectiveAlgorithm(ABC): + """A general interface for multi-objective optimizer, depending on different strategies. + + Parameters + ---------- + scenario : Scenario + seed : int | None, defaults to None """ - A general interface for multi-objective optimizer, depending on different strategies. - It can be applied to rh2epm or epmchooser. - """ - def __init__(self, rng: Optional[np.random.RandomState] = None): - if rng is None: - rng = np.random.RandomState(0) + def __init__(self) -> None: + pass + + @property + def meta(self) -> dict[str, Any]: + """Returns the meta data of the created object.""" + return {"name": self.__class__.__name__} + + def update_on_iteration_start(self) -> None: + """Update the internal state on start of each SMBO iteration.""" + pass + + @abstractmethod + def __call__(self, values: list[float]) -> float: + """Transform a multi-objective loss to a single loss. + + Parameters + ---------- + values : list[float] + Normalized values in the range [0, 1]. - self.rng = rng + Returns + ------- + cost : float + Combined cost. + """ + raise NotImplementedError diff --git a/smac/multi_objective/aggregation_strategy.py b/smac/multi_objective/aggregation_strategy.py index b5b9fac46..bac0a0097 100644 --- a/smac/multi_objective/aggregation_strategy.py +++ b/smac/multi_objective/aggregation_strategy.py @@ -1,56 +1,44 @@ from __future__ import annotations -from abc import abstractmethod +from typing import Any import numpy as np from smac.multi_objective.abstract_multi_objective_algorithm import ( AbstractMultiObjectiveAlgorithm, ) +from smac.scenario import Scenario -class AggregationStrategy(AbstractMultiObjectiveAlgorithm): - """ - An abstract class to aggregate multi-objective losses to a single objective loss, - which can then be utilized by the single-objective optimizer. - """ +class MeanAggregationStrategy(AbstractMultiObjectiveAlgorithm): + """A class to mean-aggregate multi-objective costs to a single cost. - @abstractmethod - def __call__(self, values: list[float]) -> float: - """ - Transform a multi-objective loss to a single loss. + Parameters + ---------- + scenario : Scenario + objective_weights : list[float] | None, defaults to None + Weights for an weighted average. Must be of the same length as the number of objectives. + """ - Parameters - ---------- - values : list[float] - Normalized values. + def __init__( + self, + scenario: Scenario, + objective_weights: list[float] | None = None, + ): + super(MeanAggregationStrategy, self).__init__() - Returns - ------- - cost : float - Combined cost. - """ - raise NotImplementedError + if objective_weights is not None and scenario.count_objectives() != len(objective_weights): + raise ValueError("Number of objectives and number of weights must be equal.") + self._objective_weights = objective_weights -class MeanAggregationStrategy(AggregationStrategy): - """ - A class to mean-aggregate multi-objective losses to a single objective losses, - which can then be utilized by the single-objective optimizer. - """ + @property + def meta(self) -> dict[str, Any]: + """Returns the meta data of the created object.""" + return { + "name": self.__class__.__name__, + "objective_weights": self._objective_weights, + } - def __call__(self, values: list[float]) -> float: - """ - Transform a multi-objective loss to a single loss. - - Parameters - ---------- - values : list[float] - Normalized values. - - Returns - ------- - cost : float - Combined cost. - """ - return np.mean(values, axis=0) + def __call__(self, values: list[float]) -> float: # noqa: D102 + return float(np.average(values, axis=0, weights=self._objective_weights)) diff --git a/smac/multi_objective/parego.py b/smac/multi_objective/parego.py index 94439292c..60d02e44a 100644 --- a/smac/multi_objective/parego.py +++ b/smac/multi_objective/parego.py @@ -1,41 +1,65 @@ from __future__ import annotations -from typing import Optional +from typing import Any import numpy as np -from smac.multi_objective.aggregation_strategy import AggregationStrategy +from smac.multi_objective.abstract_multi_objective_algorithm import ( + AbstractMultiObjectiveAlgorithm, +) +from smac.scenario import Scenario -class ParEGO(AggregationStrategy): +class ParEGO(AbstractMultiObjectiveAlgorithm): + """ParEGO implementation based on https://www.cs.bham.ac.uk/~jdk/UKCI-2015.pdf. + + Parameters + ---------- + scenario : Scenario + rho : float, defaults to 0.05 + A small positive value. + seed : int | None, defaults to None + """ + def __init__( self, - rng: Optional[np.random.RandomState] = None, + scenario: Scenario, rho: float = 0.05, + seed: int | None = None, ): - super(ParEGO, self).__init__(rng=rng) - self.rho = rho + super(ParEGO, self).__init__() + + if seed is None: + seed = scenario.seed + + self._n_objectives = scenario.count_objectives() + self._seed = seed + self._rng = np.random.RandomState(seed) + + self._rho = rho + self._theta = self._rng.rand(self._n_objectives) + self.update_on_iteration_start() - def __call__(self, values: list[float]) -> float: - """ - Transform a multi-objective loss to a single loss. + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update( + { + "name": self.__class__.__name__, + "rho": self._rho, + "seed": self._seed, + } + ) - Parameters - ---------- - values : list[float] - Normalized values. + return meta - Returns - ------- - cost : float - Combined cost. - """ - # Then we have to compute the weight - theta = self.rng.rand(len(values)) + def update_on_iteration_start(self) -> None: # noqa: D102 + self._theta = self._rng.rand(self._n_objectives) - # Normalize st all theta values sum up to 1 - theta = theta / (np.sum(theta) + 1e-10) + # Normalize so that all theta values sum up to 1 + self._theta = self._theta / (np.sum(self._theta) + 1e-10) + def __call__(self, values: list[float]) -> float: # noqa: D102 # Weight the values - theta_f = theta * values - return np.max(theta_f, axis=0) + self.rho * np.sum(theta_f, axis=0) + theta_f = self._theta * values + return float(np.max(theta_f, axis=0) + self._rho * np.sum(theta_f, axis=0)) diff --git a/smac/optimizer/acquisition/__init__.py b/smac/optimizer/acquisition/__init__.py deleted file mode 100644 index 1753a5c8e..000000000 --- a/smac/optimizer/acquisition/__init__.py +++ /dev/null @@ -1,731 +0,0 @@ -# encoding=utf8 -import abc -from typing import Any, List, Tuple - -import copy - -import numpy as np -from ConfigSpace.hyperparameters import FloatHyperparameter -from scipy.stats import norm - -from smac.configspace import Configuration -from smac.configspace.util import convert_configurations_to_array -from smac.epm.base_epm import BaseEPM -from smac.utils.logging import PickableLoggerAdapter - -__author__ = "Aaron Klein, Marius Lindauer" -__copyright__ = "Copyright 2017, ML4AAD" -__license__ = "3-clause BSD" - - -class AbstractAcquisitionFunction(object, metaclass=abc.ABCMeta): - """Abstract base class for acquisition function. - - Parameters - ---------- - model : BaseEPM - Models the objective function. - - Attributes - ---------- - model - logger - """ - - def __init__(self, model: BaseEPM): - self.model = model - self._required_updates = ("model",) # type: Tuple[str, ...] - self.logger = PickableLoggerAdapter(self.__module__ + "." + self.__class__.__name__) - - def update(self, **kwargs: Any) -> None: - """Update the acquisition function attributes required for calculation. - - This method will be called after fitting the model, but before maximizing the acquisition - function. As an examples, EI uses it to update the current fmin. - - The default implementation only updates the attributes of the acqusition function which - are already present. - - Parameters - ---------- - kwargs - """ - for key in self._required_updates: - if key not in kwargs: - raise ValueError( - "Acquisition function %s needs to be updated with key %s, but only got " - "keys %s." % (self.__class__.__name__, key, list(kwargs.keys())) - ) - for key in kwargs: - if key in self._required_updates: - setattr(self, key, kwargs[key]) - - def __call__(self, configurations: List[Configuration]) -> np.ndarray: - """Computes the acquisition value for a given X. - - Parameters - ---------- - configurations : list - The configurations where the acquisition function - should be evaluated. - - Returns - ------- - np.ndarray(N, 1) - acquisition values for X - """ - X = convert_configurations_to_array(configurations) - if len(X.shape) == 1: - X = X[np.newaxis, :] - - acq = self._compute(X) - if np.any(np.isnan(acq)): - idx = np.where(np.isnan(acq))[0] - acq[idx, :] = -np.finfo(float).max - return acq - - @abc.abstractmethod - def _compute(self, X: np.ndarray) -> np.ndarray: - """Computes the acquisition value for a given point X. This function has to be overwritten - in a derived class. - - Parameters - ---------- - X : np.ndarray - The input points where the acquisition function - should be evaluated. The dimensionality of X is (N, D), with N as - the number of points to evaluate at and D is the number of - dimensions of one X. - - Returns - ------- - np.ndarray(N,1) - Acquisition function values wrt X - """ - raise NotImplementedError() - - -class IntegratedAcquisitionFunction(AbstractAcquisitionFunction): - r"""Marginalize over Model hyperparameters to compute the integrated acquisition function. - - See "Practical Bayesian Optimization of Machine Learning Algorithms" by Jasper Snoek et al. - (https://papers.nips.cc/paper/4522-practical-bayesian-optimization-of-machine-learning-algorithms.pdf) - for further details. - """ - - def __init__(self, model: BaseEPM, acquisition_function: AbstractAcquisitionFunction, **kwargs: Any): - """Constructor. - - Parameters - ---------- - model : BaseEPM - The model needs to implement an additional attribute ``models`` which contains the different models to - integrate over. - kwargs - Additional keyword arguments - """ - super().__init__(model) - self.long_name = "Integrated Acquisition Function (%s)" % acquisition_function.__class__.__name__ - self.acq = acquisition_function - self._functions = [] # type: List[AbstractAcquisitionFunction] - self.eta = None - - def update(self, **kwargs: Any) -> None: - """Update the acquisition functions values. - - This method will be called if the model is updated. E.g. entropy search uses it to update its approximation - of P(x=x_min), EI uses it to update the current fmin. - - This implementation creates an acquisition function object for each model to integrate over and sets the - respective attributes for each acquisition function object. - - Parameters - ---------- - model : BaseEPM - The model needs to implement an additional attribute ``models`` which contains the different models to - integrate over. - kwargs - """ - model = kwargs["model"] - del kwargs["model"] - if not hasattr(model, "models") or len(model.models) == 0: - raise ValueError("IntegratedAcquisitionFunction requires at least one model to integrate!") - if len(self._functions) == 0 or len(self._functions) != len(model.models): - self._functions = [copy.deepcopy(self.acq) for _ in model.models] - for submodel, func in zip(model.models, self._functions): - func.update(model=submodel, **kwargs) - - def _compute(self, X: np.ndarray) -> np.ndarray: - """Computes the EI value and its derivatives. - - Parameters - ---------- - X: np.ndarray(N, D), The input points where the acquisition function - should be evaluated. The dimensionality of X is (N, D), with N as - the number of points to evaluate at and D is the number of - dimensions of one X. - - Returns - ------- - np.ndarray(N,1) - Expected Improvement of X - """ - if self._functions is None: - raise ValueError("Need to call update first!") - return np.array([func._compute(X) for func in self._functions]).mean(axis=0) - - -class PriorAcquisitionFunction(AbstractAcquisitionFunction): - r"""Weight the acquisition function with a user-defined prior over the optimum. - - See "PiBO: Augmenting Acquisition Functions with User Beliefs for Bayesian Optimization" by Carl - Hvarfner et al. (###nolinkyet###) for further details. - """ - - def __init__( - self, - model: BaseEPM, - acquisition_function: AbstractAcquisitionFunction, - decay_beta: float, - prior_floor: float = 1e-12, - discretize: bool = False, - discrete_bins_factor: float = 10.0, - **kwargs: Any, - ): - """Constructor - - Parameters - ---------- - model : BaseEPM - Models the objective function. - decay_beta: Decay factor on the user prior - defaults to n_iterations / 10 if not specifed - otherwise. - prior_floor : Lowest possible value of the prior, to ensure non-negativity for all values - in the search space. - discretize : Whether to discretize (bin) the densities for continous parameters. Triggered - for Random Forest models and continous hyperparameters to avoid a pathological case - where all Random Forest randomness is removed (RF surrogates require piecewise constant - acquisition functions to be well-behaved) - discrete_bins_factor : If discretizing, the multiple on the number of allowed bins for - each parameter - - kwargs - Additional keyword arguments - """ - super().__init__(model) - self.long_name = "Prior Acquisition Function (%s)" % acquisition_function.__class__.__name__ - self.acq = acquisition_function - self._functions = [] # type: List[AbstractAcquisitionFunction] - self.eta = None - self.hyperparameters = self.model.get_configspace().get_hyperparameters_dict() - self.decay_beta = decay_beta - self.prior_floor = prior_floor - self.discretize = discretize - if self.discretize: - self.discrete_bins_factor = discrete_bins_factor - - # check if the acquisition function is LCB or TS - then the acquisition function values - # need to be rescaled to assure positiveness & correct magnitude - if isinstance(self.acq, IntegratedAcquisitionFunction): - acquisition_type = self.acq.acq - else: - acquisition_type = self.acq - - self.rescale_acq = isinstance(acquisition_type, (LCB, TS)) - self.iteration_number = 0 - - def update(self, **kwargs: Any) -> None: - """Update the acquisition function attributes required for calculation. - - Updates the model, the accompanying acquisition function and tracks the iteration number. - - Parameters - ---------- - kwargs - Additional keyword arguments - """ - self.iteration_number += 1 - self.acq.update(**kwargs) - self.eta = kwargs.get("eta") - - def _compute_prior(self, X: np.ndarray) -> np.ndarray: - """Computes the prior-weighted acquisition function values, where the prior on each - parameter is multiplied by a decay factor controlled by the parameter decay_beta and - the iteration number. Multivariate priors are not supported, for now. - - Parameters - ---------- - X: np.ndarray(N, D), The input points where the user-specified prior - should be evaluated. The dimensionality of X is (N, D), with N as - the number of points to evaluate at and D is the number of - dimensions of one X. - - Returns - ------- - np.ndarray(N,1) - The user prior over the optimum for values of X - """ - prior_values = np.ones((len(X), 1)) - # iterate over the hyperparmeters (alphabetically sorted) and the columns, which come - # in the same order - for parameter, X_col in zip(self.hyperparameters.values(), X.T): - if self.discretize and isinstance(parameter, FloatHyperparameter): - number_of_bins = int(np.ceil(self.discrete_bins_factor * self.decay_beta / self.iteration_number)) - prior_values *= self._compute_discretized_pdf(parameter, X_col, number_of_bins) + self.prior_floor - else: - prior_values *= parameter._pdf(X_col[:, np.newaxis]) - - return prior_values - - def _compute_discretized_pdf( - self, parameter: FloatHyperparameter, X_col: np.ndarray, number_of_bins: int - ) -> np.ndarray: - """Discretizes (bins) prior values on continous a specific continous parameter - to an increasingly coarse discretization determined by the prior decay parameter. - - Parameters - ---------- - parameter: a FloatHyperparameter that, due to using a random forest - surrogate, must have its prior discretized - X_col: np.ndarray(N, ), The input points where the acquisition function - should be evaluated. The dimensionality of X is (N, ), with N as - the number of points to evaluate for the specific hyperparameter - number_of_bins: The number of unique values allowed on the - discretized version of the pdf. - - Returns - ------- - np.ndarray(N,1) - The user prior over the optimum for the parameter at hand. - """ - # evaluates the actual pdf on all the relevant points - pdf_values = parameter._pdf(X_col[:, np.newaxis]) - # retrieves the largest value of the pdf in the domain - lower, upper = (0, parameter.get_max_density()) - # creates the bins (the possible discrete options of the pdf) - bin_values = np.linspace(lower, upper, number_of_bins) - # generates an index (bin) for each evaluated point - bin_indices = np.clip( - np.round((pdf_values - lower) * number_of_bins / (upper - lower)), 0, number_of_bins - 1 - ).astype(int) - # gets the actual value for each point - prior_values = bin_values[bin_indices] - return prior_values - - def _compute(self, X: np.ndarray) -> np.ndarray: - """Computes the prior-weighted acquisition function values, where the prior on each - parameter is multiplied by a decay factor controlled by the parameter decay_beta and - the iteration number. Multivariate priors are not supported, for now. - - Parameters - ---------- - X: np.ndarray(N, D), The input points where the acquisition function - should be evaluated. The dimensionality of X is (N, D), with N as - the number of points to evaluate at and D is the number of - dimensions of one X. - - Returns - ------- - np.ndarray(N,1) - Prior-weighted acquisition function values of X - """ - if self.rescale_acq: - # for TS and UCB, we need to scale the function values to not run into issues - # of negative values or issues of varying magnitudes (here, they are both) - # negative by design and just flipping the sign leads to picking the worst point) - acq_values = np.clip(self.acq._compute(X) + self.eta, 0, np.inf) - else: - acq_values = self.acq._compute(X) - prior_values = self._compute_prior(X) + self.prior_floor - decayed_prior_values = np.power(prior_values, self.decay_beta / self.iteration_number) - - return acq_values * decayed_prior_values - - -class EI(AbstractAcquisitionFunction): - r"""Computes for a given x the expected improvement as - acquisition value. - - :math:`EI(X) := \mathbb{E}\left[ \max\{0, f(\mathbf{X^+}) - f_{t+1}(\mathbf{X}) - \xi \} \right]`, - with :math:`f(X^+)` as the best location. - """ - - def __init__(self, model: BaseEPM, par: float = 0.0): - """Constructor. - - Parameters - ---------- - model : BaseEPM - A model that implements at least - - predict_marginalized_over_instances(X) - par : float, default=0.0 - Controls the balance between exploration and exploitation of the - acquisition function. - """ - super(EI, self).__init__(model) - self.long_name = "Expected Improvement" - self.par = par - self.eta = None - self._required_updates = ("model", "eta") - - def _compute(self, X: np.ndarray) -> np.ndarray: - """Computes the EI value and its derivatives. - - Parameters - ---------- - X: np.ndarray(N, D), The input points where the acquisition function - should be evaluated. The dimensionality of X is (N, D), with N as - the number of points to evaluate at and D is the number of - dimensions of one X. - - Returns - ------- - np.ndarray(N,1) - Expected Improvement of X - """ - if len(X.shape) == 1: - X = X[:, np.newaxis] - - m, v = self.model.predict_marginalized_over_instances(X) - s = np.sqrt(v) - - if self.eta is None: - raise ValueError( - "No current best specified. Call update(" - "eta=) to inform the acquisition function " - "about the current best value." - ) - - def calculate_f(): - z = (self.eta - m - self.par) / s - return (self.eta - m - self.par) * norm.cdf(z) + s * norm.pdf(z) - - if np.any(s == 0.0): - # if std is zero, we have observed x on all instances - # using a RF, std should be never exactly 0.0 - # Avoid zero division by setting all zeros in s to one. - # Consider the corresponding results in f to be zero. - self.logger.warning("Predicted std is 0.0 for at least one sample.") - s_copy = np.copy(s) - s[s_copy == 0.0] = 1.0 - f = calculate_f() - f[s_copy == 0.0] = 0.0 - else: - f = calculate_f() - if (f < 0).any(): - raise ValueError("Expected Improvement is smaller than 0 for at least one " "sample.") - - return f - - -class EIPS(EI): - def __init__(self, model: BaseEPM, par: float = 0.0): - r"""Computes for a given x the expected improvement as - acquisition value. - :math:`EI(X) := \frac{\mathbb{E}\left[\max\{0,f(\mathbf{X^+})-f_{t+1}(\mathbf{X})-\xi\right]\}]}{np.log(r(x))}`, - with :math:`f(X^+)` as the best location and :math:`r(x)` as runtime. - - Parameters - ---------- - model : BaseEPM - A model that implements at least - - predict_marginalized_over_instances(X) returning a tuples of - predicted cost and running time - par : float, default=0.0 - Controls the balance between exploration and exploitation of the - acquisition function. - """ - super(EIPS, self).__init__(model, par=par) - self.long_name = "Expected Improvement per Second" - - def _compute(self, X: np.ndarray) -> np.ndarray: - """Computes the EIPS value. - - Parameters - ---------- - X: np.ndarray(N, D), The input point where the acquisition function - should be evaluate. The dimensionality of X is (N, D), with N as - the number of points to evaluate at and D is the number of - dimensions of one X. - - Returns - ------- - np.ndarray(N,1) - Expected Improvement per Second of X - """ - if len(X.shape) == 1: - X = X[:, np.newaxis] - - m, v = self.model.predict_marginalized_over_instances(X) - if m.shape[1] != 2: - raise ValueError("m has wrong shape: %s != (-1, 2)" % str(m.shape)) - if v.shape[1] != 2: - raise ValueError("v has wrong shape: %s != (-1, 2)" % str(v.shape)) - - m_cost = m[:, 0] - v_cost = v[:, 0] - # The model already predicts log(runtime) - m_runtime = m[:, 1] - s = np.sqrt(v_cost) - - if self.eta is None: - raise ValueError( - "No current best specified. Call update(" - "eta=) to inform the acquisition function " - "about the current best value." - ) - - def calculate_f(): - z = (self.eta - m_cost - self.par) / s - f = (self.eta - m_cost - self.par) * norm.cdf(z) + s * norm.pdf(z) - f = f / m_runtime - return f - - if np.any(s == 0.0): - # if std is zero, we have observed x on all instances - # using a RF, std should be never exactly 0.0 - # Avoid zero division by setting all zeros in s to one. - # Consider the corresponding results in f to be zero. - self.logger.warning("Predicted std is 0.0 for at least one sample.") - s_copy = np.copy(s) - s[s_copy == 0.0] = 1.0 - f = calculate_f() - f[s_copy == 0.0] = 0.0 - else: - f = calculate_f() - - if (f < 0).any(): - raise ValueError("Expected Improvement per Second is smaller than 0 " "for at least one sample.") - - return f.reshape((-1, 1)) - - -class LogEI(AbstractAcquisitionFunction): - def __init__(self, model: BaseEPM, par: float = 0.0): - r"""Computes for a given x the logarithm expected improvement as - acquisition value. - - Parameters - ---------- - model : BaseEPM - A model that implements at least - - predict_marginalized_over_instances(X) - par : float, default=0.0 - Controls the balance between exploration and exploitation of the - acquisition function. - """ - super(LogEI, self).__init__(model) - self.long_name = "Expected Improvement" - self.par = par - self.eta = None - self._required_updates = ("model", "eta") - - def _compute(self, X: np.ndarray) -> np.ndarray: - """Computes the EI value and its derivatives. - - Parameters - ---------- - X: np.ndarray(N, D), The input points where the acquisition function - should be evaluated. The dimensionality of X is (N, D), with N as - the number of points to evaluate at and D is the number of - dimensions of one X. - - Returns - ------- - np.ndarray(N,1) - Expected Improvement of X - """ - if self.eta is None: - raise ValueError( - "No current best specified. Call update(" - "eta=) to inform the acquisition function " - "about the current best value." - ) - - if len(X.shape) == 1: - X = X[:, np.newaxis] - - m, var_ = self.model.predict_marginalized_over_instances(X) - std = np.sqrt(var_) - - def calculate_log_ei(): - # we expect that f_min is in log-space - f_min = self.eta - self.par - v = (f_min - m) / std - return (np.exp(f_min) * norm.cdf(v)) - (np.exp(0.5 * var_ + m) * norm.cdf(v - std)) - - if np.any(std == 0.0): - # if std is zero, we have observed x on all instances - # using a RF, std should be never exactly 0.0 - # Avoid zero division by setting all zeros in s to one. - # Consider the corresponding results in f to be zero. - self.logger.warning("Predicted std is 0.0 for at least one sample.") - std_copy = np.copy(std) - std[std_copy == 0.0] = 1.0 - log_ei = calculate_log_ei() - log_ei[std_copy == 0.0] = 0.0 - else: - log_ei = calculate_log_ei() - - if (log_ei < 0).any(): - raise ValueError("Expected Improvement is smaller than 0 for at least one sample.") - - return log_ei.reshape((-1, 1)) - - -class PI(AbstractAcquisitionFunction): - def __init__(self, model: BaseEPM, par: float = 0.0): - r"""Computes the probability of improvement for a given x over the best so far value as acquisition value. - - :math:`P(f_{t+1}(\mathbf{X})\geq f(\mathbf{X^+}))` :math:`:= \Phi(\\frac{ \mu(\mathbf{X})-f(\mathbf{X^+}) } - { \sigma(\mathbf{X}) })` with :math:`f(X^+)` as the incumbent and :math:`\Phi` the cdf of the standard normal - - Parameters - ---------- - model : BaseEPM - A model that implements at least - - predict_marginalized_over_instances(X) - par : float, default=0.0 - Controls the balance between exploration and exploitation of the - acquisition function. - """ - super(PI, self).__init__(model) - self.long_name = "Probability of Improvement" - self.par = par - self.eta = None - self._required_updates = ("model", "eta") - - def _compute(self, X: np.ndarray) -> np.ndarray: - """Computes the PI value. - - Parameters - ---------- - X: np.ndarray(N, D) - Points to evaluate PI. N is the number of points and D the dimension for the points - - Returns - ------- - np.ndarray(N,1) - Expected Improvement of X - """ - if self.eta is None: - raise ValueError( - "No current best specified. Call update(" - "eta=) to inform the acquisition function " - "about the current best value." - ) - - if len(X.shape) == 1: - X = X[:, np.newaxis] - m, var_ = self.model.predict_marginalized_over_instances(X) - std = np.sqrt(var_) - return norm.cdf((self.eta - m - self.par) / std) - - -class LCB(AbstractAcquisitionFunction): - def __init__(self, model: BaseEPM, par: float = 1.0): - r"""Computes the lower confidence bound for a given x over the best so far value as - acquisition value. - - :math:`LCB(X) = \mu(\mathbf{X}) - \sqrt(\beta_t)\sigma(\mathbf{X})` - - Returns -LCB(X) as the acquisition_function optimizer maximizes the acquisition value. - - Parameters - ---------- - model : BaseEPM - A model that implements at least - - predict_marginalized_over_instances(X) - par : float, default=1.0 - Controls the balance between exploration and exploitation of the - acquisition function. - """ - super(LCB, self).__init__(model) - self.long_name = "Lower Confidence Bound" - self.par = par - self.num_data = None - self._required_updates = ("model", "num_data") - - def _compute(self, X: np.ndarray) -> np.ndarray: - """Computes the LCB value. - - Parameters - ---------- - X: np.ndarray(N, D) - Points to evaluate LCB. N is the number of points and D the dimension for the points - - Returns - ------- - np.ndarray(N,1) - Expected Improvement of X - """ - if self.num_data is None: - raise ValueError( - "No current number of Datapoints specified. Call update(" - "num_data=) to inform the acquisition function " - "about the number of datapoints." - ) - if len(X.shape) == 1: - X = X[:, np.newaxis] - m, var_ = self.model.predict_marginalized_over_instances(X) - std = np.sqrt(var_) - beta = 2 * np.log((X.shape[1] * self.num_data**2) / self.par) - return -(m - np.sqrt(beta) * std) - - -class TS(AbstractAcquisitionFunction): - def __init__(self, model: BaseEPM, par: float = 0.0): - r"""Do a Thompson Sampling for a given x over the best so far value as - acquisition value. - - Warning - ------- - Thompson Sampling can only be used together with - smac.optimizer.ei_optimization.RandomSearch, please do not use - smac.optimizer.ei_optimization.LocalAndSortedRandomSearch to optimize TS - acquisition function! - - :math:`TS(X) ~ \mathcal{N}(\mu(\mathbf{X}),\sigma(\mathbf{X}))' - Returns -TS(X) as the acquisition_function optimizer maximizes the acquisition value. - - Parameters - ---------- - model : BaseEPM - A model that implements at least - - predict_marginalized_over_instances(X) - par : float, default=0.0 - TS does not require par here, we only wants to make it consistent with - other acquisition functions. - """ - super(TS, self).__init__(model) - self.long_name = "Thompson Sampling" - self.par = par - self.num_data = None - self._required_updates = ("model",) - - def _compute(self, X: np.ndarray) -> np.ndarray: - """Sample a new value from a gaussian distribution whose mean and covariance values - are given by model. - - Parameters - ---------- - X: np.ndarray(N, D) - Points to be evaluated where we could sample a value. N is the number of points and D the dimension - for the points - - Returns - ------- - np.ndarray(N,1) - negative sample value of X - """ - if len(X.shape) == 1: - X = X[:, np.newaxis] - sample_function = getattr(self.model, "sample_functions", None) - if callable(sample_function): - return -sample_function(X, n_funcs=1) - - m, var_ = self.model.predict_marginalized_over_instances(X) - rng = getattr(self.model, "rng", np.random.RandomState(self.model.seed)) - m = m.flatten() - var_ = np.diag(var_.flatten()) - return -rng.multivariate_normal(m, var_, 1).T diff --git a/smac/optimizer/acquisition/maximizer.py b/smac/optimizer/acquisition/maximizer.py deleted file mode 100644 index 6c9fa446f..000000000 --- a/smac/optimizer/acquisition/maximizer.py +++ /dev/null @@ -1,902 +0,0 @@ -import abc -from typing import Callable, Iterator, List, Optional, Set, Tuple, Union - -import copy -import itertools -import logging -import time - -import numpy as np - -from smac.configspace import ( - Configuration, - ConfigurationSpace, - ForbiddenValueError, - convert_configurations_to_array, - get_one_exchange_neighbourhood, -) -from smac.optimizer.acquisition import AbstractAcquisitionFunction -from smac.optimizer.configuration_chooser.random_chooser import ( - ChooserNoCoolDown, - RandomChooser, -) -from smac.runhistory.runhistory import RunHistory -from smac.stats.stats import Stats - -__author__ = "Aaron Klein, Marius Lindauer" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Aaron Klein" -__email__ = "kleinaa@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -class AcquisitionFunctionMaximizer(object, metaclass=abc.ABCMeta): - """Abstract class for acquisition maximization. - - In order to use this class it has to be subclassed and the method - ``_maximize`` must be implemented. - - Parameters - ---------- - acquisition_function : ~smac.optimizer.acquisition.AbstractAcquisitionFunction - - config_space : ~smac.configspace.ConfigurationSpace - - rng : np.random.RandomState or int, optional - """ - - def __init__( - self, - acquisition_function: AbstractAcquisitionFunction, - config_space: ConfigurationSpace, - rng: Union[int, np.random.RandomState] = None, - ): - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - self.acquisition_function = acquisition_function - self.config_space = config_space - - if rng is None: - self.logger.debug("no rng given, using default seed of 1") - self.rng = np.random.RandomState(seed=1) - elif isinstance(rng, int): - self.rng = np.random.RandomState(seed=rng) - else: - self.rng = rng - - def maximize( - self, - runhistory: RunHistory, - stats: Stats, - num_points: int, - random_configuration_chooser: Optional[RandomChooser] = None, - ) -> Iterator[Configuration]: - """Maximize acquisition function using ``_maximize``. - - Parameters - ---------- - runhistory: ~smac.runhistory.runhistory.RunHistory - runhistory object - stats: ~smac.stats.stats.Stats - current stats object - num_points: int - number of points to be sampled - random_configuration_chooser: ~smac.optimizer.random_configuration_chooser.RandomConfigurationChooser, optional - part of the returned ChallengerList such - that we can interleave random configurations - by a scheme defined by the random_configuration_chooser; - random_configuration_chooser.next_smbo_iteration() - is called at the end of this function - - Returns - ------- - iterable - An iterable consisting of :class:`smac.configspace.Configuration`. - """ - - def next_configs_by_acq_value() -> List[Configuration]: - return [t[1] for t in self._maximize(runhistory, stats, num_points)] - - challengers = ChallengerList(next_configs_by_acq_value, self.config_space, random_configuration_chooser) - - if random_configuration_chooser is not None: - random_configuration_chooser.next_smbo_iteration() - return challengers - - @abc.abstractmethod - def _maximize( - self, - runhistory: RunHistory, - stats: Stats, - num_points: int, - ) -> List[Tuple[float, Configuration]]: - """Implements acquisition function maximization. - - In contrast to ``maximize``, this method returns an iterable of tuples, - consisting of the acquisition function value and the configuration. This - allows to plug together different acquisition function maximizers. - - Parameters - ---------- - runhistory: ~smac.runhistory.runhistory.RunHistory - runhistory object - stats: ~smac.stats.stats.Stats - current stats object - num_points: int - number of points to be sampled - - Returns - ------- - iterable - An iterable consistng of - tuple(acqusition_value, :class:`smac.configspace.Configuration`). - """ - raise NotImplementedError() - - def _sort_configs_by_acq_value(self, configs: List[Configuration]) -> List[Tuple[float, Configuration]]: - """Sort the given configurations by acquisition value. - - Parameters - ---------- - configs : list(Configuration) - - Returns - ------- - list: (acquisition value, Candidate solutions), - ordered by their acquisition function value - """ - acq_values = self.acquisition_function(configs) - - # From here - # http://stackoverflow.com/questions/20197990/how-to-make-argsort-result-to-be-random-between-equal-values - random = self.rng.rand(len(acq_values)) - # Last column is primary sort key! - indices = np.lexsort((random.flatten(), acq_values.flatten())) - - # Cannot use zip here because the indices array cannot index the - # rand_configs list, because the second is a pure python list - return [(acq_values[ind][0], configs[ind]) for ind in indices[::-1]] - - -class LocalSearch(AcquisitionFunctionMaximizer): - """Implementation of SMAC's local search. - - Parameters - ---------- - acquisition_function : ~smac.optimizer.acquisition.AbstractAcquisitionFunction - config_space : ~smac.configspace.ConfigurationSpace - rng : np.random.RandomState or int, optional - max_steps: int - Maximum number of iterations that the local search will perform - n_steps_plateau_walk: int - number of steps during a plateau walk before local search terminates - vectorization_min_obtain : int - Minimal number of neighbors to obtain at once for each local search for vectorized calls. Can be tuned to - reduce the overhead of SMAC - vectorization_max_obtain : int - Maximal number of neighbors to obtain at once for each local search for vectorized calls. Can be tuned to - reduce the overhead of SMAC - """ - - def __init__( - self, - acquisition_function: AbstractAcquisitionFunction, - config_space: ConfigurationSpace, - rng: Union[bool, np.random.RandomState] = None, - max_steps: Optional[int] = None, - n_steps_plateau_walk: int = 10, - vectorization_min_obtain: int = 2, - vectorization_max_obtain: int = 64, - ): - super().__init__(acquisition_function, config_space, rng) - self.max_steps = max_steps - self.n_steps_plateau_walk = n_steps_plateau_walk - self.vectorization_min_obtain = vectorization_min_obtain - self.vectorization_max_obtain = vectorization_max_obtain - - def _maximize( - self, - runhistory: RunHistory, - stats: Stats, - num_points: int, - additional_start_points: Optional[List[Tuple[float, Configuration]]] = None, - ) -> List[Tuple[float, Configuration]]: - """Starts a local search from the given startpoint and quits if either the max number of - steps is reached or no neighbor with an higher improvement was found. - - Parameters - ---------- - runhistory: ~smac.runhistory.runhistory.RunHistory - runhistory object - stats: ~smac.stats.stats.Stats - current stats object - num_points: int - number of points to be sampled - additional_start_points : Optional[List[Tuple[float, Configuration]]] - Additional start point - - Returns - ------- - List - """ - init_points = self._get_initial_points(num_points, runhistory, additional_start_points) - configs_acq = self._do_search(init_points) - - # shuffle for random tie-break - self.rng.shuffle(configs_acq) - - # sort according to acq value - configs_acq.sort(reverse=True, key=lambda x: x[0]) - for _, inc in configs_acq: - inc.origin = "Local Search" - - return configs_acq - - def _get_initial_points( - self, - num_points: int, - runhistory: RunHistory, - additional_start_points: Optional[List[Tuple[float, Configuration]]], - ) -> List[Configuration]: - - if runhistory.empty(): - init_points = self.config_space.sample_configuration(size=num_points) - else: - # initiate local search - configs_previous_runs = runhistory.get_all_configs() - - init_points = self._get_init_points_from_previous_configs( - num_points, configs_previous_runs, additional_start_points - ) - return init_points - - def _get_init_points_from_previous_configs( - self, - num_points: int, - configs_previous_runs: List[Configuration], - additional_start_points: Optional[List[Tuple[float, Configuration]]], - ) -> List[Configuration]: - """ - A function that generates a set of initial points from the previous configurations and additional points (if - applicable). The idea is to decouple runhistory from the local search model and replace it with a more genreal - form (List[Configuration]). - - Parameters - ---------- - num_points: int - Number of initial points to be generated - configs_previous_runs: List[Configuration] - previous configuration from runhistory - additional_start_points: Optional[List[Tuple[float, Configuration]]] - if we want to specify another set of points as initial points - - Returns - ------- - init_points: List[Configuration] - a set of initial points - """ - # configurations with the highest previous EI - configs_previous_runs_sorted = self._sort_configs_by_acq_value(configs_previous_runs) - configs_previous_runs_sorted = [conf[1] for conf in configs_previous_runs_sorted[:num_points]] - - # configurations with the lowest predictive cost, check for None to make unit tests work - if self.acquisition_function.model is not None: - conf_array = convert_configurations_to_array(configs_previous_runs) - costs = self.acquisition_function.model.predict_marginalized_over_instances(conf_array)[0] - assert len(conf_array) == len(costs), (conf_array.shape, costs.shape) - - # In case of the predictive model returning the prediction for more than one objective per configuration - # (for example multi-objective or EIPS) it is not immediately clear how to sort according to the cost - # of a configuration. Therefore, we simply follow the ParEGO approach and use a random scalarization. - if len(costs.shape) == 2 and costs.shape[1] > 1: - weights = np.array([self.rng.rand() for _ in range(costs.shape[1])]) - weights = weights / np.sum(weights) - costs = costs @ weights - - # From here - # http://stackoverflow.com/questions/20197990/how-to-make-argsort-result-to-be-random-between-equal-values - random = self.rng.rand(len(costs)) - # Last column is primary sort key! - indices = np.lexsort((random.flatten(), costs.flatten())) - - # Cannot use zip here because the indices array cannot index the - # rand_configs list, because the second is a pure python list - configs_previous_runs_sorted_by_cost = [configs_previous_runs[ind] for ind in indices][:num_points] - else: - configs_previous_runs_sorted_by_cost = [] - - if additional_start_points is not None: - additional_start_points = [asp[1] for asp in additional_start_points[:num_points]] - else: - additional_start_points = [] - - init_points = [] - init_points_as_set = set() # type: Set[Configuration] - for cand in itertools.chain( - configs_previous_runs_sorted, - configs_previous_runs_sorted_by_cost, - additional_start_points, - ): - if cand not in init_points_as_set: - init_points.append(cand) - init_points_as_set.add(cand) - return init_points - - def _do_search( - self, - start_points: List[Configuration], - ) -> List[Tuple[float, Configuration]]: - - # Gather data structure for starting points - if isinstance(start_points, Configuration): - start_points = [start_points] - candidates = start_points - # Compute the acquisition value of the candidates - num_candidates = len(candidates) - acq_val_candidates_ = self.acquisition_function(candidates) - if num_candidates == 1: - acq_val_candidates = [acq_val_candidates_[0][0]] - else: - acq_val_candidates = [a[0] for a in acq_val_candidates_] - - # Set up additional variables required to do vectorized local search: - # whether the i-th local search is still running - active = [True] * num_candidates - # number of plateau walks of the i-th local search. Reaching the maximum number is the stopping criterion of - # the local search. - n_no_plateau_walk = [0] * num_candidates - # tracking the number of steps for logging purposes - local_search_steps = [0] * num_candidates - # tracking the number of neighbors looked at for logging purposes - neighbors_looked_at = [0] * num_candidates - # tracking the number of neighbors generated for logging purposse - neighbors_generated = [0] * num_candidates - # how many neighbors were obtained for the i-th local search. Important to map the individual acquisition - # function values to the correct local search run - obtain_n = [self.vectorization_min_obtain] * num_candidates - # Tracking the time it takes to compute the acquisition function - times = [] - - # Set up the neighborhood generators - neighborhood_iterators = [] - for i, inc in enumerate(candidates): - neighborhood_iterators.append( - get_one_exchange_neighbourhood(inc, seed=self.rng.randint(low=0, high=100000)) - ) - local_search_steps[i] += 1 - # Keeping track of configurations with equal acquisition value for plateau walking - neighbors_w_equal_acq = [[] for _ in range(num_candidates)] # type: List[List[Configuration]] - - num_iters = 0 - while np.any(active): - - num_iters += 1 - # Whether the i-th local search improved. When a new neighborhood is generated, this is used to determine - # whether a step was made (improvement) or not (iterator exhausted) - improved = [False] * num_candidates - # Used to request a new neighborhood for the candidates of the i-th local search - new_neighborhood = [False] * num_candidates - - # gather all neighbors - neighbors = [] - for i, neighborhood_iterator in enumerate(neighborhood_iterators): - if active[i]: - neighbors_for_i = [] - for j in range(obtain_n[i]): - try: - n = next(neighborhood_iterator) - neighbors_generated[i] += 1 - neighbors_for_i.append(n) - except ValueError as e: - # `neighborhood_iterator` raises `ValueError` with some probability when it reaches - # an invalid configuration. - self.logger.debug(e) - new_neighborhood[i] = True - except StopIteration: - new_neighborhood[i] = True - break - obtain_n[i] = len(neighbors_for_i) - neighbors.extend(neighbors_for_i) - - if len(neighbors) != 0: - start_time = time.time() - acq_val = self.acquisition_function(neighbors) - end_time = time.time() - times.append(end_time - start_time) - if np.ndim(acq_val.shape) == 0: - acq_val = np.asarray([acq_val]) - - # Comparing the acquisition function of the neighbors with the acquisition value of the candidate - acq_index = 0 - # Iterating the all i local searches - for i in range(num_candidates): - if not active[i]: - continue - # And for each local search we know how many neighbors we obtained - for j in range(obtain_n[i]): - # The next line is only true if there was an improvement and we basically need to iterate to - # the i+1-th local search - if improved[i]: - acq_index += 1 - else: - neighbors_looked_at[i] += 1 - - # Found a better configuration - if acq_val[acq_index] > acq_val_candidates[i]: - is_valid = False - try: - neighbors[acq_index].is_valid_configuration() - is_valid = True - except (ValueError, ForbiddenValueError) as e: - self.logger.debug("Local search %d: %s", i, e) - if is_valid: - self.logger.debug( - "Local search %d: Switch to one of the neighbors (after %d configurations).", - i, - neighbors_looked_at[i], - ) - candidates[i] = neighbors[acq_index] - acq_val_candidates[i] = acq_val[acq_index] - new_neighborhood[i] = True - improved[i] = True - local_search_steps[i] += 1 - neighbors_w_equal_acq[i] = [] - obtain_n[i] = 1 - # Found an equally well performing configuration, keeping it for plateau walking - elif acq_val[acq_index] == acq_val_candidates[i]: - neighbors_w_equal_acq[i].append(neighbors[acq_index]) - - acq_index += 1 - - # Now we check whether we need to create new neighborhoods and whether we need to increase the number of - # plateau walks for one of the local searches. Also disables local searches if the number of plateau walks - # is reached (and all being switched off is the termination criterion). - for i in range(num_candidates): - if not active[i]: - continue - if obtain_n[i] == 0 or improved[i]: - obtain_n[i] = 2 - else: - obtain_n[i] = obtain_n[i] * 2 - obtain_n[i] = min(obtain_n[i], self.vectorization_max_obtain) - if new_neighborhood[i]: - if not improved[i] and n_no_plateau_walk[i] < self.n_steps_plateau_walk: - if len(neighbors_w_equal_acq[i]) != 0: - candidates[i] = neighbors_w_equal_acq[i][0] - neighbors_w_equal_acq[i] = [] - n_no_plateau_walk[i] += 1 - if n_no_plateau_walk[i] >= self.n_steps_plateau_walk: - active[i] = False - continue - - neighborhood_iterators[i] = get_one_exchange_neighbourhood( - candidates[i], - seed=self.rng.randint(low=0, high=100000), - ) - - self.logger.debug( - "Local searches took %s steps and looked at %s configurations. Computing the acquisition function in " - "vectorized for took %f seconds on average.", - local_search_steps, - neighbors_looked_at, - np.mean(times), - ) - - return [(a, i) for a, i in zip(acq_val_candidates, candidates)] - - -class DiffOpt(AcquisitionFunctionMaximizer): - """Get candidate solutions via DifferentialEvolutionSolvers. - - Parameters - ---------- - acquisition_function : ~smac.optimizer.acquisition.AbstractAcquisitionFunction - - config_space : ~smac.configspace.ConfigurationSpace - - rng : np.random.RandomState or int, optional - """ - - def _maximize( - self, - runhistory: RunHistory, - stats: Stats, - num_points: int, - _sorted: bool = False, - ) -> List[Tuple[float, Configuration]]: - """DifferentialEvolutionSolver. - - Parameters - ---------- - runhistory: ~smac.runhistory.runhistory.RunHistory - runhistory object - stats: ~smac.stats.stats.Stats - current stats object - num_points: int - number of points to be sampled - _sorted: bool - whether random configurations are sorted according to acquisition function - - Returns - ------- - iterable - An iterable consistng of - tuple(acqusition_value, :class:`smac.configspace.Configuration`). - """ - from scipy.optimize._differentialevolution import DifferentialEvolutionSolver - - configs = [] - - def func(x: np.ndarray) -> np.ndarray: - return -self.acquisition_function([Configuration(self.config_space, vector=x)]) - - ds = DifferentialEvolutionSolver( - func, - bounds=[[0, 1], [0, 1]], - args=(), - strategy="best1bin", - maxiter=1000, - popsize=50, - tol=0.01, - mutation=(0.5, 1), - recombination=0.7, - seed=self.rng.randint(1000), - polish=True, - callback=None, - disp=False, - init="latinhypercube", - atol=0, - ) - - _ = ds.solve() - for pop, val in zip(ds.population, ds.population_energies): - rc = Configuration(self.config_space, vector=pop) - rc.origin = "DifferentialEvolution" - configs.append((-val, rc)) - - configs.sort(key=lambda t: t[0]) - configs.reverse() - return configs - - -class RandomSearch(AcquisitionFunctionMaximizer): - """Get candidate solutions via random sampling of configurations. - - Parameters - ---------- - acquisition_function : ~smac.optimizer.acquisition.AbstractAcquisitionFunction - - config_space : ~smac.configspace.ConfigurationSpace - - rng : np.random.RandomState or int, optional - """ - - def _maximize( - self, - runhistory: RunHistory, - stats: Stats, - num_points: int, - _sorted: bool = False, - ) -> List[Tuple[float, Configuration]]: - """Randomly sampled configurations. - - Parameters - ---------- - runhistory: ~smac.runhistory.runhistory.RunHistory - runhistory object - stats: ~smac.stats.stats.Stats - current stats object - num_points: int - number of points to be sampled - _sorted: bool - whether random configurations are sorted according to acquisition function - - Returns - ------- - iterable - An iterable consistng of - tuple(acqusition_value, :class:`smac.configspace.Configuration`). - """ - if num_points > 1: - rand_configs = self.config_space.sample_configuration(size=num_points) - else: - rand_configs = [self.config_space.sample_configuration(size=1)] - if _sorted: - for i in range(len(rand_configs)): - rand_configs[i].origin = "Random Search (sorted)" - return self._sort_configs_by_acq_value(rand_configs) - else: - for i in range(len(rand_configs)): - rand_configs[i].origin = "Random Search" - return [(0, rand_configs[i]) for i in range(len(rand_configs))] - - -class LocalAndSortedRandomSearch(AcquisitionFunctionMaximizer): - """Implements SMAC's default acquisition function optimization. - - This optimizer performs local search from the previous best points - according, to the acquisition function, uses the acquisition function to - sort randomly sampled configurations. Random configurations are - interleaved by the main SMAC code. - - Parameters - ---------- - acquisition_function : ~smac.optimizer.acquisition.AbstractAcquisitionFunction - - config_space : ~smac.configspace.ConfigurationSpace - - rng : np.random.RandomState or int, optional - - max_steps: int - [LocalSearch] Maximum number of steps that the local search will perform - - n_steps_plateau_walk: int - [LocalSearch] number of steps during a plateau walk before local search terminates - - n_sls_iterations: int - [Local Search] number of local search iterations - """ - - def __init__( - self, - acquisition_function: AbstractAcquisitionFunction, - config_space: ConfigurationSpace, - rng: Union[bool, np.random.RandomState] = None, - max_steps: Optional[int] = None, - n_steps_plateau_walk: int = 10, - n_sls_iterations: int = 10, - ): - super().__init__(acquisition_function, config_space, rng) - self.random_search = RandomSearch(acquisition_function=acquisition_function, config_space=config_space, rng=rng) - self.local_search = LocalSearch( - acquisition_function=acquisition_function, - config_space=config_space, - rng=rng, - max_steps=max_steps, - n_steps_plateau_walk=n_steps_plateau_walk, - ) - self.n_sls_iterations = n_sls_iterations - - def _maximize( - self, - runhistory: RunHistory, - stats: Stats, - num_points: int, - ) -> List[Tuple[float, Configuration]]: - - # Get configurations sorted by EI - next_configs_by_random_search_sorted = self.random_search._maximize( - runhistory, - stats, - num_points, - _sorted=True, - ) - - next_configs_by_local_search = self.local_search._maximize( - runhistory, - stats, - self.n_sls_iterations, - additional_start_points=next_configs_by_random_search_sorted, - ) - - # Having the configurations from random search, sorted by their - # acquisition function value is important for the first few iterations - # of SMAC. As long as the random forest predicts constant value, we - # want to use only random configurations. Having them at the begging of - # the list ensures this (even after adding the configurations by local - # search, and then sorting them) - next_configs_by_acq_value = next_configs_by_random_search_sorted + next_configs_by_local_search - next_configs_by_acq_value.sort(reverse=True, key=lambda x: x[0]) - self.logger.debug( - "First 5 acq func (origin) values of selected configurations: %s", - str([[_[0], _[1].origin] for _ in next_configs_by_acq_value[:5]]), - ) - return next_configs_by_acq_value - - -class LocalAndSortedPriorRandomSearch(AcquisitionFunctionMaximizer): - """Implements SMAC's default acquisition function optimization. - - This optimizer performs local search from the previous best points - according, to the acquisition function, uses the acquisition function to - sort randomly sampled configurations. Random configurations are - interleaved by the main SMAC code. The random configurations are retrieved - from two different ConfigurationSpaces - one which uses priors (e.g. NormalFloatHP) - and is defined by the user, and one that is a uniform version of the same - space, i.e. with the priors removed. - - Parameters - ---------- - acquisition_function : ~smac.optimizer.acquisition.AbstractAcquisitionFunction - - config_space : ~smac.configspace.ConfigurationSpace - The original ConfigurationSpace specified by the user - - uniform_config_space : ~smac.configspace.ConfigurationSpace - A version of the user-defined ConfigurationSpace where all parameters are - uniform (or have their weights removed in the case of a categorical - hyperparameter) - - rng : np.random.RandomState or int, optional - - max_steps: int - [LocalSearch] Maximum number of steps that the local search will perform - - n_steps_plateau_walk: int - [LocalSearch] number of steps during a plateau walk before local search terminates - - n_sls_iterations: int - [Local Search] number of local search iterations - - prior_sampling_fraction: float - The ratio of random samples that are taken from the user-defined ConfigurationSpace, - as opposed to the uniform version. - """ - - def __init__( - self, - acquisition_function: AbstractAcquisitionFunction, - config_space: ConfigurationSpace, - uniform_config_space: ConfigurationSpace, - rng: Union[bool, np.random.RandomState] = None, - max_steps: Optional[int] = None, - n_steps_plateau_walk: int = 10, - n_sls_iterations: int = 10, - prior_sampling_fraction: float = 0.5, - ): - super().__init__(acquisition_function, config_space, rng) - self.prior_random_search = RandomSearch( - acquisition_function=acquisition_function, config_space=config_space, rng=rng - ) - self.uniform_random_search = RandomSearch( - acquisition_function=acquisition_function, config_space=uniform_config_space, rng=rng - ) - self.local_search = LocalSearch( - acquisition_function=acquisition_function, - config_space=config_space, - rng=rng, - max_steps=max_steps, - n_steps_plateau_walk=n_steps_plateau_walk, - ) - self.n_sls_iterations = n_sls_iterations - self.prior_sampling_fraction = prior_sampling_fraction - - def _maximize( - self, - runhistory: RunHistory, - stats: Stats, - num_points: int, - ) -> List[Tuple[float, Configuration]]: - - # Get configurations sorted by EI - next_configs_by_prior_random_search_sorted = self.prior_random_search._maximize( - runhistory, - stats, - round(num_points * self.prior_sampling_fraction), - _sorted=True, - ) - - # Get configurations sorted by EI - next_configs_by_uniform_random_search_sorted = self.uniform_random_search._maximize( - runhistory, - stats, - round(num_points * (1 - self.prior_sampling_fraction)), - _sorted=True, - ) - next_configs_by_random_search_sorted = [] - next_configs_by_random_search_sorted.extend(next_configs_by_prior_random_search_sorted) - next_configs_by_random_search_sorted.extend(next_configs_by_uniform_random_search_sorted) - - next_configs_by_local_search = self.local_search._maximize( - runhistory, - stats, - self.n_sls_iterations, - additional_start_points=next_configs_by_random_search_sorted, - ) - - # Having the configurations from random search, sorted by their - # acquisition function value is important for the first few iterations - # of SMAC. As long as the random forest predicts constant value, we - # want to use only random configurations. Having them at the begging of - # the list ensures this (even after adding the configurations by local - # search, and then sorting them) - next_configs_by_acq_value = next_configs_by_random_search_sorted + next_configs_by_local_search - next_configs_by_acq_value.sort(reverse=True, key=lambda x: x[0]) - self.logger.debug( - "First 5 acq func (origin) values of selected configurations: %s", - str([[_[0], _[1].origin] for _ in next_configs_by_acq_value[:5]]), - ) - return next_configs_by_acq_value - - -class ChallengerList(Iterator): - """Helper class to interleave random configurations in a list of challengers. - - Provides an iterator which returns a random configuration in each second - iteration. Reduces time necessary to generate a list of new challengers - as one does not need to sample several hundreds of random configurations - in each iteration which are never looked at. - - Parameters - ---------- - challenger_callback : Callable - Callback function which returns a list of challengers (without interleaved random configurations, must a be a - closure: https://www.programiz.com/python-programming/closure) - - configuration_space : ConfigurationSpace - ConfigurationSpace from which to sample new random configurations. - """ - - def __init__( - self, - challenger_callback: Callable, - configuration_space: ConfigurationSpace, - random_configuration_chooser: Optional[RandomChooser] = ChooserNoCoolDown(modulus=2.0), - ): - self.challengers_callback = challenger_callback - self.challengers = None # type: Optional[List[Configuration]] - self.configuration_space = configuration_space - self._index = 0 - self._iteration = 1 # 1-based to prevent from starting with a random configuration - self.random_configuration_chooser = random_configuration_chooser - - def __next__(self) -> Configuration: - if self.challengers is not None and self._index == len(self.challengers): - raise StopIteration - elif self.random_configuration_chooser is None: - if self.challengers is None: - self.challengers = self.challengers_callback() - config = self.challengers[self._index] - self._index += 1 - return config - else: - if self.random_configuration_chooser.check(self._iteration): - config = self.configuration_space.sample_configuration() - config.origin = "Random Search" - else: - if self.challengers is None: - self.challengers = self.challengers_callback() - config = self.challengers[self._index] - self._index += 1 - self._iteration += 1 - return config - - def __len__(self) -> int: - if self.challengers is None: - self.challengers = self.challengers_callback() - return len(self.challengers) - self._index - - -class FixedSet(AcquisitionFunctionMaximizer): - def __init__( - self, - configurations: List[Configuration], - acquisition_function: AbstractAcquisitionFunction, - config_space: ConfigurationSpace, - rng: Union[bool, np.random.RandomState] = None, - ): - """Maximize the acquisition function over a finite list of configurations. - - Parameters - ---------- - configurations : List[~smac.configspace.Configuration] - Candidate configurations - acquisition_function : ~smac.optimizer.acquisition.AbstractAcquisitionFunction - - config_space : ~smac.configspace.ConfigurationSpace - - rng : np.random.RandomState or int, optional - """ - super().__init__(acquisition_function=acquisition_function, config_space=config_space, rng=rng) - self.configurations = configurations - - def _maximize( - self, - runhistory: RunHistory, - stats: Stats, - num_points: int, - ) -> List[Tuple[float, Configuration]]: - configurations = copy.deepcopy(self.configurations) - for config in configurations: - config.origin = "Fixed Set" - return self._sort_configs_by_acq_value(configurations) diff --git a/smac/optimizer/configuration_chooser/boing_chooser.py b/smac/optimizer/configuration_chooser/boing_chooser.py deleted file mode 100644 index 865911033..000000000 --- a/smac/optimizer/configuration_chooser/boing_chooser.py +++ /dev/null @@ -1,658 +0,0 @@ -from typing import Dict, Iterator, List, Optional, Tuple, Type, Union - -import copy -from itertools import chain - -import numpy as np -from ConfigSpace.hyperparameters import NumericalHyperparameter - -from smac.configspace import Configuration -from smac.epm.base_epm import BaseEPM -from smac.epm.gaussian_process.augmented import GloballyAugmentedLocalGaussianProcess -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances -from smac.epm.utils import get_types -from smac.optimizer.acquisition import EI, TS, AbstractAcquisitionFunction -from smac.optimizer.acquisition.maximizer import AcquisitionFunctionMaximizer -from smac.optimizer.configuration_chooser.epm_chooser import EPMChooser -from smac.optimizer.configuration_chooser.random_chooser import ( - ChooserNoCoolDown, - RandomChooser, -) -from smac.optimizer.subspaces.boing_subspace import BOinGSubspace -from smac.optimizer.subspaces.turbo_subspace import TuRBOSubSpace -from smac.runhistory.runhistory import RunHistory -from smac.runhistory.runhistory2epm_boing import RunHistory2EPM4CostWithRaw -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.utils.constants import MAXINT - - -class BOinGChooser(EPMChooser): - """ - Interface to train the EPM and generate next configurations with both global and local models. - - Parameters - ---------- - runhistory2epm: RunHistory2EPM4CostWithRaw, - a transformer to transform rh to vectors, different from the rh2epm used in vanilla EPMChooser, this rh2epm - object needs to provide the raw values for optimizer in different stages - model: smac.epm.rf_with_instances.RandomForestWithInstances - empirical performance model (right now, we support only RandomForestWithInstances) as a global model - acq_optimizer: smac.optimizer.ei_optimization.AcquisitionFunctionMaximizer - Optimizer of acquisition function of global models - model_local: BaseEPM, - local empirical performance model, used in subspace - model_local_kwargs: Optional[Dict] = None, - parameters for initializing a local model - acquisition_func_local: AbstractAcquisitionFunction, - local acquisition function, used in subspace - acquisition_func_local_kwargs: Optional[Dict] = None, - parameters for initializing a local acquisition function optimizer - acq_optimizer_local: Optional[AcquisitionFunctionMaximizer] = None, - Optimizer of acquisition function of local models - acq_optimizer_local_kwargs: typing: Optional[Dict] = None, - parameters for the optimizer of acquisition function of local models - max_configs_local_fracs : float - The maximal number of fractions of samples to be included in the subspace. If the number of samples in the - subspace is greater than this value and n_min_config_inner, the subspace will be cropped to fit the requirement - min_configs_local: int, - Minimum number of samples included in the inner loop model - do_switching: bool - if we want to switch between turbo and boing or do a pure BOinG search - turbo_kwargs: Optional[Dict] = None - parameters for building a turbo optimizer - """ - - def __init__( - self, - scenario: Scenario, - stats: Stats, - runhistory: RunHistory, - runhistory2epm: RunHistory2EPM4CostWithRaw, - model: RandomForestWithInstances, - acq_optimizer: AcquisitionFunctionMaximizer, - acquisition_func: AbstractAcquisitionFunction, - rng: np.random.RandomState, - restore_incumbent: Configuration = None, - random_configuration_chooser: RandomChooser = ChooserNoCoolDown(2.0), - predict_x_best: bool = True, - min_samples_model: int = 1, - model_local: Union[BaseEPM, Type[BaseEPM]] = GloballyAugmentedLocalGaussianProcess, - acquisition_func_local: Union[AbstractAcquisitionFunction, Type[AbstractAcquisitionFunction]] = EI, - model_local_kwargs: Optional[Dict] = None, - acquisition_func_local_kwargs: Optional[Dict] = None, - acq_optimizer_local: Optional[AcquisitionFunctionMaximizer] = None, - acq_optimizer_local_kwargs: Optional[Dict] = None, - max_configs_local_fracs: float = 0.5, - min_configs_local: Optional[int] = None, - do_switching: bool = False, - turbo_kwargs: Optional[Dict] = None, - ): - # initialize the original EPM_Chooser - super(BOinGChooser, self).__init__( - scenario=scenario, - stats=stats, - runhistory=runhistory, - runhistory2epm=runhistory2epm, - model=model, - acq_optimizer=acq_optimizer, - acquisition_func=acquisition_func, - rng=rng, - restore_incumbent=restore_incumbent, - random_configuration_chooser=random_configuration_chooser, - predict_x_best=predict_x_best, - min_samples_model=min_samples_model, - ) - if not isinstance(self.model, RandomForestWithInstances): - raise ValueError("BOinG only supports RandomForestWithInstances as its global optimizer") - if not isinstance(self.rh2EPM, RunHistory2EPM4CostWithRaw): - raise ValueError("BOinG only supports RunHistory2EPM4CostWithRaw as its rh transformer") - - cs = self.scenario.cs # type: ignore - - self.subspace_info = { - "model_local": model_local, - "model_local_kwargs": model_local_kwargs, - "acq_func_local": acquisition_func_local, - "acq_func_local_kwargs": acquisition_func_local_kwargs, - "acq_optimizer_local": acq_optimizer_local, - "acq_optimizer_local_kwargs": acq_optimizer_local_kwargs, - } - - self.max_configs_local_fracs = max_configs_local_fracs - self.min_configs_local = ( - min_configs_local if min_configs_local is not None else 5 * len(cs.get_hyperparameters()) - ) - - types, bounds = get_types(cs, instance_features=None) - - self.types = types - self.bounds = bounds - self.cat_dims = np.where(np.array(types) != 0)[0] - self.cont_dims = np.where(np.array(types) == 0)[0] - self.config_space = cs - - self.frac_to_start_bi = 0.8 - self.split_count = np.zeros(len(types)) - self.do_switching = do_switching - self.random_search_upper_log = 1 - - self.optimal_value = np.inf - self.optimal_config = None - - self.ss_threshold = 0.1 ** len(cs.get_hyperparameters()) - if self.do_switching: - # If we want to switch between BOinG and TurBO - self.run_TuRBO = False - self.failcount_BOinG = 0 - self.failcount_TurBO = 0 - - turbo_model = copy.deepcopy(model_local) - turbo_acq = TS - turbo_opt_kwargs = dict( - config_space=cs, - bounds=bounds, - hps_types=types, - model_local=turbo_model, - model_local_kwargs=copy.deepcopy(model_local_kwargs), - acq_func_local=turbo_acq, - rng=rng, - length_min=2e-4, - ) - self.turbo_kwargs = turbo_opt_kwargs - if turbo_kwargs is not None: - turbo_opt_kwargs.update(turbo_kwargs) - self.turbo_optimizer = TuRBOSubSpace(**turbo_opt_kwargs) - - def restart_TuRBOinG(self, X: np.ndarray, Y: np.ndarray, Y_raw: np.ndarray, train_model: bool = False) -> None: - """ - Restart a new TurBO Optimizer, the bounds of the TurBO Optimizer is determined by a RF, we randomly sample 20 - points and extract subspaces that contain at least self.min_configs_local points, and we select the subspace - with the largest volume to construct a turbo optimizer - Parameters - ---------- - X: np.ndarray (N, D) - previous evaluated configurations - Y: np.ndarray (N,) - performances of previous evaluated configurations (transformed by rh2epm transformer) - Y_raw: np.ndarray (N,) - performances of previous evaluated configurations (raw values, not transformed) - train_model: bool - if we retrain the model with the given X and Y - """ - if train_model: - self.model.train(X, Y) - num_samples = 20 - union_ss = [] - union_indices = [] - rand_samples = self.config_space.sample_configuration(num_samples) - for sample in rand_samples: - sample_array = sample.get_array() - union_bounds_cont, _, ss_data_indices = subspace_extraction( - X=X, - challenger=sample_array, - model=self.model, - num_min=self.min_configs_local, - num_max=MAXINT, - bounds=self.bounds, - cont_dims=self.cont_dims, - cat_dims=self.cat_dims, - ) - union_ss.append(union_bounds_cont) - union_indices.append(ss_data_indices) - union_ss = np.asarray(union_ss) - volume_ss = np.product(union_ss[:, :, 1] - union_ss[:, :, 0], axis=1) # type: ignore - ss_idx = np.argmax(volume_ss) - ss_turbo = union_ss[ss_idx] - ss_data_indices = union_indices[ss_idx] - - # we only consider numerical(continuous) hyperparameters here - self.turbo_optimizer = TuRBOSubSpace( - **self.turbo_kwargs, # type: ignore - bounds_ss_cont=ss_turbo, # type: ignore - initial_data=(X[ss_data_indices], Y_raw[ss_data_indices]), # type: ignore - ) - self.turbo_optimizer.add_new_observations(X[ss_data_indices], Y_raw[ss_data_indices]) - - def choose_next(self, incumbent_value: float = None) -> Iterator[Configuration]: - """ - Choose next candidate solution with Bayesian optimization. We use TurBO optimizer or BOinG to suggest - the next configuration. - If we switch local model between TurBO and BOinG, we gradually increase the probability to switch to another - optimizer if we cannot make further process. (Or if TurBO find a new incumbent, we will switch to BOinG to do - further exploitation) - - Parameters - ---------- - incumbent_value: float - Cost value of incumbent configuration (required for acquisition function); - If not given, it will be inferred from runhistory or predicted; - if not given and runhistory is empty, it will raise a ValueError. - - Returns - ------- - Iterator - """ - # we also need the untransformed raw y values to used for local models - X, Y, Y_raw, X_configurations = self._collect_all_data_to_train_model() - if self.do_switching: - if self.run_TuRBO: - X, Y, Y_raw, X_configurations = self._collect_all_data_to_train_model() - - num_new_bservations = 1 # here we only consider batch_size ==1 - - new_observations = Y_raw[-num_new_bservations:] - - # give new suggestions from initialized values in TurBO - if len(self.turbo_optimizer.init_configs) > 0: - self.turbo_optimizer.add_new_observations(X[-num_new_bservations:], Y_raw[-num_new_bservations:]) - return self.turbo_optimizer.generate_challengers() - - self.turbo_optimizer.adjust_length(new_observations) - - # if we need to restart TurBO, we first check if we want to switch to BOinG - if self.turbo_optimizer.length < self.turbo_optimizer.length_min: - optimal_turbo = np.min(self.turbo_optimizer.ss_y) - - self.logger.debug(f"Best Found value by TuRBO: {optimal_turbo}") - - increment = optimal_turbo - self.optimal_value - - if increment < 0: - min_idx = np.argmin(Y_raw) - self.optimal_value = Y_raw[min_idx].item() - # compute the distance between the previous incumbent and new incumbent - cfg_diff = X[min_idx] - self.optimal_config - self.optimal_config = X[min_idx] - # we avoid sticking to a local minimum too often, e.g. either we have a relative much better - # configuration or the new configuration is a little bit far away from the current incumbent - if ( - increment < -1e-3 * np.abs(self.optimal_value) - or np.abs(np.product(cfg_diff)) >= self.ss_threshold - ): - self.failcount_TurBO -= 1 - # switch to BOinG as TurBO found a better model and we could do exploration - # also we halve the failcount of BOinG to avoid switching to TurBO too frequently - self.failcount_BOinG = self.failcount_BOinG // 2 - self.run_TuRBO = False - self.logger.debug("Optimizer switches to BOinG!") - - else: - self.failcount_TurBO += 1 - - # The probability is a linear curve. - prob_to_BOinG = 0.1 * self.failcount_TurBO - self.logger.debug(f"failure_count TuRBO :{self.failcount_TurBO}") - rand_value = self.rng.random() - - if rand_value < prob_to_BOinG: - self.failcount_BOinG = self.failcount_BOinG // 2 - self.run_TuRBO = False - self.logger.debug("Optimizer switches to BOinG!") - else: - self.restart_TuRBOinG(X=X, Y=Y, Y_raw=Y_raw, train_model=True) - return self.turbo_optimizer.generate_challengers() - - self.turbo_optimizer.add_new_observations(X[-num_new_bservations:], Y_raw[-num_new_bservations:]) - - return self.turbo_optimizer.generate_challengers() - - if X.shape[0] == 0: - # Only return a single point to avoid an overly high number of - # random search iterations - return self._random_search.maximize(runhistory=self.runhistory, stats=self.stats, num_points=1) - # if the number of points is not big enough, we simply build one subspace (the raw configuration space) and - # the local model becomes global model - if X.shape[0] < (self.min_configs_local / self.frac_to_start_bi): - if len(self.config_space.get_conditions()) == 0: - self.model.train(X, Y) - cs = self.scenario.cs # type: ignore - ss = BOinGSubspace( - config_space=cs, - bounds=self.bounds, - hps_types=self.types, - rng=self.rng, - initial_data=(X, Y_raw), - incumbent_array=None, - model_local=self.subspace_info["model_local"], # type: ignore - model_local_kwargs=self.subspace_info["model_local_kwargs"], # type: ignore - acq_func_local=self.subspace_info["acq_func_local"], # type: ignore - acq_func_local_kwargs=self.subspace_info["acq_func_local_kwargs"], # type: ignore - acq_optimizer_local=self.acq_optimizer, - ) - return ss.generate_challengers() - - # train the outer model - self.model.train(X, Y) - - if incumbent_value is not None: - best_observation = incumbent_value - x_best_array = None # type: Optional[np.ndarray] - else: - if self.runhistory.empty(): - raise ValueError("Runhistory is empty and the cost value of " "the incumbent is unknown.") - x_best_array, best_observation = self._get_x_best(self.predict_x_best, X_configurations) - - self.acquisition_func.update( - model=self.model, - eta=best_observation, - incumbent_array=x_best_array, - num_data=len(self._get_evaluated_configs()), - X=X_configurations, - ) - - if self.do_switching: - # check if we need to switch to turbo - # same as above - self.failcount_BOinG += 1 - increment = Y_raw[-1].item() - self.optimal_value - if increment < 0: - if self.optimal_config is not None: - cfg_diff = X[-1] - self.optimal_config - if ( - increment < -1e-2 * np.abs(self.optimal_value) - or np.abs(np.product(cfg_diff)) >= self.ss_threshold - ): - self.failcount_BOinG -= X.shape[-1] - self.optimal_value = Y_raw[-1].item() - self.optimal_config = X[-1] - else: - # restart - idx_min = np.argmin(Y_raw) - self.logger.debug("Better value found by BOinG, continue BOinG") - self.optimal_value = Y_raw[idx_min].item() - self.optimal_config = X[idx_min] - self.failcount_BOinG = 0 - - # similar to TurBO, we do a judgement every n_dimension times - amplify_param = self.failcount_BOinG // (X.shape[-1] * 1) - - if self.failcount_BOinG % (X.shape[-1] * 1) == 0: - prob_to_TurBO = 0.1 * amplify_param - rand_value = self.rng.random() - - if rand_value < prob_to_TurBO: - self.run_TuRBO = True - self.logger.debug("Switch To TuRBO") - self.failcount_TurBO = self.failcount_TurBO // 2 - self.restart_TuRBOinG(X=X, Y=Y, Y_raw=Y_raw, train_model=False) - - challengers_global = self.acq_optimizer.maximize( - runhistory=self.runhistory, - stats=self.stats, - num_points=self.scenario.acq_opt_challengers, # type: ignore[attr-defined] # noqa F821 - random_configuration_chooser=self.random_configuration_chooser, - ) - - if ( - X.shape[0] < (self.min_configs_local / self.frac_to_start_bi) - and len(self.config_space.get_conditions()) == 0 - ): - return challengers_global - - cfg_challenger_global_first = next(challengers_global) - array_challenger_global_first = cfg_challenger_global_first.get_array() # type: np.ndarray - - num_max_configs = int(X.shape[0] * self.max_configs_local_fracs) - - # to avoid the case that num_max_configs is only a little larger than self.min_configs_local - num_max = MAXINT if num_max_configs <= 2 * self.min_configs_local else num_max_configs - - if len(self.config_space.get_conditions()) > 0: - challanger_activate_hps = np.isfinite(array_challenger_global_first).astype(int) - rh_activate_hps = np.isfinite(X).astype(int) - indices_X_in_same_hierarchy = np.all((challanger_activate_hps - rh_activate_hps) == 0, axis=1) - num_indices_X_in_same_hierarchy = sum(indices_X_in_same_hierarchy) - - if num_indices_X_in_same_hierarchy == 0: - return chain([cfg_challenger_global_first], challengers_global) - - activate_dims = [] - hps = self.config_space.get_hyperparameters() - for idx_hp in np.where(challanger_activate_hps > 0)[0]: - if isinstance(hps[idx_hp], NumericalHyperparameter): - activate_dims.append(idx_hp) - else: - indices_X_in_same_hierarchy = indices_X_in_same_hierarchy & ( - X[:, idx_hp] == array_challenger_global_first[idx_hp] - ) - num_indices_X_in_same_hierarchy = sum(indices_X_in_same_hierarchy) - - X = X[indices_X_in_same_hierarchy] - Y_raw = Y_raw[indices_X_in_same_hierarchy] - - if len(activate_dims) == 0 or num_indices_X_in_same_hierarchy <= max(5, len(activate_dims)): - return chain([cfg_challenger_global_first], challengers_global) - n_min_configs_inner = self.min_configs_local // len(hps) * len(activate_dims) - else: - n_min_configs_inner = self.min_configs_local - activate_dims = np.arange(len(self.config_space.get_hyperparameters())) - - bounds_ss_cont, bounds_ss_cat, ss_data_indices = subspace_extraction( - X=X, - challenger=array_challenger_global_first, - model=self.model, - num_min=n_min_configs_inner, - num_max=num_max, - bounds=self.bounds, - cont_dims=self.cont_dims, - cat_dims=self.cat_dims, - ) - - self.logger.debug("contained {0} data of {1}".format(sum(ss_data_indices), Y_raw.size)) - - ss = BOinGSubspace( - config_space=self.scenario.cs, # type: ignore - bounds=self.bounds, - hps_types=self.types, - bounds_ss_cont=bounds_ss_cont, # type: ignore[arg-type] - bounds_ss_cat=bounds_ss_cat, - rng=self.rng, - initial_data=(X, Y_raw), - incumbent_array=array_challenger_global_first, # type: ignore[arg-type] - activate_dims=activate_dims, - **self.subspace_info, # type: ignore[arg-type] - ) - return ss.generate_challengers() - - def _collect_all_data_to_train_model(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: - """Similar to the implementaiton of EPMChooser, however, we also return the raw values here.""" - # if we use a float value as a budget, we want to train the model only on the highest budget - available_budgets = [] - for run_key in self.runhistory.data.keys(): - available_budgets.append(run_key.budget) - - # Sort available budgets from highest to lowest budget - available_budgets = sorted(list(set(available_budgets)), reverse=True) - - # Get #points per budget and if there are enough samples, then build a model - for b in available_budgets: - X, Y, Y_raw = self.rh2EPM.transform_with_raw( # type: ignore[attr-defined] - self.runhistory, - budget_subset=[ - b, - ], - ) # type: ignore - if X.shape[0] >= self.min_samples_model: - self.currently_considered_budgets = [ - b, - ] - configs_array = self.rh2EPM.get_configurations( - self.runhistory, budget_subset=self.currently_considered_budgets - ) - return X, Y, Y_raw, configs_array - - return ( - np.empty(shape=[0, 0]), - np.empty( - shape=[ - 0, - ] - ), - np.empty( - shape=[ - 0, - ] - ), - np.empty(shape=[0, 0]), - ) - - -def subspace_extraction( - X: np.ndarray, - challenger: np.ndarray, - model: RandomForestWithInstances, - num_min: int, - num_max: int, - bounds: Union[np.ndarray, List[Tuple]], - cat_dims: np.ndarray, - cont_dims: np.ndarray, -) -> Tuple[np.ndarray, List[Tuple], np.ndarray]: - """ - Extract a subspace that contains at least num_min points but no more than num_max points - - Parameters - ---------- - X: np.ndarray (N, D) - points used to train the model - challenger: np.ndarray (1, D) - the challenger where the subspace would grow - model: RandomForestWithInstances - a rf model - num_min: int - minimal number of points to be included in the subspace - num_max: int - maximal number of points to be included in the subspace - bounds: np.ndarray(D, 2) - bounds of the entire space, D = D_cat + D_cont - cat_dims: np.ndarray (D_cat) - categorical dimensions - cont_dims: np.ndarray(D_cont) - continuous dimensions - - Returns - ------- - union_bounds_cont: np.ndarray(D_cont, 2), - the continuous bounds of the subregion - union_bounds_cat, List[Tuple], - the categorical bounds of the subregion - in_ss_dims: - indices of the points that lie inside the subregion - """ - trees = model.rf.get_all_trees() - trees = [tree for tree in trees] - num_trees = len(trees) - node_indices = [0] * num_trees - - indices_trees = np.arange(num_trees) - np.random.shuffle(indices_trees) - ss_indices = np.full(X.shape[0], True) # type: np.ndarray - - stop_update = [False] * num_trees - - ss_bounds = np.array(bounds) - - cont_dims = np.array(cont_dims) - cat_dims = np.array(cat_dims) - - if len(cat_dims) == 0: - ss_bounds_cat = [()] - else: - ss_bounds_cat = [() for _ in range(len(cat_dims))] - for i, cat_dim in enumerate(cat_dims): - ss_bounds_cat[i] = np.arange(ss_bounds[cat_dim][0]) - - if len(cont_dims) == 0: - ss_bounds_cont = np.array([]) # type: np.ndarray - else: - ss_bounds_cont = ss_bounds[cont_dims] - - def traverse_forest(check_num_min: bool = True) -> None: - nonlocal ss_indices - np.random.shuffle(indices_trees) - for i in indices_trees: - if stop_update[i]: - continue - tree = trees[int(i)] - node_idx = node_indices[i] - node = tree.get_node(node_idx) - - if node.is_a_leaf(): - stop_update[i] = True - continue - - feature_idx = node.get_feature_index() - cont_feature_idx = np.where(feature_idx == cont_dims)[0] - if cont_feature_idx.size == 0: - # This node split the subspace w.r.t. the categorical hyperparameters - cat_feature_idx = np.where(feature_idx == cat_dims)[0][0] - split_value = node.get_cat_split() - intersect = np.intersect1d(ss_bounds_cat[cat_feature_idx], split_value, assume_unique=True) - - if len(intersect) == len(ss_bounds_cat[cat_feature_idx]): - # will fall into the left child - temp_child_idx = 0 - node_indices[i] = node.get_child_index(temp_child_idx) - elif len(intersect) == 0: - # will fall into the left child - temp_child_idx = 1 - node_indices[i] = node.get_child_index(temp_child_idx) - else: - if challenger[feature_idx] in intersect: - temp_child_idx = 0 - temp_node_indices = ss_indices & np.in1d(X[:, feature_idx], split_value) - temp_bound_ss = intersect - else: - temp_child_idx = 1 - temp_node_indices = ss_indices & np.in1d(X[:, feature_idx], split_value, invert=True) - temp_bound_ss = np.setdiff1d(ss_bounds_cat[cat_feature_idx], split_value) - if sum(temp_node_indices) > num_min: - # number of points inside subspace is still greater than num_min, we could go deeper - ss_bounds_cat[cat_feature_idx] = temp_bound_ss - ss_indices = temp_node_indices - node_indices[i] = node.get_child_index(temp_child_idx) - else: - if check_num_min: - stop_update[i] = True - else: - # if we don't check the num_min, we will stay go deeper into the child nodes without - # splitting the subspace - node_indices[i] = node.get_child_index(temp_child_idx) - else: - # This node split the subspace w.r.t. the continuous hyperparameters - split_value = node.get_num_split_value() - cont_feature_idx = cont_feature_idx.item() - if ss_bounds_cont[cont_feature_idx][0] <= split_value <= ss_bounds_cont[cont_feature_idx][1]: - # the subspace can be further split - if challenger[feature_idx] >= split_value: - temp_bound_ss = np.array([split_value, ss_bounds_cont[cont_feature_idx][1]]) - temp_node_indices = ss_indices & (X[:, feature_idx] >= split_value) - temp_child_idx = 1 - else: - temp_bound_ss = np.array([ss_bounds_cont[cont_feature_idx][0], split_value]) - temp_node_indices = ss_indices & (X[:, feature_idx] <= split_value) - temp_child_idx = 0 - if sum(temp_node_indices) > num_min: - # number of points inside subspace is still greater than num_min - ss_bounds_cont[cont_feature_idx] = temp_bound_ss - ss_indices = temp_node_indices - node_indices[i] = node.get_child_index(temp_child_idx) - else: - if check_num_min: - stop_update[i] = True - else: - node_indices[i] = node.get_child_index(temp_child_idx) - else: - temp_child_idx = 1 if challenger[feature_idx] >= split_value else 0 - node_indices[i] = node.get_child_index(temp_child_idx) - - while sum(stop_update) < num_trees: - traverse_forest() - - if sum(ss_indices) > num_max: - # number of points inside the subregion have a larger value than num_max - stop_update = [False] * num_trees - while sum(stop_update) < num_trees: - traverse_forest(False) - - return ss_bounds_cont, ss_bounds_cat, ss_indices # type: ignore[return-value] diff --git a/smac/optimizer/configuration_chooser/epm_chooser.py b/smac/optimizer/configuration_chooser/epm_chooser.py deleted file mode 100644 index 89f14a4df..000000000 --- a/smac/optimizer/configuration_chooser/epm_chooser.py +++ /dev/null @@ -1,235 +0,0 @@ -from typing import Any, Iterator, List, Optional, Tuple - -import logging - -import numpy as np - -from smac.configspace import Configuration -from smac.configspace.util import convert_configurations_to_array -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances -from smac.optimizer.acquisition import AbstractAcquisitionFunction -from smac.optimizer.acquisition.maximizer import ( - AcquisitionFunctionMaximizer, - RandomSearch, -) -from smac.optimizer.configuration_chooser.random_chooser import ( - ChooserNoCoolDown, - RandomChooser, -) -from smac.runhistory.runhistory import RunHistory -from smac.runhistory.runhistory2epm import AbstractRunHistory2EPM -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class EPMChooser: - """Interface to train the EPM and generate/choose next configurations. - - Parameters - ---------- - scenario: smac.scenario.scenario.Scenario - Scenario object - stats: smac.stats.stats.Stats - statistics object with configuration budgets - runhistory: smac.runhistory.runhistory.RunHistory - runhistory with all runs so far - model: smac.epm.rf_with_instances.RandomForestWithInstances - empirical performance model (right now, we support only - RandomForestWithInstances) - acq_optimizer: smac.optimizer.ei_optimization.AcquisitionFunctionMaximizer - Optimizer of acquisition function. - restore_incumbent: Configuration - incumbent to be used from the start. ONLY used to restore states. - rng: np.random.RandomState - Random number generator - random_configuration_chooser: - Chooser for random configuration -- one of - - * ChooserNoCoolDown(modulus) - * ChooserLinearCoolDown(start_modulus, modulus_increment, end_modulus) - predict_x_best: bool - Choose x_best for computing the acquisition function via the model instead of via the observations. - min_samples_model: int - Minimum number of samples to build a model - epm_chooser_kwargs: Any: - additional arguments passed to EPMChooser (Might be used by its subclasses) - """ - - def __init__( - self, - scenario: Scenario, - stats: Stats, - runhistory: RunHistory, - runhistory2epm: AbstractRunHistory2EPM, - model: RandomForestWithInstances, - acq_optimizer: AcquisitionFunctionMaximizer, - acquisition_func: AbstractAcquisitionFunction, - rng: np.random.RandomState, - restore_incumbent: Configuration = None, - random_configuration_chooser: RandomChooser = ChooserNoCoolDown(modulus=2.0), - predict_x_best: bool = True, - min_samples_model: int = 1, - **epm_chooser_kwargs: Any, - ): - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - self.incumbent = restore_incumbent - - self.scenario = scenario - self.stats = stats - self.runhistory = runhistory - self.rh2EPM = runhistory2epm - self.model = model - self.acq_optimizer = acq_optimizer - self.acquisition_func = acquisition_func - self.rng = rng - self.random_configuration_chooser = random_configuration_chooser - - self._random_search = RandomSearch( - acquisition_func, - self.scenario.cs, # type: ignore[attr-defined] # noqa F821 - rng, - ) - - self.initial_design_configs = [] # type: List[Configuration] - - self.predict_x_best = predict_x_best - - self.min_samples_model = min_samples_model - self.currently_considered_budgets = [ - 0.0, - ] - - def _collect_data_to_train_model(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: - # if we use a float value as a budget, we want to train the model only on the highest budget - available_budgets = [] - for run_key in self.runhistory.data.keys(): - available_budgets.append(run_key.budget) - - # Sort available budgets from highest to lowest budget - available_budgets = sorted(list(set(available_budgets)), reverse=True) - - # Get #points per budget and if there are enough samples, then build a model - for b in available_budgets: - X, Y = self.rh2EPM.transform( - self.runhistory, - budget_subset=[ - b, - ], - ) - if X.shape[0] >= self.min_samples_model: - self.currently_considered_budgets = [ - b, - ] - configs_array = self.rh2EPM.get_configurations( - self.runhistory, budget_subset=self.currently_considered_budgets - ) - return X, Y, configs_array - - return ( - np.empty(shape=[0, 0]), - np.empty( - shape=[ - 0, - ] - ), - np.empty(shape=[0, 0]), - ) - - def _get_evaluated_configs(self) -> List[Configuration]: - return self.runhistory.get_all_configs_per_budget(budget_subset=self.currently_considered_budgets) - - def choose_next(self, incumbent_value: float = None) -> Iterator[Configuration]: - """Choose next candidate solution with Bayesian optimization. The suggested configurations - depend on the argument ``acq_optimizer`` to the ``SMBO`` class. - - Parameters - ---------- - incumbent_value: float - Cost value of incumbent configuration (required for acquisition function); - If not given, it will be inferred from runhistory or predicted; - if not given and runhistory is empty, it will raise a ValueError. - - Returns - ------- - Iterator - """ - self.logger.debug("Search for next configuration") - X, Y, X_configurations = self._collect_data_to_train_model() - - if X.shape[0] == 0: - # Only return a single point to avoid an overly high number of - # random search iterations - return self._random_search.maximize(runhistory=self.runhistory, stats=self.stats, num_points=1) - self.model.train(X, Y) - - if incumbent_value is not None: - best_observation = incumbent_value - x_best_array = None # type: Optional[np.ndarray] - else: - if self.runhistory.empty(): - raise ValueError("Runhistory is empty and the cost value of " "the incumbent is unknown.") - x_best_array, best_observation = self._get_x_best(self.predict_x_best, X_configurations) - - self.acquisition_func.update( - model=self.model, - eta=best_observation, - incumbent_array=x_best_array, - num_data=len(self._get_evaluated_configs()), - X=X_configurations, - ) - - challengers = self.acq_optimizer.maximize( - runhistory=self.runhistory, - stats=self.stats, - num_points=self.scenario.acq_opt_challengers, # type: ignore[attr-defined] # noqa F821 - random_configuration_chooser=self.random_configuration_chooser, - ) - return challengers - - def _get_x_best(self, predict: bool, X: np.ndarray) -> Tuple[np.ndarray, float]: - """Get value, configuration, and array representation of the "best" configuration. - - The definition of best varies depending on the argument ``predict``. If set to ``True``, - this function will return the stats of the best configuration as predicted by the model, - otherwise it will return the stats for the best observed configuration. - - Parameters - ---------- - predict : bool - Whether to use the predicted or observed best. - - Returns - ------- - float - np.ndarry - Configuration - """ - if predict: - costs = list( - map( - lambda x: ( - self.model.predict_marginalized_over_instances(x.reshape((1, -1)))[0][0][0], - x, - ), - X, - ) - ) - costs = sorted(costs, key=lambda t: t[0]) - x_best_array = costs[0][1] - best_observation = costs[0][0] - # won't need log(y) if EPM was already trained on log(y) - else: - all_configs = self.runhistory.get_all_configs_per_budget(budget_subset=self.currently_considered_budgets) - x_best = self.incumbent - x_best_array = convert_configurations_to_array(all_configs) - best_observation = self.runhistory.get_cost(x_best) - best_observation_as_array = np.array(best_observation).reshape((1, 1)) - # It's unclear how to do this for inv scaling and potential future scaling. - # This line should be changed if necessary - best_observation = self.rh2EPM.transform_response_values(best_observation_as_array) - best_observation = best_observation[0][0] - - return x_best_array, best_observation diff --git a/smac/optimizer/configuration_chooser/random_chooser.py b/smac/optimizer/configuration_chooser/random_chooser.py deleted file mode 100644 index 6fc990376..000000000 --- a/smac/optimizer/configuration_chooser/random_chooser.py +++ /dev/null @@ -1,217 +0,0 @@ -from abc import ABC, abstractmethod -from typing import Optional - -import logging - -import numpy as np - -__author__ = "Aaron Kimmig" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Aaron Kimmig" -__email__ = "kimmiga@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -class RandomChooser(ABC): - """Abstract base of helper classes to configure interleaving of random configurations in a list - of challengers. - """ - - def __init__(self, rng: Optional[np.random.RandomState] = None): - self.rng = rng or np.random.RandomState(seed=0) - - @abstractmethod - def next_smbo_iteration(self) -> None: - """Indicate beginning of next SMBO iteration.""" - pass - - @abstractmethod - def check(self, iteration: int) -> bool: - """Check if the next configuration should be at random.""" - pass - - -class ChooserNoCoolDown(RandomChooser): - """Interleave a random configuration after a constant number of configurations found by Bayesian - optimization. - - Parameters - ---------- - modulus : float - Every modulus-th configuration will be at random. - """ - - def __init__(self, rng: Optional[np.random.RandomState] = None, modulus: float = 2.0): - super().__init__(rng) - - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - if modulus <= 1.0: - self.logger.warning("Using SMAC with random configurations only." "ROAR is the better choice for this.") - self.modulus = modulus - - def next_smbo_iteration(self) -> None: - """Does nothing.""" - ... - - def check(self, iteration: int) -> bool: - """Checks if the next configuration should be at random.""" - return iteration % self.modulus < 1 - - -class ChooserLinearCoolDown(RandomChooser): - """Interleave a random configuration, decreasing the fraction of random configurations over - time. - - Parameters - ---------- - start_modulus : float - Initially, every modulus-th configuration will be at random - modulus_increment : float - Increase modulus by this amount in every iteration - end_modulus : float - Highest modulus used in the chooser. If the value is reached before the optimization is over, it is not - further increased. If it is not reached before the optimization is over, there will be no adjustment to make - sure that the ``end_modulus`` is reached. - """ - - def __init__( - self, - rng: Optional[np.random.RandomState] = None, - start_modulus: float = 2.0, - modulus_increment: float = 0.3, - end_modulus: float = np.inf, - ): - super().__init__(rng) - - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - if start_modulus <= 1.0 and modulus_increment <= 0.0: - self.logger.warning("Using SMAC with random configurations only. ROAR is the better choice for this.") - self.modulus = start_modulus - self.modulus_increment = modulus_increment - self.end_modulus = end_modulus - self.last_iteration = 0 - - def next_smbo_iteration(self) -> None: - """Change modulus.""" - self.modulus += self.modulus_increment - self.modulus = min(self.modulus, self.end_modulus) - self.last_iteration = 0 - - def check(self, iteration: int) -> bool: - """Check if the next configuration should be interleaved based on modulus.""" - if (iteration - self.last_iteration) % self.modulus < 1: - self.last_iteration = iteration - return True - else: - return False - - -class ChooserProb(RandomChooser): - """Interleave a random configuration according to a given probability. - - Parameters - ---------- - prob : float - Probility of a random configuration - rng : np.random.RandomState - Random state - """ - - def __init__(self, rng: Optional[np.random.RandomState], prob: float): - super().__init__(rng) - self.prob = prob - - def next_smbo_iteration(self) -> None: - """Does nothing.""" - ... - - def check(self, iteration: int) -> bool: - """Check if the next configuration should be at random.""" - if self.rng.rand() < self.prob: - return True - else: - return False - - -class ChooserProbCoolDown(RandomChooser): - """Interleave a random configuration according to a given probability which is decreased over - time. - - Parameters - ---------- - prob : float - Probility of a random configuration - cool_down_fac : float - Multiply the ``prob`` by ``cool_down_fac`` in each iteration - rng : np.random.RandomState - Random state - """ - - def __init__(self, rng: Optional[np.random.RandomState], prob: float, cool_down_fac: float): - super().__init__(rng) - self.prob = prob - self.cool_down_fac = cool_down_fac - - def next_smbo_iteration(self) -> None: - """Set the probability to the current value multiplied by the `cool_down_fac`.""" - self.prob *= self.cool_down_fac - - def check(self, iteration: int) -> bool: - """Check if the next configuration should be at random.""" - if self.rng.rand() < self.prob: - return True - else: - return False - - -class ChooserCosineAnnealing(RandomChooser): - """Interleave a random configuration according to a given probability which is decreased - according to a cosine annealing schedule. - - Parameters - ---------- - prob_max : float - Initial probility of a random configuration - prob_min : float - Lowest probility of a random configuration - restart_iteration : int - Restart the annealing schedule every ``restart_iteration`` iterations. - rng : np.random.RandomState - Random state - """ - - def __init__( - self, - rng: Optional[np.random.RandomState], - prob_max: float, - prob_min: float, - restart_iteration: int, - ): - super().__init__(rng) - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - self.prob_max = prob_max - self.prob_min = prob_min - self.restart_iteration = restart_iteration - self.iteration = 0 - self.prob = prob_max - - def next_smbo_iteration(self) -> None: - """Set `self.prob` and increases the iteration counter.""" - self.prob = self.prob_min + ( - 0.5 * (self.prob_max - self.prob_min) * (1 + np.cos(self.iteration * np.pi / self.restart_iteration)) - ) - self.logger.error("Probability for random configs: %f" % self.prob) - self.iteration += 1 - if self.iteration > self.restart_iteration: - self.iteration = 0 - self.logger.error("Perform restart in next iteration!") - - def check(self, iteration: int) -> bool: - """Check if a random configuration should be interleaved.""" - if self.rng.rand() < self.prob: - self.logger.error("Random Config") - return True - else: - self.logger.error("Acq Config") - return False diff --git a/smac/optimizer/configuration_chooser/turbo_chooser.py b/smac/optimizer/configuration_chooser/turbo_chooser.py deleted file mode 100644 index 443981181..000000000 --- a/smac/optimizer/configuration_chooser/turbo_chooser.py +++ /dev/null @@ -1,131 +0,0 @@ -import typing - -import numpy as np - -from smac.configspace import Configuration -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances -from smac.epm.utils import get_types -from smac.optimizer.acquisition import TS, AbstractAcquisitionFunction -from smac.optimizer.acquisition.maximizer import AcquisitionFunctionMaximizer -from smac.optimizer.configuration_chooser.epm_chooser import EPMChooser -from smac.optimizer.configuration_chooser.random_chooser import ( - ChooserNoCoolDown, - RandomChooser, -) -from smac.optimizer.subspaces.turbo_subspace import TuRBOSubSpace -from smac.runhistory.runhistory import RunHistory -from smac.runhistory.runhistory2epm import AbstractRunHistory2EPM -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats - - -class TurBOChooser(EPMChooser): - """ - Interface to train the EPM and generate next configurations with TurBO: - D. Eriksson et al. Scalable Global Optimization via Local Bayesian Optimization - https://papers.nips.cc/paper/2019/file/6c990b7aca7bc7058f5e98ea909e924b-Paper.pdf - - Parameters - ---------- - length_init: float - Initialized length after restarting - length_min: float - If the subspace length is smaller than length_min, TurBO will restart - length_max: float - The maximum length of subspace - success_tol: int - Number of successful suggestions (suggested points become incumbent) required for expanding subspace - failure_tol_min: int - The minimum number of failure suggestions (suggested points fails to become incumbent) required for shrinking - subspace - n_init_x_params: int - how many configurations will be used at most in the initial design (X*D). Used for restarting the subspace - n_candidate_max: int - Maximal Number of points used as candidates - """ - - def __init__( - self, - scenario: Scenario, - stats: Stats, - runhistory: RunHistory, - runhistory2epm: AbstractRunHistory2EPM, - model: RandomForestWithInstances, - acq_optimizer: AcquisitionFunctionMaximizer, - acquisition_func: AbstractAcquisitionFunction, - rng: np.random.RandomState, - restore_incumbent: Configuration = None, - random_configuration_chooser: RandomChooser = ChooserNoCoolDown(2.0), - predict_x_best: bool = False, - min_samples_model: int = 1, - length_init: float = 0.8, - length_min: float = 0.5**8, - length_max: float = 1.6, - success_tol: int = 3, - failure_tol_min: int = 4, - n_init_x_params: int = 2, - n_candidate_max: int = 5000, - ): - super(TurBOChooser, self).__init__( - scenario=scenario, - stats=stats, - runhistory=runhistory, - runhistory2epm=runhistory2epm, - model=model, - acquisition_func=acquisition_func, - acq_optimizer=acq_optimizer, - restore_incumbent=restore_incumbent, - rng=rng, - random_configuration_chooser=random_configuration_chooser, - predict_x_best=predict_x_best, - min_samples_model=min_samples_model, - ) - cs = self.scenario.cs # type: ignore - types, bounds = get_types(cs, instance_features=None) - - self.turbo = TuRBOSubSpace( - config_space=cs, - bounds=bounds, - hps_types=types, - model_local=model, - acq_func_local=TS, - length_init=length_init, - length_min=length_min, - length_max=length_max, - success_tol=success_tol, - failure_tol_min=failure_tol_min, - n_init_x_params=n_init_x_params, - n_candidate_max=n_candidate_max, - ) - - def choose_next(self, incumbent_value: float = None) -> typing.Iterator[Configuration]: - """ - Choose next candidate solution with TuRBO - - Parameters - ---------- - incumbent_value: float - Cost value of incumbent configuration (required for acquisition function); - If not given, it will be inferred from runhistory or predicted; - if not given and runhistory is empty, it will raise a ValueError. - - Returns - ------- - Iterator - """ - self.logger.debug("Search for next configuration") - X, Y, X_configurations = self._collect_data_to_train_model() - - num_new_bservations = 1 # here we only consider batch size = 1 - - new_observations = Y[-num_new_bservations:] - if len(self.turbo.init_configs) > 0: - self.turbo.add_new_observations(X[-num_new_bservations:], Y[-num_new_bservations:]) - return self.turbo.generate_challengers() - - self.turbo.adjust_length(new_observations) - - self.turbo.add_new_observations(X[-num_new_bservations:], Y[-num_new_bservations:]) - - challengers = self.turbo.generate_challengers() - return challengers diff --git a/smac/optimizer/pSMAC.py b/smac/optimizer/pSMAC.py deleted file mode 100644 index 7d1e94e56..000000000 --- a/smac/optimizer/pSMAC.py +++ /dev/null @@ -1,92 +0,0 @@ -import typing - -import glob -import logging -import os -import re -import tempfile - -from smac.configspace import ConfigurationSpace -from smac.runhistory.runhistory import RunHistory - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -RUNHISTORY_FILEPATTERN = "runhistory.json" -RUNHISTORY_RE = r"runhistory\.json$" -VALIDATEDRUNHISTORY_RE = r"validated_runhistory\.json$" - - -def read( - run_history: RunHistory, - output_dirs: typing.Union[str, typing.List[str]], - configuration_space: ConfigurationSpace, - logger: logging.Logger, -) -> None: - """Update runhistory with run results from concurrent runs of pSMAC. - - Parameters - ---------- - run_history : smac.runhistory.RunHistory - RunHistory object to be updated with run information from runhistory - objects stored in the output directory. - output_dirs : typing.Union[str,typing.List[str]] - List of SMAC output directories - or Linux path expression (str) which will be casted into a list with - glob.glob(). This function will search the output directories - for files matching the runhistory regular expression. - configuration_space : ConfigSpace.ConfigurationSpace - A ConfigurationSpace object to check if loaded configurations are valid. - logger : logging.Logger - """ - numruns_in_runhistory = len(run_history.data) - initial_numruns_in_runhistory = numruns_in_runhistory - - if isinstance(output_dirs, str): - parsed_output_dirs = glob.glob(output_dirs) - if glob.glob(os.path.join(output_dirs, "run_*")): - parsed_output_dirs += glob.glob(os.path.join(output_dirs, "run_*")) - else: - parsed_output_dirs = output_dirs - - for output_directory in parsed_output_dirs: - for file_in_output_directory in os.listdir(output_directory): - match = re.match(RUNHISTORY_RE, file_in_output_directory) - valid_match = re.match(VALIDATEDRUNHISTORY_RE, file_in_output_directory) - if match or valid_match: - runhistory_file = os.path.join(output_directory, file_in_output_directory) - run_history.update_from_json(runhistory_file, configuration_space) - - new_numruns_in_runhistory = len(run_history.data) - difference = new_numruns_in_runhistory - numruns_in_runhistory - logger.debug("Shared model mode: Loaded %d new runs from %s" % (difference, runhistory_file)) - numruns_in_runhistory = new_numruns_in_runhistory - - difference = numruns_in_runhistory - initial_numruns_in_runhistory - logger.info("Shared model mode: Finished loading new runs, found %d new runs." % difference) - - -def write(run_history: RunHistory, output_directory: str, logger: logging.Logger) -> None: - """Write the runhistory to the output directory. - - Overwrites previously outputted runhistories. - - Parameters - ---------- - run_history : ~smac.runhistory.runhistory.RunHistory - RunHistory object to be saved. - - output_directory : str - - logger : logging.Logger - """ - output_filename = os.path.join(output_directory, RUNHISTORY_FILEPATTERN) - - logger.debug("Saving runhistory to %s" % output_filename) - - with tempfile.NamedTemporaryFile("wb", dir=output_directory, delete=False) as fh: - temporary_filename = fh.name - - run_history.save_json(temporary_filename, save_external=False) - os.rename(temporary_filename, output_filename) diff --git a/smac/optimizer/smbo.py b/smac/optimizer/smbo.py deleted file mode 100644 index a3cfd34d8..000000000 --- a/smac/optimizer/smbo.py +++ /dev/null @@ -1,544 +0,0 @@ -from typing import Callable, Dict, List, Optional, Type, Union - -import logging -import os -import time - -import numpy as np - -from smac.callbacks import IncorporateRunResultCallback -from smac.configspace import Configuration -from smac.epm.base_epm import BaseEPM -from smac.initial_design.initial_design import InitialDesign -from smac.intensification.abstract_racer import AbstractRacer, RunInfoIntent -from smac.optimizer import pSMAC -from smac.optimizer.acquisition import AbstractAcquisitionFunction -from smac.optimizer.acquisition.maximizer import AcquisitionFunctionMaximizer -from smac.optimizer.configuration_chooser.epm_chooser import EPMChooser -from smac.optimizer.configuration_chooser.random_chooser import ( - ChooserNoCoolDown, - RandomChooser, -) -from smac.runhistory.runhistory import RunHistory, RunInfo, RunValue -from smac.runhistory.runhistory2epm import AbstractRunHistory2EPM -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import FirstRunCrashedException, StatusType, TAEAbortException -from smac.tae.base import BaseRunner -from smac.utils.constants import MAXINT -from smac.utils.io.traj_logging import TrajLogger -from smac.utils.validate import Validator - -__author__ = "Aaron Klein, Marius Lindauer, Matthias Feurer" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" - - -class SMBO(object): - """Interface that contains the main Bayesian optimization loop. - - Parameters - ---------- - scenario: smac.scenario.scenario.Scenario - Scenario object - stats: Stats - statistics object with configuration budgets - initial_design: InitialDesign - initial sampling design - runhistory: RunHistory - runhistory with all runs so far - runhistory2epm : AbstractRunHistory2EPM - Object that implements the AbstractRunHistory2EPM to convert runhistory - data into EPM data - intensifier: Intensifier - intensification of new challengers against incumbent configuration - (probably with some kind of racing on the instances) - num_run: int - id of this run (used for pSMAC) - model: BaseEPM - empirical performance model - acq_optimizer: AcquisitionFunctionMaximizer - Optimizer of acquisition function. - acquisition_func : AcquisitionFunction - Object that implements the AbstractAcquisitionFunction (i.e., infill criterion for acq_optimizer) - restore_incumbent: Configuration - incumbent to be used from the start. ONLY used to restore states. - rng: np.random.RandomState - Random number generator - tae_runner : smac.tae.base.BaseRunner Object - target algorithm run executor - random_configuration_chooser - Chooser for random configuration -- one of - * ChooserNoCoolDown(modulus) - * ChooserLinearCoolDown(start_modulus, modulus_increment, end_modulus) - predict_x_best: bool - Choose x_best for computing the acquisition function via the model instead of via the observations. - min_samples_model: int - Minimum number of samples to build a model. - epm_chooser_kwargs: typing.Optional[typing.Dict] - Additional arguments passed to epmchooser - - Attributes - ---------- - logger - incumbent - scenario - config_space - stats - initial_design - runhistory - intensifier - num_run - rng - initial_design_configs - epm_chooser - tae_runner - """ - - def __init__( - self, - scenario: Scenario, - stats: Stats, - initial_design: InitialDesign, - runhistory: RunHistory, - runhistory2epm: AbstractRunHistory2EPM, - intensifier: AbstractRacer, - num_run: int, - model: BaseEPM, - acq_optimizer: AcquisitionFunctionMaximizer, - acquisition_func: AbstractAcquisitionFunction, - rng: np.random.RandomState, - tae_runner: BaseRunner, - restore_incumbent: Configuration = None, - random_configuration_chooser: RandomChooser = ChooserNoCoolDown(modulus=2.0), - predict_x_best: bool = True, - min_samples_model: int = 1, - epm_chooser: Type[EPMChooser] = EPMChooser, - epm_chooser_kwargs: Optional[Dict] = None, - ): - - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - self.incumbent = restore_incumbent - - self.scenario = scenario - self.config_space = scenario.cs # type: ignore[attr-defined] # noqa F821 - self.stats = stats - self.initial_design = initial_design - self.runhistory = runhistory - self.intensifier = intensifier - self.num_run = num_run - self.rng = rng - self._min_time = 10**-5 - self.tae_runner = tae_runner - - self.initial_design_configs = [] # type: List[Configuration] - - if epm_chooser_kwargs is None: - epm_chooser_kwargs = {} - # TODO: consider if we need an additional EPMChooser for multi-objective optimization - self.epm_chooser = epm_chooser( - scenario=scenario, - stats=stats, - runhistory=runhistory, - runhistory2epm=runhistory2epm, - model=model, # type: ignore - acq_optimizer=acq_optimizer, - acquisition_func=acquisition_func, - rng=rng, - restore_incumbent=restore_incumbent, - random_configuration_chooser=random_configuration_chooser, - predict_x_best=predict_x_best, - min_samples_model=min_samples_model, - **epm_chooser_kwargs, - ) - - # Internal variable - if this is set to True it will gracefully stop SMAC - self._stop = False - - # Callbacks. All known callbacks have a key. If something does not have a key here, there is - # no callback available. - self._callbacks = {"_incorporate_run_results": list()} # type: Dict[str, List[Callable]] - self._callback_to_key = { - IncorporateRunResultCallback: "_incorporate_run_results", - } # type: Dict[Type, str] - - def start(self) -> None: - """Starts the Bayesian Optimization loop. - - Detects whether the optimization is restored from a previous state. - """ - self.stats.start_timing() - - # Initialization, depends on input - if self.stats.submitted_ta_runs == 0 and self.incumbent is None: - self.logger.info("Running initial design") - # Intensifier initialization - self.initial_design_configs = self.initial_design.select_configurations() - - # to be on the safe side, never return an empty list of initial configs - if not self.initial_design_configs: - self.initial_design_configs = [self.config_space.get_default_configuration()] - - elif self.stats.submitted_ta_runs > 0 and self.incumbent is None: - raise ValueError( - "According to stats there have been runs started, " - "but the optimizer cannot detect an incumbent. Did " - "you set the incumbent (e.g. after restoring state)?" - ) - elif self.stats.submitted_ta_runs == 0 and self.incumbent is not None: - raise ValueError( - "An incumbent is specified, but there are no runs " - "recorded as started in the Stats-object. If you're " - "restoring a state, please provide the Stats-object." - ) - else: - # Restoring state! - self.logger.info( - "State Restored! Starting optimization with " "incumbent %s", - self.incumbent, - ) - self.logger.info("State restored with following budget:") - self.stats.print_stats() - - def run(self) -> Configuration: - """Runs the Bayesian optimization loop. - - Returns - ------- - incumbent: np.array(1, H) - The best found configuration. - """ - self.start() - - num_obj = len(self.scenario.multi_objectives) # type: ignore[attr-defined] # noqa F821 - - # Main BO loop - while True: - if self.scenario.shared_model: # type: ignore[attr-defined] # noqa F821 - pSMAC.read( - run_history=self.runhistory, - output_dirs=self.scenario.input_psmac_dirs, # type: ignore[attr-defined] # noqa F821 - configuration_space=self.config_space, - logger=self.logger, - ) - - start_time = time.time() - - # sample next configuration for intensification - # Initial design runs are also included in the BO loop now. - intent, run_info = self.intensifier.get_next_run( - challengers=self.initial_design_configs, - incumbent=self.incumbent, - chooser=self.epm_chooser, - run_history=self.runhistory, - repeat_configs=self.intensifier.repeat_configs, - num_workers=self.tae_runner.num_workers(), - ) - - # remove config from initial design challengers to not repeat it again - self.initial_design_configs = [c for c in self.initial_design_configs if c != run_info.config] - - # update timebound only if a 'new' configuration is sampled as the challenger - if self.intensifier.num_run == 0: - time_spent = time.time() - start_time - time_left = self._get_timebound_for_intensification(time_spent, update=False) - self.logger.debug("New intensification time bound: %f", time_left) - else: - old_time_left = time_left - time_spent = time_spent + (time.time() - start_time) - time_left = self._get_timebound_for_intensification(time_spent, update=True) - self.logger.debug( - "Updated intensification time bound from %f to %f", - old_time_left, - time_left, - ) - - # Skip starting new runs if the budget is now exhausted - if self.stats.is_budget_exhausted(): - intent = RunInfoIntent.SKIP - - # Skip the run if there was a request to do so. - # For example, during intensifier intensification, we - # don't want to rerun a config that was previously ran - if intent == RunInfoIntent.RUN: - # Track the fact that a run was launched in the run - # history. It's status is tagged as RUNNING, and once - # completed and processed, it will be updated accordingly - self.runhistory.add( - config=run_info.config, - cost=float(MAXINT) if num_obj == 1 else np.full(num_obj, float(MAXINT)), - time=0.0, - status=StatusType.RUNNING, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - ) - - run_info.config.config_id = self.runhistory.config_ids[run_info.config] - - self.tae_runner.submit_run(run_info=run_info) - - # There are 2 criteria that the stats object uses to know - # if the budged was exhausted. - # The budget time, which can only be known when the run finishes, - # And the number of ta executions. Because we submit the job at this point, - # we count this submission as a run. This prevent for using more - # runner runs than what the scenario allows - self.stats.submitted_ta_runs += 1 - - elif intent == RunInfoIntent.SKIP: - # No launch is required - # This marks a transition request from the intensifier - # To a new iteration - pass - elif intent == RunInfoIntent.WAIT: - # In any other case, we wait for resources - # This likely indicates that no further decision - # can be taken by the intensifier until more data is - # available - self.tae_runner.wait() - else: - raise NotImplementedError("No other RunInfoIntent has been coded!") - - # Check if there is any result, or else continue - for run_info, result in self.tae_runner.get_finished_runs(): - # Add the results of the run to the run history - # Additionally check for new incumbent - self._incorporate_run_results(run_info, result, time_left) - - if self.scenario.shared_model: # type: ignore[attr-defined] # noqa F821 - assert self.scenario.output_dir_for_this_run is not None # please mypy - pSMAC.write( - run_history=self.runhistory, - output_directory=self.scenario.output_dir_for_this_run, # type: ignore[attr-defined] # noqa F821 - logger=self.logger, - ) - - self.logger.debug( - "Remaining budget: %f (wallclock), %f (ta costs), %f (target runs)" - % ( - self.stats.get_remaing_time_budget(), - self.stats.get_remaining_ta_budget(), - self.stats.get_remaining_ta_runs(), - ) - ) - - if self.stats.is_budget_exhausted() or self._stop: - if self.stats.is_budget_exhausted(): - self.logger.debug("Exhausted configuration budget") - else: - self.logger.debug("Shutting down because a configuration or callback returned status STOP") - - # The budget can be exhausted for 2 reasons: number of ta runs or - # time. If the number of ta runs is reached, but there is still budget, - # wait for the runs to finish - while self.tae_runner.pending_runs(): - - self.tae_runner.wait() - - for run_info, result in self.tae_runner.get_finished_runs(): - # Add the results of the run to the run history - # Additionally check for new incumbent - self._incorporate_run_results(run_info, result, time_left) - - # Break from the intensification loop, - # as there are no more resources - break - - # print stats at the end of each intensification iteration - if self.intensifier.iteration_done: - self.stats.print_stats(debug_out=True) - - return self.incumbent - - def validate( - self, - config_mode: Union[str, List[Configuration]] = "inc", - instance_mode: Union[str, List[str]] = "train+test", - repetitions: int = 1, - use_epm: bool = False, - n_jobs: int = -1, - backend: str = "threading", - ) -> RunHistory: - """Create validator-object and run validation, using scenario- information, runhistory from - smbo and tae_runner from intensify. - - Parameters - ---------- - config_mode: str or list - string or directly a list of Configuration - str from [def, inc, def+inc, wallclock_time, cpu_time, all] - time evaluates at cpu- or wallclock-timesteps of: - [max_time/2^0, max_time/2^1, max_time/2^3, ..., default] - with max_time being the highest recorded time - instance_mode: string - what instances to use for validation, from [train, test, train+test] - repetitions: int - number of repetitions in nondeterministic algorithms (in - deterministic will be fixed to 1) - use_epm: bool - whether to use an EPM instead of evaluating all runs with the TAE - n_jobs: int - number of parallel processes used by joblib - - Returns - ------- - runhistory: RunHistory - runhistory containing all specified runs - """ - if isinstance(config_mode, str): - assert self.scenario.output_dir_for_this_run is not None # Please mypy - traj_fn = os.path.join(self.scenario.output_dir_for_this_run, "traj_aclib2.json") - trajectory = TrajLogger.read_traj_aclib_format( - fn=traj_fn, cs=self.config_space - ) # type: Optional[List[Dict[str, Union[float, int, Configuration]]]] - else: - trajectory = None - if self.scenario.output_dir_for_this_run: - new_rh_path = os.path.join( - self.scenario.output_dir_for_this_run, "validated_runhistory.json" - ) # type: Optional[str] # noqa E501 - else: - new_rh_path = None - - validator = Validator(self.scenario, trajectory, self.rng) - if use_epm: - new_rh = validator.validate_epm( - config_mode=config_mode, - instance_mode=instance_mode, - repetitions=repetitions, - runhistory=self.runhistory, - output_fn=new_rh_path, - ) - else: - new_rh = validator.validate( - config_mode, - instance_mode, - repetitions, - n_jobs, - backend, - self.runhistory, - self.tae_runner, - output_fn=new_rh_path, - ) - return new_rh - - def _get_timebound_for_intensification(self, time_spent: float, update: bool) -> float: - """Calculate time left for intensify from the time spent on choosing challengers using the - fraction of time intended for intensification (which is specified in - scenario.intensification_percentage). - - Parameters - ---------- - time_spent : float - - update : bool - Only used to check in the unit tests how this function was called - - Returns - ------- - time_left : float - """ - frac_intensify = self.scenario.intensification_percentage # type: ignore[attr-defined] # noqa F821 - if frac_intensify <= 0 or frac_intensify >= 1: - raise ValueError( - "The value for intensification_percentage-" "option must lie in (0,1), instead: %.2f" % frac_intensify - ) - total_time = time_spent / (1 - frac_intensify) - time_left = frac_intensify * total_time - self.logger.debug( - "Total time: %.4f, time spent on choosing next " - "configurations: %.4f (%.2f), time left for " - "intensification: %.4f (%.2f)" % (total_time, time_spent, (1 - frac_intensify), time_left, frac_intensify) - ) - return time_left - - def _incorporate_run_results(self, run_info: RunInfo, result: RunValue, time_left: float) -> None: - """The SMBO submits a config-run-request via a RunInfo object. When that config run is - completed, a RunValue, which contains all the relevant information obtained after running a - job, is returned. This method incorporates the status of that run into the stats/runhistory - objects so that other consumers can advance with their task. - - Additionally, it checks for a new incumbent via the intensifier process results, - which also has the side effect of moving the intensifier to a new state - - Parameters - ---------- - run_info: RunInfo - Describes the run (config) from which to process the results - result: RunValue - Contains relevant information regarding the execution of a config - time_left: float - time in [sec] available to perform intensify - """ - # update SMAC stats - self.stats.ta_time_used += float(result.time) - self.stats.finished_ta_runs += 1 - - self.logger.debug( - f"Return: Status: {result.status}, cost: {result.cost}, time: {result.time}, " - f"additional: {result.additional_info}" - ) - - self.runhistory.add( - config=run_info.config, - cost=result.cost, - time=result.time, - status=result.status, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - starttime=result.starttime, - endtime=result.endtime, - force_update=True, - additional_info=result.additional_info, - ) - self.stats.n_configs = len(self.runhistory.config_ids) - - if result.status == StatusType.ABORT: - raise TAEAbortException( - "Target algorithm status ABORT - SMAC will " - "exit. The last incumbent can be found " - "in the trajectory-file." - ) - elif result.status == StatusType.STOP: - self._stop = True - return - - if self.scenario.abort_on_first_run_crash: # type: ignore[attr-defined] # noqa F821 - if self.stats.finished_ta_runs == 1 and result.status == StatusType.CRASHED: - raise FirstRunCrashedException( - "First run crashed, abort. Please check your setup -- we assume that your default " - "configuration does not crashes. (To deactivate this exception, use the SMAC scenario option " - "'abort_on_first_run_crash'). Additional run info: %s" % result.additional_info - ) - - # Update the intensifier with the result of the runs - self.incumbent, inc_perf = self.intensifier.process_results( - run_info=run_info, - incumbent=self.incumbent, - run_history=self.runhistory, - time_bound=max(self._min_time, time_left), - result=result, - ) - - for callback in self._callbacks["_incorporate_run_results"]: - response = callback(smbo=self, run_info=run_info, result=result, time_left=time_left) - # If a callback returns False, the optimization loop should be interrupted - # the other callbacks are still being called - if response is False: - self.logger.debug("An IncorporateRunResultCallback returned False, requesting abort.") - self._stop = True - - if self.scenario.save_instantly: # type: ignore[attr-defined] # noqa F821 - self.save() - - return - - def save(self) -> None: - """Saves the current stats and runhistory.""" - self.stats.save() - - output_dir = self.scenario.output_dir_for_this_run - if output_dir is not None: - self.runhistory.save_json(fn=os.path.join(output_dir, "runhistory.json")) diff --git a/smac/optimizer/subspaces/__init__.py b/smac/optimizer/subspaces/__init__.py deleted file mode 100644 index 66b305a92..000000000 --- a/smac/optimizer/subspaces/__init__.py +++ /dev/null @@ -1,656 +0,0 @@ -from abc import ABC, abstractmethod -from typing import Any, Dict, Iterator, List, Optional, Tuple, Type, Union - -import copy -import inspect -import logging -import math - -import numpy as np -from ConfigSpace.forbidden import ( - AbstractForbiddenComponent, - ForbiddenAndConjunction, - MultipleValueForbiddenClause, -) -from ConfigSpace.hyperparameters import ( - CategoricalHyperparameter, - Constant, - Hyperparameter, - NumericalHyperparameter, - OrdinalHyperparameter, - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) - -from smac.configspace import Configuration, ConfigurationSpace -from smac.epm.base_epm import BaseEPM -from smac.epm.gaussian_process.augmented import GloballyAugmentedLocalGaussianProcess -from smac.epm.gaussian_process.kernels.boing import construct_gp_kernel -from smac.epm.utils import check_subspace_points -from smac.optimizer.acquisition import EI, AbstractAcquisitionFunction - - -class LocalSubspace(ABC): - """ - A subspace that is designed for local Bayesian Optimization. If bounds_ss_cont and bounds_ss_cat are not given, - this subspace is equivalent to the original configuration space. Additionally, this subspace - supports local BO that only works with a subset of the dimensions, where the missing values are filled by the - corresponding values from incumbent_array. - - Parameters - ---------- - config_space: ConfigurationSpace - raw Configuration space - bounds: List[Tuple[float, float]] - raw bounds of the Configuration space, notice that here bounds denotes the bounds of the entire space - hps_types: List[int], - types of the hyperparameters - bounds_ss_cont: np.ndarray(D_cont, 2) - subspaces bounds of continuous hyperparameters, its length is the number of continuous hyperparameters - bounds_ss_cat: List[Tuple] - subspaces bounds of categorical hyperparameters, its length is the number of categorical hyperparameters - rng: np.random.RandomState - random state - model_local: ~smac.epm.base_epm.BaseEPM - model in subspace - model_local_kwargs: Optional[Dict] - argument for subspace model - acq_func_local: ~smac.optimizer.ei_optimization.AbstractAcquisitionFunction - local acquisition function - acq_func_local_kwargs: Optional[Dict] - argument for acquisition function - activate_dims: Optional[np.ndarray] - activate dimensions in the subspace, if it is None, we preserve all the dimensions - incumbent_array: Optional[np.ndarray] - incumbent array, used when activate_dims has less dimension and this value is used to complementary the - resulted configurations - """ - - def __init__( - self, - config_space: ConfigurationSpace, - bounds: List[Tuple[float, float]], - hps_types: List[int], - bounds_ss_cont: Optional[np.ndarray] = None, - bounds_ss_cat: Optional[List[Tuple]] = None, - model_local: Union[BaseEPM, Type[BaseEPM]] = GloballyAugmentedLocalGaussianProcess, - model_local_kwargs: Dict = {}, - acq_func_local: Union[AbstractAcquisitionFunction, Type[AbstractAcquisitionFunction]] = EI, - acq_func_local_kwargs: Optional[Dict] = None, - rng: Optional[np.random.RandomState] = None, - initial_data: Optional[Tuple[np.ndarray, np.ndarray]] = None, - activate_dims: Optional[np.ndarray] = None, - incumbent_array: Optional[np.ndarray] = None, - ): - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - self.cs_global = config_space - if rng is None: - self.rng = np.random.RandomState(1) - else: - self.rng = np.random.RandomState(rng.randint(0, 2**20)) - - n_hypers = len(config_space.get_hyperparameters()) - model_types = copy.deepcopy(hps_types) - model_bounds = copy.deepcopy(bounds) - - cat_dims = np.where(np.array(hps_types) != 0)[0] - cont_dims = np.where(np.array(hps_types) == 0)[0] - - if activate_dims is None: - activate_dims = np.arange(n_hypers) - activate_dims_cont = cont_dims - activate_dims_cat = cat_dims - self.activate_dims = activate_dims - activate_dims_cont_ss = np.arange(len(activate_dims_cont)) - activate_dims_cat_ss = np.arange(len(activate_dims_cat)) - else: - activate_dims_cont, _, activate_dims_cont_ss = np.intersect1d( - activate_dims, cont_dims, assume_unique=True, return_indices=True - ) - activate_dims_cat, _, activate_dims_cat_ss = np.intersect1d( - activate_dims, cat_dims, assume_unique=True, return_indices=True - ) - self.activate_dims = activate_dims - - self.activate_dims_cont = activate_dims_cont_ss - self.activate_dims_cat = activate_dims_cat_ss - - lbs = np.full(n_hypers, 0.0) - scales = np.full(n_hypers, 1.0) - - if bounds_ss_cont is None and bounds_ss_cat is None: - # cs_inner is cs - self.cs_local = config_space - self.new_config_space = False - self.bounds_ss_cont = np.tile([0.0, 1.0], [len(self.activate_dims_cont), 1]) - self.bounds_ss_cat = [] # type: Optional[List[Tuple]] - self.lbs = lbs - self.scales = scales - self.new_config = False - - else: - self.new_config = True - # we normalize the non-CategoricalHyperparameter by x = (x-lb)*scale - - hps = config_space.get_hyperparameters() - - # deal with categorical hyperaprameters - for i, cat_idx in enumerate(activate_dims_cat): - hp_cat = hps[cat_idx] # type: CategoricalHyperparameter - parents = config_space.get_parents_of(hp_cat.name) - if len(parents) == 0: - can_be_inactive = False - else: - can_be_inactive = True - if bounds_ss_cat is None: - n_cats = len(hp_cat.choices) - else: - n_cats = len(bounds_ss_cat[i]) - if can_be_inactive: - n_cats = n_cats + 1 - model_types[cat_idx] = n_cats - model_bounds[cat_idx] = (int(n_cats), np.nan) - - # store the dimensions of numerical hyperparameters, UniformFloatHyperparameter and - # UniformIntegerHyperparameter - dims_cont_num = [] - idx_cont_num = [] - dims_cont_ord = [] - idx_cont_ord = [] - ord_hps = {} - - # deal with ordinary hyperaprameters - for i, cont_idx in enumerate(activate_dims_cont): - param = hps[cont_idx] - if isinstance(param, OrdinalHyperparameter): - parents = config_space.get_parents_of(param.name) - if len(parents) == 0: - can_be_inactive = False - else: - can_be_inactive = True - if bounds_ss_cont is None: - n_seqs = len(param.sequence) - else: - n_seqs = bounds_ss_cont[i][1] - bounds_ss_cont[i][0] + 1 - if can_be_inactive: - model_bounds[cont_idx] = (0, int(n_seqs)) - else: - model_bounds[cont_idx] = (0, int(n_seqs) - 1) - if bounds_ss_cont is None: - lbs[cont_idx] = 0 # in subspace, it should start from 0 - ord_hps[param.name] = (0, int(n_seqs)) - else: - lbs[cont_idx] = bounds_ss_cont[i][0] # in subspace, it should start from 0 - ord_hps[param.name] = bounds_ss_cont[i] - dims_cont_ord.append(cont_idx) - idx_cont_ord.append(i) - else: - dims_cont_num.append(cont_idx) - idx_cont_num.append(i) - - if bounds_ss_cat is not None: - self.bounds_ss_cat = [bounds_ss_cat[act_dims_cat_ss] for act_dims_cat_ss in activate_dims_cat_ss] - else: - self.bounds_ss_cat = None - self.bounds_ss_cont = bounds_ss_cont[activate_dims_cont_ss] if bounds_ss_cont is not None else None - - if bounds_ss_cont is None: - lbs[dims_cont_num] = 0.0 - scales[dims_cont_num] = 1.0 - else: - lbs[dims_cont_num] = bounds_ss_cont[idx_cont_num, 0] - # rescale numerical hyperparameters to [0., 1.] - scales[dims_cont_num] = 1.0 / (bounds_ss_cont[idx_cont_num, 1] - bounds_ss_cont[idx_cont_num, 0]) - - self.lbs = lbs[activate_dims] - self.scales = scales[activate_dims] - - self.cs_local = ConfigurationSpace() - hp_list = [] - idx_cont = 0 - idx_cat = 0 - - hps = config_space.get_hyperparameters() - - for idx in self.activate_dims: - param = hps[idx] - if isinstance(param, CategoricalHyperparameter): - if bounds_ss_cat is None: - hp_new = copy.deepcopy(param) - idx_cat += 1 - else: - choices = [param.choices[int(choice_idx)] for choice_idx in bounds_ss_cat[idx_cat]] - # cat_freq_arr = np.array((cats_freq[idx_cat])) - # weights = cat_freq_arr / np.sum(cat_freq_arr) - hp_new = CategoricalHyperparameter(param.name, choices=choices) # , weights=weights) - idx_cat += 1 - - elif isinstance(param, OrdinalHyperparameter): - param_seq = ord_hps.get(param.name) - raw_seq = param.sequence - ord_indices = np.arange(*param_seq) - new_seq = [raw_seq[int(round(idx))] for idx in ord_indices] - hp_new = OrdinalHyperparameter(param.name, sequence=new_seq) - idx_cont += 1 - - elif isinstance(param, Constant): - hp_new = copy.deepcopy(param) - elif isinstance(param, (UniformFloatHyperparameter, UniformIntegerHyperparameter)): - if bounds_ss_cont is None: - hp_new = copy.deepcopy(param) - idx_cont += 1 - else: - if isinstance(param, UniformFloatHyperparameter): - lower = param.lower - upper = param.upper - if param.log: - lower_log = np.log(lower) - upper_log = np.log(upper) - hp_new_lower = np.exp((upper_log - lower_log) * bounds_ss_cont[idx_cont][0] + lower_log) - hp_new_upper = np.exp((upper_log - lower_log) * bounds_ss_cont[idx_cont][1] + lower_log) - hp_new = UniformFloatHyperparameter( - name=param.name, - lower=max(hp_new_lower, lower), - upper=min(hp_new_upper, upper), - log=True, - ) - else: - hp_new_lower = (upper - lower) * bounds_ss_cont[idx_cont][0] + lower - hp_new_upper = (upper - lower) * bounds_ss_cont[idx_cont][1] + lower - hp_new = UniformFloatHyperparameter( - name=param.name, - lower=max(hp_new_lower, lower), - upper=min(hp_new_upper, upper), - log=False, - ) - idx_cont += 1 - elif isinstance(param, UniformIntegerHyperparameter): - lower = param.lower - upper = param.upper - if param.log: - lower_log = np.log(lower) - upper_log = np.log(upper) - hp_new_lower = int( - math.floor( - np.exp((upper_log - lower_log) * bounds_ss_cont[idx_cont][0] + lower_log) - ) - ) - hp_new_upper = int( - math.ceil(np.exp((upper_log - lower_log) * bounds_ss_cont[idx_cont][1] + lower_log)) - ) - - hp_new_lower_log = np.log(hp_new_lower) - hp_new_upper_log = np.log(hp_new_upper) - new_scale = (upper_log - lower_log) / (hp_new_upper_log - hp_new_lower_log) - new_lb = (hp_new_lower_log - lower_log) / (hp_new_upper_log - hp_new_lower_log) - - self.scales[idx] = new_scale - self.lbs[idx] = new_lb - - hp_new = UniformIntegerHyperparameter( - name=param.name, - lower=max(hp_new_lower, lower), - upper=min(hp_new_upper, upper), - log=True, - ) - else: - hp_new_lower = int(math.floor((upper - lower) * bounds_ss_cont[idx_cont][0])) + lower - hp_new_upper = int(math.ceil((upper - lower) * bounds_ss_cont[idx_cont][1])) + lower - - new_scale = (upper - lower) / (hp_new_upper - hp_new_lower) - new_lb = (hp_new_lower - lower) / (hp_new_upper - hp_new_lower) - self.scales[idx] = new_scale - self.lbs[idx] = new_lb - - hp_new = UniformIntegerHyperparameter( - name=param.name, - lower=max(hp_new_lower, lower), - upper=min(hp_new_upper, upper), - log=False, - ) - - idx_cont += 1 - else: - raise ValueError(f"Unsupported type of Hyperparameter: {type(param)}") - hp_list.append(hp_new) - - # We only consider plain hyperparameters - self.cs_local.add_hyperparameters(hp_list) - forbiddens_ss = [] - forbiddens = config_space.get_forbiddens() - for forbidden in forbiddens: - forbiden_ss = self.fit_forbidden_to_ss(cs_local=self.cs_local, forbidden=forbidden) - if forbiden_ss is not None: - forbiddens_ss.append(forbiden_ss) - if len(forbiddens_ss) > 0: - self.cs_local.add_forbidden_clauses(forbiddens_ss) - - model_kwargs = dict( - configspace=self.cs_local, - types=[model_types[activate_dim] for activate_dim in activate_dims] if model_types is not None else None, - bounds=[model_bounds[activate_dim] for activate_dim in activate_dims] if model_bounds is not None else None, - bounds_cont=np.array([[0, 1.0] for _ in range(len(activate_dims_cont))]), - bounds_cat=self.bounds_ss_cat, - seed=self.rng.randint(0, 2**20), - ) - - if inspect.isclass(model_local): - model_local_kwargs_copy = copy.deepcopy(model_local_kwargs) - if "kernel_kwargs" in model_local_kwargs_copy: - kernel_kwargs = model_local_kwargs_copy["kernel_kwargs"] - kernel = construct_gp_kernel(kernel_kwargs, activate_dims_cont_ss, activate_dims_cat_ss) - del model_local_kwargs_copy["kernel_kwargs"] - model_local_kwargs_copy["kernel"] = kernel - - if model_local_kwargs is not None: - model_kwargs.update(model_local_kwargs_copy) - - all_arguments = inspect.signature(model_local).parameters.keys() - if "bounds_cont" not in all_arguments: - del model_kwargs["bounds_cont"] - if "bounds_cat" not in all_arguments: - del model_kwargs["bounds_cat"] - model = model_local(**model_kwargs) # type: ignore - else: - model = model_local - - self.model = model - - if inspect.isclass(acq_func_local): - acq_func_kwargs = {"model": self.model} - if acq_func_local_kwargs is not None: - acq_func_kwargs.update(acq_func_local_kwargs) - acquisition_function = acq_func_local(**acq_func_kwargs) # type: ignore - else: - acquisition_function = acq_func_local - - self.acquisition_function = acquisition_function - - self.incumbent_array = incumbent_array - - self.model_x = np.empty([0, len(activate_dims)]) - self.ss_x = np.empty([0, len(activate_dims)]) - self.model_y = np.empty([0, 1]) - self.ss_y = np.empty([0, 1]) - - if initial_data is not None: - X = initial_data[0] - y = initial_data[1] - - self.add_new_observations(X, y) - - self.config_origin = "subspace" - - @staticmethod - def fit_forbidden_to_ss( - cs_local: ConfigurationSpace, forbidden: AbstractForbiddenComponent - ) -> Optional[AbstractForbiddenComponent]: - """ - Fit the forbidden to subspaces. If the target forbidden can be added to subspace, we return a new forbidden - with exactly the same type of the input forbidden. Otherwise, None is returned. - - Parameters - ---------- - cs_local: ConfigurationSpace - local configuration space of the subspace - forbidden: AbstractForbiddenComponent - forbidden to check - Returns - ------- - forbidden_ss: Optional[AbstractForbiddenComponent] - forbidden in subspaces - - """ - if isinstance(forbidden, ForbiddenAndConjunction): - forbidden_ss_components = [] - for forbid in forbidden.components: - # If any of the AndConjunction is not supported by the subspace, we simply ignore them - forbid_ss = LocalSubspace.fit_forbidden_to_ss(cs_local, forbid) - if forbid_ss is None: - return None - forbidden_ss_components.append(forbid_ss) - return type(forbidden)(*forbidden_ss_components) - else: - forbidden_hp_name = forbidden.hyperparameter.name - if forbidden_hp_name not in cs_local: - return None - hp_ss = cs_local.get_hyperparameter(forbidden_hp_name) - - def is_value_in_hp(value: Any, hp: Hyperparameter) -> bool: - """Check if the value is in the range of the hp.""" - if isinstance(hp, NumericalHyperparameter): - return hp.lower <= value <= hp.upper - elif isinstance(hp, OrdinalHyperparameter): - return value in hp.sequence - elif isinstance(hp, CategoricalHyperparameter): - return value in hp.choices - else: - raise NotImplementedError("Unsupported type of hyperparameter!") - - if isinstance(forbidden, MultipleValueForbiddenClause): - forbidden_values = forbidden.values - for forbidden_value in forbidden_values: - if not is_value_in_hp(forbidden_value, hp_ss): - return None - return type(forbidden)(hp_ss, forbidden_values) - else: - forbidden_value = forbidden.value - if is_value_in_hp(forbidden_value, hp_ss): - return type(forbidden)(hp_ss, forbidden_value) - return None - - def update_model(self, predict_x_best: bool = True, update_incumbent_array: bool = False) -> None: - """ - Update the model and acquisition function parameters - - Parameters - ---------- - predict_x_best: bool, - if the incumbent is acquired by the predicted mean of a surrogate model - update_incumbent_array: bool - if the incumbent_array of this subspace is replaced with the newly updated incumbent - """ - acq_func_kwargs = {"model": self.model, "num_data": len(self.ss_x)} - - if predict_x_best: - try: - mu, _ = self.model.predict(self.ss_x) - except Exception as e: - # Some times it could occur that LGPGA fails to predict the mean value of ss_x because of - # numerical issues - self.logger.warning(f"Fail to predict ss_x due to {e}") - mu = self.ss_y - idx_eta = np.argmin(mu) - incumbent_array = self.ss_x[idx_eta] - acq_func_kwargs.update({"incumbent_array": incumbent_array, "eta": mu[idx_eta]}) - else: - idx_eta = np.argmin(self.ss_y) - incumbent_array = self.ss_x[idx_eta] - acq_func_kwargs.update({"incumbent_array": incumbent_array, "eta": self.ss_y[idx_eta]}) - if update_incumbent_array: - if self.incumbent_array is None: - self.incumbent_array = self.ss_x[idx_eta] - else: - self.incumbent_array[self.activate_dims] = self.ss_x[idx_eta] - - self.acquisition_function.update(**acq_func_kwargs) - - def add_new_observations(self, X: np.ndarray, y: np.ndarray) -> None: - """ - Add new observations to the subspace - - Parameters - ---------- - X: np.ndarray(N,D), - new feature vector of the observations, constructed by the global configuration space - y: np.ndarray(N) - new performances of the observations - Return - ---------- - indices_in_ss:np.ndarray(N) - indices of data that included in subspaces - """ - if len(X.shape) == 1: - X = X[np.newaxis, :] - if len(y.shape) == 1: - y = y[:, np.newaxis] - - X = X[:, self.activate_dims] - - ss_indices = check_subspace_points( - X=X, - cont_dims=self.activate_dims_cont, - cat_dims=self.activate_dims_cat, - bounds_cont=self.bounds_ss_cont, - bounds_cat=self.bounds_ss_cat, - ) - - X = self.normalize_input(X=X) - - self.model_x = np.vstack([self.model_x, X]) - self.model_y = np.vstack([self.model_y, y]) - - self.ss_x = np.vstack([self.ss_x, X[ss_indices]]) - self.ss_y = np.vstack([self.ss_y, y[ss_indices]]) - - def update_incumbent_array(self, new_incumbent: np.ndarray) -> None: - """ - Update a new incumbent array. The array is generated from the global configuration - - Parameters - ---------- - new_incumbent: np.ndarray(D) - new incumbent, which correspondences to the global configuration - """ - self.incumbent_array = self.normalize_input(X=new_incumbent) - - def generate_challengers(self, **optimizer_kwargs: Any) -> Iterator: - """ - Generate a list of challengers that will be transformed into the global configuration space - - Parameters - ---------- - optimizer_kwargs: Any - additional configurations passed to 'self._generate_challengers' - - Returns - ------- - A list of challengers in the global configuration space - - """ - challengers = self._generate_challengers(**optimizer_kwargs) - return ChallengerListLocal( - cs_local=self.cs_local, - cs_global=self.cs_global, - challengers=challengers, - config_origin=self.config_origin, - incumbent_array=self.incumbent_array, - ) - - @abstractmethod - def _generate_challengers(self, **optimizer_kwargs: Dict) -> List[Tuple[float, Configuration]]: - """Generate new challengers list for this subspace""" - raise NotImplementedError - - def normalize_input(self, X: np.ndarray) -> np.ndarray: - """ - Normalize X to fit the local configuration space - - Parameters - ---------- - X: np.ndarray(N,D) - input X, configurations arrays - Returns - ------- - X_normalized: np.ndarray(N,D) - normalized input X - """ - if not self.new_config: - return X - - if len(X.shape) == 1: - X = X[np.newaxis, :] - - # normalize X - X_normalized = (X - self.lbs) * self.scales - if self.bounds_ss_cat is not None: - # normalize categorical function, for instance, if bounds_subspace[i] is a categorical bound contains - # elements [1, 3, 5], then we map 1->0, 3->1, 5->2 - for cat_idx, cat_bound in zip(self.activate_dims_cat, self.bounds_ss_cat): - X_i = X_normalized[:, cat_idx] - cond_list = [X_i == cat for cat in cat_bound] - choice_list = np.arange(len(cat_bound)) - X_i = np.select(cond_list, choice_list) - X_normalized[:, cat_idx] = X_i - - return X_normalized - - -class ChallengerListLocal(Iterator): - def __init__( - self, - cs_local: ConfigurationSpace, - cs_global: ConfigurationSpace, - challengers: List[Tuple[float, Configuration]], - config_origin: str, - incumbent_array: Optional[np.ndarray] = None, - ): - """ - A Challenger list to convert the configuration from the local configuration space to the global configuration - space - - Parameters - ---------- - cs_local: ConfigurationSpace - local configuration space - cs_global: ConfigurationSpace - global configuration space - challengers: List[Tuple[float, Configuration]], - challenger lists - config_origin: str - configuration origin - incumbent_array: Optional[np.ndarray] = None, - global incumbent array, used when cs_local and cs_global have different number of dimensions and we need to - supplement the missing values. - """ - self.cs_local = cs_local - self.challengers = challengers - self.cs_global = cs_global - self._index = 0 - self.config_origin = config_origin - # In case cs_in and cs_out have different dimensions - self.expand_dims = len(cs_global.get_hyperparameters()) != len(cs_local.get_hyperparameters()) - self.incumbent_array = incumbent_array - - if self.expand_dims and self.incumbent_array is None: - raise ValueError( - "Incumbent array must be provided if the global configuration space has more " - "hyperparameters then the local configuration space" - ) - - def __next__(self) -> Configuration: - if self.challengers is not None and self._index == len(self.challengers): - raise StopIteration - challenger = self.challengers[self._index][1] - self._index += 1 - value = challenger.get_dictionary() - if self.expand_dims: - incumbent_array = Configuration( - configuration_space=self.cs_global, vector=self.incumbent_array - ).get_dictionary() - # we replace the cooresponding value in incumbent array with the value suggested by our optimizer - for k in value.keys(): - incumbent_array[k] = value[k] - config = Configuration(configuration_space=self.cs_global, values=incumbent_array) - else: - config = Configuration(configuration_space=self.cs_global, values=value) - if self.config_origin is not None: - config.origin = self.config_origin - else: - config.origin = challenger.origin - return config - - def __len__(self) -> int: - if self.challengers is None: - self.challengers = [] - return len(self.challengers) - self._index diff --git a/smac/optimizer/subspaces/boing_subspace.py b/smac/optimizer/subspaces/boing_subspace.py deleted file mode 100644 index b9c55fd12..000000000 --- a/smac/optimizer/subspaces/boing_subspace.py +++ /dev/null @@ -1,175 +0,0 @@ -from typing import Dict, List, Optional, Tuple, Type, Union - -import inspect - -import numpy as np -from ConfigSpace import ConfigurationSpace - -from smac.configspace import Configuration -from smac.epm.base_epm import BaseEPM -from smac.epm.gaussian_process.augmented import GloballyAugmentedLocalGaussianProcess -from smac.optimizer.acquisition import EI, AbstractAcquisitionFunction -from smac.optimizer.acquisition.maximizer import ( - AcquisitionFunctionMaximizer, - LocalAndSortedRandomSearch, -) -from smac.optimizer.subspaces import LocalSubspace - - -class BOinGSubspace(LocalSubspace): - """ - Subspace for BOinG optimizer. Each time we create a new epm model for the subspace and optimize to maximize the - acquisition function inside this subregion. - - Parameters - ---------- - acq_optimizer_local: Optional[AcquisitionFunctionMaximizer] - Subspace optimizer, used to give a set of suggested points. Unlike the optimizer implemented in epm_chooser, - this optimizer does not require runhistory objects. - acq_optimizer_local_kwargs - Parameters for acq_optimizer_local - """ - - def __init__( - self, - config_space: ConfigurationSpace, - bounds: List[Tuple[float, float]], - hps_types: List[int], - bounds_ss_cont: Optional[np.ndarray] = None, - bounds_ss_cat: Optional[List[Tuple]] = None, - model_local: Union[BaseEPM, Type[BaseEPM]] = GloballyAugmentedLocalGaussianProcess, - model_local_kwargs: Dict = {}, - acq_func_local: Union[AbstractAcquisitionFunction, Type[AbstractAcquisitionFunction]] = EI, - acq_func_local_kwargs: Optional[Dict] = None, - rng: Optional[np.random.RandomState] = None, - initial_data: Optional[Tuple[np.ndarray, np.ndarray]] = None, - activate_dims: Optional[np.ndarray] = None, - incumbent_array: Optional[np.ndarray] = None, - acq_optimizer_local: Optional[AcquisitionFunctionMaximizer] = None, - acq_optimizer_local_kwargs: Optional[dict] = None, - ): - super(BOinGSubspace, self).__init__( - config_space=config_space, - bounds=bounds, - hps_types=hps_types, - bounds_ss_cont=bounds_ss_cont, - bounds_ss_cat=bounds_ss_cat, - model_local=model_local, - model_local_kwargs=model_local_kwargs, - acq_func_local=acq_func_local, - acq_func_local_kwargs=acq_func_local_kwargs, - rng=rng, - initial_data=initial_data, - activate_dims=activate_dims, - incumbent_array=incumbent_array, - ) - if bounds_ss_cont is None and bounds_ss_cat is None: - self.config_origin = None # type: ignore - else: - self.config_origin = "BOinG" - if isinstance(self.model, GloballyAugmentedLocalGaussianProcess): - num_inducing_points = min(max(min(2 * len(self.activate_dims_cont), 10), self.model_x.shape[0] // 20), 50) - self.model.update_attribute(num_inducing_points=num_inducing_points) - - subspace_acq_func_opt_kwargs = { - "acquisition_function": self.acquisition_function, - "config_space": self.cs_local, # type: ignore[attr-defined] # noqa F821 - "rng": np.random.RandomState(self.rng.randint(1, 2**20)), - } - - if isinstance(acq_optimizer_local, AcquisitionFunctionMaximizer): - # we copy the attribute of the local acquisition function optimizer but replace it with our local model - # setting. This helps a better exploration in the beginning. - for key in inspect.signature(acq_optimizer_local.__init__).parameters.keys(): # type: ignore[misc] - if key == "self": - continue - elif key in subspace_acq_func_opt_kwargs: - continue - elif hasattr(acq_func_local, key): - subspace_acq_func_opt_kwargs[key] = getattr(acq_func_local, key) - self.acq_optimizer_local = type(acq_optimizer_local)(**subspace_acq_func_opt_kwargs) - else: - if acq_optimizer_local is None: - acq_optimizer_local = LocalAndSortedRandomSearch # type: ignore - if acq_optimizer_local_kwargs is not None: - subspace_acq_func_opt_kwargs.update(acq_optimizer_local_kwargs) - else: - # Here are the setting used by squirrel-optimizer - # https://github.com/automl/Squirrel-Optimizer-BBO-NeurIPS20-automlorg/blob/main/squirrel-optimizer/smac_optim.py - n_sls_iterations = { - 1: 10, - 2: 10, - 3: 10, - 4: 10, - 5: 10, - 6: 10, - 7: 8, - 8: 6, - }.get(len(self.cs_local.get_hyperparameters()), 5) - - subspace_acq_func_opt_kwargs.update( - {"n_steps_plateau_walk": 5, "n_sls_iterations": n_sls_iterations} - ) - - elif inspect.isclass(acq_optimizer_local, AcquisitionFunctionMaximizer): - subspace_acq_func_opt_kwargs.update(acq_optimizer_local_kwargs) - else: - raise TypeError( - f"subspace_optimizer must be None or an object implementing the " - f"AcquisitionFunctionMaximizer, but is '{acq_optimizer_local}'" - ) - - self.acq_optimizer_local = acq_optimizer_local(**subspace_acq_func_opt_kwargs) # type: ignore - - def _generate_challengers(self, **optimizer_kwargs: Dict) -> List[Tuple[float, Configuration]]: - """ - Generate new challengers list for this subspace. This optimizer is similar to - smac.optimizer.ei_optimization.LocalAndSortedRandomSearch except that we don't read the past evaluated - information from the runhistory but directly assign new values to the - """ - self.model.train(self.model_x, self.model_y) - self.update_model(predict_x_best=True, update_incumbent_array=True) - num_points_rs = 1000 - - if isinstance(self.acq_optimizer_local, LocalAndSortedRandomSearch): - next_configs_random = self.acq_optimizer_local.random_search._maximize( - runhistory=None, # type: ignore - stats=None, # type: ignore - num_points=num_points_rs, - _sorted=True, - ) - if len(self.ss_x) == 0: - init_points_local = self.cs_local.sample_configuration(size=self.acq_optimizer_local.n_sls_iterations) - else: - previous_configs = [Configuration(configuration_space=self.cs_local, vector=ss_x) for ss_x in self.ss_x] - init_points_local = self.acq_optimizer_local.local_search._get_init_points_from_previous_configs( - self.acq_optimizer_local.n_sls_iterations, previous_configs, next_configs_random - ) - - configs_acq_local = self.acq_optimizer_local.local_search._do_search(init_points_local) - - # shuffle for random tie-break - self.rng.shuffle(configs_acq_local) - - # sort according to acq value - configs_acq_local.sort(reverse=True, key=lambda x: x[0]) - - for _, inc in configs_acq_local: - inc.origin = "Local Search" - - # Having the configurations from random search, sorted by their - # acquisition function value is important for the first few iterations - # of SMAC. As long as the random forest predicts constant value, we - # want to use only random configurations. Having them at the begging of - # the list ensures this (even after adding the configurations by local - # search, and then sorting them) - next_configs_by_acq_value = next_configs_random + configs_acq_local - - next_configs_by_acq_value.sort(reverse=True, key=lambda x: x[0]) - self.logger.debug( - "First 5 acq func (origin) values of selected configurations: %s", - str([[_[0], _[1].origin] for _ in next_configs_by_acq_value[:5]]), - ) - return next_configs_by_acq_value - else: - return self.acq_optimizer_local._maximize(None, None, num_points_rs) # type: ignore diff --git a/smac/optimizer/subspaces/turbo_subspace.py b/smac/optimizer/subspaces/turbo_subspace.py deleted file mode 100644 index ddacebf46..000000000 --- a/smac/optimizer/subspaces/turbo_subspace.py +++ /dev/null @@ -1,319 +0,0 @@ -from typing import Dict, List, Optional, Tuple, Type, Union - -import math -import warnings - -import numpy as np -from ConfigSpace.hyperparameters import NumericalHyperparameter -from ConfigSpace.util import deactivate_inactive_hyperparameters -from scipy.stats.qmc import LatinHypercube, Sobol - -from smac.configspace import Configuration, ConfigurationSpace -from smac.epm.base_epm import BaseEPM -from smac.epm.gaussian_process import GaussianProcess -from smac.epm.gaussian_process.augmented import GloballyAugmentedLocalGaussianProcess -from smac.epm.gaussian_process.gpytorch import GPyTorchGaussianProcess -from smac.epm.gaussian_process.mcmc import MCMCGaussianProcess -from smac.optimizer.acquisition import TS, AbstractAcquisitionFunction -from smac.optimizer.subspaces import LocalSubspace - -warnings.filterwarnings("ignore", message="The balance properties of Sobol' points require" " n to be a power of 2.") - - -class TuRBOSubSpace(LocalSubspace): - """ - Subspace designed for TurBO: - D. Eriksson et al. Scalable Global Optimization via Local Bayesian Optimization - https://proceedings.neurips.cc/paper/2019/hash/6c990b7aca7bc7058f5e98ea909e924b-Abstract.html - - The hyperparameters follow the illustration under supplementary D, `TuRBO details`. - - Parameters - ---------- - length_init: float - initialized length of subspace - length_min: float - the minimal length of subspace, if the subspace has a length smaller than this value, turbo will restart - length_max: float - the maximal length of subspace - success_tol: float - the number of successive successful evaluations required for expanding the subregion - failure_tol_min: float - the minimal number of successive successful evaluations required for shrinking the subregion (otherwise - this value is set as number of feature dimensions) - n_init_x_params: int - how many configurations will be used at most in the initial design (X*D). Used for restarting the subspace - n_candidate_max: int - The maximal Number of points used as candidates - """ - - def __init__( - self, - config_space: ConfigurationSpace, - bounds: List[Tuple[float, float]], - hps_types: List[int], - bounds_ss_cont: Optional[np.ndarray] = None, - bounds_ss_cat: Optional[List[Tuple]] = None, - model_local: Union[BaseEPM, Type[BaseEPM]] = GPyTorchGaussianProcess, - model_local_kwargs: Dict = {}, - acq_func_local: Union[AbstractAcquisitionFunction, Type[AbstractAcquisitionFunction]] = TS, - acq_func_local_kwargs: Optional[Dict] = None, - rng: Optional[np.random.RandomState] = None, - initial_data: Optional[Tuple[np.ndarray, np.ndarray]] = None, - activate_dims: Optional[np.ndarray] = None, - incumbent_array: Optional[np.ndarray] = None, - length_init: float = 0.8, - length_min: float = 0.5**7, - length_max: float = 1.6, - success_tol: int = 3, - failure_tol_min: int = 4, - n_init_x_params: int = 2, - n_candidate_max: int = 5000, - ): - self.num_valid_observations = 0 - super(TuRBOSubSpace, self).__init__( - config_space=config_space, - bounds=bounds, - hps_types=hps_types, - bounds_ss_cont=bounds_ss_cont, - bounds_ss_cat=bounds_ss_cat, - model_local=model_local, - model_local_kwargs=model_local_kwargs, - acq_func_local=acq_func_local, - acq_func_local_kwargs=acq_func_local_kwargs, - rng=rng, - initial_data=initial_data, - activate_dims=activate_dims, - incumbent_array=incumbent_array, - ) - hps = config_space.get_hyperparameters() - for hp in hps: - if not isinstance(hp, NumericalHyperparameter): - raise ValueError("Current TurBO Optimizer only supports Numerical Hyperparameters") - if len(config_space.get_conditions()) > 0 or len(config_space.get_forbiddens()) > 0: - raise ValueError("Currently TurBO does not support Conditional or Forbidden Hyperparameters") - - n_hps = len(self.activate_dims) - self.n_dims = n_hps - self.n_init = n_init_x_params * self.n_dims - self.n_candidates = min(100 * n_hps, n_candidate_max) - - self.failure_tol = max(failure_tol_min, n_hps) - self.success_tol = success_tol - self.length = length_init - self.length_init = length_init - self.length_min = length_min - self.length_max = length_max - self._restart_turbo(n_init_points=self.n_init) - - if initial_data is not None: - self.add_new_observations(initial_data[0], initial_data[1]) - self.init_configs = [] # type: List[Configuration] - - self.lb = np.zeros(self.n_dims) - self.ub = np.ones(self.n_dims) - self.config_origin = "TuRBO" - - def _restart_turbo( - self, - n_init_points: int, - ) -> None: - """ - Restart TurBO with a certain number of initialized points. New points are initialized with latin hypercube - - Parameters - ---------- - n_init_points: int - number of points required for initializing a new subspace - """ - self.logger.debug("Current length is smaller than the minimal value, a new TuRBO restarts") - self.success_count = 0 - self.failure_count = 0 - - self.num_eval_this_round = 0 - self.last_incumbent_value = np.inf - self.length = self.length_init - - self.num_valid_observations = 0 - - init_vectors = LatinHypercube(d=self.n_dims, seed=np.random.seed(self.rng.randint(1, 2**20))).random( - n=n_init_points - ) - - self.init_configs = [Configuration(self.cs_local, vector=init_vector) for init_vector in init_vectors] - - def adjust_length(self, new_observation: Union[float, np.ndarray]) -> None: - """ - Adjust the subspace length according to the performance of the latest suggested values - Parameters - ---------- - new_observation: Union[float, np.ndarray] - new observations - """ - # see Section 2: 'Trust regions' - optim_observation = new_observation if np.isscalar(new_observation) else np.min(new_observation) - - # We define a ``success'' as a candidate that improves upon $\xbest$, and a ``failure'' as a candidate that - # does not. - if optim_observation < np.min(self.model_y) - 1e-3 * math.fabs(np.min(self.model_y)): - self.logger.debug("New suggested value is better than the incumbent, success_count increases") - self.success_count += 1 - self.failure_count = 0 - else: - self.logger.debug("New suggested value is worse than the incumbent, failure_count increases") - self.success_count = 0 - self.failure_count += 1 - - # After $\tau_{\text{succ}}$ consecutive successes, we double the size of the TR, - # i.e., $\len \gets \min\{\len_{\textrm{max}}, 2\len\}$. - if self.success_count == self.success_tol: # Expand trust region - self.length = min([2.0 * self.length, self.length_max]) - self.success_count = 0 - self.logger.debug(f"Subspace length expands to {self.length}") - # After $\tau_{\text{fail}}$ consecutive failures, we halve the size of the TR: $\len \gets \len/2$. - # We reset the success and failure counters to zero after we change the size of the TR. - elif self.failure_count == self.failure_tol: # Shrink trust region - self.length /= 2.0 - self.failure_count = 0 - self.logger.debug(f"Subspace length shrinks to {self.length}") - - def _generate_challengers( # type: ignore[override] - self, _sorted: bool = True - ) -> List[Tuple[float, Configuration]]: - """ - Generate new challengers list for this subspace - - Parameters - ---------- - _sorted: bool - if the generated challengers are sorted by their acquisition function values - """ - if len(self.init_configs) > 0: - config_next = self.init_configs.pop() - return [(0, config_next)] - - if self.length < self.length_min: - self._restart_turbo(n_init_points=self.n_init) - config_next = self.init_configs.pop() - return [(0, config_next)] - - self.model.train(self.model_x[-self.num_valid_observations :], self.model_y[-self.num_valid_observations :]) - self.update_model(predict_x_best=False, update_incumbent_array=True) - - sobol_gen = Sobol(d=self.n_dims, scramble=True, seed=self.rng.randint(low=0, high=10000000)) - sobol_seq = sobol_gen.random(self.n_candidates) - - # adjust length according to kernel length - if isinstance( - self.model, - (GaussianProcess, MCMCGaussianProcess, GloballyAugmentedLocalGaussianProcess, GPyTorchGaussianProcess), - ): - if isinstance(self.model, GaussianProcess): - kernel_length = np.exp(self.model.hypers[1:-1]) - elif isinstance(self.model, MCMCGaussianProcess): - kernel_length = np.exp(np.mean((np.array(self.model.hypers)[:, 1:-1]), axis=0)) - elif isinstance(self.model, (GPyTorchGaussianProcess, GloballyAugmentedLocalGaussianProcess)): - kernel_length = self.model.kernel.base_kernel.lengthscale.cpu().detach().numpy() - - # See section 'Trust regions' of section 2 - # $\len_i = \lambda_i L / (\prod_{j=1}^d \lambda_j)^{1/d}$, - # We now have weights.prod() = 1 - # This makes the result more stable - subspace_scale = kernel_length / np.prod(np.power(kernel_length, 1.0 / self.n_dims)) - - subspace_length = self.length * subspace_scale - - subspace_lb = np.clip(self.incumbent_array - subspace_length * 0.5, 0.0, 1.0) - subspace_ub = np.clip(self.incumbent_array + subspace_length * 0.5, 0.0, 1.0) - sobol_seq = sobol_seq * (subspace_ub - subspace_lb) + subspace_lb - - prob_perturb = min(20.0 / self.n_dims, 1.0) - design = self._perturb_samples(prob_perturb, sobol_seq) - - # Only numerical hyperpameters are considered for TuRBO, we don't need to transfer the vectors to fit the - # requirements of other sorts of hyperparameters - configs = [] - for vector in design: - conf = deactivate_inactive_hyperparameters( - configuration=None, configuration_space=self.cs_local, vector=vector - ) - configs.append(conf) - - if _sorted: - return self._sort_configs_by_acq_value(configs) - else: - return [(0, configs[i]) for i in range(len(configs))] - - def _perturb_samples(self, prob_perturb: float, design: np.ndarray) -> np.ndarray: - """ - See Supplementary D, 'TuRBO details': - In order to not perturb all coordinates at once, we use the value in the Sobol sequence - with probability min{1,20/d} for a given candidate and dimension, and the value of the center otherwise - - perturb the generated design with the incumbent array accordingly - - Parameters - ---------- - prob_perturb: float - probability that a design is perturbed by the incumbent value - design: np.ndarray(self.n_candidates, self.n_dims) - design array to be perturbed - Returns - ------- - design_perturbed: np.ndarray(self.n_candidates, self.n_dims) - perturbed design array - """ - # we will use masked array, thus the indices that will be replaced will be marked with True - mask = self.rng.rand(self.n_candidates, self.n_dims) > prob_perturb - - ind = np.where(np.sum(mask, axis=1) == self.n_dims)[0] - if self.n_dims == 1: - mask[ind, 0] = 0 - else: - # ensure that no candidate will be completely replaced by the incumbent value - mask[ind, self.rng.randint(0, self.n_dims, size=len(ind))] = 0 - return np.ma.array(design, mask=mask, fill_value=self.incumbent_array).filled() - - def add_new_observations(self, X: np.ndarray, y: np.ndarray) -> None: - """ - Add new observations to the subspace, meanwhile, we add the number of valid observation to ensure that the - subspace could be scaled properly. - - Parameters - ---------- - X: np.ndarray(N,D), - new feature vector of the observations, constructed by the global configuration space - y: np.ndarray(N) - new performances of the observations - Return - ---------- - indices_in_ss:np.ndarray(N) - indices of data that included in subspaces - """ - super(TuRBOSubSpace, self).add_new_observations(X, y) - self.num_valid_observations += len(y) - - def _sort_configs_by_acq_value(self, configs: List[Configuration]) -> List[Tuple[float, Configuration]]: - """Sort the given configurations by acquisition value - comes from smac.optimizer.ei_optimization.AcquisitionFunctionMaximizer - - Parameters - ---------- - configs : list(Configuration) - - Returns - ------- - list: (acquisition value, Candidate solutions), - ordered by their acquisition function value - """ - acq_values = self.acquisition_function(configs) - - # From here - # http://stackoverflow.com/questions/20197990/how-to-make-argsort-result-to-be-random-between-equal-values - random = self.rng.rand(len(acq_values)) - # Last column is primary sort key! - indices = np.lexsort((random.flatten(), acq_values.flatten())) - - # Cannot use zip here because the indices array cannot index the - # rand_configs list, because the second is a pure python list - return [(acq_values[ind][0], configs[ind]) for ind in indices[::-1]] diff --git a/smac/random_design/__init__.py b/smac/random_design/__init__.py new file mode 100644 index 000000000..2c8c6b2bb --- /dev/null +++ b/smac/random_design/__init__.py @@ -0,0 +1,19 @@ +from smac.random_design.abstract_random_design import AbstractRandomDesign +from smac.random_design.annealing_design import CosineAnnealingRandomDesign +from smac.random_design.modulus_design import ( + DynamicModulusRandomDesign, + ModulusRandomDesign, +) +from smac.random_design.probability_design import ( + DynamicProbabilityRandomDesign, + ProbabilityRandomDesign, +) + +__all__ = [ + "AbstractRandomDesign", + "CosineAnnealingRandomDesign", + "ModulusRandomDesign", + "DynamicModulusRandomDesign", + "ProbabilityRandomDesign", + "DynamicProbabilityRandomDesign", +] diff --git a/smac/random_design/abstract_random_design.py b/smac/random_design/abstract_random_design.py new file mode 100644 index 000000000..4b50bfef0 --- /dev/null +++ b/smac/random_design/abstract_random_design.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import Any + +import numpy as np + +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class AbstractRandomDesign: + """Abstract base of helper classes to configure interleaving of random configurations in a list + of challengers. + + Parameters + ---------- + seed : int + """ + + def __init__(self, seed: int = 0): + self._seed = seed + self._rng = np.random.RandomState(seed=seed) + + @property + def meta(self) -> dict[str, Any]: + """Returns the meta data of the created object.""" + return { + "name": self.__class__.__name__, + "seed": self._seed, + } + + def next_iteration(self) -> None: + """Indicates the beginning of the next SMBO iteration.""" + pass + + @abstractmethod + def check(self, iteration: int) -> bool: + """Check if the next configuration should be random. + + Parameters + ---------- + iteration : int + Number of the i-th configuration evaluated in a SMBO iteration. + + Returns + ------- + bool + Whether the next configuration should be random. + """ + pass diff --git a/smac/random_design/annealing_design.py b/smac/random_design/annealing_design.py new file mode 100644 index 000000000..87a48480f --- /dev/null +++ b/smac/random_design/annealing_design.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np + +from smac.random_design.abstract_random_design import AbstractRandomDesign +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class CosineAnnealingRandomDesign(AbstractRandomDesign): + """Interleaves a random configuration according to a given probability which is decreased + according to a cosine annealing schedule. + + Parameters + ---------- + max_probability : float + Initial (maximum) probability of a random configuration. + min_probability : float + Final (minimal) probability of a random configuration used in iteration `restart_iteration`. + restart_iteration : int + Restart the annealing schedule every `restart_iteration` iterations. + seed : int + """ + + def __init__( + self, + min_probability: float, + max_probability: float, + restart_iteration: int, + seed: int = 0, + ): + super().__init__(seed) + assert 0 <= min_probability <= 1 + assert 0 <= max_probability <= 1 + assert max_probability > min_probability + assert restart_iteration > 2 + self._max_probability = max_probability + self._min_probability = min_probability + + # Internally, iteration indices start at 0, so we need to decrease this + self._restart_iteration = restart_iteration - 1 + self._iteration = 0 + self._probability = max_probability + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update( + { + "max_probability": self._max_probability, + "min_probability": self._min_probability, + "restart_iteration": self._restart_iteration, + } + ) + + return meta + + def next_iteration(self) -> None: # noqa: D102 + """Moves to the next iteration and set ``self._probability``.""" + self._iteration += 1 + if self._iteration > self._restart_iteration: + self._iteration = 0 + logger.debug("Perform a restart.") + + self._probability = self._min_probability + ( + 0.5 + * (self._max_probability - self._min_probability) + * (1 + np.cos(self._iteration * np.pi / self._restart_iteration)) + ) + logger.debug(f"Probability for random configs: {self._probability}") + + def check(self, iteration: int) -> bool: # noqa: D102 + assert iteration >= 0 + + if self._rng.rand() <= self._probability: + return True + else: + return False diff --git a/smac/random_design/modulus_design.py b/smac/random_design/modulus_design.py new file mode 100644 index 000000000..206571fc6 --- /dev/null +++ b/smac/random_design/modulus_design.py @@ -0,0 +1,110 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np + +from smac.random_design.abstract_random_design import AbstractRandomDesign +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class ModulusRandomDesign(AbstractRandomDesign): + """Interleave a random configuration after a constant number of configurations found by + Bayesian optimization. + + Parameters + ---------- + modulus : float + Every modulus-th configuration will be at random. + seed : int + Integer used to initialize random state. This class does not use the seed. + """ + + def __init__(self, modulus: float = 2.0, seed: int = 0): + super().__init__(seed) + assert modulus > 0 + if modulus <= 1.0: + logger.warning("Using SMAC with random configurations only. ROAR is the better choice for this.") + + self._modulus = modulus + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update({"modulus": self._modulus}) + + return meta + + def check(self, iteration: int) -> bool: # noqa: D102 + assert iteration >= 0 + return iteration % self._modulus < 1 + + +class DynamicModulusRandomDesign(AbstractRandomDesign): + """Interleave a random configuration, decreasing the fraction of random configurations over + time. + + Parameters + ---------- + start_modulus : float, defaults to 2.0 + Initially, every modulus-th configuration will be at random. + modulus_increment : float, defaults to 0.3 + Increase modulus by this amount in every iteration. + end_modulus : float, defaults to np.inf + The maximum modulus ever used. If the value is reached before the optimization + is over, it is not further increased. If it is not reached before the optimization is over, + there will be no adjustment to make sure that the `end_modulus` is reached. + seed : int, defaults to 0 + Integer used to initialize the random state (not used) + """ + + def __init__( + self, + start_modulus: float = 2.0, + modulus_increment: float = 0.3, + end_modulus: float = np.inf, + seed: int = 0, + ): + super().__init__(seed) + assert start_modulus > 0 + assert modulus_increment > 0 + assert end_modulus > 0 + assert end_modulus > start_modulus + + if start_modulus <= 1.0 and modulus_increment <= 0.0: + logger.warning("Using SMAC with random configurations only. ROAR is the better choice for this.") + + self._modulus = start_modulus + self._start_modulus = start_modulus + self._modulus_increment = modulus_increment + self._end_modulus = end_modulus + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update( + { + "start_modulus": self._start_modulus, + "end_modulus": self._end_modulus, + "modulus_increment": self._modulus_increment, + } + ) + + return meta + + def next_iteration(self) -> None: # noqa: D102 + self._modulus += self._modulus_increment + self._modulus = min(self._modulus, self._end_modulus) + + def check(self, iteration: int) -> bool: # noqa: D102 + assert iteration >= 0 + + if iteration % self._modulus < 1: + return True + else: + return False diff --git a/smac/random_design/probability_design.py b/smac/random_design/probability_design.py new file mode 100644 index 000000000..b2de48cae --- /dev/null +++ b/smac/random_design/probability_design.py @@ -0,0 +1,88 @@ +from __future__ import annotations + +from typing import Any + +from smac.random_design.abstract_random_design import AbstractRandomDesign +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class ProbabilityRandomDesign(AbstractRandomDesign): + """Interleave a random configuration according to a given probability. + + Parameters + ---------- + probability : float + Probability that a configuration will be drawn at random. + seed : int, defaults to 0 + """ + + def __init__(self, probability: float, seed: int = 0): + super().__init__(seed=seed) + assert 0 <= probability <= 1 + self._probability = probability + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update({"probability": self._probability}) + + return meta + + def check(self, iteration: int) -> bool: # noqa: D102 + assert iteration >= 0 + + if self._rng.rand() < self._probability: + return True + else: + return False + + +class DynamicProbabilityRandomDesign(AbstractRandomDesign): + """Interleave a random configuration according to a given probability which is decreased over + time. + + Parameters + ---------- + probability : float + Probability that a configuration will be drawn at random. + factor : float + Multiply the `probability` by `factor` in each iteration. + seed : int, defaults to 0 + """ + + def __init__( + self, + probability: float, + factor: float, + seed: int = 0, + ): + super().__init__(seed) + assert 0 <= probability <= 1 + assert factor > 0 + + self._probability = probability + self._factor = factor + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update({"probability": self._probability, "factor": self._factor}) + + return meta + + def next_iteration(self) -> None: + """Sets the probability to the current value multiplied by ``factor``.""" + self._probability *= self._factor + + def check(self, iteration: int) -> bool: # noqa: D102 + assert iteration >= 0 + + if self._rng.rand() <= self._probability: + return True + else: + return False diff --git a/smac/runhistory/__init__.py b/smac/runhistory/__init__.py index e69de29bb..086327319 100644 --- a/smac/runhistory/__init__.py +++ b/smac/runhistory/__init__.py @@ -0,0 +1,21 @@ +from smac.runhistory.dataclasses import ( + InstanceSeedBudgetKey, + InstanceSeedKey, + TrialInfo, + TrialKey, + TrialValue, +) +from smac.runhistory.enumerations import DataOrigin, StatusType, TrialInfoIntent +from smac.runhistory.runhistory import RunHistory + +__all__ = [ + "RunHistory", + "TrialKey", + "InstanceSeedBudgetKey", + "InstanceSeedKey", + "TrialValue", + "TrialInfo", + "StatusType", + "DataOrigin", + "TrialInfoIntent", +] diff --git a/smac/runhistory/dataclasses.py b/smac/runhistory/dataclasses.py new file mode 100644 index 000000000..90f943110 --- /dev/null +++ b/smac/runhistory/dataclasses.py @@ -0,0 +1,131 @@ +from __future__ import annotations + +from typing import Any + +from dataclasses import dataclass, field + +from ConfigSpace import Configuration + +from smac.runhistory.enumerations import StatusType + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +@dataclass(frozen=True) +class InstanceSeedKey: + """Key for instance and seed. + + Parameters + ---------- + instance : str | None, defaults to None + seed : int | None, defaults to None + """ + + instance: str | None = None + seed: int | None = None + + +@dataclass(frozen=True) +class InstanceSeedBudgetKey: + """Key for instance, seed and budget. + + Parameters + ---------- + instance : str | None, defaults to None + seed : int | None, defaults to None + budget : float | None, defaults to None + """ + + instance: str | None = None + seed: int | None = None + budget: float | None = None + + def __lt__(self, other: InstanceSeedBudgetKey) -> bool: + if self.budget is not None and other.budget is not None: + return self.budget < other.budget + + if self.instance is not None and other.instance is not None: + return self.instance < other.instance + + if self.seed is not None and other.seed is not None: + return self.seed < other.seed + + raise RuntimeError("Could not compare InstanceSeedBudgetKey.") + + +@dataclass(frozen=True) +class TrialKey: + """Key of a trial. + + Parameters + ---------- + config_id : int + instance : str | None, defaults to None + seed : int | None, defaults to None + budget : float | None, defaults to None + """ + + config_id: int + instance: str | None = None + seed: int | None = None + budget: float | None = None + + +@dataclass(frozen=True) +class TrialValue: + """Values of a trial. + + Parameters + ---------- + cost : float | list[float] + time : float, defaults to 0.0 + status : StatusType, defaults to StatusType.SUCCESS + starttime : float, defaults to 0.0 + endtime : float, defaults to 0.0 + additional_info : dict[str, Any], defaults to {} + """ + + cost: float | list[float] + time: float = 0.0 + status: StatusType = StatusType.SUCCESS + starttime: float = 0.0 + endtime: float = 0.0 + additional_info: dict[str, Any] = field(default_factory=dict) + + +@dataclass(frozen=True) +class TrialInfo: + """Information about a trial. + + Parameters + ---------- + config : Configuration + instance : str | None, defaults to None + seed : int | None, defaults to None + budget : float | None, defaults to None + source : int | None, defaults to 0 + Source is used in the intensifier to indicate from which worker the trial was coming from. + """ + + config: Configuration + instance: str | None = None + seed: int | None = None + budget: float | None = None + source: int = 0 + + +@dataclass +class TrajectoryItem: + """Item of a trajectory.""" + + incumbent: Configuration | dict[str, Any] + cost: float | list[float] + budget: float | None + walltime_used: float + num_trial: int + + def __post_init__(self) -> None: + # Transform configuration to dict + if isinstance(self.incumbent, Configuration): + self.incumbent = self.incumbent.get_dictionary() diff --git a/smac/runhistory/encoder/__init__.py b/smac/runhistory/encoder/__init__.py new file mode 100644 index 000000000..c1249c216 --- /dev/null +++ b/smac/runhistory/encoder/__init__.py @@ -0,0 +1,27 @@ +from smac.runhistory.encoder.abstract_encoder import AbstractRunHistoryEncoder +from smac.runhistory.encoder.boing_encoder import ( + RunHistoryRawEncoder, + RunHistoryRawScaledEncoder, +) +from smac.runhistory.encoder.eips_encoder import RunHistoryEIPSEncoder +from smac.runhistory.encoder.encoder import RunHistoryEncoder +from smac.runhistory.encoder.inverse_scaled_encoder import ( + RunHistoryInverseScaledEncoder, +) +from smac.runhistory.encoder.log_encoder import RunHistoryLogEncoder +from smac.runhistory.encoder.log_scaled_encoder import RunHistoryLogScaledEncoder +from smac.runhistory.encoder.scaled_encoder import RunHistoryScaledEncoder +from smac.runhistory.encoder.sqrt_scaled_encoder import RunHistorySqrtScaledEncoder + +__all__ = [ + "AbstractRunHistoryEncoder", + "RunHistoryEncoder", + "RunHistoryRawEncoder", + "RunHistoryRawScaledEncoder", + "RunHistoryEIPSEncoder", + "RunHistoryInverseScaledEncoder", + "RunHistoryLogEncoder", + "RunHistoryLogScaledEncoder", + "RunHistoryScaledEncoder", + "RunHistorySqrtScaledEncoder", +] diff --git a/smac/runhistory/encoder/abstract_encoder.py b/smac/runhistory/encoder/abstract_encoder.py new file mode 100644 index 000000000..cc89b7b6f --- /dev/null +++ b/smac/runhistory/encoder/abstract_encoder.py @@ -0,0 +1,282 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import Any, Mapping + +import numpy as np + +from smac.multi_objective import AbstractMultiObjectiveAlgorithm +from smac.runhistory.runhistory import RunHistory, TrialKey, TrialValue +from smac.runner.abstract_runner import StatusType +from smac.scenario import Scenario +from smac.utils.configspace import convert_configurations_to_array +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class AbstractRunHistoryEncoder: + """Abstract class for preparing data in order to train a surrogate model. + + Parameters + ---------- + scenario : Scenario + considered_states : list[StatusType], defaults to [StatusType.SUCCESS, StatusType.CRASHED, StatusType.MEMORYOUT, StatusType.DONOTADVANCE] # noqa: E501 + Trials with the passed states are considered. + lower_budget_states : list[StatusType], defaults to [] + Additionally consider all trials with these states for budget < current budget. + scale_percentage : int, defaults to 5 + Scaled y-transformation use a percentile to estimate distance to optimum. Only used in some sub-classes. + seed : int | None, defaults to none + + Raises + ------ + TypeError + If no success states are given. + """ + + def __init__( + self, + scenario: Scenario, + considered_states: list[StatusType] = [ + StatusType.SUCCESS, + StatusType.CRASHED, + StatusType.MEMORYOUT, + StatusType.DONOTADVANCE, + ], + lower_budget_states: list[StatusType] = [], + scale_percentage: int = 5, + seed: int | None = None, + ) -> None: + if considered_states is None: + raise TypeError("No success states are given.") + + if seed is None: + seed = scenario.seed + + self._seed = seed + self._rng = np.random.RandomState(seed) + self._scale_percentage = scale_percentage + self._n_objectives = scenario.count_objectives() + self._algorithm_walltime_limit = scenario.trial_walltime_limit + self._lower_budget_states = lower_budget_states + self._considered_states = considered_states + + self._instances = scenario.instances + self._instance_features = scenario.instance_features + self._n_features = scenario.count_instance_features() + self._n_params = len(scenario.configspace.get_hyperparameters()) + + if self._instances is not None and self._n_features == 0: + logger.warning( + "We strongly encourage to use instance features when using instances.", + "If no instance features are passed, the runhistory encoder can not distinguish between different " + "instances and therefore returns the same data points with different values, all of which are " + "used to train the surrogate model.\n" + "Consider using instance indices as features.", + ) + + # Learned statistics + self._min_y = np.array([np.NaN] * self._n_objectives) + self._max_y = np.array([np.NaN] * self._n_objectives) + self._percentile = np.array([np.NaN] * self._n_objectives) + self._multi_objective_algorithm: AbstractMultiObjectiveAlgorithm | None = None + + @property + def meta(self) -> dict[str, Any]: + """Returns the meta data of the created object.""" + return { + "name": self.__class__.__name__, + "considered_states": self._considered_states, + "lower_budget_states": self._lower_budget_states, + "scale_percentage": self._scale_percentage, + "seed": self._seed, + } + + @property + def multi_objective_algorithm(self) -> AbstractMultiObjectiveAlgorithm | None: + """The multi objecctive algorithm used to transform the data.""" + return self._multi_objective_algorithm + + @multi_objective_algorithm.setter + def multi_objective_algorithm(self, algorithm: AbstractMultiObjectiveAlgorithm) -> None: + """Sets the multi objective algorithm.""" + self._multi_objective_algorithm = algorithm + + @abstractmethod + def _build_matrix( + self, + trials: Mapping[TrialKey, TrialValue], + runhistory: RunHistory, + store_statistics: bool = False, + ) -> tuple[np.ndarray, np.ndarray]: + """Builds x and y matrixes from selected runs from the runhistory. + + Parameters + ---------- + trials : Mapping[TrialKey, TrialValue] + runhistory : RunHistory + store_statistics: bool, defaults to false + Whether to store statistics about the data (to be used at subsequent calls). + + Returns + ------- + X : np.ndarray + Y : np.ndarray + """ + raise NotImplementedError() + + def _get_considered_trials( + self, + runhistory: RunHistory, + budget_subset: list | None = None, + ) -> dict[TrialKey, TrialValue]: + trials: dict[TrialKey, TrialValue] = {} + + if budget_subset is not None: + if len(budget_subset) != 1: + raise ValueError("Can not yet handle getting runs from multiple budgets.") + + for trial_key, trial_value in runhistory.items(): + add = False + if budget_subset is not None: + if trial_key.budget in budget_subset and trial_value.status in self._considered_states: + add = True + + if ( + trial_key.budget is not None + and budget_subset[0] is not None + and trial_key.budget < budget_subset[0] + and trial_value.status in self._lower_budget_states + ): + add = True + else: + # Get only successfully finished runs + if trial_value.status in self._considered_states: + add = True + + if add: + trials[trial_key] = trial_value + + return trials + + def _get_timeout_trials( + self, + runhistory: RunHistory, + budget_subset: list | None = None, + ) -> dict[TrialKey, TrialValue]: + if budget_subset is not None: + trials = { + run: runhistory[run] + for run in runhistory + if runhistory[run].status == StatusType.TIMEOUT + # and runhistory.data[run].time >= self._algorithm_walltime_limit # type: ignore + and run.budget in budget_subset + } + else: + trials = { + run: runhistory[run] + for run in runhistory + if runhistory[run].status == StatusType.TIMEOUT + # and runhistory.data[run].time >= self._algorithm_walltime_limit # type: ignore + } + + return trials + + def get_configurations( + self, + runhistory: RunHistory, + budget_subset: list | None = None, + ) -> np.ndarray: + """Returns vector representation of the configurations. Instance features are not + appended and cost values are not taken into account. + + Parameters + ---------- + runhistory : RunHistory + budget_subset : list | None, defaults to none + List of budgets to consider. + + Returns + ------- + configs_array : np.ndarray + """ + s_trials = self._get_considered_trials(runhistory, budget_subset) + s_config_ids = set(s_trial.config_id for s_trial in s_trials) + t_trials = self._get_timeout_trials(runhistory, budget_subset) + t_config_ids = set(t_trial.config_id for t_trial in t_trials) + config_ids = s_config_ids | t_config_ids + configurations = [runhistory._ids_config[config_id] for config_id in config_ids] + configs_array = convert_configurations_to_array(configurations) + + return configs_array + + def transform( + self, + runhistory: RunHistory, + budget_subset: list | None = None, + ) -> tuple[np.ndarray, np.ndarray]: + """Returns a vector representation of the runhistory. + + Parameters + ---------- + runhistory : RunHistory + budget_subset : list | None, defauls to none + List of budgets to consider. + + Returns + ------- + X : np.ndarray + Configuration vector and instance features. + Y : np.ndarray + Cost values. + """ + logger.debug("Transforming runhistory into X, y format...") + + considered_trials = self._get_considered_trials(runhistory, budget_subset) + X, Y = self._build_matrix(trials=considered_trials, runhistory=runhistory, store_statistics=True) + + # Get real TIMEOUT runs + timeout_trials = self._get_timeout_trials(runhistory, budget_subset) + + # Use penalization (e.g. PAR10) for EPM training + store_statistics = True if np.any(np.isnan(self._min_y)) else False + tX, tY = self._build_matrix( + trials=timeout_trials, + runhistory=runhistory, + store_statistics=store_statistics, + ) + + # If we don't have successful runs, + # we have to return all timeout runs + if not considered_trials: + return tX, tY + + # If we do not impute, we also return TIMEOUT data + X = np.vstack((X, tX)) + Y = np.concatenate((Y, tY)) + + logger.debug("Converted %d observations." % (X.shape[0])) + return X, Y + + @abstractmethod + def transform_response_values( + self, + values: np.ndarray, + ) -> np.ndarray: + """Transform function response values. + + Parameters + ---------- + values : np.ndarray + Response values to be transformed. + + Returns + ------- + transformed_values : np.ndarray + """ + raise NotImplementedError diff --git a/smac/runhistory/encoder/boing_encoder.py b/smac/runhistory/encoder/boing_encoder.py new file mode 100644 index 000000000..5a89ae9f8 --- /dev/null +++ b/smac/runhistory/encoder/boing_encoder.py @@ -0,0 +1,66 @@ +from __future__ import annotations + +import copy + +import numpy as np + +from smac.runhistory.encoder.encoder import RunHistoryEncoder +from smac.runhistory.encoder.log_scaled_encoder import RunHistoryLogScaledEncoder +from smac.runhistory.runhistory import RunHistory +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class RunHistoryRawEncoder(RunHistoryEncoder): + """ + A transformer that transform the RunHistroy to vectors. This set of classes will return the raw cost values in + addition to the transformed cost values. The raw cost values can then be applied for local BO approaches. + """ + + def transform_with_raw( + self, + runhistory: RunHistory, + budget_subset: list | None = None, + ) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + """Returns vector representation of runhistory; if imputation is + disabled, censored (TIMEOUT with time < cutoff) will be skipped. This function returns both the raw + and transformed cost values + + Parameters + ---------- + runhistory : smac.runhistory.runhistory.RunHistory + Runhistory containing all evaluated configurations/instances + budget_subset : list of budgets to consider + + Returns + ------- + X: numpy.ndarray + configuration vector x instance features + Y: numpy.ndarray + cost values + Y_raw: numpy.ndarray + cost values before transformation + """ + X, Y_raw = RunHistoryEncoder.transform(self, runhistory, budget_subset) + Y = copy.deepcopy(Y_raw) + Y = self.transform_raw_values(Y) + return X, Y, Y_raw + + def transform_response_values(self, values: np.ndarray) -> np.ndarray: + """Returns the input values.""" + return values + + def transform_raw_values(self, values: np.ndarray) -> np.ndarray: + """Returns the raw input values before transformation.""" + return values + + +class RunHistoryRawScaledEncoder(RunHistoryRawEncoder, RunHistoryLogScaledEncoder): + def transform_raw_values(self, values: np.ndarray) -> np.ndarray: + """Returns the raw input values before transformation.""" + return RunHistoryLogScaledEncoder.transform_response_values(self, values) diff --git a/smac/runhistory/encoder/eips_encoder.py b/smac/runhistory/encoder/eips_encoder.py new file mode 100644 index 000000000..4c64539f2 --- /dev/null +++ b/smac/runhistory/encoder/eips_encoder.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +from typing import Mapping + +import numpy as np + +from smac.runhistory.encoder import AbstractRunHistoryEncoder +from smac.runhistory.runhistory import RunHistory, TrialKey, TrialValue +from smac.utils.configspace import convert_configurations_to_array +from smac.utils.logging import get_logger +from smac.utils.multi_objective import normalize_costs + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class RunHistoryEIPSEncoder(AbstractRunHistoryEncoder): + """Encoder specifically for the EIPS acquisition function.""" + + def _build_matrix( + self, + trials: Mapping[TrialKey, TrialValue], + runhistory: RunHistory, + store_statistics: bool = False, + ) -> tuple[np.ndarray, np.ndarray]: + if store_statistics: + # store_statistics is currently not necessary + pass + + # First build nan-matrix of size #configs x #params+1 + n_rows = len(trials) + n_cols = self._n_params + X = np.ones([n_rows, n_cols + self._n_features]) * np.nan + y = np.ones([n_rows, 2]) + + # Then populate matrix + for row, (key, run) in enumerate(trials.items()): + # Scaling is automatically done in configSpace + conf = runhistory.ids_config[key.config_id] + conf_vector = convert_configurations_to_array([conf])[0] + if self._n_features > 0 and self._instance_features is not None: + assert isinstance(key.instance, str) + feats = self._instance_features[key.instance] + X[row, :] = np.hstack((conf_vector, feats)) + else: + X[row, :] = conf_vector + + if self._n_objectives > 1: + assert self._multi_objective_algorithm is not None + assert isinstance(run.cost, list) + + # Let's normalize y here + # We use the objective_bounds calculated by the runhistory + y_ = normalize_costs(run.cost, runhistory.objective_bounds) + y_agg = self._multi_objective_algorithm(y_) + y[row, 0] = y_agg + else: + y[row, 0] = run.cost + + y[row, 1] = run.time + + y_transformed = self.transform_response_values(values=y) + + return X, y_transformed + + def transform_response_values(self, values: np.ndarray) -> np.ndarray: + """Transform function response values. Transform the runtimes by a log transformation + (log(1. + + + runtime). + + Parameters + ---------- + values : np.ndarray + Response values to be transformed. + + Returns + ------- + np.ndarray + """ + # We need to ensure that time remains positive after the log transform. + values[:, 1] = np.log(1 + values[:, 1]) + return values diff --git a/smac/runhistory/encoder/encoder.py b/smac/runhistory/encoder/encoder.py new file mode 100644 index 000000000..a8a82c8ea --- /dev/null +++ b/smac/runhistory/encoder/encoder.py @@ -0,0 +1,75 @@ +from __future__ import annotations + +from typing import Mapping + +import numpy as np + +from smac.runhistory.encoder import AbstractRunHistoryEncoder +from smac.runhistory.runhistory import RunHistory, TrialKey, TrialValue +from smac.utils.configspace import convert_configurations_to_array +from smac.utils.logging import get_logger +from smac.utils.multi_objective import normalize_costs + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class RunHistoryEncoder(AbstractRunHistoryEncoder): + def _build_matrix( + self, + trials: Mapping[TrialKey, TrialValue], + runhistory: RunHistory, + store_statistics: bool = False, + ) -> tuple[np.ndarray, np.ndarray]: + # First build nan-matrix of size #configs x #params+1 + n_rows = len(trials) + n_cols = self._n_params + X = np.ones([n_rows, n_cols + self._n_features]) * np.nan + + # For now we keep it as 1 + # TODO: Extend for native multi-objective + y = np.ones([n_rows, 1]) + + if self._multi_objective_algorithm is not None: + self._multi_objective_algorithm.update_on_iteration_start() + + # Then populate matrix + for row, (key, run) in enumerate(trials.items()): + # Scaling is automatically done in configSpace + conf = runhistory._ids_config[key.config_id] + conf_vector = convert_configurations_to_array([conf])[0] + + if self._n_features > 0 and self._instance_features is not None: + assert isinstance(key.instance, str) + feats = self._instance_features[key.instance] + X[row, :] = np.hstack((conf_vector, feats)) + else: + X[row, :] = conf_vector + + if self._n_objectives > 1: + assert self._multi_objective_algorithm is not None + assert isinstance(run.cost, list) + + # Let's normalize y here + # We use the objective_bounds calculated by the runhistory + y_ = normalize_costs(run.cost, runhistory.objective_bounds) + y_agg = self._multi_objective_algorithm(y_) + y[row] = y_agg + else: + y[row] = run.cost + + if y.size > 0: + if store_statistics: + self._percentile = np.percentile(y, self._scale_percentage, axis=0) + self._min_y = np.min(y, axis=0) + self._max_y = np.max(y, axis=0) + + y = self.transform_response_values(values=y) + return X, y + + def transform_response_values(self, values: np.ndarray) -> np.ndarray: + """Returns the input values.""" + return values diff --git a/smac/runhistory/encoder/inverse_scaled_encoder.py b/smac/runhistory/encoder/inverse_scaled_encoder.py new file mode 100644 index 000000000..e5c577fe6 --- /dev/null +++ b/smac/runhistory/encoder/inverse_scaled_encoder.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np + +from smac import constants +from smac.runhistory.encoder.encoder import RunHistoryEncoder +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class RunHistoryInverseScaledEncoder(RunHistoryEncoder): + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + if self._instances is not None and len(self._instances) > 1: + raise NotImplementedError("Handling more than one instance is not supported for inverse scaled cost.") + + def transform_response_values(self, values: np.ndarray) -> np.ndarray: + """Transform the response values by linearly scaling + them between zero and one and then using inverse scaling. + """ + min_y = self._min_y - ( + self._percentile - self._min_y + ) # Subtract the difference between the percentile and the minimum + min_y -= constants.VERY_SMALL_NUMBER # Minimal value to avoid numerical issues in the log scaling below + # linear scaling + # prevent diving by zero + + min_y[np.where(min_y == self._max_y)] *= 1 - 10**-10 + + values = (values - min_y) / (self._max_y - min_y) + values = 1 - 1 / values + return values diff --git a/smac/runhistory/encoder/log_encoder.py b/smac/runhistory/encoder/log_encoder.py new file mode 100644 index 000000000..56db1b17b --- /dev/null +++ b/smac/runhistory/encoder/log_encoder.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +import numpy as np + +from smac import constants +from smac.runhistory.encoder.encoder import RunHistoryEncoder +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class RunHistoryLogEncoder(RunHistoryEncoder): + def transform_response_values(self, values: np.ndarray) -> np.ndarray: + """Transforms the response values by using a log.""" + # ensure that minimal value is larger than 0 + if np.any(values <= 0): + logger.warning( + "Got cost of smaller/equal to 0. Replace by %f since we use" + " log cost." % constants.MINIMAL_COST_FOR_LOG + ) + values[values < constants.MINIMAL_COST_FOR_LOG] = constants.MINIMAL_COST_FOR_LOG + + return np.log(values) diff --git a/smac/runhistory/encoder/log_scaled_encoder.py b/smac/runhistory/encoder/log_scaled_encoder.py new file mode 100644 index 000000000..62585b4c3 --- /dev/null +++ b/smac/runhistory/encoder/log_scaled_encoder.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +import warnings + +import numpy as np + +from smac import constants +from smac.runhistory.encoder.encoder import RunHistoryEncoder +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class RunHistoryLogScaledEncoder(RunHistoryEncoder): + def transform_response_values(self, values: np.ndarray) -> np.ndarray: + """Transform the response values by linearly scaling them between zero and one and + then using the log transformation. + """ + min_y = self._min_y - ( + self._percentile - self._min_y + ) # Subtract the difference between the percentile and the minimum + min_y -= constants.VERY_SMALL_NUMBER # Minimal value to avoid numerical issues in the log scaling below + + # Linear scaling + # prevent diving by zero + min_y[np.where(min_y == self._max_y)] *= 1 - 10**-10 + + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=RuntimeWarning) + + values = (values - min_y) / (self._max_y - min_y) + return np.log(values) diff --git a/smac/runhistory/encoder/scaled_encoder.py b/smac/runhistory/encoder/scaled_encoder.py new file mode 100644 index 000000000..0e09a6d97 --- /dev/null +++ b/smac/runhistory/encoder/scaled_encoder.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +import numpy as np + +from smac import constants +from smac.runhistory.encoder.encoder import RunHistoryEncoder +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class RunHistoryScaledEncoder(RunHistoryEncoder): + def transform_response_values(self, values: np.ndarray) -> np.ndarray: + """Transforms the response values by linearly scaling them between zero and one.""" + min_y = self._min_y - ( + self._percentile - self._min_y + ) # Subtract the difference between the percentile and the minimum + min_y -= constants.VERY_SMALL_NUMBER # Minimal value to avoid numerical issues in the log scaling below + + # Linear scaling + # prevent diving by zero + min_y[np.where(min_y == self._max_y)] *= 1 - 10**-101 + values = (values - min_y) / (self._max_y - min_y) + return values diff --git a/smac/runhistory/encoder/sqrt_scaled_encoder.py b/smac/runhistory/encoder/sqrt_scaled_encoder.py new file mode 100644 index 000000000..022b9e341 --- /dev/null +++ b/smac/runhistory/encoder/sqrt_scaled_encoder.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np + +from smac import constants +from smac.runhistory.encoder.encoder import RunHistoryEncoder +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class RunHistorySqrtScaledEncoder(RunHistoryEncoder): + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + if self._instances is not None and len(self._instances) > 1: + raise NotImplementedError("Handling more than one instance is not supported for sqrt scaled cost.") + + def transform_response_values(self, values: np.ndarray) -> np.ndarray: + """Transform the response values by linearly scaling them between zero and one and then using the + square root. + """ + # Subtract the difference between the percentile and the minimum + min_y = self._min_y - (self._percentile - self._min_y) + + # Minimal value to avoid numerical issues in the log scaling below + min_y -= constants.VERY_SMALL_NUMBER + + # Linear scaling: prevent diving by zero + min_y[np.where(min_y == self._max_y)] *= 1 - 10**-10 + + values = (values - min_y) / (self._max_y - min_y) + values = np.sqrt(values) + + return values diff --git a/smac/runhistory/enumerations.py b/smac/runhistory/enumerations.py new file mode 100644 index 000000000..0f2b6c058 --- /dev/null +++ b/smac/runhistory/enumerations.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +from enum import Enum, IntEnum + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class StatusType(IntEnum): + """Class to define numbers for status types.""" + + SUCCESS = 1 + TIMEOUT = 2 + CRASHED = 3 + ABORT = 4 + MEMORYOUT = 5 + + # Only relevant for SH/HB. Run might have a results, but should not be considered further. + # By default, these runs will always be considered for building the model. Potential use cases: + # 1) The run has converged and does not benefit from a higher budget + # 2) The run has exhausted given resources and will not benefit from higher budgets + DONOTADVANCE = 6 + + STOP = 7 # Gracefully exit SMAC and wait for currently executed runs to finish. + RUNNING = 8 # In case a job was submited, but it has not finished. + + +class DataOrigin(Enum): + """Definition of how data in the runhistory is used. + + * ``INTERNAL``: internal data which was gathered during the current + optimization run. It will be saved to disk, used for building EPMs and + during intensify. + * ``EXTERNAL_SAME_INSTANCES``: external data, which was gathered by running + another program on the same instances as the current optimization run + runs on (for example pSMAC). It will not be saved to disk, but used both + for EPM building and during intensify. + * ``EXTERNAL_DIFFERENT_INSTANCES``: external data, which was gathered on a + different instance set as the one currently used, but due to having the + same instance features can still provide useful information. Will not be + saved to disk and only used for EPM building. + """ + + INTERNAL = 1 + EXTERNAL_SAME_INSTANCES = 2 + EXTERNAL_DIFFERENT_INSTANCES = 3 + + +class TrialInfoIntent(Enum): + """Class to define different requests on how to process the runinfo. + + Gives the flexibility to indicate whether a configuration should be skipped (SKIP) or if the + SMBO should simple run a generated run_info. + """ + + RUN = 0 # Normal run execution of a run info + SKIP = 1 # Skip running the run_info + WAIT = 2 # Wait for more configs to be processed diff --git a/smac/runhistory/runhistory.py b/smac/runhistory/runhistory.py index 75a44d292..898c7e9f7 100644 --- a/smac/runhistory/runhistory.py +++ b/smac/runhistory/runhistory.py @@ -1,146 +1,35 @@ from __future__ import annotations -from typing import ( - Any, - Dict, - Iterable, - Iterator, - List, - Mapping, - Optional, - Tuple, - Type, - Union, - cast, -) +from typing import Any, Iterable, Iterator, Mapping, cast -import collections import json -from enum import Enum +from collections import OrderedDict +from pathlib import Path import numpy as np +from ConfigSpace import Configuration, ConfigurationSpace -from smac.configspace import Configuration, ConfigurationSpace -from smac.multi_objective.utils import normalize_costs -from smac.tae import StatusType -from smac.utils.logging import PickableLoggerAdapter +from smac.multi_objective.abstract_multi_objective_algorithm import ( + AbstractMultiObjectiveAlgorithm, +) +from smac.runhistory.dataclasses import ( + InstanceSeedBudgetKey, + InstanceSeedKey, + TrialKey, + TrialValue, +) +from smac.runhistory.enumerations import DataOrigin, StatusType +from smac.utils.logging import get_logger +from smac.utils.multi_objective import normalize_costs -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2015, ML4AAD" +__copyright__ = "Copyright 2022, automl.org" __license__ = "3-clause BSD" -__maintainer__ = "Marius Lindauer" -__email__ = "lindauer@cs.uni-freiburg.de" -__version__ = "0.0.1" - -logger = PickableLoggerAdapter(__name__) - - -# NOTE class instead of collection to have a default value for budget in RunKey -class RunKey(collections.namedtuple("RunKey", ["config_id", "instance_id", "seed", "budget"])): - __slots__ = () - - def __new__( - cls, # No type annotation because the 1st argument for a namedtuble is always the class type, - # see https://docs.python.org/3/reference/datamodel.html#object.__new__ - config_id: int, - instance_id: Optional[str], - seed: Optional[int], - budget: float = 0.0, - ) -> "RunKey": - """Creates a new `RunKey` instance.""" - return super().__new__(cls, config_id, instance_id, seed, budget) - - -# NOTE class instead of collection to have a default value for budget/source_id in RunInfo -class RunInfo( - collections.namedtuple( - "RunInfo", - [ - "config", - "instance", - "instance_specific", - "seed", - "cutoff", - "capped", - "budget", - "source_id", - ], - ) -): - __slots__ = () - - def __new__( - cls, # No type annotation because the 1st argument for a namedtuble is always the class type, - # see https://docs.python.org/3/reference/datamodel.html#object.__new__ - config: Configuration, - instance: Optional[str], - instance_specific: str, - seed: int, - cutoff: Optional[float], - capped: bool, - budget: float = 0.0, - # In the context of parallel runs, one will have multiple suppliers of - # configurations. source_id is a new mechanism to track what entity launched - # this configuration - source_id: int = 0, - ) -> "RunInfo": - """Creates a new `RunInfo` instance.""" - return super().__new__( - cls, - config, - instance, - instance_specific, - seed, - cutoff, - capped, - budget, - source_id, - ) - - -InstSeedKey = collections.namedtuple("InstSeedKey", ["instance", "seed"]) - -InstSeedBudgetKey = collections.namedtuple("InstSeedBudgetKey", ["instance", "seed", "budget"]) -RunValue = collections.namedtuple("RunValue", ["cost", "time", "status", "starttime", "endtime", "additional_info"]) +logger = get_logger(__name__) -class EnumEncoder(json.JSONEncoder): - """Custom encoder for enum-serialization (implemented for StatusType from tae). - - Using encoder implied using object_hook as defined in StatusType to deserialize from json. - """ - - def default(self, obj: object) -> Any: - """Returns the default encoding of the passed object.""" - if isinstance(obj, StatusType): - return {"__enum__": str(obj)} - return json.JSONEncoder.default(self, obj) - - -class DataOrigin(Enum): - """Definition of how data in the runhistory is used. - - * ``INTERNAL``: internal data which was gathered during the current - optimization run. It will be saved to disk, used for building EPMs and - during intensify. - * ``EXTERNAL_SAME_INSTANCES``: external data, which was gathered by running - another program on the same instances as the current optimization run - runs on (for example pSMAC). It will not be saved to disk, but used both - for EPM building and during intensify. - * ``EXTERNAL_DIFFERENT_INSTANCES``: external data, which was gathered on a - different instance set as the one currently used, but due to having the - same instance features can still provide useful information. Will not be - saved to disk and only used for EPM building. - """ - - INTERNAL = 1 - EXTERNAL_SAME_INSTANCES = 2 - EXTERNAL_DIFFERENT_INSTANCES = 3 - - -class RunHistory(Mapping[RunKey, RunValue]): - """Container for target algorithm run information. +class RunHistory(Mapping[TrialKey, TrialValue]): + """Container for the target function run information. Most importantly, the runhistory contains an efficient mapping from each evaluated configuration to the empirical cost observed on either the full instance set or a subset. The cost is the average over all @@ -150,7 +39,6 @@ class RunHistory(Mapping[RunKey, RunValue]): * If using instances as the budget, the average cost over all evaluated instances is returned. * Theoretically, the runhistory object can handle instances and budgets at the same time. This is neither used nor tested. - * Capped runs are not included in this cost. Note ---- @@ -158,77 +46,93 @@ class RunHistory(Mapping[RunKey, RunValue]): Parameters ---------- - overwrite_existing_runs : bool (default=False) - If set to ``True`` and a run of a configuration on an instance-budget-seed-pair already exists, - it is overwritten. Allows to overwrites old results if pairs of algorithm-instance-seed were measured - multiple times - - Attributes - ---------- - data : collections.OrderedDict() - Internal data representation - config_ids : dict - Maps config -> id - ids_config : dict - Maps id -> config - num_runs_per_config : dict - Maps config_id -> number of runs + multi_objective_algorithm : AbstractMultiObjectiveAlgorithm | None, defaults to None + The multi-objective algorithm is required to scaralize the costs in case of multi-objective. + overwrite_existing_trials : bool, defaults to false + Overwrites a trial (combination of configuration, instance, budget and seed) if it already exists. """ def __init__( self, - overwrite_existing_runs: bool = False, + multi_objective_algorithm: AbstractMultiObjectiveAlgorithm | None = None, + overwrite_existing_trials: bool = False, ) -> None: - self.logger = PickableLoggerAdapter(self.__module__ + "." + self.__class__.__name__) - - # By having the data in a deterministic order we can do useful tests - # when we serialize the data and can assume it's still in the same - # order as it was added. - self.data = collections.OrderedDict() # type: Dict[RunKey, RunValue] - - # for fast access, we have also an unordered data structure - # to get all instance seed pairs of a configuration. - # This does not include capped runs. - self._configid_to_inst_seed_budget = {} # type: Dict[int, Dict[InstSeedKey, List[float]]] - - self.config_ids = {} # type: Dict[Configuration, int] - self.ids_config = {} # type: Dict[int, Configuration] + self._multi_objective_algorithm = multi_objective_algorithm + self._overwrite_existing_trials = overwrite_existing_trials + self.reset() + + @property + def multi_objective_algorithm(self) -> AbstractMultiObjectiveAlgorithm | None: + """The multi-objective algorithm is required to scaralize the costs in case of multi-objective.""" + return self._multi_objective_algorithm + + @multi_objective_algorithm.setter + def multi_objective_algorithm(self, value: AbstractMultiObjectiveAlgorithm) -> None: + """We want to have the option to change the multi objective algorithm.""" + self._multi_objective_algorithm = value + + @property + def ids_config(self) -> dict[int, Configuration]: + """Mapping from config id to configuration.""" + return self._ids_config + + @property + def config_ids(self) -> dict[Configuration, int]: + """Mapping from configuration to config id.""" + return self._config_ids + + @property + def objective_bounds(self) -> list[tuple[float, float]]: + """Returns the lower and upper bound of each objective.""" + return self._objective_bounds + + def reset(self) -> None: + """Resets this runhistory to it's default state.""" + # By having the data in a deterministic order we can do useful tests when we + # serialize the data and can assume it's still in the same order as it was added. + self._data: dict[TrialKey, TrialValue] = OrderedDict() + + # For fast access, we have also an unordered data structure to get all instance + # seed pairs of a configuration. + self._config_id_to_isk_to_budget: dict[int, dict[InstanceSeedKey, list[float | None]]] = {} + + self._config_ids: dict[Configuration, int] = {} + self._ids_config: dict[int, Configuration] = {} self._n_id = 0 # Stores cost for each configuration ID - self._cost_per_config = {} # type: Dict[int, float | list[float]] + self._cost_per_config: dict[int, float | list[float]] = {} # Stores min cost across all budgets for each configuration ID - self._min_cost_per_config = {} # type: Dict[int, float | list[float]] - # runs_per_config maps the configuration ID to the number of runs for that configuration - # and is necessary for computing the moving average - self.num_runs_per_config = {} # type: Dict[int, int] + self._min_cost_per_config: dict[int, float | list[float]] = {} + # Maps the configuration ID to the number of runs for that configuration + # and is necessary for computing the moving average. + self._num_trials_per_config: dict[int, int] = {} # Store whether a datapoint is "external", which means it was read from - # a JSON file. Can be chosen to not be written to disk - self.external = {} # type: Dict[RunKey, DataOrigin] - - self.overwrite_existing_runs = overwrite_existing_runs - self.num_obj = -1 # type: int - self.objective_bounds = [] # type: List[Tuple[float, float]] + # a JSON file. Can be chosen to not be written to disk. + self._external: dict[TrialKey, DataOrigin] = {} + self._n_objectives: int = -1 + self._objective_bounds: list[tuple[float, float]] = [] def __contains__(self, k: object) -> bool: - """Dictionary semantics for `k in runhistory`""" - return k in self.data - - def __getitem__(self, k: RunKey) -> RunValue: - """Dictionary semantics for `v = runhistory[k]`""" - return self.data[k] + """Dictionary semantics for `k in runhistory`.""" + return k in self._data - def __iter__(self) -> Iterator[RunKey]: - """Dictionary semantics for `for k in runhistory.keys()`, enables. + def __getitem__(self, k: TrialKey) -> TrialValue: + """Dictionary semantics for `v = runhistory[k]`.""" + return self._data[k] - .items() - """ - return iter(self.data.keys()) + def __iter__(self) -> Iterator[TrialKey]: + """Dictionary semantics for `for k in runhistory.keys()`.""" + return iter(self._data.keys()) def __len__(self) -> int: """Enables the `len(runhistory)`""" - return len(self.data) + return len(self._data) + + def __eq__(self, other: Any) -> bool: + """Enables to check equality of runhistory if the run is continued.""" + return self._data == other._data def empty(self) -> bool: """Check whether or not the RunHistory is empty. @@ -236,222 +140,101 @@ def empty(self) -> bool: Returns ------- emptiness: bool - True if runs have been added to the RunHistory, - False otherwise - """ - return len(self.data) == 0 - - def _check_json_serializable( - self, - key: str, - obj: Any, - encoder: Type[json.JSONEncoder], - runkey: RunKey, - runvalue: RunValue, - ) -> None: - try: - json.dumps(obj, cls=encoder) - except Exception as e: - raise ValueError( - "Cannot add %s: %s of type %s to runhistory because it raises an error during JSON encoding, " - "please see the error above.\nRunKey: %s\nRunValue %s" % (key, str(obj), type(obj), runkey, runvalue) - ) from e - - def _update_objective_bounds(self) -> None: - """Update the objective bounds based on the data in the runhistory.""" - all_costs = [] - for (costs, _, status, _, _, _) in self.data.values(): - if status == StatusType.SUCCESS: - if not isinstance(costs, Iterable): - costs = [costs] - - assert len(costs) == self.num_obj - all_costs.append(costs) - - all_costs = np.array(all_costs, dtype=float) # type: ignore[assignment] - - if len(all_costs) == 0: - self.objective_bounds = [(np.inf, -np.inf)] * self.num_obj - return - - min_values = np.min(all_costs, axis=0) - max_values = np.max(all_costs, axis=0) - - self.objective_bounds = [] - for min_v, max_v in zip(min_values, max_values): - self.objective_bounds += [(min_v, max_v)] - - def _add(self, k: RunKey, v: RunValue, status: StatusType, origin: DataOrigin) -> None: - """ - Actual function to add new entry to data structures. - - Note - ---- - This method always calls `update_cost` in the multi- - objective setting. + True if trials have been added to the RunHistory. """ - self.data[k] = v - self.external[k] = origin - - # Update objective bounds based on raw data - self._update_objective_bounds() - - # Capped data is added above - # Do not register the cost until the run has completed - if origin in ( - DataOrigin.INTERNAL, - DataOrigin.EXTERNAL_SAME_INSTANCES, - ) and status not in [StatusType.CAPPED, StatusType.RUNNING]: - # also add to fast data structure - is_k = InstSeedKey(k.instance_id, k.seed) - self._configid_to_inst_seed_budget[k.config_id] = self._configid_to_inst_seed_budget.get(k.config_id, {}) - if is_k not in self._configid_to_inst_seed_budget[k.config_id].keys(): - # add new inst-seed-key with budget to main dict - self._configid_to_inst_seed_budget[k.config_id][is_k] = [k.budget] - elif k.budget not in is_k: - # append new budget to existing inst-seed-key dict - self._configid_to_inst_seed_budget[k.config_id][is_k].append(k.budget) - - # if budget is used, then update cost instead of incremental updates - if not self.overwrite_existing_runs and k.budget == 0: - # assumes an average across runs as cost function aggregation, this is used for - # algorithm configuration (incremental updates are used to save time as getting the - # cost for > 100 instances is high) - self.incremental_update_cost(self.ids_config[k.config_id], v.cost) - else: - # this is when budget > 0 (only successive halving and hyperband so far) - self.update_cost(config=self.ids_config[k.config_id]) - if k.budget > 0: - if self.num_runs_per_config[k.config_id] != 1: # This is updated in update_cost - raise ValueError("This should not happen!") - - def _cost( - self, - config: Configuration, - instance_seed_budget_keys: Optional[Iterable[InstSeedBudgetKey]] = None, - ) -> list[list[float] | list[list[float]]]: - """Returns a list of all costs for the given config for further calculations. - The costs are directly taken from the runhistory data. - - Parameters - ---------- - config : Configuration - Configuration to calculate objective for. - instance_seed_budget_keys : list, optional (default=None) - List of tuples of instance-seeds-budget keys. If None, the run_history is - queried for all runs of the given configuration. - - Returns - ------- - Costs: list[list[float] | list[list[float]]] - List of all found costs. In case of multi-objective, the list contains lists. - """ - try: - id_ = self.config_ids[config] - except KeyError: # challenger was not running so far - return [] - - if instance_seed_budget_keys is None: - instance_seed_budget_keys = self.get_runs_for_config(config, only_max_observed_budget=True) - - costs = [] - for i, r, b in instance_seed_budget_keys: - k = RunKey(id_, i, r, b) - costs.append(self.data[k].cost) - - return costs + return len(self._data) == 0 def add( self, config: Configuration, - cost: Union[int, float, list, np.ndarray], + cost: int | float | list[int | float], time: float, - status: StatusType, - instance_id: Optional[str] = None, - seed: Optional[int] = None, - budget: float = 0.0, + status: StatusType = StatusType.SUCCESS, + instance: str | None = None, + seed: int | None = None, + budget: float | None = None, starttime: float = 0.0, endtime: float = 0.0, - additional_info: Optional[Dict] = None, + additional_info: dict[str, Any] = {}, origin: DataOrigin = DataOrigin.INTERNAL, force_update: bool = False, ) -> None: - """Adds a data of a new target algorithm (TA) run; it will update data if the same key - values are used (config, instance_id, seed) + """Adds a new trial. Parameters ---------- - config : dict (or other type -- depending on config space module) - Parameter configuration - cost: Union[int, float, list, np.ndarray] - Cost of TA run (will be minimized) - time: float - Runtime of TA run - status: str - Status in {SUCCESS, TIMEOUT, CRASHED, ABORT, MEMOUT} - instance_id: str - String representing an instance (default: None) - seed: int - Random seed used by TA (default: None) - budget: float - budget (cutoff) used in intensifier to limit TA (default: 0) - starttime: float - starting timestamp of TA evaluation - endtime: float - ending timestamp of TA evaluation - additional_info: dict - Additional run infos (could include further returned - information from TA or fields such as start time and host_id) - origin: DataOrigin - Defines how data will be used. - force_update: bool (default: False) - Forces the addition of a config to the history + config : Configuration + cost : int | float | list[int | float] + Cost of the evaluated trial. Might be a list in case of multi-objective. + time : float + How much time was needed to evaluate the trial. + status : StatusType, defaults to StatusType.SUCCESS + The status of the trial. + instance : str | None, defaults to none + seed : int | None, defaults to none + budget : float | None, defaults to none + starttime : float, defaults to 0.0 + endtime : float, defaults to 0.0 + additional_info : dict[str, Any], defaults to {} + origin : DataOrigin, defaults to DataOrigin.INTERNAL + force_update : bool, defaults to false + Overwrites a previous trial if the trial already exists. """ if config is None: - raise TypeError("Configuration to add to the runhistory must not be None") + raise TypeError("Configuration must not be None.") elif not isinstance(config, Configuration): - raise TypeError( - "Configuration to add to the runhistory is not of type Configuration, but %s" % type(config) - ) + raise TypeError("Configuration is not of type Configuration, but %s." % type(config)) # Squeeze is important to reduce arrays with one element # to scalars. cost_array = np.asarray(cost).squeeze() - num_obj = np.size(cost_array) + n_objectives = np.size(cost_array) # Get the config id - config_id_tmp = self.config_ids.get(config) - if config_id_tmp is None: + config_id = self._config_ids.get(config) + + if config_id is None: self._n_id += 1 - self.config_ids[config] = self._n_id - config_id = cast(int, self.config_ids.get(config)) - self.ids_config[self._n_id] = config - else: - config_id = cast(int, config_id_tmp) + self._config_ids[config] = self._n_id + self._ids_config[self._n_id] = config - if self.num_obj == -1: - self.num_obj = num_obj - elif self.num_obj != num_obj: + config_id = self._n_id + + if self._n_objectives == -1: + self._n_objectives = n_objectives + elif self._n_objectives != n_objectives: raise ValueError( - f"Cost is not of the same length ({num_obj}) as the number " f"of objectives ({self.num_obj})" + f"Cost is not of the same length ({n_objectives}) as the number of " + f"objectives ({self._n_objectives})." ) # Let's always work with floats; Makes it easier to deal with later on - # array.tolist() returns a scalar if the array has one element. + # array.tolist(), it returns a scalar if the array has one element. c = cost_array.tolist() - if self.num_obj == 1: + if self._n_objectives == 1: c = float(c) else: c = [float(i) for i in c] - k = RunKey(config_id, instance_id, seed, budget) - v = RunValue(c, time, status, starttime, endtime, additional_info) + if budget is not None: + # Just to make sure we really add a float + budget = float(budget) + + k = TrialKey(config_id=config_id, instance=instance, seed=seed, budget=budget) + v = TrialValue( + cost=c, + time=time, + status=status, + starttime=starttime, + endtime=endtime, + additional_info=additional_info, + ) # Construct keys and values for the data dictionary for key, value in ( ("config", config.get_dictionary()), ("config_id", config_id), - ("instance_id", instance_id), + ("instance", instance), ("seed", seed), ("budget", budget), ("cost", c), @@ -462,73 +245,62 @@ def add( ("additional_info", additional_info), ("origin", config.origin), ): - self._check_json_serializable(key, value, EnumEncoder, k, v) + self._check_json_serializable(key, value, k, v) - # Each runkey is supposed to be used only once. Repeated tries to add - # the same runkey will be ignored silently if not capped. - if self.overwrite_existing_runs or force_update or self.data.get(k) is None: + # Each trial_key is supposed to be used only once. Repeated tries to add + # the same trial_key will be ignored silently if not capped. + if self._overwrite_existing_trials or force_update or self._data.get(k) is None: self._add(k, v, status, origin) - elif status != StatusType.CAPPED and self.data[k].status == StatusType.CAPPED: - # overwrite capped runs with uncapped runs - self._add(k, v, status, origin) - elif status == StatusType.CAPPED and self.data[k].status == StatusType.CAPPED: - if self.num_obj > 1: - raise RuntimeError("Not supported yet.") - - # Overwrite if censored with a larger cutoff - if cost > self.data[k].cost: - self._add(k, v, status, origin) else: - logger.info("Entry was not added to the runhistory because existing runs will not overwritten.") + logger.info("Entry was not added to the runhistory because existing trials will not be overwritten.") def update_cost(self, config: Configuration) -> None: - """Stores the performance of a configuration across the instances in self.cost_per_config - and also updates self.runs_per_config; - - Note - ---- - This method ignores capped runs. + """Stores the performance of a configuration across the instances in `self._cost_per_config` + and also updates `self._num_trials_per_config`. Parameters ---------- config: Configuration - configuration to update cost based on all runs in runhistory + configuration to update cost based on all trials in runhistory """ - config_id = self.config_ids[config] + config_id = self._config_ids[config] # Removing duplicates while keeping the order - inst_seed_budgets = list(dict.fromkeys(self.get_runs_for_config(config, only_max_observed_budget=True))) + inst_seed_budgets = list(dict.fromkeys(self.get_trials(config, only_max_observed_budget=True))) self._cost_per_config[config_id] = self.average_cost(config, inst_seed_budgets) - self.num_runs_per_config[config_id] = len(inst_seed_budgets) + self._num_trials_per_config[config_id] = len(inst_seed_budgets) - all_inst_seed_budgets = list(dict.fromkeys(self.get_runs_for_config(config, only_max_observed_budget=False))) - self._min_cost_per_config[config_id] = self.min_cost(config, all_inst_seed_budgets) + all_isb = list(dict.fromkeys(self.get_trials(config, only_max_observed_budget=False))) + self._min_cost_per_config[config_id] = self.min_cost(config, all_isb) - def incremental_update_cost(self, config: Configuration, cost: Union[np.ndarray, list, float, int]) -> None: + def incremental_update_cost(self, config: Configuration, cost: float | list[float]) -> None: """Incrementally updates the performance of a configuration by using a moving average. Parameters ---------- config: Configuration - configuration to update cost based on all runs in runhistory + configuration to update cost based on all trials in runhistory cost: float cost of new run of config """ - config_id = self.config_ids[config] - n_runs = self.num_runs_per_config.get(config_id, 0) + config_id = self._config_ids[config] + n_trials = self._num_trials_per_config.get(config_id, 0) - if self.num_obj > 1: + if self._n_objectives > 1: costs = np.array(cost) - old_costs = self._cost_per_config.get(config_id, np.array([0.0 for _ in range(self.num_obj)])) + old_costs = self._cost_per_config.get(config_id, np.array([0.0 for _ in range(self._n_objectives)])) old_costs = np.array(old_costs) - new_costs = ((old_costs * n_runs) + costs) / (n_runs + 1) # type: ignore + new_costs = ((old_costs * n_trials) + costs) / (n_trials + 1) self._cost_per_config[config_id] = new_costs.tolist() else: old_cost = self._cost_per_config.get(config_id, 0.0) - self._cost_per_config[config_id] = ((old_cost * n_runs) + cost) / (n_runs + 1) # type: ignore - self.num_runs_per_config[config_id] = n_runs + 1 + assert isinstance(cost, float) + assert isinstance(old_cost, float) + self._cost_per_config[config_id] = ((old_cost * n_trials) + cost) / (n_trials + 1) + + self._num_trials_per_config[config_id] = n_trials + 1 def get_cost(self, config: Configuration) -> float: """Returns empirical cost for a configuration. See the class docstring for how the costs are @@ -543,45 +315,51 @@ def get_cost(self, config: Configuration) -> float: cost: float Computed cost for configuration """ - config_id = self.config_ids.get(config) + config_id = self._config_ids.get(config) # Cost is always a single value (Single objective) or a list of values (Multi-objective) # For example, _cost_per_config always holds the value on the highest budget cost = self._cost_per_config.get(config_id, np.nan) # type: ignore[arg-type] # noqa F821 - if self.num_obj > 1: - assert type(cost) == list + if self._n_objectives > 1: + assert isinstance(cost, list) + assert self.multi_objective_algorithm is not None + # We have to normalize the costs here - costs = normalize_costs(cost, self.objective_bounds) - return float(np.mean(costs)) + costs = normalize_costs(cost, self._objective_bounds) - assert type(cost) == float + # After normalization, we get the weighted average + return self.multi_objective_algorithm(costs) + + assert isinstance(cost, float) return float(cost) def get_min_cost(self, config: Configuration) -> float: - """Returns the lowest empirical cost for a configuration, across all runs (budgets) + """Returns the lowest empirical cost for a configuration across all trials. - See the class docstring for how the costs are computed. The costs are not re-computed, + See the class docstring for how the costs are computed. The costs are not re-computed but are read from cache. Parameters ---------- - config: Configuration + config : Configuration Returns ------- min_cost: float Computed cost for configuration """ - config_id = self.config_ids.get(config) - cost = self._min_cost_per_config.get(config_id, np.nan) # type: ignore[arg-type] # noqa F821 + config_id = self._config_ids.get(config) + cost = self._min_cost_per_config.get(config_id, np.nan) # type: ignore - if self.num_obj > 1: + if self._n_objectives > 1: assert type(cost) == list - costs = normalize_costs(cost, self.objective_bounds) + assert self.multi_objective_algorithm is not None + + costs = normalize_costs(cost, self._objective_bounds) # Note: We have to mean here because we already got the min cost - return float(np.mean(costs)) + return self.multi_objective_algorithm(costs) assert type(cost) == float return float(cost) @@ -589,7 +367,7 @@ def get_min_cost(self, config: Configuration) -> float: def average_cost( self, config: Configuration, - instance_seed_budget_keys: Optional[Iterable[InstSeedBudgetKey]] = None, + instance_seed_budget_keys: Iterable[InstanceSeedBudgetKey] | None = None, normalize: bool = False, ) -> float | list[float]: """Return the average cost of a configuration. This is the mean of costs of all instance- @@ -600,12 +378,12 @@ def average_cost( config : Configuration Configuration to calculate objective for. instance_seed_budget_keys : list, optional (default=None) - List of tuples of instance-seeds-budget keys. If None, the run_history is - queried for all runs of the given configuration. + List of tuples of instance-seeds-budget keys. If None, the runhistory is + queried for all trials of the given configuration. normalize : bool, optional (default=False) Normalizes the costs wrt objective bounds in the multi-objective setting. Only a float is returned if normalize is True. Warning: The value can change - over time because the objective bounds are changing. + over time because the objective bounds are changing. Also, the objective weights are incorporated. Returns ------- @@ -614,14 +392,16 @@ def average_cost( """ costs = self._cost(config, instance_seed_budget_keys) if costs: - if self.num_obj > 1: + if self._n_objectives > 1: # Each objective is averaged separately # [[100, 200], [0, 0]] -> [50, 100] averaged_costs = np.mean(costs, axis=0).tolist() if normalize: - normalized_costs = normalize_costs(averaged_costs, self.objective_bounds) - return float(np.mean(normalized_costs)) + assert self.multi_objective_algorithm is not None + normalized_costs = normalize_costs(averaged_costs, self._objective_bounds) + + return self.multi_objective_algorithm(normalized_costs) else: return averaged_costs @@ -632,7 +412,7 @@ def average_cost( def sum_cost( self, config: Configuration, - instance_seed_budget_keys: Optional[Iterable[InstSeedBudgetKey]] = None, + instance_seed_budget_keys: Iterable[InstanceSeedBudgetKey] | None = None, normalize: bool = False, ) -> float | list[float]: """Return the sum of costs of a configuration. This is the sum of costs of all instance-seed @@ -643,12 +423,12 @@ def sum_cost( config : Configuration Configuration to calculate objective for. instance_seed_budget_keys : list, optional (default=None) - List of tuples of instance-seeds-budget keys. If None, the run_history is - queried for all runs of the given configuration. + List of tuples of instance-seeds-budget keys. If None, the runhistory is + queried for all trials of the given configuration. normalize : bool, optional (default=False) Normalizes the costs wrt objective bounds in the multi-objective setting. Only a float is returned if normalize is True. Warning: The value can change - over time because the objective bounds are changing. + over time because the objective bounds are changing. Also, the objective weights are incorporated. Returns ------- @@ -658,14 +438,16 @@ def sum_cost( """ costs = self._cost(config, instance_seed_budget_keys) if costs: - if self.num_obj > 1: + if self._n_objectives > 1: # Each objective is summed separately # [[100, 200], [20, 10]] -> [120, 210] summed_costs = np.sum(costs, axis=0).tolist() if normalize: - normalized_costs = normalize_costs(summed_costs, self.objective_bounds) - return float(np.mean(normalized_costs)) + assert self.multi_objective_algorithm is not None + normalized_costs = normalize_costs(summed_costs, self._objective_bounds) + + return self.multi_objective_algorithm(normalized_costs) else: return summed_costs @@ -674,12 +456,10 @@ def sum_cost( def min_cost( self, config: Configuration, - instance_seed_budget_keys: Optional[Iterable[InstSeedBudgetKey]] = None, + instance_seed_budget_keys: Iterable[InstanceSeedBudgetKey] | None = None, normalize: bool = False, ) -> float | list[float]: - """Return the minimum cost of a configuration. - - This is the minimum cost of all instance-seed pairs. + """Return the minimum cost of a configuration. This is the minimum cost of all instance-seed pairs. Warning ------- @@ -690,8 +470,12 @@ def min_cost( config : Configuration Configuration to calculate objective for. instance_seed_budget_keys : list, optional (default=None) - List of tuples of instance-seeds-budget keys. If None, the run_history is - queried for all runs of the given configuration. + List of tuples of instance-seeds-budget keys. If None, the runhistory is + queried for all trials of the given configuration. + normalize : bool, optional (default=False) + Normalizes the costs wrt objective bounds in the multi-objective setting. + Only a float is returned if normalize is True. Warning: The value can change + over time because the objective bounds are changing. Also, the objective weights are incorporated. Returns ------- @@ -701,14 +485,16 @@ def min_cost( """ costs = self._cost(config, instance_seed_budget_keys) if costs: - if self.num_obj > 1: + if self._n_objectives > 1: # Each objective is viewed separately # [[100, 200], [20, 500]] -> [20, 200] min_costs = np.min(costs, axis=0).tolist() if normalize: - normalized_costs = normalize_costs(min_costs, self.objective_bounds) - return float(np.mean(normalized_costs)) + assert self.multi_objective_algorithm is not None + normalized_costs = normalize_costs(min_costs, self._objective_bounds) + + return self.multi_objective_algorithm(normalized_costs) else: return min_costs @@ -716,161 +502,187 @@ def min_cost( return np.nan - def compute_all_costs(self, instances: Optional[List[str]] = None) -> None: - """Computes the cost of all configurations from scratch and overwrites self.cost_perf_config - and self.runs_per_config accordingly. - - Note - ---- - This method is only used for ``merge_foreign_data`` and should be removed. - - Parameters - ---------- - instances: List[str] - List of instances; if given, cost is only computed wrt to this instance set. - """ - self._cost_per_config = {} - self.num_runs_per_config = {} - for config, config_id in self.config_ids.items(): - # removing duplicates while keeping the order - inst_seed_budgets = list(dict.fromkeys(self.get_runs_for_config(config, only_max_observed_budget=True))) - if instances is not None: - inst_seed_budgets = list(filter(lambda x: x.instance in cast(List, instances), inst_seed_budgets)) - - if inst_seed_budgets: # can be empty if never saw any runs on - self._cost_per_config[config_id] = self.average_cost(config, inst_seed_budgets) - self._min_cost_per_config[config_id] = self.min_cost(config, inst_seed_budgets) - self.num_runs_per_config[config_id] = len(inst_seed_budgets) - - def get_instance_costs_for_config(self, config: Configuration) -> Dict[str, List[float]]: - """Returns the average cost per instance (across seeds) for a configuration. If the - runhistory contains budgets, only the highest budget for a configuration is returned. - - Note - ---- - This is used by the pSMAC facade to determine the incumbent after the evaluation. - - Parameters - ---------- - config : Configuration from ConfigSpace - Parameter configuration - - Returns - ------- - cost_per_inst: Dict, cost> - """ - runs_ = self.get_runs_for_config(config, only_max_observed_budget=True) - cost_per_inst = {} # type: Dict[str, List[float]] - for inst, seed, budget in runs_: - cost_per_inst[inst] = cost_per_inst.get(inst, []) - rkey = RunKey(self.config_ids[config], inst, seed, budget) - vkey = self.data[rkey] - cost_per_inst[inst].append(vkey.cost) - cost_per_inst = dict([(inst, np.mean(costs)) for inst, costs in cost_per_inst.items()]) - return cost_per_inst - - def get_runs_for_config(self, config: Configuration, only_max_observed_budget: bool) -> List[InstSeedBudgetKey]: - """Return all runs (instance seed pairs) for a configuration. - - Note - ---- - This method ignores capped runs. + def get_trials( + self, + config: Configuration, + only_max_observed_budget: bool = True, + ) -> list[InstanceSeedBudgetKey]: + """Return all trials (instance seed budget key in this case) for a configuration. Parameters ---------- - config : Configuration from ConfigSpace + config : Configuration Parameter configuration only_max_observed_budget : bool Select only the maximally observed budget run for this configuration + Returns ------- - instance_seed_budget_pairs : list + trials : list[InstanceSeedBudgetKey] """ - config_id = self.config_ids.get(config) - runs = self._configid_to_inst_seed_budget.get(config_id, {}).copy() # type: ignore[arg-type] # noqa F821 + config_id = self._config_ids.get(config) + trials = {} + if config_id in self._config_id_to_isk_to_budget: + trials = self._config_id_to_isk_to_budget[config_id].copy() - # select only the max budget run if specified + # Select only the max budget run if specified if only_max_observed_budget: - for k, v in runs.items(): - runs[k] = [max(v)] + for k, v in trials.items(): + if None in v: + trials[k] = [None] + else: + trials[k] = [max([v_ for v_ in v if v_ is not None])] - # convert to inst-seed-budget key - rval = [InstSeedBudgetKey(k.instance, k.seed, budget) for k, v in runs.items() for budget in v] - return rval + # Convert to instance-seed-budget key + return [InstanceSeedBudgetKey(k.instance, k.seed, budget) for k, v in trials.items() for budget in v] - def get_all_configs(self) -> List[Configuration]: + def get_config(self, config_id: int) -> Configuration: + """Returns the configuration from the configuration id.""" + return self._ids_config[config_id] + + def get_configs(self) -> list[Configuration]: """Return all configurations in this RunHistory object. Returns ------- - parameter configurations: list + parameter configurations: list """ - return list(self.config_ids.keys()) + return list(self._config_ids.keys()) - def get_all_configs_per_budget( + def get_configs_per_budget( self, - budget_subset: Optional[List] = None, - ) -> List[Configuration]: + budget_subset: list | None = None, + ) -> list[Configuration]: """Return all configs in this RunHistory object that have been run on one of these budgets. Parameters ---------- - budget_subset: list + budget_subset: list Returns ------- - parameter configurations: list + parameter configurations: list """ if budget_subset is None: - return self.get_all_configs() + return self.get_configs() + configs = [] - for c, i, s, b in self.data.keys(): - if b in budget_subset: - configs.append(self.ids_config[c]) + for key in self._data.keys(): + if key.budget in budget_subset: + configs.append(self._ids_config[key.config_id]) + return configs - def save_json(self, fn: str = "runhistory.json", save_external: bool = False) -> None: + def get_incumbent(self) -> tuple[Configuration | None, float | list[float]]: + """Returns the incumbent configuration. The config with the lowest cost calculated by `get_cost` is returned. + + Warning + ------- + The incumbent in a multi-objective setting depends on the multi-objective algorithm. + If you use ParEGO, for example, you get a random incumbent on the Pareto front based on the current + ParEGO weights. + """ + incumbent = None + lowest_cost = np.inf + for config in self._config_ids.keys(): + cost = self.get_cost(config) + if cost < lowest_cost: + incumbent = config + lowest_cost = cost + + return incumbent, lowest_cost + + def get_pareto_front(self) -> tuple[list[Configuration], list[list[float]]]: + """Returns the Pareto front of the runhistory. + + Returns + ------- + configs : list[Configuration] + The configs of the Pareto front. + costs : list[list[float]] + The costs from the configs of the Pareto front. + """ + if self._n_objectives == 1: + raise ValueError("Pareto front is only defined for multi-objective settings.") + + # Get costs from runhistory first + average_costs = [] + configs = self.get_configs() + for config in configs: + # Since we use multiple seeds, we have to average them to get only one cost value pair for each + # configuration + # Luckily, SMAC already does this for us + average_cost = self.average_cost(config) + average_costs += [average_cost] + + # Let's work with a numpy array + costs = np.vstack(average_costs) + + is_efficient = np.arange(costs.shape[0]) + next_point_index = 0 # Next index in the is_efficient array to search for + while next_point_index < len(costs): + nondominated_point_mask = np.any(costs < costs[next_point_index], axis=1) + nondominated_point_mask[next_point_index] = True + is_efficient = is_efficient[nondominated_point_mask] # Remove dominated points + costs = costs[nondominated_point_mask] + next_point_index = np.sum(nondominated_point_mask[:next_point_index]) + 1 + + return [configs[i] for i in is_efficient], [average_costs[i] for i in is_efficient] + + def save_json(self, filename: str = "runhistory.json", save_external: bool = False) -> None: """Saves runhistory on disk. Parameters ---------- - fn : str + filename : str file name. save_external : bool Whether to save external data in the runhistory file. """ - data = [ - ( - [ - int(k.config_id), - str(k.instance_id) if k.instance_id is not None else None, - int(k.seed), - float(k.budget) if k[3] is not None else 0, - ], - [v.cost, v.time, v.status, v.starttime, v.endtime, v.additional_info], - ) - for k, v in self.data.items() - if save_external or self.external[k] == DataOrigin.INTERNAL - ] - config_ids_to_serialize = set([entry[0][0] for entry in data]) - configs = { - id_: conf.get_dictionary() for id_, conf in self.ids_config.items() if id_ in config_ids_to_serialize - } - config_origins = { - id_: conf.origin - for id_, conf in self.ids_config.items() - if (id_ in config_ids_to_serialize and conf.origin is not None) - } - - with open(fn, "w") as fp: + data = [] + for k, v in self._data.items(): + if save_external or self._external[k] == DataOrigin.INTERNAL: + data += [ + ( + int(k.config_id), + str(k.instance) if k.instance is not None else None, + int(k.seed) if k.seed is not None else None, + float(k.budget) if k.budget is not None else None, + v.cost, + v.time, + v.status, + v.starttime, + v.endtime, + v.additional_info, + ) + ] + + config_ids_to_serialize = set([entry[0] for entry in data]) + configs = {} + config_origins = {} + for id_, config in self._ids_config.items(): + if id_ in config_ids_to_serialize: + configs[id_] = config.get_dictionary() + + config_origins[id_] = config.origin + + # Some sanity-checks + assert filename.endswith(".json") + path = Path(filename) + path.parent.mkdir(parents=True, exist_ok=True) + + with open(filename, "w") as fp: json.dump( - {"data": data, "config_origins": config_origins, "configs": configs}, + { + "data": data, + "configs": configs, + "config_origins": config_origins, + }, fp, - cls=EnumEncoder, indent=2, ) - def load_json(self, fn: str, cs: ConfigurationSpace) -> None: + def load_json(self, filename: str, configspace: ConfigurationSpace) -> None: """Load and runhistory in json representation from disk. Warning @@ -879,114 +691,281 @@ def load_json(self, fn: str, cs: ConfigurationSpace) -> None: Parameters ---------- - fn : str + filename : str file name to load from - cs : ConfigSpace + configspace : ConfigSpace instance of configuration space """ try: - with open(fn) as fp: - all_data = json.load(fp, object_hook=StatusType.enum_hook) + with open(filename) as fp: + all_data = json.load(fp) except Exception as e: - self.logger.warning( - "Encountered exception %s while reading runhistory from %s. " "Not adding any runs!", - e, - fn, + logger.warning( + f"Encountered exception {e} while reading runhistory from {filename}. Not adding any trials!" ) return config_origins = all_data.get("config_origins", {}) - self.ids_config = { - int(id_): Configuration(cs, values=values, origin=config_origins.get(id_, None)) - for id_, values in all_data["configs"].items() - } + self._ids_config = {} + for id_, values in all_data["configs"].items(): + self._ids_config[int(id_)] = Configuration( + configspace, + values=values, + origin=config_origins.get(id_, None), + ) - self.config_ids = {config: id_ for id_, config in self.ids_config.items()} - self._n_id = len(self.config_ids) + self._config_ids = {config: id_ for id_, config in self._ids_config.items()} + self._n_id = len(self._config_ids) - # important to use add method to use all data structure correctly - for k, v in all_data["data"]: - # Set num_obj first - if self.num_obj == -1: - if isinstance(v[0], float) or isinstance(v[0], int): - self.num_obj = 1 + # Important to use add method to use all data structure correctly + for entry in all_data["data"]: + # Set n_objectives first + if self._n_objectives == -1: + if isinstance(entry[4], float) or isinstance(entry[4], int): + self._n_objectives = 1 else: - self.num_obj = len(np.asarray(list(map(float, v[0])))) + self._n_objectives = len(entry[4]) - if self.num_obj == 1: - cost: Union[np.ndarray, float] = float(v[0]) + cost: list[float] | float + if self._n_objectives == 1: + cost = float(entry[4]) else: - cost: Union[np.ndarray, float] = np.asarray(list(map(float, v[0]))) # type: ignore[no-redef] + cost = [float(x) for x in entry[4]] self.add( - config=self.ids_config[int(k[0])], + config=self._ids_config[int(entry[0])], cost=cost, - time=float(v[1]), - status=StatusType(v[2]), - instance_id=k[1], - seed=int(k[2]), - budget=float(k[3]) if len(k) == 4 else 0, - starttime=v[3], - endtime=v[4], - additional_info=v[5], + time=float(entry[5]), + status=StatusType(entry[6]), + instance=entry[1], + seed=entry[2], + budget=entry[3], + starttime=entry[7], + endtime=entry[8], + additional_info=entry[9], ) def update_from_json( self, - fn: str, - cs: ConfigurationSpace, + filename: str, + configspace: ConfigurationSpace, origin: DataOrigin = DataOrigin.EXTERNAL_SAME_INSTANCES, ) -> None: - """Updates the current runhistory by adding new runs from a json file. + """Updates the current runhistory by adding new trials from a json file. Parameters ---------- - fn : str + filename : str File name to load from. - cs : ConfigSpace - Instance of configuration space. - origin : DataOrigin + configspace : ConfigurationSpace + origin : DataOrigin, defaults to DataOrigin.EXTERNAL_SAME_INSTANCES What to store as data origin. """ new_runhistory = RunHistory() - new_runhistory.load_json(fn, cs) + new_runhistory.load_json(filename, configspace) self.update(runhistory=new_runhistory, origin=origin) def update( self, - runhistory: "RunHistory", + runhistory: RunHistory, origin: DataOrigin = DataOrigin.EXTERNAL_SAME_INSTANCES, ) -> None: - """Updates the current runhistory by adding new runs from a RunHistory. + """Updates the current runhistory by adding new trials from a RunHistory. Parameters ---------- - runhistory: RunHistory + runhistory : RunHistory Runhistory with additional data to be added to self - origin: DataOrigin + origin : DataOrigin, defaults to DataOrigin.EXTERNAL_SAME_INSTANCES If set to ``INTERNAL`` or ``EXTERNAL_FULL`` the data will be - added to the internal data structure self._configid_to_inst_seed_budget - and be available :meth:`through get_runs_for_config`. + added to the internal data structure self._config_id_to_inst_seed_budget + and be available :meth:`through get_trials`. """ # Configurations might be already known, but by a different ID. This # does not matter here because the add() method handles this - # correctly by assigning an ID to unknown configurations and re-using - # the ID - for key, value in runhistory.data.items(): - config_id, instance_id, seed, budget = key - cost, time, status, start, end, additional_info = value - config = runhistory.ids_config[config_id] + # correctly by assigning an ID to unknown configurations and re-using the ID. + for key, value in runhistory.items(): + config = runhistory._ids_config[key.config_id] self.add( config=config, - cost=cost, - time=time, - status=status, - instance_id=instance_id, - starttime=start, - endtime=end, - seed=seed, - budget=budget, - additional_info=additional_info, + cost=value.cost, + time=value.time, + status=value.status, + instance=key.instance, + starttime=value.starttime, + endtime=value.endtime, + seed=key.seed, + budget=key.budget, + additional_info=value.additional_info, origin=origin, ) + + def update_costs(self, instances: list[str] | None = None) -> None: + """Computes the cost of all configurations from scratch and overwrites `self._cost_per_config` + and `self._num_trials_per_config` accordingly. + + Parameters + ---------- + instances: list[str] | None, defaults to none + List of instances; if given, cost is only computed wrt to this instance set. + """ + self._cost_per_config = {} + self._num_trials_per_config = {} + for config, config_id in self._config_ids.items(): + # Removing duplicates while keeping the order + inst_seed_budgets = list(dict.fromkeys(self.get_trials(config, only_max_observed_budget=True))) + if instances is not None: + inst_seed_budgets = list(filter(lambda x: x.instance in cast(list, instances), inst_seed_budgets)) + + if inst_seed_budgets: # can be empty if never saw any trials on instances + self._cost_per_config[config_id] = self.average_cost(config, inst_seed_budgets) + self._min_cost_per_config[config_id] = self.min_cost(config, inst_seed_budgets) + self._num_trials_per_config[config_id] = len(inst_seed_budgets) + + def _check_json_serializable( + self, + key: str, + obj: Any, + trial_key: TrialKey, + trial_value: TrialValue, + ) -> None: + try: + json.dumps(obj) + except Exception as e: + raise ValueError( + "Cannot add %s: %s of type %s to runhistory because it raises an error during JSON encoding, " + "please see the error above.\ntrial_key: %s\ntrial_value %s" + % (key, str(obj), type(obj), trial_key, trial_value) + ) from e + + def _update_objective_bounds(self) -> None: + """Update the objective bounds based on the data in the runhistory.""" + all_costs = [] + for run_value in self._data.values(): + costs = run_value.cost + if run_value.status == StatusType.SUCCESS: + if not isinstance(costs, Iterable): + costs = [costs] + + assert len(costs) == self._n_objectives + all_costs.append(costs) + + all_costs = np.array(all_costs, dtype=float) # type: ignore[assignment] + + if len(all_costs) == 0: + self._objective_bounds = [(np.inf, -np.inf)] * self._n_objectives + return + + min_values = np.min(all_costs, axis=0) + max_values = np.max(all_costs, axis=0) + + self._objective_bounds = [] + for min_v, max_v in zip(min_values, max_values): + self._objective_bounds += [(min_v, max_v)] + + def _add(self, k: TrialKey, v: TrialValue, status: StatusType, origin: DataOrigin) -> None: + """ + Actual function to add new entry to data structures. + + Note + ---- + This method always calls `update_cost` in the multi-objective setting. + """ + self._data[k] = v + self._external[k] = origin + + # Update objective bounds based on raw data + self._update_objective_bounds() + + # Do not register the cost until the run has completed + if ( + origin + in ( + DataOrigin.INTERNAL, + DataOrigin.EXTERNAL_SAME_INSTANCES, + ) + and status != StatusType.RUNNING + ): + # Also add to fast data structure + isk = InstanceSeedKey(k.instance, k.seed) + self._config_id_to_isk_to_budget[k.config_id] = self._config_id_to_isk_to_budget.get(k.config_id, {}) + + # We sanity-check whether we don't mix none and str in the instances + for isk_ in self._config_id_to_isk_to_budget[k.config_id].keys(): + if isinstance(isk_, str) != isinstance(isk, str): + raise ValueError( + "Can not mix instances of different types. " + f"Wants to add {isk_.instance} but found already {isk.instance}." + ) + + if isk not in self._config_id_to_isk_to_budget[k.config_id]: + # Add new inst-seed-key with budget to main dict + self._config_id_to_isk_to_budget[k.config_id][isk] = [k.budget] + # Before it was k.budget not in isk + elif k.budget != isk.instance and k.budget != isk.seed: + # We have to make sure that we don't mix none and float budgets + if isinstance(self._config_id_to_isk_to_budget[k.config_id][isk][0], float) != isinstance( + k.budget, float + ): + raise ValueError( + "Can not mix budgets of different types for the same instance-seed pair. " + f"Wants to add {k.budget} but found already " + f"{self._config_id_to_isk_to_budget[k.config_id][isk][0]}." + ) + + # Append new budget to existing inst-seed-key dict + self._config_id_to_isk_to_budget[k.config_id][isk].append(k.budget) + + # If budget is used, then update cost instead of incremental updates + if not self._overwrite_existing_trials and k.budget == 0: + logger.debug(f"Incremental update cost for config {k.config_id}") + # Assumes an average across trials as cost function aggregation, this is used for + # algorithm configuration (incremental updates are used to save time as getting the + # cost for > 100 instances is high) + self.incremental_update_cost(self._ids_config[k.config_id], v.cost) + else: + # This happens when budget > 0 (only successive halving and hyperband so far) + logger.debug(f"Update cost for config {k.config_id}.") + self.update_cost(config=self._ids_config[k.config_id]) + + def _cost( + self, + config: Configuration, + instance_seed_budget_keys: Iterable[InstanceSeedBudgetKey] | None = None, + ) -> list[float | list[float]]: + """Returns a list of all costs for the given config for further calculations. + The costs are directly taken from the runhistory data. + + Parameters + ---------- + config : Configuration + Configuration to calculate objective for. + instance_seed_budget_keys : list, defaults to None + List of tuples of instance-seeds-budget keys. If None, the runhistory is + queried for all trials of the given configuration. + + Returns + ------- + costs: list[list[float] | list[list[float]]] + List of all found costs. In case of multi-objective, the list contains lists. + """ + try: + id_ = self._config_ids[config] + except KeyError: # Challenger was not running so far + return [] + + if instance_seed_budget_keys is None: + instance_seed_budget_keys = self.get_trials(config, only_max_observed_budget=True) + + costs = [] + for key in instance_seed_budget_keys: + k = TrialKey( + config_id=id_, + instance=key.instance, + seed=key.seed, + budget=key.budget, + ) + costs.append(self._data[k].cost) + + return costs diff --git a/smac/runhistory/runhistory2epm.py b/smac/runhistory/runhistory2epm.py deleted file mode 100644 index 2bf85d979..000000000 --- a/smac/runhistory/runhistory2epm.py +++ /dev/null @@ -1,729 +0,0 @@ -import abc -from typing import Dict, List, Mapping, Optional, Tuple - -import logging - -import numpy as np - -from smac.configspace import convert_configurations_to_array -from smac.epm.base_imputor import BaseImputor -from smac.multi_objective.aggregation_strategy import AggregationStrategy -from smac.multi_objective.utils import normalize_costs -from smac.runhistory.runhistory import RunHistory, RunKey, RunValue -from smac.scenario.scenario import Scenario -from smac.tae import StatusType -from smac.utils import constants - -__author__ = "Katharina Eggensperger" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Katharina Eggensperger" -__email__ = "eggenspk@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -class AbstractRunHistory2EPM(object): - __metaclass__ = abc.ABCMeta - - """Abstract class for preprocessing data in order to train an EPM. - - Parameters - ---------- - scenario: Scenario Object - Algorithm Configuration Scenario - num_params : int - number of parameters in config space - success_states: list, optional - List of states considered as successful (such as StatusType.SUCCESS). - If None, raise TypeError. - impute_censored_data: bool, optional - Should we impute data? - consider_for_higher_budgets_state: list, optional - Additionally consider all runs with these states for budget < current budget - imputor: epm.base_imputor Instance - Object to impute censored data - impute_state: list, optional - List of states that mark censored data (such as StatusType.TIMEOUT) - in combination with runtime < cutoff_time - If None, set to empty list []. - If None and impute_censored_data is True, raise TypeError. - scale_perc: int - scaled y-transformation use a percentile to estimate distance to optimum; - only used by some subclasses of AbstractRunHistory2EPM - rng : numpy.random.RandomState - Only used for reshuffling data after imputation. - If None, use np.random.RandomState(seed=1). - multi_objective_algorithm: Optional[MultiObjectiveAlgorithm] - Instance performing multi-objective optimization. Receives an objective cost vector as input - and returns a scalar. Is executed before transforming runhistory values. - - Attributes - ---------- - logger - scenario - rng - num_params - - success_states - impute_censored_data - impute_state - cutoff_time - imputor - instance_features - n_feats - num_params - """ - - def __init__( - self, - scenario: Scenario, - num_params: int, - success_states: List[StatusType], - impute_censored_data: bool = False, - impute_state: Optional[List[StatusType]] = None, - consider_for_higher_budgets_state: Optional[List[StatusType]] = None, - imputor: Optional[BaseImputor] = None, - scale_perc: int = 5, - rng: Optional[np.random.RandomState] = None, - multi_objective_algorithm: Optional[AggregationStrategy] = None, - ) -> None: - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - # General arguments - self.scenario = scenario - self.rng = rng - self.num_params = num_params - - self.scale_perc = scale_perc - self.num_obj = 1 # type: int - if scenario.multi_objectives is not None: # type: ignore[attr-defined] # noqa F821 - self.num_obj = len(scenario.multi_objectives) # type: ignore[attr-defined] # noqa F821 - - # Configuration - self.impute_censored_data = impute_censored_data - self.cutoff_time = self.scenario.cutoff # type: ignore[attr-defined] # noqa F821 - self.imputor = imputor - if self.num_obj > 1: - self.multi_objective_algorithm = multi_objective_algorithm - else: - self.multi_objective_algorithm = None - - # Fill with some default values - if rng is None: - self.rng = np.random.RandomState(seed=1) - - if impute_state is None and impute_censored_data: - raise TypeError("impute_state not given") - - if impute_state is None: - # please mypy - self.impute_state = [] # type: List[StatusType] - else: - self.impute_state = impute_state - - if consider_for_higher_budgets_state is None: - # please mypy - self.consider_for_higher_budgets_state = [] # type: List[StatusType] - else: - self.consider_for_higher_budgets_state = consider_for_higher_budgets_state - - if success_states is None: - raise TypeError("success_states not given") - - self.success_states = success_states - - self.instance_features = scenario.feature_dict - self.n_feats = scenario.n_features - - self.num_params = num_params - - # Sanity checks - if impute_censored_data and scenario.run_obj != "runtime": - # So far we don't know how to handle censored quality data - self.logger.critical("Cannot impute censored data when not " "optimizing runtime") - raise NotImplementedError("Cannot impute censored data when not " "optimizing runtime") - - # Check imputor stuff - if impute_censored_data and self.imputor is None: - self.logger.critical("You want me to impute censored data, but " "I don't know how. Imputor is None") - raise ValueError("impute_censored data, but no imputor given") - elif impute_censored_data and not isinstance(self.imputor, BaseImputor): - raise ValueError( - "Given imputor is not an instance of " "smac.epm.base_imputor.BaseImputor, but %s" % type(self.imputor) - ) - - # Learned statistics - self.min_y = np.array([np.NaN] * self.num_obj) - self.max_y = np.array([np.NaN] * self.num_obj) - self.perc = np.array([np.NaN] * self.num_obj) - - @abc.abstractmethod - def _build_matrix( - self, - run_dict: Mapping[RunKey, RunValue], - runhistory: RunHistory, - return_time_as_y: bool = False, - store_statistics: bool = False, - ) -> Tuple[np.ndarray, np.ndarray]: - """Builds x,y matrixes from selected runs from runhistory. - - Parameters - ---------- - run_dict: dict(RunKey -> RunValue) - dictionary from RunHistory.RunKey to RunHistory.RunValue - runhistory: RunHistory - runhistory object - return_time_as_y: bool - Return the time instead of cost as y value. Necessary to access the raw y values for imputation. - store_statistics: bool - Whether to store statistics about the data (to be used at subsequent calls) - - Returns - ------- - X: np.ndarray - Y: np.ndarray - """ - raise NotImplementedError() - - def _get_s_run_dict( - self, - runhistory: RunHistory, - budget_subset: Optional[List] = None, - ) -> Dict[RunKey, RunValue]: - # Get only successfully finished runs - if budget_subset is not None: - if len(budget_subset) != 1: - raise ValueError("Cannot yet handle getting runs from multiple budgets") - s_run_dict = { - run: runhistory.data[run] - for run in runhistory.data.keys() - if run.budget in budget_subset and runhistory.data[run].status in self.success_states - } - # Additionally add these states from lower budgets - add = { - run: runhistory.data[run] - for run in runhistory.data.keys() - if runhistory.data[run].status in self.consider_for_higher_budgets_state - and run.budget < budget_subset[0] - } - s_run_dict.update(add) - else: - s_run_dict = { - run: runhistory.data[run] - for run in runhistory.data.keys() - if runhistory.data[run].status in self.success_states - } - return s_run_dict - - def _get_t_run_dict( - self, - runhistory: RunHistory, - budget_subset: Optional[List] = None, - ) -> Dict[RunKey, RunValue]: - if budget_subset is not None: - t_run_dict = { - run: runhistory.data[run] - for run in runhistory.data.keys() - if runhistory.data[run].status == StatusType.TIMEOUT - and runhistory.data[run].time >= self.cutoff_time - and run.budget in budget_subset - } - else: - t_run_dict = { - run: runhistory.data[run] - for run in runhistory.data.keys() - if runhistory.data[run].status == StatusType.TIMEOUT and runhistory.data[run].time >= self.cutoff_time - } - return t_run_dict - - def get_configurations( - self, - runhistory: RunHistory, - budget_subset: Optional[List] = None, - ) -> np.ndarray: - """Returns vector representation of only the configurations. Instance features are not - appended and cost values are not taken into account. - - Parameters - ---------- - runhistory : smac.runhistory.runhistory.RunHistory - Runhistory containing all evaluated configurations/instances - budget_subset : list of budgets to consider - - Returns - ------- - numpy.ndarray - """ - s_runs = self._get_s_run_dict(runhistory, budget_subset) - s_config_ids = set(s_run.config_id for s_run in s_runs) - t_runs = self._get_t_run_dict(runhistory, budget_subset) - t_config_ids = set(t_run.config_id for t_run in t_runs) - config_ids = s_config_ids | t_config_ids - configurations = [runhistory.ids_config[config_id] for config_id in config_ids] - configs_array = convert_configurations_to_array(configurations) - return configs_array - - def transform( - self, - runhistory: RunHistory, - budget_subset: Optional[List] = None, - ) -> Tuple[np.ndarray, np.ndarray]: - """Returns vector representation of runhistory; if imputation is disabled, censored (TIMEOUT - with time < cutoff) will be skipped. - - Parameters - ---------- - runhistory : smac.runhistory.runhistory.RunHistory - Runhistory containing all evaluated configurations/instances - budget_subset : list of budgets to consider - - Returns - ------- - X: numpy.ndarray - configuration vector x instance features - Y: numpy.ndarray - cost values - """ - self.logger.debug("Transform runhistory into X,y format") - - s_run_dict = self._get_s_run_dict(runhistory, budget_subset) - X, Y = self._build_matrix(run_dict=s_run_dict, runhistory=runhistory, store_statistics=True) - - # Get real TIMEOUT runs - t_run_dict = self._get_t_run_dict(runhistory, budget_subset) - # use penalization (e.g. PAR10) for EPM training - store_statistics = True if np.any(np.isnan(self.min_y)) else False - tX, tY = self._build_matrix( - run_dict=t_run_dict, - runhistory=runhistory, - store_statistics=store_statistics, - ) - - # if we don't have successful runs, - # we have to return all timeout runs - if not s_run_dict: - return tX, tY - - if self.impute_censored_data: - # Get all censored runs - if budget_subset is not None: - c_run_dict = { - run: runhistory.data[run] - for run in runhistory.data.keys() - if runhistory.data[run].status in self.impute_state - and runhistory.data[run].time < self.cutoff_time - and run.budget in budget_subset - } - else: - c_run_dict = { - run: runhistory.data[run] - for run in runhistory.data.keys() - if runhistory.data[run].status in self.impute_state and runhistory.data[run].time < self.cutoff_time - } - - if len(c_run_dict) == 0: - self.logger.debug("No censored data found, skip imputation") - # If we do not impute, we also return TIMEOUT data - X = np.vstack((X, tX)) - Y = np.concatenate((Y, tY)) - else: - - # better empirical results by using PAR1 instead of PAR10 - # for censored data imputation - cen_X, cen_Y = self._build_matrix( - run_dict=c_run_dict, - runhistory=runhistory, - return_time_as_y=True, - store_statistics=False, - ) - - # Also impute TIMEOUTS - tX, tY = self._build_matrix( - run_dict=t_run_dict, - runhistory=runhistory, - return_time_as_y=True, - store_statistics=False, - ) - self.logger.debug("%d TIMEOUTS, %d CAPPED, %d SUCC" % (tX.shape[0], cen_X.shape[0], X.shape[0])) - cen_X = np.vstack((cen_X, tX)) - cen_Y = np.concatenate((cen_Y, tY)) - - # return imp_Y in PAR depending on the used threshold in imputor - assert isinstance(self.imputor, BaseImputor) # please mypy - imp_Y = self.imputor.impute(censored_X=cen_X, censored_y=cen_Y, uncensored_X=X, uncensored_y=Y) - - # Shuffle data to mix censored and imputed data - X = np.vstack((X, cen_X)) - Y = np.concatenate((Y, imp_Y)) # type: ignore - else: - # If we do not impute, we also return TIMEOUT data - X = np.vstack((X, tX)) - Y = np.concatenate((Y, tY)) - - self.logger.debug("Converted %d observations" % (X.shape[0])) - return X, Y - - @abc.abstractmethod - def transform_response_values( - self, - values: np.ndarray, - ) -> np.ndarray: - """Transform function response values. - - Parameters - ---------- - values : np.ndarray - Response values to be transformed. - - Returns - ------- - np.ndarray - """ - raise NotImplementedError - - def get_X_y(self, runhistory: RunHistory) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: - """ - Simple interface to obtain all data in runhistory in X, y format. - Note: This function should not be used as it does not consider all available StatusTypes. - - Parameters - ---------- - runhistory : smac.runhistory.runhistory.RunHistory - runhistory of all evaluated configurations x instances - - Returns - ------- - X: numpy.ndarray - matrix of all configurations (+ instance features) - y: numpy.ndarray - vector of cost values; can include censored runs - cen: numpy.ndarray - vector of bools indicating whether the y-value is censored - """ - self.logger.warning("This function is not tested and might not work as expected!") - X = [] - y = [] - cen = [] - feature_dict = self.scenario.feature_dict - params = self.scenario.cs.get_hyperparameters() # type: ignore[attr-defined] # noqa F821 - for k, v in runhistory.data.items(): - config = runhistory.ids_config[k.config_id] - x = [config.get(p.name) for p in params] - features = feature_dict.get(k.instance_id) - if features: - x.extend(features) - X.append(x) - y.append(v.cost) - cen.append(v.status != StatusType.SUCCESS) - - return np.array(X), np.array(y), np.array(cen) - - -class RunHistory2EPM4Cost(AbstractRunHistory2EPM): - """TODO.""" - - def _build_matrix( - self, - run_dict: Mapping[RunKey, RunValue], - runhistory: RunHistory, - return_time_as_y: bool = False, - store_statistics: bool = False, - ) -> Tuple[np.ndarray, np.ndarray]: - """Builds X,y matrixes from selected runs from runhistory. - - Parameters - ---------- - run_dict: dict: RunKey -> RunValue - dictionary from RunHistory.RunKey to RunHistory.RunValue - runhistory: RunHistory - runhistory object - return_time_as_y: bool - Return the time instead of cost as y value. Necessary to access the raw y values for imputation. - store_statistics: bool - Whether to store statistics about the data (to be used at subsequent calls) - - Returns - ------- - X: np.ndarray - Y: np.ndarray - """ - # First build nan-matrix of size #configs x #params+1 - n_rows = len(run_dict) - n_cols = self.num_params - X = np.ones([n_rows, n_cols + self.n_feats]) * np.nan - - # For now we keep it as 1 - # TODO: Extend for native multi-objective - y = np.ones([n_rows, 1]) - - # Then populate matrix - for row, (key, run) in enumerate(run_dict.items()): - # Scaling is automatically done in configSpace - conf = runhistory.ids_config[key.config_id] - conf_vector = convert_configurations_to_array([conf])[0] - if self.n_feats: - feats = self.instance_features[key.instance_id] - X[row, :] = np.hstack((conf_vector, feats)) # type: ignore - else: - X[row, :] = conf_vector - # run_array[row, -1] = instances[row] - - if self.num_obj > 1: - assert self.multi_objective_algorithm is not None - - # Let's normalize y here - # We use the objective_bounds calculated by the runhistory - y_ = normalize_costs(run.cost, runhistory.objective_bounds) - y_agg = self.multi_objective_algorithm(y_) - y[row] = y_agg - else: - if return_time_as_y: - y[row, 0] = run.time - else: - y[row] = run.cost - - if y.size > 0: - if store_statistics: - self.perc = np.percentile(y, self.scale_perc, axis=0) - self.min_y = np.min(y, axis=0) - self.max_y = np.max(y, axis=0) - - y = self.transform_response_values(values=y) - return X, y - - def transform_response_values(self, values: np.ndarray) -> np.ndarray: - """Transform function response values. Returns the input values. - - Parameters - ---------- - values : np.ndarray - Response values to be transformed. - - Returns - ------- - np.ndarray - """ - return values - - -class RunHistory2EPM4LogCost(RunHistory2EPM4Cost): - """TODO.""" - - def transform_response_values(self, values: np.ndarray) -> np.ndarray: - """Transform function response values. Transforms the response values by using a log - transformation. - - Parameters - ---------- - values : np.ndarray - Response values to be transformed. - - Returns - ------- - np.ndarray - """ - # ensure that minimal value is larger than 0 - if np.any(values <= 0): - self.logger.warning( - "Got cost of smaller/equal to 0. Replace by %f since we use" - " log cost." % constants.MINIMAL_COST_FOR_LOG - ) - values[values < constants.MINIMAL_COST_FOR_LOG] = constants.MINIMAL_COST_FOR_LOG - values = np.log(values) - return values - - -class RunHistory2EPM4ScaledCost(RunHistory2EPM4Cost): - """TODO.""" - - def transform_response_values(self, values: np.ndarray) -> np.ndarray: - """Transform function response values. Transforms the response values by linearly scaling - them between zero and one. - - Parameters - ---------- - values : np.ndarray - Response values to be transformed. - - Returns - ------- - np.ndarray - """ - min_y = self.min_y - (self.perc - self.min_y) # Subtract the difference between the percentile and the minimum - min_y -= constants.VERY_SMALL_NUMBER # Minimal value to avoid numerical issues in the log scaling below - # linear scaling - # prevent diving by zero - - min_y[np.where(min_y == self.max_y)] *= 1 - 10**-101 - values = (values - min_y) / (self.max_y - min_y) - return values - - -class RunHistory2EPM4InvScaledCost(RunHistory2EPM4Cost): - """TODO.""" - - def __init__(self, **kwargs): # type: ignore[no-untyped-def] # noqa F723 - super().__init__(**kwargs) - if self.instance_features is not None: - if len(self.instance_features) > 1: - raise NotImplementedError("Handling more than one instance is not supported for inverse scaled cost.") - - def transform_response_values(self, values: np.ndarray) -> np.ndarray: - """Transform function response values. Transform the response values by linearly scaling - them between zero and one and then using inverse scaling. - - Parameters - ---------- - values : np.ndarray - Response values to be transformed. - - Returns - ------- - np.ndarray - """ - min_y = self.min_y - (self.perc - self.min_y) # Subtract the difference between the percentile and the minimum - min_y -= constants.VERY_SMALL_NUMBER # Minimal value to avoid numerical issues in the log scaling below - # linear scaling - # prevent diving by zero - - min_y[np.where(min_y == self.max_y)] *= 1 - 10**-10 - - values = (values - min_y) / (self.max_y - min_y) - values = 1 - 1 / values - return values - - -class RunHistory2EPM4SqrtScaledCost(RunHistory2EPM4Cost): - """TODO.""" - - def __init__(self, **kwargs): # type: ignore[no-untyped-def] # noqa F723 - super().__init__(**kwargs) - if self.instance_features is not None: - if len(self.instance_features) > 1: - raise NotImplementedError("Handling more than one instance is not supported for sqrt scaled cost.") - - def transform_response_values(self, values: np.ndarray) -> np.ndarray: - """Transform function response values. Transform the response values by linearly scaling - them between zero and one and then using the square root. - - Parameters - ---------- - values : np.ndarray - Response values to be transformed. - - Returns - ------- - np.ndarray - """ - min_y = self.min_y - (self.perc - self.min_y) # Subtract the difference between the percentile and the minimum - min_y -= constants.VERY_SMALL_NUMBER # Minimal value to avoid numerical issues in the log scaling below - # linear scaling - # prevent diving by zero - - min_y[np.where(min_y == self.max_y)] *= 1 - 10**-10 - - values = (values - min_y) / (self.max_y - min_y) - values = np.sqrt(values) - return values - - -class RunHistory2EPM4LogScaledCost(RunHistory2EPM4Cost): - """TODO.""" - - def transform_response_values(self, values: np.ndarray) -> np.ndarray: - """Transform function response values. - - Transform the response values by linearly scaling them between zero and one and - then using the log transformation. - - Parameters - ---------- - values : np.ndarray - Response values to be transformed. - - Returns - ------- - np.ndarray - """ - min_y = self.min_y - (self.perc - self.min_y) # Subtract the difference between the percentile and the minimum - min_y -= constants.VERY_SMALL_NUMBER # Minimal value to avoid numerical issues in the log scaling below - # linear scaling - # prevent diving by zero - - min_y[np.where(min_y == self.max_y)] *= 1 - 10**-10 - - values = (values - min_y) / (self.max_y - min_y) - - values = np.log(values) - return values - - -class RunHistory2EPM4EIPS(AbstractRunHistory2EPM): - """TODO.""" - - def _build_matrix( - self, - run_dict: Mapping[RunKey, RunValue], - runhistory: RunHistory, - return_time_as_y: bool = False, - store_statistics: bool = False, - ) -> Tuple[np.ndarray, np.ndarray]: - """TODO.""" - if return_time_as_y: - raise NotImplementedError() - if store_statistics: - # store_statistics is currently not necessary - pass - - # First build nan-matrix of size #configs x #params+1 - n_rows = len(run_dict) - n_cols = self.num_params - X = np.ones([n_rows, n_cols + self.n_feats]) * np.nan - y = np.ones([n_rows, 2]) - - # Then populate matrix - for row, (key, run) in enumerate(run_dict.items()): - # Scaling is automatically done in configSpace - conf = runhistory.ids_config[key.config_id] - conf_vector = convert_configurations_to_array([conf])[0] - if self.n_feats: - feats = self.instance_features[key.instance_id] - X[row, :] = np.hstack((conf_vector, feats)) # type: ignore - else: - X[row, :] = conf_vector - - if self.num_obj > 1: - assert self.multi_objective_algorithm is not None - - # Let's normalize y here - # We use the objective_bounds calculated by the runhistory - y_ = normalize_costs(run.cost, runhistory.objective_bounds) - y_agg = self.multi_objective_algorithm(y_) - y[row, 0] = y_agg - else: - y[row, 0] = run.cost - - y[row, 1] = run.time - - y_transformed = self.transform_response_values(values=y) - - return X, y_transformed - - def transform_response_values(self, values: np.ndarray) -> np.ndarray: - """Transform function response values. Transform the runtimes by a log transformation - (log(1. - - + runtime). - - Parameters - ---------- - values : np.ndarray - Response values to be transformed. - - Returns - ------- - np.ndarray - """ - # We need to ensure that time remains positive after the log transform. - values[:, 1] = np.log(1 + values[:, 1]) - return values diff --git a/smac/runhistory/runhistory2epm_boing.py b/smac/runhistory/runhistory2epm_boing.py deleted file mode 100644 index 009abf0e8..000000000 --- a/smac/runhistory/runhistory2epm_boing.py +++ /dev/null @@ -1,92 +0,0 @@ -import typing - -import copy - -import numpy as np - -from smac.runhistory.runhistory import RunHistory -from smac.runhistory.runhistory2epm import ( - RunHistory2EPM4Cost, - RunHistory2EPM4LogScaledCost, -) - - -class RunHistory2EPM4CostWithRaw(RunHistory2EPM4Cost): - """ - A transformer that transform RunHistroy to vectors, this set of classes will return the raw cost values in - addition to the transformed cost values. The raw cost values can then be applied for local BO approaches. - """ - - def transform_with_raw( - self, - runhistory: RunHistory, - budget_subset: typing.Optional[typing.List] = None, - ) -> typing.Tuple[np.ndarray, np.ndarray, np.ndarray]: - """Returns vector representation of runhistory; if imputation is - disabled, censored (TIMEOUT with time < cutoff) will be skipped. This function returns both the raw - and transformed cost values - - Parameters - ---------- - runhistory : smac.runhistory.runhistory.RunHistory - Runhistory containing all evaluated configurations/instances - budget_subset : list of budgets to consider - - Returns - ------- - X: numpy.ndarray - configuration vector x instance features - Y: numpy.ndarray - cost values - Y_raw: numpy.ndarray - cost values before transformation - """ - X, Y_raw = RunHistory2EPM4Cost.transform(self, runhistory, budget_subset) - Y = copy.deepcopy(Y_raw) - Y = self.transform_raw_values(Y) - return X, Y, Y_raw - - def transform_response_values(self, values: np.ndarray) -> np.ndarray: - """Transform function response values. Returns the input values. - - Parameters - ---------- - values : np.ndarray - Response values to be transformed. - - Returns - ------- - np.ndarray - """ - # otherwise it will be overwritten by its superclass - return values - - def transform_raw_values(self, values: np.ndarray) -> np.ndarray: - """Transform function response values. Returns the raw input values before transformation - - Parameters - ---------- - values : np.ndarray - Response values to be transformed. - - Returns - ------- - np.ndarray - """ - return values - - -class RunHistory2EPM4ScaledLogCostWithRaw(RunHistory2EPM4CostWithRaw, RunHistory2EPM4LogScaledCost): - def transform_raw_values(self, values: np.ndarray) -> np.ndarray: - """Transform function response values. Returns the raw input values before transformation - - Parameters - ---------- - values : np.ndarray - Response values to be transformed. - - Returns - ------- - np.ndarray - """ - return RunHistory2EPM4LogScaledCost.transform_response_values(self, values) diff --git a/smac/runner/__init__.py b/smac/runner/__init__.py new file mode 100644 index 000000000..4bda4cb5e --- /dev/null +++ b/smac/runner/__init__.py @@ -0,0 +1,17 @@ +from smac.runner.abstract_runner import AbstractRunner +from smac.runner.dask_runner import DaskParallelRunner +from smac.runner.exceptions import ( + FirstRunCrashedException, + TargetAlgorithmAbortException, +) +from smac.runner.target_function_runner import TargetFunctionRunner + +__all__ = [ + # Runner + "AbstractRunner", + "TargetFunctionRunner", + "DaskParallelRunner", + # Exceptions + "TargetAlgorithmAbortException", + "FirstRunCrashedException", +] diff --git a/smac/runner/abstract_runner.py b/smac/runner/abstract_runner.py new file mode 100644 index 000000000..3499de607 --- /dev/null +++ b/smac/runner/abstract_runner.py @@ -0,0 +1,235 @@ +from __future__ import annotations + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +from abc import ABC, abstractmethod +from typing import Any, Iterator + +import time +import traceback + +import numpy as np +from ConfigSpace import Configuration + +from smac.runhistory import StatusType, TrialInfo, TrialValue +from smac.scenario import Scenario +from smac.utils.logging import get_logger + +logger = get_logger(__name__) + + +class AbstractRunner(ABC): + """Interface class to handle the execution of SMAC configurations. This interface defines how to interact with the + SMBO loop. The complexity of running a configuration as well as handling the results is abstracted to the SMBO via + an AbstractRunner. + + From SMBO perspective, launching a configuration follows a + submit/collect scheme as follows: + + 1. A run is launched via ``submit_run()`` + * ``submit_run`` internally calls ``run_wrapper()``, a method that contains common processing functions among + different unners. + * A class that implements AbstractRunner defines ``run()`` which is really the algorithm to translate a + ``TrialInfo`` to a ``TrialValue``, i.e. a configuration to an actual result. + 2. A completed run is collected via ``iter_results()``, which iterates and consumes any finished runs, if any. + 3. This interface also offers the method ``wait()`` as a mechanism to make sure we have enough data in the next + iteration to make a decision. For example, the intensifier might not be able to select the next challenger + until more results are available. + + Parameters + ---------- + scenario : Scenario + required_arguments : list[str] + A list of required arguments, which are passed to the target function. + """ + + def __init__( + self, + scenario: Scenario, + required_arguments: list[str] = [], + ): + self._scenario = scenario + self._required_arguments = required_arguments + + # The results is a FIFO structure, implemented via a list + # (because the Queue lock is not pickable). Finished runs are + # put in this list and collected via _process_pending_runs + self._results_queue: list[tuple[TrialInfo, TrialValue]] = [] + self._crash_cost = scenario.crash_cost + self._supports_memory_limit = False + + if isinstance(scenario.objectives, str): + objectives = [scenario.objectives] + else: + objectives = scenario.objectives + + self._objectives = objectives + self._n_objectives = scenario.count_objectives() + + # We need to exapdn crash cost if the user did not do it + if self._n_objectives > 1: + if not isinstance(scenario.crash_cost, list): + assert isinstance(scenario.crash_cost, float) + self._crash_cost = [scenario.crash_cost for _ in range(self._n_objectives)] + + def run_wrapper(self, trial_info: TrialInfo) -> tuple[TrialInfo, TrialValue]: + """Wrapper around run() to execute and check the execution of a given config. This function encapsulates common + handling/processing, so that run() implementation is simplified. + + Parameters + ---------- + trial_info : RunInfo + Object that contains enough information to execute a configuration run in isolation. + + Returns + ------- + info : TrialInfo + An object containing the configuration launched. + value : TrialValue + Contains information about the status/performance of config. + """ + start = time.time() + + try: + status, cost, runtime, additional_info = self.run( + config=trial_info.config, + instance=trial_info.instance, + budget=trial_info.budget, + seed=trial_info.seed, + ) + except Exception as e: + status = StatusType.CRASHED + cost = self._crash_cost + runtime = time.time() - start + + # Add context information to the error message + exception_traceback = traceback.format_exc() + error_message = repr(e) + additional_info = { + "traceback": exception_traceback, + "error": error_message, + } + + end = time.time() + + if trial_info.budget == 0 and status == StatusType.DONOTADVANCE: + raise ValueError("Cannot handle DONOTADVANCE state when using intensify or SH/HB on instances.") + + # Catch NaN or inf + if not np.all(np.isfinite(cost)): + logger.warning( + "Target function returned infinity or nothing at all. Result is treated as CRASHED" + f" and cost is set to {self._crash_cost}." + ) + + if "traceback" in additional_info: + logger.warning(f"Traceback: {additional_info['traceback']}\n") + + status = StatusType.CRASHED + + if status == StatusType.CRASHED: + cost = self._crash_cost + + trial_value = TrialValue( + status=status, + cost=cost, + time=runtime, + additional_info=additional_info, + starttime=start, + endtime=end, + ) + return trial_info, trial_value + + @property + def meta(self) -> dict[str, Any]: + """Returns the meta data of the created object.""" + return {"name": self.__class__.__name__} + + @abstractmethod + def submit_trial(self, trial_info: TrialInfo) -> None: + """This function submits a configuration embedded in a TrialInfo object, and uses one of the + workers to produce a result (such result will eventually be available on the `self._results_queue` + FIFO). + + This interface method will be called by SMBO, with the expectation + that a function will be executed by a worker. What will be executed is dictated by trial_info, and "how" will it + be executed is decided via the child class that implements a run() method. + + Because config submission can be a serial/parallel endeavor, it is expected to be implemented by a child class. + + Parameters + ---------- + trial_info : TrialInfo + An object containing the configuration launched. + """ + raise NotImplementedError + + @abstractmethod + def run( + self, + config: Configuration, + instance: str | None = None, + budget: float | None = None, + seed: int | None = None, + ) -> tuple[StatusType, float | list[float], float, dict]: + """Runs the target function with a configuration on a single instance-budget-seed combination (aka trial). + + Parameters + ---------- + config : Configuration + Configuration to be passed to the target function. + instance : str | None, defaults to None + The Problem instance. + budget : float | None, defaults to None + A positive, real-valued number representing an arbitrary limit to the target function handled by the + target function internally. + seed : int, defaults to None + + Returns + ------- + status : StatusType + Status of the trial. + cost : float | list[float] + Resulting cost(s) of the trial. + runtime : float + The time the target function function took to run. + additional_info : dict + All further additional trial information. + """ + raise NotImplementedError + + @abstractmethod + def iter_results(self) -> Iterator[tuple[TrialInfo, TrialValue]]: + """This method returns any finished configuration, and returns a list with the + results of exercising the configurations. This class keeps populating results + to ``self._results_queue`` until a call to ``get_finished`` trials is done. In this case, + the `self._results_queue` list is emptied and all trial values produced by running + `run` are returned. + + Returns + ------- + Iterator[tuple[TrialInfo, TrialValue]]: + A list of TrialInfo/TrialValue tuples, all of which have been finished. + """ + raise NotImplementedError + + @abstractmethod + def wait(self) -> None: + """The SMBO/intensifier might need to wait for trials to finish before making a decision.""" + raise NotImplementedError + + @abstractmethod + def is_running(self) -> bool: + """Whether or not there are trials still running. + + Generally, if the runner is serial, launching a trial instantly returns it's result. On + parallel runners, there might be pending configurations to complete. + """ + raise NotImplementedError + + @abstractmethod + def count_available_workers(self) -> int: + """Returns the number of available workers.""" + raise NotImplementedError diff --git a/smac/runner/abstract_serial_runner.py b/smac/runner/abstract_serial_runner.py new file mode 100644 index 000000000..3434e3d3b --- /dev/null +++ b/smac/runner/abstract_serial_runner.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from typing import Iterator + +from smac.runhistory.dataclasses import TrialInfo, TrialValue +from smac.runner.abstract_runner import AbstractRunner + + +class AbstractSerialRunner(AbstractRunner): + def submit_trial(self, trial_info: TrialInfo) -> None: + """This function submits a trial_info object in a serial fashion. As there is a single worker for this task, + this interface can be considered a wrapper over the `run` method. + + Both result/exceptions can be completely determined in this step so both lists are properly filled. + + Parameters + ---------- + trial_info : TrialInfo + An object containing the configuration launched. + """ + self._results_queue.append(self.run_wrapper(trial_info)) + + def iter_results(self) -> Iterator[tuple[TrialInfo, TrialValue]]: # noqa: D102 + while self._results_queue: + yield self._results_queue.pop(0) + + def wait(self) -> None: + """The SMBO/intensifier might need to wait for trials to finish before making a decision. + For serial runners, no wait is needed as the result is immediately available. + """ + # There is no need to wait in serial runners. When launching a trial via submit, as + # the serial trial uses the same process to run, the result is always available + # immediately after. This method implements is just an implementation of the + # abstract method via a simple return, again, because there is no need to wait + return + + def is_running(self) -> bool: # noqa: D102 + return False + + def count_available_workers(self) -> int: + """Returns the number of available workers. Serial workers only have one worker.""" + return 1 diff --git a/smac/runner/dask_runner.py b/smac/runner/dask_runner.py new file mode 100644 index 000000000..6afebcc11 --- /dev/null +++ b/smac/runner/dask_runner.py @@ -0,0 +1,188 @@ +from __future__ import annotations + +from typing import Iterator + +import time +from pathlib import Path + +import dask +from ConfigSpace import Configuration +from dask.distributed import Client, Future, wait + +from smac.runhistory import StatusType, TrialInfo, TrialValue +from smac.runner.abstract_runner import AbstractRunner +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class DaskParallelRunner(AbstractRunner): + """Interface to submit and collect a job in a distributed fashion. DaskParallelRunner is intended to comply with the + bridge design pattern. Nevertheless, to reduce the amount of code within single-vs-parallel implementations, + DaskParallelRunner wraps a BaseRunner object which is then executed in parallel on `n_workers`. + + This class then is constructed by passing an AbstractRunner that implements + a `run` method, and is capable of doing so in a serial fashion. Neext, + this wrapper class uses dask to initialize `N` number of AbstractRunner that actively wait of a TrialInfo to produce + a RunInfo object. + + To be more precise, the work model is then: + 1. The intensifier dictates "what" to run (a configuration/instance/seed) via a TrialInfo object. + 2. An abstract runner takes this TrialInfo object and launches the task via + `submit_run`. In the case of DaskParallelRunner, `n_workers` receive a pickle-object of + `DaskParallelRunner.single_worker`, each with a `run` method coming from `DaskParallelRunner.single_worker.run()` + 3. TrialInfo objects are run in a distributed fashion, an their results are available locally to each worker. + Such result is collected by `iter_results` and then passed to SMBO. + 4. Exceptions are also locally available to each worker and need to be collected. + + Dask works with `Future` object which are managed via the DaskParallelRunner.client. + + Parameters + ---------- + single_worker : AbstractRunner + A runner to run in a distributed fashion. Will be distributed using `n_workers`. + patience: int, default to 5 + How much to wait for workers (seconds) to be available if one fails. + dask_client: Client | None, defaults to None + User-created dask client, which can be used to start a dask cluster and then attach + SMAC to it. This will not be closed automatically and will have to be closed manually if provided explicitly. + If none is provided (default), a local one will be created for you and closed upon completion. + """ + + def __init__( + self, + single_worker: AbstractRunner, + patience: int = 5, + dask_client: Client | None = None, + ): + super().__init__( + scenario=single_worker._scenario, + required_arguments=single_worker._required_arguments, + ) + + # The single worker to hold on to and call run on + self._single_worker = single_worker + + # The list of futures that dask will use to indicate in progress runs + self._pending_trials: list[Future] = [] + + # Dask related variables + self._scheduler_file: Path | None = None + self._patience = patience + + self._client: Client + self._close_client_at_del: bool + + if dask_client is None: + dask.config.set({"distributed.worker.daemon": False}) + self._close_client_at_del = True + self._client = Client( + n_workers=self._scenario.n_workers, + processes=True, + threads_per_worker=1, + local_directory=str(self._scenario.output_directory), + ) + + if self._scenario.output_directory is not None: + self._scheduler_file = self._scenario.output_directory / ".dask_scheduler_file" + self._client.write_scheduler_file(scheduler_file=str(self._scheduler_file)) + else: + # We just use their set up + self._client = dask_client + self._close_client_at_del = False + + def submit_trial(self, trial_info: TrialInfo) -> None: + """This function submits a configuration embedded in a `trial_info` object, and uses one of the + workers to produce a result locally to each worker. + + The execution of a configuration follows this procedure: + 1. The SMBO/intensifier generates a `TrialInfo`. + 2. SMBO calls `submit_trial` so that a worker launches the `trial_info`. + 3. `submit_trial` internally calls self.run(). It does so via a call to `run_wrapper` which contains common + code that any `run` method will otherwise have to implement. + + All results will be only available locally to each worker, so the main node needs to collect them. + + Parameters + ---------- + trial_info : TrialInfo + An object containing the configuration launched. + """ + # Check for resources or block till one is available + if self.count_available_workers() <= 0: + wait(self._pending_trials, return_when="FIRST_COMPLETED") + self._process_pending_trials() + + # Check again to make sure that there are resources + if self.count_available_workers() <= 0: + logger.warning("No workers are available. This could mean workers crashed. Waiting for new workers...") + time.sleep(self._patience) + if self.count_available_workers() <= 0: + raise RuntimeError( + "Tried to execute a job, but no worker was ever available." + "This likely means that a worker crashed or no workers were properly configured." + ) + + # At this point we can submit the job + run = self._client.submit(self._single_worker.run_wrapper, trial_info=trial_info) + self._pending_trials.append(run) + + def iter_results(self) -> Iterator[tuple[TrialInfo, TrialValue]]: # noqa: D102 + self._process_pending_trials() + while self._results_queue: + yield self._results_queue.pop(0) + + def wait(self) -> None: # noqa: D102 + if self.is_running(): + wait(self._pending_trials, return_when="FIRST_COMPLETED") + + def is_running(self) -> bool: # noqa: D102 + return len(self._pending_trials) > 0 + + def run( + self, + config: Configuration, + instance: str | None = None, + budget: float | None = None, + seed: int | None = None, + ) -> tuple[StatusType, float | list[float], float, dict]: # noqa: D102 + return self._single_worker.run(config=config, instance=instance, seed=seed, budget=budget) + + def count_available_workers(self) -> int: + """Total number of workers available. This number is dynamic as more resources can be allocated.""" + return sum(self._client.nthreads().values()) - len(self._pending_trials) + + def close(self, force: bool = False) -> None: + """Closes the client.""" + if self._close_client_at_del or force: + self._client.close() + + def _process_pending_trials(self) -> None: + """The completed trials are moved from ``self._pending_trials`` to ``self._reseults_queue``. We make sure + pending trials never exceed the capacity of the scheduler. + """ + # In code check to make sure we don;t exceed resource allocation + if self.count_available_workers() < 0: + logger.warning( + "More running jobs than resources available. " + "Should not have more pending trials in remote workers " + "than the number of workers. This could mean a worker " + "crashed and was not able to be recovered by dask. " + ) + + # Move the done run from the worker to the results queue + done = [trial for trial in self._pending_trials if trial.done()] + for trial in done: + self._results_queue.append(trial.result()) + self._pending_trials.remove(trial) + + def __del__(self) -> None: + """Makes sure that when this object gets deleted, the client is terminated. This + is only done if the client was created by the dask runner. + """ + if self._close_client_at_del: + self.close() diff --git a/smac/runner/exceptions.py b/smac/runner/exceptions.py new file mode 100644 index 000000000..dd15d7482 --- /dev/null +++ b/smac/runner/exceptions.py @@ -0,0 +1,14 @@ +class TargetAlgorithmAbortException(Exception): + """Exception indicating that the target function suggests an ABORT of SMAC, usually because it + assumes that all further runs will surely fail. + """ + + pass + + +class FirstRunCrashedException(TargetAlgorithmAbortException): + """Exception indicating that the first run crashed (depending on options this could trigger an + ABORT of SMAC). + """ + + pass diff --git a/smac/runner/target_function_runner.py b/smac/runner/target_function_runner.py new file mode 100644 index 000000000..f33f2cc42 --- /dev/null +++ b/smac/runner/target_function_runner.py @@ -0,0 +1,231 @@ +from __future__ import annotations + +from typing import Any, Callable + +import copy +import inspect +import math +import time +import traceback + +import numpy as np +from ConfigSpace import Configuration +from pynisher import MemoryLimitException, WallTimeoutException, limit + +from smac.runner.abstract_runner import StatusType +from smac.runner.abstract_serial_runner import AbstractSerialRunner +from smac.scenario import Scenario +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class TargetFunctionRunner(AbstractSerialRunner): + """Class to execute target functions which are python functions. Evaluates functions for given configuration and + resource limit. + + The target function can either return a float (the loss), or a tuple with the first element being a float and the + second being additional run information. In a multi-objective setting, the float value is replaced by a list of + floats. + + Parameters + ---------- + target_function : Callable + The target function function. + scenario : Scenario + required_arguments : list[str] + A list of required arguments, which are passed to the target function. + """ + + def __init__( + self, + scenario: Scenario, + target_function: Callable, + required_arguments: list[str] = [], + ): + super().__init__(scenario=scenario, required_arguments=required_arguments) + self._target_function = target_function + + # Check if target function is callable + if not callable(self._target_function): + raise TypeError( + "Argument `target_function` must be a callable but is type" f"`{type(self._target_function)}`." + ) + + # Signatures here + signature = inspect.signature(self._target_function).parameters + for argument in required_arguments: + if argument not in signature.keys(): + raise RuntimeError( + f"Target function needs to have the arguments {required_arguments} " + f"but could not found {argument}." + ) + + # Now we check for additional arguments which are not used by SMAC + # However, we only want to warn the user and not + for key in list(signature.keys())[1:]: + if key not in required_arguments: + logger.warning(f"The argument {key} is not set by SMAC. Consider removing it.") + + # Pynisher limitations + if (memory := self._scenario.trial_memory_limit) is not None: + memory = int(math.ceil(memory)) + + if (time := self._scenario.trial_walltime_limit) is not None: + time = int(math.ceil(time)) + + self._memory_limit = memory + self._algorithm_walltime_limit = time + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update({"code": str(self._target_function.__code__.co_code)}) + + return meta + + def run( + self, + config: Configuration, + instance: str | None = None, + budget: float | None = None, + seed: int | None = None, + ) -> tuple[StatusType, float | list[float], float, dict]: + """Calls the target function with pynisher if algorithm walltime limit or memory limit is set. Otherwise + the function is called directly. + + Parameters + ---------- + config : Configuration + Configuration to be passed to the target function. + instance : str | None, defaults to None + The Problem instance. + budget : float | None, defaults to None + A positive, real-valued number representing an arbitrary limit to the target function handled by the + target function internally. + seed : int, defaults to None + + Returns + ------- + status : StatusType + Status of the trial. + cost : float | list[float] + Resulting cost(s) of the trial. + runtime : float + The time the target function function took to run. + additional_info : dict + All further additional trial information. + """ + # The kwargs are passed to the target function. + kwargs: dict[str, Any] = {} + + if "seed" in self._required_arguments: + kwargs["seed"] = seed + + if "instance" in self._required_arguments: + kwargs["instance"] = instance + + if "budget" in self._required_arguments: + kwargs["budget"] = budget + + # Presetting + cost: float | list[float] = self._crash_cost + runtime = 0.0 + additional_info = {} + status = StatusType.CRASHED + + # If memory limit or walltime limit is set, we wanna use pynisher + target_function: Callable + if self._memory_limit is not None or self._algorithm_walltime_limit is not None: + target_function = limit( + self._target_function, + memory=self._memory_limit, + wall_time=self._algorithm_walltime_limit, + wrap_errors=True, # Hard to describe; see https://github.com/automl/pynisher + ) + else: + target_function = self._target_function + + # We don't want the user to change the configuration + config_copy = copy.deepcopy(config) + + # Call target function + try: + start_time = time.time() + rval = self(config_copy, target_function, kwargs) + runtime = time.time() - start_time + status = StatusType.SUCCESS + except WallTimeoutException: + status = StatusType.TIMEOUT + except MemoryLimitException: + status = StatusType.MEMORYOUT + except Exception as e: + cost = np.asarray(cost).squeeze().tolist() + additional_info = { + "traceback": traceback.format_exc(), + "error": repr(e), + } + status = StatusType.CRASHED + + if status != StatusType.SUCCESS: + return status, cost, runtime, additional_info + + if isinstance(rval, tuple): + result, additional_info = rval + else: + result, additional_info = rval, {} + + # Do some sanity checking (for multi objective) + error = f"Returned costs {result} does not match the number of objectives {self._objectives}." + + # If dict convert to array and make sure the order is correct + if isinstance(result, dict): + if len(result) != len(self._objectives): + raise RuntimeError(error) + + ordered_cost: list[float] = [] + for name in self._objectives: + if name not in result: + raise RuntimeError(f"Objective {name} was not found in the returned costs.") + + ordered_cost.append(result[name]) + + result = ordered_cost + + if isinstance(result, list): + if len(result) != len(self._objectives): + raise RuntimeError(error) + + if isinstance(result, float): + if isinstance(self._objectives, list) and len(self._objectives) != 1: + raise RuntimeError(error) + + cost = result + + if cost is None: + status = StatusType.CRASHED + cost = self.crash_cost + + # We want to get either a float or a list of floats. + cost = np.asarray(cost).squeeze().tolist() + + return status, cost, runtime, additional_info + + def __call__( + self, + config: Configuration, + algorithm: Callable, + algorithm_kwargs: dict[str, Any], + ) -> ( + float + | list[float] + | dict[str, float] + | tuple[float, dict] + | tuple[list[float], dict] + | tuple[dict[str, float], dict] + ): + """Calls the algorithm, which is processed in the ``run`` method.""" + return algorithm(config, **algorithm_kwargs) diff --git a/smac/runner/target_function_script_runner.py b/smac/runner/target_function_script_runner.py new file mode 100644 index 000000000..214d13c7b --- /dev/null +++ b/smac/runner/target_function_script_runner.py @@ -0,0 +1,217 @@ +from __future__ import annotations + +from typing import Any + +import time +from subprocess import PIPE, Popen + +from ConfigSpace import Configuration + +from smac.runner.abstract_runner import StatusType +from smac.runner.abstract_serial_runner import AbstractSerialRunner +from smac.scenario import Scenario +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + +logger = get_logger(__name__) + + +class TargetFunctionScriptRunner(AbstractSerialRunner): + """Class to execute target functions from scripts. Uses `Popen` to execute the script in a subprocess. + + The following example shows how the script is called: + ``target_function --instance=test --instance_features=test --seed=0 --hyperparameter1=5323`` + + The script must return an echo in the following form (white-spaces are removed): + ``cost=0.5; runtime=0.01; status=SUCCESS; additional_info=test`` (single-objective) + ``cost=0.5, 0.4; runtime=0.01; status=SUCCESS; additional_info=test`` (multi-objective) + + The status must be a string and must be one of the ``StatusType`` values. However, ``runtime``, ``status`` and + ``additional_info`` are optional. + + Note + ---- + Everytime an instance is passed, also an instance feature in form of a comma-separated list (no spaces) of floats is + passed. If no instance feature for the instance is given, an empty list is passed. + + Parameters + ---------- + target_function : Callable + The target function function. + scenario : Scenario + required_arguments : list[str] + A list of required arguments, which are passed to the target function. + """ + + def __init__( + self, + target_function: str, + scenario: Scenario, + required_arguments: list[str] = [], + ): + super().__init__(scenario=scenario, required_arguments=required_arguments) + self._target_function = target_function + + # Check if target function is callable + if not isinstance(self._target_function, str): + raise TypeError( + "Argument `target_function` must be a string but is type" f"`{type(self._target_function)}`." + ) + + if self._scenario.trial_memory_limit is not None: + logger.warning("Trial memory limit is not supported for script target functions.") + + if self._scenario.trial_walltime_limit is not None: + logger.warning("Trial walltime limit is not supported for script target functions.") + + @property + def meta(self) -> dict[str, Any]: # noqa: D102 + meta = super().meta + meta.update({"filename": str(self._target_function)}) + + return meta + + def run( + self, + config: Configuration, + instance: str | None = None, + budget: float | None = None, + seed: int | None = None, + ) -> tuple[StatusType, float | list[float], float, dict]: + """Calls the target function. + + Parameters + ---------- + config : Configuration + Configuration to be passed to the target function. + instance : str | None, defaults to None + The Problem instance. + budget : float | None, defaults to None + A positive, real-valued number representing an arbitrary limit to the target function handled by the + target function internally. + seed : int, defaults to None + + Returns + ------- + status : StatusType + Status of the trial. + cost : float | list[float] + Resulting cost(s) of the trial. + runtime : float + The time the target function function took to run. + additional_info : dict + All further additional trial information. + """ + # The kwargs are passed to the target function. + kwargs: dict[str, Any] = {} + if "seed" in self._required_arguments: + kwargs["seed"] = seed + + if "instance" in self._required_arguments: + kwargs["instance"] = instance + + # In contrast to the normal target function runner, we also add the instance features here. + if self._scenario.instance_features is not None and instance in self._scenario.instance_features: + kwargs["instance_features"] = self._scenario.instance_features[instance] + else: + kwargs["instance_features"] = [] + + if "budget" in self._required_arguments: + kwargs["budget"] = budget + + # Presetting + cost: float | list[float] = self._crash_cost + runtime = 0.0 + additional_info = {} + status = StatusType.SUCCESS + + # Add config arguments to the kwargs + for k, v in config.get_dictionary().items(): + if k in kwargs: + raise RuntimeError(f"The key {k} is already in use. Please use a different one.") + kwargs[k] = v + + # Call target function + start_time = time.time() + output, error = self(kwargs) + runtime = time.time() - start_time + + # Now we have to parse the std output + # First remove white-spaces + output = output.replace(" ", "") + + outputs = {} + for pair in output.split(";"): + try: + kv = pair.split("=") + k, v = kv[0], kv[1] + + # Get rid of the trailing newline + v = v.strip() + + outputs[k] = v + except Exception: + pass + + # Parse status + if "status" in outputs: + status = getattr(StatusType, outputs["status"]) + + # Parse costs (depends on the number of objectives) + if "cost" in outputs: + if self._n_objectives == 1: + cost = float(outputs["cost"]) + else: + costs = outputs["cost"].split(",") + costs = [float(c) for c in costs] + cost = costs + + if len(costs) != self._n_objectives: + raise RuntimeError("The number of costs does not match the number of objectives.") + else: + status = StatusType.CRASHED + + # Overwrite runtime + if "runtime" in outputs: + runtime = float(outputs["runtime"]) + + # Add additional info + if "additional_info" in outputs: + additional_info["additional_info"] = outputs["additional_info"] + + if status != StatusType.SUCCESS: + additional_info["error"] = error + + if cost != self._crash_cost: + cost = self._crash_cost + logger.info( + "The target function crashed but returned a cost. The cost is ignored and replaced by crash cost." + ) + + return status, cost, runtime, additional_info + + def __call__( + self, + algorithm_kwargs: dict[str, Any], + ) -> tuple[str, str]: + """Calls the algorithm, which is processed in the ``run`` method.""" + cmd = [self._target_function] + for k, v in algorithm_kwargs.items(): + v = str(v) + k = str(k) + + # Let's remove some spaces + v = v.replace(" ", "") + + cmd += [f"--{k}={v}"] + + logger.debug(f"Calling: {' '.join(cmd)}") + p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, universal_newlines=True) + output, error = p.communicate() + + logger.debug("Stdout: %s" % output) + logger.debug("Stderr: %s" % error) + + return output, error diff --git a/smac/scenario.py b/smac/scenario.py new file mode 100644 index 000000000..a9ffb2510 --- /dev/null +++ b/smac/scenario.py @@ -0,0 +1,249 @@ +from __future__ import annotations + +from typing import Any + +import copy +import hashlib +import json +import random +from dataclasses import dataclass +from pathlib import Path + +import ConfigSpace +import numpy as np +from ConfigSpace.read_and_write import json as cs_json + +from smac.utils.logging import get_logger + +logger = get_logger(__name__) + + +@dataclass(frozen=True) +class Scenario: + """ + The scenario manages environment variables and therefore gives context in which frame the optimization is performed. + + Parameters + ---------- + configspace : ConfigSpace + The configuration space from which to sample the configurations. + name : str | None, defaults to None + The name of the run. If no name is passed, SMAC generates a hash from the meta data. + Specify this argument to identify your run easily. + output_directory : Path, defaults to Path("smac3_output") + The directory in which to save the output. The files are saved in `./output_directory/name/seed`. + deterministic : bool, defaults to False + If deterministic is set to true, only one seed is passed to the target function. + Otherwise, multiple seeds (if n_seeds of the intensifier is greater than 1) are passed + to the target function to ensure generalization. + objective : str | list[str] | None, defaults to "cost" + The objective(s) to optimize. This argument is required for multi-objective optimization. + crash_cost : float | list[float], defaults to np.inf + Defines the cost for a failed trial. In case of multi-objective, each objective can be associated with + a different cost. + termination_cost_threshold : float | list[float], defaults to np.inf + Defines a cost threshold when the optimization should stop. In case of multi-objective, each objective *must* be + associated with a cost. The optimization stops when all objectives crossed the threshold. + walltime_limit : float, defaults to np.inf + The maximum time in seconds that SMAC is allowed to run. + cputime_limit : float, defaults to np.inf + The maximum CPU time in seconds that SMAC is allowed to run. + trial_walltime_limit : float | None, defaults to None + The maximum time in seconds that a trial is allowed to run. If not specified, + no constraints are enforced. Otherwise, the process will be spawned by pynisher. + trial_memory_limit : int | None, defaults to None + The maximum memory in MB that a trial is allowed to use. If not specified, + no constraints are enforced. Otherwise, the process will be spawned by pynisher. + n_trials : int, defaults to 100 + The maximum number of trials (combination of configuration, seed, budget, and instance, depending on the task) + to run. + instances : list[str] | None, defaults to None + Names of the instances to use. If None, no instances are used. + Instances could be dataset names, seeds, subsets, etc. + instance_features : dict[str, list[float]] | None, defaults to None + Instances can be associated with features. For example, meta data of the dataset (mean, var, ...) can be + incorporated which are then further used to expand the training data of the surrogate model. + instance_order : str | None, defaults to "shuffle_once" + How to order the instances. Possible values are "shuffle" and "shuffle_once". You can disable this feature by + setting the argument to None. + min_budget : float | None, defaults to None + The minimum budget (epochs, subset size, number of instances, ...) that is used for the optimization. + Use this argument if you use multi-fidelity or instance optimization. + max_budget : float | None, defaults to None + The maximum budget (epochs, subset size, number of instances, ...) that is used for the optimization. + Use this argument if you use multi-fidelity or instance optimization. + seed : int, defaults to 0 + The seed is used to make results reproducible. If seed is -1, SMAC will generate a random seed. + n_workers : int, defaults to 1 + The number of workers to use for parallelization. If `n_workers` is greather than 1, SMAC will use + Dask to parallelize the optimization. + """ + + # General + configspace: ConfigSpace + name: str | None = None + output_directory: Path = Path("smac3_output") + deterministic: bool = False + + # Objectives + objectives: str | list[str] = "cost" + crash_cost: float | list[float] = np.inf + termination_cost_threshold: float | list[float] = np.inf + + # Limitations + walltime_limit: float = np.inf + cputime_limit: float = np.inf + trial_walltime_limit: float | None = None + trial_memory_limit: int | None = None + n_trials: int = 100 + + # Algorithm Configuration + instances: list[str] | None = None + instance_features: dict[str, list[float]] | None = None + + # Budgets + min_budget: float | None = None + max_budget: float | None = None + + # Others + seed: int = 0 + n_workers: int = 1 + + def __post_init__(self) -> None: + """Checks whether the config is valid.""" + # Use random seed if seed is -1 + if self.seed == -1: + seed = random.randint(0, 999999) + object.__setattr__(self, "seed", seed) + + # Change directory wrt name and seed + self._change_output_directory() + + # Set empty meta + object.__setattr__(self, "_meta", {}) + + def __eq__(self, other: object) -> bool: + if isinstance(other, Scenario): + # When using __dict__, we make sure to include the meta data. + # However, tuples are saved as lists in json. Therefore, we compare the json string + # to make sure we have the same conversion. + return Scenario.make_serializable(self) == Scenario.make_serializable(other) + + raise RuntimeError("Can only compare scenario objects.") + + @property + def meta(self) -> dict[str, Any]: + """Returns the meta data of the SMAC run. + + Note + ---- + Meta data are set when the facade is initialized. + """ + return self._meta # type: ignore + + def count_objectives(self) -> int: + """Counts the number of objectives.""" + if isinstance(self.objectives, list): + return len(self.objectives) + + return 1 + + def count_instance_features(self) -> int: + """Counts the number of instance features.""" + # Check whether key of instance features exist + n_features = 0 + if self.instance_features is not None: + for k, v in self.instance_features.items(): + if self.instances is None or k not in self.instances: + raise RuntimeError(f"Instance {k} is not specified in instances.") + + if n_features == 0: + n_features = len(v) + else: + if len(v) != n_features: + raise RuntimeError("Instances must have the same number of features.") + + return n_features + + def save(self) -> None: + """Saves internal variables and the configuration space to a file.""" + if self.meta == {}: + logger.warning("Scenario will saved without meta data. Please call the facade first to set meta data.") + + if self.name is None: + raise RuntimeError( + "Please specify meta data for generating a name. Alternatively, you can specify a name manually." + ) + + self.output_directory.mkdir(parents=True, exist_ok=True) + + data = {} + for k, v in self.__dict__.items(): + if k in ["configspace", "output_directory"]: + continue + + data[k] = v + + # Convert `output_directory` + data["output_directory"] = str(self.output_directory) + + # Save everything + filename = self.output_directory / "scenario.json" + with open(filename, "w") as fh: + json.dump(data, fh, indent=4) + + # Save configspace on its own + configspace_filename = self.output_directory / "configspace.json" + with open(configspace_filename, "w") as f: + f.write(cs_json.write(self.configspace)) + + @staticmethod + def load(path: Path) -> Scenario: + """Loads a scenario and the configuration space from a file.""" + filename = path / "scenario.json" + with open(filename, "r") as fh: + data = json.load(fh) + + # Convert `output_directory` to path object again + data["output_directory"] = Path(data["output_directory"]) + meta = data["_meta"] + del data["_meta"] + + # Read configspace + configspace_filename = path / "configspace.json" + with open(configspace_filename, "r") as f: + + configspace = cs_json.read(f.read()) + + data["configspace"] = configspace + + scenario = Scenario(**data) + scenario._set_meta(meta) + + return scenario + + @staticmethod + def make_serializable(scenario: Scenario) -> dict[str, Any]: + """Makes the scenario serializable.""" + s = copy.deepcopy(scenario.__dict__) + del s["configspace"] + s["output_directory"] = str(s["output_directory"]) + + return json.loads(json.dumps(s)) + + def _change_output_directory(self) -> None: + # Create output directory + if self.name is not None: + new = Path(self.name) / str(self.seed) + if not str(self.output_directory).endswith(str(new)): + object.__setattr__(self, "output_directory", self.output_directory / new) + + def _set_meta(self, meta: dict[str, Any]) -> None: + """Sets the meta data of the SMAC run.""" + object.__setattr__(self, "_meta", meta) + + # We overwrite name with the hash of the meta (if no name is passed) + if self.name is None: + hash = hashlib.md5(str(self.__dict__).encode("utf-8")).hexdigest() + object.__setattr__(self, "name", hash) + self._change_output_directory() diff --git a/smac/scenario/scenario.py b/smac/scenario/scenario.py deleted file mode 100644 index cfbec65ff..000000000 --- a/smac/scenario/scenario.py +++ /dev/null @@ -1,168 +0,0 @@ -from typing import Any, Dict, Iterable, List, Optional, Sequence, Union - -import copy -import logging - -import numpy as np - -from smac.utils.io.cmd_reader import CMDReader -from smac.utils.io.input_reader import InputReader -from smac.utils.io.output_writer import OutputWriter - -__author__ = "Marius Lindauer, Matthias Feurer, Aaron Kimmig" -__copyright__ = "Copyright 2016, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Marius Lindauer" -__email__ = "lindauer@cs.uni-freiburg.de" -__version__ = "0.0.2" - - -class Scenario(object): - """Scenario contains the configuration of the optimization process and constructs a scenario - object from a file or dictionary. - - All arguments set in the Scenario are set as attributes. - - Creates a scenario-object. The output_dir will be - "output_dir/run_id/" and if that exists already, the old folder and its - content will be moved (without any checks on whether it's still used by - another process) to "output_dir/run_id.OLD". If that exists, ".OLD"s - will be appended until possible. - - Parameters - ---------- - scenario : str or dict or None - If str, it will be interpreted as to a path a scenario file - If dict, it will be directly to get all scenario related information - If None, only cmd_options will be used - cmd_options : dict - Options from parsed command line arguments - """ - - use_ta_time = True - feature_dict = {} # type: Dict[str, Iterable] - run_obj = "None" - - def __init__( - self, - scenario: Union[str, Dict, None] = None, - cmd_options: Optional[Dict] = None, - ): - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - self.PCA_DIM = 7 - - self.in_reader = InputReader() - self.out_writer = OutputWriter() - - self.output_dir_for_this_run = None # type: Optional[str] - - self._arguments = {} # type: Dict[str, Any] - self._arguments.update(CMDReader().scen_cmd_actions) - - if scenario is None: - scenario = {} - if isinstance(scenario, str): - scenario_fn = scenario - scenario = {} - if cmd_options: - scenario.update(cmd_options) - cmd_reader = CMDReader() - self.logger.info("Reading scenario file: %s", scenario_fn) - smac_args_, scen_args_ = cmd_reader.read_smac_scenario_dict_cmd(scenario, scenario_fn) - scenario = {} - scenario.update(vars(smac_args_)) - scenario.update(vars(scen_args_)) - elif isinstance(scenario, dict): - scenario = copy.copy(scenario) - if cmd_options: - scenario.update(cmd_options) - cmd_reader = CMDReader() - smac_args_, scen_args_ = cmd_reader.read_smac_scenario_dict_cmd(scenario) - scenario = {} - scenario.update(vars(smac_args_)) - scenario.update(vars(scen_args_)) - else: - raise TypeError("Wrong type of scenario (str or dict are supported)") - - for arg_name, arg_value in scenario.items(): - setattr(self, arg_name, arg_value) - - self._transform_arguments() - - self.logger.debug("SMAC and Scenario Options:") - if cmd_options: - for arg_name, arg_value in cmd_options.items(): - if isinstance(arg_value, (int, str, float)): - self.logger.debug("%s = %s" % (arg_name, arg_value)) - - def _transform_arguments(self) -> None: - """TODO.""" - self.n_features = len(self.feature_dict) - self.feature_array = None - - self.instance_specific = {} # type: Dict[str, str] - - if self.run_obj == "runtime": - self.logy = True - # This pleases mypy by defining the variable above. However, we need to assign some value - elif self.run_obj == "None": - raise ValueError("Internal error - this must never happen!") - - def extract_instance_specific( - instance_list: Sequence[Union[str, List[str]]], - ) -> List[str]: - insts = [] - for inst in instance_list: - if len(inst) > 1: - self.instance_specific[inst[0]] = " ".join(inst[1:]) - insts.append(inst[0]) - return insts - - self.train_insts = extract_instance_specific(self.train_insts) # type: List[str] - if self.test_insts: - self.test_insts = extract_instance_specific(self.test_insts) # type: List[str] - - self.train_insts = self._to_str_and_warn(list_=self.train_insts) - self.test_insts = self._to_str_and_warn(list_=self.test_insts) - - if self.feature_dict: - feature_array = [] - for inst_ in self.train_insts: - feature_array.append(self.feature_dict[inst_]) - self.feature_array = np.array(feature_array) - self.n_features = self.feature_array.shape[1] - - if self.use_ta_time: - if self.algo_runs_timelimit is None or not np.isfinite(self.algo_runs_timelimit): - self.algo_runs_timelimit = self.wallclock_limit # type: float - self.wallclock_limit = np.inf # type: float - - # Update cost for crash to support multi-objective - if len(self.multi_objectives) > 1 and not isinstance(self.cost_for_crash, list): # type: ignore - self.cost_for_crash = [self.cost_for_crash] * len(self.multi_objectives) # type: ignore - - def __getstate__(self) -> Dict[str, Any]: - d = dict(self.__dict__) - del d["logger"] - return d - - def __setstate__(self, d: Dict[str, Any]) -> None: - self.__dict__.update(d) - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - def _to_str_and_warn(self, list_: List[Any]) -> List[Any]: - warn_ = False - for i, e in enumerate(list_): - if e is not None and not isinstance(e, str): - warn_ = True - try: - list_[i] = str(e) - except ValueError: - raise ValueError("Failed to cast all instances to str") - if warn_: - self.logger.warning("All instances were casted to str.") - return list_ - - def write(self) -> None: - """Write scenario to self.output_dir/scenario.txt.""" - self.out_writer.write_scenario_file(self) diff --git a/smac/smac_cli.py b/smac/smac_cli.py deleted file mode 100644 index d92e7c9b9..000000000 --- a/smac/smac_cli.py +++ /dev/null @@ -1,236 +0,0 @@ -from typing import List, Optional, Tuple - -import logging -import os -import sys - -import numpy as np - -from smac.configspace import Configuration -from smac.facade.experimental.hydra_facade import ( # type: ignore[attr-defined] # noqa F821 - Hydra, -) -from smac.facade.psmac_facade import PSMAC # type: ignore[attr-defined] # noqa F821 -from smac.facade.roar_facade import ROAR -from smac.facade.smac_ac_facade import SMAC4AC -from smac.facade.smac_bb_facade import SMAC4BB -from smac.facade.smac_hpo_facade import SMAC4HPO -from smac.runhistory.runhistory import RunHistory -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import FirstRunCrashedException, TAEAbortException -from smac.utils.io.cmd_reader import CMDReader -from smac.utils.io.output_directory import create_output_directory -from smac.utils.io.traj_logging import TrajLogger -from smac.utils.merge_foreign_data import merge_foreign_data_from_file - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Marius Lindauer" -__email__ = "lindauer@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -class SMACCLI(object): - """Main class of SMAC.""" - - def __init__(self) -> None: - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - def main_cli(self, commandline_arguments: Optional[List[str]] = None) -> None: - """Main function of SMAC for CLI interface.""" - self.logger.info("SMAC call: %s" % (" ".join(sys.argv))) - - cmd_reader = CMDReader() - kwargs = {} - if commandline_arguments: - kwargs["commandline_arguments"] = commandline_arguments - main_args_, smac_args_, scen_args_ = cmd_reader.read_cmd(**kwargs) - - root_logger = logging.getLogger() - root_logger.setLevel(main_args_.verbose_level) - logger_handler = logging.StreamHandler(stream=sys.stdout) - if root_logger.level >= logging.INFO: - formatter = logging.Formatter("%(levelname)s:\t%(message)s") - else: - formatter = logging.Formatter("%(asctime)s:%(levelname)s:%(name)s:\t%(message)s", "%Y-%m-%d %H:%M:%S") - logger_handler.setFormatter(formatter) - root_logger.addHandler(logger_handler) - # remove default handler - if len(root_logger.handlers) > 1: - root_logger.removeHandler(root_logger.handlers[0]) - - # Create defaults - rh = None - initial_configs = None - stats = None - incumbent = None - - # Create scenario-object - scenario = {} - scenario.update(vars(smac_args_)) - scenario.update(vars(scen_args_)) - scen = Scenario(scenario=scenario) - - # Restore state - if main_args_.restore_state: - root_logger.debug("Restoring state from %s...", main_args_.restore_state) - restore_state = main_args_.restore_state - rh, stats, traj_list_aclib, traj_list_old = self.restore_state(scen, restore_state) - - scen.output_dir_for_this_run = create_output_directory( - scen, - main_args_.seed, - root_logger, - ) - scen.write() - incumbent = self.restore_state_after_output_dir(scen, stats, traj_list_aclib, traj_list_old) - - if main_args_.warmstart_runhistory: - rh = RunHistory() - - scen, rh = merge_foreign_data_from_file( - scenario=scen, - runhistory=rh, - in_scenario_fn_list=main_args_.warmstart_scenario, - in_runhistory_fn_list=main_args_.warmstart_runhistory, - cs=scen.cs, # type: ignore[attr-defined] # noqa F821 - ) - - if main_args_.warmstart_incumbent: - initial_configs = [scen.cs.get_default_configuration()] # type: ignore[attr-defined] # noqa F821 - for traj_fn in main_args_.warmstart_incumbent: - trajectory = TrajLogger.read_traj_aclib_format( - fn=traj_fn, - cs=scen.cs, # type: ignore[attr-defined] # noqa F821 - ) - initial_configs.append(trajectory[-1]["incumbent"]) - - if main_args_.mode == "SMAC4AC": - optimizer = SMAC4AC( - scenario=scen, - rng=np.random.RandomState(main_args_.seed), - runhistory=rh, - initial_configurations=initial_configs, - stats=stats, - restore_incumbent=incumbent, - run_id=main_args_.seed, - ) - elif main_args_.mode == "SMAC4HPO": - optimizer = SMAC4HPO( - scenario=scen, - rng=np.random.RandomState(main_args_.seed), - runhistory=rh, - initial_configurations=initial_configs, - stats=stats, - restore_incumbent=incumbent, - run_id=main_args_.seed, - ) - elif main_args_.mode == "SMAC4BB": - optimizer = SMAC4BB( - scenario=scen, - rng=np.random.RandomState(main_args_.seed), - runhistory=rh, - initial_configurations=initial_configs, - stats=stats, - restore_incumbent=incumbent, - run_id=main_args_.seed, - ) - elif main_args_.mode == "ROAR": - optimizer = ROAR( - scenario=scen, - rng=np.random.RandomState(main_args_.seed), - runhistory=rh, - initial_configurations=initial_configs, - run_id=main_args_.seed, - ) - elif main_args_.mode == "Hydra": - optimizer = Hydra( - scenario=scen, - rng=np.random.RandomState(main_args_.seed), - runhistory=rh, - initial_configurations=initial_configs, - stats=stats, - restore_incumbent=incumbent, - run_id=main_args_.seed, - random_configuration_chooser=main_args_.random_configuration_chooser, - n_iterations=main_args_.hydra_iterations, - val_set=main_args_.hydra_validation, - incs_per_round=main_args_.hydra_incumbents_per_round, - n_optimizers=main_args_.hydra_n_optimizers, - ) - elif main_args_.mode == "PSMAC": - optimizer = PSMAC( - scenario=scen, - rng=np.random.RandomState(main_args_.seed), - run_id=main_args_.seed, - shared_model=smac_args_.shared_model, - validate=main_args_.psmac_validate, - n_optimizers=main_args_.hydra_n_optimizers, - n_incs=main_args_.hydra_incumbents_per_round, - ) - try: - optimizer.optimize() - except (TAEAbortException, FirstRunCrashedException) as err: - self.logger.error(err) - - def restore_state( - self, - scen: Scenario, - restore_state: str, - ) -> Tuple[RunHistory, Stats, List, List]: - """Read in files for state-restoration: runhistory, stats, trajectory.""" - # Check for folder and files - rh_path = os.path.join(restore_state, "runhistory.json") - stats_path = os.path.join(restore_state, "stats.json") - traj_path_aclib = os.path.join(restore_state, "traj_aclib2.json") - traj_path_old = os.path.join(restore_state, "traj_old.csv") - _ = os.path.join(restore_state, "scenario.txt") - if not os.path.isdir(restore_state): - raise FileNotFoundError("Could not find folder from which to restore.") - # Load runhistory and stats - rh = RunHistory() - rh.load_json(rh_path, scen.cs) # type: ignore[attr-defined] # noqa F821 - self.logger.debug("Restored runhistory from %s", rh_path) - stats = Stats(scen) - stats.load(stats_path) - self.logger.debug("Restored stats from %s", stats_path) - with open(traj_path_aclib, "r") as traj_fn: - traj_list_aclib = traj_fn.readlines() - with open(traj_path_old, "r") as traj_fn: - traj_list_old = traj_fn.readlines() - return rh, stats, traj_list_aclib, traj_list_old - - def restore_state_after_output_dir( - self, - scen: Scenario, - stats: Stats, - traj_list_aclib: List, - traj_list_old: List, - ) -> Configuration: - """Finish processing files for state-restoration. - - Trajectory is read in, but needs to be written to new output- - folder. Therefore, the output-dir is created. This needs to be - considered in the SMAC-facade. - """ - # write trajectory-list - traj_path_aclib = os.path.join(scen.output_dir, "traj_aclib2.json") # type: ignore[attr-defined] # noqa F821 - traj_path_old = os.path.join(scen.output_dir, "traj_old.csv") # type: ignore[attr-defined] # noqa F821 - with open(traj_path_aclib, "w") as traj_fn: - traj_fn.writelines(traj_list_aclib) - with open(traj_path_old, "w") as traj_fn: - traj_fn.writelines(traj_list_old) - # read trajectory to retrieve incumbent - # TODO replace this with simple traj_path_aclib? - trajectory = TrajLogger.read_traj_aclib_format(fn=traj_path_aclib, cs=scen.cs) # type: ignore[attr-defined] # noqa F821 - incumbent = trajectory[-1]["incumbent"] - self.logger.debug("Restored incumbent %s from %s", incumbent, traj_path_aclib) - return incumbent - - -def cmd_line_call() -> None: - """Entry point to be installable to /user/bin.""" - smac = SMACCLI() - smac.main_cli() diff --git a/smac/stats.py b/smac/stats.py new file mode 100644 index 000000000..4e878567b --- /dev/null +++ b/smac/stats.py @@ -0,0 +1,230 @@ +from __future__ import annotations + +import dataclasses +import json +import time + +from ConfigSpace.configuration_space import Configuration + +from smac.runhistory.dataclasses import TrajectoryItem +from smac.scenario import Scenario +from smac.utils.logging import get_logger + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +logger = get_logger(__name__) + + +class Stats: + """Statistics which are collected during the optimization process. + + Parameters + ---------- + scenario : Scenario + """ + + def __init__(self, scenario: Scenario): + self._scenario = scenario + self.reset() + + @property + def submitted(self) -> int: + """How many trials have been submitted.""" + return self._submitted + + @property + def finished(self) -> int: + """How many trials have been evaluated.""" + return self._finished + + @property + def n_configs(self) -> int: + """How many different configurations have been evaluated.""" + return self._n_configs + + @property + def incumbent_changed(self) -> int: + """How often the incumbent (aka best found configuration) changed.""" + return self._incumbent_changed + + def reset(self) -> None: + """Resets the internal variables.""" + self._submitted = 0 + self._finished = 0 + self._n_configs = 0 + self._incumbent_changed = 0 + self._walltime_used = 0.0 + self._target_function_walltime_used = 0.0 + + # Trajectory + self._trajectory: list[TrajectoryItem] = [] + + # Debug stats + self._n_configs_per_intensify = 0 + self._n_calls_of_intensify = 0 + + # Exponential moving average + self._ema_n_configs_per_intensifiy = 0.0 + self._EMA_ALPHA = 0.2 + + self._start_time = 0.0 + + def add_incumbent( + self, + cost: float | list[float], + incumbent: Configuration, + budget: float | None = None, + ) -> None: + """Adds a new incumbent to the trajectory. + + Parameters + ---------- + cost : float | list[float] + Cost(s) of the incumbent. + incumbent : Configuration + The incumbent configuration. + budget : float | None, defaults to None + The used budget for the incumbent. + """ + self._incumbent_changed += 1 + item = TrajectoryItem( + incumbent=incumbent, + cost=cost, + budget=budget, + walltime_used=self.get_used_walltime(), + num_trial=self._finished, + ) + self._trajectory.append(item) + + def get_incumbent(self) -> Configuration | None: + """Returns the incumbent configuration. + + Returns + ------- + incumbent : Configuration | None + The incumbent configuration if it is available. + """ + if self._incumbent_changed == 0: + return None + + # Transform dictionary to configuration + incumbent = self._trajectory[-1].incumbent + return Configuration(self._scenario.configspace, values=incumbent) + + def start_timing(self) -> None: + """Starts the timer (for the runtime configuration budget). Substracting wallclock time so we can continue + loaded stats. + """ + self._start_time = time.time() - self._walltime_used + + def get_used_walltime(self) -> float: + """Returns used wallclock time.""" + return time.time() - self._start_time + + def get_remaing_walltime(self) -> float: + """Subtracts the runtime configuration budget with the used wallclock time.""" + return self._scenario.walltime_limit - (time.time() - self._start_time) + + def get_remaining_cputime(self) -> float: + """Subtracts the target function running budget with the used time.""" + return self._scenario.cputime_limit - self._target_function_walltime_used + + def get_remaining_trials(self) -> int: + """Subtract the target function runs in the scenario with the used ta runs.""" + return self._scenario.n_trials - self._submitted + + def is_budget_exhausted(self) -> bool: + """Check whether the the remaining walltime, cputime or trials was exceeded.""" + A = self.get_remaing_walltime() < 0 + B = self.get_remaining_cputime() < 0 + C = self.get_remaining_trials() < 0 + + return A or B or C + + def update_average_configs_per_intensify(self, n_configs: int) -> None: + """Updates statistics how many configurations on average per used in intensify. + + Parameters + ---------- + n_configs : int + Number of configurations in current intensify. + """ + self._n_calls_of_intensify += 1 + self._n_configs_per_intensify += n_configs + + if self._n_calls_of_intensify == 1: + self._ema_n_configs_per_intensifiy = float(n_configs) + else: + self._ema_n_configs_per_intensifiy = ( + 1 - self._EMA_ALPHA + ) * self._ema_n_configs_per_intensifiy + self._EMA_ALPHA * n_configs + + def print(self, debug: bool = True) -> None: + """Prints all statistics.""" + log = logger.info + if debug: + log = logger.debug + + log( + "\n" + f"--- STATISTICS -------------------------------------\n" + f"--- Incumbent changed: {self._incumbent_changed - 1}\n" + f"--- Submitted trials: {self._submitted} / {self._scenario.n_trials}\n" + f"--- Finished trials: {self._finished} / {self._scenario.n_trials}\n" + f"--- Configurations: {self._n_configs}\n" + f"--- Used wallclock time: {round(self.get_used_walltime())} / {self._scenario.walltime_limit} sec\n" + "--- Used target function runtime: " + f"{round(self._target_function_walltime_used, 2)} / {self._scenario.cputime_limit} sec\n" + f"----------------------------------------------------" + ) + + if self._n_calls_of_intensify > 0: + logger.debug("Debug Statistics:") + logger.debug( + "Average Configurations per Intensify: %.2f" + % (self._n_configs_per_intensify / self._n_calls_of_intensify) + ) + logger.debug( + "Exponential Moving Average of Configurations per Intensify: %.2f" + % (self._ema_n_configs_per_intensifiy) + ) + + def save(self) -> None: + """Save all relevant attributes to a json file.""" + data = { + "submitted": self._submitted, + "finished": self._finished, + "n_configs": self._n_configs, + "walltime_used": self.get_used_walltime(), + "target_function_walltime_used": self._target_function_walltime_used, + "incumbent_changed": self._incumbent_changed, + "trajectory": [dataclasses.asdict(item) for item in self._trajectory], + } + + assert self._scenario.output_directory + filename = self._scenario.output_directory + filename.mkdir(parents=True, exist_ok=True) + filename = filename / "stats.json" + + logger.debug(f"Saving stats to `{filename}`") + + with open(filename, "w") as fh: + json.dump(data, fh, indent=4) + + def load(self) -> None: + """Load all attributes from a file into the stats object.""" + assert self._scenario.output_directory + filename = self._scenario.output_directory / "stats.json" + + with open(filename, "r") as fh: + data = json.load(fh) + + self._submitted = data["submitted"] + self._finished = data["finished"] + self._n_configs = data["n_configs"] + self._walltime_used = data["walltime_used"] + self._target_function_walltime_used = data["target_function_walltime_used"] + self._incumbent_changed = data["incumbent_changed"] + self._trajectory = [TrajectoryItem(**item) for item in data["trajectory"]] diff --git a/smac/stats/stats.py b/smac/stats/stats.py deleted file mode 100644 index 903dc0fde..000000000 --- a/smac/stats/stats.py +++ /dev/null @@ -1,210 +0,0 @@ -from typing import Optional - -import json -import logging -import os -import time - -import numpy as np - -from smac.scenario.scenario import Scenario - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2016, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Marius Lindauer" -__email__ = "lindauer@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -class Stats(object): - """All statistics collected during configuration run. Written to output- directory to be - restored. - - Parameters - ---------- - scenario : Scenario - - Attributes - ---------- - submitted_ta_runs - finished_ta_runs - n_configs - wallclock_time_used - ta_time_used - inc_changed - """ - - def __init__(self, scenario: Scenario): - self.__scenario = scenario - - self.submitted_ta_runs = 0 - self.finished_ta_runs = 0 - self.n_configs = 0 - self.wallclock_time_used = 0.0 - self.ta_time_used = 0.0 - self.inc_changed = 0 - - # debug stats - self._n_configs_per_intensify = 0 - self._n_calls_of_intensify = 0 - # exponential moving average - self._ema_n_configs_per_intensifiy = 0.0 - self._EMA_ALPHA = 0.2 - - self._start_time = np.NaN - self._logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - def save(self) -> None: - """Save all relevant attributes to json-dictionary.""" - if not self.__scenario.output_dir_for_this_run: - self._logger.debug("No scenario.output_dir: not saving stats!") - return - # Set used_wallclock_time - self.wallclock_time_used = self.get_used_wallclock_time() - - data = {} - - for v in vars(self): - if v not in ["_Stats__scenario", "_logger", "_start_time"]: - data[v] = getattr(self, v) - - path = os.path.join(self.__scenario.output_dir_for_this_run, "stats.json") - self._logger.debug("Saving stats to %s", path) - with open(path, "w") as fh: - json.dump(data, fh) - - def load(self, fn: Optional[str] = None) -> None: - """Load all attributes from dictionary in file into stats-object. - - Parameters - ---------- - fn: string or None - Path to file to load stats from. If no path is given, the path given - in the current scenario is used. - """ - if not fn: - assert self.__scenario.output_dir_for_this_run is not None # please mypy - fn = os.path.join(self.__scenario.output_dir_for_this_run, "stats.json") - with open(fn, "r") as fh: - data = json.load(fh) - - # Set attributes - for key in data: - if hasattr(self, key): - setattr(self, key, data[key]) - else: - raise ValueError("Stats does not recognize {}".format(key)) - - def start_timing(self) -> None: - """Starts the timer (for the runtime configuration budget). - - Substracting wallclock time used so we can continue loaded Stats. - """ - if self.__scenario: - self._start_time = time.time() - self.wallclock_time_used - else: - raise ValueError("Scenario is missing") - - def get_used_wallclock_time(self) -> float: - """Returns used wallclock time. - - Returns - ------- - wallclock_time : int - used wallclock time in sec - """ - return time.time() - self._start_time - - def get_remaing_time_budget(self) -> float: - """Subtracts the runtime configuration budget with the used wallclock time.""" - if self.__scenario: - return self.__scenario.wallclock_limit - (time.time() - self._start_time) - else: - raise ValueError("Scenario is missing") - - def get_remaining_ta_runs(self) -> int: - """Subtract the target algorithm runs in the scenario with the used ta runs.""" - if self.__scenario: - return self.__scenario.ta_run_limit - self.submitted_ta_runs # type: ignore[attr-defined] # noqa F821 - else: - raise ValueError("Scenario is missing") - - def get_remaining_ta_budget(self) -> float: - """Subtracts the ta running budget with the used time.""" - if self.__scenario: - return self.__scenario.algo_runs_timelimit - self.ta_time_used - else: - raise ValueError("Scenario is missing") - - def is_budget_exhausted(self) -> bool: - """Check whether the configuration budget for time budget, ta_budget and submitted_ta_runs - is exhausted. - - Returns - ------- - exhaustedness: boolean - true if one of the budgets is exhausted. - """ - return ( - self.get_remaing_time_budget() < 0 or self.get_remaining_ta_budget() < 0 - ) or self.get_remaining_ta_runs() <= 0 - - def update_average_configs_per_intensify(self, n_configs: int) -> None: - """Updates statistics how many configurations on average per used in intensify. - - Parameters - ---------- - n_configs: int - number of configurations in current intensify - """ - self._n_calls_of_intensify += 1 - self._n_configs_per_intensify += n_configs - - if self._n_calls_of_intensify == 1: - self._ema_n_configs_per_intensifiy = float(n_configs) - else: - self._ema_n_configs_per_intensifiy = ( - 1 - self._EMA_ALPHA - ) * self._ema_n_configs_per_intensifiy + self._EMA_ALPHA * n_configs - - def print_stats(self, debug_out: bool = False) -> None: - """Prints all statistics. - - Parameters - ---------- - debug: bool - use logging.debug instead of logging.info if set to true - """ - log_func = self._logger.info - if debug_out: - log_func = self._logger.debug - - log_func("---------------------STATISTICS---------------------") - log_func("Incumbent changed: %d" % (self.inc_changed - 1)) # first change is default conf - log_func( - "Submitted target algorithm runs: %d / %s" - % (self.submitted_ta_runs, str(self.__scenario.ta_run_limit)) # type: ignore[attr-defined] # noqa F821 - ) - log_func( - "Finished target algorithm runs: %d / %s" - % (self.finished_ta_runs, str(self.__scenario.ta_run_limit)) # type: ignore[attr-defined] # noqa F821 - ) - log_func("Configurations: %d" % (self.n_configs)) - log_func( - "Used wallclock time: %.2f / %.2f sec " % (time.time() - self._start_time, self.__scenario.wallclock_limit) - ) - log_func( - "Used target algorithm runtime: %.2f / %.2f sec" % (self.ta_time_used, self.__scenario.algo_runs_timelimit) - ) - self._logger.debug("Debug Statistics:") - if self._n_calls_of_intensify > 0: - self._logger.debug( - "Average Configurations per Intensify: %.2f" - % (self._n_configs_per_intensify / self._n_calls_of_intensify) - ) - self._logger.debug( - "Exponential Moving Average of Configurations per Intensify: %.2f" - % (self._ema_n_configs_per_intensifiy) - ) - log_func("----------------------------------------------------") diff --git a/smac/tae/__init__.py b/smac/tae/__init__.py deleted file mode 100644 index 527c1ee56..000000000 --- a/smac/tae/__init__.py +++ /dev/null @@ -1,59 +0,0 @@ -from typing import Any, Dict - -from enum import Enum - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Marius Lindauer" -__email__ = "lindauer@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -class StatusType(Enum): - """Class to define numbers for status types.""" - - SUCCESS = 1 - TIMEOUT = 2 - CRASHED = 3 - ABORT = 4 - MEMOUT = 5 - CAPPED = 6 - # Only relevant for SH/HB. Run might have a results, but should not be considered further. - # By default, these runs will always be considered for building the model. Potential use cases: - # 1) The run has converged and does not benefit from a higher budget - # 2) The run has exhausted given resources and will not benefit from higher budgets - DONOTADVANCE = 7 - # Gracefully exit SMAC - wait for currently executed runs to finish - STOP = 8 - # In case a job was submited, but it has not finished - RUNNING = 9 - - @staticmethod - def enum_hook(obj: Dict) -> Any: - """Hook function passed to json-deserializer as "object_hook". - - EnumEncoder in runhistory/runhistory. - """ - if "__enum__" in obj: - # object is marked as enum - name, member = obj["__enum__"].split(".") - if name == "StatusType": - return getattr(globals()[name], member) - return obj - - -class TAEAbortException(Exception): - """Exception indicating that the target algorithm suggests an ABORT of SMAC, usually because it - assumes that all further runs will surely fail. - """ - - pass - - -class FirstRunCrashedException(TAEAbortException): - """Exception indicating that the first run crashed (depending on options this could trigger an - ABORT of SMAC). - """ - - pass diff --git a/smac/tae/base.py b/smac/tae/base.py deleted file mode 100644 index 2178934ee..000000000 --- a/smac/tae/base.py +++ /dev/null @@ -1,323 +0,0 @@ -from abc import ABC, abstractmethod -from typing import Callable, Dict, List, Optional, Tuple, Union - -import math -import time -import traceback - -import numpy as np - -from smac.configspace import Configuration -from smac.runhistory.runhistory import RunInfo, RunValue -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.utils.constants import MAXINT -from smac.utils.logging import PickableLoggerAdapter - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class BaseRunner(ABC): - """Interface class to handle the execution of SMAC' configurations. - - This interface defines how to interact with the SMBO loop. - The complexity of running a configuration as well as handling the - results is abstracted to the SMBO via a BaseRunner. - - From SMBO perspective, launching a configuration follows a - submit/collect scheme as follows: - - 1. A run is launched via submit_run() - - 1. Submit_run internally calls run_wrapper(), a method that - contains common processing functions among different runners, - for example, handling capping and stats checking. - - 2. A class that implements BaseRunner defines run() which is - really the algorithm to translate a RunInfo to a RunValue, i.e. - a configuration to an actual result. - - 2. A completed run is collected via get_finished_runs(), which returns - any finished runs, if any. - - 3. This interface also offers the method wait() as a mechanism to make - sure we have enough data in the next iteration to make a decision. For - example, the intensifier might not be able to select the next challenger - until more results are available. - - Parameters - ---------- - ta : Union[List[str], Callable] - target algorithm - stats: Stats - stats object to collect statistics about runtime/additional info - multi_objectives: List[str] - names of the objectives, by default it is a single objective parameter "cost" - run_obj: str - run objective of SMAC - par_factor: int - penalization factor - cost_for_crash : float - cost that is used in case of crashed runs (including runs - that returned NaN or inf) - abort_on_first_run_crash: bool - if true and first run crashes, raise FirstRunCrashedException - - - Attributes - ---------- - results - ta - stats - run_obj - par_factor - cost_for_crash - abort_on_first_run_crash - """ - - def __init__( - self, - ta: Union[List[str], Callable], - stats: Stats, - multi_objectives: List[str] = ["cost"], - run_obj: str = "runtime", - par_factor: int = 1, - cost_for_crash: Union[float, List[float]] = float(MAXINT), - abort_on_first_run_crash: bool = True, - ): - # The results is a FIFO structure, implemented via a list - # (because the Queue lock is not pickable). Finished runs are - # put in this list and collected via process_finished_runs - self.results = [] # type: List[Tuple[RunInfo, RunValue]] - - # Below state the support for a Runner algorithm that - # implements a ta - self.ta = ta - self.stats = stats - self.multi_objectives = multi_objectives - self.run_obj = run_obj - self.par_factor = par_factor - self.cost_for_crash = cost_for_crash - self.abort_on_first_run_crash = abort_on_first_run_crash - self.logger = PickableLoggerAdapter(self.__module__ + "." + self.__class__.__name__) - self._supports_memory_limit = False - - super().__init__() - - @abstractmethod - def submit_run(self, run_info: RunInfo) -> None: - """This function submits a configuration embedded in a RunInfo object, and uses one of the - workers to produce a result (such result will eventually be available on the self.results - FIFO). - - This interface method will be called by SMBO, with the expectation - that a function will be executed by a worker. - - What will be executed is dictated by run_info, and "how" will it be - executed is decided via the child class that implements a run() method. - - Because config submission can be a serial/parallel endeavor, - it is expected to be implemented by a child class. - - Parameters - ---------- - run_info: RunInfo - An object containing the configuration and the necessary data to run it - """ - pass - - @abstractmethod - def run( - self, - config: Configuration, - instance: str, - cutoff: Optional[float] = None, - seed: int = 12345, - budget: Optional[float] = None, - instance_specific: str = "0", - ) -> Tuple[StatusType, float, float, Dict]: - """Runs target algorithm with configuration on instance with - instance specifics. - - for at most. - - seconds and random seed - - This method exemplifies how to defined the run() method - - Parameters - ---------- - config : Configuration - dictionary param -> value - instance : string - problem instance - cutoff : float, optional - Wallclock time limit of the target algorithm. If no value is - provided no limit will be enforced. - seed : int - random seed - budget : float, optional - A positive, real-valued number representing an arbitrary limit to the target - algorithm. Handled by the target algorithm internally - instance_specific: str - instance specific information (e.g., domain file or solution) - - Returns - ------- - status: enum of StatusType (int) - {SUCCESS, TIMEOUT, CRASHED, ABORT} - cost: float - cost/regret/quality (float) (None, if not returned by TA) - runtime: float - runtime (None if not returned by TA) - additional_info: dict - all further additional run information - """ - pass - - def run_wrapper( - self, - run_info: RunInfo, - ) -> Tuple[RunInfo, RunValue]: - """Wrapper around run() to exec and check the execution of a given config file. - - This function encapsulates common handling/processing, so that run() implementation - is simplified. - - Parameters - ---------- - run_info : RunInfo - Object that contains enough information to execute a configuration run in - isolation. - - Returns - ------- - RunInfo: - an object containing the configuration launched - RunValue: - Contains information about the status/performance of config - """ - start = time.time() - - if run_info.cutoff is None and self.run_obj == "runtime": - if self.logger: - self.logger.critical( - "For scenarios optimizing running time " - "(run objective), a cutoff time is required, " - "but not given to this call." - ) - raise ValueError( - "For scenarios optimizing running time " - "(run objective), a cutoff time is required, " - "but not given to this call." - ) - cutoff = None - if run_info.cutoff is not None: - cutoff = int(math.ceil(run_info.cutoff)) - - try: - status, cost, runtime, additional_info = self.run( - config=run_info.config, - instance=run_info.instance, - cutoff=cutoff, - seed=run_info.seed, - budget=run_info.budget, - instance_specific=run_info.instance_specific, - ) - except Exception as e: - status = StatusType.CRASHED - cost = self.cost_for_crash # type: ignore - runtime = time.time() - start - - # Add context information to the error message - exception_traceback = traceback.format_exc() - error_message = repr(e) - additional_info = {"traceback": exception_traceback, "error": error_message} - - end = time.time() - - if run_info.budget == 0 and status == StatusType.DONOTADVANCE: - raise ValueError("Cannot handle DONOTADVANCE state when using intensify or SH/HB on " "instances.") - - # Catch NaN or inf. - if (self.run_obj == "runtime" and not np.isfinite(runtime)) or ( - self.run_obj == "quality" and not np.all(np.isfinite(cost)) - ): - if self.logger: - self.logger.warning( - "Target Algorithm returned NaN or inf as {}. " - "Algorithm run is treated as CRASHED, cost " - "is set to {} for quality scenarios. " - '(Change value through "cost_for_crash"' - "-option.)".format(self.run_obj, self.cost_for_crash) - ) - status = StatusType.CRASHED - - if self.run_obj == "runtime": - # The following line pleases mypy - we already check for cutoff not being none above, - # prior to calling run. However, mypy assumes that the data type of cutoff - # is still Optional[int] - assert cutoff is not None - if runtime > self.par_factor * cutoff: - self.logger.warning( - "Returned running time is larger " - "than {0} times the passed cutoff time. " - "Clamping to {0} x cutoff.".format(self.par_factor) - ) - runtime = cutoff * self.par_factor - status = StatusType.TIMEOUT - if status == StatusType.SUCCESS: - cost = runtime - else: - cost = cutoff * self.par_factor - if status == StatusType.TIMEOUT and run_info.capped: - status = StatusType.CAPPED - else: - if status == StatusType.CRASHED: - cost = self.cost_for_crash # type: ignore - - return run_info, RunValue( - status=status, - cost=cost, - time=runtime, - additional_info=additional_info, - starttime=start, - endtime=end, - ) - - @abstractmethod - def get_finished_runs(self) -> List[Tuple[RunInfo, RunValue]]: - """This method returns any finished configuration, and returns a list with the results of - exercising the configurations. This class keeps populating results to self.results until a - call to get_finished runs is done. In this case, the self.results list is emptied and all - RunValues produced by running run() are returned. - - Returns - ------- - List[RunInfo, RunValue]: A list of pais RunInfo/RunValues - a submitted configuration - """ - raise NotImplementedError() - - @abstractmethod - def wait(self) -> None: - """SMBO/intensifier might need to wait for runs to finish before making a decision. - - This method waits until 1 run completes - """ - pass - - @abstractmethod - def pending_runs(self) -> bool: - """Whether or not there are configs still running. - - Generally if the runner is serial, launching a run instantly returns it's result. On - parallel runners, there might be pending configurations to complete. - """ - pass - - @abstractmethod - def num_workers(self) -> int: - """Return the active number of workers that will execute tae runs.""" - pass diff --git a/smac/tae/dask_runner.py b/smac/tae/dask_runner.py deleted file mode 100644 index 3e7fa8d62..000000000 --- a/smac/tae/dask_runner.py +++ /dev/null @@ -1,305 +0,0 @@ -from typing import Dict, List, Optional, Tuple - -import os -import time -import warnings - -import dask -from dask.distributed import Client, Future, wait - -from smac.configspace import Configuration -from smac.runhistory.runhistory import RunInfo, RunValue -from smac.tae import StatusType -from smac.tae.base import BaseRunner - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class DaskParallelRunner(BaseRunner): - """Interface to submit and collect a job in a distributed fashion. - - DaskParallelRunner is intended to comply with the bridge design pattern. - - Nevertheless, to reduce the amount of code within single-vs-parallel - implementations, DaskParallelRunner wraps a BaseRunner object which - is then executed in parallel on n_workers. - - This class then is constructed by passing a BaseRunner that implements - a run() method, and is capable of doing so in a serial fashion. Then, - this wrapper class called DaskParallelRunner uses dask to initialize - N number of BaseRunner that actively wait of a RunInfo to produce a - RunValue object. - - To be more precise, the work model is then: - 1. The smbo.intensifier dictates "what" to run (a configuration/instance/seed) - via a RunInfo object. - 2. a tae_runner takes this RunInfo object and launches the task via - tae_runner.submit_run(). In the case of DaskParallelRunner, n_workers - receive a pickle-object of DaskParallelRunner.single_worker, each with a - run() method coming from DaskParallelRunner.single_worker.run() - 3. RunInfo objects are run in a distributed fashion, an their results are - available locally to each worker. Such result is collected by - DaskParallelRunner.get_finished_runs() and then passed to the SMBO. - 4. Exceptions are also locally available to each worker and need to be - collected. - - Dask works with Future object which are managed via the DaskParallelRunner.client. - - - Parameters - ---------- - single_worker: BaseRunner - A runner to run in a distributed fashion - n_workers: int - Number of workers to use for distributed run. Will be ignored if ``dask_client`` is not ``None``. - patience: int - How much to wait for workers to be available if one fails - output_directory: str, optional - If given, this will be used for the dask worker directory and for storing server information. - If a dask client is passed, it will only be used for storing server information as the - worker directory must be set by the program/user starting the workers. - dask_client: dask.distributed.Client - User-created dask client, can be used to start a dask cluster and then attach SMAC to it. - - - Attributes - ---------- - results - ta - stats - run_obj - par_factor - cost_for_crash - abort_i_first_run_crash - n_workers - futures - client - """ - - def __init__( - self, - single_worker: BaseRunner, - n_workers: int, - patience: int = 5, - output_directory: Optional[str] = None, - dask_client: Optional[dask.distributed.Client] = None, - ): - super(DaskParallelRunner, self).__init__( - ta=single_worker.ta, - stats=single_worker.stats, - multi_objectives=single_worker.multi_objectives, - run_obj=single_worker.run_obj, - par_factor=single_worker.par_factor, - cost_for_crash=single_worker.cost_for_crash, - abort_on_first_run_crash=single_worker.abort_on_first_run_crash, - ) - - # The single worker, which is replicated on a need - # basis to every compute node - self.single_worker = single_worker - self.n_workers = n_workers - - # How much time to wait for workers to be available - self.patience = patience - - self.output_directory = output_directory - - # Because a run() method can have pynisher, we need to prevent the multiprocessing - # workers to be instantiated as demonic - this cannot be passed via worker_kwargs - dask.config.set({"distributed.worker.daemon": False}) - if dask_client is None: - self.close_client_at_del = True - self.client = Client( - n_workers=self.n_workers, - processes=True, - threads_per_worker=1, - local_directory=output_directory, - ) - if self.output_directory: - self.scheduler_file = os.path.join(self.output_directory, ".dask_scheduler_file") - self.client.write_scheduler_file(scheduler_file=self.scheduler_file) - else: - self.close_client_at_del = False - self.client = dask_client - self.futures = [] # type: List[Future] - - self.scheduler_info = self.client._get_scheduler_info() - - def submit_run(self, run_info: RunInfo) -> None: - """This function submits a configuration embedded in a run_info object, and uses one of the - workers to produce a result locally to each worker. - - The execution of a configuration follows this procedure: - 1. SMBO/intensifier generates a run_info - 2. SMBO calls submit_run so that a worker launches the run_info - 3. submit_run internally calls self.run(). it does so via a call to self.run_wrapper() - which contains common code that any run() method will otherwise have to implement, like - capping check. - - Child classes must implement a run() method. - All results will be only available locally to each worker, so the - main node needs to collect them. - - Parameters - ---------- - run_info: RunInfo - An object containing the configuration and the necessary data to run it - """ - # Check for resources or block till one is available - if not self._workers_available(): - futures = wait(self.futures, return_when="FIRST_COMPLETED") - futures.done - self._extract_completed_runs_from_futures() - - # In code check to make sure that there are resources - if not self._workers_available(): - warnings.warn("No workers are available. This could mean workers crashed" "Waiting for new workers...") - time.sleep(self.patience) - if not self._workers_available(): - raise ValueError( - "Tried to execute a job, but no worker was " - "available. This likely means that a worker crashed " - "or no workers were properly configured." - ) - - # At this point we can submit the job - # For `pure=False`, see - # http://distributed.dask.org/en/stable/client.html#pure-functions-by-default - self.futures.append(self.client.submit(self.single_worker.run_wrapper, run_info, pure=False)) - - def get_finished_runs(self) -> List[Tuple[RunInfo, RunValue]]: - """This method returns any finished configuration, and returns a list with the results of - exercising the configurations. This class keeps populating results to self.results until a - call to get_finished runs is done. In this case, the self.results list is emptied and all - RunValues produced by running run() are returned. - - Returns - ------- - List[RunInfo, RunValue]: A list of RunValues (and respective RunInfo), that is, - the results of executing a run_info - a submitted configuration - """ - # Proactively see if more configs have finished - self._extract_completed_runs_from_futures() - - results_list = [] - while self.results: - results_list.append(self.results.pop()) - return results_list - - def _extract_completed_runs_from_futures(self) -> None: - """A run is over, when a future has done() equal true. This function collects the completed - futures and move them from self.futures to self.results. - - We make sure futures never exceed the capacity of the scheduler - """ - # In code check to make sure we don;t exceed resource allocation - if len(self.futures) > sum(self.client.nthreads().values()): - warnings.warn( - "More running jobs than resources available " - "Should not have more futures/runs in remote workers " - "than the number of workers. This could mean a worker " - "crashed and was not able to be recovered by dask. " - ) - - # A future is removed to the list of futures as an indication - # that a worker is available to take in an extra job - done_futures = [f for f in self.futures if f.done()] - for future in done_futures: - self.results.append(future.result()) - self.futures.remove(future) - - def wait(self) -> None: - """SMBO/intensifier might need to wait for runs to finish before making a decision. - - This class waits until 1 run completes - """ - if self.futures: - futures = wait(self.futures, return_when="FIRST_COMPLETED") - futures.done - - def pending_runs(self) -> bool: - """Whether or not there are configs still running. - - Generally if the runner is serial, launching a run instantly returns it's result. On - parallel runners, there might be pending configurations to complete. - """ - # If there are futures available, it translates - # to runs still not finished/processed - return len(self.futures) > 0 - - def run( - self, - config: Configuration, - instance: str, - cutoff: Optional[float] = None, - seed: int = 12345, - budget: Optional[float] = None, - instance_specific: str = "0", - ) -> Tuple[StatusType, float, float, Dict]: - """This method only complies with the abstract parent class. In the parallel case, we call - the single worker run() method. - - Parameters - ---------- - config : Configuration - dictionary param -> value - instance : string - problem instance - cutoff : float, optional - Wallclock time limit of the target algorithm. If no value is - provided no limit will be enforced. - seed : int - random seed - budget : float, optional - A positive, real-valued number representing an arbitrary limit to the target - algorithm. Handled by the target algorithm internally - instance_specific: str - instance specific information (e.g., domain file or solution) - - Returns - ------- - status: enum of StatusType (int) - {SUCCESS, TIMEOUT, CRASHED, ABORT} - cost: float - cost/regret/quality (float) (None, if not returned by TA) - runtime: float - runtime (None if not returned by TA) - additional_info: dict - all further additional run information - """ - return self.single_worker.run( - config=config, - instance=instance, - cutoff=cutoff, - seed=seed, - budget=budget, - instance_specific=instance_specific, - ) - - def num_workers(self) -> int: - """Total number of workers available. - - This number is dynamic as more resources can be allocated - """ - return sum(self.client.nthreads().values()) - - def _workers_available(self) -> bool: - """ - Query if there are workers available, which means that there are resources to launch a - dask job. - """ - total_compute_power = sum(self.client.nthreads().values()) - if len(self.futures) < total_compute_power: - return True - return False - - def __del__(self) -> None: - """ - Make sure that when this object gets deleted, the client is terminated. - - This is only done if the client was created by the dask runner. - """ - if self.close_client_at_del: - self.client.close() diff --git a/smac/tae/execute_func.py b/smac/tae/execute_func.py deleted file mode 100644 index b787b1106..000000000 --- a/smac/tae/execute_func.py +++ /dev/null @@ -1,356 +0,0 @@ -from typing import Callable, Dict, List, Optional, Tuple, Union, cast - -import inspect -import math -import time -import traceback - -import numpy as np -import pynisher - -from smac.configspace import Configuration -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.tae.serial_runner import SerialRunner -from smac.utils.constants import MAX_CUTOFF, MAXINT -from smac.utils.logging import PickableLoggerAdapter - -__author__ = "Marius Lindauer, Matthias Feurer" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Marius Lindauer" -__email__ = "lindauer@cs.uni-freiburg.de" -__version__ = "0.0.2" - - -class AbstractTAFunc(SerialRunner): - """Baseclass to execute target algorithms which are python functions. - - **Note:*** Do not use directly - - Parameters - ---------- - ta : callable - Function (target algorithm) to be optimized. - stats: Stats() - stats object to collect statistics about runtime and so on - multi_objectives: List[str] - names of the objectives, by default it is a single objective parameter "cost" - run_obj: str - run objective of SMAC - memory_limit : int, optional - Memory limit (in MB) that will be applied to the target algorithm. - par_factor: int - penalization factor - cost_for_crash : float - cost that is used in case of crashed runs (including runs - that returned NaN or inf) - use_pynisher: bool - use pynisher to limit resources; - if disabled - * TA func can use as many resources - as it wants (time and memory) --- use with caution - * all runs will be returned as SUCCESS if returned value is not None - - Attributes - ---------- - memory_limit - use_pynisher - """ - - def __init__( - self, - ta: Callable, - stats: Stats, - multi_objectives: List[str] = ["cost"], - run_obj: str = "quality", - memory_limit: Optional[int] = None, - par_factor: int = 1, - cost_for_crash: float = float(MAXINT), - abort_on_first_run_crash: bool = False, - use_pynisher: bool = True, - ): - - super().__init__( - ta=ta, - stats=stats, - multi_objectives=multi_objectives, - run_obj=run_obj, - par_factor=par_factor, - cost_for_crash=cost_for_crash, - abort_on_first_run_crash=abort_on_first_run_crash, - ) - self.ta = ta - self.stats = stats - self.multi_objectives = multi_objectives - self.run_obj = run_obj - - self.par_factor = par_factor - self.cost_for_crash = cost_for_crash - self.abort_on_first_run_crash = abort_on_first_run_crash - - signature = inspect.signature(ta).parameters - self._accepts_seed = "seed" in signature.keys() - self._accepts_instance = "instance" in signature.keys() - self._accepts_budget = "budget" in signature.keys() - if not callable(ta): - raise TypeError("Argument `ta` must be a callable, but is %s" % type(ta)) - self._ta = cast(Callable, ta) - - if memory_limit is not None: - memory_limit = int(math.ceil(memory_limit)) - self.memory_limit = memory_limit - - self.use_pynisher = use_pynisher - - self.logger = PickableLoggerAdapter(self.__module__ + "." + self.__class__.__name__) - - def run( - self, - config: Configuration, - instance: Optional[str] = None, - cutoff: Optional[float] = None, - seed: int = 12345, - budget: Optional[float] = None, - instance_specific: str = "0", - ) -> Tuple[StatusType, float, float, Dict]: - """Runs target algorithm with configuration for at most - seconds, allowing it to use at most RAM. - - Whether the target algorithm is called with the and - depends on the subclass implementing the actual call to - the target algorithm. - - Parameters - ---------- - config : Configuration, dictionary (or similar) - Dictionary param -> value - instance : str, optional - Problem instance - cutoff : float, optional - Wallclock time limit of the target algorithm. If no value is - provided no limit will be enforced. It is casted to integer internally. - seed : int - Random seed - budget : float, optional - A positive, real-valued number representing an arbitrary limit to the target algorithm - Handled by the target algorithm internally - instance_specific: str - Instance specific information (e.g., domain file or solution) - - Returns - ------- - status: enum of StatusType (int) - {SUCCESS, TIMEOUT, CRASHED, ABORT} - cost: np.ndarray - cost/regret/quality/runtime (float) (None, if not returned by TA) - runtime: float - runtime (None if not returned by TA) - additional_info: dict - all further additional run information - """ - obj_kwargs = {} # type: Dict[str, Union[int, str, float, None]] - if self._accepts_seed: - obj_kwargs["seed"] = seed - if self._accepts_instance: - obj_kwargs["instance"] = instance - if self._accepts_budget: - obj_kwargs["budget"] = budget - - cost = self.cost_for_crash # type: Union[float, List[float]] - - if self.use_pynisher: - # walltime for pynisher has to be a rounded up integer - if cutoff is not None: - cutoff = int(math.ceil(cutoff)) - if cutoff > MAX_CUTOFF: - raise ValueError( - "%d is outside the legal range of [0, 65535] " - "for cutoff (when using pynisher, due to OS limitations)" % cutoff - ) - - arguments = { - "logger": self.logger, - "wall_time_in_s": cutoff, - "mem_in_mb": self.memory_limit, - } - - # call ta - try: - obj = pynisher.enforce_limits(**arguments)(self._ta) - rval = self._call_ta(obj, config, obj_kwargs) - except Exception as e: - cost = np.asarray(cost).squeeze().tolist() - exception_traceback = traceback.format_exc() - error_message = repr(e) - additional_info = { - "traceback": exception_traceback, - "error": error_message, - } - - return StatusType.CRASHED, cost, 0.0, additional_info # type: ignore - - if isinstance(rval, tuple): - result = rval[0] - additional_run_info = rval[1] - else: - result = rval - additional_run_info = {} - - # get status, cost, time - if obj.exit_status is pynisher.TimeoutException: - status = StatusType.TIMEOUT - elif obj.exit_status is pynisher.MemorylimitException: - status = StatusType.MEMOUT - elif obj.exit_status == 0 and result is not None: - status = StatusType.SUCCESS - cost = result # type: ignore # noqa - else: - status = StatusType.CRASHED - - runtime = float(obj.wall_clock_time) - else: - start_time = time.time() - - # call ta - try: - rval = self._call_ta(self._ta, config, obj_kwargs) - - if isinstance(rval, tuple): - result = rval[0] - additional_run_info = rval[1] - else: - result = rval - additional_run_info = {} - - status = StatusType.SUCCESS - cost = result # type: ignore - except Exception as e: - self.logger.exception(e) - status = StatusType.CRASHED - additional_run_info = {} - - runtime = time.time() - start_time - - # Do some sanity checking (for multi objective) - if len(self.multi_objectives) > 1: - error = f"Returned costs {cost} does not match the number of objectives {len(self.multi_objectives)}." - - # If dict convert to array - # Make sure the ordering is correct - if isinstance(cost, dict): - ordered_cost = [] - for name in self.multi_objectives: - if name not in cost: - raise RuntimeError(f"Objective {name} was not found in the returned costs.") - - ordered_cost.append(cost[name]) - cost = ordered_cost - - if isinstance(cost, list): - if len(cost) != len(self.multi_objectives): - raise RuntimeError(error) - - if isinstance(cost, float): - raise RuntimeError(error) - - if cost is None or status == StatusType.CRASHED: - status = StatusType.CRASHED - cost = self.cost_for_crash - - cost = np.asarray(cost).squeeze().tolist() - - return status, cost, runtime, additional_run_info # type: ignore - - def _call_ta( - self, - obj: Callable, - config: Configuration, - obj_kwargs: Dict[str, Union[int, str, float, None]], - ) -> Union[float, Tuple[float, Dict]]: - raise NotImplementedError() - - -class ExecuteTAFuncDict(AbstractTAFunc): - """Evaluate function for given configuration and resource limit. - - Passes the configuration as a dictionary to the target algorithm. The - target algorithm needs to implement one of the following signatures: - - * ``target_algorithm(config: Configuration) -> Union[float, Tuple[float, Any]]`` - * ``target_algorithm(config: Configuration, seed: int) -> Union[float, Tuple[float, Any]]`` - * ``target_algorithm(config: Configuration, seed: int, instance: str) -> Union[float, Tuple[float, Any]]`` - - The target algorithm can either return a float (the loss), or a tuple - with the first element being a float and the second being additional run - information. - - ExecuteTAFuncDict will use inspection to figure out the correct call to - the target algorithm. - - Parameters - ---------- - ta : callable - Function (target algorithm) to be optimized. - stats : smac.stats.stats.Stats, optional - Stats object to collect statistics about runtime etc. - run_obj : str, optional - Run objective (runtime or quality) - memory_limit : int, optional - Memory limit (in MB) that will be applied to the target algorithm. - par_factor : int, optional - Penalized average runtime factor. Only used when `run_obj='runtime'` - use_pynisher: bool, optional - use pynisher to limit resources; - """ - - def _call_ta( - self, - obj: Callable, - config: Configuration, - obj_kwargs: Dict[str, Union[int, str, float, None]], - ) -> Union[float, Tuple[float, Dict]]: - - return obj(config, **obj_kwargs) - - -class ExecuteTAFuncArray(AbstractTAFunc): - """Evaluate function for given configuration and resource limit. - - Passes the configuration as an array-like to the target algorithm. The - target algorithm needs to implement one of the following signatures: - - * ``target_algorithm(config: np.ndarray) -> Union[float, Tuple[float, Any]]`` - * ``target_algorithm(config: np.ndarray, seed: int) -> Union[float, Tuple[float, Any]]`` - * ``target_algorithm(config: np.ndarray, seed: int, instance: str) -> Union[float, Tuple[float, Any]]`` - - The target algorithm can either return a float (the loss), or a tuple - with the first element being a float and the second being additional run - information. - - ExecuteTAFuncDict will use inspection to figure out the correct call to - the target algorithm. - - Parameters - ---------- - ta : callable - Function (target algorithm) to be optimized. - stats : smac.stats.stats.Stats, optional - Stats object to collect statistics about runtime etc. - run_obj: str, optional - Run objective (runtime or quality) - memory_limit : int, optional - Memory limit (in MB) that will be applied to the target algorithm. - par_factor: int, optional - Penalized average runtime factor. Only used when `run_obj='runtime'` - """ - - def _call_ta( - self, - obj: Callable, - config: Configuration, - obj_kwargs: Dict[str, Union[int, str, float, None]], - ) -> Union[float, Tuple[float, Dict]]: - - x = np.array([val for _, val in sorted(config.get_dictionary().items())], dtype=float) - return obj(x, **obj_kwargs) diff --git a/smac/tae/execute_ta_run_aclib.py b/smac/tae/execute_ta_run_aclib.py deleted file mode 100644 index 65180d14e..000000000 --- a/smac/tae/execute_ta_run_aclib.py +++ /dev/null @@ -1,168 +0,0 @@ -from typing import Dict, List, Optional, Tuple - -import json -from subprocess import PIPE, Popen - -from smac.configspace import Configuration -from smac.tae import StatusType -from smac.tae.serial_runner import SerialRunner - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Marius Lindauer" -__email__ = "lindauer@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -class ExecuteTARunAClib(SerialRunner): - """Executes a target algorithm run with a given configuration on a given instance and some - resource limitations. - - Uses the AClib 2.0 style - """ - - def run( - self, - config: Configuration, - instance: str, - cutoff: Optional[float] = None, - seed: int = 12345, - budget: Optional[float] = None, - instance_specific: str = "0", - ) -> Tuple[StatusType, float, float, Dict]: - """Runs target algorithm with configuration on instance with - instance specifics. - - for at most. - - seconds and random seed - - Parameters - ---------- - config : Configuration - Dictionary param -> value - instance : str - Problem instance - cutoff : float - Runtime cutoff - seed : int - Random seed - budget : float (optional) - Not implemented - instance_specific: str - Instance specific information -- ignored here - Returns - ------- - status: enum of StatusType (int) - {SUCCESS, TIMEOUT, CRASHED, ABORT} - cost: float - cost/regret/quality/runtime (float) (None, if not returned by TA) - runtime: float - runtime (None if not returned by TA) - additional_info: dict - all further additional run information - """ - if budget is not None: - raise NotImplementedError() - - if instance is None: - instance = "0" - if cutoff is None: - cutoff = 99999999999999 - - results, stdout_, stderr_ = self._call_ta( - config=config, - instance=instance, - instance_specific=instance_specific, - cutoff=cutoff, - seed=seed, - ) - - if results["status"] in ["SAT", "UNSAT", "SUCCESS"]: - status = StatusType.SUCCESS - elif results["status"] in ["TIMEOUT"]: - status = StatusType.TIMEOUT - elif results["status"] in ["CRASHED"]: - status = StatusType.CRASHED - elif results["status"] in ["ABORT"]: - status = StatusType.ABORT - elif results["status"] in ["MEMOUT"]: - status = StatusType.MEMOUT - else: - self.logger.warning( - "Could not identify status; should be one of the following: " - "SAT, UNSAT, SUCCESS, TIMEOUT, CRASHED, ABORT or MEMOUT; " - "Treating as CRASHED run." - ) - status = StatusType.CRASHED - - if status in [StatusType.CRASHED, StatusType.ABORT]: - self.logger.warning("Target algorithm crashed. Last 5 lines of stdout and stderr") - self.logger.warning("\n".join(stdout_.split("\n")[-5:])) - self.logger.warning("\n".join(stderr_.split("\n")[-5:])) - - if results.get("runtime") is None: - self.logger.warning("The target algorithm has not returned a" " runtime -- imputed by 0.") - # (TODO) Check 0 - results["runtime"] = 0 - - runtime = float(results["runtime"]) - - if self.run_obj == "quality" and results.get("cost") is None: - self.logger.error("The target algorithm has not returned a quality/cost value although we optimize cost.") - # (TODO) Do not return 0 - results["cost"] = 0 - - if self.run_obj == "runtime": - cost = float(results["runtime"]) - else: - cost = float(results["cost"]) - - del results["status"] - try: - del results["runtime"] - except KeyError: - pass - try: - del results["cost"] - except KeyError: - pass - - return status, cost, runtime, results - - def _call_ta( - self, - config: Configuration, - instance: str, - instance_specific: str, - cutoff: float, - seed: int, - ) -> Tuple[Dict, str, str]: - - # TODO: maybe replace fixed instance specific and cutoff_length (0) to - # other value - cmd = [] # type: List[str] - if not isinstance(self.ta, (list, tuple)): - raise TypeError("self.ta needs to be of type list or tuple, but is %s" % type(self.ta)) - cmd.extend(self.ta) - cmd.extend(["--instance", instance, "--cutoff", str(cutoff), "--seed", str(seed), "--config"]) - - for p in config: - if not config.get(p) is None: - cmd.extend(["-" + str(p), str(config[p])]) - - self.logger.debug("Calling: %s" % (" ".join(cmd))) - p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, universal_newlines=True) - stdout_, stderr_ = p.communicate() - - self.logger.debug("Stdout: %s" % (stdout_)) - self.logger.debug("Stderr: %s" % (stderr_)) - - results = {"status": "CRASHED", "cost": 1234567890} - for line in stdout_.split("\n"): - if line.startswith("Result of this algorithm run:"): - fields = ":".join(line.split(":")[1:]) - results = json.loads(fields) - - return results, stdout_, stderr_ diff --git a/smac/tae/execute_ta_run_hydra.py b/smac/tae/execute_ta_run_hydra.py deleted file mode 100644 index 39563fd95..000000000 --- a/smac/tae/execute_ta_run_hydra.py +++ /dev/null @@ -1,80 +0,0 @@ -from typing import Any, Dict, Mapping, Optional, Tuple, Type - -from smac.configspace import Configuration -from smac.tae import StatusType -from smac.tae.execute_func import ExecuteTAFuncArray, ExecuteTAFuncDict -from smac.tae.execute_ta_run_aclib import ExecuteTARunAClib -from smac.tae.execute_ta_run_old import ExecuteTARunOld -from smac.tae.serial_runner import SerialRunner - -__copyright__ = "Copyright 2018, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Marius Lindauer" - - -class ExecuteTARunHydra(SerialRunner): - """Returns min(cost, cost_portfolio) - - Parameters - ---------- - cost_oracle: Mapping[str,float] - cost of oracle per instance - tae: Type[SerialRunner] - target algorithm evaluator - """ - - def __init__( - self, - cost_oracle: Mapping[str, float], - tae: Type[SerialRunner] = ExecuteTARunOld, - **kwargs: Any, - ) -> None: - super().__init__(**kwargs) - self.cost_oracle = cost_oracle - if tae is ExecuteTARunAClib: - self.runner = ExecuteTARunAClib(**kwargs) # type: SerialRunner - elif tae is ExecuteTARunOld: - self.runner = ExecuteTARunOld(**kwargs) - elif tae is ExecuteTAFuncDict: - self.runner = ExecuteTAFuncDict(**kwargs) - elif tae is ExecuteTAFuncArray: - self.runner = ExecuteTAFuncArray(**kwargs) - else: - raise Exception("TAE not supported") - - def run( - self, - config: Configuration, - instance: str, - cutoff: Optional[float] = None, - seed: int = 12345, - budget: Optional[float] = None, - instance_specific: str = "0", - ) -> Tuple[StatusType, float, float, Dict]: - """See ~smac.tae.execute_ta_run.ExecuteTARunOld for docstring.""" - if cutoff is None: - raise ValueError("Cutoff of type None is not supported") - - status, cost, runtime, additional_info = self.runner.run( - config=config, - instance=instance, - cutoff=cutoff, - seed=seed, - budget=budget, - instance_specific=instance_specific, - ) - if instance in self.cost_oracle: - oracle_perf = self.cost_oracle[instance] - if self.run_obj == "runtime": - self.logger.debug("Portfolio perf: %f vs %f = %f", oracle_perf, runtime, min(oracle_perf, runtime)) - runtime = min(oracle_perf, runtime) - cost = runtime - else: - self.logger.debug("Portfolio perf: %f vs %f = %f", oracle_perf, cost, min(oracle_perf, cost)) - cost = min(oracle_perf, cost) - if oracle_perf < cutoff and status is StatusType.TIMEOUT: - status = StatusType.SUCCESS - else: - self.logger.error("Oracle performance missing --- should not happen") - - return status, cost, runtime, additional_info diff --git a/smac/tae/execute_ta_run_old.py b/smac/tae/execute_ta_run_old.py deleted file mode 100644 index 546606886..000000000 --- a/smac/tae/execute_ta_run_old.py +++ /dev/null @@ -1,178 +0,0 @@ -from typing import Dict, List, Optional, Tuple - -from subprocess import PIPE, Popen - -from smac.configspace import Configuration -from smac.tae import StatusType -from smac.tae.serial_runner import SerialRunner - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Marius Lindauer" -__email__ = "lindauer@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -class ExecuteTARunOld(SerialRunner): - """Executes a target algorithm run with a given configuration on a given instance and some - resource limitations. - - Uses the original SMAC/PILS format (SMAC < v2.10). - """ - - def run( - self, - config: Configuration, - instance: Optional[str] = None, - cutoff: Optional[float] = None, - seed: int = 12345, - budget: Optional[float] = 0.0, - instance_specific: str = "0", - ) -> Tuple[StatusType, float, float, Dict]: - """Runs target algorithm with configuration on instance with - instance specifics. - - for at most. - seconds and random seed . - - Parameters - ---------- - config : Configuration - Dictionary param -> value - instance : string, optional - Problem instance - cutoff : float - Runtime cutoff - seed : int - Random seed - budget : float, optional - A positive, real-valued number representing an arbitrary limit to the target algorithm. - Handled by the target algorithm internally. Currently ignored - instance_specific: str - Instance specific information (e.g., domain file or solution) - - Returns - ------- - status: enum of StatusType (int) - {SUCCESS, TIMEOUT, CRASHED, ABORT} - cost: float - cost/regret/quality/runtime (float) (None, if not returned by TA) - runtime: float - runtime (None if not returned by TA) - additional_info: dict - all further additional run information - """ - if instance is None: - instance = "0" - if cutoff is None: - cutoff = 99999999999999.0 - - stdout_, stderr_ = self._call_ta( - config=config, - instance=instance, - instance_specific=instance_specific, - cutoff=cutoff, - seed=seed, - ) - - status_string = "CRASHED" - quality = 1234567890.0 - runtime = 1234567890.0 - additional_info = {} # type: Dict[str, str] - for line in stdout_.split("\n"): - if ( - line.startswith("Result of this algorithm run:") - or line.startswith("Result for ParamILS") - or line.startswith("Result for SMAC") - ): - fields = line.split(":")[1].split(",") - - # If we have more than 6 fields, we combine them all together - if len(fields) > 5: - fields[5 : len(fields)] = ["".join(map(str, fields[5 : len(fields)]))] - - # Make it prettier - for char in [",", ";", "'", "[", "]"]: - fields[5] = fields[5].replace(char, "") - - fields = list(map(lambda x: x.strip(" "), fields)) - if len(fields) == 5: - status_string, runtime_string, _, quality_string, _ = fields - additional_info = {} - else: - ( - status_string, - runtime_string, - _, - quality_string, - _, - additional_info_string, - ) = fields - additional_info = {"additional_info": additional_info_string} - - runtime = min(float(runtime_string), cutoff) - quality = float(quality_string) - - if "StatusType." in status_string: - status_string = status_string.split(".")[1] - - status_string = status_string.upper() - - if status_string in ["SAT", "UNSAT", "SUCCESS"]: - status = StatusType.SUCCESS - elif status_string in ["TIMEOUT"]: - status = StatusType.TIMEOUT - elif status_string in ["CRASHED"]: - status = StatusType.CRASHED - elif status_string in ["ABORT"]: - status = StatusType.ABORT - elif status_string in ["MEMOUT"]: - status = StatusType.MEMOUT - else: - self.logger.warning( - "Could not parse output of target algorithm. Expected format: " - '"Result of this algorithm run: ,,,"; ' - "Treating as CRASHED run." - ) - status = StatusType.CRASHED - - if status in [StatusType.CRASHED, StatusType.ABORT]: - self.logger.warning("Target algorithm crashed. Last 5 lines of stdout and stderr") - self.logger.warning("\n".join(stdout_.split("\n")[-5:])) - self.logger.warning("\n".join(stderr_.split("\n")[-5:])) - - if self.run_obj == "runtime": - cost = runtime - else: - cost = quality - - return status, cost, float(runtime), additional_info - - def _call_ta( - self, - config: Configuration, - instance: str, - instance_specific: str, - cutoff: float, - seed: int, - ) -> Tuple[str, str]: - - # TODO: maybe replace fixed instance specific and cutoff_length (0) to other value - cmd = [] # type: List[str] - if not isinstance(self.ta, (list, tuple)): - raise TypeError("self.ta needs to be of type list or tuple, but is %s" % type(self.ta)) - cmd.extend(self.ta) - cmd.extend([instance, instance_specific, str(cutoff), "0", str(seed)]) - for p in config: - if not config.get(p) is None: - cmd.extend(["-" + str(p), str(config[p])]) - - self.logger.debug("Calling: %s" % (" ".join(cmd))) - p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, universal_newlines=True) - stdout_, stderr_ = p.communicate() - - self.logger.debug("Stdout: %s" % stdout_) - self.logger.debug("Stderr: %s" % stderr_) - - return stdout_, stderr_ diff --git a/smac/tae/serial_runner.py b/smac/tae/serial_runner.py deleted file mode 100644 index 72e89ef6d..000000000 --- a/smac/tae/serial_runner.py +++ /dev/null @@ -1,178 +0,0 @@ -from typing import Callable, Dict, List, Optional, Tuple, Union - -from smac.configspace import Configuration -from smac.runhistory.runhistory import RunInfo, RunValue -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.tae.base import BaseRunner -from smac.utils.constants import MAXINT - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class SerialRunner(BaseRunner): - """Interface to submit and collect a job in a serial fashion. - - It dictates what a worker should do to convert a - configuration/instance/seed to a result. - - This class is expected to be extended via the implementation of - a run() method for the desired task. - - Attributes - ---------- - results - ta - stats - run_obj - par_factor - cost_for_crash - abort_i_first_run_crash - - Parameters - ---------- - ta : list - target algorithm command line as list of arguments - stats: Stats() - stats object to collect statistics about runtime and so on - multi_objectives: List[str] - names of the objectives, by default it is a single objective parameter "cost" - run_obj: str - run objective of SMAC - par_factor: int - penalization factor - cost_for_crash : float - cost that is used in case of crashed runs (including runs - that returned NaN or inf) - abort_on_first_run_crash: bool - if true and first run crashes, raise FirstRunCrashedException - """ - - def __init__( - self, - ta: Union[List[str], Callable], - stats: Stats, - multi_objectives: List[str] = ["cost"], - run_obj: str = "runtime", - par_factor: int = 1, - cost_for_crash: Union[float, List[float]] = float(MAXINT), - abort_on_first_run_crash: bool = True, - ): - super(SerialRunner, self).__init__( - ta=ta, - stats=stats, - multi_objectives=multi_objectives, - run_obj=run_obj, - par_factor=par_factor, - cost_for_crash=cost_for_crash, - abort_on_first_run_crash=abort_on_first_run_crash, - ) - - def submit_run(self, run_info: RunInfo) -> None: - """This function submits a run_info object in a serial fashion. - - As there is a single worker for this task, this - interface can be considered a wrapper over the run() - method. - - Both result/exceptions can be completely determined in this - step so both lists are properly filled. - - Parameters - ---------- - run_info: RunInfo - An object containing the configuration and the necessary data to run it - """ - self.results.append(self.run_wrapper(run_info)) - - def get_finished_runs(self) -> List[Tuple[RunInfo, RunValue]]: - """This method returns any finished configuration, and returns a list with the results of - exercising the configurations. This class keeps populating results to self.results until a - call to get_finished runs is done. In this case, the self.results list is emptied and all - RunValues produced by running self.run() are returned. - - Returns - ------- - List[RunInfo, RunValue]: A list of RunInfo/RunValues pairs - a submitted configuration - """ - results_list = [] - while self.results: - results_list.append(self.results.pop()) - return results_list - - def wait(self) -> None: - """SMBO/intensifier might need to wait for runs to finish before making a decision. - - For serial runs, no wait is needed as the result is immediately available. - """ - # There is no need to wait in serial runs. - # When launching a run via submit, as the serial run - # uses the same process to run, the result is always available - # immediately after. This method implements is just an implementation of the - # abstract method via a simple return, again, because there is - # no need to wait (as in distributed runs) - return - - def pending_runs(self) -> bool: - """Whether or not there are configs still running. - - Generally if the runner is serial, launching a run instantly returns it's result. On - parallel runners, there might be pending configurations to complete. - """ - # No pending runs in a serial run. Execution is blocking - return False - - def run( - self, - config: Configuration, - instance: str, - cutoff: Optional[float] = None, - seed: int = 12345, - budget: Optional[float] = None, - instance_specific: str = "0", - ) -> Tuple[StatusType, float, float, Dict]: - """Runs target algorithm with configuration on instance with - instance specifics. - - for at most. - - seconds and random seed - - This method exemplifies how to defined the run() method - - Parameters - ---------- - config : Configuration - dictionary param -> value - instance : string - problem instance - cutoff : float, optional - Wallclock time limit of the target algorithm. If no value is - provided no limit will be enforced. - seed : int - random seed - budget : float, optional - A positive, real-valued number representing an arbitrary limit to the target - algorithm. Handled by the target algorithm internally - instance_specific: str - instance specific information (e.g., domain file or solution) - - Returns - ------- - status: enum of StatusType (int) - {SUCCESS, TIMEOUT, CRASHED, ABORT} - cost: float - cost/regret/quality (float) (None, if not returned by TA) - runtime: float - runtime (None if not returned by TA) - additional_info: dict - all further additional run information - """ - pass - - def num_workers(self) -> int: - """Total number of workers available.""" - # Any serial runner supports only 1 worker - return 1 diff --git a/smac/utils/configspace.py b/smac/utils/configspace.py new file mode 100644 index 000000000..32aef92cd --- /dev/null +++ b/smac/utils/configspace.py @@ -0,0 +1,225 @@ +from __future__ import annotations + +from functools import partial + +import numpy as np +from ConfigSpace import Configuration, ConfigurationSpace +from ConfigSpace.hyperparameters import ( + BetaFloatHyperparameter, + BetaIntegerHyperparameter, + CategoricalHyperparameter, + Constant, + NormalFloatHyperparameter, + NormalIntegerHyperparameter, + OrdinalHyperparameter, + UniformFloatHyperparameter, + UniformIntegerHyperparameter, +) +from ConfigSpace.util import get_one_exchange_neighbourhood + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +get_one_exchange_neighbourhood = partial(get_one_exchange_neighbourhood, stdev=0.05, num_neighbors=8) + + +def convert_configurations_to_array(configs: list[Configuration]) -> np.ndarray: + """Impute inactive hyperparameters in configurations with their default. + + Parameters + ---------- + configs : List[Configuration] + List of configuration objects. + + Returns + ------- + np.ndarray + """ + return np.array([config.get_array() for config in configs], dtype=np.float64) + + +def get_types( + configspace: ConfigurationSpace, + instance_features: dict[str, list[float]] | None = None, +) -> tuple[list[int], list[tuple[float, float]]]: + """Return the types of the hyperparameters and the bounds of the + hyperparameters and instance features. + + Warning + ------- + The bounds for the instance features are *not* added in this function. + """ + # Extract types vector for rf from config space and the bounds + types = [0] * len(configspace.get_hyperparameters()) + bounds = [(np.nan, np.nan)] * len(types) + + for i, param in enumerate(configspace.get_hyperparameters()): + parents = configspace.get_parents_of(param.name) + if len(parents) == 0: + can_be_inactive = False + else: + can_be_inactive = True + + if isinstance(param, (CategoricalHyperparameter)): + n_cats = len(param.choices) + if can_be_inactive: + n_cats = len(param.choices) + 1 + types[i] = n_cats + bounds[i] = (int(n_cats), np.nan) + elif isinstance(param, (OrdinalHyperparameter)): + n_cats = len(param.sequence) + types[i] = 0 + if can_be_inactive: + bounds[i] = (0, int(n_cats)) + else: + bounds[i] = (0, int(n_cats) - 1) + elif isinstance(param, Constant): + # For constants we simply set types to 0 which makes it a numerical parameter + if can_be_inactive: + bounds[i] = (2, np.nan) + types[i] = 2 + else: + bounds[i] = (0, np.nan) + types[i] = 0 + # and we leave the bounds to be 0 for now + elif isinstance(param, UniformFloatHyperparameter): + # Are sampled on the unit hypercube thus the bounds + # are always 0.0, 1.0 + if can_be_inactive: + bounds[i] = (-1.0, 1.0) + else: + bounds[i] = (0, 1.0) + elif isinstance(param, UniformIntegerHyperparameter): + if can_be_inactive: + bounds[i] = (-1.0, 1.0) + else: + bounds[i] = (0, 1.0) + elif isinstance(param, NormalFloatHyperparameter): + if can_be_inactive: + raise ValueError("Inactive parameters not supported for Beta and Normal Hyperparameters") + + bounds[i] = (param._lower, param._upper) + elif isinstance(param, NormalIntegerHyperparameter): + if can_be_inactive: + raise ValueError("Inactive parameters not supported for Beta and Normal Hyperparameters") + + bounds[i] = (param.nfhp._lower, param.nfhp._upper) + elif isinstance(param, BetaFloatHyperparameter): + if can_be_inactive: + raise ValueError("Inactive parameters not supported for Beta and Normal Hyperparameters") + + bounds[i] = (param._lower, param._upper) + elif isinstance(param, BetaIntegerHyperparameter): + if can_be_inactive: + raise ValueError("Inactive parameters not supported for Beta and Normal Hyperparameters") + + bounds[i] = (param.bfhp._lower, param.bfhp._upper) + elif not isinstance( + param, + ( + UniformFloatHyperparameter, + UniformIntegerHyperparameter, + OrdinalHyperparameter, + CategoricalHyperparameter, + NormalFloatHyperparameter, + NormalIntegerHyperparameter, + BetaFloatHyperparameter, + BetaIntegerHyperparameter, + ), + ): + raise TypeError("Unknown hyperparameter type %s" % type(param)) + + if instance_features is not None: + n_features = len(list(instance_features.values())[0]) + types = types + [0] * n_features + + return types, bounds + + +def get_conditional_hyperparameters(X: np.ndarray, Y: np.ndarray | None = None) -> np.ndarray: + """Returns conditional hyperparameters if values with -1 or smaller are observed. X is used + if Y is not specified. + """ + # Taking care of conditional hyperparameters according to Levesque et al. + X_cond = X <= -1 + + if Y is not None: + Y_cond = Y <= -1 + else: + Y_cond = X <= -1 + + active = ~((np.expand_dims(X_cond, axis=1) != Y_cond).any(axis=2)) + return active + + +# def check_subspace_points( +# X: np.ndarray, +# cont_dims: np.ndarray | list = [], +# cat_dims: np.ndarray | list = [], +# bounds_cont: np.ndarray | None = None, +# bounds_cat: list[tuple] | None = None, +# expand_bound: bool = False, +# ) -> np.ndarray: +# """Check which points are place inside a given subspace. + +# Parameters +# ---------- +# X: Optional[np.ndarray(N,D)], +# points to be checked, where D = D_cont + D_cat +# cont_dims: Union[np.ndarray(D_cont), List] +# which dimensions represent continuous hyperparameters +# cat_dims: Union[np.ndarray(D_cat), List] +# which dimensions represent categorical hyperparameters +# bounds_cont: optional[List[Tuple]] +# subspaces bounds of categorical hyperparameters, its length is the number of continuous hyperparameters +# bounds_cat: Optional[List[Tuple]] +# subspaces bounds of continuous hyperparameters, its length is the number of categorical hyperparameters +# expand_bound: bool +# if the bound needs to be expanded to contain more points rather than the points inside the subregion +# Return +# ---------- +# indices_in_ss:np.ndarray(N) +# indices of data that included in subspaces +# """ +# if len(X.shape) == 1: +# X = X[np.newaxis, :] +# if len(cont_dims) == 0 and len(cat_dims) == 0: +# return np.ones(X.shape[0], dtype=bool) + +# if len(cont_dims) > 0: +# if bounds_cont is None: +# raise ValueError("bounds_cont must be given if cont_dims provided") + +# if len(bounds_cont.shape) != 2 or bounds_cont.shape[1] != 2 or bounds_cont.shape[0] != len(cont_dims): +# raise ValueError( +# f"bounds_cont (with shape {bounds_cont.shape}) should be an array with shape of" +# f"({len(cont_dims)}, 2)" +# ) + +# data_in_ss = np.all(X[:, cont_dims] <= bounds_cont[:, 1], axis=1) & np.all( +# X[:, cont_dims] >= bounds_cont[:, 0], axis=1 +# ) + +# if expand_bound: +# bound_left = bounds_cont[:, 0] - np.min(X[data_in_ss][:, cont_dims] - bounds_cont[:, 0], axis=0) +# bound_right = bounds_cont[:, 1] + np.min(bounds_cont[:, 1] - X[data_in_ss][:, cont_dims], axis=0) +# data_in_ss = np.all(X[:, cont_dims] <= bound_right, axis=1) & np.all(X[:, cont_dims] >= bound_left, +# axis=1) +# else: +# data_in_ss = np.ones(X.shape[0], dtype=bool) + +# if len(cat_dims) == 0: +# return data_in_ss +# if bounds_cat is None: +# raise ValueError("bounds_cat must be given if cat_dims provided") + +# if len(bounds_cat) != len(cat_dims): +# raise ValueError( +# f"bounds_cat ({len(bounds_cat)}) and cat_dims ({len(cat_dims)}) must have " f"the same number of elements" +# ) + +# for bound_cat, cat_dim in zip(bounds_cat, cat_dims): +# data_in_ss &= np.in1d(X[:, cat_dim], bound_cat) + +# return data_in_ss diff --git a/smac/utils/constants.py b/smac/utils/constants.py deleted file mode 100644 index b9ef2b5ff..000000000 --- a/smac/utils/constants.py +++ /dev/null @@ -1,7 +0,0 @@ -MAXINT = 2**31 - 1 -MINIMAL_COST_FOR_LOG = 0.00001 -MAX_CUTOFF = 65535 -# The very small number is used to avoid numerical instabilities or dividing by zero -VERY_SMALL_NUMBER = 1e-10 - -N_TREES = 10 diff --git a/smac/utils/data_structures.py b/smac/utils/data_structures.py new file mode 100644 index 000000000..cd9ba1904 --- /dev/null +++ b/smac/utils/data_structures.py @@ -0,0 +1,66 @@ +from __future__ import annotations + +from smac.utils.logging import get_logger + +logger = get_logger(__name__) + + +def recursively_compare_dicts( + d1: dict, + d2: dict, + *, + level: str = "root", + diff: list[str] | None = None, +) -> list[str]: + """Compares dictionaries recursively. Returns a list of differences in string format. + + Parameters + ---------- + d1 : dict + First dictionary. + d2 : dict + Second dictionary. + level : str, defaults to "root" + How the first level is called. + diff : list[str] | None, defaults to None + Used for recursion. + + Returns + ------- + list[str] + List of differences in string format. + """ + if diff is None: + diff = [] + + if isinstance(d1, dict) and isinstance(d2, dict): + if d1.keys() != d2.keys(): + s1 = set(d1.keys()) + s2 = set(d2.keys()) + # logger.info("{:<20} + {} - {}".format(level, s1 - s2, s2 - s1)) + # logger.info("{} - {}".format(s1 - s2, s2 - s1)) + diff += [f"{level} + {s1 - s2} - {s2 - s1}"] + common_keys = s1 & s2 + else: + common_keys = set(d1.keys()) + + for k in common_keys: + recursively_compare_dicts(d1[k], d2[k], level="{}.{}".format(level, k), diff=diff) + + elif isinstance(d1, list) and isinstance(d2, list): + if len(d1) != len(d2): + diff += [f"{level}: len1={len(d1)}; len2={len(d2)}"] + # logger.info("{:<20} len1={}; len2={}".format(level, len(d1), len(d2))) + # logger.info("len1={}; len2={}".format(len(d1), len(d2))) + common_len = min(len(d1), len(d2)) + + for i in range(common_len): + recursively_compare_dicts(d1[i], d2[i], level="{}[{}]".format(level, i), diff=diff) + + else: + if d1 != d2: + diff += [f"{level}: {d1} != {d2}"] + # logger.info("{:<20} {} != {}".format(level, d1, d2)) + # logger.info("len1={}; len2={}".format(len(d1), len(d2))) + + return diff diff --git a/smac/utils/dependencies.py b/smac/utils/dependencies.py deleted file mode 100644 index 303b5d1ee..000000000 --- a/smac/utils/dependencies.py +++ /dev/null @@ -1,138 +0,0 @@ -from typing import List, Union - -import importlib -import re - -import pkg_resources # type: ignore -from packaging.version import Version - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -SUBPATTERN = r"((?P==|>=|>|<)(?P(\d+)?(\.[a-zA-Z0-9]+)?(\.\d+)?))" -RE_PATTERN = re.compile(r"^(?P[\w\-]+)%s?(,%s)?$" % (SUBPATTERN % (1, 1), SUBPATTERN % (2, 2))) - - -def verify_packages(packages: Union[List[str], str]) -> None: - """Verifies packages. Calls `_verify_packages` as subroutine. - - Parameters - ---------- - packages : Union[List[str], str] - Packages to verify. - - Raises - ------ - ValueError - If requirements can not be read. - """ - if not packages: - return - if isinstance(packages, str): - packages = packages.splitlines() - - for package in packages: - if not package: - continue - - match = RE_PATTERN.match(package) - if match: - name = match.group("name") - for group_id in range(1, 3): - if "operation%d" % group_id in match.groupdict(): - operation = match.group("operation%d" % group_id) - version = match.group("version%d" % group_id) - _verify_package(name, operation, version) - - else: - raise ValueError("Unable to read requirement: %s" % package) - - -def _verify_package(name: str, operation: str, version: str) -> None: - """Verifies a package. - - Parameters - ---------- - name : str - Name of the package. - operation : str - Operation of the package. - version : str - Version of the package. - - Raises - ------ - MissingPackageError - If package is missing. - NotImplementedError - If operation is not implemented. - IncorrectPackageVersionError - If package version is incorrect. - """ - try: - distribution = pkg_resources.get_distribution(name) - installed_version = Version(distribution.version) - except pkg_resources.DistributionNotFound: - try: - module = importlib.import_module(name) - installed_version = Version(module.__version__) # type: ignore[attr-defined] # noqa F821 - except ImportError: - raise MissingPackageError(name) - - if not operation: - return - - # pkg_resources.get_distribution can (not) find a version depending on how the package was built - # if we get version 0.0.0 we fallback to the module's version - if installed_version == Version("0.0.0"): - module = importlib.import_module(name) - installed_version = Version(module.__version__) - - required_version = Version(version) - - if operation == "==": - check = required_version == installed_version - elif operation == ">": - check = installed_version > required_version - elif operation == "<": - check = installed_version < required_version - elif operation == ">=": - check = installed_version > required_version or installed_version == required_version - elif operation == "<=": - check = installed_version < required_version or installed_version == required_version - else: - raise NotImplementedError("operation '%s' is not supported" % operation) - if not check: - raise IncorrectPackageVersionError(name, installed_version, operation, required_version) - - -class MissingPackageError(Exception): - error_message = "Mandatory package '{name}' not found!" - - def __init__(self, package_name: str) -> None: - self.package_name = package_name - super(MissingPackageError, self).__init__(self.error_message.format(name=package_name)) - - -class IncorrectPackageVersionError(Exception): - error_message = "'{name} {installed_version}' version mismatch ({operation}{required_version})" - - def __init__( - self, - package_name: str, - installed_version: Version, - operation: str, - required_version: Version, - ) -> None: - self.package_name = package_name - self.installed_version = installed_version - self.operation = operation - self.required_version = required_version - message = self.error_message.format( - name=package_name, - installed_version=installed_version, - operation=operation, - required_version=required_version, - ) - super(IncorrectPackageVersionError, self).__init__(message) diff --git a/smac/utils/io/__init__.py b/smac/utils/io/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/smac/utils/io/cmd_reader.py b/smac/utils/io/cmd_reader.py deleted file mode 100644 index bb60e618f..000000000 --- a/smac/utils/io/cmd_reader.py +++ /dev/null @@ -1,1371 +0,0 @@ -from typing import ( - IO, - Any, - Callable, - Dict, - Iterable, - List, - Optional, - Sequence, - Tuple, - Type, - Union, -) - -import datetime -import distutils.util -import logging -import os -import re -import shlex -import sys -import time -from argparse import ( - SUPPRESS, - Action, - ArgumentDefaultsHelpFormatter, - ArgumentParser, - FileType, - HelpFormatter, - Namespace, -) - -import numpy as np - -from smac.utils.constants import MAXINT, N_TREES -from smac.utils.io.input_reader import ( - INSTANCE_FEATURES_TYPE, - INSTANCE_TYPE, - InputReader, -) - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2018, ML4AAD" -__license__ = "3-clause BSD" - - -in_reader = InputReader() -PARSED_SCENARIO_ARGS_TYPE = Dict[ - str, - Union[str, int, Dict, INSTANCE_TYPE, INSTANCE_FEATURES_TYPE, np.ndarray, List[str]], -] -parsed_scen_args = {} # type: PARSED_SCENARIO_ARGS_TYPE -# Placeholder logger that will not be used in practice, but which will be replaced by -# the logger onces the parsing logic is instantiated -logger = logging.getLogger(__name__) - - -def truthy(x: Any) -> bool: - """Convert x into its truth value.""" - if isinstance(x, bool): - return x - elif isinstance(x, (int, float)): - return x != 0 - elif isinstance(x, str): - return bool(distutils.util.strtobool(x)) - else: - return False - - -def multi_objectives(x: Union[str, List[str]]) -> List[str]: - """Convert objectives into an array.""" - if isinstance(x, str): - # Convert a (comma-separated) string into list of strings. - x = x.replace(", ", ",") - return x.split(",") - elif isinstance(x, List): - return x - else: - raise RuntimeError("Expected string or list of strings, got %s" % type(x)) - - -def cost_for_crash(x: Union[str, List]) -> Union[int, float, List[Union[int, float]]]: - """Convert cost for crash into an array.""" - if isinstance(x, List): - x = [float(i) for i in x] - else: - if isinstance(x, str): - if x[0] == "[" and x[-1] == "]": - x = x[1:-1] - - # Convert a (comma-separated) string into list of strings. - x = x.replace(", ", ",").split(",") - - x = [float(i) for i in x] - - if len(x) == 1: - return x[0] - - return x - - -class CheckScenarioFileAction(Action): - """Check scenario file given by user.""" - - def __call__( # type: ignore[override] # noqa F821 - self, - parser: ArgumentParser, - namespace: Namespace, - values: str, - option_string: Optional[str] = None, - ) -> None: - """Check scenario file given by user.""" - fn = values - if fn: - if not os.path.isfile(fn): - parser.exit(1, "Could not find scenario file: {}".format(fn)) - setattr(namespace, self.dest, values) - - -class ParseRandomConfigurationChooserAction(Action): - """Parse random configuration chooser given by user.""" - - def __call__( # type: ignore[override] # noqa F821 - self, - parser: ArgumentParser, - namespace: Namespace, - values: IO, - option_string: Optional[str] = None, - ) -> None: - """Parse random configuration chooser given by user.""" - module_file = values - module_path = module_file.name - module_file.close() - import importlib.util - - spec = importlib.util.spec_from_file_location("smac.custom.random_configuration_chooser", module_path) - assert spec is not None - assert spec.loader is not None # please mypy - rcc_module = importlib.util.module_from_spec(spec) # type: ignore - spec.loader.exec_module(rcc_module) # type: ignore[attr-defined] # noqa F821 - setattr(namespace, self.dest, rcc_module.RandomConfigurationChooserImpl()) # type: ignore[attr-defined] # noqa F821 - - -class ProcessRunObjectiveAction(Action): - """Process run objective given by user.""" - - def __call__( # type: ignore[override] # noqa F821 - self, - parser: ArgumentParser, - namespace: Namespace, - values: str, - option_string: Optional[str] = None, - ) -> None: - """Process run objective given by user.""" - if values == "runtime": - parsed_scen_args["cutoff_time_required"] = { - "error": '--cutoff-time is required when --run-objective is set to "runtime"' - } - setattr(namespace, self.dest, values) - - -class ParseOverallObjectiveAction(Action): - """Parse overall objective given by user.""" - - def __call__( # type: ignore[override] # noqa F821 - self, - parser: ArgumentParser, - namespace: Namespace, - values: str, - option_string: Optional[str] = None, - ) -> None: - """Parse overall objective given by user.""" - par_str = values - if par_str[:3] in ["PAR", "par"]: - par_str = par_str[3:] - elif par_str[:4] in ["mean", "MEAN"]: - par_str = par_str[4:] - # Check for par-value as in "par10"/ "mean5" - if par_str: - parsed_scen_args["par_factor"] = int(par_str) - else: - logger.debug("No par-factor detected. Using 1 by default.") - parsed_scen_args["par_factor"] = 1 - setattr(namespace, self.dest, values) - - -class ReadTrainInstFileAction(Action): - """Read training instance file given by user.""" - - def __call__( # type: ignore[override] # noqa F821 - self, - parser: ArgumentParser, - namespace: Namespace, - values: Optional[str], - option_string: Optional[str] = None, - ) -> None: - """Read training instance file given by user.""" - fn = values - if fn: - if os.path.isfile(fn): - parsed_scen_args["train_insts"] = in_reader.read_instance_file(fn) - else: - parser.exit(1, "Could not find instance file: {}".format(fn)) - setattr(namespace, self.dest, values) - - -class ReadTestInstFileAction(Action): - """Read test instance file given by user.""" - - def __call__( # type: ignore[override] # noqa F821 - self, - parser: ArgumentParser, - namespace: Namespace, - values: Optional[str], - option_string: Optional[str] = None, - ) -> None: - """Read test instance file given by user.""" - fn = values - if fn: - if os.path.isfile(fn): - parsed_scen_args["test_insts"] = in_reader.read_instance_file(fn) - else: - parser.exit(1, "Could not find test instance file: {}".format(fn)) - setattr(namespace, self.dest, values) - - -class ReadFeatureFileAction(Action): - """Read feature file given by user.""" - - def __call__( # type: ignore[override] # noqa F821 - self, - parser: ArgumentParser, - namespace: Namespace, - values: Optional[str], - option_string: Optional[str] = None, - ) -> None: - """Read feature file given by user.""" - fn = values - if fn: - if os.path.isfile(fn): - instance_features = in_reader.read_instance_features_file(fn) - ( - parsed_scen_args["feature_names"], - parsed_scen_args["feature_dict"], - ) = instance_features - parsed_scen_args["features"] = instance_features - else: - parser.exit(1, "Could not find feature file: {}".format(fn)) - setattr(namespace, self.dest, values) - - -class ReadPCSFileAction(Action): - """Read PCS (parameter configuration space) file given by user.""" - - def __call__( # type: ignore[override] # noqa F821 - self, - parser: ArgumentParser, - namespace: Namespace, - values: Optional[str], - option_string: Optional[str] = None, - ) -> None: - """Read PCS (parameter configuration space) file given by user.""" - fn = values - if fn: - if os.path.isfile(fn): - cs = in_reader.read_pcs_file(fn) - cs.seed(42) - parsed_scen_args["cs"] = cs - else: - parser.exit(1, "Could not find pcs file: {}".format(fn)) - setattr(namespace, self.dest, values) - - -class ProcessOutputDirAction(Action): - """Process output directory given by user.""" - - def __call__( # type: ignore[override] # noqa F821 - self, - parser: ArgumentParser, - namespace: Namespace, - values: Optional[str], - option_string: str = None, - ) -> None: - """Process output directory given by user.""" - directory = values - if not directory: - logger.debug("Deactivate output directory.") - values = None - else: - logger.info("Output to {}".format(directory)) - setattr(namespace, self.dest, values) - - -class ConfigurableHelpFormatter(ArgumentDefaultsHelpFormatter): - """Configurable Help Formatter. Can filter out developer options.""" - - def __init__(self, *args: Any, help_type: str = "standard", **kwargs: Any): - self.help_type = help_type - super(ConfigurableHelpFormatter, self).__init__(*args, **kwargs) - - def _add_item(self, func: Callable, args: Any) -> None: - """ - Add item to help formatter. - - Parameters - ---------- - func : Callable - Function to call. - args : Any - Arguments to pass to function. - """ - - def filter_actions(actions: List[Action]) -> List[Action]: - filtered_actions = [] - for action in actions: - dev = False - if isinstance(action.help, str): - if action.help.startswith("[dev]"): - dev = True - else: - for s in action.option_strings: - if s.startswith("--dev"): - dev = True - break - if not dev: - filtered_actions.append(action) - return filtered_actions - - if self.help_type == "standard": - if func.__name__ == "_format_usage": - args = (args[0], filter_actions(args[1]), args[2], args[3]) - elif isinstance(args, list): - if args: - args = filter_actions(args) - if not args: - return - self._current_section.items.append((func, args)) - - -class SMACArgumentParser(ArgumentParser): - """ArgumentParser that can be extended by additional parsers.""" - - def __init__(self, *args: Any, **kwargs: Any) -> None: - self.additional_parsers = [] # type: List[ArgumentParser] - self.help_type = "standard" # standard or dev - super(SMACArgumentParser, self).__init__(*args, **kwargs) - - def set_help_type(self, help_type: str) -> None: - """Set help type to `self.additional_parsers`.""" - self.help_type = help_type - for parser in self.additional_parsers: - parser.help_type = help_type # type: ignore[attr-defined] # noqa F821 - - def add_parser(self, additional_parser: ArgumentParser) -> None: - """Add parser to `self.additional_parsers`.""" - self.additional_parsers.append(additional_parser) - - def _get_formatter(self) -> HelpFormatter: - """Returns formatter from `self.formatter_class`.""" - return self.formatter_class(prog=self.prog, help_type=self.help_type) # type: ignore[call-arg] # noqa F821 - - def format_help(self) -> str: - """Get help string from formatter.""" - formatter = self._get_formatter() - - # usage - formatter.add_usage( - self.usage, # type: ignore[arg-type] # noqa F821 - self._actions, - self._mutually_exclusive_groups, - ) - - # description - formatter.add_text(self.description) - - # positionals, optionals and user-defined groups - def add_action_groups(parser: ArgumentParser) -> None: - for action_group in parser._action_groups: - formatter.start_section(action_group.title) - formatter.add_text(action_group.description) - formatter.add_arguments(action_group._group_actions) - formatter.end_section() - - add_action_groups(self) - - # positionals, optionals and user-defined groups from additional parsers - for parser in self.additional_parsers: - add_action_groups(parser) - - # epilog - formatter.add_text(self.epilog) - - # determine help from format above - return formatter.format_help() - - -class StandardHelpAction(Action): - """Action to only show standard options in help message.""" - - def __init__(self, *args: Any, **kwargs: Any) -> None: - # https://github.com/python/mypy/issues/6799 - super().__init__(default=SUPPRESS, nargs=0, *args, **kwargs) # type: ignore - - def __call__( # type: ignore[override] # noqa F821 - self, - parser: SMACArgumentParser, - namespace: Namespace, - values: list, - option_string: Optional[str] = None, - ) -> None: - """Action to only show standard options in help message.""" - parser.set_help_type("standard") - parser.print_help() - parser.exit() - - -class DevHelpAction(Action): - """Action to show standard and developer options in help message.""" - - def __init__(self, *args: Any, **kwargs: Any): - # https://github.com/python/mypy/issues/6799 - super().__init__(default=SUPPRESS, nargs=0, *args, **kwargs) # type: ignore - - def __call__( # type: ignore[override] # noqa F821 - self, - parser: SMACArgumentParser, - namespace: Namespace, - values: list, - option_string: Optional[str] = None, - ) -> None: - """Action to show standard and developer options in help message.""" - parser.set_help_type("dev") - parser.print_help() - parser.exit() - - -class CMDReader(object): - """Use argparse to parse command line options. - - Attributes - ---------- - logger : Logger - """ - - def __init__(self) -> None: - global logger, parsed_scen_args - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - logger = self.logger - - # initialized in _add_main_options - self.parser = None # type: SMACArgumentParser # type: ignore[assignment] - self.main_cmd_actions = {} # type: Dict[str, Dict] - self.main_cmd_translations = {} # type: Dict[str, str] - # initialized in _add_smac_options - self.smac_parser = None # type: SMACArgumentParser # type: ignore[assignment] - self.smac_cmd_actions = {} # type: Dict[str, Dict] - self.smac_cmd_translations = {} # type: Dict[str, str] - # initialized in _add_scen_options - self.scen_parser = None # type: SMACArgumentParser # type: ignore[assignment] - self.scen_cmd_actions = {} # type: Dict[str, Dict] - self.scen_cmd_translations = {} # type: Dict[str, str] - # needed for argument interdependencies - self.parsed_scen_args = {} # type: PARSED_SCENARIO_ARGS_TYPE - parsed_scen_args = self.parsed_scen_args - - # add arguments to parser - self._add_main_options() - self._add_smac_options() - self._add_scen_options() - - @staticmethod - def _extract_action_info(actions: List[Action]) -> Tuple[Dict, Dict]: - """Extracts action information.""" - extracted_info = {} - translations = {} - for action in actions: - name_list = list(filter(lambda e: e.startswith("--"), action.option_strings)) - if name_list: - name = name_list[0] - else: - name = action.option_strings[0] - dest = name - if hasattr(action, "dest"): - dest = action.dest - cmd_action = ( - dict() - ) # type: Dict[str, Union[str, Callable[[str], Any], IO, None, Type, bool, Sequence[str], Iterable]] # noqa 501 - cmd_action["dest"] = dest - for name in action.option_strings: - translations[name] = dest - translations[name.lstrip("-")] = dest - if hasattr(action, "type"): - cmd_action["type"] = action.type - else: - cmd_action["type"] = str - if hasattr(action, "default"): - if action.default == SUPPRESS: - continue - cmd_action["default"] = action.default - else: - cmd_action["default"] = None - if hasattr(action, "choices"): - cmd_action["choices"] = action.choices - else: - cmd_action["choices"] = None - if hasattr(action, "required"): - cmd_action["required"] = action.required - else: - cmd_action["required"] = False - if hasattr(action, "help"): - cmd_action["help"] = action.help - else: - cmd_action["help"] = None - cmd_action["option_strings"] = action.option_strings - extracted_info[name] = cmd_action - return extracted_info, translations - - def _add_main_options(self) -> None: - """Add main options.""" - prog = sys.argv[0] - if re.match("^python[0-9._-]*$", sys.argv[0]): - prog = sys.argv[1] - self.parser = SMACArgumentParser(formatter_class=ConfigurableHelpFormatter, add_help=False, prog=prog) - # let a help message begin with "[dev]" to add a developer option - req_opts = self.parser.add_argument_group("Required Options") - req_opts.add_argument( - "--scenario", - "--scenario-file", - "--scenario_file", - dest="scenario_file", - required=True, - type=str, - action=CheckScenarioFileAction, - help="Scenario file in AClib format.", - ) - opt_opts = self.parser.add_argument_group("Optional Options") - opt_opts.add_argument( - "--help", - action=StandardHelpAction, - help="Show help messages for standard options.", - ) - opt_opts.add_argument( - "--help-all", - action=DevHelpAction, - help="Show help messages for both standard and developer options.", - ) - opt_opts.add_argument("--seed", default=1, type=int, help="Random Seed.") - opt_opts.add_argument( - "--verbose", - "--verbose-level", - "--verbose_level", - dest="verbose_level", - default=logging.INFO, - choices=["INFO", "DEBUG"], - help="Verbosity level.", - ) - opt_opts.add_argument( - "--mode", - default="SMAC4AC", - choices=["SMAC4AC", "ROAR", "Hydra", "PSMAC", "SMAC4HPO", "SMAC4BB"], - help="Configuration mode.", - ) - opt_opts.add_argument( - "--restore-state", - "--restore_state", - dest="restore_state", - default=None, - help="Path to directory with SMAC-files.", - ) - # list of runhistory dump files - # scenario corresponding to --warmstart_runhistory; - # pcs and feature space has to be identical to --scenario_file - opt_opts.add_argument( - "--warmstart-runhistory", - "--warmstart_runhistory", - dest="warmstart_runhistory", - default=None, - nargs="*", - help=SUPPRESS, - ) - opt_opts.add_argument( - "--warmstart-scenario", - "--warmstart_scenario", - dest="warmstart_scenario", - default=None, - nargs="*", - help=SUPPRESS, - ) - # list of trajectory dump files, reads runhistory and uses final incumbent as challenger - opt_opts.add_argument( - "--warmstart-incumbent", - "--warmstart_incumbent", - dest="warmstart_incumbent", - default=None, - nargs="*", - help=SUPPRESS, - ) - req_opts.add_argument( - "--random_configuration_chooser", - default=None, - type=FileType("r"), - help="[dev] path to a python module containing a class `RandomConfigurationChooserImpl`" - "implementing the interface of `RandomConfigurationChooser`", - ) - req_opts.add_argument( - "--hydra_iterations", - default=3, - type=int, - help="[dev] number of hydra iterations. Only active if mode is set to Hydra", - ) - req_opts.add_argument( - "--hydra_validation", - default="train", - choices=["train", "val10", "val20", "val30", "val40", "val50", "none"], - type=str.lower, - help="[dev] set to validate incumbents on. valX =>" " validation set of size training_set * 0.X", - ) - req_opts.add_argument( - "--incumbents_per_round", - default=1, - type=int, - help="[dev] number of configurations to keep per psmac/hydra iteration.", - dest="hydra_incumbents_per_round", - ) - req_opts.add_argument( - "--n_optimizers", - default=1, - type=int, - help="[dev] number of optimizers to run in parallel per psmac/hydra iteration.", - dest="hydra_n_optimizers", - ) - req_opts.add_argument( - "--psmac_validate", - default=False, - type=truthy, - help="[dev] Validate all psmac configurations.", - ) - - ( - self.main_cmd_actions, - self.main_cmd_translations, - ) = CMDReader._extract_action_info(self.parser._actions) - - def _add_smac_options(self) -> None: - """Add SMAC options.""" - self.smac_parser = SMACArgumentParser(formatter_class=ConfigurableHelpFormatter, add_help=False) - smac_opts = self.smac_parser.add_argument_group("SMAC Options") - smac_opts.add_argument( - "--abort-on-first-run-crash", - "--abort_on_first_run_crash", - dest="abort_on_first_run_crash", - default=True, - type=truthy, - help="If true, *SMAC* will abort if the first run of " "the target algorithm crashes.", - ) - smac_opts.add_argument( - "--limit-resources", - "--limit_resources", - dest="limit_resources", - default=False, - type=truthy, - help="If true, *SMAC* will use pynisher to limit time and memory for " - "the target algorithm. Allows SMAC to use all resources available. " - "Applicable only to func TAEs. Set to 'False' by default. " - "(Warning: This only works on Linux. Use with caution!)", - ) - smac_opts.add_argument( - "--minr", - "--minR", - dest="minR", - default=1, - type=int, - help="[dev] Minimum number of calls per configuration.", - ) - smac_opts.add_argument( - "--maxr", - "--maxR", - dest="maxR", - default=2000, - type=int, - help="[dev] Maximum number of calls per configuration.", - ) - self.output_dir_arg = smac_opts.add_argument( - "--output-dir", - "--output_dir", - dest="output_dir", - type=str, - action=ProcessOutputDirAction, - default="smac3-output_%s" % (datetime.datetime.fromtimestamp(time.time()).strftime("%Y-%m-%d_%H:%M:%S_%f")), - help="Specifies the output-directory for all emerging " "files, such as logging and results.", - ) - smac_opts.add_argument( - "--input-psmac-dirs", - "--input_psmac_dirs", - dest="input_psmac_dirs", - default=None, - help="For parallel SMAC, multiple output-directories " "are used.", - ) # TODO: type (list of strings? --> str, nargs=*) - smac_opts.add_argument( - "--shared-model", - "--shared_model", - dest="shared_model", - default=False, - type=truthy, - help="Whether to run SMAC in parallel mode.", - ) - smac_opts.add_argument( - "--random-configuration-chooser", - "--random_configuration_chooser", - dest="random_configuration_chooser", - default=None, - type=FileType("r"), - action=ParseRandomConfigurationChooserAction, - help="[dev] path to a python module containing a class" - "`RandomConfigurationChooserImpl` implementing" - "the interface of `RandomConfigurationChooser`", - ) - smac_opts.add_argument( - "--hydra-iterations", - "--hydra_iterations", - dest="hydra_iterations", - default=3, - type=int, - help="[dev] number of hydra iterations. Only active if mode is set to Hydra", - ) - smac_opts.add_argument( - "--use-ta-time", - "--use_ta_time", - dest="use_ta_time", - default=False, - type=truthy, - help="[dev] Instead of measuring SMAC's wallclock time, " - "only consider time reported by the target algorithm (ta).", - ) - - # Hyperparameters - smac_opts.add_argument( - "--always-race-default", - "--always_race_default", - dest="always_race_default", - default=False, - type=truthy, - help="[dev] Race new incumbents always against default " "configuration.", - ) - smac_opts.add_argument( - "--intensification-percentage", - "--intensification_percentage", - dest="intensification_percentage", - default=0.5, - type=float, - help="[dev] The fraction of time to be used on " - "intensification (versus choice of next " - "Configurations).", - ) - smac_opts.add_argument( - "--transform_y", - "--transform-y", - dest="transform_y", - choices=["NONE", "LOG", "LOGS", "INVS"], - default="NONE", - help="[dev] Transform all observed cost values" - " via log-transformations or inverse scaling." - ' The subfix "s" indicates that SMAC scales the' - " y-values accordingly to apply the transformation.", - ) - - # RF Hyperparameters - smac_opts.add_argument( - "--rf_num_trees", - "--rf-num-trees", - dest="rf_num_trees", - default=N_TREES, - type=int, - help="[dev] Number of trees in the random forest (> 1).", - ) - smac_opts.add_argument( - "--rf_do_bootstrapping", - "--rf-do-bootstrapping", - dest="rf_do_bootstrapping", - default=True, - type=bool, - help="[dev] Use bootstraping in random forest.", - ) - smac_opts.add_argument( - "--rf_ratio_features", - "--rf-ratio-features", - dest="rf_ratio_features", - default=5.0 / 6.0, - type=float, - help="[dev] Ratio of sampled features in each split ([0.,1.]).", - ) - smac_opts.add_argument( - "--rf_min_samples_split", - "--rf-min-samples-split", - dest="rf_min_samples_split", - default=3, - type=int, - help="[dev] Minimum number of samples" " to split for building a tree in the random forest.", - ) - smac_opts.add_argument( - "--rf_min_samples_leaf", - "--rf-min-samples-leaf", - dest="rf_min_samples_leaf", - default=3, - type=int, - help="[dev] Minimum required number of" " samples in each leaf of a tree in the random forest.", - ) - smac_opts.add_argument( - "--rf_max_depth", - "--rf-max-depth", - dest="rf_max_depth", - default=20, - type=int, - help="[dev] Maximum depth of each tree in the random forest.", - ) - # AcquisitionOptimizer SLS - smac_opts.add_argument( - "--sls_n_steps_plateau_walk", - "--sls-n-steps-plateau-walk", - dest="sls_n_steps_plateau_walk", - default=10, - type=int, - help="[dev] Maximum number of steps on plateaus during " "the optimization of the acquisition function.", - ) - smac_opts.add_argument( - "--sls_max_steps", - "--sls-max-steps", - dest="sls_max_steps", - default=None, - type=int, - help="[dev] Maximum number of local search steps in one iteration" - " during the optimization of the acquisition function.", - ) - smac_opts.add_argument( - "--acq_opt_challengers", - "--acq-opt-challengers", - dest="acq_opt_challengers", - default=5000, - type=int, - help="[dev] Number of challengers returned by acquisition function" - " optimization. Also influences the number of randomly sampled" - " configurations to optimized the acquisition function", - ) - - # Intensification - smac_opts.add_argument( - "--intens_adaptive_capping_slackfactor", - "--intens-adaptive-capping-slackfactork", - dest="intens_adaptive_capping_slackfactor", - default=1.2, - type=float, - help="[dev] Slack factor of adpative capping (factor * adpative cutoff)." - " Only active if obj is runtime." - " If set to very large number it practically deactivates adaptive capping.", - ) - smac_opts.add_argument( - "--intens_min_chall", - "--intens-min-chall", - dest="intens_min_chall", - default=2, - type=int, - help="[dev] Minimal number of challengers to be" - " considered in each intensification run (> 1)." - " Set to 1 and in combination with very small intensification-percentage." - " it will deactivate randomly sampled configurations" - " (and hence, extrapolation of random forest will be an issue.)", - ) - smac_opts.add_argument( - "--rand_prob", - "--rand-prob", - dest="rand_prob", - default=0.5, - type=float, - help="[dev] probablity to run a random configuration" - " instead of configuration optimized on the acquisition function", - ) - self.parser.add_parser(self.smac_parser) - ( - self.smac_cmd_actions, - self.smac_cmd_translations, - ) = CMDReader._extract_action_info(self.smac_parser._actions) - - def _add_scen_options(self) -> None: - """Add scenario options.""" - self.scen_parser = SMACArgumentParser(formatter_class=ConfigurableHelpFormatter, add_help=False) - scen_opts = self.scen_parser.add_argument_group("Scenario Options") - scen_opts.add_argument( - "--algo", - "--ta", - dest="ta", - type=shlex.split, - help="[dev] Specifies the target algorithm call that *SMAC* " - "will optimize. Interpreted as a bash-command.", - ) - scen_opts.add_argument( - "--execdir", - dest="execdir", - default=".", - type=str, - help="[dev] Specifies the path to the execution-directory.", - ) - scen_opts.add_argument( - "--deterministic", - dest="deterministic", - default=True, - type=truthy, - help="[dev] If true, SMAC assumes that the target function or algorithm is deterministic" - " (the same static seed of 0 is always passed to the function/algorithm)." - " If false, different random seeds are passed to the target function/algorithm.", - ) - scen_opts.add_argument( - "--run-obj", - "--run_obj", - dest="run_obj", - type=str, - action=ProcessRunObjectiveAction, - required=True, - choices=["runtime", "quality"], - help="[dev] Defines what metric to optimize. When " - "optimizing runtime, *cutoff_time* is " - "required as well.", - ) - scen_opts.add_argument( - "--multi-objectives", - "--multi_objectives", - dest="multi_objectives", - default="cost", - type=multi_objectives, - help="List of string or comma-separated strings of objectives to optimize.", - ) - self.overall_obj_arg = scen_opts.add_argument( - "--overall-obj", - "--overall_obj", - dest="overall_obj", - type=str, - action=ParseOverallObjectiveAction, - default="par10", - help="[dev] PARX, where X is an integer defining the " - "penalty imposed on timeouts (i.e. runtimes that " - "exceed the *cutoff-time*).", - ) - - scen_opts.add_argument( - "--save-instantly", - "--save_instantly", - "--save-results-instantly", - dest="save_instantly", - default=True, - type=truthy, - help="If true, runhistory and stats are saved immediately on changes. " - "Otherwise, runhistory and states are only saved once after the optimization " - "process has finished.", - ) - scen_opts.add_argument( - "--par-factor", - "--par_factor", - dest="par_factor", - type=float, - default=10.0, - help=SUPPRESS, - ) # added after parsing --overall-obj - scen_opts.add_argument( - "--cost-for-crash", - "--cost_for_crash", - dest="cost_for_crash", - default=float(MAXINT), - type=cost_for_crash, - help="[dev] Defines the cost-value for crashed runs " - "on scenarios with quality as run-obj. " - "If multi-objective is used, a list or comma separated string is accepted too.", - ) - scen_opts.add_argument( - "--cutoff-time", - "--cutoff_time", - "--cutoff", - dest="cutoff", - default=None, - type=float, - help="[dev] Maximum runtime, after which the " - "target algorithm is cancelled. **Required " - "if *run_obj* is runtime.**", - ) - scen_opts.add_argument( - "--memory-limit", - "--memory_limit", - dest="memory_limit", - type=float, - help="[dev] Maximum available memory the target algorithm " "can occupy before being cancelled in MB.", - ) - scen_opts.add_argument( - "--tuner-timeout", - "--tuner_timeout", - "--algo-runs-timelimit", - "--algo_runs_timelimit", - dest="algo_runs_timelimit", - default=float("inf"), - type=float, - help="[dev] Maximum amount of CPU-time used for optimization.", - ) - scen_opts.add_argument( - "--wallclock-limit", - "--wallclock_limit", - dest="wallclock_limit", - default=float("inf"), - type=float, - help="[dev] Maximum amount of wallclock-time used for optimization.", - ) - scen_opts.add_argument( - "--always-race-default", - "--always_race_default", - dest="always_race_default", - default=False, - type=truthy, - help="[dev] Race new incumbents always against default configuration.", - ) - scen_opts.add_argument( - "--runcount-limit", - "--runcount_limit", - "--ta-run-limit", - "--ta_run_limit", - dest="ta_run_limit", - default=float("inf"), - type=float, - help="[dev] Maximum number of algorithm-calls during optimization.", - ) - scen_opts.add_argument( - "--instance-file", - "--instance_file", - "--train-inst-fn", - "--train_inst_fn", - dest="train_inst_fn", - type=str, - action=ReadTrainInstFileAction, - help="[dev] Specifies the file with the training-instances.", - ) - scen_opts.add_argument( - "--instances", - "--train-insts", - "--train_insts", - dest="train_insts", - default=[[None]], # overridden by --instance-file - help=SUPPRESS, - ) - scen_opts.add_argument( - "--test-instance-file", - "--test_instance_file", - "--test-inst-fn", - "--test_inst_fn", - dest="test_inst_fn", - type=str, - action=ReadTestInstFileAction, - help="[dev] Specifies the file with the test-instances.", - ) - scen_opts.add_argument( - "--test-instances", - "--test_instances", - "--test-insts", - "--test_insts", - dest="test_insts", - default=[[None]], # overridden by --test-instance-file - help=SUPPRESS, - ) - scen_opts.add_argument( - "--feature-file", - "--feature_file", - "--feature-fn", - "--feature_fn", - dest="feature_fn", - type=str, - action=ReadFeatureFileAction, - help="[dev] Specifies the file with the instance-features.", - ) - scen_opts.add_argument( - "--features", - "--feature-dict", - "--feature_dict", - dest="feature_dict", - default={}, # instance name -> feature vector, overridden by --feature-file - help=SUPPRESS, - ) - scen_opts.add_argument( - "--feature-names", - "--feature_names", - dest="feature_names", - type=list, # type: ignore[arg-type] # noqa F821 - help=SUPPRESS, - ) # added after parsing --features - scen_opts.add_argument( - "--initial-incumbent", - "--initial_incumbent", - dest="initial_incumbent", - default="DEFAULT", - type=str, - choices=["DEFAULT", "RANDOM", "LHD", "SOBOL", "FACTORIAL"], - help="[dev] DEFAULT is the default from the PCS.", - ) - scen_opts.add_argument( - "--paramfile", - "--param-file", - "--param_file", - "--pcs-fn", - "--pcs_fn", - dest="pcs_fn", - type=str, - action=ReadPCSFileAction, - help="[dev] Specifies the path to the " "PCS-file.", - ) - scen_opts.add_argument( - "--cs", - default=None, # ConfigSpace object, overridden by --paramfile - help=SUPPRESS, - ) - - self.parser.add_parser(self.scen_parser) - ( - self.scen_cmd_actions, - self.scen_cmd_translations, - ) = CMDReader._extract_action_info(self.scen_parser._actions) - - def parse_main_command(self, main_cmd_opts: Sequence[str]) -> Tuple[Namespace, List[str]]: - """Parse main options.""" - args_, misc = self.parser.parse_known_args(main_cmd_opts) - try: - misc.remove(self.parser.prog) - except ValueError: - pass - return args_, misc - - def parse_smac_command( - self, - smac_dict: dict = {}, - smac_cmd_opts: List[str] = [], - ) -> Tuple[Namespace, Dict, List[str]]: - """Parse SMAC options.""" - # transform smac dict to smac_args - try: - smac_cmd_opts.remove(self.parser.prog) - except ValueError: - pass - smac_cmd = [] - misc_dict = {} - - parsed_smac_args = {} - for k, v in smac_dict.items(): - if k in self.smac_cmd_translations: - if not isinstance( - v, - ( - str, - bool, - int, - float, - ), - ): - parsed_smac_args[self.smac_cmd_translations[k]] = v - else: - smac_cmd.append("--" + k.replace("_", "-")) - smac_cmd.append(v) - else: - misc_dict[k] = v - smac_cmd.extend(smac_cmd_opts) - - args_, misc_cmd = self.smac_parser.parse_known_args([str(e) for e in smac_cmd]) - - # execute output_dir action for default value - if args_.output_dir == self.output_dir_arg.default: - self.output_dir_arg(self.parser, args_, self.output_dir_arg.default) - - if args_.shared_model and args_.input_psmac_dirs is None: - # per default, we assume that - # all pSMAC runs write to the default output dir - args_.input_psmac_dirs = "smac3-output*/run*/" - - # copy parsed smac args to smac_args_ - for k, v in parsed_smac_args.items(): - setattr(args_, k, v) - - return args_, misc_dict, misc_cmd - - def parse_scenario_command( - self, - scenario_file: str = None, - scenario_dict: dict = {}, - scenario_cmd_opts: List[str] = [], - ) -> Namespace: - """Parses scenario options. - - Parameters - ---------- - scenario_file : str, optional - Path to the scenario file, by default None - scenario_dict : dict, optional - Mappings of scenario options to values, by default {} - scenario_cmd_opts : List[str], optional - Scenario options from command line, by default [] - - Returns - ------- - Namespace - Parsed scenario arguments. - - Raises - ------ - ValueError - If scenario is not a string or dictionary. - """ - # read scenario file - scenario_file_dict = {} # type: Dict[str, Any] - if isinstance(scenario_file, str): - scenario_file_dict = in_reader.read_scenario_file(scenario_file) - elif scenario_file is None: - pass - else: - raise ValueError("Scenario has to be a string or a dictionary") - # add options from scenario dict (with overwriting) - scenario_file_dict.update(scenario_dict) - # transform scenario dict to scen_args - scen_dict = scenario_file_dict - scen_cmd = [] - misc_dict = {} - - self.parsed_scen_args.clear() - for k, v in scen_dict.items(): - if k in self.scen_cmd_translations: - if not isinstance( - v, - ( - str, - bool, - int, - float, - ), - ): - # e.g. train_insts, test_insts, cs, features - self.parsed_scen_args[self.scen_cmd_translations[k]] = v - else: - scen_cmd.append("--" + k.replace("_", "-")) - scen_cmd.append(str(v)) - else: - misc_dict[k] = v - scen_cmd.extend(scenario_cmd_opts) - - if misc_dict.keys(): - self.logger.warning("Adding unsupported scenario options: {}".format(misc_dict)) - for k, v in misc_dict.items(): - self.parsed_scen_args[k] = v - # Fail in a later version: - # self.scen_parser.exit(1, 'Error: Unknown arguments: {}'.format(misc_dict)) - - # append rest of arguments (= override) options from scenario file and - # parse them with the scenario parser - scen_args_, misc = self.scen_parser.parse_known_args([str(e) for e in scen_cmd]) - - if misc: - self.scen_parser.exit(1, "Error: Can not parse arguments: {}".format(misc)) - - # execute overall_obj action for default value - if scen_args_.overall_obj == self.overall_obj_arg.default: - self.overall_obj_arg(self.scen_parser, scen_args_, self.overall_obj_arg.default) - - # make checks that argparse can't perform natively - - if "cutoff_time_required" in self.parsed_scen_args: - if self.parsed_scen_args["cutoff_time_required"] and not scen_args_.cutoff: - self.parser.exit(1, "Error: {}".format(self.parsed_scen_args["cutoff_time_required"]["error"])) # type: ignore # noqa 501 - self.parsed_scen_args.pop("cutoff_time_required") - - # copy parsed scenario args to scen_args_ - # par_factor, train_insts, test_insts, (features, features_names), feature_dict, cs - for k, v in self.parsed_scen_args.items(): - setattr(scen_args_, k, v) - - return scen_args_ - - def read_smac_scenario_dict_cmd( - self, - dict_cmd: dict, - scenario_file: str = None, - ) -> Tuple[Namespace, Namespace]: - """Reads smac and scenario options provided in a dictionary. - - Returns - ------- - smac_args_, scen_args_: smac and scenario options parsed with corresponding ArgumentParser - """ - smac_args_, misc_dict, misc_cmd = self.parse_smac_command(smac_dict=dict_cmd) - scen_args_ = self.parse_scenario_command( - scenario_file=scenario_file, - scenario_dict=misc_dict, - scenario_cmd_opts=misc_cmd, - ) - - return smac_args_, scen_args_ - - def read_cmd( - self, - commandline_arguments: Sequence[str] = tuple(sys.argv[1:]), - ) -> Tuple[Namespace, Namespace, Namespace]: - """Reads command line options (main, smac and scenario options) - - Returns - ------- - smac_args_, scen_args_: parsed arguments; - main, smac and scenario options parsed with corresponding ArgumentParser - """ - main_args_, misc = self.parse_main_command(main_cmd_opts=commandline_arguments) - smac_args_, misc_dict, misc_cmd = self.parse_smac_command(smac_cmd_opts=misc) - scen_args_ = self.parse_scenario_command( - scenario_file=main_args_.scenario_file, - scenario_dict=misc_dict, - scenario_cmd_opts=misc_cmd, - ) - return main_args_, smac_args_, scen_args_ - - @staticmethod - def _write_options_to_doc(_arguments: dict, path: str, exclude: List[str]) -> None: - """Writes options to the documentation.""" - with open(path, "w") as fh: - for arg in sorted(_arguments.keys()): - print_arg = arg.lstrip("-").replace("-", "_") - if print_arg in exclude: - continue - if _arguments[arg]["help"] == SUPPRESS: - continue - fh.write(":{}: ".format(print_arg)) - fh.write("{}".format(_arguments[arg]["help"].lstrip("[dev] "))) - if "default" in _arguments[arg] and _arguments[arg]["default"]: - fh.write(" Default: {}.".format(_arguments[arg]["default"])) - if "choice" in _arguments[arg] and _arguments[arg]["choice"]: - fh.write(" Must be from: {}.".format(_arguments[arg]["choice"])) - fh.write("\n") - fh.write("\n\n") - - def write_main_options_to_doc(self, path: str = "main_options.rst") -> None: - """Writes the SMAC option-list to file for autogeneration in documentation. - The list is created in doc/conf.py and read in doc/options.rst. - - Parameters - ---------- - path: string - Where to write to (relative to doc-folder since executed in conf.py) - """ - exclude = [] # type: List - _arguments = self.main_cmd_actions - CMDReader._write_options_to_doc(_arguments, path, exclude) - - def write_smac_options_to_doc(self, path: str = "smac_options.rst") -> None: - """Writes the SMAC option-list to file for autogeneration in documentation. - The list is created in doc/conf.py and read in doc/options.rst. - - Parameters - ---------- - path: string - Where to write to (relative to doc-folder since executed in conf.py) - """ - exclude = [] # type: List - _arguments = self.smac_cmd_actions - CMDReader._write_options_to_doc(_arguments, path, exclude) - - def write_scenario_options_to_doc(self, path: str = "scenario_options.rst") -> None: - """Writes the Scenario option-list to file for autogeneration in documentation. - The list is created in doc/conf.py and read in doc/options.rst. - - Parameters - ---------- - path: string - Where to write to (relative to doc-folder since executed in conf.py) - """ - exclude = ["cs", "features", "instances", "test_instances"] - _arguments = self.scen_cmd_actions - CMDReader._write_options_to_doc(_arguments, path, exclude) diff --git a/smac/utils/io/input_reader.py b/smac/utils/io/input_reader.py deleted file mode 100644 index de8aef933..000000000 --- a/smac/utils/io/input_reader.py +++ /dev/null @@ -1,216 +0,0 @@ -from typing import Any, Dict, List, Optional, Tuple - -import numpy as np - -from smac.configspace import ConfigurationSpace -from smac.configspace import json as pcs_json -from smac.configspace import pcs, pcs_new -from smac.utils.logging import PickableLoggerAdapter - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Marius Lindauer" -__email__ = "lindauer@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -INSTANCE_TYPE = List[List[str]] -INSTANCE_FEATURES_TYPE = Tuple[List[str], Dict[str, np.ndarray]] - - -class InputReader(object): - """Reading all input files for SMAC (scenario file, instance files, ...) - - Note: Most of this code was taken from the pysmac repository. - We copy it here because we don't want smac3 to depend - on an earlier version! - """ - - def __init__(self) -> None: - pass - - def read_scenario_file(self, fn: str) -> Dict[str, Any]: - """Encapsulates read_scenario_file of pysmac - - Parameters - ---------- - fn: string - File name of scenario file - - Returns - ------- - dict : dictionary - (key, value) pairs are (variable name, variable value) - """ - # translate the difference option names to a canonical name - # kept for backwards-compatibility - scenario_option_names = { - "algo-exec": "algo", - "algoExec": "algo", - "algo": "algo", - "algo-exec-dir": "execdir", - "exec-dir": "execdir", - "execDir": "execdir", - "execdir": "execdir", - "algo-deterministic": "deterministic", - "deterministic": "deterministic", - "paramFile": "paramfile", - "pcs-file": "paramfile", - "param-file": "paramfile", - "paramfile": "paramfile", - "run-obj": "run_obj", - "run-objective": "run_obj", - "runObj": "run_obj", - "run_obj": "run_obj", - "overall_obj": "overall_obj", - "intra-obj": "overall_obj", - "intra-instance-obj": "overall_obj", - "overall-obj": "overall_obj", - "intraInstanceObj": "overall_obj", - "overallObj": "overall_obj", - "intra_instance_obj": "overall_obj", - "cost-for-crash": "cost_for_crash", - "cost_for_crash": "cost_for_crash", - "algo-cutoff-time": "cutoff_time", - "target-run-cputime-limit": "cutoff_time", - "target_run_cputime_limit": "cutoff_time", - "cutoff-time": "cutoff_time", - "cutoffTime": "cutoff_time", - "cutoff_time": "cutoff_time", - "memory-limit": "memory_limit", - "memory_limit": "memory_limit", - "cputime-limit": "tuner_timeout", - "cputime_limit": "tuner_timeout", - "tunertime-limit": "tuner_timeout", - "tuner-timeout": "tuner_timeout", - "tunerTimeout": "tuner_timeout", - "tuner_timeout": "tuner_timeout", - "wallclock-limit": "wallclock_limit", - "runtime-limit": "wallclock_limit", - "runtimeLimit": "wallclock_limit", - "wallClockLimit": "wallclock_limit", - "wallclock_limit": "wallclock_limit", - "output-dir": "output_dir", - "outputDirectory": "output_dir", - "outdir": "output_dir", - "output_dir": "output_dir", - "instances": "instance_file", - "instance-file": "instance_file", - "instance-dir": "instance_file", - "instanceFile": "instance_file", - "instance_file": "instance_file", - "i": "instance_file", - "instance_seed_file": "instance_file", - "test-instances": "test_instance_file", - "test-instance-file": "test_instance_file", - "test-instance-dir": "test_instance_file", - "testInstanceFile": "test_instance_file", - "test_instance_file": "test_instance_file", - "test_instance_seed_file": "test_instance_file", - "feature-file": "feature_file", - "instanceFeatureFile": "feature_file", - "feature_file": "feature_file", - "runcount-limit": "runcount_limit", - "runcount_limit": "runcount_limit", - "totalNumRunsLimit": "runcount_limit", - "numRunsLimit": "runcount_limit", - "numberOfRunsLimit": "runcount_limit", - "initial-incumbent": "initial_incumbent", - "initial_incumbent": "initial_incumbent", - } - - scenario_dict = {} - with open(fn, "r") as fh: - for line in fh: - line = line.replace("\n", "").strip(" ") - # remove comments - if line.find("#") > -1: - line = line[: line.find("#")] - - # skip empty lines - if line == "": - continue - if "=" in line: - tmp = line.split("=") - tmp = [" ".join(s.split()) for s in tmp] - else: - tmp = line.split() - scenario_dict[scenario_option_names.get(tmp[0], tmp[0])] = " ".join(tmp[1:]) - return scenario_dict - - def read_instance_file(self, fn: str) -> INSTANCE_TYPE: - """Encapsulates read_instances_file of pysmac - - Parameters - ---------- - fn: string - File name of instance file - - Returns - ------- - instances: list - Each element is a list where the first element is the - instance name followed by additional - information for the specific instance. - """ - with open(fn, "r") as fh: - instance_names = fh.readlines() - return [s.strip().split() for s in instance_names] - - def read_instance_features_file( - self, - fn: str, - ) -> INSTANCE_FEATURES_TYPE: - """Encapsulates read_instances_file of pysmac - - Parameters - ---------- - fn: string - File name of instance feature file - - Returns - ------- - features: tuple - first entry is a list of the feature names, - second one is a dict with 'instance name' - - 'numpy array containing the features' key-value pairs - """ - instances = {} - with open(fn, "r") as fh: - lines = fh.readlines() - for line in lines[1:]: - tmp = line.strip().split(",") - instances[tmp[0]] = np.array(tmp[1:], dtype=np.double) - return [f.strip() for f in lines[0].rstrip("\n").split(",")[1:]], instances # type: ignore - - @staticmethod - def read_pcs_file(fn: str, logger: Optional[PickableLoggerAdapter] = None) -> ConfigurationSpace: - """Encapsulates generating configuration space object from file. - - Automatically detects whether the cs is saved in json, pcs or pcs_new. - - Parameters - ---------- - fn: string - File name of pcs file - - Returns - ------- - ConfigSpace: ConfigSpace - """ - # Three possible formats: json, pcs and pcs_new. We prefer json. - with open(fn) as fp: - if fn.endswith(".json"): - cs = pcs_json.read(fp.read()) - if logger: - logger.debug("Loading pcs as json from: %s", fn) - else: - pcs_str = fp.readlines() - try: - cs = pcs.read(pcs_str) - except NotImplementedError: - if logger: - logger.debug("Could not parse pcs file with old format; trying new format ...") - cs = pcs_new.read(pcs_str) - return cs diff --git a/smac/utils/io/output_directory.py b/smac/utils/io/output_directory.py deleted file mode 100644 index 87601b83a..000000000 --- a/smac/utils/io/output_directory.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import shutil -from logging import Logger - -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def create_output_directory( - scenario: Scenario, - run_id: int, - logger: Logger = None, -) -> str: - """Create output directory for this run. - - Side effect: Adds the current output directory to the scenario object! - - Parameters - ---------- - scenario : ~smac.scenario.scenario.Scenario - run_id : int - - Returns - ------- - str - """ - if scenario.output_dir: # type: ignore[attr-defined] # noqa F821 - output_dir = os.path.join( - scenario.output_dir, # type: ignore[attr-defined] # noqa F821 - "run_%d" % (run_id), - ) - else: - return "" - if os.path.exists(output_dir): - move_to = output_dir + ".OLD" - while os.path.exists(move_to): - move_to += ".OLD" - shutil.move(output_dir, move_to) - if logger is not None: - logger.warning( - 'Output directory "%s" already exists! ' 'Moving old folder to "%s".', - output_dir, - move_to, - ) - scenario.output_dir_for_this_run = output_dir - return output_dir diff --git a/smac/utils/io/output_writer.py b/smac/utils/io/output_writer.py deleted file mode 100644 index 8fb7cde47..000000000 --- a/smac/utils/io/output_writer.py +++ /dev/null @@ -1,213 +0,0 @@ -from typing import TYPE_CHECKING, Any, Dict, Iterable, List - -import os -import shutil -import warnings - -from smac.configspace import ConfigurationSpace, json, pcs_new -from smac.utils.logging import PickableLoggerAdapter - -if TYPE_CHECKING: - from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class OutputWriter(object): - """Writing scenario to file.""" - - def __init__(self) -> None: - self.logger = PickableLoggerAdapter(name=self.__module__ + "." + self.__class__.__name__) - - def write_scenario_file(self, scenario: "Scenario") -> None: - """Write scenario to a file (format is compatible with input_reader). - Will overwrite if file exists. If you have arguments that need special - parsing when saving, specify so in the _parse_argument-function. - Creates output-dir if necessesary. - - Parameters - ---------- - scenario: Scenario - Scenario to be written to file - - Returns - ------- - status: False or None - False indicates that writing process failed - """ - if scenario.output_dir_for_this_run is None or scenario.output_dir_for_this_run == "": - scenario.logger.info( - "No output directory for scenario logging " "specified -- scenario will not be logged." - ) - return - - # Create output-dir if necessary - if not os.path.isdir(scenario.output_dir_for_this_run): - scenario.logger.debug("Output directory does not exist! Will be " "created.") - try: - os.makedirs(scenario.output_dir_for_this_run) - except OSError: - scenario.logger.debug("Could not make output directory.", exc_info=True) - raise OSError("Could not make output directory: " "{}.".format(scenario.output_dir_for_this_run)) - - # options_dest2name maps scenario._arguments from dest -> name - options_dest2name = { - (scenario._arguments[v]["dest"] if scenario._arguments[v]["dest"] else v): v.lstrip("-").replace("-", "_") - for v in scenario._arguments - } - - # Write all options into "output_dir/scenario.txt" - path = os.path.join(scenario.output_dir_for_this_run, "scenario.txt") - scenario.logger.debug("Writing scenario-file to {}.".format(path)) - with open(path, "w") as fh: - for key in options_dest2name: - key = key.lstrip("-").replace("-", "_") - new_value = self._parse_argument(scenario, key, getattr(scenario, key)) - - # Make array to string again - if key == "multi_objectives" and isinstance(new_value, list): - new_value = ",".join(new_value) - - if new_value is not None: - fh.write("{} = {}\n".format(options_dest2name[key], new_value)) - - def _parse_argument(self, scenario: "Scenario", key: str, value: Any) -> Any: - """Some values of the scenario-file need to be changed upon writing, - such as the 'ta' (target algorithm), due to it's callback. Also, - the configspace, features, train_inst- and test-inst-lists are saved - to output_dir, if they exist. - - Sideeffects - copies files pcs_fn, train_inst_fn, test_inst_fn and feature_fn to - output if possible, creates the files from attributes otherwise - - Parameters - ---------- - scenario: Scenario - Scenario-file to be written - key: string - Name of the attribute in scenario-file - value: Any - Corresponding attribute - - Returns - ------- - new value: string - The altered value, to be written to file - - """ - if key in ["pcs_fn", "train_inst_fn", "test_inst_fn", "feature_fn"]: - # Copy if file exists, else write to new file - if value is not None and os.path.isfile(value): - try: - assert scenario.output_dir_for_this_run is not None # please mypy - new_path = shutil.copy(value, scenario.output_dir_for_this_run) - except shutil.SameFileError: - new_path = value # File is already in output_dir - # For .pcs-file, also save with the same basename as json and use json-path! - if key == "pcs_fn" and scenario.cs is not None and value.endswith(".pcs"): # type: ignore[attr-defined] # noqa F821 - file_name = os.path.splitext(os.path.basename(value))[0] - assert scenario.output_dir_for_this_run is not None # please mypy - new_path = os.path.join(scenario.output_dir_for_this_run, file_name + ".json") - self.save_configspace(scenario.cs, new_path, "json") # type: ignore[attr-defined] # noqa F821 - scenario.logger.debug("Setting the pcs_fn-attr of written scenario from %s to %s", value, new_path) - elif key == "pcs_fn" and scenario.cs is not None: # type: ignore[attr-defined] # noqa F821 - try: - assert scenario.output_dir_for_this_run is not None # please mypy - pcs_path = os.path.join(scenario.output_dir_for_this_run, "configspace.pcs") - self.save_configspace(scenario.cs, pcs_path, "pcs_new") # type: ignore[attr-defined] # noqa F821 - except (TypeError, ValueError) as e: - if isinstance(e, TypeError): - self.logger.error( - "Could not write pcs file to disk." " ConfigSpace not compatible with (new) pcs format." - ) - else: - warnings.warn(f"{e}. No pcs file will be generated in the output.") - - assert scenario.output_dir_for_this_run is not None # please mypy - new_path = os.path.join(scenario.output_dir_for_this_run, "configspace.json") - self.save_configspace(scenario.cs, new_path, "json") # type: ignore[attr-defined] # noqa F821 - elif key == "train_inst_fn" and scenario.train_insts != [None]: - assert scenario.output_dir_for_this_run is not None # please mypy - new_path = os.path.join(scenario.output_dir_for_this_run, "train_insts.txt") - self.write_inst_file(scenario.train_insts, new_path) - elif key == "test_inst_fn" and scenario.test_insts != [None]: - assert scenario.output_dir_for_this_run is not None # please mypy - new_path = os.path.join(scenario.output_dir_for_this_run, "test_insts.txt") - self.write_inst_file(scenario.test_insts, new_path) - elif key == "feature_fn" and scenario.feature_dict != {}: - assert scenario.output_dir_for_this_run is not None # please mypy - new_path = os.path.join(scenario.output_dir_for_this_run, "features.txt") - self.write_inst_features_file(scenario.n_features, scenario.feature_dict, new_path) - else: - return None - # New value -> new path - return new_path - elif key == "ta" and value is not None: - # Reversing the callback on 'ta' (shlex.split) - return " ".join(value) - elif key in ["train_insts", "test_insts", "cs", "feature_dict"]: - # No need to log, recreated from files - return None - - return value - - def write_inst_file(self, insts: List[str], fn: str) -> None: - """Writes instance-list to file. - - Parameters - ---------- - insts: list - Instance list to be written - fn: string - Output path - """ - with open(fn, "w") as fh: - fh.write("\n".join(insts)) - - def write_inst_features_file( - self, - n_features: int, - feat_dict: Dict[str, Iterable[float]], - fn: str, - ) -> None: - """Writes features to file. - - Parameters - ---------- - n_features: int - Number of features - feat_dict: dict - Features to be written - fn: string - File name of instance feature file - """ - header = "Instance, " + ", ".join(["feature" + str(i) for i in range(n_features)]) + "\n" - body = [", ".join([inst] + [str(f) for f in feat_dict[inst]]) + "\n" for inst in feat_dict] - with open(fn, "w") as fh: - fh.write(header + "".join(body)) - - def save_configspace(self, cs: ConfigurationSpace, fn: str, output_format: str) -> None: - """Writing ConfigSpace to file. - - Parameters - ---------- - cs : ConfigurationSpace - Config-space to be written - fn : str - Output-file-path - output_format : str - Output format of the configuration space file. Currently, - ``json`` and ``pcs_new`` are supported. - """ - writers = {"pcs_new": pcs_new.write, "json": json.write} - - writer = writers.get(output_format) - if writer: - with open(fn, "w") as fh: - fh.write(writer(cs)) - else: - raise ValueError( - "Configuration space output format %s not supported. " - "Please choose one of %s" % (output_format, set(writers.keys())) - ) diff --git a/smac/utils/io/result_merging.py b/smac/utils/io/result_merging.py deleted file mode 100644 index dd13968ab..000000000 --- a/smac/utils/io/result_merging.py +++ /dev/null @@ -1,207 +0,0 @@ -from typing import Any, Dict, List, Optional, Sequence, Union - -import json -import os -from pathlib import Path - -import numpy as np -import regex as re -from ConfigSpace.read_and_write import json as csjson - -from smac.runhistory.runhistory import RunHistory - - -def glob_re(pattern: str, strings: List[str]) -> filter: - """ - Filter strings according to pattern. - - Parameters - ---------- - pattern: str - Regex pattern - strings: List[str] - List of strings to filter. - - Returns - ------- - filter[str] - """ - return filter(re.compile(pattern).match, strings) - - -def get_rundirs(pattern: str, path: Union[str, Path]) -> Sequence[str]: - """ - Get SMAC run dirs, often starting with `run_`. - - Parameters - ---------- - pattern: str - Regex expresssion. - path: Union[str, Path] - Path to folder containing single SMAC rundirs - - Returns - ------- - Sequence[str] - Single SMAC rundirs - - """ - subdirs = list(glob_re(pattern, os.listdir(path))) - rundirs = [os.path.join(path, sd) for sd in subdirs] - return rundirs - - -class ResultMerger: - def __init__( - self, - output_dir: Optional[Union[str, Path]] = None, - rundir_pattern: str = r"run_*\d$", - rundirs: Optional[List[Union[str, Path]]] = None, - ): - """ - Merge runhistories from different SMAC runs. - - Parameters - ---------- - output_dir : Optional[Union[str, Path]] - Output directory containing single SMAC run folders. The rundirs are inside - and collected via the pattern `rundir_pattern`. - rundir_pattern : str - Regex expression to find single rundirs in `output_dir`. - rundirs : Optional[List[Union[str, Path]]] - Paths to all SMAC output folders. - If not specified, please specify `output_dir`. - """ - self.output_dir = output_dir - self.run_dirs: Sequence[Union[str, Path]] - if rundirs: - self.run_dirs = rundirs - else: - if self.output_dir is None: - raise ValueError("Please provide either `rundirs` or `output_dir` with" " an optional pattern.") - self.run_dirs = get_rundirs(pattern=rundir_pattern, path=self.output_dir) - - cs_fn = Path(self.run_dirs[0]) / "configspace.json" - with open(cs_fn, "r") as fh: - json_string = fh.read() - self.configuration_space = csjson.read(json_string) - - def get_runhistory(self) -> RunHistory: - """ - Get runhistory - - For this, merge all runhistories in pSMAC subfolders. - - Returns - ------- - RunHistory - Empty, if `self.run_dirs` is None. - - """ - runhistory = RunHistory() - if self.run_dirs: - runhistory_filenames = [os.path.join(d, "runhistory.json") for d in self.run_dirs] - for fn in runhistory_filenames: - runhistory.update_from_json(fn=fn, cs=self.configuration_space) - return runhistory - - def get_trajectory(self) -> Optional[List[Dict[str, Any]]]: - """ - Get trajectory - - For this, extract trajectory from merged runhistories. - Return trajectories in json format. - - Returns - ------- - Optional[List[Dict[str, Any]] - - None, if `self.run_dirs` is None. - - List of trajectory entries. Each trajectory entry is a dict with keys - ['cpu_time', 'wallclock_time', 'evaluations', 'cost', 'incumbent', 'budget', 'origin']. - - """ - trajectory = None - if self.run_dirs is None: - return trajectory - rh = self.get_runhistory() - - # Sort configurations chronologically by starttime - rvals = rh.values() - starttimes = np.array([rv.starttime for rv in rvals]) - ids = np.argsort(starttimes) - rhitems = list(rh.items()) - rhitems = [rhitems[i] for i in ids] - - # Find incumbents - # Incumbent = cost is lower than alltime cost - trajectory = [] - - # Inject first trajectory entry from file from first rundir - rundir = self.run_dirs[0] - traj_fn = Path(rundir) / "traj.json" - with open(traj_fn, "r") as file: - line = file.readline() - traj_entry = json.loads(line) - trajectory.append(traj_entry) - - # Populate from merged runhistory - cost = np.inf - for i, (rk, rv) in enumerate(rhitems): - if rv.cost < cost: - cost = rv.cost - # traj_entry = TrajEntry( - # rv.cost, # train_perf - # rk.config_id, # incumbent_id - # rh.ids_config[rk.config_id], # incumbent - # i + 1, # ta_runs - # rv.time, # ta_time_used - # rv.starttime, # wallclock_time - # rk.budget, # budget - # ) # TODO return traj_entry as TrajEntry and convert to json for write_trajectory - incumbent = rh.ids_config[rk.config_id] - traj_entry = { - "cpu_time": rv.time, - "wallclock_time": rv.starttime, - "evaluations": i + 1, - "cost": rv.cost, - "incumbent": incumbent.get_dictionary(), - "budget": rk.budget, - "origin": incumbent.origin, - } - trajectory.append(traj_entry) - - return trajectory - - def write_trajectory(self) -> None: - """ - Write trajectory to traj.json - - Returns - ------- - None - - """ - if self.output_dir is not None: - traj_fn = Path(self.output_dir) / "traj.json" - traj = self.get_trajectory() - - traj_fn.open("w") - if traj is not None: - for traj_entry in traj: - with open(traj_fn, "a") as fp: # TODO: write or append? - json.dump(traj_entry, fp) - fp.write("\n") - - def write_runhistory(self) -> None: - """ - Write runhistory to runhistory.json - - Returns - ------- - None - - """ - if self.output_dir is not None: - rh_fn = Path(self.output_dir) / "runhistory.json" - rh = self.get_runhistory() - rh.save_json(fn=str(rh_fn), save_external=True) diff --git a/smac/utils/io/traj_logging.py b/smac/utils/io/traj_logging.py deleted file mode 100644 index d41d94613..000000000 --- a/smac/utils/io/traj_logging.py +++ /dev/null @@ -1,384 +0,0 @@ -from typing import Dict, List, Optional, Union - -import collections -import json -import logging -import os - -import numpy as np -from ConfigSpace.configuration_space import Configuration, ConfigurationSpace -from ConfigSpace.hyperparameters import ( - CategoricalHyperparameter, - Constant, - FloatHyperparameter, - IntegerHyperparameter, -) - -from smac.stats.stats import Stats -from smac.utils.logging import format_array - -__author__ = "Marius Lindauer" -__copyright__ = "Copyright 2016, ML4AAD" -__license__ = "3-clause BSD" - -TrajEntry = collections.namedtuple( - "TrajEntry", - [ - "train_perf", - "incumbent_id", - "incumbent", - "ta_runs", - "ta_time_used", - "wallclock_time", - "budget", - ], -) - - -class TrajLogger(object): - """ - Writes trajectory logs files and creates output directory if not exists already - - Parameters - ---------- - output_dir: str - directory for logging (or None to disable logging) - stats: Stats() - Stats object - - Attributes - ---------- - stats - logger - output_dir - aclib_traj_fn - old_traj_fn - trajectory - """ - - def __init__(self, output_dir: Optional[str], stats: Stats) -> None: - self.stats = stats - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - self.output_dir = output_dir - if output_dir is None or output_dir == "": - self.output_dir = None - self.logger.info( - "No output directory for trajectory logging " "specified -- trajectory will not be logged." - ) - - else: - if not os.path.isdir(output_dir): - try: - os.makedirs(output_dir) - except OSError: - e = OSError("Could not make output directory: {}.".format(output_dir)) - self.logger.error("Could not make output directory.", exc_info=e) - raise e - - self.old_traj_fn = os.path.join(output_dir, "traj_old.csv") - if not os.path.isfile(self.old_traj_fn): - with open(self.old_traj_fn, "w") as fp: - fp.write( - '"CPU Time Used","Estimated Training Performance",' - '"Wallclock Time","Incumbent ID",' - '"Automatic Configurator (CPU) Time",' - '"Configuration..."\n' - ) - - self.aclib_traj_fn = os.path.join(output_dir, "traj_aclib2.json") - self.alljson_traj_fn = os.path.join(output_dir, "traj.json") - - self.trajectory = [] # type: List[TrajEntry] - - def add_entry( - self, - train_perf: Union[float, np.ndarray], - incumbent_id: int, - incumbent: Configuration, - budget: float = 0, - ) -> None: - """Adds entries to trajectory files (several formats) with using the - same timestamps for each entry - - Parameters - ---------- - train_perf: float or np.ndarray - estimated performance on training (sub)set - incumbent_id: int - id of incumbent - incumbent: Configuration() - current incumbent configuration - budget: float - budget used in intensifier to limit TA (default: 0) - """ - perf = format_array(train_perf) - - finished_ta_runs = self.stats.finished_ta_runs - ta_time_used = self.stats.ta_time_used - wallclock_time = self.stats.get_used_wallclock_time() - self.trajectory.append( - TrajEntry( - perf, - incumbent_id, - incumbent, - finished_ta_runs, - ta_time_used, - wallclock_time, - budget, - ) - ) - if self.output_dir is not None: - self._add_in_old_format(perf, incumbent_id, incumbent, ta_time_used, wallclock_time) - self._add_in_aclib_format(perf, incumbent_id, incumbent, ta_time_used, wallclock_time) - self._add_in_alljson_format(perf, incumbent_id, incumbent, budget, ta_time_used, wallclock_time) - - def _add_in_old_format( - self, - train_perf: Union[float, List[float]], - incumbent_id: int, - incumbent: Configuration, - ta_time_used: float, - wallclock_time: float, - ) -> None: - """Adds entries to old SMAC2-like trajectory file - - Parameters - ---------- - train_perf: float or list of floats - Estimated performance on training (sub)set - incumbent_id: int - Id of incumbent - incumbent: Configuration() - Current incumbent configuration - ta_time_used: float - CPU time used by the target algorithm - wallclock_time: float - Wallclock time used so far - """ - conf = [] - for p in incumbent: - if not incumbent.get(p) is None: - conf.append("%s='%s'" % (p, repr(incumbent[p]))) - if isinstance(train_perf, float): - # Make it compatible with old format - with open(self.old_traj_fn, "a") as fp: - fp.write( - f"{ta_time_used:f}, {train_perf:f}, {wallclock_time:f}, {incumbent_id:d}, " - f"{wallclock_time - ta_time_used:f}, {','.join(conf):s}\n" - ) - else: - # We recommend to use pandas to read this csv file - with open(self.old_traj_fn, "a") as fp: - fp.write( - f"{ta_time_used:f}, {train_perf}, {wallclock_time:f}, {incumbent_id:d}, " - f"{wallclock_time - ta_time_used:f}, {','.join(conf):s}\n" - ) - - def _add_in_aclib_format( - self, - train_perf: Union[float, List[float]], - incumbent_id: int, - incumbent: Configuration, - ta_time_used: float, - wallclock_time: float, - ) -> None: - """Adds entries to AClib2-like trajectory file - - Parameters - ---------- - train_perf: float or list of floats - Estimated performance on training (sub)set - incumbent_id: int - Id of incumbent - incumbent: Configuration() - Current incumbent configuration - ta_time_used: float - CPU time used by the target algorithm - wallclock_time: float - Wallclock time used so far - """ - conf = [] - for p in incumbent: - if not incumbent.get(p) is None: - conf.append("%s='%s'" % (p, repr(incumbent[p]))) - - traj_entry = { - "cpu_time": ta_time_used, - "wallclock_time": wallclock_time, - "evaluations": self.stats.finished_ta_runs, - "cost": format_array(train_perf, False), - "incumbent": conf, - "origin": incumbent.origin, - } - - with open(self.aclib_traj_fn, "a") as fp: - json.dump(traj_entry, fp) - fp.write("\n") - - def _add_in_alljson_format( - self, - train_perf: Union[float, List[float]], - incumbent_id: int, - incumbent: Configuration, - budget: float, - ta_time_used: float, - wallclock_time: float, - ) -> None: - """Adds entries to AClib2-like (but with configs as json) trajectory file - - Parameters - ---------- - train_perf: float or list of floats - Estimated performance on training (sub)set - incumbent_id: int - Id of incumbent - incumbent: Configuration() - Current incumbent configuration - budget: float - budget (cutoff) used in intensifier to limit TA (default: 0) - ta_time_used: float - CPU time used by the target algorithm - wallclock_time: float - Wallclock time used so far - """ - traj_entry = { - "cpu_time": ta_time_used, - "wallclock_time": wallclock_time, - "evaluations": self.stats.finished_ta_runs, - "cost": train_perf, - "incumbent": incumbent.get_dictionary(), - "budget": budget, - "origin": incumbent.origin, - } - - with open(self.alljson_traj_fn, "a") as fp: - json.dump(traj_entry, fp) - fp.write("\n") - - @staticmethod - def read_traj_alljson_format( - fn: str, - cs: ConfigurationSpace, - ) -> List[Dict[str, Union[float, int, Configuration]]]: - """Reads trajectory from file - - Parameters - ---------- - fn: str - Filename with saved runhistory in self._add_in_alljson_format format - cs: ConfigurationSpace - Configuration Space to translate dict object into Confiuration object - - Returns - ------- - trajectory: list - Each entry in the list is a dictionary of the form - { - "cpu_time": float, - "wallclock_time": float, - "evaluations": int - "cost": float or list of floats, - "budget": budget, - "incumbent": Configuration - } - """ - trajectory = [] - with open(fn) as fp: - for line in fp: - entry = json.loads(line) - entry["incumbent"] = Configuration(cs, entry["incumbent"]) - trajectory.append(entry) - - return trajectory - - @staticmethod - def read_traj_aclib_format( - fn: str, - cs: ConfigurationSpace, - ) -> List[Dict[str, Union[float, int, Configuration]]]: - """Reads trajectory from file - - Parameters - ---------- - fn: str - Filename with saved runhistory in self._add_in_aclib_format format - cs: ConfigurationSpace - Configuration Space to translate dict object into Confiuration object - - Returns - ------- - trajectory: list - Each entry in the list is a dictionary of the form - { - "cpu_time": float, - "wallclock_time": float, - "evaluations": int - "cost": float or list of floats, - "incumbent": Configuration - } - """ - trajectory = [] - with open(fn) as fp: - for line in fp: - entry = json.loads(line) - entry["incumbent"] = TrajLogger._convert_dict_to_config(entry["incumbent"], cs=cs) - trajectory.append(entry) - - return trajectory - - @staticmethod - def _convert_dict_to_config(config_list: List[str], cs: ConfigurationSpace) -> Configuration: - """Since we save a configurations in a dictionary str->str we have to - try to figure out the type (int, float, str) of each parameter value - - Parameters - ---------- - config_list: List[str] - Configuration as a list of "str='str'" - cs: ConfigurationSpace - Configuration Space to translate dict object into Confiuration object - """ - config_dict = {} - v = "" # type: Union[str, float, int, bool] - for param in config_list: - k, v = param.split("=") - v = v.strip("'") - hp = cs.get_hyperparameter(k) - if isinstance(hp, FloatHyperparameter): - v = float(v) - elif isinstance(hp, IntegerHyperparameter): - v = int(v) - elif isinstance(hp, (CategoricalHyperparameter, Constant)): - # Checking for the correct type requires jumping some hoops - # First, we gather possible interpretations of our string - interpretations = [v] # type: List[Union[str, bool, int, float]] - if v in ["True", "False"]: - # Special Case for booleans (assuming we support them) - # This is important to avoid false positive warnings triggered by 1 == True or "False" == True - interpretations.append(True if v == "True" else False) - else: - for t in [int, float]: - try: - interpretations.append(t(v)) - except ValueError: - continue - - # Second, check if it's in the choices / the correct type. - legal = {interpretation for interpretation in interpretations if hp.is_legal(interpretation)} - - # Third, issue warnings if the interpretation is ambigious - if len(legal) != 1: - logging.getLogger("smac.trajlogger").warning( - "Ambigous or no interpretation of value {} for hp {} found ({} possible interpretations). " - "Passing string, but this will likely result in an error".format(v, hp.name, len(legal)) - ) - else: - v = legal.pop() - - config_dict[k] = v - - config = Configuration(configuration_space=cs, values=config_dict) - config.origin = "External Trajectory" - - return config diff --git a/smac/utils/logging.py b/smac/utils/logging.py index 8180cf8f1..3509e3eda 100644 --- a/smac/utils/logging.py +++ b/smac/utils/logging.py @@ -1,77 +1,52 @@ -from typing import Any, Dict, Iterable, List, Union +from __future__ import annotations + +from typing import Iterable import logging +import logging.config +from pathlib import Path import numpy as np +import yaml -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class PickableLoggerAdapter(object): - def __init__(self, name: str) -> None: - self.name = name - self.logger = logging.getLogger(self.name) - - def __getstate__(self) -> Dict[str, str]: - """ - Method is called when pickle dumps an object. - - Returns - ------- - Dictionary, representing the object state to be pickled. Ignores - the self.logger field and only returns the logger name. - """ - return {"name": self.name} +import smac - def __setstate__(self, state: Dict[str, Any]) -> None: - """ - Method is called when pickle loads an object. Retrieves the name and - creates a logger. - - Parameters - ---------- - state - dictionary, containing the logger name. - """ - self.name = state["name"] - self.logger = logging.getLogger(self.name) +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" - def debug(self, msg, *args, **kwargs): # type: ignore[no-untyped-def] # noqa F821 - """Debug method.""" - self.logger.debug(msg, *args, **kwargs) - def info(self, msg, *args, **kwargs): # type: ignore[no-untyped-def] # noqa F821 - """Info method.""" - self.logger.info(msg, *args, **kwargs) +def setup_logging(level: int | Path | None = None) -> None: + """Sets up the logging configuration for all modules. - def warning(self, msg, *args, **kwargs): # type: ignore[no-untyped-def] # noqa F821 - """Warning method.""" - self.logger.warning(msg, *args, **kwargs) + Parameters + ---------- + level : int | Path | None, defaults to None + An integer representing the logging level. An own logging configuration can be used when passing a path. + """ + if isinstance(level, Path): + log_filename = level + else: + path = Path() / smac.__file__ + log_filename = path.parent / "logging.yml" - def error(self, msg, *args, **kwargs): # type: ignore[no-untyped-def] # noqa F821 - """Error method.""" - self.logger.error(msg, *args, **kwargs) + with (log_filename).open("r") as stream: + config = yaml.safe_load(stream) - def exception(self, msg, *args, **kwargs): # type: ignore[no-untyped-def] # noqa F821 - """Exception method.""" - self.logger.exception(msg, *args, **kwargs) + if isinstance(level, int): + config["root"]["level"] = level + config["handlers"]["console"]["level"] = level - def critical(self, msg, *args, **kwargs): # type: ignore[no-untyped-def] # noqa F821 - """Critical method.""" - self.logger.critical(msg, *args, **kwargs) + logging.config.dictConfig(config) - def log(self, level, msg, *args, **kwargs): # type: ignore[no-untyped-def] # noqa F821 - """Log method.""" - self.logger.log(level, msg, *args, **kwargs) - def isEnabledFor(self, level): # type: ignore[no-untyped-def] # noqa F821 - """Check if logger is enabled for a given level.""" - return self.logger.isEnabledFor(level) +def get_logger(logger_name: str) -> logging.Logger: + """Get the logger by name""" + logger = logging.getLogger(logger_name) + return logger -def format_array( - inputs: Union[str, int, float, np.ndarray, list], format_vals: bool = True -) -> Union[float, List[float]]: +# TODO: Move me +def format_array(inputs: str | int | float | np.ndarray | list, format_vals: bool = True) -> float | list[float]: """Transform a numpy array to a list of format so that it can be printed by logger. If the list holds one element only, then a formatted string is returned. diff --git a/smac/utils/merge_foreign_data.py b/smac/utils/merge_foreign_data.py deleted file mode 100644 index 22662f10b..000000000 --- a/smac/utils/merge_foreign_data.py +++ /dev/null @@ -1,107 +0,0 @@ -from typing import List, Tuple - -from smac.configspace import ConfigurationSpace -from smac.runhistory.runhistory import DataOrigin, RunHistory -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def merge_foreign_data_from_file( - scenario: Scenario, - runhistory: RunHistory, - in_scenario_fn_list: List[str], - in_runhistory_fn_list: List[str], - cs: ConfigurationSpace, -) -> Tuple[Scenario, RunHistory]: - """Extend and with runhistory data from another. - - assuming the same pcs, feature space, but different instances - - Parameters - ---------- - scenario: Scenario - original scenario -- feature dictionary will be extended - runhistory: RunHistory - original runhistory -- will be extended by further data points - in_scenario_fn_list: List[str] - input scenario file names - in_runhistory_fn_list: List[str] - list filenames of runhistory dumps - cs: ConfigurationSpace - parameter configuration space to read runhistory from file - - Returns - ------- - scenario: Scenario - runhistory: Runhistory - """ - if not in_scenario_fn_list: - raise ValueError( - "To read warmstart data from previous runhistories," - " the corresponding scenarios are required. Use option --warmstart_scenario" - ) - scens = [Scenario(scenario=scen_fn, cmd_options={"output_dir": ""}) for scen_fn in in_scenario_fn_list] - rhs = [] - for rh_fn in in_runhistory_fn_list: - rh = RunHistory() - rh.load_json(rh_fn, cs) - rhs.append(rh) - - return merge_foreign_data(scenario, runhistory, in_scenario_list=scens, in_runhistory_list=rhs) - - -def merge_foreign_data( - scenario: Scenario, - runhistory: RunHistory, - in_scenario_list: List[Scenario], - in_runhistory_list: List[RunHistory], -) -> Tuple[Scenario, RunHistory]: - """Extend and with runhistory data from another. - - assuming the same pcs, feature space, but different instances - - Parameters - ---------- - scenario: Scenario - original scenario -- feature dictionary will be extended - runhistory: RunHistory - original runhistory -- will be extended by further data points - in_scenario_list: List[Scenario] - input scenario - in_runhistory_list: List[RunHistory] - list of runhistories wrt - - Returns - ------- - scenario: Scenario - runhistory: Runhistory - """ - # add further instance features - for in_scenario in in_scenario_list: - if scenario.n_features != in_scenario.n_features: - raise ValueError( - "Feature Space has to be the same for both scenarios (%d vs %d)." - % (scenario.n_features, in_scenario.n_features) - ) - - if scenario.cs != in_scenario.cs: # type: ignore[attr-defined] # noqa F821 - raise ValueError("PCS of both scenarios have to be identical.") - - if scenario.cutoff != in_scenario.cutoff: # type: ignore[attr-defined] # noqa F821 - raise ValueError("Cutoffs of both scenarios have to be identical.") - - scenario.feature_dict.update(in_scenario.feature_dict) - - # extend runhistory - for rh in in_runhistory_list: - runhistory.update(rh, origin=DataOrigin.EXTERNAL_DIFFERENT_INSTANCES) - - for date in runhistory.data: - if scenario.feature_dict.get(date.instance_id) is None: - raise ValueError('Instance feature for "%s" was not found in scenario data.' % (date.instance_id)) - - runhistory.compute_all_costs(instances=scenario.train_insts) - - return scenario, runhistory diff --git a/smac/multi_objective/utils.py b/smac/utils/multi_objective.py similarity index 90% rename from smac/multi_objective/utils.py rename to smac/utils/multi_objective.py index a88eb397f..edbc88774 100644 --- a/smac/multi_objective/utils.py +++ b/smac/utils/multi_objective.py @@ -1,7 +1,10 @@ from __future__ import annotations -def normalize_costs(values: list[float], bounds: list[tuple[float, float]] | None = None) -> list[float]: +def normalize_costs( + values: list[float], + bounds: list[tuple[float, float]] | None = None, +) -> list[float]: """ Normalizes a list of floats with corresponding bounds. diff --git a/smac/utils/subspaces/__init__.py b/smac/utils/subspaces/__init__.py new file mode 100644 index 000000000..686099830 --- /dev/null +++ b/smac/utils/subspaces/__init__.py @@ -0,0 +1,666 @@ +# from __future__ import annotations + +# from abc import ABC, abstractmethod +# from typing import Any, Dict, Iterator, List, Tuple, Type + +# import copy +# import inspect +# import logging +# import math + +# import numpy as np +# from ConfigSpace.forbidden import ( +# AbstractForbiddenComponent, +# ForbiddenAndConjunction, +# MultipleValueForbiddenClause, +# ) +# from ConfigSpace.hyperparameters import ( +# CategoricalHyperparameter, +# Constant, +# Hyperparameter, +# NumericalHyperparameter, +# OrdinalHyperparameter, +# UniformFloatHyperparameter, +# UniformIntegerHyperparameter, +# ) + +# from smac.acquisition.function import EI, AbstractAcquisitionFunction +# from ConfigSpace import Configuration, ConfigurationSpace +# from smac.model.abstract_model import AbstractModel +# from smac.model.gaussian_process.gpytorch_gaussian_process import GloballyAugmentedLocalGaussianProcess +# from smac.model.gaussian_process.kernels._boing import construct_gp_kernel +# from smac.model.utils import check_subspace_points +# from smac.utils.logging import get_logger + +# logger = get_logger(__name__) + + +# class LocalSubspace(ABC): +# """ +# A subspace that is designed for local Bayesian Optimization. If bounds_ss_cont and bounds_ss_cat are not given, +# this subspace is equivalent to the original configuration space. Additionally, this subspace +# supports local BO that only works with a subset of the dimensions, where the missing values are filled by the +# corresponding values from incumbent_array. + +# Parameters +# ---------- +# config_space: ConfigurationSpace +# raw Configuration space +# bounds: List[Tuple[float, float]] +# raw bounds of the Configuration space, notice that here bounds denotes the bounds of the entire space +# hps_types: List[int], +# types of the hyperparameters +# bounds_ss_cont: np.ndarray(D_cont, 2) +# subspaces bounds of continuous hyperparameters, its length is the number of continuous hyperparameters +# bounds_ss_cat: List[Tuple] +# subspaces bounds of categorical hyperparameters, its length is the number of categorical hyperparameters +# rng: np.random.RandomState +# random state +# model_local: ~smac.epm.base_epm.BaseEPM +# model in subspace +# model_local_kwargs: Dict | None +# argument for subspace model +# acq_func_local: ~smac.optimizer.ei_optimization.AbstractAcquisitionFunction +# local acquisition function +# acq_func_local_kwargs: Dict | None +# argument for acquisition function +# activate_dims: np.ndarray | None +# activate dimensions in the subspace, if it is None, we preserve all the dimensions +# incumbent_array: np.ndarray | None +# incumbent array, used when activate_dims has less dimension and this value is used to complementary the +# resulted configurations +# """ + +# def __init__( +# self, +# config_space: ConfigurationSpace, +# bounds: List[Tuple[float, float]], +# hps_types: List[int], +# bounds_ss_cont: np.ndarray | None = None, +# bounds_ss_cat: List[Tuple] | None = None, +# model_local: AbstractModel | Type[AbstractModel] = GloballyAugmentedLocalGaussianProcess, +# model_local_kwargs: Dict = {}, +# acq_func_local: AbstractAcquisitionFunction | Type[AbstractAcquisitionFunction] = EI, +# acq_func_local_kwargs: Dict | None = None, +# rng: np.random.RandomState | None = None, +# initial_data: Tuple[np.ndarray, np.ndarray] | None = None, +# activate_dims: np.ndarray | None = None, +# incumbent_array: np.ndarray | None = None, +# ): +# self.cs_global = config_space +# if rng is None: +# self.rng = np.random.RandomState(1) +# else: +# self.rng = np.random.RandomState(rng.randint(0, 2**20)) + +# n_hypers = len(config_space.get_hyperparameters()) +# model_types = copy.deepcopy(hps_types) +# model_bounds = copy.deepcopy(bounds) + +# cat_dims = np.where(np.array(hps_types) != 0)[0] +# cont_dims = np.where(np.array(hps_types) == 0)[0] + +# if activate_dims is None: +# activate_dims = np.arange(n_hypers) +# activate_dims_cont = cont_dims +# activate_dims_cat = cat_dims +# self.activate_dims = activate_dims +# activate_dims_cont_ss = np.arange(len(activate_dims_cont)) +# activate_dims_cat_ss = np.arange(len(activate_dims_cat)) +# else: +# activate_dims_cont, _, activate_dims_cont_ss = np.intersect1d( +# activate_dims, cont_dims, assume_unique=True, return_indices=True +# ) +# activate_dims_cat, _, activate_dims_cat_ss = np.intersect1d( +# activate_dims, cat_dims, assume_unique=True, return_indices=True +# ) +# self.activate_dims = activate_dims + +# self.activate_dims_cont = activate_dims_cont_ss +# self.activate_dims_cat = activate_dims_cat_ss + +# lbs = np.full(n_hypers, 0.0) +# scales = np.full(n_hypers, 1.0) + +# if bounds_ss_cont is None and bounds_ss_cat is None: +# # cs_inner is cs +# self.cs_local = config_space +# self.new_config_space = False +# self.bounds_ss_cont = np.tile([0.0, 1.0], [len(self.activate_dims_cont), 1]) +# self.bounds_ss_cat = [] # type: Optional[List[Tuple]] +# self.lbs = lbs +# self.scales = scales +# self.new_config = False + +# else: +# self.new_config = True +# # we normalize the non-CategoricalHyperparameter by x = (x-lb)*scale + +# hps = config_space.get_hyperparameters() + +# # deal with categorical hyperaprameters +# for i, cat_idx in enumerate(activate_dims_cat): +# hp_cat = hps[cat_idx] # type: CategoricalHyperparameter +# parents = config_space.get_parents_of(hp_cat.name) +# if len(parents) == 0: +# can_be_inactive = False +# else: +# can_be_inactive = True +# if bounds_ss_cat is None: +# n_cats = len(hp_cat.choices) +# else: +# n_cats = len(bounds_ss_cat[i]) +# if can_be_inactive: +# n_cats = n_cats + 1 +# model_types[cat_idx] = n_cats +# model_bounds[cat_idx] = (int(n_cats), np.nan) + +# # store the dimensions of numerical hyperparameters, UniformFloatHyperparameter and +# # UniformIntegerHyperparameter +# dims_cont_num = [] +# idx_cont_num = [] +# dims_cont_ord = [] +# idx_cont_ord = [] +# ord_hps = {} + +# # deal with ordinary hyperaprameters +# for i, cont_idx in enumerate(activate_dims_cont): +# param = hps[cont_idx] +# if isinstance(param, OrdinalHyperparameter): +# parents = config_space.get_parents_of(param.name) +# if len(parents) == 0: +# can_be_inactive = False +# else: +# can_be_inactive = True +# if bounds_ss_cont is None: +# n_seqs = len(param.sequence) +# else: +# n_seqs = bounds_ss_cont[i][1] - bounds_ss_cont[i][0] + 1 +# if can_be_inactive: +# model_bounds[cont_idx] = (0, int(n_seqs)) +# else: +# model_bounds[cont_idx] = (0, int(n_seqs) - 1) +# if bounds_ss_cont is None: +# lbs[cont_idx] = 0 # in subspace, it should start from 0 +# ord_hps[param.name] = (0, int(n_seqs)) +# else: +# lbs[cont_idx] = bounds_ss_cont[i][0] # in subspace, it should start from 0 +# ord_hps[param.name] = bounds_ss_cont[i] +# dims_cont_ord.append(cont_idx) +# idx_cont_ord.append(i) +# else: +# dims_cont_num.append(cont_idx) +# idx_cont_num.append(i) + +# if bounds_ss_cat is not None: +# self.bounds_ss_cat = [bounds_ss_cat[act_dims_cat_ss] for act_dims_cat_ss in activate_dims_cat_ss] +# else: +# self.bounds_ss_cat = None +# self.bounds_ss_cont = bounds_ss_cont[activate_dims_cont_ss] if bounds_ss_cont is not None else None + +# if bounds_ss_cont is None: +# lbs[dims_cont_num] = 0.0 +# scales[dims_cont_num] = 1.0 +# else: +# lbs[dims_cont_num] = bounds_ss_cont[idx_cont_num, 0] +# # rescale numerical hyperparameters to [0., 1.] +# scales[dims_cont_num] = 1.0 / (bounds_ss_cont[idx_cont_num, 1] - bounds_ss_cont[idx_cont_num, 0]) + +# self.lbs = lbs[activate_dims] +# self.scales = scales[activate_dims] + +# self.cs_local = ConfigurationSpace() +# hp_list = [] +# idx_cont = 0 +# idx_cat = 0 + +# hps = config_space.get_hyperparameters() + +# for idx in self.activate_dims: +# param = hps[idx] +# if isinstance(param, CategoricalHyperparameter): +# if bounds_ss_cat is None: +# hp_new = copy.deepcopy(param) +# idx_cat += 1 +# else: +# choices = [param.choices[int(choice_idx)] for choice_idx in bounds_ss_cat[idx_cat]] +# # cat_freq_arr = np.array((cats_freq[idx_cat])) +# # weights = cat_freq_arr / np.sum(cat_freq_arr) +# hp_new = CategoricalHyperparameter(param.name, choices=choices) # , weights=weights) +# idx_cat += 1 + +# elif isinstance(param, OrdinalHyperparameter): +# param_seq = ord_hps.get(param.name) +# raw_seq = param.sequence +# ord_indices = np.arange(*param_seq) +# new_seq = [raw_seq[int(round(idx))] for idx in ord_indices] +# hp_new = OrdinalHyperparameter(param.name, sequence=new_seq) +# idx_cont += 1 + +# elif isinstance(param, Constant): +# hp_new = copy.deepcopy(param) +# elif isinstance(param, (UniformFloatHyperparameter, UniformIntegerHyperparameter)): +# if bounds_ss_cont is None: +# hp_new = copy.deepcopy(param) +# idx_cont += 1 +# else: +# if isinstance(param, UniformFloatHyperparameter): +# lower = param.lower +# upper = param.upper +# if param.log: +# lower_log = np.log(lower) +# upper_log = np.log(upper) +# hp_new_lower = np.exp((upper_log - lower_log) * bounds_ss_cont[idx_cont][0] + +# lower_log) +# hp_new_upper = np.exp((upper_log - lower_log) * bounds_ss_cont[idx_cont][1] + +# lower_log) +# hp_new = UniformFloatHyperparameter( +# name=param.name, +# lower=max(hp_new_lower, lower), +# upper=min(hp_new_upper, upper), +# log=True, +# ) +# else: +# hp_new_lower = (upper - lower) * bounds_ss_cont[idx_cont][0] + lower +# hp_new_upper = (upper - lower) * bounds_ss_cont[idx_cont][1] + lower +# hp_new = UniformFloatHyperparameter( +# name=param.name, +# lower=max(hp_new_lower, lower), +# upper=min(hp_new_upper, upper), +# log=False, +# ) +# idx_cont += 1 +# elif isinstance(param, UniformIntegerHyperparameter): +# lower = param.lower +# upper = param.upper +# if param.log: +# lower_log = np.log(lower) +# upper_log = np.log(upper) +# hp_new_lower = int( +# math.floor( +# np.exp((upper_log - lower_log) * bounds_ss_cont[idx_cont][0] + lower_log) +# ) +# ) +# hp_new_upper = int( +# math.ceil(np.exp((upper_log - lower_log) * bounds_ss_cont[idx_cont][1] + +# lower_log)) +# ) + +# hp_new_lower_log = np.log(hp_new_lower) +# hp_new_upper_log = np.log(hp_new_upper) +# new_scale = (upper_log - lower_log) / (hp_new_upper_log - hp_new_lower_log) +# new_lb = (hp_new_lower_log - lower_log) / (hp_new_upper_log - hp_new_lower_log) + +# self.scales[idx] = new_scale +# self.lbs[idx] = new_lb + +# hp_new = UniformIntegerHyperparameter( +# name=param.name, +# lower=max(hp_new_lower, lower), +# upper=min(hp_new_upper, upper), +# log=True, +# ) +# else: +# hp_new_lower = int(math.floor((upper - lower) * bounds_ss_cont[idx_cont][0])) + lower +# hp_new_upper = int(math.ceil((upper - lower) * bounds_ss_cont[idx_cont][1])) + lower + +# new_scale = (upper - lower) / (hp_new_upper - hp_new_lower) +# new_lb = (hp_new_lower - lower) / (hp_new_upper - hp_new_lower) +# self.scales[idx] = new_scale +# self.lbs[idx] = new_lb + +# hp_new = UniformIntegerHyperparameter( +# name=param.name, +# lower=max(hp_new_lower, lower), +# upper=min(hp_new_upper, upper), +# log=False, +# ) + +# idx_cont += 1 +# else: +# raise ValueError(f"Unsupported type of Hyperparameter: {type(param)}") +# hp_list.append(hp_new) + +# # We only consider plain hyperparameters +# self.cs_local.add_hyperparameters(hp_list) +# forbiddens_ss = [] +# forbiddens = config_space.get_forbiddens() +# for forbidden in forbiddens: +# forbiden_ss = self.fit_forbidden_to_ss(cs_local=self.cs_local, forbidden=forbidden) +# if forbiden_ss is not None: +# forbiddens_ss.append(forbiden_ss) +# if len(forbiddens_ss) > 0: +# self.cs_local.add_forbidden_clauses(forbiddens_ss) + +# model_kwargs = dict( +# configspace=self.cs_local, +# # types=[model_types[activate_dim] for activate_dim in activate_dims] if model_types is not None else +# None, +# # bounds=[model_bounds[activate_dim] for activate_dim in activate_dims] if model_bounds is not None +# # else None, +# bounds_cont=np.array([[0, 1.0] for _ in range(len(activate_dims_cont))]), +# bounds_cat=self.bounds_ss_cat, +# seed=self.rng.randint(0, 2**20), +# ) + +# if inspect.isclass(model_local): +# model_local_kwargs_copy = copy.deepcopy(model_local_kwargs) +# if "kernel_kwargs" in model_local_kwargs_copy: +# kernel_kwargs = model_local_kwargs_copy["kernel_kwargs"] +# kernel = construct_gp_kernel(kernel_kwargs, activate_dims_cont_ss, activate_dims_cat_ss) +# del model_local_kwargs_copy["kernel_kwargs"] +# model_local_kwargs_copy["kernel"] = kernel + +# if model_local_kwargs is not None: +# model_kwargs.update(model_local_kwargs_copy) + +# all_arguments = inspect.signature(model_local).parameters.keys() +# if "bounds_cont" not in all_arguments: +# del model_kwargs["bounds_cont"] +# if "bounds_cat" not in all_arguments: +# del model_kwargs["bounds_cat"] +# model = model_local(**model_kwargs) # type: ignore +# else: +# model = model_local + +# self.model = model + +# if inspect.isclass(acq_func_local): +# acq_func_kwargs = {} +# if acq_func_local_kwargs is not None: +# acq_func_kwargs.update(acq_func_local_kwargs) +# acquisition_function = acq_func_local(**acq_func_kwargs) # type: ignore +# else: +# acquisition_function = acq_func_local + +# self.acquisition_function = acquisition_function + +# self.incumbent_array = incumbent_array + +# self.model_x = np.empty([0, len(activate_dims)]) +# self.ss_x = np.empty([0, len(activate_dims)]) +# self.model_y = np.empty([0, 1]) +# self.ss_y = np.empty([0, 1]) + +# if initial_data is not None: +# X = initial_data[0] +# y = initial_data[1] + +# self.add_new_observations(X, y) + +# self.config_origin = "subspace" + +# @staticmethod +# def fit_forbidden_to_ss( +# cs_local: ConfigurationSpace, forbidden: AbstractForbiddenComponent +# ) -> AbstractForbiddenComponent | None: +# """ +# Fit the forbidden to subspaces. If the target forbidden can be added to subspace, we return a new forbidden +# with exactly the same type of the input forbidden. Otherwise, None is returned. + +# Parameters +# ---------- +# cs_local: ConfigurationSpace +# local configuration space of the subspace +# forbidden: AbstractForbiddenComponent +# forbidden to check +# Returns +# ------- +# forbidden_ss: AbstractForbiddenComponent | None +# forbidden in subspaces + +# """ +# if isinstance(forbidden, ForbiddenAndConjunction): +# forbidden_ss_components = [] +# for forbid in forbidden.components: +# # If any of the AndConjunction is not supported by the subspace, we simply ignore them +# forbid_ss = LocalSubspace.fit_forbidden_to_ss(cs_local, forbid) +# if forbid_ss is None: +# return None +# forbidden_ss_components.append(forbid_ss) +# return type(forbidden)(*forbidden_ss_components) +# else: +# forbidden_hp_name = forbidden.hyperparameter.name +# if forbidden_hp_name not in cs_local: +# return None +# hp_ss = cs_local.get_hyperparameter(forbidden_hp_name) + +# def is_value_in_hp(value: Any, hp: Hyperparameter) -> bool: +# """Check if the value is in the range of the hp.""" +# if isinstance(hp, NumericalHyperparameter): +# return hp.lower <= value <= hp.upper +# elif isinstance(hp, OrdinalHyperparameter): +# return value in hp.sequence +# elif isinstance(hp, CategoricalHyperparameter): +# return value in hp.choices +# else: +# raise NotImplementedError("Unsupported type of hyperparameter!") + +# if isinstance(forbidden, MultipleValueForbiddenClause): +# forbidden_values = forbidden.values +# for forbidden_value in forbidden_values: +# if not is_value_in_hp(forbidden_value, hp_ss): +# return None +# return type(forbidden)(hp_ss, forbidden_values) +# else: +# forbidden_value = forbidden.value +# if is_value_in_hp(forbidden_value, hp_ss): +# return type(forbidden)(hp_ss, forbidden_value) +# return None + +# def update_model(self, predict_x_best: bool = True, update_incumbent_array: bool = False) -> None: +# """ +# Update the model and acquisition function parameters + +# Parameters +# ---------- +# predict_x_best: bool, +# if the incumbent is acquired by the predicted mean of a surrogate model +# update_incumbent_array: bool +# if the incumbent_array of this subspace is replaced with the newly updated incumbent +# """ +# acq_func_kwargs = {"model": self.model, "num_data": len(self.ss_x)} + +# if predict_x_best: +# try: +# mu, _ = self.model.predict(self.ss_x) +# except Exception as e: +# # Some times it could occur that LGPGA fails to predict the mean value of ss_x because of +# # numerical issues +# logger.warning(f"Fail to predict ss_x due to {e}") +# mu = self.ss_y +# idx_eta = np.argmin(mu) +# incumbent_array = self.ss_x[idx_eta] +# acq_func_kwargs.update({"incumbent_array": incumbent_array, "eta": mu[idx_eta]}) +# else: +# idx_eta = np.argmin(self.ss_y) +# incumbent_array = self.ss_x[idx_eta] +# acq_func_kwargs.update({"incumbent_array": incumbent_array, "eta": self.ss_y[idx_eta]}) +# if update_incumbent_array: +# if self.incumbent_array is None: +# self.incumbent_array = self.ss_x[idx_eta] +# else: +# self.incumbent_array[self.activate_dims] = self.ss_x[idx_eta] + +# self.acquisition_function.update(**acq_func_kwargs) + +# def add_new_observations(self, X: np.ndarray, y: np.ndarray) -> None: +# """ +# Add new observations to the subspace + +# Parameters +# ---------- +# X: np.ndarray(N,D), +# new feature vector of the observations, constructed by the global configuration space +# y: np.ndarray(N) +# new performances of the observations +# Return +# ---------- +# indices_in_ss:np.ndarray(N) +# indices of data that included in subspaces +# """ +# if len(X.shape) == 1: +# X = X[np.newaxis, :] +# if len(y.shape) == 1: +# y = y[:, np.newaxis] + +# X = X[:, self.activate_dims] + +# ss_indices = check_subspace_points( +# X=X, +# cont_dims=self.activate_dims_cont, +# cat_dims=self.activate_dims_cat, +# bounds_cont=self.bounds_ss_cont, +# bounds_cat=self.bounds_ss_cat, +# ) + +# X = self.normalize_input(X=X) + +# self.model_x = np.vstack([self.model_x, X]) +# self.model_y = np.vstack([self.model_y, y]) + +# self.ss_x = np.vstack([self.ss_x, X[ss_indices]]) +# self.ss_y = np.vstack([self.ss_y, y[ss_indices]]) + +# def update_incumbent_array(self, new_incumbent: np.ndarray) -> None: +# """ +# Update a new incumbent array. The array is generated from the global configuration + +# Parameters +# ---------- +# new_incumbent: np.ndarray(D) +# new incumbent, which correspondences to the global configuration +# """ +# self.incumbent_array = self.normalize_input(X=new_incumbent) + +# def generate_challengers(self, **optimizer_kwargs: Any) -> Iterator: +# """ +# Generate a list of challengers that will be transformed into the global configuration space + +# Parameters +# ---------- +# optimizer_kwargs: Any +# additional configurations passed to 'self._generate_challengers' + +# Returns +# ------- +# A list of challengers in the global configuration space + +# """ +# challengers = self._generate_challengers(**optimizer_kwargs) +# return ChallengerListLocal( +# cs_local=self.cs_local, +# cs_global=self.cs_global, +# challengers=challengers, +# config_origin=self.config_origin, +# incumbent_array=self.incumbent_array, +# ) + +# @abstractmethod +# def _generate_challengers(self, **optimizer_kwargs: Any) -> List[Tuple[float, Configuration]]: +# """Generate new challengers list for this subspace""" +# raise NotImplementedError + +# def normalize_input(self, X: np.ndarray) -> np.ndarray: +# """ +# Normalize X to fit the local configuration space + +# Parameters +# ---------- +# X: np.ndarray(N,D) +# input X, configurations arrays +# Returns +# ------- +# X_normalized: np.ndarray(N,D) +# normalized input X +# """ +# if not self.new_config: +# return X + +# if len(X.shape) == 1: +# X = X[np.newaxis, :] + +# # normalize X +# X_normalized = (X - self.lbs) * self.scales +# if self.bounds_ss_cat is not None: +# # normalize categorical function, for instance, if bounds_subspace[i] is a categorical bound contains +# # elements [1, 3, 5], then we map 1->0, 3->1, 5->2 +# for cat_idx, cat_bound in zip(self.activate_dims_cat, self.bounds_ss_cat): +# X_i = X_normalized[:, cat_idx] +# cond_list = [X_i == cat for cat in cat_bound] +# choice_list = np.arange(len(cat_bound)) +# X_i = np.select(cond_list, choice_list) +# X_normalized[:, cat_idx] = X_i + +# return X_normalized + + +# class ChallengerListLocal(Iterator): +# def __init__( +# self, +# cs_local: ConfigurationSpace, +# cs_global: ConfigurationSpace, +# challengers: List[Tuple[float, Configuration]], +# config_origin: str, +# incumbent_array: np.ndarray | None = None, +# ): +# """ +# A Challenger list to convert the configuration from the local configuration space to the global configuration +# space + +# Parameters +# ---------- +# cs_local: ConfigurationSpace +# local configuration space +# cs_global: ConfigurationSpace +# global configuration space +# challengers: List[Tuple[float, Configuration]], +# challenger lists +# config_origin: str +# configuration origin +# incumbent_array: np.ndarray | None = None, +# global incumbent array, used when cs_local and cs_global have different number of dimensions and we need +# to +# supplement the missing values. +# """ +# self.cs_local = cs_local +# self.challengers = challengers +# self.cs_global = cs_global +# self._index = 0 +# self.config_origin = config_origin +# # In case cs_in and cs_out have different dimensions +# self.expand_dims = len(cs_global.get_hyperparameters()) != len(cs_local.get_hyperparameters()) +# self.incumbent_array = incumbent_array + +# if self.expand_dims and self.incumbent_array is None: +# raise ValueError( +# "Incumbent array must be provided if the global configuration space has more " +# "hyperparameters then the local configuration space" +# ) + +# def __next__(self) -> Configuration: +# if self.challengers is not None and self._index == len(self.challengers): +# raise StopIteration +# challenger = self.challengers[self._index][1] +# self._index += 1 +# value = challenger.get_dictionary() +# if self.expand_dims: +# incumbent_array = Configuration( +# configuration_space=self.cs_global, vector=self.incumbent_array +# ).get_dictionary() +# # we replace the cooresponding value in incumbent array with the value suggested by our optimizer +# for k in value.keys(): +# incumbent_array[k] = value[k] +# config = Configuration(configuration_space=self.cs_global, values=incumbent_array) +# else: +# config = Configuration(configuration_space=self.cs_global, values=value) +# if self.config_origin is not None: +# config.origin = self.config_origin +# else: +# config.origin = challenger.origin +# return config + +# def __len__(self) -> int: +# if self.challengers is None: +# self.challengers = [] +# return len(self.challengers) - self._index diff --git a/smac/utils/subspaces/boing_subspace.py b/smac/utils/subspaces/boing_subspace.py new file mode 100644 index 000000000..3dc4798e3 --- /dev/null +++ b/smac/utils/subspaces/boing_subspace.py @@ -0,0 +1,178 @@ +# from __future__ import annotations + +# from typing import Dict, List, Tuple, Type + +# import inspect + +# import numpy as np +# from ConfigSpace import ConfigurationSpace + +# from smac.acquisition import AbstractAcquisitionMaximizer, LocalAndSortedRandomSearch +# from smac.acquisition.function import EI, AbstractAcquisitionFunction +# from ConfigSpace import Configuration +# from smac.model.abstract_model import AbstractModel +# from smac.model.gaussian_process.gpytorch_gaussian_process import GloballyAugmentedLocalGaussianProcess +# from smac.utils.logging import get_logger +# from smac.utils.subspaces import LocalSubspace + +# logger = get_logger(__name__) + + +# class BOinGSubspace(LocalSubspace): +# """ +# Subspace for BOinG optimizer. Each time we create a new epm model for the subspace and optimize to maximize the +# acquisition function inside this subregion. + +# Parameters +# ---------- +# acq_optimizer_local: Optional[AcquisitionFunctionMaximizer] +# Subspace optimizer, used to give a set of suggested points. Unlike the optimizer implemented in epm_chooser, +# this optimizer does not require runhistory objects. +# acq_optimizer_local_kwargs +# Parameters for acq_optimizer_local +# """ + +# def __init__( +# self, +# config_space: ConfigurationSpace, +# bounds: List[Tuple[float, float]], +# hps_types: List[int], +# bounds_ss_cont: np.ndarray | None = None, +# bounds_ss_cat: List[Tuple] | None = None, +# model_local: AbstractModel | Type[AbstractModel] = GloballyAugmentedLocalGaussianProcess, +# model_local_kwargs: Dict = {}, +# acq_func_local: AbstractAcquisitionFunction | Type[AbstractAcquisitionFunction] = EI, +# acq_func_local_kwargs: Dict | None = None, +# rng: np.random.RandomState | None = None, +# initial_data: Tuple[np.ndarray, np.ndarray] | None = None, +# activate_dims: np.ndarray | None = None, +# incumbent_array: np.ndarray | None = None, +# acq_optimizer_local: AbstractAcquisitionMaximizer | None = None, +# acq_optimizer_local_kwargs: Dict | None = None, +# ): +# super(BOinGSubspace, self).__init__( +# config_space=config_space, +# bounds=bounds, +# hps_types=hps_types, +# bounds_ss_cont=bounds_ss_cont, +# bounds_ss_cat=bounds_ss_cat, +# model_local=model_local, +# model_local_kwargs=model_local_kwargs, +# acq_func_local=acq_func_local, +# acq_func_local_kwargs=acq_func_local_kwargs, +# rng=rng, +# initial_data=initial_data, +# activate_dims=activate_dims, +# incumbent_array=incumbent_array, +# ) +# if bounds_ss_cont is None and bounds_ss_cat is None: +# self.config_origin = None # type: ignore +# else: +# self.config_origin = "BOinG" +# if isinstance(self.model, GloballyAugmentedLocalGaussianProcess): +# num_inducing_points = min(max(min(2 * len(self.activate_dims_cont), 10), self.model_x.shape[0] // 20), 50) +# self.model.update_attribute(num_inducing_points=num_inducing_points) + +# subspace_acq_func_opt_kwargs = { +# "acquisition_function": self.acquisition_function, +# "configspace": self.cs_local, # type: ignore[attr-defined] # noqa F821 +# "seed": self.rng.randint(1, 2**20), +# } + +# if isinstance(acq_optimizer_local, AbstractAcquisitionMaximizer): +# # we copy the attribute of the local acquisition function optimizer but replace it with our local model +# # setting. This helps a better exploration in the beginning. +# for key in inspect.signature(acq_optimizer_local.__init__).parameters.keys(): # type: ignore[misc] +# if key == "self": +# continue +# elif key in subspace_acq_func_opt_kwargs: +# continue +# elif hasattr(acq_func_local, key): +# subspace_acq_func_opt_kwargs[key] = getattr(acq_func_local, key) +# self.acq_optimizer_local = type(acq_optimizer_local)(**subspace_acq_func_opt_kwargs) +# else: +# if acq_optimizer_local is None: +# acq_optimizer_local = LocalAndSortedRandomSearch # type: ignore +# if acq_optimizer_local_kwargs is not None: +# subspace_acq_func_opt_kwargs.update(acq_optimizer_local_kwargs) +# else: +# # Here are the setting used by squirrel-optimizer +# # https://github.com/automl/Squirrel-Optimizer-BBO-NeurIPS20-automlorg/blob/main/squirrel-optim +# izer/smac_optim.py +# local_search_iterations = { +# 1: 10, +# 2: 10, +# 3: 10, +# 4: 10, +# 5: 10, +# 6: 10, +# 7: 8, +# 8: 6, +# }.get(len(self.cs_local.get_hyperparameters()), 5) + +# subspace_acq_func_opt_kwargs.update( +# {"n_steps_plateau_walk": 5, "local_search_iterations": local_search_iterations} +# ) + +# elif inspect.isclass(acq_optimizer_local, AbstractAcquisitionMaximizer): +# subspace_acq_func_opt_kwargs.update(acq_optimizer_local_kwargs) +# else: +# raise TypeError( +# f"subspace_optimizer must be None or an object implementing the " +# f"AcquisitionFunctionMaximizer, but is '{acq_optimizer_local}'" +# ) + +# self.acq_optimizer_local = acq_optimizer_local(**subspace_acq_func_opt_kwargs) # type: ignore + +# def _generate_challengers(self, **optimizer_kwargs: Dict) -> List[Tuple[float, Configuration]]: +# """ +# Generate new challengers list for this subspace. This optimizer is similar to +# smac.optimizer.ei_optimization.LocalAndSortedRandomSearch except that we don't read the past evaluated +# information from the runhistory but directly assign new values to the +# """ +# self.model.train(self.model_x, self.model_y) +# self.update_model(predict_x_best=True, update_incumbent_array=True) +# num_points_rs = 1000 + +# if isinstance(self.acq_optimizer_local, LocalAndSortedRandomSearch): +# next_configs_random = self.acq_optimizer_local.random_search._maximize( +# previous_configs=[], +# num_points=num_points_rs, +# _sorted=True, +# ) +# if len(self.ss_x) == 0: +# init_points_local = self.cs_local.sample_configuration(size=self.acq_optimizer_local.n_sls_iterations) +# else: +# previous_configs = [Configuration(configuration_space=self.cs_local, vector=ss_x) for ss_x in +# self.ss_x] +# init_points_local = self.acq_optimizer_local.local_search._get_init_points_from_previous_configs( +# self.acq_optimizer_local.local_search_iterations, previous_configs, next_configs_random +# ) + +# configs_acq_local = self.acq_optimizer_local.local_search._do_search(init_points_local) + +# # shuffle for random tie-break +# self.rng.shuffle(configs_acq_local) + +# # sort according to acq value +# configs_acq_local.sort(reverse=True, key=lambda x: x[0]) + +# for _, inc in configs_acq_local: +# inc.origin = "Local Search" + +# # Having the configurations from random search, sorted by their +# # acquisition function value is important for the first few iterations +# # of SMAC. As long as the random forest predicts constant value, we +# # want to use only random configurations. Having them at the begging of +# # the list ensures this (even after adding the configurations by local +# # search, and then sorting them) +# next_configs_by_acq_value = next_configs_random + configs_acq_local + +# next_configs_by_acq_value.sort(reverse=True, key=lambda x: x[0]) +# logger.debug( +# "First 5 acq func (origin) values of selected configurations: %s", +# str([[_[0], _[1].origin] for _ in next_configs_by_acq_value[:5]]), +# ) +# return next_configs_by_acq_value +# else: +# return self.acq_optimizer_local._maximize([], num_points_rs) # type: ignore diff --git a/smac/utils/subspaces/turbo_subspace.py b/smac/utils/subspaces/turbo_subspace.py new file mode 100644 index 000000000..079b1742a --- /dev/null +++ b/smac/utils/subspaces/turbo_subspace.py @@ -0,0 +1,305 @@ +# from __future__ import annotations + +# from typing import Dict, List, Optional, Tuple, Type, Union + +# import math +# import warnings + +# import numpy as np +# from ConfigSpace.hyperparameters import NumericalHyperparameter +# from ConfigSpace.util import deactivate_inactive_hyperparameters +# from scipy.stats.qmc import LatinHypercube, Sobol + +# from smac.acquisition import AbstractAcquisitionMaximizer +# from smac.acquisition.function import AbstractAcquisitionFunction, TS +# from ConfigSpace import Configuration, ConfigurationSpace +# from smac.model.abstract_model import AbstractModel +# from smac.model.gaussian_process.gpytorch_gaussian_process import GloballyAugmentedLocalGaussianProcess +# from smac.model.gaussian_process.abstract_gaussian_process import AbstractGaussianProcess +# from smac.model.gaussian_process.gpytorch_gaussian_process import GPyTorchGaussianProcess +# from smac.model.gaussian_process.mcmc_gaussian_process import MCMCGaussianProcess +# from smac.utils.logging import get_logger +# from smac.utils.subspaces import LocalSubspace + +# logger = get_logger(__name__) + +# warnings.filterwarnings("ignore", message="The balance properties of Sobol' points require" " n to be a power of 2.") + + +# class TuRBOSubSpace(LocalSubspace): +# """ +# Subspace designed for TurBO: +# D. Eriksson et al. Scalable Global Optimization via Local Bayesian Optimization +# https://proceedings.neurips.cc/paper/2019/hash/6c990b7aca7bc7058f5e98ea909e924b-Abstract.html + +# The hyperparameters follow the illustration under supplementary D, `TuRBO details`. + +# Parameters +# ---------- +# length_init: float +# initialized length of subspace +# length_min: float +# the minimal length of subspace, if the subspace has a length smaller than this value, turbo will restart +# length_max: float +# the maximal length of subspace +# success_tol: float +# the number of successive successful evaluations required for expanding the subregion +# failure_tol_min: float +# the minimal number of successive successful evaluations required for shrinking the subregion (otherwise +# this value is set as number of feature dimensions) +# n_init_x_params: int +# how many configurations will be used at most in the initial design (X*D). Used for restarting the subspace +# n_candidate_max: int +# The maximal Number of points used as candidates +# """ + +# def __init__( +# self, +# config_space: ConfigurationSpace, +# bounds: List[Tuple[float, float]], +# hps_types: List[int], +# bounds_ss_cont: Optional[np.ndarray] = None, +# bounds_ss_cat: Optional[List[Tuple]] = None, +# model_local: Union[AbstractModel, Type[AbstractModel]] = GPyTorchGaussianProcess, +# model_local_kwargs: Dict = {}, +# acq_func_local: Union[AbstractAcquisitionFunction, Type[AbstractAcquisitionFunction]] = TS, +# acq_func_local_kwargs: Optional[Dict] = None, +# rng: Optional[np.random.RandomState] = None, +# initial_data: Optional[Tuple[np.ndarray, np.ndarray]] = None, +# activate_dims: Optional[np.ndarray] = None, +# incumbent_array: Optional[np.ndarray] = None, +# length_init: float = 0.8, +# length_min: float = 0.5**7, +# length_max: float = 1.6, +# success_tol: int = 3, +# failure_tol_min: int = 4, +# n_init_x_params: int = 2, +# n_candidate_max: int = 5000, +# ): +# self.num_valid_observations = 0 +# super(TuRBOSubSpace, self).__init__( +# config_space=config_space, +# bounds=bounds, +# hps_types=hps_types, +# bounds_ss_cont=bounds_ss_cont, +# bounds_ss_cat=bounds_ss_cat, +# model_local=model_local, +# model_local_kwargs=model_local_kwargs, +# acq_func_local=acq_func_local, +# acq_func_local_kwargs=acq_func_local_kwargs, +# rng=rng, +# initial_data=initial_data, +# activate_dims=activate_dims, +# incumbent_array=incumbent_array, +# ) +# hps = config_space.get_hyperparameters() +# for hp in hps: +# if not isinstance(hp, NumericalHyperparameter): +# raise ValueError("Current TurBO Optimizer only supports Numerical Hyperparameters") +# if len(config_space.get_conditions()) > 0 or len(config_space.get_forbiddens()) > 0: +# raise ValueError("Currently TurBO does not support Conditional or Forbidden Hyperparameters") + +# n_hps = len(self.activate_dims) +# self.n_dims = n_hps +# self.n_init = n_init_x_params * self.n_dims +# self.n_candidates = min(100 * n_hps, n_candidate_max) + +# self.failure_tol = max(failure_tol_min, n_hps) +# self.success_tol = success_tol +# self.length = length_init +# self.length_init = length_init +# self.length_min = length_min +# self.length_max = length_max +# self._restart_turbo(n_init_points=self.n_init) + +# if initial_data is not None: +# self.add_new_observations(initial_data[0], initial_data[1]) +# self.init_configs = [] # type: List[Configuration] + +# self.lb = np.zeros(self.n_dims) +# self.ub = np.ones(self.n_dims) +# self.config_origin = "TuRBO" + +# def _restart_turbo( +# self, +# n_init_points: int, +# ) -> None: +# """ +# Restart TurBO with a certain number of initialized points. New points are initialized with latin hypercube + +# Parameters +# ---------- +# n_init_points: int +# number of points required for initializing a new subspace +# """ +# logger.debug("Current length is smaller than the minimal value, a new TuRBO restarts") +# self.success_count = 0 +# self.failure_count = 0 + +# self.num_eval_this_round = 0 +# self.last_incumbent_value = np.inf +# self.length = self.length_init + +# self.num_valid_observations = 0 + +# init_vectors = LatinHypercube(d=self.n_dims, seed=np.random.seed(self.rng.randint(1, 2**20))).random( +# n=n_init_points +# ) + +# self.init_configs = [Configuration(self.cs_local, vector=init_vector) for init_vector in init_vectors] + +# def adjust_length(self, new_observation: Union[float, np.ndarray]) -> None: +# """ +# Adjust the subspace length according to the performance of the latest suggested values +# Parameters +# ---------- +# new_observation: Union[float, np.ndarray] +# new observations +# """ +# # see Section 2: 'Trust regions' +# optim_observation = new_observation if np.isscalar(new_observation) else np.min(new_observation) + +# # We define a ``success'' as a candidate that improves upon $\xbest$, and a ``failure'' as a candidate that +# # does not. +# if optim_observation < np.min(self.model_y) - 1e-3 * math.fabs(np.min(self.model_y)): +# logger.debug("New suggested value is better than the incumbent, success_count increases") +# self.success_count += 1 +# self.failure_count = 0 +# else: +# logger.debug("New suggested value is worse than the incumbent, failure_count increases") +# self.success_count = 0 +# self.failure_count += 1 + +# # After $\tau_{\text{succ}}$ consecutive successes, we double the size of the TR, +# # i.e., $\len \gets \min\{\len_{\textrm{max}}, 2\len\}$. +# if self.success_count == self.success_tol: # Expand trust region +# self.length = min([2.0 * self.length, self.length_max]) +# self.success_count = 0 +# logger.debug(f"Subspace length expands to {self.length}") +# # After $\tau_{\text{fail}}$ consecutive failures, we halve the size of the TR: $\len \gets \len/2$. +# # We reset the success and failure counters to zero after we change the size of the TR. +# elif self.failure_count == self.failure_tol: # Shrink trust region +# self.length /= 2.0 +# self.failure_count = 0 +# logger.debug(f"Subspace length shrinks to {self.length}") + +# def _generate_challengers( # type: ignore[override] +# self, _sorted: bool = True +# ) -> List[Tuple[float, Configuration]]: +# """ +# Generate new challengers list for this subspace + +# Parameters +# ---------- +# _sorted: bool +# if the generated challengers are sorted by their acquisition function values +# """ +# if len(self.init_configs) > 0: +# config_next = self.init_configs.pop() +# return [(0, config_next)] + +# if self.length < self.length_min: +# self._restart_turbo(n_init_points=self.n_init) +# config_next = self.init_configs.pop() +# return [(0, config_next)] + +# self.model.train(self.model_x[-self.num_valid_observations :], self.model_y[-self.num_valid_observations :]) +# self.update_model(predict_x_best=False, update_incumbent_array=True) + +# sobol_gen = Sobol(d=self.n_dims, scramble=True, seed=self.rng.randint(low=0, high=10000000)) +# sobol_seq = sobol_gen.random(self.n_candidates) + +# # adjust length according to kernel length +# if isinstance( +# self.model, +# ( +# AbstractGaussianProcess, +# MCMCGaussianProcess, +# GloballyAugmentedLocalGaussianProcess, +# GPyTorchGaussianProcess, +# ), +# ): +# if isinstance(self.model, AbstractGaussianProcess): +# kernel_length = np.exp(self.model.hypers[1:-1]) +# elif isinstance(self.model, MCMCGaussianProcess): +# kernel_length = np.exp(np.mean((np.array(self.model.hypers)[:, 1:-1]), axis=0)) +# elif isinstance(self.model, (GPyTorchGaussianProcess, GloballyAugmentedLocalGaussianProcess)): +# kernel_length = self.model.kernel.base_kernel.lengthscale.cpu().detach().numpy() + +# # See section 'Trust regions' of section 2 +# # $\len_i = \lambda_i L / (\prod_{j=1}^d \lambda_j)^{1/d}$, +# # We now have weights.prod() = 1 +# # This makes the result more stable +# subspace_scale = kernel_length / np.prod(np.power(kernel_length, 1.0 / self.n_dims)) + +# subspace_length = self.length * subspace_scale + +# subspace_lb = np.clip(self.incumbent_array - subspace_length * 0.5, 0.0, 1.0) +# subspace_ub = np.clip(self.incumbent_array + subspace_length * 0.5, 0.0, 1.0) +# sobol_seq = sobol_seq * (subspace_ub - subspace_lb) + subspace_lb + +# prob_perturb = min(20.0 / self.n_dims, 1.0) +# design = self._perturb_samples(prob_perturb, sobol_seq) + +# # Only numerical hyperpameters are considered for TuRBO, we don't need to transfer the vectors to fit the +# # requirements of other sorts of hyperparameters +# configs = [] +# for vector in design: +# conf = deactivate_inactive_hyperparameters( +# configuration=None, configuration_space=self.cs_local, vector=vector +# ) +# configs.append(conf) + +# if _sorted: +# return AbstractAcquisitionMaximizer._sort_configs_by_acq_value(self, configs) +# else: +# return [(0, configs[i]) for i in range(len(configs))] + +# def _perturb_samples(self, prob_perturb: float, design: np.ndarray) -> np.ndarray: +# """ +# See Supplementary D, 'TuRBO details': +# In order to not perturb all coordinates at once, we use the value in the Sobol sequence +# with probability min{1,20/d} for a given candidate and dimension, and the value of the center otherwise + +# perturb the generated design with the incumbent array accordingly + +# Parameters +# ---------- +# prob_perturb: float +# probability that a design is perturbed by the incumbent value +# design: np.ndarray(self.n_candidates, self.n_dims) +# design array to be perturbed +# Returns +# ------- +# design_perturbed: np.ndarray(self.n_candidates, self.n_dims) +# perturbed design array +# """ +# # we will use masked array, thus the indices that will be replaced will be marked with True +# mask = self.rng.rand(self.n_candidates, self.n_dims) > prob_perturb + +# ind = np.where(np.sum(mask, axis=1) == self.n_dims)[0] +# if self.n_dims == 1: +# mask[ind, 0] = 0 +# else: +# # ensure that no candidate will be completely replaced by the incumbent value +# mask[ind, self.rng.randint(0, self.n_dims, size=len(ind))] = 0 +# return np.ma.array(design, mask=mask, fill_value=self.incumbent_array).filled() + +# def add_new_observations(self, X: np.ndarray, y: np.ndarray) -> None: +# """ +# Add new observations to the subspace, meanwhile, we add the number of valid observation to ensure that the +# subspace could be scaled properly. + +# Parameters +# ---------- +# X: np.ndarray(N,D), +# new feature vector of the observations, constructed by the global configuration space +# y: np.ndarray(N) +# new performances of the observations +# Return +# ---------- +# indices_in_ss:np.ndarray(N) +# indices of data that included in subspaces +# """ +# super(TuRBOSubSpace, self).add_new_observations(X, y) +# self.num_valid_observations += len(y) diff --git a/smac/utils/test_helpers.py b/smac/utils/test_helpers.py deleted file mode 100644 index 9bdb884e4..000000000 --- a/smac/utils/test_helpers.py +++ /dev/null @@ -1,13 +0,0 @@ -from ConfigSpace.configuration_space import ConfigurationSpace -from ConfigSpace.hyperparameters import UniformFloatHyperparameter - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def get_branin_config_space() -> ConfigurationSpace: - """Returns the branin configspace.""" - cs = ConfigurationSpace() - cs.add_hyperparameter(UniformFloatHyperparameter("x", -5, 10)) - cs.add_hyperparameter(UniformFloatHyperparameter("y", 0, 15)) - return cs diff --git a/smac/utils/validate.py b/smac/utils/validate.py deleted file mode 100644 index 2ef1beffb..000000000 --- a/smac/utils/validate.py +++ /dev/null @@ -1,701 +0,0 @@ -from typing import Any, Dict, List, Optional, Sequence, Tuple, Union, cast - -import logging -import os -from collections import namedtuple - -import numpy as np -from joblib import Parallel, delayed - -from smac.configspace import Configuration, convert_configurations_to_array -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances -from smac.epm.random_forest.rfr_imputator import RFRImputator -from smac.epm.utils import get_types -from smac.runhistory.runhistory import RunHistory, RunInfo, RunKey, RunValue, StatusType -from smac.runhistory.runhistory2epm import RunHistory2EPM4Cost -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae.base import BaseRunner -from smac.tae.execute_ta_run_old import ExecuteTARunOld -from smac.utils.constants import MAXINT - -__author__ = "Joshua Marben" -__copyright__ = "Copyright 2017, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Joshua Marben" -__email__ = "joshua.marben@neptun.uni-freiburg.de" - - -def _unbound_tae_starter( - tae: BaseRunner, - runhistory: Optional[RunHistory], - run_info: RunInfo, - *args: Any, - **kwargs: Any, -) -> RunValue: - """Unbound function to be used by joblibs Parallel, since directly passing the TAE results in - pickling-problems. - - Parameters - ---------- - tae: BaseRunner - tae to be used - runhistory: RunHistory - runhistory to save - run_info: RunInfo - Config to be launched - *args, **kwargs: various - arguments to the tae - - Returns - ------- - tae_results: RunValue - return from tae.start - """ - run_info, result = tae.run_wrapper(run_info) - tae.stats.submitted_ta_runs += 1 - tae.stats.finished_ta_runs += 1 - tae.stats.ta_time_used += float(result.time) - if runhistory: - runhistory.add( - config=run_info.config, - cost=result.cost, - time=result.time, - status=result.status, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - ) - tae.stats.n_configs = len(runhistory.config_ids) - - return result - - -_Run = namedtuple("_Run", "config inst seed inst_specs") - - -class Validator(object): - """Validator for the output of SMAC-scenarios. - - Evaluates specified configurations on specified instances. - - Parameters - ---------- - scenario: Scenario - scenario object for cutoff, instances, features and specifics - trajectory: trajectory-list - trajectory to take incumbent(s) from - rng: np.random.RandomState or int - Random number generator or seed - """ - - def __init__( - self, - scenario: Scenario, - trajectory: Optional[List], - rng: Union[np.random.RandomState, int, None] = None, - ) -> None: - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - self.traj = trajectory - self.scen = scenario - self.epm = None # type: Optional[RandomForestWithInstances] - - if isinstance(rng, np.random.RandomState): - self.rng = rng - elif isinstance(rng, int): - self.rng = np.random.RandomState(seed=rng) - else: - self.logger.debug("no seed given, using default seed of 1") - num_run = 1 - self.rng = np.random.RandomState(seed=num_run) - - def _save_results( - self, - rh: RunHistory, - output_fn: Optional[str], - backup_fn: Optional[str] = None, - ) -> None: - """Helper to save results to file. - - Parameters - ---------- - rh: RunHistory - runhistory to save - output_fn: str - if ends on '.json': filename to save history to - else: directory to save runhistory to (filename is backup_fn) - backup_fn: str - if output_fn does not end on '.json', treat output_fn as dir and - append backup_fn as filename (if output_fn ends on '.json', this - argument is ignored) - """ - if not output_fn: - self.logger.info("No output specified, validated runhistory not saved.") - return - # Check if a folder or a file is specified as output - if not output_fn.endswith(".json"): - if backup_fn is None: - raise ValueError("If output_fn does not end with .json the argument backup_fn needs to be given.") - output_dir = output_fn - output_fn = os.path.join(output_dir, backup_fn) - self.logger.debug('Output is "%s", changing to "%s"!', output_dir, output_fn) - base = os.path.split(output_fn)[0] - if not base == "" and not os.path.exists(base): - self.logger.debug('Folder ("%s") doesn\'t exist, creating.', base) - os.makedirs(base) - rh.save_json(output_fn) - self.logger.info("Saving validation-results in %s", output_fn) - - def validate( - self, - config_mode: Union[str, List[Configuration]] = "def", - instance_mode: Union[str, List[str]] = "test", - repetitions: int = 1, - n_jobs: int = 1, - backend: str = "threading", - runhistory: Optional[RunHistory] = None, - tae: BaseRunner = None, - output_fn: Optional[str] = None, - ) -> RunHistory: - """Validate configs on instances and save result in runhistory. If a runhistory is provided - as input it is important that you run it on the same/comparable hardware. - - side effect: if output is specified, saves runhistory to specified - output directory. - - Parameters - ---------- - config_mode: str or list - string or directly a list of Configuration. - string from [def, inc, def+inc, wallclock_time, cpu_time, all]. - time evaluates at cpu- or wallclock-timesteps of: - [max_time/2^0, max_time/2^1, max_time/2^3, ..., default] - with max_time being the highest recorded time - instance_mode: str or list - what instances to use for validation, either from - [train, test, train+test] or directly a list of instances - repetitions: int - number of repetitions in nondeterministic algorithms - n_jobs: int - number of parallel processes used by joblib - backend: str - what backend joblib should use for parallel runs - runhistory: RunHistory - optional, RunHistory-object to reuse runs - tae: BaseRunner - tae to be used. if None, will initialize ExecuteTARunOld - output_fn: str - path to runhistory to be saved. if the suffix is not '.json', will - be interpreted as directory and filename will be - 'validated_runhistory.json' - - Returns - ------- - runhistory: RunHistory - runhistory with validated runs - """ - self.logger.debug( - "Validating configs '%s' on instances '%s', repeating %d times" " with %d parallel runs on backend '%s'.", - config_mode, - instance_mode, - repetitions, - n_jobs, - backend, - ) - - # Get all runs to be evaluated as list - runs, validated_rh = self._get_runs(config_mode, instance_mode, repetitions, runhistory) - - # Create new Stats without limits - inf_scen = Scenario( - { - "run_obj": self.scen.run_obj, - "cutoff_time": self.scen.cutoff, # type: ignore[attr-defined] # noqa F821 - "output_dir": "", - } - ) - inf_stats = Stats(inf_scen) - inf_stats.start_timing() - - # Create TAE - if not tae: - tae = ExecuteTARunOld( - ta=self.scen.ta, # type: ignore[attr-defined] # noqa F821 - stats=inf_stats, - run_obj=self.scen.run_obj, - par_factor=self.scen.par_factor, # type: ignore[attr-defined] # noqa F821 - cost_for_crash=self.scen.cost_for_crash, - ) # type: ignore[attr-defined] # noqa F821 - else: - # Inject endless-stats - tae.stats = inf_stats - - # Validate! - run_results = self._validate_parallel(tae, runs, n_jobs, backend, runhistory) - assert len(run_results) == len(runs), (run_results, runs) - - # tae returns (status, cost, runtime, additional_info) - # Add runs to RunHistory - for run, result in zip(runs, run_results): - validated_rh.add( - config=run.config, - cost=result.cost, - time=result.time, - status=result.status, - instance_id=run.inst, - seed=run.seed, - additional_info=result.additional_info, - ) - - self._save_results(validated_rh, output_fn, backup_fn="validated_runhistory.json") - return validated_rh - - def _validate_parallel( - self, - tae: BaseRunner, - runs: List[_Run], - n_jobs: int, - backend: str, - runhistory: Optional[RunHistory] = None, - ) -> List[RunValue]: - """Validate runs with joblibs Parallel-interface. - - Parameters - ---------- - tae: BaseRunner - tae to be used for validation - runs: list<_Run> - list with _Run-objects - [_Run(config=CONFIG1,inst=INSTANCE1,seed=SEED1,inst_specs=INST_SPECIFICS1), ...] - n_jobs: int - number of cpus to use for validation (-1 to use all) - backend: str - what backend to use for parallelization - runhistory: RunHistory - optional, RunHistory-object to reuse runs - - Returns - ------- - run_results: list - results as returned by tae - """ - # Runs with parallel - run_results = Parallel(n_jobs=n_jobs, backend=backend)( - delayed(_unbound_tae_starter)( - tae, - runhistory, - RunInfo( - config=run.config, - instance=run.inst, - instance_specific="0", - seed=run.seed, - cutoff=self.scen.cutoff, # type: ignore[attr-defined] # noqa F821 - capped=False, - budget=0, - ), - ) - for run in runs - ) - return run_results - - def validate_epm( - self, - config_mode: Union[str, List[Configuration]] = "def", - instance_mode: Union[str, List[str]] = "test", - repetitions: int = 1, - runhistory: Optional[RunHistory] = None, - output_fn: Optional[str] = None, - reuse_epm: bool = True, - ) -> RunHistory: - """Use EPM to predict costs/runtimes for unknown config/inst-pairs. - - side effect: if output is specified, saves runhistory to specified - output directory. - - Parameters - ---------- - output_fn: str - path to runhistory to be saved. if the suffix is not '.json', will - be interpreted as directory and filename will be - 'validated_runhistory_EPM.json' - config_mode: str or list - string or directly a list of Configuration, string from [def, inc, def+inc, wallclock_time, cpu_time, all]. - time evaluates at cpu- or wallclock-timesteps of: - [max_time/2^0, max_time/2^1, max_time/2^3, ..., default] with max_time being the highest recorded time - instance_mode: str or list - what instances to use for validation, either from - [train, test, train+test] or directly a list of instances - repetitions: int - number of repetitions in nondeterministic algorithms - runhistory: RunHistory - optional, RunHistory-object to reuse runs - reuse_epm: bool - if true (and if `self.epm`), reuse epm to validate runs - - Returns - ------- - runhistory: RunHistory - runhistory with predicted runs - """ - if not isinstance(runhistory, RunHistory) and (self.epm is None or not reuse_epm): - raise ValueError("No runhistory specified for validating with EPM!") - elif not reuse_epm or self.epm is None: - # Create RandomForest - types, bounds = get_types(self.scen.cs, self.scen.feature_array) # type: ignore[attr-defined] # noqa F821 - epm = RandomForestWithInstances( - configspace=self.scen.cs, # type: ignore[attr-defined] # noqa F821 - types=types, - bounds=bounds, - instance_features=self.scen.feature_array, - seed=self.rng.randint(MAXINT), - ratio_features=1.0, - ) - # Use imputor if objective is runtime - imputor = None - impute_state = None - impute_censored_data = False - if self.scen.run_obj == "runtime": - threshold = self.scen.cutoff * self.scen.par_factor # type: ignore[attr-defined] # noqa F821 - imputor = RFRImputator( - rng=self.rng, - cutoff=self.scen.cutoff, # type: ignore[attr-defined] # noqa F821 - threshold=threshold, - model=epm, - ) - impute_censored_data = True - impute_state = [StatusType.CAPPED] - success_states = [ - StatusType.SUCCESS, - ] - else: - success_states = [StatusType.SUCCESS, StatusType.CRASHED, StatusType.MEMOUT] - - # Transform training data (from given rh) - rh2epm = RunHistory2EPM4Cost( - num_params=len(self.scen.cs.get_hyperparameters()), # type: ignore[attr-defined] # noqa F821 - scenario=self.scen, - rng=self.rng, - impute_censored_data=impute_censored_data, - imputor=imputor, - impute_state=impute_state, - success_states=success_states, - ) - assert runhistory is not None # please mypy - X, y = rh2epm.transform(runhistory) - self.logger.debug("Training model with data of shape X: %s, y:%s", str(X.shape), str(y.shape)) - # Train random forest - epm.train(X, y) - else: - epm = cast(RandomForestWithInstances, self.epm) - - # Predict desired runs - runs, rh_epm = self._get_runs(config_mode, instance_mode, repetitions, runhistory) - - feature_array_size = len(self.scen.cs.get_hyperparameters()) # type: ignore[attr-defined] # noqa F821 - if self.scen.feature_array is not None: - feature_array_size += self.scen.feature_array.shape[1] - - X_pred = np.empty((len(runs), feature_array_size)) - for idx, run in enumerate(runs): - if self.scen.feature_array is not None and run.inst is not None: - X_pred[idx] = np.hstack( - [ - convert_configurations_to_array([run.config])[0], - self.scen.feature_dict[run.inst], # type: ignore - ] - ) - else: - X_pred[idx] = convert_configurations_to_array([run.config])[0] - self.logger.debug("Predicting desired %d runs, data has shape %s", len(runs), str(X_pred.shape)) - - y_pred = epm.predict(X_pred) - self.epm = epm - - # Add runs to runhistory - for run, pred in zip(runs, y_pred[0]): - rh_epm.add( - config=run.config, - cost=float(pred), - time=float(pred), - status=StatusType.SUCCESS, - instance_id=run.inst, - seed=-1, - additional_info={"additional_info": "ESTIMATED USING EPM!"}, - ) - - if output_fn: - self._save_results(rh_epm, output_fn, backup_fn="validated_runhistory_EPM.json") - return rh_epm - - def _get_runs( - self, - configs: Union[str, List[Configuration]], - insts: Union[str, List[str]], - repetitions: int = 1, - runhistory: RunHistory = None, - ) -> Tuple[List[_Run], RunHistory]: - """Generate list of SMAC-TAE runs to be executed. This means combinations of configs with - all instances on a certain number of seeds. - - side effect: Adds runs that don't need to be reevaluated to self.rh! - - Parameters - ---------- - configs: str or list - string or directly a list of Configuration - str from [def, inc, def+inc, wallclock_time, cpu_time, all] - time evaluates at cpu- or wallclock-timesteps of: - [max_time/2^0, max_time/2^1, max_time/2^3, ..., default] - with max_time being the highest recorded time - insts: str or list - what instances to use for validation, either from - [train, test, train+test] or directly a list of instances - repetitions: int - number of seeds per instance/config-pair to be evaluated - runhistory: RunHistory - optional, try to reuse this runhistory and save some runs - - Returns - ------- - runs: list<_Run> - list with _Runs - [_Run(config=CONFIG1,inst=INSTANCE1,seed=SEED1,inst_specs=INST_SPECIFICS1), - _Run(config=CONFIG2,inst=INSTANCE2,seed=SEED2,inst_specs=INST_SPECIFICS2), - ...] - """ - # Get relevant configurations and instances - if isinstance(configs, str): - configs = self._get_configs(configs) - if isinstance(insts, str): - instances = sorted(self._get_instances(insts)) # type: Sequence[Union[str, None]] - elif insts is not None: - instances = sorted(insts) - else: - instances = [None] - # If no instances are given, fix the instances to one "None" instance - if not instances: - instances = [None] - - # If algorithm is deterministic, fix repetitions to 1 - if self.scen.deterministic and repetitions != 1: # type: ignore[attr-defined] # noqa F821 - self.logger.warning( - "Specified %d repetitions, but fixing to 1, " "because algorithm is deterministic.", - repetitions, - ) - repetitions = 1 - - # Extract relevant information from given runhistory - inst_seed_config = self._process_runhistory(configs, instances, runhistory) - - # Now create the actual run-list - runs = [] - # Counter for runs without the need of recalculation - runs_from_rh = 0 - # If we reuse runs, we want to return them as well - new_rh = RunHistory() - - for i in instances: - for rep in range(repetitions): - # First, find a seed and add all the data we can take from the - # given runhistory to "our" validation runhistory. - configs_evaluated = [] # type: Configuration - if runhistory and i in inst_seed_config: - # Choose seed based on most often evaluated inst-seed-pair - seed, configs_evaluated = inst_seed_config[i].pop(0) - # Delete inst if all seeds are used - if not inst_seed_config[i]: - inst_seed_config.pop(i) - # Add runs to runhistory - for c in configs_evaluated[:]: - runkey = RunKey(runhistory.config_ids[c], i, seed) - cost, time, status, start, end, additional_info = runhistory.data[runkey] - if status in [StatusType.CRASHED, StatusType.ABORT, StatusType.CAPPED]: - # Not properly executed target algorithm runs should be repeated - configs_evaluated.remove(c) - continue - new_rh.add( - c, - cost, - time, - status, - instance_id=i, - seed=seed, - starttime=start, - endtime=end, - additional_info=additional_info, - ) - runs_from_rh += 1 - else: - # If no runhistory or no entries for instance, get new seed - seed = self.rng.randint(MAXINT) - - # We now have a seed and add all configs that are not already - # evaluated on that seed to the runs-list. This way, we - # guarantee the same inst-seed-pairs for all configs. - for config in [c for c in configs if c not in configs_evaluated]: - # Only use specifics if specific exists, else use string "0" - specs = self.scen.instance_specific[i] if i and i in self.scen.instance_specific else "0" - runs.append(_Run(config=config, inst=i, seed=seed, inst_specs=specs)) - - self.logger.info( - "Collected %d runs from %d configurations on %d " - "instances with %d repetitions. Reusing %d runs from " - "given runhistory.", - len(runs), - len(configs), - len(instances), - repetitions, - runs_from_rh, - ) - - return runs, new_rh - - def _process_runhistory( - self, - configs: List[Configuration], - insts: Sequence[Optional[str]], - runhistory: Optional[RunHistory], - ) -> Dict[str, List[Tuple[int, List[Configuration]]]]: - """Processes runhistory from self._get_runs by extracting already evaluated (relevant) - config-inst-seed tuples. - - Parameters - ---------- - configs: list(Configuration) - list of configs of interest - insts: list(str) - list of instances of interest - runhistory: RunHistory - runhistory to extract runs from - - Returns - ------- - inst_seed_config: dict - dictionary mapping instances to a list of tuples of already used - seeds and the configs that this inst-seed-pair has been evaluated - on, sorted by the number of configs - """ - # We want to reuse seeds that have been used on most configurations - # To this end, we create a dictionary as {instances:{seed:[configs]}} - # Like this we can easily retrieve the most used instance-seed pairs to - # minimize the number of runs to be evaluated - if runhistory: - inst_seed_config = {} # type: Dict[str, Dict[int, List[Configuration]]] - relevant = dict() - for key in runhistory.data: - if runhistory.ids_config[key.config_id] in configs and key.instance_id in insts: - relevant[key] = runhistory.data[key] - - # Change data-structure to {instances:[(seed1, (configs)), (seed2, (configs), ... ]} - # to make most used seed easily accessible, we sort after length of configs - for key in relevant: - inst, seed = key.instance_id, key.seed - config = runhistory.ids_config[key.config_id] - if inst in inst_seed_config: - if seed in inst_seed_config[inst]: - inst_seed_config[inst][seed].append(config) - else: - inst_seed_config[inst][seed] = [config] - else: - inst_seed_config[inst] = {seed: [config]} - - return { - i: sorted( - [(seed, list(inst_seed_config[i][seed])) for seed in inst_seed_config[i]], - key=lambda x: len(x[1]), - ) - for i in inst_seed_config - } - else: - rval = {} # type: Dict[str, List[Tuple[int, List[Configuration]]]] - return rval - - def _get_configs(self, mode: str) -> List[str]: - """Return desired configs. - - Parameters - ---------- - mode: str - str from [def, inc, def+inc, wallclock_time, cpu_time, all] - time evaluates at cpu- or wallclock-timesteps of: - [max_time/2^0, max_time/2^1, max_time/2^3, ..., default] - with max_time being the highest recorded time - - Returns - ------- - configs: list - list with desired configurations - """ - # Get trajectory and make sure it's not None to please mypy - traj = self.traj - assert traj is not None # please mypy - - # Add desired configs - configs = [] - mode = mode.lower() - if mode not in ["def", "inc", "def+inc", "wallclock_time", "cpu_time", "all"]: - raise ValueError("%s not a valid option for config_mode in validation." % mode) - if mode == "def" or mode == "def+inc": - configs.append(self.scen.cs.get_default_configuration()) # type: ignore[attr-defined] # noqa F821 - if mode == "inc" or mode == "def+inc": - configs.append(traj[-1]["incumbent"]) - if mode in ["wallclock_time", "cpu_time"]: - # get highest time-entry and add entries from there - # not using wallclock_limit in case it's inf - if mode == "wallclock_time" and np.isfinite(self.scen.wallclock_limit): - max_time = self.scen.wallclock_limit - elif mode == "cpu_time" and np.isfinite(self.scen.algo_runs_timelimit): - max_time = self.scen.algo_runs_timelimit - else: - max_time = traj[-1][mode] - counter = 2**0 - for entry in traj[::-1]: - if entry[mode] <= max_time / counter and entry["incumbent"] not in configs: - configs.append(entry["incumbent"]) - counter *= 2 - if not traj[0]["incumbent"] in configs: - configs.append(traj[0]["incumbent"]) # add first - if mode == "all": - for entry in traj: - if not entry["incumbent"] in configs: - configs.append(entry["incumbent"]) - self.logger.debug("Gathered %d configurations for mode %s.", len(configs), mode) - return configs - - def _get_instances(self, mode: str) -> List[str]: - """Get desired instances. - - Parameters - ---------- - mode: str - what instances to use for validation, from [train, test, train+test] - - Returns - ------- - instances: list - instances to be used - """ - instance_mode = mode.lower() - if mode not in ["train", "test", "train+test"]: - raise ValueError("%s not a valid option for instance_mode in validation." % mode) - - # Make sure if instances matter, than instances should be passed - if (instance_mode == "train" and self.scen.train_insts == [None]) or ( - instance_mode == "test" and self.scen.test_insts == [None] - ): - self.logger.warning( - "Instance mode is set to %s, but there are no " - "%s-instances specified in the scenario. Setting instance mode to" - '"train+test"!', - instance_mode, - instance_mode, - ) - instance_mode = "train+test" - - instances = [] # type: List[str] - if (instance_mode == "train" or instance_mode == "train+test") and not self.scen.train_insts == [None]: - instances.extend(self.scen.train_insts) - if (instance_mode == "test" or instance_mode == "train+test") and not self.scen.test_insts == [None]: - instances.extend(self.scen.test_insts) - return instances diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 000000000..92a415162 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,199 @@ +""" +Testing +======= +The following are some features, guidelines and functionality for testing which makes +updating, adding and refactoring tests easier, especially as features and functionality +changes. + +**Marks** +* todo - ``pytest.mark.todo``` to mark a test which xfails as it's todo +* slow - ``pytest.mark.slow``` to mark a test which is skipped if `pytest --fast` + +**Documenting Tests** +To ease in understanding of tests, what is being tested and what's expected of the test, +each test should doc expected bhaviour, not describe the steps of the test. +Commenst relating to how a test does things can be left in the tests and not the doc. + + Expects + ------- + * Something should raise a ValueError when called with X as X is not handled by the + validator Y. + +**Test same module across files** +When a module has many complicated avenues to be tested, create a folder and split the +tests according to each avenue. See `test/test_automl` for example as the `automl.py` +module is quite complicated to test and all tests in a single file become difficult to +follow and change. + +**pytest_cases** +Using pytest_cases, we seperate a `case`, something that defines the state of the +object, from the actual `test`, which tests properties of these cases. + +A complicated example can be seen at `test/test_automl/cases.py` where we have +autoML instances that are classifier/regressor, fitted or not, with cv or holdout, +or fitted with no ensemble. TODO: Easier example. + +Docs: https://smarie.github.io/python-pytest-cases/ + +**Fixtures** +All fixtures in "test/fixtures" are known in every test file. We try to make use +of fixture `factories` which can be used to construct objects in complicated ways, +removing these complications from the tests themselves, importantly, keeping tests +short. A convention we use is to prefix them with `make`, for example, +`make_something`. This is useful for making data, e.g. `test/fixtures/data::make_data` + +..code:: python + + # Example of fixture factory + @fixture + def make_something(): + def _make(...args): + # ... complicated setup + # ... more complications + # ... make some sub objects which are complicated + return something + + return _make + + @parametrize("arg1", ['a', 'b', 'c']) + def test_something_does_x(arg1, make_something): + something = make_something(arg1, ...) + result = something.run() + assert something == expected +""" +from typing import Any, Iterator, List, Optional + +import re +import signal +from pathlib import Path + +import psutil +import pytest +from pytest import ExitCode, Item, Session + +DEFAULT_SEED = 0 + + +HERE = Path(__file__) + + +def walk(path: Path, include: Optional[str] = None) -> Iterator[Path]: + """Yeilds all files, iterating over directory + + Parameters + ---------- + path: Path + The root path to walk from + + include: Optional[str] = None + Include only directories which match this string + + Returns + ------- + Iterator[Path] + All file paths that could be found from this walk + """ + for p in path.iterdir(): + if p.is_dir(): + if include is None or re.match(include, p.name): + yield from walk(p, include) + else: + yield p.resolve() + + +def is_fixture(path: Path) -> bool: + """Whether a path is a fixture""" + return path.name.endswith("fixtures.py") + + +def as_module(path: Path) -> str: + """Convert a path to a module as seen from here""" + root = HERE.parent.parent + parts = path.relative_to(root).parts + return ".".join(parts).replace(".py", "") + + +def fixture_modules() -> List[str]: + """Get all fixture modules""" + fixtures_folder = HERE.parent / "fixtures" + return [as_module(path) for path in walk(fixtures_folder) if path.name.endswith(".py")] + + +def pytest_runtest_setup(item: Item) -> None: + """Run before each test""" + todos = [marker for marker in item.iter_markers(name="todo")] + if todos: + pytest.xfail(f"Test needs to be implemented, {item.location}") + + +def pytest_sessionstart(session: Session) -> None: + """Called after the ``Session`` object has been created and before performing + collection and entering the run test loop. + + Parameters + ---------- + session : Session + The pytest session object + """ + return + + +def pytest_sessionfinish(session: Session, exitstatus: ExitCode) -> None: + """Clean up any child processes""" + proc = psutil.Process() + kill_signal = signal.SIGTERM + for child in proc.children(recursive=True): + + # https://stackoverflow.com/questions/57336095/access-verbosity-level-in-a-pytest-helper-function + if session.config.getoption("verbose") > 0: + print(child, child.cmdline()) + + # https://psutil.readthedocs.io/en/latest/#kill-process-tree + try: + child.send_signal(kill_signal) + except psutil.NoSuchProcess: + pass + + +Config = Any # Can't find import? + + +def pytest_collection_modifyitems( + session: Session, + config: Config, + items: List[Item], +) -> None: + """Modifys the colelction of tests that are captured""" + if config.getoption("--fast"): + skip = pytest.mark.skip(reason="Test marked `slow` and `--fast` arg used") + + slow_items = [item for item in items if "slow" in item.keywords] + for item in slow_items: + item.add_marker(skip) + + +def pytest_configure(config: Config) -> None: + """Used to register marks""" + config.addinivalue_line("markers", "todo: Mark test as todo") + + +pytest_plugins = fixture_modules() + + +Parser = Any # Can't find import? + + +def pytest_addoption(parser: Parser) -> None: + """ + + Parameters + ---------- + parser : Parser + The parser to add options to + """ + parser.addoption( + "--fast", + action="store_true", + default=False, + help="Disable tests marked as slow", + ) diff --git a/smac/epm/gaussian_process/utils/__init__.py b/tests/fixtures/__init__.py similarity index 100% rename from smac/epm/gaussian_process/utils/__init__.py rename to tests/fixtures/__init__.py diff --git a/tests/fixtures/configspace.py b/tests/fixtures/configspace.py new file mode 100644 index 000000000..77cf9522d --- /dev/null +++ b/tests/fixtures/configspace.py @@ -0,0 +1,62 @@ +import pytest +from ConfigSpace import ( + Categorical, + ConfigurationSpace, + EqualsCondition, + Float, + InCondition, + Integer, +) + + +@pytest.fixture +def configspace_small() -> ConfigurationSpace: + cs = ConfigurationSpace(seed=0) + + a = Integer("a", (1, 10000), default=1) + b = Float("b", (1e-5, 1e-1), log=True, default=1e-1) + c = Categorical("c", ["cat", "dog", "mouse"], default="cat") + + # Add all hyperparameters at once: + cs.add_hyperparameters([a, b, c]) + + return cs + + +@pytest.fixture +def configspace_large() -> ConfigurationSpace: + cs = ConfigurationSpace(seed=0) + + n_layer = Integer("n_layer", (1, 5), default=1) + n_neurons = Integer("n_neurons", (8, 256), log=True, default=10) + activation = Categorical("activation", ["logistic", "tanh", "relu"], default="tanh") + solver = Categorical("solver", ["lbfgs", "sgd", "adam"], default="adam") + batch_size = Integer("batch_size", (30, 300), default=200) + learning_rate = Categorical("learning_rate", ["constant", "invscaling", "adaptive"], default="constant") + learning_rate_init = Float("learning_rate_init", (0.0001, 1.0), default=0.001, log=True) + + # Add all hyperparameters at once: + cs.add_hyperparameters( + [ + n_layer, + n_neurons, + activation, + solver, + batch_size, + learning_rate, + learning_rate_init, + ] + ) + + # Adding conditions to restrict the hyperparameter space... + # ... since learning rate is used when solver is 'sgd'. + use_lr = EqualsCondition(child=learning_rate, parent=solver, value="sgd") + # ... since learning rate initialization will only be accounted for when using 'sgd' or 'adam'. + use_lr_init = InCondition(child=learning_rate_init, parent=solver, values=["sgd", "adam"]) + # ... since batch size will not be considered when optimizer is 'lbfgs'. + use_batch_size = InCondition(child=batch_size, parent=solver, values=["sgd", "adam"]) + + # We can also add multiple conditions on hyperparameters at once: + cs.add_conditions([use_lr, use_batch_size, use_lr_init]) + + return cs diff --git a/tests/fixtures/datasets.py b/tests/fixtures/datasets.py new file mode 100644 index 000000000..95b5b0ed0 --- /dev/null +++ b/tests/fixtures/datasets.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from abc import abstractmethod + +import itertools + +import numpy as np +import pytest +from sklearn import datasets + + +class Dataset: + @abstractmethod + def get_instances(self, n: int = 45) -> list[str]: + raise NotImplementedError + + @abstractmethod + def get_instance_features(self, n: int = 45) -> dict[str, list[int | float]]: + raise NotImplementedError + + @abstractmethod + def get_instance_data(self, instance: str) -> tuple[np.ndarray, np.ndarray]: + raise NotImplementedError + + +class DigitsDataset(Dataset): + def __init__(self) -> None: + self._data = datasets.load_digits() + + def get_instances(self, n: int = 45) -> list[str]: + """Create instances from the dataset which include two classes only.""" + return [f"{classA}-{classB}" for classA, classB in itertools.combinations(self._data.target_names, 2)][:n] + + def get_instance_features(self, n: int = 45) -> dict[str, list[int | float]]: + """Returns the mean and variance of all instances as features.""" + features = {} + for instance in self.get_instances(n): + data, _ = self.get_instance_data(instance) + features[instance] = [np.mean(data), np.var(data)] + + return features + + def get_instance_data(self, instance: str) -> tuple[np.ndarray, np.ndarray]: + """Retrieve data from the passed instance.""" + # We split the dataset into two classes + classA, classB = instance.split("-") + indices = np.where(np.logical_or(int(classA) == self._data.target, int(classB) == self._data.target)) + + data = self._data.data[indices] + target = self._data.target[indices] + + return data, target + + +@pytest.fixture +def digits_dataset() -> DigitsDataset: + return DigitsDataset() diff --git a/tests/fixtures/models.py b/tests/fixtures/models.py new file mode 100644 index 000000000..326717a61 --- /dev/null +++ b/tests/fixtures/models.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +import warnings + +import numpy as np +import pytest +from ConfigSpace import Categorical, Configuration, ConfigurationSpace, Float +from sklearn.linear_model import SGDClassifier +from sklearn.model_selection import StratifiedKFold, cross_val_score + +from tests.fixtures.datasets import Dataset + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +class Rosenbrock2D: + @property + def configspace(self) -> ConfigurationSpace: + cs = ConfigurationSpace(seed=0) + x0 = Float("x0", (-5, 10), default=-3) + x1 = Float("x1", (-5, 10), default=-4) + cs.add_hyperparameters([x0, x1]) + + return cs + + def train(self, config: Configuration, seed: int = 0, budget: float = None, instance: str = None) -> float: + """The 2-dimensional Rosenbrock function as a toy model. + The Rosenbrock function is well-known in the optimization community and + often serves as a toy problem. It can be defined for arbitrary + dimensions. The minimum is always at x_i = 1 with a function value of + zero. All input parameters are continuous. The search domain for + all x's is the interval [-5, 10]. + """ + x1 = config["x0"] + x2 = config["x1"] + + cost = 100.0 * (x2 - x1**2.0) ** 2.0 + (1 - x1) ** 2.0 + return cost + + +@pytest.fixture +def rosenbrock() -> Rosenbrock2D: + return Rosenbrock2D() + + +class SGD: + def __init__(self, dataset: Dataset) -> None: + self.dataset = dataset + + @property + def configspace(self) -> ConfigurationSpace: + """Build the configuration space which defines all parameters and their ranges for the SGD classifier.""" + cs = ConfigurationSpace(seed=0) + + # We define a few possible parameters for the SGD classifier + alpha = Float("alpha", (0, 1), default=1.0) + l1_ratio = Float("l1_ratio", (0, 1), default=0.5) + learning_rate = Categorical("learning_rate", ["constant", "invscaling", "adaptive"], default="constant") + eta0 = Float("eta0", (0.00001, 1), default=0.1, log=True) + + # Add the parameters to configuration space + cs.add_hyperparameters([alpha, l1_ratio, learning_rate, eta0]) + + return cs + + def train(self, config: Configuration, instance: str = "0-1", budget: float = 1, seed: int = 0) -> float: + """Creates a SGD classifier based on a configuration and evaluates it on the + digits dataset using cross-validation.""" + + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + + # SGD classifier using given configuration + clf = SGDClassifier( + loss="log", + penalty="elasticnet", + alpha=config["alpha"], + l1_ratio=config["l1_ratio"], + learning_rate=config["learning_rate"], + eta0=config["eta0"], + max_iter=budget, + early_stopping=True, + random_state=seed, + ) + + # Get instance + data, target = self.dataset.get_instance_data(instance) + + cv = StratifiedKFold(n_splits=4, random_state=seed, shuffle=True) # to make CV splits consistent + scores = cross_val_score(clf, data, target, cv=cv) + + return 1 - np.mean(scores) + + +@pytest.fixture +def make_sgd(): + def create(dataset: Dataset) -> SGD: + return SGD(dataset) + + return create diff --git a/tests/fixtures/runhistory.py b/tests/fixtures/runhistory.py new file mode 100644 index 000000000..00600fd21 --- /dev/null +++ b/tests/fixtures/runhistory.py @@ -0,0 +1,8 @@ +import pytest + +from smac import RunHistory + + +@pytest.fixture +def runhistory() -> RunHistory: + return RunHistory() diff --git a/tests/fixtures/scenario.py b/tests/fixtures/scenario.py new file mode 100644 index 000000000..2bcabe2f5 --- /dev/null +++ b/tests/fixtures/scenario.py @@ -0,0 +1,52 @@ +from typing import Callable + +import pytest +from ConfigSpace import ConfigurationSpace + +from smac import Scenario + + +@pytest.fixture +def make_scenario() -> Callable: + def _make( + configspace: ConfigurationSpace, + deterministic: bool = False, + use_multi_objective: bool = False, + use_instances: bool = False, + n_instances: int = 3, + n_instance_features: int = 3, + min_budget: int = 2, + max_budget: int = 5, + n_workers: int = 1, + n_trials: int = 100, + ) -> Scenario: + objectives = "cost" + if use_multi_objective: + objectives = ["cost1", "cost2"] + + instances = None + instance_features = None + if use_instances and n_instances > 0: + instances = [] + instance_features = {} + for i in range(n_instances): + instance_name = f"i{i+1}" + instances += [instance_name] + instance_features[instance_name] = [j + i for j in range(n_instance_features)] + + return Scenario( + configspace=configspace, + name="test", + output_directory="smac3_output_test", + objectives=objectives, + deterministic=deterministic, + walltime_limit=30, + n_trials=n_trials, + n_workers=n_workers, + instances=instances, + instance_features=instance_features, + min_budget=min_budget, + max_budget=max_budget, + ) + + return _make diff --git a/tests/fixtures/stats.py b/tests/fixtures/stats.py new file mode 100644 index 000000000..a4fefe838 --- /dev/null +++ b/tests/fixtures/stats.py @@ -0,0 +1,14 @@ +from typing import Callable + +import pytest + +from smac import Scenario +from smac.stats import Stats + + +@pytest.fixture +def make_stats() -> Callable: + def _make(scenario: Scenario) -> Scenario: + return Stats(scenario) + + return _make diff --git a/tests/test_acquisition/test_functions.py b/tests/test_acquisition/test_functions.py new file mode 100644 index 000000000..53c8b5f3a --- /dev/null +++ b/tests/test_acquisition/test_functions.py @@ -0,0 +1,760 @@ +from typing import Any + +import numpy as np +import pytest + +from smac.acquisition.function import ( + EI, + EIPS, + LCB, + PI, + TS, + IntegratedAcquisitionFunction, + PriorAcquisitionFunction, +) + +__copyright__ = "Copyright 2022, automl.org" +__license__ = "3-clause BSD" + + +class ConfigurationMock: + def __init__(self, values=None): + self.values = values + + def get_array(self): + return self.values + + +class MockModel: + def __init__(self, num_targets=1, seed=0): + self.num_targets = num_targets + self._seed = seed + self._rng = np.random.RandomState(self._seed) + + def predict_marginalized(self, X): + return np.array([np.mean(X, axis=1).reshape((1, -1))] * self.num_targets).reshape((-1, 1)), np.array( + [np.mean(X, axis=1).reshape((1, -1))] * self.num_targets + ).reshape((-1, 1)) + + +class MockModelDual: + def __init__(self, num_targets=1): + self.num_targets = num_targets + + def predict_marginalized(self, X): + return np.array([np.mean(X, axis=1).reshape((1, -1))] * self.num_targets).reshape((-1, 2)), np.array( + [np.mean(X, axis=1).reshape((1, -1))] * self.num_targets + ).reshape((-1, 2)) + + +class MockPrior: + def __init__(self, pdf, max_density): + self.pdf = pdf + self.max_density = max_density + + def _pdf(self, X): + return self.pdf(X) + + def get_max_density(self): + return self.max_density + + +class GetHPDict: + def __init__(self, hyperparameter_dict) -> None: + self._hyperparameter_dict = None + self._return_value = None + self.return_value = hyperparameter_dict + + @property + def hyperparameter_dict(self): + return self._hyperparameter_dict + + @hyperparameter_dict.setter + def hyperparameter_dict(self, value): + self._hyperparameter_dict = value + self._return_value = value + + @property + def return_value(self): + return self._return_value + + @return_value.setter + def return_value(self, value): + self._return_value = value + self._hyperparameter_dict = value + + def __call__(self, *args: Any, **kwds: Any) -> Any: + return self.hyperparameter_dict + + +class MockConfigurationSpace: + def __init__(self, hyperparameter_dict): + self.hyperparameter_dict = hyperparameter_dict + self.get_hyperparameters_dict = GetHPDict(hyperparameter_dict=hyperparameter_dict) + + +class PriorMockModel: + def __init__(self, hyperparameter_dict=None, num_targets=1, seed=0): + self.num_targets = num_targets + self._seed = seed + self._rng = np.random.RandomState(self._seed) + self._configspace = MockConfigurationSpace(hyperparameter_dict) + self.hyperparameter_dict = hyperparameter_dict + # since the PriorAcquisitionFunction needs to return the hyperparameters in dict + # form through two function calls (self.model.get_configspace().get_hyperparameters_dict()), + # we need a slightly intricate solution + self._configspace.get_hyperparameters_dict.return_value = self.hyperparameter_dict + + def get_configspace(self): + return self._configspace + + def predict_marginalized(self, X): + return np.array([np.mean(X, axis=1).reshape((1, -1))] * self.num_targets).reshape((-1, 1)), np.array( + [np.mean(X, axis=1).reshape((1, -1))] * self.num_targets + ).reshape((-1, 1)) + + def update_prior(self, hyperparameter_dict): + self._configspace.get_hyperparameters_dict.return_value = hyperparameter_dict + + +class MockModelRNG(MockModel): + def __init__(self, num_targets=1, seed=0): + self.num_targets = num_targets + self._seed = seed + self._rng = np.random.RandomState(self._seed) + + +class MockModelSampler(MockModelRNG): + def __init__(self, num_targets=1, seed=0): + self.num_targets = num_targets + self._seed = seed + self._rng = np.random.RandomState(seed) + + def sample_functions(self, X, n_funcs=1): + m = np.array([np.mean(X, axis=1).reshape((1, -1))] * self.num_targets).reshape((-1,)) + var = np.array([np.mean(X, axis=1).reshape((1, -1))] * self.num_targets).reshape((-1,)) + var = np.diag(var) + return self._rng.multivariate_normal(m, var, n_funcs).T + + +@pytest.fixture +def model(): + return MockModel() + + +@pytest.fixture +def acquisition_function(model): + ei = EI() + ei.model = model + + return ei + + +# -------------------------------------------------------------- +# Test AbstractAcquisitionFunction +# -------------------------------------------------------------- + + +def test_update_model_and_eta(model, acquisition_function): + model = "abc" + assert acquisition_function._eta is None + acquisition_function.update(model=model, eta=0.1) + assert acquisition_function.model == model + assert acquisition_function._eta == 0.1 + + +def test_update_with_kwargs(acquisition_function): + acquisition_function.update(model="abc", eta=0.0, other="hi there:)") + assert acquisition_function.model == "abc" + + +def test_update_without_required(acquisition_function): + with pytest.raises( + TypeError, + ): + acquisition_function.update(other=None) + + +# -------------------------------------------------------------- +# Test IntegratedAcquisitionFunction +# -------------------------------------------------------------- + + +@pytest.fixture +def multimodel(): + model = MockModel() + model.models = [MockModel()] * 3 + return model + + +@pytest.fixture +def acq_multi(multimodel, acquisition_function): + acquisition_function.model = multimodel + return acquisition_function + + +@pytest.fixture +def iaf(multimodel, acq_multi): + iaf = IntegratedAcquisitionFunction(acquisition_function=acq_multi) + iaf.model = multimodel + return iaf + + +def test_integrated_acquisition_function_update(iaf, model, multimodel): + iaf.update(model=multimodel, eta=2) + for func in iaf._functions: + assert func._eta == 2 + + with pytest.raises(ValueError) as exc_info: + iaf.update(model=MockModel()) + assert exc_info.value.args[0] == "IntegratedAcquisitionFunction requires at least one model to integrate!" + + with pytest.raises(ValueError) as exc_info: + iaf.model.models = [] + iaf.update(model=model) + assert exc_info.value.args[0] == "IntegratedAcquisitionFunction requires at least one model to integrate!" + + +def test_integrated_acquisition_function_compute(iaf, multimodel): + class CountingMock: + counter = 0 + long_name = "CountingMock" + + def _compute(self, *args, **kwargs): + self.counter += 1 + return self.counter + + def update(self, **kwargs): + pass + + iaf = IntegratedAcquisitionFunction(acquisition_function=CountingMock()) + iaf.update(model=multimodel) + configurations = [ConfigurationMock([1.0, 1.0, 1.0])] + rval = iaf(configurations) + assert rval == 1 + + # Test that every counting mock is called only once! + for counting_mock in iaf._functions: + assert counting_mock.counter == 1 + + +def test_integrated_acquisition_function_compute_with_different_numbers_of_models(iaf, multimodel): + for i in range(1, 3): + multimodel.models = [MockModel()] * i + iaf.update(model=multimodel, eta=1) + configurations = [ConfigurationMock([1.0, 1.0, 1.0])] + rval = iaf(configurations) + assert rval.shape == (1, 1) + + configurations = [ + ConfigurationMock([1.0, 1.0, 1.0]), + ConfigurationMock([1.0, 2.0, 3.0]), + ] + rval = iaf(configurations) + assert rval.shape == (2, 1) + + +# -------------------------------------------------------------- +# Test PriorAcquisitionFunction +# -------------------------------------------------------------- + + +@pytest.fixture +def x0_prior(): + return MockPrior(pdf=lambda x: 2 * x, max_density=2) + + +@pytest.fixture +def hyperparameter_dict(x0_prior): + return {"x0": x0_prior} + + +@pytest.fixture +def prior_model(hyperparameter_dict): + return PriorMockModel(hyperparameter_dict=hyperparameter_dict) + + +@pytest.fixture +def beta(): + return 2 + + +@pytest.fixture +def prior_floor(): + return 1e-1 + + +def test_prior_init_ei(prior_model, acquisition_function, beta): + acq_ei = acquisition_function + paf = PriorAcquisitionFunction(prior_model, acq_ei, beta) + assert paf._rescale is False + + +def test_prior_init_ts(prior_model, acq_ts, beta): + paf = PriorAcquisitionFunction(acquisition_function=acq_ts, decay_beta=beta) + paf.update(model=prior_model, eta=1, num_data=1) + assert paf._rescale is True + + +def test_prior_update(prior_model, acquisition_function, beta): + paf = PriorAcquisitionFunction(acquisition_function=acquisition_function, decay_beta=beta) + paf.update(model=prior_model, eta=2) + assert paf._eta == 2 + assert paf._acquisition_function._eta == 2 + assert paf._iteration_number == 1 + + +def test_prior_compute_prior_Nx1(prior_model, hyperparameter_dict, acquisition_function, beta): + prior_model.update_prior(hyperparameter_dict) + paf = PriorAcquisitionFunction(acquisition_function=acquisition_function, decay_beta=beta) + paf.update(model=prior_model, eta=1) + + X = np.array([0, 0.5, 1]).reshape(3, 1) + prior_values = paf._compute_prior(X) + + assert prior_values.shape == (3, 1) + assert prior_values[0][0] == 0 + assert prior_values[1][0] == 1 + assert prior_values[2][0] == 2 + + +def test_prior_compute_prior_NxD(prior_model, hyperparameter_dict, acquisition_function, beta): + prior_model.update_prior(hyperparameter_dict) + paf = PriorAcquisitionFunction(acquisition_function=acquisition_function, decay_beta=beta) + paf.update(model=prior_model, eta=1) + + X = np.array([[0, 0], [0, 1], [1, 1]]) + prior_values = paf._compute_prior(X) + + assert prior_values.shape == (3, 1) + assert prior_values[0][0] == 0 + assert prior_values[1][0] == 0 + assert prior_values[2][0] == 2 + + +def test_prior_compute_prior_1xD(prior_model, acquisition_function, beta): + x0_prior = MockPrior(pdf=lambda x: 2 * x, max_density=2) + x1_prior = MockPrior(pdf=lambda x: np.ones_like(x), max_density=1) + hyperparameter_dict = {"x0": x0_prior, "x1": x1_prior} + + prior_model.update_prior(hyperparameter_dict) + paf = PriorAcquisitionFunction(acquisition_function=acquisition_function, decay_beta=beta) + paf.update(model=prior_model, eta=1) + + X = np.array([[0.5, 0.5]]) + prior_values = paf._compute_prior(X) + + assert prior_values.shape == (1, 1) + assert prior_values[0][0] == 1 + + +def test_prior_compute_prior_1x1(prior_model, hyperparameter_dict, acquisition_function, beta): + prior_model.update_prior(hyperparameter_dict) + paf = PriorAcquisitionFunction(acquisition_function=acquisition_function, decay_beta=beta) + paf.update(model=prior_model, eta=1) + + X = np.array([0.5]).reshape(1, 1) + prior_values = paf._compute_prior(X) + + assert prior_values.shape == (1, 1) + assert prior_values[0][0] == 1 + + +@pytest.fixture +def x1_prior(): + return MockPrior(pdf=lambda x: np.ones_like(x), max_density=1) + + +@pytest.fixture +def x2_prior(): + return MockPrior(pdf=lambda x: 2 - 2 * x, max_density=2) + + +@pytest.fixture +def hp_dict3(x0_prior, x1_prior, x2_prior): + return {"x0": x0_prior, "x1": x1_prior, "x2": x2_prior} + + +def test_prior_1xD(hp_dict3, prior_model, acquisition_function, beta, prior_floor): + prior_model.update_prior(hp_dict3) + paf = PriorAcquisitionFunction(acquisition_function=acquisition_function, decay_beta=beta, prior_floor=prior_floor) + paf.update(model=prior_model, eta=1.0) + configurations = [ConfigurationMock([1.0, 1.0, 1.0])] + acq = paf(configurations) + assert acq.shape == (1, 1) + + prior_0_factor = np.power(2.0 * 1.0 * 0.0 + paf._prior_floor, beta / 1.0) + + assert np.isclose(acq[0][0], 0.3989422804014327 * prior_0_factor) + + +def test_prior_NxD(hp_dict3, prior_model, acquisition_function, beta, prior_floor): + prior_model.update_prior(hp_dict3) + paf = PriorAcquisitionFunction(acquisition_function=acquisition_function, decay_beta=beta, prior_floor=prior_floor) + paf.update(model=prior_model, eta=1.0) + + # These are the exact same numbers as in the EI tests below + configurations = [ + ConfigurationMock([0.0, 0.0, 0.0]), + ConfigurationMock([0.1, 0.1, 0.1]), + ConfigurationMock([1.0, 1.0, 1.0]), + ] + acq = paf(configurations) + assert acq.shape == (3, 1) + + prior_0_factor = np.power(0.0 * 1.0 * 2.0 + paf._prior_floor, beta / 1.0) + prior_1_factor = np.power(0.2 * 1.0 * 1.8 + paf._prior_floor, beta / 1.0) + prior_2_factor = np.power(2.0 * 1.0 * 0.0 + paf._prior_floor, beta / 1.0) + + # We do only one update, so we are at iteration 1 (beta/iteration_nbr=2) + assert np.isclose(acq[0][0], 0.0 * prior_0_factor) + assert np.isclose(acq[1][0], 0.90020601136712231 * prior_1_factor) + assert np.isclose(acq[2][0], 0.3989422804014327 * prior_2_factor) + + +def test_prior_NxD_TS(prior_model, hp_dict3, acq_ts, beta, prior_floor): + prior_model.update_prior(hp_dict3) + paf = PriorAcquisitionFunction(acquisition_function=acq_ts, decay_beta=beta, prior_floor=prior_floor) + + eta = 1.0 + paf.update(model=prior_model, eta=eta, num_data=1) + + configurations = [ + ConfigurationMock([0.0001, 0.0001, 0.0001]), + ConfigurationMock([0.1, 0.1, 0.1]), + ConfigurationMock([1.0, 1.0, 1.0]), + ] + acq = paf(configurations) + assert acq.shape == (3, 1) + + # retrieved from TS example + ts_value_0 = -0.00988738 + ts_value_1 = -0.22654082 + ts_value_2 = -2.76405235 + + prior_0_factor = np.power(0.0002 * 1 * 1.9998 + paf._prior_floor, beta / 1.0) + prior_1_factor = np.power(0.2 * 1.0 * 1.8 + paf._prior_floor, beta / 1.0) + prior_2_factor = np.power(2.0 * 1.0 * 0.0 + paf._prior_floor, beta / 1.0) + + # rescaling to avoid negative values, and keep the TS ranking intact + combined_value_0 = np.clip(ts_value_0 + eta, 0, np.inf) * prior_0_factor + combined_value_1 = np.clip(ts_value_1 + eta, 0, np.inf) * prior_1_factor + combined_value_2 = np.clip(ts_value_2 + eta, 0, np.inf) * prior_2_factor + + assert np.isclose(acq[0][0], combined_value_0) + assert np.isclose(acq[1][0], combined_value_1) + assert np.isclose(acq[2][0], combined_value_2) + + +def test_prior_decay(hp_dict3, prior_model, acquisition_function, beta, prior_floor): + prior_model.update_prior(hp_dict3) + paf = PriorAcquisitionFunction(acquisition_function=acquisition_function, decay_beta=beta, prior_floor=prior_floor) + paf.update(model=prior_model, eta=1.0) + configurations = [ConfigurationMock([0.1, 0.1, 0.1])] + + for i in range(1, 6): + prior_factor = np.power(0.2 * 1.0 * 1.8 + paf._prior_floor, beta / i) + acq = paf(configurations) + print(acq, 0.90020601136712231 * prior_factor) + assert np.isclose(acq[0][0], 0.90020601136712231 * prior_factor) + paf.update(model=prior_model, eta=1.0) # increase iteration number + + +def test_prior_discretize_pdf(prior_model, acquisition_function, beta, prior_floor): + x0_prior = MockPrior(pdf=lambda x: 2 * x, max_density=2) + hyperparameter_dict = {"x0": x0_prior} + prior_model.update_prior(hyperparameter_dict) + paf = PriorAcquisitionFunction( + acquisition_function=acquisition_function, decay_beta=beta, prior_floor=prior_floor, discretize=True + ) + paf.update(model=prior_model, eta=1) + + number_of_bins_1 = 13 + number_of_bins_2 = 27521 + number_of_points = 1001 + + discrete_values_1 = paf._compute_discretized_pdf( + x0_prior, np.linspace(0, 1, number_of_points), number_of_bins=number_of_bins_1 + ) + discrete_values_2 = paf._compute_discretized_pdf( + x0_prior, np.linspace(0, 1, number_of_points), number_of_bins=number_of_bins_2 + ) + number_unique_values_1 = len(np.unique(discrete_values_1)) + number_unique_values_2 = len(np.unique(discrete_values_2)) + + assert number_unique_values_1 == number_of_bins_1 + assert number_unique_values_2 == number_of_points + with pytest.raises(ValueError): + paf._compute_discretized_pdf(x0_prior, np.linspace(0, 1, number_of_points), number_of_bins=-1) + + +# -------------------------------------------------------------- +# Test EI +# -------------------------------------------------------------- + + +def test_ei_1xD(model, acquisition_function): + ei = acquisition_function + ei.update(model=model, eta=1.0) + configurations = [ConfigurationMock([1.0, 1.0, 1.0])] + acq = ei(configurations) + assert acq.shape == (1, 1) + assert np.isclose(acq[0][0], 0.3989422804014327) + + +def test_ei_NxD(model, acquisition_function): + ei = acquisition_function + ei.update(model=model, eta=1.0) + configurations = [ + ConfigurationMock([0.0, 0.0, 0.0]), + ConfigurationMock([0.1, 0.1, 0.1]), + ConfigurationMock([1.0, 1.0, 1.0]), + ] + acq = ei(configurations) + assert acq.shape == (3, 1) + assert np.isclose(acq[0][0], 0.0) + assert np.isclose(acq[1][0], 0.90020601136712231) + assert np.isclose(acq[2][0], 0.3989422804014327) + + +def test_ei_1x1(model, acquisition_function): + ei = acquisition_function + ei.update(model=model, eta=1.0) + configurations = [ConfigurationMock([1.0])] + acq = ei(configurations) + assert acq.shape == (1, 1) + assert np.isclose(acq[0][0], 0.3989422804014327) + + +def test_ei_Nx1(model, acquisition_function): + ei = acquisition_function + ei.update(model=model, eta=1.0) + configurations = [ + ConfigurationMock([0.0001]), + ConfigurationMock([1.0]), + ConfigurationMock([2.0]), + ] + acq = ei(configurations) + assert acq.shape == (3, 1) + assert np.isclose(acq[0][0], 0.9999) + assert np.isclose(acq[1][0], 0.3989422804014327) + assert np.isclose(acq[2][0], 0.19964122837424575) + + +def test_ei_zero_variance(model, acquisition_function): + ei = acquisition_function + ei.update(model=model, eta=1.0) + X = np.array([ConfigurationMock([0.0])]) + acq = np.array(ei(X)) + assert np.isclose(acq[0][0], 0.0) + + +# -------------------------------------------------------------- +# Test EIPS +# -------------------------------------------------------------- + + +@pytest.fixture +def model_eips(): + return MockModelDual() + + +@pytest.fixture +def acq_eips(model_eips): + ei = EIPS() + ei.model = model_eips + return ei + + +def test_eips_1xD(model_eips, acq_eips): + ei = acq_eips + ei.update(model=model_eips, eta=1.0) + configurations = [ConfigurationMock([1.0, 1.0]), ConfigurationMock([1.0, 1.0])] + acq = ei(configurations) + assert acq.shape == (1, 1) + assert np.isclose(acq[0][0], 0.3989422804014327) + + +def test_eips_fail(model_eips, acq_eips): + ei = acq_eips + with pytest.raises(ValueError): + configurations = [ConfigurationMock([1.0, 1.0])] + ei(configurations) + + +@pytest.fixture +def acq_logei(model): + ei = EI(log=True) + ei.model = model + return ei + + +def test_logei_1xD(model, acq_logei): + ei = acq_logei + ei.update(model=model, eta=1.0) + configurations = [ConfigurationMock([1.0, 1.0, 1.0])] + acq = ei(configurations) + assert acq.shape == (1, 1) + assert np.isclose(acq[0][0], 0.6480973967332011) + + +def test_logei_NxD(model, acq_logei): + ei = acq_logei + ei.update(model=model, eta=1.0) + configurations = [ + ConfigurationMock([0.1, 0.0, 0.0]), + ConfigurationMock([0.1, 0.1, 0.1]), + ConfigurationMock([1.0, 1.0, 1.0]), + ] + acq = ei(configurations) + assert acq.shape == (3, 1) + assert np.isclose(acq[0][0], 1.6670107375002425) + assert np.isclose(acq[1][0], 1.5570607606556273) + assert np.isclose(acq[2][0], 0.6480973967332011) + + +# -------------------------------------------------------------- +# Test PI +# -------------------------------------------------------------- + + +@pytest.fixture +def acq_pi(model): + pi = PI() + pi.model = model + return pi + + +def test_pi_1xD(model, acq_pi): + ei = acq_pi + ei.update(model=model, eta=1.0) + configurations = [ConfigurationMock([0.5, 0.5, 0.5])] + acq = ei(configurations) + assert acq.shape == (1, 1) + assert np.isclose(acq[0][0], 0.7602499389065233) + + +def test_pi_1xD_zero(model, acq_pi): + ei = acq_pi + ei.update(model=model, eta=1.0) + configurations = [ConfigurationMock([100, 100, 100])] + acq = ei(configurations) + assert acq.shape == (1, 1) + assert np.isclose(acq[0][0], 0) + + +def test_pi_NxD(model, acq_pi): + ei = acq_pi + ei.update(model=model, eta=1.0) + configurations = [ + ConfigurationMock([0.0001, 0.0001, 0.0001]), + ConfigurationMock([0.1, 0.1, 0.1]), + ConfigurationMock([1.0, 1.0, 1.0]), + ] + acq = ei(configurations) + assert acq.shape == (3, 1) + assert np.isclose(acq[0][0], 1.0) + assert np.isclose(acq[1][0], 0.99778673707104) + assert np.isclose(acq[2][0], 0.5) + + +# -------------------------------------------------------------- +# Test LCB +# -------------------------------------------------------------- + + +@pytest.fixture +def acq_lcb(model): + lcb = LCB() + lcb.model = model + return lcb + + +def test_lcb_1xD(model, acq_lcb): + ei = acq_lcb + ei.update(model=model, eta=1.0, par=1, num_data=3) + configurations = [ConfigurationMock([0.5, 0.5, 0.5])] + acq = ei(configurations) + assert acq.shape == (1, 1) + assert np.isclose(acq[0][0], 1.315443985917585) + ei.update(model=model, eta=1.0, par=1, num_data=100) + configurations = [ConfigurationMock([0.5, 0.5, 0.5])] + acq = ei(configurations) + assert acq.shape == (1, 1) + assert np.isclose(acq[0][0], 2.7107557771721433) + + +def test_lcb_1xD_no_improvement_vs_improvement(model, acq_lcb): + ei = acq_lcb + ei.update(model=model, par=1, num_data=1) + configurations = [ConfigurationMock([100, 100])] + acq = ei(configurations) + assert acq.shape == (1, 1) + assert np.isclose(acq[0][0], -88.22589977) + configurations = [ConfigurationMock([0.001, 0.001])] + acq = ei(configurations) + assert acq.shape == (1, 1) + assert np.isclose(acq[0][0], 0.03623297) + + +def test_lcb_NxD(model, acq_lcb): + ei = acq_lcb + ei.update(model=model, eta=1.0, num_data=100) + configurations = [ + ConfigurationMock([0.0001, 0.0001, 0.0001]), + ConfigurationMock([0.1, 0.1, 0.1]), + ConfigurationMock([1.0, 1.0, 1.0]), + ] + acq = ei(configurations) + assert acq.shape == (3, 1) + assert np.isclose(acq[0][0], 0.045306943655446116) + assert np.isclose(acq[1][0], 1.3358936353814157) + assert np.isclose(acq[2][0], 3.5406943655446117) + + +# -------------------------------------------------------------- +# Test TS +# -------------------------------------------------------------- + + +@pytest.fixture +def acq_ts(model): + ts = TS() + ts.model = model + return ts + + +def test_ts_1xD(model, acq_ts): + ei = acq_ts + configurations = [ConfigurationMock([0.5, 0.5, 0.5])] + acq = ei(configurations) + assert acq.shape == (1, 1) + assert np.isclose(acq[0][0], -1.74737338) + + +def test_ts_NxD(model, acq_ts): + ei = acq_ts + configurations = [ + ConfigurationMock([0.0001, 0.0001, 0.0001]), + ConfigurationMock([0.1, 0.1, 0.1]), + ConfigurationMock([1.0, 1.0, 1.0]), + ] + acq = ei(configurations) + assert acq.shape == (3, 1) + assert np.isclose(acq[0][0], -0.00988738) + assert np.isclose(acq[1][0], -0.22654082) + assert np.isclose(acq[2][0], -2.76405235) + + +def test_ts_rng(): + """Test TS acqusition function with model that only has attribute 'rng'""" + model = MockModelRNG() + ts = TS() + ts.model = model + + +def test_ts_sampler(): + "Test TS acqusition function with model that only has attribute 'sample_functions'" + model = MockModelSampler() + ts = TS() + ts.model = model diff --git a/tests/test_acquisition/test_maximizers.py b/tests/test_acquisition/test_maximizers.py new file mode 100644 index 000000000..bb9e70e1e --- /dev/null +++ b/tests/test_acquisition/test_maximizers.py @@ -0,0 +1,432 @@ +from __future__ import annotations + +from typing import Any + +import os +import unittest +import unittest.mock + +import numpy as np +import pytest +from ConfigSpace import ( + Categorical, + Configuration, + ConfigurationSpace, + EqualsCondition, + Float, + InCondition, + Integer, +) +from ConfigSpace.hyperparameters import ( + BetaIntegerHyperparameter, + CategoricalHyperparameter, + NormalFloatHyperparameter, + UniformFloatHyperparameter, + UniformIntegerHyperparameter, +) +from ConfigSpace.read_and_write import pcs +from scipy.spatial.distance import euclidean + +from smac.acquisition.function import EI +from smac.acquisition.maximizer import ( + DifferentialEvolution, + LocalAndSortedPriorRandomSearch, + LocalAndSortedRandomSearch, + LocalSearch, + RandomSearch, +) +from smac.model.random_forest.random_forest import RandomForest +from smac.runhistory.runhistory import RunHistory +from smac.runner.abstract_runner import StatusType + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +class ConfigurationMock(object): + def __init__(self, value=None): + self.value = value + + def get_array(self): + return [self.value] + + +@pytest.fixture +def configspace_branin() -> ConfigurationSpace: + """Returns the branin configspace.""" + cs = ConfigurationSpace() + cs.add_hyperparameter(Float("x", (-5, 10))) + cs.add_hyperparameter(Float("y", (0, 15))) + return cs + + +def rosenbrock_4d(cfg): + x1 = cfg["x1"] + x2 = cfg["x2"] + x3 = cfg["x3"] + x4 = cfg["x4"] + + val = ( + 100 * (x2 - x1**2) ** 2 + + (x1 - 1) ** 2 + + 100 * (x3 - x2**2) ** 2 + + (x2 - 1) ** 2 + + 100 * (x4 - x3**2) ** 2 + + (x3 - 1) ** 2 + ) + + return val + + +class MockEI(EI): + def __init__(self, values: list, **kwargs): + super().__init__(**kwargs) + self.values = values + + def __call__(self, configurations: list) -> np.ndarray: + return np.array([[_] for _ in self.values], dtype=float) + + +class MockConfigurationSpace(object): + def sample_configuration(self, size: int = 1) -> ConfigurationMock: + if size == 1: + ret = ConfigurationMock() + else: + ret = [ConfigurationMock()] * size + + return ret + + +class MockRandomSearch(RandomSearch): + def _maximize(self, *args, **kwargs): + return [(0, 0)] + + +class MethodCallLogger(object): + def __init__(self, meth): + self.meth = meth + self.call_count = 0 + + def __call__(self, *args): + self.call_count += 1 + return self.meth(*args) + + +def test_ei_maximization_challenger_list_callback(): + values = (10, 1, 9, 2, 8, 3, 7, 4, 6, 5) + cs = MockConfigurationSpace() + ei = MockEI(values=values) + rs = MockRandomSearch(configspace=cs, acquisition_function=ei) + rs._maximize = MethodCallLogger(rs._maximize) + + rval = rs.maximize( + previous_configs=[], + n_points=10, + ) + assert rs._maximize.call_count == 0 + next(rval) + assert rs._maximize.call_count == 1 + + random_design = unittest.mock.Mock() + random_design.check.side_effect = [True, False, False, False] + rs._maximize = unittest.mock.Mock() + rs._maximize.return_value = [(0, 0), (1, 1)] + + rval = rs.maximize( + previous_configs=[], + n_points=10, + random_design=random_design, + ) + assert rs._maximize.call_count == 0 + + # The first configuration is chosen at random (see the random_configuration_chooser mock) + conf = next(rval) + assert isinstance(conf, ConfigurationMock) + assert rs._maximize.call_count == 0 + + # The 2nd configuration triggers the call to the callback (see the random_configuration_chooser mock) + conf = next(rval) + assert rs._maximize.call_count == 1 + assert conf == 0 + + # The 3rd configuration doesn't trigger the callback any more + conf = next(rval) + assert rs._maximize.call_count == 1 + assert conf == 1 + + with pytest.raises(StopIteration): + next(rval) + + +def test_ei_maximization_get_next_by_random_search(): + cs = MockConfigurationSpace() + ei = EI(None) + rs = RandomSearch(configspace=cs, acquisition_function=ei) + rval = rs._maximize(previous_configs=[], n_points=10, _sorted=False) + assert len(rval) == 10 + for i in range(10): + assert isinstance(rval[i][1], ConfigurationMock) + assert rval[i][1].origin == "Random Search" + assert rval[i][0] == 0 + + +def test_get_next_by_random_search(): + cs = ConfigurationSpace() + ei = EI(None) + rs = RandomSearch(configspace=cs, acquisition_function=ei) + rval = rs._maximize(previous_configs=[], n_points=10, _sorted=False) + assert len(rval) == 10 + for i in range(10): + assert isinstance(rval[i][1], Configuration) + assert rval[i][1].origin == "Random Search" + assert rval[i][0] == 0 + + +# -------------------------------------------------------------- +# TestLocalSearch +# -------------------------------------------------------------- + + +@pytest.fixture +def configspace() -> ConfigurationSpace: + cs = ConfigurationSpace(seed=0) + + a = Float("a", (0, 1), default=0.5) + b = Float("b", (0, 1), default=0.5) + c = Float("c", (0, 1), default=0.5) + + # Add all hyperparameters at once: + cs.add_hyperparameters([a, b, c]) + + return cs + + +@pytest.fixture +def model(configspace: ConfigurationSpace): + model = RandomForest(configspace) + + np.random.seed(0) + X = np.random.rand(100, len(configspace.get_hyperparameters())) + y = 1 - (np.sum(X, axis=1) / len(configspace.get_hyperparameters())) + model.train(X, y) + + return model + + +@pytest.fixture +def acquisition_function(model): + ei = EI() + ei.update(model=model, eta=0.5) + + return ei + + +def test_local_search(configspace): + def acquisition_function(points): + rval = [] + for point in points: + opt = np.array([1, 1, 1]) + rval.append([-euclidean(point.get_array(), opt)]) + + return np.array(rval) + + ls = LocalSearch(configspace, acquisition_function, max_steps=100) + + start_point = configspace.sample_configuration() + acq_val_start_point = acquisition_function([start_point]) + acq_val_incumbent, _ = ls._search(start_point)[0] + + # Local search needs to find something that is as least as good as the + # start point + assert acq_val_start_point < acq_val_incumbent + + +def test_local_search_2(configspace, acquisition_function): + start_points = configspace.sample_configuration(100) + ls = LocalSearch(configspace, acquisition_function, max_steps=1000) + + start_point = start_points[0] + _, incumbent = ls._maximize([start_point], 1, None)[0] + + assert incumbent.origin == "Local Search" + assert start_point != incumbent + + # Check if they are sorted + values = ls._maximize(start_points, 100, None) + assert values[0][0] > values[-1][0] + assert values[0][0] >= values[1][0] + + +def test_get_initial_points_moo(configspace): + class Model: + def predict_marginalized(self, X): + return X, X + + class AcquisitionFunction: + + model = Model() + + def __call__(self, X): + return np.array([x.get_array().sum() for x in X]).reshape((-1, 1)) + + ls = LocalSearch( + configspace=configspace, + acquisition_function=AcquisitionFunction(), + n_steps_plateau_walk=10, + max_steps=np.inf, + ) + + random_configs = configspace.sample_configuration(size=100) + points = ls._get_initial_points(random_configs, n_points=5, additional_start_points=None) + assert len(points) == 10 + + +# -------------------------------------------------------------- +# TestRandomSearch +# -------------------------------------------------------------- + + +def test_random_search(configspace, acquisition_function): + start_points = configspace.sample_configuration(100) + rs = RandomSearch(configspace, acquisition_function, challengers=1000) + + start_point = start_points[0] + _, incumbent = rs._maximize([start_point], 1, None)[0] + + assert incumbent.origin == "Random Search" + assert start_point != incumbent + + # Check if they all are 0 (because we don't get a maximize value for random search) + values = rs._maximize(start_points, 100) + assert values[0][0] == 0 + assert values[0][0] == values[-1][0] + assert values[0][0] == values[1][0] + assert all([v[0] == 0 for v in values]) + + +def test_random_search_sorted(configspace, acquisition_function): + start_points = configspace.sample_configuration(100) + rs = RandomSearch(configspace, acquisition_function, challengers=1000) + + start_point = start_points[0] + _, incumbent = rs._maximize([start_point], 1, _sorted=True)[0] + + assert incumbent.origin == "Random Search (sorted)" + assert start_point != incumbent + + # Check if they are higher than 0 (because we sorted them) + values = rs._maximize(start_points, 100, _sorted=True) + assert all([v[0] > 0 for v in values]) + + +# -------------------------------------------------------------- +# TestLocalAndRandomSearch +# -------------------------------------------------------------- + + +def test_local_and_random_search(configspace, acquisition_function): + start_points = configspace.sample_configuration(100) + rs = LocalAndSortedRandomSearch(configspace, acquisition_function, challengers=1000) + assert rs.acquisition_function == acquisition_function + assert rs._random_search.acquisition_function == acquisition_function + assert rs._local_search.acquisition_function == acquisition_function + + values = rs._maximize(start_points, 100) + config_origins = [] + v_old = np.inf + for (v, config) in values: + config_origins += [config.origin] + if isinstance(v, np.ndarray): + v = float(v[0]) + + assert v_old >= v + v_old = v + + assert "Random Search (sorted)" in config_origins + assert "Local Search" in config_origins + + +# -------------------------------------------------------------- +# TestLocalAndSortedPriorRandomSearch +# -------------------------------------------------------------- + + +@pytest.fixture +def configspace_rosenbrock(): + seed = 1 + uniform_cs = ConfigurationSpace(seed=seed) + x1 = UniformFloatHyperparameter("x1", -5, 5, default_value=5) + x2 = UniformIntegerHyperparameter("x2", -5, 5, default_value=5) + x3 = CategoricalHyperparameter("x3", [5, 2, 0, 1, -1, -2, 4, -3, 3, -5, -4], default_value=5) + x4 = UniformIntegerHyperparameter("x4", -5, 5, default_value=5) + uniform_cs.add_hyperparameters([x1, x2, x3, x4]) + + return uniform_cs + + +@pytest.fixture +def configspace_prior(): + seed = 1 + + prior_cs = ConfigurationSpace(seed=seed) + x1 = NormalFloatHyperparameter("x1", lower=-5, upper=5, mu=0, sigma=1e-3, default_value=5) + x2 = BetaIntegerHyperparameter("x2", lower=-5, upper=5, alpha=100, beta=1, default_value=5) + x3 = CategoricalHyperparameter( + "x3", [5, 2, 0, 1, -1, -2, 4, -3, 3, -5, -4], default_value=5, weights=[999, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + ) + x4 = UniformIntegerHyperparameter("x4", lower=-5, upper=5, default_value=5) + prior_cs.add_hyperparameters([x1, x2, x3, x4]) + + return prior_cs + + +def test_sampling_fractions(configspace_rosenbrock, configspace_prior): + class AcquisitionFunction: + def __call__(self, arrays): + rval = [] + for array in arrays: + rval.append([-rosenbrock_4d(array)]) + return np.array(rval) + + budget_kwargs = {"max_steps": 2, "n_steps_plateau_walk": 2, "local_search_iterations": 2} + + prs_0 = LocalAndSortedPriorRandomSearch( + configspace_prior, + configspace_rosenbrock, + AcquisitionFunction(), + prior_sampling_fraction=0, + **budget_kwargs, + ) + + prs_05 = LocalAndSortedPriorRandomSearch( + configspace_prior, + configspace_rosenbrock, + AcquisitionFunction(), + prior_sampling_fraction=0.9, + **budget_kwargs, + ) + + prs_1 = LocalAndSortedPriorRandomSearch( + configspace_prior, + configspace_rosenbrock, + AcquisitionFunction(), + prior_sampling_fraction=1, + **budget_kwargs, + ) + + prs_0._maximize(previous_configs=[], n_points=10) + prs_05._maximize(previous_configs=[], n_points=10) + prs_1._maximize(previous_configs=[], n_points=10) + + +# -------------------------------------------------------------- +# TestDifferentialEvolution +# -------------------------------------------------------------- + + +def test_differential_evolution(configspace, acquisition_function): + start_points = configspace.sample_configuration(100) + rs = DifferentialEvolution(configspace, acquisition_function, challengers=1000) + + values = rs._maximize(start_points, 1) + values[0][1].origin == "Differential Evolution" diff --git a/tests/test_ask_and_tell/test_hyperband.py b/tests/test_ask_and_tell/test_hyperband.py new file mode 100644 index 000000000..8583f69d6 --- /dev/null +++ b/tests/test_ask_and_tell/test_hyperband.py @@ -0,0 +1,231 @@ +from __future__ import annotations + +import pytest + +from smac import MultiFidelityFacade, Scenario +from smac.runhistory.dataclasses import TrialInfo, TrialValue + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +@pytest.fixture +def make_facade(digits_dataset, make_sgd) -> MultiFidelityFacade: + def create(deterministic=True, use_instances=False, n_seeds=2) -> MultiFidelityFacade: + model = make_sgd(digits_dataset) + + instances_kwargs = {} + if use_instances: + instances_kwargs = { + "instances": digits_dataset.get_instances(), + "instance_features": digits_dataset.get_instance_features(), + } + + scenario = Scenario( + model.configspace, + deterministic=deterministic, + n_trials=15, # We want to try max 5000 different configurations + min_budget=1, # Use min one instance + max_budget=9, # Use max 45 instances (if we have a lot of instances we could constraint it) + **instances_kwargs, + ) + + # Create our SMAC object and pass the scenario and the train method + smac = MultiFidelityFacade( + scenario, + model.train, + initial_design=MultiFidelityFacade.get_initial_design(scenario, n_configs=2, max_ratio=1), + intensifier=MultiFidelityFacade.get_intensifier(scenario, n_seeds=n_seeds, eta=3), + logging_level=0, + overwrite=True, + ) + + return model, smac + + return create + + +# -------------------------------------------------------------- +# Test target function arguments +# -------------------------------------------------------------- + + +def test_target_function_arguments_deterministic(make_facade): + """Tests whether we get the right arguments.""" + model, smac = make_facade( + deterministic=True, + use_instances=False, + n_seeds=1, # Multiple seeds are not supported + ) + + # The intensifier should have 3 budgets (because of SH) + budgets = smac.get_target_function_budgets() + assert budgets == [1.0, 3.0, 9.0] + + # The intensifier should not use any instances + instances = smac.get_target_function_instances() + assert instances == [None] + + # However, we expect ONE seeds (deterministic), although we have max_config_calls=5 + seeds = smac.get_target_function_seeds() + assert seeds == [0] + + +def test_target_function_arguments_deterministic_instances(make_facade): + """Tests whether we get the right arguments.""" + model, smac = make_facade(deterministic=True, use_instances=True) + + # The intensifier should not use any budgets as we use instances instead + budgets = smac.get_target_function_budgets() + assert budgets == [None] + + # The intensifier should have 45 instances + instances = smac.get_target_function_instances() + assert len(instances) == 45 + + # However, we expect ONE seeds (deterministic), although we have max_config_calls=5 + seeds = smac.get_target_function_seeds() + assert seeds == [0] + + +def test_target_function_arguments_non_deterministic(make_facade): + """Tests whether we get the right arguments.""" + model, smac = make_facade(deterministic=False, use_instances=False, n_seeds=1) + + # The intensifier should not use any budgets + budgets = smac.get_target_function_budgets() + assert budgets == [1.0, 3.0, 9.0] + + # The intensifier should not use any instances + instances = smac.get_target_function_instances() + assert instances == [None] + + # However, we expect ONE seeds (deterministic), although we have max_config_calls=5 + seeds = smac.get_target_function_seeds() + assert len(seeds) == 1 + + +def test_target_function_arguments_non_deterministic_instances(make_facade): + """Tests whether we get the right arguments.""" + model, smac = make_facade(deterministic=False, use_instances=True, n_seeds=38) + + # The intensifier should not use any budgets + budgets = smac.get_target_function_budgets() + assert budgets == [None] + + # The intensifier should not use any instances + instances = smac.get_target_function_instances() + assert len(instances) == 45 + + # However, we expect ONE seeds (deterministic), although we have max_config_calls=5 + seeds = smac.get_target_function_seeds() + assert len(seeds) == 38 + + +# -------------------------------------------------------------- +# Test tell without ask +# -------------------------------------------------------------- + + +def test_tell_without_ask(make_facade): + """Tests whether tell works without ask. In the case of ``intensifier`` it should work.""" + model, smac = make_facade(deterministic=False, use_instances=True) + seed = smac.get_target_function_seeds()[0] + budget = smac.get_target_function_budgets()[0] + instance = smac.get_target_function_instances()[0] + + # We can provide SMAC with custom configurations first + for config in model.configspace.sample_configuration(10): + cost = model.train(config, seed=seed, instance=instance) + trial_info = TrialInfo(config, seed=seed, instance=instance) + trial_value = TrialValue(cost=cost, time=0.5) + + # SH does not support calling without ask + with pytest.raises(RuntimeError): + smac.tell(trial_info, trial_value) + + +# -------------------------------------------------------------- +# Test ask and tell +# -------------------------------------------------------------- + + +def test_ask_and_tell(make_facade): + """Tests whether ask followed by a tell works. We can not ensure the same results when using + ask and tell and optimize.""" + model, smac = make_facade(deterministic=False, use_instances=True) + smac.optimize() + + # for i in range(smac._scenario.n_trials): + # trial_info = smac.ask() + + # cost = model.train(trial_info.config, seed=trial_info.seed, instance=trial_info.instance) + # trial_value = TrialValue(cost=cost, time=0.5) + + # smac.tell(trial_info, trial_value) + + # # Seed is not supported + # with pytest.raises(ValueError): + # trial_info = TrialInfo(trial_info.config, seed=23412351234) + # trial_value = TrialValue(cost=cost, time=0.5) + + # smac.tell(trial_info, trial_value) + + # # Instance is not supported + # with pytest.raises(ValueError): + # trial_info = TrialInfo(trial_info.config, instance=None) + # trial_value = TrialValue(cost=cost, time=0.5) + + # smac.tell(trial_info, trial_value) + + # # Budget is not supported + # with pytest.raises(ValueError): + # trial_info = TrialInfo(trial_info.config, budget=4.2) + # trial_value = TrialValue(cost=cost, time=0.5) + + # smac.tell(trial_info, trial_value) + + assert smac.incumbent is not None + + +# def test_ask_and_tell_after_optimization(make_facade): +# """ +# Tests whether ask followed by a tell works after the optimization. +# """ +# model, smac = make_facade(deterministic=False, use_instances=True) +# smac.optimize() +# trials = len(smac.runhistory) + +# for _ in range(10): +# trial_info = smac.ask() + +# cost = model.train(trial_info.config, seed=trial_info.seed, instance=trial_info.instance) +# trial_value = TrialValue(cost=cost, time=0.5) + +# smac.tell(trial_info, trial_value) + +# # We should have more entries in the runhistory now +# assert trials < len(smac.runhistory) + + +# # -------------------------------------------------------------- +# # Test multiple asks successively +# # -------------------------------------------------------------- + + +# def test_multiple_asks_successively(make_facade): +# """ +# model, smac = make_facade(deterministic=False, use_instances=True) + +# info = [] +# for i in range(10): +# trial_info = smac.ask() + +# # Make sure the trials are different +# assert trial_info not in info + +# info += [trial_info] +# """ + +# # Not supported yet +# pass diff --git a/tests/test_ask_and_tell/test_intensifier.py b/tests/test_ask_and_tell/test_intensifier.py new file mode 100644 index 000000000..a6fef1ac1 --- /dev/null +++ b/tests/test_ask_and_tell/test_intensifier.py @@ -0,0 +1,233 @@ +from __future__ import annotations + +import pytest + +from smac import HyperparameterOptimizationFacade, Scenario +from smac.runhistory.dataclasses import TrialInfo, TrialValue + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +@pytest.fixture +def make_facade(digits_dataset, make_sgd) -> HyperparameterOptimizationFacade: + def create(deterministic=True, use_instances=False) -> HyperparameterOptimizationFacade: + model = make_sgd(digits_dataset) + + instances_kwargs = {} + if use_instances: + instances_kwargs = { + "instances": digits_dataset.get_instances(), + "instance_features": digits_dataset.get_instance_features(), + } + + scenario = Scenario( + model.configspace, + deterministic=deterministic, + n_trials=15, # We want to try max 5000 different configurations + min_budget=1, # Use min one instance + max_budget=45, # Use max 45 instances (if we have a lot of instances we could constraint it) + **instances_kwargs, + ) + + # Create our SMAC object and pass the scenario and the train method + smac = HyperparameterOptimizationFacade( + scenario, + model.train, + initial_design=HyperparameterOptimizationFacade.get_initial_design(scenario, n_configs=2, max_ratio=1), + intensifier=HyperparameterOptimizationFacade.get_intensifier( + scenario, max_config_calls=5, intensify_percentage=0.0 + ), + logging_level=0, + overwrite=True, + ) + + return model, smac + + return create + + +# -------------------------------------------------------------- +# Test target function arguments +# -------------------------------------------------------------- + + +def test_target_function_arguments_deterministic(make_facade): + """Tests whether we get the right arguments.""" + model, smac = make_facade(deterministic=True) + + # The intensifier should not use any budgets + budgets = smac.get_target_function_budgets() + assert budgets == [None] + + # The intensifier should not use any instances + instances = smac.get_target_function_instances() + assert instances == [None] + + # However, we expect ONE seeds (deterministic), although we have max_config_calls=5 + seeds = smac.get_target_function_seeds() + assert seeds == [0] + + +def test_target_function_arguments_deterministic_instances(make_facade): + """Tests whether we get the right arguments.""" + model, smac = make_facade(deterministic=True, use_instances=True) + + # The intensifier should not use any budgets + budgets = smac.get_target_function_budgets() + assert budgets == [None] + + # The intensifier should not use any instances + instances = smac.get_target_function_instances() + assert len(instances) == 45 + + # However, we expect ONE seeds (deterministic), although we have max_config_calls=5 + seeds = smac.get_target_function_seeds() + assert seeds == [0] + + +def test_target_function_arguments_non_deterministic(make_facade): + """Tests whether we get the right arguments.""" + model, smac = make_facade(deterministic=False) + + # The intensifier should not use any budgets + budgets = smac.get_target_function_budgets() + assert budgets == [None] + + # The intensifier should not use any instances + instances = smac.get_target_function_instances() + assert instances == [None] + + # However, we expect ONE seeds (deterministic), although we have max_config_calls=5 + seeds = smac.get_target_function_seeds() + assert len(seeds) == 5 + + +def test_target_function_arguments_non_deterministic_instances(make_facade): + """Tests whether we get the right arguments.""" + model, smac = make_facade(deterministic=False, use_instances=True) + + # The intensifier should not use any budgets + budgets = smac.get_target_function_budgets() + assert budgets == [None] + + # The intensifier should not use any instances + instances = smac.get_target_function_instances() + assert len(instances) == 45 + + # However, we expect ONE seeds (deterministic), although we have max_config_calls=5 + seeds = smac.get_target_function_seeds() + assert len(seeds) == 5 + + +# -------------------------------------------------------------- +# Test tell without ask +# -------------------------------------------------------------- + + +def test_tell_without_ask(make_facade): + """Tests whether tell works without ask. In the case of ``intensifier`` it should work.""" + model, smac = make_facade(deterministic=False, use_instances=True) + seed = smac.get_target_function_seeds()[0] + instance = smac.get_target_function_instances()[0] + + # We can provide SMAC with custom configurations first + for config in model.configspace.sample_configuration(10): + cost = model.train(config, seed=seed, instance=instance) + + trial_info = TrialInfo(config, seed=seed, instance=instance) + trial_value = TrialValue(cost=cost, time=0.5) + + smac.tell(trial_info, trial_value) + + # Seed is not supported + with pytest.raises(ValueError): + trial_info = TrialInfo(config, seed=23412351234) + trial_value = TrialValue(cost=cost, time=0.5) + + smac.tell(trial_info, trial_value) + + # Instance is not supported + with pytest.raises(ValueError): + trial_info = TrialInfo(config, seed=seed, instance=None) + trial_value = TrialValue(cost=cost, time=0.5) + + smac.tell(trial_info, trial_value) + + assert smac.stats.finished == 10 + assert smac.stats.submitted == 0 # We have 0 submittions because we don't call the ask method + + smac.optimize() + + # After optimization we expect to have +10 finished + assert smac.stats.finished == smac._scenario.n_trials + 10 + assert smac.stats.submitted == smac._scenario.n_trials + 1 # We have one submittion which is skipped + assert len(smac.runhistory) == smac._scenario.n_trials + 10 # However, the skipped one is not saved anymore + + # We expect SMAC to use the same seed if configs with a seed were passed + for k in smac.runhistory.keys(): + assert k.seed in smac.get_target_function_seeds() + + +# -------------------------------------------------------------- +# Test ask and tell +# -------------------------------------------------------------- + + +def test_ask_and_tell(make_facade): + """Tests whether ask followed by a tell works. We can not ensure the same results when using + ask and tell and optimize.""" + model, smac = make_facade(deterministic=False, use_instances=True) + + for i in range(smac._scenario.n_trials): + trial_info = smac.ask() + + cost = model.train(trial_info.config, seed=trial_info.seed, instance=trial_info.instance) + trial_value = TrialValue(cost=cost, time=0.5) + + smac.tell(trial_info, trial_value) + + assert smac.incumbent is not None + + +def test_ask_and_tell_after_optimization(make_facade): + """ + Tests whether ask followed by a tell works after the optimization. + """ + model, smac = make_facade(deterministic=False, use_instances=True) + smac.optimize() + trials = len(smac.runhistory) + + for _ in range(10): + trial_info = smac.ask() + + cost = model.train(trial_info.config, seed=trial_info.seed, instance=trial_info.instance) + trial_value = TrialValue(cost=cost, time=0.5) + + smac.tell(trial_info, trial_value) + + # We should have more entries in the runhistory now + assert trials < len(smac.runhistory) + + +# -------------------------------------------------------------- +# Test multiple asks successively +# -------------------------------------------------------------- + + +def test_multiple_asks_successively(make_facade): + """ + model, smac = make_facade(deterministic=False, use_instances=True) + + info = [] + for i in range(10): + trial_info = smac.ask() + + # Make sure the trials are different + assert trial_info not in info + + info += [trial_info] + """ + + # Not supported yet + pass diff --git a/tests/test_callback.py b/tests/test_callback.py new file mode 100644 index 000000000..7d3c84574 --- /dev/null +++ b/tests/test_callback.py @@ -0,0 +1,124 @@ +from __future__ import annotations + +import pytest +from ConfigSpace import Categorical, Configuration, ConfigurationSpace, Float, Integer + +import smac +from smac import HyperparameterOptimizationFacade, Scenario +from smac.callback import Callback +from smac.initial_design import DefaultInitialDesign +from smac.intensifier.intensifier import Intensifier +from smac.runhistory import TrialInfo, TrialInfoIntent, TrialValue + + +class CustomCallback(Callback): + def __init__(self) -> None: + self.start_counter = 0 + self.end_counter = 0 + self.iteration_start_counter = 0 + self.iteration_end_counter = 0 + self.next_configurations_start_counter = 0 + self.next_configurations_end_counter = 0 + self.ask_start_counter = 0 + self.ask_end_counter = 0 + self.tell_start_counter = 0 + self.tell_end_counter = 0 + + def on_start(self, smbo: smac.main.BaseSMBO) -> None: + self.start_counter += 1 + + def on_end(self, smbo: smac.main.BaseSMBO) -> None: + self.end_counter += 1 + + def on_iteration_start(self, smbo: smac.main.BaseSMBO) -> None: + self.iteration_start_counter += 1 + + def on_iteration_end(self, smbo: smac.main.BaseSMBO) -> None: + self.iteration_end_counter += 1 + + def on_next_configurations_start(self, smbo: smac.main.BaseSMBO) -> None: + self.next_configurations_start_counter += 1 + + def on_next_configurations_end(self, smbo: smac.main.BaseSMBO, configurations: list[Configuration]) -> None: + self.next_configurations_end_counter += 1 + + def on_ask_start(self, smbo: smac.main.BaseSMBO) -> None: + self.ask_start_counter += 1 + + def on_ask_end(self, smbo: smac.main.BaseSMBO, intent: TrialInfoIntent, info: TrialInfo) -> None: + self.ask_end_counter += 1 + + def on_tell_start(self, smbo: smac.main.BaseSMBO, info: TrialInfo, value: TrialValue) -> bool | None: + self.tell_start_counter += 1 + return None + + def on_tell_end(self, smbo: smac.main.BaseSMBO, info: TrialInfo, value: TrialValue) -> bool | None: + self.tell_end_counter += 1 + return None + + +def test_callback(rosenbrock): + N_TRIALS = 5 + + scenario = Scenario(rosenbrock.configspace, n_trials=N_TRIALS) + + # The intensify percentage indirectly influences the number of calls of configuration + # sampling. + intensifier = Intensifier(scenario, max_config_calls=1, intensify_percentage=1e-10) + initial_design = DefaultInitialDesign(scenario) + + smac = HyperparameterOptimizationFacade( + scenario, + rosenbrock.train, + intensifier=intensifier, + initial_design=initial_design, + callbacks=[Callback()], + overwrite=True, + ) + smac.optimize() + + +def test_custom_callback(rosenbrock): + N_TRIALS = 40 + + scenario = Scenario(rosenbrock.configspace, n_trials=N_TRIALS) + callback = CustomCallback() + + # The intensify percentage indirectly influences the number of calls of configuration + # sampling. + intensifier = Intensifier(scenario, max_config_calls=1, intensify_percentage=1e-10) + initial_design = DefaultInitialDesign(scenario) + + smac = HyperparameterOptimizationFacade( + scenario, + rosenbrock.train, + intensifier=intensifier, + initial_design=initial_design, + callbacks=[callback], + overwrite=True, + ) + smac.optimize() + + # Those functions are called only once + assert callback.start_counter == 1 + assert callback.end_counter == 1 + + # Those function may differ depending on the runtime + assert callback.ask_start_counter > 0 + assert callback.ask_end_counter > 0 + + # Those function may differ depending on the runtime + assert callback.tell_start_counter > 0 + assert callback.tell_end_counter > 0 + + assert callback.ask_start_counter > callback.tell_start_counter + assert callback.ask_end_counter > callback.tell_end_counter + + # Those function may differ depending on the runtime + assert callback.iteration_start_counter > 0 + assert callback.iteration_end_counter > 0 + assert callback.iteration_start_counter >= callback.iteration_end_counter + + # This is depending on the number of challengers/intensify percentage + assert callback.next_configurations_start_counter > 0 + assert callback.next_configurations_end_counter > 0 diff --git a/tests/test_cli/__init__.py b/tests/test_cli/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/test_cli/random_configuration_chooser_impl.py b/tests/test_cli/random_configuration_chooser_impl.py deleted file mode 100644 index d47175768..000000000 --- a/tests/test_cli/random_configuration_chooser_impl.py +++ /dev/null @@ -1,24 +0,0 @@ -from smac.optimizer.configuration_chooser.random_chooser import ChooserNoCoolDown - -__author__ = "Aaron Kimmig" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "3-clause BSD" -__maintainer__ = "Aaron Kimmig" -__email__ = "kimmiga@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -""" -This class is used to test in test_deterministic_smac if the CLI accepts -custom Random Conf choosers. -""" - - -class RandomConfigurationChooserImpl(ChooserNoCoolDown): - """ - Implementation of helper class to configure interleaving of - random configurations in a list of challengers. - """ - - def __init__(self): - super().__init__(modulus=3.0) diff --git a/tests/test_cli/test_deterministic_smac.py b/tests/test_cli/test_deterministic_smac.py deleted file mode 100644 index 28c329177..000000000 --- a/tests/test_cli/test_deterministic_smac.py +++ /dev/null @@ -1,202 +0,0 @@ -import json -import os -import shutil -import unittest -from unittest import mock - -from ConfigSpace.util import get_one_exchange_neighbourhood - -from smac.smac_cli import SMACCLI - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestDeterministicSMAC(unittest.TestCase): - def setUp(self): - base_directory = os.path.split(__file__)[0] - base_directory = os.path.abspath(os.path.join(base_directory, "../../tests", "..")) - self.current_dir = os.getcwd() - os.chdir(base_directory) - - self.output_dir_1 = "tests/test_files/out_test_deterministic_1" - self.output_dir_2 = "tests/test_files/out_test_deterministic_2" - self.output_dir_3 = "tests/test_files/out_test_deterministic_3" - self.scenario_file = "tests/test_files/test_deterministic_scenario.txt" - self.output_dirs = [self.output_dir_1, self.output_dir_2, self.output_dir_3] - - self.maxDiff = None - - def tearDown(self): - for output_dir in self.output_dirs: - if output_dir: - shutil.rmtree(output_dir, ignore_errors=True) - os.chdir(self.current_dir) - - def ignore_timestamps(self, rh): - for i, (k, val) in enumerate(rh["data"]): - rh["data"][i][1] = [v for j, v in enumerate(val) if j not in [3, 4]] # 3, 4 are start and end timestamps - return rh - - @unittest.mock.patch("smac.optimizer.acquisition.maximizer.get_one_exchange_neighbourhood") - def test_deterministic(self, patch): - """ - Testing deterministic behaviour. - """ - - # Make SMAC a bit faster - patch.side_effect = lambda configuration, seed: get_one_exchange_neighbourhood( - configuration=configuration, - stdev=0.05, - num_neighbors=2, - seed=seed, - ) - - testargs = [ - "scripts/smac", - "--scenario", - self.scenario_file, - "--verbose_level", - "DEBUG", - "--seed", - "1", - "--random_configuration_chooser", - "tests/test_cli/random_configuration_chooser_impl.py", - "--output_dir", - self.output_dir_1, - ] - SMACCLI().main_cli(testargs[1:]) - testargs = [ - "scripts/smac", - "--scenario", - self.scenario_file, - "--verbose_level", - "DEBUG", - "--seed", - "1", - "--random_configuration_chooser", - "tests/test_cli/random_configuration_chooser_impl.py", - "--output_dir", - self.output_dir_2, - ] - SMACCLI().main_cli(testargs[1:]) - testargs = [ - "scripts/smac", - "--scenario", - self.scenario_file, - "--verbose_level", - "DEBUG", - "--seed", - "2", - "--random_configuration_chooser", - "tests/test_cli/random_configuration_chooser_impl.py", - "--output_dir", - self.output_dir_3, - ] - SMACCLI().main_cli(testargs[1:]) - # compare trajectories in output_dir_{1,2,3} - h1 = json.load(open(self.output_dir_1 + "/run_1/runhistory.json")) - h2 = json.load(open(self.output_dir_2 + "/run_1/runhistory.json")) - h3 = json.load(open(self.output_dir_3 + "/run_2/runhistory.json")) - self.assertEqual(self.ignore_timestamps(h1), self.ignore_timestamps(h2)) - # As h1 is changed inplace in the line above we need to reload it - h1 = json.load(open(self.output_dir_1 + "/run_1/runhistory.json")) - self.assertNotEqual(self.ignore_timestamps(h1), self.ignore_timestamps(h3)) - - def test_modes(self): - """ - Test if different modes are accepted - """ - testargs = [ - "scripts/smac", - "--scenario", - self.scenario_file, - "--verbose_level", - "DEBUG", - "--seed", - "2", - "--random_configuration_chooser", - "tests/test_cli/random_configuration_chooser_impl.py", - "--output_dir", - self.output_dir_3, - "--mode", - "SMAC4AC", - ] - cli = SMACCLI() - with mock.patch("smac.smac_cli.SMAC4AC") as MSMAC: - MSMAC.return_value.optimize.return_value = True - cli.main_cli(testargs[1:]) - MSMAC.assert_called_once_with( - initial_configurations=None, - restore_incumbent=None, - run_id=2, - runhistory=None, - stats=None, - scenario=mock.ANY, - rng=mock.ANY, - ) - - testargs[-1] = "SMAC4BB" - cli = SMACCLI() - with mock.patch("smac.smac_cli.SMAC4BB") as MSMAC: - MSMAC.return_value.optimize.return_value = True - cli.main_cli(testargs[1:]) - MSMAC.assert_called_once_with( - initial_configurations=None, - restore_incumbent=None, - run_id=2, - runhistory=None, - stats=None, - scenario=mock.ANY, - rng=mock.ANY, - ) - - testargs[-1] = "SMAC4HPO" - cli = SMACCLI() - with mock.patch("smac.smac_cli.SMAC4HPO") as MSMAC: - MSMAC.return_value.optimize.return_value = True - cli.main_cli(testargs[1:]) - MSMAC.assert_called_once_with( - initial_configurations=None, - restore_incumbent=None, - run_id=2, - runhistory=None, - stats=None, - scenario=mock.ANY, - rng=mock.ANY, - ) - - testargs[-1] = "Hydra" - cli = SMACCLI() - with mock.patch("smac.smac_cli.Hydra") as MSMAC: - MSMAC.return_value.optimize.return_value = True - cli.main_cli(testargs[1:]) - MSMAC.assert_called_once_with( - initial_configurations=None, - restore_incumbent=None, - run_id=2, - incs_per_round=1, - n_iterations=3, - n_optimizers=1, - random_configuration_chooser=mock.ANY, - runhistory=None, - stats=None, - scenario=mock.ANY, - rng=mock.ANY, - val_set="train", - ) - - testargs[-1] = "PSMAC" - cli = SMACCLI() - with mock.patch("smac.smac_cli.PSMAC") as MSMAC: - MSMAC.return_value.optimize.return_value = True - cli.main_cli(testargs[1:]) - MSMAC.assert_called_once_with( - run_id=2, - scenario=mock.ANY, - rng=mock.ANY, - n_incs=1, - n_optimizers=1, - shared_model=False, - validate=False, - ) diff --git a/tests/test_cli/test_restore_state.py b/tests/test_cli/test_restore_state.py deleted file mode 100644 index e54472942..000000000 --- a/tests/test_cli/test_restore_state.py +++ /dev/null @@ -1,133 +0,0 @@ -import os -import shutil -import unittest - -import numpy as np -from ConfigSpace.hyperparameters import UniformFloatHyperparameter - -from smac.configspace import ConfigurationSpace -from smac.facade.smac_ac_facade import SMAC4AC -from smac.scenario.scenario import Scenario -from smac.smac_cli import SMACCLI -from smac.stats.stats import Stats - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestSMACCLI(unittest.TestCase): - def setUp(self): - base_directory = os.path.split(__file__)[0] - base_directory = os.path.abspath(os.path.join(base_directory, "../../tests", "..")) - self.current_dir = os.getcwd() - os.chdir(base_directory) - - output_one_dir = "tests/test_files/test_restore_state" # From scenario_one.txt - self.output_one = output_one_dir + "/run_1" - output_two_dir = "tests/test_files/test_restored_state" # From scenario_two.txt - self.output_two = output_two_dir + "/run_1" - self.smaccli = SMACCLI() - self.scenario_one = "tests/test_files/restore_scenario_one.txt" - self.scenario_two = "tests/test_files/restore_scenario_two.txt" - self.output_dirs = [output_one_dir, output_two_dir] - - def tearDown(self): - for output_dir in self.output_dirs: - if output_dir: - shutil.rmtree(output_dir, ignore_errors=True) - os.chdir(self.current_dir) - - def test_run_and_restore(self): - """ - Testing basic restore functionality. - """ - # Run for 5 algo-calls - testargs = [ - "python", - "scripts/smac.py", - "--scenario_file", - self.scenario_one, - "--verbose", - "DEBUG", - ] - self.smaccli.main_cli(testargs[2:]) - # Increase limit and run for 10 (so 5 more) by using restore_state - testargs = [ - "python", - "scripts/smac.py", - "--restore_state", - self.output_one, - "--scenario_file", - self.scenario_two, - "--verbose", - "DEBUG", - ] - self.smaccli.main_cli(testargs[2:]) - - def test_missing_dir(self): - """ - Testing error if dir is missing. - """ - testargs = [ - "python", - "scripts/smac.py", - "--restore_state", - "nonsense_test_dir", - "--scenario_file", - self.scenario_two, - "--verbose", - "DEBUG", - ] - self.assertRaises(FileNotFoundError, lambda: self.smaccli.main_cli(testargs[2:])) - - def test_illegal_input(self): - """ - Testing illegal input in smbo - """ - cs = ConfigurationSpace() - cs.add_hyperparameter(UniformFloatHyperparameter("test", 1, 10, 5)) - scen = Scenario({"run_obj": "quality", "cs": cs}) - stats = Stats(scen) - # Recorded runs but no incumbent. - stats.submitted_ta_runs = 10 - smac = SMAC4AC(scen, stats=stats, rng=np.random.RandomState(42)) - self.output_dirs.append(scen.output_dir) - self.assertRaises(ValueError, smac.optimize) - # Incumbent but no recoreded runs. - incumbent = cs.get_default_configuration() - smac = SMAC4AC(scen, restore_incumbent=incumbent, rng=np.random.RandomState(42)) - self.assertRaises(ValueError, smac.optimize) - - def test_same_dir(self): - """ - Testing possible error using same dir for restore - """ - # Run for 5 algo-calls - testargs = [ - "python", - "scripts/smac.py", - "--scenario", - self.scenario_one, - "--verbose", - "DEBUG", - ] - self.smaccli.main_cli(testargs[2:]) - # Increase limit and run for 10 (so 5 more) by using restore_state - testargs = [ - "python", - "scripts/smac.py", - "--restore_state", - self.output_one, - "--scenario", - self.scenario_two, - "--verbose", - "DEBUG", - ] - try: - self.smaccli.main_cli(testargs[2:]) - except FileNotFoundError: - pass - self.assertTrue(os.path.exists(self.output_one)) - self.assertFalse(os.path.exists(self.output_one + ".OLD")) - self.assertTrue(os.path.exists(self.output_two)) - self.assertFalse(os.path.exists(self.output_two + ".OLD")) diff --git a/tests/test_configspace/__init__.py b/tests/test_configspace/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/test_configspace/test_configspace.py b/tests/test_configspace/test_configspace.py deleted file mode 100644 index 03e7f1a28..000000000 --- a/tests/test_configspace/test_configspace.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -import unittest - -from ConfigSpace.read_and_write import pcs - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class ConfigSpaceTest(unittest.TestCase): - def test_spear(self): - """ - simply getting some random configuration from spear pcs - """ - file_path = os.path.join(os.path.dirname(__file__), "../../tests", "test_files", "spear-params.pcs") - - with open(file_path) as fp: - pcs_str = fp.readlines() - cs = pcs.read(pcs_str) - - for i in range(100): - _ = cs.sample_configuration() diff --git a/tests/test_continue.py b/tests/test_continue.py new file mode 100644 index 000000000..6d719262e --- /dev/null +++ b/tests/test_continue.py @@ -0,0 +1,57 @@ +import os +import shutil +from io import StringIO + +import pytest + +from smac import BlackBoxFacade, HyperparameterOptimizationFacade, Scenario + + +def test_continue_same_scenario(rosenbrock): + for facade in [BlackBoxFacade]: + # That should work: We did not optimize in the first run + scenario = Scenario(rosenbrock.configspace, n_trials=10) + smac = facade(scenario, rosenbrock.train, overwrite=True) + + scenario = Scenario(rosenbrock.configspace, n_trials=10) + smac = facade(scenario, rosenbrock.train) + smac.optimize() + + # Should not work: After optimization, we can not continue (yet) + # However, we can load the results + scenario = Scenario(rosenbrock.configspace, n_trials=10) + smac = facade(scenario, rosenbrock.train, overwrite=True) + smac.optimize() + scenario = Scenario(rosenbrock.configspace, n_trials=10) + smac2 = facade(scenario, rosenbrock.train) + assert smac.incumbent == smac2.incumbent + with pytest.raises(NotImplementedError, match="Unfortunately, previous runs can not be continued yet.*"): + smac2.optimize() + + +def test_continue_different_scenario(rosenbrock, monkeypatch): + for facade in [BlackBoxFacade, HyperparameterOptimizationFacade]: + # Overwrite completely + number_inputs = StringIO("1\n") + monkeypatch.setattr("sys.stdin", number_inputs) + scenario = Scenario(rosenbrock.configspace, name="blub1", n_trials=10) + smac = facade(scenario, rosenbrock.train, overwrite=True) + smac.optimize() + scenario = Scenario(rosenbrock.configspace, name="blub1", n_trials=11) + smac = facade(scenario, rosenbrock.train) + + # Keep old run + try: + shutil.rmtree("smac3_output/blub2") + shutil.rmtree("smac3_output/blub2-old") + except FileNotFoundError: + pass + + number_inputs = StringIO("2\n") + monkeypatch.setattr("sys.stdin", number_inputs) + scenario = Scenario(rosenbrock.configspace, name="blub2", n_trials=10) + smac = facade(scenario, rosenbrock.train, overwrite=True) + smac.optimize() + scenario = Scenario(rosenbrock.configspace, name="blub2", n_trials=11) + smac = facade(scenario, rosenbrock.train) + assert os.path.isdir("smac3_output/blub2-old") diff --git a/tests/test_epm/__init__.py b/tests/test_epm/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/test_epm/test_base_epm.py b/tests/test_epm/test_base_epm.py deleted file mode 100644 index 333461f8a..000000000 --- a/tests/test_epm/test_base_epm.py +++ /dev/null @@ -1,69 +0,0 @@ -import unittest -import unittest.mock - -import numpy as np -from ConfigSpace import UniformFloatHyperparameter - -import smac -import smac.configspace -from smac.epm.base_epm import BaseEPM -from smac.epm.utils import get_types - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestRFWithInstances(unittest.TestCase): - def _get_cs(self, n_dimensions): - configspace = smac.configspace.ConfigurationSpace() - for i in range(n_dimensions): - configspace.add_hyperparameter(UniformFloatHyperparameter("x%d" % i, 0, 1)) - return configspace - - def test_apply_pca(self): - cs = self._get_cs(5) - instance_features = np.array([np.random.rand(10) for _ in range(5)]) - types, bounds = get_types(cs, instance_features) - - def get_X_y(num_samples, num_instance_features): - X = smac.configspace.convert_configurations_to_array(cs.sample_configuration(num_samples)) - if num_instance_features: - X_inst = np.random.rand(num_samples, num_instance_features) - X = np.hstack((X, X_inst)) - y = np.random.rand(num_samples) - return X, y - - with unittest.mock.patch.object(BaseEPM, "_train"): - with unittest.mock.patch.object(BaseEPM, "_predict") as predict_mock: - - predict_mock.side_effect = lambda x, _: (x, x) - - epm = BaseEPM( - configspace=cs, - types=types, - bounds=bounds, - seed=1, - pca_components=7, - instance_features=instance_features, - ) - - X, y = get_X_y(5, 10) - epm.train(X, y) - self.assertFalse(epm._apply_pca) - X_test, _ = get_X_y(5, None) - epm.predict_marginalized_over_instances(X_test) - - # more data points than pca components - X, y = get_X_y(8, 10) - epm.train(X, y) - self.assertTrue(epm._apply_pca) - X_test, _ = get_X_y(5, None) - epm.predict_marginalized_over_instances(X_test) - - # and less again - this ensures that the types array inside the epm is reverted - # and the pca is disabled again - X, y = get_X_y(5, 10) - epm.train(X, y) - self.assertFalse(epm._apply_pca) - X_test, _ = get_X_y(5, None) - epm.predict_marginalized_over_instances(X_test) diff --git a/tests/test_epm/test_gp.py b/tests/test_epm/test_gp.py deleted file mode 100644 index 5ed6624c3..000000000 --- a/tests/test_epm/test_gp.py +++ /dev/null @@ -1,404 +0,0 @@ -import unittest.mock - -import numpy as np -import scipy.optimize -import sklearn.datasets -import sklearn.model_selection -from ConfigSpace import EqualsCondition - -from smac.configspace import ( - CategoricalHyperparameter, - ConfigurationSpace, - UniformFloatHyperparameter, - convert_configurations_to_array, -) -from smac.epm.gaussian_process import GaussianProcess -from smac.epm.gaussian_process.utils.prior import HorseshoePrior, LognormalPrior - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def get_gp(n_dimensions, rs, noise=1e-3, normalize_y=True) -> GaussianProcess: - from smac.epm.gaussian_process.kernels import ConstantKernel, Matern, WhiteKernel - - cov_amp = ConstantKernel( - 2.0, - constant_value_bounds=(1e-10, 2), - prior=LognormalPrior(mean=0.0, sigma=1.0, rng=rs), - ) - exp_kernel = Matern( - np.ones([n_dimensions]), - [(np.exp(-10), np.exp(2)) for _ in range(n_dimensions)], - nu=2.5, - ) - noise_kernel = WhiteKernel( - noise_level=noise, - noise_level_bounds=(1e-10, 2), - prior=HorseshoePrior(scale=0.1, rng=rs), - ) - kernel = cov_amp * exp_kernel + noise_kernel - - bounds = [(0.0, 1.0) for _ in range(n_dimensions)] - types = np.zeros(n_dimensions) - - configspace = ConfigurationSpace() - for i in range(n_dimensions): - configspace.add_hyperparameter(UniformFloatHyperparameter("x%d" % i, 0, 1)) - - model = GaussianProcess( - configspace=configspace, - bounds=bounds, - types=types, - kernel=kernel, - seed=rs.randint(low=1, high=10000), - normalize_y=normalize_y, - n_opt_restarts=2, - ) - return model - - -def get_cont_data(rs): - X = rs.rand(20, 10) - Y = rs.rand(10, 1) - n_dims = 10 - return X, Y, n_dims - - -def get_cat_data(rs): - X_cont = rs.rand(20, 5) - X_cat = rs.randint(low=0, high=3, size=(20, 5)) - X = np.concatenate([X_cat, X_cont], axis=1) - Y = rs.rand(10, 1) - cat_dims = [0, 1, 2, 3, 4] - cont_dims = [5, 6, 7, 8, 9] - return X, Y, cat_dims, cont_dims - - -def get_mixed_gp(cat_dims, cont_dims, rs, noise=1e-3, normalize_y=True): - from smac.epm.gaussian_process.kernels import ( - ConstantKernel, - HammingKernel, - Matern, - WhiteKernel, - ) - - cat_dims = np.array(cat_dims, dtype=np.int) - cont_dims = np.array(cont_dims, dtype=np.int) - n_dimensions = len(cat_dims) + len(cont_dims) - cov_amp = ConstantKernel( - 2.0, - constant_value_bounds=(1e-10, 2), - prior=LognormalPrior(mean=0.0, sigma=1.0, rng=rs), - ) - - exp_kernel = Matern( - np.ones([len(cont_dims)]), - [(np.exp(-10), np.exp(2)) for _ in range(len(cont_dims))], - nu=2.5, - operate_on=cont_dims, - ) - - ham_kernel = HammingKernel( - np.ones([len(cat_dims)]), - [(np.exp(-10), np.exp(2)) for _ in range(len(cat_dims))], - operate_on=cat_dims, - ) - noise_kernel = WhiteKernel( - noise_level=noise, - noise_level_bounds=(1e-10, 2), - prior=HorseshoePrior(scale=0.1, rng=rs), - ) - kernel = cov_amp * (exp_kernel * ham_kernel) + noise_kernel - - bounds = [0] * n_dimensions - types = np.zeros(n_dimensions) - for c in cont_dims: - bounds[c] = (0.0, 1.0) - for c in cat_dims: - types[c] = 3 - bounds[c] = (3, np.nan) - - cs = ConfigurationSpace() - for c in cont_dims: - cs.add_hyperparameter(UniformFloatHyperparameter("X%d" % c, 0, 1)) - for c in cat_dims: - cs.add_hyperparameter(CategoricalHyperparameter("X%d" % c, [0, 1, 2, 3])) - - model = GaussianProcess( - configspace=cs, - bounds=bounds, - types=types, - kernel=kernel, - seed=rs.randint(low=1, high=10000), - normalize_y=normalize_y, - ) - return model - - -class TestGP(unittest.TestCase): - def test_predict_wrong_X_dimensions(self): - rs = np.random.RandomState(1) - - # cont - X, Y, n_dims = get_cont_data(rs) - # cat - X, Y, cat_dims, cont_dims = get_cat_data(rs) - - for model in (get_gp(n_dims, rs), get_mixed_gp(cat_dims, cont_dims, rs)): - X = rs.rand(10) - self.assertRaisesRegex(ValueError, "Expected 2d array, got 1d array!", model.predict, X) - X = rs.rand(10, 10, 10) - self.assertRaisesRegex(ValueError, "Expected 2d array, got 3d array!", model.predict, X) - - X = rs.rand(10, 5) - self.assertRaisesRegex(ValueError, "Rows in X should have 10 entries " "but have 5!", model.predict, X) - - def test_predict(self): - rs = np.random.RandomState(1) - - # cont - X, Y, n_dims = get_cont_data(rs) - # cat - X, Y, cat_dims, cont_dims = get_cat_data(rs) - - for model in (get_gp(n_dims, rs), get_mixed_gp(cat_dims, cont_dims, rs)): - model.train(X[:10], Y[:10]) - m_hat, v_hat = model.predict(X[10:]) - self.assertEqual(m_hat.shape, (10, 1)) - self.assertEqual(v_hat.shape, (10, 1)) - - def test_train_do_optimize(self): - # Check that do_optimize does not mess with the kernel hyperparameters given to the Gaussian process! - - rs = np.random.RandomState(1) - X, Y, n_dims = get_cont_data(rs) - - model = get_gp(n_dims, rs) - model._train(X[:10], Y[:10], do_optimize=False) - theta = model.gp.kernel.theta - theta_ = model.gp.kernel_.theta - fixture = np.array([0.693147, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.907755]) - np.testing.assert_array_almost_equal(theta, fixture) - np.testing.assert_array_almost_equal(theta_, fixture) - np.testing.assert_array_almost_equal(theta, theta_) - - model._train(X[:10], Y[:10], do_optimize=True) - theta = model.gp.kernel.theta - theta_ = model.gp.kernel_.theta - fixture = np.array([0.693147, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.907755]) - self.assertFalse(np.any(theta == fixture)) - self.assertFalse(np.any(theta_ == fixture)) - np.testing.assert_array_almost_equal(theta, theta_) - - @unittest.mock.patch("sklearn.gaussian_process.GaussianProcessRegressor.fit") - def test_train_continue_on_linalg_error(self, fit_mock): - # Check that training does not stop on a linalg error, but that uncertainty is increased! - - class Dummy: - counter = 0 - - def __call__(self, X, y): - if self.counter >= 10: - return None - - self.counter += 1 - raise np.linalg.LinAlgError - - fit_mock.side_effect = Dummy() - - rs = np.random.RandomState(1) - X, Y, n_dims = get_cont_data(rs) - - model = get_gp(n_dims, rs) - fixture = np.exp(model.kernel.theta[-1]) - model._train(X[:10], Y[:10], do_optimize=False) - self.assertAlmostEqual(np.exp(model.gp.kernel.theta[-1]), fixture + 10) - - @unittest.mock.patch("sklearn.gaussian_process.GaussianProcessRegressor.log_marginal_likelihood") - def test_train_continue_on_linalg_error_2(self, fit_mock): - # Check that training does not stop on a linalg error during hyperparameter optimization - - class Dummy: - counter = 0 - - def __call__(self, X, eval_gradient=True, clone_kernel=True): - # If this is not aligned with the GP an error will be raised that None is not iterable - if self.counter == 13: - return None - - self.counter += 1 - raise np.linalg.LinAlgError - - fit_mock.side_effect = Dummy() - - rs = np.random.RandomState(1) - X, Y, n_dims = get_cont_data(rs) - - model = get_gp(n_dims, rs) - _ = model.kernel.theta - model._train(X[:10], Y[:10], do_optimize=True) - np.testing.assert_array_almost_equal( - model.gp.kernel.theta, - [0.69314718, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.69314718], - ) - - @unittest.mock.patch.object(GaussianProcess, "predict") - def test_predict_marginalized_over_instances_no_features(self, rf_mock): - """The GP should fall back to the regular predict() method.""" - rs = np.random.RandomState(1) - - # cont - X, Y, n_dims = get_cont_data(rs) - # cat - X, Y, cat_dims, cont_dims = get_cat_data(rs) - - for ct, model in enumerate((get_gp(n_dims, rs), get_mixed_gp(cat_dims, cont_dims, rs))): - model.train(X[:10], Y[:10]) - model.predict(X[10:]) - self.assertEqual(rf_mock.call_count, ct + 1) - - def test_predict_with_actual_values(self): - X = np.array( - [ - [0.0, 0.0, 0.0], - [0.0, 0.0, 1.0], - [0.0, 1.0, 0.0], - [0.0, 1.0, 1.0], - [1.0, 0.0, 0.0], - [1.0, 0.0, 1.0], - [1.0, 1.0, 0.0], - [1.0, 1.0, 1.0], - ], - dtype=np.float64, - ) - y = np.array([[0.1], [0.2], [9], [9.2], [100.0], [100.2], [109.0], [109.2]], dtype=np.float64) - rs = np.random.RandomState(1) - model = get_gp(3, rs) - model.train(np.vstack((X, X, X, X, X, X, X, X)), np.vstack((y, y, y, y, y, y, y, y))) - - mu_hat, var_hat = model.predict(X) - for y_i, y_hat_i, mu_hat_i in zip( - y.reshape((1, -1)).flatten(), - mu_hat.reshape((1, -1)).flatten(), - var_hat.reshape((1, -1)).flatten(), - ): - self.assertAlmostEqual(y_hat_i, y_i, delta=2) - self.assertAlmostEqual(mu_hat_i, 0, delta=2) - - # Regression test that performance does not drastically decrease in the near future - mu_hat, var_hat = model.predict(np.array([[10, 10, 10]])) - self.assertAlmostEqual(mu_hat[0][0], 54.612500000000004) - # There's a slight difference between my local installation and travis - self.assertLess(abs(var_hat[0][0] - 1121.8409184001594), 2) - - # test other covariance results - _, var_fc = model.predict(X, cov_return_type="full_cov") - self.assertEqual(var_fc.shape, (8, 8)) - _, var_sd = model.predict(X, cov_return_type="diagonal_std") - self.assertEqual(var_sd.shape, (8, 1)) - _, var_no = model.predict(np.array([[10, 10, 10]]), cov_return_type=None) - self.assertIsNone(var_no) - # check values - _, var_fc = model.predict(np.array([[10, 10, 10]]), cov_return_type="full_cov") - self.assertAlmostEqual(var_fc[0][0], var_hat[0][0]) - _, var_sd = model.predict(np.array([[10, 10, 10]]), cov_return_type="diagonal_std") - self.assertAlmostEqual(var_sd[0][0] ** 2, var_hat[0][0]) - - def test_gp_on_sklearn_data(self): - X, y = sklearn.datasets.load_boston(return_X_y=True) - # Normalize such that the bounds in get_gp (10) hold - X = X / X.max(axis=0) - rs = np.random.RandomState(1) - model = get_gp(X.shape[1], rs) - cv = sklearn.model_selection.KFold(shuffle=True, random_state=rs, n_splits=2) - - maes = [8.712875586609810299, 8.7608419489812271634] - - for i, (train_split, test_split) in enumerate(cv.split(X, y)): - X_train = X[train_split] - y_train = y[train_split] - X_test = X[test_split] - y_test = y[test_split] - model.train(X_train, y_train) - y_hat, mu_hat = model.predict(X_test) - mae = np.mean(np.abs(y_hat - y_test), dtype=np.float128) - self.assertAlmostEqual(mae, maes[i]) - - def test_nll(self): - rs = np.random.RandomState(1) - gp = get_gp(1, rs) - gp.train(np.array([[0], [1]]), np.array([0, 1])) - n_above_1 = 0 - for i in range(1000): - theta = np.array( - [rs.uniform(1e-10, 10), rs.uniform(-10, 2), rs.uniform(-10, 1)] - ) # Values from the default prior - error = scipy.optimize.check_grad(lambda x: gp._nll(x)[0], lambda x: gp._nll(x)[1], theta, epsilon=1e-5) - if error > 0.1: - n_above_1 += 1 - self.assertLessEqual(n_above_1, 10) - - def test_sampling_shape(self): - X = np.arange(-5, 5, 0.1).reshape((-1, 1)) - X_test = np.arange(-5.05, 5.05, 0.1).reshape((-1, 1)) - for shape in (None, (-1, 1)): - - if shape is None: - y = np.sin(X).flatten() - else: - y = np.sin(X).reshape(shape) - - rng = np.random.RandomState(1) - for gp in ( - get_gp(n_dimensions=1, rs=rng, noise=1e-10, normalize_y=False), - get_gp(n_dimensions=1, rs=rng, noise=1e-10, normalize_y=True), - ): - gp._train(X, y) - func = gp.sample_functions(X_test=X_test, n_funcs=1) - self.assertEqual(func.shape, (101, 1), msg=shape) - func = gp.sample_functions(X_test=X_test, n_funcs=2) - self.assertEqual(func.shape, (101, 2)) - - def test_normalization(self): - X = np.arange(-5, 5, 0.1).reshape((-1, 1)) - X_test = np.arange(-5.05, 5.05, 0.1).reshape((-1, 1)) - y = np.sin(X) - rng = np.random.RandomState(1) - gp = get_gp(n_dimensions=1, rs=rng, noise=1e-10, normalize_y=False) - gp._train(X, y, do_optimize=False) - mu_hat, var_hat = gp.predict(X_test) - gp_norm = get_gp(n_dimensions=1, rs=rng, noise=1e-10, normalize_y=True) - gp_norm._train(X, y, do_optimize=False) - mu_hat_prime, var_hat_prime = gp_norm.predict(X_test) - np.testing.assert_array_almost_equal(mu_hat, mu_hat_prime, decimal=4) - np.testing.assert_array_almost_equal(var_hat, var_hat_prime, decimal=4) - - func = gp.sample_functions(X_test=X_test, n_funcs=2) - func_prime = gp_norm.sample_functions(X_test=X_test, n_funcs=2) - np.testing.assert_array_almost_equal(func, func_prime, decimal=1) - - def test_impute_inactive_hyperparameters(self): - cs = ConfigurationSpace() - a = cs.add_hyperparameter(CategoricalHyperparameter("a", [0, 1])) - b = cs.add_hyperparameter(CategoricalHyperparameter("b", [0, 1])) - c = cs.add_hyperparameter(UniformFloatHyperparameter("c", 0, 1)) - cs.add_condition(EqualsCondition(b, a, 1)) - cs.add_condition(EqualsCondition(c, a, 0)) - cs.seed(1) - - configs = cs.sample_configuration(size=100) - config_array = convert_configurations_to_array(configs) - for line in config_array: - if line[0] == 0: - self.assertTrue(np.isnan(line[1])) - elif line[0] == 1: - self.assertTrue(np.isnan(line[2])) - - gp = get_gp(3, np.random.RandomState(1)) - config_array = gp._impute_inactive(config_array) - for line in config_array: - if line[0] == 0: - self.assertEqual(line[1], -1) - elif line[0] == 1: - self.assertEqual(line[2], -1) diff --git a/tests/test_epm/test_gp_mcmc.py b/tests/test_epm/test_gp_mcmc.py deleted file mode 100644 index 7d18bf853..000000000 --- a/tests/test_epm/test_gp_mcmc.py +++ /dev/null @@ -1,211 +0,0 @@ -import unittest.mock - -import numpy as np -import sklearn.datasets -import sklearn.model_selection - -from smac.configspace import ConfigurationSpace, UniformFloatHyperparameter -from smac.epm.gaussian_process.mcmc import MCMCGaussianProcess -from smac.epm.gaussian_process.utils.prior import HorseshoePrior, LognormalPrior - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def get_gp(n_dimensions, rs, noise=1e-3, normalize_y=True, average_samples=False, n_iter=50): - from smac.epm.gaussian_process.kernels import ConstantKernel, Matern, WhiteKernel - - cov_amp = ConstantKernel( - 2.0, - constant_value_bounds=(1e-10, 2), - prior=LognormalPrior(mean=0.0, sigma=1.0, rng=rs), - ) - exp_kernel = Matern( - np.ones([n_dimensions]), - [(np.exp(-10), np.exp(2)) for _ in range(n_dimensions)], - nu=2.5, - prior=None, - ) - noise_kernel = WhiteKernel( - noise_level=noise, - noise_level_bounds=(1e-10, 2), - prior=HorseshoePrior(scale=0.1, rng=rs), - ) - kernel = cov_amp * exp_kernel + noise_kernel - - n_mcmc_walkers = 3 * len(kernel.theta) - if n_mcmc_walkers % 2 == 1: - n_mcmc_walkers += 1 - - bounds = [(0.0, 1.0) for _ in range(n_dimensions)] - types = np.zeros(n_dimensions) - - configspace = ConfigurationSpace() - for i in range(n_dimensions): - configspace.add_hyperparameter(UniformFloatHyperparameter("x%d" % i, 0, 1)) - - model = MCMCGaussianProcess( - configspace=configspace, - types=types, - bounds=bounds, - kernel=kernel, - n_mcmc_walkers=n_mcmc_walkers, - chain_length=n_iter, - burnin_steps=n_iter, - normalize_y=normalize_y, - seed=rs.randint(low=1, high=10000), - mcmc_sampler="emcee", - average_samples=average_samples, - ) - return model - - -class TestGPMCMC(unittest.TestCase): - def test_predict_wrong_X_dimensions(self): - rs = np.random.RandomState(1) - model = get_gp(10, rs) - - X = rs.rand(10) - self.assertRaisesRegex(ValueError, "Expected 2d array, got 1d array!", model.predict, X) - X = rs.rand(10, 10, 10) - self.assertRaisesRegex(ValueError, "Expected 2d array, got 3d array!", model.predict, X) - - X = rs.rand(10, 5) - self.assertRaisesRegex(ValueError, "Rows in X should have 10 entries " "but have 5!", model.predict, X) - - def test_gp_train(self): - rs = np.random.RandomState(1) - X = rs.rand(20, 10) - Y = rs.rand(10, 1) - - fixture = np.array([0.693147, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.907755]) - - model = get_gp(10, rs) - np.testing.assert_array_almost_equal(model.kernel.theta, fixture) - model.train(X[:10], Y[:10]) - self.assertEqual(len(model.models), 36) - - for base_model in model.models: - theta = base_model.gp.kernel.theta - theta_ = base_model.gp.kernel_.theta - # Test that the kernels of the base GP are actually changed! - np.testing.assert_array_almost_equal(theta, theta_) - self.assertFalse(np.any(theta == fixture)) - self.assertFalse(np.any(theta_ == fixture)) - - def test_gp_train_posterior_mean(self): - rs = np.random.RandomState(1) - X = rs.rand(20, 10) - Y = rs.rand(10, 1) - - fixture = np.array([0.693147, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.907755]) - - model = get_gp(10, rs, average_samples=True) - np.testing.assert_array_almost_equal(model.kernel.theta, fixture) - model.train(X[:10], Y[:10]) - - for base_model in model.models: - theta = base_model.gp.kernel.theta - theta_ = base_model.gp.kernel_.theta - # Test that the kernels of the base GP are actually changed! - np.testing.assert_array_almost_equal(theta, theta_) - self.assertFalse(np.any(theta == fixture)) - self.assertFalse(np.any(theta_ == fixture)) - - self.assertEqual(len(model.models), 1) - - def test_predict(self): - rs = np.random.RandomState(1) - X = rs.rand(20, 10) - Y = rs.rand(10, 1) - model = get_gp(10, rs) - model.train(X[:10], Y[:10]) - m_hat, v_hat = model.predict(X[10:]) - self.assertEqual(m_hat.shape, (10, 1)) - self.assertEqual(v_hat.shape, (10, 1)) - - @unittest.mock.patch.object(MCMCGaussianProcess, "predict") - def test_predict_marginalized_over_instances_no_features(self, rf_mock): - """The GP should fall back to the regular predict() method.""" - - rs = np.random.RandomState(1) - X = rs.rand(20, 10) - Y = rs.rand(10, 1) - model = get_gp(10, rs) - model.train(X[:10], Y[:10]) - model.predict(X[10:]) - self.assertEqual(rf_mock.call_count, 1) - - def test_predict_with_actual_values(self): - X = np.array( - [ - [0.0, 0.0, 0.0], - [0.0, 0.0, 1.0], - [0.0, 1.0, 0.0], - [0.0, 1.0, 1.0], - [1.0, 0.0, 0.0], - [1.0, 0.0, 1.0], - [1.0, 1.0, 0.0], - [1.0, 1.0, 1.0], - ], - dtype=np.float64, - ) - y = np.array([[0.1], [0.2], [9], [9.2], [100.0], [100.2], [109.0], [109.2]], dtype=np.float64) - rs = np.random.RandomState(1) - model = get_gp(3, rs, noise=1e-10, n_iter=200) - model.train(np.vstack((X, X, X, X, X, X, X, X)), np.vstack((y, y, y, y, y, y, y, y))) - - y_hat, var_hat = model.predict(X) - for y_i, y_hat_i, var_hat_i in zip( - y.reshape((1, -1)).flatten(), - y_hat.reshape((1, -1)).flatten(), - var_hat.reshape((1, -1)).flatten(), - ): - # Chain length too short to get excellent predictions, apparently there's a lot of predictive variance - self.assertAlmostEqual(y_i, y_hat_i, delta=1) - self.assertAlmostEqual(var_hat_i, 0, delta=500) - - # Regression test that performance does not drastically decrease in the near future - y_hat, var_hat = model.predict(np.array([[10, 10, 10]])) - self.assertAlmostEqual(y_hat[0][0], 54.613410745846785, delta=0.1) - # Massive variance due to internally used law of total variances, also a massive difference locally and on - # travis-ci - self.assertLessEqual(abs(var_hat[0][0]) - 3700, 200, msg=str(var_hat)) - - def test_gp_on_sklearn_data(self): - X, y = sklearn.datasets.load_boston(return_X_y=True) - # Normalize such that the bounds in get_gp hold - X = X / X.max(axis=0) - rs = np.random.RandomState(1) - model = get_gp(X.shape[1], rs, noise=1e-10, normalize_y=True) - cv = sklearn.model_selection.KFold(shuffle=True, random_state=rs, n_splits=2) - - maes = [6.841565457149357281, 7.4943401900804902144] - - for i, (train_split, test_split) in enumerate(cv.split(X, y)): - X_train = X[train_split] - y_train = y[train_split] - X_test = X[test_split] - y_test = y[test_split] - model.train(X_train, y_train) - y_hat, mu_hat = model.predict(X_test) - mae = np.mean(np.abs(y_hat - y_test), dtype=np.float128) - self.assertAlmostEqual(mae, maes[i]) - - def test_normalization(self): - X = np.arange(-5, 5, 0.1).reshape((-1, 1)) - X_test = np.arange(-5.05, 5.05, 0.1).reshape((-1, 1)) - y = np.sin(X) - rng = np.random.RandomState(1) - gp = get_gp(n_dimensions=1, rs=rng, noise=1e-10, normalize_y=False) - gp._train(X, y, do_optimize=False) - self.assertFalse(gp.models[0].normalize_y) - self.assertFalse(hasattr(gp.models[0], "mean_y_")) - mu_hat, var_hat = gp.predict(X_test) - gp_norm = get_gp(n_dimensions=1, rs=rng, noise=1e-10, normalize_y=True) - gp_norm._train(X, y, do_optimize=False) - self.assertTrue(gp_norm.models[0].normalize_y) - self.assertTrue(hasattr(gp_norm.models[0], "mean_y_")) - mu_hat_prime, var_hat_prime = gp_norm.predict(X_test) - np.testing.assert_array_almost_equal(mu_hat, mu_hat_prime, decimal=4) - np.testing.assert_array_almost_equal(var_hat, var_hat_prime, decimal=4) diff --git a/tests/test_epm/test_gp_priors.py b/tests/test_epm/test_gp_priors.py deleted file mode 100644 index e03105647..000000000 --- a/tests/test_epm/test_gp_priors.py +++ /dev/null @@ -1,249 +0,0 @@ -import unittest -from functools import partial - -import numpy as np -import scipy.optimize - -from smac.epm.gaussian_process.utils.prior import ( - GammaPrior, - HorseshoePrior, - LognormalPrior, - SoftTopHatPrior, - TophatPrior, -) -from smac.utils.constants import VERY_SMALL_NUMBER - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def wrap_ln(theta, prior): - return np.exp(prior.lnprob(np.log(theta))) - - -def wrap_lnprob(x, prior): - return prior.lnprob(x[0]) - - -def wrap_gradient(x, prior): - return prior.gradient(x[0]) - - -class TestTophatPrior(unittest.TestCase): - def test_lnprob_and_grad_scalar(self): - prior = TophatPrior( - lower_bound=np.exp(-10), - upper_bound=np.exp(2), - rng=np.random.RandomState(1), - ) - - # Legal scalar - for val in (-1, 0, 1): - self.assertEqual(prior.lnprob(val), 0, msg=str(val)) - self.assertEqual(prior.gradient(val), 0, msg=str(val)) - - # Boundary - for val in (-10, 2): - self.assertEqual(prior.lnprob(val), 0) - self.assertEqual(prior.gradient(val), 0) - - # Values outside the boundary - for val in (-10 - VERY_SMALL_NUMBER, 2 + VERY_SMALL_NUMBER, -50, 50): - self.assertTrue(np.isinf(prior.lnprob(val))) - self.assertEqual(prior.gradient(val), 0) - - def test_sample_from_prior(self): - prior = TophatPrior(lower_bound=np.exp(-10), upper_bound=np.exp(2), rng=np.random.RandomState(1)) - samples = prior.sample_from_prior(10) - np.testing.assert_array_equal(samples >= -10, True) - np.testing.assert_array_equal(samples <= 2, True) - # Test that the rng is set - self.assertAlmostEqual(samples[0], -4.995735943569112) - - def test_sample_from_prior_shapes(self): - rng = np.random.RandomState(1) - lower_bound = 2 + rng.random_sample() * 50 - upper_bound = lower_bound + rng.random_sample() * 50 - prior = TophatPrior( - lower_bound=lower_bound, - upper_bound=upper_bound, - rng=np.random.RandomState(1), - ) - sample = prior.sample_from_prior(1) - self.assertEqual(sample.shape, (1,)) - sample = prior.sample_from_prior(2) - self.assertEqual(sample.shape, (2,)) - sample = prior.sample_from_prior(10) - self.assertEqual(sample.shape, (10,)) - with self.assertRaises(ValueError): - prior.sample_from_prior(0) - with self.assertRaises(ValueError): - prior.sample_from_prior((2,)) - - -class TestHorseshoePrior(unittest.TestCase): - def test_lnprob_and_grad_scalar(self): - prior = HorseshoePrior(scale=1, rng=np.random.RandomState(1)) - - # Legal scalar - self.assertEqual(prior.lnprob(-1), 1.1450937952919953) - self.assertEqual(prior.gradient(-1), -0.6089187456211098) - - # Boundary - self.assertTrue(np.isinf(prior._lnprob(0))) - self.assertTrue(np.isinf(prior._gradient(0))) - - def test_sample_from_prior(self): - prior = HorseshoePrior(scale=1, rng=np.random.RandomState(1)) - samples = prior.sample_from_prior(10) - # Test that the rng is set - self.assertAlmostEqual(samples[0], 1.0723988839129437) - - def test_sample_from_prior_shapes(self): - rng = np.random.RandomState(1) - lower_bound = 2 + rng.random_sample() * 50 - upper_bound = lower_bound + rng.random_sample() * 50 - prior = TophatPrior( - lower_bound=lower_bound, - upper_bound=upper_bound, - rng=np.random.RandomState(1), - ) - sample = prior.sample_from_prior(1) - self.assertEqual(sample.shape, (1,)) - sample = prior.sample_from_prior(2) - self.assertEqual(sample.shape, (2,)) - sample = prior.sample_from_prior(10) - self.assertEqual(sample.shape, (10,)) - with self.assertRaises(ValueError): - prior.sample_from_prior(0) - with self.assertRaises(ValueError): - prior.sample_from_prior((2,)) - - def test_gradient(self): - for scale in (0.1, 0.5, 1.0, 2.0): - prior = HorseshoePrior(scale=scale, rng=np.random.RandomState(1)) - # The function appears to be unstable above 15 - for theta in range(-20, 15): - if theta == 0: - continue - - wrap_lnprob_ = partial(wrap_lnprob, prior=prior) - wrap_gradient_ = partial(wrap_gradient, prior=prior) - - error = scipy.optimize.check_grad( - wrap_lnprob_, - wrap_gradient_, - np.array([theta]), - epsilon=1e-5, - ) - self.assertAlmostEqual(error, 0, delta=5) - - -class TestGammaPrior(unittest.TestCase): - def test_lnprob_and_grad_scalar(self): - prior = GammaPrior(a=0.5, scale=1 / 2, loc=0, rng=np.random.RandomState(1)) - - # Legal scalar - x = -1 - self.assertAlmostEqual(prior.lnprob(x), -0.46155023, 7) - self.assertEqual(prior.gradient(x), -1.2357588823428847) - - def test_lnprob_and_grad_array(self): - prior = GammaPrior(a=0.5, scale=1 / 2, loc=0, rng=np.random.RandomState(1)) - val = np.array([-1, -1]) - with self.assertRaises(NotImplementedError): - prior.lnprob(val) - with self.assertRaises(NotImplementedError): - prior.gradient(val) - - def test_gradient(self): - for scale in (0.5, 1.0, 2.0): - prior = GammaPrior(a=2, scale=scale, loc=0, rng=np.random.RandomState(1)) - # The function appears to be unstable above 10 - for theta in np.arange(1e-15, 10, 0.01): - if theta == 0: - continue - - wrap_lnprob_ = partial(wrap_lnprob, prior=prior) - wrap_gradient_ = partial(wrap_gradient, prior=prior) - - error = scipy.optimize.check_grad( - wrap_lnprob_, - wrap_gradient_, - np.array([theta]), - epsilon=1e-5, - ) - self.assertAlmostEqual(error, 0, delta=5, msg=str(theta)) - - -class TestLogNormalPrior(unittest.TestCase): - def test_gradient(self): - for sigma in (0.5, 1.0, 2.0): - prior = LognormalPrior(mean=0, sigma=sigma, rng=np.random.RandomState(1)) - # The function appears to be unstable above 15 - for theta in range(0, 15): - # Gradient approximation becomes unstable when going closer to zero - theta += 1e-2 - - wrap_lnprob_ = partial(wrap_lnprob, prior=prior) - wrap_gradient_ = partial(wrap_gradient, prior=prior) - - error = scipy.optimize.check_grad( - wrap_lnprob_, - wrap_gradient_, - np.array([theta]), - epsilon=1e-5, - ) - self.assertAlmostEqual(error, 0, delta=5, msg=theta) - - -class TestSoftTopHatPrior(unittest.TestCase): - def test_lnprob(self): - prior = SoftTopHatPrior( - lower_bound=np.exp(-5), - upper_bound=np.exp(5), - exponent=2, - rng=np.random.RandomState(1), - ) - - # Legal values - self.assertEqual(prior.lnprob(-5), 0) - self.assertEqual(prior.lnprob(0), 0) - self.assertEqual(prior.lnprob(5), 0) - - # Illegal values - self.assertAlmostEqual(prior.lnprob(-5.1), -0.01) - self.assertAlmostEqual(prior.lnprob(-6), -1) - self.assertAlmostEqual(prior.lnprob(-7), -4) - self.assertAlmostEqual(prior.lnprob(5.1), -0.01) - self.assertAlmostEqual(prior.lnprob(6), -1) - self.assertAlmostEqual(prior.lnprob(7), -4) - - def test_grad(self): - prior = SoftTopHatPrior( - lower_bound=np.exp(-5), - upper_bound=np.exp(5), - exponent=2, - rng=np.random.RandomState(1), - ) - - # Legal values - self.assertEqual(prior.gradient(-5), 0) - self.assertEqual(prior.gradient(0), 0) - self.assertEqual(prior.gradient(5), 0) - - for theta in [-10, -7, -6, -5.1, 5.1, 6, 7, 10]: - # Gradient approximation becomes unstable when going closer to zero - theta += 1e-2 - grad = prior.gradient(theta) - grad_vector = prior.gradient(theta) - self.assertEqual(grad, grad_vector) - - def prob(x): - return prior.lnprob(x[0]) - - def grad(x): - return prior.gradient(x[0]) - - error = scipy.optimize.check_grad(prob, grad, np.array([theta]), epsilon=1e-5) - self.assertAlmostEqual(error, 0, delta=5, msg=theta) diff --git a/tests/test_epm/test_rf_with_instances.py b/tests/test_epm/test_rf_with_instances.py deleted file mode 100644 index 931d1c4d1..000000000 --- a/tests/test_epm/test_rf_with_instances.py +++ /dev/null @@ -1,335 +0,0 @@ -import unittest -import unittest.mock - -import numpy as np -from ConfigSpace import ( - CategoricalHyperparameter, - EqualsCondition, - OrdinalHyperparameter, - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) - -import smac -import smac.configspace -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances -from smac.epm.utils import get_types - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestRFWithInstances(unittest.TestCase): - def _get_cs(self, n_dimensions): - configspace = smac.configspace.ConfigurationSpace() - for i in range(n_dimensions): - configspace.add_hyperparameter(UniformFloatHyperparameter("x%d" % i, 0, 1)) - return configspace - - def test_predict_wrong_X_dimensions(self): - rs = np.random.RandomState(1) - - model = RandomForestWithInstances( - configspace=self._get_cs(10), - types=np.zeros((10,), dtype=np.uint), - bounds=list(map(lambda x: (0, 10), range(10))), - seed=1, - ) - X = rs.rand(10) - self.assertRaisesRegex(ValueError, "Expected 2d array, got 1d array!", model.predict, X) - X = rs.rand(10, 10, 10) - self.assertRaisesRegex(ValueError, "Expected 2d array, got 3d array!", model.predict, X) - - X = rs.rand(10, 5) - self.assertRaisesRegex(ValueError, "Rows in X should have 10 entries " "but have 5!", model.predict, X) - - def test_predict(self): - rs = np.random.RandomState(1) - X = rs.rand(20, 10) - Y = rs.rand(10, 1) - model = RandomForestWithInstances( - configspace=self._get_cs(10), - types=np.zeros((10,), dtype=np.uint), - bounds=list(map(lambda x: (0, 10), range(10))), - seed=1, - ) - model.train(X[:10], Y[:10]) - m_hat, v_hat = model.predict(X[10:]) - self.assertEqual(m_hat.shape, (10, 1)) - self.assertEqual(v_hat.shape, (10, 1)) - - def test_train_with_pca(self): - rs = np.random.RandomState(1) - X = rs.rand(20, 20) - F = rs.rand(10, 10) - Y = rs.rand(20, 1) - model = RandomForestWithInstances( - configspace=self._get_cs(10), - types=np.zeros((20,), dtype=np.uint), - bounds=list(map(lambda x: (0, 10), range(10))), - seed=1, - pca_components=2, - instance_features=F, - ) - model.train(X, Y) - - self.assertEqual(model.n_params, 10) - self.assertEqual(model.n_feats, 10) - self.assertIsNotNone(model.pca) - self.assertIsNotNone(model.scaler) - - def test_predict_marginalized_over_instances_wrong_X_dimensions(self): - rs = np.random.RandomState(1) - - model = RandomForestWithInstances( - configspace=self._get_cs(10), - types=np.zeros((10,), dtype=np.uint), - instance_features=rs.rand(10, 2), - seed=1, - bounds=list(map(lambda x: (0, 10), range(10))), - ) - X = rs.rand(10) - self.assertRaisesRegex( - ValueError, - "Expected 2d array, got 1d array!", - model.predict_marginalized_over_instances, - X, - ) - X = rs.rand(10, 10, 10) - self.assertRaisesRegex( - ValueError, - "Expected 2d array, got 3d array!", - model.predict_marginalized_over_instances, - X, - ) - - @unittest.mock.patch.object(RandomForestWithInstances, "predict") - def test_predict_marginalized_over_instances_no_features(self, rf_mock): - """The RF should fall back to the regular predict() method.""" - - rs = np.random.RandomState(1) - X = rs.rand(20, 10) - Y = rs.rand(10, 1) - model = RandomForestWithInstances( - configspace=self._get_cs(10), - types=np.zeros((10,), dtype=np.uint), - bounds=list(map(lambda x: (0, 10), range(10))), - seed=1, - ) - model.train(X[:10], Y[:10]) - model.predict(X[10:]) - self.assertEqual(rf_mock.call_count, 1) - - def test_predict_marginalized_over_instances(self): - rs = np.random.RandomState(1) - X = rs.rand(20, 10) - F = rs.rand(10, 5) - Y = rs.rand(len(X) * len(F), 1) - X_ = rs.rand(200, 15) - - model = RandomForestWithInstances( - configspace=self._get_cs(10), - types=np.zeros((15,), dtype=np.uint), - instance_features=F, - bounds=list(map(lambda x: (0, 10), range(10))), - seed=1, - ) - model.train(X_, Y) - means, variances = model.predict_marginalized_over_instances(X) - self.assertEqual(means.shape, (20, 1)) - self.assertEqual(variances.shape, (20, 1)) - - @unittest.mock.patch.object(RandomForestWithInstances, "predict") - def test_predict_marginalized_over_instances_mocked(self, rf_mock): - """Use mock to count the number of calls to predict()""" - - class SideEffect(object): - def __call__(self, X): - # Numpy array of number 0 to X.shape[0] - rval = np.array(list(range(X.shape[0]))).reshape((-1, 1)) - # Return mean and variance - return rval, rval - - rf_mock.side_effect = SideEffect() - - rs = np.random.RandomState(1) - F = rs.rand(10, 5) - - model = RandomForestWithInstances( - configspace=self._get_cs(10), - types=np.zeros((15,), dtype=np.uint), - instance_features=F, - bounds=list(map(lambda x: (0, 10), range(10))), - seed=1, - ) - X = rs.rand(20, 10) - F = rs.rand(10, 5) - Y = rs.randint(1, size=(len(X) * len(F), 1)) * 1.0 - X_ = rs.rand(200, 15) - model.train(X_, Y) - means, variances = model.predict_marginalized_over_instances(rs.rand(11, 10)) - # expected to be 0 as the predict is replaced by manual unloggin the trees - self.assertEqual(rf_mock.call_count, 0) - self.assertEqual(means.shape, (11, 1)) - self.assertEqual(variances.shape, (11, 1)) - for i in range(11): - self.assertEqual(means[i], 0.0) - self.assertEqual(variances[i], 1.0e-10) - - def test_predict_with_actual_values(self): - X = np.array( - [ - [0.0, 0.0, 0.0], - [0.0, 0.0, 1.0], - [0.0, 1.0, 0.0], - [0.0, 1.0, 1.0], - [1.0, 0.0, 0.0], - [1.0, 0.0, 1.0], - [1.0, 1.0, 0.0], - [1.0, 1.0, 1.0], - ], - dtype=np.float64, - ) - y = np.array([[0.1], [0.2], [9], [9.2], [100.0], [100.2], [109.0], [109.2]], dtype=np.float64) - model = RandomForestWithInstances( - configspace=self._get_cs(3), - types=np.array([0, 0, 0], dtype=np.uint), - bounds=[(0, np.nan), (0, np.nan), (0, np.nan)], - instance_features=None, - seed=12345, - ratio_features=1.0, - ) - model.train(np.vstack((X, X, X, X, X, X, X, X)), np.vstack((y, y, y, y, y, y, y, y))) - - y_hat, _ = model.predict(X) - for y_i, y_hat_i in zip(y.reshape((1, -1)).flatten(), y_hat.reshape((1, -1)).flatten()): - self.assertAlmostEqual(y_i, y_hat_i, delta=0.1) - - def test_with_ordinal(self): - cs = smac.configspace.ConfigurationSpace() - _ = cs.add_hyperparameter(CategoricalHyperparameter("a", [0, 1], default_value=0)) - _ = cs.add_hyperparameter(OrdinalHyperparameter("b", [0, 1], default_value=1)) - _ = cs.add_hyperparameter(UniformFloatHyperparameter("c", lower=0.0, upper=1.0, default_value=1)) - _ = cs.add_hyperparameter(UniformIntegerHyperparameter("d", lower=0, upper=10, default_value=1)) - cs.seed(1) - - feat_array = np.array([0, 0, 0]).reshape(1, -1) - types, bounds = get_types(cs, feat_array) - model = RandomForestWithInstances( - configspace=cs, - types=types, - bounds=bounds, - instance_features=feat_array, - seed=1, - ratio_features=1.0, - pca_components=9, - ) - self.assertEqual(bounds[0][0], 2) - self.assertTrue(bounds[0][1] is np.nan) - self.assertEqual(bounds[1][0], 0) - self.assertEqual(bounds[1][1], 1) - self.assertEqual(bounds[2][0], 0.0) - self.assertEqual(bounds[2][1], 1.0) - self.assertEqual(bounds[3][0], 0.0) - self.assertEqual(bounds[3][1], 1.0) - X = np.array( - [ - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 0.0, 9.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 1.0, 4.0, 0.0, 0.0, 0.0], - ], - dtype=np.float64, - ) - y = np.array([0, 1, 2, 3], dtype=np.float64) - - X_train = np.vstack((X, X, X, X, X, X, X, X, X, X)) - y_train = np.vstack((y, y, y, y, y, y, y, y, y, y)) - - model.train(X_train, y_train.reshape((-1, 1))) - mean, _ = model.predict(X) - for idx, m in enumerate(mean): - self.assertAlmostEqual(y[idx], m, 0.05) - - def test_rf_on_sklearn_data(self): - import sklearn.datasets - - X, y = sklearn.datasets.load_boston(return_X_y=True) - rs = np.random.RandomState(1) - - types = np.zeros(X.shape[1]) - bounds = [(np.min(X[:, i]), np.max(X[:, i])) for i in range(X.shape[1])] - - cv = sklearn.model_selection.KFold(shuffle=True, random_state=rs, n_splits=2) - - for do_log in [False, True]: - if do_log: - targets = np.log(y) - model = RandomForestWithInstances( - configspace=self._get_cs(X.shape[1]), - types=types, - bounds=bounds, - seed=1, - ratio_features=1.0, - pca_components=100, - log_y=True, - ) - maes = [0.43169704431695493156, 0.4267519520332511912] - else: - targets = y - model = RandomForestWithInstances( - configspace=self._get_cs(X.shape[1]), - types=types, - bounds=bounds, - seed=1, - ratio_features=1.0, - pca_components=100, - ) - maes = [9.3298376833224042496, 9.348010654109179346] - - for i, (train_split, test_split) in enumerate(cv.split(X, targets)): - X_train = X[train_split] - y_train = targets[train_split] - X_test = X[test_split] - y_test = targets[test_split] - model.train(X_train, y_train) - y_hat, mu_hat = model.predict(X_test) - mae = np.mean(np.abs(y_hat - y_test), dtype=np.float128) - self.assertAlmostEqual( - mae, - maes[i], - msg=("Do log: %s, iteration %i" % (str(do_log), i)), - # We observe a difference of around 0.00017 - # in github actions if doing log - places=3 if do_log else 7, - ) - - def test_impute_inactive_hyperparameters(self): - cs = smac.configspace.ConfigurationSpace() - a = cs.add_hyperparameter(CategoricalHyperparameter("a", [0, 1])) - b = cs.add_hyperparameter(CategoricalHyperparameter("b", [0, 1])) - c = cs.add_hyperparameter(UniformFloatHyperparameter("c", 0, 1)) - cs.add_condition(EqualsCondition(b, a, 1)) - cs.add_condition(EqualsCondition(c, a, 0)) - cs.seed(1) - - configs = cs.sample_configuration(size=100) - config_array = smac.configspace.convert_configurations_to_array(configs) - for line in config_array: - if line[0] == 0: - self.assertTrue(np.isnan(line[1])) - elif line[0] == 1: - self.assertTrue(np.isnan(line[2])) - - model = RandomForestWithInstances( - configspace=cs, - types=np.zeros((3,), dtype=np.uint), - bounds=list(map(lambda x: (0, 1), range(10))), - seed=1, - ) - config_array = model._impute_inactive(config_array) - for line in config_array: - if line[0] == 0: - self.assertEqual(line[1], 2) - elif line[0] == 1: - self.assertEqual(line[2], -1) diff --git a/tests/test_epm/test_uncorrelated_mo_rf_with_instances.py b/tests/test_epm/test_uncorrelated_mo_rf_with_instances.py deleted file mode 100644 index 890b16bf0..000000000 --- a/tests/test_epm/test_uncorrelated_mo_rf_with_instances.py +++ /dev/null @@ -1,103 +0,0 @@ -import unittest -from unittest import mock - -import numpy as np - -import smac.configspace -from smac.epm.random_forest.rf_mo import MultiObjectiveRandomForest -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestUncorrelatedMultiObjectiveWrapper(unittest.TestCase): - def _get_cs(self, n_dimensions): - configspace = smac.configspace.ConfigurationSpace() - for i in range(n_dimensions): - configspace.add_hyperparameter(smac.configspace.UniformFloatHyperparameter("x%d" % i, 0, 1)) - return configspace - - def test_train_and_predict_with_rf(self): - rs = np.random.RandomState(1) - X = rs.rand(20, 10) - Y = rs.rand(10, 2) - model = MultiObjectiveRandomForest( - configspace=self._get_cs(10), - target_names=["cost", "ln(runtime)"], - types=np.zeros((10,), dtype=np.uint), - bounds=[ - (0, np.nan), - (0, np.nan), - (0, np.nan), - (0, np.nan), - (0, np.nan), - (0, np.nan), - (0, np.nan), - (0, np.nan), - (0, np.nan), - (0, np.nan), - ], - seed=1, - model_kwargs={"seed": 1}, - pca_components=5, - ) - self.assertEqual(model.estimators[0].seed, 1) - self.assertEqual(model.estimators[1].seed, 1) - self.assertEqual(model.pca_components, 5) - model.train(X[:10], Y) - m, v = model.predict(X[10:]) - self.assertEqual(m.shape, (10, 2)) - self.assertEqual(v.shape, (10, 2)) - - m, v = model.predict_marginalized_over_instances(X[10:]) - self.assertEqual(m.shape, (10, 2)) - self.assertEqual(v.shape, (10, 2)) - - # We need to track how often the base model was called! - @mock.patch.object(RandomForestWithInstances, "predict") - def test_predict_mocked(self, rf_mock): - class SideEffect(object): - def __init__(self): - self.counter = 0 - - def __call__(self, X): - self.counter += 1 - # Mean and variance - rval = np.array([self.counter] * X.shape[0]) - return rval, rval - - rf_mock.side_effect = SideEffect() - - rs = np.random.RandomState(1) - X = rs.rand(20, 10) - Y = rs.rand(10, 3) - model = MultiObjectiveRandomForest( - target_names=["cost", "ln(runtime)", "foo"], - configspace=self._get_cs(10), - types=np.zeros((10,), dtype=np.uint), - bounds=[ - (0, np.nan), - (0, np.nan), - (0, np.nan), - (0, np.nan), - (0, np.nan), - (0, np.nan), - (0, np.nan), - (0, np.nan), - (0, np.nan), - (0, np.nan), - ], - seed=1, - model_kwargs={"seed": 1}, - ) - - model.train(X[:10], Y[:10]) - m_hat, v_hat = model.predict(X[10:]) - self.assertEqual(m_hat.shape, (10, 3)) - self.assertEqual(v_hat.shape, (10, 3)) - self.assertEqual(rf_mock.call_count, 3) - for i in range(10): - for j in range(3): - self.assertEqual(m_hat[i][j], j + 1) - self.assertEqual(v_hat[i][j], j + 1) diff --git a/tests/test_epm/test_util_funcs.py b/tests/test_epm/test_util_funcs.py deleted file mode 100644 index 476ddc589..000000000 --- a/tests/test_epm/test_util_funcs.py +++ /dev/null @@ -1,115 +0,0 @@ -import unittest - -import numpy as np -from ConfigSpace import ConfigurationSpace, EqualsCondition -from ConfigSpace.hyperparameters import ( - CategoricalHyperparameter, - Constant, - OrdinalHyperparameter, - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) - -from smac.epm.utils import check_subspace_points, get_types - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestUtilFuncs(unittest.TestCase): - def test_get_types(self): - cs = ConfigurationSpace() - cs.add_hyperparameter(CategoricalHyperparameter("a", ["a", "b"])) - cs.add_hyperparameter(UniformFloatHyperparameter("b", 1, 5)) - cs.add_hyperparameter(UniformIntegerHyperparameter("c", 3, 7)) - cs.add_hyperparameter(Constant("d", -5)) - cs.add_hyperparameter(OrdinalHyperparameter("e", ["cold", "hot"])) - cs.add_hyperparameter(CategoricalHyperparameter("f", ["x", "y"])) - types, bounds = get_types(cs, None) - np.testing.assert_array_equal(types, [2, 0, 0, 0, 0, 2]) - self.assertEqual(bounds[0][0], 2) - self.assertFalse(np.isfinite(bounds[0][1])) - np.testing.assert_array_equal(bounds[1], [0, 1]) - np.testing.assert_array_equal(bounds[2], [0, 1]) - self.assertEqual(bounds[3][0], 0) - self.assertFalse(np.isfinite(bounds[3][1])) - np.testing.assert_array_equal(bounds[4], [0, 1]) - self.assertEqual(bounds[5][0], 2) - self.assertFalse(np.isfinite(bounds[5][1])) - - def test_get_types_with_inactive(self): - cs = ConfigurationSpace() - a = cs.add_hyperparameter(CategoricalHyperparameter("a", ["a", "b"])) - b = cs.add_hyperparameter(UniformFloatHyperparameter("b", 1, 5)) - c = cs.add_hyperparameter(UniformIntegerHyperparameter("c", 3, 7)) - d = cs.add_hyperparameter(Constant("d", -5)) - e = cs.add_hyperparameter(OrdinalHyperparameter("e", ["cold", "hot"])) - f = cs.add_hyperparameter(CategoricalHyperparameter("f", ["x", "y"])) - cs.add_condition(EqualsCondition(b, a, "a")) - cs.add_condition(EqualsCondition(c, a, "a")) - cs.add_condition(EqualsCondition(d, a, "a")) - cs.add_condition(EqualsCondition(e, a, "a")) - cs.add_condition(EqualsCondition(f, a, "a")) - types, bounds = get_types(cs, None) - np.testing.assert_array_equal(types, [2, 0, 0, 2, 0, 3]) - self.assertEqual(bounds[0][0], 2) - self.assertFalse(np.isfinite(bounds[0][1])) - np.testing.assert_array_equal(bounds[1], [-1, 1]) - np.testing.assert_array_equal(bounds[2], [-1, 1]) - self.assertEqual(bounds[3][0], 2) - self.assertFalse(np.isfinite(bounds[3][1])) - np.testing.assert_array_equal(bounds[4], [0, 2]) - self.assertEqual(bounds[5][0], 3) - self.assertFalse(np.isfinite(bounds[5][1])) - - def test_check_subspace_points(self): - # 1D array - np.testing.assert_equal([True], check_subspace_points(np.array([0.5, 0.5]))) - bounds_cont_base = np.array([0.0, 1.0]) - X_cont = np.array([[0.5, 0.8], [-0.2, 0.2], [-0.7, 0.3]]) - np.testing.assert_equal(check_subspace_points(X_cont), [True] * len(X_cont)) - cont_dims = np.arange(X_cont.shape[-1]) - - with self.assertRaises(ValueError): - # bounds_cont missing - check_subspace_points(X_cont, cont_dims=cont_dims) - - with self.assertRaises(ValueError): - # bounds_cont does not match - check_subspace_points(X_cont, cont_dims=cont_dims, bounds_cont=bounds_cont_base) - - bounds_cont = np.tile(bounds_cont_base, [X_cont.shape[-1], 1]) - - np.testing.assert_equal( - check_subspace_points(X_cont, cont_dims=cont_dims, bounds_cont=bounds_cont), [True, False, False] - ) - np.testing.assert_equal( - check_subspace_points(X_cont, cont_dims=cont_dims, bounds_cont=bounds_cont, expand_bound=True), - [True, True, False], - ) - - # categorical hps - X_cat = np.array([[0, 1], [2, 1], [1, 4]]) - cat_dims = np.arange(X_cat.shape[-1]) - - bounds_cat = [(0, 2), (1, 4)] - - with self.assertRaises(ValueError): - # bounds_cont missing - check_subspace_points(X_cat, cat_dims=cat_dims) - - with self.assertRaises(ValueError): - # bounds_cat doe not match - check_subspace_points(X_cat, cat_dims=cat_dims, bounds_cat=[(0, 1)]) - - np.testing.assert_equal( - check_subspace_points(X_cat, cat_dims=cat_dims, bounds_cat=bounds_cat), [True, True, False] - ) - - # cat + cont - X_mix = np.hstack([X_cont, X_cat]) - cat_dims += len(cont_dims) - ss_mix = check_subspace_points( - X_mix, cont_dims=cont_dims, cat_dims=cat_dims, bounds_cont=bounds_cont, bounds_cat=bounds_cat - ) - np.testing.assert_equal(ss_mix, [True, False, False]) diff --git a/tests/test_facade/__init__.py b/tests/test_facade/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/test_facade/test_boing_facade.py b/tests/test_facade/test_boing_facade.py deleted file mode 100644 index 07658e137..000000000 --- a/tests/test_facade/test_boing_facade.py +++ /dev/null @@ -1,61 +0,0 @@ -import shutil -import unittest -from contextlib import suppress - -from ConfigSpace import ConfigurationSpace -from ConfigSpace.conditions import EqualsCondition -from ConfigSpace.forbidden import ( - ForbiddenAndConjunction, - ForbiddenEqualsClause, - ForbiddenInClause, -) -from ConfigSpace.hyperparameters import ( - CategoricalHyperparameter, - UniformFloatHyperparameter, -) - -from smac.facade.smac_boing_facade import SMAC4BOING -from smac.optimizer.configuration_chooser.boing_chooser import BOinGChooser -from smac.scenario.scenario import Scenario - - -def rosenbrock_2d(x): - x0 = x["x0"] - x1 = x["x1"] - return 100.0 * (x1 - x0**2.0) ** 2.0 + (1 - x0) ** 2.0 - - -class TestSMAC4BOinGFacade(unittest.TestCase): - def setUp(self) -> None: - cs = ConfigurationSpace() - x0 = UniformFloatHyperparameter("x0", -5, 10, default_value=-3) - x1 = UniformFloatHyperparameter("x1", -5, 10, default_value=-4) - x2 = CategoricalHyperparameter("x2", [0, 1], default_value=0) - x3 = UniformFloatHyperparameter("x3", -5, 10, default_value=-4) - cs.add_hyperparameters([x0, x1, x2, x3]) - cs.add_condition(EqualsCondition(x3, x2, 0)) - cs.add_forbidden_clause(ForbiddenAndConjunction(ForbiddenInClause(x2, [0, 1]), ForbiddenEqualsClause(x0, 0.1))) - # Scenario object - scenario = Scenario({"run_obj": "quality", "runcount-limit": 10, "cs": cs, "deterministic": "true"}) - self.scenario = scenario - self.output_dirs = [] - - def tearDown(self): - shutil.rmtree("run_1", ignore_errors=True) - for i in range(20): - with suppress(Exception): - dirname = "run_1" + (".OLD" * i) - shutil.rmtree(dirname) - for output_dir in self.output_dirs: - if output_dir: - shutil.rmtree(output_dir, ignore_errors=True) - - def test_smac4boing(self): - - smac = SMAC4BOING( - scenario=self.scenario, - tae_runner=rosenbrock_2d, - ) - smac.optimize() - self.assertIsInstance(smac.solver.epm_chooser, BOinGChooser) - self.output_dirs.append(smac.scenario.output_dir) diff --git a/tests/test_facade/test_func_facade.py b/tests/test_facade/test_func_facade.py deleted file mode 100644 index 25325fe05..000000000 --- a/tests/test_facade/test_func_facade.py +++ /dev/null @@ -1,55 +0,0 @@ -import shutil -import unittest - -from scipy.optimize import fmin_l_bfgs_b - -from smac.facade.func_facade import fmin_smac - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def rosenbrock_2d(x, seed=1): - return 100.0 * (x[1] - x[0] ** 2.0) ** 2.0 + (1 - x[0]) ** 2.0 - - -class TestSMACFacade(unittest.TestCase): - def setUp(self): - self.output_dirs = [] - - def tearDown(self): - for output_dir in self.output_dirs: - if output_dir: - shutil.rmtree(output_dir, ignore_errors=True) - - def test_func_smac(self): - func = rosenbrock_2d - x0 = [-3, -4] - bounds = [(-5, 5), (-5, 5)] - - x, f, smac = fmin_smac(func, x0, bounds, maxfun=10) - x_s, f_s, _ = fmin_l_bfgs_b(func, x0, bounds, maxfun=10, approx_grad=True) - - self.assertEqual(type(x), type(x_s)) - self.assertEqual(type(f), type(f_s.tolist())) - self.output_dirs.append(smac.scenario.output_dir) - - def test_parameter_order(self): - def func(x): - for i, item in enumerate(x): - self.assertLessEqual(i - 1, x[i]) - self.assertGreaterEqual(i, x[i]) - return 1 - - default = [i - 0.5 for i in range(10)] - bounds = [(i - 1, i) for i in range(10)] - print(default, bounds) - _, _, smac = fmin_smac(func=func, x0=default, bounds=bounds, maxfun=1) - - self.output_dirs.append(smac.scenario.output_dir) - - -if __name__ == "__main__": - t = TestSMACFacade() - t.setUp() - t.test_func_smac() diff --git a/tests/test_facade/test_hb4ac_facade.py b/tests/test_facade/test_hb4ac_facade.py deleted file mode 100644 index 1a553ee5e..000000000 --- a/tests/test_facade/test_hb4ac_facade.py +++ /dev/null @@ -1,45 +0,0 @@ -import contextlib -import shutil -import unittest.mock - -from ConfigSpace.hyperparameters import UniformFloatHyperparameter - -from smac.configspace import ConfigurationSpace -from smac.epm.random_epm import RandomEPM -from smac.facade.hyperband_facade import HB4AC -from smac.initial_design.random_configuration_design import RandomConfigurations -from smac.intensification.hyperband import Hyperband -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestHBFacade(unittest.TestCase): - def setUp(self): - self.cs = ConfigurationSpace() - self.scenario = Scenario({"cs": self.cs, "run_obj": "quality", "output_dir": ""}) - self.output_dirs = [] - - def tearDown(self): - for i in range(20): - with contextlib.suppress(Exception): - dirname = "run_1" + (".OLD" * i) - shutil.rmtree(dirname) - for output_dir in self.output_dirs: - if output_dir: - shutil.rmtree(output_dir, ignore_errors=True) - - def test_initializations(self): - cs = ConfigurationSpace() - for i in range(40): - cs.add_hyperparameter(UniformFloatHyperparameter("x%d" % (i + 1), 0, 1)) - scenario = Scenario({"cs": cs, "run_obj": "quality"}) - hb_kwargs = {"initial_budget": 1, "max_budget": 3} - facade = HB4AC(scenario=scenario, intensifier_kwargs=hb_kwargs) - - self.assertIsInstance(facade.solver.initial_design, RandomConfigurations) - self.assertIsInstance(facade.solver.epm_chooser.model, RandomEPM) - self.assertIsInstance(facade.solver.intensifier, Hyperband) - self.assertEqual(facade.solver.intensifier.min_chall, 1) - self.output_dirs.append(scenario.output_dir) diff --git a/tests/test_facade/test_hydra_facade.py b/tests/test_facade/test_hydra_facade.py deleted file mode 100644 index c97ab2926..000000000 --- a/tests/test_facade/test_hydra_facade.py +++ /dev/null @@ -1,75 +0,0 @@ -import glob -import os -import shutil -import unittest -from contextlib import suppress -from unittest.mock import patch - -import numpy as np - -from smac.facade.experimental.hydra_facade import PSMAC, Hydra -from smac.scenario.scenario import Scenario -from smac.utils.io.output_writer import OutputWriter - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -MOCKCALLS = 0 - - -class MockPSMAC(PSMAC): - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.ow = OutputWriter() - self.ow.write_scenario_file(self.scenario) - global MOCKCALLS - MOCKCALLS += 1 - - def optimize(self): - return np.array(self.scenario.cs.sample_configuration(self.n_workers)) - - def get_best_incumbents_ids(self, incs): - cost_per_conf_v = cost_per_conf_e = {} - val_ids = est_ids = list(range(len(incs))) - global MOCKCALLS - for inc in incs: - # in successive runs will always be smaller -> hydra doesn't terminate early - cost_per_conf_v[inc] = cost_per_conf_e[inc] = { - inst: max(100 - MOCKCALLS, 0) for inst in self.scenario.train_insts - } - if not self.validate: - cost_per_conf_v = val_ids = None - return cost_per_conf_v, val_ids, cost_per_conf_e, est_ids - - -class TestHydraFacade(unittest.TestCase): - def setUp(self): - self.output_dirs = [] - fn = os.path.join(os.path.dirname(__file__), "../test_files/spear_hydra_test_scenario.txt") - self.scenario = Scenario(fn) - self.scenario.limit_resources = True - - @patch("smac.facade.experimental.hydra_facade.PSMAC", new=MockPSMAC) - def test_hydra(self): - optimizer = Hydra(self.scenario, n_iterations=3) - portfolio = optimizer.optimize() - self.assertEqual(len(portfolio), 3) - - @patch("smac.facade.experimental.hydra_facade.PSMAC", new=MockPSMAC) - def test_hydra_mip(self): - optimizer = Hydra(self.scenario, n_iterations=3, incs_per_round=2) - portfolio = optimizer.optimize() - self.assertEqual(len(portfolio), 6) - - def tearDown(self): - hydras = glob.glob1(".", "hydra*") - for folder in hydras: - shutil.rmtree(folder, ignore_errors=True) - for i in range(20): - with suppress(Exception): - dirname = "run_1" + (".OLD" * i) - shutil.rmtree(dirname) - for output_dir in self.output_dirs: - if output_dir: - shutil.rmtree(output_dir, ignore_errors=True) diff --git a/tests/test_facade/test_psmac_facade.py b/tests/test_facade/test_psmac_facade.py deleted file mode 100644 index 0ac3b2558..000000000 --- a/tests/test_facade/test_psmac_facade.py +++ /dev/null @@ -1,67 +0,0 @@ -import os -import glob -import shutil -import unittest -from contextlib import suppress -from unittest.mock import patch - -from smac.optimizer.smbo import SMBO -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class MockSMBO(SMBO): - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.stats.start_timing() - - def run(self): # mock call such that we don't have to test with real algorithm - return self.config_space.sample_configuration() - - -class TestPSMACFacade(unittest.TestCase): - def setUp(self): - base_directory = os.path.split(__file__)[0] - base_directory = os.path.abspath(os.path.join(base_directory, "../../tests", "..")) - os.chdir(base_directory) - self.output_dirs = [] - fn = "tests/test_files/spear_hydra_test_scenario.txt" - fn = "tests/test_files/test_deterministic_scenario.txt" - self.scenario = Scenario(fn) - self.scenario.limit_resources = True - - @patch("smac.facade.smac_ac_facade.SMBO", new=MockSMBO) - def test_psmac(self): - import joblib - from smac.facade.psmac_facade import PSMAC - from smac.facade.smac_ac_facade import SMAC4AC - from smac.facade.smac_bb_facade import SMAC4BB - from smac.facade.smac_hpo_facade import SMAC4HPO - from smac.facade.smac_mf_facade import SMAC4MF - - facades = [None, SMAC4AC, SMAC4BB, SMAC4HPO, SMAC4MF] - n_workers_list = [1, 2, 3, 4] - n_facades = len(facades) - target = {"x1": 7.290709845323256, "x2": 10.285684762665337} - for i, facade in enumerate(facades): - for j, n_workers in enumerate(n_workers_list): - idx = n_facades * i + j - with self.subTest(i=idx): - with joblib.parallel_backend("multiprocessing", n_jobs=1): - optimizer = PSMAC(self.scenario, facade_class=facade, n_workers=n_workers, validate=False) - inc = optimizer.optimize() - self.assertDictEqual(target, dict(inc)) - - def tearDown(self): - hydras = glob.glob1(".", "psmac*") - for folder in hydras: - shutil.rmtree(folder, ignore_errors=True) - for i in range(20): - with suppress(Exception): - dirname = "run_1" + (".OLD" * i) - shutil.rmtree(dirname) - for output_dir in self.output_dirs: - if output_dir: - shutil.rmtree(output_dir, ignore_errors=True) diff --git a/tests/test_facade/test_roar_facade.py b/tests/test_facade/test_roar_facade.py deleted file mode 100644 index 455c525f2..000000000 --- a/tests/test_facade/test_roar_facade.py +++ /dev/null @@ -1,68 +0,0 @@ -import shutil -import unittest -from contextlib import suppress - -import numpy as np -from ConfigSpace.hyperparameters import UniformFloatHyperparameter - -from smac.configspace import ConfigurationSpace -from smac.facade.roar_facade import ROAR -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestROARFacade(unittest.TestCase): - def setUp(self): - self.cs = ConfigurationSpace() - self.scenario = Scenario({"cs": self.cs, "run_obj": "quality", "output_dir": ""}) - self.output_dirs = [] - - def tearDown(self): - shutil.rmtree("run_1", ignore_errors=True) - for i in range(20): - with suppress(Exception): - dirname = "run_1" + (".OLD" * i) - shutil.rmtree(dirname) - for output_dir in self.output_dirs: - if output_dir: - shutil.rmtree(output_dir, ignore_errors=True) - - def test_check_deterministic_rosenbrock(self): - def rosenbrock_2d(x): - x1 = x["x1"] - x2 = x["x2"] - val = 100.0 * (x2 - x1**2.0) ** 2.0 + (1 - x1) ** 2.0 - return val - - def opt_rosenbrock(): - cs = ConfigurationSpace() - - cs.add_hyperparameter(UniformFloatHyperparameter("x1", -5, 5, default_value=-3)) - cs.add_hyperparameter(UniformFloatHyperparameter("x2", -5, 5, default_value=-4)) - - scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternatively runtime) - "runcount-limit": 50, # maximum function evaluations - "cs": cs, # configuration space - "deterministic": "true", - "intensification_percentage": 0.000000001, - } - ) - - roar = ROAR(scenario=scenario, rng=np.random.RandomState(42), tae_runner=rosenbrock_2d) - incumbent = roar.optimize() - return incumbent, roar.scenario.output_dir - - i1, output_dir = opt_rosenbrock() - self.output_dirs.append(output_dir) - x1_1 = i1.get("x1") - x2_1 = i1.get("x2") - i2, output_dir = opt_rosenbrock() - self.output_dirs.append(output_dir) - x1_2 = i2.get("x1") - x2_2 = i2.get("x2") - self.assertAlmostEqual(x1_1, x1_2) - self.assertAlmostEqual(x2_1, x2_2) diff --git a/tests/test_facade/test_smac4mf_facade.py b/tests/test_facade/test_smac4mf_facade.py deleted file mode 100644 index 59c92c693..000000000 --- a/tests/test_facade/test_smac4mf_facade.py +++ /dev/null @@ -1,43 +0,0 @@ -import contextlib -import shutil -import unittest.mock - -from ConfigSpace.hyperparameters import UniformFloatHyperparameter - -from smac.configspace import ConfigurationSpace -from smac.facade.smac_mf_facade import SMAC4MF -from smac.initial_design.random_configuration_design import RandomConfigurations -from smac.scenario.scenario import Scenario - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestSMAC4MFFacade(unittest.TestCase): - def setUp(self): - self.cs = ConfigurationSpace() - self.scenario = Scenario({"cs": self.cs, "run_obj": "quality", "output_dir": ""}) - self.output_dirs = [] - - def tearDown(self): - for i in range(20): - with contextlib.suppress(Exception): - dirname = "run_1" + (".OLD" * i) - shutil.rmtree(dirname) - for output_dir in self.output_dirs: - if output_dir: - shutil.rmtree(output_dir, ignore_errors=True) - - def test_initializations(self): - cs = ConfigurationSpace() - for i in range(40): - cs.add_hyperparameter(UniformFloatHyperparameter("x%d" % (i + 1), 0, 1)) - scenario = Scenario({"cs": cs, "run_obj": "quality"}) - hb_kwargs = {"initial_budget": 1, "max_budget": 3} - facade = SMAC4MF(scenario=scenario, intensifier_kwargs=hb_kwargs) - - self.assertIsInstance(facade.solver.initial_design, RandomConfigurations) - # ensure number of samples required is D+1 - self.assertEqual(facade.solver.epm_chooser.min_samples_model, 41) - self.assertEqual(facade.solver.intensifier.min_chall, 1) - self.output_dirs.append(scenario.output_dir) diff --git a/tests/test_facade/test_smac_facade.py b/tests/test_facade/test_smac_facade.py deleted file mode 100644 index 51fe23758..000000000 --- a/tests/test_facade/test_smac_facade.py +++ /dev/null @@ -1,577 +0,0 @@ -import os -import shutil -import unittest -import unittest.mock -from contextlib import suppress - -import numpy as np -from ConfigSpace.hyperparameters import UniformFloatHyperparameter -from ConfigSpace.util import get_one_exchange_neighbourhood - -from smac.callbacks import IncorporateRunResultCallback -from smac.configspace import ConfigurationSpace -from smac.epm.random_epm import RandomEPM -from smac.epm.random_forest.rf_mo import MultiObjectiveRandomForest -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances -from smac.epm.utils import get_rng -from smac.facade.smac_ac_facade import SMAC4AC -from smac.initial_design.default_configuration_design import DefaultConfiguration -from smac.initial_design.factorial_design import FactorialInitialDesign -from smac.initial_design.initial_design import InitialDesign -from smac.initial_design.latin_hypercube_design import LHDesign -from smac.initial_design.random_configuration_design import RandomConfigurations -from smac.initial_design.sobol_design import SobolDesign -from smac.intensification.hyperband import Hyperband -from smac.intensification.intensification import Intensifier -from smac.intensification.successive_halving import SuccessiveHalving -from smac.optimizer.acquisition import EI, EIPS, LCB -from smac.optimizer.configuration_chooser.random_chooser import ( - ChooserNoCoolDown, - ChooserProb, -) -from smac.runhistory.runhistory import RunHistory -from smac.runhistory.runhistory2epm import ( - RunHistory2EPM4Cost, - RunHistory2EPM4EIPS, - RunHistory2EPM4LogCost, -) -from smac.scenario.scenario import Scenario -from smac.tae import StatusType -from smac.tae.execute_func import ExecuteTAFuncDict - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestSMACFacade(unittest.TestCase): - def setUp(self): - self.cs = ConfigurationSpace() - self.scenario_dict_default = { - "cs": self.cs, - "run_obj": "quality", - "output_dir": "", - "limit_resources": True, - "deterministic": False, - } - self.scenario = Scenario(self.scenario_dict_default) - self.sh_intensifier_kwargs = { - "n_seeds": 1, - "initial_budget": 1, - "eta": 3, - "min_chall": 1, - "max_budget": 100, - } - self.output_dirs = [] - - def tearDown(self): - for i in range(20): - with suppress(Exception): - dirname = "run_1" + (".OLD" * i) - shutil.rmtree(dirname) - for output_dir in self.output_dirs: - if output_dir: - shutil.rmtree(output_dir, ignore_errors=True) - - #################################################################################################################### - # Test that the objects are constructed correctly - - def test_pass_callable(self): - # Check that SMAC accepts a callable as target algorithm and that it is - # correctly wrapped with ExecuteTaFunc - def target_algorithm(conf, inst): - return 5 - - smac = SMAC4AC(tae_runner=target_algorithm, scenario=self.scenario) - self.assertIsInstance(smac.solver.tae_runner, ExecuteTAFuncDict) - self.assertIs(smac.solver.tae_runner.ta, target_algorithm) - - def test_pass_invalid_tae_runner(self): - self.assertRaisesRegex( - TypeError, - "Argument 'tae_runner' is , but must be either None, a callable or an " - "object implementing BaseRunner.", - SMAC4AC, - tae_runner=1, - scenario=self.scenario, - ) - - def test_pass_tae_runner_objective(self): - self.assertRaisesRegex( - ValueError, - "Objective for the target algorithm runner and the scenario must be the same, but are 'runtime' and " - "'quality'", - SMAC4AC, - tae_runner=lambda: 1, - tae_runner_kwargs={"run_obj": "runtime"}, - scenario=self.scenario, - ) - - def test_construct_runhistory2epm(self): - """Check default setup up for consistency""" - smbo = SMAC4AC(self.scenario) - self.assertTrue(type(smbo.solver.epm_chooser.rh2EPM) == RunHistory2EPM4Cost) - self.assertSetEqual( - set(smbo.solver.epm_chooser.rh2EPM.success_states), - {StatusType.SUCCESS, StatusType.CRASHED, StatusType.MEMOUT}, - ) - self.assertSetEqual(set(smbo.solver.epm_chooser.rh2EPM.impute_state), set()) - self.assertSetEqual(set(smbo.solver.epm_chooser.rh2EPM.consider_for_higher_budgets_state), set()) - - for intensifier in (SuccessiveHalving, Hyperband): - smbo = SMAC4AC( - self.scenario, - intensifier=intensifier, - intensifier_kwargs=self.sh_intensifier_kwargs, - ) - self.assertTrue(type(smbo.solver.epm_chooser.rh2EPM) == RunHistory2EPM4Cost) - self.assertSetEqual( - set(smbo.solver.epm_chooser.rh2EPM.success_states), - { - StatusType.SUCCESS, - StatusType.CRASHED, - StatusType.MEMOUT, - StatusType.DONOTADVANCE, - }, - ) - self.assertSetEqual(set(smbo.solver.epm_chooser.rh2EPM.impute_state), set()) - self.assertSetEqual( - set(smbo.solver.epm_chooser.rh2EPM.consider_for_higher_budgets_state), - set( - [ - StatusType.DONOTADVANCE, - StatusType.TIMEOUT, - StatusType.CRASHED, - StatusType.MEMOUT, - ] - ), - ) - - self.scenario.run_obj = "runtime" - smbo = SMAC4AC(self.scenario) - self.assertTrue(type(smbo.solver.epm_chooser.rh2EPM) == RunHistory2EPM4LogCost) - self.assertSetEqual( - set(smbo.solver.epm_chooser.rh2EPM.success_states), - { - StatusType.SUCCESS, - }, - ) - self.assertSetEqual( - set(smbo.solver.epm_chooser.rh2EPM.impute_state), - { - StatusType.CAPPED, - }, - ) - self.assertSetEqual(set(smbo.solver.epm_chooser.rh2EPM.consider_for_higher_budgets_state), set()) - - def test_construct_runhistory(self): - - smbo = SMAC4AC(self.scenario) - self.assertIsInstance(smbo.solver.runhistory, RunHistory) - self.assertFalse(smbo.solver.runhistory.overwrite_existing_runs) - smbo = SMAC4AC(self.scenario, runhistory_kwargs={"overwrite_existing_runs": True}) - self.assertIsInstance(smbo.solver.runhistory, RunHistory) - self.assertTrue(smbo.solver.runhistory.overwrite_existing_runs) - smbo = SMAC4AC(self.scenario, runhistory=RunHistory) - self.assertIsInstance(smbo.solver.runhistory, RunHistory) - - def test_construct_random_configuration_chooser(self): - rng = np.random.RandomState(42) - smbo = SMAC4AC(self.scenario) - self.assertIsInstance(smbo.solver.epm_chooser.random_configuration_chooser, ChooserProb) - self.assertIsNot(smbo.solver.epm_chooser.random_configuration_chooser, rng) - smbo = SMAC4AC(self.scenario, rng=rng) - self.assertIsInstance(smbo.solver.epm_chooser.random_configuration_chooser, ChooserProb) - self.assertIs(smbo.solver.epm_chooser.random_configuration_chooser.rng, rng) - smbo = SMAC4AC(self.scenario, random_configuration_chooser_kwargs={"rng": rng}) - self.assertIsInstance(smbo.solver.epm_chooser.random_configuration_chooser, ChooserProb) - self.assertIs(smbo.solver.epm_chooser.random_configuration_chooser.rng, rng) - smbo = SMAC4AC(self.scenario, random_configuration_chooser_kwargs={"prob": 0.1}) - self.assertIsInstance(smbo.solver.epm_chooser.random_configuration_chooser, ChooserProb) - self.assertEqual(smbo.solver.epm_chooser.random_configuration_chooser.prob, 0.1) - smbo = SMAC4AC( - self.scenario, - random_configuration_chooser=ChooserNoCoolDown, - random_configuration_chooser_kwargs={"modulus": 10}, - ) - self.assertIsInstance(smbo.solver.epm_chooser.random_configuration_chooser, ChooserNoCoolDown) - # Check for construction failure on wrong argument - with self.assertRaisesRegex(Exception, "got an unexpected keyword argument"): - SMAC4AC(self.scenario, random_configuration_chooser_kwargs={"dummy": 0.1}) - - def test_construct_epm(self): - rng = np.random.RandomState(42) - smbo = SMAC4AC(self.scenario) - self.assertIsInstance(smbo.solver.epm_chooser.model, RandomForestWithInstances) - smbo = SMAC4AC(self.scenario, rng=rng) - self.assertIsInstance(smbo.solver.epm_chooser.model, RandomForestWithInstances) - self.assertEqual(smbo.solver.epm_chooser.model.seed, 1935803228) - smbo = SMAC4AC(self.scenario, model_kwargs={"seed": 2}) - self.assertIsInstance(smbo.solver.epm_chooser.model, RandomForestWithInstances) - self.assertEqual(smbo.solver.epm_chooser.model.seed, 2) - smbo = SMAC4AC(self.scenario, model_kwargs={"num_trees": 20}) - self.assertIsInstance(smbo.solver.epm_chooser.model, RandomForestWithInstances) - self.assertEqual(smbo.solver.epm_chooser.model.rf_opts.num_trees, 20) - smbo = SMAC4AC(self.scenario, model=RandomEPM, model_kwargs={"seed": 2}) - self.assertIsInstance(smbo.solver.epm_chooser.model, RandomEPM) - self.assertEqual(smbo.solver.epm_chooser.model.seed, 2) - # Check for construction failure on wrong argument - with self.assertRaisesRegex(Exception, "got an unexpected keyword argument"): - SMAC4AC(self.scenario, model_kwargs={"dummy": 0.1}) - - def test_construct_acquisition_function(self): - rng = np.random.RandomState(42) - smbo = SMAC4AC(self.scenario) - self.assertIsInstance(smbo.solver.epm_chooser.acquisition_func, EI) - smbo = SMAC4AC(self.scenario, rng=rng) - self.assertIsInstance(smbo.solver.epm_chooser.acquisition_func.model, RandomForestWithInstances) - self.assertEqual(smbo.solver.epm_chooser.acquisition_func.model.seed, 1935803228) - smbo = SMAC4AC(self.scenario, acquisition_function_kwargs={"par": 17}) - self.assertIsInstance(smbo.solver.epm_chooser.acquisition_func, EI) - self.assertEqual(smbo.solver.epm_chooser.acquisition_func.par, 17) - smbo = SMAC4AC( - self.scenario, - acquisition_function=LCB, - acquisition_function_kwargs={"par": 19}, - ) - self.assertIsInstance(smbo.solver.epm_chooser.acquisition_func, LCB) - self.assertEqual(smbo.solver.epm_chooser.acquisition_func.par, 19) - # Check for construction failure on wrong argument - with self.assertRaisesRegex(Exception, "got an unexpected keyword argument"): - SMAC4AC(self.scenario, acquisition_function_kwargs={"dummy": 0.1}) - - def test_construct_intensifier(self): - class DummyIntensifier(Intensifier): - pass - - rng = np.random.RandomState(42) - smbo = SMAC4AC(self.scenario) - self.assertIsInstance(smbo.solver.intensifier, Intensifier) - self.assertIsNot(smbo.solver.intensifier.rs, rng) - smbo = SMAC4AC(self.scenario, rng=rng) - self.assertIsInstance(smbo.solver.intensifier, Intensifier) - self.assertIs(smbo.solver.intensifier.rs, rng) - smbo = SMAC4AC(self.scenario, intensifier_kwargs={"maxR": 987}) - self.assertEqual(smbo.solver.intensifier.maxR, 987) - smbo = SMAC4AC( - self.scenario, - intensifier=DummyIntensifier, - intensifier_kwargs={"maxR": 987}, - ) - self.assertIsInstance(smbo.solver.intensifier, DummyIntensifier) - self.assertEqual(smbo.solver.intensifier.maxR, 987) - - dummy_intensifier = DummyIntensifier(stats=None, traj_logger=None, rng=rng, instances=self.scenario.train_insts) - smbo = SMAC4AC(self.scenario, intensifier=dummy_intensifier) - self.assertEqual(smbo.solver.intensifier, dummy_intensifier) - - # Assert that minR, maxR and use_ta_time propagate from scenario to the default intensifier. - for scenario_dict in [ - {}, - { - "minR": self.scenario.minR + 1, - "maxR": self.scenario.maxR + 1, - "use_ta_time": not self.scenario.use_ta_time, - }, - ]: - for k, v in self.scenario_dict_default.items(): - if k not in scenario_dict: - scenario_dict[k] = v - scenario = Scenario(scenario_dict) - smac = SMAC4AC(scenario=scenario) - self.assertEqual(scenario.minR, smac.solver.intensifier.minR) - self.assertEqual(scenario.maxR, smac.solver.intensifier.maxR) - self.assertEqual(scenario.use_ta_time, smac.solver.intensifier.use_ta_time_bound) - - def test_construct_initial_design(self): - - rng = np.random.RandomState(42) - smbo = SMAC4AC(self.scenario) - self.assertIsInstance(smbo.solver.initial_design, DefaultConfiguration) - self.assertIsNot(smbo.solver.intensifier.rs, rng) - smbo = SMAC4AC(self.scenario, rng=rng) - self.assertIsInstance(smbo.solver.intensifier, Intensifier) - self.assertIs(smbo.solver.intensifier.rs, rng) - smbo = SMAC4AC(self.scenario, intensifier_kwargs={"maxR": 987}) - self.assertEqual(smbo.solver.intensifier.maxR, 987) - smbo = SMAC4AC( - self.scenario, - initial_design=InitialDesign, - initial_design_kwargs={"configs": "dummy"}, - ) - self.assertIsInstance(smbo.solver.initial_design, InitialDesign) - self.assertEqual(smbo.solver.initial_design.configs, "dummy") - - for initial_incumbent_string, expected_instance in ( - ("DEFAULT", DefaultConfiguration), - ("RANDOM", RandomConfigurations), - ("LHD", LHDesign), - ("FACTORIAL", FactorialInitialDesign), - ("SOBOL", SobolDesign), - ): - self.scenario.initial_incumbent = initial_incumbent_string - smbo = SMAC4AC(self.scenario) - self.assertIsInstance(smbo.solver.initial_design, expected_instance) - - def test_init_EIPS_as_arguments(self): - for objective in ["runtime", "quality"]: - self.scenario.run_obj = objective - smbo = SMAC4AC( - self.scenario, - model=MultiObjectiveRandomForest, - model_kwargs={"target_names": ["a", "b"], "model_kwargs": {"seed": 1}}, - acquisition_function=EIPS, - runhistory2epm=RunHistory2EPM4EIPS, - ).solver - self.assertIsInstance( - smbo.epm_chooser.model, - MultiObjectiveRandomForest, - ) - self.assertIsInstance(smbo.epm_chooser.acquisition_func, EIPS) - self.assertIsInstance( - smbo.epm_chooser.acquisition_func.model, - MultiObjectiveRandomForest, - ) - self.assertIsInstance(smbo.epm_chooser.rh2EPM, RunHistory2EPM4EIPS) - - with self.assertRaisesRegex(TypeError, "the surrogate model must support multi-objective prediction!"): - SMAC4AC(self.scenario, acquisition_function=EIPS, runhistory2epm=RunHistory2EPM4EIPS) - - #################################################################################################################### - # Other tests... - - @unittest.mock.patch.object(SMAC4AC, "__init__") - def test_check_random_states(self, patch): - patch.return_value = None - smac = SMAC4AC() - smac.logger = unittest.mock.MagicMock() - - # Check some properties - # Check whether different seeds give different random states - _, rng_1 = get_rng(1) - _, rng_2 = get_rng(2) - self.assertNotEqual(sum(rng_1.get_state()[1] - rng_2.get_state()[1]), 0) - - # Check whether no seeds gives different random states - _, rng_1 = get_rng(logger=smac.logger) - self.assertEqual(smac.logger.debug.call_count, 1) - _, rng_2 = get_rng(logger=smac.logger) - self.assertEqual(smac.logger.debug.call_count, 2) - - self.assertNotEqual(sum(rng_1.get_state()[1] - rng_2.get_state()[1]), 0) - - # Check whether the same int seeds give the same random states - _, rng_1 = get_rng(1) - _, rng_2 = get_rng(1) - self.assertEqual(sum(rng_1.get_state()[1] - rng_2.get_state()[1]), 0) - - # Check all execution paths - self.assertRaisesRegex( - TypeError, - "Argument rng accepts only arguments of type None, int or np.random.RandomState, " - "you provided .", - get_rng, - rng="ABC", - ) - self.assertRaisesRegex( - TypeError, - "Argument run_id accepts only arguments of type None, int, you provided .", - get_rng, - run_id="ABC", - ) - - run_id, rng_1 = get_rng(rng=None, run_id=None, logger=smac.logger) - self.assertIsInstance(run_id, int) - self.assertIsInstance(rng_1, np.random.RandomState) - self.assertEqual(smac.logger.debug.call_count, 3) - - run_id, rng_1 = get_rng(rng=None, run_id=1, logger=smac.logger) - self.assertEqual(run_id, 1) - self.assertIsInstance(rng_1, np.random.RandomState) - - run_id, rng_1 = get_rng(rng=1, run_id=None, logger=smac.logger) - self.assertEqual(run_id, 1) - self.assertIsInstance(rng_1, np.random.RandomState) - - run_id, rng_1 = get_rng(rng=1, run_id=1337, logger=smac.logger) - self.assertEqual(run_id, 1337) - self.assertIsInstance(rng_1, np.random.RandomState) - - rs = np.random.RandomState(1) - run_id, rng_1 = get_rng(rng=rs, run_id=None, logger=smac.logger) - self.assertIsInstance(run_id, int) - self.assertIs(rng_1, rs) - - run_id, rng_1 = get_rng(rng=rs, run_id=2505, logger=smac.logger) - self.assertEqual(run_id, 2505) - self.assertIs(rng_1, rs) - - @unittest.mock.patch("smac.optimizer.acquisition.maximizer.get_one_exchange_neighbourhood") - def test_check_deterministic_rosenbrock(self, patch): - - # Make SMAC a bit faster - patch.side_effect = lambda configuration, seed: get_one_exchange_neighbourhood( - configuration=configuration, - stdev=0.05, - num_neighbors=2, - seed=seed, - ) - - def rosenbrock_2d(x): - x1 = x["x1"] - x2 = x["x2"] - val = 100.0 * (x2 - x1**2.0) ** 2.0 + (1 - x1) ** 2.0 - return val - - def opt_rosenbrock(): - cs = ConfigurationSpace() - - cs.add_hyperparameter(UniformFloatHyperparameter("x1", -5, 5, default_value=-3)) - cs.add_hyperparameter(UniformFloatHyperparameter("x2", -5, 5, default_value=-4)) - - scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternatively runtime) - "runcount-limit": 50, # maximum function evaluations - "cs": cs, # configuration space - "deterministic": True, - "limit_resources": True, - "intensification_percentage": 0.000000001, - } - ) - - smac = SMAC4AC( - scenario=scenario, - rng=np.random.RandomState(42), - tae_runner=rosenbrock_2d, - ) - incumbent = smac.optimize() - return incumbent, smac.scenario.output_dir - - i1, output_dir = opt_rosenbrock() - self.output_dirs.append(output_dir) - x1_1 = i1.get("x1") - x2_1 = i1.get("x2") - i2, output_dir = opt_rosenbrock() - self.output_dirs.append(output_dir) - x1_2 = i2.get("x1") - x2_2 = i2.get("x2") - self.assertAlmostEqual(x1_1, x1_2) - self.assertAlmostEqual(x2_1, x2_2) - - def test_get_runhistory_and_trajectory_and_tae_runner(self): - def func(x): - return x**2 - - smac = SMAC4AC(tae_runner=func, scenario=self.scenario) - self.assertRaises(ValueError, smac.get_runhistory) - self.assertRaises(ValueError, smac.get_trajectory) - smac.trajectory = "dummy" - self.assertEqual(smac.get_trajectory(), "dummy") - smac.runhistory = "dummy" - self.assertEqual(smac.get_runhistory(), "dummy") - self.assertEqual(smac.get_tae_runner().ta, func) - - def test_output_structure(self): - """Test whether output-dir is moved correctly.""" - test_scenario_dict = { - "output_dir": "tests/test_files/scenario_test/tmp_output", - "run_obj": "quality", - "cs": ConfigurationSpace(), - } - scen1 = Scenario(test_scenario_dict) - self.output_dirs.append(scen1.output_dir) - smac = SMAC4AC(scenario=scen1, run_id=1) - - self.assertEqual(smac.output_dir, os.path.join(test_scenario_dict["output_dir"], "run_1")) - self.assertTrue(os.path.isdir(smac.output_dir)) - - smac2 = SMAC4AC(scenario=scen1, run_id=1) - self.assertTrue(os.path.isdir(smac2.output_dir + ".OLD")) - - smac3 = SMAC4AC(scenario=scen1, run_id=1) - self.assertTrue(os.path.isdir(smac3.output_dir + ".OLD.OLD")) - - smac4 = SMAC4AC(scenario=scen1, run_id=2) - self.assertEqual(smac4.output_dir, os.path.join(test_scenario_dict["output_dir"], "run_2")) - self.assertTrue(os.path.isdir(smac4.output_dir)) - self.assertFalse(os.path.isdir(smac4.output_dir + ".OLD.OLD.OLD")) - - # clean up (at least whats not cleaned up by tearDown) - shutil.rmtree(smac.output_dir + ".OLD.OLD") - shutil.rmtree(smac.output_dir + ".OLD") - # This is done by teardown! - # shutil.rmtree(smac.output_dir) - shutil.rmtree(smac4.output_dir) - - def test_no_output(self): - """Test whether a scenario with "" as output really does not create an - output.""" - test_scenario_dict = { - "output_dir": "", - "run_obj": "quality", - "cs": ConfigurationSpace(), - } - scen1 = Scenario(test_scenario_dict) - smac = SMAC4AC(scenario=scen1, run_id=1) - self.assertFalse(os.path.isdir(smac.output_dir)) - - def test_register_callback(self): - smac = SMAC4AC(scenario=self.scenario, run_id=1) - - with self.assertRaisesRegex(ValueError, "Cannot register callback of type "): - smac.register_callback(lambda: 1) - - with self.assertRaisesRegex(ValueError, "Cannot register callback of type "): - smac.register_callback(IncorporateRunResultCallback) - - smac.register_callback(IncorporateRunResultCallback()) - self.assertEqual(len(smac.solver._callbacks["_incorporate_run_results"]), 1) - - class SubClass(IncorporateRunResultCallback): - pass - - smac.register_callback(SubClass()) - self.assertEqual(len(smac.solver._callbacks["_incorporate_run_results"]), 2) - - def test_set_limit_resources_with_tae_func_dict(self): - # To optimize, we pass the function to the SMAC-object - def tmp(**kwargs): - return 1 - - scenario = Scenario( - { - "cs": self.cs, - "run_obj": "quality", - "output_dir": "", - "limit_resources": True, - } - ) - smac = SMAC4AC(scenario=scenario, tae_runner=tmp, rng=1) - self.assertTrue(smac.solver.tae_runner.use_pynisher) - self.assertIsNone(smac.solver.tae_runner.memory_limit) - - scenario = Scenario( - { - "cs": self.cs, - "run_obj": "quality", - "output_dir": "", - "memory_limit": 333, - "limit_resources": True, - } - ) - smac = SMAC4AC(scenario=scenario, tae_runner=tmp, rng=1) - self.assertTrue(smac.solver.tae_runner.use_pynisher) - self.assertEqual(smac.solver.tae_runner.memory_limit, 333) - - scenario = Scenario( - { - "cs": self.cs, - "run_obj": "quality", - "output_dir": "", - "memory_limit": 333, - "limit_resources": False, - } - ) - smac = SMAC4AC(scenario=scenario, tae_runner=tmp, rng=1) - self.assertFalse(smac.solver.tae_runner.use_pynisher) - self.assertEqual(smac.solver.tae_runner.memory_limit, 333) diff --git a/tests/test_files/example_run/configspace.json b/tests/test_files/example_run/configspace.json deleted file mode 100644 index ebe9cb128..000000000 --- a/tests/test_files/example_run/configspace.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "hyperparameters": [ - { - "name": "x0", - "type": "uniform_float", - "log": false, - "lower": -5.0, - "upper": 10.0, - "default": -3.0 - }, - { - "name": "x1", - "type": "uniform_float", - "log": false, - "lower": -5.0, - "upper": 10.0, - "default": -4.0 - } - ], - "conditions": [], - "forbiddens": [], - "python_module_version": "0.5.0", - "json_format_version": 0.3 -} \ No newline at end of file diff --git a/tests/test_files/example_run/configspace.pcs b/tests/test_files/example_run/configspace.pcs deleted file mode 100644 index 149264b36..000000000 --- a/tests/test_files/example_run/configspace.pcs +++ /dev/null @@ -1,2 +0,0 @@ -x0 real [-5.0, 10.0] [-3.0] -x1 real [-5.0, 10.0] [-4.0] \ No newline at end of file diff --git a/tests/test_files/example_run/runhistory.json b/tests/test_files/example_run/runhistory.json deleted file mode 100644 index 8d03b198d..000000000 --- a/tests/test_files/example_run/runhistory.json +++ /dev/null @@ -1,467 +0,0 @@ -{ - "data": [ - [ - [ - 1, - null, - 0, - 0.0 - ], - [ - 35306.427514114344, - 7.3909759521484375e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580052.286752, - 1656580052.2867658, - {} - ] - ], - [ - [ - 2, - null, - 0, - 0.0 - ], - [ - 4790.9918833769925, - 9.298324584960938e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580052.2892885, - 1656580052.2893078, - {} - ] - ], - [ - [ - 4, - null, - 0, - 0.0 - ], - [ - 16916.0, - 8.821487426757812e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580052.2927234, - 1656580052.2927403, - {} - ] - ], - [ - [ - 6, - null, - 0, - 0.0 - ], - [ - 10608.016312533568, - 1.2874603271484375e-05, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580052.2975984, - 1656580052.2976253, - {} - ] - ], - [ - [ - 8, - null, - 0, - 0.0 - ], - [ - 353670.81639177905, - 8.58306884765625e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580052.3019018, - 1656580052.30192, - {} - ] - ], - [ - [ - 9, - null, - 0, - 0.0 - ], - [ - 586130.2616971313, - 7.867813110351562e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580052.3053734, - 1656580052.3053882, - {} - ] - ], - [ - [ - 11, - null, - 0, - 0.0 - ], - [ - 3069.4098218212166, - 7.62939453125e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580053.0159395, - 1656580053.0159583, - {} - ] - ], - [ - [ - 13, - null, - 0, - 0.0 - ], - [ - 3069.4378507187926, - 7.867813110351562e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580053.0301464, - 1656580053.0301633, - {} - ] - ], - [ - [ - 14, - null, - 0, - 0.0 - ], - [ - 22453.13089714366, - 9.5367431640625e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580054.1407666, - 1656580054.1407883, - {} - ] - ], - [ - [ - 17, - null, - 0, - 0.0 - ], - [ - 1122.0405239677336, - 1.239776611328125e-05, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580055.4874706, - 1656580055.4875, - {} - ] - ], - [ - [ - 18, - null, - 0, - 0.0 - ], - [ - 1114.7602703228965, - 1.4066696166992188e-05, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580055.5124798, - 1656580055.51251, - {} - ] - ], - [ - [ - 19, - null, - 0, - 0.0 - ], - [ - 1125.9942517107133, - 7.152557373046875e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580055.5299509, - 1656580055.529967, - {} - ] - ], - [ - [ - 20, - null, - 0, - 0.0 - ], - [ - 276.63099176419354, - 7.3909759521484375e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580056.6161146, - 1656580056.6161325, - {} - ] - ], - [ - [ - 22, - null, - 0, - 0.0 - ], - [ - 292.9345783569001, - 1.2874603271484375e-05, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580056.638499, - 1656580056.6385262, - {} - ] - ], - [ - [ - 23, - null, - 0, - 0.0 - ], - [ - 16.631655546321536, - 8.58306884765625e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580057.808115, - 1656580057.8081357, - {} - ] - ], - [ - [ - 25, - null, - 0, - 0.0 - ], - [ - 16.655482927330727, - 7.62939453125e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580057.8390152, - 1656580057.8390324, - {} - ] - ], - [ - [ - 26, - null, - 0, - 0.0 - ], - [ - 5921.25150782499, - 7.62939453125e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580059.0303519, - 1656580059.0303702, - {} - ] - ], - [ - [ - 30, - null, - 0, - 0.0 - ], - [ - 37.17181126173809, - 7.62939453125e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580060.185514, - 1656580060.185532, - {} - ] - ], - [ - [ - 32, - null, - 0, - 0.0 - ], - [ - 5052.653708552866, - 6.9141387939453125e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580061.4077194, - 1656580061.407737, - {} - ] - ], - [ - [ - 35, - null, - 0, - 0.0 - ], - [ - 499.63250389257433, - 7.152557373046875e-06, - { - "__enum__": "StatusType.SUCCESS" - }, - 1656580062.6834888, - 1656580062.6835065, - {} - ] - ] - ], - "config_origins": { - "1": "Sobol", - "2": "Sobol", - "6": "Sobol", - "8": "Sobol", - "9": "Sobol", - "11": "Local Search", - "13": "Local Search", - "14": "Local Search", - "17": "Local Search", - "18": "Local Search", - "19": "Local Search", - "20": "Local Search", - "22": "Local Search", - "23": "Local Search", - "25": "Local Search", - "26": "Local Search", - "30": "Local Search", - "32": "Local Search", - "35": "Local Search" - }, - "configs": { - "1": { - "x0": 3.9592544734477997, - "x1": -3.1119782524183393 - }, - "2": { - "x0": 0.18435552716255188, - "x1": 6.955205500125885 - }, - "4": { - "x0": -3.0, - "x1": -4.0 - }, - "6": { - "x0": -3.3045367896556854, - "x1": 0.6294399499893188 - }, - "8": { - "x0": 7.916896343231201, - "x1": 3.2110410928726196 - }, - "9": { - "x0": 8.741189241409302, - "x1": -0.1468414068222046 - }, - "11": { - "x0": -0.7338459842998875, - "x1": -4.998981786821483 - }, - "13": { - "x0": -0.7350118937940833, - "x1": -4.997290887842019 - }, - "14": { - "x0": -4.996274385480495, - "x1": 9.99039132530636 - }, - "17": { - "x0": 2.50241005426406, - "x1": 2.9157397646922227 - }, - "18": { - "x0": 2.501623772758304, - "x1": 2.922697394910098 - }, - "19": { - "x0": 2.5033081137958684, - "x1": 2.9143368469972177 - }, - "20": { - "x0": -2.894621968919399, - "x1": 9.995817825477454 - }, - "22": { - "x0": -2.8858075002583674, - "x1": 9.994723488300119 - }, - "23": { - "x0": -0.9143679946679786, - "x1": 1.1961639660928434 - }, - "25": { - "x0": -0.9140660380949015, - "x1": 1.1959585915023512 - }, - "26": { - "x0": 1.5181997972097419, - "x1": 9.999723004029732 - }, - "30": { - "x0": 2.6077548954523264, - "x1": 7.388492179125169 - }, - "32": { - "x0": 2.277884729585529, - "x1": -1.918294499713205 - }, - "35": { - "x0": 0.8904953064181615, - "x1": 3.0282011475923767 - } - } -} \ No newline at end of file diff --git a/tests/test_files/example_run/scenario.txt b/tests/test_files/example_run/scenario.txt deleted file mode 100644 index 9086d36c5..000000000 --- a/tests/test_files/example_run/scenario.txt +++ /dev/null @@ -1,14 +0,0 @@ -execdir = . -deterministic = True -run_obj = quality -multi_objectives = cost -overall_obj = par10 -save_results_instantly = True -par_factor = 10 -cost_for_crash = 2147483647.0 -algo_runs_timelimit = inf -wallclock_limit = inf -always_race_default = False -ta_run_limit = 20.0 -initial_incumbent = DEFAULT -pcs_fn = psmac3-output_2022-06-30_11:07:32_274878/run_0/configspace.json diff --git a/tests/test_files/example_run/stats.json b/tests/test_files/example_run/stats.json deleted file mode 100644 index 7cc47ef5e..000000000 --- a/tests/test_files/example_run/stats.json +++ /dev/null @@ -1 +0,0 @@ -{"submitted_ta_runs": 20, "finished_ta_runs": 20, "n_configs": 35, "wallclock_time_used": 10.399263620376587, "ta_time_used": 0.000179290771484375, "inc_changed": 7, "_n_configs_per_intensify": 0, "_n_calls_of_intensify": 14, "_ema_n_configs_per_intensifiy": 0.0, "_EMA_ALPHA": 0.2} \ No newline at end of file diff --git a/tests/test_files/example_run/traj.json b/tests/test_files/example_run/traj.json deleted file mode 100644 index 90e9fdac4..000000000 --- a/tests/test_files/example_run/traj.json +++ /dev/null @@ -1,8 +0,0 @@ -{"cpu_time": 0.0, "wallclock_time": 0.0006117820739746094, "evaluations": 0, "cost": 2147483648.0, "incumbent": {"x0": 3.9592544734477997, "x1": -3.1119782524183393}, "budget": 0, "origin": "Sobol"} -{"cpu_time": 7.3909759521484375e-06, "wallclock_time": 0.0018901824951171875, "evaluations": 1, "cost": 35306.4275, "incumbent": {"x0": 3.9592544734477997, "x1": -3.1119782524183393}, "budget": 0, "origin": "Sobol"} -{"cpu_time": 1.6689300537109375e-05, "wallclock_time": 0.004310131072998047, "evaluations": 2, "cost": 4790.9919, "incumbent": {"x0": 0.18435552716255188, "x1": 6.955205500125885}, "budget": 0, "origin": "Sobol"} -{"cpu_time": 6.246566772460938e-05, "wallclock_time": 0.7310254573822021, "evaluations": 7, "cost": 3069.4098, "incumbent": {"x0": -0.7338459842998875, "x1": -4.998981786821483}, "budget": 0, "origin": "Local Search"} -{"cpu_time": 9.226799011230469e-05, "wallclock_time": 3.2085988521575928, "evaluations": 10, "cost": 1122.0405, "incumbent": {"x0": 2.50241005426406, "x1": 2.9157397646922227}, "budget": 0, "origin": "Local Search"} -{"cpu_time": 0.00010633468627929688, "wallclock_time": 3.2276248931884766, "evaluations": 11, "cost": 1114.7603, "incumbent": {"x0": 2.501623772758304, "x1": 2.922697394910098}, "budget": 0, "origin": "Local Search"} -{"cpu_time": 0.00012087821960449219, "wallclock_time": 4.331298589706421, "evaluations": 13, "cost": 276.631, "incumbent": {"x0": -2.894621968919399, "x1": 9.995817825477454}, "budget": 0, "origin": "Local Search"} -{"cpu_time": 0.0001423358917236328, "wallclock_time": 5.523259878158569, "evaluations": 15, "cost": 16.6317, "incumbent": {"x0": -0.9143679946679786, "x1": 1.1961639660928434}, "budget": 0, "origin": "Local Search"} diff --git a/tests/test_files/example_run/traj_aclib2.json b/tests/test_files/example_run/traj_aclib2.json deleted file mode 100644 index f8bd47c30..000000000 --- a/tests/test_files/example_run/traj_aclib2.json +++ /dev/null @@ -1,8 +0,0 @@ -{"cpu_time": 0.0, "wallclock_time": 0.0006117820739746094, "evaluations": 0, "cost": 2147483648.0, "incumbent": ["x0='3.9592544734477997'", "x1='-3.1119782524183393'"], "origin": "Sobol"} -{"cpu_time": 7.3909759521484375e-06, "wallclock_time": 0.0018901824951171875, "evaluations": 1, "cost": 35306.4275, "incumbent": ["x0='3.9592544734477997'", "x1='-3.1119782524183393'"], "origin": "Sobol"} -{"cpu_time": 1.6689300537109375e-05, "wallclock_time": 0.004310131072998047, "evaluations": 2, "cost": 4790.9919, "incumbent": ["x0='0.18435552716255188'", "x1='6.955205500125885'"], "origin": "Sobol"} -{"cpu_time": 6.246566772460938e-05, "wallclock_time": 0.7310254573822021, "evaluations": 7, "cost": 3069.4098, "incumbent": ["x0='-0.7338459842998875'", "x1='-4.998981786821483'"], "origin": "Local Search"} -{"cpu_time": 9.226799011230469e-05, "wallclock_time": 3.2085988521575928, "evaluations": 10, "cost": 1122.0405, "incumbent": ["x0='2.50241005426406'", "x1='2.9157397646922227'"], "origin": "Local Search"} -{"cpu_time": 0.00010633468627929688, "wallclock_time": 3.2276248931884766, "evaluations": 11, "cost": 1114.7603, "incumbent": ["x0='2.501623772758304'", "x1='2.922697394910098'"], "origin": "Local Search"} -{"cpu_time": 0.00012087821960449219, "wallclock_time": 4.331298589706421, "evaluations": 13, "cost": 276.631, "incumbent": ["x0='-2.894621968919399'", "x1='9.995817825477454'"], "origin": "Local Search"} -{"cpu_time": 0.0001423358917236328, "wallclock_time": 5.523259878158569, "evaluations": 15, "cost": 16.6317, "incumbent": ["x0='-0.9143679946679786'", "x1='1.1961639660928434'"], "origin": "Local Search"} diff --git a/tests/test_files/example_run/traj_old.csv b/tests/test_files/example_run/traj_old.csv deleted file mode 100644 index e664f399f..000000000 --- a/tests/test_files/example_run/traj_old.csv +++ /dev/null @@ -1,9 +0,0 @@ -"CPU Time Used","Estimated Training Performance","Wallclock Time","Incumbent ID","Automatic Configurator (CPU) Time","Configuration..." -0.000000, 2147483648.000000, 0.000612, 1, 0.000612, x0='3.9592544734477997',x1='-3.1119782524183393' -0.000007, 35306.427500, 0.001890, 1, 0.001883, x0='3.9592544734477997',x1='-3.1119782524183393' -0.000017, 4790.991900, 0.004310, 2, 0.004293, x0='0.18435552716255188',x1='6.955205500125885' -0.000062, 3069.409800, 0.731025, 3, 0.730963, x0='-0.7338459842998875',x1='-4.998981786821483' -0.000092, 1122.040500, 3.208599, 4, 3.208507, x0='2.50241005426406',x1='2.9157397646922227' -0.000106, 1114.760300, 3.227625, 5, 3.227519, x0='2.501623772758304',x1='2.922697394910098' -0.000121, 276.631000, 4.331299, 6, 4.331178, x0='-2.894621968919399',x1='9.995817825477454' -0.000142, 16.631700, 5.523260, 7, 5.523118, x0='-0.9143679946679786',x1='1.1961639660928434' diff --git a/tests/test_files/example_ta.py b/tests/test_files/example_ta.py deleted file mode 100644 index 109f1c9d5..000000000 --- a/tests/test_files/example_ta.py +++ /dev/null @@ -1,19 +0,0 @@ -import sys - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def sample(x): - x1 = x[0] - x2 = x[1] - ret = x1 + x2 - return ret - - -if __name__ == "__main__": - seed = sys.argv[5] - x = float(sys.argv[7]) - y = float(sys.argv[9]) - tmp = sample((x, y)) - print("Result for SMAC: SUCCESS, -1, -1, %f, %s" % (tmp, seed)) diff --git a/tests/test_files/features_example.csv b/tests/test_files/features_example.csv deleted file mode 100644 index 229e2348a..000000000 --- a/tests/test_files/features_example.csv +++ /dev/null @@ -1,4 +0,0 @@ -instance,feature1, feature2, feature3 -inst1,1.0,2.0, 3.0 -inst2,1.5,2.5 ,3.5 -inst3,1.7, 1.8,1.9 diff --git a/tests/test_files/params_branin.pcs b/tests/test_files/params_branin.pcs deleted file mode 100644 index 2cdf2715b..000000000 --- a/tests/test_files/params_branin.pcs +++ /dev/null @@ -1,2 +0,0 @@ -x1 [-5, 10] [1] -x2 [0, 15] [1] \ No newline at end of file diff --git a/tests/test_files/restore_scenario_one.txt b/tests/test_files/restore_scenario_one.txt deleted file mode 100644 index 026a6dbd8..000000000 --- a/tests/test_files/restore_scenario_one.txt +++ /dev/null @@ -1,6 +0,0 @@ -paramfile = examples/commandline/branin/configspace.pcs -run_obj = quality -runcount_limit = 5 -output_dir = tests/test_files/test_restore_state -algo = python examples/commandline/branin.py -deterministic = 0 diff --git a/tests/test_files/restore_scenario_two.txt b/tests/test_files/restore_scenario_two.txt deleted file mode 100644 index e6063f52b..000000000 --- a/tests/test_files/restore_scenario_two.txt +++ /dev/null @@ -1,6 +0,0 @@ -paramfile = examples/commandline/branin/configspace.pcs -run_obj = quality -runcount_limit = 10 -output_dir = tests/test_files/test_restored_state -algo = python examples/commandline/branin.py -deterministic = 0 diff --git a/tests/test_files/scenario_test/features.txt b/tests/test_files/scenario_test/features.txt deleted file mode 100644 index 4e510ee5b..000000000 --- a/tests/test_files/scenario_test/features.txt +++ /dev/null @@ -1,4 +0,0 @@ -id, mockstuff -d, 1 -e, 2 -f, 3 diff --git a/tests/test_files/scenario_test/features_multiple.txt b/tests/test_files/scenario_test/features_multiple.txt deleted file mode 100644 index 79345ca16..000000000 --- a/tests/test_files/scenario_test/features_multiple.txt +++ /dev/null @@ -1,4 +0,0 @@ -id, mockstuff, answer, random -d, 1, 42, 122 -e, 2, -42, 313.5 -f, 3, 42, -3.7 diff --git a/tests/test_files/scenario_test/param.pcs b/tests/test_files/scenario_test/param.pcs deleted file mode 100644 index 91c0ef125..000000000 --- a/tests/test_files/scenario_test/param.pcs +++ /dev/null @@ -1,38 +0,0 @@ -sp-var-dec-heur {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}[0] # Originally, 3,4,9,10 not used following Domagoj's advice. 20 requires modular arithmetic input format -sp-learned-clause-sort-heur {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}[0] # All values make sense here. 20 requires modular arithmetic input format -sp-orig-clause-sort-heur {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}[0] # All values make sense here. 20 requires modular arithmetic input format -sp-res-order-heur {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}[0] # All values make sense here. 20 requires modular arithmetic input format -sp-clause-del-heur {0,1,2}[2] # All values make sense here. -sp-phase-dec-heur {0,1,2,3,4,5,6}[5] # All values make sense here. -sp-resolution {0,1,2}[1] # 0 renders a whole bunch of conditionals irrelevant. -sp-variable-decay {1.1,1.4,2.0}[1.4] # Should be bigger than 1 (o/w increase not decay). -sp-clause-decay {1.1,1.4,2.0}[1.4] # Same thing. -sp-restart-inc {1.1,1.3,1.5,1.7,1.9}[1.5] # Uniform because multiplicative. -sp-learned-size-factor {0.1,0.2,0.4,0.8,1.6}[0.4] # Uniform on logarithmic scale (starting value). -sp-learned-clauses-inc {1.1,1.2,1.3,1.4,1.5}[1.3] # Uniform because multiplicative -sp-clause-activity-inc {0.5,1,1.5}[1] # -sp-var-activity-inc {0.5,1,1.5}[1] # -sp-rand-phase-dec-freq{0, 0.0001, 0.001, 0.005, 0.01, 0.05}[0.001] # Never picked 0.05 in previous experiments, always zero. -sp-rand-var-dec-freq {0, 0.0001, 0.001, 0.005, 0.01, 0.05}[0.001] # Never picked 0.05 in previous experiments, always zero. -sp-rand-var-dec-scaling {0.3,0.6,0.9,1,1.1}[1] # Domagoj said the previous values were too coarse, so finer discretization now. -sp-rand-phase-scaling {0.3,0.6,0.9,1,1.1}[1] # Same thing. -sp-max-res-lit-inc {0.25,0.5,1,2,4}[1] # -sp-first-restart {25,50,100,200,400,800,1600,3200}[100] # Uniform on logarithmic scale (starting value). -sp-res-cutoff-cls {2,4,8,16,20}[8] # Only up to 20 allowed, would've used 32 otherwise. -sp-res-cutoff-lits {100,200,400,800,1600}[400] # -sp-max-res-runs {1,2,4,8,16,32}[4] # -sp-update-dec-queue {0,1}[1] # Enable by default. -sp-use-pure-literal-rule {0,1}[1] # Enable by default. -sp-clause-inversion {0,1}[1] # Enable by default. Enable reversion of learned clauses if fixed order (sp-learned-clause-sort-heur=19) - -Conditionals: -sp-rand-phase-dec-freq|sp-phase-dec-heur in {0,1,3,4,5,6} # when heuristic is random, then additional random steps don't change anything -sp-rand-var-dec-scaling|sp-rand-var-dec-freq in {0.0001, 0.001, 0.005, 0.01, 0.05} # not 0 -sp-rand-phase-scaling|sp-rand-phase-dec-freq in {0.0001, 0.001, 0.005, 0.01, 0.05} # not 0 -sp-clause-inversion|sp-learned-clause-sort-heur in {19} - -sp-res-order-heur|sp-resolution in {1,2} -sp-max-res-lit-inc|sp-resolution in {1,2} -sp-res-cutoff-cls|sp-resolution in {1,2} -sp-res-cutoff-lits|sp-resolution in {1,2} -sp-max-res-runs|sp-resolution in {1,2} diff --git a/tests/test_files/scenario_test/scenario.txt b/tests/test_files/scenario_test/scenario.txt deleted file mode 100644 index ecf7fac90..000000000 --- a/tests/test_files/scenario_test/scenario.txt +++ /dev/null @@ -1,12 +0,0 @@ -algo = echo Hello -paramfile = tests/test_files/scenario_test/param.pcs -execdir = . -deterministic = 0 -run_obj = runtime -overall_obj = mean10 -cutoff_time = 5 -wallclock-limit = 18000 -instance_file = tests/test_files/scenario_test/training.txt -test_instance_file = tests/test_files/scenario_test/test.txt -feature_file = tests/test_files/scenario_test/features.txt -output_dir = \ No newline at end of file diff --git a/tests/test_files/scenario_test/test.txt b/tests/test_files/scenario_test/test.txt deleted file mode 100644 index de980441c..000000000 --- a/tests/test_files/scenario_test/test.txt +++ /dev/null @@ -1,3 +0,0 @@ -a -b -c diff --git a/tests/test_files/scenario_test/training.txt b/tests/test_files/scenario_test/training.txt deleted file mode 100644 index 2bb9b90bc..000000000 --- a/tests/test_files/scenario_test/training.txt +++ /dev/null @@ -1,3 +0,0 @@ -d -e -f diff --git a/tests/test_files/spear-params.pcs b/tests/test_files/spear-params.pcs deleted file mode 100755 index 91c0ef125..000000000 --- a/tests/test_files/spear-params.pcs +++ /dev/null @@ -1,38 +0,0 @@ -sp-var-dec-heur {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}[0] # Originally, 3,4,9,10 not used following Domagoj's advice. 20 requires modular arithmetic input format -sp-learned-clause-sort-heur {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}[0] # All values make sense here. 20 requires modular arithmetic input format -sp-orig-clause-sort-heur {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}[0] # All values make sense here. 20 requires modular arithmetic input format -sp-res-order-heur {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}[0] # All values make sense here. 20 requires modular arithmetic input format -sp-clause-del-heur {0,1,2}[2] # All values make sense here. -sp-phase-dec-heur {0,1,2,3,4,5,6}[5] # All values make sense here. -sp-resolution {0,1,2}[1] # 0 renders a whole bunch of conditionals irrelevant. -sp-variable-decay {1.1,1.4,2.0}[1.4] # Should be bigger than 1 (o/w increase not decay). -sp-clause-decay {1.1,1.4,2.0}[1.4] # Same thing. -sp-restart-inc {1.1,1.3,1.5,1.7,1.9}[1.5] # Uniform because multiplicative. -sp-learned-size-factor {0.1,0.2,0.4,0.8,1.6}[0.4] # Uniform on logarithmic scale (starting value). -sp-learned-clauses-inc {1.1,1.2,1.3,1.4,1.5}[1.3] # Uniform because multiplicative -sp-clause-activity-inc {0.5,1,1.5}[1] # -sp-var-activity-inc {0.5,1,1.5}[1] # -sp-rand-phase-dec-freq{0, 0.0001, 0.001, 0.005, 0.01, 0.05}[0.001] # Never picked 0.05 in previous experiments, always zero. -sp-rand-var-dec-freq {0, 0.0001, 0.001, 0.005, 0.01, 0.05}[0.001] # Never picked 0.05 in previous experiments, always zero. -sp-rand-var-dec-scaling {0.3,0.6,0.9,1,1.1}[1] # Domagoj said the previous values were too coarse, so finer discretization now. -sp-rand-phase-scaling {0.3,0.6,0.9,1,1.1}[1] # Same thing. -sp-max-res-lit-inc {0.25,0.5,1,2,4}[1] # -sp-first-restart {25,50,100,200,400,800,1600,3200}[100] # Uniform on logarithmic scale (starting value). -sp-res-cutoff-cls {2,4,8,16,20}[8] # Only up to 20 allowed, would've used 32 otherwise. -sp-res-cutoff-lits {100,200,400,800,1600}[400] # -sp-max-res-runs {1,2,4,8,16,32}[4] # -sp-update-dec-queue {0,1}[1] # Enable by default. -sp-use-pure-literal-rule {0,1}[1] # Enable by default. -sp-clause-inversion {0,1}[1] # Enable by default. Enable reversion of learned clauses if fixed order (sp-learned-clause-sort-heur=19) - -Conditionals: -sp-rand-phase-dec-freq|sp-phase-dec-heur in {0,1,3,4,5,6} # when heuristic is random, then additional random steps don't change anything -sp-rand-var-dec-scaling|sp-rand-var-dec-freq in {0.0001, 0.001, 0.005, 0.01, 0.05} # not 0 -sp-rand-phase-scaling|sp-rand-phase-dec-freq in {0.0001, 0.001, 0.005, 0.01, 0.05} # not 0 -sp-clause-inversion|sp-learned-clause-sort-heur in {19} - -sp-res-order-heur|sp-resolution in {1,2} -sp-max-res-lit-inc|sp-resolution in {1,2} -sp-res-cutoff-cls|sp-resolution in {1,2} -sp-res-cutoff-lits|sp-resolution in {1,2} -sp-max-res-runs|sp-resolution in {1,2} diff --git a/tests/test_files/spear_hydra_test_scenario.txt b/tests/test_files/spear_hydra_test_scenario.txt deleted file mode 100644 index e90242596..000000000 --- a/tests/test_files/spear_hydra_test_scenario.txt +++ /dev/null @@ -1,10 +0,0 @@ -algo = python3 -u examples/commandline/spear_qcp/target_algorithm/scripts/SATCSSCWrapper.py --mem-limit 1024 --script examples/commandline/spear_qcp/target_algorithm/spear-python/spearCSSCWrapper.py -paramfile = examples/commandline/spear_qcp/target_algorithm/spear-python/spear-params-mixed.pcs -execdir = . -deterministic = 0 -run_obj = runtime -overall_obj = PAR10 -cutoff_time = 5 -wallclock-limit = 5 -instance_file = examples/commandline/spear_qcp/instances.txt -feature_file = examples/commandline/spear_qcp/features.txt diff --git a/tests/test_files/test_deterministic.pcs b/tests/test_files/test_deterministic.pcs deleted file mode 100644 index 7266d40bf..000000000 --- a/tests/test_files/test_deterministic.pcs +++ /dev/null @@ -1,2 +0,0 @@ -x1 [0,15] [0] -x2 [0,15] [0] \ No newline at end of file diff --git a/tests/test_files/test_deterministic_func.py b/tests/test_files/test_deterministic_func.py deleted file mode 100644 index b55a8833d..000000000 --- a/tests/test_files/test_deterministic_func.py +++ /dev/null @@ -1,18 +0,0 @@ -import sys - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -if __name__ == "__main__": - # Unused in this example: - # instance, instance_specific, cutoff, runlength = sys.argv[1:5] - seed = sys.argv[5] - # sys.argv[6] and sys.argv[8] are the names of the target algorithm - # parameters (here: "-x1", "-x2") - x1 = float(sys.argv[7]) - x2 = float(sys.argv[9]) - r1 = x1 % 0.9 - (x1 * 0.5) % 1.6 - r2 = -(x2 * 0.7) % 2.3 + (x2 * 1.5) % 3.5 - result = r1 + r2 - print("Result for SMAC: SUCCESS, -1, -1, %f, %s" % (result, seed)) diff --git a/tests/test_files/test_deterministic_scenario.txt b/tests/test_files/test_deterministic_scenario.txt deleted file mode 100644 index 8b5feac05..000000000 --- a/tests/test_files/test_deterministic_scenario.txt +++ /dev/null @@ -1,5 +0,0 @@ -algo = python tests/test_files/test_deterministic_func.py -paramfile = tests/test_files/test_deterministic.pcs -run_obj = quality -runcount_limit = 3 -deterministic = 1 \ No newline at end of file diff --git a/tests/test_files/test_local_search.pcs b/tests/test_files/test_local_search.pcs deleted file mode 100644 index e6dc0eab1..000000000 --- a/tests/test_files/test_local_search.pcs +++ /dev/null @@ -1,10 +0,0 @@ -param1 {0, 1} [0] -param2 {0, 1} [0] -param3 {0, 1} [0] -param4 {0, 1} [0] -param5 {0, 1} [0] -param6 {0, 1} [0] -param7 {0, 1} [0] -param8 {0, 1} [0] -param9 {0, 1} [0] -param10 {0, 1} [0] \ No newline at end of file diff --git a/tests/test_files/train_insts_example.txt b/tests/test_files/train_insts_example.txt deleted file mode 100644 index ab258e030..000000000 --- a/tests/test_files/train_insts_example.txt +++ /dev/null @@ -1,3 +0,0 @@ -inst1 -inst2 -inst3 diff --git a/tests/test_files/validation/scenario.txt b/tests/test_files/validation/scenario.txt deleted file mode 100644 index 11da455dc..000000000 --- a/tests/test_files/validation/scenario.txt +++ /dev/null @@ -1,2 +0,0 @@ -param_file = tests/test_files/validation/test_validation_pcs.pcs -algo = python -u tests/test_files/example_ta.py diff --git a/tests/test_files/validation/test_validation_pcs.pcs b/tests/test_files/validation/test_validation_pcs.pcs deleted file mode 100644 index 233c070d8..000000000 --- a/tests/test_files/validation/test_validation_pcs.pcs +++ /dev/null @@ -1,2 +0,0 @@ -x1 [-5,10] [0] -x2 [0,15] [0] \ No newline at end of file diff --git a/tests/test_files/validation/test_validation_traj.json b/tests/test_files/validation/test_validation_traj.json deleted file mode 100644 index 25c9f1473..000000000 --- a/tests/test_files/validation/test_validation_traj.json +++ /dev/null @@ -1,11 +0,0 @@ -{"cpu_time": 0, "wallclock_time": 0.001, "cost": 2147483648, "total_cpu_time": null, "incumbent": ["x1='0.0'", "x2='0.0'"], "evaluations": 0} -{"cpu_time": 0.20, "wallclock_time": 0.002, "cost": 55.602112642270264, "total_cpu_time": null, "incumbent": ["x1='0.0'", "x2='0.0'"], "evaluations": 1} -{"cpu_time": 0.22, "wallclock_time": 0.500, "cost": 55.602112642270264, "total_cpu_time": null, "incumbent": ["x1='0.1'", "x2='0.1'"], "evaluations": 1} -{"cpu_time": 0.50, "wallclock_time": 1.902, "cost": 17.641576428006122, "total_cpu_time": null, "incumbent": ["x1='0.655920760774074'", "x2='4.8326801576775065'"], "evaluations": 2} -{"cpu_time": 1.00, "wallclock_time": 2.001, "cost": 14.014540297459412, "total_cpu_time": null, "incumbent": ["x1='3.7496937839189446'", "x2='5.297497302145709'"], "evaluations": 4} -{"cpu_time": 1.50, "wallclock_time": 2.020, "cost": 13.031911360272302, "total_cpu_time": null, "incumbent": ["x1='3.5195197564687604'", "x2='5.456526646632339'"], "evaluations": 8} -{"cpu_time": 2.10, "wallclock_time": 4.000, "cost": 3.5323124593076036, "total_cpu_time": null, "incumbent": ["x1='2.832941033815576'", "x2='0.8907371029600825'"], "evaluations": 15} -{"cpu_time": 2.30, "wallclock_time": 4.200, "cost": 2.422682519326294, "total_cpu_time": null, "incumbent": ["x1='-3.800505065373173'", "x2='14.03575351720709'"], "evaluations": 32} -{"cpu_time": 4.20, "wallclock_time": 8.080, "cost": 0.950842566916382, "total_cpu_time": null, "incumbent": ["x1='9.398646099038757'", "x2='3.194447405521492'"], "evaluations": 64} -{"cpu_time": 8.10, "wallclock_time": 16.00, "cost": 0.5566360045103647, "total_cpu_time": null, "incumbent": ["x1='9.562579003361625'", "x2='2.853931383384232'"], "evaluations": 100} -{"cpu_time": 16.8, "wallclock_time": 32.32, "cost": 0.41989158144444794, "total_cpu_time": null, "incumbent": ["x1='-3.1470294216090715'", "x2='12.14021057882007'"], "evaluations": 129} diff --git a/tests/test_initial_design/test_factorical_design.py b/tests/test_initial_design/test_factorical_design.py index 68ddbd1dd..b963fa70b 100644 --- a/tests/test_initial_design/test_factorical_design.py +++ b/tests/test_initial_design/test_factorical_design.py @@ -1,6 +1,3 @@ -import unittest -import unittest.mock - import numpy as np from ConfigSpace import ( CategoricalHyperparameter, @@ -16,42 +13,39 @@ __license__ = "3-clause BSD" -class TestFactorial(unittest.TestCase): - def test_factorial(self): - def get_uniform_param(name: str): - return UniformFloatHyperparameter(name, 0, 1) - - def get_constant_param(name: str): - return Constant(name, 0.0) - - def get_categorical_param(name: str): - return CategoricalHyperparameter(name, choices=["a", "b", "c"]) - - def get_ordinal_param(name: str): - return OrdinalHyperparameter(name, [8, 6, 4, 2]) - - get_params = [ - get_uniform_param, - get_constant_param, - get_categorical_param, - get_ordinal_param, - ] - - dims = np.arange(1, 5) - for n_dim in dims: - cs = ConfigurationSpace() - for i in range(n_dim): - for j, get_param in enumerate(get_params): - param_name = f"x{i+1}_{j}" - cs.add_hyperparameter(get_param(param_name)) - - factorial_kwargs = dict( - rng=np.random.RandomState(1), - traj_logger=unittest.mock.Mock(), - ta_run_limit=1000, - configs=None, - n_configs_x_params=None, - max_config_fracs=0.25, - init_budget=1, - ) - FactorialInitialDesign(cs=cs, **factorial_kwargs).select_configurations() +def test_factorial(make_scenario): + def get_uniform_param(name: str): + return UniformFloatHyperparameter(name, 0, 1) + + def get_constant_param(name: str): + return Constant(name, 0.0) + + def get_categorical_param(name: str): + return CategoricalHyperparameter(name, choices=["a", "b", "c"]) + + def get_ordinal_param(name: str): + return OrdinalHyperparameter(name, [8, 6, 4, 2]) + + get_params = [ + get_uniform_param, + get_constant_param, + get_categorical_param, + get_ordinal_param, + ] + + dims = np.arange(1, 5) + for n_dim in dims: + cs = ConfigurationSpace() + for i in range(n_dim): + for j, get_param in enumerate(get_params): + param_name = f"x{i+1}_{j}" + cs.add_hyperparameter(get_param(param_name)) + + design = FactorialInitialDesign( + make_scenario(configspace=cs), + n_configs=10, + n_configs_per_hyperparameter=None, + max_ratio=0.25, + seed=1, + ) + design.select_configurations() diff --git a/tests/test_initial_design/test_initial_design.py b/tests/test_initial_design/test_initial_design.py index 1659cc7f2..d4b221cd4 100644 --- a/tests/test_initial_design/test_initial_design.py +++ b/tests/test_initial_design/test_initial_design.py @@ -1,145 +1,123 @@ -import unittest -import unittest.mock +import pytest -import numpy as np -from ConfigSpace import Configuration, UniformFloatHyperparameter - -from smac.configspace import ConfigurationSpace -from smac.initial_design.default_configuration_design import DefaultConfiguration -from smac.initial_design.initial_design import InitialDesign -from smac.runhistory.runhistory import RunHistory -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae.execute_func import ExecuteTAFuncDict -from smac.utils.io.traj_logging import TrajLogger +from smac.initial_design import AbstractInitialDesign +from smac.initial_design.default_design import DefaultInitialDesign __copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" __license__ = "3-clause BSD" -class TestSingleInitialDesign(unittest.TestCase): - def setUp(self): - self.cs = ConfigurationSpace() - self.cs.add_hyperparameter(UniformFloatHyperparameter(name="x1", lower=1, upper=10, default_value=1)) - self.scenario = Scenario( - { - "cs": self.cs, - "run_obj": "quality", - "output_dir": "", - "ta_run_limit": 100, - } - ) - self.stats = Stats(scenario=self.scenario) - self.rh = RunHistory() - self.ta = ExecuteTAFuncDict(lambda x: x["x1"] ** 2, stats=self.stats) - - def test_single_default_config_design(self): - self.stats.start_timing() - tj = TrajLogger(output_dir=None, stats=self.stats) - - dc = DefaultConfiguration( - cs=self.cs, - traj_logger=tj, - rng=np.random.RandomState(seed=12345), - ta_run_limit=self.scenario.ta_run_limit, +def test_single_default_config_design(make_scenario, configspace_small): + dc = DefaultInitialDesign( + scenario=make_scenario(configspace_small), + n_configs=10, + ) + + # should return only the default config + configs = dc.select_configurations() + assert len(configs) == 1 + assert configs[0]["a"] == 1 + assert configs[0]["b"] == 1e-1 + assert configs[0]["c"] == "cat" + + +def test_multi_config_design(make_scenario, configspace_small): + scenario = make_scenario(configspace_small) + configs = configspace_small.sample_configuration(5) + + dc = AbstractInitialDesign( + scenario=scenario, + n_configs=0, + additional_configs=configs, + ) + + # Selects multiple initial configurations to run. + # Since the configs were passed to initial design (and n_configs == 0), it should return the same. + init_configs = dc.select_configurations() + assert len(init_configs) == len(configs) + assert init_configs == configs + + +def test_config_numbers(make_scenario, configspace_small): + n_configs = 5 + n_configs_per_hyperparameter = 10 + + scenario = make_scenario(configspace_small) + configs = configspace_small.sample_configuration(n_configs) + + n_hps = len(configspace_small.get_hyperparameters()) + + dc = AbstractInitialDesign( + scenario=scenario, + n_configs=15, + max_ratio=1.0, + ) + + assert dc._n_configs == 15 + + dc = AbstractInitialDesign( + scenario=scenario, + n_configs_per_hyperparameter=n_configs_per_hyperparameter, + additional_configs=configs, + max_ratio=1.0, + ) + + assert dc._n_configs == n_hps * n_configs_per_hyperparameter + + # If we have max ratio then we expect less + dc = AbstractInitialDesign( + scenario=scenario, + n_configs_per_hyperparameter=1234523, + # additional_configs=configs, + max_ratio=0.5, + ) + + assert dc._n_configs == int(scenario.n_trials / 2) + + # If we have max ratio then we expect less + dc = AbstractInitialDesign( + scenario=scenario, + n_configs_per_hyperparameter=1234523, + additional_configs=configs, + max_ratio=0.5, + ) + + assert dc._n_configs == int(scenario.n_trials / 2) + + dc = AbstractInitialDesign( + scenario=scenario, + n_configs_per_hyperparameter=n_configs_per_hyperparameter, + max_ratio=1.0, + ) + + assert dc._n_configs == n_hps * n_configs_per_hyperparameter + + # We can't have more initial configs than + with pytest.raises(ValueError): + dc = AbstractInitialDesign( + scenario=scenario, + n_configs=32351235, + # If we add additional configs then we should get a value although n_configs is cut by max ratio + additional_configs=configs, + max_ratio=1.0, ) - # should return only the default config - configs = dc.select_configurations() - self.assertEqual(len(configs), 1) - self.assertEqual(configs[0]["x1"], 1) - - def test_multi_config_design(self): - self.stats.start_timing() - tj = TrajLogger(output_dir=None, stats=self.stats) - _ = np.random.RandomState(seed=12345) - - configs = [ - Configuration(configuration_space=self.cs, values={"x1": 4}), - Configuration(configuration_space=self.cs, values={"x1": 2}), - ] - dc = InitialDesign( - cs=self.cs, - traj_logger=tj, - rng=np.random.RandomState(seed=12345), - ta_run_limit=self.scenario.ta_run_limit, - configs=configs, + # We need to specify at least `n_configs`, `configs` or `n_configs_per_hyperparameter` + with pytest.raises(ValueError): + dc = AbstractInitialDesign( + scenario=scenario, + n_configs_per_hyperparameter=None, ) - # selects multiple initial configurations to run - # since the configs were passed to initial design, it should return the same - init_configs = dc.select_configurations() - self.assertEqual(len(init_configs), 2) - self.assertEqual(init_configs, configs) - - def test_init_budget(self): - self.stats.start_timing() - tj = TrajLogger(output_dir=None, stats=self.stats) - _ = np.random.RandomState(seed=12345) - - kwargs = dict( - cs=self.cs, - traj_logger=tj, - rng=np.random.RandomState(seed=12345), - ta_run_limit=self.scenario.ta_run_limit, - ) - configs = [ - Configuration(configuration_space=self.cs, values={"x1": 4}), - Configuration(configuration_space=self.cs, values={"x1": 2}), - ] - dc = InitialDesign( - configs=configs, - init_budget=3, - **kwargs, - ) - self.assertEqual(dc.init_budget, 3) +def test_select_configurations(make_scenario, configspace_small): + scenario = make_scenario(configspace_small) - dc = InitialDesign( - init_budget=3, - **kwargs, - ) - self.assertEqual(dc.init_budget, 3) - - configs = [ - Configuration(configuration_space=self.cs, values={"x1": 4}), - Configuration(configuration_space=self.cs, values={"x1": 2}), - ] - dc = InitialDesign( - configs=configs, - **kwargs, - ) - self.assertEqual(dc.init_budget, 2) + dc = AbstractInitialDesign( + scenario=scenario, + n_configs=15, + ) - dc = InitialDesign( - **kwargs, - ) - self.assertEqual(dc.init_budget, 10) - - with self.assertRaisesRegex( - ValueError, - "Initial budget 200 cannot be higher than the run limit 100.", - ): - InitialDesign(init_budget=200, **kwargs) - - with self.assertRaisesRegex( - ValueError, - "Need to provide either argument `init_budget`, `configs` or `n_configs_x_params`, " - "but provided none of them.", - ): - InitialDesign(**kwargs, n_configs_x_params=None) - - def test__select_configurations(self): - kwargs = dict( - cs=self.cs, - rng=np.random.RandomState(1), - traj_logger=unittest.mock.Mock(), - ta_run_limit=1000, - configs=None, - n_configs_x_params=None, - max_config_fracs=0.25, - init_budget=1, - ) - init_design = InitialDesign(**kwargs) - with self.assertRaises(NotImplementedError): - init_design._select_configurations() + # We expect empty list here + with pytest.raises(NotImplementedError): + dc.select_configurations() diff --git a/tests/test_initial_design/test_latin_hypercube_design.py b/tests/test_initial_design/test_latin_hypercube_design.py index 040988d26..c5775b702 100644 --- a/tests/test_initial_design/test_latin_hypercube_design.py +++ b/tests/test_initial_design/test_latin_hypercube_design.py @@ -1,63 +1,17 @@ -import unittest -import unittest.mock - -import numpy as np -from ConfigSpace import ( - CategoricalHyperparameter, - ConfigurationSpace, - Constant, - ForbiddenEqualsClause, - OrdinalHyperparameter, - UniformFloatHyperparameter, -) - -from smac.initial_design.latin_hypercube_design import LHDesign +from smac.initial_design.latin_hypercube_design import LatinHypercubeInitialDesign __copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" __license__ = "3-clause BSD" -class TestLHDesign(unittest.TestCase): - def setUp(self): - def get_uniform_param(name: str): - return UniformFloatHyperparameter(name, 0, 1) - - def get_constant_param(name: str): - return Constant(name, 0.0) - - def get_categorical_param(name: str): - return CategoricalHyperparameter(name, choices=["a", "b", "c"]) - - def get_ordinal_param(name: str): - return OrdinalHyperparameter(name, [8, 6, 4, 2]) - - get_params = [ - get_uniform_param, - get_constant_param, - get_categorical_param, - get_ordinal_param, - ] - - self.cs = ConfigurationSpace() - for j, get_param in enumerate(get_params): - param_name = f"x{j}" - self.cs.add_hyperparameter(get_param(param_name)) - - param_constrained = CategoricalHyperparameter("constrained", choices=["a", "b", "c"]) - self.cs.add_hyperparameter(param_constrained) - self.cs.add_forbidden_clause(ForbiddenEqualsClause(param_constrained, "b")) +def test_latin_hypercube_design(make_scenario, configspace_large): + scenario = make_scenario(configspace_large) - for i in range(5): - self.cs.add_hyperparameter(UniformFloatHyperparameter("x%d" % (i + len(get_params)), 0, 1)) + initial_design = LatinHypercubeInitialDesign( + scenario=scenario, + n_configs=54, + max_ratio=1, + ) - def test_latin_hypercube_design(self): - kwargs = dict( - rng=np.random.RandomState(1), - traj_logger=unittest.mock.Mock(), - ta_run_limit=1000, - configs=None, - n_configs_x_params=None, - max_config_fracs=0.25, - init_budget=1000, - ) - LHDesign(cs=self.cs, **kwargs).select_configurations() + configs = initial_design.select_configurations() + assert len(configs) == 54 diff --git a/tests/test_initial_design/test_random_config_design.py b/tests/test_initial_design/test_random_config_design.py deleted file mode 100644 index f8c46314b..000000000 --- a/tests/test_initial_design/test_random_config_design.py +++ /dev/null @@ -1,58 +0,0 @@ -import unittest -import unittest.mock - -import numpy as np -from ConfigSpace import ( - CategoricalHyperparameter, - ConfigurationSpace, - Constant, - OrdinalHyperparameter, - UniformFloatHyperparameter, -) - -from smac.initial_design.random_configuration_design import RandomConfigurations - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestRandomConfigurationDesign(unittest.TestCase): - def setUp(self): - def get_uniform_param(name: str): - return UniformFloatHyperparameter(name, 0, 1) - - def get_constant_param(name: str): - return Constant(name, 0.0) - - def get_categorical_param(name: str): - return CategoricalHyperparameter(name, choices=["a", "b", "c"]) - - def get_ordinal_param(name: str): - return OrdinalHyperparameter(name, [8, 6, 4, 2]) - - get_params = [ - get_uniform_param, - get_constant_param, - get_categorical_param, - get_ordinal_param, - ] - - self.cs = ConfigurationSpace() - for j, get_param in enumerate(get_params): - param_name = f"x{j}" - self.cs.add_hyperparameter(get_param(param_name)) - - for i in range(5): - self.cs.add_hyperparameter(UniformFloatHyperparameter("x%d" % (i + len(get_params)), 0, 1)) - - def test_random_configurations(self): - kwargs = dict( - rng=np.random.RandomState(1), - traj_logger=unittest.mock.Mock(), - ta_run_limit=1000, - configs=None, - n_configs_x_params=None, - max_config_fracs=0.25, - init_budget=1, - ) - RandomConfigurations(cs=self.cs, **kwargs).select_configurations() diff --git a/tests/test_initial_design/test_random_design.py b/tests/test_initial_design/test_random_design.py new file mode 100644 index 000000000..852dab682 --- /dev/null +++ b/tests/test_initial_design/test_random_design.py @@ -0,0 +1,17 @@ +from smac.initial_design.random_design import RandomInitialDesign + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def test_random_initial_design(make_scenario, configspace_large): + scenario = make_scenario(configspace_large) + + initial_design = RandomInitialDesign( + scenario=scenario, + n_configs=54, + max_ratio=1, + ) + + configs = initial_design.select_configurations() + assert len(configs) == 54 diff --git a/tests/test_initial_design/test_sobol_design.py b/tests/test_initial_design/test_sobol_design.py index 33c87048d..8663e2972 100644 --- a/tests/test_initial_design/test_sobol_design.py +++ b/tests/test_initial_design/test_sobol_design.py @@ -1,35 +1,36 @@ -import unittest -import unittest.mock +import pytest +from ConfigSpace import ConfigurationSpace, Float -import numpy as np -from ConfigSpace import ConfigurationSpace, UniformFloatHyperparameter - -from smac.initial_design.sobol_design import SobolDesign +from smac.initial_design.sobol_design import SobolInitialDesign __copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" __license__ = "3-clause BSD" -class TestSobol(unittest.TestCase): - def test_sobol(self): - cs = ConfigurationSpace() - hyperparameters = [UniformFloatHyperparameter("x%d" % (i + 1), 0, 1) for i in range(21201)] - cs.add_hyperparameters(hyperparameters) - - sobol_kwargs = dict( - rng=np.random.RandomState(1), - traj_logger=unittest.mock.Mock(), - ta_run_limit=1000, - configs=None, - n_configs_x_params=None, - max_config_fracs=0.25, - init_budget=1, +def test_sobol_design(make_scenario, configspace_large): + scenario = make_scenario(configspace_large) + + initial_design = SobolInitialDesign( + scenario=scenario, + n_configs=54, + max_ratio=1, + ) + + configs = initial_design.select_configurations() + assert initial_design._n_configs == 54 + assert len(configs) == 54 + + +def test_max_hyperparameters(make_scenario): + cs = ConfigurationSpace() + hyperparameters = [Float("x%d" % (i + 1), (0, 1)) for i in range(21202)] + cs.add_hyperparameters(hyperparameters) + + scenario = make_scenario(cs) + + with pytest.raises(ValueError): + initial_design = SobolInitialDesign( + scenario=scenario, + n_configs=5, ) - SobolDesign(cs=cs, **sobol_kwargs).select_configurations() - - cs.add_hyperparameter(UniformFloatHyperparameter("x21202", 0, 1)) - with self.assertRaisesRegex( - Exception, - "Maximum supported dimensionality is 21201.", - ): - SobolDesign(cs=cs, **sobol_kwargs).select_configurations() + initial_design.select_configurations() diff --git a/smac/facade/experimental/__init__.py b/tests/test_intensifier/__init__.py similarity index 100% rename from smac/facade/experimental/__init__.py rename to tests/test_intensifier/__init__.py diff --git a/tests/test_intensifier/test_abstract_intensifier.py b/tests/test_intensifier/test_abstract_intensifier.py new file mode 100644 index 000000000..d8daf559c --- /dev/null +++ b/tests/test_intensifier/test_abstract_intensifier.py @@ -0,0 +1,168 @@ +import pytest + +from smac.intensifier.abstract_intensifier import AbstractIntensifier +from smac.runner.abstract_runner import StatusType + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +@pytest.fixture +def intensifier(make_scenario, configspace_small): + scenario = make_scenario(configspace_small) + return AbstractIntensifier(scenario=scenario) + + +@pytest.fixture +def configs(configspace_small): + return configspace_small.sample_configuration(3) + + +def test_compare_configs_no_joint_set(intensifier, runhistory, configs): + for i in range(2): + runhistory.add( + config=configs[0], + cost=2, + time=2, + status=StatusType.SUCCESS, + instance=1, + seed=i, + additional_info=None, + ) + + for i in range(2, 5): + runhistory.add( + config=configs[1], + cost=1, + time=1, + status=StatusType.SUCCESS, + instance=1, + seed=i, + additional_info=None, + ) + + # The sets for the incumbent are completely disjoint. + conf = intensifier._compare_configs(incumbent=configs[0], challenger=configs[1], runhistory=runhistory) + assert conf is None + + # The incumbent has still one instance-seed pair left on which the + # challenger was not run yet. + runhistory.add( + config=configs[1], + cost=1, + time=1, + status=StatusType.SUCCESS, + instance=1, + seed=1, + additional_info=None, + ) + conf = intensifier._compare_configs(incumbent=configs[0], challenger=configs[1], runhistory=runhistory) + assert conf is None + + +def test_compare_configs_chall(intensifier, runhistory, configs): + """ + Challenger is better. + """ + runhistory.add( + config=configs[0], + cost=1, + time=2, + status=StatusType.SUCCESS, + instance=1, + seed=None, + additional_info=None, + ) + + runhistory.add( + config=configs[1], + cost=0, + time=1, + status=StatusType.SUCCESS, + instance=1, + seed=None, + additional_info=None, + ) + + conf = intensifier._compare_configs( + incumbent=configs[0], + challenger=configs[1], + runhistory=runhistory, + log_trajectory=False, + ) + + # Challenger has enough runs and is better + assert conf == configs[1] + + +def test_compare_configs_inc(intensifier, runhistory, configs): + """ + Incumbent is better + """ + + runhistory.add( + config=configs[0], + cost=1, + time=1, + status=StatusType.SUCCESS, + instance=1, + seed=None, + additional_info=None, + ) + + runhistory.add( + config=configs[1], + cost=2, + time=2, + status=StatusType.SUCCESS, + instance=1, + seed=None, + additional_info=None, + ) + + conf = intensifier._compare_configs(incumbent=configs[0], challenger=configs[1], runhistory=runhistory) + + # Challenger worse than inc + assert conf == configs[0] + + +def test_compare_configs_unknow(intensifier, runhistory, configs): + """ + Challenger is better but has less runs; + -> no decision (None) + """ + + runhistory.add( + config=configs[0], + cost=1, + time=1, + status=StatusType.SUCCESS, + instance=1, + seed=None, + additional_info=None, + ) + + runhistory.add( + config=configs[0], + cost=1, + time=2, + status=StatusType.SUCCESS, + instance=2, + seed=None, + additional_info=None, + ) + + runhistory.add( + config=configs[0], + cost=1, + time=1, + status=StatusType.SUCCESS, + instance=2, + seed=None, + additional_info=None, + ) + + conf = intensifier._compare_configs(incumbent=configs[0], challenger=configs[1], runhistory=runhistory) + + # Challenger worse than inc + assert conf is None diff --git a/tests/test_intensifier/test_hyperband.py b/tests/test_intensifier/test_hyperband.py new file mode 100644 index 000000000..8995f57d7 --- /dev/null +++ b/tests/test_intensifier/test_hyperband.py @@ -0,0 +1,648 @@ +import time +from unittest import mock + +import numpy as np +import pytest + +from smac.intensifier.hyperband import Hyperband +from smac.intensifier.hyperband_worker import HyperbandWorker +from smac.intensifier.successive_halving import SuccessiveHalving +from smac.intensifier.successive_halving_worker import SuccessiveHalvingWorker +from smac.runhistory import RunHistory, TrialInfo, TrialInfoIntent, TrialValue +from smac.runner.abstract_runner import StatusType +from smac.runner.target_function_runner import TargetFunctionRunner +from smac.stats import Stats + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def evaluate_challenger( + trial_info: TrialInfo, + target_function: TargetFunctionRunner, + stats: Stats, + runhistory: RunHistory, + force_update=False, +): + """ + Wrapper over challenger evaluation + + SMBO objects handles run history now, but to keep + same testing functionality this function is a small + wrapper to launch the target_function and add it to the history + """ + # evaluating configuration + trial_info, result = target_function.run_wrapper(trial_info=trial_info) + stats._target_function_walltime_used += float(result.time) + stats._finished += 1 + + runhistory.add( + config=trial_info.config, + cost=result.cost, + time=result.time, + status=result.status, + instance=trial_info.instance, + seed=trial_info.seed, + budget=trial_info.budget, + force_update=force_update, + ) + stats._n_configs = len(runhistory.config_ids) + + return result + + +def target_from_trial_info(trial_info: TrialInfo): + value_from_config = trial_info.config.get_dictionary()["a"] + + return TrialValue( + cost=value_from_config, + time=0.5, + status=StatusType.SUCCESS, + starttime=time.time(), + endtime=time.time() + 1, + additional_info={}, + ) + + +@pytest.fixture +def HB(make_scenario, make_stats, configspace_small) -> Hyperband: + scenario = make_scenario( + configspace_small, + use_instances=True, + n_instances=5, + deterministic=False, + min_budget=2, + max_budget=5, + ) + stats = make_stats(scenario) + intensifier = Hyperband(scenario=scenario, eta=2, n_seeds=2) + intensifier._stats = stats + + return intensifier + + +@pytest.fixture +def _HB(HB): + return HyperbandWorker(HB) + + +@pytest.fixture +def make_hb_worker(make_scenario, make_stats, configspace_small): + def _make( + deterministic=False, + min_budget=2, + max_budget=5, + eta=2, + n_instances=3, + n_seeds=1, + min_challenger=1, + instance_order="shuffle_once", + incumbent_selection="highest_executed_budget", + ): + scenario = make_scenario( + configspace_small, + use_instances=True, + n_instances=n_instances, + deterministic=deterministic, + min_budget=min_budget, + max_budget=max_budget, + ) + stats = make_stats(scenario) + hb = Hyperband( + scenario=scenario, + instance_order=instance_order, + incumbent_selection=incumbent_selection, + min_challenger=min_challenger, + eta=eta, + n_seeds=n_seeds, + ) + hb._stats = stats + + return HyperbandWorker(hb) + + return _make + + +@pytest.fixture +def make_target_function(): + def _make(scenario, func, required_arguments=[]): + return TargetFunctionRunner( + target_function=func, + scenario=scenario, + required_arguments=required_arguments, + ) + + return _make + + +@pytest.fixture +def configs(configspace_small): + configs = configspace_small.sample_configuration(20) + return (configs[16], configs[15], configs[2], configs[3]) + + +def test_initialization(runhistory: RunHistory, HB: Hyperband): + """Makes sure that a proper_HB is created""" + + # We initialize the HB with zero intensifier_instances + assert len(HB._intensifier_instances) == 0 + + # Add an instance to check the_HB initialization + assert HB._add_new_instance(n_workers=1) + + # Some default init + assert HB._intensifier_instances[0]._hb_iters == 0 + assert HB._max_budget == 5 + assert HB._min_budget == 2 + + # Update the stage + HB._intensifier_instances[0]._update_stage(runhistory) + + # Parameters properly passed to SH + assert len(HB._instance_seed_pairs) == 10 + assert HB._intensifier_instances[0]._successive_halving._min_budget == 2 + assert HB._intensifier_instances[0]._successive_halving._max_budget == 5 + + +def test_process_results_via_sourceid(runhistory, HB: Hyperband, configs): + """Makes sure source id is honored when deciding + which_HB instance will consume the result/trial_info + + """ + # Mock the_HB instance so we can make sure the correct item is passed + for i in range(10): + HB._intensifier_instances[i] = mock.Mock() + HB._intensifier_instances[i].process_results.return_value = (configs[0], 0.5) + # make iter false so the mock object is not overwritten + HB._intensifier_instances[i]._iteration_done = False + + # randomly create trial_infos and push into HB. Then we will make + # sure they got properly allocated + for i in np.random.choice(list(range(10)), 30): + trial_info = TrialInfo( + config=configs[0], + instance=0, + seed=0, + budget=0.0, + source=i, + ) + + # make sure results aren't messed up via magic variable + # That is we check only the proper_HB instance has this + magic = time.time() + + result = TrialValue( + cost=1, + time=0.5, + status=StatusType.SUCCESS, + starttime=1, + endtime=2, + additional_info=magic, + ) + HB.process_results( + trial_info=trial_info, + trial_value=result, + incumbent=None, + runhistory=runhistory, + time_bound=None, + log_trajectory=False, + ) + + # Check the call arguments of each sh instance and make sure + # it is the correct one + + # First the expected one + assert HB._intensifier_instances[i].process_results.call_args[1]["trial_info"] == trial_info + assert HB._intensifier_instances[i].process_results.call_args[1]["trial_value"] == result + all_other_trial_infos, all_other_results = [], [] + for j, item in enumerate(HB._intensifier_instances): + # Skip the expected_HB instance + if i == j: + continue + if HB._intensifier_instances[j].process_results.call_args is None: + all_other_trial_infos.append(None) + else: + all_other_trial_infos.append(HB._intensifier_instances[j].process_results.call_args[1]["trial_info"]) + all_other_results.append(HB._intensifier_instances[j].process_results.call_args[1]["trial_value"]) + + assert trial_info not in all_other_trial_infos + assert result not in all_other_results + + +def test_get_next_trial_single_HB_instance(runhistory, HB: Hyperband, configs): + """Makes sure that a single_HB instance returns a valid config""" + + challengers = configs[:4] + for i in range(30): + intent, trial_info = HB.get_next_trial( + challengers=challengers, + incumbent=None, + get_next_configurations=None, + runhistory=runhistory, + n_workers=1, + ) + + # Regenerate challenger list + challengers = [c for c in challengers if c != trial_info.config] + + if intent == TrialInfoIntent.WAIT: + break + + # Add the config to rh in order to make HB aware that this + # config/instance was launched + runhistory.add( + config=trial_info.config, + cost=10, + time=0.0, + status=StatusType.RUNNING, + instance=trial_info.instance, + seed=trial_info.seed, + budget=trial_info.budget, + ) + + # smax==1 (int(np.floor(np.log(max_budget / min_budget) / np.log( eta)))) + assert HB._intensifier_instances[0]._s_max == 1 + + # And we do not even complete 1 iteration, so s has to be 1 + assert HB._intensifier_instances[0]._s == 1 + + # We should not create more_HB instance intensifier_instances + assert len(HB._intensifier_instances) == 1 + + # We are running with: + # 'all_budgets': array([2.5, 5. ]) -> 2 intensifier_instances per config top + # 'n_configs_in_stage': [2.0, 1.0], + # This means we run int(2.5) + 2.0 = 4 runs before waiting + assert i == 4 + + # Also, check the internals of the unique sh instance + + # sh_min_budget==2.5 (eta ** -s * max_budget) + assert HB._min_budget == 2 + + # n_challengers=2 (int(np.floor((s_max + 1) / (s + 1)) * eta ** s)) + assert len(HB._intensifier_instances[0]._sh_intensifier._n_configs_in_stage) == 2 + + +def test_get_next_trial_multiple_HB_instances(runhistory, HB: Hyperband, configs): + """Makes sure that two _HB instance can properly coexist and tag + trial_info properly""" + + # We allow 2_HB instance to be created. This means, we have a newer iteration + # to expect in hyperband + challengers = configs[:4] + trial_infos = [] + for i in range(30): + intent, trial_info = HB.get_next_trial( + challengers=challengers, + incumbent=None, + get_next_configurations=None, + runhistory=runhistory, + n_workers=2, + ) + trial_infos.append(trial_info) + + # Regenerate challenger list + challengers = [c for c in challengers if c != trial_info.config] + + # Add the config to rh in order to make HB aware that this + # config/instance was launched + if intent == TrialInfoIntent.WAIT: + break + runhistory.add( + config=trial_info.config, + cost=10, + time=0.0, + status=StatusType.RUNNING, + instance=trial_info.instance, + seed=trial_info.seed, + budget=trial_info.budget, + ) + + # We have not completed an iteration + assert HB._intensifier_instances[0]._hb_iters == 0 + + # Because n workers is now 2, we expect 2 sh intensifier_instances + assert len(HB._intensifier_instances) == 2 + + # Each of the intensifier_instances should have s equal to 1 + # As no iteration has been completed + assert HB._intensifier_instances[0]._s_max == 1 + assert HB._intensifier_instances[0]._s == 1 + assert HB._intensifier_instances[1]._s_max == 1 + assert HB._intensifier_instances[1]._s == 1 + + # First let us check everything makes sense in_HB-SH-0 HB-SH-0 + assert len(HB._intensifier_instances[0]._sh_intensifier._n_configs_in_stage) == 2 + assert len(HB._intensifier_instances[1]._sh_intensifier._n_configs_in_stage) == 2 + + # We are running with: + # + 4 runs for sh instance 0 ('all_budgets': array([2.5, 5. ]), 'n_configs_in_stage': [2.0, 1.0]) + # that is, for SH0 we run in stage==0 int(2.5) intensifier_instances * 2.0 configs + # And this times 2 because we have 2_HB intensifier_instances + assert i == 8 + + # Adding a new worker is not possible as we already have 2 intensifier_instances + # and n_workers==2 + intent, trial_info = HB.get_next_trial( + challengers=challengers, + incumbent=None, + get_next_configurations=None, + runhistory=runhistory, + n_workers=2, + ) + assert intent == TrialInfoIntent.WAIT + + +def test_add_new_instance(HB): + """Test whether we can add a instance and when we should not""" + + # By default we do not create a_HB + # test adding the first instance! + assert len(HB._intensifier_instances) == 0 + assert HB._add_new_instance(n_workers=1) + assert len(HB._intensifier_instances) == 1 + assert isinstance(HB._intensifier_instances[0], HyperbandWorker) + # A second call should not add a new_HB instance + assert not HB._add_new_instance(n_workers=1) + + # We try with 2_HB instance active + + # We effectively return true because we added a new_HB instance + assert HB._add_new_instance(n_workers=2) + + assert len(HB._intensifier_instances) == 2 + assert isinstance(HB._intensifier_instances[1], HyperbandWorker) + + # Trying to add a third one should return false + assert not HB._add_new_instance(n_workers=2) + assert len(HB._intensifier_instances) == 2 + + +def _exhaust_run_and_get_incumbent(runhistory, sh: SuccessiveHalving, configs, n_workers=1): + """ + Runs all provided configs on all intensifier_instances and return the incumbent + as a nice side effect runhistory/stats are properly filled + """ + challengers = configs[:4] + incumbent = None + for i in range(100): + try: + intent, trial_info = sh.get_next_trial( + challengers=challengers, + incumbent=None, + get_next_configurations=None, + runhistory=runhistory, + n_workers=n_workers, + ) + except ValueError as e: + # Get configurations until you run out of them + print(e) + break + + # Regenerate challenger list + challengers = [c for c in challengers if c != trial_info.config] + + if intent == TrialInfoIntent.WAIT: + break + + result = target_from_trial_info(trial_info) + runhistory.add( + config=trial_info.config, + cost=result.cost, + time=result.time, + status=result.status, + instance=trial_info.instance, + seed=trial_info.seed, + budget=trial_info.budget, + ) + incumbent, inc_perf = sh.process_results( + trial_info=trial_info, + trial_value=result, + incumbent=incumbent, + runhistory=runhistory, + time_bound=100.0, + log_trajectory=False, + ) + + return incumbent, inc_perf + + +def test_parallel_same_as_serial_HB(make_hb_worker, configs): + """Makes sure we behave the same as a serial run at the end""" + runhistory1 = RunHistory() + _HB = make_hb_worker(min_budget=2, max_budget=5, eta=2, n_instances=5) + + incumbent, inc_perf = _exhaust_run_and_get_incumbent(runhistory1, _HB, configs, n_workers=1) + + # Just to make sure nothing has changed from the_HB instance side to make + # this check invalid: + # We add config values, so config 3 with 0 and 7 should be the lesser cost + assert incumbent == configs[0] + assert inc_perf == 203 + + # Do the same for HB, but have multiple_HB instance in there + # This_HB instance will be created via n_workers==2 + # in _exhaust_run_and_get_incumbent + runhistory2 = RunHistory() + HB = make_hb_worker(min_budget=2, max_budget=5, eta=2, n_instances=5)._hyperband + + incumbent_phb, inc_perf_phb = _exhaust_run_and_get_incumbent(runhistory2, HB, configs) + assert incumbent, incumbent_phb + + # This makes sure there is a single incumbent in HB + assert inc_perf == inc_perf_phb + + # We don't want to loose any configuration, and particularly + # we want to make sure the values of_HB instance to HB match + assert len(runhistory1._data) == len(runhistory2._data) + + # Because it is a deterministic run, the run histories must be the + # same on exhaustion + assert runhistory1._data == runhistory2._data + + +def test_update_stage(make_hb_worker): + """ + test initialization of all parameters and tracking variables + """ + + intensifier: HyperbandWorker = make_hb_worker(deterministic=True, n_instances=9, min_budget=1, max_budget=9, eta=2) + + # intensifier._update_stage() + + assert intensifier._s == 3 + assert intensifier._s_max == 3 + assert intensifier._hb_iters == 0 + assert isinstance(intensifier._sh_intensifier, SuccessiveHalvingWorker) + assert intensifier._sh_intensifier._min_budget == 1.125 + assert intensifier._sh_intensifier._n_configs_in_stage == [8.0, 4.0, 2.0, 1.0] + + # next HB stage + intensifier._update_stage() + + assert intensifier._s == 2 + assert intensifier._hb_iters == 0 + assert intensifier._sh_intensifier._min_budget == 2.25 + assert intensifier._sh_intensifier._n_configs_in_stage == [4.0, 2.0, 1.0] + + intensifier._update_stage() # s = 1 + intensifier._update_stage() # s = 0 + # HB iteration completed + intensifier._update_stage() + + assert intensifier._s == intensifier._s_max + assert intensifier._hb_iters == 1 + assert intensifier._sh_intensifier._min_budget == 1.125 + assert intensifier._sh_intensifier._n_configs_in_stage == [8.0, 4.0, 2.0, 1.0] + + +def test_eval_challenger(runhistory, make_target_function, make_hb_worker, configs): + """ + since hyperband uses eval_challenger and get_next_trial of the internal successive halving, + we don't test these method extensively + """ + + def target(x, seed, budget): + return 0.1 + + config1 = configs[0] + config2 = configs[1] + config3 = configs[2] + + intensifier = make_hb_worker( + min_budget=0.5, + max_budget=1, + eta=2, + n_instances=0, + deterministic=True, + ) + + target_function = make_target_function(intensifier._scenario, target, required_arguments=["seed", "budget"]) + + # Testing get_next_trial - get next configuration + intent, trial_info = intensifier.get_next_trial( + challengers=[config2, config3], + get_next_configurations=None, + incumbent=None, + runhistory=runhistory, + ) + assert intensifier._s == intensifier._s_max + assert trial_info.config == config2 + + # Update to the last SH iteration of the given HB stage + assert intensifier._s == 1 + assert intensifier._s_max == 1 + + # We assume now that process results was called with below successes. + # We track closely run execution through run_tracker, so this also + # has to be update -- the fact that the succesive halving inside hyperband + # processed the given configurations + runhistory.add(config=config1, cost=1, time=1, status=StatusType.SUCCESS, seed=0, budget=1) + intensifier._sh_intensifier._run_tracker[(config1, None, 0, 1)] = True + runhistory.add(config=config2, cost=2, time=2, status=StatusType.SUCCESS, seed=0, budget=0.5) + intensifier._sh_intensifier._run_tracker[(config2, None, 0, 0.5)] = True + runhistory.add(config=config3, cost=3, time=2, status=StatusType.SUCCESS, seed=0, budget=0.5) + intensifier._sh_intensifier._run_tracker[(config3, None, 0, 0.5)] = True + + intensifier._sh_intensifier._success_challengers = {config2, config3} + intensifier._sh_intensifier._update_stage(runhistory) + intent, trial_info = intensifier.get_next_trial( + challengers=[config2, config3], + get_next_configurations=None, + incumbent=None, + runhistory=runhistory, + ) + + # evaluation should change the incumbent to config2 + assert trial_info.config is not None + trial_value = evaluate_challenger(trial_info, target_function, intensifier._stats, runhistory) + + inc, inc_value = intensifier.process_results( + trial_info=trial_info, + trial_value=trial_value, + incumbent=config1, + runhistory=runhistory, + time_bound=np.inf, + ) + + assert inc == config2 + assert intensifier._s == 0 + assert inc_value == 0.1 + assert list(runhistory._data.keys())[-1].config_id, runhistory.config_ids[config2] + assert intensifier._stats.incumbent_changed == 1 + + +def test_budget_initialization(make_hb_worker): + """ + Check computing budgets (only for non-instance cases) + """ + + intensifier = make_hb_worker( + min_budget=1, + max_budget=81, + eta=3, + n_instances=0, + deterministic=True, + ) + + assert [1, 3, 9, 27, 81] == intensifier._all_budgets + assert [81, 27, 9, 3, 1] == intensifier._n_configs_in_stage + + to_check = [ + # minb, maxb, eta, n_configs_in_stage, all_budgets + [1, 81, 3, [81, 27, 9, 3, 1], [1, 3, 9, 27, 81]], + [ + 1, + 600, + 3, + [243, 81, 27, 9, 3, 1], + [2.469135, 7.407407, 22.222222, 66.666666, 200, 600], + ], + [1, 100, 10, [100, 10, 1], [1, 10, 100]], + [ + 0.001, + 1, + 3, + [729, 243, 81, 27, 9, 3, 1], + [0.001371, 0.004115, 0.012345, 0.037037, 0.111111, 0.333333, 1.0], + ], + [ + 1, + 1000, + 3, + [729, 243, 81, 27, 9, 3, 1], + [1.371742, 4.115226, 12.345679, 37.037037, 111.111111, 333.333333, 1000.0], + ], + [ + 0.001, + 100, + 10, + [100000, 10000, 1000, 100, 10, 1], + [0.001, 0.01, 0.1, 1.0, 10.0, 100.0], + ], + ] + + for minb, maxb, eta, n_configs_in_stage, all_budgets in to_check: + intensifier = make_hb_worker( + min_budget=minb, + max_budget=maxb, + eta=eta, + n_instances=0, + deterministic=True, + ) + + for i in range(len(all_budgets) + 10): + comp_budgets = intensifier._sh_intensifier._all_budgets + comp_configs = intensifier._sh_intensifier._n_configs_in_stage + + assert isinstance(comp_configs, list) + for c in comp_configs: + assert isinstance(c, int) + + # all_budgets for SH is always a subset of all_budgets of HB + np.testing.assert_array_almost_equal(all_budgets[i % len(all_budgets) :], comp_budgets, decimal=5) + + # The content of these lists might differ + assert len(n_configs_in_stage[i % len(n_configs_in_stage) :]) == len(comp_configs) + intensifier._update_stage() diff --git a/tests/test_intensifier/test_intensify.py b/tests/test_intensifier/test_intensify.py new file mode 100644 index 000000000..79bd91674 --- /dev/null +++ b/tests/test_intensifier/test_intensify.py @@ -0,0 +1,743 @@ +import numpy as np +import pytest + +from smac.facade.algorithm_configuration_facade import AlgorithmConfigurationFacade +from smac.intensifier.intensifier import Intensifier, IntensifierStage +from smac.runhistory import RunHistory, TrialInfo, TrialInfoIntent +from smac.runner.abstract_runner import StatusType +from smac.runner.target_function_runner import TargetFunctionRunner +from smac.stats import Stats + + +def evaluate_challenger( + trial_info: TrialInfo, + target_function: TargetFunctionRunner, + stats: Stats, + runhistory: RunHistory, + force_update: bool = False, +): + """ + Wrapper over challenger evaluation. + + SMBO objects handles the run history, but to keep same testing functionality this function is a small + wrapper to launch the taf and add it to the history. + """ + # Evaluating configuration + trial_info, result = target_function.run_wrapper(trial_info=trial_info) + + stats._target_function_walltime_used += float(result.time) + stats._finished += 1 + + runhistory.add( + config=trial_info.config, + cost=result.cost, + time=result.time, + status=result.status, + instance=trial_info.instance, + seed=trial_info.seed, + budget=trial_info.budget, + force_update=force_update, + ) + stats._n_configs = len(runhistory.config_ids) + + return result + + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def test_race_challenger_1(make_scenario, make_stats, configspace_small, runhistory): + """ + Makes sure that a racing configuration with better performance, is selected as incumbent. + """ + + def target(x, seed=0): + return (x["a"] + 1) / 1000.0 + + scenario = make_scenario(configspace_small, use_instances=True) + stats = make_stats(scenario) + intensifier = Intensifier(scenario=scenario) + intensifier._stats = stats + target_function = TargetFunctionRunner(scenario, target, required_arguments=["seed"]) + configs = configspace_small.sample_configuration(3) + + assert intensifier._stage == IntensifierStage.RUN_FIRST_CONFIG + + runhistory.add( + config=configs[0], + cost=1, + time=1, + status=StatusType.SUCCESS, + instance="i1", + seed=55, + additional_info=None, + ) + + intensifier._N = 1 + incumbent, instance, seed = intensifier._get_next_racer( + challenger=configs[1], + incumbent=configs[0], + runhistory=runhistory, + ) + + assert intensifier._stage == IntensifierStage.RUN_FIRST_CONFIG + assert incumbent == configs[0] # Must be the first config since incumbent is only passed through + assert instance == "i1" + assert seed == 55 + + # Here we evaluate configs[1] and add it to the runhistory. + # Cost is around ~7.1 on the sampled config. + trial_info = TrialInfo(config=configs[1], instance=instance, seed=seed, budget=0.0) + trial_value = evaluate_challenger(trial_info, target_function, stats, runhistory) + + incumbent, _ = intensifier.process_results( + trial_info=trial_info, + trial_value=trial_value, + incumbent=configs[1], + runhistory=runhistory, + time_bound=np.inf, + ) + + assert intensifier._stage == IntensifierStage.RUN_INCUMBENT + assert incumbent == configs[1] # Well, again it's basically passed down + assert intensifier._num_challenger_run == 1 + + +def test_race_challenger_large(make_scenario, make_stats, configspace_small, runhistory): + """ + Makes sure that a racing configuration with better performance, is selected as incumbent. + """ + + def target(x, seed=0, instance=None): + return 1 + + scenario = make_scenario(configspace_small, use_instances=True, deterministic=True) + stats = make_stats(scenario) + intensifier = Intensifier(scenario=scenario) + intensifier._stats = stats + target_function = TargetFunctionRunner(scenario, target, required_arguments=["seed", "instance"]) + configs = configspace_small.sample_configuration(3) + + for i in range(3): + runhistory.add( + config=configs[0], + cost=i + 1, + time=1, + status=StatusType.SUCCESS, + instance=f"i{i+1}", + seed=12345, + additional_info=None, + ) + + intensifier._stage = IntensifierStage.RUN_CHALLENGER + + # Tie on first instances and then challenger should always win + # and be returned as inc + while True: + if intensifier._continue_challenger: + config = intensifier._current_challenger + else: + config, _ = intensifier.get_next_challenger( + challengers=[configs[1], configs[2]], get_next_configurations=None + ) + + incumbent, instance, seed = intensifier._get_next_racer( + challenger=config, incumbent=configs[0], runhistory=runhistory + ) + + trial_info = TrialInfo(config=config, instance=instance, seed=seed) + trial_value = evaluate_challenger(trial_info, target_function, stats, runhistory) + + incumbent, _ = intensifier.process_results( + trial_info=trial_info, + trial_value=trial_value, + incumbent=configs[0], + runhistory=runhistory, + time_bound=np.inf, + ) + + # stop when challenger evaluation is over + if not intensifier._stage == IntensifierStage.RUN_CHALLENGER: + break + + assert incumbent == configs[1] + assert runhistory.get_cost(configs[1]) == 1 + + # Get data for config2 to check that the correct run was performed + runs = runhistory.get_trials(configs[1], only_max_observed_budget=True) + assert len(runs) == 3 + assert intensifier._num_trials == 3 + assert intensifier._num_challenger_run == 3 + + +def test_race_challenger_large_blocked_seed(make_scenario, make_stats, configspace_small, runhistory): + """ + Test _race_challenger whether seeds are blocked for challenger runs. + """ + + def target(x, seed=0): + return 1 + + scenario = make_scenario(configspace_small, use_instances=True, deterministic=False) + stats = make_stats(scenario) + intensifier = Intensifier(scenario=scenario) + intensifier._stats = stats + target_function = TargetFunctionRunner(scenario, target, required_arguments=["seed"]) + configs = configspace_small.sample_configuration(3) + + for i in range(3): + runhistory.add( + config=configs[0], + cost=i + 1, + time=1, + status=StatusType.SUCCESS, + instance=f"i{i+1}", + seed=i, + additional_info=None, + ) + + intensifier._stage = IntensifierStage.RUN_CHALLENGER + + # Tie on first instances and then challenger should always win and be returned as inc + while True: + if intensifier._continue_challenger: + config = intensifier._current_challenger + else: + config, _ = intensifier.get_next_challenger( + challengers=[configs[1], configs[2]], get_next_configurations=None + ) + + incumbent, instance, seed = intensifier._get_next_racer( + challenger=config, incumbent=configs[0], runhistory=runhistory + ) + + trial_info = TrialInfo(config=config, instance=instance, seed=seed, budget=None) + trial_value = evaluate_challenger(trial_info, target_function, stats, runhistory) + + incumbent, _ = intensifier.process_results( + trial_info=trial_info, + trial_value=trial_value, + incumbent=configs[0], + runhistory=runhistory, + time_bound=np.inf, + ) + + # Stop when challenger evaluation is over + if not intensifier._stage == IntensifierStage.RUN_CHALLENGER: + break + + assert incumbent == configs[1] + assert runhistory.get_cost(configs[1]) == 1 + + # Get data for config2 to check that the correct run was performed + runs = runhistory.get_trials(configs[1], only_max_observed_budget=True) + assert len(runs) == 3 + + seeds = sorted([r.seed for r in runs]) + assert list(range(3)) == seeds + + assert intensifier._num_trials == 3 + assert intensifier._num_challenger_run == 3 + + +def test_add_incumbent(make_scenario, make_stats, configspace_small, runhistory): + """ + Test _race_challenger whether seeds are blocked for challenger runs. + """ + + def target(x, seed=0): + return (x["a"] + 1) / 1000.0 + + scenario = make_scenario(configspace_small, use_instances=True, deterministic=True) + stats = make_stats(scenario) + intensifier = Intensifier(scenario=scenario) + intensifier._stats = stats + target_function = TargetFunctionRunner(scenario, target, required_arguments=["seed"]) + configs = configspace_small.sample_configuration(3) + pending_instances = intensifier._get_pending_instances(incumbent=configs[0], runhistory=runhistory) + assert len(pending_instances) == 3 + instance, seed = intensifier._get_next_instance(pending_instances=pending_instances) + + trial_info = TrialInfo(config=configs[0], instance=instance, seed=seed, budget=0.0) + trial_value = evaluate_challenger(trial_info, target_function, stats, runhistory) + intensifier._stage = IntensifierStage.PROCESS_FIRST_CONFIG_RUN + inc, perf = intensifier.process_results( + trial_info=trial_info, + trial_value=trial_value, + incumbent=configs[0], + runhistory=runhistory, + time_bound=np.inf, + ) + + assert len(runhistory._data) == 1 + + # Since we assume deterministic=1, + # the second call should not add any more runs + # given only one instance + # So the returned seed/instance is None so that a new + # run to be triggered is not launched + pending_instances = intensifier._get_pending_instances(incumbent=configs[0], runhistory=runhistory) + # Make sure that the list is empty (or the first instance removed), and hence no new call + # of incumbent will be triggered + assert len(pending_instances) == 2 + + # The following two tests evaluate to zero because _next_iteration is triggered by _add_inc_run + # as it is the first evaluation of this intensifier + # After the above incumbent run, the stage is + # IntensifierStage.RUN_CHALLENGER. Change it to test next iteration + intensifier._stage = IntensifierStage.PROCESS_FIRST_CONFIG_RUN + intensifier.process_results( + trial_info=trial_info, + trial_value=trial_value, + incumbent=None, + runhistory=runhistory, + time_bound=np.inf, + ) + assert intensifier._num_challenger_run == 0 + + +def test_add_incumbent_non_deterministic(make_scenario, make_stats, configspace_small, runhistory): + """ + Test _add_inc_run(). + """ + + def target(x, seed=0): + return (x["a"] + 1) / 1000.0 + + scenario = make_scenario(configspace_small, use_instances=True, deterministic=False) + stats = make_stats(scenario) + intensifier = Intensifier(scenario=scenario) + intensifier._stats = stats + target_function = TargetFunctionRunner(scenario, target, required_arguments=["seed"]) + configs = configspace_small.sample_configuration(3) + + instance, seed = intensifier._get_next_instance( + pending_instances=intensifier._get_pending_instances(incumbent=configs[0], runhistory=runhistory) + ) + trial_info = TrialInfo(config=configs[0], instance=instance, seed=seed, budget=0.0) + trial_value = evaluate_challenger(trial_info, target_function, stats, runhistory) + intensifier.process_results( + trial_info=trial_info, + incumbent=configs[0], + runhistory=runhistory, + time_bound=np.inf, + trial_value=trial_value, + ) + assert len(runhistory._data) == 1 + + instance, seed = intensifier._get_next_instance( + pending_instances=intensifier._get_pending_instances(incumbent=configs[0], runhistory=runhistory) + ) + trial_info = TrialInfo(config=configs[0], instance=instance, seed=seed, budget=0.0) + trial_value = evaluate_challenger(trial_info, target_function, stats, runhistory) + intensifier.process_results( + trial_info=trial_info, + incumbent=configs[0], + runhistory=runhistory, + time_bound=np.inf, + trial_value=trial_value, + ) + assert len(runhistory._data) == 2 + runs = runhistory.get_trials(config=configs[0], only_max_observed_budget=True) + + # Exactly one run on each instance + assert "i2" in [runs[0].instance, runs[1].instance] + assert "i3" in [runs[0].instance, runs[1].instance] + + instance, seed = intensifier._get_next_instance( + pending_instances=intensifier._get_pending_instances( + incumbent=configs[0], + runhistory=runhistory, + ) + ) + trial_info = TrialInfo(config=configs[0], instance=instance, seed=seed, budget=0.0) + trial_value = evaluate_challenger(trial_info, target_function, stats, runhistory) + intensifier.process_results( + trial_info=trial_info, + incumbent=configs[0], + runhistory=runhistory, + time_bound=np.inf, + trial_value=trial_value, + ) + assert len(runhistory._data) == 3 + + # The number of runs performed should be 3 + # No Next iteration call as an incumbent is provided + assert intensifier._num_trials == 2 + assert intensifier._num_challenger_run == 0 + + +def test_get_next_challenger(make_scenario, make_stats, configspace_small): + """ + Test get_next_challenger(). + """ + + def target(x, seed=0): + return (x["a"] + 1) / 1000.0 + + scenario = make_scenario(configspace_small, use_instances=True, deterministic=True) + stats = make_stats(scenario) + intensifier = Intensifier(scenario=scenario) + intensifier._stats = stats + configs = configspace_small.sample_configuration(3) + + intensifier._stage = IntensifierStage.RUN_CHALLENGER + + # get a new challenger to evaluate + config, new = intensifier.get_next_challenger(challengers=[configs[0], configs[1]], get_next_configurations=None) + + assert config == configs[0] == intensifier._current_challenger + assert intensifier._challenger_id == 1 + assert intensifier._N == 1 + assert new + + # when already evaluating a challenger, return the same challenger + intensifier._to_run = [(1, 1, 0)] + config, new = intensifier.get_next_challenger(challengers=[configs[1]], get_next_configurations=None) + assert config == configs[0] == intensifier._current_challenger + assert intensifier._challenger_id, 1 + assert not new + + +def test_generate_challenger(make_scenario, make_stats, configspace_small, runhistory): + """ + Test generate_challenger() + """ + + def target(x, seed=0, instance=None): + return (x["a"] + 1) / 1000.0 + + scenario = make_scenario(configspace_small, use_instances=True, deterministic=True) + stats = make_stats(scenario) + intensifier = Intensifier(scenario=scenario) + intensifier._stats = stats + configs = configspace_small.sample_configuration(3) + + gen = intensifier._generate_challengers(challengers=[configs[0], configs[1]], get_next_configurations=None) + + assert next(gen) == configs[0] + assert next(gen) == configs[1] + with pytest.raises(StopIteration): + next(gen) + + smac = AlgorithmConfigurationFacade(scenario, target, overwrite=True) + gen = intensifier._generate_challengers(challengers=None, get_next_configurations=smac.get_next_configurations) + + assert next(gen).get_dictionary() == {"a": 5489, "b": 0.007257005721594277, "c": "dog"} + + with pytest.raises(StopIteration): + next(gen) + + # when both are none, raise error + with pytest.raises(ValueError, match="No .* provided"): + intensifier._generate_challengers(challengers=None, get_next_configurations=None) + + +def test_evaluate_challenger_1(make_scenario, make_stats, configspace_small, runhistory): + """ + Test evaluate_challenger() - a complete intensification run without a `always_race_against` configuration. + """ + + def target(x, seed=0): + return 2 * x["a"] + x["b"] + + scenario = make_scenario(configspace_small, use_instances=True, n_instances=1, deterministic=False) + stats = make_stats(scenario) + intensifier = Intensifier(scenario=scenario, race_against=None, min_challenger=2) + intensifier._stats = stats + target_function = TargetFunctionRunner(scenario, target, required_arguments=["seed"]) + configs = configspace_small.sample_configuration(20) + + config0 = configs[16] + config1 = configs[15] + config2 = configs[2] + + assert intensifier._n_iters == 0 + assert intensifier._stage == IntensifierStage.RUN_FIRST_CONFIG + + # intensification iteration #1 + # run first config as incumbent if incumbent is None + intent, trial_info = intensifier.get_next_trial( + challengers=[config2], + incumbent=None, + runhistory=runhistory, + get_next_configurations=None, + ) + assert trial_info.config == config2 + assert intensifier._stage == IntensifierStage.PROCESS_FIRST_CONFIG_RUN + + # eval config 2 (=first run) + trial_value = evaluate_challenger(trial_info, target_function, stats, runhistory) + inc, perf = intensifier.process_results( + trial_info=trial_info, + trial_value=trial_value, + incumbent=None, + runhistory=runhistory, + time_bound=np.inf, + ) + assert inc == config2 + assert intensifier._stage == IntensifierStage.RUN_INCUMBENT + assert stats.incumbent_changed == 1 + assert intensifier._n_iters == 1 # 1 intensification run complete! + + # Regular intensification begins - run incumbent + # Normally a challenger will be given, which in this case is the incumbent + # But no more instances are available. So to prevent cicles + # where No iteration happens, provide the challengers + intent, trial_info = intensifier.get_next_trial( + challengers=[ + config1, + config0, + ], # since incumbent is run, no configs required + incumbent=inc, + runhistory=runhistory, + get_next_configurations=None, + ) + + # no new TA runs as there are no more instances to run + assert inc == config2 + assert stats.incumbent_changed == 1 + assert len(runhistory.get_trials(config2, only_max_observed_budget=True)) == 1 + + assert intensifier._stage == IntensifierStage.PROCESS_INCUMBENT_RUN + + # run challenger now that the incumbent has been executed + # So this call happen above, to save one iteration + # assert intensifier._stage == IntensifierStage.RUN_CHALLENGER + assert trial_info.config == config2 + trial_value = evaluate_challenger(trial_info, target_function, stats, runhistory) + inc, perf = intensifier.process_results( + trial_info=trial_info, + trial_value=trial_value, + incumbent=inc, + runhistory=runhistory, + time_bound=np.inf, + ) + + # challenger has a better performance, so incumbent has changed + assert inc == config2 + assert stats.incumbent_changed == 1 + assert intensifier._stage == IntensifierStage.RUN_CHALLENGER + assert not intensifier._continue_challenger + assert intensifier._n_iters == 1 # iteration continues as `min_chall` condition is not met + + # intensification continues running incumbent again in same iteration... + # run incumbent + # Same here, No further instance-seed pairs for incumbent available + # so above call gets the new config to run + assert trial_info.config == config2 + + # There is a transition from: + # IntensifierStage.RUN_FIRST_CONFIG-> IntensifierStage.RUN_INCUMBENT + # Because after the first run, incumbent is run. + # Nevertheless, there is now a transition: + # IntensifierStage.RUN_INCUMBENT->IntensifierStage.RUN_CHALLENGER + # because in add_inc_run, there are more available instance pairs + # FROM: IntensifierStage.RUN_INCUMBENT TO: IntensifierStage.RUN_INCUMBENT WHY: no more to run + # if all have been run, compare challenger performance + # assert intensifier._stage == IntensifierStage.RUN_CHALLENGER + assert intensifier._stage == IntensifierStage.RUN_CHALLENGER + + trial_value = evaluate_challenger(trial_info, target_function, stats, runhistory) + inc, perf = intensifier.process_results( + trial_info=trial_info, + trial_value=trial_value, + incumbent=inc, + runhistory=runhistory, + time_bound=np.inf, + ) + + # run challenger + intent, trial_info = intensifier.get_next_trial( + challengers=None, # don't need a new list here as old one is cont'd + incumbent=inc, + runhistory=runhistory, + get_next_configurations=None, + ) + assert trial_info.config == config2 + assert intensifier._stage == IntensifierStage.PROCESS_INCUMBENT_RUN + trial_value = evaluate_challenger(trial_info, target_function, stats, runhistory) + inc, perf = intensifier.process_results( + trial_info=trial_info, + incumbent=inc, + runhistory=runhistory, + time_bound=np.inf, + trial_value=trial_value, + ) + + assert inc == config2 + assert stats.incumbent_changed == 1 + assert intensifier._stage == IntensifierStage.RUN_CHALLENGER + assert intensifier._n_iters == 1 # 2 intensification run complete! + + # No configs should be left at the end + with pytest.raises(StopIteration): + next(intensifier._configs_to_run) + + assert len(runhistory.get_trials(config0, only_max_observed_budget=True)) == 0 + assert len(runhistory.get_trials(config1, only_max_observed_budget=True)) == 0 + assert len(runhistory.get_trials(config2, only_max_observed_budget=True)) == 3 + + +def test_evaluate_challenger_2(make_scenario, make_stats, configspace_small, runhistory): + """ + Test evaluate_challenger for a resumed SMAC run (first run with incumbent) + """ + + def target(x, seed=0): + return 2 * x["a"] + x["b"] + + scenario = make_scenario(configspace_small, use_instances=True, n_instances=1, deterministic=True) + stats = make_stats(scenario) + intensifier = Intensifier(scenario=scenario, race_against=None) + intensifier._stats = stats + target_function = TargetFunctionRunner(scenario, target, required_arguments=["seed"]) + configs = configspace_small.sample_configuration(20) + + config0 = configs[16] + config1 = configs[15] + + assert intensifier._n_iters == 0 + assert intensifier._stage == IntensifierStage.RUN_FIRST_CONFIG + + # adding run for incumbent configuration + runhistory.add( + config=config0, + cost=1, + time=1, + status=StatusType.SUCCESS, + instance=1, + seed=None, + additional_info=None, + ) + + # intensification - incumbent will be run, but not as RUN_FIRST_CONFIG stage + intent_, trial_info = intensifier.get_next_trial( + challengers=[config1], + incumbent=config0, + runhistory=runhistory, + get_next_configurations=None, + ) + trial_value = evaluate_challenger(trial_info, target_function, stats, runhistory) + inc, perf = intensifier.process_results( + trial_info=trial_info, + incumbent=config0, + runhistory=runhistory, + time_bound=np.inf, + trial_value=trial_value, + ) + + assert intensifier._stage == IntensifierStage.RUN_CHALLENGER + assert len(runhistory.get_trials(config0, only_max_observed_budget=True)) == 2 + + +def test_no_new_intensification_wo_challenger_run(make_scenario, make_stats, configspace_small, runhistory): + """ + This test ensures that no new iteration is started if no challenger run was conducted. + """ + + def target(x, seed=0): + return 2 * x["a"] + x["b"] + + scenario = make_scenario(configspace_small, use_instances=True, n_instances=1, deterministic=True) + stats = make_stats(scenario) + intensifier = Intensifier(scenario=scenario, race_against=None, min_challenger=1) + intensifier._stats = stats + target_function = TargetFunctionRunner(scenario, target, required_arguments=["seed"]) + configs = configspace_small.sample_configuration(20) + + config0 = configs[16] + config1 = configs[15] + config2 = configs[2] + + assert intensifier._n_iters == 0 + assert intensifier._stage == IntensifierStage.RUN_FIRST_CONFIG + + intent, trial_info = intensifier.get_next_trial( + challengers=[config2], + incumbent=None, + runhistory=runhistory, + get_next_configurations=None, + ) + assert trial_info.config == config2 + assert intensifier._stage == IntensifierStage.PROCESS_FIRST_CONFIG_RUN + trial_value = evaluate_challenger(trial_info, target_function, stats, runhistory) + inc, perf = intensifier.process_results( + trial_info=trial_info, + incumbent=None, + runhistory=runhistory, + time_bound=np.inf, + trial_value=trial_value, + ) + assert inc == config2 + assert intensifier._stage == IntensifierStage.RUN_INCUMBENT + assert intensifier._n_iters == 1 # 1 intensification run complete! + + # Regular intensification begins - run incumbent + + # No further instance-seed pairs for incumbent available + # Here None challenger is suggested. Code jumps to next iteration + # This causes a transition from RUN_INCUMBENT->RUN_CHALLENGER + # But then, the next configuration to run is the incumbent + # We don't rerun the incumbent (see message): + # Challenger was the same as the current incumbent; Skipping challenger + # Then, we try to get more challengers, but below all challengers + # Provided are config3, the incumbent which means nothing more to run + intent, trial_info = intensifier.get_next_trial( + challengers=[config2], # since incumbent is run, no configs required + incumbent=inc, + runhistory=runhistory, + get_next_configurations=None, + ) + + assert trial_info.config is None + assert intensifier._stage == IntensifierStage.RUN_CHALLENGER + + intensifier._next_iteration() + + # Add a configuration, then try to execute it afterwards + assert intensifier._n_iters == 2 + + runhistory.add( + config=config0, + cost=1, + time=1, + status=StatusType.SUCCESS, + instance="i1", + seed=0, + additional_info=None, + ) + intensifier._stage = IntensifierStage.RUN_CHALLENGER + + # In the upcoming get next run, the stage is RUN_CHALLENGER + # so the intensifier tries to run config1. Nevertheless, + # there are no further instances for this configuration available. + # In this scenario, the intensifier produces a SKIP intent as an indication + # that a new iteration must be initiated, and for code simplicity, + # relies on a new call to get_next_trial to yield more configurations + intent, trial_info = intensifier.get_next_trial( + challengers=[config0], incumbent=inc, runhistory=runhistory, get_next_configurations=None + ) + assert intent == TrialInfoIntent.SKIP + + # This doesn't return a config because the array of configs is exhausted + intensifier._stage = IntensifierStage.RUN_CHALLENGER + config, _ = intensifier.get_next_challenger(challengers=None, get_next_configurations=None) + assert config is None + # This finally gives a runable configuration + intent, trial_info = intensifier.get_next_trial( + challengers=[config1], incumbent=inc, runhistory=runhistory, get_next_configurations=None + ) + trial_value = evaluate_challenger(trial_info, target_function, stats, runhistory) + inc, perf = intensifier.process_results( + trial_info=trial_info, + incumbent=inc, + runhistory=runhistory, + time_bound=np.inf, + trial_value=trial_value, + ) + # 4 Iterations due to the proactive runs + # of get next challenger + assert intensifier._n_iters == 3 + assert intensifier._num_challenger_run == 1 diff --git a/tests/test_intensifier/test_parallel_scheduler.py b/tests/test_intensifier/test_parallel_scheduler.py new file mode 100644 index 000000000..8b8da59a1 --- /dev/null +++ b/tests/test_intensifier/test_parallel_scheduler.py @@ -0,0 +1,171 @@ +import unittest +from unittest import mock + +from smac.intensifier.abstract_parallel_intensifier import AbstractParallelIntensifier +from smac.runhistory import TrialInfo, TrialInfoIntent, TrialValue +from smac.runner.abstract_runner import StatusType + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def mock_ranker(sh): + return sh._stage, len(sh.run_tracker) + + +def test_sort_instances_by_stage(make_scenario, make_stats, configspace_small, runhistory): + """Ensures that we prioritize the more advanced stage iteration""" + scenario = make_scenario(configspace_small, use_instances=True, n_instances=3, deterministic=True) + stats = make_stats(scenario) + scheduler = AbstractParallelIntensifier(scenario=scenario) + scheduler._stats = stats + + scheduler._get_intensifier_ranking = mock_ranker + + def add_sh_mock(stage, config_inst_pairs): + sh = mock.Mock() + sh.run_tracker = [] + for i in range(config_inst_pairs): + sh.run_tracker.append((i, i, i)) + sh._stage = stage + return sh + + # Add more SH to make testing interesting + instances = {} + instances[0] = add_sh_mock(stage=1, config_inst_pairs=6) + instances[1] = add_sh_mock(stage=1, config_inst_pairs=2) + + # We only have two configurations in the same stage. + # In this case, we want to prioritize the one with more launched runs + # that is zero + assert list(scheduler._sort_instances_by_stage(instances)) == [0, 1] + + # One more instance comparison to be supper safe + instances[2] = add_sh_mock(stage=1, config_inst_pairs=7) + assert list(scheduler._sort_instances_by_stage(instances)) == [2, 0, 1] + + # Not let us add a more advanced stage run + instances[3] = add_sh_mock(stage=2, config_inst_pairs=1) + assert list(scheduler._sort_instances_by_stage(instances)) == [3, 2, 0, 1] + + # Make 1 the oldest stage + instances[1] = add_sh_mock(stage=4, config_inst_pairs=1) + assert list(scheduler._sort_instances_by_stage(instances)) == [1, 3, 2, 0] + + # Add a new run that's empty + instances[4] = add_sh_mock(stage=0, config_inst_pairs=0) + assert list(scheduler._sort_instances_by_stage(instances)) == [1, 3, 2, 0, 4] + + # Make 4 stage 4 but with not as many instances as 1 + instances[4] = add_sh_mock(stage=4, config_inst_pairs=0) + assert list(scheduler._sort_instances_by_stage(instances)) == [1, 4, 3, 2, 0] + + # And lastly 0 -> stage 4 + instances[0] = add_sh_mock(stage=4, config_inst_pairs=0) + assert list(scheduler._sort_instances_by_stage(instances)) == [1, 0, 4, 3, 2] + + +def test_process_results(make_scenario, make_stats, configspace_small, runhistory): + """Ensures that the results are processed by the pertinent intensifer, + based on the source id""" + scenario = make_scenario(configspace_small, use_instances=True, n_instances=3, deterministic=True) + stats = make_stats(scenario) + scheduler = AbstractParallelIntensifier(scenario=scenario) + scheduler._stats = stats + + scheduler._intensifier_instances = { + 0: mock.Mock(), + 1: mock.Mock(), + 2: mock.Mock(), + } + + trial_info = TrialInfo( + config=None, + instance=0, + seed=0, + budget=0.0, + source=2, + ) + + trial_value = TrialValue(cost=1, time=0.5, status=StatusType.SUCCESS, starttime=1, endtime=2, additional_info={}) + + scheduler.process_results( + trial_info=trial_info, trial_value=trial_value, incumbent=None, runhistory=None, time_bound=None + ) + assert scheduler._intensifier_instances[0].process_results.call_args is None + assert scheduler._intensifier_instances[1].process_results.call_args is None + assert scheduler._intensifier_instances[2].process_results.call_args[1]["trial_info"] == trial_info + + +def test_get_next_trial_wait(make_scenario, make_stats, configspace_small, runhistory): + """Makes sure we wait if all intensifiers are busy, and no new instance got added. + This test the case that number of workers are equal to number of instances + """ + scenario = make_scenario(configspace_small, use_instances=True, n_instances=3, deterministic=True) + stats = make_stats(scenario) + scheduler = AbstractParallelIntensifier(scenario=scenario) + scheduler._stats = stats + + scheduler._get_intensifier_ranking = mock_ranker + scheduler._intensifier_instances = {0: mock.Mock()} + scheduler._intensifier_instances[0].get_next_trial.return_value = (TrialInfoIntent.WAIT, None) + scheduler._intensifier_instances[0]._stage = 0 + scheduler._intensifier_instances[0].run_tracker = () + + with unittest.mock.patch( + "smac.intensifier.abstract_parallel_intensifier.AbstractParallelIntensifier._add_new_instance" + ) as add_new_instance: + add_new_instance.return_value = False + intent, trial_info = scheduler.get_next_trial( + challengers=None, + incumbent=None, + get_next_configurations=None, + runhistory=None, + repeat_configs=False, + n_workers=1, + ) + assert intent == TrialInfoIntent.WAIT + + +def test_get_next_trial_add_instance(make_scenario, make_stats, configspace_small, runhistory): + """Makes sure we add an instance only when all other instances are waiting, + This happens when n_workers greater than the number of instances + """ + scenario = make_scenario(configspace_small, use_instances=True, n_instances=3, deterministic=True) + stats = make_stats(scenario) + + with unittest.mock.patch( + "smac.intensifier.abstract_parallel_intensifier.AbstractParallelIntensifier._add_new_instance" + ) as add_new_instance: + scheduler = AbstractParallelIntensifier(scenario=scenario) + scheduler._stats = stats + + def instance_added(args): + source = len(scheduler._intensifier_instances) + scheduler._intensifier_instances[source] = mock.Mock() + scheduler._intensifier_instances[source].get_next_trial.return_value = ( + TrialInfoIntent.RUN, + None, + ) + return True + + add_new_instance.side_effect = instance_added + scheduler._get_intensifier_ranking = mock_ranker + scheduler._intensifier_instances = {0: mock.Mock()} + scheduler._intensifier_instances[0].get_next_trial.return_value = ( + TrialInfoIntent.WAIT, + None, + ) + scheduler._intensifier_instances[0]._stage = 0 + scheduler._intensifier_instances[0].run_tracker = () + + intent, trial_info = scheduler.get_next_trial( + challengers=None, + incumbent=None, + get_next_configurations=None, + runhistory=None, + repeat_configs=False, + n_workers=1, + ) + assert len(scheduler._intensifier_instances) == 2 + assert intent == TrialInfoIntent.RUN diff --git a/tests/test_intensifier/test_successive_halving.py b/tests/test_intensifier/test_successive_halving.py new file mode 100644 index 000000000..0b33ed108 --- /dev/null +++ b/tests/test_intensifier/test_successive_halving.py @@ -0,0 +1,1651 @@ +import time +from multiprocessing.sharedctypes import Value +from unittest import mock + +import numpy as np +import pytest +from ConfigSpace import Configuration + +from smac.intensifier.successive_halving import SuccessiveHalving +from smac.intensifier.successive_halving_worker import SuccessiveHalvingWorker +from smac.runhistory import RunHistory, TrialInfo, TrialInfoIntent, TrialValue +from smac.runner.abstract_runner import StatusType +from smac.runner.target_function_runner import TargetFunctionRunner +from smac.stats import Stats + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def evaluate_challenger( + trial_info: TrialInfo, + target_function: TargetFunctionRunner, + stats: Stats, + runhistory: RunHistory, + force_update=False, +): + """ + Wrapper over challenger evaluation + + SMBO objects handles run history now, but to keep + same testing functionality this function is a small + wrapper to launch the taf and add it to the history + """ + # evaluating configuration + trial_info, result = target_function.run_wrapper( + trial_info=trial_info, + ) + + stats._target_function_walltime_used += float(result.time) + stats._finished += 1 + + runhistory.add( + config=trial_info.config, + cost=result.cost, + time=result.time, + status=result.status, + instance=trial_info.instance, + seed=trial_info.seed, + budget=trial_info.budget, + force_update=force_update, + ) + stats._n_configs = len(runhistory.config_ids) + + return result + + +def target_from_trial_info(trial_info: TrialInfo): + value_from_config = sum([a for a in trial_info.config.get_dictionary().values() if not isinstance(a, str)]) + return TrialValue( + cost=value_from_config, + time=0.5, + status=StatusType.SUCCESS, + starttime=time.time(), + endtime=time.time() + 1, + additional_info={}, + ) + + +@pytest.fixture +def SH(make_scenario, make_stats, configspace_small): + scenario = make_scenario( + configspace_small, + use_instances=True, + n_instances=3, + deterministic=False, + min_budget=2, + max_budget=5, + ) + stats = make_stats(scenario) + intensifier = SuccessiveHalving(scenario=scenario, eta=2, n_seeds=2) + intensifier._stats = stats + + return intensifier + + +@pytest.fixture +def _SH(SH): + return SuccessiveHalvingWorker(SH) + + +@pytest.fixture +def make_sh_worker(make_scenario, make_stats, configspace_small): + def _make( + deterministic=False, + min_budget=2, + max_budget=5, + eta=2, + n_instances=3, + n_seeds=1, + min_challenger=1, + instance_order="shuffle_once", + incumbent_selection="highest_executed_budget", + _all_budgets=None, + _n_configs_in_stage=None, + ): + scenario = make_scenario( + configspace_small, + use_instances=True, + n_instances=n_instances, + deterministic=deterministic, + min_budget=min_budget, + max_budget=max_budget, + ) + stats = make_stats(scenario) + sh = SuccessiveHalving( + scenario=scenario, + instance_order=instance_order, + incumbent_selection=incumbent_selection, + min_challenger=min_challenger, + eta=eta, + n_seeds=n_seeds, + ) + sh._stats = stats + + return SuccessiveHalvingWorker( + sh, + _all_budgets=_all_budgets, + _n_configs_in_stage=_n_configs_in_stage, + ) + + return _make + + +@pytest.fixture +def make_target_function(): + def _make(scenario, func, required_arguments=[]): + return TargetFunctionRunner( + target_function=func, + scenario=scenario, + required_arguments=required_arguments, + ) + + return _make + + +@pytest.fixture +def configs(configspace_small): + configs = configspace_small.sample_configuration(20) + return (configs[16], configs[15], configs[2], configs[3]) + + +def test_init(SH): + """Makes sure that a proper _SH is created""" + + # We initialize the SH with zero intensifier_instances + assert len(SH._intensifier_instances) == 0 + + # Add an instance to check the _SH initialization + assert SH._add_new_instance(n_workers=1) + + # Parameters properly passed to _SH + assert len(SH._instance_seed_pairs) == 6 + assert SH._min_budget == 2 + assert SH._max_budget == 5 + + +def test_process_results_via_sourceid(SH, runhistory, configs): + """Makes sure source id is honored when deciding + which _SH will consume the result/trial_info""" + # Mock the _SH so we can make sure the correct item is passed + for i in range(10): + SH._intensifier_instances[i] = mock.Mock() + + # randomly create trial_infos and push into SH. Then we will make + # sure they got properly allocated + for i in np.random.choice(list(range(10)), 30): + trial_info = TrialInfo( + config=configs[0], + instance="i1", + seed=0, + budget=None, + source=i, + ) + + # make sure results aren't messed up via magic variable + # That is we check only the proper _SH has this + magic = time.time() + + trial_value = TrialValue( + cost=1, + time=0.5, + status=StatusType.SUCCESS, + starttime=1, + endtime=2, + additional_info=magic, + ) + SH.process_results( + trial_info=trial_info, + incumbent=None, + runhistory=runhistory, + time_bound=None, + trial_value=trial_value, + log_trajectory=False, + ) + + # Check the call arguments of each sh instance and make sure + # it is the correct one + + # First the expected one + assert SH._intensifier_instances[i].process_results.call_args[1]["trial_info"] == trial_info + assert SH._intensifier_instances[i].process_results.call_args[1]["trial_value"] == trial_value + + all_other_trial_infos, all_other_results = [], [] + for j, item in enumerate(SH._intensifier_instances): + # Skip the expected _SH + if i == j: + continue + if SH._intensifier_instances[j].process_results.call_args is None: + all_other_trial_infos.append(None) + else: + all_other_trial_infos.append(SH._intensifier_instances[j].process_results.call_args[1]["trial_info"]) + all_other_results.append(SH._intensifier_instances[j].process_results.call_args[1]["trial_value"]) + + assert trial_info not in all_other_trial_infos + assert trial_value not in all_other_results + + +def test_get_next_trial_single_SH(SH, runhistory, configs): + """Makes sure that a single _SH returns a valid config""" + + challengers = configs[:4] + for i in range(30): + intent, trial_info = SH.get_next_trial( + challengers=challengers, + incumbent=None, + get_next_configurations=None, + runhistory=runhistory, + n_workers=1, + ) + + # Regenerate challenger list + challengers = [c for c in challengers if c != trial_info.config] + + if intent == TrialInfoIntent.WAIT: + break + + # Add the config to rh in order to make SH aware that this + # config/instance was launched + runhistory.add( + config=trial_info.config, + cost=10, + time=0.0, + status=StatusType.RUNNING, + instance=trial_info.instance, + seed=trial_info.seed, + budget=trial_info.budget, + ) + + # We should not create more _SH intensifier_instances + assert len(SH._intensifier_instances) == 1 + + # We are running with: + # 'all_budgets': array([2.5, 5. ]) -> 2 intensifier_instances per config top + # 'n_configs_in_stage': [2.0, 1.0], + # This means we run int(2.5) + 2.0 = 4 runs before waiting + assert i == 4 + + +def test_get_next_trial_dual_SH(SH, runhistory, configs): + """Makes sure that two _SH can properly coexist and tag + trial_info properly""" + + # Everything here will be tested with a single _SH + challengers = configs[:4] + for i in range(30): + intent, trial_info = SH.get_next_trial( + challengers=challengers, + incumbent=None, + get_next_configurations=None, + runhistory=runhistory, + n_workers=2, + ) + + # Regenerate challenger list + challengers = [c for c in challengers if c != trial_info.config] + + # Add the config to rh in order to make SH aware that this + # config/instance was launched + if intent == TrialInfoIntent.WAIT: + break + runhistory.add( + config=trial_info.config, + cost=10, + time=0.0, + status=StatusType.RUNNING, + instance=trial_info.instance, + seed=trial_info.seed, + budget=trial_info.budget, + ) + + # We create a second sh intensifier_instances as after 4 runs, the _SH + # number zero needs to wait + assert len(SH._intensifier_instances) == 2 + + # We are running with: + # 'all_budgets': array([2.5, 5. ]) -> 2 intensifier_instances per config top + # 'n_configs_in_stage': [2.0, 1.0], + # This means we run int(2.5) + 2.0 = 4 runs before waiting + # But we have 2 successive halvers now! + assert i == 8 + + +def test_add_new_instance(SH): + """Test whether we can add a _SH and when we should not""" + + # By default we do not create a _SH + # test adding the first instance! + assert len(SH._intensifier_instances) == 0 + assert SH._add_new_instance(n_workers=1) + assert len(SH._intensifier_instances) == 1 + assert isinstance(SH._intensifier_instances[0], SuccessiveHalvingWorker) + # A second call should not add a new _SH + assert not SH._add_new_instance(n_workers=1) + + # We try with 2 _SH active + + # We effectively return true because we added a new _SH + assert SH._add_new_instance(n_workers=2) + + assert len(SH._intensifier_instances) == 2 + assert isinstance(SH._intensifier_instances[1], SuccessiveHalvingWorker) + + # Trying to add a third one should return false + assert not SH._add_new_instance(n_workers=2) + assert len(SH._intensifier_instances) == 2 + + +def _exhaust_run_and_get_incumbent(SH, runhistory, configs, n_workers=2): + """ + Runs all provided configs on all intensifier_instances and return the incumbent + as a nice side effect runhistory/stats are properly filled + """ + challengers = configs[:4] + incumbent = None + inc_perf = None + for i in range(100): + try: + intent, trial_info = SH.get_next_trial( + challengers=challengers, + incumbent=None, + get_next_configurations=None, + runhistory=runhistory, + n_workers=n_workers, + ) + except ValueError as e: + # Get configurations until you run out of them + print(e) + break + + # Regenerate challenger list + challengers = [c for c in challengers if c != trial_info.config] + + if intent == TrialInfoIntent.WAIT: + break + + trial_value = target_from_trial_info(trial_info) + runhistory.add( + config=trial_info.config, + cost=trial_value.cost, + time=trial_value.time, + status=trial_value.status, + instance=trial_info.instance, + seed=trial_info.seed, + budget=trial_info.budget, + ) + incumbent, inc_perf = SH.process_results( + trial_info=trial_info, + incumbent=incumbent, + runhistory=runhistory, + time_bound=100.0, + trial_value=trial_value, + log_trajectory=False, + ) + + return incumbent, inc_perf + + +def test_parallel_same_as_serial_SH(SH, _SH, configs): + """Makes sure we behave the same as a serial run at the end""" + runhistory1 = RunHistory() + runhistory2 = RunHistory() + + incumbent, inc_perf = _exhaust_run_and_get_incumbent(_SH, runhistory1, configs) + + # Just to make sure nothing has changed from the _SH side to make + # this check invalid: + # We add config values, so config x with y and z should be the lesser cost + assert incumbent == configs[0] + # assert inc_perf == 7.0 + + # Do the same for SH, but have multiple _SH in there + # _add_new_instance returns true if it was able to add a new _SH + # We call this method twice because we want 2 workers + assert SH._add_new_instance(n_workers=2) + assert SH._add_new_instance(n_workers=2) + incumbent_psh, inc_perf_psh = _exhaust_run_and_get_incumbent(SH, runhistory2, configs) + assert incumbent == incumbent_psh + + # This makes sure there is a single incumbent in SH + assert inc_perf == inc_perf_psh + + # We don't want to loose any configuration, and particularly + # we want to make sure the values of _SH to SH match + assert len(runhistory1._data) == len(runhistory2._data) + + # We are comparing exhausted single vs parallel successive + # halving runs. The number and type of configs should be the same + # and is enforced as a dictionary key argument check. The number + # of runs will be different ParallelSuccesiveHalving has 2 _SH intensifier_instances + # yet we make sure that after exhaustion, the budgets a config was run + # should match + configs_sh_rh = {} + for k, v in runhistory1._data.items(): + config_sh = runhistory1.ids_config[k.config_id] + if config_sh not in configs_sh_rh: + configs_sh_rh[config_sh] = [] + if v.cost not in configs_sh_rh[config_sh]: + configs_sh_rh[config_sh].append(v.cost) + + configs_psh_rh = {} + for k, v in runhistory2._data.items(): + config_psh = runhistory2.ids_config[k.config_id] + if config_psh not in configs_psh_rh: + configs_psh_rh[config_psh] = [] + if v.cost not in configs_psh_rh[config_psh]: + configs_psh_rh[config_psh].append(v.cost) + + # If this dictionaries are equal it means we have all configs + # and the values track the numbers and actual cost! + assert configs_sh_rh == configs_psh_rh + + +def test_init_1(make_sh_worker): + """Test parameter initializations for successive halving - instance as budget.""" + _SH = make_sh_worker(deterministic=False, min_budget=None, max_budget=None, n_seeds=2) + + assert len(_SH._successive_halving._instance_seed_pairs) == 6 # since instance-seed pairs + assert len(_SH._successive_halving._instances) == 3 + assert _SH._successive_halving._min_budget == 1 + assert _SH._successive_halving._max_budget == 6 + assert _SH._n_configs_in_stage == [4.0, 2.0, 1.0] + assert _SH._successive_halving._instance_as_budget + assert _SH._successive_halving._repeat_configs + + +def test_init_2(make_sh_worker): + """Test parameter initialiations for successive halving - real-valued budget.""" + _SH = make_sh_worker(deterministic=False, min_budget=1, max_budget=10, n_instances=1, n_seeds=10) + + assert len(_SH._successive_halving._instance_seed_pairs) == 10 # since instance-seed pairs + assert _SH._successive_halving._min_budget == 1 + assert _SH._successive_halving._max_budget == 10 + assert _SH._n_configs_in_stage == [8.0, 4.0, 2.0, 1.0] + assert list(_SH._all_budgets) == [1.25, 2.5, 5.0, 10.0] + assert _SH._successive_halving._instance_as_budget + assert _SH._successive_halving._repeat_configs + + # Max budget > instance seed pairs + with pytest.raises(ValueError): + _SH = make_sh_worker(deterministic=False, min_budget=1, max_budget=10, n_instances=1, n_seeds=1) + + +def test_init_3(make_sh_worker): + """Test parameter initialiations for successive halving - real-valued budget, high initial budget.""" + # In case of deterministic, only one seed is used + with pytest.raises(ValueError): + _SH = make_sh_worker(deterministic=True, min_budget=9, max_budget=10, n_instances=1, n_seeds=10) + + _SH = make_sh_worker(deterministic=True, min_budget=9, max_budget=10, n_instances=10, n_seeds=1) + assert len(_SH._successive_halving._instance_seed_pairs) == 10 # since instance-seed pairs + assert _SH._successive_halving._min_budget == 9 + assert _SH._successive_halving._max_budget == 10 + assert _SH._n_configs_in_stage == [1.0] + assert list(_SH._all_budgets) == [10.0] + assert _SH._successive_halving._instance_as_budget + assert not _SH._successive_halving._repeat_configs + + +def test_init_4(make_sh_worker): + """ + Test wrong parameter initializations for successive halving + """ + # eta < 1 + with pytest.raises(ValueError, match="The parameter `eta` must be greater than 1."): + make_sh_worker(deterministic=True, min_budget=None, max_budget=None, n_instances=1, n_seeds=1, eta=0) + + # max budget > instance-seed pairs + with pytest.raises(ValueError, match="Max budget.*"): + make_sh_worker(deterministic=False, min_budget=1, max_budget=5, n_instances=3, n_seeds=1) + + +def test_top_k_1(make_sh_worker, runhistory, configs): + """ + test _top_k() for configs with same instance-seed-budget keys + """ + intensifier = make_sh_worker(n_instances=2, min_budget=1, max_budget=4, n_seeds=2) + config1 = configs[0] + config2 = configs[1] + config3 = configs[2] + config4 = configs[3] + + runhistory.add( + config=config1, + cost=1, + time=1, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + additional_info=None, + ) + runhistory.add( + config=config1, + cost=1, + time=1, + status=StatusType.SUCCESS, + instance="i2", + seed=None, + additional_info=None, + ) + runhistory.add( + config=config2, + cost=2, + time=2, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + additional_info=None, + ) + runhistory.add( + config=config2, + cost=2, + time=2, + status=StatusType.SUCCESS, + instance="i2", + seed=None, + additional_info=None, + ) + runhistory.add( + config=config3, + cost=3, + time=3, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + additional_info=None, + ) + runhistory.add( + config=config3, + cost=3, + time=3, + status=StatusType.SUCCESS, + instance="i2", + seed=None, + additional_info=None, + ) + runhistory.add( + config=config4, + cost=0.5, + time=0.5, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + additional_info=None, + ) + runhistory.add( + config=config4, + cost=0.5, + time=0.5, + status=StatusType.SUCCESS, + instance="i2", + seed=None, + additional_info=None, + ) + conf = intensifier._top_k( + configs=configs[:4], + k=2, + runhistory=runhistory, + ) + + # Check that config4 is also before config1 (as it has the lower cost) + assert conf == [config4, config1] + + +def test_top_k_2(make_sh_worker, runhistory, configs): + """Test _top_k() for configs with different instance-seed-budget keys""" + intensifier = make_sh_worker(n_instances=2, min_budget=1, max_budget=4, n_seeds=2) + config1 = configs[0] + config2 = configs[1] + config3 = configs[2] + + runhistory.add( + config=config1, + cost=1, + time=1, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + additional_info=None, + ) + runhistory.add( + config=config2, + cost=10, + time=10, + status=StatusType.SUCCESS, + instance="i2", + seed=None, + additional_info=None, + ) + + with pytest.raises(ValueError, match="Can not compare configs"): + intensifier._top_k( + configs=[config2, config1, config3], + k=1, + runhistory=runhistory, + ) + + +def test_top_k_3(make_sh_worker, runhistory, configs): + """Test _top_k() for not enough configs to generate for the next budget""" + intensifier = make_sh_worker(n_instances=1, min_budget=1, max_budget=4, n_seeds=4) + config1 = configs[0] + config2 = configs[1] + + runhistory.add( + config=config1, + cost=1, + time=1, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + additional_info=None, + ) + runhistory.add( + config=config2, + cost=1, + time=1, + status=StatusType.CRASHED, + instance="i1", + seed=None, + additional_info=None, + ) + configs = intensifier._top_k(configs=[config1], k=2, runhistory=runhistory) + + # top_k should return whatever configuration is possible + assert configs == [config1] + + +def test_top_k_4(make_sh_worker, runhistory, configs): + """Test _top_k() for not enough configs to generate for the next budget""" + intensifier = make_sh_worker(n_instances=1, min_budget=1, max_budget=10, n_seeds=10, eta=2, min_challenger=1) + config1 = configs[0] + config2 = configs[1] + config3 = configs[2] + config4 = configs[3] + + intensifier._update_stage(runhistory) + runhistory.add( + config=config1, + cost=1, + time=1, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + budget=1, + additional_info=None, + ) + runhistory.add( + config=config2, + cost=1, + time=1, + status=StatusType.DONOTADVANCE, + instance="i1", + seed=None, + budget=1, + additional_info=None, + ) + runhistory.add( + config=config3, + cost=1, + time=1, + status=StatusType.DONOTADVANCE, + instance="i1", + seed=None, + budget=1, + additional_info=None, + ) + runhistory.add( + config=config4, + cost=1, + time=1, + status=StatusType.DONOTADVANCE, + instance="i1", + seed=None, + budget=1, + additional_info=None, + ) + intensifier._success_challengers.add(config1) + intensifier._failed_challengers.add(config2) + intensifier._failed_challengers.add(config3) + intensifier._failed_challengers.add(config4) + intensifier._update_stage(runhistory) + assert intensifier._failed_challenger_offset == 3 # We miss three challenger for this round + + configs = intensifier._top_k(configs=[config1], k=2, runhistory=runhistory) + assert configs == [config1] + + runhistory.add( + config=config1, + cost=1, + time=1, + status=StatusType.DONOTADVANCE, + instance="i1", + seed=None, + budget=intensifier._all_budgets[1], + additional_info=None, + ) + intensifier._failed_challengers.add(config2) + intensifier._update_stage(runhistory) + assert intensifier._stage == 0 # Going back, since there are not enough to advance + + +def test_get_next_trial_1(make_sh_worker, runhistory, configs): + """ + test get_next_trial for a presently running configuration + """ + + def target(x): + return 1 + + intensifier = make_sh_worker(deterministic=False, n_instances=2, min_budget=1, max_budget=2, n_seeds=1, eta=2) + target_function = TargetFunctionRunner(intensifier._scenario, target) + config1 = configs[0] + config2 = configs[1] + + # next challenger from a list + intent, trial_info = intensifier.get_next_trial( + challengers=[config1], + get_next_configurations=None, + runhistory=runhistory, + incumbent=None, + ) + runhistory.add( + config=trial_info.config, + instance=trial_info.instance, + seed=trial_info.seed, + budget=trial_info.budget, + cost=10, + time=1, + status=StatusType.RUNNING, + additional_info=None, + ) + assert trial_info.config == config1 + assert intensifier._new_challenger + + # In the parallel scenario, we cannot wait for a configuration + # to be evaluated before moving to the next configuration in the same + # stage. That is, for this example we will have n_configs_in_stage=[2, 1] + # with all_budgets=[1. 2.]. In other words, in this stage we + # will have 2 configs each with 1 instance. + intent, trial_info_new = intensifier.get_next_trial( + challengers=[config2], + get_next_configurations=None, + runhistory=runhistory, + incumbent=None, + ) + runhistory.add( + config=trial_info_new.config, + instance=trial_info_new.instance, + seed=trial_info_new.seed, + budget=trial_info_new.budget, + cost=10, + time=1, + status=StatusType.RUNNING, + additional_info=None, + ) + assert trial_info_new.config == config2 + assert intensifier._running_challenger == trial_info_new.config + assert intensifier._new_challenger + + # evaluating configuration + assert trial_info.config is not None + trial_value = evaluate_challenger(trial_info, target_function, intensifier._stats, runhistory) + inc, inc_value = intensifier.process_results( + trial_info=trial_info, + incumbent=None, + runhistory=runhistory, + time_bound=np.inf, + trial_value=trial_value, + log_trajectory=False, + ) + + # We already launched trial_info_new. We expect 2 configs each with 1 seed/instance + # 1 has finished and already processed. We have not even run trial_info_new + # So we cannot advance to a new stage + intent, trial_info = intensifier.get_next_trial( + challengers=[config2], get_next_configurations=None, incumbent=inc, runhistory=runhistory + ) + assert trial_info.config is None + assert intent == TrialInfoIntent.WAIT + assert len(intensifier._success_challengers) == 1 + assert intensifier._new_challenger + + +def test_get_next_trial_2(make_sh_worker, configs, runhistory): + """ + test get_next_trial for higher stages of SH iteration + """ + intensifier = make_sh_worker(deterministic=True, n_instances=2, min_budget=1, max_budget=2, n_seeds=1, eta=2) + config1 = configs[0] + + intensifier._update_stage(runhistory=None) + intensifier._stage += 1 + intensifier._configs_to_run = [config1] + + # next challenger should come from configs to run + intent, trial_info = intensifier.get_next_trial( + challengers=None, + get_next_configurations=None, + runhistory=runhistory, + incumbent=None, + ) + assert trial_info.config == config1 + assert len(intensifier._configs_to_run) == 0 + assert not intensifier._new_challenger + + +def test_update_stage(make_sh_worker, runhistory, configs): + """Test update_stage - initializations for all tracking variables.""" + intensifier = make_sh_worker(deterministic=True, n_instances=2, min_budget=1, max_budget=2, n_seeds=1, eta=2) + config1 = configs[0] + config2 = configs[1] + + # First stage update + # Change in 2.0: Workers are initialized with stage 0 directly + # intensifier._update_stage(runhistory=None) + + assert intensifier._stage == 0 + assert intensifier._sh_iters == 0 + assert intensifier._running_challenger is None + assert intensifier._success_challengers == set() + + # higher stages + runhistory.add(config1, 1, 1, StatusType.SUCCESS) + runhistory.add(config2, 2, 2, StatusType.SUCCESS) + intensifier._success_challengers = {config1, config2} + intensifier._update_stage(runhistory=runhistory) + + assert intensifier._stage == 1 + assert intensifier._sh_iters == 0 + assert intensifier._configs_to_run == [config1] + + # next iteration + intensifier._success_challengers = {config1} + intensifier._update_stage(runhistory=runhistory) + + assert intensifier._stage == 0 + assert intensifier._sh_iters == 1 + assert isinstance(intensifier._configs_to_run, list) + assert len(intensifier._configs_to_run) == 0 + + +''' +# @unittest.mock.patch.object(_SuccessiveHalving, "_top_k") +def test_update_stage_2(make_sh_worker, runhistory, configs): + """ + test update_stage - everything good is in state do not advance + """ + + intensifier = make_sh_worker(deterministic=True, n_instances=0, min_budget=1, max_budget=4, n_seeds=1, eta=2) + config1 = configs[0] + config2 = configs[1] + config3 = configs[2] + config4 = configs[3] + + # update variables + intensifier._update_stage(runhistory=runhistory) + + intensifier._success_challengers.add(config1) + intensifier._success_challengers.add(config2) + intensifier.do_not_advance_challengers.add(config3) + intensifier.do_not_advance_challengers.add(config4) + + # top_k_mock.return_value = [config1, config3] + intensifier.return_value = [config1, config3] + + # Test that we update the stage as there is one configuration advanced to the next budget + assert intensifier._stage == 0 + intensifier._update_stage(runhistory=runhistory) + assert intensifier._stage == 1 + assert intensifier._configs_to_run == [config1] + assert intensifier.fail_chal_offset == 1 + assert len(intensifier._success_challengers) == 0 + assert len(intensifier.do_not_advance_challengers) == 0 + + intensifier = make_sh_worker(deterministic=True, n_instances=0, min_budget=1, max_budget=4, eta=2) + + # update variables + intensifier._update_stage(runhistory=runhistory) + + intensifier._success_challengers.add(config1) + intensifier._success_challengers.add(config2) + intensifier.do_not_advance_challengers.add(config3) + intensifier.do_not_advance_challengers.add(config4) + + intensifier.return_value = [config3, config4] + # top_k_mock.return_value = [config3, config4] + + # Test that we update the stage as there is no configuration advanced to the next budget + assert intensifier._stage == 0 + intensifier._update_stage(runhistory=runhistory) + assert intensifier._stage == 0 + assert intensifier._configs_to_run == [] + assert intensifier.fail_chal_offset == 0 + assert len(intensifier._success_challengers) == 0 + assert len(intensifier.do_not_advance_challengers) == 0 + + # top_k_mock.return_value = [] + intensifier.return_value = [] +''' + + +def test_evaluate_challenger_1(make_sh_worker, make_target_function, runhistory, configs): + """Test evaluate_challenger with quality objective & real-valued budget.""" + + def target(x: Configuration, seed: int, budget: float): + return 0.1 * budget + + config1 = configs[0] + config2 = configs[1] + config3 = configs[2] + + # instances = [None]??? + intensifier = make_sh_worker(deterministic=True, n_instances=0, min_budget=0.25, max_budget=0.5, eta=2) + intensifier._update_stage(runhistory=None) + + target_function = make_target_function(intensifier._scenario, target, required_arguments=["seed", "budget"]) + + runhistory.add( + config=config1, + cost=1, + time=1, + status=StatusType.SUCCESS, + seed=0, + budget=0.5, + ) + runhistory.add( + config=config2, + cost=1, + time=1, + status=StatusType.SUCCESS, + seed=0, + budget=0.25, + ) + runhistory.add( + config=config3, + cost=2, + time=1, + status=StatusType.SUCCESS, + seed=0, + budget=0.25, + ) + + intensifier._success_challengers = {config2, config3} + intensifier._update_stage(runhistory=runhistory) + + intent, trial_info = intensifier.get_next_trial( + challengers=[config1], + get_next_configurations=None, + incumbent=config1, + runhistory=runhistory, + ) + trial_value = evaluate_challenger(trial_info, target_function, intensifier._stats, runhistory) + inc, inc_value = intensifier.process_results( + trial_info=trial_info, + incumbent=config1, + runhistory=runhistory, + time_bound=np.inf, + trial_value=trial_value, + ) + + assert inc == config2 + assert inc_value == 0.05 + assert list(runhistory._data.keys())[-1].config_id == runhistory.config_ids[config2] + assert intensifier._stats.incumbent_changed == 1 + + +def test_incumbent_selection_default(make_sh_worker, make_target_function, runhistory, configs): + """Test _compare_config for default incumbent selection design (highest budget so far).""" + + config1 = configs[0] + config2 = configs[1] + config3 = configs[2] + config4 = configs[3] + + # instances = [None]??? + intensifier = make_sh_worker(deterministic=True, n_instances=2, min_budget=1, max_budget=2, eta=2) + intensifier._stage = 0 + # intensifier._update_stage(runhistory=None) + # SH considers challenger as incumbent in first run in evaluate_challenger + runhistory.add( + config=config1, + cost=1, + time=1, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + additional_info=None, + budget=1, + ) + inc = intensifier._compare_configs( + challenger=config1, + incumbent=config1, + runhistory=runhistory, + log_trajectory=False, + ) + assert inc == config1 + runhistory.add( + config=config1, + cost=1, + time=1, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + additional_info=None, + budget=2, + ) + inc = intensifier._compare_configs(challenger=config1, incumbent=inc, runhistory=runhistory, log_trajectory=False) + assert inc == config1 + + # Adding a worse configuration + runhistory.add( + config=config2, + cost=2, + time=2, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + additional_info=None, + budget=1, + ) + inc = intensifier._compare_configs(challenger=config2, incumbent=inc, runhistory=runhistory, log_trajectory=False) + assert inc == config1 + runhistory.add( + config=config2, + cost=2, + time=2, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + additional_info=None, + budget=2, + ) + inc = intensifier._compare_configs(challenger=config2, incumbent=inc, runhistory=runhistory, log_trajectory=False) + assert inc == config1 + + # Adding a better configuration, but the incumbent will only be changed on budget=2 + runhistory.add( + config=config3, + cost=0.5, + time=3, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + additional_info=None, + budget=1, + ) + inc = intensifier._compare_configs(challenger=config3, incumbent=inc, runhistory=runhistory, log_trajectory=False) + assert inc == config1 + runhistory.add( + config=config3, + cost=0.5, + time=3, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + additional_info=None, + budget=2, + ) + inc = intensifier._compare_configs(challenger=config3, incumbent=inc, runhistory=runhistory, log_trajectory=False) + assert inc == config3 + + intensifier = make_sh_worker(deterministic=True, n_instances=5, min_budget=1, eta=2) + intensifier._stage = 0 + + # Adding a better configuration, but the incumbent will only be changed on budget=2 + runhistory.add( + config=config4, + cost=0.1, + time=3, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + additional_info=None, + budget=1, + ) + inc = intensifier._compare_configs(challenger=config4, incumbent=inc, runhistory=runhistory, log_trajectory=False) + assert inc == config3 + runhistory.add( + config=config4, + cost=0.1, + time=3, + status=StatusType.SUCCESS, + instance="i1", + seed=None, + additional_info=None, + budget=2, + ) + inc = intensifier._compare_configs(challenger=config4, incumbent=inc, runhistory=runhistory, log_trajectory=False) + assert inc == config4 + + +def test_incumbent_selection_designs(make_sh_worker, make_target_function, runhistory, configs): + """Test _compare_config with different incumbent selection designs.""" + + config1 = configs[0] + config2 = configs[1] + config3 = configs[2] + config4 = configs[3] + + # instances = [None]??? + intensifier = make_sh_worker( + deterministic=True, + n_instances=2, + min_budget=1, + max_budget=2, + eta=2, + incumbent_selection="any_budget", + ) + intensifier._stage = 0 + + runhistory.add( + config=config1, + instance="i1", + seed=0, + budget=None, + cost=10, + time=1, + status=StatusType.SUCCESS, + additional_info=None, + ) + runhistory.add( + config=config1, + instance="i2", + seed=0, + budget=None, + cost=10, + time=1, + status=StatusType.SUCCESS, + additional_info=None, + ) + runhistory.add( + config=config2, + instance="i1", + seed=0, + budget=None, + cost=5, + time=1, + status=StatusType.SUCCESS, + additional_info=None, + ) + + # incumbent should be config2, since it has the best performance in one of the budgets + inc = intensifier._compare_configs( + incumbent=config2, + challenger=config1, + runhistory=runhistory, + log_trajectory=False, + ) + assert config2 == inc + + # if config1 is incumbent already, it shouldn't change + inc = intensifier._compare_configs( + incumbent=config1, + challenger=config2, + runhistory=runhistory, + log_trajectory=False, + ) + assert config1 == inc + + # select best on highest budget only + intensifier = make_sh_worker( + deterministic=True, + n_instances=4, + min_budget=1, + max_budget=4, + eta=2, + incumbent_selection="highest_budget", + ) + intensifier._stage = 0 + + # incumbent should not change, since there is no run on the highest budget, + # though config3 is run on a higher budget + runhistory.add( + config=config3, + instance="i1", + seed=0, + budget=None, + cost=0.5, + time=1, + status=StatusType.SUCCESS, + additional_info=None, + ) + runhistory.add( + config=config4, + instance="i1", + seed=0, + budget=None, + cost=5, + time=1, + status=StatusType.SUCCESS, + additional_info=None, + ) + inc = intensifier._compare_configs( + incumbent=config4, + challenger=config3, + runhistory=runhistory, + log_trajectory=False, + ) + assert config3 == inc + assert intensifier._stats.incumbent_changed == 0 + + # incumbent changes to config3 since that is run on the highest budget + runhistory.add( + config=config3, + instance="i1", + seed=0, + budget=None, + cost=10, + time=1, + status=StatusType.SUCCESS, + additional_info=None, + ) + inc = intensifier._compare_configs( + incumbent=config4, + challenger=config3, + runhistory=runhistory, + log_trajectory=False, + ) + assert config3 == inc + + +def test_launched_all_configs_for_current_stage(make_sh_worker, make_target_function, runhistory, configs): + """This check makes sure we can identify when all the current runs (config/instance/seed) pairs for a given stage + have been launched.""" + + config2 = configs[1] + config3 = configs[2] + config4 = configs[3] + + # instances = [None]??? + intensifier = make_sh_worker( + deterministic=True, n_instances=10, min_budget=2, max_budget=10, eta=2, incumbent_selection="any_budget" + ) + intensifier._stage = 0 + + # So there are 2 instances per config. + # stage=0 + # n_configs_in_stage=[4.0, 2.0, 1.0] + # all_budgets=[ 2.5 5. 10. ] + total_configs_in_stage = 4 + instances_per_stage = 2 + + # get all configs and add them to the dict + run_tracker = {} + challengers = configs[:4] + for i in range(total_configs_in_stage * instances_per_stage): + intent, trial_info = intensifier.get_next_trial( + challengers=challengers, + get_next_configurations=None, + runhistory=runhistory, + incumbent=None, + ) + + # All this runs are valid for this stage + assert intent == TrialInfoIntent.RUN + + # Remove from the challengers, the launched configs + challengers = [c for c in challengers if c != trial_info.config] + run_tracker[(trial_info.config, trial_info.instance, trial_info.seed)] = False + runhistory.add( + config=trial_info.config, + instance=trial_info.instance, + seed=trial_info.seed, + budget=trial_info.budget, + cost=10, + time=1, + status=StatusType.RUNNING, + additional_info=None, + ) + + # This will get us the second instance of config 1 + intent, trial_info = intensifier.get_next_trial( + challengers=[config2, config3, config4], + get_next_configurations=None, + runhistory=runhistory, + incumbent=None, + ) + + # We have launched all runs, that are expected for this stage + # not registered any, so for sure we have to wait + # For all runs to be completed before moving to the next stage + assert intent == TrialInfoIntent.WAIT + + +def _exhaust_stage_execution(intensifier, target_function, runhistory, challengers, incumbent): + """ + Exhaust configuration/instances seed and returns the + trial_info that were not launched. + + The idea with this procedure is to emulate the fact that some + configurations will finish while others won't. We need to be + robust against this scenario + """ + pending_processing = [] + stage = 0 if not hasattr(intensifier, "stage") else intensifier._stage + curr_budget = intensifier._all_budgets[stage] + prev_budget = int(intensifier._all_budgets[stage - 1]) if stage > 0 else 0 + if intensifier._successive_halving._instance_as_budget: + total_runs = int(curr_budget - prev_budget) * int(intensifier._n_configs_in_stage[stage]) + toggle = np.random.choice([True, False], total_runs).tolist() + while not np.any(toggle) or not np.any(np.invert(toggle)): + # make sure we have both true and false! + toggle = np.random.choice([True, False], total_runs).tolist() + else: + # If we directly use the budget, then there are no instances to wait + # But we still want to mimic pending configurations. That is, we don't + # advance to the next stage until all configurations are done for a given + # budget. + # Here if we do not launch a configuration because toggle was false, is + # like this configuration never exited as there is only 1 instance in this + # and if toggle is false, it is never run. So we cannot do a random toggle + toggle = [False, True, False, True] + + while True: + intent, trial_info = intensifier.get_next_trial( + challengers=challengers, + get_next_configurations=None, + runhistory=runhistory, + incumbent=incumbent, + ) + + # Update the challengers + challengers = [c for c in challengers if c != trial_info.config] + + if intent == TrialInfoIntent.WAIT: + break + + # Add this configuration as running + runhistory.add( + config=trial_info.config, + instance=trial_info.instance, + seed=trial_info.seed, + budget=trial_info.budget, + cost=1000, + time=1000, + status=StatusType.RUNNING, + additional_info=None, + ) + + if toggle.pop(): + trial_value = evaluate_challenger( + trial_info, target_function, intensifier._stats, runhistory, force_update=True + ) + incumbent, inc_value = intensifier.process_results( + trial_info=trial_info, + incumbent=incumbent, + runhistory=runhistory, + time_bound=np.inf, + trial_value=trial_value, + log_trajectory=False, + ) + else: + pending_processing.append(trial_info) + + # In case a iteration is done, break + # This happens if the configs per stage is 1 + if intensifier._iteration_done: + break + + return pending_processing, incumbent + + +def test_iteration_done_only_when_all_configs_processed_instance_as_budget( + make_sh_worker, make_target_function, runhistory, configs +): + """Makes sure that iteration done for a given stage is asserted ONLY after all + configurations AND instances are completed, when instance is used as budget.""" + + def target(x: Configuration): + return 1 + + config1 = configs[0] + + # instances = [None]??? + intensifier = make_sh_worker(deterministic=True, n_instances=5, min_budget=2, max_budget=5, eta=2) + intensifier._update_stage(runhistory=None) + + target_function = make_target_function(intensifier._scenario, target) + target_function.runhistory = runhistory + + # we want to test instance as budget + assert intensifier._successive_halving._instance_as_budget + + # Run until there are no more configurations to be proposed + # Skip running some configurations to emulate the fact that runs finish on different time + # We need this because there was a bug where not all instances had finished, yet + # the SH instance assumed all configurations finished + challengers = configs[:4] + incumbent = None + pending_processing, incumbent = _exhaust_stage_execution( + intensifier, target_function, runhistory, challengers, incumbent + ) + + # We have configurations pending, so iteration should NOT be done + assert not intensifier._iteration_done + + # Make sure we launched all configurations we were meant to: + # all_budgets=[2.5 5. ] n_configs_in_stage=[2.0, 1.0] + # We need 2 configurations in the run history + configurations = set([k.config_id for k, v in runhistory._data.items()]) + assert configurations == {1, 2} + # We need int(2.5) instances in the run history per config + config_inst_seed = set([k for k, v in runhistory._data.items()]) + assert len(config_inst_seed) == 4 + + # Go to the last stage. Notice that iteration should not be done + # as we are in stage 1 out of 2 + for trial_info in pending_processing: + trial_value = evaluate_challenger( + trial_info, target_function, intensifier._stats, runhistory, force_update=True + ) + incumbent, inc_value = intensifier.process_results( + trial_info=trial_info, + incumbent=config1, + runhistory=runhistory, + time_bound=np.inf, + trial_value=trial_value, + log_trajectory=False, + ) + assert not intensifier._iteration_done + + # we transition to stage 1, where the budget is 5 + assert intensifier._stage == 1 + + pending_processing, incumbent = _exhaust_stage_execution( + intensifier, + target_function, + runhistory, + challengers, + incumbent, + ) + + # Because budget is 5, BUT we previously ran 2 instances in stage 0 + # we expect that the run history will be populated with 3 new instances for 1 + # config more 4 (stage0, 2 config on 2 instances) + 3 (stage1, 1 config 3 instances) = 7 + config_inst_seed = [k for k, v in runhistory._data.items()] + assert len(config_inst_seed) == 7 + + # All new runs should be on the same config + assert len(set([c.config_id for c in config_inst_seed[4:]])) == 1 + # We need 3 new instance seed pairs + assert len(set(config_inst_seed[4:])) == 3 + + # because there are configurations pending, no iteration should be done + assert not intensifier._iteration_done + + # Finish the pending runs + for trial_info in pending_processing: + trial_value = evaluate_challenger( + trial_info, target_function, intensifier._stats, runhistory, force_update=True + ) + incumbent, _ = intensifier.process_results( + trial_info=trial_info, + incumbent=incumbent, + runhistory=runhistory, + time_bound=np.inf, + trial_value=trial_value, + log_trajectory=False, + ) + + # Finally, all stages are done, so iteration should be done!! + assert intensifier._iteration_done + + +def test_iteration_done_only_when_all_configs_processed_no_instance_as_budget( + make_sh_worker, make_target_function, runhistory, configs +): + """Makes sure that iteration done for a given stage is asserted ONLY after all + configurations AND instances are completed, when instance is NOT used as budget.""" + + def target(x: Configuration, seed, budget): + return 1 + + # instances = [None]??? + intensifier = make_sh_worker( + deterministic=True, + n_instances=5, + min_budget=2, + max_budget=5, + eta=2, + ) + intensifier._update_stage(runhistory=None) + + target_function = make_target_function( + intensifier._scenario, + target, + required_arguments=["seed", "budget"], + ) + target_function.runhistory = runhistory + + assert intensifier._successive_halving._instance_as_budget + + # Run until there are no more configurations to be proposed + # Skip running some configurations to emulate the fact that runs finish on different time + # We need this because there was a bug where not all instances had finished, yet + # the SH instance assumed all configurations finished + challengers = configs[:4] + incumbent = None + pending_processing, incumbent = _exhaust_stage_execution( + intensifier, target_function, runhistory, challengers, incumbent + ) + + # We have configurations pending, so iteration should NOT be done + assert not intensifier._iteration_done + + # Make sure we launched all configurations we were meant to: + # all_budgets=[2.5 5. ] n_configs_in_stage=[2.0, 1.0] + # We need 2 configurations in the run history + configurations = set([k.config_id for k, v in runhistory._data.items()]) + assert configurations == {1, 2} + # There is only one instance always -- so we only have 2 configs for 1 instances each + config_inst_seed = set([k for k, v in runhistory._data.items()]) + assert len(config_inst_seed) == 4 + + # Go to the last stage. Notice that iteration should not be done + # as we are in stage 1 out of 2 + for trial_info in pending_processing: + trial_value = evaluate_challenger( + trial_info, + target_function, + intensifier._stats, + runhistory, + force_update=True, + ) + incumbent, _ = intensifier.process_results( + trial_info=trial_info, + incumbent=incumbent, + runhistory=runhistory, + time_bound=np.inf, + trial_value=trial_value, + log_trajectory=False, + ) + assert not intensifier._iteration_done + + # we transition to stage 1, where the budget is 5 + assert intensifier._stage == 1 + + pending_processing, incumbent = _exhaust_stage_execution( + intensifier, target_function, runhistory, challengers, incumbent + ) + + # The next configuration per stage is just one (n_configs_in_stage=[2.0, 1.0]) + # We ran previously 2 configs and with this new, we should have 3 total + config_inst_seed = [k for k, v in runhistory._data.items()] + assert len(config_inst_seed) == 7 + + # Because it is only 1 config, the iteration is completed + assert not intensifier._iteration_done + + +def test_budget_initialization(make_sh_worker, make_target_function, runhistory, configs): + """Check computing budgets (only for non-instance cases).""" + intensifier = make_sh_worker(deterministic=True, n_instances=0, min_budget=1, max_budget=81, eta=3) + + assert [1, 3, 9, 27, 81] == intensifier._all_budgets + assert [81, 27, 9, 3, 1] == intensifier._n_configs_in_stage + + to_check = [ + # minb, maxb, eta, n_configs_in_stage, all_budgets + [1, 81, 3, [81, 27, 9, 3, 1], [1, 3, 9, 27, 81]], + [ + 1, + 600, + 3, + [243, 81, 27, 9, 3, 1], + [2.469135, 7.407407, 22.222222, 66.666666, 200, 600], + ], + [1, 100, 10, [100, 10, 1], [1, 10, 100]], + [ + 0.001, + 1, + 3, + [729, 243, 81, 27, 9, 3, 1], + [0.001371, 0.004115, 0.012345, 0.037037, 0.111111, 0.333333, 1.0], + ], + [ + 1, + 1000, + 3, + [729, 243, 81, 27, 9, 3, 1], + [ + 1.371742, + 4.115226, + 12.345679, + 37.037037, + 111.111111, + 333.333333, + 1000.0, + ], + ], + [ + 0.001, + 100, + 10, + [100000, 10000, 1000, 100, 10, 1], + [0.001, 0.01, 0.1, 1.0, 10.0, 100.0], + ], + ] + + for minb, maxb, eta, n_configs_in_stage, all_budgets in to_check: + + intensifier = make_sh_worker( + deterministic=True, + n_instances=0, + min_budget=minb, + max_budget=maxb, + eta=eta, + _all_budgets=all_budgets, + _n_configs_in_stage=n_configs_in_stage, + ) + + comp_budgets = intensifier._all_budgets + comp_configs = intensifier._n_configs_in_stage + + assert len(all_budgets) == len(comp_budgets) + assert comp_budgets[-1] == maxb + np.testing.assert_array_almost_equal(all_budgets, comp_budgets, decimal=5) + + assert comp_configs[-1] == 1 + assert len(n_configs_in_stage) == len(comp_configs) + np.testing.assert_array_almost_equal(n_configs_in_stage, comp_configs, decimal=5) diff --git a/tests/test_intensify/__init__.py b/tests/test_intensify/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/test_intensify/test_abstract_racer.py b/tests/test_intensify/test_abstract_racer.py deleted file mode 100644 index 17ca425fe..000000000 --- a/tests/test_intensify/test_abstract_racer.py +++ /dev/null @@ -1,254 +0,0 @@ -import logging -import unittest - -import numpy as np -from ConfigSpace import Configuration, ConfigurationSpace -from ConfigSpace.hyperparameters import UniformIntegerHyperparameter - -from smac.intensification.abstract_racer import AbstractRacer -from smac.runhistory.runhistory import RunHistory -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.utils.io.traj_logging import TrajLogger - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def get_config_space(): - cs = ConfigurationSpace() - cs.add_hyperparameter(UniformIntegerHyperparameter(name="a", lower=0, upper=100)) - cs.add_hyperparameter(UniformIntegerHyperparameter(name="b", lower=0, upper=100)) - return cs - - -class TestAbstractRacer(unittest.TestCase): - def setUp(self): - unittest.TestCase.setUp(self) - - self.rh = RunHistory() - self.cs = get_config_space() - self.config1 = Configuration(self.cs, values={"a": 0, "b": 100}) - self.config2 = Configuration(self.cs, values={"a": 100, "b": 0}) - self.config3 = Configuration(self.cs, values={"a": 100, "b": 100}) - - self.scen = Scenario({"cutoff_time": 2, "cs": self.cs, "run_obj": "runtime", "output_dir": ""}) - self.stats = Stats(scenario=self.scen) - self.stats.start_timing() - - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - def test_compare_configs_no_joint_set(self): - intensifier = AbstractRacer( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=None, - instances=[1], - ) - - for i in range(2): - self.rh.add( - config=self.config1, - cost=2, - time=2, - status=StatusType.SUCCESS, - instance_id=1, - seed=i, - additional_info=None, - ) - - for i in range(2, 5): - self.rh.add( - config=self.config2, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=i, - additional_info=None, - ) - - # The sets for the incumbent are completely disjoint. - conf = intensifier._compare_configs(incumbent=self.config1, challenger=self.config2, run_history=self.rh) - self.assertIsNone(conf) - - # The incumbent has still one instance-seed pair left on which the - # challenger was not run yet. - self.rh.add( - config=self.config2, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - additional_info=None, - ) - conf = intensifier._compare_configs(incumbent=self.config1, challenger=self.config2, run_history=self.rh) - self.assertIsNone(conf) - - def test_compare_configs_chall(self): - """ - challenger is better - """ - intensifier = AbstractRacer( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=None, - instances=[1], - ) - - self.rh.add( - config=self.config1, - cost=1, - time=2, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - ) - - self.rh.add( - config=self.config2, - cost=0, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - ) - - conf = intensifier._compare_configs(incumbent=self.config1, challenger=self.config2, run_history=self.rh) - - # challenger has enough runs and is better - self.assertEqual(conf, self.config2, "conf: %s" % (conf)) - - def test_compare_configs_inc(self): - """ - incumbent is better - """ - intensifier = AbstractRacer( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=None, - instances=[1], - ) - - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - ) - - self.rh.add( - config=self.config2, - cost=2, - time=2, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - ) - - conf = intensifier._compare_configs(incumbent=self.config1, challenger=self.config2, run_history=self.rh) - - # challenger worse than inc - self.assertEqual(conf, self.config1, "conf: %s" % (conf)) - - def test_compare_configs_unknow(self): - """ - challenger is better but has less runs; - -> no decision (None) - """ - intensifier = AbstractRacer( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=None, - instances=[1], - ) - - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - ) - - self.rh.add( - config=self.config1, - cost=1, - time=2, - status=StatusType.SUCCESS, - instance_id=2, - seed=None, - additional_info=None, - ) - - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=2, - seed=None, - additional_info=None, - ) - - conf = intensifier._compare_configs(incumbent=self.config1, challenger=self.config2, run_history=self.rh) - - # challenger worse than inc - self.assertIsNone(conf, "conf: %s" % (conf)) - - def test_adaptive_capping(self): - """ - test _adapt_cutoff() - """ - intensifier = AbstractRacer( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - instances=list(range(5)), - deterministic=False, - ) - - for i in range(5): - self.rh.add( - config=self.config1, - cost=i + 1, - time=i + 1, - status=StatusType.SUCCESS, - instance_id=i, - seed=i, - additional_info=None, - ) - for i in range(3): - self.rh.add( - config=self.config2, - cost=i + 1, - time=i + 1, - status=StatusType.SUCCESS, - instance_id=i, - seed=i, - additional_info=None, - ) - - inst_seed_pairs = self.rh.get_runs_for_config(self.config1, only_max_observed_budget=True) - # cost used by incumbent for going over all runs in inst_seed_pairs - inc_sum_cost = self.rh.sum_cost(config=self.config1, instance_seed_budget_keys=inst_seed_pairs) - - cutoff = intensifier._adapt_cutoff(challenger=self.config2, run_history=self.rh, inc_sum_cost=inc_sum_cost) - # 15*1.2 - 6 - self.assertEqual(cutoff, 12) - - intensifier.cutoff = 5 - - cutoff = intensifier._adapt_cutoff(challenger=self.config2, run_history=self.rh, inc_sum_cost=inc_sum_cost) - # scenario cutoff - self.assertEqual(cutoff, 5) diff --git a/tests/test_intensify/test_eval_utils.py b/tests/test_intensify/test_eval_utils.py deleted file mode 100644 index 1da551a03..000000000 --- a/tests/test_intensify/test_eval_utils.py +++ /dev/null @@ -1,40 +0,0 @@ -from smac.runhistory.runhistory import RunHistory, RunInfo -from smac.stats.stats import Stats -from smac.tae.execute_func import ExecuteTAFuncDict - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def eval_challenger( - run_info: RunInfo, - taf: ExecuteTAFuncDict, - stats: Stats, - runhistory: RunHistory, - force_update=False, -): - """ - Wrapper over challenger evaluation - - SMBO objects handles run history now, but to keep - same testing functionality this function is a small - wrapper to launch the taf and add it to the history - """ - # evaluating configuration - run_info, result = taf.run_wrapper( - run_info=run_info, - ) - - stats.ta_time_used += float(result.time) - runhistory.add( - config=run_info.config, - cost=result.cost, - time=result.time, - status=result.status, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - force_update=force_update, - ) - stats.n_configs = len(runhistory.config_ids) - return result diff --git a/tests/test_intensify/test_hyperband.py b/tests/test_intensify/test_hyperband.py deleted file mode 100644 index 37beb3c4b..000000000 --- a/tests/test_intensify/test_hyperband.py +++ /dev/null @@ -1,643 +0,0 @@ -import logging -import time -import unittest -from unittest import mock - -import numpy as np -from ConfigSpace import Configuration, ConfigurationSpace -from ConfigSpace.hyperparameters import UniformIntegerHyperparameter - -from smac.intensification.abstract_racer import RunInfoIntent -from smac.intensification.hyperband import Hyperband, _Hyperband -from smac.intensification.successive_halving import _SuccessiveHalving -from smac.runhistory.runhistory import RunHistory, RunInfo, RunValue -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.tae.execute_func import ExecuteTAFuncDict -from smac.utils.io.traj_logging import TrajLogger - -from .test_eval_utils import eval_challenger - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def get_config_space(): - cs = ConfigurationSpace() - cs.add_hyperparameter(UniformIntegerHyperparameter(name="a", lower=0, upper=100)) - cs.add_hyperparameter(UniformIntegerHyperparameter(name="b", lower=0, upper=100)) - return cs - - -def target_from_run_info(RunInfo): - value_from_config = sum([a for a in RunInfo.config.get_dictionary().values()]) - return RunValue( - cost=value_from_config, - time=0.5, - status=StatusType.SUCCESS, - starttime=time.time(), - endtime=time.time() + 1, - additional_info={}, - ) - - -class TestHyperband(unittest.TestCase): - def setUp(self): - unittest.TestCase.setUp(self) - - self.rh = RunHistory() - self.cs = get_config_space() - self.config1 = Configuration(self.cs, values={"a": 7, "b": 11}) - self.config2 = Configuration(self.cs, values={"a": 13, "b": 17}) - self.config3 = Configuration(self.cs, values={"a": 0, "b": 7}) - self.config4 = Configuration(self.cs, values={"a": 29, "b": 31}) - self.config5 = Configuration(self.cs, values={"a": 31, "b": 33}) - - self.scen = Scenario({"cutoff_time": 2, "cs": self.cs, "run_obj": "runtime", "output_dir": ""}) - self.stats = Stats(scenario=self.scen) - self.stats.start_timing() - - # Create the base object - self.HB = Hyperband( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=False, - run_obj_time=False, - instances=[1, 2, 3, 4, 5], - n_seeds=2, - initial_budget=2, - max_budget=5, - eta=2, - ) - - def test_initialization(self): - """Makes sure that a proper_HB is created""" - - # We initialize the HB with zero intensifier_instances - self.assertEqual(len(self.HB.intensifier_instances), 0) - - # Add an instance to check the_HB initialization - self.assertTrue(self.HB._add_new_instance(num_workers=1)) - - # Some default init - self.assertEqual(self.HB.intensifier_instances[0].hb_iters, 0) - self.assertEqual(self.HB.intensifier_instances[0].max_budget, 5) - self.assertEqual(self.HB.intensifier_instances[0].initial_budget, 2) - - # Update the stage - (self.HB.intensifier_instances[0]._update_stage(self.rh)) - - # Parameters properly passed to SH - self.assertEqual(len(self.HB.intensifier_instances[0].sh_intensifier.inst_seed_pairs), 10) - self.assertEqual(self.HB.intensifier_instances[0].sh_intensifier.initial_budget, 2) - self.assertEqual(self.HB.intensifier_instances[0].sh_intensifier.max_budget, 5) - - def test_process_results_via_sourceid(self): - """Makes sure source id is honored when deciding - which_HB instance will consume the result/run_info - - """ - # Mock the_HB instance so we can make sure the correct item is passed - for i in range(10): - self.HB.intensifier_instances[i] = mock.Mock() - self.HB.intensifier_instances[i].process_results.return_value = (self.config1, 0.5) - # make iter false so the mock object is not overwritten - self.HB.intensifier_instances[i].iteration_done = False - - # randomly create run_infos and push into HB. Then we will make - # sure they got properly allocated - for i in np.random.choice(list(range(10)), 30): - run_info = RunInfo( - config=self.config1, - instance=0, - instance_specific="0", - cutoff=None, - seed=0, - capped=False, - budget=0.0, - source_id=i, - ) - - # make sure results aren't messed up via magic variable - # That is we check only the proper_HB instance has this - magic = time.time() - - result = RunValue( - cost=1, - time=0.5, - status=StatusType.SUCCESS, - starttime=1, - endtime=2, - additional_info=magic, - ) - self.HB.process_results( - run_info=run_info, - incumbent=None, - run_history=self.rh, - time_bound=None, - result=result, - log_traj=False, - ) - - # Check the call arguments of each sh instance and make sure - # it is the correct one - - # First the expected one - self.assertEqual(self.HB.intensifier_instances[i].process_results.call_args[1]["run_info"], run_info) - self.assertEqual(self.HB.intensifier_instances[i].process_results.call_args[1]["result"], result) - all_other_run_infos, all_other_results = [], [] - for j, item in enumerate(self.HB.intensifier_instances): - # Skip the expected_HB instance - if i == j: - continue - if self.HB.intensifier_instances[j].process_results.call_args is None: - all_other_run_infos.append(None) - else: - all_other_run_infos.append( - self.HB.intensifier_instances[j].process_results.call_args[1]["run_info"] - ) - all_other_results.append(self.HB.intensifier_instances[j].process_results.call_args[1]["result"]) - self.assertNotIn(run_info, all_other_run_infos) - self.assertNotIn(result, all_other_results) - - def test_get_next_run_single_HB_instance(self): - """Makes sure that a single_HB instance returns a valid config""" - - challengers = [self.config1, self.config2, self.config3, self.config4] - for i in range(30): - intent, run_info = self.HB.get_next_run( - challengers=challengers, - incumbent=None, - chooser=None, - run_history=self.rh, - num_workers=1, - ) - - # Regenerate challenger list - challengers = [c for c in challengers if c != run_info.config] - - if intent == RunInfoIntent.WAIT: - break - - # Add the config to self.rh in order to make HB aware that this - # config/instance was launched - self.rh.add( - config=run_info.config, - cost=10, - time=0.0, - status=StatusType.RUNNING, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - ) - - # smax==1 (int(np.floor(np.log(self.max_budget / self.initial_budget) / np.log(self. eta)))) - self.assertEqual(self.HB.intensifier_instances[0].s_max, 1) - - # And we do not even complete 1 iteration, so s has to be 1 - self.assertEqual(self.HB.intensifier_instances[0].s, 1) - - # We should not create more_HB instance intensifier_instances - self.assertEqual(len(self.HB.intensifier_instances), 1) - - # We are running with: - # 'all_budgets': array([2.5, 5. ]) -> 2 intensifier_instances per config top - # 'n_configs_in_stage': [2.0, 1.0], - # This means we run int(2.5) + 2.0 = 4 runs before waiting - self.assertEqual(i, 4) - - # Also, check the internals of the unique sh instance - - # sh_initial_budget==2.5 (self.eta ** -self.s * self.max_budget) - self.assertEqual(self.HB.intensifier_instances[0].sh_intensifier.initial_budget, 2) - - # n_challengers=2 (int(np.floor((self.s_max + 1) / (self.s + 1)) * self.eta ** self.s)) - self.assertEqual(len(self.HB.intensifier_instances[0].sh_intensifier.n_configs_in_stage), 2) - - def test_get_next_run_multiple_HB_instances(self): - """Makes sure that two _HB instance can properly coexist and tag - run_info properly""" - - # We allow 2_HB instance to be created. This means, we have a newer iteration - # to expect in hyperband - challengers = [self.config1, self.config2, self.config3, self.config4] - run_infos = [] - for i in range(30): - intent, run_info = self.HB.get_next_run( - challengers=challengers, - incumbent=None, - chooser=None, - run_history=self.rh, - num_workers=2, - ) - run_infos.append(run_info) - - # Regenerate challenger list - challengers = [c for c in challengers if c != run_info.config] - - # Add the config to self.rh in order to make HB aware that this - # config/instance was launched - if intent == RunInfoIntent.WAIT: - break - self.rh.add( - config=run_info.config, - cost=10, - time=0.0, - status=StatusType.RUNNING, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - ) - - # We have not completed an iteration - self.assertEqual(self.HB.intensifier_instances[0].hb_iters, 0) - - # Because n workers is now 2, we expect 2 sh intensifier_instances - self.assertEqual(len(self.HB.intensifier_instances), 2) - - # Each of the intensifier_instances should have s equal to 1 - # As no iteration has been completed - self.assertEqual(self.HB.intensifier_instances[0].s_max, 1) - self.assertEqual(self.HB.intensifier_instances[0].s, 1) - self.assertEqual(self.HB.intensifier_instances[1].s_max, 1) - self.assertEqual(self.HB.intensifier_instances[1].s, 1) - - # First let us check everything makes sense in_HB-SH-0 HB-SH-0 - self.assertEqual(self.HB.intensifier_instances[0].sh_intensifier.initial_budget, 2) - self.assertEqual(self.HB.intensifier_instances[0].sh_intensifier.max_budget, 5) - self.assertEqual(len(self.HB.intensifier_instances[0].sh_intensifier.n_configs_in_stage), 2) - self.assertEqual(self.HB.intensifier_instances[1].sh_intensifier.initial_budget, 2) - self.assertEqual(self.HB.intensifier_instances[1].sh_intensifier.max_budget, 5) - self.assertEqual(len(self.HB.intensifier_instances[1].sh_intensifier.n_configs_in_stage), 2) - - # We are running with: - # + 4 runs for sh instance 0 ('all_budgets': array([2.5, 5. ]), 'n_configs_in_stage': [2.0, 1.0]) - # that is, for SH0 we run in stage==0 int(2.5) intensifier_instances * 2.0 configs - # And this times 2 because we have 2_HB intensifier_instances - self.assertEqual(i, 8) - - # Adding a new worker is not possible as we already have 2 intensifier_instances - # and n_workers==2 - intent, run_info = self.HB.get_next_run( - challengers=challengers, - incumbent=None, - chooser=None, - run_history=self.rh, - num_workers=2, - ) - self.assertEqual(intent, RunInfoIntent.WAIT) - - def test_add_new_instance(self): - """Test whether we can add a instance and when we should not""" - - # By default we do not create a_HB - # test adding the first instance! - self.assertEqual(len(self.HB.intensifier_instances), 0) - self.assertTrue(self.HB._add_new_instance(num_workers=1)) - self.assertEqual(len(self.HB.intensifier_instances), 1) - self.assertIsInstance(self.HB.intensifier_instances[0], _Hyperband) - # A second call should not add a new_HB instance - self.assertFalse(self.HB._add_new_instance(num_workers=1)) - - # We try with 2_HB instance active - - # We effectively return true because we added a new_HB instance - self.assertTrue(self.HB._add_new_instance(num_workers=2)) - - self.assertEqual(len(self.HB.intensifier_instances), 2) - self.assertIsInstance(self.HB.intensifier_instances[1], _Hyperband) - - # Trying to add a third one should return false - self.assertFalse(self.HB._add_new_instance(num_workers=2)) - self.assertEqual(len(self.HB.intensifier_instances), 2) - - def _exhaust_run_and_get_incumbent(self, sh, rh, num_workers=2): - """ - Runs all provided configs on all intensifier_instances and return the incumbent - as a nice side effect runhistory/stats are properly filled - """ - challengers = [self.config1, self.config2, self.config3, self.config4] - incumbent = None - for i in range(100): - try: - intent, run_info = sh.get_next_run( - challengers=challengers, - incumbent=None, - chooser=None, - run_history=rh, - num_workers=num_workers, - ) - except ValueError as e: - # Get configurations until you run out of them - print(e) - break - - # Regenerate challenger list - challengers = [c for c in challengers if c != run_info.config] - - if intent == RunInfoIntent.WAIT: - break - - result = target_from_run_info(run_info) - rh.add( - config=run_info.config, - cost=result.cost, - time=result.time, - status=result.status, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - ) - incumbent, inc_perf = sh.process_results( - run_info=run_info, - incumbent=incumbent, - run_history=rh, - time_bound=100.0, - result=result, - log_traj=False, - ) - return incumbent, inc_perf - - def test_parallel_same_as_serial_HB(self): - """Makes sure we behave the same as a serial run at the end""" - - # Get the run_history for a_HB instance run: - rh = RunHistory() - stats = Stats(scenario=self.scen) - stats.start_timing() - _HB = _Hyperband( - stats=stats, - traj_logger=TrajLogger(output_dir=None, stats=stats), - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - instances=[1, 2, 3, 4, 5], - initial_budget=2, - max_budget=5, - eta=2, - ) - incumbent, inc_perf = self._exhaust_run_and_get_incumbent(_HB, rh, num_workers=1) - - # Just to make sure nothing has changed from the_HB instance side to make - # this check invalid: - # We add config values, so config 3 with 0 and 7 should be the lesser cost - self.assertEqual(incumbent, self.config3) - self.assertEqual(inc_perf, 7.0) - - # Do the same for HB, but have multiple_HB instance in there - # This_HB instance will be created via num_workers==2 - # in self._exhaust_run_and_get_incumbent - HB = Hyperband( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - instances=[1, 2, 3, 4, 5], - initial_budget=2, - max_budget=5, - eta=2, - ) - incumbent_phb, inc_perf_phb = self._exhaust_run_and_get_incumbent(HB, self.rh) - self.assertEqual(incumbent, incumbent_phb) - - # This makes sure there is a single incumbent in HB - self.assertEqual(inc_perf, inc_perf_phb) - - # We don't want to loose any configuration, and particularly - # we want to make sure the values of_HB instance to HB match - self.assertEqual(len(self.rh.data), len(rh.data)) - - # Because it is a deterministic run, the run histories must be the - # same on exhaustion - self.assertDictEqual(self.rh.data, rh.data) - - -class Test_Hyperband(unittest.TestCase): - def setUp(self): - unittest.TestCase.setUp(self) - - self.rh = RunHistory() - self.cs = get_config_space() - self.config1 = Configuration(self.cs, values={"a": 0, "b": 100}) - self.config2 = Configuration(self.cs, values={"a": 100, "b": 0}) - self.config3 = Configuration(self.cs, values={"a": 100, "b": 100}) - - self.scen = Scenario({"cutoff_time": 2, "cs": self.cs, "run_obj": "runtime", "output_dir": ""}) - self.stats = Stats(scenario=self.scen) - self.stats.start_timing() - - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - def test_update_stage(self): - """ - test initialization of all parameters and tracking variables - """ - intensifier = _Hyperband( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - instances=[1], - initial_budget=0.1, - max_budget=1, - eta=2, - ) - intensifier._update_stage() - - self.assertEqual(intensifier.s, 3) - self.assertEqual(intensifier.s_max, 3) - self.assertEqual(intensifier.hb_iters, 0) - self.assertIsInstance(intensifier.sh_intensifier, _SuccessiveHalving) - self.assertEqual(intensifier.sh_intensifier.initial_budget, 0.125) - self.assertEqual(intensifier.sh_intensifier.n_configs_in_stage, [8.0, 4.0, 2.0, 1.0]) - - # next HB stage - intensifier._update_stage() - - self.assertEqual(intensifier.s, 2) - self.assertEqual(intensifier.hb_iters, 0) - self.assertEqual(intensifier.sh_intensifier.initial_budget, 0.25) - self.assertEqual(intensifier.sh_intensifier.n_configs_in_stage, [4.0, 2.0, 1.0]) - - intensifier._update_stage() # s = 1 - intensifier._update_stage() # s = 0 - # HB iteration completed - intensifier._update_stage() - - self.assertEqual(intensifier.s, intensifier.s_max) - self.assertEqual(intensifier.hb_iters, 1) - self.assertEqual(intensifier.sh_intensifier.initial_budget, 0.125) - self.assertEqual(intensifier.sh_intensifier.n_configs_in_stage, [8.0, 4.0, 2.0, 1.0]) - - def test_eval_challenger(self): - """ - since hyperband uses eval_challenger and get_next_run of the internal successive halving, - we don't test these method extensively - """ - - def target(x): - return 0.1 - - taf = ExecuteTAFuncDict(ta=target, stats=self.stats) - taf.runhistory = self.rh - - intensifier = _Hyperband( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - instances=[None], - initial_budget=0.5, - max_budget=1, - eta=2, - ) - - self.assertFalse(hasattr(intensifier, "s")) - - # Testing get_next_run - get next configuration - intent, run_info = intensifier.get_next_run( - challengers=[self.config2, self.config3], - chooser=None, - incumbent=None, - run_history=self.rh, - ) - self.assertEqual(intensifier.s, intensifier.s_max) - self.assertEqual(run_info.config, self.config2) - - # update to the last SH iteration of the given HB stage - self.assertEqual(intensifier.s, 1) - self.assertEqual(intensifier.s_max, 1) - - # We assume now that process results was called with below successes. - # We track closely run execution through run_tracker, so this also - # has to be update -- the fact that the succesive halving inside hyperband - # processed the given configurations - self.rh.add(config=self.config1, cost=1, time=1, status=StatusType.SUCCESS, seed=0, budget=1) - intensifier.sh_intensifier.run_tracker[(self.config1, None, 0, 1)] = True - self.rh.add(config=self.config2, cost=2, time=2, status=StatusType.SUCCESS, seed=0, budget=0.5) - intensifier.sh_intensifier.run_tracker[(self.config2, None, 0, 0.5)] = True - self.rh.add(config=self.config3, cost=3, time=2, status=StatusType.SUCCESS, seed=0, budget=0.5) - intensifier.sh_intensifier.run_tracker[(self.config3, None, 0, 0.5)] = True - - intensifier.sh_intensifier.success_challengers = {self.config2, self.config3} - intensifier.sh_intensifier._update_stage(self.rh) - intent, run_info = intensifier.get_next_run( - challengers=[self.config2, self.config3], - chooser=None, - incumbent=None, - run_history=self.rh, - ) - - # evaluation should change the incumbent to config2 - self.assertIsNotNone(run_info.config) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(inc, self.config2) - self.assertEqual(intensifier.s, 0) - self.assertEqual(inc_value, 0.1) - self.assertEqual(list(self.rh.data.keys())[-1][0], self.rh.config_ids[self.config2]) - self.assertEqual(self.stats.inc_changed, 1) - - -class Test__Hyperband(unittest.TestCase): - def test_budget_initialization(self): - """ - Check computing budgets (only for non-instance cases) - """ - intensifier = _Hyperband( - stats=None, - traj_logger=None, - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - instances=None, - initial_budget=1, - max_budget=81, - eta=3, - ) - self.assertListEqual([1, 3, 9, 27, 81], intensifier.all_budgets.tolist()) - self.assertListEqual([81, 27, 9, 3, 1], intensifier.n_configs_in_stage) - - to_check = [ - # minb, maxb, eta, n_configs_in_stage, all_budgets - [1, 81, 3, [81, 27, 9, 3, 1], [1, 3, 9, 27, 81]], - [ - 1, - 600, - 3, - [243, 81, 27, 9, 3, 1], - [2.469135, 7.407407, 22.222222, 66.666666, 200, 600], - ], - [1, 100, 10, [100, 10, 1], [1, 10, 100]], - [ - 0.001, - 1, - 3, - [729, 243, 81, 27, 9, 3, 1], - [0.001371, 0.004115, 0.012345, 0.037037, 0.111111, 0.333333, 1.0], - ], - [ - 1, - 1000, - 3, - [729, 243, 81, 27, 9, 3, 1], - [1.371742, 4.115226, 12.345679, 37.037037, 111.111111, 333.333333, 1000.0], - ], - [ - 0.001, - 100, - 10, - [100000, 10000, 1000, 100, 10, 1], - [0.001, 0.01, 0.1, 1.0, 10.0, 100.0], - ], - ] - - for minb, maxb, eta, n_configs_in_stage, all_budgets in to_check: - intensifier = _Hyperband( - stats=None, - traj_logger=None, - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - instances=None, - initial_budget=minb, - max_budget=maxb, - eta=eta, - ) - intensifier._init_sh_params( - initial_budget=minb, - max_budget=maxb, - eta=eta, - _all_budgets=None, - _n_configs_in_stage=None, - ) - for i in range(len(all_budgets) + 10): - intensifier._update_stage() - comp_budgets = intensifier.sh_intensifier.all_budgets.tolist() - comp_configs = intensifier.sh_intensifier.n_configs_in_stage - - self.assertIsInstance(comp_configs, list) - for c in comp_configs: - self.assertIsInstance(c, int) - - # all_budgets for SH is always a subset of all_budgets of HB - np.testing.assert_array_almost_equal(all_budgets[i % len(all_budgets) :], comp_budgets, decimal=5) - - # The content of these lists might differ - self.assertEqual(len(n_configs_in_stage[i % len(n_configs_in_stage) :]), len(comp_configs)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_intensify/test_intensify.py b/tests/test_intensify/test_intensify.py deleted file mode 100644 index cd7a1c872..000000000 --- a/tests/test_intensify/test_intensify.py +++ /dev/null @@ -1,1244 +0,0 @@ -import collections -import logging -import time -import unittest - -import numpy as np -from ConfigSpace import Configuration, ConfigurationSpace -from ConfigSpace.hyperparameters import UniformIntegerHyperparameter - -from smac.facade.smac_ac_facade import SMAC4AC -from smac.intensification.abstract_racer import RunInfoIntent -from smac.intensification.intensification import Intensifier, IntensifierStage -from smac.runhistory.runhistory import RunHistory, RunInfo -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.tae.execute_func import ExecuteTAFuncDict -from smac.utils.io.traj_logging import TrajLogger - - -def eval_challenger( - run_info: RunInfo, - taf: ExecuteTAFuncDict, - stats: Stats, - runhistory: RunHistory, - force_update=False, -): - """ - Wrapper over challenger evaluation - - SMBO objects handles run history now, but to keep - same testing functionality this function is a small - wrapper to launch the taf and add it to the history - """ - # evaluating configuration - run_info, result = taf.run_wrapper( - run_info=run_info, - ) - - stats.ta_time_used += float(result.time) - runhistory.add( - config=run_info.config, - cost=result.cost, - time=result.time, - status=result.status, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - force_update=force_update, - ) - stats.n_configs = len(runhistory.config_ids) - return result - - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def get_config_space(): - cs = ConfigurationSpace() - cs.add_hyperparameter(UniformIntegerHyperparameter(name="a", lower=0, upper=100)) - cs.add_hyperparameter(UniformIntegerHyperparameter(name="b", lower=0, upper=100)) - return cs - - -class TestIntensify(unittest.TestCase): - def setUp(self): - unittest.TestCase.setUp(self) - - self.rh = RunHistory() - self.cs = get_config_space() - self.config1 = Configuration(self.cs, values={"a": 0, "b": 100}) - self.config2 = Configuration(self.cs, values={"a": 100, "b": 0}) - self.config3 = Configuration(self.cs, values={"a": 100, "b": 100}) - - self.scen = Scenario( - { - "cutoff_time": 2, - "cs": self.cs, - "run_obj": "runtime", - "output_dir": "", - "deterministic": False, - "limit_resources": True, - } - ) - self.stats = Stats(scenario=self.scen) - self.stats.start_timing() - - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - def test_race_challenger_1(self): - """ - Makes sure that a racing configuration with better performance, - is selected as incumbent - No adaptive capping - """ - - def target(x): - return (x["a"] + 1) / 1000.0 - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats) - taf.runhistory = self.rh - - intensifier = Intensifier( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - instances=[1], - run_obj_time=False, - ) - - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - ) - - intensifier.N = 1 - inc, instance, seed, cutoff = intensifier._get_next_racer( - challenger=self.config2, incumbent=self.config1, run_history=self.rh - ) - - run_info = RunInfo( - config=self.config2, - instance=instance, - instance_specific="0", - cutoff=cutoff, - seed=seed, - capped=False, - budget=0.0, - ) - - result = eval_challenger(run_info, taf, self.stats, self.rh) - - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(inc, self.config2) - self.assertEqual(intensifier.num_run, 1) - self.assertEqual(intensifier.num_chall_run, 1) - - def test_race_challenger_2(self): - """ - Makes sure that a racing configuration with better performance, - that is capped, doesn't substitute the incumbent. - """ - - def target(x): - time.sleep(1.5) - return (x["a"] + 1) / 1000.0 - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="runtime") - taf.runhistory = self.rh - - intensifier = Intensifier( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - instances=[1], - ) - - self.rh.add( - config=self.config1, - cost=0.001, - time=0.001, - status=StatusType.SUCCESS, - instance_id=1, - seed=12345, - additional_info=None, - ) - intensifier.N = 1 - # config2 should have a timeout (due to adaptive capping) - # and config1 should still be the incumbent - inc, instance, seed, cutoff = intensifier._get_next_racer( - challenger=self.config2, incumbent=self.config1, run_history=self.rh - ) - run_info = RunInfo( - config=self.config2, - instance=instance, - instance_specific="0", - seed=seed, - cutoff=cutoff, - capped=True, - budget=0.0, - ) - - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(inc, self.config1) - self.assertEqual(intensifier.num_run, 1) - self.assertEqual(intensifier.num_chall_run, 1) - - def test_race_challenger_3(self): - """ - test _race_challenger with adaptive capping on a previously capped configuration - """ - - def target(config: Configuration, seed: int, instance: str): - if instance == 1: - time.sleep(2.1) - else: - time.sleep(0.6) - return (config["a"] + 1) / 1000.0 - - taf = ExecuteTAFuncDict( - use_pynisher=False, - ta=target, - stats=self.stats, - run_obj="runtime", - par_factor=1, - ) - taf.runhistory = self.rh - - intensifier = Intensifier( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - cutoff=2, - instances=[1], - ) - - self.rh.add( - config=self.config1, - cost=0.5, - time=0.5, - status=StatusType.SUCCESS, - instance_id=1, - seed=12345, - additional_info=None, - ) - - # config2 should have a timeout (due to adaptive capping) - # and config1 should still be the incumbent - config, _ = intensifier.get_next_challenger(challengers=[self.config2, self.config3], chooser=None) - inc, instance, seed, cutoff = intensifier._get_next_racer( - challenger=config, incumbent=self.config1, run_history=self.rh - ) - run_info = RunInfo( - config=config, - instance=instance, - instance_specific="0", - seed=seed, - cutoff=cutoff, - capped=True, - budget=0.0, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(inc, self.config1) - - # further run for incumbent - self.rh.add( - config=self.config1, - cost=2, - time=2, - status=StatusType.TIMEOUT, - instance_id=2, - seed=12345, - additional_info=None, - ) - - # give config2 a second chance - now it should run on both instances - - # run on instance 1 - config, _ = intensifier.get_next_challenger(challengers=[self.config2, self.config3], chooser=None) - inc, instance, seed, cutoff = intensifier._get_next_racer( - challenger=config, incumbent=self.config1, run_history=self.rh - ) - run_info = RunInfo( - config=config, - instance=instance, - instance_specific="0", - seed=seed, - cutoff=cutoff, - capped=False, - budget=0.0, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - # run on instance 2 - config, _ = intensifier.get_next_challenger(challengers=[self.config3], chooser=None) - self.assertEqual(config, self.config2) - self.assertTrue(intensifier.continue_challenger) - - inc, instance, seed, cutoff = intensifier._get_next_racer( - challenger=config, incumbent=self.config1, run_history=self.rh - ) - run_info = RunInfo( - config=config, - instance=instance, - instance_specific="0", - seed=seed, - cutoff=cutoff, - capped=False, - budget=0.0, - ) - - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - # the incumbent should still be config1 because - # config2 should get on inst 1 a full timeout - # such that c(config1) = 1.25 and c(config2) close to 1.3 - self.assertEqual(inc, self.config1) - # the capped run should not be counted in runs_perf_config - self.assertAlmostEqual(self.rh.num_runs_per_config[2], 2) - self.assertFalse(intensifier.continue_challenger) - - self.assertEqual(intensifier.num_run, 3) - self.assertEqual(intensifier.num_chall_run, 3) - - def test_race_challenger_large(self): - """ - test _race_challenger using solution_quality - """ - - def target(x): - return 1 - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats) - taf.runhistory = self.rh - - intensifier = Intensifier( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - instances=list(range(10)), - run_obj_time=False, - deterministic=True, - ) - - for i in range(10): - self.rh.add( - config=self.config1, - cost=i + 1, - time=1, - status=StatusType.SUCCESS, - instance_id=i, - seed=12345, - additional_info=None, - ) - - intensifier.stage = IntensifierStage.RUN_CHALLENGER - - # tie on first instances and then challenger should always win - # and be returned as inc - while True: - if intensifier.continue_challenger: - config = intensifier.current_challenger - else: - config, _ = intensifier.get_next_challenger(challengers=[self.config2, self.config3], chooser=None) - inc, instance, seed, cutoff = intensifier._get_next_racer( - challenger=config, incumbent=self.config1, run_history=self.rh - ) - run_info = RunInfo( - config=config, - instance=instance, - instance_specific="0", - seed=seed, - cutoff=cutoff, - capped=False, - budget=0.0, - ) - - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - # stop when challenger evaluation is over - if not intensifier.stage == IntensifierStage.RUN_CHALLENGER: - break - - self.assertEqual(inc, self.config2) - self.assertEqual(self.rh.get_cost(self.config2), 1) - - # get data for config2 to check that the correct run was performed - runs = self.rh.get_runs_for_config(self.config2, only_max_observed_budget=True) - self.assertEqual(len(runs), 10) - - self.assertEqual(intensifier.num_run, 10) - self.assertEqual(intensifier.num_chall_run, 10) - - def test_race_challenger_large_blocked_seed(self): - """ - test _race_challenger whether seeds are blocked for challenger runs - """ - - def target(x): - return 1 - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats) - taf.runhistory = self.rh - - intensifier = Intensifier( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - instances=list(range(10)), - run_obj_time=False, - deterministic=False, - ) - - for i in range(10): - self.rh.add( - config=self.config1, - cost=i + 1, - time=1, - status=StatusType.SUCCESS, - instance_id=i, - seed=i, - additional_info=None, - ) - - intensifier.stage = IntensifierStage.RUN_CHALLENGER - - # tie on first instances and then challenger should always win - # and be returned as inc - while True: - if intensifier.continue_challenger: - config = intensifier.current_challenger - else: - config, _ = intensifier.get_next_challenger(challengers=[self.config2, self.config3], chooser=None) - inc, instance, seed, cutoff = intensifier._get_next_racer( - challenger=config, incumbent=self.config1, run_history=self.rh - ) - run_info = RunInfo( - config=config, - instance=instance, - instance_specific="0", - seed=seed, - cutoff=cutoff, - capped=False, - budget=0.0, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - # stop when challenger evaluation is over - if not intensifier.stage == IntensifierStage.RUN_CHALLENGER: - break - - self.assertEqual(inc, self.config2) - self.assertEqual(self.rh.get_cost(self.config2), 1) - - # get data for config2 to check that the correct run was performed - runs = self.rh.get_runs_for_config(self.config2, only_max_observed_budget=True) - self.assertEqual(len(runs), 10) - - seeds = sorted([r.seed for r in runs]) - self.assertEqual(seeds, list(range(10)), seeds) - - self.assertEqual(intensifier.num_run, 10) - self.assertEqual(intensifier.num_chall_run, 10) - - def test_add_inc_run_det(self): - """ - test _add_inc_run() - """ - - def target(x): - return (x["a"] + 1) / 1000.0 - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="solution_quality") - taf.runhistory = self.rh - - intensifier = Intensifier( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - instances=[1], - deterministic=True, - ) - - instance, seed, cutoff = intensifier._get_next_inc_run( - available_insts=intensifier._get_inc_available_inst(incumbent=self.config1, run_history=self.rh) - ) - run_info = RunInfo( - config=self.config1, - instance=instance, - instance_specific="0", - seed=seed, - cutoff=cutoff, - capped=False, - budget=0.0, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - intensifier.stage = IntensifierStage.PROCESS_FIRST_CONFIG_RUN - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(len(self.rh.data), 1, self.rh.data) - - # since we assume deterministic=1, - # the second call should not add any more runs - # given only one instance - # So the returned seed/instance is None so that a new - # run to be triggered is not launched - available_insts = intensifier._get_inc_available_inst(incumbent=self.config1, run_history=self.rh) - # Make sure that the list is empty, and hence no new call - # of incumbent will be triggered - self.assertFalse(available_insts) - - # The following two tests evaluate to zero because _next_iteration is triggered by _add_inc_run - # as it is the first evaluation of this intensifier - # After the above incumbent run, the stage is - # IntensifierStage.RUN_CHALLENGER. Change it to test next iteration - intensifier.stage = IntensifierStage.PROCESS_FIRST_CONFIG_RUN - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=None, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(intensifier.num_run, 0) - self.assertEqual(intensifier.num_chall_run, 0) - - def test_add_inc_run_nondet(self): - """ - test _add_inc_run() - """ - - def target(x): - return (x["a"] + 1) / 1000.0 - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="solution_quality") - - intensifier = Intensifier( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - instances=[1, 2], - deterministic=False, - ) - - instance, seed, cutoff = intensifier._get_next_inc_run( - available_insts=intensifier._get_inc_available_inst(incumbent=self.config1, run_history=self.rh) - ) - run_info = RunInfo( - config=self.config1, - instance=instance, - instance_specific="0", - seed=seed, - cutoff=cutoff, - capped=False, - budget=0.0, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(len(self.rh.data), 1, self.rh.data) - - instance, seed, cutoff = intensifier._get_next_inc_run( - available_insts=intensifier._get_inc_available_inst(incumbent=self.config1, run_history=self.rh) - ) - run_info = RunInfo( - config=self.config1, - instance=instance, - instance_specific="0", - seed=seed, - cutoff=cutoff, - capped=False, - budget=0.0, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(len(self.rh.data), 2, self.rh.data) - runs = self.rh.get_runs_for_config(config=self.config1, only_max_observed_budget=True) - # exactly one run on each instance - self.assertIn(1, [runs[0].instance, runs[1].instance]) - self.assertIn(2, [runs[0].instance, runs[1].instance]) - - instance, seed, cutoff = intensifier._get_next_inc_run( - available_insts=intensifier._get_inc_available_inst(incumbent=self.config1, run_history=self.rh) - ) - run_info = RunInfo( - config=self.config1, - instance=instance, - instance_specific="0", - seed=seed, - cutoff=cutoff, - capped=False, - budget=0.0, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(len(self.rh.data), 3, self.rh.data) - - # The number of runs performed should be 3 - # No Next iteration call as an incumbent is provided - self.assertEqual(intensifier.num_run, 2) - self.assertEqual(intensifier.num_chall_run, 0) - - def testget_next_challenger(self): - """ - test get_next_challenger() - """ - intensifier = Intensifier( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - instances=[1], - deterministic=True, - ) - - intensifier.stage = IntensifierStage.RUN_CHALLENGER - - # get a new challenger to evaluate - config, new = intensifier.get_next_challenger(challengers=[self.config1, self.config2], chooser=None) - - self.assertEqual(config, self.config1, intensifier.current_challenger) - self.assertEqual(intensifier._chall_indx, 1) - self.assertEqual(intensifier.N, 1) - self.assertTrue(new) - - # when already evaluating a challenger, return the same challenger - intensifier.to_run = [(1, 1, 0)] - config, new = intensifier.get_next_challenger(challengers=[self.config2], chooser=None) - self.assertEqual(config, self.config1, intensifier.current_challenger) - self.assertEqual(intensifier._chall_indx, 1) - self.assertFalse(new) - - def test_generate_challenger(self): - """ - test generate_challenger() - """ - # test get generator from a list of challengers - intensifier = Intensifier( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - instances=[1], - deterministic=True, - ) - - gen = intensifier._generate_challengers(challengers=[self.config1, self.config2], chooser=None) - - self.assertEqual(next(gen), self.config1) - self.assertEqual(next(gen), self.config2) - self.assertRaises(StopIteration, next, gen) - - # test get generator from a chooser - would return only 1 configuration - intensifier = Intensifier( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - instances=[1], - deterministic=True, - ) - chooser = SMAC4AC(self.scen, rng=1).solver.epm_chooser - - gen = intensifier._generate_challengers(challengers=None, chooser=chooser) - - self.assertEqual(next(gen).get_dictionary(), {"a": 24, "b": 68}) - self.assertRaises(StopIteration, next, gen) - - # when both are none, raise error - with self.assertRaisesRegex(ValueError, "No configurations/chooser provided"): - intensifier._generate_challengers(challengers=None, chooser=None) - - def test_eval_challenger_1(self): - """ - test eval_challenger() - a complete intensification run with a `always_race_against` configuration - """ - - print(self.rh) - - def target(x): - if x["a"] == 100: - time.sleep(1) - return x["a"] - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="runtime") - taf.runhistory = self.rh - - intensifier = Intensifier( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - instances=[1, 2], - run_obj_time=True, - cutoff=2, - deterministic=False, - always_race_against=self.config3, - run_limit=1, - ) - - self.assertEqual(intensifier.n_iters, 0) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_FIRST_CONFIG) - - # intensification iteration #1 - # run first config as incumbent if incumbent is None - intent, run_info = intensifier.get_next_run( - incumbent=None, - run_history=self.rh, - challengers=[self.config2], - chooser=None, - ) - self.assertEqual(run_info.config, self.config2) - self.assertEqual(intensifier.stage, IntensifierStage.PROCESS_FIRST_CONFIG_RUN) - # eval config 2 (=first run) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=None, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(inc, self.config2) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_INCUMBENT) - self.assertEqual(self.stats.inc_changed, 1) - self.assertEqual(intensifier.n_iters, 1) # 1 intensification run complete! - - # intensification iteration #2 - # regular intensification begins - run incumbent first - intent, run_info = intensifier.get_next_run( - challengers=None, # don't need a new list here as old one is cont'd - incumbent=inc, - run_history=self.rh, - chooser=None, - ) - self.assertEqual(run_info.config, inc) - self.assertEqual(intensifier.stage, IntensifierStage.PROCESS_INCUMBENT_RUN) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_CHALLENGER) - self.assertEqual(self.stats.inc_changed, 1) - - # run challenger now that the incumbent has been executed - intent, run_info = intensifier.get_next_run( - challengers=[self.config1], incumbent=inc, run_history=self.rh, chooser=None - ) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_CHALLENGER) - self.assertEqual(run_info.config, self.config1) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - # challenger has a better performance, but not run on all instances yet. so incumbent stays the same - self.assertEqual(inc, self.config2) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_CHALLENGER) - self.assertTrue(intensifier.continue_challenger) - - # run challenger again on the other instance - intent, run_info = intensifier.get_next_run( - challengers=None, # don't need a new list here as old one is cont'd - incumbent=inc, - run_history=self.rh, - chooser=None, - ) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_CHALLENGER) - self.assertEqual(run_info.config, self.config1) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - # challenger better than incumbent in both instances. so incumbent changed - self.assertEqual(inc, self.config1) - self.assertEqual(self.stats.inc_changed, 2) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_BASIS) - self.assertFalse(intensifier.continue_challenger) - - # run basis configuration (`always_race_against`) - intent, run_info = intensifier.get_next_run( - challengers=None, # don't need a new list here as old one is cont'd - incumbent=inc, - run_history=self.rh, - chooser=None, - ) - self.assertEqual(run_info.config, self.config3) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_BASIS) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - # the basis configuration (config3) not better than incumbent, so can move on - self.assertEqual(inc, self.config1) - self.assertEqual(self.stats.inc_changed, 2) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_INCUMBENT, msg=self.rh.data.items()) - self.assertEqual(list(self.rh.data.values())[4][2], StatusType.CAPPED) - self.assertEqual(intensifier.n_iters, 1) # iteration continues as `min_chall` condition is not met - self.assertIsInstance(intensifier.configs_to_run, collections.abc.Iterator) - # no configs should be left at the end - with self.assertRaises(StopIteration): - next(intensifier.configs_to_run) - - # intensification continues running incumbent again in same iteration... - intent, run_info = intensifier.get_next_run( - challengers=None, # don't need a new list here as old one is cont'd - incumbent=inc, - run_history=self.rh, - chooser=None, - ) - self.assertEqual(intensifier.stage, IntensifierStage.PROCESS_INCUMBENT_RUN) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(inc, self.config1) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_CHALLENGER) - - self.assertEqual( - len(self.rh.get_runs_for_config(self.config1, only_max_observed_budget=True)), - 3, - ) - self.assertEqual( - len(self.rh.get_runs_for_config(self.config2, only_max_observed_budget=True)), - 2, - ) - self.assertEqual( - len(self.rh.get_runs_for_config(self.config3, only_max_observed_budget=True)), - 0, - ) # capped - - def test_eval_challenger_2(self): - """ - test eval_challenger() - a complete intensification run without a `always_race_against` configuration - """ - - def target(x): - return 2 * x["a"] + x["b"] - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="quality") - taf.runhistory = self.rh - - intensifier = Intensifier( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - instances=[1], - run_obj_time=False, - deterministic=True, - always_race_against=None, - run_limit=1, - ) - - self.assertEqual(intensifier.n_iters, 0) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_FIRST_CONFIG) - - # intensification iteration #1 - # run first config as incumbent if incumbent is None - intent, run_info = intensifier.get_next_run( - challengers=[self.config3], - incumbent=None, - run_history=self.rh, - chooser=None, - ) - self.assertEqual(run_info.config, self.config3) - self.assertEqual(intensifier.stage, IntensifierStage.PROCESS_FIRST_CONFIG_RUN) - # eval config 2 (=first run) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=None, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(inc, self.config3) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_INCUMBENT) - self.assertEqual(self.stats.inc_changed, 1) - self.assertEqual(intensifier.n_iters, 1) # 1 intensification run complete! - - # regular intensification begins - run incumbent - # Normally a challenger will be given, which in this case is the incumbent - # But no more instances are available. So to prevent cicles - # where No iteration happens, provide the challengers - intent, run_info = intensifier.get_next_run( - challengers=[ - self.config2, - self.config1, - ], # since incumbent is run, no configs required - incumbent=inc, - run_history=self.rh, - chooser=None, - ) - - # no new TA runs as there are no more instances to run - self.assertEqual(inc, self.config3) - self.assertEqual(self.stats.inc_changed, 1) - self.assertEqual( - len(self.rh.get_runs_for_config(self.config3, only_max_observed_budget=True)), - 1, - ) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_CHALLENGER) - - # run challenger now that the incumbent has been executed - # So this call happen above, to save one iteration - self.assertEqual(intensifier.stage, IntensifierStage.RUN_CHALLENGER) - self.assertEqual(run_info.config, self.config2) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - # challenger has a better performance, so incumbent has changed - self.assertEqual(inc, self.config2) - self.assertEqual(self.stats.inc_changed, 2) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_INCUMBENT) # since there is no `always_race_against` - self.assertFalse(intensifier.continue_challenger) - self.assertEqual(intensifier.n_iters, 1) # iteration continues as `min_chall` condition is not met - - # intensification continues running incumbent again in same iteration... - # run incumbent - # Same here, No further instance-seed pairs for incumbent available - # so above call gets the new config to run - self.assertEqual(run_info.config, self.config2) - - # There is a transition from: - # IntensifierStage.RUN_FIRST_CONFIG-> IntensifierStage.RUN_INCUMBENT - # Because after the first run, incumbent is run. - # Nevertheless, there is now a transition: - # IntensifierStage.RUN_INCUMBENT->IntensifierStage.RUN_CHALLENGER - # because in add_inc_run, there are more available instance pairs - # FROM: IntensifierStage.RUN_INCUMBENT TO: IntensifierStage.RUN_INCUMBENT WHY: no more to run - # if all have been run, compare challenger performance - # self.assertEqual(intensifier.stage, IntensifierStage.RUN_CHALLENGER) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_INCUMBENT) - - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - # run challenger - intent, run_info = intensifier.get_next_run( - challengers=None, # don't need a new list here as old one is cont'd - incumbent=inc, - run_history=self.rh, - chooser=None, - ) - self.assertEqual(run_info.config, self.config1) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_CHALLENGER) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(inc, self.config1) - self.assertEqual(self.stats.inc_changed, 3) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_INCUMBENT) - self.assertEqual(intensifier.n_iters, 2) # 2 intensification run complete! - # no configs should be left at the end - with self.assertRaises(StopIteration): - next(intensifier.configs_to_run) - - self.assertEqual( - len(self.rh.get_runs_for_config(self.config1, only_max_observed_budget=True)), - 1, - ) - self.assertEqual( - len(self.rh.get_runs_for_config(self.config2, only_max_observed_budget=True)), - 1, - ) - self.assertEqual( - len(self.rh.get_runs_for_config(self.config3, only_max_observed_budget=True)), - 1, - ) - - def test_eval_challenger_3(self): - """ - test eval_challenger for a resumed SMAC run (first run with incumbent) - """ - - def target(x): - return x["a"] - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="quality") - taf.runhistory = self.rh - - intensifier = Intensifier( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - instances=[1], - run_obj_time=False, - deterministic=False, - always_race_against=None, - run_limit=1, - ) - - self.assertEqual(intensifier.n_iters, 0) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_FIRST_CONFIG) - - # adding run for incumbent configuration - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - ) - - # intensification - incumbent will be run, but not as RUN_FIRST_CONFIG stage - intent_, run_info = intensifier.get_next_run( - challengers=[self.config2], - incumbent=self.config1, - run_history=self.rh, - chooser=None, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(intensifier.stage, IntensifierStage.RUN_CHALLENGER) - self.assertEqual( - len(self.rh.get_runs_for_config(self.config1, only_max_observed_budget=True)), - 2, - ) - - def test_no_new_intensification_wo_challenger_run(self): - """ - This test ensures that no new iteration is started if no challenger run was conducted - """ - - def target(x): - return 2 * x["a"] + x["b"] - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="quality") - taf.runhistory = self.rh - - intensifier = Intensifier( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - instances=[1], - run_obj_time=False, - deterministic=True, - always_race_against=None, - run_limit=1, - min_chall=1, - ) - - self.assertEqual(intensifier.n_iters, 0) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_FIRST_CONFIG) - - intent, run_info = intensifier.get_next_run( - challengers=[self.config3], - incumbent=None, - run_history=self.rh, - chooser=None, - ) - self.assertEqual(run_info.config, self.config3) - self.assertEqual(intensifier.stage, IntensifierStage.PROCESS_FIRST_CONFIG_RUN) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=None, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(inc, self.config3) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_INCUMBENT) - self.assertEqual(intensifier.n_iters, 1) # 1 intensification run complete! - - # regular intensification begins - run incumbent - - # No further instance-seed pairs for incumbent available - # Here None challenger is suggested. Code jumps to next iteration - # This causes a transition from RUN_INCUMBENT->RUN_CHALLENGER - # But then, the next configuration to run is the incumbent - # We don't rerun the incumbent (see message): - # Challenger was the same as the current incumbent; Skipping challenger - # Then, we try to get more challengers, but below all challengers - # Provided are config3, the incumbent which means nothing more to run - intent, run_info = intensifier.get_next_run( - challengers=[self.config3], # since incumbent is run, no configs required - incumbent=inc, - run_history=self.rh, - chooser=None, - ) - - self.assertEqual(run_info.config, None) - self.assertEqual(intensifier.stage, IntensifierStage.RUN_CHALLENGER) - - intensifier._next_iteration() - - # Add a configuration, then try to execute it afterwards - self.assertEqual(intensifier.n_iters, 2) - - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=0, - additional_info=None, - ) - intensifier.stage = IntensifierStage.RUN_CHALLENGER - - # In the upcoming get next run, the stage is RUN_CHALLENGER - # so the intensifier tries to run config1. Nevertheless, - # there are no further instances for this configuration available. - # In this scenario, the intensifier produces a SKIP intent as an indication - # that a new iteration must be initiated, and for code simplicity, - # relies on a new call to get_next_run to yield more configurations - intent, run_info = intensifier.get_next_run( - challengers=[self.config1], incumbent=inc, run_history=self.rh, chooser=None - ) - self.assertEqual(intent, RunInfoIntent.SKIP) - - # This doesn't return a config because the array of configs is exhausted - intensifier.stage = IntensifierStage.RUN_CHALLENGER - config, _ = intensifier.get_next_challenger(challengers=None, chooser=None) - self.assertIsNone(config) - # This finally gives a runable configuration - intent, run_info = intensifier.get_next_run( - challengers=[self.config2], incumbent=inc, run_history=self.rh, chooser=None - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, perf = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - # 4 Iterations due to the proactive runs - # of get next challenger - self.assertEqual(intensifier.n_iters, 3) - self.assertEqual(intensifier.num_chall_run, 1) - - -if __name__ == "__main__": - t = TestIntensify() - t.setUp() - t.test_eval_challenger_1() diff --git a/tests/test_intensify/test_parallel_scheduler.py b/tests/test_intensify/test_parallel_scheduler.py deleted file mode 100644 index e9cccaf84..000000000 --- a/tests/test_intensify/test_parallel_scheduler.py +++ /dev/null @@ -1,188 +0,0 @@ -import unittest -from unittest import mock - -import numpy as np - -from smac.intensification.abstract_racer import RunInfoIntent -from smac.intensification.parallel_scheduling import ParallelScheduler -from smac.runhistory.runhistory import RunInfo, RunValue -from smac.tae import StatusType - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def mock_ranker(sh): - return sh.stage, len(sh.run_tracker) - - -class TestParallelScheduler(unittest.TestCase): - def test_sort_instances_by_stage(self): - """Ensures that we prioritize the more advanced stage iteration""" - - scheduler = ParallelScheduler( - stats=None, - traj_logger=None, - instances=[1, 2, 3], - rng=np.random.RandomState(12345), - deterministic=True, - ) - - scheduler._get_intensifier_ranking = mock_ranker - - def add_sh_mock(stage, config_inst_pairs): - sh = mock.Mock() - sh.run_tracker = [] - for i in range(config_inst_pairs): - sh.run_tracker.append((i, i, i)) - sh.stage = stage - return sh - - # Add more SH to make testing interesting - instances = {} - instances[0] = add_sh_mock(stage=1, config_inst_pairs=6) - instances[1] = add_sh_mock(stage=1, config_inst_pairs=2) - - # We only have two configurations in the same stage. - # In this case, we want to prioritize the one with more launched runs - # that is zero - self.assertEqual(list(scheduler._sort_instances_by_stage(instances)), [0, 1]) - - # One more instance comparison to be supper safe - instances[2] = add_sh_mock(stage=1, config_inst_pairs=7) - self.assertEqual(list(scheduler._sort_instances_by_stage(instances)), [2, 0, 1]) - - # Not let us add a more advanced stage run - instances[3] = add_sh_mock(stage=2, config_inst_pairs=1) - self.assertEqual(list(scheduler._sort_instances_by_stage(instances)), [3, 2, 0, 1]) - - # Make 1 the oldest stage - instances[1] = add_sh_mock(stage=4, config_inst_pairs=1) - self.assertEqual(list(scheduler._sort_instances_by_stage(instances)), [1, 3, 2, 0]) - - # Add a new run that's empty - instances[4] = add_sh_mock(stage=0, config_inst_pairs=0) - self.assertEqual(list(scheduler._sort_instances_by_stage(instances)), [1, 3, 2, 0, 4]) - - # Make 4 stage 4 but with not as many instances as 1 - instances[4] = add_sh_mock(stage=4, config_inst_pairs=0) - self.assertEqual(list(scheduler._sort_instances_by_stage(instances)), [1, 4, 3, 2, 0]) - - # And lastly 0 -> stage 4 - instances[0] = add_sh_mock(stage=4, config_inst_pairs=0) - self.assertEqual(list(scheduler._sort_instances_by_stage(instances)), [1, 0, 4, 3, 2]) - - def test_process_results(self): - """Ensures that the results are processed by the pertinent intensifer, - based on the source id""" - scheduler = ParallelScheduler( - stats=None, - traj_logger=None, - instances=[1, 2, 3], - rng=np.random.RandomState(12345), - deterministic=True, - ) - - scheduler.intensifier_instances = { - 0: mock.Mock(), - 1: mock.Mock(), - 2: mock.Mock(), - } - - run_info = RunInfo( - config=None, - instance=0, - instance_specific="0", - cutoff=None, - seed=0, - capped=False, - budget=0.0, - source_id=2, - ) - - result = RunValue(cost=1, time=0.5, status=StatusType.SUCCESS, starttime=1, endtime=2, additional_info={}) - - scheduler.process_results(run_info=run_info, result=result, incumbent=None, run_history=None, time_bound=None) - self.assertIsNone(scheduler.intensifier_instances[0].process_results.call_args) - self.assertIsNone(scheduler.intensifier_instances[1].process_results.call_args) - self.assertEqual(scheduler.intensifier_instances[2].process_results.call_args[1]["run_info"], run_info) - - def test_get_next_run_wait(self): - """Makes sure we wait if all intensifiers are busy, and no new instance got added. - This test the case that number of workers are equal to number of instances - """ - scheduler = ParallelScheduler( - stats=None, - traj_logger=None, - instances=[1, 2, 3], - rng=np.random.RandomState(12345), - deterministic=True, - ) - scheduler._get_intensifier_ranking = mock_ranker - scheduler.intensifier_instances = {0: mock.Mock()} - scheduler.intensifier_instances[0].get_next_run.return_value = (RunInfoIntent.WAIT, None) - scheduler.intensifier_instances[0].stage = 0 - scheduler.intensifier_instances[0].run_tracker = () - - with unittest.mock.patch( - "smac.intensification.parallel_scheduling.ParallelScheduler._add_new_instance" - ) as add_new_instance: - add_new_instance.return_value = False - intent, run_info = scheduler.get_next_run( - challengers=None, - incumbent=None, - chooser=None, - run_history=None, - repeat_configs=False, - num_workers=1, - ) - self.assertEqual(intent, RunInfoIntent.WAIT) - - def test_get_next_run_add_instance(self): - """Makes sure we add an instance only when all other instances are waiting, - This happens when n_workers greater than the number of instances - """ - with unittest.mock.patch( - "smac.intensification.parallel_scheduling.ParallelScheduler._add_new_instance" - ) as add_new_instance: - scheduler = ParallelScheduler( - stats=None, - traj_logger=None, - instances=[1, 2, 3], - rng=np.random.RandomState(12345), - deterministic=True, - ) - - def instance_added(args): - source_id = len(scheduler.intensifier_instances) - scheduler.intensifier_instances[source_id] = mock.Mock() - scheduler.intensifier_instances[source_id].get_next_run.return_value = ( - RunInfoIntent.RUN, - None, - ) - return True - - add_new_instance.side_effect = instance_added - scheduler._get_intensifier_ranking = mock_ranker - scheduler.intensifier_instances = {0: mock.Mock()} - scheduler.intensifier_instances[0].get_next_run.return_value = ( - RunInfoIntent.WAIT, - None, - ) - scheduler.intensifier_instances[0].stage = 0 - scheduler.intensifier_instances[0].run_tracker = () - - intent, run_info = scheduler.get_next_run( - challengers=None, - incumbent=None, - chooser=None, - run_history=None, - repeat_configs=False, - num_workers=1, - ) - self.assertEqual(len(scheduler.intensifier_instances), 2) - self.assertEqual(intent, RunInfoIntent.RUN) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_intensify/test_simple_intensifier.py b/tests/test_intensify/test_simple_intensifier.py deleted file mode 100644 index 6d1c85896..000000000 --- a/tests/test_intensify/test_simple_intensifier.py +++ /dev/null @@ -1,186 +0,0 @@ -import time -import unittest - -import numpy as np -from ConfigSpace import Configuration, ConfigurationSpace -from ConfigSpace.hyperparameters import UniformIntegerHyperparameter - -from smac.intensification.abstract_racer import RunInfoIntent -from smac.intensification.simple_intensifier import SimpleIntensifier -from smac.runhistory.runhistory import RunHistory, RunInfo, RunValue -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.utils.io.traj_logging import TrajLogger - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def get_config_space(): - cs = ConfigurationSpace() - cs.add_hyperparameter(UniformIntegerHyperparameter(name="a", lower=0, upper=100)) - cs.add_hyperparameter(UniformIntegerHyperparameter(name="b", lower=0, upper=100)) - return cs - - -def target_from_run_info(RunInfo): - value_from_config = sum([a for a in RunInfo.config.get_dictionary().values()]) - return RunValue( - cost=value_from_config, - time=0.5, - status=StatusType.SUCCESS, - starttime=time.time(), - endtime=time.time() + 1, - additional_info={}, - ) - - -class TestSimpleIntensifier(unittest.TestCase): - def setUp(self): - unittest.TestCase.setUp(self) - - self.rh = RunHistory() - self.cs = get_config_space() - self.config1 = Configuration(self.cs, values={"a": 7, "b": 11}) - self.config2 = Configuration(self.cs, values={"a": 13, "b": 17}) - self.config3 = Configuration(self.cs, values={"a": 0, "b": 7}) - self.config4 = Configuration(self.cs, values={"a": 29, "b": 31}) - - self.scen = Scenario({"cutoff_time": 2, "cs": self.cs, "run_obj": "runtime", "output_dir": ""}) - self.stats = Stats(scenario=self.scen) - self.stats.start_timing() - - # Create the base object - self.intensifier = SimpleIntensifier( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - instances=[1], - ) - - def test_get_next_run(self): - """ - Makes sure that sampling a configuration returns a valid - configuration - """ - intent, run_info = self.intensifier.get_next_run( - challengers=[self.config1], - incumbent=None, - run_history=self.rh, - num_workers=1, - chooser=None, - ) - - self.assertEqual(intent, RunInfoIntent.RUN) - - self.assertEqual( - run_info, - RunInfo( - config=self.config1, - instance=1, - instance_specific="0", - seed=0, - cutoff=None, - capped=False, - budget=0.0, - ), - ) - - def test_get_next_run_waits_if_no_workers(self): - """ - In the case all workers are busy, we wait so that we do - not saturate the process with configurations that will not - finish in time - """ - intent, run_info = self.intensifier.get_next_run( - challengers=[self.config1, self.config2], - incumbent=None, - run_history=self.rh, - num_workers=1, - chooser=None, - ) - - # We can get the configuration 1 - self.assertEqual(intent, RunInfoIntent.RUN) - self.assertEqual( - run_info, - RunInfo( - config=self.config1, - instance=1, - instance_specific="0", - seed=0, - cutoff=None, - capped=False, - budget=0.0, - ), - ) - - # We should not get configuration 2 - # As there is just 1 worker - intent, run_info = self.intensifier.get_next_run( - challengers=[self.config2], - incumbent=None, - run_history=self.rh, - num_workers=1, - chooser=None, - ) - self.assertEqual(intent, RunInfoIntent.WAIT) - self.assertEqual( - run_info, - RunInfo( - config=None, - instance=None, - instance_specific="0", - seed=0, - cutoff=None, - capped=False, - budget=0.0, - ), - ) - - def test_process_results(self): - """ - Makes sure that we can process the results of a completed - configuration - """ - intent, run_info = self.intensifier.get_next_run( - challengers=[self.config1, self.config2], - incumbent=None, - run_history=self.rh, - num_workers=1, - chooser=None, - ) - result = RunValue( - cost=1, - time=0.5, - status=StatusType.SUCCESS, - starttime=1, - endtime=2, - additional_info=None, - ) - self.rh.add( - config=run_info.config, - cost=1, - time=0.5, - status=StatusType.SUCCESS, - instance_id=run_info.instance, - seed=run_info.seed, - additional_info=None, - ) - - incumbent, inc_perf = self.intensifier.process_results( - run_info=run_info, - incumbent=None, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(incumbent, run_info.config) - self.assertEqual(inc_perf, 1) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_intensify/test_successive_halving.py b/tests/test_intensify/test_successive_halving.py deleted file mode 100644 index c248bd61f..000000000 --- a/tests/test_intensify/test_successive_halving.py +++ /dev/null @@ -1,2204 +0,0 @@ -import logging -import time -import unittest -from unittest import mock - -import numpy as np -from ConfigSpace import Configuration, ConfigurationSpace -from ConfigSpace.hyperparameters import UniformIntegerHyperparameter - -from smac.intensification.abstract_racer import RunInfoIntent -from smac.intensification.successive_halving import ( - SuccessiveHalving, - _SuccessiveHalving, -) -from smac.runhistory.runhistory import RunHistory, RunInfo, RunValue -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.tae.execute_func import ExecuteTAFuncDict -from smac.utils.io.traj_logging import TrajLogger - -from .test_eval_utils import eval_challenger - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def get_config_space(): - cs = ConfigurationSpace() - cs.add_hyperparameter(UniformIntegerHyperparameter(name="a", lower=0, upper=100)) - cs.add_hyperparameter(UniformIntegerHyperparameter(name="b", lower=0, upper=100)) - return cs - - -def target_from_run_info(RunInfo): - value_from_config = sum([a for a in RunInfo.config.get_dictionary().values()]) - return RunValue( - cost=value_from_config, - time=0.5, - status=StatusType.SUCCESS, - starttime=time.time(), - endtime=time.time() + 1, - additional_info={}, - ) - - -class TestSuccessiveHalving(unittest.TestCase): - def setUp(self): - unittest.TestCase.setUp(self) - - self.rh = RunHistory() - self.cs = get_config_space() - self.config1 = Configuration(self.cs, values={"a": 7, "b": 11}) - self.config2 = Configuration(self.cs, values={"a": 13, "b": 17}) - self.config3 = Configuration(self.cs, values={"a": 0, "b": 7}) - self.config4 = Configuration(self.cs, values={"a": 29, "b": 31}) - - self.scen = Scenario( - { - "cutoff_time": 2, - "cs": self.cs, - "run_obj": "runtime", - "output_dir": "", - "deterministic": False, - "limit_resources": False, - } - ) - self.stats = Stats(scenario=self.scen) - self.stats.start_timing() - - # Create the base object - self.SH = SuccessiveHalving( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=False, - run_obj_time=False, - instances=[1, 2, 3, 4, 5], - n_seeds=2, - initial_budget=2, - max_budget=5, - eta=2, - ) - - def test_initialization(self): - """Makes sure that a proper _SH is created""" - - # We initialize the SH with zero intensifier_instances - self.assertEqual(len(self.SH.intensifier_instances), 0) - - # Add an instance to check the _SH initialization - self.assertTrue(self.SH._add_new_instance(num_workers=1)) - - # Parameters properly passed to _SH - self.assertEqual(len(self.SH.intensifier_instances[0].inst_seed_pairs), 10) - self.assertEqual(self.SH.intensifier_instances[0].initial_budget, 2) - self.assertEqual(self.SH.intensifier_instances[0].max_budget, 5) - - def test_process_results_via_sourceid(self): - """Makes sure source id is honored when deciding - which _SH will consume the result/run_info""" - # Mock the _SH so we can make sure the correct item is passed - for i in range(10): - self.SH.intensifier_instances[i] = mock.Mock() - - # randomly create run_infos and push into SH. Then we will make - # sure they got properly allocated - for i in np.random.choice(list(range(10)), 30): - run_info = RunInfo( - config=self.config1, - instance=0, - instance_specific="0", - cutoff=None, - seed=0, - capped=False, - budget=0.0, - source_id=i, - ) - - # make sure results aren't messed up via magic variable - # That is we check only the proper _SH has this - magic = time.time() - - result = RunValue( - cost=1, - time=0.5, - status=StatusType.SUCCESS, - starttime=1, - endtime=2, - additional_info=magic, - ) - self.SH.process_results( - run_info=run_info, - incumbent=None, - run_history=self.rh, - time_bound=None, - result=result, - log_traj=False, - ) - - # Check the call arguments of each sh instance and make sure - # it is the correct one - - # First the expected one - self.assertEqual( - self.SH.intensifier_instances[i].process_results.call_args[1]["run_info"], - run_info, - ) - self.assertEqual( - self.SH.intensifier_instances[i].process_results.call_args[1]["result"], - result, - ) - all_other_run_infos, all_other_results = [], [] - for j, item in enumerate(self.SH.intensifier_instances): - # Skip the expected _SH - if i == j: - continue - if self.SH.intensifier_instances[j].process_results.call_args is None: - all_other_run_infos.append(None) - else: - all_other_run_infos.append( - self.SH.intensifier_instances[j].process_results.call_args[1]["run_info"] - ) - all_other_results.append(self.SH.intensifier_instances[j].process_results.call_args[1]["result"]) - self.assertNotIn(run_info, all_other_run_infos) - self.assertNotIn(result, all_other_results) - - def test_get_next_run_single_SH(self): - """Makes sure that a single _SH returns a valid config""" - - challengers = [self.config1, self.config2, self.config3, self.config4] - for i in range(30): - intent, run_info = self.SH.get_next_run( - challengers=challengers, - incumbent=None, - chooser=None, - run_history=self.rh, - num_workers=1, - ) - - # Regenerate challenger list - challengers = [c for c in challengers if c != run_info.config] - - if intent == RunInfoIntent.WAIT: - break - - # Add the config to self.rh in order to make SH aware that this - # config/instance was launched - self.rh.add( - config=run_info.config, - cost=10, - time=0.0, - status=StatusType.RUNNING, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - ) - - # We should not create more _SH intensifier_instances - self.assertEqual(len(self.SH.intensifier_instances), 1) - - # We are running with: - # 'all_budgets': array([2.5, 5. ]) -> 2 intensifier_instances per config top - # 'n_configs_in_stage': [2.0, 1.0], - # This means we run int(2.5) + 2.0 = 4 runs before waiting - self.assertEqual(i, 4) - - def test_get_next_run_dual_SH(self): - """Makes sure that two _SH can properly coexist and tag - run_info properly""" - - # Everything here will be tested with a single _SH - challengers = [self.config1, self.config2, self.config3, self.config4] - for i in range(30): - intent, run_info = self.SH.get_next_run( - challengers=challengers, - incumbent=None, - chooser=None, - run_history=self.rh, - num_workers=2, - ) - - # Regenerate challenger list - challengers = [c for c in challengers if c != run_info.config] - - # Add the config to self.rh in order to make SH aware that this - # config/instance was launched - if intent == RunInfoIntent.WAIT: - break - self.rh.add( - config=run_info.config, - cost=10, - time=0.0, - status=StatusType.RUNNING, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - ) - - # We create a second sh intensifier_instances as after 4 runs, the _SH - # number zero needs to wait - self.assertEqual(len(self.SH.intensifier_instances), 2) - - # We are running with: - # 'all_budgets': array([2.5, 5. ]) -> 2 intensifier_instances per config top - # 'n_configs_in_stage': [2.0, 1.0], - # This means we run int(2.5) + 2.0 = 4 runs before waiting - # But we have 2 successive halvers now! - self.assertEqual(i, 8) - - def test_add_new_instance(self): - """Test whether we can add a _SH and when we should not""" - - # By default we do not create a _SH - # test adding the first instance! - self.assertEqual(len(self.SH.intensifier_instances), 0) - self.assertTrue(self.SH._add_new_instance(num_workers=1)) - self.assertEqual(len(self.SH.intensifier_instances), 1) - self.assertIsInstance(self.SH.intensifier_instances[0], _SuccessiveHalving) - # A second call should not add a new _SH - self.assertFalse(self.SH._add_new_instance(num_workers=1)) - - # We try with 2 _SH active - - # We effectively return true because we added a new _SH - self.assertTrue(self.SH._add_new_instance(num_workers=2)) - - self.assertEqual(len(self.SH.intensifier_instances), 2) - self.assertIsInstance(self.SH.intensifier_instances[1], _SuccessiveHalving) - - # Trying to add a third one should return false - self.assertFalse(self.SH._add_new_instance(num_workers=2)) - self.assertEqual(len(self.SH.intensifier_instances), 2) - - def _exhaust_run_and_get_incumbent(self, sh, rh, num_workers=2): - """ - Runs all provided configs on all intensifier_instances and return the incumbent - as a nice side effect runhistory/stats are properly filled - """ - challengers = [self.config1, self.config2, self.config3, self.config4] - incumbent = None - for i in range(100): - try: - intent, run_info = sh.get_next_run( - challengers=challengers, - incumbent=None, - chooser=None, - run_history=rh, - num_workers=num_workers, - ) - except ValueError as e: - # Get configurations until you run out of them - print(e) - break - - # Regenerate challenger list - challengers = [c for c in challengers if c != run_info.config] - - if intent == RunInfoIntent.WAIT: - break - - result = target_from_run_info(run_info) - rh.add( - config=run_info.config, - cost=result.cost, - time=result.time, - status=result.status, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - ) - incumbent, inc_perf = sh.process_results( - run_info=run_info, - incumbent=incumbent, - run_history=rh, - time_bound=100.0, - result=result, - log_traj=False, - ) - return incumbent, inc_perf - - def test_parallel_same_as_serial_SH(self): - """Makes sure we behave the same as a serial run at the end""" - - # Get the run_history for a _SH run: - rh = RunHistory() - stats = Stats(scenario=self.scen) - stats.start_timing() - _SH = _SuccessiveHalving( - stats=stats, - traj_logger=TrajLogger(output_dir=None, stats=stats), - rng=np.random.RandomState(12345), - deterministic=False, - run_obj_time=False, - instances=[1, 2, 3, 4, 5], - n_seeds=2, - initial_budget=2, - max_budget=5, - eta=2, - ) - incumbent, inc_perf = self._exhaust_run_and_get_incumbent(_SH, rh) - - # Just to make sure nothing has changed from the _SH side to make - # this check invalid: - # We add config values, so config 3 with 0 and 7 should be the lesser cost - self.assertEqual(incumbent, self.config3) - self.assertEqual(inc_perf, 7.0) - - # Do the same for SH, but have multiple _SH in there - # _add_new_instance returns true if it was able to add a new _SH - # We call this method twice because we want 2 workers - self.assertTrue(self.SH._add_new_instance(num_workers=2)) - self.assertTrue(self.SH._add_new_instance(num_workers=2)) - incumbent_psh, inc_perf_psh = self._exhaust_run_and_get_incumbent(self.SH, self.rh) - self.assertEqual(incumbent, incumbent_psh) - - # This makes sure there is a single incumbent in SH - self.assertEqual(inc_perf, inc_perf_psh) - - # We don't want to loose any configuration, and particularly - # we want to make sure the values of _SH to SH match - self.assertEqual(len(self.rh.data), len(rh.data)) - - # We are comparing exhausted single vs parallel successive - # halving runs. The number and type of configs should be the same - # and is enforced as a dictionary key argument check. The number - # of runs will be different ParallelSuccesiveHalving has 2 _SH intensifier_instances - # yet we make sure that after exhaustion, the budgets a config was run - # should match - configs_sh_rh = {} - for k, v in rh.data.items(): - config_sh = rh.ids_config[k.config_id] - if config_sh not in configs_sh_rh: - configs_sh_rh[config_sh] = [] - if v.cost not in configs_sh_rh[config_sh]: - configs_sh_rh[config_sh].append(v.cost) - configs_psh_rh = {} - for k, v in self.rh.data.items(): - config_psh = self.rh.ids_config[k.config_id] - if config_psh not in configs_psh_rh: - configs_psh_rh[config_psh] = [] - if v.cost not in configs_psh_rh[config_psh]: - configs_psh_rh[config_psh].append(v.cost) - - # If this dictionaries are equal it means we have all configs - # and the values track the numbers and actual cost! - self.assertDictEqual(configs_sh_rh, configs_psh_rh) - - -class Test_SuccessiveHalving(unittest.TestCase): - def setUp(self): - unittest.TestCase.setUp(self) - - self.rh = RunHistory() - self.cs = get_config_space() - self.config1 = Configuration(self.cs, values={"a": 0, "b": 100}) - self.config2 = Configuration(self.cs, values={"a": 100, "b": 0}) - self.config3 = Configuration(self.cs, values={"a": 100, "b": 100}) - self.config4 = Configuration(self.cs, values={"a": 0, "b": 0}) - - self.scen = Scenario( - { - "cutoff_time": 2, - "cs": self.cs, - "run_obj": "runtime", - "output_dir": "", - "deterministic": False, - "limit_resources": True, - } - ) - self.stats = Stats(scenario=self.scen) - self.stats.start_timing() - - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - - def test_init_1(self): - """ - test parameter initializations for successive halving - instance as budget - """ - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=False, - run_obj_time=False, - instances=[1, 2, 3], - n_seeds=2, - initial_budget=None, - max_budget=None, - eta=2, - ) - - self.assertEqual(len(intensifier.inst_seed_pairs), 6) # since instance-seed pairs - self.assertEqual(len(intensifier.instances), 3) - self.assertEqual(intensifier.initial_budget, 1) - self.assertEqual(intensifier.max_budget, 6) - self.assertListEqual(intensifier.n_configs_in_stage, [4.0, 2.0, 1.0]) - self.assertTrue(intensifier.instance_as_budget) - self.assertTrue(intensifier.repeat_configs) - - def test_init_2(self): - """ - test parameter initialiations for successive halving - real-valued budget - """ - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - instances=[1], - initial_budget=1, - max_budget=10, - eta=2, - ) - - self.assertEqual(len(intensifier.inst_seed_pairs), 1) # since instance-seed pairs - self.assertEqual(intensifier.initial_budget, 1) - self.assertEqual(intensifier.max_budget, 10) - self.assertListEqual(intensifier.n_configs_in_stage, [8.0, 4.0, 2.0, 1.0]) - self.assertListEqual(list(intensifier.all_budgets), [1.25, 2.5, 5.0, 10.0]) - self.assertFalse(intensifier.instance_as_budget) - self.assertFalse(intensifier.repeat_configs) - - def test_init_3(self): - """ - test parameter initialiations for successive halving - real-valued budget, high initial budget - """ - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - instances=[1], - initial_budget=9, - max_budget=10, - eta=2, - ) - - self.assertEqual(len(intensifier.inst_seed_pairs), 1) # since instance-seed pairs - self.assertEqual(intensifier.initial_budget, 9) - self.assertEqual(intensifier.max_budget, 10) - self.assertListEqual(intensifier.n_configs_in_stage, [1.0]) - self.assertListEqual(list(intensifier.all_budgets), [10.0]) - self.assertFalse(intensifier.instance_as_budget) - self.assertFalse(intensifier.repeat_configs) - - def test_init_4(self): - """ - test wrong parameter initializations for successive halving - """ - - # runtime as budget (no param provided) - with self.assertRaisesRegex( - ValueError, - "requires parameters initial_budget and max_budget for intensification!", - ): - _SuccessiveHalving( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - cutoff=10, - instances=[1], - ) - - # eta < 1 - with self.assertRaisesRegex(ValueError, "eta must be greater than 1"): - _SuccessiveHalving( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - cutoff=10, - instances=[1], - eta=0, - ) - - # max budget > instance-seed pairs - with self.assertRaisesRegex( - ValueError, - "Max budget cannot be greater than the number of instance-seed pairs", - ): - _SuccessiveHalving( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - cutoff=10, - instances=[1, 2, 3], - initial_budget=1, - max_budget=5, - n_seeds=1, - ) - - def test_top_k_1(self): - """ - test _top_k() for configs with same instance-seed-budget keys - """ - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - instances=[1, 2], - initial_budget=1, - ) - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - ) - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=2, - seed=None, - additional_info=None, - ) - self.rh.add( - config=self.config2, - cost=2, - time=2, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - ) - self.rh.add( - config=self.config2, - cost=2, - time=2, - status=StatusType.SUCCESS, - instance_id=2, - seed=None, - additional_info=None, - ) - self.rh.add( - config=self.config3, - cost=3, - time=3, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - ) - self.rh.add( - config=self.config3, - cost=3, - time=3, - status=StatusType.SUCCESS, - instance_id=2, - seed=None, - additional_info=None, - ) - self.rh.add( - config=self.config4, - cost=0.5, - time=0.5, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - ) - self.rh.add( - config=self.config4, - cost=0.5, - time=0.5, - status=StatusType.SUCCESS, - instance_id=2, - seed=None, - additional_info=None, - ) - conf = intensifier._top_k( - configs=[self.config1, self.config2, self.config3, self.config4], - k=2, - run_history=self.rh, - ) - - # Check that config4 is also before config1 (as it has the lower cost) - self.assertEqual(conf, [self.config4, self.config1]) - - def test_top_k_2(self): - """ - test _top_k() for configs with different instance-seed-budget keys - """ - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - instances=[1, 2], - initial_budget=1, - ) - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - ) - self.rh.add( - config=self.config2, - cost=10, - time=10, - status=StatusType.SUCCESS, - instance_id=2, - seed=None, - additional_info=None, - ) - - with self.assertRaisesRegex(ValueError, "Cannot compare configs"): - intensifier._top_k( - configs=[self.config2, self.config1, self.config3], - k=1, - run_history=self.rh, - ) - - def test_top_k_3(self): - """ - test _top_k() for not enough configs to generate for the next budget - """ - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - instances=[1], - initial_budget=1, - ) - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - ) - self.rh.add( - config=self.config2, - cost=1, - time=1, - status=StatusType.CRASHED, - instance_id=1, - seed=None, - additional_info=None, - ) - configs = intensifier._top_k(configs=[self.config1], k=2, run_history=self.rh) - - # top_k should return whatever configuration is possible - self.assertEqual(configs, [self.config1]) - - def test_top_k_4(self): - """ - test _top_k() for not enough configs to generate for the next budget - """ - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - run_obj_time=False, - rng=np.random.RandomState(12345), - eta=2, - num_initial_challengers=4, - instances=[1], - initial_budget=1, - max_budget=10, - ) - intensifier._update_stage(self.rh) - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - budget=1, - additional_info=None, - ) - self.rh.add( - config=self.config2, - cost=1, - time=1, - status=StatusType.DONOTADVANCE, - instance_id=1, - seed=None, - budget=1, - additional_info=None, - ) - self.rh.add( - config=self.config3, - cost=1, - time=1, - status=StatusType.DONOTADVANCE, - instance_id=1, - seed=None, - budget=1, - additional_info=None, - ) - self.rh.add( - config=self.config4, - cost=1, - time=1, - status=StatusType.DONOTADVANCE, - instance_id=1, - seed=None, - budget=1, - additional_info=None, - ) - intensifier.success_challengers.add(self.config1) - intensifier.fail_challengers.add(self.config2) - intensifier.fail_challengers.add(self.config3) - intensifier.fail_challengers.add(self.config4) - intensifier._update_stage(self.rh) - self.assertEqual(intensifier.fail_chal_offset, 1) # we miss one challenger for this round - configs = intensifier._top_k(configs=[self.config1], k=2, run_history=self.rh) - self.assertEqual(configs, [self.config1]) - - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.DONOTADVANCE, - instance_id=1, - seed=None, - budget=intensifier.all_budgets[1], - additional_info=None, - ) - intensifier.fail_challengers.add(self.config2) - intensifier._update_stage(self.rh) - self.assertEqual(intensifier.stage, 0) # going back, since there are not enough to advance - - def test_get_next_run_1(self): - """ - test get_next_run for a presently running configuration - """ - - def target(x): - return 1 - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="quality") - - taf.runhistory = self.rh - - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - cutoff=1, - instances=[1, 2], - initial_budget=1, - max_budget=2, - eta=2, - ) - - # next challenger from a list - intent, run_info = intensifier.get_next_run( - challengers=[self.config1], - chooser=None, - run_history=self.rh, - incumbent=None, - ) - self.rh.add( - config=run_info.config, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - cost=10, - time=1, - status=StatusType.RUNNING, - additional_info=None, - ) - self.assertEqual(run_info.config, self.config1) - self.assertTrue(intensifier.new_challenger) - - # In the parallel scenario, we cannot wait for a configuration - # to be evaluated before moving to the next configuration in the same - # stage. That is, for this example we will have self.n_configs_in_stage=[2, 1] - # with self.all_budgets=[1. 2.]. In other words, in this stage we - # will have 2 configs each with 1 instance. - intent, run_info_new = intensifier.get_next_run( - challengers=[self.config2], - chooser=None, - run_history=self.rh, - incumbent=None, - ) - self.rh.add( - config=run_info_new.config, - instance_id=run_info_new.instance, - seed=run_info_new.seed, - budget=run_info_new.budget, - cost=10, - time=1, - status=StatusType.RUNNING, - additional_info=None, - ) - self.assertEqual(run_info_new.config, self.config2) - self.assertEqual(intensifier.running_challenger, run_info_new.config) - self.assertTrue(intensifier.new_challenger) - - # evaluating configuration - self.assertIsNotNone(run_info.config) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=None, - run_history=self.rh, - time_bound=np.inf, - result=result, - log_traj=False, - ) - - # We already launched run_info_new. We expect 2 configs each with 1 seed/instance - # 1 has finished and already processed. We have not even run run_info_new - # So we cannot advance to a new stage - intent, run_info = intensifier.get_next_run( - challengers=[self.config2], chooser=None, incumbent=inc, run_history=self.rh - ) - self.assertIsNone(run_info.config) - self.assertEqual(intent, RunInfoIntent.WAIT) - self.assertEqual(len(intensifier.success_challengers), 1) - self.assertTrue(intensifier.new_challenger) - - def test_get_next_run_2(self): - """ - test get_next_run for higher stages of SH iteration - """ - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - cutoff=1, - instances=[1], - initial_budget=1, - max_budget=2, - eta=2, - ) - - intensifier._update_stage(run_history=None) - intensifier.stage += 1 - intensifier.configs_to_run = [self.config1] - - # next challenger should come from configs to run - intent, run_info = intensifier.get_next_run( - challengers=None, - chooser=None, - run_history=self.rh, - incumbent=None, - ) - self.assertEqual(run_info.config, self.config1) - self.assertEqual(len(intensifier.configs_to_run), 0) - self.assertFalse(intensifier.new_challenger) - - def test_update_stage(self): - """ - test update_stage - initializations for all tracking variables - """ - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - cutoff=1, - instances=[1], - initial_budget=1, - max_budget=2, - eta=2, - ) - - # first stage update - intensifier._update_stage(run_history=None) - - self.assertEqual(intensifier.stage, 0) - self.assertEqual(intensifier.sh_iters, 0) - self.assertEqual(intensifier.running_challenger, None) - self.assertEqual(intensifier.success_challengers, set()) - - # higher stages - self.rh.add(self.config1, 1, 1, StatusType.SUCCESS) - self.rh.add(self.config2, 2, 2, StatusType.SUCCESS) - intensifier.success_challengers = {self.config1, self.config2} - intensifier._update_stage(run_history=self.rh) - - self.assertEqual(intensifier.stage, 1) - self.assertEqual(intensifier.sh_iters, 0) - self.assertEqual(intensifier.configs_to_run, [self.config1]) - - # next iteration - intensifier.success_challengers = {self.config1} - intensifier._update_stage(run_history=self.rh) - - self.assertEqual(intensifier.stage, 0) - self.assertEqual(intensifier.sh_iters, 1) - self.assertIsInstance(intensifier.configs_to_run, list) - self.assertEqual(len(intensifier.configs_to_run), 0) - - @unittest.mock.patch.object(_SuccessiveHalving, "_top_k") - def test_update_stage_2(self, top_k_mock): - """ - test update_stage - everything good is in state do not advance - """ - - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - cutoff=1, - initial_budget=1, - max_budget=4, - eta=2, - instances=None, - ) - - # update variables - intensifier._update_stage(run_history=None) - - intensifier.success_challengers.add(self.config1) - intensifier.success_challengers.add(self.config2) - intensifier.do_not_advance_challengers.add(self.config3) - intensifier.do_not_advance_challengers.add(self.config4) - - top_k_mock.return_value = [self.config1, self.config3] - - # Test that we update the stage as there is one configuration advanced to the next budget - self.assertEqual(intensifier.stage, 0) - intensifier._update_stage(run_history=None) - self.assertEqual(intensifier.stage, 1) - self.assertEqual(intensifier.configs_to_run, [self.config1]) - self.assertEqual(intensifier.fail_chal_offset, 1) - self.assertEqual(len(intensifier.success_challengers), 0) - self.assertEqual(len(intensifier.do_not_advance_challengers), 0) - - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - cutoff=1, - initial_budget=1, - max_budget=4, - eta=2, - instances=None, - ) - - # update variables - intensifier._update_stage(run_history=None) - - intensifier.success_challengers.add(self.config1) - intensifier.success_challengers.add(self.config2) - intensifier.do_not_advance_challengers.add(self.config3) - intensifier.do_not_advance_challengers.add(self.config4) - - top_k_mock.return_value = [self.config3, self.config4] - - # Test that we update the stage as there is no configuration advanced to the next budget - self.assertEqual(intensifier.stage, 0) - intensifier._update_stage(run_history=None) - self.assertEqual(intensifier.stage, 0) - self.assertEqual(intensifier.configs_to_run, []) - self.assertEqual(intensifier.fail_chal_offset, 0) - self.assertEqual(len(intensifier.success_challengers), 0) - self.assertEqual(len(intensifier.do_not_advance_challengers), 0) - - top_k_mock.return_value = [] - - def test_eval_challenger_1(self): - """ - test eval_challenger with quality objective & real-valued budget - """ - - def target(x: Configuration, instance: str, seed: int, budget: float): - return 0.1 * budget - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="quality") - taf.runhistory = self.rh - - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - cutoff=1, - instances=[None], - initial_budget=0.25, - max_budget=0.5, - eta=2, - ) - intensifier._update_stage(run_history=None) - - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - seed=0, - budget=0.5, - ) - self.rh.add( - config=self.config2, - cost=1, - time=1, - status=StatusType.SUCCESS, - seed=0, - budget=0.25, - ) - self.rh.add( - config=self.config3, - cost=2, - time=1, - status=StatusType.SUCCESS, - seed=0, - budget=0.25, - ) - - intensifier.success_challengers = {self.config2, self.config3} - intensifier._update_stage(run_history=self.rh) - - intent, run_info = intensifier.get_next_run( - challengers=[self.config1], - chooser=None, - incumbent=self.config1, - run_history=self.rh, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(inc, self.config2) - self.assertEqual(inc_value, 0.05) - self.assertEqual(list(self.rh.data.keys())[-1][0], self.rh.config_ids[self.config2]) - self.assertEqual(self.stats.inc_changed, 1) - - def test_eval_challenger_2(self): - """ - test eval_challenger with runtime objective and adaptive capping - """ - - def target(x: Configuration, instance: str): - if x["a"] == 100 or instance == 2: - time.sleep(1.5) - return (x["a"] + 1) / 1000.0 - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="runtime") - taf.runhistory = self.rh - - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=True, - cutoff=1, - instances=[1, 2], - initial_budget=1, - max_budget=2, - eta=2, - instance_order=None, - ) - - # config1 should be executed successfully and selected as incumbent - intent, run_info = intensifier.get_next_run( - challengers=[self.config1], - chooser=None, - incumbent=None, - run_history=self.rh, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=None, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(run_info.config, self.config1) - self.assertEqual(self.stats.inc_changed, 1) - - # config2 should be capped and config1 should still be the incumbent - - intent, run_info = intensifier.get_next_run( - challengers=[self.config2], chooser=None, incumbent=inc, run_history=self.rh - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(inc, self.config1) - self.assertEqual(self.stats.inc_changed, 1) - self.assertEqual(list(self.rh.data.values())[1][2], StatusType.CAPPED) - - # config1 is selected for the next stage and allowed to timeout since this is the 1st run for this instance - intent, run_info = intensifier.get_next_run(challengers=[], chooser=None, incumbent=inc, run_history=self.rh) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(inc, self.config1) - self.assertEqual(list(self.rh.data.values())[2][2], StatusType.TIMEOUT) - - @mock.patch.object(_SuccessiveHalving, "_top_k") - def test_eval_challenger_capping(self, patch): - """ - test eval_challenger with adaptive capping and all configurations capped/crashed - """ - - def target(x): - if x["b"] == 100: - time.sleep(1.5) - if x["a"] == 100: - raise ValueError("You shall not pass") - return (x["a"] + 1) / 1000.0 - - taf = ExecuteTAFuncDict( - use_pynisher=False, - ta=target, - stats=self.stats, - run_obj="runtime", - abort_on_first_run_crash=False, - ) - taf.runhistory = self.rh - - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=True, - cutoff=1, - instances=[1, 2], - initial_budget=1, - max_budget=2, - eta=2, - instance_order=None, - ) - - for i in range(2): - self.rh.add( - config=self.config1, - cost=0.001, - time=0.001, - status=StatusType.SUCCESS, - instance_id=i + 1, - seed=0, - additional_info=None, - ) - - # provide configurations - intent, run_info = intensifier.get_next_run( - challengers=[self.config2], - chooser=None, - incumbent=self.config1, - run_history=self.rh, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(inc, self.config1) - self.assertEqual(list(self.rh.data.values())[2][2], StatusType.CRASHED) - self.assertEqual(len(intensifier.success_challengers), 0) - - # provide configurations - intent, run_info = intensifier.get_next_run( - challengers=[self.config3], - chooser=None, - incumbent=self.config1, - run_history=self.rh, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(inc, self.config1) - self.assertEqual(self.stats.inc_changed, 0) - self.assertEqual(list(self.rh.data.values())[3][2], StatusType.CAPPED) - self.assertEqual(len(intensifier.success_challengers), 0) - # top_k() should not be called since all configs were capped - self.assertFalse(patch.called) - - # now the SH iteration should begin a new iteration since all configs were capped! - self.assertEqual(intensifier.sh_iters, 1) - self.assertEqual(intensifier.stage, 0) - - # should raise an error as this is a new iteration but no configs were provided - with self.assertRaisesRegex(ValueError, "No configurations/chooser provided."): - intent, run_info = intensifier.get_next_run( - challengers=None, - chooser=None, - incumbent=self.config1, - run_history=self.rh, - ) - - def test_eval_challenger_capping_2(self): - """ - test eval_challenger for adaptive capping with all but one configuration capped - """ - - def target(x): - if x["a"] + x["b"] > 0: - time.sleep(1.5) - return x["a"] - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="runtime") - taf.runhistory = self.rh - - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - deterministic=False, - cutoff=1, - instances=[1, 2], - n_seeds=2, - initial_budget=1, - max_budget=4, - eta=2, - instance_order=None, - ) - - # first configuration run - intent, run_info = intensifier.get_next_run( - challengers=[self.config4], - chooser=None, - incumbent=None, - run_history=self.rh, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=None, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(inc, self.config4) - - # remaining 3 runs should be capped - for i in [self.config1, self.config2, self.config3]: - intent, run_info = intensifier.get_next_run( - challengers=[i], chooser=None, incumbent=inc, run_history=self.rh - ) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(inc, self.config4) - self.assertEqual(list(self.rh.data.values())[1][2], StatusType.CAPPED) - self.assertEqual(list(self.rh.data.values())[2][2], StatusType.CAPPED) - self.assertEqual(list(self.rh.data.values())[3][2], StatusType.CAPPED) - self.assertEqual(intensifier.stage, 1) - self.assertEqual(intensifier.fail_chal_offset, 1) # 2 configs expected, but 1 failure - - # run next stage - should run only 1 configuration since other 3 were capped - # 1 runs for config1 - intent, run_info = intensifier.get_next_run(challengers=[], chooser=None, incumbent=inc, run_history=self.rh) - self.assertEqual(run_info.config, self.config4) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - self.assertEqual(intensifier.stage, 2) - - # run next stage with only config1 - # should go to next iteration since no more configurations left - for _ in range(2): - intent, run_info = intensifier.get_next_run( - challengers=[], chooser=None, incumbent=inc, run_history=self.rh - ) - self.assertEqual(run_info.config, self.config4) - result = eval_challenger(run_info, taf, self.stats, self.rh) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(inc, self.config4) - self.assertEqual( - len(self.rh.get_runs_for_config(self.config4, only_max_observed_budget=True)), - 4, - ) - self.assertEqual(intensifier.sh_iters, 1) - self.assertEqual(intensifier.stage, 0) - - with self.assertRaisesRegex(ValueError, "No configurations/chooser provided."): - intensifier.get_next_run(challengers=[], chooser=None, incumbent=inc, run_history=self.rh) - - def test_eval_challenger_3(self): - """ - test eval_challenger for updating to next stage and shuffling instance order every run - """ - - def target(x: Configuration, instance: str): - return (x["a"] + int(instance)) / 1000.0 - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="quality") - taf.runhistory = self.rh - - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=TrajLogger(output_dir=None, stats=self.stats), - rng=np.random.RandomState(12345), - run_obj_time=False, - instances=[0, 1], - instance_order="shuffle", - eta=2, - deterministic=True, - cutoff=1, - ) - - intensifier._update_stage(run_history=self.rh) - - self.assertEqual(intensifier.inst_seed_pairs, [(0, 0), (1, 0)]) - - intent, run_info = intensifier.get_next_run( - challengers=[self.config1], - chooser=None, - incumbent=None, - run_history=self.rh, - ) - - # Mark the configuration as launched - self.rh.add( - config=run_info.config, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - cost=10, - time=1, - status=StatusType.RUNNING, - additional_info=None, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh, force_update=True) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=None, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(inc, self.config1) - self.assertEqual( - len(self.rh.get_runs_for_config(self.config1, only_max_observed_budget=True)), - 1, - ) - self.assertEqual(intensifier.configs_to_run, []) - self.assertEqual(intensifier.stage, 0) - - intent, run_info = intensifier.get_next_run( - challengers=[self.config2], chooser=None, incumbent=inc, run_history=self.rh - ) - self.rh.add( - config=run_info.config, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - cost=10, - time=1, - status=StatusType.RUNNING, - additional_info=None, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh, force_update=True) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(inc, self.config1) - self.assertEqual( - len(self.rh.get_runs_for_config(self.config2, only_max_observed_budget=True)), - 1, - ) - self.assertEqual(intensifier.configs_to_run, [self.config1]) # Incumbent is promoted to the next stage - self.assertEqual(intensifier.stage, 1) - - intent, run_info = intensifier.get_next_run( - challengers=[self.config3], chooser=None, incumbent=inc, run_history=self.rh - ) - self.rh.add( - config=run_info.config, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - cost=10, - time=1, - status=StatusType.RUNNING, - additional_info=None, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh, force_update=True) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(inc, self.config1) - - self.assertEqual( - len(self.rh.get_runs_for_config(self.config1, only_max_observed_budget=True)), - 2, - ) - self.assertEqual(intensifier.sh_iters, 1) - self.assertEqual(self.stats.inc_changed, 1) - - # For the 2nd SH iteration, we should still be able to run the old configurations again - # since instance order is "shuffle" - - self.assertEqual(intensifier.inst_seed_pairs, [(1, 0), (0, 0)]) - - intent, run_info = intensifier.get_next_run( - challengers=[self.config2], chooser=None, incumbent=inc, run_history=self.rh - ) - self.rh.add( - config=run_info.config, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - cost=10, - time=1, - status=StatusType.RUNNING, - additional_info=None, - ) - result = eval_challenger(run_info, taf, self.stats, self.rh, force_update=True) - inc, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=inc, - run_history=self.rh, - time_bound=np.inf, - result=result, - ) - - self.assertEqual(run_info.config, self.config2) - self.assertEqual( - len(self.rh.get_runs_for_config(self.config2, only_max_observed_budget=True)), - 2, - ) - - def test_incumbent_selection_default(self): - """ - test _compare_config for default incumbent selection design (highest budget so far) - """ - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - run_obj_time=False, - instances=[1], - initial_budget=1, - max_budget=2, - eta=2, - ) - intensifier.stage = 0 - - # SH considers challenger as incumbent in first run in eval_challenger - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - budget=1, - ) - inc = intensifier._compare_configs( - challenger=self.config1, - incumbent=self.config1, - run_history=self.rh, - log_traj=False, - ) - self.assertEqual(inc, self.config1) - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - budget=2, - ) - inc = intensifier._compare_configs(challenger=self.config1, incumbent=inc, run_history=self.rh, log_traj=False) - self.assertEqual(inc, self.config1) - - # Adding a worse configuration - self.rh.add( - config=self.config2, - cost=2, - time=2, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - budget=1, - ) - inc = intensifier._compare_configs(challenger=self.config2, incumbent=inc, run_history=self.rh, log_traj=False) - self.assertEqual(inc, self.config1) - self.rh.add( - config=self.config2, - cost=2, - time=2, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - budget=2, - ) - inc = intensifier._compare_configs(challenger=self.config2, incumbent=inc, run_history=self.rh, log_traj=False) - self.assertEqual(inc, self.config1) - - # Adding a better configuration, but the incumbent will only be changed on budget=2 - self.rh.add( - config=self.config3, - cost=0.5, - time=3, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - budget=1, - ) - inc = intensifier._compare_configs(challenger=self.config3, incumbent=inc, run_history=self.rh, log_traj=False) - self.assertEqual(inc, self.config1) - self.rh.add( - config=self.config3, - cost=0.5, - time=3, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - budget=2, - ) - inc = intensifier._compare_configs(challenger=self.config3, incumbent=inc, run_history=self.rh, log_traj=False) - self.assertEqual(inc, self.config3) - - # Test that the state is only based on the runhistory - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - instances=[1], - initial_budget=1, - ) - intensifier.stage = 0 - - # Adding a better configuration, but the incumbent will only be changed on budget=2 - self.rh.add( - config=self.config4, - cost=0.1, - time=3, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - budget=1, - ) - inc = intensifier._compare_configs(challenger=self.config4, incumbent=inc, run_history=self.rh, log_traj=False) - self.assertEqual(inc, self.config3) - self.rh.add( - config=self.config4, - cost=0.1, - time=3, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - budget=2, - ) - inc = intensifier._compare_configs(challenger=self.config4, incumbent=inc, run_history=self.rh, log_traj=False) - self.assertEqual(inc, self.config4) - - def test_incumbent_selection_designs(self): - """ - test _compare_config with different incumbent selection designs - """ - - # select best on any budget - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - run_obj_time=False, - instances=[1], - initial_budget=1, - max_budget=2, - eta=2, - incumbent_selection="any_budget", - ) - intensifier.stage = 0 - - self.rh.add( - config=self.config1, - instance_id=1, - seed=None, - budget=1, - cost=0.5, - time=1, - status=StatusType.SUCCESS, - additional_info=None, - ) - self.rh.add( - config=self.config1, - instance_id=1, - seed=None, - budget=2, - cost=10, - time=1, - status=StatusType.SUCCESS, - additional_info=None, - ) - self.rh.add( - config=self.config2, - instance_id=1, - seed=None, - budget=2, - cost=5, - time=1, - status=StatusType.SUCCESS, - additional_info=None, - ) - - # incumbent should be config1, since it has the best performance in one of the budgets - inc = intensifier._compare_configs( - incumbent=self.config2, - challenger=self.config1, - run_history=self.rh, - log_traj=False, - ) - self.assertEqual(self.config1, inc) - # if config1 is incumbent already, it shouldn't change - inc = intensifier._compare_configs( - incumbent=self.config1, - challenger=self.config2, - run_history=self.rh, - log_traj=False, - ) - self.assertEqual(self.config1, inc) - - # select best on highest budget only - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - run_obj_time=False, - instances=[1], - initial_budget=1, - max_budget=4, - eta=2, - incumbent_selection="highest_budget", - ) - intensifier.stage = 0 - - # incumbent should not change, since there is no run on the highest budget, - # though config3 is run on a higher budget - self.rh.add( - config=self.config3, - instance_id=1, - seed=None, - budget=2, - cost=0.5, - time=1, - status=StatusType.SUCCESS, - additional_info=None, - ) - self.rh.add( - config=self.config4, - instance_id=1, - seed=None, - budget=1, - cost=5, - time=1, - status=StatusType.SUCCESS, - additional_info=None, - ) - inc = intensifier._compare_configs( - incumbent=self.config4, - challenger=self.config3, - run_history=self.rh, - log_traj=False, - ) - self.assertEqual(self.config4, inc) - self.assertEqual(self.stats.inc_changed, 0) - - # incumbent changes to config3 since that is run on the highest budget - self.rh.add( - config=self.config3, - instance_id=1, - seed=None, - budget=4, - cost=10, - time=1, - status=StatusType.SUCCESS, - additional_info=None, - ) - inc = intensifier._compare_configs( - incumbent=self.config4, - challenger=self.config3, - run_history=self.rh, - log_traj=False, - ) - self.assertEqual(self.config3, inc) - - def test_launched_all_configs_for_current_stage(self): - """ - This check makes sure we can identify when all the current runs - (config/instance/seed) pairs for a given stage have been launched - """ - - def target(x): - return 1 - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="quality") - - taf.runhistory = self.rh - # select best on any budget - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - run_obj_time=False, - instances=list(range(10)), - initial_budget=2, - max_budget=10, - eta=2, - ) - - # So there are 2 instances per config. - # self.stage=0 - # self.n_configs_in_stage=[4.0, 2.0, 1.0] - # self.all_budgets=[ 2.5 5. 10. ] - total_configs_in_stage = 4 - instances_per_stage = 2 - - # get all configs and add them to the dict - run_tracker = {} - challengers = [self.config1, self.config2, self.config3, self.config4] - for i in range(total_configs_in_stage * instances_per_stage): - intent, run_info = intensifier.get_next_run( - challengers=challengers, - chooser=None, - run_history=self.rh, - incumbent=None, - ) - - # All this runs are valid for this stage - self.assertEqual(intent, RunInfoIntent.RUN) - - # Remove from the challengers, the launched configs - challengers = [c for c in challengers if c != run_info.config] - run_tracker[(run_info.config, run_info.instance, run_info.seed)] = False - self.rh.add( - config=run_info.config, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - cost=10, - time=1, - status=StatusType.RUNNING, - additional_info=None, - ) - - # This will get us the second instance of config 1 - intent, run_info = intensifier.get_next_run( - challengers=[self.config2, self.config3, self.config4], - chooser=None, - run_history=self.rh, - incumbent=None, - ) - - # We have launched all runs, that are expected for this stage - # not registered any, so for sure we have to wait - # For all runs to be completed before moving to the next stage - self.assertEqual(intent, RunInfoIntent.WAIT) - - def _exhaust_stage_execution(self, intensifier, taf, challengers, incumbent): - """ - Exhaust configuration/instances seed and returns the - run_info that were not launched. - - The idea with this procedure is to emulate the fact that some - configurations will finish while others won't. We need to be - robust against this scenario - """ - pending_processing = [] - stage = 0 if not hasattr(intensifier, "stage") else intensifier.stage - curr_budget = intensifier.all_budgets[stage] - prev_budget = int(intensifier.all_budgets[stage - 1]) if stage > 0 else 0 - if intensifier.instance_as_budget: - total_runs = int(curr_budget - prev_budget) * int(intensifier.n_configs_in_stage[stage]) - toggle = np.random.choice([True, False], total_runs).tolist() - while not np.any(toggle) or not np.any(np.invert(toggle)): - # make sure we have both true and false! - toggle = np.random.choice([True, False], total_runs).tolist() - else: - # If we directly use the budget, then there are no instances to wait - # But we still want to mimic pending configurations. That is, we don't - # advance to the next stage until all configurations are done for a given - # budget. - # Here if we do not launch a configuration because toggle was false, is - # like this configuration never exited as there is only 1 instance in this - # and if toggle is false, it is never run. So we cannot do a random toggle - toggle = [False, True, False, True] - - while True: - intent, run_info = intensifier.get_next_run( - challengers=challengers, - chooser=None, - run_history=self.rh, - incumbent=incumbent, - ) - - # Update the challengers - challengers = [c for c in challengers if c != run_info.config] - - if intent == RunInfoIntent.WAIT: - break - - # Add this configuration as running - self.rh.add( - config=run_info.config, - instance_id=run_info.instance, - seed=run_info.seed, - budget=run_info.budget, - cost=1000, - time=1000, - status=StatusType.RUNNING, - additional_info=None, - ) - - if toggle.pop(): - result = eval_challenger(run_info, taf, self.stats, self.rh, force_update=True) - incumbent, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=incumbent, - run_history=self.rh, - time_bound=np.inf, - result=result, - log_traj=False, - ) - else: - pending_processing.append(run_info) - - # In case a iteration is done, break - # This happens if the configs per stage is 1 - if intensifier.iteration_done: - break - - return pending_processing, incumbent - - def test_iteration_done_only_when_all_configs_processed_instance_as_budget(self): - """ - Makes sure that iteration done for a given stage is asserted ONLY after all - configurations AND instances are completed, when instance is used as budget - """ - - def target(x): - return 1 - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="quality") - - taf.runhistory = self.rh - # select best on any budget - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - run_obj_time=False, - deterministic=True, - instances=list(range(5)), - initial_budget=2, - max_budget=5, - eta=2, - ) - - # we want to test instance as budget - self.assertTrue(intensifier.instance_as_budget) - - # Run until there are no more configurations to be proposed - # Skip running some configurations to emulate the fact that runs finish on different time - # We need this because there was a bug where not all instances had finished, yet - # the SH instance assumed all configurations finished - challengers = [self.config1, self.config2, self.config3, self.config4] - incumbent = None - pending_processing, incumbent = self._exhaust_stage_execution(intensifier, taf, challengers, incumbent) - - # We have configurations pending, so iteration should NOT be done - self.assertFalse(intensifier.iteration_done) - - # Make sure we launched all configurations we were meant to: - # all_budgets=[2.5 5. ] n_configs_in_stage=[2.0, 1.0] - # We need 2 configurations in the run history - configurations = set([k.config_id for k, v in self.rh.data.items()]) - self.assertEqual(configurations, {1, 2}) - # We need int(2.5) instances in the run history per config - config_inst_seed = set([k for k, v in self.rh.data.items()]) - self.assertEqual(len(config_inst_seed), 4) - - # Go to the last stage. Notice that iteration should not be done - # as we are in stage 1 out of 2 - for run_info in pending_processing: - result = eval_challenger(run_info, taf, self.stats, self.rh, force_update=True) - incumbent, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=self.config1, - run_history=self.rh, - time_bound=np.inf, - result=result, - log_traj=False, - ) - self.assertFalse(intensifier.iteration_done) - - # we transition to stage 1, where the budget is 5 - self.assertEqual(intensifier.stage, 1) - - pending_processing, incumbent = self._exhaust_stage_execution(intensifier, taf, challengers, incumbent) - - # Because budget is 5, BUT we previously ran 2 instances in stage 0 - # we expect that the run history will be populated with 3 new instances for 1 - # config more 4 (stage0, 2 config on 2 instances) + 3 (stage1, 1 config 3 instances) = 7 - config_inst_seed = [k for k, v in self.rh.data.items()] - self.assertEqual(len(config_inst_seed), 7) - - # All new runs should be on the same config - self.assertEqual(len(set([c.config_id for c in config_inst_seed[4:]])), 1) - # We need 3 new instance seed pairs - self.assertEqual(len(set(config_inst_seed[4:])), 3) - - # because there are configurations pending, no iteration should be done - self.assertFalse(intensifier.iteration_done) - - # Finish the pending runs - for run_info in pending_processing: - result = eval_challenger(run_info, taf, self.stats, self.rh, force_update=True) - incumbent, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=incumbent, - run_history=self.rh, - time_bound=np.inf, - result=result, - log_traj=False, - ) - - # Finally, all stages are done, so iteration should be done!! - self.assertTrue(intensifier.iteration_done) - - def test_iteration_done_only_when_all_configs_processed_no_instance_as_budget(self): - """ - Makes sure that iteration done for a given stage is asserted ONLY after all - configurations AND instances are completed, when instance is NOT used as budget - """ - - def target(x): - return 1 - - taf = ExecuteTAFuncDict(use_pynisher=False, ta=target, stats=self.stats, run_obj="quality") - - taf.runhistory = self.rh - # select best on any budget - intensifier = _SuccessiveHalving( - stats=self.stats, - traj_logger=None, - rng=np.random.RandomState(12345), - run_obj_time=False, - deterministic=True, - instances=[0], - initial_budget=2, - max_budget=5, - eta=2, - ) - - # we do not want to test instance as budget - self.assertFalse(intensifier.instance_as_budget) - - # Run until there are no more configurations to be proposed - # Skip running some configurations to emulate the fact that runs finish on different time - # We need this because there was a bug where not all instances had finished, yet - # the SH instance assumed all configurations finished - challengers = [self.config1, self.config2, self.config3, self.config4] - incumbent = None - pending_processing, incumbent = self._exhaust_stage_execution(intensifier, taf, challengers, incumbent) - - # We have configurations pending, so iteration should NOT be done - self.assertFalse(intensifier.iteration_done) - - # Make sure we launched all configurations we were meant to: - # all_budgets=[2.5 5. ] n_configs_in_stage=[2.0, 1.0] - # We need 2 configurations in the run history - configurations = set([k.config_id for k, v in self.rh.data.items()]) - self.assertEqual(configurations, {1, 2}) - # There is only one instance always -- so we only have 2 configs for 1 instances each - config_inst_seed = set([k for k, v in self.rh.data.items()]) - self.assertEqual(len(config_inst_seed), 2) - - # Go to the last stage. Notice that iteration should not be done - # as we are in stage 1 out of 2 - for run_info in pending_processing: - result = eval_challenger(run_info, taf, self.stats, self.rh, force_update=True) - incumbent, inc_value = intensifier.process_results( - run_info=run_info, - incumbent=incumbent, - run_history=self.rh, - time_bound=np.inf, - result=result, - log_traj=False, - ) - self.assertFalse(intensifier.iteration_done) - - # we transition to stage 1, where the budget is 5 - self.assertEqual(intensifier.stage, 1) - - pending_processing, incumbent = self._exhaust_stage_execution(intensifier, taf, challengers, incumbent) - - # The next configuration per stage is just one (n_configs_in_stage=[2.0, 1.0]) - # We ran previously 2 configs and with this new, we should have 3 total - config_inst_seed = [k for k, v in self.rh.data.items()] - self.assertEqual(len(config_inst_seed), 3) - - # Because it is only 1 config, the iteration is completed - self.assertTrue(intensifier.iteration_done) - - # We make sure the proper budget got allocated on the whole run: - # all_budgets=[2.5 5. ] - # We ran 2 configs in small budget and 1 in full budget - self.assertEqual([k.budget for k in self.rh.data.keys()], [2.5, 2.5, 5]) - - -class Test__SuccessiveHalving(unittest.TestCase): - def test_budget_initialization(self): - """ - Check computing budgets (only for non-instance cases) - """ - intensifier = _SuccessiveHalving( - stats=None, - traj_logger=None, - rng=np.random.RandomState(12345), - deterministic=True, - run_obj_time=False, - instances=None, - initial_budget=1, - max_budget=81, - eta=3, - ) - self.assertListEqual([1, 3, 9, 27, 81], intensifier.all_budgets.tolist()) - self.assertListEqual([81, 27, 9, 3, 1], intensifier.n_configs_in_stage) - - to_check = [ - # minb, maxb, eta, n_configs_in_stage, all_budgets - [1, 81, 3, [81, 27, 9, 3, 1], [1, 3, 9, 27, 81]], - [ - 1, - 600, - 3, - [243, 81, 27, 9, 3, 1], - [2.469135, 7.407407, 22.222222, 66.666666, 200, 600], - ], - [1, 100, 10, [100, 10, 1], [1, 10, 100]], - [ - 0.001, - 1, - 3, - [729, 243, 81, 27, 9, 3, 1], - [0.001371, 0.004115, 0.012345, 0.037037, 0.111111, 0.333333, 1.0], - ], - [ - 1, - 1000, - 3, - [729, 243, 81, 27, 9, 3, 1], - [ - 1.371742, - 4.115226, - 12.345679, - 37.037037, - 111.111111, - 333.333333, - 1000.0, - ], - ], - [ - 0.001, - 100, - 10, - [100000, 10000, 1000, 100, 10, 1], - [0.001, 0.01, 0.1, 1.0, 10.0, 100.0], - ], - ] - - for minb, maxb, eta, n_configs_in_stage, all_budgets in to_check: - intensifier._init_sh_params( - initial_budget=minb, - max_budget=maxb, - eta=eta, - _all_budgets=None, - _n_configs_in_stage=None, - ) - comp_budgets = intensifier.all_budgets.tolist() - comp_configs = intensifier.n_configs_in_stage - - self.assertEqual(len(all_budgets), len(comp_budgets)) - self.assertEqual(comp_budgets[-1], maxb) - np.testing.assert_array_almost_equal(all_budgets, comp_budgets, decimal=5) - - self.assertEqual(comp_configs[-1], 1) - self.assertEqual(len(n_configs_in_stage), len(comp_configs)) - np.testing.assert_array_almost_equal(n_configs_in_stage, comp_configs, decimal=5) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_local_bo/__init__.py b/tests/test_local_bo/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/test_local_bo/test_abstract_subspace.py b/tests/test_local_bo/test_abstract_subspace.py deleted file mode 100644 index afb8d1117..000000000 --- a/tests/test_local_bo/test_abstract_subspace.py +++ /dev/null @@ -1,470 +0,0 @@ -import unittest - -import numpy as np -from ConfigSpace import Configuration, ConfigurationSpace -from ConfigSpace.forbidden import ( - ForbiddenAndConjunction, - ForbiddenEqualsClause, - ForbiddenInClause, -) -from ConfigSpace.hyperparameters import ( - CategoricalHyperparameter, - OrdinalHyperparameter, - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) - -from smac.epm.utils import check_subspace_points, get_types -from smac.optimizer.subspaces import ChallengerListLocal, LocalSubspace - - -def generate_cont_hps(): - hp1 = UniformIntegerHyperparameter("non_log_uniform_int", lower=0, upper=100, log=False) - hp2 = UniformIntegerHyperparameter("log_uniform_int", lower=1, upper=100, log=True) - - hp3 = UniformFloatHyperparameter("non_log_uniform_float", lower=0.0, upper=100.0, log=False) - hp4 = UniformFloatHyperparameter("log_uniform_float", lower=1.0, upper=100.0, log=True) - return [hp1, hp2, hp3, hp4] - - -def generate_ord_hps(): - hp1 = OrdinalHyperparameter("ord_hp_1", sequence=[1, 2, 3, 4, 5, 6, 7]) - return [hp1] - - -def generate_cat_hps(num_hps: int = 1): - hps = [] - for i in range(num_hps): - hp = CategoricalHyperparameter(f"cat_hp_{i}", choices=["a", "b", "c", "d"]) - hps.append(hp) - return hps - - -def generate_ss_bounds(cs): - bounds_ss_cont = [] - for hp in cs.get_hyperparameters(): - if isinstance(hp, (UniformIntegerHyperparameter, UniformFloatHyperparameter)): - if hp.log: - bounds_ss_cont.append((0.1, 0.9)) - else: - bounds_ss_cont.append((0.05, 0.95)) - if isinstance(hp, OrdinalHyperparameter): - bounds_ss_cont.append((1, 4)) - return bounds_ss_cont - - -@unittest.mock.patch.multiple(LocalSubspace, __abstractmethods__=set()) -class TestAbstachSubSpace(unittest.TestCase): - def test_cs_subspace_1(self): - cs = ConfigurationSpace() - hps = generate_cont_hps() - hps.extend(generate_ord_hps()) - hps.extend(generate_cat_hps()) - cs.add_hyperparameters(hps) - - types, bounds = get_types(cs) - bounds_ss_cont = np.array(generate_ss_bounds(cs)) - - bounds_ss_cat = [(1, 3)] - subspace = LocalSubspace( - config_space=cs, - bounds=bounds, - hps_types=types, - bounds_ss_cont=bounds_ss_cont, - bounds_ss_cat=bounds_ss_cat, - model_local=None, - ) - - for hp in subspace.cs_local.get_hyperparameters(): - if isinstance(hp, CategoricalHyperparameter): - # for categorical hps, we set bound as 1, 3, i.e., the 2nd and 4th element should be selected for - # building a subspace - self.assertTrue(hp.choices == ("b", "d")) - - elif isinstance(hp, OrdinalHyperparameter): - # for ordinary hps, we set bound as (1,3), i.e., we select the 2nd, 3rd, 4th values of - # the ordinary sequence - self.assertTrue(hp.sequence, (2, 3, 4)) - elif isinstance(hp, UniformFloatHyperparameter): - if hp.log: - raw_hp_range = (1, 100) - # we map from [0., 1.] to [0.1, 0.5] - raw_hp_range_log = np.log(raw_hp_range) - new_hp_range_log_lower = 0.1 * (raw_hp_range_log[1] - raw_hp_range_log[0]) + raw_hp_range_log[0] - new_hp_range_log_upper = 0.9 * (raw_hp_range_log[1] - raw_hp_range_log[0]) + raw_hp_range_log[0] - - new_range = np.exp([new_hp_range_log_lower, new_hp_range_log_upper]) - np.testing.assert_almost_equal([hp.lower, hp.upper], new_range) - else: - raw_hp_range = (0, 100) - new_hp_range_lower = 0.05 * (raw_hp_range[1] - raw_hp_range[0]) + raw_hp_range[0] - new_hp_range_upper = 0.95 * (raw_hp_range[1] - raw_hp_range[0]) + raw_hp_range[0] - new_range = [new_hp_range_lower, new_hp_range_upper] - - np.testing.assert_almost_equal([hp.lower, hp.upper], new_range) - elif isinstance(hp, UniformIntegerHyperparameter): - if hp.log: - raw_hp_range = (1, 100) - raw_hp_range_log = np.log(raw_hp_range) - new_hp_range_log_lower = 0.1 * (raw_hp_range_log[1] - raw_hp_range_log[0]) + raw_hp_range_log[0] - new_hp_range_log_upper = 0.9 * (raw_hp_range_log[1] - raw_hp_range_log[0]) + raw_hp_range_log[0] - new_range[0] = np.floor(np.exp(new_hp_range_log_lower)) - new_range[1] = np.ceil(np.exp(new_hp_range_log_upper)) - new_range = np.asarray(new_range, dtype=np.int32) - np.testing.assert_equal([hp.lower, hp.upper], new_range) - else: - raw_hp_range = (0, 100) - new_hp_range_lower = 0.05 * (raw_hp_range[1] - raw_hp_range[0]) + raw_hp_range[0] - new_hp_range_upper = 0.95 * (raw_hp_range[1] - raw_hp_range[0]) + raw_hp_range[0] - new_range[0] = np.floor(new_hp_range_lower) - new_range[1] = np.ceil(new_hp_range_upper) - new_range = np.asarray(new_range, dtype=np.int32) - np.testing.assert_equal([hp.lower, hp.upper], new_range) - - def test_cs_subspace_2(self): - # check act_dims - cs = ConfigurationSpace() - hps = generate_cont_hps() - hps.extend(generate_ord_hps()) - hps.extend(generate_cat_hps(2)) - - cs.add_hyperparameters(hps) - - types, bounds = get_types(cs) - bounds_ss_cont = np.array(generate_ss_bounds(cs)) - - bounds_ss_cont = np.array(bounds_ss_cont) - - bounds_ss_cat = [(1, 3), (0, 2)] - - activ_dims = [0, 2, 6] - - subspace = LocalSubspace( - config_space=cs, - bounds=bounds, - hps_types=types, - bounds_ss_cont=bounds_ss_cont, - bounds_ss_cat=bounds_ss_cat, - model_local=None, - activate_dims=activ_dims, - ) - np.testing.assert_equal(subspace.activate_dims_cat, [0]) - np.testing.assert_equal(subspace.activate_dims_cont, [0, 4]) - - hps_global = cs.get_hyperparameters() - hps_local = subspace.cs_local.get_hyperparameters() - for dim_idx, act_dim in enumerate(activ_dims): - self.assertTrue(hps_local[dim_idx].__class__ == hps_global[act_dim].__class__) - - def test_cs_subspace_3(self): - # check if bounds is None works correctly - # check act_dims - cs = ConfigurationSpace() - hps = generate_cont_hps() - hps.extend(generate_ord_hps()) - hps.extend(generate_cat_hps(2)) - - cs.add_hyperparameters(hps) - - types, bounds = get_types(cs) - bounds_ss_cont = np.array(generate_ss_bounds(cs)) - - bounds_ss_cont = np.array(bounds_ss_cont) - bounds_ss_cat = [(1, 3), (0, 2)] - hps_global = cs.get_hyperparameters() - - subspace = LocalSubspace( - config_space=cs, - bounds=bounds, - hps_types=types, - bounds_ss_cont=None, - bounds_ss_cat=bounds_ss_cat, - model_local=None, - ) - hps_local = subspace.cs_local.get_hyperparameters() - for hp_local, hp_global in zip(hps_global, hps_local): - if isinstance(hp_local, CategoricalHyperparameter): - self.assertTrue(hp_local != hp_global) - else: - self.assertTrue(hp_local == hp_global) - - subspace = LocalSubspace( - config_space=cs, - bounds=bounds, - hps_types=types, - bounds_ss_cont=bounds_ss_cont, - bounds_ss_cat=None, - model_local=None, - ) - hps_local = subspace.cs_local.get_hyperparameters() - for hp_local, hp_global in zip(hps_global, hps_local): - if isinstance(hp_local, CategoricalHyperparameter): - self.assertTrue(hp_local == hp_global) - else: - self.assertTrue(hp_local != hp_global) - - subspace = LocalSubspace( - config_space=cs, bounds=bounds, hps_types=types, bounds_ss_cont=None, bounds_ss_cat=None, model_local=None - ) - hps_local = subspace.cs_local.get_hyperparameters() - for hp_local, hp_global in zip(hps_global, hps_local): - self.assertTrue(hp_local == hp_global) - - def test_ss_normalization(self): - cs_global = ConfigurationSpace(1) - hps = generate_cont_hps() - hps.extend(generate_cat_hps(1)) - cs_global.add_hyperparameters(hps) - - types, bounds = get_types(cs_global) - bounds_ss_cont = np.array(generate_ss_bounds(cs_global)) - - bounds_ss_cat = [(1, 3)] - - subspace = LocalSubspace( - config_space=cs_global, - bounds=bounds, - hps_types=types, - bounds_ss_cont=bounds_ss_cont, - bounds_ss_cat=bounds_ss_cat, - model_local=None, - ) - - cs_local = subspace.cs_local - samples_global = cs_global.sample_configuration(20) - X_samples = np.array([sample.get_array() for sample in samples_global]) - X_normalized = subspace.normalize_input(X_samples) - - ss_indices = check_subspace_points( - X=X_normalized, - cont_dims=subspace.activate_dims_cont, - cat_dims=subspace.activate_dims_cat, - bounds_cont=subspace.bounds_ss_cont, - bounds_cat=subspace.bounds_ss_cat, - ) - - ss_indices = np.where(ss_indices)[0] - for ss_idx in ss_indices: - x_normalized = X_normalized[ss_idx] - - sample_local = Configuration(cs_local, vector=x_normalized).get_dictionary() - sample_global = samples_global[ss_idx].get_dictionary() - for key in sample_local.keys(): - if "int" in key: - # There is some numerical issues here for int hps - self.assertLess(sample_local[key] - sample_global[key], 3) - else: - self.assertAlmostEqual(sample_local[key], sample_global[key]) - - def test_add_new_observations(self): - cs_global = ConfigurationSpace(1) - hps = generate_cont_hps() - hps.extend(generate_cat_hps(1)) - cs_global.add_hyperparameters(hps) - - types, bounds = get_types(cs_global) - bounds_ss_cont = np.array(generate_ss_bounds(cs_global)) - - bounds_ss_cat = [(1, 3)] - - subspace = LocalSubspace( - config_space=cs_global, - bounds=bounds, - hps_types=types, - bounds_ss_cont=bounds_ss_cont, - bounds_ss_cat=bounds_ss_cat, - model_local=None, - ) - - samples_global = cs_global.sample_configuration(20) - X_samples = np.array([sample.get_array() for sample in samples_global]) - y_samples = np.ones(np.shape(X_samples)[0]) - - ss_indices = check_subspace_points( - X=X_samples, - cont_dims=subspace.activate_dims_cont, - cat_dims=subspace.activate_dims_cat, - bounds_cont=subspace.bounds_ss_cont, - bounds_cat=subspace.bounds_ss_cat, - ) - - subspace.add_new_observations(X_samples, y_samples) - self.assertEqual(sum(ss_indices), len(subspace.ss_x)) - self.assertEqual(sum(ss_indices), len(subspace.ss_y)) - - self.assertEqual(len(X_samples), len(subspace.model_x)) - self.assertEqual(len(y_samples), len(subspace.model_y)) - - # test if initialization works - subspace_1 = LocalSubspace( - config_space=cs_global, - bounds=bounds, - hps_types=types, - bounds_ss_cont=bounds_ss_cont, - bounds_ss_cat=bounds_ss_cat, - model_local=None, - initial_data=(X_samples, y_samples), - ) - - np.testing.assert_allclose(subspace.ss_x, subspace_1.ss_x) - np.testing.assert_allclose(subspace.ss_y, subspace_1.ss_y) - np.testing.assert_allclose(subspace.model_x, subspace_1.model_x) - np.testing.assert_allclose(subspace.model_y, subspace_1.model_y) - - -@unittest.mock.patch.multiple(LocalSubspace, __abstractmethods__=set()) -class TestChallengerListLocal(unittest.TestCase): - def test_challenger_list_local_full(self): - # check act_dims - cs_global = ConfigurationSpace(1) - hps = generate_cont_hps() - hps.extend(generate_ord_hps()) - hps.extend(generate_cat_hps(2)) - - cs_global.add_hyperparameters(hps) - - types, bounds = get_types(cs_global) - bounds_ss_cont = np.array(generate_ss_bounds(cs_global)) - - bounds_ss_cont = np.array(bounds_ss_cont) - - bounds_ss_cat = [(1, 3), (0, 2)] - - subspace = LocalSubspace( - config_space=cs_global, - bounds=bounds, - hps_types=types, - bounds_ss_cont=bounds_ss_cont, - bounds_ss_cat=bounds_ss_cat, - model_local=None, - ) - cs_local = subspace.cs_local - - num_data = 10 - - rs = np.random.RandomState(1) - - challengers = cs_local.sample_configuration(num_data) - challengers = [(rs.rand(), challenger) for challenger in challengers] - - cl = ChallengerListLocal(cs_local, cs_global, challengers, config_origin="test", incumbent_array=None) - - self.assertEqual(len(cl), num_data) - new_challenger = next(cl).get_dictionary() - challenger_local = challengers[0][1].get_dictionary() - - for key in new_challenger.keys(): - if "int" in key: - # There is some numerical issues here for int hps - self.assertLess(new_challenger[key] - challenger_local[key], 3) - else: - self.assertAlmostEqual(new_challenger[key], challenger_local[key]) - - self.assertEqual(next(cl).origin, "test") - - def test_challenger_list_local_reduced(self): - # check act_dims - cs_global = ConfigurationSpace(1) - hps = generate_cont_hps() - hps.extend(generate_ord_hps()) - hps.extend(generate_cat_hps(2)) - - cs_global.add_hyperparameters(hps) - - types, bounds = get_types(cs_global) - bounds_ss_cont = np.array(generate_ss_bounds(cs_global)) - - bounds_ss_cont = np.array(bounds_ss_cont) - - bounds_ss_cat = [(1, 3), (0, 2)] - - activ_dims = [0, 2, 6] - - subspace = LocalSubspace( - config_space=cs_global, - bounds=bounds, - hps_types=types, - bounds_ss_cont=bounds_ss_cont, - bounds_ss_cat=bounds_ss_cat, - model_local=None, - activate_dims=activ_dims, - ) - - cs_local = subspace.cs_local - - incumbent_array = cs_global.sample_configuration(1).get_array() - - num_data = 10 - - rs = np.random.RandomState(1) - - challengers = cs_local.sample_configuration(num_data) - challengers = [(rs.rand(), challenger) for challenger in challengers] - - cl = ChallengerListLocal( - cs_local, cs_global, challengers, config_origin="test", incumbent_array=incumbent_array - ) - - new_challenger = next(cl) - - for i, hp in enumerate((cs_global.get_hyperparameters())): - if i not in activ_dims: - self.assertAlmostEqual(incumbent_array[i], new_challenger.get_array()[i]) - else: - self.assertTrue(new_challenger.get_dictionary()[hp.name] == challengers[0][1].get_dictionary()[hp.name]) - - def test_exception(self): - cs_local = ConfigurationSpace(1) - hps = generate_cont_hps() - cs_local.add_hyperparameters(hps) - - cs_global = ConfigurationSpace(1) - hps.extend(generate_cat_hps()) - cs_global.add_hyperparameters(hps) - - challengers = cs_local.sample_configuration(5) - challengers = [(0.0, challenger) for challenger in challengers] - self.assertRaisesRegex( - ValueError, - "Incumbent array must be provided if the global configuration space has more " - "hyperparameters then the local configuration space", - ChallengerListLocal, - cs_local, - cs_global, - challengers, - "test", - ) - - def test_add_forbidden_ss(self): - f0 = UniformFloatHyperparameter("f0", 0.0, 100.0) - c0 = CategoricalHyperparameter("c0", [0, 1, 2]) - o0 = OrdinalHyperparameter("o0", [1, 2, 3]) - - i0 = UniformIntegerHyperparameter("i0", 0, 100) - - forbid_1 = ForbiddenEqualsClause(c0, 0) - forbid_2 = ForbiddenInClause(o0, [1, 2]) - - forbid_3 = ForbiddenEqualsClause(f0, 0.3) - forbid_4 = ForbiddenEqualsClause(f0, 59.0) - - forbid_5 = ForbiddenAndConjunction(forbid_2, forbid_3) - forbid_6 = ForbiddenAndConjunction(forbid_1, forbid_4) - forbid_7 = ForbiddenEqualsClause(i0, 10) - - cs_local = ConfigurationSpace() - f0_ss = UniformFloatHyperparameter("f0", 0.0, 50.0) - c0_ss = CategoricalHyperparameter("c0", [0, 1, 2]) - o0_ss = OrdinalHyperparameter("o0", [1, 2, 3]) - cs_local.add_hyperparameters([f0_ss, c0_ss, o0_ss]) - - self.assertIsNotNone(LocalSubspace.fit_forbidden_to_ss(cs_local, forbid_1)) - self.assertIsNotNone(LocalSubspace.fit_forbidden_to_ss(cs_local, forbid_2)) - - self.assertIsNotNone(LocalSubspace.fit_forbidden_to_ss(cs_local, forbid_3)) - self.assertIsNone(LocalSubspace.fit_forbidden_to_ss(cs_local, forbid_4)) - - self.assertIsNotNone(LocalSubspace.fit_forbidden_to_ss(cs_local, forbid_5)) - self.assertIsNone(LocalSubspace.fit_forbidden_to_ss(cs_local, forbid_6)) - - self.assertIsNone(LocalSubspace.fit_forbidden_to_ss(cs_local, forbid_7)) diff --git a/tests/test_local_bo/test_epm_chooser_boing.py b/tests/test_local_bo/test_epm_chooser_boing.py deleted file mode 100644 index f33547e07..000000000 --- a/tests/test_local_bo/test_epm_chooser_boing.py +++ /dev/null @@ -1,283 +0,0 @@ -import unittest - -import numpy as np -import torch -from ConfigSpace import ( - CategoricalHyperparameter, - ConfigurationSpace, - UniformFloatHyperparameter, -) -from gpytorch.constraints.constraints import Interval -from gpytorch.kernels import MaternKernel, ScaleKernel -from gpytorch.likelihoods.gaussian_likelihood import GaussianLikelihood -from gpytorch.priors import HorseshoePrior, LogNormalPrior - -from smac.epm.gaussian_process.augmented import GloballyAugmentedLocalGaussianProcess -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances -from smac.epm.utils import check_subspace_points, get_types -from smac.facade.smac_bb_facade import SMAC4BB -from smac.facade.smac_hpo_facade import SMAC4HPO -from smac.optimizer.configuration_chooser.boing_chooser import ( - BOinGChooser, - subspace_extraction, -) -from smac.runhistory.runhistory import RunHistory -from smac.runhistory.runhistory2epm_boing import RunHistory2EPM4ScaledLogCostWithRaw -from smac.scenario.scenario import Scenario -from smac.tae import StatusType -from smac.utils import test_helpers - - -class TestEPMChooserBOinG(unittest.TestCase): - def setUp(self): - self.scenario = Scenario( - {"cs": test_helpers.get_branin_config_space(), "run_obj": "quality", "output_dir": "data-test_epmchooser"} - ) - self.output_dirs = [] - self.output_dirs.append(self.scenario.output_dir) - - exp_kernel = MaternKernel( - 2.5, - lengthscale_constraint=Interval( - torch.tensor(np.exp(-6.754111155189306).repeat(2)), - torch.tensor(np.exp(0.0858637988771976).repeat(2)), - transform=None, - initial_value=1.0, - ), - ard_num_dims=2, - active_dims=(0, 1), - ).double() - - noise_prior = HorseshoePrior(0.1) - likelihood = GaussianLikelihood( - noise_prior=noise_prior, noise_constraint=Interval(np.exp(-25), np.exp(2), transform=None) - ).double() - - kernel = ScaleKernel( - exp_kernel, - outputscale_constraint=Interval(np.exp(-10.0), np.exp(2.0), transform=None, initial_value=2.0), - outputscale_prior=LogNormalPrior(0.0, 1.0), - ) - - self.model_kwargs = dict(kernel=kernel, likelihood=likelihood) - - def test_init(self): - seed = 42 - config = self.scenario.cs.sample_configuration() - rh = RunHistory() - rh.add(config, 10, 10, StatusType.SUCCESS) - - epm_chooser_kwargs = { - "model_local": GloballyAugmentedLocalGaussianProcess, - "model_local_kwargs": self.model_kwargs, - } - - smbo_kwargs = {"epm_chooser": BOinGChooser, "epm_chooser_kwargs": epm_chooser_kwargs} - - self.assertRaisesRegex( - ValueError, - "BOinG only supports RandomForestWithInstances as its global optimizer", - SMAC4BB, - scenario=self.scenario, - rng=seed, - runhistory=rh, - smbo_kwargs=smbo_kwargs, - runhistory2epm=RunHistory2EPM4ScaledLogCostWithRaw, - ) - self.assertRaisesRegex( - ValueError, - "BOinG only supports RunHistory2EPM4CostWithRaw as its rh transformer", - SMAC4HPO, - scenario=self.scenario, - rng=seed, - runhistory=rh, - smbo_kwargs=smbo_kwargs, - ) - - epm_chooser = SMAC4HPO( - scenario=self.scenario, - rng=seed, - runhistory=rh, - smbo_kwargs=smbo_kwargs, - runhistory2epm=RunHistory2EPM4ScaledLogCostWithRaw, - ).solver.epm_chooser - self.assertFalse(hasattr(epm_chooser, "turbo_optimizer")) - - epm_chooser_kwargs.update({"do_switching": True}) - epm_chooser = SMAC4HPO( - scenario=self.scenario, - rng=seed, - runhistory=rh, - smbo_kwargs=smbo_kwargs, - runhistory2epm=RunHistory2EPM4ScaledLogCostWithRaw, - ).solver.epm_chooser - self.assertTrue(hasattr(epm_chooser, "turbo_optimizer")) - - def test_choose_next(self): - seed = 42 - config = self.scenario.cs.sample_configuration() - rh = RunHistory() - rh.add(config, 10, 10, StatusType.SUCCESS) - - epm_chooser_kwargs = { - "model_local": GloballyAugmentedLocalGaussianProcess, - "model_local_kwargs": self.model_kwargs, - } - - smbo_kwargs = {"epm_chooser": BOinGChooser, "epm_chooser_kwargs": epm_chooser_kwargs} - - epm_chooser = SMAC4HPO( - scenario=self.scenario, - rng=seed, - runhistory=rh, - smbo_kwargs=smbo_kwargs, - runhistory2epm=RunHistory2EPM4ScaledLogCostWithRaw, - ).solver.epm_chooser - x = next(epm_chooser.choose_next()) - # when number of points is not large enough for building a subspace, GP works locally - self.assertEqual(x.origin, "Local Search") - for i in range(15): - config = self.scenario.cs.sample_configuration() - rh.add(config, 10, 10, StatusType.SUCCESS) - - x = next(epm_chooser.choose_next()) - # when number of points is already large enough for building a subspace, BOinG takes over - self.assertEqual(x.origin, "BOinG") - - epm_chooser_kwargs.update({"do_switching": True}) - epm_chooser = SMAC4HPO( - scenario=self.scenario, - rng=seed, - runhistory=rh, - smbo_kwargs=smbo_kwargs, - runhistory2epm=RunHistory2EPM4ScaledLogCostWithRaw, - ).solver.epm_chooser - epm_chooser.run_TuRBO = True - x = next(epm_chooser.choose_next()) - self.assertEqual(x.origin, "TuRBO") - - def test_do_switching(self): - seed = 42 - - config = self.scenario.cs.sample_configuration() - rh = RunHistory() - rh.add(config, 10, 10, StatusType.SUCCESS) - - epm_chooser_kwargs = { - "model_local": GloballyAugmentedLocalGaussianProcess, - "model_local_kwargs": self.model_kwargs, - "do_switching": True, - } - turbo_kwargs = {"failure_tol_min": 1, "length_min": 0.6} - epm_chooser_kwargs.update({"turbo_kwargs": turbo_kwargs}) - - smbo_kwargs = {"epm_chooser": BOinGChooser, "epm_chooser_kwargs": epm_chooser_kwargs} - - epm_chooser = SMAC4HPO( - scenario=self.scenario, - rng=seed, - runhistory=rh, - smbo_kwargs=smbo_kwargs, - runhistory2epm=RunHistory2EPM4ScaledLogCostWithRaw, - ).solver.epm_chooser - - for i in range(15): - config = self.scenario.cs.sample_configuration() - rh.add(config, 10, 10, StatusType.SUCCESS) - config = self.scenario.cs.sample_configuration() - # ensure config is the incumbent - rh.add(config, 9.99, 10, StatusType.SUCCESS) - next(epm_chooser.choose_next()) - - # init an optimal config - np.testing.assert_allclose(config.get_array(), epm_chooser.optimal_config) - self.assertAlmostEqual(9.99, epm_chooser.optimal_value) - self.assertEqual(0, epm_chooser.failcount_BOinG) - - epm_chooser.failcount_BOinG = 19 - # in this case, prob_to_TurBO becomes 1 - with unittest.mock.patch("smac.optimizer.configuration_chooser.boing_chooser.BOinGChooser." - "restart_TuRBOinG") as mk: - next(epm_chooser.choose_next()) - self.assertTrue(epm_chooser.run_TuRBO) - self.assertTrue(mk.called) - - # switch to TuRBO - for i in range(1000): - next(epm_chooser.choose_next()) - if not epm_chooser.run_TuRBO: - break - # TuRBO will be replaced with BOinG if it cannot find a better value conintuously - self.assertLess(i, 999) - - epm_chooser.failcount_BOinG = 19 - next(epm_chooser.choose_next()) - - config = self.scenario.cs.sample_configuration() - rh.add(config, 9.5, 10, StatusType.SUCCESS) - epm_chooser.turbo_optimizer.init_configs = [] - - for i in range(10): - next(epm_chooser.choose_next()) - if not epm_chooser.run_TuRBO: - break - # one time success and two times failure totally 3 times evaluations and in this case we have i==2 - self.assertEqual(i, 2) - - -class TestSubSpaceExtraction(unittest.TestCase): - def test_subspace_extraction(self): - cs = ConfigurationSpace(0) - cs.add_hyperparameter(UniformFloatHyperparameter("x0", 0.0, 1.0)) - cs.add_hyperparameter(CategoricalHyperparameter("x1", [0, 1, 2, 3, 4, 5])) - - types, bounds = get_types(cs) - rf = RandomForestWithInstances( - cs, - types=types, - bounds=bounds, - seed=0, - num_trees=10, - ratio_features=1.0, - min_samples_split=2, - min_samples_leaf=1, - ) - - X = np.array([[0.0, 0], [0.2, 1], [0.3, 2], [0.7, 5], [0.6, 3]]) - - Y = np.array([0.1, 0.2, 0.7, 0.6, 0.5]) - - X_inc = np.array([0.4, 3]) - rf.train(X, Y) - - ss_extraction_kwargs = dict(X=X, challenger=X_inc, model=rf, bounds=bounds, cat_dims=[1], cont_dims=[0]) - - num_min = 2 - num_max = 5 - - ss_bounds_cont, ss_bounds_cat, ss_indices = subspace_extraction( - num_min=num_min, num_max=np.inf, **ss_extraction_kwargs - ) - self.assertTrue(num_min <= sum(ss_indices)) - x_in_ss = check_subspace_points(X_inc, [0], [1], ss_bounds_cont, ss_bounds_cat) - self.assertTrue(x_in_ss[0]) - ss_indices_re_exam = check_subspace_points(X, [0], [1], ss_bounds_cont, ss_bounds_cat) - self.assertEqual(sum(ss_indices), sum(ss_indices_re_exam)) - - ss_bounds_cont, ss_bounds_cat, ss_indices = subspace_extraction( - num_min=num_min, num_max=num_max, **ss_extraction_kwargs - ) - self.assertTrue(num_min <= sum(ss_indices) <= num_max) - x_in_ss = check_subspace_points(X_inc, [0], [1], ss_bounds_cont, ss_bounds_cat) - self.assertTrue(x_in_ss[0]) - ss_indices_re_exam = check_subspace_points(X, [0], [1], ss_bounds_cont, ss_bounds_cat) - self.assertEqual(sum(ss_indices), sum(ss_indices_re_exam)) - - num_max = 3 - ss_bounds_cont, ss_bounds_cat, ss_indices = subspace_extraction( - num_min=num_min, num_max=num_max, **ss_extraction_kwargs - ) - self.assertTrue(num_min <= sum(ss_indices) <= num_max) - self.assertTrue(x_in_ss[0]) - ss_indices_re_exam = check_subspace_points(X, [0], [1], ss_bounds_cont, ss_bounds_cat) - self.assertEqual(sum(ss_indices), sum(ss_indices_re_exam)) diff --git a/tests/test_local_bo/test_epm_chooser_turbo.py b/tests/test_local_bo/test_epm_chooser_turbo.py deleted file mode 100644 index 11994fb9e..000000000 --- a/tests/test_local_bo/test_epm_chooser_turbo.py +++ /dev/null @@ -1,41 +0,0 @@ -import unittest - -import numpy as np - -from smac.facade.smac_bb_facade import SMAC4BB -from smac.optimizer.configuration_chooser.turbo_chooser import TurBOChooser -from smac.runhistory.runhistory import RunHistory -from smac.scenario.scenario import Scenario -from smac.tae import StatusType -from smac.utils import test_helpers - - -class TestEPMChooserTuRBO(unittest.TestCase): - def setUp(self): - self.scenario = Scenario( - {"cs": test_helpers.get_branin_config_space(), "run_obj": "quality", "output_dir": "data-test_epmchooser"} - ) - self.output_dirs = [] - self.output_dirs.append(self.scenario.output_dir) - - def test_choose_next(self): - config = self.scenario.cs.sample_configuration() - rh = RunHistory() - rh.add(config, 10, 10, StatusType.SUCCESS) - smbo = SMAC4BB( - scenario=self.scenario, - rng=np.random.RandomState(42), - model_type="gp", - smbo_kwargs={"epm_chooser": TurBOChooser}, - initial_design_kwargs={"init_budget": 0}, - runhistory=rh, - ).solver - - x = next(smbo.epm_chooser.choose_next()).get_array() - self.assertEqual(x.shape, (2,)) - - # remove the init configs - smbo.epm_chooser.turbo.init_configs = [] - x = next(smbo.epm_chooser.choose_next()).get_array() - - self.assertEqual(x.shape, (2,)) diff --git a/tests/test_local_bo/test_rh2epm_boing.py b/tests/test_local_bo/test_rh2epm_boing.py deleted file mode 100644 index 8c7959e79..000000000 --- a/tests/test_local_bo/test_rh2epm_boing.py +++ /dev/null @@ -1,62 +0,0 @@ -import numpy as np - -from smac.runhistory.runhistory2epm import ( - RunHistory2EPM4Cost, - RunHistory2EPM4LogScaledCost, -) -from smac.runhistory.runhistory2epm_boing import ( - RunHistory2EPM4CostWithRaw, - RunHistory2EPM4ScaledLogCostWithRaw, -) -from smac.tae import StatusType - -from tests.test_runhistory.test_runhistory2epm import RunhistoryTest - - -class TestRH2EPMBOinG(RunhistoryTest): - def test_cost_without_imputation(self): - rh2epm_kwargs = dict( - num_params=2, - success_states=[StatusType.SUCCESS, StatusType.CRASHED, StatusType.MEMOUT], - impute_censored_data=False, - scenario=self.scen, - ) - rh2epm = RunHistory2EPM4Cost(**rh2epm_kwargs) - rh2epm_log = RunHistory2EPM4LogScaledCost(**rh2epm_kwargs) - - rh2epm_with_raw = RunHistory2EPM4CostWithRaw(**rh2epm_kwargs) - - rh2epm_log_with_raw = RunHistory2EPM4ScaledLogCostWithRaw(**rh2epm_kwargs) - - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=23, - seed=None, - additional_info=None, - ) - - # rh2epm should use cost and not time field later - self.rh.add( - config=self.config3, - cost=200, - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - additional_info=None, - ) - - _, y = rh2epm.transform(self.rh) - _, y_log = rh2epm_log.transform(self.rh) - _, y_raw_transformed, y_raw_ = rh2epm_with_raw.transform_with_raw(self.rh) - _, y_log_transformed, y_log_raw = rh2epm_log_with_raw.transform_with_raw(self.rh) - # all are the raw runhistory values - - raw_values = [y_raw_transformed, y_raw_, y_log_raw] - for raw_value in raw_values: - np.testing.assert_array_equal(raw_value, y) - - np.testing.assert_equal(y_log, y_log_transformed) diff --git a/tests/test_local_bo/test_subspace_boing.py b/tests/test_local_bo/test_subspace_boing.py deleted file mode 100644 index f8de2926c..000000000 --- a/tests/test_local_bo/test_subspace_boing.py +++ /dev/null @@ -1,68 +0,0 @@ -import unittest - -import numpy as np -from ConfigSpace import ConfigurationSpace -from ConfigSpace.hyperparameters import UniformFloatHyperparameter -from gpytorch.kernels import MaternKernel, ScaleKernel - -from smac.epm.gaussian_process.augmented import GloballyAugmentedLocalGaussianProcess -from smac.epm.utils import get_types -from smac.optimizer.acquisition import EI -from smac.optimizer.acquisition.maximizer import LocalAndSortedRandomSearch -from smac.optimizer.subspaces import ChallengerListLocal -from smac.optimizer.subspaces.boing_subspace import BOinGSubspace - - -def generate_data(num_data, rs: np.random.RandomState): - x = rs.rand(num_data, 1) - y = rs.rand(num_data) - return x, y - - -class TestBOinGSubspace(unittest.TestCase): - def setUp(self) -> None: - self.cs = ConfigurationSpace() - self.cs.add_hyperparameter(UniformFloatHyperparameter("x0", 0, 1, 0.5)) - - self.model_local = GloballyAugmentedLocalGaussianProcess - cont_dims = [0] - exp_kernel = MaternKernel(2.5, ard_num_dims=1, active_dims=tuple(cont_dims)).double() - - kernel = ScaleKernel(exp_kernel) - self.model_local_kwargs = {"kernel": kernel} - self.types, self.bounds = get_types(self.cs) - self.acq_local = EI - self.ss_kwargs = dict( - config_space=self.cs, - bounds=self.bounds, - hps_types=self.types, - model_local=self.model_local, - model_local_kwargs=self.model_local_kwargs, - ) - - def test_init(self): - boing_ss_1 = BOinGSubspace(**self.ss_kwargs) - self.assertEqual(boing_ss_1.model.num_inducing_points, 2) - self.assertIsInstance(boing_ss_1.acq_optimizer_local, LocalAndSortedRandomSearch) - self.assertEqual(boing_ss_1.acq_optimizer_local.n_sls_iterations, 10) - self.assertEqual(boing_ss_1.acq_optimizer_local.local_search.n_steps_plateau_walk, 5) - - acq_optimiozer = LocalAndSortedRandomSearch( - acquisition_function=None, config_space=self.cs, n_steps_plateau_walk=10, n_sls_iterations=10 - ) - - boing_ss_2 = BOinGSubspace(**self.ss_kwargs, acq_optimizer_local=acq_optimiozer) - self.assertEqual(boing_ss_2.acq_optimizer_local.n_sls_iterations, 10) - self.assertEqual(boing_ss_2.acq_optimizer_local.local_search.n_steps_plateau_walk, 10) - - def test_generate_challangers(self): - rs = np.random.RandomState(1) - init_data = generate_data(10, rs) - boing_ss = BOinGSubspace(**self.ss_kwargs, initial_data=init_data) - challenge = boing_ss.generate_challengers() - self.assertIsInstance(challenge, ChallengerListLocal) - eval_next = next(challenge) - acq_value_challenge = boing_ss.acquisition_function([eval_next]) - acq_value_init_points = boing_ss.acquisition_function._compute(init_data[0]) - for acq_init in acq_value_init_points: - self.assertLess(acq_init, acq_value_challenge) diff --git a/tests/test_local_bo/test_turbo_subspace.py b/tests/test_local_bo/test_turbo_subspace.py deleted file mode 100644 index de057d40c..000000000 --- a/tests/test_local_bo/test_turbo_subspace.py +++ /dev/null @@ -1,185 +0,0 @@ -import copy -import unittest - -import numpy as np -from ConfigSpace import ConfigurationSpace -from ConfigSpace.conditions import GreaterThanCondition -from ConfigSpace.hyperparameters import ( - CategoricalHyperparameter, - UniformFloatHyperparameter, -) - -from smac.epm.gaussian_process import GaussianProcess -from smac.epm.gaussian_process.kernels import ConstantKernel, Matern, WhiteKernel -from smac.epm.utils import get_types -from smac.optimizer.acquisition import TS -from smac.optimizer.subspaces.turbo_subspace import TuRBOSubSpace - - -class TestTurBoSubspace(unittest.TestCase): - def setUp(self) -> None: - self.cs = ConfigurationSpace() - self.cs.add_hyperparameter(UniformFloatHyperparameter("x0", 0, 1, 0.5)) - self.model_local = GaussianProcess - exp_kernel = Matern(nu=2.5) - cov_amp = ConstantKernel( - 2.0, - ) - noise_kernel = WhiteKernel(1e-8) - kernel = cov_amp * exp_kernel + noise_kernel - self.types, self.bounds = get_types(self.cs) - self.model_local_kwargs = {"kernel": kernel} - self.acq_local = TS - self.ss_kwargs = dict( - config_space=self.cs, - bounds=self.bounds, - hps_types=self.types, - model_local=self.model_local, - model_local_kwargs=self.model_local_kwargs, - ) - - def test_init(self): - ss = TuRBOSubSpace(**self.ss_kwargs) - self.assertEqual(len(ss.init_configs), ss.n_init) - ss_init_configs = copy.deepcopy(ss.init_configs) - self.assertEqual(ss.num_valid_observations, 0) - - # init configurations are poped - for i in reversed(range(len(ss_init_configs))): - eval_next = next(ss.generate_challengers()) - self.assertEqual(eval_next, ss_init_configs[i]) - - cs_mix = ConfigurationSpace() - cs_mix.add_hyperparameter(UniformFloatHyperparameter("x0", 0, 1, 0.5)) - cs_mix.add_hyperparameter(CategoricalHyperparameter("x1", [0, 1, 2])) - - self.assertRaisesRegex( - ValueError, - "Current TurBO Optimizer only supports Numerical Hyperparameters", - TuRBOSubSpace, - config_space=cs_mix, - bounds=None, - hps_types=None, - model_local=None, - ) - - x0 = UniformFloatHyperparameter("x0", 0, 1, 0.5) - x1 = UniformFloatHyperparameter("x1", 0, 1, 0.5) - - cs_condition = ConfigurationSpace() - cs_condition.add_hyperparameters([x0, x1]) - - cs_condition.add_condition(GreaterThanCondition(x0, x1, 0.5)) - self.assertRaisesRegex( - ValueError, - "Currently TurBO does not support Conditional or Forbidden Hyperparameters", - TuRBOSubSpace, - config_space=cs_condition, - bounds=None, - hps_types=None, - model_local=None, - ) - - def test_adjust_length(self): - ss = TuRBOSubSpace(**self.ss_kwargs) - ss.add_new_observations(np.array([0.5]), np.array([0.5])) - - self.assertEqual(ss.num_valid_observations, 1) - - success_tol = ss.success_tol - failure_tol = ss.failure_tol - length = ss.length - - for i in range(success_tol): - ss.adjust_length(0.3 - i * 0.01) - self.assertGreater(ss.length, length) - - # make sure that length cannot be greater than length_max - for i in range(100): - ss.adjust_length(0.3 - i * 0.01) - self.assertLessEqual(ss.length, ss.length_max) - - length = ss.length - for i in range(failure_tol): - ss.adjust_length(0.5 + i * 0.01) - self.assertLessEqual(ss.length, length / 2) - - @unittest.mock.patch.object(GaussianProcess, "predict") - def test_restart(self, rf_mock): - ss = TuRBOSubSpace(**self.ss_kwargs) - ss.add_new_observations(np.array([0.5]), np.array([0.5])) - ss.init_configs = [] - - ss.length = 0.0 - challenge = ss.generate_challengers() - - self.assertEqual(ss.length, ss.length_init) - self.assertGreater(len(ss.init_configs), 0) - - eval_next = next(challenge) - self.assertTrue(eval_next.origin == "TuRBO") - self.assertEqual(rf_mock.call_count, 0) - - def test_perturb_samples(self): - ss = TuRBOSubSpace(**self.ss_kwargs, incumbent_array=np.array([2.0])) - - prob = 0.0 - perturb_sample = ss._perturb_samples(prob, np.random.rand(ss.n_candidates, ss.n_dims)) - # make sure that no new suggestion is replaced by the incumbent - self.assertEqual(len(np.where(perturb_sample == 2.0)[0]), 0) - - prob = 1.0 - perturb_sample = ss._perturb_samples(prob, np.random.rand(ss.n_candidates, ss.n_dims)) - self.assertEqual(len(np.where(perturb_sample == 2.0)[0]), 0) - - cs = ConfigurationSpace() - cs.add_hyperparameter(UniformFloatHyperparameter("x0", 0, 1, 0.5)) - cs.add_hyperparameter(UniformFloatHyperparameter("x1", 0, 1, 0.5)) - model_local = GaussianProcess - exp_kernel = Matern(nu=2.5) - cov_amp = ConstantKernel( - 2.0, - ) - noise_kernel = WhiteKernel(1e-8) - kernel = cov_amp * exp_kernel + noise_kernel - types, bounds = get_types(cs) - model_local_kwargs = {"kernel": kernel} - - ss = TuRBOSubSpace( - config_space=cs, - bounds=bounds, - hps_types=types, - model_local=model_local, - model_local_kwargs=model_local_kwargs, - incumbent_array=np.array([2.0, 2.0]), - ) - - prob = 0.0 - perturb_sample = ss._perturb_samples(prob, np.random.rand(ss.n_candidates, ss.n_dims)) - - idx_from_incumbent = np.transpose(perturb_sample == 2.0) - self.assertTrue(np.all(np.sum(idx_from_incumbent, axis=1)) < 2) - - prob = 1.0 - perturb_sample = ss._perturb_samples(prob, np.random.rand(ss.n_candidates, ss.n_dims)) - - idx_from_incumbent = np.transpose(perturb_sample == 2.0) - self.assertEqual(len(np.where(perturb_sample == 2.0)[0]), 0) - - def test_suggestion(self): - num_init_points = 5 - ss = TuRBOSubSpace(**self.ss_kwargs, incumbent_array=np.array([0.5])) - ss.length = 0.1 - ss.init_configs = [] - new_data_x = np.vstack([np.random.rand(num_init_points, 1), np.array([[0.5]])]) - new_data_y = np.vstack([np.random.rand(num_init_points, 1), np.array([[-0.1]])]) - ss.add_new_observations(new_data_x, new_data_y) - challengers = ss._generate_challengers() - - challenger_arrays = np.asarray([challenger[1].get_array() for challenger in challengers]) - # suggestions are constrained - self.assertTrue(np.all(0.4 < challenger_arrays) and np.all(challenger_arrays < 0.6)) - - challengers = ss._generate_challengers(_sorted=False) - challenger_acq_values = np.asarray([challenger[0] for challenger in challengers]) - np.testing.assert_equal(0.0, challenger_acq_values) diff --git a/smac/intensification/__init__.py b/tests/test_main/__init__.py similarity index 100% rename from smac/intensification/__init__.py rename to tests/test_main/__init__.py diff --git a/tests/test_main/_test_boing.py b/tests/test_main/_test_boing.py new file mode 100644 index 000000000..795670f53 --- /dev/null +++ b/tests/test_main/_test_boing.py @@ -0,0 +1,202 @@ +from unittest.mock import patch + +import numpy as np +import pytest +from ConfigSpace import ( + CategoricalHyperparameter, + ConfigurationSpace, + UniformFloatHyperparameter, +) + +import smac +from smac.facade.blackbox_facade import BlackBoxFacade +from smac.facade.boing_facade import BOinGFacade +from smac.facade.hyperparameter_optimization_facade import ( + HyperparameterOptimizationFacade, +) +from smac.main.boing import subspace_extraction +from smac.model.random_forest.random_forest import RandomForest +from smac.model.utils import check_subspace_points, get_types +from smac.runhistory.runhistory import RunHistory +from smac.runner.abstract_runner import StatusType +from smac.utils import _test_helpers + + +def test_init(make_scenario): + scenario = make_scenario( + configspace=_test_helpers.get_branin_config_space(), + ) + tae = lambda x: x + with pytest.raises(ValueError) as excinfo: + BOinGFacade(scenario=scenario, target_function=tae, model=BlackBoxFacade.get_model(scenario)) + assert excinfo.values == "BOinG only supports RandomForestWithInstances as its global optimizer" + + with pytest.raises(ValueError) as excinfo: + BOinGFacade( + scenario=scenario, + target_function=tae, + runhistory_encoder=HyperparameterOptimizationFacade.get_runhistory_encoder(scenario), + ) + assert excinfo.values == "BOinG only supports RunHistory2EPM4CostWithRaw as its rh transformer" + + facade = BOinGFacade(scenario=scenario, target_function=tae, overwrite=True) + assert not hasattr(facade._optimizer, "turbo_optimizer") + + # facade.do_switching = True + # facade._init_optimizer() + # assert hasattr(facade.optimizer, "turbo_optimizer") + + +def test_chooser_next(make_scenario): + configspace = _test_helpers.get_branin_config_space() + scenario = make_scenario( + configspace=configspace, + ) + config = scenario.configspace.sample_configuration() + rh = RunHistory() + rh.add(config, 10, 10, StatusType.SUCCESS) + tae = lambda x: x + facade = BOinGFacade(scenario=scenario, runhistory=rh, target_function=tae, do_switching=False, overwrite=True) + optimizer = facade.optimizer + + x = next(optimizer.ask()) + # when number of points is not large enough for building a subspace, GP works locally + assert x.origin == "Local Search" + for i in range(15): + config = scenario.configspace.sample_configuration() + rh.add(config, 10, 10, StatusType.SUCCESS) + + x = next(optimizer.ask()) + # when number of points is already large enough for building a subspace, BOinG takes over + assert x.origin == "BOinG" + + facade.do_switching = True + facade._init_optimizer() + optimizer = facade.optimizer + optimizer.run_TuRBO = True + x = next(optimizer.ask()) + assert x.origin == "TuRBO" + + +def test_do_switching(make_scenario): + seed = 42 + configspace = _test_helpers.get_branin_config_space() + scenario = make_scenario( + configspace=configspace, + ) + config = scenario.configspace.sample_configuration() + rh = RunHistory() + rh.add(config, 10, 10, StatusType.SUCCESS) + tae = lambda x: x + turbo_kwargs = {"failure_tol_min": 1, "length_min": 0.6} + + facade = BOinGFacade( + scenario=scenario, + runhistory=rh, + target_function=tae, + do_switching=True, + turbo_kwargs=turbo_kwargs, + overwrite=True, + ) + optimizer = facade.optimizer + + for i in range(15): + config = scenario.configspace.sample_configuration() + rh.add(config, 10, 10, StatusType.SUCCESS) + config = scenario.configspace.sample_configuration() + # ensure config is the incumbent + rh.add(config, 9.99, 10, StatusType.SUCCESS) + next(optimizer.ask()) + + # init an optimal config + np.testing.assert_allclose(config.get_array(), optimizer.optimal_config) + assert optimizer.optimal_value == 9.99 + assert 0 == optimizer.failcount_BOinG + + optimizer.failcount_BOinG = 19 + # in this case, prob_to_TurBO becomes 1 + with patch("smac.main.boing.BOinGSMBO." "restart_TuRBOinG") as mk: + next(optimizer.ask()) + assert optimizer.run_TuRBO is True + assert mk.called is True + + # switch to TuRBO + from smac.utils.subspaces.turbo_subspace import TuRBOSubSpace + + for i in range(1000): + with patch.object(smac.utils.subspaces.turbo_subspace.TuRBOSubSpace, "generate_challengers", return_value=None): + optimizer.ask() + while len(optimizer.turbo_optimizer.init_configs) > 0: + optimizer.turbo_optimizer.init_configs.pop() + if not optimizer.run_TuRBO: + break + # TuRBO will be replaced with BOinG if it cannot find a better value continuously + assert i < 999 + + optimizer.failcount_BOinG = 19 + next(optimizer.ask()) + + config = scenario.configspace.sample_configuration() + rh.add(config, 9.5, 10, StatusType.SUCCESS) + optimizer.turbo_optimizer.init_configs = [] + for i in range(10): + next(optimizer.ask()) + if not optimizer.run_TuRBO: + break + # one time success and two times failure totally, 3 times evaluations and in this case we have i==2 + assert i == 2 + + +def test_subspace_extraction(): + cs = ConfigurationSpace(0) + cs.add_hyperparameter(UniformFloatHyperparameter("x0", 0.0, 1.0)) + cs.add_hyperparameter(CategoricalHyperparameter("x1", [0, 1, 2, 3, 4, 5])) + + rf = RandomForest( + cs, + num_trees=10, + ratio_features=1.0, + min_samples_split=2, + min_samples_leaf=1, + seed=0, + ) + + X = np.array([[0.0, 0], [0.2, 1], [0.3, 2], [0.7, 5], [0.6, 3]]) + + Y = np.array([0.1, 0.2, 0.7, 0.6, 0.5]) + + X_inc = np.array([0.4, 3]) + rf.train(X, Y) + _, bounds = get_types(cs) + + ss_extraction_kwargs = dict(X=X, challenger=X_inc, model=rf, bounds=bounds, cat_dims=[1], cont_dims=[0]) + + num_min = 2 + num_max = 5 + + ss_bounds_cont, ss_bounds_cat, ss_indices = subspace_extraction( + num_min=num_min, num_max=np.inf, **ss_extraction_kwargs + ) + assert num_min <= sum(ss_indices) + x_in_ss = check_subspace_points(X_inc, [0], [1], ss_bounds_cont, ss_bounds_cat) + assert x_in_ss[0] + ss_indices_re_exam = check_subspace_points(X, [0], [1], ss_bounds_cont, ss_bounds_cat) + assert sum(ss_indices) == sum(ss_indices_re_exam) + + ss_bounds_cont, ss_bounds_cat, ss_indices = subspace_extraction( + num_min=num_min, num_max=num_max, **ss_extraction_kwargs + ) + assert num_min <= sum(ss_indices) <= num_max + x_in_ss = check_subspace_points(X_inc, [0], [1], ss_bounds_cont, ss_bounds_cat) + assert x_in_ss[0] + ss_indices_re_exam = check_subspace_points(X, [0], [1], ss_bounds_cont, ss_bounds_cat) + assert sum(ss_indices) == sum(ss_indices_re_exam) + + num_max = 3 + ss_bounds_cont, ss_bounds_cat, ss_indices = subspace_extraction( + num_min=num_min, num_max=num_max, **ss_extraction_kwargs + ) + assert num_min <= sum(ss_indices) <= num_max + assert x_in_ss[0] + ss_indices_re_exam = check_subspace_points(X, [0], [1], ss_bounds_cont, ss_bounds_cat) + assert sum(ss_indices) == sum(ss_indices_re_exam) diff --git a/tests/test_main/_test_turbo.py b/tests/test_main/_test_turbo.py new file mode 100644 index 000000000..fd9bdda5a --- /dev/null +++ b/tests/test_main/_test_turbo.py @@ -0,0 +1,65 @@ +import unittest + +import numpy as np + +from smac.facade.blackbox_facade import BlackBoxFacade +from smac.main.turbo import TuRBOSMBO +from smac.runhistory.runhistory import RunHistory +from smac.runner.abstract_runner import StatusType +from smac.scenario import Scenario +from smac.utils import _test_helpers + + +class TuRBOFacade(BlackBoxFacade): + """A wrapper that allows to run TuRBO optimizer. Its arguments are described under smac.main.turbo.TuRBOSMBO""" + + def _init_optimizer( + self, + length_init=0.8, + length_min=0.5**8, + length_max=1.6, + success_tol=3, + failure_tol_min=4, + n_init_x_params=2, + n_candidate_max=5000, + ) -> None: + self.optimizer = TuRBOSMBO( + length_init=length_init, + length_min=length_min, + length_max=length_max, + success_tol=success_tol, + failure_tol_min=failure_tol_min, + n_init_x_params=n_init_x_params, + n_candidate_max=n_candidate_max, + scenario=self._scenario, + stats=self.stats, + runner=self.runner, + initial_design=self.initial_design, + runhistory=self.runhistory, + runhistory_encoder=self.runhistory_encoder, + intensifier=self.intensifier, + model=self.model, + acquisition_function=self.acquisition_function, + acquisition_optimizer=self.acquisition_optimizer, + random_design=self.random_design, + seed=self.seed, + ) + + +def test_choose_next(make_scenario): + cs = _test_helpers.get_branin_config_space() + config = cs.sample_configuration() + scenario = make_scenario(cs) + rh = RunHistory() + rh.add(config, 10, 10, StatusType.SUCCESS) + tae = lambda x: x + smbo = TuRBOFacade(scenario=scenario, target_function=tae, runhistory=rh, overwrite=True).optimizer + + x = next(smbo.ask()).get_array() + assert x.shape == (2,) + + # remove the init configs + smbo.turbo.init_configs = [] + x = next(smbo.ask()).get_array() + + assert x.shape == (2,) diff --git a/tests/test_main/test_smbo.py b/tests/test_main/test_smbo.py new file mode 100644 index 000000000..05e4e5b2b --- /dev/null +++ b/tests/test_main/test_smbo.py @@ -0,0 +1,57 @@ +import pytest + +from smac import HyperparameterOptimizationFacade, MultiFidelityFacade, Scenario + + +def test_termination_cost_threshold(rosenbrock): + termination_cost_threshold = 100 + scenario = Scenario(rosenbrock.configspace, n_trials=200, termination_cost_threshold=termination_cost_threshold) + smac = HyperparameterOptimizationFacade( + scenario, + rosenbrock.train, + intensifier=HyperparameterOptimizationFacade.get_intensifier(scenario, max_config_calls=1), + overwrite=True, + ) + i = smac.optimize() + + counter = 0 + config = None + for k, v in smac.runhistory.items(): + if v.cost < termination_cost_threshold: + config = smac.runhistory.get_config(k.config_id) + counter += 1 + + # We expect only one cost below termination_cost_threshold + assert config == i + assert counter == 1 + assert smac.validate(i) < termination_cost_threshold + + +def test_termination_cost_threshold_with_fidelities(rosenbrock): + max_budget = 9 + termination_cost_threshold = 100 + scenario = Scenario( + rosenbrock.configspace, + n_trials=200, + min_budget=1, + max_budget=max_budget, + termination_cost_threshold=termination_cost_threshold, + ) + smac = MultiFidelityFacade( + scenario, + rosenbrock.train, + overwrite=True, + ) + i = smac.optimize() + + counter = 0 + config = None + for c in smac.runhistory.get_configs(): + if smac.runhistory.get_cost(c) < termination_cost_threshold: + config = c + counter += 1 + + # We expect only one cost below termination_cost_threshold + assert config == i + assert counter == 1 + assert smac.validate(i) < termination_cost_threshold diff --git a/smac/optimizer/__init__.py b/tests/test_model/__init__.py similarity index 100% rename from smac/optimizer/__init__.py rename to tests/test_model/__init__.py diff --git a/tests/test_epm/test_boing_kernel.py b/tests/test_model/_test_boing_kernel.py similarity index 98% rename from tests/test_epm/test_boing_kernel.py rename to tests/test_model/_test_boing_kernel.py index cbee007f5..2effe4be2 100644 --- a/tests/test_epm/test_boing_kernel.py +++ b/tests/test_model/_test_boing_kernel.py @@ -13,8 +13,8 @@ from gpytorch.means.zero_mean import ZeroMean from gpytorch.models.exact_gp import ExactGP -from smac.epm.gaussian_process.kernels.boing import FITCKernel, FITCMean -from smac.epm.utils import check_subspace_points +from smac.model.gaussian_process.kernels._boing import FITCKernel, FITCMean +from smac.model.utils import check_subspace_points class FITC(ExactGP): diff --git a/tests/test_epm/test_gp_gpytorch.py b/tests/test_model/_test_gp_gpytorch.py similarity index 96% rename from tests/test_epm/test_gp_gpytorch.py rename to tests/test_model/_test_gp_gpytorch.py index 6cc5bfe4d..95f3a59d8 100644 --- a/tests/test_epm/test_gp_gpytorch.py +++ b/tests/test_model/_test_gp_gpytorch.py @@ -17,9 +17,11 @@ ConfigurationSpace, UniformFloatHyperparameter, ) -from smac.epm.gaussian_process.gpytorch import GPyTorchGaussianProcess +from smac.model.gaussian_process.gpytorch_gaussian_process import ( + GPyTorchGaussianProcess, +) -from .test_gp import TestGP, get_cat_data, get_cont_data +from ._test_gp import TestGP, get_cat_data, get_cont_data torch.manual_seed(0) pyro.set_rng_seed(0) @@ -146,7 +148,7 @@ def test_gp_model(self): self.assertEqual(model.is_trained, False) self.assertEqual(bool(model.property_dict), False) - mll = model._get_gp(X, Y) + mll = model._get_gaussian_process(X, Y) self.assertIsInstance(mll, ExactMarginalLogLikelihood) self.assertIsInstance(mll.model, ExactGP) @@ -234,7 +236,7 @@ def __call__(self, train_inputs, train_targets, likelihood): with self.assertRaises(RuntimeError): model._train(X[:10], Y[:10], do_optimize=False) with self.assertRaises(RuntimeError): - model._get_gp(X[:10], Y[:10]) + model._get_gaussian_process(X[:10], Y[:10]) @unittest.mock.patch("gpytorch.mlls.exact_marginal_log_likelihood.ExactMarginalLogLikelihood.forward") def test_train_continue_on_linalg_error(self, fit_mock): @@ -301,19 +303,19 @@ def test_predict_with_actual_values(self): self.assertLess(abs(var_hat[0][0] - 1017.1374468449195), 15) # test other covariance results - _, var_fc = model.predict(X, cov_return_type="full_cov") + _, var_fc = model.predict(X, cov_return_type="full") self.assertEqual(var_fc.shape, (8, 8)) - _, var_sd = model.predict(X, cov_return_type="diagonal_std") + _, var_sd = model.predict(X, cov_return_type="std") self.assertEqual(var_sd.shape, (8, 1)) _, var_no = model.predict(np.array([[10.0, 10.0, 10.0]]), cov_return_type=None) self.assertIsNone(var_no) # check values - _, var_fc = model.predict(np.array([[10.0, 10.0, 10.0]]), cov_return_type="full_cov") + _, var_fc = model.predict(np.array([[10.0, 10.0, 10.0]]), cov_return_type="full") self.assertAlmostEqual(var_fc[0][0], var_hat[0][0]) - _, var_sd = model.predict(np.array([[10.0, 10.0, 10.0]]), cov_return_type="diagonal_std") + _, var_sd = model.predict(np.array([[10.0, 10.0, 10.0]]), cov_return_type="std") self.assertAlmostEqual(var_sd[0][0] ** 2, var_hat[0][0]) - _, var_fc = model.predict(np.array([[10.0, 10.0, 10.0], [5.0, 5.0, 5.0]]), cov_return_type="full_cov") + _, var_fc = model.predict(np.array([[10.0, 10.0, 10.0], [5.0, 5.0, 5.0]]), cov_return_type="full") self.assertEqual(var_fc.shape, (2, 2)) def test_normalization(self): diff --git a/tests/test_epm/test_lgpga.py b/tests/test_model/_test_lgpga.py similarity index 94% rename from tests/test_epm/test_lgpga.py rename to tests/test_model/_test_lgpga.py index 8c79aeffb..fead691f5 100644 --- a/tests/test_epm/test_lgpga.py +++ b/tests/test_model/_test_lgpga.py @@ -11,14 +11,16 @@ from gpytorch.priors import HorseshoePrior from smac.configspace import ConfigurationSpace, UniformFloatHyperparameter -from smac.epm.gaussian_process.augmented import ( +from smac.model.gaussian_process.augmented_local_gaussian_process import ( AugmentedLocalGaussianProcess, GloballyAugmentedLocalGaussianProcess, ) -from smac.epm.gaussian_process.gpytorch import ExactGPModel +from smac.model.gaussian_process.gpytorch_gaussian_process import ( + ExactGaussianProcessModel, +) -from .test_boing_kernel import generate_kernel, generate_test_data -from .test_gp_gpytorch import TestGPGPyTorch +from ._test_boing_kernel import generate_kernel, generate_test_data +from ._test_gp_gpytorch import TestGPGPyTorch torch.manual_seed(0) pyro.set_rng_seed(0) @@ -96,14 +98,14 @@ def test_update_attribute(self): def test_get_gp(self): self.assertIsNone(self.gp_model.gp) self.gp_model._get_gp(self.X_in, self.Y_in) - self.assertIsInstance(self.gp_model.gp_model, ExactGPModel) + self.assertIsInstance(self.gp_model.gp_model, ExactGaussianProcessModel) self.gp_model._get_gp(self.X_in, self.Y_in, self.X_out, self.Y_out) self.assertIsInstance(self.gp_model.gp_model, AugmentedLocalGaussianProcess) # num_outer is not enough, we return to a vanilla GP model self.gp_model._train(self.X_in, self.Y_in, do_optimize=False) - self.assertIsInstance(self.gp_model.gp_model, ExactGPModel) + self.assertIsInstance(self.gp_model.gp_model, ExactGaussianProcessModel) self.gp_model._train(self.X_all, self.Y_all, do_optimize=False) self.assertIsInstance(self.gp_model.gp_model, AugmentedLocalGaussianProcess) @@ -130,7 +132,7 @@ def test_augmented_gp(self): augmented_gp = AugmentedLocalGaussianProcess( X_in, Y_in, X_out, Y_out, self.gp_model.likelihood, self.kernel ).double() - exact_gp = ExactGPModel(X_in, Y_in, self.kernel, self.gp_model.likelihood).double() + exact_gp = ExactGaussianProcessModel(X_in, Y_in, self.kernel, self.gp_model.likelihood).double() # if augmented_gp.augmented is false, it should behave the same as an exact gp output_agp = augmented_gp(X_in) diff --git a/tests/test_model/_test_util_funcs.py b/tests/test_model/_test_util_funcs.py new file mode 100644 index 000000000..3e8e69c7b --- /dev/null +++ b/tests/test_model/_test_util_funcs.py @@ -0,0 +1,67 @@ +import unittest + +import numpy as np +from ConfigSpace import ConfigurationSpace, EqualsCondition +from ConfigSpace.hyperparameters import ( + CategoricalHyperparameter, + Constant, + OrdinalHyperparameter, + UniformFloatHyperparameter, + UniformIntegerHyperparameter, +) + +from smac.model.utils import check_subspace_points, get_types + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def test_check_subspace_points(self): + # 1D array + np.testing.assert_equal([True], check_subspace_points(np.array([0.5, 0.5]))) + bounds_cont_base = np.array([0.0, 1.0]) + X_cont = np.array([[0.5, 0.8], [-0.2, 0.2], [-0.7, 0.3]]) + np.testing.assert_equal(check_subspace_points(X_cont), [True] * len(X_cont)) + cont_dims = np.arange(X_cont.shape[-1]) + + with self.assertRaises(ValueError): + # bounds_cont missing + check_subspace_points(X_cont, cont_dims=cont_dims) + + with self.assertRaises(ValueError): + # bounds_cont does not match + check_subspace_points(X_cont, cont_dims=cont_dims, bounds_cont=bounds_cont_base) + + bounds_cont = np.tile(bounds_cont_base, [X_cont.shape[-1], 1]) + + np.testing.assert_equal( + check_subspace_points(X_cont, cont_dims=cont_dims, bounds_cont=bounds_cont), [True, False, False] + ) + np.testing.assert_equal( + check_subspace_points(X_cont, cont_dims=cont_dims, bounds_cont=bounds_cont, expand_bound=True), + [True, True, False], + ) + + # categorical hps + X_cat = np.array([[0, 1], [2, 1], [1, 4]]) + cat_dims = np.arange(X_cat.shape[-1]) + + bounds_cat = [(0, 2), (1, 4)] + + with self.assertRaises(ValueError): + # bounds_cont missing + check_subspace_points(X_cat, cat_dims=cat_dims) + + with self.assertRaises(ValueError): + # bounds_cat doe not match + check_subspace_points(X_cat, cat_dims=cat_dims, bounds_cat=[(0, 1)]) + + np.testing.assert_equal(check_subspace_points(X_cat, cat_dims=cat_dims, bounds_cat=bounds_cat), [True, True, False]) + + # cat + cont + X_mix = np.hstack([X_cont, X_cat]) + cat_dims += len(cont_dims) + ss_mix = check_subspace_points( + X_mix, cont_dims=cont_dims, cat_dims=cat_dims, bounds_cont=bounds_cont, bounds_cat=bounds_cat + ) + np.testing.assert_equal(ss_mix, [True, False, False]) diff --git a/tests/test_model/test_abstract_model.py b/tests/test_model/test_abstract_model.py new file mode 100644 index 000000000..76be9ce02 --- /dev/null +++ b/tests/test_model/test_abstract_model.py @@ -0,0 +1,90 @@ +import numpy as np +import pytest + +from smac.model.abstract_model import AbstractModel +from smac.utils.configspace import convert_configurations_to_array + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def get_X_y(cs, n_samples, n_instance_features): + X = convert_configurations_to_array(cs.sample_configuration(n_samples)) + + if n_instance_features is not None and n_instance_features > 0: + X_inst = np.random.rand(n_samples, n_instance_features) + X = np.hstack((X, X_inst)) + + y = np.random.rand(n_samples) + + return X, y + + +def _train(X, Y): + return None + + +def test_no_pca(configspace_small, make_scenario): + n_instances = 100 + n_instance_features = 10 + n_samples = 5 + + scenario = make_scenario( + configspace_small, + use_instances=True, + n_instances=n_instances, + n_instance_features=n_instance_features, + ) + model = AbstractModel(configspace_small, scenario.instance_features, pca_components=7) + # We just overwrite the function as mock here + model._train = _train + + # No PCA + X, y = get_X_y(configspace_small, n_samples, n_instance_features) + model.train(X, y) + assert not model._apply_pca + + X, y = get_X_y(configspace_small, n_samples, n_instance_features + 1) + with pytest.raises(ValueError, match="Feature mismatch.*"): + model.train(X, y) + + X_test, _ = get_X_y(configspace_small, n_samples, None) + with pytest.raises(NotImplementedError): + model.predict_marginalized(X_test) + + X_test, _ = get_X_y(configspace_small, n_samples, 10) + with pytest.raises(ValueError, match="Feature mismatch.*"): + model.predict_marginalized(X_test) + + +def test_pca(configspace_small, make_scenario): + n_instances = 100 + n_instance_features = 10 + n_samples = 155 + + scenario = make_scenario( + configspace_small, + use_instances=True, + n_instances=n_instances, + n_instance_features=n_instance_features, + ) + model = AbstractModel(configspace_small, scenario.instance_features, pca_components=7) + # We just overwrite the function as mock here + model._train = _train + + # PCA + X, y = get_X_y(configspace_small, n_samples, n_instance_features) + model.train(X, y) + assert model._apply_pca + + X, y = get_X_y(configspace_small, n_samples, n_instance_features + 1) + with pytest.raises(ValueError, match="Feature mismatch.*"): + model.train(X, y) + + X_test, _ = get_X_y(configspace_small, n_samples, None) + with pytest.raises(NotImplementedError): + model.predict_marginalized(X_test) + + X_test, _ = get_X_y(configspace_small, n_samples, 10) + with pytest.raises(ValueError, match="Feature mismatch.*"): + model.predict_marginalized(X_test) diff --git a/tests/test_model/test_gp.py b/tests/test_model/test_gp.py new file mode 100644 index 000000000..f4925547f --- /dev/null +++ b/tests/test_model/test_gp.py @@ -0,0 +1,444 @@ +from unittest.mock import patch + +import numpy as np +import pytest +import scipy.optimize +import sklearn.datasets +import sklearn.model_selection +from ConfigSpace import ( + CategoricalHyperparameter, + ConfigurationSpace, + EqualsCondition, + UniformFloatHyperparameter, +) + +from smac.model.gaussian_process.gaussian_process import GaussianProcess +from smac.model.gaussian_process.priors import HorseshoePrior, LogNormalPrior +from smac.utils.configspace import convert_configurations_to_array + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def get_gp(n_dimensions, seed, noise=1e-3, normalize_y=True) -> GaussianProcess: + from smac.model.gaussian_process.kernels import ( + ConstantKernel, + MaternKernel, + WhiteKernel, + ) + + cov_amp = ConstantKernel( + 2.0, + constant_value_bounds=(1e-10, 2), + prior=LogNormalPrior(mean=0.0, sigma=1.0, seed=seed), + ) + + exp_kernel = MaternKernel( + np.ones([n_dimensions]), + [(np.exp(-10), np.exp(2)) for _ in range(n_dimensions)], + nu=2.5, + ) + + noise_kernel = WhiteKernel( + noise_level=noise, + noise_level_bounds=(1e-10, 2), + prior=HorseshoePrior(scale=0.1, seed=seed), + ) + + kernel = cov_amp * exp_kernel + noise_kernel + + configspace = ConfigurationSpace() + for i in range(n_dimensions): + configspace.add_hyperparameter(UniformFloatHyperparameter("x%d" % i, 0, 1)) + + rs = np.random.RandomState(seed) + + model = GaussianProcess( + configspace=configspace, + kernel=kernel, + n_restarts=2, + normalize_y=normalize_y, + seed=rs.randint(low=1, high=10000), + ) + return model + + +def get_cont_data(rs): + X = rs.rand(20, 10) + Y = rs.rand(10, 1) + n_dims = 10 + + return X, Y, n_dims + + +def get_cat_data(rs): + X_cont = rs.rand(20, 5) + X_cat = rs.randint(low=0, high=3, size=(20, 5)) + X = np.concatenate([X_cat, X_cont], axis=1) + Y = rs.rand(10, 1) + cat_dims = [0, 1, 2, 3, 4] + cont_dims = [5, 6, 7, 8, 9] + + return X, Y, cat_dims, cont_dims + + +def get_mixed_gp(cat_dims, cont_dims, seed, noise=1e-3, normalize_y=True): + from smac.model.gaussian_process.kernels import ( + ConstantKernel, + HammingKernel, + MaternKernel, + WhiteKernel, + ) + + cat_dims = np.array(cat_dims, dtype=int) + cont_dims = np.array(cont_dims, dtype=int) + + cov_amp = ConstantKernel( + 2.0, + constant_value_bounds=(1e-10, 2), + prior=LogNormalPrior(mean=0.0, sigma=1.0, seed=seed), + ) + + exp_kernel = MaternKernel( + np.ones([len(cont_dims)]), + [(np.exp(-10), np.exp(2)) for _ in range(len(cont_dims))], + nu=2.5, + operate_on=cont_dims, + ) + + ham_kernel = HammingKernel( + np.ones([len(cat_dims)]), + [(np.exp(-10), np.exp(2)) for _ in range(len(cat_dims))], + operate_on=cat_dims, + ) + + noise_kernel = WhiteKernel( + noise_level=noise, + noise_level_bounds=(1e-10, 2), + prior=HorseshoePrior(scale=0.1, seed=seed), + ) + + kernel = cov_amp * (exp_kernel * ham_kernel) + noise_kernel + + cs = ConfigurationSpace() + for c in cont_dims: + cs.add_hyperparameter(UniformFloatHyperparameter("X%d" % c, 0, 1)) + for c in cat_dims: + cs.add_hyperparameter(CategoricalHyperparameter("X%d" % c, [0, 1, 2, 3])) + + rs = np.random.RandomState(seed) + + model = GaussianProcess( + configspace=cs, + kernel=kernel, + normalize_y=normalize_y, + seed=rs.randint(low=1, high=10000), + ) + + return model + + +def test_predict_wrong_X_dimensions(): + seed = 1 + rs = np.random.RandomState(seed) + + # cont + X, Y, n_dims = get_cont_data(rs) + # cat + X, Y, cat_dims, cont_dims = get_cat_data(rs) + + for model in (get_gp(n_dims, seed), get_mixed_gp(cat_dims, cont_dims, seed=1)): + X = rs.rand(10) + with pytest.raises(ValueError, match="Expected 2d array.*"): + model.predict(X) + + X = rs.rand(10, 10, 10) + with pytest.raises(ValueError, match="Expected 2d array.*"): + model.predict(X) + + X = rs.rand(10, 5) + with pytest.raises(ValueError, match="Feature mismatch:.*"): + model.predict(X) + + +def test_predict(): + seed = 1 + rs = np.random.RandomState(seed) + + # cont + X, Y, n_dims = get_cont_data(rs) + # cat + X, Y, cat_dims, cont_dims = get_cat_data(rs) + + for model in (get_gp(n_dims, seed), get_mixed_gp(cat_dims, cont_dims, seed)): + model.train(X[:10], Y[:10]) + m_hat, v_hat = model.predict(X[10:]) + assert m_hat.shape == (10, 1) + assert v_hat.shape == (10, 1) + + +def test_train_do_optimize(): + # Check that do_optimize does not mess with the kernel hyperparameters given to the Gaussian process! + seed = 1 + rs = np.random.RandomState(seed) + X, Y, n_dims = get_cont_data(rs) + + model = get_gp(n_dims, seed) + model._train(X[:10], Y[:10], optimize_hyperparameters=False) + theta = model._kernel.theta + theta_ = model._kernel.theta + fixture = np.array([0.693147, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.907755]) + np.testing.assert_array_almost_equal(theta, fixture) + np.testing.assert_array_almost_equal(theta_, fixture) + np.testing.assert_array_almost_equal(theta, theta_) + + model._train(X[:10], Y[:10], optimize_hyperparameters=True) + theta = model._kernel.theta + theta_ = model._kernel.theta + fixture = np.array([0.693147, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.907755]) + assert not np.any(theta == fixture) + assert not np.any(theta_ == fixture) + np.testing.assert_array_almost_equal(theta, theta_) + + +def test_train_continue_on_linalg_error(): + """Checks that training does not stop on a linalg error, but that uncertainty is increased!""" + + class Dummy: + counter = 0 + + def __call__(self, X, y): + if self.counter >= 10: + return None + + self.counter += 1 + raise np.linalg.LinAlgError + + with patch.object(sklearn.gaussian_process.GaussianProcessRegressor, "fit", Dummy().__call__): + seed = 1 + rs = np.random.RandomState(seed) + X, Y, n_dims = get_cont_data(rs) + + model = get_gp(n_dims, seed) + fixture = np.exp(model._kernel.theta[-1]) + model._train(X[:10], Y[:10], optimize_hyperparameters=False) + assert pytest.approx(np.exp(model._kernel.theta[-1])) == fixture + 10 + + +def test_train_continue_on_linalg_error_2(): + # Check that training does not stop on a linalg error during hyperparameter optimization + + class Dummy: + counter = 0 + + def __call__(self, X, eval_gradient=True, clone_kernel=True): + # If this is not aligned with the GP an error will be raised that None is not iterable + if self.counter == 13: + return None + + self.counter += 1 + raise np.linalg.LinAlgError + + with patch.object(sklearn.gaussian_process.GaussianProcessRegressor, "log_marginal_likelihood", Dummy().__call__): + + seed = 1 + rs = np.random.RandomState(seed) + X, Y, n_dims = get_cont_data(rs) + + model = get_gp(n_dims, seed) + _ = model._kernel.theta + model._train(X[:10], Y[:10], optimize_hyperparameters=True) + np.testing.assert_array_almost_equal( + model._kernel.theta, + [0.69314718, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.69314718], + ) + + +def test_predict_marginalized_over_instances_no_features(): + """The GP should fall back to the regular predict() method.""" + + class Dummy: + counter = 0 + + def __call__(self, X, Y=None): + self.counter += 1 + + dummy = Dummy() + with patch.object(GaussianProcess, "predict", dummy.__call__): + + seed = 1 + rs = np.random.RandomState(seed) + + # cont + X, Y, n_dims = get_cont_data(rs) + # cat + X, Y, cat_dims, cont_dims = get_cat_data(rs) + + for ct, model in enumerate((get_gp(n_dims, seed), get_mixed_gp(cat_dims, cont_dims, seed))): + model.train(X[:10], Y[:10]) + model.predict(X[10:]) + assert dummy.counter == ct + 1 + + +def test_predict_with_actual_values(): + X = np.array( + [ + [0.0, 0.0, 0.0], + [0.0, 0.0, 1.0], + [0.0, 1.0, 0.0], + [0.0, 1.0, 1.0], + [1.0, 0.0, 0.0], + [1.0, 0.0, 1.0], + [1.0, 1.0, 0.0], + [1.0, 1.0, 1.0], + ], + dtype=np.float64, + ) + y = np.array([[0.1], [0.2], [9], [9.2], [100.0], [100.2], [109.0], [109.2]], dtype=np.float64) + seed = 1 + rs = np.random.RandomState(seed) + model = get_gp(3, seed) + model.train(np.vstack((X, X, X, X, X, X, X, X)), np.vstack((y, y, y, y, y, y, y, y))) + + mu_hat, var_hat = model.predict(X) + for y_i, y_hat_i, mu_hat_i in zip( + y.reshape((1, -1)).flatten(), + mu_hat.reshape((1, -1)).flatten(), + var_hat.reshape((1, -1)).flatten(), + ): + assert pytest.approx(y_hat_i, 0.0001) == y_i + assert round(mu_hat_i) == 0 + + # Regression test that performance does not drastically decrease in the near future + mu_hat, var_hat = model.predict(np.array([[10, 10, 10]])) + assert pytest.approx(mu_hat[0][0]) == 54.612500000000004 + # There's a slight difference between my local installation and travis + # assert abs(var_hat[0][0] - 1121.8409184001594) < 2 + + # test other covariance results + _, var_fc = model.predict(X, covariance_type="full") + assert var_fc.shape == (8, 8) + + _, var_sd = model.predict(X, covariance_type="diagonal") + assert var_sd.shape == (8, 1) + + _, var_no = model.predict(np.array([[10, 10, 10]]), covariance_type=None) + assert var_no is None + + # check values + _, var_fc = model.predict(np.array([[10, 10, 10]]), covariance_type="full") + assert pytest.approx(var_fc[0][0]) == var_hat[0][0] + + _, var_sd = model.predict(np.array([[10, 10, 10]]), covariance_type="std") + assert pytest.approx(var_sd[0][0] ** 2) == var_hat[0][0] + + +def test_gp_on_sklearn_data(): + X, y = sklearn.datasets.load_boston(return_X_y=True) + # Normalize such that the bounds in get_gp (10) hold + X = X / X.max(axis=0) + seed = 1 + rs = np.random.RandomState(seed) + model = get_gp(X.shape[1], seed) + cv = sklearn.model_selection.KFold(shuffle=True, random_state=rs, n_splits=2) + + maes = [8.886514052493349145, 8.868823494592284107] + + for i, (train_split, test_split) in enumerate(cv.split(X, y)): + X_train = X[train_split] + y_train = y[train_split] + X_test = X[test_split] + y_test = y[test_split] + model.train(X_train, y_train) + y_hat, mu_hat = model.predict(X_test) + mae = np.mean(np.abs(y_hat - y_test), dtype=float) + assert pytest.approx(mae) == maes[i] + + +def test_nll(): + seed = 1 + rs = np.random.RandomState(seed) + gp = get_gp(seed, seed) + gp.train(np.array([[0], [1]]), np.array([0, 1])) + n_above_1 = 0 + for i in range(1000): + theta = np.array( + [rs.uniform(1e-10, 10), rs.uniform(-10, 2), rs.uniform(-10, 1)] + ) # Values from the default prior + error = scipy.optimize.check_grad(lambda x: gp._nll(x)[0], lambda x: gp._nll(x)[1], theta, epsilon=1e-5) + if error > 0.1: + n_above_1 += 1 + assert n_above_1 < 10 + + +def test_sampling_shape(): + X = np.arange(-5, 5, 0.1).reshape((-1, 1)) + X_test = np.arange(-5.05, 5.05, 0.1).reshape((-1, 1)) + for shape in (None, (-1, 1)): + + if shape is None: + y = np.sin(X).flatten() + else: + y = np.sin(X).reshape(shape) + + seed = 1 + rng = np.random.RandomState(seed) + for gp in ( + get_gp(n_dimensions=1, seed=seed, noise=1e-10, normalize_y=False), + get_gp(n_dimensions=1, seed=seed, noise=1e-10, normalize_y=True), + ): + gp._train(X, y) + func = gp.sample_functions(X_test=X_test, n_funcs=1) + assert func.shape == (101, 1) + func = gp.sample_functions(X_test=X_test, n_funcs=2) + assert func.shape == (101, 2) + + +def test_normalization(): + X = np.arange(-5, 5, 0.1).reshape((-1, 1)) + X_test = np.arange(-5.05, 5.05, 0.1).reshape((-1, 1)) + y = np.sin(X) + seed = 1 + + gp = get_gp(n_dimensions=1, seed=seed, noise=1e-10, normalize_y=False) + gp._train(X, y, optimize_hyperparameters=False) + mu_hat, var_hat = gp.predict(X_test) + + gp_norm = get_gp(n_dimensions=1, seed=seed, noise=1e-10, normalize_y=True) + gp_norm._train(X, y, optimize_hyperparameters=False) + mu_hat_prime, var_hat_prime = gp_norm.predict(X_test) + + np.testing.assert_array_almost_equal(mu_hat, mu_hat_prime, decimal=4) + np.testing.assert_array_almost_equal(var_hat, var_hat_prime, decimal=4) + + func = gp.sample_functions(X_test=X_test, n_funcs=2) + func_prime = gp_norm.sample_functions(X_test=X_test, n_funcs=2) + np.testing.assert_array_almost_equal(func, func_prime, decimal=1) + + +""" +def test_impute_inactive_hyperparameters(): + cs = ConfigurationSpace() + a = cs.add_hyperparameter(CategoricalHyperparameter("a", [0, 1])) + b = cs.add_hyperparameter(CategoricalHyperparameter("b", [0, 1])) + c = cs.add_hyperparameter(UniformFloatHyperparameter("c", 0, 1)) + cs.add_condition(EqualsCondition(b, a, 1)) + cs.add_condition(EqualsCondition(c, a, 0)) + cs.seed(1) + + configs = cs.sample_configuration(size=100) + config_array = convert_configurations_to_array(configs) + for line in config_array: + if line[0] == 0: + assert np.isnan(line[1]) + elif line[0] == 1: + assert np.isnan(line[2]) + + gp = get_gp(3, 1) + config_array = gp._impute_inactive(config_array) + for line in config_array: + if line[0] == 0: + assert line[1] == -1 + elif line[0] == 1: + assert line[2] == -1 +""" diff --git a/tests/test_model/test_gp_mcmc.py b/tests/test_model/test_gp_mcmc.py new file mode 100644 index 000000000..da5b947ed --- /dev/null +++ b/tests/test_model/test_gp_mcmc.py @@ -0,0 +1,242 @@ +from unittest.mock import patch + +import numpy as np +import pytest +import sklearn.datasets +import sklearn.model_selection +from ConfigSpace import ConfigurationSpace, UniformFloatHyperparameter + +from smac.model.gaussian_process.mcmc_gaussian_process import MCMCGaussianProcess +from smac.model.gaussian_process.priors import HorseshoePrior, LogNormalPrior + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def get_gp(n_dimensions, seed, noise=1e-3, normalize_y=True, average_samples=False, n_iter=50): + from smac.model.gaussian_process.kernels import ( + ConstantKernel, + MaternKernel, + WhiteKernel, + ) + + cov_amp = ConstantKernel( + 2.0, + constant_value_bounds=(1e-10, 2), + prior=LogNormalPrior(mean=0.0, sigma=1.0, seed=seed), + ) + + exp_kernel = MaternKernel( + np.ones([n_dimensions]), + [(np.exp(-10), np.exp(2)) for _ in range(n_dimensions)], + nu=2.5, + prior=None, + ) + + noise_kernel = WhiteKernel( + noise_level=noise, + noise_level_bounds=(1e-10, 2), + prior=HorseshoePrior(scale=0.1, seed=seed), + ) + + kernel = cov_amp * exp_kernel + noise_kernel + + n_mcmc_walkers = 3 * len(kernel.theta) + if n_mcmc_walkers % 2 == 1: + n_mcmc_walkers += 1 + + configspace = ConfigurationSpace() + for i in range(n_dimensions): + configspace.add_hyperparameter(UniformFloatHyperparameter("x%d" % i, 0, 1)) + + rs = np.random.RandomState(seed) + model = MCMCGaussianProcess( + configspace=configspace, + kernel=kernel, + n_mcmc_walkers=n_mcmc_walkers, + chain_length=n_iter, + burning_steps=n_iter, + normalize_y=normalize_y, + seed=rs.randint(low=1, high=10000), + mcmc_sampler="emcee", + average_samples=average_samples, + ) + return model + + +def test_predict_wrong_X_dimensions(): + seed = 1 + rs = np.random.RandomState(seed) + model = get_gp(10, seed) + + X = rs.rand(10) + with pytest.raises(ValueError, match="Expected 2d array.*"): + model.predict(X) + + X = rs.rand(10, 10, 10) + with pytest.raises(ValueError, match="Expected 2d array.*"): + model.predict(X) + + X = rs.rand(10, 5) + with pytest.raises(ValueError, match="Feature mismatch.*"): + model.predict(X) + + +def test_gp_train(): + seed = 1 + rs = np.random.RandomState(seed) + X = rs.rand(20, 10) + Y = rs.rand(10, 1) + + fixture = np.array([0.693147, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.907755]) + + model = get_gp(10, seed) + np.testing.assert_array_almost_equal(model._kernel.theta, fixture) + model.train(X[:10], Y[:10]) + assert len(model.models) == 36 + + for base_model in model.models: + theta = base_model._gp.kernel.theta + theta_ = base_model._gp.kernel_.theta + + # Test that the kernels of the base GP are actually changed! + np.testing.assert_array_almost_equal(theta, theta_) + assert not np.any(theta == fixture) + assert not np.any(theta_ == fixture) + + +def test_gp_train_posterior_mean(): + seed = 1 + rs = np.random.RandomState(seed) + X = rs.rand(20, 10) + Y = rs.rand(10, 1) + + fixture = np.array([0.693147, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.907755]) + + model = get_gp(10, seed, average_samples=True) + np.testing.assert_array_almost_equal(model._kernel.theta, fixture) + model.train(X[:10], Y[:10]) + + for base_model in model.models: + theta = base_model._gp.kernel.theta + theta_ = base_model._gp.kernel_.theta + # Test that the kernels of the base GP are actually changed! + np.testing.assert_array_almost_equal(theta, theta_) + assert not np.any(theta == fixture) + assert not np.any(theta_ == fixture) + + assert len(model.models) == 1 + + +def test_predict(): + seed = 1 + rs = np.random.RandomState(seed) + X = rs.rand(20, 10) + Y = rs.rand(10, 1) + model = get_gp(10, seed) + model.train(X[:10], Y[:10]) + m_hat, v_hat = model.predict(X[10:]) + assert m_hat.shape == (10, 1) + assert v_hat.shape == (10, 1) + + +def test_predict_marginalized_over_instances_no_features(): + """The GP should fall back to the regular predict() method.""" + + class Dummy: + counter = 0 + + def __call__(self, X, Y=None): + self.counter += 1 + + dummy = Dummy() + with patch.object(MCMCGaussianProcess, "predict", dummy.__call__): + seed = 1 + rs = np.random.RandomState(seed) + X = rs.rand(20, 10) + Y = rs.rand(10, 1) + model = get_gp(10, seed) + model.train(X[:10], Y[:10]) + model.predict(X[10:]) + assert dummy.counter == 1 + + +# def test_predict_with_actual_values(): +# X = np.array( +# [ +# [0.0, 0.0, 0.0], +# [0.0, 0.0, 1.0], +# [0.0, 1.0, 0.0], +# [0.0, 1.0, 1.0], +# [1.0, 0.0, 0.0], +# [1.0, 0.0, 1.0], +# [1.0, 1.0, 0.0], +# [1.0, 1.0, 1.0], +# ], +# dtype=np.float64, +# ) +# y = np.array([[0.1], [0.2], [9], [9.2], [100.0], [100.2], [109.0], [109.2]], dtype=np.float64) +# seed = 1 +# model = get_gp(3, seed, noise=1e-10, n_iter=200) +# model.train(np.vstack((X, X, X, X, X, X, X, X)), np.vstack((y, y, y, y, y, y, y, y))) + +# y_hat, var_hat = model.predict(X) +# for y_i, y_hat_i, var_hat_i in zip( +# y.reshape((1, -1)).flatten(), +# y_hat.reshape((1, -1)).flatten(), +# var_hat.reshape((1, -1)).flatten(), +# ): +# # Chain length too short to get excellent predictions, apparently there's a lot of predictive variance +# np.testing.assert_almost_equal(y_i, y_hat_i, decimal=1) +# np.testing.assert_almost_equal(var_hat_i, 0, decimal=500) + +# # Regression test that performance does not drastically decrease in the near future +# y_hat, var_hat = model.predict(np.array([[10, 10, 10]])) +# np.testing.assert_almost_equal(y_hat[0][0], 54.613410745846785, decimal=0.1) +# # Massive variance due to internally used law of total variances, also a massive difference locally and on +# # travis-ci +# assert abs(var_hat[0][0]) - 3700 <= 200 + + +# def test_gp_on_sklearn_data(): +# X, y = sklearn.datasets.load_boston(return_X_y=True) +# # Normalize such that the bounds in get_gp hold +# X = X / X.max(axis=0) +# seed = 1 +# rs = np.random.RandomState(seed) +# model = get_gp(X.shape[1], seed, noise=1e-10, normalize_y=True) +# cv = sklearn.model_selection.KFold(shuffle=True, random_state=rs, n_splits=2) + +# maes = [7.1383486992745653755, 7.453042020795519766] + +# for i, (train_split, test_split) in enumerate(cv.split(X, y)): +# X_train = X[train_split] +# y_train = y[train_split] +# X_test = X[test_split] +# y_test = y[test_split] +# model.train(X_train, y_train) +# y_hat, mu_hat = model.predict(X_test) +# mae = np.mean(np.abs(y_hat - y_test), dtype=float) + +# np.testing.assert_almost_equal(mae, maes[i]) + + +def test_normalization(): + X = np.arange(-5, 5, 0.1).reshape((-1, 1)) + X_test = np.arange(-5.05, 5.05, 0.1).reshape((-1, 1)) + y = np.sin(X) + seed = 1 + gp = get_gp(n_dimensions=1, seed=seed, noise=1e-10, normalize_y=False) + gp._train(X, y, optimize_hyperparameters=False) + assert not gp.models[0]._normalize_y + assert not hasattr(gp.models[0], "mean_y_") + + mu_hat, var_hat = gp.predict(X_test) + gp_norm = get_gp(n_dimensions=1, seed=seed, noise=1e-10, normalize_y=True) + gp_norm._train(X, y, optimize_hyperparameters=False) + assert gp_norm.models[0]._normalize_y + assert hasattr(gp_norm.models[0], "mean_y_") + + mu_hat_prime, var_hat_prime = gp_norm.predict(X_test) + np.testing.assert_array_almost_equal(mu_hat, mu_hat_prime, decimal=4) + np.testing.assert_array_almost_equal(var_hat, var_hat_prime, decimal=4) diff --git a/tests/test_model/test_gp_priors.py b/tests/test_model/test_gp_priors.py new file mode 100644 index 000000000..116e09661 --- /dev/null +++ b/tests/test_model/test_gp_priors.py @@ -0,0 +1,262 @@ +from functools import partial + +import numpy as np +import pytest +import scipy.optimize + +from smac.constants import VERY_SMALL_NUMBER +from smac.model.gaussian_process.priors import ( + GammaPrior, + HorseshoePrior, + LogNormalPrior, + SoftTopHatPrior, + TophatPrior, +) + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def wrap_ln(theta, prior): + return np.exp(prior.get_log_probability(np.log(theta))) + + +def wrap_lnprob(x, prior): + return prior.get_log_probability(x[0]) + + +def wrap_gradient(x, prior): + return prior.get_gradient(x[0]) + + +def test_lnprob_and_grad_scalar(): + prior = TophatPrior( + lower_bound=np.exp(-10), + upper_bound=np.exp(2), + seed=1, + ) + + # Legal scalar + for val in (-1, 0, 1): + assert prior.get_log_probability(val) == 0 + assert prior.get_gradient(val) == 0 + + # Boundary + for val in (-10, 2): + assert prior.get_log_probability(val) == 0 + assert prior.get_gradient(val) == 0 + + # Values outside the boundary + for val in (-10 - VERY_SMALL_NUMBER, 2 + VERY_SMALL_NUMBER, -50, 50): + assert np.isinf(prior.get_log_probability(val)) + assert prior.get_gradient(val) == 0 + + +def test_sample_from_prior(): + prior = TophatPrior(lower_bound=np.exp(-10), upper_bound=np.exp(2), seed=1) + samples = prior.sample_from_prior(10) + np.testing.assert_array_equal(samples >= -10, True) + np.testing.assert_array_equal(samples <= 2, True) + # Test that the rng is set + assert pytest.approx(samples[0]) == -4.995735943569112 + + +def test_sample_from_prior_shapes(): + rng = np.random.RandomState(1) + lower_bound = 2 + rng.random_sample() * 50 + upper_bound = lower_bound + rng.random_sample() * 50 + prior = TophatPrior( + lower_bound=lower_bound, + upper_bound=upper_bound, + seed=1, + ) + sample = prior.sample_from_prior(1) + assert sample.shape == (1,) + sample = prior.sample_from_prior(2) + assert sample.shape == (2,) + sample = prior.sample_from_prior(10) + assert sample.shape == (10,) + + with pytest.raises(ValueError): + prior.sample_from_prior(0) + + with pytest.raises(ValueError): + prior.sample_from_prior((2,)) + + +def test_lnprob_and_grad_scalar(): + prior = HorseshoePrior(scale=1, seed=1) + + # Legal scalar + assert prior.get_log_probability(-1) == 1.1450937952919953 + assert prior.get_gradient(-1) == -0.6089187456211098 + + # Boundary + assert np.isinf(prior._get_log_probability(0)) + assert np.isinf(prior._get_gradient(0)) + + +def test_sample_from_prior(): + prior = HorseshoePrior(scale=1, seed=1) + samples = prior.sample_from_prior(10) + + # Test that the rng is set + assert pytest.approx(samples[0]) == 1.0723988839129437 + + +def test_sample_from_prior_shapes(): + rng = np.random.RandomState(1) + lower_bound = 2 + rng.random_sample() * 50 + upper_bound = lower_bound + rng.random_sample() * 50 + prior = TophatPrior( + lower_bound=lower_bound, + upper_bound=upper_bound, + seed=1, + ) + sample = prior.sample_from_prior(1) + assert sample.shape == (1,) + + sample = prior.sample_from_prior(2) + assert sample.shape == (2,) + + sample = prior.sample_from_prior(10) + assert sample.shape == (10,) + + with pytest.raises(ValueError): + prior.sample_from_prior(0) + + with pytest.raises(ValueError): + prior.sample_from_prior((2,)) + + +def test_gradient(): + for scale in (0.1, 0.5, 1.0, 2.0): + prior = HorseshoePrior(scale=scale, seed=1) + # The function appears to be unstable above 15 + for theta in range(-20, 15): + if theta == 0: + continue + + wrap_lnprob_ = partial(wrap_lnprob, prior=prior) + wrap_gradient_ = partial(wrap_gradient, prior=prior) + + error = scipy.optimize.check_grad( + wrap_lnprob_, + wrap_gradient_, + np.array([theta]), + epsilon=1e-5, + ) + assert pytest.approx(error) == 0 + + +def test_lnprob_and_grad_scalar(): + prior = GammaPrior(a=0.5, scale=1 / 2, loc=0, seed=1) + + # Legal scalar + x = -1 + assert pytest.approx(prior.get_log_probability(x)) == -0.46155023 # 7 + assert prior.get_gradient(x) == -1.2357588823428847 + + +def test_lnprob_and_grad_array(): + prior = GammaPrior(a=0.5, scale=1 / 2, loc=0, seed=1) + val = np.array([-1, -1]) + + with pytest.raises(NotImplementedError): + prior.get_log_probability(val) + + with pytest.raises(NotImplementedError): + prior.get_gradient(val) + + +def test_gradient(): + for scale in (0.5, 1.0, 2.0): + prior = GammaPrior(a=2, scale=scale, loc=0, seed=1) + # The function appears to be unstable above 10 + for theta in np.arange(1e-15, 10, 0.01): + if theta == 0: + continue + + wrap_lnprob_ = partial(wrap_lnprob, prior=prior) + wrap_gradient_ = partial(wrap_gradient, prior=prior) + + error = scipy.optimize.check_grad( + wrap_lnprob_, + wrap_gradient_, + np.array([theta]), + epsilon=1e-5, + ) + + assert pytest.approx(error) == 0 # , delta=5, msg=str(theta)) + + +def test_gradient(): + for sigma in (0.5, 1.0, 2.0): + prior = LogNormalPrior(mean=0, sigma=sigma, seed=1) + # The function appears to be unstable above 15 + for theta in range(0, 15): + # Gradient approximation becomes unstable when going closer to zero + theta += 1e-2 + + wrap_lnprob_ = partial(wrap_lnprob, prior=prior) + wrap_gradient_ = partial(wrap_gradient, prior=prior) + + error = scipy.optimize.check_grad( + wrap_lnprob_, + wrap_gradient_, + np.array([theta]), + epsilon=1e-5, + ) + assert round(error) == 0 # , delta=5, msg=theta) + + +def test_lnprob(): + prior = SoftTopHatPrior( + lower_bound=np.exp(-5), + upper_bound=np.exp(5), + exponent=2, + seed=1, + ) + + # Legal values + assert prior.get_log_probability(-5) == 0 + assert prior.get_log_probability(0) == 0 + assert prior.get_log_probability(5) == 0 + + # Illegal values + assert pytest.approx(prior.get_log_probability(-5.1)) == -0.01 + assert pytest.approx(prior.get_log_probability(-6)) == -1 + assert pytest.approx(prior.get_log_probability(-7)) == -4 + assert pytest.approx(prior.get_log_probability(5.1)) == -0.01 + assert pytest.approx(prior.get_log_probability(6)) == -1 + assert pytest.approx(prior.get_log_probability(7)) == -4 + + +def test_grad(): + prior = SoftTopHatPrior( + lower_bound=np.exp(-5), + upper_bound=np.exp(5), + exponent=2, + seed=1, + ) + + # Legal values + assert prior.get_gradient(-5) == 0 + assert prior.get_gradient(0) == 0 + assert prior.get_gradient(5) == 0 + + for theta in [-10, -7, -6, -5.1, 5.1, 6, 7, 10]: + # Gradient approximation becomes unstable when going closer to zero + theta += 1e-2 + grad = prior.get_gradient(theta) + grad_vector = prior.get_gradient(theta) + assert grad == grad_vector + + def prob(x): + return prior.get_log_probability(x[0]) + + def grad(x): + return prior.get_gradient(x[0]) + + error = scipy.optimize.check_grad(prob, grad, np.array([theta]), epsilon=1e-5) + assert np.round(error) == 0 diff --git a/tests/test_model/test_mo.py b/tests/test_model/test_mo.py new file mode 100644 index 000000000..fd93d59ba --- /dev/null +++ b/tests/test_model/test_mo.py @@ -0,0 +1,39 @@ +import unittest +from unittest import mock + +import numpy as np +from ConfigSpace import ConfigurationSpace, UniformFloatHyperparameter + +from smac.model.multi_objective_model import MultiObjectiveModel +from smac.model.random_forest.random_forest import RandomForest + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def _get_cs(n_dimensions): + configspace = ConfigurationSpace() + for i in range(n_dimensions): + configspace.add_hyperparameter(UniformFloatHyperparameter("x%d" % i, 0, 1)) + + return configspace + + +def test_train_and_predict_with_rf(): + rs = np.random.RandomState(1) + X = rs.rand(20, 10) + Y = rs.rand(10, 2) + + model = MultiObjectiveModel( + models=RandomForest(_get_cs(10)), + objectives=["cost", "ln(runtime)"], + ) + + model.train(X[:10], Y) + m, v = model.predict(X[10:]) + assert m.shape == (10, 2) + assert v.shape == (10, 2) + + m, v = model.predict_marginalized(X[10:]) + assert m.shape == (10, 2) + assert v.shape == (10, 2) diff --git a/tests/test_model/test_rf.py b/tests/test_model/test_rf.py new file mode 100644 index 000000000..271ce7cf0 --- /dev/null +++ b/tests/test_model/test_rf.py @@ -0,0 +1,280 @@ +import numpy as np +import pytest +from ConfigSpace import ( + CategoricalHyperparameter, + ConfigurationSpace, + EqualsCondition, + OrdinalHyperparameter, + UniformFloatHyperparameter, + UniformIntegerHyperparameter, +) + +from smac.model.random_forest.random_forest import RandomForest +from smac.utils.configspace import convert_configurations_to_array + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def _get_cs(n_dimensions): + configspace = ConfigurationSpace(seed=0) + for i in range(n_dimensions): + configspace.add_hyperparameter(UniformFloatHyperparameter("x%d" % i, 0, 1)) + + return configspace + + +def test_predict_wrong_X_dimensions(): + rs = np.random.RandomState(1) + + model = RandomForest( + configspace=_get_cs(10), + ) + X = rs.rand(10) + with pytest.raises(ValueError, match="Expected 2d array.*"): + model.predict(X) + + X = rs.rand(10, 10, 10) + with pytest.raises(ValueError, match="Expected 2d array.*"): + model.predict(X) + + X = rs.rand(10, 5) + + with pytest.raises(ValueError, match="Feature mismatch: .*"): + model.predict(X) + + +def test_predict(): + rs = np.random.RandomState(1) + X = rs.rand(20, 10) + Y = rs.rand(10, 1) + model = RandomForest(configspace=_get_cs(10)) + model.train(X[:10], Y[:10]) + m_hat, v_hat = model.predict(X[10:]) + assert m_hat.shape == (10, 1) + assert v_hat.shape == (10, 1) + + +def test_train_with_pca(): + rs = np.random.RandomState(1) + X = rs.rand(20, 20) + Y = rs.rand(20, 1) + + F = {} + for i in range(10): + F[f"instance-{i}"] = list(rs.rand(10)) + + model = RandomForest( + configspace=_get_cs(10), + pca_components=2, + instance_features=F, + ) + model.train(X, Y) + + assert model._n_features == 10 + assert model._n_hps == 10 + assert model._pca is not None + assert model._scaler is not None + + +def test_predict_marginalized_over_instances_wrong_X_dimensions(): + rs = np.random.RandomState(1) + F = {} + for i in range(10): + F[f"instance-{i}"] = list(rs.rand(2)) + + model = RandomForest( + configspace=_get_cs(10), + instance_features=F, + ) + X = rs.rand(10) + with pytest.raises(ValueError, match="Expected 2d array.*"): + model.predict_marginalized(X) + + X = rs.rand(10, 10, 10) + with pytest.raises(ValueError, match="Expected 2d array.*"): + model.predict_marginalized(X) + + +def test_predict_marginalized_no_features(): + """The RF should fall back to the regular predict() method.""" + + rs = np.random.RandomState(1) + X = rs.rand(20, 10) + Y = rs.rand(10, 1) + model = RandomForest(configspace=_get_cs(10)) + model.train(X[:10], Y[:10]) + model.predict(X[10:]) + + +def test_predict_marginalized(): + rs = np.random.RandomState(1) + X = rs.rand(20, 10) + F = {} + for i in range(10): + F[f"instance-{i}"] = list(rs.rand(5)) + Y = rs.rand(len(X) * len(F), 1) + X_ = rs.rand(200, 15) + + model = RandomForest( + configspace=_get_cs(10), + instance_features=F, + ) + model.train(X_, Y) + means, variances = model.predict_marginalized(X) + assert means.shape == (20, 1) + assert variances.shape == (20, 1) + + +def test_predict_marginalized_mocked(): + + rs = np.random.RandomState(1) + F = {} + for i in range(10): + F[f"instance-{i}"] = list(rs.rand(5)) + + model = RandomForest( + configspace=_get_cs(10), + instance_features=F, + ) + X = rs.rand(20, 10) + Y = rs.randint(1, size=(len(X) * len(F), 1)) * 1.0 + X_ = rs.rand(200, 15) + model.train(X_, Y) + means, variances = model.predict_marginalized(rs.rand(11, 10)) + assert means.shape == (11, 1) + assert variances.shape == (11, 1) + for i in range(11): + assert means[i] == 0.0 + assert variances[i] == 1.0e-10 + + +def test_predict_with_actual_values(): + X = np.array( + [ + [0.0, 0.0, 0.0], + [0.0, 0.0, 1.0], + [0.0, 1.0, 0.0], + [0.0, 1.0, 1.0], + [1.0, 0.0, 0.0], + [1.0, 0.0, 1.0], + [1.0, 1.0, 0.0], + [1.0, 1.0, 1.0], + ], + dtype=np.float64, + ) + y = np.array([[0.1], [0.2], [9], [9.2], [100.0], [100.2], [109.0], [109.2]], dtype=np.float64) + model = RandomForest( + configspace=_get_cs(3), + instance_features=None, + seed=12345, + ratio_features=1.0, + ) + model.train(np.vstack((X, X, X, X, X, X, X, X)), np.vstack((y, y, y, y, y, y, y, y))) + + y_hat, _ = model.predict(X) + for y_i, y_hat_i in zip(y.reshape((1, -1)).flatten(), y_hat.reshape((1, -1)).flatten()): + assert pytest.approx(y_i, 0.1) == y_hat_i + + +def test_with_ordinal(): + cs = ConfigurationSpace(seed=0) + _ = cs.add_hyperparameter(CategoricalHyperparameter("a", [0, 1], default_value=0)) + _ = cs.add_hyperparameter(OrdinalHyperparameter("b", [0, 1], default_value=1)) + _ = cs.add_hyperparameter(UniformFloatHyperparameter("c", lower=0.0, upper=1.0, default_value=1)) + _ = cs.add_hyperparameter(UniformIntegerHyperparameter("d", lower=0, upper=10, default_value=1)) + + F = {} + for i in range(1): + F[f"instance-{i}"] = [0, 0, 0] + + model = RandomForest( + configspace=cs, + instance_features=F, + ratio_features=1.0, + pca_components=9, + ) + + X = np.array( + [ + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 9.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 1.0, 4.0, 0.0, 0.0, 0.0], + ], + dtype=np.float64, + ) + y = np.array([0, 1, 2, 3], dtype=np.float64) + + X_train = np.vstack((X, X, X, X, X, X, X, X, X, X)) + y_train = np.vstack((y, y, y, y, y, y, y, y, y, y)) + + model.train(X_train, y_train.reshape((-1, 1))) + mean, _ = model.predict(X) + for idx, m in enumerate(mean): + assert pytest.approx(y[idx], 0.05) == m + + +def test_rf_on_sklearn_data(): + import sklearn.datasets + + X, y = sklearn.datasets.load_boston(return_X_y=True) + rs = np.random.RandomState(1) + + cv = sklearn.model_selection.KFold(shuffle=True, random_state=rs, n_splits=2) + + for do_log in [False, True]: + if do_log: + targets = np.log(y) + model = RandomForest( + configspace=_get_cs(X.shape[1]), + ratio_features=1.0, + pca_components=100, + log_y=True, + ) + maes = [0.43169704431695493156, 0.4267519520332511912] + else: + targets = y + model = RandomForest( + configspace=_get_cs(X.shape[1]), + seed=1, + ratio_features=1.0, + pca_components=100, + ) + maes = [9.3298376833224042496, 9.348010654109179346] + + for i, (train_split, test_split) in enumerate(cv.split(X, targets)): + X_train = X[train_split] + y_train = targets[train_split] + X_test = X[test_split] + y_test = targets[test_split] + model.train(X_train, y_train) + y_hat, mu_hat = model.predict(X_test) + mae = np.mean(np.abs(y_hat - y_test), dtype=np.float) + + assert pytest.approx(mae, 0.1) == maes[i] + + +def test_impute_inactive_hyperparameters(): + cs = ConfigurationSpace(seed=0) + a = cs.add_hyperparameter(CategoricalHyperparameter("a", [0, 1])) + b = cs.add_hyperparameter(CategoricalHyperparameter("b", [0, 1])) + c = cs.add_hyperparameter(UniformFloatHyperparameter("c", 0, 1)) + cs.add_condition(EqualsCondition(b, a, 1)) + cs.add_condition(EqualsCondition(c, a, 0)) + + configs = cs.sample_configuration(size=100) + config_array = convert_configurations_to_array(configs) + for line in config_array: + if line[0] == 0: + assert np.isnan(line[1]) + elif line[0] == 1: + assert np.isnan(line[2]) + + model = RandomForest(configspace=cs) + config_array = model._impute_inactive(config_array) + for line in config_array: + if line[0] == 0: + assert line[1] == 2 + elif line[0] == 1: + assert line[2] == -1 diff --git a/smac/optimizer/configuration_chooser/__init__.py b/tests/test_multi_objective/__init__.py similarity index 100% rename from smac/optimizer/configuration_chooser/__init__.py rename to tests/test_multi_objective/__init__.py diff --git a/tests/test_multi_objective/test_multi_objective.py b/tests/test_multi_objective/test_multi_objective.py new file mode 100644 index 000000000..c6c00ffb6 --- /dev/null +++ b/tests/test_multi_objective/test_multi_objective.py @@ -0,0 +1,43 @@ +import pytest + +from smac.utils.multi_objective import normalize_costs + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +@pytest.fixture +def bounds(): + return [(0, 50), (50, 100)] + + +@pytest.fixture +def bounds_invalid(): + return [(0, 0), (5, 5)] + + +def test_normalize_costs(bounds, bounds_invalid): + # If no bounds are passed, we get the same result back + v = [5, 2] + nv = normalize_costs(v) + assert nv == [5, 2] + + # Normalize between 0..1 given data only + v = [25, 50] + nv = normalize_costs(v, bounds) + assert nv == [0.5, 0] + + # Invalid bounds + v = [25, 50] + nv = normalize_costs(v, bounds_invalid) + assert nv == [1, 1] + + # Invalid input + v = [[25], [50]] + with pytest.raises(AssertionError): + nv = normalize_costs(v, bounds) + + # Wrong shape + v = [25, 50, 75] + with pytest.raises(ValueError): + nv = normalize_costs(v, bounds) diff --git a/tests/test_multi_objective/test_schaffer.py b/tests/test_multi_objective/test_schaffer.py index 40facd513..9b712e877 100644 --- a/tests/test_multi_objective/test_schaffer.py +++ b/tests/test_multi_objective/test_schaffer.py @@ -1,19 +1,18 @@ -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - -import unittest - import numpy as np -from ConfigSpace.hyperparameters import UniformFloatHyperparameter -from matplotlib import pyplot as plt - -from smac.configspace import ConfigurationSpace -from smac.facade.roar_facade import ROAR -from smac.facade.smac_ac_facade import SMAC4AC -from smac.facade.smac_bb_facade import SMAC4BB -from smac.facade.smac_hpo_facade import SMAC4HPO +import pytest +from ConfigSpace import ConfigurationSpace, Float + +from smac import ( + AlgorithmConfigurationFacade, + BlackBoxFacade, + HyperparameterOptimizationFacade, + RandomFacade, +) +from smac.multi_objective.aggregation_strategy import MeanAggregationStrategy from smac.multi_objective.parego import ParEGO -from smac.scenario.scenario import Scenario + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" MIN_V = -2 MAX_V = 2 @@ -40,88 +39,68 @@ def get_optimum(): return optimum -def plot(all_x): - plt.figure() +def tae(cfg, seed=0): + f1, f2 = schaffer(cfg["x"]) + return {"cost1": f1, "cost2": f2} - for x in all_x: - f1, f2 = schaffer(x) - plt.scatter(f1, f2, c="blue", alpha=0.2, zorder=3000) - plt.vlines([1], 0, 4, linestyles="dashed", colors=["red"]) - plt.hlines([1], 0, 4, linestyles="dashed", colors=["red"]) +@pytest.fixture +def configspace(): + cs = ConfigurationSpace() + cs.add_hyperparameter(Float("x", (MIN_V, MAX_V))) - plt.show() + return cs -def plot_from_smac(smac): - rh = smac.get_runhistory() - all_x = [] - for (config_id, _, _, _) in rh.data.keys(): - config = rh.ids_config[config_id] - all_x.append(config["x"]) +@pytest.mark.parametrize( + "facade", [BlackBoxFacade, HyperparameterOptimizationFacade, AlgorithmConfigurationFacade, RandomFacade] +) +def test_mean_aggregation(facade, make_scenario, configspace): + scenario = make_scenario(configspace, use_multi_objective=True) - plot(all_x) + smac = facade( + scenario=scenario, + target_function=tae, + multi_objective_algorithm=MeanAggregationStrategy(scenario=scenario), + overwrite=True, + ) + incumbent = smac.optimize() + f1_inc, f2_inc = schaffer(incumbent["x"]) + f1_opt, f2_opt = get_optimum() -def tae(cfg): - f1, f2 = schaffer(cfg["x"]) - return {"metric1": f1, "metric2": f2} - - -class SchafferTest(unittest.TestCase): - def setUp(self): - self.cs = ConfigurationSpace() - self.cs.add_hyperparameter(UniformFloatHyperparameter("x", lower=MIN_V, upper=MAX_V)) - - # Scenario object - self.scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternatively runtime) - "runcount-limit": 25, # max. number of function evaluations - "cs": self.cs, # configuration space - "deterministic": True, - "multi_objectives": "metric1, metric2", - "limit_resources": False, - } - ) - - self.facade_kwargs = { - "scenario": self.scenario, - "rng": np.random.RandomState(0), - "tae_runner": tae, - } - - self.parego_facade_kwargs = { - "scenario": self.scenario, - "rng": np.random.RandomState(0), - "tae_runner": tae, - "multi_objective_algorithm": ParEGO, - "multi_objective_kwargs": {"rho": 0.05}, - } - - def test_facades(self): - results = [] - for facade in [ROAR, SMAC4BB, SMAC4HPO, SMAC4AC]: - for kwargs in [self.facade_kwargs, self.parego_facade_kwargs]: - smac = facade(**kwargs) - incumbent = smac.optimize() - - f1_inc, f2_inc = schaffer(incumbent["x"]) - f1_opt, f2_opt = get_optimum() - - inc = f1_inc + f2_inc - opt = f1_opt + f2_opt - diff = abs(inc - opt) - - assert diff < 0.5 - results.append(smac) - - return results - - -if __name__ == "__main__": - t = SchafferTest() - t.setUp() - - for smac in t.test_facades(): - plot_from_smac(smac) + inc = f1_inc + f2_inc + opt = f1_opt + f2_opt + diff = abs(inc - opt) + + assert diff < 0.06 + + +@pytest.mark.parametrize( + "facade", [BlackBoxFacade, HyperparameterOptimizationFacade, AlgorithmConfigurationFacade, RandomFacade] +) +def test_parego(facade, make_scenario, configspace): + scenario = make_scenario(configspace, use_multi_objective=True) + + smac = facade( + scenario=scenario, + target_function=tae, + multi_objective_algorithm=ParEGO(scenario=scenario), + overwrite=True, + ) + # The incumbent is not ambiguously because we have a Pareto front + smac.optimize() + + # We use the mean aggregation strategy to get the same weights + multi_objective_algorithm = MeanAggregationStrategy(scenario=scenario) + smac.runhistory.multi_objective_algorithm = multi_objective_algorithm + + incumbent, _ = smac.runhistory.get_incumbent() + + f1_inc, f2_inc = schaffer(incumbent["x"]) + f1_opt, f2_opt = get_optimum() + inc = f1_inc + f2_inc + opt = f1_opt + f2_opt + diff = abs(inc - opt) + + assert diff < 0.06 diff --git a/tests/test_multi_objective/test_schaffer_upscaled.py b/tests/test_multi_objective/test_schaffer_upscaled.py index dd3264f8b..541e6e81f 100644 --- a/tests/test_multi_objective/test_schaffer_upscaled.py +++ b/tests/test_multi_objective/test_schaffer_upscaled.py @@ -1,15 +1,17 @@ -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - -import unittest - import numpy as np -from ConfigSpace.hyperparameters import UniformFloatHyperparameter -from matplotlib import pyplot as plt +import pytest +from ConfigSpace import ConfigurationSpace, Float + +from smac import ( + AlgorithmConfigurationFacade, + BlackBoxFacade, + HyperparameterOptimizationFacade, +) +from smac.multi_objective.aggregation_strategy import MeanAggregationStrategy +from smac.multi_objective.parego import ParEGO -from smac.configspace import ConfigurationSpace -from smac.facade.smac_ac_facade import SMAC4AC -from smac.scenario.scenario import Scenario +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" MIN_V = -2 MAX_V = 2 @@ -29,7 +31,6 @@ def get_optimum(): for v in np.linspace(MIN_V, MAX_V, 200): f1, f2 = schaffer(v) - f2 = f2 / UPSCALING_FACTOR if f1 + f2 < optimum_sum: @@ -39,78 +40,66 @@ def get_optimum(): return optimum -def plot(all_x): - plt.figure() - for x in all_x: - f1, f2 = schaffer(x) - plt.scatter(f1, f2, c="blue", alpha=0.1) - - plt.show() - +def tae(cfg, seed=0): + f1, f2 = schaffer(cfg["x"]) + return {"cost1": f1, "cost2": f2} -def plot_from_smac(smac): - rh = smac.get_runhistory() - all_x = [] - for (config_id, instance_id, seed, budget), ( - cost, - time, - status, - starttime, - endtime, - additional_info, - ) in rh.data.items(): - config = rh.ids_config[config_id] - all_x.append(config["x"]) - plot(all_x) +@pytest.fixture +def configspace(): + cs = ConfigurationSpace() + cs.add_hyperparameter(Float("x", (MIN_V, MAX_V))) + return cs -def tae(cfg): - f1, f2 = schaffer(cfg["x"]) - return {"metric1": f1, "metric2": f2} +@pytest.mark.parametrize("facade", [BlackBoxFacade, HyperparameterOptimizationFacade, AlgorithmConfigurationFacade]) +def test_mean_aggregation(facade, make_scenario, configspace): + scenario = make_scenario(configspace, use_multi_objective=True) + smac = facade( + scenario=scenario, + target_function=tae, + multi_objective_algorithm=MeanAggregationStrategy(scenario=scenario), + overwrite=True, + ) + incumbent = smac.optimize() -class SchafferTest(unittest.TestCase): - def setUp(self): - self.cs = ConfigurationSpace() - self.cs.add_hyperparameter(UniformFloatHyperparameter("x", lower=MIN_V, upper=MAX_V)) + f1_inc, f2_inc = schaffer(incumbent["x"]) + f1_opt, f2_opt = get_optimum() + f2_inc = f2_inc / UPSCALING_FACTOR - # Scenario object - self.scenario = Scenario( - { - "run_obj": "quality", # we optimize quality (alternatively runtime) - "runcount-limit": 50, # max. number of function evaluations - "cs": self.cs, # configuration space - "deterministic": True, - "multi_objectives": "metric1, metric2", - "limit_resources": False, - } - ) + inc = f1_inc + f2_inc + opt = f1_opt + f2_opt + diff = abs(inc - opt) - self.facade_kwargs = { - "scenario": self.scenario, - "rng": np.random.RandomState(0), - "tae_runner": tae, - } + assert diff < 0.06 - def test_AC(self): - smac = SMAC4AC(**self.facade_kwargs) - incumbent = smac.optimize() - f1_inc, f2_inc = schaffer(incumbent["x"]) - f1_opt, f2_opt = get_optimum() +@pytest.mark.parametrize("facade", [BlackBoxFacade, HyperparameterOptimizationFacade, AlgorithmConfigurationFacade]) +def test_parego(facade, make_scenario, configspace): + scenario = make_scenario(configspace, use_multi_objective=True) - f2_inc = f2_inc / UPSCALING_FACTOR + smac = facade( + scenario=scenario, + target_function=tae, + multi_objective_algorithm=ParEGO(scenario=scenario), + overwrite=True, + ) + smac.optimize() - self.assertAlmostEqual(f1_inc + f2_inc, f1_opt + f2_opt, places=1) + # We use the mean aggregation strategy to get the same weights + multi_objective_algorithm = MeanAggregationStrategy(scenario=scenario) + smac.runhistory.multi_objective_algorithm = multi_objective_algorithm - return smac + incumbent, _ = smac.runhistory.get_incumbent() + f1_inc, f2_inc = schaffer(incumbent["x"]) + f1_opt, f2_opt = get_optimum() + f2_inc = f2_inc / UPSCALING_FACTOR -if __name__ == "__main__": - t = SchafferTest() - t.setUp() + inc = f1_inc + f2_inc + opt = f1_opt + f2_opt + diff = abs(inc - opt) - smac = t.test_AC() - plot_from_smac(smac) + assert diff < 0.06 diff --git a/smac/scenario/__init__.py b/tests/test_random_design/__init__.py similarity index 100% rename from smac/scenario/__init__.py rename to tests/test_random_design/__init__.py diff --git a/tests/test_random_design/test_annealing_design.py b/tests/test_random_design/test_annealing_design.py new file mode 100644 index 000000000..284b37458 --- /dev/null +++ b/tests/test_random_design/test_annealing_design.py @@ -0,0 +1,17 @@ +import numpy as np + +from smac.random_design.annealing_design import CosineAnnealingRandomDesign + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def test_cosineannealing_down(): + c = CosineAnnealingRandomDesign(seed=1, min_probability=0.0, max_probability=1.0, restart_iteration=5) + expected = [1.0, 0.8535, 0.5, 0.1464, 0.0] + for i in range(20): + stats = [] + for j in range(10000): + stats.append(c.check(j)) + assert np.isclose(np.mean(stats), expected[i % 5], atol=10**-2) + c.next_iteration() diff --git a/tests/test_random_design/test_modulus_design.py b/tests/test_random_design/test_modulus_design.py new file mode 100644 index 000000000..0bc8a4906 --- /dev/null +++ b/tests/test_random_design/test_modulus_design.py @@ -0,0 +1,53 @@ +from smac.random_design.modulus_design import ( + DynamicModulusRandomDesign, + ModulusRandomDesign, +) + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def test_no_cool_down(): + c = ModulusRandomDesign(seed=1, modulus=3.0) + for _ in range(10): + assert c.check(1) is False + assert c.check(2) is False + assert c.check(3) is True + assert c.check(4) is False + assert c.check(5) is False + assert c.check(6) is True + assert c.check(30) is True + c.next_iteration() + + c = ModulusRandomDesign(seed=1, modulus=1.0) + for _ in range(10): + assert c.check(1) is True + assert c.check(2) is True + assert c.check(30) is True + c.next_iteration() + + +def test_linear_cool_down(): + c = DynamicModulusRandomDesign(seed=1, start_modulus=2.0, modulus_increment=1.0, end_modulus=4.0) + for i in range(1, 100, 2): + assert c.check(i) is False + assert c.check(i + 1) is True + + c.next_iteration() + for i in range(1, 100, 3): + assert c.check(i) is False + assert c.check(i + 1) is False + assert c.check(i + 2) is True + + for i in range(10): + c.next_iteration() + for i in [1, 2, 3]: + assert c.check(i) is False + assert c.check(4) is True + for i in [5, 6, 7]: + assert c.check(i) is False + assert c.check(8) is True + # Repeat + for i in [5, 6, 7]: + assert c.check(i) is False + assert c.check(8) is True diff --git a/tests/test_random_design/test_probability_design.py b/tests/test_random_design/test_probability_design.py new file mode 100644 index 000000000..14540426b --- /dev/null +++ b/tests/test_random_design/test_probability_design.py @@ -0,0 +1,31 @@ +import numpy as np + +from smac.random_design.probability_design import ( + DynamicProbabilityRandomDesign, + ProbabilityRandomDesign, +) + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def test_chooser_prob(): + for i in range(10): + c = ProbabilityRandomDesign(seed=1, probability=0.1 * i) + stats = [] + for j in range(100000): + stats.append(c.check(j)) + assert np.isclose(np.mean(stats), 0.1 * i, atol=10**-2) + + +def test_cooldownchooser_prob(): + for i in range(10): + c = DynamicProbabilityRandomDesign(seed=1, probability=0.1 * i, factor=0.5) + expected = 0.1 * i + for _ in range(3): + stats = [] + for j in range(100000): + stats.append(c.check(j)) + assert np.isclose(np.mean(stats), expected, atol=10**-2) + expected *= 0.5 + c.next_iteration() diff --git a/tests/test_random_design/test_random_design.py b/tests/test_random_design/test_random_design.py new file mode 100644 index 000000000..6e9434961 --- /dev/null +++ b/tests/test_random_design/test_random_design.py @@ -0,0 +1,38 @@ +from smac.random_design.annealing_design import CosineAnnealingRandomDesign +from smac.random_design.modulus_design import ( + DynamicModulusRandomDesign, + ModulusRandomDesign, +) +from smac.random_design.probability_design import ProbabilityRandomDesign + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def test_chooser_rng(): + dc = { + "NoCoolDownRandomDesign": [ModulusRandomDesign, {"modulus": 2}], + "LinearCoolDownRandomDesign": [ + DynamicModulusRandomDesign, + { + "start_modulus": 2, + "modulus_increment": 1, + "end_modulus": 10, + }, + ], + "ProbabilityRandomDesign": [ProbabilityRandomDesign, {"probability": 0.5}], + "CosineAnnealingRandomDesign": [ + CosineAnnealingRandomDesign, + {"min_probability": 0.0, "max_probability": 0.9, "restart_iteration": 7}, + ], + } + + for method in dc: + for i in range(100): + c1 = dc[method][0](**dc[method][1], seed=i) + c2 = dc[method][0](**dc[method][1], seed=i) + for i in range(100): + assert c1.check(0) == c2.check(0) + assert c1.check(10) == c2.check(10) + c1.next_iteration() + c2.next_iteration() diff --git a/tests/test_runhistory/test_rfr_imputor.py b/tests/test_runhistory/test_rfr_imputor.py deleted file mode 100644 index 02429a62e..000000000 --- a/tests/test_runhistory/test_rfr_imputor.py +++ /dev/null @@ -1,251 +0,0 @@ -import logging -import unittest -import unittest.mock - -import numpy -from ConfigSpace import Configuration, ConfigurationSpace -from ConfigSpace.hyperparameters import ( - CategoricalHyperparameter, - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) - -from smac.epm.random_forest import rfr_imputator -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances -from smac.epm.utils import get_types -from smac.runhistory import runhistory, runhistory2epm -from smac.scenario import scenario -from smac.tae import StatusType - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def generate_config(cs, rs): - i = rs.randint(-10, 10) - f = rs.rand(1)[0] - seed = rs.randint(0, 10000) - - # 'a' occurs more often than 'b' - c = "a" if rs.binomial(1, 0.2) == 0 else "b" - - # We have 100 instance, but prefer the middle ones - instance_id = int(rs.normal(loc=50, scale=20, size=1)[0]) - instance_id = min(max(0, instance_id), 100) - - status = StatusType.SUCCESS - runtime = 10 ** (numpy.sin(i) + f) + seed / 10000 - numpy.sin(instance_id) - - if runtime > 40: - status = StatusType.TIMEOUT - runtime = 40 - elif instance_id > 50 and runtime > 15: - # This is a timeout with probability 0.5 - status = StatusType.TIMEOUT - runtime /= 2.0 - - config = Configuration(cs, values={"cat_a_b": c, "float_0_1": f, "integer_0_100": i}) - - return config, seed, runtime, status, instance_id - - -class Scen(scenario.Scenario): - """ - DUMMY class to fake scenario - """ - - def __init__(self): - self.run_obj = None - self.overall_obj = None - self.cutoff = None - self.feature_dict = None - self.n_features = 0 - self.par_factor = 1 - - -class ImputorTest(unittest.TestCase): - def setUp(self): - logging.basicConfig(level=logging.DEBUG) - - def get_model(self, cs, instance_features=None): - if instance_features: - instance_features = numpy.array([instance_features[key] for key in instance_features]) - types, bounds = get_types(cs, instance_features) - model = RandomForestWithInstances( - configspace=cs, - types=types, - bounds=bounds, - instance_features=instance_features, - seed=1234567980, - pca_components=7, - ) - return model - - def get_runhistory(self, num_success, num_capped, num_timeout): - cs = ConfigurationSpace() - cs.add_hyperparameter(CategoricalHyperparameter(name="cat_a_b", choices=["a", "b"], default_value="a")) - cs.add_hyperparameter(UniformFloatHyperparameter(name="float_0_1", lower=0, upper=1, default_value=0.5)) - cs.add_hyperparameter(UniformIntegerHyperparameter(name="integer_0_100", lower=-10, upper=10, default_value=0)) - - rh = runhistory.RunHistory() - rs = numpy.random.RandomState(1) - successes = 0 - capped = 0 - timeouts = 0 - while successes < num_success or capped < num_capped or timeouts < num_timeout: - config, seed, runtime, status, instance_id = generate_config(cs=cs, rs=rs) - if status == StatusType.SUCCESS and successes < num_success: - successes += 1 - add = True - elif status == StatusType.TIMEOUT: - if runtime < 40 and capped < num_capped: - capped += 1 - add = True - elif runtime == 40 and timeouts < num_timeout: - timeouts += 1 - add = True - else: - add = False - else: - add = False - - if add: - rh.add( - config=config, - cost=runtime, - time=runtime, - status=status, - instance_id=instance_id, - seed=seed, - additional_info=None, - ) - return cs, rh - - def get_scenario(self, instance_features=None): - scen = Scen() - scen.run_obj = "runtime" - scen.overall_obj = "par10" - # we only test single objective here - scen.multi_objectives = None - scen.cutoff = 40 - if instance_features: - scen.feature_dict = instance_features - scen.n_features = len(list(instance_features.values())[0]) - return scen - - def testRandomImputation(self): - rs = numpy.random.RandomState(1) - - for i in range(0, 150, 15): - # First random imputation sanity check - num_samples = max(1, i * 10) - num_feat = max(1, i) - num_censored = int(num_samples * 0.1) - X = rs.rand(num_samples, num_feat) - y = numpy.sin(X[:, 0:1]) - - cutoff = max(y) * 0.9 - y[y > cutoff] = cutoff - - # We have some cen data - cen_X = X[:num_censored, :] - cen_y = y[:num_censored] - uncen_X = X[num_censored:, :] - uncen_y = y[num_censored:] - - cen_y /= 2 - - cs = ConfigurationSpace() - for i in range(num_feat): - cs.add_hyperparameter(UniformFloatHyperparameter(name="a_%d" % i, lower=0, upper=1, default_value=0.5)) - - imputor = rfr_imputator.RFRImputator( - rng=rs, - cutoff=cutoff, - threshold=cutoff * 10, - change_threshold=0.01, - max_iter=5, - model=self.get_model(cs), - ) - - imp_y = imputor.impute(censored_X=cen_X, censored_y=cen_y, uncensored_X=uncen_X, uncensored_y=uncen_y) - - if imp_y is None: - continue - - for idx in range(cen_y.shape[0]): - self.assertGreater(imp_y[idx], cen_y[idx]) - self.assertTrue(numpy.isfinite(imp_y).all()) - - def testRealImputation(self): - - # Without instance features - rs = numpy.random.RandomState(1) - - cs, rh = self.get_runhistory(num_success=5, num_timeout=1, num_capped=2) - - scen = self.get_scenario() - model = self.get_model(cs) - imputor = rfr_imputator.RFRImputator( - rng=rs, - cutoff=scen.cutoff, - threshold=scen.cutoff * 10, - change_threshold=0.01, - max_iter=10, - model=model, - ) - - r2e = runhistory2epm.RunHistory2EPM4LogCost( - scenario=scen, - num_params=3, - success_states=[ - StatusType.SUCCESS, - ], - impute_censored_data=True, - impute_state=[StatusType.TIMEOUT], - imputor=imputor, - rng=rs, - ) - - self.assertEqual(r2e.transform(rh)[1].shape, (8, 1)) - self.assertEqual(r2e.transform(rh)[1].shape, (8, 1)) - - # Now with instance features - instance_features = {run_key.instance_id: numpy.random.rand(10) for run_key in rh.data} - scen = self.get_scenario(instance_features) - model = self.get_model(cs, instance_features) - - with unittest.mock.patch.object(model, attribute="train", wraps=model.train) as train_wrapper: - imputor = rfr_imputator.RFRImputator( - rng=rs, - cutoff=scen.cutoff, - threshold=scen.cutoff * 10, - change_threshold=0.01, - max_iter=10, - model=model, - ) - r2e = runhistory2epm.RunHistory2EPM4LogCost( - scenario=scen, - num_params=3, - success_states=[ - StatusType.SUCCESS, - ], - impute_censored_data=True, - impute_state=[StatusType.TIMEOUT], - imputor=imputor, - rng=rs, - ) - X, y = r2e.transform(rh) - self.assertEqual(X.shape, (8, 13)) - self.assertEqual(y.shape, (8, 1)) - num_calls = len(train_wrapper.call_args_list) - self.assertGreater(num_calls, 1) - self.assertEqual(train_wrapper.call_args_list[0][0][0].shape, (5, 13)) - self.assertEqual(train_wrapper.call_args_list[1][0][0].shape, (8, 13)) - - X, y = r2e.transform(rh) - self.assertEqual(X.shape, (8, 13)) - self.assertEqual(y.shape, (8, 1)) - self.assertGreater(len(train_wrapper.call_args_list), num_calls + 1) - self.assertEqual(train_wrapper.call_args_list[num_calls][0][0].shape, (5, 13)) - self.assertEqual(train_wrapper.call_args_list[num_calls + 1][0][0].shape, (8, 13)) diff --git a/tests/test_runhistory/test_runhistory.py b/tests/test_runhistory/test_runhistory.py index 15b375e3a..18e30d69e 100644 --- a/tests/test_runhistory/test_runhistory.py +++ b/tests/test_runhistory/test_runhistory.py @@ -1,544 +1,529 @@ -from typing import Optional +from __future__ import annotations import os import pickle import tempfile -import unittest -from ConfigSpace import Configuration, ConfigurationSpace -from ConfigSpace.hyperparameters import UniformIntegerHyperparameter +import pytest -from smac.runhistory.runhistory import RunHistory, RunKey -from smac.tae import StatusType +from smac.runhistory.runhistory import RunHistory, TrialKey +from smac.runner.abstract_runner import StatusType __copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" __license__ = "3-clause BSD" -def get_config_space(): - cs = ConfigurationSpace() - cs.add_hyperparameter(UniformIntegerHyperparameter(name="a", lower=0, upper=100)) - cs.add_hyperparameter(UniformIntegerHyperparameter(name="b", lower=0, upper=100)) - return cs +@pytest.fixture +def config1(configspace_small): + configspace_small.seed(0) + return configspace_small.sample_configuration() -class RunhistoryTest(unittest.TestCase): - def test_add_and_pickle(self): - """ - simply adding some rundata to runhistory, then pickle it - """ - rh = RunHistory() - cs = get_config_space() - config = Configuration(cs, values={"a": 1, "b": 2}) +@pytest.fixture +def config2(configspace_small): + configspace_small.seed(1) + return configspace_small.sample_configuration() - self.assertTrue(rh.empty()) - rh.add( - config=config, - cost=10, - time=20, - status=StatusType.SUCCESS, - instance_id=None, - seed=None, - starttime=100, - endtime=120, - additional_info=None, - ) +@pytest.fixture +def config3(configspace_small): + configspace_small.seed(2) + return configspace_small.sample_configuration() - rh.add( - config=config, - cost=10, - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=12354, - starttime=10, - endtime=30, - additional_info={"start_time": 10}, - ) - self.assertFalse(rh.empty()) - - tmpfile = tempfile.NamedTemporaryFile(mode="wb", delete=False) - pickle.dump(rh, tmpfile, -1) - name = tmpfile.name - tmpfile.close() - - with open(name, "rb") as fh: - loaded_rh = pickle.load(fh) # nosec - self.assertEqual(loaded_rh.data, rh.data) - - def test_illegal_input(self): - rh = RunHistory() - - with self.assertRaisesRegex(TypeError, "Configuration to add to the runhistory must not be None"): - rh.add(config=None, cost=1.23, time=2.34, status=StatusType.SUCCESS) - - with self.assertRaisesRegex( - TypeError, - "Configuration to add to the runhistory is not of type Configuration, but ", - ): - rh.add(config="abc", cost=1.23, time=2.34, status=StatusType.SUCCESS) - - def test_add_multiple_times(self): - rh = RunHistory() - cs = get_config_space() - config = Configuration(cs, values={"a": 1, "b": 2}) - - for i in range(5): - rh.add( - config=config, - cost=i + 1, - time=i + 1, - status=StatusType.SUCCESS, - instance_id=None, - seed=12345, - additional_info=None, - budget=0, - ) - - self.assertEqual(len(rh.data), 1) - self.assertEqual(len(rh.get_runs_for_config(config, only_max_observed_budget=True)), 1) - self.assertEqual(len(rh._configid_to_inst_seed_budget[1]), 1) - self.assertEqual(list(rh.data.values())[0].cost, 1) - - def test_get_config_runs(self): - """ - get some config runs from runhistory - """ - # return max observed budget only - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) - config2 = Configuration(cs, values={"a": 1, "b": 3}) - rh.add( - config=config1, - cost=10, - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - rh.add( - config=config1, - cost=10, - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=2, - ) - with self.assertRaisesRegex(ValueError, "This should not happen!"): - rh.add( - config=config1, - cost=10, - time=20, - status=StatusType.SUCCESS, - instance_id=2, - seed=2, - budget=1, - ) - - rh.add( - config=config2, - cost=10, - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) +def test_add_and_pickle(runhistory, config1): + """ + simply adding some rundata to runhistory, then pickle it + """ + assert runhistory.empty() - ist = rh.get_runs_for_config(config=config1, only_max_observed_budget=True) + runhistory.add( + config=config1, + cost=10, + time=20, + status=StatusType.SUCCESS, + instance=None, + seed=None, + starttime=100, + endtime=120, + additional_info=None, + ) + + runhistory.add( + config=config1, + cost=10, + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=12354, + starttime=10, + endtime=30, + additional_info={"start_time": 10}, + ) - self.assertEqual(len(ist), 2) - self.assertEqual(ist[0].instance, 1) - self.assertEqual(ist[1].instance, 2) - self.assertEqual(ist[0].budget, 2) - self.assertEqual(ist[1].budget, 1) + assert not runhistory.empty() - # multiple budgets (only_max_observed_budget=False) - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) - config2 = Configuration(cs, values={"a": 1, "b": 3}) - rh.add( - config=config1, - cost=5, - time=10, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - rh.add( - config=config1, - cost=10, - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=2, - ) + tmpfile = tempfile.NamedTemporaryFile(mode="wb", delete=False) + pickle.dump(runhistory, tmpfile, -1) + name = tmpfile.name + tmpfile.close() - rh.add( - config=config2, - cost=5, - time=10, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - rh.add( - config=config2, - cost=10, - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=2, - ) + with open(name, "rb") as fh: + loaded_runhistory = pickle.load(fh) + + assert loaded_runhistory._data == runhistory._data + + +def test_illegal_input(runhistory): + + with pytest.raises(TypeError, match="Configuration must not be None."): + runhistory.add(config=None, cost=1.23, time=2.34, status=StatusType.SUCCESS) - ist = rh.get_runs_for_config(config=config1, only_max_observed_budget=False) + with pytest.raises( + TypeError, + match="Configuration is not of type Configuration, but .*str.*", + ): + runhistory.add(config="abc", cost=1.23, time=2.34, status=StatusType.SUCCESS) - self.assertEqual(len(ist), 2) - self.assertEqual(ist[0].instance, 1) - self.assertEqual(ist[0].budget, 1) - self.assertEqual(ist[1].budget, 2) - def test_full_update(self): - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) - config2 = Configuration(cs, values={"a": 1, "b": 3}) - rh.add( +def test_add_multiple_times(runhistory, config1): + + for i in range(5): + runhistory.add( config=config1, - cost=10, - time=20, + cost=i + 1, + time=i + 1, status=StatusType.SUCCESS, - instance_id=1, - seed=1, + instance=None, + seed=12345, + additional_info=None, + budget=0, ) - rh.add( - config=config2, - cost=10, - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - ) + assert len(runhistory._data) == 1 + assert len(runhistory.get_trials(config1, only_max_observed_budget=True)) == 1 + assert len(runhistory._config_id_to_isk_to_budget[1]) == 1 + assert list(runhistory._data.values())[0].cost == 1 + + +def test_get_config_runs(runhistory, config1, config2): + """ + get some config runs from runhistory + """ + # Return max observed budget only + runhistory.add( + config=config1, + cost=10, + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + runhistory.add( + config=config1, + cost=10, + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=2, + ) + + # Why should this not happen? + # with pytest.raises(ValueError, "This should not happen!"): + # runhistory.add( + # config=config1, + # cost=10, + # time=20, + # status=StatusType.SUCCESS, + # instance=2, + # seed=2, + # budget=1, + # ) + + runhistory.add( + config=config2, + cost=10, + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + ist = runhistory.get_trials(config=config1, only_max_observed_budget=True) + + # assert len(ist) == 2 + assert len(ist) == 1 + + assert ist[0].instance == 1 + # assert ist[1].instance == 2 + assert ist[0].budget == 2 + # assert ist[1].budget == 1 + + +def test_get_config_runs2(runhistory, config1, config2): + """ + get some config runs from runhistory (multiple budgets (only_max_observed_budget=False)) + """ + runhistory.add( + config=config1, + cost=5, + time=10, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + runhistory.add( + config=config1, + cost=10, + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=2, + ) + + runhistory.add( + config=config2, + cost=5, + time=10, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + runhistory.add( + config=config2, + cost=10, + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=2, + ) - rh.add( - config=config2, - cost=20, - time=20, - status=StatusType.SUCCESS, - instance_id=2, - seed=2, - ) + ist = runhistory.get_trials(config=config1, only_max_observed_budget=False) - cost_config2 = rh.get_cost(config2) + assert len(ist) == 2 + assert ist[0].instance == 1 + assert ist[0].budget == 1 + assert ist[1].budget == 2 - rh.compute_all_costs() - updated_cost_config2 = rh.get_cost(config2) - self.assertEqual(cost_config2, updated_cost_config2) - rh.compute_all_costs(instances=[2]) - updated_cost_config2 = rh.get_cost(config2) - self.assertNotEqual(cost_config2, updated_cost_config2) - self.assertEqual(updated_cost_config2, 20) +def test_full_update(runhistory, config1, config2): + runhistory.add( + config=config1, + cost=10, + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + ) + + runhistory.add( + config=config2, + cost=10, + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + ) + + runhistory.add( + config=config2, + cost=20, + time=20, + status=StatusType.SUCCESS, + instance=2, + seed=2, + ) - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) - config2 = Configuration(cs, values={"a": 1, "b": 3}) - rh.add( - config=config1, - cost=[10], - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - ) + cost_config2 = runhistory.get_cost(config2) - rh.add( - config=config2, - cost=[10], - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - ) + runhistory.update_costs() + updated_cost_config2 = runhistory.get_cost(config2) + assert cost_config2 == updated_cost_config2 - rh.add( - config=config2, - cost=[20], - time=20, - status=StatusType.SUCCESS, - instance_id=2, - seed=2, - ) + runhistory.update_costs(instances=[2]) + updated_cost_config2 = runhistory.get_cost(config2) + assert cost_config2 != updated_cost_config2 + assert updated_cost_config2 == 20 - cost_config2 = rh.get_cost(config2) - rh.compute_all_costs() - updated_cost_config2 = rh.get_cost(config2) - self.assertEqual(cost_config2, updated_cost_config2) +def test_full_update2(runhistory, config1, config2): + runhistory.add( + config=config1, + cost=[10], + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + ) + + runhistory.add( + config=config2, + cost=[10], + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + ) + + runhistory.add( + config=config2, + cost=[20], + time=20, + status=StatusType.SUCCESS, + instance=2, + seed=2, + ) - rh.compute_all_costs(instances=[2]) - updated_cost_config2 = rh.get_cost(config2) - self.assertNotEqual(cost_config2, updated_cost_config2) - self.assertEqual(updated_cost_config2, 20) + cost_config2 = runhistory.get_cost(config2) - def test_incremental_update(self): + runhistory.update_costs() + updated_cost_config2 = runhistory.get_cost(config2) + assert cost_config2 == updated_cost_config2 - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) + runhistory.update_costs(instances=[2]) + updated_cost_config2 = runhistory.get_cost(config2) + assert cost_config2 != updated_cost_config2 + assert updated_cost_config2 == 20 - rh.add( - config=config1, - cost=10, - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - ) - self.assertEqual(rh.get_cost(config1), 10) +def test_incremental_update(runhistory, config1): - rh.add( - config=config1, - cost=20, - time=20, - status=StatusType.SUCCESS, - instance_id=2, - seed=1, - ) + runhistory.add( + config=config1, + cost=10, + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + ) - self.assertEqual(rh.get_cost(config1), 15) + assert runhistory.get_cost(config1) == 10 - def test_multiple_budgets(self): + runhistory.add( + config=config1, + cost=20, + time=20, + status=StatusType.SUCCESS, + instance=2, + seed=1, + ) - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) + assert runhistory.get_cost(config1) == 15 - rh.add( - config=config1, - cost=10, - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - self.assertEqual(rh.get_cost(config1), 10) +def test_multiple_budgets(runhistory, config1): - # only the higher budget gets included in the config cost - rh.add( - config=config1, - cost=20, - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=2, - ) + runhistory.add( + config=config1, + cost=10, + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + assert runhistory.get_cost(config1) == 10 + + # only the higher budget gets included in the config cost + runhistory.add( + config=config1, + cost=20, + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=2, + ) - self.assertEqual(rh.get_cost(config1), 20) - self.assertEqual(rh.get_min_cost(config1), 10) + assert runhistory.get_cost(config1) == 20 + assert runhistory.get_min_cost(config1) == 10 - def test_get_configs_per_budget(self): - rh = RunHistory() - cs = get_config_space() +def test_get_configs_per_budget(runhistory, config1, config2, config3): + runhistory.add( + config=config1, + cost=10, + time=10, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + runhistory.add( + config=config2, + cost=20, + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + runhistory.add( + config=config3, + cost=30, + time=30, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=3, + ) + + assert runhistory.get_configs_per_budget([1]) == [config1, config2] + + +def test_json_origin(configspace_small, config1): - config1 = Configuration(cs, values={"a": 1, "b": 1}) - rh.add( + for i, origin in enumerate(["test_origin", None]): + config1.origin = origin + runhistory = RunHistory() + runhistory.add( config=config1, cost=10, - time=10, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - - config2 = Configuration(cs, values={"a": 2, "b": 2}) - rh.add( - config=config2, - cost=20, time=20, status=StatusType.SUCCESS, - instance_id=1, + instance=1, seed=1, - budget=1, ) - config3 = Configuration(cs, values={"a": 3, "b": 3}) - rh.add( - config=config3, - cost=30, - time=30, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=3, - ) - - self.assertListEqual(rh.get_all_configs_per_budget([1]), [config1, config2]) + path = f"tests/test_files/test_json_origin_{i}.json" + runhistory.save_json(path) + runhistory.load_json(path, configspace_small) - def test_json_origin(self): + assert runhistory.get_configs()[0].origin == origin - for origin in ["test_origin", None]: - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}, origin=origin) + os.remove(path) - rh.add( - config=config1, - cost=10, - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - ) - path = "tests/test_files/test_json_origin.json" - rh.save_json(path) - _ = rh.load_json(path, cs) +def add_item( + runhistory, + config, + cost: float = 0.0, + time: float = 1.0, + seed: int = 0, + instance: str | None = None, + budget: float | None = None, + status=StatusType.SUCCESS, +) -> TrialKey: + """No easy way to generate a key before hand.""" + runhistory.add( + config=config, + cost=cost, + time=time, + instance=instance, + seed=seed, + budget=budget, + status=status, + ) - self.assertEqual(rh.get_all_configs()[0].origin, origin) + return TrialKey( + config_id=runhistory._n_id, # What's used internally during `add` + instance=instance, + seed=seed, + budget=budget, + ) - os.remove(path) +def test_contains(runhistory, config1): + """Test that keys added are contained `in` and not if not added""" + k = add_item(runhistory, config1) + assert k in runhistory -class RunHistoryMappingTest(unittest.TestCase): - def setUp(self) -> None: - self.cs = get_config_space() - self.runhistory = RunHistory() - - def add_item( - self, - cost: float = 0.0, - time: float = 1.0, - seed: int = 0, - instance_id: Optional[str] = None, - budget: float = 0.0, - status=StatusType.SUCCESS, - ) -> RunKey: - """No easy way to generate a key before hand""" - self.runhistory.add( - config=self.cs.sample_configuration(), - cost=cost, - time=time, - instance_id=instance_id, - seed=seed, - budget=budget, - status=status, - ) - return RunKey( - config_id=self.runhistory._n_id, # What's used internally during `add` - instance_id=instance_id, - seed=seed, - budget=budget, - ) + new_rh = RunHistory() + assert k not in new_rh - def test_contains(self): - """Test that keys added are contained `in` and not if not added""" - k = self.add_item() - assert k in self.runhistory - new_rh = RunHistory() - assert k not in new_rh +def test_getting(runhistory, config1, config2): + """Test that rh[k] will return the correct value""" + k = add_item(runhistory, config1, cost=1.0) + k2 = add_item(runhistory, config2, cost=2.0) - def test_getting(self): - """Test that rh[k] will return the correct value""" - k = self.add_item(cost=1.0) - k2 = self.add_item(cost=2.0) + v = runhistory[k] + assert v.cost == 1.0 - v = self.runhistory[k] - assert v.cost == 1.0 + v2 = runhistory[k2] + assert v2.cost == 2.0 - v2 = self.runhistory[k2] - assert v2.cost == 2.0 - def test_len(self): - """Test that adding items will increase the length monotonically""" - assert len(self.runhistory) == 0 +def test_len(runhistory, config1): + """Test that adding items will increase the length monotonically""" + assert len(runhistory) == 0 - n_items = 5 + n_items = 5 - for i in range(n_items): - assert len(self.runhistory) == i + for i in range(n_items): + assert len(runhistory) == i - self.add_item() + add_item(runhistory, config1, budget=i) - assert len(self.runhistory) == i + 1 + assert len(runhistory) == i + 1 - assert len(self.runhistory) == n_items + assert len(runhistory) == n_items - def test_iter(self): - """Test that iter goes in the order of insertion and has consitent length - with the runhistory's advertised length and it's internal `data` - """ - params = [ - {"instance_id": "a", "cost": 1.0}, - {"instance_id": "b", "cost": 2.0}, - {"instance_id": "c", "cost": 3.0}, - {"instance_id": "d", "cost": 4.0}, - ] - for p in params: - self.add_item(**p) +def test_iter(runhistory, config1): + """Test that iter goes in the order of insertion and has consitent length + with the runhistory's advertised length and it's internal `data` + """ + params = [ + {"instance": "a", "cost": 1.0}, + {"instance": "b", "cost": 2.0}, + {"instance": "c", "cost": 3.0}, + {"instance": "d", "cost": 4.0}, + ] - expected_id_order = [p["instance_id"] for p in params] - assert [k.instance_id for k in iter(self.runhistory)] == expected_id_order + for p in params: + add_item(runhistory, config1, **p) - assert len(list(iter(self.runhistory))) == len(self.runhistory) - assert len(list(iter(self.runhistory))) == len(self.runhistory.data) + expected_id_order = [p["instance"] for p in params] + assert [k.instance for k in iter(runhistory)] == expected_id_order - def test_items(self): - """Test that items goes in correct insertion order and returns key values - as expected. - """ - params = [ - {"instance_id": "a", "cost": 1.0}, - {"instance_id": "b", "cost": 2.0}, - {"instance_id": "c", "cost": 3.0}, - {"instance_id": "d", "cost": 4.0}, - ] + assert len(list(iter(runhistory))) == len(runhistory) + assert len(list(iter(runhistory))) == len(runhistory._data) - for p in params: - self.add_item(**p) - for (k, v), expected in zip(self.runhistory.items(), params): - assert k.instance_id == expected["instance_id"] - assert v.cost == expected["cost"] +def test_items(runhistory, config1): + """Test that items goes in correct insertion order and returns key values + as expected. + """ + params = [ + {"instance": "a", "cost": 1.0}, + {"instance": "b", "cost": 2.0}, + {"instance": "c", "cost": 3.0}, + {"instance": "d", "cost": 4.0}, + ] - def test_unpack(self): - """Test that unpacking maintains order and returns key values as expected""" - params = [ - {"instance_id": "a", "cost": 1.0}, - {"instance_id": "b", "cost": 2.0}, - {"instance_id": "c", "cost": 3.0}, - {"instance_id": "d", "cost": 4.0}, - ] + for p in params: + add_item(runhistory, config1, **p) + + for (k, v), expected in zip(runhistory.items(), params): + assert k.instance == expected["instance"] + assert v.cost == expected["cost"] - for p in params: - self.add_item(**p) - unpacked = {**self.runhistory} +def test_unpack(runhistory, config1): + """Test that unpacking maintains order and returns key values as expected""" + params = [ + {"instance": "a", "cost": 1.0}, + {"instance": "b", "cost": 2.0}, + {"instance": "c", "cost": 3.0}, + {"instance": "d", "cost": 4.0}, + ] - for (k, v), expected in zip(unpacked.items(), params): - assert k.instance_id == expected["instance_id"] - assert v.cost == expected["cost"] + for p in params: + add_item(runhistory, config1, **p) + unpacked = {**runhistory} -if __name__ == "__main__": - unittest.main() + for (k, v), expected in zip(unpacked.items(), params): + assert k.instance == expected["instance"] + assert v.cost == expected["cost"] diff --git a/tests/test_runhistory/test_runhistory2epm.py b/tests/test_runhistory/test_runhistory2epm.py deleted file mode 100644 index c2a88aa83..000000000 --- a/tests/test_runhistory/test_runhistory2epm.py +++ /dev/null @@ -1,611 +0,0 @@ -import unittest - -import numpy as np -from ConfigSpace import Configuration, ConfigurationSpace -from ConfigSpace.hyperparameters import UniformIntegerHyperparameter - -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances -from smac.epm.random_forest.rfr_imputator import RFRImputator -from smac.epm.utils import get_types -from smac.runhistory import runhistory, runhistory2epm -from smac.scenario.scenario import Scenario -from smac.tae import StatusType - -__author__ = "Katharina Eggensperger" -__copyright__ = "Copyright 2015, ML4AAD" -__license__ = "GPLv3" -__maintainer__ = "Katharina Eggensperger" -__email__ = "eggenspk@cs.uni-freiburg.de" -__version__ = "0.0.1" - - -def get_config_space(): - cs = ConfigurationSpace() - cs.add_hyperparameter(UniformIntegerHyperparameter(name="a", lower=0, upper=100)) - cs.add_hyperparameter(UniformIntegerHyperparameter(name="b", lower=0, upper=100)) - return cs - - -class RunhistoryTest(unittest.TestCase): - def setUp(self): - unittest.TestCase.setUp(self) - - self.rh = runhistory.RunHistory() - self.cs = get_config_space() - self.config1 = Configuration(self.cs, values={"a": 0, "b": 100}) - self.config2 = Configuration(self.cs, values={"a": 100, "b": 0}) - self.config3 = Configuration(self.cs, values={"a": 100, "b": 100}) - self.config4 = Configuration(self.cs, values={"a": 23, "b": 23}) - self.config5 = Configuration(self.cs, values={"a": 5, "b": 10}) - self.scen = Scenario({"run_obj": "runtime", "cutoff_time": 20, "cs": self.cs}) - self.types, self.bounds = get_types(self.cs, None) - self.scen = Scenario({"run_obj": "runtime", "cutoff_time": 20, "cs": self.cs, "output_dir": ""}) - - def test_log_runtime_with_imputation(self): - """ - adding some rundata to RunHistory2EPM4LogCost and impute censored data - """ - self.imputor = RFRImputator( - rng=np.random.RandomState(seed=12345), - cutoff=np.log(self.scen.cutoff), - threshold=np.log(self.scen.cutoff * self.scen.par_factor), - model=RandomForestWithInstances( - configspace=self.cs, - types=self.types, - bounds=self.bounds, - instance_features=None, - seed=12345, - ratio_features=1.0, - ), - ) - - rh2epm = runhistory2epm.RunHistory2EPM4LogCost( - num_params=2, - scenario=self.scen, - impute_censored_data=True, - impute_state=[ - StatusType.TIMEOUT, - ], - success_states=[ - StatusType.SUCCESS, - ], - imputor=self.imputor, - ) - - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=23, - seed=None, - additional_info=None, - ) - - X, y = rh2epm.transform(self.rh) - self.assertTrue(np.allclose(X, np.array([[0.005, 0.995]]), atol=0.001)) - self.assertTrue(np.allclose(y, np.array([[0.0]]))) # 10^0 = 1 - - # rh2epm should use time and not cost field later - self.rh.add( - config=self.config3, - cost=200, - time=20, - status=StatusType.TIMEOUT, - instance_id=1, - seed=45, - additional_info={"start_time": 20}, - ) - - X, y = rh2epm.transform(self.rh) - self.assertTrue(np.allclose(X, np.array([[0.005, 0.995], [0.995, 0.995]]), atol=0.001)) - # ln(20 * 10) - self.assertTrue(np.allclose(y, np.array([[0.0], [5.2983]]), atol=0.001)) - - self.rh.add( - config=self.config2, - cost=100, - time=10, - status=StatusType.TIMEOUT, - instance_id=1, - seed=12354, - additional_info={"start_time": 10}, - ) - - X, y = rh2epm.transform(self.rh) - np.testing.assert_array_almost_equal(X, np.array([[0.005, 0.995], [0.995, 0.005], [0.995, 0.995]]), decimal=3) - - np.testing.assert_array_almost_equal(y, np.array([[0.0], [2.727], [5.2983]]), decimal=3) - - def test_log_cost_without_imputation(self): - """ - adding some rundata to RunHistory2EPM4LogCost - """ - - rh2epm = runhistory2epm.RunHistory2EPM4LogCost( - num_params=2, - success_states=[ - StatusType.SUCCESS, - ], - impute_censored_data=False, - scenario=self.scen, - ) - - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=23, - seed=None, - additional_info=None, - ) - - X, y = rh2epm.transform(self.rh) - self.assertTrue(np.allclose(X, np.array([[0.005, 0.995]]), atol=0.001)) - self.assertTrue(np.allclose(y, np.array([[0.0]]))) # 10^0 = 1 - - # rh2epm should use time and not cost field later - self.rh.add( - config=self.config3, - cost=200, - time=20, - status=StatusType.TIMEOUT, - instance_id=1, - seed=45, - additional_info={"start_time": 20}, - ) - - X, y = rh2epm.transform(self.rh) - self.assertTrue(np.allclose(X, np.array([[0.005, 0.995], [0.995, 0.995]]), atol=0.001)) - # ln(20 * 10) - self.assertTrue(np.allclose(y, np.array([[0.0], [5.2983]]), atol=0.001)) - - self.rh.add( - config=self.config2, - cost=100, - time=10, - status=StatusType.TIMEOUT, - instance_id=1, - seed=12354, - additional_info={"start_time": 10}, - ) - - X, y = rh2epm.transform(self.rh) - # last entry gets skipped since imputation is disabled - self.assertTrue(np.allclose(X, np.array([[0.005, 0.995], [0.995, 0.995]]), atol=0.001)) - self.assertTrue(np.allclose(y, np.array([[0.0], [5.2983]]), atol=0.001)) - - def test_cost_with_imputation(self): - """ - adding some rundata to RunHistory2EPM4Cost and impute censored data - """ - - self.imputor = RFRImputator( - rng=np.random.RandomState(seed=12345), - cutoff=self.scen.cutoff, - threshold=self.scen.cutoff * self.scen.par_factor, - model=RandomForestWithInstances( - configspace=self.cs, - types=self.types, - bounds=self.bounds, - instance_features=None, - seed=12345, - n_points_per_tree=90, - ratio_features=1.0, - ), - ) - - rh2epm = runhistory2epm.RunHistory2EPM4Cost( - num_params=2, - scenario=self.scen, - impute_censored_data=True, - success_states=[ - StatusType.SUCCESS, - ], - impute_state=[StatusType.TIMEOUT], - imputor=self.imputor, - ) - - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=23, - seed=None, - additional_info=None, - ) - - X, y = rh2epm.transform(self.rh) - self.assertTrue(np.allclose(X, np.array([[0.005, 0.995]]), atol=0.001)) - self.assertTrue(np.allclose(y, np.array([[1.0]]))) - - # rh2epm should use time and not cost field later - self.rh.add( - config=self.config3, - cost=200, - time=20, - status=StatusType.TIMEOUT, - instance_id=1, - seed=45, - additional_info={"start_time": 20}, - ) - - X, y = rh2epm.transform(self.rh) - self.assertTrue(np.allclose(X, np.array([[0.005, 0.995], [0.995, 0.995]]), atol=0.001)) - self.assertTrue(np.allclose(y, np.array([[1.0], [200.0]]), atol=0.001)) - - self.rh.add( - config=self.config2, - cost=100, - time=10, - status=StatusType.TIMEOUT, - instance_id=1, - seed=12354, - additional_info={"start_time": 10}, - ) - - X, y = rh2epm.transform(self.rh) - np.testing.assert_array_almost_equal(X, np.array([[0.005, 0.995], [0.995, 0.005], [0.995, 0.995]]), decimal=3) - np.testing.assert_array_almost_equal(y, np.array([[1.0], [11.0], [200.0]]), decimal=1) - - def test_cost_without_imputation(self): - """ - adding some rundata to RunHistory2EPM4Cost without imputation - """ - - rh2epm = runhistory2epm.RunHistory2EPM4Cost( - num_params=2, - success_states=[StatusType.SUCCESS, StatusType.CRASHED, StatusType.MEMOUT], - impute_censored_data=False, - scenario=self.scen, - ) - - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=23, - seed=None, - additional_info=None, - ) - - X, y = rh2epm.transform(self.rh) - self.assertTrue(np.allclose(X, np.array([[0.005, 0.995]]), atol=0.001)) - self.assertTrue(np.allclose(y, np.array([[1.0]]))) - - # rh2epm should use cost and not time - self.rh.add( - config=self.config3, - cost=200, - time=20, - status=StatusType.TIMEOUT, - instance_id=1, - seed=45, - additional_info={"start_time": 20}, - ) - - X, y = rh2epm.transform(self.rh) - np.testing.assert_allclose(X, np.array([[0.005, 0.995], [0.995, 0.995]]), atol=0.001) - # log_10(20 * 10) - np.testing.assert_allclose(y, np.array([[1.0], [200.0]]), atol=0.001) - - self.rh.add( - config=self.config2, - cost=100, - time=10, - status=StatusType.TIMEOUT, - instance_id=1, - seed=12354, - additional_info={"start_time": 10}, - ) - - X, y = rh2epm.transform(self.rh) - # last entry gets skipped since imputation is disabled - self.assertTrue(np.allclose(X, np.array([[0.005, 0.995], [0.995, 0.995]]), atol=0.001)) - self.assertTrue(np.allclose(y, np.array([[1.0], [200.0]]), atol=0.001)) - - def test_cost_quality(self): - """ - adding some rundata to RunHistory2EPM4LogCost - """ - self.scen = Scenario({"cutoff_time": 20, "cs": self.cs, "run_obj": "quality", "output_dir": ""}) - - rh2epm = runhistory2epm.RunHistory2EPM4Cost( - num_params=2, - success_states=[StatusType.SUCCESS, StatusType.CRASHED, StatusType.MEMOUT], - impute_censored_data=False, - scenario=self.scen, - ) - - self.rh.add( - config=self.config1, - cost=1, - time=10, - status=StatusType.SUCCESS, - instance_id=23, - seed=None, - additional_info=None, - ) - - X, y = rh2epm.transform(self.rh) - self.assertTrue(np.allclose(X, np.array([[0.005, 0.995]]), atol=0.001)) - # should use the cost field and not runtime - self.assertTrue(np.allclose(y, np.array([[1.0]]))) - - # rh2epm should use cost and not time field later - self.rh.add( - config=self.config3, - cost=200, - time=20, - status=StatusType.TIMEOUT, - instance_id=1, - seed=45, - additional_info={"start_time": 20}, - ) - - X, y = rh2epm.transform(self.rh) - self.assertTrue(np.allclose(X, np.array([[0.005, 0.995], [0.995, 0.995]]), atol=0.001)) - # log_10(20 * 10) - self.assertTrue(np.allclose(y, np.array([[1.0], [200.0]]), atol=0.001)) - - def test_get_X_y(self): - """ - add some data to RH and check returned values in X,y format - """ - - self.scen = Scenario( - { - "cutoff_time": 20, - "cs": self.cs, - "run_obj": "runtime", - "instances": [["1"], ["2"]], - "features": {"1": [1, 1], "2": [2, 2]}, - "output_dir": "", - } - ) - - rh2epm = runhistory2epm.RunHistory2EPM4Cost( - num_params=2, - success_states=[ - StatusType.SUCCESS, - ], - impute_state=[ - StatusType.CAPPED, - ], - impute_censored_data=False, - scenario=self.scen, - ) - - self.rh.add( - config=self.config1, - cost=1, - time=10, - status=StatusType.SUCCESS, - instance_id="1", - seed=None, - additional_info=None, - ) - - self.rh.add( - config=self.config1, - cost=2, - time=10, - status=StatusType.SUCCESS, - instance_id="2", - seed=None, - additional_info=None, - ) - - self.rh.add( - config=self.config2, - cost=1, - time=10, - status=StatusType.TIMEOUT, - instance_id="1", - seed=None, - additional_info=None, - ) - - self.rh.add( - config=self.config2, - cost=0.1, - time=10, - status=StatusType.CAPPED, - instance_id="2", - seed=None, - additional_info=None, - ) - - X, y, c = rh2epm.get_X_y(self.rh) - - X_sol = np.array([[0, 100, 1, 1], [0, 100, 2, 2], [100, 0, 1, 1], [100, 0, 2, 2]]) - self.assertTrue(np.all(X == X_sol)) - - y_sol = np.array([1, 2, 1, 0.1]) - self.assertTrue(np.all(y == y_sol)) - - c_sol = np.array([False, False, True, True]) - self.assertTrue(np.all(c == c_sol)) - - def test_budget_selection(self): - """ - adding some rundata and check budget selection - """ - - rh2epm = runhistory2epm.RunHistory2EPM4Cost( - num_params=2, - success_states=[StatusType.SUCCESS, StatusType.CRASHED, StatusType.MEMOUT], - impute_censored_data=False, - scenario=self.scen, - ) - - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - budget=1, - additional_info=None, - ) - self.rh.add( - config=self.config1, - cost=2, - time=2, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - budget=2, - additional_info=None, - ) - - X, y = rh2epm.transform(self.rh, budget_subset=[1]) - self.assertTrue(np.allclose(X, np.array([[0.005, 0.995]]), atol=0.001)) - self.assertTrue(np.allclose(y, np.array([[1]]))) - - X, y = rh2epm.transform(self.rh, budget_subset=[2]) - self.assertTrue(np.allclose(X, np.array([[0.005, 0.995]]), atol=0.001)) - self.assertTrue(np.allclose(y, np.array([[2]]))) - - def test_run_selection(self): - """ - adding some rundata and check budget selection - """ - self.rh.add( - config=self.config1, - cost=1, - time=1, - status=StatusType.SUCCESS, - instance_id=1, - seed=None, - budget=1, - additional_info=None, - ) - self.rh.add( - config=self.config2, - cost=2, - time=2, - status=StatusType.CRASHED, - instance_id=1, - seed=None, - budget=2, - additional_info=None, - ) - self.rh.add( - config=self.config3, - cost=3, - time=3, - status=StatusType.MEMOUT, - instance_id=1, - seed=None, - budget=2, - additional_info=None, - ) - self.rh.add( - config=self.config4, - cost=4, - time=4, - status=StatusType.DONOTADVANCE, - instance_id=1, - seed=None, - budget=3, - additional_info=None, - ) - self.rh.add( - config=self.config5, - cost=20, - time=20, - status=StatusType.TIMEOUT, - instance_id=1, - seed=None, - budget=4, - additional_info=None, - ) - - for s, v in [ - (StatusType.SUCCESS, 1), - (StatusType.CRASHED, 2), - (StatusType.MEMOUT, 3), - (StatusType.DONOTADVANCE, 4), - ]: - rh2epm = runhistory2epm.RunHistory2EPM4Cost( - num_params=2, - success_states=[ - s, - ], - impute_censored_data=False, - scenario=self.scen, - ) - X, y = rh2epm.transform(self.rh, budget_subset=None) - self.assertSetEqual(set(y.flatten()), {v, 20}) - - for s, v in [ - ( - StatusType.SUCCESS, - [ - 1, - ], - ), - (StatusType.CRASHED, []), - (StatusType.MEMOUT, []), - (StatusType.DONOTADVANCE, []), - (StatusType.TIMEOUT, []), - ]: - rh2epm = runhistory2epm.RunHistory2EPM4Cost( - num_params=2, - success_states=[ - s, - ], - impute_censored_data=False, - scenario=self.scen, - ) - X, y = rh2epm.transform(self.rh, budget_subset=[1]) - self.assertSetEqual(set(y.flatten()), set(v)) - - # Test defaults set in SMAC facade - rh2epm = runhistory2epm.RunHistory2EPM4Cost( - num_params=2, - success_states=[ - StatusType.SUCCESS, - StatusType.CRASHED, - StatusType.MEMOUT, - StatusType.DONOTADVANCE, - ], - consider_for_higher_budgets_state=[ - StatusType.TIMEOUT, - StatusType.CRASHED, - StatusType.MEMOUT, - StatusType.DONOTADVANCE, - ], - impute_censored_data=False, - scenario=self.scen, - ) - X, y = rh2epm.transform(self.rh, budget_subset=[1]) - self.assertSetEqual( - set(y.flatten()), - { - 1, - }, - ) - self.assertTrue(len(y) == 1) - X, y = rh2epm.transform(self.rh, budget_subset=[2]) - self.assertSetEqual(set(y.flatten()), {2, 3}) - self.assertTrue(len(y) == 2) - X, y = rh2epm.transform(self.rh, budget_subset=[3]) - self.assertSetEqual(set(y.flatten()), {2, 3, 4}) - self.assertTrue(len(y) == 3) - X, y = rh2epm.transform(self.rh, budget_subset=[4]) - self.assertSetEqual(set(y.flatten()), {2, 3, 4, 20}) - self.assertTrue(len(y) == 4) - X, y = rh2epm.transform(self.rh, budget_subset=[5]) - self.assertSetEqual(set(y.flatten()), {2, 3, 4, 20}) - self.assertTrue(len(y) == 4) - self.assertRaises(ValueError, rh2epm.transform, self.rh, budget_subset=[4, 5]) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_runhistory/test_runhistory_encoder.py b/tests/test_runhistory/test_runhistory_encoder.py new file mode 100644 index 000000000..8e7af34af --- /dev/null +++ b/tests/test_runhistory/test_runhistory_encoder.py @@ -0,0 +1,275 @@ +import numpy as np +import pytest + +from smac.multi_objective.aggregation_strategy import MeanAggregationStrategy +from smac.runhistory.encoder import ( + RunHistoryEIPSEncoder, + RunHistoryInverseScaledEncoder, + RunHistoryLogEncoder, + RunHistoryLogScaledEncoder, + RunHistoryScaledEncoder, + RunHistorySqrtScaledEncoder, +) +from smac.runhistory.encoder.encoder import RunHistoryEncoder +from smac.runner.abstract_runner import StatusType + + +@pytest.fixture +def configs(configspace_small): + configs = configspace_small.sample_configuration(20) + return (configs[16], configs[15], configs[2], configs[3]) + + +def test_transform(runhistory, make_scenario, configspace_small, configs): + """Test if all encoders are working.""" + scenario = make_scenario(configspace_small) + + runhistory.add( + config=configs[0], + cost=1, + time=1, + status=StatusType.SUCCESS, + ) + + runhistory.add( + config=configs[1], + cost=5, + time=4, + status=StatusType.SUCCESS, + ) + + # Normal encoder + encoder = RunHistoryEncoder(scenario=scenario, considered_states=[StatusType.SUCCESS]) + X1, Y1 = encoder.transform(runhistory) + + assert Y1.tolist() == [[1.0], [5.0]] + assert ((X1 <= 1.0) & (X1 >= 0.0)).all() + + # Log encoder + encoder = RunHistoryLogEncoder(scenario=scenario, considered_states=[StatusType.SUCCESS]) + X, Y = encoder.transform(runhistory) + assert Y.tolist() != Y1.tolist() + assert ((X <= 1.0) & (X >= 0.0)).all() + + encoder = RunHistoryLogScaledEncoder(scenario=scenario, considered_states=[StatusType.SUCCESS]) + X, Y = encoder.transform(runhistory) + assert Y.tolist() != Y1.tolist() + assert ((X <= 1.0) & (X >= 0.0)).all() + + encoder = RunHistoryScaledEncoder(scenario=scenario, considered_states=[StatusType.SUCCESS]) + X, Y = encoder.transform(runhistory) + assert Y.tolist() != Y1.tolist() + assert ((X <= 1.0) & (X >= 0.0)).all() + + encoder = RunHistoryInverseScaledEncoder(scenario=scenario, considered_states=[StatusType.SUCCESS]) + X, Y = encoder.transform(runhistory) + assert Y.tolist() != Y1.tolist() + assert ((X <= 1.0) & (X >= 0.0)).all() + + encoder = RunHistorySqrtScaledEncoder(scenario=scenario, considered_states=[StatusType.SUCCESS]) + X, Y = encoder.transform(runhistory) + assert Y.tolist() != Y1.tolist() + assert ((X <= 1.0) & (X >= 0.0)).all() + + encoder = RunHistoryEIPSEncoder(scenario=scenario, considered_states=[StatusType.SUCCESS]) + X, Y = encoder.transform(runhistory) + assert Y.tolist() != Y1.tolist() + assert ((X <= 1.0) & (X >= 0.0)).all() + + +def test_transform_conditionals(runhistory, make_scenario, configspace_large, configs): + configs = configspace_large.sample_configuration(20) + scenario = make_scenario(configspace_large) + + runhistory.add( + config=configs[0], + cost=1, + time=1, + status=StatusType.SUCCESS, + ) + + runhistory.add( + config=configs[2], + cost=5, + time=4, + status=StatusType.SUCCESS, + ) + + encoder = RunHistoryEncoder(scenario=scenario, considered_states=[StatusType.SUCCESS]) + X, Y = encoder.transform(runhistory) + + assert Y.tolist() == [[1.0], [5.0]] + assert np.isnan(X[0][5]) + assert not np.isnan(X[1][5]) + + +def test_multi_objective(runhistory, make_scenario, configspace_small, configs): + configs = configspace_small.sample_configuration(20) + scenario = make_scenario(configspace_small, use_multi_objective=True) + + runhistory.add( + config=configs[0], + cost=[0.0, 100.0], + time=5, + status=StatusType.SUCCESS, + ) + + # Multi objective algorithm must be set + with pytest.raises(AssertionError): + encoder = RunHistoryEncoder(scenario=scenario, considered_states=[StatusType.SUCCESS]) + _, Y = encoder.transform(runhistory) + + encoder.multi_objective_algorithm = MeanAggregationStrategy(scenario) + _, Y = encoder.transform(runhistory) + + # We expect the result to be 1 because no normalization could be done yet + assert Y.flatten() == 1.0 + + runhistory.add( + config=configs[2], + cost=[50.0, 50.0], + time=4, + status=StatusType.SUCCESS, + ) + + # Now we expect something different + _, Y = encoder.transform(runhistory) + assert Y.tolist() == [[0.5], [0.5]] + + runhistory.add( + config=configs[3], + cost=[200.0, 0.0], + time=4, + status=StatusType.SUCCESS, + ) + + _, Y = encoder.transform(runhistory) + assert Y.tolist() == [ + [0.5], # (0+1)/2 + [0.375], # (0.25+0.5) / 2 + [0.5], # (1+0) / 2 + ] + + +def test_ignore(runhistory, make_scenario, configspace_small, configs): + """Tests if only successful states are considered.""" + scenario = make_scenario(configspace_small) + + runhistory.add( + config=configs[0], + cost=1, + time=1, + status=StatusType.MEMORYOUT, + ) + + runhistory.add( + config=configs[1], + cost=5, + time=4, + status=StatusType.MEMORYOUT, + ) + + # Normal encoder + encoder = RunHistoryEncoder(scenario=scenario, considered_states=[StatusType.SUCCESS]) + X1, Y1 = encoder.transform(runhistory) + + assert Y1.tolist() == [] + + runhistory.add( + config=configs[3], + cost=5, + time=4, + status=StatusType.SUCCESS, + ) + + X1, Y1 = encoder.transform(runhistory) + + # cost 5 should be included now + assert Y1.tolist() == [[5.0]] + + +def test_budgets(runhistory, make_scenario, configspace_small, configs): + """Tests if only successful states are considered.""" + scenario = make_scenario(configspace_small) + + runhistory.add( + config=configs[0], + cost=1, + time=1, + status=StatusType.SUCCESS, + budget=5, + ) + + runhistory.add( + config=configs[1], + cost=99999999, + time=1, + status=StatusType.SUCCESS, + budget=2, + ) + + runhistory.add(config=configs[1], cost=5, time=4, status=StatusType.SUCCESS, budget=2) + + # Normal encoder + encoder = RunHistoryEncoder(scenario=scenario, considered_states=[StatusType.SUCCESS]) + X, Y = encoder.transform(runhistory, budget_subset=[2]) + assert Y.tolist() == [[99999999]] + + X, Y = encoder.transform(runhistory, budget_subset=[5]) + assert Y.tolist() == [[1]] + + +def test_budgets(runhistory, make_scenario, configspace_small, configs): + """Tests if only specific budgets are considered.""" + scenario = make_scenario(configspace_small) + + runhistory.add( + config=configs[0], + cost=1, + time=1, + status=StatusType.SUCCESS, + budget=5, + ) + + runhistory.add( + config=configs[1], + cost=99999999, + time=1, + status=StatusType.SUCCESS, + budget=2, + ) + + runhistory.add(config=configs[1], cost=5, time=4, status=StatusType.SUCCESS, budget=2) + + # Normal encoder + encoder = RunHistoryEncoder(scenario=scenario, considered_states=[StatusType.SUCCESS]) + X, Y = encoder.transform(runhistory, budget_subset=[2]) + assert Y.tolist() == [[99999999]] + + X, Y = encoder.transform(runhistory, budget_subset=[5]) + assert Y.tolist() == [[1]] + + +def test_lower_budget_states(runhistory, make_scenario, configspace_small, configs): + """Tests lower budgets based on budget subset and considered states.""" + scenario = make_scenario(configspace_small) + encoder = RunHistoryEncoder(scenario=scenario, considered_states=[StatusType.SUCCESS]) + + runhistory.add(config=configs[0], cost=1, time=1, status=StatusType.SUCCESS, budget=3) + runhistory.add(config=configs[0], cost=2, time=2, status=StatusType.SUCCESS, budget=4) + runhistory.add(config=configs[0], cost=3, time=4, status=StatusType.TIMEOUT, budget=5) + + # We request a higher budget but can't find it, so we expect an empty list + X, Y = encoder.transform(runhistory, budget_subset=[500]) + assert Y.tolist() == [] + + encoder = RunHistoryEncoder( + scenario=scenario, + considered_states=[StatusType.SUCCESS], + lower_budget_states=[StatusType.TIMEOUT], + ) + + # We request a higher budget but can't find it but since we consider TIMEOUT for lower budget states, we should + # receive the cost of 3 + X, Y = encoder.transform(runhistory, budget_subset=[500]) + assert Y.tolist() == [[3.0]] diff --git a/tests/test_runhistory/test_runhistory_multi_objective.py b/tests/test_runhistory/test_runhistory_multi_objective.py index 3ef3a84a7..c1b44a4a4 100644 --- a/tests/test_runhistory/test_runhistory_multi_objective.py +++ b/tests/test_runhistory/test_runhistory_multi_objective.py @@ -1,614 +1,635 @@ -import os import pickle import tempfile -import unittest import pytest -from ConfigSpace import Configuration, ConfigurationSpace -from ConfigSpace.hyperparameters import UniformIntegerHyperparameter -from smac.runhistory.runhistory import RunHistory -from smac.tae import StatusType +from smac.multi_objective.aggregation_strategy import MeanAggregationStrategy +from smac.runner.abstract_runner import StatusType +from smac.scenario import Scenario __copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" __license__ = "3-clause BSD" -def get_config_space(): - cs = ConfigurationSpace() - cs.add_hyperparameter(UniformIntegerHyperparameter(name="a", lower=0, upper=100)) - cs.add_hyperparameter(UniformIntegerHyperparameter(name="b", lower=0, upper=100)) - return cs - - -class RunhistoryMultiObjectiveTest(unittest.TestCase): - def test_add_and_pickle(self): - """ - Simply adding some rundata to runhistory, then pickle it. - """ - rh = RunHistory() - cs = get_config_space() - config = Configuration(cs, values={"a": 1, "b": 2}) - - self.assertTrue(rh.empty()) - - rh.add( - config=config, - cost=[10, 20], - time=20, - status=StatusType.SUCCESS, - instance_id=None, - seed=None, - starttime=100, - endtime=120, - additional_info=None, - ) - - rh.add( - config=config, - cost=[4.5, 5.5], +@pytest.fixture +def scenario(configspace_small): + return Scenario(configspace_small, objectives=["a", "b"]) + + +@pytest.fixture +def config1(configspace_small): + configspace_small.seed(0) + return configspace_small.sample_configuration() + + +@pytest.fixture +def config2(configspace_small): + configspace_small.seed(1) + return configspace_small.sample_configuration() + + +@pytest.fixture +def config3(configspace_small): + configspace_small.seed(2) + return configspace_small.sample_configuration() + + +def test_add_and_pickle(scenario, runhistory, config1): + """ + Simply adding some rundata to runhistory, then pickle it. + """ + assert runhistory.empty() + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + + runhistory.add( + config=config1, + cost=[10, 20], + time=20, + status=StatusType.SUCCESS, + instance=None, + seed=None, + starttime=100, + endtime=120, + additional_info=None, + ) + + runhistory.add( + config=config1, + cost=[4.5, 5.5], + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=12354, + starttime=10, + endtime=30, + additional_info={"start_time": 10}, + ) + + runhistory.add( + config=config1, + cost=["4.8", "5.8"], + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=12354, + starttime=10, + endtime=30, + additional_info={"start_time": 10}, + ) + + assert not runhistory.empty() + + tmpfile = tempfile.NamedTemporaryFile(mode="wb", delete=False) + pickle.dump(runhistory, tmpfile, -1) + name = tmpfile.name + tmpfile.close() + + with open(name, "rb") as fh: + loaded_runhistory = pickle.load(fh) # nosec + + assert loaded_runhistory._data == runhistory._data + + +def test_illegal_input(scenario, runhistory, config1): + assert runhistory.empty() + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + + with pytest.raises(ValueError): + runhistory.add( + config=config1, + cost=[4.5, 5.5, 6.5], time=20, status=StatusType.SUCCESS, - instance_id=1, + instance=1, seed=12354, starttime=10, endtime=30, additional_info={"start_time": 10}, ) - rh.add( - config=config, - cost=["4.8", "5.8"], + runhistory.add( + config=config1, + cost=[2.5, 5.5], time=20, status=StatusType.SUCCESS, - instance_id=1, + instance=1, seed=12354, starttime=10, endtime=30, additional_info={"start_time": 10}, ) - self.assertFalse(rh.empty()) - - tmpfile = tempfile.NamedTemporaryFile(mode="wb", delete=False) - pickle.dump(rh, tmpfile, -1) - name = tmpfile.name - tmpfile.close() - - with open(name, "rb") as fh: - loaded_rh = pickle.load(fh) # nosec - - self.assertEqual(loaded_rh.data, rh.data) - - def test_illegal_input(self): - rh = RunHistory() - cs = get_config_space() - config = Configuration(cs, values={"a": 1, "b": 2}) - - self.assertTrue(rh.empty()) - - with pytest.raises(ValueError): - rh.add( - config=config, - cost=[4.5, 5.5, 6.5], - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=12354, - starttime=10, - endtime=30, - additional_info={"start_time": 10}, - ) - - rh.add( - config=config, - cost=[2.5, 5.5], - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=12354, - starttime=10, - endtime=30, - additional_info={"start_time": 10}, - ) - - def test_add_multiple_times(self): - rh = RunHistory() - cs = get_config_space() - config = Configuration(cs, values={"a": 1, "b": 2}) - - for i in range(5): - rh.add( - config=config, - cost=[i + 1, i + 2], - time=i + 1, - status=StatusType.SUCCESS, - instance_id=None, - seed=12345, - additional_info=None, - ) - - self.assertEqual(len(rh.data), 1) - self.assertEqual(len(rh.get_runs_for_config(config, only_max_observed_budget=True)), 1) - self.assertEqual(len(rh._configid_to_inst_seed_budget[1]), 1) - - # We expect to get 1.0 and 2.0 because runhistory does not overwrite by default - self.assertEqual(list(rh.data.values())[0].cost, [1.0, 2.0]) - - def test_full(self): - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) - config2 = Configuration(cs, values={"a": 1, "b": 3}) - config3 = Configuration(cs, values={"a": 1, "b": 4}) - rh.add( - config=config1, - cost=[50, 100], - time=20, - status=StatusType.SUCCESS, - ) - - # Only one value: Normalization goes to 1.0 - self.assertEqual(rh.get_cost(config1), 1.0) - - rh.add( - config=config2, - cost=[150, 50], - time=30, - status=StatusType.SUCCESS, - ) - - # The cost of the first config must be updated - # We would expect [0, 1] and the normalized value would be 0.5 - self.assertEqual(rh.get_cost(config1), 0.5) - - # We would expect [1, 0] and the normalized value would be 0.5 - self.assertEqual(rh.get_cost(config2), 0.5) - - rh.add( - config=config3, - cost=[100, 0], - time=40, - status=StatusType.SUCCESS, - ) - - # [0, 1] -> 0.5 - self.assertEqual(rh.get_cost(config1), 0.5) - - # [1, 0.5] -> 0.75 - self.assertEqual(rh.get_cost(config2), 0.75) - - # [0.5, 0] -> 0.25 - self.assertEqual(rh.get_cost(config3), 0.25) - - def test_full_update(self): - rh = RunHistory(overwrite_existing_runs=True) - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) - config2 = Configuration(cs, values={"a": 1, "b": 3}) - rh.add( - config=config1, - cost=[10, 40], - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - ) - - rh.add( - config=config1, - cost=[0, 100], - time=20, - status=StatusType.SUCCESS, - instance_id=2, - seed=2, - ) - - rh.add( - config=config2, - cost=[10, 40], - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - ) - - rh.add( - config=config2, - cost=[20, 80], - time=20, - status=StatusType.SUCCESS, - instance_id=2, - seed=2, - ) - - cost_config2 = rh.get_cost(config2) - - rh.compute_all_costs() - updated_cost_config2 = rh.get_cost(config2) - - self.assertEqual(cost_config2, updated_cost_config2) - - rh.compute_all_costs(instances=[2]) - updated_cost_config2 = rh.get_cost(config2) - - self.assertAlmostEqual(updated_cost_config2, 0.833, places=3) - - def test_incremental_update(self): - - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) - - rh.add( - config=config1, - cost=[10, 100], - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - ) - - self.assertEqual(rh.get_cost(config1), 1.0) - - rh.add( - config=config1, - cost=[20, 50], - time=20, - status=StatusType.SUCCESS, - instance_id=2, - seed=1, - ) - - # We don't except moving average of 0.75 here because - # the costs should always be updated. - self.assertEqual(rh.get_cost(config1), 0.5) - - rh.add( - config=config1, - cost=[0, 100], - time=20, - status=StatusType.SUCCESS, - instance_id=3, - seed=1, - ) - - self.assertAlmostEqual(rh.get_cost(config1), 0.583, places=3) - - def test_multiple_budgets(self): - - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) - - rh.add( - config=config1, - cost=[10, 50], - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - - self.assertEqual(rh.get_cost(config1), 1.0) - - # Only the higher budget gets included in the config cost - # However, we expect that the bounds are changed - rh.add( - config=config1, - cost=[20, 25], - time=25, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=5, - ) - - self.assertEqual(rh.get_cost(config1), 0.5) - - def test_get_configs_per_budget(self): - rh = RunHistory() - cs = get_config_space() - - config1 = Configuration(cs, values={"a": 1, "b": 1}) - rh.add( - config=config1, - cost=[10, 20], - time=10, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - - config2 = Configuration(cs, values={"a": 2, "b": 2}) - rh.add( - config=config2, - cost=[20, 30], - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - - config3 = Configuration(cs, values={"a": 3, "b": 3}) - rh.add( - config=config3, - cost=[30, 40], - time=30, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=3, - ) - - configs = rh.get_all_configs_per_budget([1]) - self.assertListEqual(configs, [config1, config2]) - def test_json_origin(self): - for origin in ["test_origin", None]: - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}, origin=origin) +def test_add_multiple_times(scenario, runhistory, config1): + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) - rh.add( - config=config1, - cost=[10.0, 20.0], - time=20, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - ) - - path = "tests/test_files/test_json_origin.json" - rh.save_json(path) - _ = rh.load_json(path, cs) - - self.assertEqual(rh.get_all_configs()[0].origin, origin) - - os.remove(path) - - def test_objective_bounds(self): - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) - config2 = Configuration(cs, values={"a": 2, "b": 3}) - config3 = Configuration(cs, values={"a": 3, "b": 4}) - - rh.add( + for i in range(5): + runhistory.add( config=config1, - cost=[10, 50], - time=5, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - - rh.add( - config=config2, - cost=[5, 100], - time=10, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - - rh.add( - config=config3, - cost=[7.5, 150], - time=15, + cost=[i + 1, i + 2], + time=i + 1, status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - - self.assertEqual(rh.objective_bounds[0], (5, 10)) - self.assertEqual(rh.objective_bounds[1], (50, 150)) - - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) - config2 = Configuration(cs, values={"a": 2, "b": 3}) - config3 = Configuration(cs, values={"a": 3, "b": 4}) - - rh.add( - config=config1, - cost=10, - time=5, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - - rh.add( - config=config2, - cost=5, - time=10, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - - rh.add( - config=config3, - cost=7.5, - time=15, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - - self.assertEqual(rh.objective_bounds[0], (5, 10)) - - def test_bounds_on_crash(self): - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) - config2 = Configuration(cs, values={"a": 2, "b": 3}) - config3 = Configuration(cs, values={"a": 3, "b": 4}) - - rh.add( - config=config1, - cost=[10, 50], - time=5, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - - rh.add( - config=config2, - cost=[100, 100], - time=10, - status=StatusType.CRASHED, - instance_id=1, - seed=1, - budget=1, - ) - - rh.add( - config=config3, - cost=[0, 150], - time=15, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=1, - ) - - self.assertEqual(rh.objective_bounds[0], (0, 10)) - self.assertEqual(rh.objective_bounds[1], (50, 150)) - - def test_instances(self): - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) - config2 = Configuration(cs, values={"a": 2, "b": 3}) - - rh.add( - config=config1, - cost=[0, 10], - time=5, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=0, - ) - - rh.add( - config=config1, - cost=[50, 20], - time=10, - status=StatusType.SUCCESS, - instance_id=2, - seed=1, - budget=0, - ) - - rh.add( - config=config1, - cost=[75, 20], - time=10, - status=StatusType.SUCCESS, - instance_id=3, - seed=1, - budget=0, - ) - - rh.add( - config=config2, - cost=[100, 30], - time=15, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=0, - ) - - rh.add( - config=config2, - cost=[0, 30], - time=15, - status=StatusType.SUCCESS, - instance_id=2, - seed=1, - budget=0, - ) - - self.assertEqual(rh.objective_bounds[0], (0, 100)) - self.assertEqual(rh.objective_bounds[1], (10, 30)) - - # Average cost returns us the cost of the latest budget - self.assertEqual(rh.get_cost(config1), 0.375) - self.assertEqual(rh.get_cost(config2), 0.75) - - def test_budgets(self): - rh = RunHistory() - cs = get_config_space() - config1 = Configuration(cs, values={"a": 1, "b": 2}) - config2 = Configuration(cs, values={"a": 2, "b": 3}) - - rh.add( - config=config1, - cost=[0, 50], - time=5, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=5, - ) - - rh.add( - config=config1, - cost=[40, 100], - time=10, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=15, - ) - - # SMAC does not overwrite by default - rh.add( - config=config1, - cost=[502342352, 23425234], - time=11, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=15, - ) - - rh.add( - config=config2, - cost=[0, 150], - time=15, - status=StatusType.SUCCESS, - instance_id=1, - seed=1, - budget=5, + instance=None, + seed=12345, + additional_info=None, ) - self.assertEqual(rh.objective_bounds[0], (0, 40)) - self.assertEqual(rh.objective_bounds[1], (50, 150)) - - # Average cost returns us the cost of the latest budget - self.assertEqual(rh.get_cost(config1), 0.75) - self.assertEqual(rh.average_cost(config1), [40.0, 100.0]) + assert len(runhistory._data) == 1 + assert len(runhistory.get_trials(config1, only_max_observed_budget=True)) == 1 + assert len(runhistory._config_id_to_isk_to_budget[1]) == 1 - self.assertEqual(rh.get_cost(config2), 0.5) - self.assertEqual(rh.average_cost(config2), [0, 150]) + # We expect to get 1.0 and 2.0 because runhistory does not overwrite by default + assert list(runhistory._data.values())[0].cost == [1.0, 2.0] -if __name__ == "__main__": - t = RunhistoryMultiObjectiveTest() - t.test_budgets() +def test_full(scenario, runhistory, config1, config2, config3): + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + runhistory.add( + config=config1, + cost=[50, 100], + time=20, + status=StatusType.SUCCESS, + ) + + # Only one value: Normalization goes to 1.0 + assert runhistory.get_cost(config1) == 1.0 + + runhistory.add( + config=config2, + cost=[150, 50], + time=30, + status=StatusType.SUCCESS, + ) + + # The cost of the first config must be updated + # We would expect [0, 1] and the normalized value would be 0.5 + assert runhistory.get_cost(config1) == 0.5 + + # We would expect [1, 0] and the normalized value would be 0.5 + assert runhistory.get_cost(config2) == 0.5 + + runhistory.add( + config=config3, + cost=[100, 0], + time=40, + status=StatusType.SUCCESS, + ) + + # [0, 1] -> 0.5 + assert runhistory.get_cost(config1) == 0.5 + + # [1, 0.5] -> 0.75 + assert runhistory.get_cost(config2) == 0.75 + + # [0.5, 0] -> 0.25 + assert runhistory.get_cost(config3) == 0.25 + + +def test_full_update(scenario, runhistory, config1, config2): + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + runhistory.overwrite_existing_runs = True + + runhistory.add( + config=config1, + cost=[10, 40], + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + ) + + runhistory.add( + config=config1, + cost=[0, 100], + time=20, + status=StatusType.SUCCESS, + instance=2, + seed=2, + ) + + runhistory.add( + config=config2, + cost=[10, 40], + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + ) + + runhistory.add( + config=config2, + cost=[20, 80], + time=20, + status=StatusType.SUCCESS, + instance=2, + seed=2, + ) + + cost_config2 = runhistory.get_cost(config2) + + runhistory.update_costs() + updated_cost_config2 = runhistory.get_cost(config2) + + assert cost_config2 == updated_cost_config2 + + runhistory.update_costs(instances=[2]) + updated_cost_config2 = runhistory.get_cost(config2) + + assert updated_cost_config2 == pytest.approx(0.833, 0.001) + + +def test_incremental_update(scenario, runhistory, config1): + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + + runhistory.add( + config=config1, + cost=[10, 100], + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + ) + + assert runhistory.get_cost(config1) == 1.0 + + runhistory.add( + config=config1, + cost=[20, 50], + time=20, + status=StatusType.SUCCESS, + instance=2, + seed=1, + ) + + # We don't except moving average of 0.75 here because + # the costs should always be updated. + assert runhistory.get_cost(config1) == 0.5 + + runhistory.add( + config=config1, + cost=[0, 100], + time=20, + status=StatusType.SUCCESS, + instance=3, + seed=1, + ) + + assert runhistory.get_cost(config1) == pytest.approx(0.583, 0.001) + + +def test_multiple_budgets(scenario, runhistory, config1): + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + + runhistory.add( + config=config1, + cost=[10, 50], + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + assert runhistory.get_cost(config1) == 1.0 + + # Only the higher budget gets included in the config cost + # However, we expect that the bounds are changed + runhistory.add( + config=config1, + cost=[20, 25], + time=25, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=5, + ) + + assert runhistory.get_cost(config1) == 0.5 + + +def test_get_configs_per_budget(scenario, runhistory, config1, config2, config3): + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + runhistory.add( + config=config1, + cost=[10, 20], + time=10, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + runhistory.add( + config=config2, + cost=[20, 30], + time=20, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + runhistory.add( + config=config3, + cost=[30, 40], + time=30, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=3, + ) + + configs = runhistory.get_configs_per_budget([1]) + assert configs == [config1, config2] + + +def test_objective_bounds(scenario, runhistory, config1, config2, config3): + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + + runhistory.add( + config=config1, + cost=[10, 50], + time=5, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + runhistory.add( + config=config2, + cost=[5, 100], + time=10, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + runhistory.add( + config=config3, + cost=[7.5, 150], + time=15, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + assert runhistory._objective_bounds[0] == (5, 10) + assert runhistory._objective_bounds[1] == (50, 150) + + +def test_objective_bounds2(scenario, runhistory, config1, config2, config3): + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + runhistory.add( + config=config1, + cost=10, + time=5, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + runhistory.add( + config=config2, + cost=5, + time=10, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + runhistory.add( + config=config3, + cost=7.5, + time=15, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + assert runhistory._objective_bounds[0] == (5, 10) + + +def test_bounds_on_crash(scenario, runhistory, config1, config2, config3): + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + + runhistory.add( + config=config1, + cost=[10, 50], + time=5, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + runhistory.add( + config=config2, + cost=[100, 100], + time=10, + status=StatusType.CRASHED, + instance=1, + seed=1, + budget=1, + ) + + runhistory.add( + config=config3, + cost=[0, 150], + time=15, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=1, + ) + + assert runhistory._objective_bounds[0] == (0, 10) + assert runhistory._objective_bounds[1] == (50, 150) + + +def test_instances(scenario, runhistory, config1, config2): + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + + runhistory.add( + config=config1, + cost=[0, 10], + time=5, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=0, + ) + + runhistory.add( + config=config1, + cost=[50, 20], + time=10, + status=StatusType.SUCCESS, + instance=2, + seed=1, + budget=0, + ) + + runhistory.add( + config=config1, + cost=[75, 20], + time=10, + status=StatusType.SUCCESS, + instance=3, + seed=1, + budget=0, + ) + + runhistory.add( + config=config2, + cost=[100, 30], + time=15, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=0, + ) + + runhistory.add( + config=config2, + cost=[0, 30], + time=15, + status=StatusType.SUCCESS, + instance=2, + seed=1, + budget=0, + ) + + assert runhistory._objective_bounds[0] == (0, 100) + assert runhistory._objective_bounds[1] == (10, 30) + + # Average cost returns us the cost of the latest budget + assert runhistory.get_cost(config1) == 0.375 + assert runhistory.get_cost(config2) == 0.75 + + +def test_budgets(scenario, runhistory, config1, config2): + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + + runhistory.add( + config=config1, + cost=[0, 50], + time=5, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=5, + ) + + runhistory.add( + config=config1, + cost=[40, 100], + time=10, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=15, + ) + + # SMAC does not overwrite by default + runhistory.add( + config=config1, + cost=[502342352, 23425234], + time=11, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=15, + ) + + runhistory.add( + config=config2, + cost=[0, 150], + time=15, + status=StatusType.SUCCESS, + instance=1, + seed=1, + budget=5, + ) + + assert runhistory._objective_bounds[0] == (0, 40) + assert runhistory._objective_bounds[1] == (50, 150) + + # Average cost returns us the cost of the latest budget + assert runhistory.get_cost(config1) == 0.75 + assert runhistory.average_cost(config1), [40.0, 100.0] + + assert runhistory.get_cost(config2) == 0.5 + assert runhistory.average_cost(config2) == [0, 150] + + +def test_objective_weights(scenario, runhistory, config1, config2): + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + runhistory.add( + config=config1, + cost=[0, 10], + time=5, + status=StatusType.SUCCESS, + ) + + runhistory.add( + config=config2, + cost=[100, 0], + time=15, + status=StatusType.SUCCESS, + ) + + assert runhistory._objective_bounds[0] == (0, 100) + assert runhistory._objective_bounds[1] == (0, 10) + + # Average cost returns us 0.5 + assert runhistory.get_cost(config1) == 0.5 + + # If we change the weights/mo algorithm now, we expect a higher value in the second cost + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario, objective_weights=[1, 2]) + assert round(runhistory.get_cost(config1), 2) == 0.67 + + +def test_pareto_front1(scenario, runhistory, config1, config2): + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + runhistory.add( + config=config1, + cost=[0, 10], + time=5, + status=StatusType.SUCCESS, + ) + + runhistory.add( + config=config2, + cost=[100, 0], + time=15, + status=StatusType.SUCCESS, + ) + + incumbents, _ = runhistory.get_pareto_front() + assert config1 in incumbents and config2 in incumbents + + +def test_pareto_front2(scenario, runhistory, config1, config2): + runhistory.multi_objective_algorithm = MeanAggregationStrategy(scenario) + runhistory.add( + config=config1, + cost=[0, 10], + time=5, + status=StatusType.SUCCESS, + ) + + runhistory.add( + config=config2, + cost=[0, 15], + time=15, + status=StatusType.SUCCESS, + ) + + incumbents, _ = runhistory.get_pareto_front() + assert config1 in incumbents and config2 not in incumbents diff --git a/smac/stats/__init__.py b/tests/test_runner/__init__.py similarity index 100% rename from smac/stats/__init__.py rename to tests/test_runner/__init__.py diff --git a/tests/test_runner/files/crashed.sh b/tests/test_runner/files/crashed.sh new file mode 100755 index 000000000..9ef49e8f6 --- /dev/null +++ b/tests/test_runner/files/crashed.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Set arguments first +for argument in "$@" +do + key=$(echo $argument | cut -f1 -d=) + value=$(echo $argument | cut -f2 -d=) + + if [[ $key == *"--"* ]]; then + v="${key/--/}" + declare $v="${value}" + fi +done + +# We simply set the cost to our parameter +cost=$x0 + +# Return everything +echo "cost=$cost; status=CRASHED" \ No newline at end of file diff --git a/tests/test_runner/files/exit.sh b/tests/test_runner/files/exit.sh new file mode 100755 index 000000000..2b3e850e4 --- /dev/null +++ b/tests/test_runner/files/exit.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Set arguments first +for argument in "$@" +do + key=$(echo $argument | cut -f1 -d=) + value=$(echo $argument | cut -f2 -d=) + + if [[ $key == *"--"* ]]; then + v="${key/--/}" + declare $v="${value}" + fi +done + +# We simply set the cost to our parameter +exit 1 + +# Return everything +echo "cost=$cost; status=SUCCESS" \ No newline at end of file diff --git a/tests/test_runner/files/python.py b/tests/test_runner/files/python.py new file mode 100755 index 000000000..036b5b113 --- /dev/null +++ b/tests/test_runner/files/python.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument("--seed", type=int) +parser.add_argument("--instance", type=str) +parser.add_argument("--instance_features", type=str) +parser.add_argument("--x0", type=int) + +args = parser.parse_args() + +print(f"cost={args.x0}; status=SUCCESS; additional_info=blub") diff --git a/tests/test_runner/files/success.sh b/tests/test_runner/files/success.sh new file mode 100755 index 000000000..c6f559ed3 --- /dev/null +++ b/tests/test_runner/files/success.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Set arguments first +for argument in "$@" +do + key=$(echo $argument | cut -f1 -d=) + value=$(echo $argument | cut -f2 -d=) + + if [[ $key == *"--"* ]]; then + v="${key/--/}" + declare $v="${value}" + fi +done + +# We simply set the cost to our parameter +cost=$x0 + +# Return everything +echo "cost=$cost; status=SUCCESS; additional_info=blub" \ No newline at end of file diff --git a/tests/test_runner/files/success_multi_objective.sh b/tests/test_runner/files/success_multi_objective.sh new file mode 100755 index 000000000..49af20744 --- /dev/null +++ b/tests/test_runner/files/success_multi_objective.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Set arguments first +for argument in "$@" +do + key=$(echo $argument | cut -f1 -d=) + value=$(echo $argument | cut -f2 -d=) + + if [[ $key == *"--"* ]]; then + v="${key/--/}" + declare $v="${value}" + fi +done + +# We simply set the cost to our parameter +cost=$x0 + +# Return everything +echo "cost=$cost, $cost; status=SUCCESS" \ No newline at end of file diff --git a/tests/test_runner/test_dask_runner.py b/tests/test_runner/test_dask_runner.py new file mode 100644 index 000000000..38ef880f2 --- /dev/null +++ b/tests/test_runner/test_dask_runner.py @@ -0,0 +1,198 @@ +from __future__ import annotations + +from typing import Callable + +import time + +import pytest +from ConfigSpace import ConfigurationSpace +from dask.distributed import Client + +from smac.runhistory import TrialInfo, TrialValue +from smac.runner.abstract_runner import StatusType +from smac.runner.dask_runner import DaskParallelRunner +from smac.runner.target_function_runner import TargetFunctionRunner +from smac.scenario import Scenario + +# https://github.com/dask/distributed/issues/4168 +# import multiprocessing.popen_spawn_posix # noqa + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def target(x: float, seed: int, instance: str) -> tuple[float, dict]: + """Simple target function""" + return x**2, {"key": seed, "instance": instance} + + +def target_delayed(x: float, seed: int, instance: str) -> tuple[float, dict]: + """Target function which sleeps for a second""" + time.sleep(1) + return x**2, {"key": seed, "instance": instance} + + +def target_failed(x: float, seed: int, instance: str) -> tuple[float, dict]: + """Target function which fails""" + raise RuntimeError("Failed.") + + +@pytest.fixture +def make_dummy_ta( + configspace_small: ConfigurationSpace, + make_scenario: Callable[..., Scenario], +) -> Callable[..., TargetFunctionRunner]: + """Make a TargetFunctionRunner, ``make_dummy_ta(func)``""" + + def _make(target_function: Callable, n_workers: int = 2) -> TargetFunctionRunner: + scenario = make_scenario(configspace=configspace_small, n_workers=n_workers) + return TargetFunctionRunner( + target_function=target_function, + scenario=scenario, + required_arguments=["seed", "instance"], + ) + + return _make + + +def test_run(make_dummy_ta: Callable[..., TargetFunctionRunner]) -> None: + """Makes sure that we are able to run a configuration and get expected values/types""" + single_worker = make_dummy_ta(target, n_workers=2) + runner = DaskParallelRunner(single_worker=single_worker) + trial_info = TrialInfo(config=2, instance="test", seed=0, budget=0.0) + + # Submit runs! then get the value + runner.submit_trial(trial_info) + result = next(runner.iter_results(), None) + + # Run will not have finished so fast + assert result is None + + # Wait until there is a result + runner.wait() + result = next(runner.iter_results(), None) + assert result is not None + + trial_info, run_value = result + assert isinstance(trial_info, TrialInfo) + assert isinstance(run_value, TrialValue) + + assert run_value.cost == 4 + assert run_value.status == StatusType.SUCCESS + + +def test_parallel_runs(make_dummy_ta: Callable[..., TargetFunctionRunner]) -> None: + """Make sure because there are 2 workers, the trials are launched close in time""" + single_runner = make_dummy_ta(target_delayed, n_workers=2) + runner = DaskParallelRunner(single_worker=single_runner) + + assert runner.count_available_workers() == 2 + + trial_info = TrialInfo(config=2, instance="test", seed=0, budget=0.0) + runner.submit_trial(trial_info) + + assert runner.count_available_workers() == 1 + + trial_info = TrialInfo(config=3, instance="test", seed=0, budget=0.0) + runner.submit_trial(trial_info) + + assert runner.count_available_workers() == 0 + + # At this stage, we submitted 2 jobs, that are running in remote workers. + # We have to wait for each one of them to complete. The runner provides a + # wait() method to do so, yet it can only wait for a single job to be completed. + # It does internally via dask wait() so we take wait for the + # first job to complete, and take it out + runner.wait() + first = next(runner.iter_results(), None) + + # The iter results could have freed up two so we only check it's one or greater + assert first is not None + assert runner.count_available_workers() >= 1 + + # Again, the runs might take slightly different time depending if we have + # heterogeneous workers, so calling wait 2 times is a guarantee that 2 + # jobs are finished + runner.wait() + second = next(runner.iter_results(), None) + + assert second is not None + assert runner.count_available_workers() == 2 + + # To make it is parallel, we just make sure that the start of the second run is + # earlier than the end of the first run + _, first_run_value = first + _, second_run_value = second + + assert int(first_run_value.starttime) <= int(second_run_value.endtime) + + +def test_additional_info_crash_msg(make_dummy_ta: Callable[..., TargetFunctionRunner]) -> None: + """ + We want to make sure we catch errors as additional info, and in particular when + doing multiprocessing runs, we want to make sure we capture dask exceptions + """ + single_worker = make_dummy_ta(target_failed, n_workers=2) + runner = DaskParallelRunner(single_worker=single_worker) + + trial_info = TrialInfo(config=2, instance="test", seed=0, budget=0.0) + runner.submit_trial(trial_info) + runner.wait() + trial_info, run_value = next(runner.iter_results()) + + # Make sure the traceback message is included + assert "traceback" in run_value.additional_info + assert "RuntimeError" in run_value.additional_info["traceback"] + + +def test_internally_created_client(make_dummy_ta: Callable[..., TargetFunctionRunner]) -> None: + """ + Expects + ------- + * When no Client is passed, we create one and as such we need to make sure that + we have a scheduler file is create and the client is closed at the end + """ + single_worker = make_dummy_ta(target, n_workers=2) + runner = DaskParallelRunner(single_worker=single_worker) + + runner = DaskParallelRunner(single_worker=runner) + assert runner._scheduler_file is not None and runner._scheduler_file.exists() + + # Check the client is running + client = runner._client + assert client.status == "running" + + # End the runner + runner.close() + + # Check the client it created is closed + assert client.status == "closed" + + +def test_with_external_client(make_dummy_ta: Callable[..., TargetFunctionRunner]) -> None: + """ + Expects + ------- + * A user Client passed directly to a DaskParallelRunner will not not be closed + upon completion. + * It will also not create an additional scheduler file + """ + client = Client() + + # We use the same client twice just to be sure + for _ in range(2): + single_worker = make_dummy_ta(target, n_workers=2) + runner = DaskParallelRunner( + single_worker=single_worker, + dask_client=client, + ) + + # Calling close should not do anything to + # the client + runner.close() + + assert runner._scheduler_file is None + assert client.status == "running" + + assert client.status == "running" + client.close() diff --git a/tests/test_runner/test_script_target_algorithm_runner.py b/tests/test_runner/test_script_target_algorithm_runner.py new file mode 100644 index 000000000..61d87f331 --- /dev/null +++ b/tests/test_runner/test_script_target_algorithm_runner.py @@ -0,0 +1,73 @@ +import numpy as np +import pytest +from ConfigSpace import ConfigurationSpace + +from smac.runhistory import StatusType +from smac.runner.target_function_script_runner import TargetFunctionScriptRunner + + +@pytest.fixture +def configspace(): + return ConfigurationSpace({"x0": (0, 1000)}, seed=0) + + +def test_success(configspace, make_scenario): + script = "tests/test_runner/files/success.sh" + scenario = make_scenario(configspace, use_instances=True) + runner = TargetFunctionScriptRunner(script, scenario, required_arguments=["seed", "instance"]) + + config = configspace.get_default_configuration() + status, cost, runtime, additional_info = runner.run(config, instance=scenario.instances[0], seed=0) + + assert status == StatusType.SUCCESS + assert cost == config["x0"] + assert additional_info == {"additional_info": "blub"} + + +def test_success_multi_objective(configspace, make_scenario): + script = "tests/test_runner/files/success_multi_objective.sh" + scenario = make_scenario(configspace, use_instances=True, use_multi_objective=True) + runner = TargetFunctionScriptRunner(script, scenario, required_arguments=["seed", "instance"]) + + config = configspace.get_default_configuration() + status, cost, runtime, additional_info = runner.run(config, instance=scenario.instances[0], seed=0) + + assert status == StatusType.SUCCESS + assert cost == [config["x0"], config["x0"]] + assert additional_info == {} + + +def test_exit(configspace, make_scenario): + script = "tests/test_runner/files/exit.sh" + scenario = make_scenario(configspace, use_instances=True) + runner = TargetFunctionScriptRunner(script, scenario, required_arguments=["seed", "instance"]) + + config = configspace.get_default_configuration() + status, cost, runtime, additional_info = runner.run(config, instance=scenario.instances[0], seed=0) + + assert status == StatusType.CRASHED + assert "error" in additional_info + + +def test_crashed(configspace, make_scenario): + script = "tests/test_runner/files/crashed.sh" + scenario = make_scenario(configspace, use_instances=True) + runner = TargetFunctionScriptRunner(script, scenario, required_arguments=["seed", "instance"]) + + config = configspace.get_default_configuration() + status, cost, runtime, additional_info = runner.run(config, instance=scenario.instances[0], seed=0) + + assert status == StatusType.CRASHED + assert cost == np.inf + + +def test_python(configspace, make_scenario): + script = "tests/test_runner/files/python.py" + scenario = make_scenario(configspace, use_instances=True) + runner = TargetFunctionScriptRunner(script, scenario, required_arguments=["seed", "instance"]) + + config = configspace.get_default_configuration() + status, cost, runtime, additional_info = runner.run(config, instance=scenario.instances[0], seed=0) + + assert status == StatusType.SUCCESS + assert cost == config["x0"] diff --git a/tests/test_runner/test_target_algorithm_runner.py b/tests/test_runner/test_target_algorithm_runner.py new file mode 100644 index 000000000..44fa7cc01 --- /dev/null +++ b/tests/test_runner/test_target_algorithm_runner.py @@ -0,0 +1,170 @@ +from __future__ import annotations + +from typing import Callable + +import time + +import pytest +from ConfigSpace import Configuration, ConfigurationSpace + +from smac.runhistory import TrialInfo +from smac.runner.abstract_runner import StatusType +from smac.runner.target_function_runner import TargetFunctionRunner +from smac.scenario import Scenario + +__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" +__license__ = "3-clause BSD" + + +def target(x: float, seed: int, instance: str) -> tuple[float, dict]: + """Simple target function""" + return x**2, {"key": seed, "instance": instance} + + +def target_delayed(x: float, seed: int, instance: str) -> tuple[float, dict]: + """Target function which sleeps for a second""" + time.sleep(1) + return x**2, {"key": seed, "instance": instance} + + +def target_failed(x: float, seed: int, instance: str) -> tuple[float, dict]: + """Target function which fails""" + raise RuntimeError("Failed.") + + +def target_dummy(config: Configuration, seed: int) -> int: + """Target function just returning the seed""" + return seed + + +def target_multi_objective1( + config: Configuration, + seed: int, + # instance: str, + # budget: float, +) -> list[float]: + """Target function multi-objective return the seed twice""" + return [seed, seed] + + +def target_multi_objective2( + config: Configuration, + seed: int, + # instance: str, + # budget: float, +) -> dict[str, float]: + """Target function multi-objective return the seed twice as a dict""" + return {"cost1": seed, "cost2": seed} + + +@pytest.fixture +def make_runner( + configspace_small: ConfigurationSpace, + make_scenario: Callable[..., Scenario], +) -> Callable[..., TargetFunctionRunner]: + """Make a TargetFunctionRunner, ``make_dummy_ta(func)``""" + + def _make( + target_function: Callable, + use_multi_objective: bool = False, + use_instances: bool = False, + ) -> TargetFunctionRunner: + scenario = make_scenario( + configspace=configspace_small, + use_multi_objective=use_multi_objective, + ) + + required_arguments = ["seed"] + if use_instances: + required_arguments += ["instance"] + + return TargetFunctionRunner( + target_function=target_function, + scenario=scenario, + required_arguments=required_arguments, + ) + + return _make + + +def test_run(make_runner: Callable[..., TargetFunctionRunner]) -> None: + """Makes sure that we are able to run a config and return the expected values/types""" + runner = make_runner(target, use_instances=True) + run_info = TrialInfo(config=2, instance="test", seed=0, budget=0.0) + + # submit runs! then get the value + runner.submit_trial(run_info) + result = next(runner.iter_results(), None) + + assert result is not None + + run_info, run_value = result + + assert run_value.cost == 4 + assert run_value.status == StatusType.SUCCESS + + +def test_serial_runs(make_runner: Callable[..., TargetFunctionRunner]) -> None: + """Test submitting two runs in succession and that they complete after eachother in results""" + runner = make_runner(target_delayed, use_instances=True) + + run_info = TrialInfo(config=2, instance="test2", seed=0, budget=0.0) + runner.submit_trial(run_info) + + run_info = TrialInfo(config=3, instance="test3", seed=0, budget=0.0) + runner.submit_trial(run_info) + + results = runner.iter_results() + + first = next(results, None) + assert first is not None + + second = next(results, None) + assert second is not None + + # To make sure runs launched serially, we just make sure that the end time of a run + # is later than the other # Results are returned in left to right + _, first_run_value = first + _, second_run_value = second + assert int(first_run_value.endtime) <= int(second_run_value.starttime) + + +def test_fail(make_runner: Callable[..., TargetFunctionRunner]) -> None: + """Test traceback and error end up in the additional info of a failing run""" + runner = make_runner(target_failed, use_instances=True) + run_info = TrialInfo(config=2, instance="test", seed=0, budget=0.0) + + runner.submit_trial(run_info) + run_info, run_value = next(runner.iter_results()) + + # Make sure the traceback message is included + assert "traceback" in run_value.additional_info + assert "RuntimeError" in run_value.additional_info["traceback"] + + +def test_call(make_runner: Callable[..., TargetFunctionRunner]) -> None: + """Test call functionality returns things as expected""" + runner = make_runner(target_dummy) + config = runner._scenario.configspace.get_default_configuration() + + SEED = 2345 + status, cost, _, _ = runner.run(config=config, instance=None, seed=SEED, budget=None) + + assert cost == SEED + assert status == StatusType.SUCCESS + + +def test_multi_objective(make_runner: Callable[..., TargetFunctionRunner]) -> None: + """Test multiobjective function processed properly""" + # We always expect a list of costs (although a dict is returned). + # Internally, target function runner maps the dict to a list of costs in the right order. + for target in [target_multi_objective1, target_multi_objective2]: + runner = make_runner(target, use_multi_objective=True) + config = runner._scenario.configspace.get_default_configuration() + + SEED = 2345 + status, cost, _, _ = runner.run(config=config, instance=None, seed=SEED, budget=None) + + assert isinstance(cost, list) + assert cost == [SEED, SEED] + assert status == StatusType.SUCCESS diff --git a/tests/test_scenario/__init__.py b/tests/test_scenario/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/test_scenario/test_scenario.py b/tests/test_scenario/test_scenario.py index e47a6ba7f..21394e28a 100644 --- a/tests/test_scenario/test_scenario.py +++ b/tests/test_scenario/test_scenario.py @@ -1,385 +1,121 @@ -import logging -import os -import pickle -import shutil -import sys -import unittest - -import numpy as np -from ConfigSpace import Configuration -from ConfigSpace.hyperparameters import UniformIntegerHyperparameter - -from smac.configspace import ConfigurationSpace -from smac.runhistory.runhistory import RunHistory -from smac.scenario.scenario import Scenario -from smac.tae import StatusType -from smac.utils.io.cmd_reader import truthy as _is_truthy -from smac.utils.io.input_reader import InputReader -from smac.utils.merge_foreign_data import merge_foreign_data - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -in_reader = InputReader() - -if sys.version_info[0] == 2: - import mock -else: - from unittest import mock - - -class InitFreeScenario(Scenario): - def __init__(self): - pass - - -class ScenarioTest(unittest.TestCase): - def setUp(self): - logging.basicConfig() - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - self.logger.setLevel(logging.DEBUG) - - base_directory = os.path.split(__file__)[0] - base_directory = os.path.abspath(os.path.join(base_directory, "../../tests", "..")) - self.current_dir = os.getcwd() - os.chdir(base_directory) - - self.cs = ConfigurationSpace() - - self.test_scenario_dict = { - "algo": "echo Hello", - "paramfile": "tests/test_files/scenario_test/param.pcs", - "execdir": ".", - "deterministic": 0, - "run_obj": "runtime", - "overall_obj": "mean10", - "multi_objectives": "accuracy, mse", - "cutoff_time": 5, - "wallclock-limit": 18000, - "instance_file": "tests/test_files/scenario_test/training.txt", - "test_instance_file": "tests/test_files/scenario_test/test.txt", - "feature_file": "tests/test_files/scenario_test/features.txt", - "output_dir": "tests/test_files/scenario_test/tmp_output", - } - self.output_dirs = [] - self.output_files = [] - self.output_dirs.append(self.test_scenario_dict["output_dir"]) - - def tearDown(self): - for output_dir in self.output_dirs: - if output_dir: - shutil.rmtree(output_dir, ignore_errors=True) - for output_file in self.output_files: - if output_file: - try: - os.remove(output_file) - except FileNotFoundError: - pass - os.chdir(self.current_dir) - - def test_Exception(self): - with self.assertRaises(TypeError): - _ = Scenario(["a", "b"]) - - def test_string_scenario(self): - scenario = Scenario("tests/test_files/scenario_test/scenario.txt") - - self.assertEqual(scenario.ta, ["echo", "Hello"]) - self.assertEqual(scenario.execdir, ".") - self.assertFalse(scenario.deterministic) - self.assertEqual(scenario.pcs_fn, "tests/test_files/scenario_test/param.pcs") - self.assertEqual(scenario.overall_obj, "mean10") - self.assertEqual(scenario.cutoff, 5.0) - self.assertEqual(scenario.algo_runs_timelimit, np.inf) - self.assertEqual(scenario.wallclock_limit, 18000) - self.assertEqual(scenario.par_factor, 10) - self.assertEqual(scenario.train_insts, ["d", "e", "f"]) - self.assertEqual(scenario.test_insts, ["a", "b", "c"]) - test_dict = {"d": 1, "e": 2, "f": 3} - self.assertEqual(scenario.feature_dict, test_dict) - self.assertEqual(scenario.feature_array[0], 1) - - def test_dict_scenario(self): - scenario = Scenario(self.test_scenario_dict) - - self.assertEqual(scenario.ta, ["echo", "Hello"]) - self.assertEqual(scenario.execdir, ".") - self.assertFalse(scenario.deterministic) - self.assertEqual(scenario.pcs_fn, "tests/test_files/scenario_test/param.pcs") - self.assertEqual(scenario.overall_obj, "mean10") - self.assertEqual(scenario.cutoff, 5.0) - self.assertEqual(scenario.algo_runs_timelimit, np.inf) - self.assertEqual(scenario.wallclock_limit, 18000) - self.assertEqual(scenario.par_factor, 10) - self.assertEqual(scenario.train_insts, ["d", "e", "f"]) - self.assertEqual(scenario.test_insts, ["a", "b", "c"]) - test_dict = {"d": 1, "e": 2, "f": 3} - self.assertEqual(scenario.feature_dict, test_dict) - self.assertEqual(scenario.feature_array[0], 1) - - def unknown_parameter_in_scenario(self): - self.assertRaisesRegex( - ValueError, - "Could not parse the following arguments: " "duairznbvulncbzpneairzbnuqdae", - Scenario, - {"wallclock-limit": "12345", "duairznbvulncbzpneairzbnuqdae": "uqpab"}, - ) - - def test_merge_foreign_data(self): - """test smac.utils.merge_foreign_data""" - - scenario = Scenario(self.test_scenario_dict) - scenario_2 = Scenario(self.test_scenario_dict) - scenario_2.feature_dict = {"inst_new": [4]} - - # init cs - cs = ConfigurationSpace() - cs.add_hyperparameter(UniformIntegerHyperparameter(name="a", lower=0, upper=100)) - cs.add_hyperparameter(UniformIntegerHyperparameter(name="b", lower=0, upper=100)) - # build runhistory - rh_merge = RunHistory() - config = Configuration(cs, values={"a": 1, "b": 2}) - - rh_merge.add( - config=config, - instance_id="inst_new", - cost=10, - time=20, - status=StatusType.SUCCESS, - seed=None, - additional_info=None, - ) - - # "d" is an instance in - rh_merge.add( - config=config, - instance_id="d", - cost=5, - time=20, - status=StatusType.SUCCESS, - seed=None, - additional_info=None, - ) - - # build empty rh - rh_base = RunHistory() - - merge_foreign_data( - scenario=scenario, - runhistory=rh_base, - in_scenario_list=[scenario_2], - in_runhistory_list=[rh_merge], - ) - - # both runs should be in the runhistory - # but we should not use the data to update the cost of config - self.assertTrue(len(rh_base.data) == 2) - self.assertTrue(np.isnan(rh_base.get_cost(config))) - - # we should not get direct access to external run data - runs = rh_base.get_runs_for_config(config, only_max_observed_budget=True) - self.assertTrue(len(runs) == 0) - - rh_merge.add( - config=config, - instance_id="inst_new_2", - cost=10, - time=20, - status=StatusType.SUCCESS, - seed=None, - additional_info=None, - ) - - self.assertRaises( - ValueError, - merge_foreign_data, - **{ - "scenario": scenario, - "runhistory": rh_base, - "in_scenario_list": [scenario_2], - "in_runhistory_list": [rh_merge], - }, - ) - - def test_pickle_dump(self): - scenario = Scenario(self.test_scenario_dict) - - packed_scenario = pickle.dumps(scenario) - self.assertIsNotNone(packed_scenario) - - unpacked_scenario = pickle.loads(packed_scenario) # nosec - self.assertIsNotNone(unpacked_scenario) - self.assertIsNotNone(unpacked_scenario.logger) - self.assertEqual(scenario.logger.name, unpacked_scenario.logger.name) - - def test_write(self): - """Test whether a reloaded scenario still holds all the necessary - information. A subset of parameters might change, such as the paths to - pcs- or instance-files, so they are checked manually.""" - - def check_scen_eq(scen1, scen2): - """Customized check for scenario-equality, ignoring file-paths""" - print("check_scen_eq") - for name in scen1._arguments: - dest = scen1._arguments[name]["dest"] - name = dest if dest else name # if 'dest' is None, use 'name' - if name in ["pcs_fn", "train_inst_fn", "test_inst_fn", "feature_fn", "output_dir"]: - continue # Those values are allowed to change when writing to disk - elif name == "cs": - # Using repr because of cs-bug - # (https://github.com/automl/ConfigSpace/issues/25) - self.assertEqual(repr(scen1.cs), repr(scen2.cs)) - elif name == "feature_dict": - self.assertEqual(len(scen1.feature_dict), len(scen2.feature_dict)) - for key in scen1.feature_dict: - self.assertTrue((scen1.feature_dict[key] == scen2.feature_dict[key]).all()) - else: - print(name, getattr(scen1, name), getattr(scen2, name)) - self.assertEqual(getattr(scen1, name), getattr(scen2, name)) - - # First check with file-paths defined - feature_filename = "tests/test_files/scenario_test/features_multiple.txt" - feature_filename = os.path.abspath(feature_filename) - self.test_scenario_dict["feature_file"] = feature_filename - scenario = Scenario(self.test_scenario_dict) - # This injection would usually happen by the facade object! - scenario.output_dir_for_this_run = scenario.output_dir - scenario.write() - path = os.path.join(scenario.output_dir, "scenario.txt") - scenario_reloaded = Scenario(path) - - check_scen_eq(scenario, scenario_reloaded) - # Test whether json is the default pcs_fn - self.assertTrue(os.path.exists(os.path.join(scenario.output_dir, "param.pcs"))) - self.assertTrue(os.path.exists(os.path.join(scenario.output_dir, "param.json"))) - self.assertEqual(scenario_reloaded.pcs_fn, os.path.join(scenario.output_dir, "param.json")) - - # Now create new scenario without filepaths - self.test_scenario_dict.update( - { - "paramfile": None, - "cs": scenario.cs, - "feature_file": None, - "features": scenario.feature_dict, - "feature_names": scenario.feature_names, - "instance_file": None, - "instances": scenario.train_insts, - "test_instance_file": None, - "test_instances": scenario.test_insts, - } - ) - logging.debug(scenario_reloaded) - scenario_no_fn = Scenario(self.test_scenario_dict) - scenario_reloaded = Scenario(path) - check_scen_eq(scenario_no_fn, scenario_reloaded) - # Test whether json is the default pcs_fn - self.assertTrue(os.path.exists(os.path.join(scenario.output_dir, "param.pcs"))) - self.assertTrue(os.path.exists(os.path.join(scenario.output_dir, "param.json"))) - self.assertEqual(scenario_reloaded.pcs_fn, os.path.join(scenario.output_dir, "param.json")) - - @mock.patch.object(os, "makedirs") - @mock.patch.object(os.path, "isdir") - def test_write_except(self, patch_isdir, patch_mkdirs): - patch_isdir.return_value = False - patch_mkdirs.side_effect = OSError() - scenario = Scenario(self.test_scenario_dict) - # This injection would usually happen by the facade object! - scenario.output_dir_for_this_run = scenario.output_dir - with self.assertRaises(OSError): - scenario.write() - - def test_no_output_dir(self): - self.test_scenario_dict["output_dir"] = "" - scenario = Scenario(self.test_scenario_dict) - self.assertFalse(scenario.out_writer.write_scenario_file(scenario)) - - def test_par_factor(self): - # Test setting the default value of 1 if no factor is given - scenario_dict = self.test_scenario_dict - scenario_dict["overall_obj"] = "mean" - scenario = Scenario(scenario_dict) - self.assertEqual(scenario.par_factor, 1) - scenario_dict["overall_obj"] = "par" - scenario = Scenario(scenario_dict) - self.assertEqual(scenario.par_factor, 1) - - def test_truth_value(self): - self.assertTrue(_is_truthy("1")) - self.assertTrue(_is_truthy("true")) - self.assertTrue(_is_truthy(True)) - self.assertFalse(_is_truthy("0")) - self.assertFalse(_is_truthy("false")) - self.assertFalse(_is_truthy(False)) - self.assertRaises(ValueError, _is_truthy, "something") - - def test_str_cast_instances(self): - self.scen = Scenario({"cs": None, "instances": [[1], [2]], "run_obj": "quality"}) - self.assertIsInstance(self.scen.train_insts[0], str) - self.assertIsInstance(self.scen.train_insts[1], str) - - def test_features(self): - cmd_options = { - "feature_file": "tests/test_files/features_example.csv", - "instance_file": "tests/test_files/train_insts_example.txt", - } - scenario = Scenario(self.test_scenario_dict, cmd_options=cmd_options) - self.assertEqual(scenario.feature_names, ["feature1", "feature2", "feature3"]) - - def test_multi_objectives(self): - scenario = Scenario({"run_obj": "quality", "multi_objectives": "test1, test2"}) - - assert scenario.multi_objectives == ["test1", "test2"] - - scenario = Scenario({"run_obj": "quality", "multi_objectives": "test1,test2"}) - - assert scenario.multi_objectives == ["test1", "test2"] - - scenario = Scenario({"run_obj": "quality", "multi_objectives": ["test1", "test2"]}) - - assert scenario.multi_objectives == ["test1", "test2"] - - scenario = Scenario({"run_obj": "quality", "multi_objectives": "test1"}) - - assert scenario.multi_objectives == ["test1"] - - scenario = Scenario({"run_obj": "quality"}) - - assert scenario.multi_objectives == ["cost"] - - scenario = Scenario( - { - "run_obj": "quality", - "multi_objectives": "m1, m2", - "cost_for_crash": "1., 500", - } - ) - - assert scenario.multi_objectives == ["m1", "m2"] - assert scenario.cost_for_crash == [1.0, 500.0] - - scenario = Scenario( - { - "run_obj": "quality", - "multi_objectives": "m1, m2", - "cost_for_crash": "2.5", - } - ) - - assert scenario.multi_objectives == ["m1", "m2"] - assert scenario.cost_for_crash == [2.5, 2.5] - - scenario = Scenario( - { - "run_obj": "quality", - "multi_objectives": "m1, m2", - "cost_for_crash": 500, - } - ) - - assert scenario.multi_objectives == ["m1", "m2"] - assert scenario.cost_for_crash == [500.0, 500.0] - - -if __name__ == "__main__": - unittest.main() +from pathlib import Path + +import pytest +from ConfigSpace import ConfigurationSpace + +from smac import Scenario + + +@pytest.fixture +def configspace() -> ConfigurationSpace: + return ConfigurationSpace({"a": (0, 5)}) + + +@pytest.fixture +def scenario1(configspace: ConfigurationSpace) -> Scenario: + return Scenario(configspace, output_directory=Path("smac3_output_test"), n_trials=50) + + +@pytest.fixture +def scenario2(configspace: ConfigurationSpace) -> Scenario: + return Scenario(configspace, output_directory=Path("smac3_output_test"), n_trials=50) + + +@pytest.fixture +def scenario3(configspace: ConfigurationSpace) -> Scenario: + return Scenario( + configspace, + name="test_scenario", + output_directory=Path("smac3_output_test"), + n_trials=20, + seed=5, + ) + + +@pytest.fixture +def scenario4(configspace: ConfigurationSpace) -> Scenario: + return Scenario(configspace, objectives=["test", "blub"]) + + +@pytest.fixture +def scenario5(configspace: ConfigurationSpace) -> Scenario: + return Scenario( + configspace, + name="test_scenario", + output_directory=Path("smac3_output_test"), + n_trials=100, + seed=5, + instances=["i1", "i2", "i3"], + instance_features={"i1": [1, 2, 3], "i2": [4, 5, 6], "i3": [7, 8, 9]}, + ) + + +@pytest.fixture +def scenario6(configspace: ConfigurationSpace) -> Scenario: + return Scenario( + configspace, + name="test_scenario", + output_directory=Path("smac3_output_test"), + instances=["i1", "i2", "i3"], + instance_features={"i1": [1, 2, 3], "i2": [4, 5], "i3": [7, 8, 9]}, + ) + + +@pytest.fixture +def scenario7(configspace: ConfigurationSpace) -> Scenario: + return Scenario( + configspace, + name="test_scenario", + output_directory=Path("smac3_output_test"), + instances=["i1", "i2", "i3"], + instance_features={"blub": [1, 2, 3], "i2": [4, 5, 6], "i3": [7, 8, 9]}, + ) + + +def test_comparison(scenario1: Scenario, scenario2: Scenario, scenario3: Scenario) -> None: + assert scenario1 == scenario2 + assert scenario1 != scenario3 + + +def test_directory(scenario3: Scenario) -> None: + assert str(scenario3.output_directory) == "smac3_output_test/test_scenario/5" + + +def test_frozen(scenario1: Scenario) -> None: + with pytest.raises(Exception): + scenario1.deterministic = False + + +def test_objectives(scenario3: Scenario, scenario4: Scenario) -> None: + assert scenario3.count_objectives() == 1 + assert scenario4.count_objectives() == 2 + + +def test_save_load(scenario1: Scenario, scenario3: Scenario) -> None: + # This should fail because we don't know the name of the scenario as meta data are not defined either + with pytest.raises(RuntimeError): + scenario1.save() + + # If we set meta data, it should work + meta = {"test": {"test": "test"}} + scenario1._set_meta(meta) + scenario1.save() + + # We reload the scenario again and it should be the same as before + reloaded_scenario = Scenario.load(scenario1.output_directory) + assert scenario1 == reloaded_scenario + + # Do it one more time with scenario 3 + scenario3.save() + reloaded_scenario = Scenario.load(scenario3.output_directory) + assert scenario3 == reloaded_scenario + + +def test_instances(scenario5: Scenario, scenario6: Scenario, scenario7: Scenario) -> None: + assert scenario5.count_instance_features() == 3 + + with pytest.raises(RuntimeError, match="Instances must have the same number of features"): + scenario6.count_instance_features() + + with pytest.raises(RuntimeError, match="Instance blub is not specified"): + scenario7.count_instance_features() diff --git a/tests/test_smbo/__init__.py b/tests/test_smbo/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/test_smbo/test/traj_old.csv b/tests/test_smbo/test/traj_old.csv deleted file mode 100644 index 561325e25..000000000 --- a/tests/test_smbo/test/traj_old.csv +++ /dev/null @@ -1 +0,0 @@ -"CPU Time Used","Estimated Training Performance","Wallclock Time","Incumbent ID","Automatic Configurator (CPU) Time","Configuration..." diff --git a/tests/test_smbo/test_acquisition.py b/tests/test_smbo/test_acquisition.py deleted file mode 100644 index 447dcb743..000000000 --- a/tests/test_smbo/test_acquisition.py +++ /dev/null @@ -1,609 +0,0 @@ -import unittest -import unittest.mock - -import numpy as np - -from smac.optimizer.acquisition import ( - EI, - EIPS, - LCB, - PI, - TS, - IntegratedAcquisitionFunction, - LogEI, - PriorAcquisitionFunction, -) - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class ConfigurationMock(object): - def __init__(self, values=None): - self.values = values - self.configuration_space = unittest.mock.MagicMock() - self.configuration_space.get_hyperparameters.return_value = [] - - def get_array(self): - return self.values - - -class MockModel(object): - def __init__(self, num_targets=1, seed=0): - self.num_targets = num_targets - self.seed = seed - - def predict_marginalized_over_instances(self, X): - return np.array([np.mean(X, axis=1).reshape((1, -1))] * self.num_targets).reshape((-1, 1)), np.array( - [np.mean(X, axis=1).reshape((1, -1))] * self.num_targets - ).reshape((-1, 1)) - - -class MockModelDual(object): - def __init__(self, num_targets=1): - self.num_targets = num_targets - - def predict_marginalized_over_instances(self, X): - return np.array([np.mean(X, axis=1).reshape((1, -1))] * self.num_targets).reshape((-1, 2)), np.array( - [np.mean(X, axis=1).reshape((1, -1))] * self.num_targets - ).reshape((-1, 2)) - - -class MockPrior(object): - def __init__(self, pdf, max_density): - self.pdf = pdf - self.max_density = max_density - - def _pdf(self, X): - return self.pdf(X) - - def get_max_density(self): - return self.max_density - - -class PriorMockModel(object): - def __init__(self, hyperparameter_dict=None, num_targets=1, seed=0): - self.num_targets = num_targets - self.seed = seed - self.configuration_space = unittest.mock.MagicMock() - self.hyperparameter_dict = hyperparameter_dict - # since the PriorAcquisitionFunction needs to return the hyperparameters in dict - # form through two function calls (self.model.get_configspace().get_hyperparameters_dict()), - # we need a slightly intricate solution - self.configuration_space.get_hyperparameters_dict.return_value = self.hyperparameter_dict - - def get_configspace(self): - return self.configuration_space - - def predict_marginalized_over_instances(self, X): - return np.array([np.mean(X, axis=1).reshape((1, -1))] * self.num_targets).reshape((-1, 1)), np.array( - [np.mean(X, axis=1).reshape((1, -1))] * self.num_targets - ).reshape((-1, 1)) - - def update_prior(self, hyperparameter_dict): - self.configuration_space.get_hyperparameters_dict.return_value = hyperparameter_dict - - -class MockModelRNG(MockModel): - def __init__(self, num_targets=1, seed=0): - self.num_targets = num_targets - self.seed = seed - self.rng = np.random.RandomState(self.seed) - - -class MockModelSampler(MockModelRNG): - def __init__(self, num_targets=1, seed=0): - self.num_targets = num_targets - self.seed = seed - self.rng = np.random.RandomState(seed) - - def sample_functions(self, X, n_funcs=1): - m = np.array([np.mean(X, axis=1).reshape((1, -1))] * self.num_targets).reshape((-1,)) - var = np.array([np.mean(X, axis=1).reshape((1, -1))] * self.num_targets).reshape((-1,)) - var = np.diag(var) - return self.rng.multivariate_normal(m, var, n_funcs).T - - -class TestAcquisitionFunction(unittest.TestCase): - def setUp(self): - self.model = unittest.mock.Mock() - self.acq = EI(model=self.model) - - def test_update_model_and_eta(self): - model = "abc" - self.assertIsNone(self.acq.eta) - self.acq.update(model=model, eta=0.1) - self.assertEqual(self.acq.model, model) - self.assertEqual(self.acq.eta, 0.1) - - def test_update_other(self): - self.acq.other = "other" - - with self.assertRaisesRegex( - ValueError, - r"Acquisition function EI needs to be updated with key model, but only got keys " r"\['other'\].", - ): - self.acq.update(other=None) - - model = "abc" - self.acq.update(model=model, eta=0.1, other=None) - self.assertEqual(self.acq.other, "other") - - -class TestIntegratedAcquisitionFunction(unittest.TestCase): - def setUp(self): - self.model = unittest.mock.Mock() - self.model.models = [MockModel(), MockModel(), MockModel()] - self.ei = EI(self.model) - - def test_update(self): - iaf = IntegratedAcquisitionFunction(model=self.model, acquisition_function=self.ei) - iaf.update(model=self.model, eta=2) - for func in iaf._functions: - self.assertEqual(func.eta, 2) - - with self.assertRaisesRegex( - ValueError, - "IntegratedAcquisitionFunction requires at least one model to integrate!", - ): - iaf.update(model=MockModel()) - - with self.assertRaisesRegex( - ValueError, - "IntegratedAcquisitionFunction requires at least one model to integrate!", - ): - self.model.models = [] - iaf.update(model=self.model) - - def test_compute(self): - class CountingMock: - counter = 0 - long_name = "CountingMock" - - def _compute(self, *args, **kwargs): - self.counter += 1 - return self.counter - - def update(self, **kwargs): - pass - - iaf = IntegratedAcquisitionFunction(model=self.model, acquisition_function=CountingMock()) - iaf.update(model=self.model) - configurations = [ConfigurationMock([1.0, 1.0, 1.0])] - rval = iaf(configurations) - self.assertEqual(rval, 1) - - # Test that every counting mock is called only once! - for counting_mock in iaf._functions: - self.assertEqual(counting_mock.counter, 1) - - def test_compute_with_different_numbers_of_models(self): - - for i in range(1, 3): - self.model.models = [MockModel()] * i - iaf = IntegratedAcquisitionFunction(model=self.model, acquisition_function=self.ei) - iaf.update(model=self.model, eta=1) - configurations = [ConfigurationMock([1.0, 1.0, 1.0])] - rval = iaf(configurations) - self.assertEqual(rval.shape, (1, 1)) - - configurations = [ - ConfigurationMock([1.0, 1.0, 1.0]), - ConfigurationMock([1.0, 2.0, 3.0]), - ] - rval = iaf(configurations) - self.assertEqual(rval.shape, (2, 1)) - - -class TestPriorAcquisitionFunction(unittest.TestCase): - def setUp(self): - x0_prior = MockPrior(pdf=lambda x: 2 * x, max_density=2) - hyperparameter_dict = {"x0": x0_prior} - self.model = PriorMockModel(hyperparameter_dict=hyperparameter_dict) - self.ei = EI(self.model) - self.ts = TS(self.model) - self.beta = 2 - self.prior_floor = 1e-1 - - def test_init_ei(self): - paf = PriorAcquisitionFunction(self.model, self.ei, self.beta) - self.assertFalse(paf.rescale_acq) - - def test_init_ts(self): - paf = PriorAcquisitionFunction(self.model, self.ts, self.beta) - self.assertTrue(paf.rescale_acq) - - def test_update(self): - paf = PriorAcquisitionFunction(self.model, self.ei, self.beta) - paf.update(model=self.model, eta=2) - self.assertEqual(paf.eta, 2) - self.assertEqual(paf.acq.eta, 2) - self.assertEqual(paf.iteration_number, 1) - - def test_compute_prior_Nx1(self): - x0_prior = MockPrior(pdf=lambda x: 2 * x, max_density=2) - hyperparameter_dict = {"x0": x0_prior} - self.model.update_prior(hyperparameter_dict) - paf = PriorAcquisitionFunction(self.model, self.ei, self.beta) - - X = np.array([0, 0.5, 1]).reshape(3, 1) - prior_values = paf._compute_prior(X) - - self.assertEqual(prior_values.shape, (3, 1)) - self.assertEqual(prior_values[0][0], 0) - self.assertEqual(prior_values[1][0], 1) - self.assertEqual(prior_values[2][0], 2) - - def test_compute_prior_NxD(self): - x0_prior = MockPrior(pdf=lambda x: 2 * x, max_density=2) - x1_prior = MockPrior(pdf=lambda x: np.ones_like(x), max_density=1) - hyperparameter_dict = {"x0": x0_prior, "x1": x1_prior} - self.model.update_prior(hyperparameter_dict) - paf = PriorAcquisitionFunction(self.model, self.ei, self.beta) - - X = np.array([[0, 0], [0, 1], [1, 1]]) - prior_values = paf._compute_prior(X) - - self.assertEqual(prior_values.shape, (3, 1)) - self.assertEqual(prior_values[0][0], 0) - self.assertEqual(prior_values[1][0], 0) - self.assertEqual(prior_values[2][0], 2) - - def test_compute_prior_1xD(self): - x0_prior = MockPrior(pdf=lambda x: 2 * x, max_density=2) - x1_prior = MockPrior(pdf=lambda x: np.ones_like(x), max_density=1) - hyperparameter_dict = {"x0": x0_prior, "x1": x1_prior} - self.model.update_prior(hyperparameter_dict) - paf = PriorAcquisitionFunction(self.model, self.ei, self.beta) - - X = np.array([[0.5, 0.5]]) - prior_values = paf._compute_prior(X) - - self.assertEqual(prior_values.shape, (1, 1)) - self.assertEqual(prior_values[0][0], 1) - - def test_compute_prior_1x1(self): - x0_prior = MockPrior(pdf=lambda x: 2 * x, max_density=2) - hyperparameter_dict = {"x0": x0_prior} - self.model.update_prior(hyperparameter_dict) - paf = PriorAcquisitionFunction(self.model, self.ei, self.beta) - - X = np.array([0.5]).reshape(1, 1) - prior_values = paf._compute_prior(X) - - self.assertEqual(prior_values.shape, (1, 1)) - self.assertEqual(prior_values[0][0], 1) - - def test_1xD(self): - x0_prior = MockPrior(pdf=lambda x: 2 * x, max_density=2) - x1_prior = MockPrior(pdf=lambda x: np.ones_like(x), max_density=1) - x2_prior = MockPrior(pdf=lambda x: 2 - 2 * x, max_density=2) - hyperparameter_dict = {"x0": x0_prior, "x1": x1_prior, "x2": x2_prior} - self.model.update_prior(hyperparameter_dict) - paf = PriorAcquisitionFunction(self.model, self.ei, self.beta, self.prior_floor) - - paf.update(model=self.model, eta=1.0) - configurations = [ConfigurationMock([1.0, 1.0, 1.0])] - acq = paf(configurations) - self.assertEqual(acq.shape, (1, 1)) - - prior_0_factor = np.power(2.0 * 1.0 * 0.0 + paf.prior_floor, self.beta / 1.0) - - self.assertAlmostEqual(acq[0][0], 0.3989422804014327 * prior_0_factor) - - def test_NxD(self): - x0_prior = MockPrior(pdf=lambda x: 2 * x, max_density=2) - x1_prior = MockPrior(pdf=lambda x: np.ones_like(x), max_density=1) - x2_prior = MockPrior(pdf=lambda x: 2 - 2 * x, max_density=2) - hyperparameter_dict = {"x0": x0_prior, "x1": x1_prior, "x2": x2_prior} - self.model.update_prior(hyperparameter_dict) - paf = PriorAcquisitionFunction(self.model, self.ei, self.beta, self.prior_floor) - - paf.update(model=self.model, eta=1.0) - # These are the exact same numbers as in the EI tests below - configurations = [ - ConfigurationMock([0.0, 0.0, 0.0]), - ConfigurationMock([0.1, 0.1, 0.1]), - ConfigurationMock([1.0, 1.0, 1.0]), - ] - acq = paf(configurations) - self.assertEqual(acq.shape, (3, 1)) - - prior_0_factor = np.power(0.0 * 1.0 * 2.0 + paf.prior_floor, self.beta / 1.0) - prior_1_factor = np.power(0.2 * 1.0 * 1.8 + paf.prior_floor, self.beta / 1.0) - prior_2_factor = np.power(2.0 * 1.0 * 0.0 + paf.prior_floor, self.beta / 1.0) - - # We do only one update, so we are at iteration 1 (beta/iteration_nbr=2) - self.assertAlmostEqual(acq[0][0], 0.0 * prior_0_factor) - self.assertAlmostEqual(acq[1][0], 0.90020601136712231 * prior_1_factor) - self.assertAlmostEqual(acq[2][0], 0.3989422804014327 * prior_2_factor) - - def test_NxD_TS(self): - # since there is some rescaling that needs to be done for TS and UCB (the same - # scaling for the two of them), it justifies testing as well - x0_prior = MockPrior(pdf=lambda x: 2 * x, max_density=2) - x1_prior = MockPrior(pdf=lambda x: np.ones_like(x), max_density=1) - x2_prior = MockPrior(pdf=lambda x: 2 - 2 * x, max_density=2) - hyperparameter_dict = {"x0": x0_prior, "x1": x1_prior, "x2": x2_prior} - self.model.update_prior(hyperparameter_dict) - paf = PriorAcquisitionFunction(self.model, self.ts, self.beta, self.prior_floor) - eta = 1.0 - - paf.update(model=self.model, eta=eta) - - configurations = [ - ConfigurationMock([0.0001, 0.0001, 0.0001]), - ConfigurationMock([0.1, 0.1, 0.1]), - ConfigurationMock([1.0, 1.0, 1.0]), - ] - acq = paf(configurations) - self.assertEqual(acq.shape, (3, 1)) - - # retrieved from TS example - ts_value_0 = -0.00988738 - ts_value_1 = -0.22654082 - ts_value_2 = -2.76405235 - - prior_0_factor = np.power(0.0002 * 1 * 1.9998 + paf.prior_floor, self.beta / 1.0) - prior_1_factor = np.power(0.2 * 1.0 * 1.8 + paf.prior_floor, self.beta / 1.0) - prior_2_factor = np.power(2.0 * 1.0 * 0.0 + paf.prior_floor, self.beta / 1.0) - - # rescaling to avoid negative values, and keep the TS ranking intact - combined_value_0 = np.clip(ts_value_0 + eta, 0, np.inf) * prior_0_factor - combined_value_1 = np.clip(ts_value_1 + eta, 0, np.inf) * prior_1_factor - combined_value_2 = np.clip(ts_value_2 + eta, 0, np.inf) * prior_2_factor - - self.assertAlmostEqual(acq[0][0], combined_value_0) - self.assertAlmostEqual(acq[1][0], combined_value_1) - self.assertAlmostEqual(acq[2][0], combined_value_2) - - def test_decay(self): - x0_prior = MockPrior(pdf=lambda x: 2 * x, max_density=2) - x1_prior = MockPrior(pdf=lambda x: np.ones_like(x), max_density=1) - x2_prior = MockPrior(pdf=lambda x: 2 - 2 * x, max_density=2) - hyperparameter_dict = {"x0": x0_prior, "x1": x1_prior, "x2": x2_prior} - self.model.update_prior(hyperparameter_dict) - paf = PriorAcquisitionFunction(self.model, self.ei, self.beta, self.prior_floor) - configurations = [ConfigurationMock([0.1, 0.1, 0.1])] - - for i in range(1, 6): - paf.update(model=self.model, eta=1.0) - prior_factor = np.power(0.2 * 1.0 * 1.8 + paf.prior_floor, self.beta / i) - acq = paf(configurations) - self.assertAlmostEqual(acq[0][0], 0.90020601136712231 * prior_factor) - - def test_discretize_pdf(self): - x0_prior = MockPrior(pdf=lambda x: 2 * x, max_density=2) - hyperparameter_dict = {"x0": x0_prior} - self.model.update_prior(hyperparameter_dict) - paf = PriorAcquisitionFunction(self.model, self.ei, self.beta, self.prior_floor, discretize=True) - - number_of_bins_1 = 13 - number_of_bins_2 = 27521 - number_of_points = 1001 - - discrete_values_1 = paf._compute_discretized_pdf( - x0_prior, np.linspace(0, 1, number_of_points), number_of_bins=number_of_bins_1 - ) - discrete_values_2 = paf._compute_discretized_pdf( - x0_prior, np.linspace(0, 1, number_of_points), number_of_bins=number_of_bins_2 - ) - number_unique_values_1 = len(np.unique(discrete_values_1)) - number_unique_values_2 = len(np.unique(discrete_values_2)) - - self.assertEqual(number_unique_values_1, number_of_bins_1) - self.assertEqual(number_unique_values_2, number_of_points) - with self.assertRaises(ValueError): - paf._compute_discretized_pdf(x0_prior, np.linspace(0, 1, number_of_points), number_of_bins=-1) - - -class TestEI(unittest.TestCase): - def setUp(self): - self.model = MockModel() - self.ei = EI(self.model) - - def test_1xD(self): - self.ei.update(model=self.model, eta=1.0) - configurations = [ConfigurationMock([1.0, 1.0, 1.0])] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (1, 1)) - self.assertAlmostEqual(acq[0][0], 0.3989422804014327) - - def test_NxD(self): - self.ei.update(model=self.model, eta=1.0) - configurations = [ - ConfigurationMock([0.0, 0.0, 0.0]), - ConfigurationMock([0.1, 0.1, 0.1]), - ConfigurationMock([1.0, 1.0, 1.0]), - ] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (3, 1)) - self.assertAlmostEqual(acq[0][0], 0.0) - self.assertAlmostEqual(acq[1][0], 0.90020601136712231) - self.assertAlmostEqual(acq[2][0], 0.3989422804014327) - - def test_1x1(self): - self.ei.update(model=self.model, eta=1.0) - configurations = [ConfigurationMock([1.0])] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (1, 1)) - self.assertAlmostEqual(acq[0][0], 0.3989422804014327) - - def test_Nx1(self): - self.ei.update(model=self.model, eta=1.0) - configurations = [ - ConfigurationMock([0.0001]), - ConfigurationMock([1.0]), - ConfigurationMock([2.0]), - ] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (3, 1)) - self.assertAlmostEqual(acq[0][0], 0.9999) - self.assertAlmostEqual(acq[1][0], 0.3989422804014327) - self.assertAlmostEqual(acq[2][0], 0.19964122837424575) - - def test_zero_variance(self): - self.ei.update(model=self.model, eta=1.0) - X = np.array([ConfigurationMock([0.0])]) - acq = np.array(self.ei(X)) - self.assertAlmostEqual(acq[0][0], 0.0) - - -class TestEIPS(unittest.TestCase): - def setUp(self): - self.model = MockModelDual() - self.ei = EIPS(self.model) - - def test_1xD(self): - self.ei.update(model=self.model, eta=1.0) - configurations = [ConfigurationMock([1.0, 1.0]), ConfigurationMock([1.0, 1.0])] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (1, 1)) - self.assertAlmostEqual(acq[0][0], 0.3989422804014327) - - def test_fail(self): - with self.assertRaises(ValueError): - configurations = [ConfigurationMock([1.0, 1.0])] - self.ei(configurations) - - -class TestLogEI(unittest.TestCase): - def setUp(self): - self.model = MockModel() - self.ei = LogEI(self.model) - - def test_1xD(self): - self.ei.update(model=self.model, eta=1.0) - configurations = [ConfigurationMock([1.0, 1.0, 1.0])] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (1, 1)) - self.assertAlmostEqual(acq[0][0], 0.6480973967332011) - - def test_NxD(self): - self.ei.update(model=self.model, eta=1.0) - configurations = [ - ConfigurationMock([0.1, 0.0, 0.0]), - ConfigurationMock([0.1, 0.1, 0.1]), - ConfigurationMock([1.0, 1.0, 1.0]), - ] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (3, 1)) - self.assertAlmostEqual(acq[0][0], 1.6670107375002425) - self.assertAlmostEqual(acq[1][0], 1.5570607606556273) - self.assertAlmostEqual(acq[2][0], 0.6480973967332011) - - -class TestPI(unittest.TestCase): - def setUp(self): - self.model = MockModel() - self.ei = PI(self.model) - - def test_1xD(self): - self.ei.update(model=self.model, eta=1.0) - configurations = [ConfigurationMock([0.5, 0.5, 0.5])] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (1, 1)) - self.assertAlmostEqual(acq[0][0], 0.7602499389065233) - - def test_1xD_zero(self): - self.ei.update(model=self.model, eta=1.0) - configurations = [ConfigurationMock([100, 100, 100])] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (1, 1)) - self.assertAlmostEqual(acq[0][0], 0) - - def test_NxD(self): - self.ei.update(model=self.model, eta=1.0) - configurations = [ - ConfigurationMock([0.0001, 0.0001, 0.0001]), - ConfigurationMock([0.1, 0.1, 0.1]), - ConfigurationMock([1.0, 1.0, 1.0]), - ] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (3, 1)) - self.assertAlmostEqual(acq[0][0], 1.0) - self.assertAlmostEqual(acq[1][0], 0.99778673707104) - self.assertAlmostEqual(acq[2][0], 0.5) - - -class TestLCB(unittest.TestCase): - def setUp(self): - self.model = MockModel() - self.ei = LCB(self.model) - - def test_1xD(self): - self.ei.update(model=self.model, eta=1.0, par=1, num_data=3) - configurations = [ConfigurationMock([0.5, 0.5, 0.5])] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (1, 1)) - self.assertAlmostEqual(acq[0][0], 1.315443985917585) - self.ei.update(model=self.model, eta=1.0, par=1, num_data=100) - configurations = [ConfigurationMock([0.5, 0.5, 0.5])] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (1, 1)) - self.assertAlmostEqual(acq[0][0], 2.7107557771721433) - - def test_1xD_no_improvement_vs_improvement(self): - self.ei.update(model=self.model, par=1, num_data=1) - configurations = [ConfigurationMock([100, 100])] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (1, 1)) - self.assertAlmostEqual(acq[0][0], -88.22589977) - configurations = [ConfigurationMock([0.001, 0.001])] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (1, 1)) - self.assertAlmostEqual(acq[0][0], 0.03623297) - - def test_NxD(self): - self.ei.update(model=self.model, eta=1.0, num_data=100) - configurations = [ - ConfigurationMock([0.0001, 0.0001, 0.0001]), - ConfigurationMock([0.1, 0.1, 0.1]), - ConfigurationMock([1.0, 1.0, 1.0]), - ] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (3, 1)) - self.assertAlmostEqual(acq[0][0], 0.045306943655446116) - self.assertAlmostEqual(acq[1][0], 1.3358936353814157) - self.assertAlmostEqual(acq[2][0], 3.5406943655446117) - - -class TestTS(unittest.TestCase): - def setUp(self): - # Test TS acqusition function with model that only has attribute 'seed' - self.model = MockModel() - self.ei = TS(self.model) - - def test_1xD(self): - self.ei.update(model=self.model) - configurations = [ConfigurationMock([0.5, 0.5, 0.5])] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (1, 1)) - self.assertAlmostEqual(acq[0][0], -1.74737338) - - def test_NxD(self): - self.ei.update(model=self.model) - configurations = [ - ConfigurationMock([0.0001, 0.0001, 0.0001]), - ConfigurationMock([0.1, 0.1, 0.1]), - ConfigurationMock([1.0, 1.0, 1.0]), - ] - acq = self.ei(configurations) - self.assertEqual(acq.shape, (3, 1)) - self.assertAlmostEqual(acq[0][0], -0.00988738) - self.assertAlmostEqual(acq[1][0], -0.22654082) - self.assertAlmostEqual(acq[2][0], -2.76405235) - - -class TestTSRNG(TestTS): - def setUp(self): - # Test TS acqusition function with model that only has attribute 'rng' - self.model = MockModelRNG() - self.ei = TS(self.model) - - -class TestTSSampler(TestTS): - def setUp(self): - # Test TS acqusition function with model that only has attribute 'sample_functions' - self.model = MockModelSampler() - self.ei = TS(self.model) diff --git a/tests/test_smbo/test_ei_optimization.py b/tests/test_smbo/test_ei_optimization.py deleted file mode 100644 index 42467579f..000000000 --- a/tests/test_smbo/test_ei_optimization.py +++ /dev/null @@ -1,375 +0,0 @@ -import os -import unittest -import unittest.mock - -import numpy as np -from ConfigSpace.hyperparameters import ( - BetaIntegerHyperparameter, - CategoricalHyperparameter, - NormalFloatHyperparameter, - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) -from scipy.spatial.distance import euclidean - -from smac.configspace import ConfigurationSpace, pcs -from smac.optimizer.acquisition import EI -from smac.optimizer.acquisition.maximizer import ( - LocalAndSortedPriorRandomSearch, - LocalSearch, - RandomSearch, -) -from smac.runhistory.runhistory import RunHistory -from smac.tae import StatusType -from smac.utils import test_helpers - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class ConfigurationMock(object): - def __init__(self, value=None): - self.value = value - - def get_array(self): - return [self.value] - - -def rosenbrock_4d(cfg): - x1 = cfg["x1"] - x2 = cfg["x2"] - x3 = cfg["x3"] - x4 = cfg["x4"] - - val = ( - 100 * (x2 - x1**2) ** 2 - + (x1 - 1) ** 2 - + 100 * (x3 - x2**2) ** 2 - + (x2 - 1) ** 2 - + 100 * (x4 - x3**2) ** 2 - + (x3 - 1) ** 2 - ) - - return val - - -class TestEIMaximization(unittest.TestCase): - @unittest.mock.patch("smac.optimizer.acquisition.convert_configurations_to_array") - @unittest.mock.patch.object(EI, "__call__") - @unittest.mock.patch.object(ConfigurationSpace, "sample_configuration") - def test_challenger_list_callback(self, patch_sample, patch_ei, patch_impute): - values = (10, 1, 9, 2, 8, 3, 7, 4, 6, 5) - patch_sample.return_value = ConfigurationMock(1) - patch_ei.return_value = np.array([[_] for _ in values], dtype=float) - patch_impute.side_effect = lambda l: values - cs = ConfigurationSpace() - ei = EI(None) - rs = RandomSearch(ei, cs) - rs._maximize = unittest.mock.Mock() - rs._maximize.return_value = [(0, 0)] - - rval = rs.maximize( - runhistory=None, - stats=None, - num_points=10, - ) - self.assertEqual(rs._maximize.call_count, 0) - next(rval) - self.assertEqual(rs._maximize.call_count, 1) - - random_configuration_chooser = unittest.mock.Mock() - random_configuration_chooser.check.side_effect = [True, False, False, False] - rs._maximize = unittest.mock.Mock() - rs._maximize.return_value = [(0, 0), (1, 1)] - - rval = rs.maximize( - runhistory=None, - stats=None, - num_points=10, - random_configuration_chooser=random_configuration_chooser, - ) - self.assertEqual(rs._maximize.call_count, 0) - # The first configuration is chosen at random (see the random_configuration_chooser mock) - conf = next(rval) - self.assertIsInstance(conf, ConfigurationMock) - self.assertEqual(rs._maximize.call_count, 0) - # The 2nd configuration triggers the call to the callback (see the random_configuration_chooser mock) - conf = next(rval) - self.assertEqual(rs._maximize.call_count, 1) - self.assertEqual(conf, 0) - # The 3rd configuration doesn't trigger the callback any more - conf = next(rval) - self.assertEqual(rs._maximize.call_count, 1) - self.assertEqual(conf, 1) - - with self.assertRaises(StopIteration): - next(rval) - - @unittest.mock.patch.object(ConfigurationSpace, "sample_configuration") - def test_get_next_by_random_search(self, patch): - def side_effect(size): - return [ConfigurationMock()] * size - - patch.side_effect = side_effect - cs = ConfigurationSpace() - ei = EI(None) - rs = RandomSearch(ei, cs) - rval = rs._maximize(runhistory=None, stats=None, num_points=10, _sorted=False) - self.assertEqual(len(rval), 10) - for i in range(10): - self.assertIsInstance(rval[i][1], ConfigurationMock) - self.assertEqual(rval[i][1].origin, "Random Search") - self.assertEqual(rval[i][0], 0) - - -class TestLocalSearch(unittest.TestCase): - def setUp(self): - current_dir = os.path.dirname(__file__) - self.test_files_dir = os.path.join(current_dir, "../../tests", "test_files") - seed = np.random.randint(1, 100000) - self.cs = ConfigurationSpace(seed=seed) - x1 = UniformFloatHyperparameter("x1", -5, 5, default_value=5) - self.cs.add_hyperparameter(x1) - x2 = UniformIntegerHyperparameter("x2", -5, 5, default_value=5) - self.cs.add_hyperparameter(x2) - x3 = CategoricalHyperparameter("x3", [5, 2, 0, 1, -1, -2, 4, -3, 3, -5, -4], default_value=5) - self.cs.add_hyperparameter(x3) - x4 = UniformIntegerHyperparameter("x4", -5, 5, default_value=5) - self.cs.add_hyperparameter(x4) - - def test_local_search(self): - def acquisition_function(points): - rval = [] - for point in points: - opt = np.array([1, 1, 1, 1]) - rval.append([-euclidean(point.get_array(), opt)]) - return np.array(rval) - - ls = LocalSearch(acquisition_function, self.cs, max_steps=100) - - start_point = self.cs.sample_configuration() - acq_val_start_point = acquisition_function([start_point]) - - acq_val_incumbent, _ = ls._do_search(start_point)[0] - - # Local search needs to find something that is as least as good as the - # start point - self.assertLessEqual(acq_val_start_point, acq_val_incumbent) - - @unittest.mock.patch.object(LocalSearch, "_get_initial_points") - def test_local_search_2( - self, - _get_initial_points_patch, - ): - pcs_file = os.path.join(self.test_files_dir, "test_local_search.pcs") - seed = np.random.randint(1, 100000) - - runhistory = unittest.mock.Mock() - runhistory.data = [None] * 1000 - - with open(pcs_file) as fh: - config_space = pcs.read(fh.readlines()) - config_space.seed(seed) - - def acquisition_function(points): - return np.array([[np.count_nonzero(point.get_array())] for point in points]) - - start_point = config_space.get_default_configuration() - _get_initial_points_patch.return_value = [start_point] - - ls = LocalSearch(acquisition_function, config_space, max_steps=100000) - # To have some data in a mock runhistory - ls.runhistory = [None] * 1000 - acq_val_incumbent, incumbent = ls._maximize(runhistory, None, 1)[0] - - np.testing.assert_allclose(incumbent.get_array(), np.ones(len(config_space.get_hyperparameters()))) - - @unittest.mock.patch.object(LocalSearch, "_do_search") - @unittest.mock.patch.object(LocalSearch, "_get_initial_points") - def test_get_next_by_local_search(self, _get_initial_points_patch, patch): - # Without known incumbent - class SideEffect(object): - def __call__(self, *args, **kwargs): - rval = [] - for i in range(len(args[0])): - rval.append((i, ConfigurationMock(i))) - return rval - - patch.side_effect = SideEffect() - cs = test_helpers.get_branin_config_space() - rand_confs = cs.sample_configuration(size=9) - _get_initial_points_patch.return_value = rand_confs - acq_func = EI(None) - - ls = LocalSearch(acq_func, cs) - - # To have some data in a mock runhistory - runhistory = unittest.mock.Mock() - runhistory.data = [None] * 1000 - - rval = ls._maximize(runhistory, None, 9) - self.assertEqual(len(rval), 9) - self.assertEqual(patch.call_count, 1) - for i in range(9): - self.assertIsInstance(rval[i][1], ConfigurationMock) - self.assertEqual(rval[i][1].value, 8 - i) - self.assertEqual(rval[i][0], 8 - i) - self.assertEqual(rval[i][1].origin, "Local Search") - - # Check that the known 'incumbent' is transparently passed through - patch.side_effect = SideEffect() - _get_initial_points_patch.return_value = ["Incumbent"] + rand_confs - rval = ls._maximize(runhistory, None, 10) - self.assertEqual(len(rval), 10) - self.assertEqual(patch.call_count, 2) - # Only the first local search in each iteration starts from the - # incumbent - self.assertEqual(patch.call_args_list[1][0][0][0], "Incumbent") - for i in range(10): - self.assertEqual(rval[i][1].origin, "Local Search") - - def test_local_search_finds_minimum(self): - class AcquisitionFunction: - - model = None - - def __call__(self, arrays): - rval = [] - for array in arrays: - rval.append([-rosenbrock_4d(array)]) - return np.array(rval) - - ls = LocalSearch( - acquisition_function=AcquisitionFunction(), - config_space=self.cs, - n_steps_plateau_walk=10, - max_steps=np.inf, - ) - - runhistory = RunHistory() - self.cs.seed(1) - random_configs = self.cs.sample_configuration(size=100) - costs = [rosenbrock_4d(random_config) for random_config in random_configs] - self.assertGreater(np.min(costs), 100) - for random_config, cost in zip(random_configs, costs): - runhistory.add(config=random_config, cost=cost, time=0, status=StatusType.SUCCESS) - minimizer = ls.maximize(runhistory, None, 10) - minima = [-rosenbrock_4d(m) for m in minimizer] - self.assertGreater(minima[0], -0.05) - - def test_get_initial_points_moo(self): - class Model: - def predict_marginalized_over_instances(self, X): - return X, X - - class AcquisitionFunction: - - model = Model() - - def __call__(self, X): - return np.array([x.get_array().sum() for x in X]).reshape((-1, 1)) - - ls = LocalSearch( - acquisition_function=AcquisitionFunction(), - config_space=self.cs, - n_steps_plateau_walk=10, - max_steps=np.inf, - ) - - runhistory = RunHistory() - random_configs = self.cs.sample_configuration(size=100) - costs = np.array([rosenbrock_4d(random_config) for random_config in random_configs]) - for random_config, cost in zip(random_configs, costs): - runhistory.add(config=random_config, cost=cost, time=0, status=StatusType.SUCCESS) - - points = ls._get_initial_points(num_points=5, runhistory=runhistory, additional_start_points=None) - self.assertEqual(len(points), 10) - - -class TestRandomSearch(unittest.TestCase): - @unittest.mock.patch("smac.optimizer.acquisition.convert_configurations_to_array") - @unittest.mock.patch.object(EI, "__call__") - @unittest.mock.patch.object(ConfigurationSpace, "sample_configuration") - def test_get_next_by_random_search_sorted(self, patch_sample, patch_ei, patch_impute): - values = (10, 1, 9, 2, 8, 3, 7, 4, 6, 5) - patch_sample.return_value = [ConfigurationMock(i) for i in values] - patch_ei.return_value = np.array([[_] for _ in values], dtype=float) - patch_impute.side_effect = lambda l: values - cs = ConfigurationSpace() - ei = EI(None) - rs = RandomSearch(ei, cs) - rval = rs._maximize(runhistory=None, stats=None, num_points=10, _sorted=True) - self.assertEqual(len(rval), 10) - for i in range(10): - self.assertIsInstance(rval[i][1], ConfigurationMock) - self.assertEqual(rval[i][1].value, 10 - i) - self.assertEqual(rval[i][0], 10 - i) - self.assertEqual(rval[i][1].origin, "Random Search (sorted)") - - # Check that config.get_array works as desired and imputation is used - # in between, we therefore have to retrieve the value from the mock! - np.testing.assert_allclose([v.value for v in patch_ei.call_args[0][0]], np.array(values, dtype=float)) - - @unittest.mock.patch.object(ConfigurationSpace, "sample_configuration") - def test_get_next_by_random_search(self, patch): - def side_effect(size): - return [ConfigurationMock()] * size - - patch.side_effect = side_effect - cs = ConfigurationSpace() - ei = EI(None) - rs = RandomSearch(ei, cs) - rval = rs._maximize(runhistory=None, stats=None, num_points=10, _sorted=False) - self.assertEqual(len(rval), 10) - for i in range(10): - self.assertIsInstance(rval[i][1], ConfigurationMock) - self.assertEqual(rval[i][1].origin, "Random Search") - self.assertEqual(rval[i][0], 0) - - -class TestLocalAndSortedPriorRandomSearch(unittest.TestCase): - def setUp(self): - seed = 1 - self.uniform_cs = ConfigurationSpace(seed=seed) - x1 = UniformFloatHyperparameter("x1", -5, 5, default_value=5) - x2 = UniformIntegerHyperparameter("x2", -5, 5, default_value=5) - x3 = CategoricalHyperparameter("x3", [5, 2, 0, 1, -1, -2, 4, -3, 3, -5, -4], default_value=5) - x4 = UniformIntegerHyperparameter("x4", -5, 5, default_value=5) - self.uniform_cs.add_hyperparameters([x1, x2, x3, x4]) - - self.prior_cs = ConfigurationSpace(seed=seed) - x1 = NormalFloatHyperparameter("x1", lower=-5, upper=5, mu=0, sigma=1e-3, default_value=5) - x2 = BetaIntegerHyperparameter("x2", lower=-5, upper=5, alpha=100, beta=1, default_value=5) - x3 = CategoricalHyperparameter( - "x3", [5, 2, 0, 1, -1, -2, 4, -3, 3, -5, -4], default_value=5, weights=[999, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - ) - x4 = UniformIntegerHyperparameter("x4", lower=-5, upper=5, default_value=5) - self.prior_cs.add_hyperparameters([x1, x2, x3, x4]) - - self.budget_kwargs = {"max_steps": 2, "n_steps_plateau_walk": 2, "n_sls_iterations": 2} - - def test_sampling_fractions(self): - class AcquisitionFunction: - def __call__(self, arrays): - rval = [] - for array in arrays: - rval.append([-rosenbrock_4d(array)]) - return np.array(rval) - - prs_0 = LocalAndSortedPriorRandomSearch( - AcquisitionFunction(), self.prior_cs, self.uniform_cs, prior_sampling_fraction=0, **self.budget_kwargs - ) - prs_05 = LocalAndSortedPriorRandomSearch( - AcquisitionFunction(), self.prior_cs, self.uniform_cs, prior_sampling_fraction=0.9, **self.budget_kwargs - ) - prs_1 = LocalAndSortedPriorRandomSearch( - AcquisitionFunction(), self.prior_cs, self.uniform_cs, prior_sampling_fraction=1, **self.budget_kwargs - ) - - res_0 = prs_0._maximize(runhistory=unittest.mock.Mock(), stats=None, num_points=10) - res_05 = prs_05._maximize(runhistory=unittest.mock.Mock(), stats=None, num_points=10) - res_1 = prs_1._maximize(runhistory=unittest.mock.Mock(), stats=None, num_points=10) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_smbo/test_epm_configuration_chooser.py b/tests/test_smbo/test_epm_configuration_chooser.py deleted file mode 100644 index 80fed8532..000000000 --- a/tests/test_smbo/test_epm_configuration_chooser.py +++ /dev/null @@ -1,277 +0,0 @@ -import shutil -import unittest -from unittest import mock - -import numpy as np -from ConfigSpace import Configuration - -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances -from smac.facade.smac_ac_facade import SMAC4AC -from smac.runhistory.runhistory import RunHistory -from smac.scenario.scenario import Scenario -from smac.tae import StatusType -from smac.utils import test_helpers - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class ConfigurationMock(object): - def __init__(self, value=None): - self.value = value - - def get_array(self): - return [self.value] - - -class TestEPMChooser(unittest.TestCase): - def setUp(self): - self.scenario = Scenario( - { - "cs": test_helpers.get_branin_config_space(), - "run_obj": "quality", - "output_dir": "data-test_epmchooser", - "deterministic": False, - "limit_resources": True, - } - ) - self.output_dirs = [] - self.output_dirs.append(self.scenario.output_dir) - - def tearDown(self): - for output_dir in self.output_dirs: - if output_dir: - shutil.rmtree(output_dir, ignore_errors=True) - - def branin(self, x): - y = (x[:, 1] - (5.1 / (4 * np.pi**2)) * x[:, 0] ** 2 + 5 * x[:, 0] / np.pi - 6) ** 2 - y += 10 * (1 - 1 / (8 * np.pi)) * np.cos(x[:, 0]) + 10 - - return y[:, np.newaxis] - - def test_choose_next(self): - seed = 42 - config = self.scenario.cs.sample_configuration() - rh = RunHistory() - rh.add(config, 10, 10, StatusType.SUCCESS) - - smbo = SMAC4AC(self.scenario, rng=seed, runhistory=rh).solver - - x = next(smbo.epm_chooser.choose_next()).get_array() - self.assertEqual(x.shape, (2,)) - - def test_choose_next_budget(self): - seed = 42 - config = self.scenario.cs.sample_configuration() - rh = RunHistory() - rh.add( - config=config, - cost=10, - time=10, - instance_id=None, - seed=1, - budget=1, - additional_info=None, - status=StatusType.SUCCESS, - ) - - smbo = SMAC4AC(self.scenario, rng=seed, runhistory=rh).solver - smbo.epm_chooser.min_samples_model = 2 - - # There is no model, so it returns a single random configuration - x = smbo.epm_chooser.choose_next() - self.assertEqual(len(x), 1) - self.assertEqual(next(x).origin, "Random Search") - - def test_choose_next_higher_budget(self): - seed = 42 - config = self.scenario.cs.sample_configuration - rh = RunHistory() - rh.add( - config=config(), - cost=1, - time=10, - instance_id=None, - seed=1, - budget=1, - additional_info=None, - status=StatusType.SUCCESS, - ) - rh.add( - config=config(), - cost=2, - time=10, - instance_id=None, - seed=1, - budget=2, - additional_info=None, - status=StatusType.SUCCESS, - ) - rh.add( - config=config(), - cost=3, - time=10, - instance_id=None, - seed=1, - budget=2, - additional_info=None, - status=StatusType.SUCCESS, - ) - rh.add( - config=config(), - cost=4, - time=10, - instance_id=None, - seed=1, - budget=3, - additional_info=None, - status=StatusType.SUCCESS, - ) - - smbo = SMAC4AC(self.scenario, rng=seed, runhistory=rh).solver - smbo.epm_chooser.min_samples_model = 2 - - # Return two configurations evaluated with budget==2 - X, Y, X_configurations = smbo.epm_chooser._collect_data_to_train_model() - self.assertListEqual(list(Y.flatten()), [2, 3]) - self.assertEqual(X.shape[0], 2) - self.assertEqual(X_configurations.shape[0], 2) - - def test_choose_next_w_empty_rh(self): - seed = 42 - smbo = SMAC4AC(self.scenario, rng=seed).solver - smbo.runhistory = RunHistory() - - # should return random search configuration - x = smbo.epm_chooser.choose_next(incumbent_value=0.0) - self.assertEqual(len(x), 1) - next_one = next(x) - self.assertEqual(next_one.get_array().shape, (2,)) - self.assertEqual(next_one.origin, "Random Search") - - def test_choose_next_empty_X(self): - epm_chooser = SMAC4AC(self.scenario, rng=1).solver.epm_chooser - epm_chooser.acquisition_func._compute = mock.Mock(spec=RandomForestWithInstances) - epm_chooser._random_search.maximize = mock.Mock(spec=epm_chooser._random_search.maximize) - epm_chooser._random_search.maximize.return_value = [0, 1, 2] - - x = epm_chooser.choose_next() - self.assertEqual(x, [0, 1, 2]) - self.assertEqual(epm_chooser._random_search.maximize.call_count, 1) - self.assertEqual(epm_chooser.acquisition_func._compute.call_count, 0) - - def test_choose_next_empty_X_2(self): - epm_chooser = SMAC4AC(self.scenario, rng=1).solver.epm_chooser - - challengers = epm_chooser.choose_next() - x = [c for c in challengers] - self.assertEqual(len(x), 1) - self.assertIsInstance(x[0], Configuration) - - def test_choose_next_2(self): - # Test with a single configuration in the runhistory! - def side_effect(X): - return np.mean(X, axis=1).reshape((-1, 1)) - - def side_effect_predict(X): - m, v = np.ones((X.shape[0], 1)), None - return m, v - - seed = 42 - incumbent = self.scenario.cs.get_default_configuration() - rh = RunHistory() - rh.add(incumbent, 10, 10, StatusType.SUCCESS) - epm_chooser = SMAC4AC(self.scenario, rng=seed, runhistory=rh).solver.epm_chooser - - epm_chooser.model = mock.Mock(spec=RandomForestWithInstances) - epm_chooser.model.predict_marginalized_over_instances.side_effect = side_effect_predict - epm_chooser.acquisition_func._compute = mock.Mock(spec=RandomForestWithInstances) - epm_chooser.acquisition_func._compute.side_effect = side_effect - epm_chooser.incumbent = incumbent - - challengers = epm_chooser.choose_next() - # Convert challenger list (a generator) to a real list - challengers = [c for c in challengers] - - self.assertEqual(epm_chooser.model.train.call_count, 1) - - # For each configuration it is randomly sampled whether to take it from the list of challengers or to sample it - # completely at random. Therefore, it is not guaranteed to obtain twice the number of configurations selected - # by EI. - self.assertEqual(len(challengers), 10198) - num_random_search_sorted = 0 - num_random_search = 0 - num_local_search = 0 - for c in challengers: - self.assertIsInstance(c, Configuration) - if "Random Search (sorted)" == c.origin: - num_random_search_sorted += 1 - elif "Random Search" == c.origin: - num_random_search += 1 - elif "Local Search" == c.origin: - num_local_search += 1 - else: - raise ValueError( - ( - c.origin, - "Local Search" == c.origin, - type("Local Search"), - type(c.origin), - ) - ) - - self.assertEqual(num_local_search, 11) - self.assertEqual(num_random_search_sorted, 5000) - self.assertEqual(num_random_search, 5187) - - def test_choose_next_3(self): - # Test with ten configurations in the runhistory - def side_effect(X): - return np.mean(X, axis=1).reshape((-1, 1)) - - def side_effect_predict(X): - m, v = np.ones((X.shape[0], 1)), None - return m, v - - epm_chooser = SMAC4AC(self.scenario, rng=1).solver.epm_chooser - epm_chooser.incumbent = self.scenario.cs.sample_configuration() - previous_configs = [epm_chooser.incumbent] + [self.scenario.cs.sample_configuration() for _ in range(0, 20)] - epm_chooser.runhistory = RunHistory() - for i, config in enumerate(previous_configs): - epm_chooser.runhistory.add(config, i, 10, StatusType.SUCCESS) - epm_chooser.model = mock.Mock(spec=RandomForestWithInstances) - epm_chooser.model.predict_marginalized_over_instances.side_effect = side_effect_predict - epm_chooser.acquisition_func._compute = mock.Mock(spec=RandomForestWithInstances) - epm_chooser.acquisition_func._compute.side_effect = side_effect - - challengers = epm_chooser.choose_next() - # Convert challenger list (a generator) to a real list - challengers = [c for c in challengers] - - self.assertEqual(epm_chooser.model.train.call_count, 1) - - # For each configuration it is randomly sampled whether to take it from the list of challengers or to sample it - # completely at random. Therefore, it is not guaranteed to obtain twice the number of configurations selected - # by EI - self.assertEqual(len(challengers), 9982) - num_random_search_sorted = 0 - num_random_search = 0 - num_local_search = 0 - for c in challengers: - self.assertIsInstance(c, Configuration) - if "Random Search (sorted)" == c.origin: - num_random_search_sorted += 1 - elif "Random Search" == c.origin: - num_random_search += 1 - elif "Local Search" == c.origin: - num_local_search += 1 - else: - raise ValueError(c.origin) - - self.assertEqual(num_local_search, 26) - self.assertEqual(num_random_search_sorted, 5000) - self.assertEqual(num_random_search, 4956) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_smbo/test_pSMAC.py b/tests/test_smbo/test_pSMAC.py deleted file mode 100644 index b9bf516df..000000000 --- a/tests/test_smbo/test_pSMAC.py +++ /dev/null @@ -1,164 +0,0 @@ -import json -import logging -import os -import shutil -import unittest - -from smac.optimizer import pSMAC -from smac.runhistory.runhistory import DataOrigin, RunHistory, RunKey -from smac.tae import StatusType -from smac.utils import test_helpers - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestPSMAC(unittest.TestCase): - def setUp(self): - self.tmp_dir = os.path.join(os.path.dirname(__file__), ".test_pSMAC") - self._remove_tmp_dir() - os.makedirs(self.tmp_dir) - - def tearDown(self): - self._remove_tmp_dir() - - def _remove_tmp_dir(self): - try: - shutil.rmtree(self.tmp_dir) - except Exception: - pass - - def test_write(self): - # The nulls make sure that we correctly emit the python None value - fixture = ( - '{"data": [[[1, "branin", 1, 0.0], [1, 1, {"__enum__": ' - '"StatusType.SUCCESS"}, 0.0, 0.0, null]], ' - '[[1, "branini", 1, 0.0], [1, 1, {"__enum__": ' - '"StatusType.SUCCESS"}, 0.0, 0.0, null]], ' - '[[2, "branini", 1, 0.0], [1, 1, {"__enum__": ' - '"StatusType.SUCCESS"}, 0.0, 0.0, null]], ' - '[[2, null, 1, 0.0], [1, 1, {"__enum__": ' - '"StatusType.SUCCESS"}, 0.0, 0.0, null]], ' - '[[3, "branin-hoo", 1, 0.0], [1, 1, {"__enum__": ' - '"StatusType.SUCCESS"}, 0.0, 0.0, null]], ' - '[[4, null, 1, 0.0], [1, 1, {"__enum__": ' - '"StatusType.SUCCESS"}, 0.0, 0.0, null]]],' - '"config_origins": {},' - '"configs": {' - '"4": {"x": -2.2060968293349363, "y": 5.183410905645716}, ' - '"3": {"x": -2.7986616377433045, "y": 1.385078921531967}, ' - '"1": {"x": 1.2553300705386103, "y": 10.804867401632372}, ' - '"2": {"x": -4.998284377739827, "y": 4.534988589477597}}}' - ) - - run_history = RunHistory() - configuration_space = test_helpers.get_branin_config_space() - configuration_space.seed(1) - - config = configuration_space.sample_configuration() - # Config on two instances - run_history.add(config, 1, 1, StatusType.SUCCESS, seed=1, instance_id="branin") - run_history.add(config, 1, 1, StatusType.SUCCESS, seed=1, instance_id="branini") - config_2 = configuration_space.sample_configuration() - # Another config on a known instance - run_history.add(config_2, 1, 1, StatusType.SUCCESS, seed=1, instance_id="branini") - # Known Config on no instance - run_history.add(config_2, 1, 1, StatusType.SUCCESS, seed=1) - # New config on new instance - config_3 = configuration_space.sample_configuration() - run_history.add(config_3, 1, 1, StatusType.SUCCESS, seed=1, instance_id="branin-hoo") - # New config on no instance - config_4 = configuration_space.sample_configuration() - run_history.add(config_4, 1, 1, StatusType.SUCCESS, seed=1) - - # External configuration which will not be written to json file! - config_5 = configuration_space.sample_configuration() - run_history.add(config_5, 1, 1, StatusType.SUCCESS, seed=1, origin=DataOrigin.EXTERNAL_SAME_INSTANCES) - - logger = logging.getLogger("Test") - pSMAC.write(run_history, self.tmp_dir, logger=logger) - r_size = len(run_history.data) - pSMAC.read( - run_history=run_history, - output_dirs=[self.tmp_dir], - configuration_space=configuration_space, - logger=logger, - ) - self.assertEqual( - r_size, - len(run_history.data), - "Runhistory should be the same and not changed after reading", - ) - - output_filename = os.path.join(self.tmp_dir, "runhistory.json") - self.assertTrue(os.path.exists(output_filename)) - - fixture = json.loads(fixture, object_hook=StatusType.enum_hook) - with open(output_filename) as fh: - output = json.load(fh, object_hook=StatusType.enum_hook) - self.assertEqual(output, fixture) - - def test_load(self): - configuration_space = test_helpers.get_branin_config_space() - - other_runhistory = ( - '{"data": [[[2, "branini", 1], [1, 1,' - '{"__enum__": "StatusType.SUCCESS"}, 0.0, 0.0, null]], ' - '[[1, "branin", 1], [1, 1,' - '{"__enum__": "StatusType.SUCCESS"}, 0.0, 0.0, null]], ' - '[[3, "branin-hoo", 1], [1, 1,' - '{"__enum__": "StatusType.SUCCESS"}, 0.0, 0.0, null]], ' - "[[2, null, 1], [1, 1," - '{"__enum__": "StatusType.SUCCESS"}, 0.0, 0.0, null]], ' - '[[1, "branini", 1], [1, 1,' - '{"__enum__": "StatusType.SUCCESS"}, 0.0, 0.0, null]], ' - "[[4, null, 1], [1, 1," - '{"__enum__": "StatusType.SUCCESS"}, 0.0, 0.0, null]]], ' - '"configs": {' - '"4": {"x": -2.2060968293349363, "y": 5.183410905645716}, ' - '"3": {"x": -2.7986616377433045, "y": 1.385078921531967}, ' - '"1": {"x": 1.2553300705386103, "y": 10.804867401632372}, ' - '"2": {"x": -4.998284377739827, "y": 4.534988589477597}}}' - ) - - other_runhistory_filename = os.path.join(self.tmp_dir, "runhistory.json") - with open(other_runhistory_filename, "w") as fh: - fh.write(other_runhistory) - - # load from an empty runhistory - runhistory = RunHistory() - runhistory.load_json(other_runhistory_filename, configuration_space) - self.assertEqual(sorted(list(runhistory.ids_config.keys())), [1, 2, 3, 4]) - self.assertEqual(len(runhistory.data), 6) - - # load from non-empty runhistory, in case of a duplicate the existing - # result will be kept and the new one silently discarded - runhistory = RunHistory() - configuration_space.seed(1) - config = configuration_space.sample_configuration() - runhistory.add(config, 1, 1, StatusType.SUCCESS, seed=1, instance_id="branin") - id_before = id(runhistory.data[RunKey(1, "branin", 1)]) - runhistory.update_from_json(other_runhistory_filename, configuration_space) - id_after = id(runhistory.data[RunKey(1, "branin", 1)]) - self.assertEqual(len(runhistory.data), 6) - self.assertEqual(id_before, id_after) - - # load from non-empty runhistory, in case of a duplicate the existing - # result will be kept and the new one silently discarded - runhistory = RunHistory() - configuration_space.seed(1) - config = configuration_space.sample_configuration() - config = configuration_space.sample_configuration() - # This is the former config_3 - config = configuration_space.sample_configuration() - runhistory.add(config, 1, 1, StatusType.SUCCESS, seed=1, instance_id="branin") - id_before = id(runhistory.data[RunKey(1, "branin", 1)]) - runhistory.update_from_json(other_runhistory_filename, configuration_space) - id_after = id(runhistory.data[RunKey(1, "branin", 1)]) - self.assertEqual(len(runhistory.data), 7) - self.assertEqual(id_before, id_after) - self.assertEqual(sorted(list(runhistory.ids_config.keys())), [1, 2, 3, 4]) - self.assertEqual( - [runhistory.external[run_key] for run_key in runhistory.data], - [DataOrigin.INTERNAL] + [DataOrigin.EXTERNAL_SAME_INSTANCES] * 6, - ) diff --git a/tests/test_smbo/test_random_configuration_chooser.py b/tests/test_smbo/test_random_configuration_chooser.py deleted file mode 100644 index 17d32e088..000000000 --- a/tests/test_smbo/test_random_configuration_chooser.py +++ /dev/null @@ -1,85 +0,0 @@ -import unittest - -import numpy as np - -from smac.optimizer.configuration_chooser.random_chooser import ( - ChooserLinearCoolDown, - ChooserNoCoolDown, - ChooserProb, -) - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestRandomConfigurationChooser(unittest.TestCase): - def test_no_cool_down(self): - c = ChooserNoCoolDown(rng=np.random.RandomState(), modulus=3.0) - self.assertFalse(c.check(1)) - self.assertFalse(c.check(2)) - self.assertTrue(c.check(3)) - self.assertFalse(c.check(4)) - self.assertFalse(c.check(5)) - self.assertTrue(c.check(6)) - self.assertTrue(c.check(30)) - c.next_smbo_iteration() - self.assertFalse(c.check(1)) - self.assertFalse(c.check(2)) - self.assertTrue(c.check(3)) - self.assertFalse(c.check(4)) - self.assertFalse(c.check(5)) - self.assertTrue(c.check(6)) - self.assertTrue(c.check(30)) - c = ChooserNoCoolDown(rng=np.random.RandomState(), modulus=1.0) - self.assertTrue(c.check(1)) - self.assertTrue(c.check(2)) - c.next_smbo_iteration() - self.assertTrue(c.check(1)) - self.assertTrue(c.check(2)) - - def test_linear_cool_down(self): - c = ChooserLinearCoolDown(None, 2.0, 1.0, 4.0) - self.assertFalse(c.check(1)) - self.assertTrue(c.check(2)) - self.assertFalse(c.check(3)) - self.assertTrue(c.check(4)) - self.assertFalse(c.check(5)) - self.assertTrue(c.check(6)) - self.assertFalse(c.check(7)) - self.assertTrue(c.check(8)) - c.next_smbo_iteration() - self.assertFalse(c.check(1)) - self.assertFalse(c.check(2)) - self.assertTrue(c.check(3)) - self.assertFalse(c.check(4)) - self.assertFalse(c.check(5)) - self.assertTrue(c.check(6)) - self.assertFalse(c.check(7)) - self.assertFalse(c.check(8)) - c.next_smbo_iteration() - self.assertFalse(c.check(1)) - self.assertFalse(c.check(2)) - self.assertFalse(c.check(3)) - self.assertTrue(c.check(4)) - self.assertFalse(c.check(5)) - self.assertFalse(c.check(6)) - self.assertFalse(c.check(7)) - self.assertTrue(c.check(8)) - c.next_smbo_iteration() - self.assertFalse(c.check(1)) - self.assertFalse(c.check(2)) - self.assertFalse(c.check(3)) - self.assertTrue(c.check(4)) - self.assertFalse(c.check(5)) - self.assertFalse(c.check(6)) - self.assertFalse(c.check(7)) - self.assertTrue(c.check(8)) - - def test_chooser_prob(self): - for i in range(10): - c = ChooserProb(rng=np.random.RandomState(1), prob=0.1 * i) - stats = [] - for j in range(100000): - stats.append(c.check(j)) - print(np.sum(stats) / 100000, 0.1 * i) - self.assertAlmostEqual(np.sum(stats) / 100000, 0.1 * i, places=2) diff --git a/tests/test_smbo/test_smbo.py b/tests/test_smbo/test_smbo.py deleted file mode 100644 index f7ca0d4d9..000000000 --- a/tests/test_smbo/test_smbo.py +++ /dev/null @@ -1,543 +0,0 @@ -import shutil -import time -import unittest -from unittest import mock - -import numpy as np -from ConfigSpace.hyperparameters import UniformFloatHyperparameter - -import smac.facade.smac_ac_facade -from smac.callbacks import IncorporateRunResultCallback -from smac.configspace import ConfigurationSpace -from smac.epm.random_forest.rf_with_instances import RandomForestWithInstances -from smac.facade.smac_ac_facade import SMAC4AC -from smac.facade.smac_hpo_facade import SMAC4HPO -from smac.intensification.abstract_racer import RunInfoIntent -from smac.optimizer.acquisition import EI, LogEI -from smac.runhistory.runhistory import RunInfo, RunValue -from smac.runhistory.runhistory2epm import RunHistory2EPM4Cost, RunHistory2EPM4LogCost -from smac.scenario.scenario import Scenario -from smac.tae import FirstRunCrashedException, StatusType -from smac.tae.execute_func import ExecuteTAFuncArray -from smac.utils import test_helpers -from smac.utils.io.traj_logging import TrajLogger -from smac.utils.validate import Validator - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def target(x, seed, instance): - """A target function for dummy testing of TA - perform x^2 for easy result calculations in checks. - """ - # Return x[i] (with brackets) so we pass the value, not the - # np array element - return x[0] ** 2, {"key": seed, "instance": instance} - - -class ConfigurationMock(object): - def __init__(self, value=None): - self.value = value - - def get_array(self): - return [self.value] - - -class TestSMBO(unittest.TestCase): - def setUp(self): - self.scenario = Scenario( - { - "cs": test_helpers.get_branin_config_space(), - "run_obj": "quality", - "output_dir": "data-test_smbo", - "runcount-limit": 5, - "deterministic": False, - "limit_resources": True, - } - ) - self.output_dirs = [] - self.output_dirs.append(self.scenario.output_dir) - - def tearDown(self): - for output_dir in self.output_dirs: - if output_dir: - shutil.rmtree(output_dir, ignore_errors=True) - - def branin(self, x): - y = (x[:, 1] - (5.1 / (4 * np.pi**2)) * x[:, 0] ** 2 + 5 * x[:, 0] / np.pi - 6) ** 2 - y += 10 * (1 - 1 / (8 * np.pi)) * np.cos(x[:, 0]) + 10 - - return y[:, np.newaxis] - - def test_init_only_scenario_runtime(self): - self.scenario.run_obj = "runtime" - self.scenario.cutoff = 300 - smbo = SMAC4AC(self.scenario).solver - self.assertIsInstance(smbo.epm_chooser.model, RandomForestWithInstances) - self.assertIsInstance(smbo.epm_chooser.rh2EPM, RunHistory2EPM4LogCost) - self.assertIsInstance(smbo.epm_chooser.acquisition_func, LogEI) - - def test_init_only_scenario_quality(self): - smbo = SMAC4AC(self.scenario).solver - self.assertIsInstance(smbo.epm_chooser.model, RandomForestWithInstances) - self.assertIsInstance(smbo.epm_chooser.rh2EPM, RunHistory2EPM4Cost) - self.assertIsInstance(smbo.epm_chooser.acquisition_func, EI) - - def test_rng(self): - smbo = SMAC4AC(self.scenario, rng=None).solver - self.assertIsInstance(smbo.rng, np.random.RandomState) - self.assertIsInstance(smbo.num_run, int) - smbo = SMAC4AC(self.scenario, rng=1).solver - rng = np.random.RandomState(1) - self.assertEqual(smbo.num_run, 1) - self.assertIsInstance(smbo.rng, np.random.RandomState) - smbo = SMAC4AC(self.scenario, rng=rng).solver - self.assertIsInstance(smbo.num_run, int) - self.assertIs(smbo.rng, rng) - # ML: I don't understand the following line and it throws an error - self.assertRaisesRegex( - TypeError, - "Argument rng accepts only arguments of type None, int or np.random.RandomState, you provided " - ".", - SMAC4AC, - self.scenario, - rng="BLA", - ) - - @mock.patch("smac.tae.execute_func.ExecuteTAFuncDict._call_ta") - def test_abort_on_initial_design(self, patch): - def target(x): - return 5 - - # should raise an error if abort_on_first_run_crash is True - patch.side_effect = FirstRunCrashedException() - scen = Scenario( - { - "cs": test_helpers.get_branin_config_space(), - "run_obj": "quality", - "output_dir": "data-test_smbo-abort", - "abort_on_first_run_crash": True, - "deterministic": False, - "limit_resources": True, - } - ) - self.output_dirs.append(scen.output_dir) - smbo = SMAC4AC(scen, tae_runner=target, rng=1).solver - with self.assertRaisesRegex(FirstRunCrashedException, "in _mock_call"): - smbo.run() - - # should not raise an error if abort_on_first_run_crash is False - patch.side_effect = FirstRunCrashedException() - scen = Scenario( - { - "cs": test_helpers.get_branin_config_space(), - "run_obj": "quality", - "output_dir": "data-test_smbo-abort", - "abort_on_first_run_crash": False, - "wallclock-limit": 1, - "deterministic": False, - "limit_resources": True, - } - ) - self.output_dirs.append(scen.output_dir) - smbo = SMAC4AC(scen, tae_runner=target, rng=1).solver - - try: - smbo.start() - smbo.run() - except FirstRunCrashedException: - self.fail("Raises FirstRunCrashedException unexpectedly!") - - @mock.patch("smac.tae.execute_func.AbstractTAFunc.run") - def test_abort_on_runner(self, patch): - def target(x): - return 5 - - # should raise an error if abort_on_first_run_crash is True - patch.side_effect = FirstRunCrashedException() - scen = Scenario( - { - "cs": test_helpers.get_branin_config_space(), - "run_obj": "quality", - "output_dir": "data-test_smbo-abort", - "abort_on_first_run_crash": True, - "deterministic": False, - "limit_resources": True, - } - ) - self.output_dirs.append(scen.output_dir) - smbo = SMAC4AC(scen, tae_runner=target, rng=1).solver - self.assertRaises(FirstRunCrashedException, smbo.run) - - # should not raise an error if abort_on_first_run_crash is False - patch.side_effect = FirstRunCrashedException() - scen = Scenario( - { - "cs": test_helpers.get_branin_config_space(), - "run_obj": "quality", - "output_dir": "data-test_smbo-abort", - "abort_on_first_run_crash": False, - "wallclock-limit": 1, - "deterministic": False, - "limit_resources": True, - } - ) - self.output_dirs.append(scen.output_dir) - smbo = SMAC4AC(scen, tae_runner=target, rng=1).solver - - try: - smbo.start() - smbo.run() - except FirstRunCrashedException: - self.fail("Raises FirstRunCrashedException unexpectedly!") - - @mock.patch("smac.tae.execute_func.AbstractTAFunc.run") - def test_stop_smbo(self, patch): - def target(x): - return 5 - - # should raise an error if abort_on_first_run_crash is True - patch.return_value = StatusType.STOP, 0.5, 0.5, {} - scen = Scenario( - { - "cs": test_helpers.get_branin_config_space(), - "run_obj": "quality", - "output_dir": "data-test_smbo-abort", - "abort_on_first_run_crash": True, - "deterministic": False, - "limit_resources": True, - } - ) - self.output_dirs.append(scen.output_dir) - smbo = SMAC4AC(scen, tae_runner=target, rng=1) - self.assertFalse(smbo.solver._stop) - smbo.optimize() - self.assertEqual(len(smbo.runhistory.data), 1) - # After an optimization, we expect no running instances. - self.assertEqual(list(smbo.runhistory.data.values())[0].status, StatusType.STOP) - self.assertTrue(smbo.solver._stop) - - def test_intensification_percentage(self): - def target(x): - return 5 - - def get_smbo(intensification_perc): - """Return SMBO with intensification_percentage.""" - scen = Scenario( - { - "cs": test_helpers.get_branin_config_space(), - "run_obj": "quality", - "output_dir": "data-test_smbo-intensification", - "intensification_percentage": intensification_perc, - "deterministic": False, - "limit_resources": True, - } - ) - self.output_dirs.append(scen.output_dir) - return SMAC4AC(scen, tae_runner=target, rng=1).solver - - # Test for valid values - smbo = get_smbo(0.3) - self.assertAlmostEqual(3.0, smbo._get_timebound_for_intensification(7.0, update=False)) - smbo = get_smbo(0.5) - self.assertAlmostEqual(0.03, smbo._get_timebound_for_intensification(0.03, update=False)) - smbo = get_smbo(0.7) - self.assertAlmostEqual(1.4, smbo._get_timebound_for_intensification(0.6, update=False)) - # Test for invalid <= 0 - smbo = get_smbo(0) - self.assertRaises(ValueError, smbo.run) - smbo = get_smbo(-0.2) - self.assertRaises(ValueError, smbo.run) - # Test for invalid >= 1 - smbo = get_smbo(1) - self.assertRaises(ValueError, smbo.run) - smbo = get_smbo(1.2) - self.assertRaises(ValueError, smbo.run) - - def test_update_intensification_percentage(self): - """ - This test checks the intensification time bound is updated in subsequent iterations as long as - num_runs of the intensifier is not reset to zero. - """ - - def target(x): - return 5 - - scen = Scenario( - { - "cs": test_helpers.get_branin_config_space(), - "run_obj": "quality", - "output_dir": "data-test_smbo-intensification", - "save_instantly": False, - "deterministic": False, - "limit_resources": True, - }, - ) - self.output_dirs.append(scen.output_dir) - solver = SMAC4AC(scen, tae_runner=target, rng=1).solver - - solver.stats.is_budget_exhausted = unittest.mock.Mock() - solver.stats.is_budget_exhausted.side_effect = tuple(([False] * 10) + [True] * 8) - - solver._get_timebound_for_intensification = unittest.mock.Mock(wraps=solver._get_timebound_for_intensification) - - class SideEffect: - def __init__(self, intensifier, get_next_run): - self.intensifier = intensifier - self.get_next_run = get_next_run - self.counter = 0 - - def __call__(self, *args, **kwargs): - self.counter += 1 - if self.counter % 4 == 0: - self.intensifier.num_run = 0 - return self.get_next_run(*args, **kwargs) - - solver.intensifier.get_next_run = unittest.mock.Mock( - side_effect=SideEffect(solver.intensifier, solver.intensifier.get_next_run) - ) - - solver.run() - - get_timebound_mock = solver._get_timebound_for_intensification - self.assertEqual(get_timebound_mock.call_count, 6) - self.assertFalse(get_timebound_mock.call_args_list[0][1]["update"]) - self.assertFalse(get_timebound_mock.call_args_list[1][1]["update"]) - self.assertTrue(get_timebound_mock.call_args_list[2][1]["update"]) - self.assertFalse(get_timebound_mock.call_args_list[3][1]["update"]) - self.assertTrue(get_timebound_mock.call_args_list[4][1]["update"]) - self.assertTrue(get_timebound_mock.call_args_list[5][1]["update"]) - - self.assertGreater( - get_timebound_mock.call_args_list[2][0][0], - get_timebound_mock.call_args_list[1][0][0], - ) - self.assertLess( - get_timebound_mock.call_args_list[3][0][0], - get_timebound_mock.call_args_list[2][0][0], - ) - self.assertGreater( - get_timebound_mock.call_args_list[4][0][0], - get_timebound_mock.call_args_list[3][0][0], - ) - self.assertGreater( - get_timebound_mock.call_args_list[5][0][0], - get_timebound_mock.call_args_list[4][0][0], - ) - - def test_validation(self): - with mock.patch.object(TrajLogger, "read_traj_aclib_format", return_value=None): - self.scenario.output_dir = "test" - smac = SMAC4AC(self.scenario) - self.output_dirs.append(smac.output_dir) - smbo = smac.solver - with mock.patch.object(Validator, "validate", return_value=None) as validation_mock: - smbo.validate( - config_mode="inc", - instance_mode="train+test", - repetitions=1, - use_epm=False, - n_jobs=-1, - backend="threading", - ) - self.assertTrue(validation_mock.called) - with mock.patch.object(Validator, "validate_epm", return_value=None) as epm_validation_mock: - smbo.validate( - config_mode="inc", - instance_mode="train+test", - repetitions=1, - use_epm=True, - n_jobs=-1, - backend="threading", - ) - self.assertTrue(epm_validation_mock.called) - - def test_no_initial_design(self): - self.scenario.output_dir = "test" - smac = SMAC4AC(self.scenario) - self.output_dirs.append(smac.output_dir) - smbo = smac.solver - # SMBO should have the default configuration as the 1st config if no initial design is given - smbo.start() - self.assertEqual(smbo.initial_design_configs[0], smbo.scenario.cs.get_default_configuration()) - - def test_ta_integration_to_smbo(self): - """ - In SMBO. 3 objects need to actively comunicate: - -> stats - -> epm - -> runhistory - - This method makes sure that executed jobs are properly registered - in the above objects - - It uses n_workers to test parallel and serial implementations!! - """ - - for n_workers in range(1, 2): - # We create a controlled setting, in which we optimize x^2 - # This will allow us to make sure every component act as expected - - # FIRST: config space - cs = ConfigurationSpace() - cs.add_hyperparameter(UniformFloatHyperparameter("x", -10.0, 10.0)) - smac = SMAC4HPO( - scenario=Scenario( - { - "n_workers": n_workers, - "cs": cs, - "runcount_limit": 5, - "run_obj": "quality", - "deterministic": True, - "limit_resources": True, - "initial_incumbent": "DEFAULT", - "output_dir": "data-test_smbo", - } - ), - tae_runner=ExecuteTAFuncArray, - tae_runner_kwargs={"ta": target}, - ) - - # Register output dir for deletion - self.output_dirs.append(smac.output_dir) - - smbo = smac.solver - - # SECOND: Intensifier that tracks configs - all_configs = [] - - def mock_get_next_run(**kwargs): - config = cs.sample_configuration() - all_configs.append(config) - return ( - RunInfoIntent.RUN, - RunInfo( - config=config, - instance=time.time() % 10, - instance_specific={}, - seed=0, - cutoff=None, - capped=False, - budget=0.0, - ), - ) - - intensifier = unittest.mock.Mock() - intensifier.num_run = 0 - intensifier.process_results.return_value = (0.0, 0.0) - intensifier.get_next_run = mock_get_next_run - smac.solver.intensifier = intensifier - - # THIRD: Run in this controlled setting - smbo.run() - - # FOURTH: Checks - - # Make sure all configs where launched - self.assertEqual(len(all_configs), 5) - - # Run history - for k, v in smbo.runhistory.data.items(): - - # All configuration should be successful - self.assertEqual(v.status, StatusType.SUCCESS) - - # The value should be the square version of the config - # The runhistory has config_ids = {config: int} - # The k here is {config_id: int}. We search for the actual config - # by inverse searching this runhistory.config dict - config = list(smbo.runhistory.config_ids.keys())[ - list(smbo.runhistory.config_ids.values()).index(k.config_id) - ] - - self.assertEqual(v.cost, config.get("x") ** 2) - - # No config is lost in the config history - self.assertCountEqual(smbo.runhistory.config_ids.keys(), all_configs) - - # Stats! - # We do not exceed the number of target algorithm runs - self.assertEqual(smbo.stats.submitted_ta_runs, len(all_configs)) - self.assertEqual(smbo.stats.finished_ta_runs, len(all_configs)) - - # No config is lost - self.assertEqual(smbo.stats.n_configs, len(all_configs)) - - # The EPM can access all points. This is something that - # also relies on the runhistory - X, Y, X_config = smbo.epm_chooser._collect_data_to_train_model() - self.assertEqual(X.shape[0], len(all_configs)) - - @unittest.mock.patch.object(smac.facade.smac_ac_facade.Intensifier, "process_results") - def test_incorporate_run_results_callback(self, process_results_mock): - - process_results_mock.return_value = None, None - - class TestCallback(IncorporateRunResultCallback): - def __init__(self): - self.num_call = 0 - - def __call__(self, smbo, run_info, result, time_left) -> None: - self.num_call += 1 - self.config = run_info.config - - callback = TestCallback() - - self.scenario.output_dir = None - smac = SMAC4AC(self.scenario) - smac.register_callback(callback) - - self.output_dirs.append(smac.output_dir) - smbo = smac.solver - - config = self.scenario.cs.sample_configuration() - - run_info = RunInfo( - config=config, - instance=None, - instance_specific=None, - seed=1, - cutoff=None, - capped=False, - budget=0.0, - source_id=0, - ) - result = RunValue(1.2345, 2.3456, "status", "starttime", "endtime", "additional_info") - time_left = 10 - - smbo._incorporate_run_results(run_info=run_info, result=result, time_left=time_left) - self.assertEqual(callback.num_call, 1) - self.assertEqual(callback.config, config) - - @unittest.mock.patch.object(smac.facade.smac_ac_facade.Intensifier, "process_results") - def test_incorporate_run_results_callback_stop_loop(self, process_results_mock): - def target(x): - return 5 - - process_results_mock.return_value = None, None - - class TestCallback(IncorporateRunResultCallback): - def __init__(self): - self.num_call = 0 - - def __call__(self, smbo, run_info, result, time_left) -> None: - self.num_call += 1 - if self.num_call > 2: - return False - - callback = TestCallback() - - self.scenario.output_dir = None - smac = SMAC4AC(self.scenario, tae_runner=target, rng=1) - smac.register_callback(callback) - - self.output_dirs.append(smac.output_dir) - - smac.optimize() - - self.assertEqual(callback.num_call, 3) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_tae/__init__.py b/tests/test_tae/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/test_tae/dummy_ta_wrapper.py b/tests/test_tae/dummy_ta_wrapper.py deleted file mode 100644 index 3a9cdb0b8..000000000 --- a/tests/test_tae/dummy_ta_wrapper.py +++ /dev/null @@ -1,13 +0,0 @@ -import sys - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -if __name__ == "__main__": - - if sys.argv[1] == "1": - print("Result of this algorithm run: UNSAT,1,1,1,12354") - - if sys.argv[1] == "2": - print("Result of this algorithm run: UNSAT,2,3,4,12354,additional info") diff --git a/tests/test_tae/dummy_ta_wrapper_aclib.py b/tests/test_tae/dummy_ta_wrapper_aclib.py deleted file mode 100644 index 96478652a..000000000 --- a/tests/test_tae/dummy_ta_wrapper_aclib.py +++ /dev/null @@ -1,16 +0,0 @@ -import sys - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -if __name__ == "__main__": - - if sys.argv[1] == "1": - print("""Result of this algorithm run: {"status": "TIMEOUT", "cost": 1.0, "runtime": 2.0}""") - - if sys.argv[1] == "2": - print( - """Result of this algorithm run: {"status": \ - "SUCCESS", "cost": 2.0, "runtime": 3.0, "additional_info": "hello world!"}""" - ) diff --git a/tests/test_tae/test_exec_func.py b/tests/test_tae/test_exec_func.py deleted file mode 100644 index c07265577..000000000 --- a/tests/test_tae/test_exec_func.py +++ /dev/null @@ -1,203 +0,0 @@ -import os -import sys -import time -import unittest -import unittest.mock - -import numpy as np - -from smac.configspace import Configuration, ConfigurationSpace -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.tae.execute_func import ExecuteTAFuncArray, ExecuteTAFuncDict - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestExecuteFunc(unittest.TestCase): - def setUp(self): - self.cs = ConfigurationSpace() - self.scenario = Scenario({"cs": self.cs, "run_obj": "quality", "output_dir": ""}) - self.stats = Stats(scenario=self.scenario) - - def test_run(self): - def target(x): - return x**2 - - taf = ExecuteTAFuncDict(ta=target, stats=self.stats) - rval = taf.run(config=2) - - self.assertFalse(taf._accepts_instance) - self.assertFalse(taf._accepts_seed) - self.assertEqual(rval[0], StatusType.SUCCESS) - self.assertEqual(rval[1], 4) - self.assertGreaterEqual(rval[2], 0.0) - self.assertEqual(rval[3], dict()) - - def target(x, seed): - return x**2, {"key": seed} - - taf = ExecuteTAFuncDict(ta=target, stats=self.stats) - rval = taf.run(config=2, instance="test") - self.assertFalse(taf._accepts_instance) - self.assertTrue(taf._accepts_seed) - self.assertEqual(rval[0], StatusType.SUCCESS) - self.assertEqual(rval[1], 4) - self.assertGreaterEqual(rval[2], 0.0) - self.assertEqual(rval[3], {"key": 12345}) - - def target(x, seed, instance): - return x**2, {"key": seed, "instance": instance} - - taf = ExecuteTAFuncDict(ta=target, stats=self.stats) - rval = taf.run(config=2, instance="test") - self.assertTrue(taf._accepts_instance) - self.assertTrue(taf._accepts_seed) - self.assertEqual(rval[0], StatusType.SUCCESS) - self.assertEqual(rval[1], 4) - self.assertGreaterEqual(rval[2], 0.0) - self.assertEqual(rval[3], {"key": 12345, "instance": "test"}) - - def target(x): - raise Exception(x) - - taf = ExecuteTAFuncDict(ta=target, stats=self.stats) - rval = taf.run(config=2) - self.assertFalse(taf._accepts_instance) - self.assertFalse(taf._accepts_seed) - self.assertEqual(rval[0], StatusType.CRASHED) - self.assertEqual(rval[1], 2147483647.0) - self.assertGreaterEqual(rval[2], 0.0) - self.assertEqual(rval[3], dict()) - - def test_run_wo_pynisher(self): - def target(x): - return x**2 - - taf = ExecuteTAFuncDict(ta=target, stats=self.stats, use_pynisher=False) - rval = taf.run(config=2) - self.assertFalse(taf._accepts_instance) - self.assertFalse(taf._accepts_seed) - self.assertEqual(rval[0], StatusType.SUCCESS) - self.assertEqual(rval[1], 4) - self.assertGreaterEqual(rval[2], 0.0) - self.assertEqual(rval[3], dict()) - - def target(x, seed, instance): - return x**2, {"key": seed, "instance": instance} - - taf = ExecuteTAFuncDict(ta=target, stats=self.stats, use_pynisher=False) - rval = taf.run(config=2, instance="test") - self.assertTrue(taf._accepts_instance) - self.assertTrue(taf._accepts_seed) - self.assertEqual(rval[0], StatusType.SUCCESS) - self.assertEqual(rval[1], 4) - self.assertGreaterEqual(rval[2], 0.0) - self.assertEqual(rval[3], {"key": 12345, "instance": "test"}) - - def target(x): - return None - - taf = ExecuteTAFuncDict(ta=target, stats=self.stats, use_pynisher=False) - rval = taf.run(config=2) - - self.assertFalse(taf._accepts_instance) - self.assertFalse(taf._accepts_seed) - self.assertEqual(rval[0], StatusType.CRASHED) - self.assertEqual(rval[1], 2147483647.0) - self.assertGreaterEqual(rval[2], 0.0) - self.assertEqual(rval[3], dict()) - - def target(x): - raise Exception(x) - - taf = ExecuteTAFuncDict(ta=target, stats=self.stats, use_pynisher=False) - rval = taf.run(config=2) - self.assertFalse(taf._accepts_instance) - self.assertFalse(taf._accepts_seed) - self.assertEqual(rval[0], StatusType.CRASHED) - self.assertEqual(rval[1], 2147483647.0) - self.assertGreaterEqual(rval[2], 0.0) - self.assertEqual(rval[3], dict()) - - @unittest.mock.patch.object(Configuration, "get_dictionary") - def test_run_execute_func_for_fmin(self, mock): - def target(x): - return x[0] ** 2 + x[1] - - mock.return_value = {"x1": 2, "x2": 1} - c = Configuration(configuration_space=self.cs, values={}) - taf = ExecuteTAFuncArray(target, stats=self.stats) - rval = taf._call_ta(target, c, {}) - self.assertEqual(rval, 5) - - def test_memout(self): - def fill_memory(*args): - a = np.random.random_sample((10000, 10000)).astype(np.float64) - return np.sum(a) - - taf = ExecuteTAFuncDict(ta=fill_memory, stats=self.stats, memory_limit=1024) - rval = taf.run(config=None) - - platform = os.getenv("TRAVIS_OS_NAME") - if platform is None: - platform = {"linux": "linux", "darwin": "osx"}.get(sys.platform) - - print(platform, sys.platform) - if platform == "linux": - self.assertEqual(rval[0], StatusType.MEMOUT) - self.assertEqual(rval[1], 2147483647.0) - self.assertGreaterEqual(rval[2], 0.0) - self.assertEqual(rval[3], dict()) - elif platform == "osx": - self.assertEqual(rval[0], StatusType.SUCCESS) - else: - raise ValueError("Test cannot be performed on platform %s" % sys.platform) - - def test_timeout(self): - def run_over_time(*args): - time.sleep(5) - - taf = ExecuteTAFuncDict(ta=run_over_time, stats=self.stats) - rval = taf.run(config=None, cutoff=1) - self.assertEqual(rval[0], StatusType.TIMEOUT) - self.assertEqual(rval[1], 2147483647.0) - self.assertGreaterEqual(rval[2], 0.0) - self.assertEqual(rval[3], dict()) - - def test_timeout_runtime(self): - def run_over_time(*args): - time.sleep(5) - - taf = ExecuteTAFuncDict(ta=run_over_time, stats=self.stats, run_obj="runtime", par_factor=11) - rval = taf.run(config=None, cutoff=1) - self.assertEqual(rval[0], StatusType.TIMEOUT) - self.assertGreaterEqual(rval[1], 11) - self.assertGreaterEqual(rval[2], 1) - self.assertEqual(rval[3], dict()) - - def test_fail_silent(self): - def function(*args): - return - - taf = ExecuteTAFuncDict(ta=function, stats=self.stats) - rval = taf.run(config=None, cutoff=1) - self.assertEqual(rval[0], StatusType.CRASHED) - self.assertEqual(rval[1], 2147483647.0) - self.assertGreaterEqual(rval[2], 0.0) - self.assertEqual(rval[3], dict()) - - def test_cutoff_too_large(self): - def target(x): - return x**2 - - taf = ExecuteTAFuncDict(ta=target, stats=self.stats) - self.assertRaises(ValueError, taf.run, config=2, cutoff=65536) - - -if __name__ == "__main__": - t = TestExecuteFunc() - t.setUp() - t.test_run() diff --git a/tests/test_tae/test_exec_tae_run.py b/tests/test_tae/test_exec_tae_run.py deleted file mode 100644 index eaab8e64a..000000000 --- a/tests/test_tae/test_exec_tae_run.py +++ /dev/null @@ -1,251 +0,0 @@ -import os -import sys -import unittest - -import numpy as np - -from smac.configspace import ConfigurationSpace -from smac.runhistory.runhistory import RunInfo -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.tae.serial_runner import SerialRunner - -if sys.version_info[0] == 2: - import mock -else: - from unittest import mock - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TaeTest(unittest.TestCase): - def setUp(self): - self.current_dir = os.getcwd() - base_dir = os.path.split(__file__)[0] - base_dir = os.path.join(base_dir, "../../tests", "..") - os.chdir(base_dir) - - def tearDown(self): - os.chdir(self.current_dir) - - @mock.patch.object(SerialRunner, "run") - def test_start_tae_return_abort(self, test_run): - """ - testing abort - """ - # Patch run-function for custom-return - test_run.return_value = StatusType.ABORT, 12345.0, 1.2345, {} - - scen = Scenario( - scenario={ - "cs": ConfigurationSpace(), - "run_obj": "quality", - "output_dir": "", - }, - cmd_options=None, - ) - stats = Stats(scen) - stats.start_timing() - eta = SerialRunner(ta=lambda *args: None, stats=stats) - - _, run_value = eta.run_wrapper( - RunInfo( - config=None, - instance=1, - instance_specific=None, - cutoff=30, - seed=None, - capped=False, - budget=0.0, - ) - ) - self.assertEqual(run_value.status, StatusType.ABORT) - - @mock.patch.object(SerialRunner, "run") - def test_start_tae_return_nan_inf(self, test_run): - """ - test nan-handling and inf-handling - """ - - def get_tae(obj): - """Create SerialRunner-object for testing.""" - scen = Scenario( - scenario={"cs": ConfigurationSpace(), "run_obj": obj, "cutoff_time": "10"}, - cmd_options=None, - ) - stats = Stats(scen) - stats.start_timing() - # Add first run to not trigger FirstRunCrashedException - stats.submitted_ta_runs += 1 - eta = SerialRunner(ta=lambda *args: None, stats=stats, run_obj=obj) - return eta - - # TEST NAN - eta = get_tae("runtime") - # Patch run-function for custom-return (obj = runtime, cost = nan) - test_run.return_value = StatusType.SUCCESS, np.nan, 1, {} - run_info, result = eta.run_wrapper( - RunInfo( - config={}, - instance=1, - instance_specific="0", - cutoff=10, - seed=None, - capped=False, - budget=0.0, - ) - ) - self.assertEqual(result.status, StatusType.SUCCESS) - # (obj = runtime, runtime = nan) - test_run.return_value = StatusType.SUCCESS, 1, np.nan, {} - run_info, result = eta.run_wrapper( - RunInfo( - config={}, - instance=1, - instance_specific="0", - cutoff=10, - seed=None, - capped=False, - budget=0.0, - ) - ) - self.assertEqual(result.status, StatusType.CRASHED) - - eta = get_tae("quality") - # Patch run-function for custom-return (obj = quality, cost = nan) - test_run.return_value = StatusType.SUCCESS, np.nan, 1, {} - run_info, result = eta.run_wrapper( - RunInfo( - config={}, - instance=1, - instance_specific="0", - cutoff=10, - seed=None, - capped=False, - budget=0.0, - ) - ) - self.assertEqual(result.status, StatusType.CRASHED) - # (obj = quality, runtime = nan) - test_run.return_value = StatusType.SUCCESS, 1, np.nan, {} - run_info, result = eta.run_wrapper( - RunInfo( - config={}, - instance=1, - instance_specific="0", - cutoff=10, - seed=None, - capped=False, - budget=0.0, - ) - ) - self.assertEqual(result.status, StatusType.SUCCESS) - - # TEST INF - eta = get_tae("runtime") - # Patch run-function for custom-return (obj = runtime, cost = inf) - test_run.return_value = StatusType.SUCCESS, np.inf, 1, {} - run_info, result = eta.run_wrapper( - RunInfo( - config={}, - instance=1, - instance_specific="0", - cutoff=10, - seed=None, - capped=False, - budget=0.0, - ) - ) - self.assertEqual(result.status, StatusType.SUCCESS) - # (obj = runtime, runtime = inf) - test_run.return_value = StatusType.SUCCESS, 1, np.inf, {} - run_info, result = eta.run_wrapper( - RunInfo( - config={}, - instance=1, - instance_specific="0", - cutoff=10, - seed=None, - capped=False, - budget=0.0, - ) - ) - self.assertEqual(result.status, StatusType.TIMEOUT) - - eta = get_tae("quality") - # Patch run-function for custom-return (obj = quality, cost = inf) - test_run.return_value = StatusType.SUCCESS, np.inf, 1, {} - run_info, result = eta.run_wrapper( - RunInfo( - config={}, - instance=1, - instance_specific="0", - cutoff=10, - seed=None, - capped=False, - budget=0.0, - ) - ) - self.assertEqual(result.status, StatusType.CRASHED) - # (obj = quality, runtime = inf) - test_run.return_value = StatusType.SUCCESS, 1, np.inf, {} - run_info, result = eta.run_wrapper( - RunInfo( - config={}, - instance=1, - instance_specific="0", - cutoff=10, - seed=None, - capped=False, - budget=0.0, - ) - ) - self.assertEqual(result.status, StatusType.SUCCESS) - - @mock.patch.object(SerialRunner, "run") - def test_crashed_cost_value(self, test_run): - """ - test cost on crashed runs - """ - # Patch run-function for custom-return - scen = Scenario(scenario={"cs": ConfigurationSpace(), "run_obj": "quality"}, cmd_options=None) - stats = Stats(scen) - stats.start_timing() - stats.submitted_ta_runs += 1 - - # Check quality - test_run.return_value = StatusType.CRASHED, np.nan, np.nan, {} - eta = SerialRunner(ta=lambda *args: None, stats=stats, run_obj="quality", cost_for_crash=100) - run_info, result = eta.run_wrapper( - RunInfo( - config={}, - instance=1, - instance_specific="0", - cutoff=None, - seed=None, - capped=False, - budget=0.0, - ) - ) - self.assertEqual(100, result.cost) - - # Check runtime - eta = SerialRunner(ta=lambda *args: None, stats=stats, run_obj="runtime", cost_for_crash=10.7) - run_info, result = eta.run_wrapper( - RunInfo( - config={}, - instance=1, - instance_specific="0", - cutoff=20, - seed=None, - capped=False, - budget=0.0, - ) - ) - self.assertEqual(20.0, result.cost) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_tae/test_hydra_tae.py b/tests/test_tae/test_hydra_tae.py deleted file mode 100644 index da3de7b37..000000000 --- a/tests/test_tae/test_hydra_tae.py +++ /dev/null @@ -1,80 +0,0 @@ -import os -import shlex -import unittest - -from smac.configspace import ConfigurationSpace -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.tae.execute_ta_run_aclib import ExecuteTARunAClib -from smac.tae.execute_ta_run_hydra import ExecuteTARunHydra - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TaeHydra(unittest.TestCase): - def setUp(self): - self.current_dir = os.getcwd() - base_dir = os.path.split(__file__)[0] - base_dir = os.path.join(base_dir, "../../tests", "..") - self.oracle = {} - os.chdir(base_dir) - - def tearDown(self): - os.chdir(self.current_dir) - - def test_run(self): - """ - running some simple algo in aclib 2.0 style - """ - scen = Scenario( - scenario={"cs": ConfigurationSpace(), "run_obj": "quality", "output_dir": ""}, - cmd_options=None, - ) - stats = Stats(scen) - self.oracle["inst"] = 0.0 - - eta = ExecuteTARunHydra( - cost_oracle=self.oracle, - tae=ExecuteTARunAClib, - ta=shlex.split("python tests/test_tae/dummy_ta_wrapper_aclib.py 1"), - stats=stats, - ) - status, cost, runtime, ar_info = eta.run(config={}, instance="inst", cutoff=10) - assert status == StatusType.SUCCESS - assert cost == 0 - assert runtime == 0 - - print(status, cost, runtime) - - eta = ExecuteTARunHydra( - cost_oracle=self.oracle, - tae=ExecuteTARunAClib, - ta=shlex.split("python tests/test_tae/dummy_ta_wrapper_aclib.py 2"), - stats=stats, - ) - status, cost, runtime, ar_info = eta.run(config={}, instance="inst", cutoff=10) - assert status == StatusType.SUCCESS - assert cost == 0, cost - assert runtime == 0 - - print(status, cost, runtime) - - eta = ExecuteTARunHydra( - cost_oracle=self.oracle, - tae=ExecuteTARunAClib, - ta=shlex.split("python tests/test_tae/dummy_ta_wrapper_aclib.py 2"), - stats=stats, - run_obj="quality", - ) - status, cost, runtime, ar_info = eta.run(config={}, instance="inst", cutoff=10) - assert status == StatusType.SUCCESS - assert cost == 0 - assert runtime == 3.0 - - print(status, cost, runtime, ar_info) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_tae/test_parallel_runner.py b/tests/test_tae/test_parallel_runner.py deleted file mode 100644 index dd6410b51..000000000 --- a/tests/test_tae/test_parallel_runner.py +++ /dev/null @@ -1,218 +0,0 @@ -# Add below as a WA for -# https://github.com/dask/distributed/issues/4168 -import multiprocessing.popen_spawn_posix # noqa -import os -import tempfile -import time -import unittest -import unittest.mock - -import dask # noqa -from dask.distributed import Client - -from smac.configspace import ConfigurationSpace -from smac.runhistory.runhistory import RunInfo, RunValue -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.tae.dask_runner import DaskParallelRunner -from smac.tae.execute_func import ExecuteTAFuncDict - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def target(x, seed, instance): - return x**2, {"key": seed, "instance": instance} - - -def target_delayed(x, seed, instance): - time.sleep(1) - return x**2, {"key": seed, "instance": instance} - - -class TestDaskRunner(unittest.TestCase): - def setUp(self): - self.cs = ConfigurationSpace() - self.scenario = Scenario({"cs": self.cs, "run_obj": "quality", "output_dir": ""}) - self.stats = Stats(scenario=self.scenario) - - def test_run(self): - """Makes sure that we are able to run a configuration and - return the expected values/types""" - - # We use the funcdict as a mechanism to test Parallel Runner - runner = ExecuteTAFuncDict(ta=target, stats=self.stats, run_obj="quality") - runner = DaskParallelRunner(runner, n_workers=2) - self.assertIsInstance(runner, DaskParallelRunner) - - run_info = RunInfo( - config=2, - instance="test", - instance_specific="0", - seed=0, - cutoff=None, - capped=False, - budget=0.0, - ) - - # submit runs! then get the value - runner.submit_run(run_info) - run_values = runner.get_finished_runs() - # Run will not have finished so fast - self.assertEqual(len(run_values), 0) - runner.wait() - run_values = runner.get_finished_runs() - self.assertEqual(len(run_values), 1) - self.assertIsInstance(run_values, list) - self.assertIsInstance(run_values[0][0], RunInfo) - self.assertIsInstance(run_values[0][1], RunValue) - self.assertEqual(run_values[0][1].cost, 4) - self.assertEqual(run_values[0][1].status, StatusType.SUCCESS) - - def test_parallel_runs(self): - """Make sure because there are 2 workers, the runs are launched - closely in time together""" - - # We use the funcdict as a mechanism to test Runner - runner = ExecuteTAFuncDict(ta=target_delayed, stats=self.stats, run_obj="quality") - runner = DaskParallelRunner(runner, n_workers=2) - - run_info = RunInfo( - config=2, - instance="test", - instance_specific="0", - seed=0, - cutoff=None, - capped=False, - budget=0.0, - ) - runner.submit_run(run_info) - run_info = RunInfo( - config=3, - instance="test", - instance_specific="0", - seed=0, - cutoff=None, - capped=False, - budget=0.0, - ) - runner.submit_run(run_info) - - # At this stage, we submitted 2 jobs, that are running in remote - # workers. We have to wait for each one of them to complete. The - # runner provides a wait() method to do so, yet it can only wait for - # a single job to be completed. It does internally via dask wait() - # so we take wait for the first job to complete, and take it out - runner.wait() - run_values = runner.get_finished_runs() - - # To be on the safe side, we don't check for: - # self.assertEqual(len(run_values), 1) - # In the ideal world, two runs were launched which take the same time - # so waiting for one means, the second one is completed. But some - # overhead might cause it to be delayed. - - # Above took the first run results and put it on run_values - # But for this check we need the second run we submitted - # Again, the runs might take slightly different time depending if we have - # heterogeneous workers, so calling wait 2 times is a guarantee that 2 - # jobs are finished - runner.wait() - run_values.extend(runner.get_finished_runs()) - self.assertEqual(len(run_values), 2) - - # To make it is parallel, we just make sure that the start of the second - # run is earlier than the end of the first run - # Results are returned in left to right - self.assertLessEqual(int(run_values[0][1].starttime), int(run_values[1][1].endtime)) - - def test_num_workers(self): - """Make sure we can properly return the number of workers""" - - # We use the funcdict as a mechanism to test Runner - runner = ExecuteTAFuncDict(ta=target_delayed, stats=self.stats, run_obj="quality") - runner = DaskParallelRunner(runner, n_workers=2) - self.assertEqual(runner.num_workers(), 2) - - # Reduce the number of workers - # have to give time for the worker to be killed - runner.client.cluster.scale(1) - time.sleep(2) - self.assertEqual(runner.num_workers(), 1) - - def test_file_output(self): - tmp_dir = tempfile.mkdtemp() - single_worker_mock = unittest.mock.Mock() - parallel_runner = DaskParallelRunner( # noqa F841 - single_worker=single_worker_mock, n_workers=1, output_directory=tmp_dir - ) - self.assertTrue(os.path.exists(os.path.join(tmp_dir, ".dask_scheduler_file"))) - - def test_do_not_close_external_client(self): - tmp_dir = tempfile.mkdtemp() - - single_worker_mock = unittest.mock.Mock() - client = Client() - parallel_runner = DaskParallelRunner( - single_worker=single_worker_mock, - dask_client=client, - n_workers=1, - output_directory=tmp_dir, - ) # noqa F841 - del parallel_runner - self.assertFalse(os.path.exists(os.path.join(tmp_dir, ".dask_scheduler_file"))) - self.assertEqual(client.status, "running") - parallel_runner = DaskParallelRunner( - single_worker=single_worker_mock, - dask_client=client, - n_workers=1, - output_directory=tmp_dir, - ) # noqa F841 - del parallel_runner - self.assertEqual(client.status, "running") - client.shutdown() - - def test_additional_info_crash_msg(self): - """ - We want to make sure we catch errors as additional info, - and in particular when doing multiprocessing runs, we - want to make sure we capture dask exceptions - """ - - def target_nonpickable(x, seed, instance): - return x**2, {"key": seed, "instance": instance} - - runner = ExecuteTAFuncDict(ta=target_nonpickable, stats=self.stats, run_obj="quality") - - runner = DaskParallelRunner(runner, n_workers=2) - - run_info = RunInfo( - config=2, - instance="test", - instance_specific="0", - seed=0, - cutoff=None, - capped=False, - budget=0.0, - ) - runner.submit_run(run_info) - runner.wait() - run_info, result = runner.get_finished_runs()[0] - - # Make sure the traceback message is included - self.assertIn("traceback", result.additional_info) - self.assertIn( - # We expect the problem to occur in the run wrapper - # So traceback should show this! - "target_nonpickable", - result.additional_info["traceback"], - ) - - # Make sure the error message is included - self.assertIn("error", result.additional_info) - self.assertIn("Can't pickle local object", result.additional_info["error"]) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_tae/test_serial_runner.py b/tests/test_tae/test_serial_runner.py deleted file mode 100644 index dd7efb9d3..000000000 --- a/tests/test_tae/test_serial_runner.py +++ /dev/null @@ -1,103 +0,0 @@ -import time -import unittest -import unittest.mock - -from smac.configspace import ConfigurationSpace -from smac.runhistory.runhistory import RunInfo, RunValue -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.tae.execute_func import ExecuteTAFuncDict -from smac.tae.serial_runner import SerialRunner - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -def target(x, seed, instance): - return x**2, {"key": seed, "instance": instance} - - -def target_delayed(x, seed, instance): - time.sleep(1) - return x**2, {"key": seed, "instance": instance} - - -class TestSerialRunner(unittest.TestCase): - def setUp(self): - self.cs = ConfigurationSpace() - self.scenario = Scenario({"cs": self.cs, "run_obj": "quality", "output_dir": ""}) - self.stats = Stats(scenario=self.scenario) - - def test_run(self): - """Makes sure that we are able to run a configuration and - return the expected values/types""" - - # We use the funcdict as a mechanism to test SerialRunner - runner = ExecuteTAFuncDict(ta=target, stats=self.stats, run_obj="quality") - self.assertIsInstance(runner, SerialRunner) - - run_info = RunInfo( - config=2, - instance="test", - instance_specific="0", - seed=0, - cutoff=None, - capped=False, - budget=0.0, - ) - - # submit runs! then get the value - runner.submit_run(run_info) - run_values = runner.get_finished_runs() - self.assertEqual(len(run_values), 1) - self.assertIsInstance(run_values, list) - self.assertIsInstance(run_values[0][0], RunInfo) - self.assertIsInstance(run_values[0][1], RunValue) - self.assertEqual(run_values[0][1].cost, 4) - self.assertEqual(run_values[0][1].status, StatusType.SUCCESS) - - def test_serial_runs(self): - - # We use the funcdict as a mechanism to test SerialRunner - runner = ExecuteTAFuncDict(ta=target_delayed, stats=self.stats, run_obj="quality") - self.assertIsInstance(runner, SerialRunner) - - run_info = RunInfo( - config=2, - instance="test", - instance_specific="0", - seed=0, - cutoff=None, - capped=False, - budget=0.0, - ) - runner.submit_run(run_info) - run_info = RunInfo( - config=3, - instance="test", - instance_specific="0", - seed=0, - cutoff=None, - capped=False, - budget=0.0, - ) - runner.submit_run(run_info) - run_values = runner.get_finished_runs() - self.assertEqual(len(run_values), 2) - - # To make sure runs launched serially, we just make sure that the end time of - # a run is later than the other - # Results are returned in left to right - self.assertLessEqual(int(run_values[1][1].endtime), int(run_values[0][1].starttime)) - - # No wait time in serial runs! - start = time.time() - runner.wait() - - # The run takes a second, so 0.5 is sufficient - self.assertLess(time.time() - start, 0.5) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_tae/test_tae_aclib.py b/tests/test_tae/test_tae_aclib.py deleted file mode 100644 index 3592b02a7..000000000 --- a/tests/test_tae/test_tae_aclib.py +++ /dev/null @@ -1,99 +0,0 @@ -import os -import shlex -import unittest - -from smac.configspace import ConfigurationSpace -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.tae.execute_ta_run_aclib import ExecuteTARunAClib - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TaeOldTest(unittest.TestCase): - def setUp(self): - self.current_dir = os.getcwd() - base_dir = os.path.split(__file__)[0] - base_dir = os.path.join(base_dir, "../../tests", "..") - os.chdir(base_dir) - - def tearDown(self): - os.chdir(self.current_dir) - - def test_run(self): - """ - running some simple algo in aclib 2.0 style - """ - scen = Scenario( - scenario={"cs": ConfigurationSpace(), "run_obj": "quality", "output_dir": ""}, - cmd_options=None, - ) - stats = Stats(scen) - - eta = ExecuteTARunAClib(ta=shlex.split("python tests/test_tae/dummy_ta_wrapper_aclib.py 1"), stats=stats) - status, cost, runtime, ar_info = eta.run(config={}, instance="0") - assert status == StatusType.TIMEOUT - assert cost == 2.0 - assert runtime == 2.0 - - print(status, cost, runtime) - - eta = ExecuteTARunAClib(ta=shlex.split("python tests/test_tae/dummy_ta_wrapper_aclib.py 2"), stats=stats) - status, cost, runtime, ar_info = eta.run(config={}, instance="0") - assert status == StatusType.SUCCESS - assert cost == 3.0 - assert runtime == 3.0 - - print(status, cost, runtime) - - eta = ExecuteTARunAClib( - ta=shlex.split("python tests/test_tae/dummy_ta_wrapper_aclib.py 2"), - stats=stats, - run_obj="quality", - ) - status, cost, runtime, ar_info = eta.run(config={}, instance="0") - assert status == StatusType.SUCCESS - assert cost == 2.0 - assert runtime == 3.0 - - print(status, cost, runtime, ar_info) - - def test_status(self): - - scen = Scenario( - scenario={"cs": ConfigurationSpace(), "run_obj": "quality", "output_dir": ""}, - cmd_options=None, - ) - stats = Stats(scen) - - eta = ExecuteTARunAClib(ta=shlex.split(""), stats=stats) - - def test_success(**kwargs): - results = {"status": "SUCCESS", "cost": 1234567890} - return results, "", "" - - eta._call_ta = test_success - status, cost, runtime, ar_info = eta.run(config={}, instance="0") - self.assertEqual(status, StatusType.SUCCESS) - - def test_success(**kwargs): - results = {"status": "SUCESS", "cost": 1234567890} - return results, "", "" - - eta._call_ta = test_success - status, cost, runtime, ar_info = eta.run(config={}, instance="0") - self.assertEqual(status, StatusType.CRASHED) - - def test_success(**kwargs): - results = {"status": "success", "cost": 1234567890} - return results, "", "" - - eta._call_ta = test_success - status, cost, runtime, ar_info = eta.run(config={}, instance="0") - self.assertEqual(status, StatusType.CRASHED) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_tae/test_tae_old.py b/tests/test_tae/test_tae_old.py deleted file mode 100644 index 90d88c1f3..000000000 --- a/tests/test_tae/test_tae_old.py +++ /dev/null @@ -1,104 +0,0 @@ -import os -import shlex -import unittest - -from smac.configspace import ConfigurationSpace -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.tae.execute_ta_run_old import ExecuteTARunOld - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TaeOldTest(unittest.TestCase): - def setUp(self): - self.current_dir = os.getcwd() - base_dir = os.path.split(__file__)[0] - base_dir = os.path.join(base_dir, "../../tests", "..") - os.chdir(base_dir) - - def tearDown(self): - os.chdir(self.current_dir) - - def test_run(self): - """ - running some simple algo in old style - """ - scen = Scenario( - scenario={"cs": ConfigurationSpace(), "run_obj": "quality", "output_dir": ""}, - cmd_options=None, - ) - stats = Stats(scen) - - eta = ExecuteTARunOld(ta=shlex.split("python tests/test_tae/dummy_ta_wrapper.py 1"), stats=stats) - status, cost, runtime, ar_info = eta.run(config={}) - assert status == StatusType.SUCCESS - assert cost == 1.0 - assert runtime == 1.0 - - print(status, cost, runtime) - - eta = ExecuteTARunOld(ta=shlex.split("python tests/test_tae/dummy_ta_wrapper.py 2"), stats=stats) - status, cost, runtime, ar_info = eta.run(config={}) - assert status == StatusType.SUCCESS - assert cost == 2.0 - assert runtime == 2.0 - - print(status, cost, runtime) - - eta = ExecuteTARunOld( - ta=shlex.split("python tests/test_tae/dummy_ta_wrapper.py 2"), - stats=stats, - run_obj="quality", - ) - status, cost, runtime, ar_info = eta.run( - config={}, - ) - assert status == StatusType.SUCCESS - assert cost == 4.0 - assert runtime == 2.0 - - print(status, cost, runtime, ar_info) - - def test_status(self): - - scen = Scenario( - scenario={"cs": ConfigurationSpace(), "run_obj": "quality", "output_dir": ""}, - cmd_options=None, - ) - stats = Stats(scen) - - eta = ExecuteTARunOld(ta=shlex.split(""), stats=stats) - - def test_success(**kwargs): - return "Result of this algorithm run: SUCCESS,1,1,1,12354", "" - - eta._call_ta = test_success - status, cost, runtime, ar_info = eta.run( - config={}, - ) - self.assertEqual(status, StatusType.SUCCESS) - - def test_success(**kwargs): - return "Result of this algorithm run: SUCESS,1,1,1,12354", "" - - eta._call_ta = test_success - status, cost, runtime, ar_info = eta.run( - config={}, - ) - self.assertEqual(status, StatusType.CRASHED) - - def test_success(**kwargs): - return "Result of this algorithm run: success,1,1,1,12354", "" - - eta._call_ta = test_success - status, cost, runtime, ar_info = eta.run( - config={}, - ) - self.assertEqual(status, StatusType.SUCCESS) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_utils/__init__.py b/tests/test_utils/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/test_utils/io/__init__.py b/tests/test_utils/io/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/test_utils/io/test_CMDReader.py b/tests/test_utils/io/test_CMDReader.py deleted file mode 100644 index 8e04768b3..000000000 --- a/tests/test_utils/io/test_CMDReader.py +++ /dev/null @@ -1,62 +0,0 @@ -import logging -import os -import unittest - -from smac.utils.io.cmd_reader import CMDReader - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TestArgs: - def __init__(self, sf, seed, mi, vl): - self.scenario_file = sf - self.seed = seed - self.max_iterations = mi - self.verbose_level = vl - - def cmdline(self): - return [ - "--scenario-file", - self.scenario_file, - "--seed", - str(self.seed), - "--runcount-limit", - str(self.max_iterations), - "--verbose", - self.verbose_level, - ] - - -class CMDReaderTest(unittest.TestCase): - def setUp(self): - logging.basicConfig() - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - self.logger.setLevel(logging.DEBUG) - self.cr = CMDReader() - self.current_dir = os.getcwd() - base_directory = os.path.split(__file__)[0] - base_directory = os.path.abspath(os.path.join(base_directory, "..", "..", "..")) - os.chdir(base_directory) - - def tearDown(self): - os.chdir(self.current_dir) - - def test_check_args_exception(self): # Tests if the Exception is correctly raised - targs = TestArgs(".", 1234, 2, "DEBUG") - with self.assertRaises(SystemExit): - self.cr.read_cmd(targs.cmdline()) - - def test_check_args(self): # Tests if no Exception is raised - targs = TestArgs("tests/test_files/scenario_test/scenario.txt", 1234, 2, "DEBUG") - self.cr.read_cmd(targs.cmdline()) - - def test_doc_files(self): - self.cr.write_main_options_to_doc(path="test.rst") - self.cr.write_smac_options_to_doc(path="test.rst") - self.cr.write_scenario_options_to_doc(path="test.rst") - os.remove("./test.rst") - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_utils/io/test_inputreader.py b/tests/test_utils/io/test_inputreader.py deleted file mode 100644 index 5d9f09ccb..000000000 --- a/tests/test_utils/io/test_inputreader.py +++ /dev/null @@ -1,85 +0,0 @@ -import logging -import os -import unittest - -from ConfigSpace.hyperparameters import UniformFloatHyperparameter - -from smac.configspace import ConfigurationSpace, pcs -from smac.utils.io.input_reader import InputReader -from smac.utils.io.output_writer import OutputWriter - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class InputReaderTest(unittest.TestCase): - def setUp(self): - logging.basicConfig() - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - self.logger.setLevel(logging.DEBUG) - self.current_dir = os.getcwd() - base_directory = os.path.split(__file__)[0] - base_directory = os.path.abspath(os.path.join(base_directory, "..", "..", "..")) - os.chdir(base_directory) - - # Files that will be created: - self.pcs_fn = "tests/test_files/configspace.pcs" - self.json_fn = "tests/test_files/configspace.json" - - self.output_files = [self.pcs_fn, self.json_fn] - - def tearDown(self): - for output_file in self.output_files: - if output_file: - try: - os.remove(output_file) - except FileNotFoundError: - pass - - os.chdir(self.current_dir) - - def test_feature_input(self): - feature_fn = "tests/test_files/features_example.csv" - in_reader = InputReader() - feats = in_reader.read_instance_features_file(fn=feature_fn) - self.assertEqual(feats[0], ["feature1", "feature2", "feature3"]) - feats_original = { - "inst1": [1.0, 2.0, 3.0], - "inst2": [1.5, 2.5, 3.5], - "inst3": [1.7, 1.8, 1.9], - } - for i in feats[1]: - self.assertEqual(feats_original[i], list(feats[1][i])) - - def test_save_load_configspace(self): - """Check if inputreader can load different config-spaces""" - cs = ConfigurationSpace() - hyp = UniformFloatHyperparameter("A", 0.0, 1.0, default_value=0.5) - cs.add_hyperparameters([hyp]) - - output_writer = OutputWriter() - input_reader = InputReader() - - # pcs_new - output_writer.save_configspace(cs, self.pcs_fn, "pcs_new") - restored_cs = input_reader.read_pcs_file(self.pcs_fn) - self.assertEqual(cs, restored_cs) - restored_cs = input_reader.read_pcs_file(self.pcs_fn, self.logger) - self.assertEqual(cs, restored_cs) - - # json - output_writer.save_configspace(cs, self.json_fn, "json") - restored_cs = input_reader.read_pcs_file(self.json_fn) - self.assertEqual(cs, restored_cs) - restored_cs = input_reader.read_pcs_file(self.json_fn, self.logger) - self.assertEqual(cs, restored_cs) - - # pcs - with open(self.pcs_fn, "w") as fh: - fh.write(pcs.write(cs)) - restored_cs = input_reader.read_pcs_file(self.pcs_fn) - self.assertEqual(cs, restored_cs) - restored_cs = input_reader.read_pcs_file(self.pcs_fn) - self.assertEqual(cs, restored_cs) - restored_cs = input_reader.read_pcs_file(self.pcs_fn, self.logger) - self.assertEqual(cs, restored_cs) diff --git a/tests/test_utils/io/test_result_merging.py b/tests/test_utils/io/test_result_merging.py deleted file mode 100644 index 981843c61..000000000 --- a/tests/test_utils/io/test_result_merging.py +++ /dev/null @@ -1,48 +0,0 @@ -import json -import logging -import os -import tempfile -import unittest.mock -import json -from unittest.mock import patch - -from smac.configspace import ( - CategoricalHyperparameter, - Configuration, - ConfigurationSpace, - Constant, - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.utils.io.traj_logging import TrajEntry, TrajLogger - -from smac.utils.io.result_merging import ResultMerger - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class ResultMergerTest(unittest.TestCase): - def setUp(self) -> None: - base_directory = os.path.split(__file__)[0] - base_directory = os.path.abspath(os.path.join(base_directory, "../../tests", "..")) - os.chdir(base_directory) - - def test_init_valueerror(self): - with self.assertRaises(ValueError): - rm = ResultMerger() - - def test_merge(self): - print(os.getcwd()) - outdir = "test_files/example_run" - rundirs = [outdir] * 3 - rm = ResultMerger(rundirs=rundirs) - rh = rm.get_runhistory() - traj = rm.get_trajectory() - traj_fn = os.path.join(outdir, "traj.json") - with open(traj_fn, "r") as file: - lines = file.readlines() - traj_from_file = [json.loads(line) for line in lines] - self.assertEqual(len(traj_from_file), len(traj)) diff --git a/tests/test_utils/io/test_traj_logging.py b/tests/test_utils/io/test_traj_logging.py deleted file mode 100644 index 643100561..000000000 --- a/tests/test_utils/io/test_traj_logging.py +++ /dev/null @@ -1,229 +0,0 @@ -import json -import logging -import os -import tempfile -import unittest.mock -from unittest.mock import patch - -from smac.configspace import ( - CategoricalHyperparameter, - Configuration, - ConfigurationSpace, - Constant, - UniformFloatHyperparameter, - UniformIntegerHyperparameter, -) -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.utils.io.traj_logging import TrajEntry, TrajLogger - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class TrajLoggerTest(unittest.TestCase): - def mocked_get_used_wallclock_time(self): - self.value += 1 - return self.value - - def setUp(self): - logging.basicConfig() - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - self.logger.setLevel(logging.DEBUG) - self.value = 0 - self.cs = ConfigurationSpace() - self.cs.add_hyperparameters( - [ - UniformFloatHyperparameter("param_a", -0.2, 1.77, 1.1), - UniformIntegerHyperparameter("param_b", -3, 10, 1), - Constant("param_c", "value"), - CategoricalHyperparameter("ambigous_categorical", choices=["True", True, 5]), # True is ambigous here - ] - ) - self.test_config = Configuration( - self.cs, {"param_a": 0.5, "param_b": 1, "param_c": "value", "ambigous_categorical": 5} - ) - - def test_init(self): - scen = Scenario(scenario={"run_obj": "quality", "cs": self.cs, "output_dir": ""}) - stats = Stats(scen) - with tempfile.TemporaryDirectory() as tmpdir: - path = os.path.join(tmpdir, "tmp_test_folder") - TrajLogger(output_dir=path, stats=stats) - self.assertTrue(os.path.exists(path)) - - def test_oserror(self): - scen = Scenario(scenario={"run_obj": "quality", "cs": self.cs, "output_dir": ""}) - stats = Stats(scen) - # test OSError - with patch("os.makedirs") as osMock: - osMock.side_effect = OSError() - self.assertRaises(OSError, TrajLogger, output_dir="random_directory", stats=stats) - - @patch("smac.stats.stats.Stats") - def test_add_entries(self, mock_stats): - # Mock stats - mock_stats.ta_time_used = 0.5 - mock_stats.get_used_wallclock_time = self.mocked_get_used_wallclock_time - mock_stats.finished_ta_runs = 1 - - with tempfile.TemporaryDirectory() as tmpdir: - tl = TrajLogger(output_dir=tmpdir, stats=mock_stats) - - # Add some entries - tl.add_entry(0.9, 1, self.test_config, 0) - mock_stats.ta_runs = 2 - mock_stats.ta_time_used = 0 - tl.add_entry(1.3, 1, self.test_config, 10) - mock_stats.ta_time_used = 0 - tl.add_entry( - 0.7, - 2, - Configuration(self.cs, dict(self.test_config.get_dictionary(), **{"param_a": 0.0})), - 10, - ) - - # Test the list that's added to the trajectory class - self.assertEqual(tl.trajectory[0], TrajEntry(0.9, 1, self.test_config, 1, 0.5, 1, 0)) - # Test named-tuple-access: - self.assertEqual(tl.trajectory[0].train_perf, 0.9) - self.assertEqual(tl.trajectory[0].incumbent_id, 1) - self.assertEqual(tl.trajectory[0].ta_runs, 1) - self.assertEqual(tl.trajectory[0].ta_time_used, 0.5) - self.assertEqual(tl.trajectory[0].wallclock_time, 1) - self.assertEqual(tl.trajectory[0].budget, 0) - self.assertEqual(len(tl.trajectory), 3) - - # Check if the trajectories are generated - for fn in ["traj_old.csv", "traj_aclib2.json", "traj.json"]: - self.assertTrue(os.path.exists(os.path.join(tmpdir, fn))) - - # Load trajectories - with open(os.path.join(tmpdir, "traj_old.csv")) as to: - data = to.read().split("\n") - with open(os.path.join(tmpdir, "traj_aclib2.json")) as js_aclib: - json_dicts_aclib2 = [json.loads(line) for line in js_aclib.read().splitlines()] - with open(os.path.join(tmpdir, "traj.json")) as js: - json_dicts_alljson = [json.loads(line) for line in js.read().splitlines()] - - # Check old format - header = data[0].split(",") - self.assertEqual(header[0], '"CPU Time Used"') - self.assertEqual(header[-1], '"Configuration..."') - - data = list(map(lambda x: x.split(", "), data[1:])) - frmt_str = "%1.6f" - self.assertEqual(frmt_str % 0.5, data[0][0]) - self.assertEqual(frmt_str % 0.9, data[0][1]) - self.assertEqual(frmt_str % 0.5, data[0][4]) - - self.assertEqual(frmt_str % 0, data[1][0]) - self.assertEqual(frmt_str % 1.3, data[1][1]) - self.assertEqual(frmt_str % 2, data[1][4]) - - self.assertEqual(frmt_str % 0, data[2][0]) - self.assertEqual(frmt_str % 0.7, data[2][1]) - self.assertEqual(frmt_str % 3, data[2][4]) - - # Check aclib2-format - self.assertEqual(json_dicts_aclib2[0]["cpu_time"], 0.5) - self.assertEqual(json_dicts_aclib2[0]["cost"], 0.9) - self.assertEqual(len(json_dicts_aclib2[0]["incumbent"]), 4) - self.assertTrue("param_a='0.5'" in json_dicts_aclib2[0]["incumbent"]) - self.assertTrue("param_a='0.0'" in json_dicts_aclib2[2]["incumbent"]) - - # Check alljson-format - self.assertEqual(json_dicts_alljson[0]["cpu_time"], 0.5) - self.assertEqual(json_dicts_alljson[0]["cost"], 0.9) - self.assertEqual(len(json_dicts_alljson[0]["incumbent"]), 4) - self.assertTrue(json_dicts_alljson[0]["incumbent"]["param_a"] == 0.5) - self.assertTrue(json_dicts_alljson[2]["incumbent"]["param_a"] == 0.0) - self.assertEqual(json_dicts_alljson[0]["budget"], 0) - self.assertEqual(json_dicts_alljson[2]["budget"], 10) - - @patch("smac.stats.stats.Stats") - def test_add_entries_multi_objectives(self, mock_stats): - # Mock stats - mock_stats.ta_time_used = 0.5 - mock_stats.get_used_wallclock_time = self.mocked_get_used_wallclock_time - mock_stats.finished_ta_runs = 1 - - num_obj = 2 - - with tempfile.TemporaryDirectory() as tmpdir: - tl = TrajLogger(output_dir=tmpdir, stats=mock_stats) - - # Add some entries - tl.add_entry([0.9, 0.8], 1, self.test_config, 0) - - # Test the list that's added to the trajectory class - self.assertEqual(tl.trajectory[0], TrajEntry([0.9, 0.8], 1, self.test_config, 1, 0.5, 1, 0)) - # Test named-tuple-access: - self.assertEqual(tl.trajectory[0].train_perf, [0.9, 0.8]) - self.assertEqual(len(tl.trajectory), 1) - - # Check if the trajectories are generated - for fn in ["traj_old.csv", "traj_aclib2.json", "traj.json"]: - self.assertTrue(os.path.exists(os.path.join(tmpdir, fn))) - - # Load trajectories - with open(os.path.join(tmpdir, "traj_old.csv")) as to: - data = to.read().split("\n") - with open(os.path.join(tmpdir, "traj_aclib2.json")) as js_aclib: - json_dicts_aclib2 = [json.loads(line) for line in js_aclib.read().splitlines()] - with open(os.path.join(tmpdir, "traj.json")) as js: - json_dicts_alljson = [json.loads(line) for line in js.read().splitlines()] - - # Check old format - header = data[0].split(",") - self.assertEqual(header[0], '"CPU Time Used"') - self.assertEqual(header[-1], '"Configuration..."') - - data = list(map(lambda x: x.split(", "), data[1:])) - data[0][1] = ", ".join(data[0][1 : 1 + num_obj]) - del data[0][1 + 1 : 1 + num_obj] - frmt_str = "%1.6f" - - self.assertEqual(frmt_str % 0.5, data[0][0]) - self.assertEqual(f"[{0.9}, {0.8}]", data[0][1]) - self.assertEqual(frmt_str % 0.5, data[0][4]) - - # Check aclib2-format - self.assertEqual(json_dicts_aclib2[0]["cpu_time"], 0.5) - self.assertEqual(json_dicts_aclib2[0]["cost"], [0.9, 0.8]) - self.assertEqual(len(json_dicts_aclib2[0]["incumbent"]), 4) - self.assertTrue("param_a='0.5'" in json_dicts_aclib2[0]["incumbent"]) - - # Check alljson-format - self.assertEqual(json_dicts_alljson[0]["cpu_time"], 0.5) - self.assertEqual(json_dicts_alljson[0]["cost"], [0.9, 0.8]) - self.assertEqual(len(json_dicts_alljson[0]["incumbent"]), 4) - self.assertTrue(json_dicts_alljson[0]["incumbent"]["param_a"] == 0.5) - self.assertEqual(json_dicts_alljson[0]["budget"], 0) - - @patch("smac.stats.stats.Stats") - def test_ambigious_categoricals(self, mock_stats): - mock_stats.ta_time_used = 0.5 - mock_stats.get_used_wallclock_time = self.mocked_get_used_wallclock_time - mock_stats.finished_ta_runs = 1 - - with tempfile.TemporaryDirectory() as tmpdir: - tl = TrajLogger(output_dir=tmpdir, stats=mock_stats) - - problem_config = Configuration( - self.cs, - {"param_a": 0.0, "param_b": 2, "param_c": "value", "ambigous_categorical": True}, - ) # not recoverable without json - tl.add_entry(0.9, 1, problem_config) - - from_aclib2 = tl.read_traj_aclib_format(os.path.join(tmpdir, "traj_aclib2.json"), self.cs) - from_alljson = tl.read_traj_alljson_format(os.path.join(tmpdir, "traj.json"), self.cs) - - # Wrong! but passes: - self.assertIsInstance(from_aclib2[0]["incumbent"]["ambigous_categorical"], str) - # Works good for alljson: - self.assertIsInstance(from_alljson[0]["incumbent"]["ambigous_categorical"], bool) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_utils/test_multi_objective.py b/tests/test_utils/test_multi_objective.py deleted file mode 100644 index 6edc7d4fa..000000000 --- a/tests/test_utils/test_multi_objective.py +++ /dev/null @@ -1,48 +0,0 @@ -import unittest -from multiprocessing.sharedctypes import Value - -import numpy as np -import pytest - -from smac.multi_objective.utils import normalize_costs - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class MultiObjectiveTest(unittest.TestCase): - def setUp(self): - self.bounds = [(0, 50), (50, 100)] - self.bounds_invalid = [(0, 0), (5, 5)] - - def test_normalize_costs(self): - # If no bounds are passed, we get the same result back - v = [5, 2] - nv = normalize_costs(v) - self.assertEqual(nv, [5, 2]) - - # Normalize between 0..1 given data only - v = [25, 50] - nv = normalize_costs(v, self.bounds) - self.assertEqual(nv, [0.5, 0]) - - # Invalid bounds - v = [25, 50] - nv = normalize_costs(v, self.bounds_invalid) - self.assertEqual(nv, [1, 1]) - - # Invalid input - v = [[25], [50]] - with pytest.raises(AssertionError): - nv = normalize_costs(v, self.bounds) - - # Wrong shape - v = [25, 50, 75] - with pytest.raises(ValueError): - nv = normalize_costs(v, self.bounds) - - -if __name__ == "__main__": - t = MultiObjectiveTest() - t.setUp() - t.test_normalize_costs() diff --git a/tests/test_utils/test_validate.py b/tests/test_utils/test_validate.py deleted file mode 100644 index 863fc675d..000000000 --- a/tests/test_utils/test_validate.py +++ /dev/null @@ -1,459 +0,0 @@ -import logging -import os -import shutil -import unittest -from unittest import mock - -import numpy as np - -from smac.configspace import Configuration -from smac.runhistory.runhistory import RunHistory -from smac.scenario.scenario import Scenario -from smac.stats.stats import Stats -from smac.tae import StatusType -from smac.tae.execute_ta_run_old import ExecuteTARunOld -from smac.utils.io.traj_logging import TrajLogger -from smac.utils.validate import Validator, _Run - -__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover" -__license__ = "3-clause BSD" - - -class ValidationTest(unittest.TestCase): - def setUp(self): - base_directory = os.path.split(__file__)[0] - base_directory = os.path.abspath(os.path.join(base_directory, "../../tests", "..")) - self.current_dir = os.getcwd() - os.chdir(base_directory) - - logging.basicConfig() - self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) - self.logger.setLevel(logging.DEBUG) - self.rng = np.random.RandomState(seed=42) - self.scen_fn = "tests/test_files/validation/scenario.txt" - self.train_insts = ["0", "1", "2"] - self.test_insts = ["3", "4", "5"] - self.inst_specs = { - "0": "null", - "1": "one", - "2": "two", - "3": "three", - "4": "four", - "5": "five", - } - self.feature_dict = { - "0": np.array((1, 2, 3)), - "1": np.array((1, 2, 3)), - "2": np.array((1, 2, 3)), - "3": np.array((1, 2, 3)), - "4": np.array((1, 2, 3)), - "5": np.array((1, 2, 3)), - } - self.output_rh = "tests/test_files/validation/" - scen = Scenario(self.scen_fn, cmd_options={"run_obj": "quality"}) - self.stats = Stats(scen) - self.trajectory = TrajLogger.read_traj_aclib_format( - fn="tests/test_files/validation/test_validation_traj.json", cs=scen.cs - ) - self.output_dirs = [self.output_rh + "test"] - self.output_files = [ - self.output_rh + "validated_runhistory_EPM.json", - self.output_rh + "validated_runhistory.json", - ] - - self.maxDiff = None - - def tearDown(self): - for output_dir in self.output_dirs: - if output_dir: - shutil.rmtree(output_dir, ignore_errors=True) - for output_file in self.output_files: - if output_file: - try: - os.remove(output_file) - except FileNotFoundError: - pass - os.chdir(self.current_dir) - - def test_rng(self): - scen = Scenario(self.scen_fn, cmd_options={"run_obj": "quality"}) - validator = Validator(scen, self.trajectory, 42) - self.assertTrue(isinstance(validator.rng, np.random.RandomState)) - validator = Validator(scen, self.trajectory) - self.assertTrue(isinstance(validator.rng, np.random.RandomState)) - validator = Validator(scen, self.trajectory, np.random.RandomState()) - self.assertTrue(isinstance(validator.rng, np.random.RandomState)) - - def test_nonexisting_output(self): - scen = Scenario(self.scen_fn, cmd_options={"run_obj": "quality"}) - validator = Validator(scen, self.trajectory) - path = "tests/test_files/validation/test/nonexisting/output" - validator.validate(output_fn=path) - self.assertTrue(os.path.exists(path)) - - def test_pass_tae(self): - scen = Scenario(self.scen_fn, cmd_options={"run_obj": "quality"}) - tae = ExecuteTARunOld(ta=scen.ta, stats=self.stats) - validator = Validator(scen, self.trajectory) - rh_mock = mock.Mock() - with mock.patch.object( - Validator, - "_validate_parallel", - return_value=[mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), mock.MagicMock()], - ) as validate_parallel_mock: - with mock.patch.object( - Validator, - "_get_runs", - return_value=[[mock.Mock(), mock.Mock(), mock.Mock(), mock.Mock()], rh_mock], - ): - validator.validate(tae=tae) - self.assertIs(validate_parallel_mock.call_args[0][0], tae) - self.assertEqual(rh_mock.add.call_count, 4) - - def test_no_rh_epm(self): - scen = Scenario(self.scen_fn, cmd_options={"run_obj": "quality"}) - scen.feature_array = None - validator = Validator(scen, self.trajectory) - self.assertRaises(ValueError, validator.validate_epm) - - def test_epm_reuse_rf(self): - """if no runhistory is passed to epm, but there was a model trained - before, that model should be reused! (if reuse_epm flag is set)""" - scen = Scenario(self.scen_fn, cmd_options={"run_obj": "quality"}) - scen.feature_array = None - validator = Validator(scen, self.trajectory) - old_rh = RunHistory() - for config in [e["incumbent"] for e in self.trajectory]: - old_rh.add(config, 1, 1, StatusType.SUCCESS, instance_id="0", seed=127) - self.assertTrue(isinstance(validator.validate_epm(runhistory=old_rh), RunHistory)) - self.assertTrue(isinstance(validator.validate_epm(output_fn="tests/test_files/validation/"), RunHistory)) - self.assertRaises(ValueError, validator.validate_epm, reuse_epm=False) - - def test_no_feature_dict(self): - scen = Scenario(self.scen_fn, cmd_options={"run_obj": "quality"}) - scen.feature_array = None - validator = Validator(scen, self.trajectory) - old_rh = RunHistory() - for config in [e["incumbent"] for e in self.trajectory]: - old_rh.add(config, 1, 1, StatusType.SUCCESS, instance_id="0", seed=127) - validator.validate_epm(runhistory=old_rh) - - def test_get_configs(self): - scen = Scenario(self.scen_fn, cmd_options={"run_obj": "quality"}) - validator = Validator(scen, self.trajectory, self.rng) - self.assertEqual(1, len(validator._get_configs("def"))) - self.assertEqual(1, len(validator._get_configs("inc"))) - self.assertEqual(2, len(validator._get_configs("def+inc"))) - self.assertEqual(7, len(validator._get_configs("wallclock_time"))) - self.assertEqual(8, len(validator._get_configs("cpu_time"))) - self.assertEqual(10, len(validator._get_configs("all"))) - # Using maxtime - validator.scen.wallclock_limit = 65 - validator.scen.algo_runs_timelimit = 33 - self.assertEqual(8, len(validator._get_configs("wallclock_time"))) - self.assertEqual(9, len(validator._get_configs("cpu_time"))) - # Exceptions - self.assertRaises(ValueError, validator._get_configs, "notanoption") - self.assertRaises(ValueError, validator._get_instances, "notanoption") - - def test_get_runs_capped(self): - """test if capped, crashed and aborted runs are ignored - during rh-recovery""" - scen = Scenario(self.scen_fn, cmd_options={"run_obj": "quality", "instances": ["0"]}) - - validator = Validator(scen, self.trajectory, self.rng) - - # Get runhistory - old_configs = [Configuration(scen.cs, values={"x1": i, "x2": i}) for i in range(1, 7)] - old_rh = RunHistory() - old_rh.add(old_configs[0], 1, 1, StatusType.SUCCESS, instance_id="0", seed=0) - old_rh.add(old_configs[1], 1, 1, StatusType.TIMEOUT, instance_id="0", seed=0) - old_rh.add(old_configs[2], 1, 1, StatusType.CRASHED, instance_id="0", seed=0) - old_rh.add(old_configs[3], 1, 1, StatusType.ABORT, instance_id="0", seed=0) - old_rh.add(old_configs[4], 1, 1, StatusType.MEMOUT, instance_id="0", seed=0) - old_rh.add(old_configs[5], 1, 1, StatusType.CAPPED, instance_id="0", seed=0) - - # Get multiple configs - expected = [ - _Run(inst_specs="0", seed=0, inst="0", config=old_configs[2]), - _Run(inst_specs="0", seed=0, inst="0", config=old_configs[3]), - _Run(inst_specs="0", seed=0, inst="0", config=old_configs[5]), - ] - - runs = validator._get_runs(old_configs, ["0"], repetitions=1, runhistory=old_rh) - self.assertEqual(runs[0], expected) - - def test_get_runs(self): - """test if the runs are generated as expected""" - scen = Scenario( - self.scen_fn, - cmd_options={ - "run_obj": "quality", - "train_insts": self.train_insts, - "test_insts": self.test_insts, - "deterministic": False, - }, - ) - scen.instance_specific = self.inst_specs - - validator = Validator(scen, self.trajectory, self.rng) - # Get multiple configs - self.maxDiff = None - expected = [ - _Run(config="config1", inst="3", seed=1608637542, inst_specs="three"), - _Run(config="config2", inst="3", seed=1608637542, inst_specs="three"), - _Run(config="config1", inst="3", seed=1273642419, inst_specs="three"), - _Run(config="config2", inst="3", seed=1273642419, inst_specs="three"), - _Run(config="config1", inst="4", seed=1935803228, inst_specs="four"), - _Run(config="config2", inst="4", seed=1935803228, inst_specs="four"), - _Run(config="config1", inst="4", seed=787846414, inst_specs="four"), - _Run(config="config2", inst="4", seed=787846414, inst_specs="four"), - _Run(config="config1", inst="5", seed=996406378, inst_specs="five"), - _Run(config="config2", inst="5", seed=996406378, inst_specs="five"), - _Run(config="config1", inst="5", seed=1201263687, inst_specs="five"), - _Run(config="config2", inst="5", seed=1201263687, inst_specs="five"), - ] - - runs = validator._get_runs(["config1", "config2"], scen.test_insts, repetitions=2) - self.assertEqual(runs[0], expected) - - # Only train - expected = [ - _Run(config="config1", inst="0", seed=423734972, inst_specs="null"), - _Run(config="config1", inst="0", seed=415968276, inst_specs="null"), - _Run(config="config1", inst="1", seed=670094950, inst_specs="one"), - _Run(config="config1", inst="1", seed=1914837113, inst_specs="one"), - _Run(config="config1", inst="2", seed=669991378, inst_specs="two"), - _Run(config="config1", inst="2", seed=429389014, inst_specs="two"), - ] - - runs = validator._get_runs(["config1"], scen.train_insts, repetitions=2) - self.assertEqual(runs[0], expected) - - # Test and train - expected = [ - _Run(config="config1", inst="0", seed=249467210, inst_specs="null"), - _Run(config="config1", inst="1", seed=1972458954, inst_specs="one"), - _Run(config="config1", inst="2", seed=1572714583, inst_specs="two"), - _Run(config="config1", inst="3", seed=1433267572, inst_specs="three"), - _Run(config="config1", inst="4", seed=434285667, inst_specs="four"), - _Run(config="config1", inst="5", seed=613608295, inst_specs="five"), - ] - - insts = self.train_insts - insts.extend(self.test_insts) - runs = validator._get_runs(["config1"], insts, repetitions=1) - self.assertEqual(runs[0], expected) - - def test_validate(self): - """test validation""" - scen = Scenario( - self.scen_fn, - cmd_options={ - "run_obj": "quality", - "train_insts": self.train_insts, - "test_insts": self.test_insts, - "deterministic": False, - }, - ) - scen.instance_specific = self.inst_specs - validator = Validator(scen, self.trajectory, self.rng) - # Test basic usage - rh = validator.validate(config_mode="def", instance_mode="test", repetitions=3) - self.assertEqual(len(rh.get_all_configs()), 1) - self.assertEqual(len(rh.get_runs_for_config(rh.get_all_configs()[0], only_max_observed_budget=True)), 9) - - rh = validator.validate(config_mode="inc", instance_mode="train+test") - self.assertEqual(len(rh.get_all_configs()), 1) - self.assertEqual(len(rh.get_runs_for_config(rh.get_all_configs()[0], only_max_observed_budget=True)), 6) - - rh = validator.validate(config_mode="wallclock_time", instance_mode="train") - self.assertEqual(len(rh.get_all_configs()), 7) - self.assertEqual( - sum([len(rh.get_runs_for_config(c, only_max_observed_budget=True)) for c in rh.get_all_configs()]), - 21, - ) - - # Test with backend multiprocessing - rh = validator.validate(config_mode="def", instance_mode="test", repetitions=3, backend="multiprocessing") - self.assertEqual(len(rh.get_all_configs()), 1) - self.assertEqual(len(rh.get_runs_for_config(rh.get_all_configs()[0], only_max_observed_budget=True)), 9) - - def test_validate_no_insts(self): - """no instances""" - scen = Scenario( - self.scen_fn, - cmd_options={ - "run_obj": "quality", - "save-instantly": False, - "deterministic": False, - }, - ) - validator = Validator(scen, self.trajectory, self.rng) - rh = validator.validate(config_mode="def+inc", instance_mode="train", repetitions=3, output_fn=self.output_rh) - self.assertEqual(len(rh.get_all_configs()), 2) - self.assertEqual( - sum([len(rh.get_runs_for_config(c, only_max_observed_budget=True)) for c in rh.get_all_configs()]), - 6, - ) - - def test_validate_deterministic(self): - """deterministic ta""" - scen = Scenario( - self.scen_fn, - cmd_options={ - "run_obj": "quality", - "train_insts": self.train_insts, - "deterministic": True, - }, - ) - scen.instance_specific = self.inst_specs - validator = Validator(scen, self.trajectory, self.rng) - rh = validator.validate(config_mode="def+inc", instance_mode="train", repetitions=3) - self.assertEqual(len(rh.get_all_configs()), 2) - self.assertEqual( - sum([len(rh.get_runs_for_config(c, only_max_observed_budget=True)) for c in rh.get_all_configs()]), - 6, - ) - - def test_parallel(self): - """test parallel""" - scen = Scenario(self.scen_fn, cmd_options={"run_obj": "quality"}) - validator = Validator(scen, self.trajectory, self.rng) - validator.validate(config_mode="all", instance_mode="train+test", n_jobs=-1) - - def test_passed_runhistory(self): - """test if passed runhistory is in resulting runhistory""" - scen = Scenario( - self.scen_fn, - cmd_options={ - "run_obj": "quality", - "train_insts": self.train_insts, - "test_insts": self.test_insts, - "deterministic": False, - }, - ) - scen.instance_specific = self.inst_specs - validator = Validator(scen, self.trajectory, self.rng) - # Add a few runs and check, if they are correctly processed - old_configs = [entry["incumbent"] for entry in self.trajectory] - old_rh = RunHistory() - seeds = [127 for i in range(int(len(old_configs) / 2))] - seeds[-1] = 126 # Test instance_seed-structure in validation - for config in old_configs[: int(len(old_configs) / 2)]: - old_rh.add( - config, - 1, - 1, - StatusType.SUCCESS, - instance_id="0", - seed=seeds[old_configs.index(config)], - ) - - configs = validator._get_configs("all") - insts = validator._get_instances("train") - runs_w_rh = validator._get_runs(configs, insts, repetitions=2, runhistory=old_rh) - runs_wo_rh = validator._get_runs(configs, insts, repetitions=2) - self.assertEqual(len(runs_w_rh[0]), len(runs_wo_rh[0]) - 4) - self.assertEqual(len(runs_w_rh[1].data), 4) - self.assertEqual(len(runs_wo_rh[1].data), 0) - - def test_passed_runhistory_deterministic(self): - """test if passed runhistory is in resulting runhistory""" - scen = Scenario( - self.scen_fn, - cmd_options={ - "run_obj": "quality", - "train_insts": self.train_insts, - "deterministic": True, - }, - ) - scen.instance_specific = self.inst_specs - validator = Validator(scen, self.trajectory, self.rng) - # Add a few runs and check, if they are correctly processed - old_configs = [entry["incumbent"] for entry in self.trajectory] - old_rh = RunHistory() - for config in old_configs[: int(len(old_configs) / 2)]: - old_rh.add(config, 1, 1, StatusType.SUCCESS, instance_id="0") - - configs = validator._get_configs("all") - insts = validator._get_instances("train") - runs_w_rh = validator._get_runs(configs, insts, repetitions=2, runhistory=old_rh) - runs_wo_rh = validator._get_runs(configs, insts, repetitions=2) - self.assertEqual(len(runs_w_rh[0]), len(runs_wo_rh[0]) - 4) - self.assertEqual(len(runs_w_rh[1].data), 4) - self.assertEqual(len(runs_wo_rh[1].data), 0) - - def test_passed_runhistory_no_insts(self): - """test passed runhistory, without instances""" - scen = Scenario(self.scen_fn, cmd_options={"run_obj": "quality"}) - scen.instance_specific = self.inst_specs - validator = Validator(scen, self.trajectory, self.rng) - # Add a few runs and check, if they are correctly processed - old_configs = [entry["incumbent"] for entry in self.trajectory] - old_rh = RunHistory() - for config in old_configs[: int(len(old_configs) / 2)]: - old_rh.add(config, 1, 1, StatusType.SUCCESS, seed=127) - - configs = validator._get_configs("all") - insts = validator._get_instances("train") - runs_w_rh = validator._get_runs(configs, insts, repetitions=2, runhistory=old_rh) - runs_wo_rh = validator._get_runs(configs, insts, repetitions=2) - self.assertEqual(len(runs_w_rh[0]), len(runs_wo_rh[0]) - 4) - self.assertEqual(len(runs_w_rh[1].data), 4) - self.assertEqual(len(runs_wo_rh[1].data), 0) - - def test_validate_epm(self): - """test using epm to validate""" - scen = Scenario( - self.scen_fn, - cmd_options={ - "run_obj": "quality", - "train_insts": self.train_insts, - "test_insts": self.test_insts, - "features": self.feature_dict, - }, - ) - scen.instance_specific = self.inst_specs - validator = Validator(scen, self.trajectory, self.rng) - # Add a few runs and check, if they are correctly processed - old_configs = [entry["incumbent"] for entry in self.trajectory] - old_rh = RunHistory() - for config in old_configs[: int(len(old_configs) / 2)]: - old_rh.add(config, 1, 1, StatusType.SUCCESS, instance_id="0", seed=127) - validator.validate_epm("all", "train", 1, old_rh) - - def test_objective_runtime(self): - """test if everything is ok with objective runtime (imputing!)""" - scen = Scenario(self.scen_fn, cmd_options={"run_obj": "runtime", "cutoff_time": 5}) - validator = Validator(scen, self.trajectory, self.rng) - old_configs = [entry["incumbent"] for entry in self.trajectory] - old_rh = RunHistory() - for config in old_configs[: int(len(old_configs) / 2)]: - old_rh.add(config, 1, 1, StatusType.SUCCESS, instance_id="0") - validator.validate_epm("all", "train", 1, old_rh) - - def test_inst_no_feat(self): - """test if scenarios are treated correctly if no features are - specified.""" - scen = Scenario( - self.scen_fn, - cmd_options={ - "run_obj": "quality", - "train_insts": self.train_insts, - "test_insts": self.test_insts, - }, - ) - self.assertTrue(scen.feature_array is None) - self.assertEqual(len(scen.feature_dict), 0) - - scen.instance_specific = self.inst_specs - validator = Validator(scen, self.trajectory, self.rng) - # Add a few runs and check, if they are correctly processed - old_configs = [entry["incumbent"] for entry in self.trajectory] - old_rh = RunHistory() - for config in old_configs[: int(len(old_configs) / 2)]: - old_rh.add(config, 1, 1, StatusType.SUCCESS, instance_id="0", seed=127) - rh = validator.validate_epm("all", "train+test", 1, old_rh) - self.assertEqual(len(old_rh.get_all_configs()), 4) - self.assertEqual(len(rh.get_all_configs()), 10)

    `_D{R30M80$KHA_cY5O#%Z+>T`mRYW+o_KL-Pb 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. - */ - -// if TIMESTAMPSEPARATESTDOUTANDERR is defined, when time stamping the -// solver output, stdout and stderr are transmitted separately to -// runsolver. This lets us print whether the line comes from stdout or -// stderr. Unfortunately, when streams are separated, we have no -// guarantee to get the lines in the order they were sent (try with -// testtimestamper.cc). For this reason, this flag should be -// undefined. When undefined, stdout and stderr are combined together -// in the solver and are transmitted through one single pipe to -// runsolver. Anyway, this is the usual way to pipe a program output -// to another program input -#undef TIMESTAMPSEPARATESTDOUTANDERR - -#define SENDSIGNALBOTTOMUP - -/* - * TODO - * - * - arrange so that display occurs each n*period seconds (and not n*(period+epsilon)) - * - print the command line of new processes (??) - * - * - use /proc/%pid/status - * - * - man pthreads : le comportement de la mesure du temps pour les - * threads depend de la version du noyau - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include - -#include "Cores.hh" -#include "SignalNames.hh" -#include "ProcessList.hh" -#include "CircularBufferFilter.hh" - -#ifdef WATCHSYSCALLS -#include "SyscallsTracer.hh" -#endif - -#ifndef SVNVERSION -#define SVNVERSION -#endif - -#ifndef VERSION -#define VERSION -#endif - -#include "TimeStamper.hh" -#include "ProcessTree.hh" -#include "ProcessHistory.hh" - -#include -#include - -#include -#include - -#include "aeatk.c" - -using namespace std; - -const char *version=VERSION; - -#define SIGWAIT_RETURN_ERRORCODE 25 -#define PTHREAD_SIGMASK_ERROR_CODE 26 -#define PTHREAD_CREATE_ERROR_CODE 27 - - - - - -/** - * Flag variable which indicates whether the solver is presently running. - */ -bool solverIsRunningFlag; - -pthread_mutex_t solverIsRunningFlagLock = PTHREAD_MUTEX_INITIALIZER; -void setSolverIsRunning(bool running) -{ - pthread_mutex_lock(&solverIsRunningFlagLock); - solverIsRunningFlag = running; - pthread_mutex_unlock(&solverIsRunningFlagLock); -} -bool getSolverIsRunning() -{ - bool returnValue; - pthread_mutex_lock(&solverIsRunningFlagLock); - returnValue = solverIsRunningFlag; - pthread_mutex_unlock(&solverIsRunningFlagLock); - return returnValue; -} - - - -/** - * Controls the timer thread, and once set true it signals - * that the timer thread should shutdown gracefully. - */ -bool timerThreadShouldShutdown; - -pthread_mutex_t timerThreadShouldShutdownFlagLock = PTHREAD_MUTEX_INITIALIZER; -void setTimerThreadShouldShutdown(bool shouldShutdown) -{ - pthread_mutex_lock(&timerThreadShouldShutdownFlagLock); - timerThreadShouldShutdown = shouldShutdown; - pthread_mutex_unlock(&timerThreadShouldShutdownFlagLock); -} -bool getTimerThreadShouldShutdownFlag() -{ - bool returnValue; - pthread_mutex_lock(&timerThreadShouldShutdownFlagLock); - returnValue = timerThreadShouldShutdown; - pthread_mutex_unlock(&timerThreadShouldShutdownFlagLock); - return returnValue; -} - - -/** - * This is essentially a CAS that replaces the old - * logic with respect to stopSolver(). If multiple threads - * call stopSolver() only one of them will be allowed to pass, the - * others will return immediately. - */ - -bool stopSolverInvokedByCallerFlag; -pthread_mutex_t stopSolverFlagLock = PTHREAD_MUTEX_INITIALIZER; -void initStopSolverInvokedByCallerFlag() -{ - pthread_mutex_lock(&stopSolverFlagLock); - stopSolverInvokedByCallerFlag = false; - pthread_mutex_unlock(&stopSolverFlagLock); -} - - -bool compareAndSetStopSolverInvokedByCallerFlag(bool expect, bool newValue) -{ - bool changed; - pthread_mutex_lock(&stopSolverFlagLock); - if(stopSolverInvokedByCallerFlag == expect) - { - stopSolverInvokedByCallerFlag = newValue; - changed = true; - } else - { - changed = false; - } - pthread_mutex_unlock(&stopSolverFlagLock); - return changed; -} - - -/** - * This condition variable is set, once - * the stopSolver() method has finished being called, - * it essentially releases the other thread to start executing. - */ -pthread_mutex_t stopSolverCalledLock = PTHREAD_MUTEX_INITIALIZER; -pthread_cond_t stopSolverCalledCondition = PTHREAD_COND_INITIALIZER; - - -bool stopSolverShouldExecuteFlag; -void initStopSolverShouldExecuteFlag() -{ - pthread_mutex_lock(&stopSolverCalledLock); - stopSolverShouldExecuteFlag = false; - pthread_mutex_unlock(&stopSolverCalledLock); -} - -void setStopSolverShouldExecuteFlag() -{ - pthread_mutex_lock(&stopSolverCalledLock); - stopSolverShouldExecuteFlag = true; - pthread_cond_broadcast(&stopSolverCalledCondition); - pthread_mutex_unlock(&stopSolverCalledLock); -} - -void waitForStopSolverShouldExecuteFlag() -{ - - pthread_mutex_lock(&stopSolverCalledLock); - - while(1) - { - if(stopSolverShouldExecuteFlag == true) - { - pthread_mutex_unlock(&stopSolverCalledLock); - return; - } else - { - pthread_cond_wait(&stopSolverCalledCondition, &stopSolverCalledLock); - } - } - - return; -} - - - - -pthread_mutex_t timerThreadStoppedLock = PTHREAD_MUTEX_INITIALIZER; -pthread_cond_t timerThreadStoppedCondition = PTHREAD_COND_INITIALIZER; - - -/** - * This flag should be set true after the timer thread - * has finished. According to the pthreads_join() main page, - * multiple threads cannot call pthread_join() and so - * we have falled back to the main thread creating the thread, - * joining, and then other threads that want to know can watch this condition - */ -bool timerThreadStopped; -void initTimerThreadStopped() -{ - pthread_mutex_lock(&timerThreadStoppedLock); - timerThreadStopped = false; - pthread_mutex_unlock(&timerThreadStoppedLock); -} - -void setTimerThreadStopped() -{ - pthread_mutex_lock(&timerThreadStoppedLock); - timerThreadStopped = true; - pthread_cond_broadcast(&timerThreadStoppedCondition); - pthread_mutex_unlock(&timerThreadStoppedLock); -} - -void waitForTimerThreadStopped() -{ - - pthread_mutex_lock(&timerThreadStoppedLock); - - while(1) - { - if(timerThreadStopped == true) - { - pthread_mutex_unlock(&timerThreadStoppedLock); - return; - } else - { - pthread_cond_wait(&timerThreadStoppedCondition, &timerThreadStoppedLock); - } - } - - return; -} - - - - /** - * use only one instance - * - */ -class RunSolver { -private: - class Limit { - private: - int resource; - rlim_t limit; - protected: - const char *name, *unit; // name and unit of this limit - int scale; // call setrlimit() with limit<> scale; - } - }; - -public: - /** - * This is a fake limit. It doesn't enforce anything by its own. - */ - class SoftVSIZELimit: public Limit { - public: - SoftVSIZELimit(rlim_t size) : - Limit(-1) { - name = "VSIZE limit (soft limit, will send SIGTERM then SIGKILL)"; - unit = "KiB"; - scale = 10; - setLimit(size); - } - }; - - class HardVSIZELimit: public Limit { - public: - HardVSIZELimit(rlim_t size) : - Limit(RLIMIT_AS) { - name = - "VSIZE limit (hard limit, stack expansion will fail with SIGSEGV, brk() and mmap() will return ENOMEM)"; - unit = "KiB"; - scale = 10; - setLimit(size); - } - }; - - /** - * This is a fake limit. It doesn't enforce anything by its own. - */ - class SoftCPULimit: public Limit { - public: - SoftCPULimit(rlim_t cpuTime) : - Limit(-1) { - name = "CPUTime limit (soft limit, will send SIGTERM then SIGKILL)"; - unit = "seconds"; - setLimit(cpuTime); - } - }; - - /** - * This is a fake limit. It doesn't enforce anything by its own. - */ - class WallClockLimit: public Limit { - public: - WallClockLimit(rlim_t wallClockTime) : - Limit(-1) { - name = - "wall clock limit (soft limit, will send SIGTERM then SIGKILL)"; - unit = "seconds"; - setLimit(wallClockTime); - } - }; - - class HardCPULimit: public Limit { - public: - HardCPULimit(rlim_t cpuTime) : - Limit(RLIMIT_CPU) { - name = "CPUTime limit (hard limit, will send SIGXCPU)"; - unit = "seconds"; - setLimit(cpuTime); - } - }; - - class FileSizeLimit: public Limit { - public: - FileSizeLimit(rlim_t fileSize) : - Limit(RLIMIT_FSIZE) { - name = "FSIZE limit"; - unit = "KiB"; - scale = 10; - setLimit(fileSize); - } - }; - - class NumberOfFilesLimit: public Limit { - public: - NumberOfFilesLimit(rlim_t nbFiles) : - Limit(RLIMIT_NOFILE) { - name = "NbFILES limit"; - unit = "files"; - setLimit(nbFiles); - } - }; - - class StackSizeLimit: public Limit { - public: - /** - * won't enforce limit - */ - StackSizeLimit() : - Limit(RLIMIT_STACK) { - name = "StackSize limit"; - unit = "KiB"; - scale = 10; - } - - /** - * will enforce limit - */ - StackSizeLimit(rlim_t size) : - Limit(RLIMIT_STACK) { - name = "Stack size limit"; - unit = "KiB"; - scale = 10; - setLimit(size); - } - }; - - static const unsigned long int clockTicksPerSecond; - -private: - // when the solver uses less than cpuUsageThreshold % of the CPU, - // try to identify process of other users which use more than - // heavyProcessThreshold % of the CPU - static const float cpuUsageThreshold = 0.8; // % of CPU - static const float heavyProcessThreshold = 0.1; // % of CPU - - pid_t childpid; // pid of the process we're watching - //Improperly synchronized so no longer used: bool solverIsRunning; - - ProcessTree *procTree, *lastProcTree; // to gather data about a process and its children - ProcessHistory procHistory; // history of the last procTree - -#ifdef WATCHSYSCALLS - // list of processes we're watching - ProcessList childrenList; -#endif - - // list of all tasks created by the solver. Only updated where - // cleanupSolverOwnIPCQueues is set - set listAllProcesses; - - float limitCPUTime; // CPU time accorded to solver before we stop it - float limitWallClockTime; // Wall clock time given to solver before we stop it - long limitVSize; // VSize accorded to solver before we stop it - -#ifdef WATCHSYSCALLS - bool InterceptKernelCalls; // should we intercept kernel system calls ? -#endif - -private: - static RunSolver *instance; // instance of RunSolver for signal handlers - - clock_t start, stop; - - // used in timerThread - float maxDisplayPeriod; // max. period between two samplings - - struct timeval starttv, stoptv; - float elapsed; // elapsed time in seconds since the start of the - // watched process. Updated by timerThread - float lastDisplayedElapsedTime; // last elapsed time at which a - // process tree was displayed - struct tms tmp; - -#ifdef WATCHSYSCALLS - class MemOverQuotaCallback : public SyscallCallback - { - public: - MemOverQuotaCallback() {alreadyActivated=false;} - - virtual void operator() () - { - if (!alreadyActivated) - { - alreadyActivated=true; - RunSolver::instance->stopSolver("Mem limit exceeded: sending SIGTERM then SIGKILL"); - } - } - private: - bool alreadyActivated; - }; -#endif - - /** - * signal handler to gather data about the solver - * - * may be called for SIGINT, SIGTERM or SIGALRM - */ -/* static void watcherSigHandler(int signum, siginfo_t *siginfo, - void *ucontext) { - - cout << "" -#ifdef debug - cout << "signal handler called for " << getSignalName(signum) << endl; -#endif - -#ifdef disabled - // if we don't use the SA_NOCLDSTOP flag for sigaction() when - // registering the handler for SIGCHLD, we get a SIGCHLD each time - // the traced process is stopped by a sigtrap. These must be - // ignored. The code below ignores all SIGCHLD including the one - // which let us know that the process we're watching has - // terminated (despite the fact that this is something we want to - // know). For this reason, this code is disabled and we use - // SA_NOCLDSTOP - - if (signum==SIGCHLD) - return; -#endif - - if (signum == SIGTERM || signum == SIGINT) { - instance->stopSolver("Received SIGTERM or SIGINT, killing child"); - return; - } - } -*/ - - - /** - * This function will handle signal requests synchronously. - */ - static void * synchronousSignalHandler(void *arg) - { - - int err, signo; - - sigset_t mask; - - - sigemptyset(&mask); - getSignalMask(mask); - - while(1) - { - err = sigwait(&mask, &signo); - - -#ifdef debug - cout << "signal handler called for " << getSignalName(signum) << endl; -#endif - - if( err != 0 ) - { - cout << "Error occurred while calling sigwait"; - exit(SIGWAIT_RETURN_ERRORCODE); - } - -#ifdef disabled - // if we don't use the SA_NOCLDSTOP flag for sigaction() when - // registering the handler for SIGCHLD, we get a SIGCHLD each time - // the traced process is stopped by a sigtrap. These must be - // ignored. The code below ignores all SIGCHLD including the one - // which let us know that the process we're watching has - // terminated (despite the fact that this is something we want to - // know). For this reason, this code is disabled and we use - // SA_NOCLDSTOP - - if (signo==SIGCHLD) - continue; -#endif - switch(signo) - { - case SIGINT: - case SIGTERM: - cout << "Recieved signal " << getSignalName(signo) << " setting flag."; - setTimerThreadShouldShutdown(true); - instance->stopSolver("Received SIGTERM or SIGINT, killing child"); - return NULL; - /*if (signum == SIGTERM || signum == SIGINT) { - instance->stopSolver("Received SIGTERM or SIGINT, killing child"); - return; - }*/ - } - - - } - return NULL; - - } - - - - /** - * procedure run by the thread in charge of gathering data about the - * watched process - */ - static void *timerThread(void *) { - struct timespec d, delay = { 0, 100000000 }; // 0.1 s - struct timeval tv; - float displayPeriod, nextDisplayTime = 0; - int count = 0; - - displayPeriod = 0.1; - - while (getSolverIsRunning()) - { - - d = delay; - - // try to compensate possible delays - d.tv_nsec -= ((tv.tv_usec - instance->starttv.tv_usec + 1000000) - % 100000) * 1000; - - - - //We nano sleep but poll for shutdown. - // wait (use a loop in case of an interrupt) - - - while (nanosleep(&d, &d) == -1 && errno == EINTR) - { - //This is on purpose; - } - if(getTimerThreadShouldShutdownFlag()) - { - break; - } - - - - // get currenttime - gettimeofday(&tv, NULL); - instance->elapsed = tv.tv_sec + tv.tv_usec / 1E6 - - instance->starttv.tv_sec - - instance->starttv.tv_usec / 1E6; - - if (count == 10) { - // identify new children - if (instance->readProcessData(true)) { - setSolverIsRunning(false); - cout << "Root process has terminated, timer thread aborting"; - break; - } - count = 0; - } else { - // simple update - if (instance->readProcessData(false)) { - setSolverIsRunning(false); - cout << "Root process has terminated, timer thread aborting"; - break; - } - count++; - } - - if (instance->elapsed >= nextDisplayTime) { - - instance->displayProcessData(); - nextDisplayTime += displayPeriod; - displayPeriod = std::min(2 * displayPeriod, - instance->maxDisplayPeriod); - //cout << "displayPeriod=" << displayPeriod << endl; - //cout << "nextDisplayTime=" << nextDisplayTime << endl; - } - } - - if(getTimerThreadShouldShutdownFlag()) - { - cout << "Timer Thread detected shutdown flag, shutting down"; - - } - - setTimerThreadStopped(); - return NULL; // meaningless - } - - /** - * gather data about the watched processes - * - * if updateChildrenList is false, only update process informations - * but don't try to identify new processes (faster), if - * updateChildrenList is true, identify all processes which are a - * children of a watched process. - * - * @return true iff the main process was terminated - */ - bool readProcessData(bool updateChildrenList = true) { - if (!childpid) - return false; // trying to collect data before parent got the pid - -#ifdef debug - cout << "Reading process data" << endl; -#endif - - lastProcTree = procTree; - procTree = new ProcessTree(*procTree); - - procTree->setElapsedTime(elapsed); - - if (updateChildrenList) { - procTree->readProcesses(); - if (cleanupSolverOwnIPCQueues) - procTree->listProcesses(listAllProcesses); - } else - procTree->updateProcessesData(); - - if (procTree->rootProcessEnded()) { - delete procTree; - procTree = lastProcTree; - return true; // don't go any further - } - - lastCPUTime = currentCPUTime; - lastSystemTime = currentSystemTime; - lastUserTime = currentUserTime; - lastVSize = currentVSize; - - procTree->currentCPUTime(currentUserTime, currentSystemTime); - currentCPUTime = currentUserTime + currentSystemTime; - - currentVSize = procTree->currentVSize(); - - maxVSize = max(maxVSize, currentVSize); - - if (currentCPUTime < lastCPUTime) { - lostCPUTime += lastCPUTime - currentCPUTime; - lostSystemTime += lastSystemTime - currentSystemTime; - lostUserTime += lastUserTime - currentUserTime; - - cout << "\n############\n# WARNING:\n" - << "# current cumulated CPU time (" << currentCPUTime - << " s) is less than in the last sample (" << lastCPUTime - << " s)\n" - << "# The time of a child was probably not reported to its father.\n" - << "# (see the two samples below)\n" - << "# Adding the difference (" - << lastCPUTime - currentCPUTime - << " s) to the 'lost time'.\n"; - - if (lastProcTree) { - lastProcTree->dumpProcessTree(cout); - lastProcTree->dumpCPUTimeAndVSize(cout, lastCPUTime, lastVSize); - } - - procTree->dumpProcessTree(cout); - procTree->dumpCPUTimeAndVSize(cout, currentCPUTime, currentVSize); - - cout << "#\n############\n" << endl; - } - - procHistory.push(procTree); - - tStamp.setCPUtimeFromAnotherThread(currentCPUTime + lostCPUTime); - - if (limitCPUTime && currentCPUTime + lostCPUTime >= limitCPUTime) { - cout << "runsolver_max_cpu_time_exceeded" << endl; - stopSolver( "Maximum CPU time exceeded: sending SIGTERM then SIGKILL"); - } - - if (limitWallClockTime && elapsed >= limitWallClockTime) { - stopSolver("Maximum wall clock time exceeded: sending SIGTERM then SIGKILL"); - } - - if (limitVSize && currentVSize >= limitVSize) { - cout << "runsolver_max_memory_limit_exceeded" << endl; - stopSolver("Maximum VSize exceeded: sending SIGTERM then SIGKILL"); - } - - writeCPUTimeToSocket(currentCPUTime); - - return false; - } - - /** - * display the data we have about all watched processes - * - */ - void displayProcessData() { - lastDisplayedElapsedTime = procTree->getElapsedTime(); - procTree->dumpProcessTree(cout); - procTree->dumpCPUTimeAndVSize(cout, currentCPUTime, currentVSize); - - if (elapsed > 2 && currentCPUTime < cpuUsageThreshold * elapsed) { - // its looks like we're not using all CPU. Maybe there's another - // heavy process running. Try to identify it. - procTree->dumpHeavyProcesses(cout, heavyProcessThreshold); - } - } - - /** - * Install the signal handlers - */ - void installSignalHandler() { - - - int err; - sigset_t mask; - sigset_t oldmask; - pthread_t tid; - getSignalMask(mask); - err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask); - if(err != 0) - { - cout << "[ERROR] Couldn't block signals, error occurred exiting:" << err << endl << flush; - exit(PTHREAD_SIGMASK_ERROR_CODE); - } - - err = pthread_create(&tid, NULL, synchronousSignalHandler, 0); - - if (err != 0) - { - cout << "[ERROR] Couldn't create signal handling thread, exiting:" << err << endl << flush; - exit(PTHREAD_CREATE_ERROR_CODE); - } - - //sigaddset(&mask); - - - /** - * This was the old code for it, but it is NOT - * safe for a multi-threaded environment. - * See Advanced Programming in Unix Environment 2nd Edition p415 - * (Threads and Signals) - * - * - * - struct sigaction handler; - handler.sa_sigaction = watcherSigHandler; - sigemptyset(&handler.sa_mask); - handler.sa_flags = SA_SIGINFO | SA_NOCLDSTOP | SA_RESTART; - - The SA_RESTART flag tells that system calls which are - interrupted by a signal should be automatically restarted. This - way, we don't have to encapsulate system calls in a loop which - would restart them when they return with - errno=EINTR. Alternatively, we could have used - siginterrupt(). - - The SA_NOCLDSTOP prevent us from getting a SIGCHLD each time a - process is stopped for tracing. - *//* - sigaction(SIGALRM, &handler, NULL); - sigaction(SIGINT, &handler, NULL); - sigaction(SIGTERM, &handler, NULL); - */ - } - - static void getSignalMask(sigset_t& mask) { - sigemptyset(&mask); - sigaddset(&mask, SIGALRM); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGTERM); - } - - vector limits; - - ofstream out; - - // when set, redirect the standard input of the child to this file - char *inputRedirectionFilename; - - // when set, redirect the standard output of the child to this file - char *outputRedirectionFilename; - - // when set, save the most relevant information in an easy to parse - // format consisting of VAR=VALUE lines - char *varOutputFilename; - - // a class to timestamp each line of some output streams - static TimeStamper tStamp; - - bool timeStamping; // true iff the solver output must be timestamped - int stdoutRedirFD[2]; // pipe for timestamping the solver stdout -#ifdef TIMESTAMPSEPARATESTDOUTANDERR - int stderrRedirFD[2]; // pipe for timestamping the solver stderr -#endif - bool usePTY; // use a PTY to collect output from the solver ? (to - // force the solver to line-buffer its output) - int ptymaster; // PTY master - - bool cleanupSolverOwnIPCQueues; // delete IPC queues that the solver may have created? - bool cleanupAllIPCQueues; // delete all IPC queues that are owned by the user on exit - - pthread_t timeStamperTID; // tid of the thread which does the timestamping - //Moved to run() method - //Moved to run() method - //Removed no longer needed. - - // limits imposed on the size of the solver output (0 if unlimited) - unsigned long long int limitedOutputActivateSize, limitedOutputMaxSize; - - CircularBufferFilter limitedOutputSizeFilter; // a filter to limit - // the solver output - // size - - // time in seconds between a SIGTERM and a SIGKILL sent to children - int delayBeforeKill; - - // maximum cumulated vsize of all children - long maxVSize; - - // current CPU time of the watched process - float currentCPUTime, currentSystemTime, currentUserTime; - - // last CPU time obtained from the watcher - float lastCPUTime, lastUserTime, lastSystemTime; - - // sometimes, the time of a child is not reported to its father - // (e.g. when the father didn't wait its child). We try to detect - // this and cumulate the lost time in these variables - float lostCPUTime, lostUserTime, lostSystemTime; - - long currentVSize; // current VSize of the watched process - long lastVSize; // last VSize of the watched process - - streambuf *coutSaveBuf; // backup of the cout buf (in case of a redirection) - -public: - RunSolver() : - procHistory(10) { - - - maxDisplayPeriod = 60; - - limitCPUTime = 0; // no CPU limit by default - limitWallClockTime = 0; // no CPU limit by default - limitVSize = 0; // no memory limit by default - - delayBeforeKill = 2; - - inputRedirectionFilename = NULL; - outputRedirectionFilename = NULL; - varOutputFilename = NULL; - timeStamping = false; - - maxVSize = 0; - - - -#ifdef WATCHSYSCALLS - InterceptKernelCalls=false; -#endif - - coutSaveBuf = NULL; - - currentCPUTime = 0; - currentUserTime = 0; - currentSystemTime = 0; - - lastCPUTime = 0; - lastUserTime = 0; - lastSystemTime = 0; - - lostCPUTime = 0; - lostUserTime = 0; - lostSystemTime = 0; - - limitedOutputActivateSize = 0; - limitedOutputMaxSize = 0; - - currentVSize = 0; - lastVSize = 0; - - usePTY = false; - cleanupAllIPCQueues = false; - cleanupSolverOwnIPCQueues = false; - - lastProcTree = NULL; - procTree = new ProcessTree(); - procHistory.push(procTree); - - - } - - ~RunSolver() { - // cancel redirection before we leave - if (coutSaveBuf) - cout.rdbuf(coutSaveBuf); - - // procTree and lastProcTree are deleted by ~ProcessHistory() - } - - /** - * use a PTY to collect the solver output - */ - void setUsePTY(bool usePTY) { - this->usePTY = usePTY; - } - - /** - * delete IPC queues that the solver may have created - */ - void setCleanupSolverOwnIPCQueues(bool cleanup) { - cleanupSolverOwnIPCQueues = cleanup; - } - - /** - * delete all IPC queues that were created by this user - */ - void setCleanupAllIPCQueues(bool cleanup) { - cleanupAllIPCQueues = cleanup; - } - - /** - * send the output of the watching process to a given file - */ - void setWatcherOutputFile(char *filename) { - out.open(filename); - coutSaveBuf = cout.rdbuf(out.rdbuf()); - } - - /** - * send the output of the watching process to a given file - */ - void setVarOutputFile(char *filename) { - varOutputFilename = filename; - } - - /** - * redirect the standard input of the solver to a given file - */ - void setInputRedirection(char *filename) { - inputRedirectionFilename = filename; - } - - /** - * redirect the standard output of the solver to a given file - */ - void setOutputRedirection(char *filename) { - outputRedirectionFilename = filename; - } - - /** - * decide if we should timestamp the solver output or not - */ - void setTimeStamping(bool val) { - timeStamping = val; - } - - /** - * decide if we should add an EOF line to the solver output or not - */ - void setTimeStampingAddEOF(bool val) { - if (!timeStamping) - throw runtime_error( - "EOF line can only be added when timestamping is on"); - - tStamp.addEOF(val); - } - - /** - * limit the size of the solver output - */ - void setSolverOutputLimits(unsigned long long int activateSize, - unsigned long long int maxSize) { - if (!timeStamping) - throw runtime_error( - "limit on the output size can only be enforced when timestamping is on"); - - limitedOutputActivateSize = activateSize; - limitedOutputMaxSize = maxSize; - } - - /** - * set the time we should wait between sending a SIGTERM and a - * SIGKILL to a solver we want to stop - */ - void setDelayBeforeKill(int seconds) { - delayBeforeKill = seconds; - } - - void setCPULimit(int sec) { - limitCPUTime = sec; - // SoftCPULimit doesn't enforce anything by its own - addLimit(new SoftCPULimit(sec)); - - addLimit(new HardCPULimit(sec + 30)); - // add an extra delay because we want to stop the solver by - // stopSolver() instead of SIGXCPU - } - - void setWallClockLimit(int sec) { - limitWallClockTime = sec; - // WallClockLimit doesn't enforce anything by its own - addLimit(new WallClockLimit(sec)); - } - - /** - * limits are expressed in kilobytes - * - * soft limit= limit (calls stopSolver()) - * hard limit= limit+reserve (causes immediate SIGKILL) - */ - void setMemLimit(long limit, long reserve) { -#ifdef WATCHSYSCALLS - if (InterceptKernelCalls) - { - addLimit(new SoftVSIZELimit((limit))); - - heapSizeWatcher.setMaximumCumulatedSize(limit); - - // add an extra amount of memory because we want to stop the solver by - // stopSolver() instead of SIGKILL - addLimit(new HardVSIZELimit((limit+reserve))); - } - else -#endif - { - limitVSize = limit; - // SoftVSIZELimit doesn't enforce anything by its own - addLimit(new SoftVSIZELimit((limit))); - - // add an extra amount of memory because we want to stop the solver by - // stopSolver() instead of SIGKILL - addLimit(new HardVSIZELimit((limit + reserve))); - } - } - - /** - * ask to intercept system calls to enforce the policy concerning - * file and network accesses - * - * slows down the solver - */ - void watchSyscalls() { -#ifdef WATCHSYSCALLS - cout << "watching syscalls " << endl; - InterceptKernelCalls=true; -#else - throw runtime_error("disabled function"); -#endif - } - - /** - * add a limit to respect - * - * @parm limit: must be dynamically allocated - */ - void addLimit(Limit *limit) { - limits.push_back(limit); - } - - void printLimits(ostream &s) { - for (size_t i = 0; i < limits.size(); ++i) - limits[i]->output(s); - - if (limitedOutputMaxSize) { - if (timeStamping) - cout << "Solver output will be limited to a maximum of " - << limitedOutputMaxSize << " bytes. The first " - << limitedOutputActivateSize << " bytes and the last " - << limitedOutputMaxSize - limitedOutputActivateSize - << " bytes will be preserved" << endl; - else - cout << "Solver output limit is ignored (currently only " - "available with timestamping)" << endl; - } - - if (timeStamping && usePTY) - cout << "Using a pseudo terminal to collect output from the solver" - << endl; - } - - /** - * select cores that will be available to the process we watch - * - * if physicalView is true, desc contains the id of the cores to be - * used as they are known on the system. In this case, cores 0 and - * 1 (for example) may belong to different processors. - * - * if physicalView is false, desc contains the index of the cores to - * be used in a list of available cores sorted by the processor to - * which they belong. In this case, cores 0 and 1 (for example) will - * necessarily belong to the same processor (unless it has only one - * core!) - */ - void selectCores(const string &desc, bool physicalView) { - vector availableCores, selectedCores; - - getExistingCores(availableCores, physicalView); - - istringstream d(desc); - size_t a, b; - - while (true) { - if (!(d >> a)) { - printCoresListSyntax(); - exit(1); - } - - while (isspace(d.peek())) - d.get(); - - if (d.peek() == '-') { - d.get(); - if (d >> b) { - //cout << "read " << a << "-" << b << endl; - if (b < a) - swap(a, b); - - for (size_t i = a; i <= b; ++i) - if (i >= 0 && i < availableCores.size()) - selectedCores.push_back(availableCores[i]); - } else { - printCoresListSyntax(); - exit(1); - } - } else { - //cout << "read " << a << endl; - selectedCores.push_back(availableCores[a]); - } - - if (d.peek() == ',') - d.get(); - else if (d.peek() == EOF) - break; - else { - printCoresListSyntax(); - exit(1); - } - } - - cpu_set_t mask = affinityMask(selectedCores); - if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) != 0) - perror("sched_setaffinity failed: "); - } - - void printCoresListSyntax() { - cout << "Syntax of a core list:\n" - << " range first-last or individual numbers separated by commas\n" - << " examples: 0-1,5,7 or 0,1,5,7 or 0-7\n" << endl; - } - -#ifdef WATCHSYSCALLS - // for system calls watcher - SyscallsTracer syscallsTracer; - - FileWatcher fileWatcher; - NetworkWatcher networkWatcher; - - //MemOverQuotaCallback memOverQuotaCallback; - //HeapSizeWatcher heapSizeWatcher; -#endif - - void initSignalHandlers() - { - - } - - /** - * run a solver - */ - void run(char **cmd) { - instance = this; - - installSignalHandler(); - initStopSolverInvokedByCallerFlag(); - initTimerThreadStopped(); - initStopSolverShouldExecuteFlag(); - setTimerThreadShouldShutdown(false); - setSolverIsRunning(false); - - childpid = 0; - -#ifdef WATCHSYSCALLS - if (InterceptKernelCalls) - { - syscallsTracer.registerAction(&fileWatcher); - syscallsTracer.registerAction(&networkWatcher); - - //heapSizeWatcher.setOverQuotaHandler(&memOverQuotaCallback); - //syscallsTracer.registerAction(&heapSizeWatcher); - - // intercept system calls that create and destroy processes and - // maintain a list of subprocesses - syscallsTracer.registerAction(new ProcessWatcher(childrenList)); - } -#endif - - if (timeStamping) { - int fd = STDOUT_FILENO; - - if (outputRedirectionFilename) { - fd = open(outputRedirectionFilename, - O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (fd < 0) - throw runtime_error( - string("open failed during output redirection: ") - + strerror(errno)); - } - - int outputFromSolverFD = 0; - - if (usePTY) { - ptymaster = posix_openpt(O_RDWR); - if (ptymaster < 0) { - perror("Failed to create pseudo-terminal"); - exit(1); - } - - outputFromSolverFD = ptymaster; - - if (grantpt(ptymaster) != 0) { - perror("Failed to grant the pseudo-terminal"); - exit(1); - } - - if (unlockpt(ptymaster) != 0) { - perror("Failed to unlock the pseudo-terminal"); - exit(1); - } - } else { - pipe(stdoutRedirFD); -#ifdef TIMESTAMPSEPARATESTDOUTANDERR - pipe(stderrRedirFD); -#endif - } - -#ifdef TIMESTAMPSEPARATESTDOUTANDERR - tStamp.watch(stdoutRedirFD[0],'o',fd); // 'o' as output - tStamp.watch(stderrRedirFD[0],'e',fd);// 'e' as error -#else - - if (usePTY) - outputFromSolverFD = ptymaster; - else - outputFromSolverFD = stdoutRedirFD[0]; - - if (limitedOutputMaxSize) { - limitedOutputSizeFilter.setup(fd, limitedOutputActivateSize, - limitedOutputMaxSize); - tStamp.watch(outputFromSolverFD, &limitedOutputSizeFilter, 0); - } else - tStamp.watch(outputFromSolverFD, 0, fd); -#endif - tStamp.resetTimeStamp(); - - int err = pthread_create(&timeStamperTID, NULL, timeStampThread, - NULL); - if (err) - cout - << "Failed to create a thread to timestamp the solver output" - << endl; - } - - start = times(&tmp); - gettimeofday(&starttv, NULL); - childpid = fork(); - if (childpid < 0) { - perror("fork failed"); - exit(127); - } else if (childpid == 0) { - // child - - // enforce limits - for (size_t i = 0; i < limits.size(); ++i) - limits[i]->enforceLimit(); - - StackSizeLimit stackLimit; - stackLimit.outputEnforcedLimit(cout); - cout << endl; - - // create a new process group (for several reasons, see for - // example ProcessTree::rootProcessEnded()) - setpgid(0, 0); - -#ifdef WATCHSYSCALLS - if (InterceptKernelCalls) - syscallsTracer.childTraceKernelCalls(); -#endif - - if (inputRedirectionFilename) { - int err; - int fd; - - fd = open(inputRedirectionFilename, O_RDONLY); - if (fd < 0) - throw runtime_error( - string("open failed during input redirection: ") - + strerror(errno)); - - err = dup2(fd, STDIN_FILENO); - if (err < 0) - throw runtime_error( - string("dup2 failed during input redirection: ") - + strerror(errno)); - - close(fd); - } - - if (outputRedirectionFilename && !timeStamping) { - int err; - int fd; - - fd = open(outputRedirectionFilename, - O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (fd < 0) - throw runtime_error( - string("open failed during output redirection: ") - + strerror(errno)); - - err = dup2(fd, STDOUT_FILENO); - if (err < 0) - throw runtime_error( - string("dup2 failed during output redirection: ") - + strerror(errno)); - - err = dup2(fd, STDERR_FILENO); - if (err < 0) - throw runtime_error( - string("dup2 failed during output redirection: ") - + strerror(errno)); - - close(fd); - } - - if (timeStamping) { - if (usePTY) { - int err; - int fd; - - char *pts = ptsname(ptymaster); - - if (pts == NULL) - throw runtime_error( - string("Failed to get pty slave name:") - + strerror(errno)); - - fd = open(pts, O_RDWR); - if (fd < 0) - throw runtime_error( - string("open of pty slave failed: ") - + strerror(errno)); - - err = dup2(fd, STDOUT_FILENO); - if (err < 0) - throw runtime_error( - string( - "dup2 failed during output redirection: ") - + strerror(errno)); - - err = dup2(fd, STDERR_FILENO); - if (err < 0) - throw runtime_error( - string( - "dup2 failed during output redirection: ") - + strerror(errno)); - - close(fd); - } else { - // plain tube - - // redirecting stdout and stderr to the write side of the - // pipes to runsolver; close the read side of the pipe which - // belongs to our father - - close(stdoutRedirFD[0]); - dup2(stdoutRedirFD[1], STDOUT_FILENO); - close(stdoutRedirFD[1]); - -#ifdef TIMESTAMPSEPARATESTDOUTANDERR - close(stderrRedirFD[0]); - dup2(stderrRedirFD[1],STDERR_FILENO); - close(stderrRedirFD[1]); -#else - dup2(STDOUT_FILENO, STDERR_FILENO); -#endif - } - } - - // ??? check the way it uses PATH - execvp(cmd[0], cmd); - // only returns when it failed - perror("exec failed"); - - int i = 0; - cerr << "Solver command line was: "; - while (cmd[i]) - cerr << cmd[i++] << ' '; - - cerr << '\n' << endl; - - exit(127); - } else { - // parent - - setSolverIsRunning(true); - - // We don't care about stdin. In case someone writes 'echo - // data | runsolver program', runsolver just closes its stdin - // and the child will be the only one to access it. - close(STDIN_FILENO); - - // let runsolver run on the last allocated core - vector cores; - - getAllocatedCoresByProcessorOrder(cores); - if (cores.size() > 1) { - int tmp = cores.back(); - cores.clear(); - cores.push_back(tmp); - } - - cpu_set_t mask = affinityMask(cores); - if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) != 0) - perror("sched_setaffinity failed: "); - -#ifdef debug - cout << "child has pid " << childpid << endl; -#endif - -#ifdef WATCHSYSCALLS - // add the child to the list of processes we're watching - childrenList.add(childpid); - - if (InterceptKernelCalls) - syscallsTracer.parentTraceKernelCalls(childpid); -#endif - - procTree->setDefaultRootPID(childpid); - - if (timeStamping && !usePTY) { - // the write side of the pipe belongs to the child - close(stdoutRedirFD[1]); -#ifdef TIMESTAMPSEPARATESTDOUTANDERR - close(stderrRedirFD[1]); -#endif - } - - procTree->setElapsedTime(0); - procTree->readProcesses(); - if (cleanupSolverOwnIPCQueues) - procTree->listProcesses(listAllProcesses); - procTree->dumpProcessTree(cout); - - pthread_t timerThreadTID; // tid of the thread which watches the process - int err = pthread_create(&timerThreadTID, NULL, timerThread, NULL); - - if (err) - cout << "Failed to create the timer thread" << endl; - - pthread_t stopSolverThreadTID; // tid of the thread which kills the process - - //We create the stop solver thread now, - //It will wait for some condition variables before actually executing. - err = pthread_create(&stopSolverThreadTID, NULL, waitAndKillSolver,NULL); - if (err) - cout << "Failed to create a thread to stop solver" << endl; - - - int childstatus; - struct rusage childrusage; - int wait4result; - -#ifdef WATCHSYSCALLS - if (InterceptKernelCalls) - syscallsTracer.watchKernelCalls(childpid,childstatus,childrusage); - else -#endif - wait4result = wait4(childpid, &childstatus, 0, &childrusage); - - //This will dispatch the stop solver thread - instance->stopSolver("Solver termination detected (return from wait4)"); - - //This should cause the timer thread to terminate - - cout << "Requesting Timer Thread Should Shutdown \n"; - setTimerThreadShouldShutdown(true); - - cout << "Setting solver is running = false \n"; - instance->stop = times(&instance->tmp); - gettimeofday(&instance->stoptv, NULL); - - setSolverIsRunning(false); - - - //Wait for the timer thread to terminate - cout << "Joining on timer Thread \n"; - pthread_join(timerThreadTID, NULL); - - cout << "Timer Thread Done\n"; - - - - // the program we started has just terminated but some of its - // processes can still be running. Kill them all! - // A kill(-childpid,SIGKILL) alone is not sufficient because - // children may have created their own session - /* - if (procTree) { - procTree->sendSignalNow(SIGKILL); - // just in case a new process was created in between - procTree->sendSignalToProcessGroups(SIGKILL); - } else { - // fallback - - }*/ - - if (timeStamping) { - // wait for the time stamper thread to output the last lines - pthread_join(timeStamperTID, NULL); - } - - cout << "Waiting for stop solver thread\n"; - // wait for the stop solver thread to output the last lines - pthread_join(stopSolverThreadTID, NULL); - - cout << "Sending fallback SIGKILL signal to process group\n"; - - //We no longer do the procees tree approach, instead at this point - //we have already done our best attempt at a clean up. - //So we will just always call kill just in case. - kill(-childpid, SIGKILL); - - - cout << endl; - - cout << "Solver just ended. Dumping a history of the " - "last processes samples" << endl; - - procHistory.dumpHistory(cout, lastDisplayedElapsedTime); - cout << endl; - - if (WIFEXITED(childstatus)) - cout << "Child status: " << WEXITSTATUS(childstatus) << endl; - else if (WIFSIGNALED(childstatus)) { - int sig = WTERMSIG(childstatus); - - cout << "Child ended because it received signal " << sig << " (" - << getSignalName(sig) << ")" << endl; - -#ifdef WCOREDUMP - if (WCOREDUMP(childstatus)) - cout << "Child dumped core" << endl; -#endif - - } else if (WIFSTOPPED(childstatus)) { - cout << "Child was stopped by signal " - << getSignalName(WSTOPSIG(childstatus)) << endl; - } else { - cout << "Child ended for unknown reason !!" << endl; - } - - float realtime; // Elapsed real milliseconds - float virtualtime; // Elapsed virtual (CPU) milliseconds - float virtualUserTime; // Elapsed virtual (User CPU) milliseconds - float virtualSystemTime; // Elapsed virtual (System CPU) milliseconds - - //realtime=(stop-start)*1000/(float)clockTicksPerSecond; - - realtime = stoptv.tv_sec * 1000 + stoptv.tv_usec / 1000.0 - - starttv.tv_sec * 1000 - starttv.tv_usec / 1000.0; - -#if 1 - // use wait4 data - virtualtime = childrusage.ru_utime.tv_sec * 1000 - + childrusage.ru_utime.tv_usec / 1000.0 - + childrusage.ru_stime.tv_sec * 1000 - + childrusage.ru_stime.tv_usec / 1000.0; - - virtualUserTime = childrusage.ru_utime.tv_sec * 1000 - + childrusage.ru_utime.tv_usec / 1000.0; - - virtualSystemTime = childrusage.ru_stime.tv_sec * 1000 - + childrusage.ru_stime.tv_usec / 1000.0; - - // don't get fooled - if (currentCPUTime > virtualtime / 1000 - || virtualtime / 1000 > currentCPUTime + 60) { - cout << "\n# WARNING:\n" - << "# CPU time reported by wait4() is probably wrong !\n" - << "# wait4(...,&childrusage) returns " << wait4result; - - if (wait4result < 0) - cout << " (errno=" << errno << ", \"" << strerror(errno) - << "\")"; - - cout << " and gives\n" << "# childrusage.ru_utime.tv_sec=" - << childrusage.ru_utime.tv_sec - << "\n# childrusage.ru_utime.tv_usec=" - << childrusage.ru_utime.tv_usec - << "\n# childrusage.ru_stime.tv_sec=" - << childrusage.ru_stime.tv_sec - << "\n# childrusage.ru_stime.tv_usec=" - << childrusage.ru_stime.tv_usec << endl; - - cout << "# CPU time returned by wait4() is " - << virtualtime / 1000 << endl - << "# while last known CPU time is " << currentCPUTime - << "\n" << "#\n"; - - if (currentCPUTime > virtualtime / 1000) - cout - << "# Solver probably didn't/couldn't wait for its children" - << endl; - - cout << "# Using CPU time of the last sample as value...\n"; - cout << endl; - - virtualtime = currentCPUTime * 1000; - virtualUserTime = currentUserTime * 1000; - virtualSystemTime = currentSystemTime * 1000; - } -#else - // use the watcher data - virtualtime=currentCPUTime*1000; - virtualUserTime=currentUserTime*1000; - virtualSystemTime=currentSystemTime*1000; -#endif - - if (lostCPUTime != 0) { - cout << endl << "# WARNING:" << endl; - cout - << "# The CPU time of some children was not reported to their father\n" - << "# (probably because of a missing or aborted wait())." - << endl - << "# This 'lost CPU time' is added to the watched process CPU time." - << endl; - cout << "# lost CPU time (s): " << lostCPUTime << endl; - cout << "# lost CPU user time (s): " << lostUserTime << endl; - cout << "# lost CPU system time (s): " << lostSystemTime - << endl << endl; - - virtualtime += lostCPUTime * 1000; - virtualUserTime += lostUserTime * 1000; - virtualSystemTime += lostSystemTime * 1000; - } - - //cout << "Real time (s): " << realtime/1000.0 << endl; - cout << "Real time (s): " << realtime / 1000.0 << endl; - cout << "runsolver_walltime: " << realtime / 1000.0 << endl; - cout << "CPU time (s): " << virtualtime / 1000.0 << endl; - cout << "runsolver_cputime: " << virtualtime / 1000.0 << endl; - cout << "CPU user time (s): " << virtualUserTime / 1000.0 << endl; - cout << "CPU system time (s): " << virtualSystemTime / 1000.0 - << endl; - cout << "CPU usage (%): " - << 100 * virtualtime - / (float) ((realtime != 0) ? realtime : 1) << endl; - cout << "Max. virtual memory (cumulated for all children) (KiB): " - << maxVSize << endl; - - if (cleanupAllIPCQueues || cleanupSolverOwnIPCQueues) - cleanupIPCMsgQueues(); - - struct rusage r; - getrusage(RUSAGE_CHILDREN, &r); - - cout << endl; - cout << "getrusage(RUSAGE_CHILDREN,...) data:" << endl; - cout << "user time used= " - << r.ru_utime.tv_sec + r.ru_utime.tv_usec * 1E-6 << endl; - cout << "system time used= " - << r.ru_stime.tv_sec + r.ru_stime.tv_usec * 1E-6 << endl; - cout << "maximum resident set size= " << r.ru_maxrss << endl; - cout << "integral shared memory size= " << r.ru_ixrss << endl; - cout << "integral unshared data size= " << r.ru_idrss << endl; - cout << "integral unshared stack size= " << r.ru_isrss << endl; - cout << "page reclaims= " << r.ru_minflt << endl; - cout << "page faults= " << r.ru_majflt << endl; - cout << "swaps= " << r.ru_nswap << endl; - cout << "block input operations= " << r.ru_inblock << endl; - cout << "block output operations= " << r.ru_oublock << endl; - cout << "messages sent= " << r.ru_msgsnd << endl; - cout << "messages received= " << r.ru_msgrcv << endl; - cout << "signals received= " << r.ru_nsignals << endl; - cout << "voluntary context switches= " << r.ru_nvcsw << endl; - cout << "involuntary context switches= " << r.ru_nivcsw << endl; - cout << endl; - - if (varOutputFilename) { - ofstream var(varOutputFilename); - - var << "# WCTIME: wall clock time in seconds\n" << "WCTIME=" - << realtime / 1000.0 << endl; - - var << "# CPUTIME: CPU time in seconds\n" << "CPUTIME=" - << virtualtime / 1000.0 << endl; - - var << "# USERTIME: CPU time spent in user mode in seconds\n" - << "USERTIME=" << virtualUserTime / 1000.0 << endl; - - var - << "# SYSTEMTIME: CPU time spent in system mode in seconds\n" - << "SYSTEMTIME=" << virtualSystemTime / 1000.0 << endl; - - var << "# CPUUSAGE: CPUTIME/WCTIME in percent\n" << "CPUUSAGE=" - << 100 * virtualtime - / (float) ((realtime != 0) ? realtime : 1) - << endl; - - var << "# MAXVM: maximum virtual memory used in KiB\n" - << "MAXVM=" << maxVSize << endl; - } - - getrusage(RUSAGE_SELF, &r); - cout << "runsolver used " - << r.ru_utime.tv_sec + r.ru_utime.tv_usec * 1E-6 - << " second user time and " - << r.ru_stime.tv_sec + r.ru_stime.tv_usec * 1E-6 - << " second system time\n" << endl; - - cout << "The end" << endl; - } - } - - //bool stopSolverStarted; - - /** - * properly stop a solver - * - * to be used when the caller cannot wait (for example from a - * system call callback). - */ - void stopSolver(const char *msg) { - - if(!compareAndSetStopSolverInvokedByCallerFlag(false, true)) - { - //*Stop solver was already called; - return; - } - - - /** - * We want the timer thread to shutdown, prior to the stopSolverThread continuing. - */ - setTimerThreadShouldShutdown(true); - - /*The solver will first receive a SIGTERM to give it a chance to - output the best solution it found so far (in the case of an - optimizing solver). A few seconds later, the program will receive a - SIGKILL signal from the controlling program to terminate the - solver.*/ - - cout << "\n\n\nShutdown Reason:\n" << msg << endl; - - - // (Previously sent SIGTERM as soon as possible, not sure why) - // Now we just start the other thread. The other thread won't fire until the - //timer thread is dead. - setStopSolverShouldExecuteFlag(); - } - - /** - * procedure run by the thread in charge of killing the solver - */ - static void *waitAndKillSolver(void *) { - cout << "Stop Solver Thread: Started\n"; - waitForStopSolverShouldExecuteFlag(); - cout << "Stop Solver Thread: Should Execute\n"; - waitForTimerThreadStopped(); - cout << "Stop Solver Thread: Timer Thread Stopped\n"; - - // give some evidence to the user - RunSolver::instance->readProcessData(); - RunSolver::instance->displayProcessData(); - - RunSolver::instance->sendSIGTERM(); - cout << "Stop Solver Thread: SIGTERM sent, waiting and sending SIGKILL\n"; - RunSolver::instance->waitAndSendSIGKILL(); - cout << "Stop Solver Thread: DONE\n"; - return NULL; - } - - /** - * procedure run by the thread in charge of timestamping the solver - * output stream - */ - static void *timeStampThread(void *) { - tStamp.timeStampLines(); // enless loop - - return NULL; - } - - void sendSIGTERM() { -#ifdef SENDSIGNALBOTTOMUP - cout << "\nSending SIGTERM to process tree (bottom up)" << endl; - procTree->sendSignalBottomUp(SIGTERM); -#else - // signal the whole group - pid_t pidToSignal=-childpid; - - cout << "\nSending SIGTERM to " << pidToSignal << endl; - kill(pidToSignal,SIGTERM); -#endif - } - - void waitAndSendSIGKILL() { - struct timespec delay = { delayBeforeKill, 0 }; - - cout << "Sleeping " << delayBeforeKill << " seconds" << endl; - - // use a loop in case of an interrupt - while (getSolverIsRunning() && nanosleep(&delay, &delay) == -1 - && errno == EINTR) - ; - - if (!getSolverIsRunning()) - return; - -#ifdef SENDSIGNALBOTTOMUP - cout << "\nSending SIGKILL to process tree (bottom up)" << endl; - procTree->sendSignalBottomUp(SIGKILL); -#endif - - // signal the whole group, in case we missed a process - pid_t pidToSignal = -childpid; - - cout << "\nSending SIGKILL to " << pidToSignal << endl; - kill(pidToSignal, SIGKILL); - } - - /** - * delete IPC queues that may have been created by the solver - */ - void cleanupIPCMsgQueues() { -#ifndef __linux__ -#error This code is linux specific -#endif - - struct msginfo msginfo; - struct msqid_ds msgqueue; - int maxid, msqid; - uid_t myUid = geteuid(); - - cout << "\n"; - - maxid = msgctl(0, MSG_INFO, - reinterpret_cast(&msginfo)); - if (maxid < 0) - return; - - for (int id = 0; id <= maxid; ++id) { - msqid = msgctl(id, MSG_STAT, &msgqueue); - - if (msqid < 0) - continue; - - if (msgqueue.msg_perm.cuid == myUid - && (cleanupAllIPCQueues - || listAllProcesses.find(msgqueue.msg_lspid) - != listAllProcesses.end() - || listAllProcesses.find(msgqueue.msg_lrpid) - != listAllProcesses.end())) { - cout << "deleting IPC queue " << msqid; - if (msgctl(msqid, IPC_RMID, &msgqueue)) - cout << " (failed: " << strerror(errno) << ")"; - cout << "\n"; - } - } - } -}; - -// static members -RunSolver *RunSolver::instance = NULL; -const unsigned long int RunSolver::clockTicksPerSecond = sysconf(_SC_CLK_TCK); -TimeStamper RunSolver::tStamp; - -static struct option longopts[] = { - { "cpu-limit", required_argument, NULL, 'C' }, { "wall-clock-limit", - required_argument, NULL, 'W' }, { "mem-limit", - required_argument, NULL, 'M' }, { "stack-limit", - required_argument, NULL, 'S' }, { "output-limit", - required_argument, NULL, 'O' }, { "input", required_argument, - NULL, 'i' }, { "delay", required_argument, NULL, 'd' }, { - "help", no_argument, NULL, 'h' }, { "watcher-data", - required_argument, NULL, 'w' }, { "var", required_argument, - NULL, 'v' }, { "solver-data", required_argument, NULL, 'o' }, { - "timestamp", no_argument, NULL, 1000 }, { "watch-syscalls", - no_argument, NULL, 1001 }, - { "use-pty", no_argument, NULL, 1002 }, { "cleanup-own-ipc-queues", - no_argument, NULL, 1003 }, { "cleanup-all-ipc-queues", - no_argument, NULL, 1004 }, { "cores", required_argument, NULL, - 1005 }, { "phys-cores", required_argument, NULL, 1006 }, { - "add-eof", no_argument, NULL, 1007 }, { NULL, no_argument, NULL, - 0 } }; - -void usage(char *prgname) { - cout << "Usage: " << prgname << endl - << " [-w file | --watcher-data file]\n" - << " [-v file | --var file]\n" - << " [-o file | --solver-data file]\n" - << " [--cores first-last]\n" - << " [-C cpu-limit | --cpu-limit cpu-limit]\n" - << " [-W time-limit | --wall-clock-limit time-limit]\n" - << " [-M mem-limit | --mem-soft-limit mem-limit]\n" - << " [-S stack-limit | --stack-limit stack-limit]\n" - << " [-d delay | --delay d]\n" - << " [--input filename]\n" << " [--timestamp]\n" - << " [-O start,max | --output-limit start,max]\n" - << " [--use-pty]\n" - << " [--cleanup-own-ipc-queues | --cleanup-all-ipc-queues]\n" -#ifdef WATCHSYSCALLS - << " [--watch-syscalls]\n" -#endif - << " command\n" << endl; - - cout << "The mem-limit must be expressed in mega-bytes" << endl; - cout << "The stack-limit must be expressed in mega-bytes" << endl; - cout << "The cpu-limit must be expressed in seconds (CPU time)" << endl; - cout << "The time-limit must be expressed in seconds (wall clock time)" - << endl; - cout << "When the time or memory limit is exceeded, the watching " - << "process will try to send a SIGTERM and after " - << "seconds will send a SIGKILL to the watched process" << endl; - cout << "-w filename or --watcher-data filename\n" - << " sends the watcher informations to filename" << endl; - cout << "-v filename or --var filename\n" - << " save the most relevant information (times,...) in an easy to parse VAR=VALUE file" - << endl; - cout << "-o filename or --solver-data filename\n" - << " redirects the solver output (both stdout and stderr) to filename\n" - << "--input filename\n" - << " redirects the standard input of the runned program to filename\n" - << "--timestamp\n" - << " instructs to timestamp each line of the solver standard output and\n" - << " error files (which are then redirected to stdout)\n" - << "--add-eof\n" - << " when timestamps are used, request to add an 'EOF' line at the end of the solver output\n" - << "--output-limit start,max or -O start,max:\n" - << " limits the size of the solver output.\n" - << " Currently implies --timestamp. The solver output will be limited\n" - << " to a maximum of MiB. The first MiB will be\n" - << " preserved as well as the last MiB.\n" - << "--phys-cores list\n" - << " allocate a subset of the cores to the solver. The list contains\n" - << " core numbers separated by commas, or ranges first-last. This list\n" - << " must contain core identifiers as they are known by the system in\n" - << " /proc/cpuinfo.\n" << "--cores list\n" - << " allocate a subset of the cores to the solver. The list contains\n" - << " core numbers separated by commas, or ranges first-last. This list\n" - << " contains the index of the selected cores in a list of core identifiers\n" - << " sorted by the processor they belong to. For example, if processor 0\n" - << " contains cores 0, 2, 4, 6 and processor 1 contains cores 1, 3, 5, 7,\n" - << " the sorted list is 0, 2, 4, 6, 1, 3, 5, 7 and the argument 0-3 will select\n" - << " the 4 cores of processor 0 (with physical id 0, 2, 4, 6). This option\n" - << " allows to ignore the details of the core numbering scheme used by the kernel.\n" - << "--use-pty\n" - << " use a pseudo-terminal to collect the solver output. Currently only\n" - << " available when lines are timestamped. Some I/O libraries (including\n" - << " the C library) automatically flushes the output after each line when\n" - << " the standard output is a terminal. There's no automatic flush when\n" - << " the standard output is a pipe or a plain file. See setlinebuf() for\n" - << " some details. This option instructs runsolver to use a\n" - << " pseudo-terminal instead of a pipe/file to collect the solver\n" - << " output. This fools the solver which will line-buffer its output.\n" - << "--cleanup-own-ipc-queues\n" - << " on exit, delete IPC queues that the user owns and to which the solver\n" - << " was the last process to read/write [may fail to delete some queues]\n" - << "--cleanup-all-ipc-queues\n" - << " on exit, delete all IPC queues that the user created [will also delete\n" - << " queues that don't belong to the solver]\n" << endl; -#ifdef WATCHSYSCALLS - cout << "--watch-syscalls intercepts some kernel system calls and checks\n" - << " that the program doesn't make forbidden calls (slows down the\n" - << " execution of the solver)." - << endl; -#endif - - exit(1); -} - -int main(int argc, char **argv) { - RunSolver solver; - int optc; - - // memLimit in KiB - long memLimit = 0; - // difference between the 'hard' and the 'soft' limit (in KiB) - int memSoftToHardLimit = 50 * 1024; - - string cmdline; - for (int i = 0; i < argc; ++i) { - cmdline += argv[i]; - cmdline += ' '; - } - - try { - ios_base::sync_with_stdio(); - char* set_affinity; - while ((optc = getopt_long(argc, argv, "+o:w:v:C:W:M:S:O:d:h", longopts, - NULL)) != EOF) { - switch (optc) { - case 'o': - solver.setOutputRedirection(optarg); - break; - case 'i': - solver.setInputRedirection(optarg); - break; - case 'w': - solver.setWatcherOutputFile(optarg); - break; - case 'v': - solver.setVarOutputFile(optarg); - break; - case 'M': - memLimit = atol(optarg) * 1024; - break; - case 'C': - solver.setCPULimit(atoi(optarg)); - break; - case 'W': - solver.setWallClockLimit(atoi(optarg)); - break; - case 'S': - solver.addLimit( - new RunSolver::StackSizeLimit(atoi(optarg) * 1024)); - break; - case 'd': - solver.setDelayBeforeKill(atoi(optarg)); - break; - case 'O': - int activate, max; - if (sscanf(optarg, "%d,%d", &activate, &max) != 2 - || activate >= max) { - cout << "Syntax: --output-limit A,M with A cores; - - getAllocatedCoresByProcessorOrder(cores); - - cout << "running on " << cores.size() << " cores: "; - printAllocatedCores(cout, cores); - cout << "\n\n"; - - if (memLimit) - solver.setMemLimit(memLimit, memSoftToHardLimit); - - solver.printLimits(cout); - - solver.run(&argv[optind]); - } catch (exception &e) { - cout.flush(); - cerr << "\n\tUnexpected exception in runsolver:\n"; - cerr << "\t" << e.what() << endl; - exit(1); - } -} - -/* - setitimer(ITIMER_PROF,...) to receive SIGPROF regularly - - alarm to receive SIGALRM at the timeout ??? - */ - -// Local Variables: -// mode: C++ -// End: diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/runsolver.d b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/runsolver.d deleted file mode 100644 index c6d0f7e1c..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/runsolver.d +++ /dev/null @@ -1,3 +0,0 @@ -runsolver.d runsolver.o: runsolver.cc Cores.hh SignalNames.hh ProcessList.hh \ - CircularBufferFilter.hh TimeStamper.hh ProcessTree.hh ProcessData.hh \ - ProcessHistory.hh aeatk.c diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/runsolver.o b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/runsolver.o deleted file mode 100644 index 636833a153ed1fcfc7889f07cbb3f3ae9fc5b61d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176416 zcmeFa3w%`7y)V8)G7u2iQKL;gR=0IHCYoTPSP7tJ$PDZa6G#cK`ht*5Ajgp8Br^dk zFP(|7&vuZWqphc>^>|KgYkNFB?X3?QEhfAIwzYs&v9=c93_(Pw2&m+Kf4{Z%V4WlzxP^u?Ny=hpFS;M|r(pULTa#Z_Dey$m@6I^&xqE7+3lQW>qBm1LmJ+>*jB2jJiG1^ZEkn zB^8SPpkB0D&)TYYz8bw!H-E322ZQFj`>0B1_Z_cuB0er>ee|`PZw=h?Eaj)@58Z9n zt?|0~e9+u<^R2oyEO3i%DgApJ)JuYjqIdS!=;p`KOSlEyd`&kuac#cez)fscFHv;M zM`_(!wokvSmy*^}E&hB~da(*6FsE#HV76g?7?_12d|IQ&59IYtYZ#+mLYP#PaQT~R z;@^;%q&_vaG^jpRKkuY&zAt)DM*XU8_I435ZVUR=63z~r?;ul@Fn@1YA;qv(|X};B`Mpmm&eW2fUvOils zH!!D*CMshJH0!pJ8TJPx_ zaYQ{Q>>tU%1$lBf$;AwFi!5mq!)sy50$s!1W_opVGgmsI<^}yDb@NZ^P2FxQX-$;v zFsug_=hEOiTXE5gcIvy1>E%6Y{0LeMCu>1eTh#bVxS5t5ON2FXArn>y2rGK^$@sRc z@{gi3_0Ao!7Nh(RYT`bWB+9v(Nc*NV(!NBJTrWubl0iuOl~fDJ&Y>Hkv(4?_M?1lr zh|osf6o_|ch01%PZ=$>CH*|1I9yiJSibnGU6$1`ADz)?PB_UsJ6LQ#mJ(bJdNPbL? z9gYE|#&ceUVwaPYk=TmyV6sMb4MoD9D8{B|dN?1`L%d7n__^uM4lrxib!Nv%#uj64 zxM;u%<#l$)g6MRRjz}g60b$Qw7d2>4vypsaaSq*zw(PQce2b==`yAe*wr%*mK%#tV z4hGL`V;7JEu-md(!+iUb{(cC^_}(mi*M5EYCgKocmFEQc?@-Mb2bLH|B{qo?f_2QT z151?-EcHZ2Dala-3w}SNAeR7>cBj_kNt%3^O%l0aHK}?&f3+W&fboUo(6T!CP2darL13cSjWY|j)#G}?gn)XKULQ}fJQL0 z1vlTiS?Di|3wG{~rFyvt%TW|yp1Ao|)`28Xt+T0ruxZcf%elG_qA>C9$^L#fhf})A z{VGb1_)=xWcWo*F;N(#UiTvCn-xlcRdv}8uEwJlaJkUacxIhpJY(JQ?A}P-K1+YN zS8hlQi~-UG9`RW5BpW=~5lM4TL(leHnV;JGX$C#km>;_!uKf}<$M-jjF|4m~pQ=x~ zj~OI$&&`|vyY;~hxo#KR81(`IkXfz(B2B-}BIC^bl7V|9=qTkd&h`NskkMn;&gl4FRU# zb>LyTSlN1VmLJAmWiF$rY)9YUI`U5MJQ~ezFh}X}Z9{akJy(a4)$cm4z+#L})6MOA z&p!BILD-5f?0YmTx>vVQWyOP1%rVT9f!XkfYXI?LctLvd8GkiKrS}H?rKqIT>sHR_LL_uE;h%>RW!*dy1O6cZ zsOI68^&9472?HwTQbj$P@Gqd27$`I7FYpYhnunw&z6PZhE)x?j`kZvUe>~aicNA>Jw`?yRHk6K|bgiyBgKfPtGiha8E zotz3hMEpWcl)y&@icSd@xBKs*Y#3Rk0c*;>fVJva zFu9<=v)`!u!?FT*zLO}xbwp8h<3thK=%i`|o%@;3*RO zBOa@3F9&rPbqC5`ZCD?$mLBUox=hv6+YPB3i?0f-{Nw(STl}w?pT(8ic-aD z;uFjg;64W2m<3d0^`}><>ux|w*lD;gSMM8x`_67}JD^QJ!Xz7u3j^`@N&`o?W<~4u zyFTWiz|DsFC-W7f=l!Aa*KeF^1wW| zD;Q=+uKFaE?%f2NuU~&*lYZ&WsV3Tx@a+1OJ8KCi@61ZpWEpq$bD#6MyEjC4y1C1o zC3~D~_lUv`$VI19+)ghzbEm>S3CMmIq2D3Nnjv<-b5GUp4Q{_fWWQve%E6wg-}Qm` z+e4xY!^zvTgULIyjk`YOQGV6vd2eVS{w@vkKy_IC-m!L1KaeLpzVbo(k@yUn#~qX& zsD@oe)P~qmjvjw6D-iz#uF@t@1k-5hK^VoheKsfsfo_OiR-Q}LLlH)%mUa74k8&Os zISbVL-a$&tUpf_WKQ_%)sOt_=4QuYP&fQ*GODi>5y>C00BAWG5w~_6B)wg4y$1EZ3%K4Xp_`lf6`WR< z&(BiSO~?{R)G-8Zh18`YT0|14!U-aP>nbScDm^KsTNmovK@1UT5ONOot@S)y zvNw{qj{E-V($r6=+YCWXVwyv?IShzbSSM;2Orewnj!5bVjM(F+_DD6=>(>ZfrFQ-k z^?277QfYRWB`_bs=5!5_(Q&GrIx|sBcHyGf84aFF^}^8fy&!-xx1xk@_KD0t=(ry^ zPkq8Av-xQ@o5ZAfw*y+y4phN8oc#+hFmSf*$Pc1l5T-NkX-HHSAqMcRCmXDm_!5|w z<08;|UYi@Rh_=ml_1+vmH{&R2B#8QC?^cdqw{Ag@;5~}y@A_CT|Dzgz50CX^{$ip_ z4Q-=}MpY)MM;AjsSDCD1g9Wg(r}#QXn~O zzM#MGrat_ZKUWaxh7rAfxvZZp0&X57?RgR>_3IX?7zs0@+gL6wl- zF1}KaqZCgx*RtD?6f4TDe3yzGv{9L6+1hz9z8nda}qev`(V`LS)knHZ5I(2BKD|0RRiZ;ovz@I+ReoF#ga z%TsE;tS9eh`;FHEvUR8x7@L*3W!-&kw)|sqA(PW;g<;ythADJwoa`&xwxu#`-=YYn zEKl@F^g530D_-Y`qgPnI%h5N>sh{C5u}`jT0avq$NLFxcH;s-g#elivOvYl~DU8KS zx&3rea4KVQG|H}C?!N>pFC#HKjs(fH8+TGUwi_RX%sH*y=)plAL(=3gwL<%>6|fpx z*=l@k*|`)iarBFBp@_P1`IiLsxc1`Fl^3E~Abya{#ou~M#9mt;>O88h{Z~+WRo%f* zS^s(~mSHY(vy;`0^(D~SpTJ%`3A|cWWGOEbJ3g@EurG8`^cedt8t@__$+N>W;KN;pH29(Lekc&q0 zUjKJ+<)xlN-CEO)8!U#dd62K?ta(J^CrTJlKDoG^CkmkeZq7;gsbiip!@A$Uo|@+>3=dNG`UxTfu@8b& zFW;%F;Z5q8?UX~M)ip!uIVr19*?X)%bJ0KaXaT{Yn>qflxzEUkO=RW8Ph_d<#y~C# zKggPeaE2B2=klHfB{}k316kwXC|{&o7hOHo8u?>{Bq+R-rzU8*C2ZY^_{Z(JM)~{E zNe%0(tfhrj(6g5t)<#+=cC8`9T3PMh*#>^tl$KHXKEv#dbt9fc@gqsFKVL?=DEYKl zu)MuM@2zCZP&aSakS?U*$fBNVvbsG+9)Ojb{QInIjKVB6;#+e_Ey4045V#_w9j5N>0r(bE&*;jr1q;ClR*C>2i7CrBL{P;{sZlRQ9EfNPdrG72VehcY=!}j zTZ>yno1&wP34$KfbzRhzLoxPhK2|*n%ep;|2irNk)K<15^*Y8R>TKusd)ofpHTs;j z%PvfZo1Iz?MM9PCXNpB?-u-?Wr#fbrdN=u~U6-ISwnhae-0{1uxGC9liRV)VJ>Ry93U9D;O1Xd zZxUU0k=OtT+&p(kf-8^x1oCO!{3C{X#p8Y!kRXd^SWa~n+C}Wii~`x}ITQW%cz>@lfJa^V1N-ZuEsl&nx8YN! z2Jc65st&_oK$jU!c^eoMnz8?FXi^7&+5a{iH~jbEFaQTyIpq&RbCBnUZnP^5{@i}* zUZK9yewAr=V*jc)Yp(YO>CF07XLw$7hUYWR@VxO1&+9XuXWlN($jF3$=^38Wepl}= zvwn;B;UMXKknX4ZrOxnt{~4b5o#FX`jOUr~zjub`2Q!{$j^+I`JkKnDs(amfRs(Eg z2#)04BGNxUvKg-QFemmYc5B?Ud3`lAElVzzN49`ZySWLu+}P5##%M%qsBLVD)Q{Jc znmd}Kwac`Y2CY`BYiWyUO^xl*@gtO(k-El6N2FeBZ(P(|+oUO4dn6idY+j@_G}SH| zql_3aqGoX{THms?S(_QDZEtBtrJGwD>#tX?iqhX(wN}28tFuGX zE5D|cU47k{Qv6q@jBXgAgqj;#+Uf{M1@-aY!aJgo_A%P5#Tap8TYJ>b(rTOQwJ36% zYCV}t8=IQ6g%Pb4;6>Uncx8m56*{OYQtBg35ypX0U8((jEE0=o%Ix;qMG@*v{BABO z;o*t?N~rx?(Tel1vAH!C#lxmZbM2By+9Sdakg%jREjQL4DQS(~;pWw~ELj5ZM<^vF z>5Wm2(+1tt*4LLrT7Z|5lGep{v`fHff(asl{<_v!Bd|7x3JJi)^O6$TF+XUE)HcUj zOEB3bjjeShJoI+&liH@Hv?rtMMvu9=;i}P1^eAkwKzeH(l?x<~Qtk>!m*S6KxMI&umW3z_wX-hz0l7wnPv*ynV)J&Tn zm^|5-Zp;D&*~B$tdgbhyGegs50VAl~%oKR5Mg-bO%%ascG&D9hM(?0>d&`nYbTJWI zW4pGrt)+QUu@+g@Nc5q!*Da3J&j-iY6(qSSt~SGJ27+I8xhj2dKQIF4Eo}X$RwK+GsErSW~cUj2Kv}TSCmtOo|!6T~lLo zL>X~2u-_JqwO$Dn+?w7m&?3sqp2urQOm2&S>90cRrM0c%X2fNl--ye!xq+F}jpu#@syn+}+1!XchcVtCmS)|VHuBN4_1H>r#7A-eNw57G}K-Sb2P!q=4(#!*CgP`Jh9-+*_ zxSZQyOLJs|Qn|RX3E~)x5Tm-lhRCUpEop_ECgznnXdLBQS}A)WK$a3b9nmBK7d3W7 zz{Xl(q^+&F<$9%9`?B)oA|Mk}V9yDX8lxHn!cc03?M3L{n+;LXcB*HRT4|zWO{j|| zZ|SyJb33DG{!)k$Dv0JYdUMNwp*=-1d!}jwd!*bL$!V={G{NrHLaP3HP4QNdSxah{ zH7Q# zT~lr2l6JJk_YJkNrYPMJkI;2tQ%l|LkYA)MKxS>V(Z-f$_d!c6I^YSE2MrNZ4`|VZ zbC1p-BUe35Ced$@ZWhqz~qfG}nVmsY<)x zNyeUJ%F*@3qwAIW#&(j8^;$!$xsI@-T-h>y>G+QEmE-4*uNq%7e#ZFv@r%(Kq%;0^ zAp$S9qd0L;dq*>I=IJtr>KHH7XsoZHnMJaZfyjrk2QJC76;rblOh5 zLiUGJH>eHZWTzVdKq2@>ajgzT+9Jv|W3CxPzAAp2)>hjLW?;*VEhVjmbas7XM`L{q z+|?XgvJjI6;%S9O2U1Cz@#H{gQT@m=01SmVUMnprE55e)I%=S_*iDvPhk~KDHe|Qd z)sd91*Oo$#Ml>26%>;zw(ppG*{9y7jQ_$?p3eBvd#*j3o&nnh#hnlA1btHtTFn(z& z$D`0bOA@j;fZ2~?lu#g}o0?+6R@Ih5*J+ZyFIGZi7WOkJJ;FwCq@K#pXl@d1a1X)+W}8@p+|t^i%Ie8Tf*I4!rUk5DXwC>~a#kz! zQvFBbj@@utX! z)KL^Ea3qn~P74bPj1R5X!r~Rm3$&G4URXjT6c;sPs_V5v=8NViD1~RawgW~FBM^FE zF)L~o3?Tm^9^dSC;)GhQwLKE6hcOmugPBPxy#>tK)C4r!1l-WpvV_T0NE)t9e9OO^ zOO^_nTdtLqcx4mky8h11B^_=#Os@UdZKuV3?h$DN%TOtL*@?1lt z>eI$CgW7G27K19dj8{##k0dmn@k8Sb=JyHQ$w%pOpW7e>?u=2hnI*D}+-kB0z&1-F zi)u?)fPf~byk~G{tU=|S2^+k$v>miUviyjBh;peOrR`~uT$~1&Q<0`xV~y+7pSu|5 zepEI>$^nBUv(MouA($ie#axO>nm9$0y&@ehjX5@oieMO$L|`RbkMAWTM&$oZp2g+7sdh&h5DSR$-Wg3}IIP|K1Z z-F9uJ6wOh4kvdOm9!UEQXK#v@b9B-F@^{7k_Jwe7DimRSh$0b(}XSw zDV~asq=?5IwFP^JEg6mhXoaw2P@Qxssx~{Y3Kby62Va`&EoI8J`)%h+WSAGD7GgQ% zM?-7WgZ7%W%MpONTtEeTf}kQ2O$SiGWsk37PSGHNxI&gpcEibcyg@`Xpy4MbVVTqx9;=iI9jlXjP4qydlDgEG#)&umonvJ4Dr@UWLgAR%xi6tO`>t(CZyodIYXWJyaMEDdmadu$;&l5)f%;s)fqvoAoQ zARM$Q95c|4gEuOEI;*&tP|ij*nRAGDvQl8ygzQ+!kZH zgQ?{9W(x31LsYtHjm?gl9M%%NKgPqOWJQYcdQ3@@Kot2B!pD(; za8uww10dt78F;?14aPgl6gD>3HO0tP=J}-VD&^xlinQ8Tw1spN`EYk&JtEeQXa$em z7MSFUc*HBoKW9EOge&H>*0Euk`Pw3vx5G_vyAfT$aimu4dCU~j+)is_E9+Vq$B6GV zv+lwGBM|M8C=GvMtf8<-gKp(G;lrLgmJbarEub2=06EG~Ff2+5^b)|1NUTPZTUHd1pdDy$ zM81p34uzkkIm;u|p41`$9ZvYtW}qAT1R#m#-Tv%h0WJz)(gPN&l1r!x1xK$E3s^TV zfx%1RR?3&%GCIZDgs^`z;Ub&vl(5yIb&@_e^N4Gk+U<62z%;SsBga(V(tJ62fl?@W z5N8|GBJKbxm<7#SLmEGVm_Ly|--L-93X6(C8YC-07Pli{M6tPOtc?RstnT3i%2kCi zV%n9_SJZ2xuV}}=I&CzPbm#pS!+pj-gcnA8|D}~jUHMFOa24eIRrQgMMo3>uW3k~X z*xU4vR%BXQTbf!H-EkF(@%inI3qc!;+Nq}PKcB%LAFu@DI_JewHrnbtDyIaF>Js90 zF%zG$pUMFXIs-dm>uJzVJmX>Ru%m`xEtex<4n*4;$*zN1mvl;!s6}|v2;1|3SaR4Q zgu%dq?0|f*=a+&wix?=AY}4hL9MN|%^YFn zX=EN{<%V`@LR01xYYW?M2ep$Jz)EH-l3Y;Gy%w52qbgKIoq@)&L`Z+Vi7V`!&nuDM z1^UfcbmJ8*mgIsLlo>58VAD%A&^ZV8-6^18jxa?q4Ok(OkriB*#F|*R*#0P1Zx9xS z>jS$CHjbEQhzdvQwR2>XilLoZAOMmZLa=iTl4T@1TZl-_9URnZi%=j50u1CwGPsrJ%u?{vUxPTR)@b5I`d97b7#2P1xJA5)9 zhVgtzkNGr)`L>s=RLLNfgA@D7cu=epC+A2kV0Y-ysh8oRgYYOWO1m0PTRj6V;l$5p zc5q>%f!grR;RV6Z+B*0zG;`*RnYU^V|B1yMu?$DzJUt#QrTh4S^@K*E5H*F@APHy% zmR3a}9N@b-6~(lO9r+ITWJiOeRK0e`zrw>o5kF{Pc*IsQr zeAaTG4POKy!As(qra}UE+VRnPtQ?j-o*J<(EbVJD?a3AtZd0L@h8#hq1&Xmn%1q&_ z)4a~&-o=uWRNG7R2`?3bO_-QHTk0)pYl*cw{fqJa9mUzu1ey9ltW{H2wHiE9Y=Jylu-gxVwsy;fY7R|B_kNB%U;8@!H|pJq8yDS)-1eNOcAyxVXuTl{AIKiNyt-2 zX2KO>EJGOqPmWyWZ5`UBVnmn8B8XWP|1AsQHeOUNk|tZbCOW{%xaaAZw%B}Lu^toc zz>bi*>#=Th>H-)WN{HVe<=TntoQ6I_Id6Mu<0cyuLLhzIHvD3VjORq4&_NiG1AQV^ z0Gv$k97??f10I`kViDlVCI{4$6z`z7zLD$`Sd!9Ep($W8B>g3D7-^RJ&)XXt`u20y zgtV&TOeTqw#gRs=6tD`k0e~KKdba6^J*6d$?YyJ`nyFnVbSObmSX9I&nOt@g8%}I6 zE+;xc$V>=Qk_Z_wSck}LvySx17!B)GQCi!Sv12U0*roxIEIBClxQc`xC5uk{Xdep` zT;WV!ZJ%GM8{r^!EQu&8*&^eqq_aE0*?%AmGr2th&riGN4}6Pd|M3`7S`ZvwDO4-3 z$`ekRo@fKWcxS9>B|s-~w}7WWJNC>_B$!-TM9^F6c^S>qDz)qGEf}n(3_}g5%kVH} zxE4Wnl^6;22j{B}T?6pTG~T6v%}LS#$(R#B#)}lNVwiL=v#8u%oD^M6GwUH7;5VzoW89ySlWj zv;=>y(PlJZ$sgn}bH?nNnowAF$bJ_%mKMLMJ?!{^x3w@vHm)?47sDn1uhYVJRCo~p z3G>$wYeE1|{!l}kYs}JT%$}tMrhiSt&Ze1x>9f8z0X3q~oj_3s%uC{75njd~f!a1Q zc}P8tP^v;RD|J)|OftgQF-S4i$;Pbdp_&?P@{E~S1PfX?PaA2lZJ$q*LjGE9G zje|aD5>QN;K3kg-X~rg=CapRKf4NQz!?(tsKbCb^Lvn*a1oVjEvzsw#!Vn;{lf!;A zWCxQRN*7yq$0HUl%oe$>6)3LafoXw>Vn_PS7El53NWoNCk#IJ|iY@Snl`4-NBcvZ@ zParPM$Z#x^cH7LLeZcVfaLd*M?F1I}2Hh1SY?KhT#bN%$)C^WGtxqa|J#6+wdUwwV zufR1t5GlasE-dXJGKhtC_EiUKA8f`|!E*>#k(4K5C0EqrGmy87PQCrvi)}U<2kTFg z8=ohIGwbqnYl^P-4x?;(n^eDx-&%-WpeOGfr}t)4{yu&f$9cY+`DjNyIT!B??TCIM zzP*m~C^==w7-uT2Zz<4K{4h+}L zo#CRr;qpUmOT$*BW=$&%l^yL1(RnsS+i{+BuxM)t$G&EL6f}>9vfc@29ptg292E~{ zy>4zdkuL9U8{S*V$5)i?=zMSGg}QZTq4_)xFE;nw8=r?kUbo z#$h7s14Shs}d> zHqf*7xVx>Wi}&5- znFZ;ojic~XkRCnYQ#n1i^!^p!_Ko28ALIRM^h?|r&bHuz8NBWGL)|CNJ@HcGngfIo{#i?4CEt zj|XpJz@tImts-f1dH@-o0gVj`mwkrypr@Xv-pBlLwJyKisc4&C(Bu?en5Ki@_#pJV z4vfP=3SnzeVcKyag~%-H2KlOqd6=cJH3Nh>rNF=$)c3SdhPfl0toLi;s5ZT*M;yqZ zpc=omjKbP4=nkhiGkYK-59eajd%p5aH|sn)c`A8#|H+dlKiYli&e5y!i>AQ7){?P3 z`}2OZdNtm04Q|qL-j=Qgf3I}w@mI6MMQ`?vTF>Xs8(CZE-1)!7x$`)TJt$9OJ{5lj ztw^bE-KtURXe68+DfIFyPQCfd+OuwjG>H_VwG4RZ9DKmzg7XF=op^GuJDuV^aC(`a zQ0*LF6q|ul0cb`9)8ecjAc3&>E30L!e&zhUzH|5-3OaXR!u#}T`s-UK`jOL4P4SQ9 zFQ5O1H`fER44fTXLr0|Q*0&Dfxo)jFMi(4A-g7J)uRX`~)KpBm%Ma?4p-Oe*A-uqf z*SrN&L4~o+%m-bhl0gt*YZhWQ6ZFviPdWE36X z`>u0Q%odvVED%(tP6DY=jx@B4aQ28$UIEOnlF7NV2pUD|0V*hM=;Zu2D<)e7OW!xF(>H%{L zXP05(-q<+OF4fs>ZdKQQ3%6yjRwZNe`f$e1YTPMw0L&lNdsiSy$Af4a&qB)4rUA2e z*&l=D_5R}6OM%tDyjIN6b`CC~H+rNtKDye2hq1oRA9 zw3^%bKsOKf<*}SGcj?wl*ps`Xqsk{~E8fRl0q$al4Scy`czHlm*Bug7$xsQq^$zP+ z2kIS-mXuHCdL0FQhfc4!YQS$s`Ace|0WG38b=?EZW#E@o4N~a9{y}%WXxocCAcs%s z*e;j9VFKwmkNZ}f>o$RnQ97M1{=PiEQ;O*R{;ZKBVIF+hSElG}CMbg5h}g1t1Uo+rJ)lV0pew|dgcoOHs!nujL4md9P#kl?tJ^rsKv zmaz^aZtVp_GjT(wp_n^iK|_1^Ao<&G3Y&d;&zruWwfy27qwalsvV!Ib9R9II>p5{r z&;APoSufMsmVu&|>10dw?zN0VFVacZq_y1k10WqUB+=eUryMyi>|aQNTm(YZ&PDK{ z%8t5BVGsREF=R2R`jzKnWOUAlJWgL-`v8s3y>5x@0WBs-G7YC+rKmX=4S7;2a8Wn$O5Rexv@8IJOFdI!iBl?iW- z#@vl5Vdk*~%x=f+vzFrY)Efmqh{+2OXWyW zN*N=W=uC9>KFpfI=!|KI$pdtT(|{P!4xNpqT7sbT4e4|Ss?tA7={+DQK}ZRlaC`xc zRYHVQ{c>@F@2})x%N3Xgp(!XO2{Om;ei_21I)-R!a%`Dl{&w}sFM?`rB`A{LVX}~V z6~`3YCx7A?HbIfZRXFKKP0)F9deNR_HJBO4jD*c&;jBZe$0^q0+;H8AXhG^_Kp^d7 z_`AJ&)@CTV=#^oJ3-h%>?CJ)5U&`f9(pz+{ZSYE*G(zVpw}s6^dNvcV;Pa=^zJjhq zpHa2%>*QCTxu3!8cRCSAViX!3>N%y5*(j97q;0{kd@)P-jY{e(e0mqmU3jn7u>L&$ zzzaTI$a+(g*_TY(p{ZQ=jyN0Y9p z;eL0^8()y#t2iGllV%2xO6O>FWpgd0 ?IdUJw2Qhxwj5PN)lyuTm#h<`j>z3)z% z?=PkNSi3sBY{Ao~s#}n0{uHtLzAkJ9<0<31x#xb5a;NcILKZyr- z-Y)-)^dYAUb5r_79PibesvwvpMov4_OlDxnTLr8Fg0%TPV3c61LbHT5ROaSH!boNlU{A9^B_IB0ZAUNj8#932L!f3m@ZI@5>f z=CoY(sSBamPU=Pb^<5{T=jd55vJR5}DxJ~>^H%ymkN2Brs|hmTS1;O*k87z3Qj&Ud zEDm}*q~6nw8^b(c^lrs$^czLLHgjgxF9Api);|Y+32~taP*c?;gXj#< z>j-@FKsZ_a74e92JIjz;W$q;p)qEbY(^L={+>rdk_OE%A!bAb{cXp(x5R7cbMw+wp z(#+oiWS4b!?hYp(zYcmcaC23*yhLbB63rxc= zt1!3mH-|U_N5I{JkjF+cKH&41p&aBaFhg=;V3Q`YNF&*?!emI-lT|o9Z+3n-SydQL zhBWS&m5-hbRiF{pOh}KT1gI)h>IqcEkL9ZOhwuRb1j9I{VRh1(d)A7Pe552FP^>4v z%jfW!{J}Pc6CZpiwC9H$p={@S4^Qw%-8`RssP=pxp~Cs(gD5uPiDl@FKKxJ`+dv%x zv_Dadt&n@6Z!8pgKm71G=|7fK__zU%-eUvrB5B~gOUS)z3r(cQz&p)6NqLp}D)=CD z>gkll2A$B0(OEe8eQ~(pCOK{I3gBt7{uMN5Pgd$-l73H@lxV!5|BDhl>+M{Pzu1FZ zqMNra)}N;Bh_sKV!AuhIBePY1nl=v7nJAD^=B>+8Sw#OE>}EDnGmN#+VtoVEr!6;u z-YEsts$ova)i>A;Gz8407a_~Gl2cC-L{3v(C1?u}f%a%yv~G6txSU2C%m)&9hWu8# zh-Q~)4cj{J!9C?KJwdlivJyxp~i+J4N%~<>tk(z0H4@nor#Xjm8Lu zYx|XPzHg3FHf8-}_~WCLN6zy7YLxP`vwW|PQeHZ1Qx>v*b~Zh~ca#t5{~86QyN#2m zdFhydqTHM1!)F+GW%)kNRz4hZnWB6*dloYOCFcvsK%r{`zMqX$p2)Ite>+n7^Kjog zBbB>G_>PWLelTJ#a=v*MJ^tXV2}nOjPWI~~eMq9>Rn@-l4_A(6`JNxH{4m>hV7T(z zY~O#5P~OV+?HHkan*DC}t0R=dxm0M!a8yJU?Q-9@d`hKn^-v||>l~`wmE}7;RN0y3 z`)H{0u}_pKobP*bgwmPqd-E*i$JxHu&r+Vq_C0!*lJfcfI70c+2zt6_geZG$JPU~v zLwxUyQo6Ih4LW^psPEUR^2*RhkbZl(?>QAi`ZuJ%aklS4Rr&GR4@0m3kaJ33N5uz+ z`Mxn+`TnqnMCPSmIu6|b%TV93ValUJ>lLMEm~UsU^87I0k8_nH!*1o8PY*dbqg~2uJC<-vads0Znz|?Gdpo2&6!g6qQhpNjeJ`YZ5cF*gDQ3v`L`eDP zknhou^2}r^xot9N`6p9+FHTdoP4R7=ro1%8_s}%uohh24{7UzIf2#7P?t5S=KI-Y) zYbZZ4e4SI3ZHDhdLwU*Yy=^E5ruv?krX;5M9-XG#Kh5_&3K{liC$h4ZmI9iF;*ZY7 z7wrCh$mYE4PqKZT=VhBYzK71s{#nk39Aq8K_5JF+?3ai8kUluvci_D2U!1j*%5E6- z&8$bx%|5Q8jmPrNM$RAeCiR}H{P3LlNFP2I*{jc+jO4%M`(DV;{z1NPPk#3E`P2@M zYg+*r=m#3O_-9$ik@e0{-$SbMtznOXNb!GRE`qk>_ml~sJ0XH?8E{}2x*#r4Dpxf!M23I6iuV; zXZ+-thpo|+t>eI4`qmHq=v;rX3PE?MP{tLk%6*hiYZ3ILbcH9ad(zc*+Wd~_#C#-& zqX4Kw3X^_%_MQ72D952|YLE)ikJ2_clvX5Yl+N{}^E_$NY<6AElP29}=a(%TXSrHWgWyzbd;eGo-Ce=j<7B-bcNe?D^8zZwtzd8 z{xHQJktyatLIb~X=DwrBJk1w9J2edrBhDfodCN%y7S6NGjfpQw#E+FkZ=xfF=jjuo z{soAmQS6XT>QClZtBC^y8H^4XqWJ#IP;odq7526rKUQqti!Up1_o*4AJiX-zdM!Q2 za-ml{*HLExKnvrrbgbL&YdkE5**D+M@*b~k|{Ma5_^n$@g()V-Xmud#N?}u(W^+hzwxvt5$p6}zw`eX0d zpF1$$=dT+?{nfg)+&@ovE5eSC7UJs^lhvno>35yr@9yC16#Ma+;~wLM*TciZziUN2 z2w%ZH7TZJ14EwzwsECeWpNYGpwLPL2BC|6Ry1ThU?VLfCfPb4yY;KUPf-f`q%%*De zfYf0ohU z6Dr|B1V`T^pdbgAdn36avy7;OV`7zAf zpYV^Xz*ty(|04x^{)9R_){r&Sss@v7M*S67tA8(+TlQ+-Q)K^#%ioL6#)lv931cl# zJ~W_yr}x=HjDzdx@nd^YD>^h_4K*vZFolm&o(PhG!@3J`WH-ZOL&LE_fD~Y^>iZd^ z#rflYiv*wX#Zo`J>S7reBUKZRp(Fyf=r&izZ|Qmn@mt6d{TQyIR#MmW_&$tj8gS90 zK6#;D^g+=C-4FpWk%TMC#zJx3DI3fWadcp!6 zs$*b7bSr@BlTbZ?>XWdb5I_hO7>V})cbgEXem=HSkM{zJhxDRjdVKHRs$^461qd%! zFW-jviTMGhT*gBu00pp7W3YC&Q@h(VTG$bd`hf2VuKWmRy&X0$ zLvRA{AwcnCW~_+_s2uCK7--Xbx9$Z_CE`-_fwwAiYpR~|m?M!G7B25y`T`X;x1yll z^Y&22G6MCT-#~@H2EsieC#gqphot?g4GJL{Mv9i|3uOo>c0>> zF~HW{9Dz|2w{W(TgJpxu({l>c#9=h=simojUm^!PN(y*U{2y^sWi7(ZThZGMlAshW zpUU#lT7vBNVk7!Sa~!$K{18jwU_;@TS6MOefCy6IGYhf3VQUGP&CYl)>Vp>Q>!LPk zQfTsvMu*+$M)OzqL=H|FFDTXM5T3Wh1a5#JYq9a zxO^`xjaWCB%V8+PdV+#fM)H>w+p01n1^A~Ky`elJE?mh_5ta&7BRd4VC}@#45aFqZ z{^AD;A(TyQLWN1NZZZprxw#hhJ*(@41&uXEY7twAp|9{&1U-4t6O=)+24B2W*S&)q z=o|=Eqv!1$k`Gg@i>~6$SwF`k@3&VZKK>8-{t7WN#6dOqeHq3F#6vTp3Pu&O zXd76eD`b{Lfk{hb&dGBhv&`$_i z`b43ge3}9QdT-o~4a5~Y2GJX*hi;AJ)5~x#YA2r-4p~MKxqd4*fh3=%wsezQ=k0;! z{n^wLI<$L{z!0F;2KF{QNFore_6688FT7K0S^<$}n3@3n;o%%j&}TVkt;AM-heij` zXP&x_EW-b*=@UbSqhc+CRSpU|6@BJfdO-$(g0O**fduMNa%j|pd!7+Nv!8)LA)5pn zoQm26CDP=dX2!!bsrcUS0wcLn%s3ME7BgW`prgK@d3tk5Qq)auB)T+u?@1@!G`cjB z8;SZ1^B$%}Y+E(V`-f5*_0s4vHJcb7J^nlXq;%X!{}tc+4#RWhJ(Sq(8^)IGDl%gy zp%Lo>Y|_H34}E8o{F7Ey^a~TJwN;1Jq!myS8tF4#}cN~HpQ1!>)iP`(5$vsTmgpp79sUB>4%b#hDQ9usZ}c3tm|BJT7C_7}q~d0CL( zAWn(EPg#H81~G5Eo-KB5pVB|U^1Jj;vNQY>c(Pc|hClKSL5WY$pR2~_$W zKH3l-Jr8aNy8*zCy=HB%Y~yoiyt3VM97TnPLcY;o?4g_kFzx+Bw7#8-HiUnYo$jAp z;`L7uEy9X6mwP)0z6@Z%{?;P2l~VDC0Kxpg-g%K*iw2BtCY<``5YOI8{ab)rxZ?@op60 zr06p1y+QjJPPI^kw`bWQygWSva>*|yj_4ubl)iocI2@pS?6*4Jw zAL}=gvvWFTA!i73a*%TpIk_E^kduuZuu|+(RE*$9>2_ zd_49sa`HRq*$JcykejcU|0Y^*nEQ>Q=c>$IMo((U^kfeE)sT?UYl7xjzmZ&?MDV{s zZ64`wP+NNdSr4LQz1Xv~YRaK-`Hr@O;i7F-<(sA^^Y2E?FMcvBXkHy1g~X7c*_IPU z%&9FGt^B8g4YHlx2H8Gp=LckLWPOU@o&o18SH^#mm55Z}HIjpboT6=vpPqw+BeK*O zPk1}-1EA_9v=3D6r)Gevpm|M41rq489T=+2?YJ6u*|-CCDo1wUJE_M3NIOPfnb(02 z(I5BW4p^zo@1PO|xC1txkJjl$&+FK%+4KI8a59G+YlTp86`?{;jB?W- zx=!yRPQ&j;-P~yueTX>F5F#(UNwVfG(1lTsUB=LXe=^ic#EL#LD3+3i*G!ht!``hF zgkkB1@3=9O8Hmgo=Bu<}f`pt=bj0ZS7y+7#{L_;88?E661wnVl_ktMr0nvNndqIr* zfMM(yCt}Q{c7b7YL^wISKb)MD(=h}0L(Hi;$T*3N+>RTNk!?=RMaHMd7}-&Rj2v_7 zNMwA1jJ%GEkl{0@<{{%_WaM|8h13b83e2hbVNhWN(SoWX>{RV1)aw%UBy-1N(?{;~ z(miSw8sl%8ydJgf@c@**%k#Y+8?@ukDy<%e= zdw`zrlh600zKZ=0!Y=4}dq|b}J_TKgm*2MoeEAgM!67RQ16_elh&ut7 z_yC6*<{<;*IyE^_Ob+ZK7FG_2`iVogq;sfIzH{k8z5F8(*9JAQlv)y8s+Yfr*Gk1b zb1G$Y&O}D)tN7!^e|KyumN7_Tnmbd36vjU|TV1mXMJc#e6E-LMtIUt_J^<}8`zEqI z^A|gp=5|j?>`?1T{Tm+Z=C0I>13+^;5RI}-?JBBbw);qo^C z6-*sNo$_lV{s{!aA{5oZUSXk~a!Kx<0F|*G$|JdZ0`?VaoC1=&CvtFy=tE2+xqAXJ z#F&P=LXx|&Qrs09<-d#S2&5WCJ1LyI>wUvKUX`^YS{8;|j#A+lm9pKuWfr{O)ICt_ zR+VqZ*6in_==UpzH9Hq{@djCAq-|C}+1yVaMAj#uWW-#cYc|seleE|ZM&CP8r;z(T z>==J9n?8^25r6zr%|3OF#cJjl#>{Hw7=}xVWf~9n7`i1@GL46O48v{5aHsKbk72m& z816J4F5bR2+Y2Gfqe0rp3=?-3yo8VRto|sT@L2OVmvwuGiY3?hCekpIpkWTRfqp*) zksqg_mSqT-hV{Dj1$`rHkN*Bkcp>CPz30yp$XF(fJOLa^OHpw<7 z4aDnd*(TY>xPonrMHbvKu8u>VU*XJ+Yu?3k1c77IggpN<7nlAy%AYIdppp6JgVaeX zZ5~Pu;#x^iIj8 z3VQDp>5EMfVcujIJ5PFPml;1Sq-*b#WAwf;!fYDdGI1j;hcI0Qjzi%1TPZzA<`0+4 zrufD*xw@n%?uE-jLN4cJxak$0J68S;&PveAx?!jEg3a+3Da)pu36%xQ55hK>2Tzg$ zGQE}a=qbLSy+B@<(KVd>IYn*YU!l3h_=k;1aq>Lrha@?Q^rLVi%F&PE)vXdrZ;S7x zPKit9tA%b@lD2Zi_b^OU@O4V**(0^FIW5H*&AxA`O{1&z?!%oj*&Bb*Nj>bO><`8F#_9X+^y`g3>ZBfXQa^Q4^i63|Zi7sr zzX~_0yUA`hxm_l=#fvGmEza)`Z;OwmJNpysjAOin(p!;BJ$Rh{LcT{d0lSSLIdS%a zIYl#)YiQ#yy|v%VDJo?2uAv1LnOtut%{8=3*)Z3v#RZ$4nZN_v)-0d`+;6~K^B``> zTF$0@Aa~|cAUc;Iu>;cb%k`Im5Gh^&-w8}pGp?6=%i1y38vA2-CHQDPv?Q$^AXrp9 z0zyJfTuL;KHPvu3l3P{&PBcjMDPTzNa^bu6&!(><8P+P#I+DG7YP%&^M}o^n->ru^ zWv?-bbtGM^BZaY!q{}rVq&1`&gFMi*YO=Z{@egBC` zab>_E-I@)*i^T`P2%FEGN0J2n*x_kgNc26__X0}vy~SjNKP2YE9GXTP2^Ksob)0@= zKwI|8JbD=nMjrlz#Tv|0b)ASq>+!D_D7qTjjDIfx zbKZA;l$DcpYc96P999#40xi9Ti`xXwDJ#^zfF&*-aAyTA^{WNjaXq>cln39 zI(?_1PT#@@!$`U#RsH+aYarPw^8oZ1G3a^r?4YLAs?m8y-M}+Khx7^rh90Et6ku`M z5phXsJqG6B1L6EUesLD#iCze*2w;t?{Ke((;z0rEd!Ot#vBP``ZN}z%0Na+Da6VvNdzNwA&?Y!26L`N8=obq?0KyW zTl09kBMtG~FwJ%t`(v@eONV@}F66}&yoM~doWv^&;fWmtl^5#C$#ON8_OEe{xs3!4 zJ;0_mc=FMIgb{B|(#io9b_8zeyITw%f3ZHBoSaK%!eEahZv|jCdYRM~!1zSrXPBFG zOf^&O-xDo|{S*5}l5=SB$`DS8(D}3n-nbJxte0aTYJyhf%cpAUeb*DETa&Pt00bDM z?LSu{=h?Nnj*YV%8-aQ&stGb3t8={-(uS&7z*)VkNo~j^y+$RI%Vg4;l7p$MHY;pa?# zZ145@26_r7>#A{v!%-XwHbKOj;40);LwJKVT)6gmq#UrY1>f1Kt_D(^CU6YGOqiP} zP`Lox@DODBajeJYxW4V8p4Eyv%jTgC1hDZZi{FvPk`S%C48bnZN=)oBj~bzks}pSr zteeoy)IFNesVS_diJDm1-$ryu#0LB$VddXLJR8{7$>sp&19Myv?PNtxGuq?KD3$c zolm#{r=CwtOhv}T01ZKVE6TtbQr8_LhYpidFzDo9Q~y(>wZU?^edpQmo@t&EsKWDf zClX=n4zj+~L^EzYQ)JJM&1^6sdMg$%t(=M90$@0U7%r59WG>#q#AT!z8i`Au8Op;Y z-wfp&%yc0QS1?_u5LYl=s1#Q)U1%JxV7icwtHUwSB-oG?5e)Zg-?gOIGUg{XkNW0H zc-h?M*7D8mn3@=PAS7As+cB6aa*^vd#52|I)Bu)MXl7}(-kam+F?-lXU8lkgp?Fe` zf6!T3pe85`EoS9>M<1S%e}0ZiJTE^+B5c)SUhsD5chiO8e`{WXeoV|PznB?V^*l8} zznB|n0G^znU(602NMq9Ukoe&FArZtAL@#FW3*ZZ{5%}`=AEh0nKQM8-Z{wIM>Em4!-*GT&K6+K%EZerx`|nH8WgZV z#$Ds!-^%}X*o2|@KDXnRO-@4;(doauXZcHlfL6`J1ncn}e^ye*CTqw|7X0NsE3g3m zQtDH1rjYkI;OIr-QK%1+?<}ja^%_i@SyN3GBH}@{3po;UxbJr`tJ&$vFws4Jk7Lx> zrn=m22TkZP>QA@huJ6~xCw=!5{@kA(9~Wlvz5Z-2> z`9&x$zOoQ)K980?;Gf@D;>c5vefRmmPj5@)!$)=ceu>TbmwFh9V#MxD>h69uCOl*ClvK zFEAIU!8n^MlBf5IUb`TyMR^Hqfk_>=F0H?iPV49N6_I@Q z`Y{te7xHKqo-=X#M#0xd%ah&Jt(7Y5=0|{Oi1n=Fw&gA^B4z+6zwHKu5$S{6f z;ZVExF}=tD_a?qKOW(C$AHInQl*sEj%pGWNhic9tt1Z1mDV5kQO6*q6$r&Z+=_XX} zPL-YV2`zkhTjdr}XLrhvC*J+f@K^6T@F0-yi1d00J3jA0HxvG~cp^_{u(@v)7y>Vb z6-t4L61?|xz4R+|M?;X{e3*TTQSl{bUx z)xQjWhgZ*Hy(D+N#<%8#5f(XzW-zq?oaWebcsG_P(>eE6?K}W@a5M=~vPZG#$z|u2 z9p%L!o`(q_MUZ2un%D(CaD5oyx|AYe*k9^6o6?;@bp12CGusgPt{4fP@5k;taml9( z8em$$Rrt`o6@>s4vCRriUUj-MjHL94jQA#O!nwhl%m%%s4W=pu^u=nT3x&yl%miPsM0yA#6#BQ+5m>pr9>?v1vz#+-)pE29@4iF!@Utq^t0{L08S#t zvD6G}jh?&}JH!r*;J_DJei5jpT^QSFC)J9vknMaDju@K^L%X%T<*(6` z$qKZgCm$GxOE`(WI&|sODN~*YYm4~PwuEE+AGqbzm1xh-1*r=l*VcRG@*WJ+_M})Y z+c$VAu0X_(vaowmO{_;jX-9+M*&IvU46e5H{P^+y=mKd)(P7Mc=osrDfxpv^r;eI1 zmE%nEd|Av?bj$=@TaFi1DhdPEYz-kToORH;0D&sJ4XBg2U2LHqk%Ci(F}Dl(Rr_)Ex;FY&ght!|m7z$(6yvk(gvWSe8rlfpx#n zOQb-y33oPZmZolJVTyABV_Mf}^pZh3FqD`I1szF#&cqKLJ(PMCe;{P5*(c-TnZ-(} z*yH%~Y#uriErS15Qt$RjTAO7yOJE`RgqKYA1ezBoX^_wpklI4Pur>;h*yG>gKs=8} zY)cut2JDDIT6Uib6w%^RAN3}GwtRtb`4Fp1WHOQ2on|SKze(#N6a8=~V3zZcRzJ=0 zQIJHwnz#t&OV>GQG6UPB#KYDC!~tk~H#`&CWRxIAq9f7nLIn|6fj5%)0a-cnO(RQ3 zthN;n&u(>X6#2qzw5lsOhfY|Lrs8dIcqaPQ!tFRib`5dx5%rcHb?y#zOpj_EB2LKq zKwbMO5>#OaJe%k6Z+p;c%?V;91faL$nK}k5MCv_@k?a~~j{-y$!19#F$BSOXV@4;m zq>B?W)UTaQ2t1+|f51<=d^8p*a%?q0TXuws8T2to5zhn9u0~b25yqh1g0LjbS59F} z+Cd+(HxX#+=2qyi*=_2&e?qHzGU2Dz36K*w5I9I1$+pQ-YG*edVV7%tyf4cxNn1p- z`ueVP;zg{o*?`5c*FsBkZKM28c+n$zg%R(~QlC^3-8f$s1g+Txx4{_EJSAfM@!(`aQ`XOrbSU)cUNLW8alpgEHfiG)w{Nq*XnsP|A)YCN$z( z%=#s(QBzUPY`Qm}i@z=$mFI{MKm_x4qz+P#=F9On@Z^1c_{;R9m)(!=4f5n=b~2XY z36sTO!bP4xF9#~kf^*4A0v}kdFnwg+PTTf?em6h^;?N^H?x+ysG#~fhOQTMTt7y)G zPzMigCN@HB^*6atAJutQD^?^#qY3cP50MKq8S%HudE{0L)65P7s_VYR&na+;wn;02 z0Ii(6;SJHvz>XH<{g6RB%FgJh0@d;M0>=iy%u-Oz#<*-ue8I!?r)vHm=V;uUo8 z2+neUX#y4{4yM7WPJb@x& zG@ZM##TLPZ=xoeW;Fl*=Mdts3+S+Uka@gW1Y3sQ zus_;i#3#cSg6RoA!XEMGkfX6E&Bzq6S2ZVfj(b1L={|rcE zumrmq#F%)i13o*2jA%hv%8IacKQombH(^)-5zc0AftT~+xDv<%t$ArY=vER@gQXI-THz-TEFunw9Y(DR*r2V4?KKV_Wulk8La}iB&4fpPk z{Tp#!vpk%P!Z`Fvzj5R)({3hG{Q0RnA(T!Z|EI0qiCHKXoX~;&9aE7Ci(Q+rl5pd! zxC?f|c4al;L%yB_<^s&QBg_UUA+_}?K5u`F?#{ZteF&by8x_EXWmzm**R zhl>})%VeisMHAC86=o!Lb z>nNWK{3eF+`NHqSW>u1vL81)dqT5p$z-Ce$7qZ1(mnT^?mG~Z3sD@!9%EfC}6+i&5 zubyNvK<6or5-EJ!>@AWnR+KuBK2SXE1R)u3nMFDiGf9yG(W7z>=A&-8f;aJBIhPH$jLB(*dX^#YCJVcFSoscgovrSt33IsUyH}IL5}4gQ8I*I z-`!lNndCQboAcJ0hg`x9|D=Dz+vnsbst7UUEiMNGE@v$1UXu#0z~y8-x#tTwwt}sv z)O9{8VPlEt5!?J8_pe6Fj-x?0X{|#*s)_HB-9=s$)(7Qm*yf8NW z43%N&4az4eQ5SxMHfUZ?KAQYe?dPL=l6U1trYUc_Gw2N%dD5lX_-AWR=*Z+}w$h)6 zre#a6b=44TxMzfnV2ZN=i0#506mF#(hP~f6T(}D(g;_=yW=duk20i|~o}-bQx0n#S z3z1uW%6W^2cg$C2W86DD4|Jgn=0vs4i3-)MbWFXmi7+5QP{17SH|dg z910F(A)9l+T9<7~(+wFh#>WrP+)3L$Xfj)caZQ@R(m=X+jieS04M`VXEFXEq^iRp; zk-Wxcx6bn0wZ62ugFEm4y5*z8_^?zUTPlLSD?}(4iV-+udP$q8tz&zn%kajk=88`< zFf?v!xaT|W!<0LQBoJ$6JYb592W(~k^FLC;{0&M3`k()LJU8a`hx?vJL&Me=&C2QFhH*fFPg*v7Y|y^8sl=Q7*N5(LvP zon{Ke3~bwFZsr8G9Sw@(v5ZlzLfL^4?KK(W(dAM)^%ie9srT-|oABPOjXMuz!bEd0 zXwQ*y>yAK0uM?{f###$?W0b4%;K^~c4H4~|n&!zbwDVrkd#g9p3&wPS_{3ubdd>b1D zZ=}98vFm));HfhZ_L32D{%d-w$OXK&3I*UAG@4)g2I{5s@-VYEoT)$51LgG5Q2i{{!}GNdephxE2ESSJ^oC{e4cXG9m! zyq>G18R1A(qnL)v*+Q0Xw4iX>it%JDa7A~>@m~sQ5r0|xn%U=<5|fkF8Mn8No%8iQh(z^QUbwfGMgnRce93leh+g2 zVP@MQZYbw?OSt7GgPeZ^j+ui)aZM+g76f7DxJpfoj{l3I7wz?v7XKCGTW&xdX~;hW zEw`a;{)^%ML)*lWEbKLLWC)&{I9r~dC|itA3p3{@PDH%LR=swdtIUl#$_n_h#nVUX z`~*G|(~e}!&@ZZ9*}_^ZYo*EO*0Lo3`Y|o}?>nw7QYjfvh$_YBYU9)OvE(76$SN6u zUZQ;Z7Li6m{HCG^8RCSD1ScZ|VL`^sm;iYmueP$?Gw}J3n8?2eiTWRpe9R2JFE)cH zl<;V^A@S;G9Eq2;uK81_@nSl->y1wr`SRdJenDPOgA-y&qsaHOu?U&qgqYT*t|x~= z*w$l9Yuc^>w4Mlrg@S*HZ&X*4Ci?HWlqRH`;qG zP!xF?qrGeUE|j*`(b!rF3v6IZG8{4~V>ooQSePZa6XDG!xK?7pwcK9(MDAgLeG`Eq zg452f$I!~#tjCaLQe}&e6%skZp%P}3u{>iw-lrFq+V_vmlhf!{f^! zd@LPh2xfO7UgRyhX@9h#);e!_3vb0^??dt`4J`+hW^<<`U2nH+gV1}IkfCdp*JIIZ zWS<1=dDtkIx__1|zE?g;!#CkGa1n>?Fe(MXxYXinYUFNlcN;E7)Z(}7+0aWWc1v`> ze%v81*rgXQ{kd%WU7r-+N1Ht{&fk7X=^%RoDb6pFL>{#G?h$X`T7gvtn{d_hM~@yS zzCg4Ypj>54U{GGq$h|=$8$}lFDa4kE*;}z^#0K%d7z0><4X?$ROvb&i0FF*2+AX;4 zUR~hMjm|h}ddWlM5uCJ9xhPESYb=zriE>!h&om*$j&N6}ZRl9*et|D(Ltu zPUbDz3(Ds$l)Wy>dav0Gg0kM;P2vGN*$8s#blx_x_v8X5gx|84*2_|K@#e@f z&&e^$It}_3px3xmr96f(sh_-sax`Q`uaz&~B&`~Pw@q?mqUehv>lt}&h(tO*&H4&0Ou|a{Mc>#*(kseCi=_0>QEAGm zf80g#k;%<@J(rpXmp6Y750vRlmilx*3Cvnk%B9yqVFr59Qy?~bNsz?dKx`=8Xg~7g zqLN*qg%18-fZXwp>=JHbA`0KCJ+h;jX^&<%z+Xh(HSJ8)8rbE@ z0wc$9>U>zs;S4${n0GLAnplrrq$YrR&9Ez9`xI=%mfn5x(d?|Ht|*#l;s87Mn)>vi z5l9@vOG~PSr=r zgkJQg{Vj|4H?lu}IPqr5<)#54vpdB_FB)z}0#+oNhxGaEheO*)_NqhrbugATsgiuE;c~BBM(ki;+Hc4D2FY9$t>B`2PvlVd3hFxtjUWNSQ zO2Sq9Ws?fGWq_5WD4V4B=ts8)`mrsMD5j3jrj6cWe-w=5-TDk@Ft~@=dyL%_bZ;1x zx9AAuyRy1P7{!-*muM>M{`=r^Y^{R>!mz#YZBOkmDkH^jV4*KiU1}MR`3R zz<`%bAJp~fpu8(q# z-pf17EI3AE(iUyktN<$O2&G<-lCDOsO{+J+^u*WdvZh~{`@9*C%k#RjrpN8`ndZ57 zC2@TZi>7Dg?Yeqa-WltJxM#!+aDz+}d1jn=h-31U3V0cD>xdhPcvjxX7x3mK{GJC( zY=wNrr%0V^qzwi}_^cW0uT5VjZxmM40vLvUQ4uz*uH6kPVJ-HjdEagU01ntYXjg12 zea0tFxtEQHk@!CoA3B}(M&77fSAy?(+vfGi#8i|}8S$<0x@scWN9JLY$a_@MSpNvd z_e9*P-81+YZS2ae{w?%Y*)46=UcTr0tupUx9u&`LJ5dNAS=Ws9BU!@)5|^B(cS|r3 z;h1d(5Az?r6D`dd(Lx-Gj8)RPjS?>JjE^AB^j;tit0y8B1u^2#k|fXl(n1_QD_)i9 zBkznCAU){jWxaPTMZz0j zn7znWvFo96c%SIp-nB+j_}IhNT-xM)EZr`Re6Jdj5i5p$^wyR`(q4Qo?{pI=K^``J zrBx_!DLzqBtVLQNG*MdEfH{?IVQV=`-;_CCZhEe(GHY~je0(c7dYQSb_wEt0p<6hj zybLTJh78n0e*Yf!&4?P2Fsy$M=_xsv)_0F6=--1>M3FIzgpvJw*e=V1oV?smEDg)@ zAV((j;~}$OCMp&8?;$(T@=;Mg5;6+`ksxO~_v0bcpv1$B{yku*!i!d!O6o^Kc1kz) z>mkZp$k4-(Va>{Evr-8?g+|Tc?jUS*O=X_d3^}%eZ-w1tReD}RDo!?4?HN(*x+CyV zF1O{$^G9ru7cxI9PVTkOBKI|fH@bVAZ#%+BS$u>I@sb|%EcAja9~F%+=WOS^ObM6Z z(1axqZU$ingH?qo597%)qv` zQl^&Dwfhu|dI6b?hCn%!l_Ew3>L;Gjm<%6EyGl9>9wV%Xv+Bk(Ix545(ykhs0oyuB zUuG~$S4gOc@-DjvC3N1xJMd3cknCZjF?kU9q88e0lte9rn#{6Og3J(@$_WW-L*Um> zimy6MwJq!YUQ|UXOH9jBq^Kv1%47sYai@+QL~~;gRAr#@!5Qve<#Da8jxtLVX}Fro z!?c<0AfVPbXdZ9E`}go(J3l6l^P|ieH80Ub!ul1Xmmx~ms!^MXNVLk0tTW;n-AUh5 zn*O|NQqPF_$cFUI-A`pg`{Fe+@j$bVW}QOO>a5Xo8P(f_GTt9%TOgR^UDr2~VYVD# z?okK#79Q|6*5Q8}e=`0mLl|XCIx2flv#p}Ok^eCBHYNnNn{G#kq9A3xilQeXT6KgP zLxctI;^p*Bhbp}fjnsltR7EGZ@9cNXCZIhKfIf&cO`Lt%r0cb<5?O|}4DZC;q|atD z0>)p{W@kEbc$ppy*2OU5!wxKCeat&)dJDN`7D_UCmae6KQ!J&M+f63_fClSX`u_61Je3$u@_ee96~*&07}ID#W{qU4hJEM4>@m3Q>|4~y z``Ozj@uEc8_=20)jr{kG*@#Km)qS78DRZ5Z#@l%5)p{EU&fy)>+gYe~F2hFap63?5!%m?Q(4|&8=Md7G6h6VM9pbM?tvT< z5ZTxM_w*JfT5_292GicTw$zD|B*P9vV@`c#GAo^)esOEh0La?2ZP96yV#|~?D1@Zm zupx3I>lHfEzWBYqKW&JJ$q0tNf$b6C&w8A-wC^@dOXU6RGsDNc(2z(Y&BbkB7l@=i z)_ZP2_pj|oXnnIk3Tyft#_J-E<}Tuq`+c(kzvx zsy0sz_n4~I^@{tgBuwoLo?EOW(kD%&CmwBO(Dx2j7yOq+^#1onjFdGJvZsZu#xkll z9bHhvl}#-itzQjpe01Z_XGQDR%=YTS)2CB8GGbwq!0pYk@v4o_fVV5(HvS&Z*Al-) zeWxLtq|3*?HhGHPTRY1qdoc(#3jY1{{v03Fp`q9(XRrL1)BPHV>bn}bP3wccg?OOw z_LY0jvbhz%mu;B%xfsuqh32F3(bg`u!I5uHH)yXAc*Wwoca^?cpUhQkd|iB)E}Uy0 z_gx9t&(kPLv+{ZW*nc;jPDKgU8_m7irle^#TbqcQmZsTkW){txzNXjz>nZ#55C7{T zPKAiR=}0I}|5ERHd+)ixd+x*JG5b&8y)k+_|8C+-?dU(6%dYv+>>%P-H zecHU|3Ep!ho?(~juuy+7P9N3%x< zC3|XcKFGz7ov&H=jUiC2`-vfPpz(Jj>n!>^`VXa;7Z-I*=K`&D{ninmLgKUmv~IdL z&&%ssje7FJieoq8f(?K0zx1LDkKJINdgXt=MBWv$574oF29vt@*tg_%;a#}J2-7oo z7_{-~sC@0-T$R@|2XS^`XZPm%SxfVJj>ALV70)80cW-XO^Yif8y}7lsSyv-rI$!2L z`pQvKFFp3}M$&Q!&Aa>p+#man0S=Yhf8eikY}dmhL~m$Zh6r$d!@i;L4Sl;epOn{g z4_SY?1$R9j%IaQ{g%HbdYY*^^w(XO$5RDppj&IrG zO0$6zmfapDg9EZWLKcB__ZT)qj1-&q9Oorf`zq(Mx);|9v&0sdW8v^ku#_z3eLqCS z*1h?(yoIta@79DtG#Y`?uWTAYP)Kw@L zA@#RL&^>r?2`!5VuzNZVUN)gMEV?)6+$tFsL_>OzcvZs$WN18zM3SH95@NhosW%E&KI948#~FmJhR^NpAxT&_wD--S8Ei# zW=_>aKt{r6JeWmiF(V=wME}_b?O2seFjjz%tPuvX)L!|dUA~__RXfC1KlNuqBx{Vj z5F^r!lB+nmUu191{ZeC6nIGXw zNin9JHeSzN@EMXfs7P$gC=x%BZV0`t$$0y!_qHwL?ZfEotu2<>8|dA*;i z*`AHrXX+a1e`4K|mR3Tg6P$^KMBZ$8ZXMYtTgw<_h@Lg?lP&4=JB;qPheY9shK3_7 zS_W_K+(K@nv1vTW7%ufOorPIKNpL>k_<2>;5WmGD<~@#DqE=fP^%m3{n{{6D@K z#{_?xD|>P`y}0bsL0D$@LRr%gM@#BftKg1cf*lyAqsoR6Xw2~i36hvCYskQJ? z#HeYkyGDb|PeD7m!0xdwMds$AxCzn3?$358Tl`0}mH`C4EAUEIdI@!C`PXVhQ1?0z z=JmWN)oW5#r4~c6iP!`sZ`9xsd6$@PyKn@piGIYZS&s^D3!i~ElU6d2dyljp@b-Q) zKAMmu9L8g zG6eGu#<01$%Nu3B37EHg8NvZ70~-C5|Cu8OyX4Os`4T8bz=CGTP1iCo#Qk61&-*5} zs^3XoVCBi+zPr#Mgk2d1-Qx%6Ei6Kjfbq^7Ms_gDz+@-f+tx30ETkGlg#JiDgD`X`I64DvSahA z1YT&w8RId7`g0_Oolf?H0SPfqSk6Cb2@V36PfBEaIMKdBzL(v!F;C(tcy3eqgkR_N z{2iufP6v`tSjxH=$eAVoE4Qv(#};P`TcibLS1YIm^c`zbrvw(gOKIU7*jBdZ2q1BZ zQum)q6zsb$sbbeYgp#FaL{_@|H6~r`Dhn{N31gRLq*KN&wa84G)QEwlO?n;q&WOFN zgt}EqfGDAMkNV%7UK1+$gu99f@8M=En06;fo{jAqUV?b zoezFdLtlcGO9fcnR}Vq5Yfkd6%f5uZu>bq z+6sP$An83aw>D}bHesk({1>Sk_cn!%`|JX?tF>WwMs3*ZYD2RO+RRl(j8RHfnA&if zJ97$LaKwmFT_-BFM*BBwLtC@E+Hkk1HndaJ-!7DlY1@pVk2rh~D*Z!qyZAMfHmLe>M&}YkAT@uw;_vPP9X^DVWjipc zZqppliobhln#nths|ug$u90U@oBBs9{%)yX%CzF|Hub+&{N3q_zsywp-{OkjRCMdW zvA3hJ90M6r_ge*A#XJ$CsP0Rp8(?N9Nfc09Aaxp5FMc~9vtB_vAaw}pl#8%W7V7`h zKIwwqnr`1*w6xRnarmeX^~ax5Cu)DEcaCfkmO8l19UvF=CVTrkJ%Dsm~(my05N zsmYqgDmUIQ>#w8|9fARk5zz{)N>l#dD^^(M4nxvqOT8T^>RsNOq5WUulF2iA+H{<~UvIC)R~9e% z`~Ay4erlzZb&0N?Q4>)H7$)MV_B-Y{A1wZGNJp0Tu#{w^Y_L+*~5>qBk`f9<* z=`%Z9Qk?~3M~xpf_VCf2=BenyqOqe2#~xx)c#y9m#0@%cYGTm*{6Tx|xZRMY$T-qC zkqT6ttU?ecw1ws_k=RYH6La&ca)%^}aa$(OaA(Wk@wxez=S;~Rc4@LCx1f9Qyd83f zf%N#?Atwx-n7*Gl^aKN!f`gTHx%h>vkiU}L{O+8Z9dd`146Or&$i7=}xKZ*4h7G_A zaJxpH;Tq)+csFDhH1Cj`KQVX6L@YEd0BlST*EampG^vQD2Jnsuw>X)lkT6`CbA0Zw zE0V|O7AzTDnmh9HZAx+rFWq)>?)dKQrsP)UT)#u^_=&lN6LUv`0+jH9ARa$7X9ZqN z&U#Tg^f0h(b29!USEuW;K{=m7lS#QlP8^zZnY|Oei$P*^!F3;}(-k=-xxB#yyVK2H$$q2s$m!g_fUzAqhIk~^|{ zn<=^L2Pdx{v_tMlU=uZTWwLI!7(ZDkFK!np-VyREB)wzws!V>AvqNqnSh8Xhhe{fs z?|5{}6>)KC4&-#hW(sp2w`PI_oHBF zJ>hPX_=@rX?2YrlM9BkUbcZ5wV=_UhBvecuC?8q|wkaoLn@kx|K0~g$bXy|>RyZi< zrC1qLhDv;_cD()|;W6nGm0N?(tE@1k+IA+eNiEwXACACJ+)4Nu7RLUNPTk2VxrNnf z%aVL3wmQ;`-N?5`c!=V~=zP4C4Ux{}xtq36{v$qON;Ln>cl=T=|6L*ZZ;6yOoUf{K#wRs&x3h%0eBi=J`uWzwM3<*A&nL$mDaW8%=9F1!FKS2$Coh8qvI z>zs^vE>CfJ4i8eE7w7!kE6rhz#qc|L2>#%%b^2w66GA1N#W_>5Lpf!rsr$DV2H-_J66=pWRY_=3J3G5)GAf zC07H|VUo|`Zr^fvN^)zG=f!86(xJ#%$PJuiXd{4cJ$@^UVI>k5p?FdoyHfOXZ4R$o z5jlJg)%+oyRocJu(ndT0~o^HxJ6s3$hH;m5L-<- z9)*sFOOvSWQ3Wm5c(w{VlQ>)H{9cW`9=0=6o|WWQCQpcuuM>wRUyM&NDWFBpFezN% zej(*&lxHJ*HhI?7gO$nm;<-&4n!G9gjWnbh!TN3q2iyiKXRgdC581$pxm|;jt5Dd) zSXJ53vtw~n4mCr@+b&G91X9A}^7&+`bI?G$RO&FB&tvMGlA&nE`xVg%Vt0kGA+DW} zQnv0nb4T33eJp<_??r7)w5O#^s?7N*(ixVR&U_`pGRlsVZ4=(qJuka5?!}C{2ld1i zlAm4sB&vH#P#Gq#i;wLCL2>48cd*2!*bE znBE^xWAEisN0p)cJ~5uD+M0Ag?n!ouc_V3%eNi;Q44{JPXA7@CI9@$sGe$ShPNt>X*;2Rs zQNo#R!#12Fr6UPd1(5^QNBZ)Jtz4@GMolt$ID$=^yY!_cI@#yA$Rrm$pyn= zc}^Bq65h>@=K|Cbnm4YH`jq>nu8do`eR6x4iC>_ZbSMF@5?6LDqq4^FLYTR{l&D*e z7H;9B%wlljP~mgAk_YPZ)$y=!l1t{s(Xn4e0FGPm`$soKP-nH?SOSze8*KI7z~qG?TY8>&*Z zbK4r)OGcN@n(C@S-PAIBX6LN( zI)FuWjkWF77=~-Y($%`ss#^S&`Vt51zc5o{_>Uj08+UGdQ>vk~^2E9(Pu~esJ6oz+ zo98vOj~?5xprx*{y|tyav!kkMc1vw@S#3*wb3=P+Wk$S&rOayX>}bqVDH2gs)P^`o zRW!{-ER;^IY7Ht=G@-7!p|+*7t-Ny5NgcCK>1^n1=*}f-=GQf}rK({y%?+@Fe~nSv$i$K;kmFKnQ_~vT8*1yb zqpvD;EiD>TeNuH(OGiU{s=Bswe(A{*s;g(WbXM2RpI>xTb#+T? z``p^*Y9wKMt@x@;RUuFamD+b?&z0@1bqyUIC9v1AMtGB^xF7Ct+!u8;G(){ft?h_& zL|q5+SVmy0Qsb+q&V)5LG?X_rRi!3Wm)AG6q?%F-%9~27Qe&H;f!x*Nue9jMC~TcA zP3LqrfTt>@ibpY$nJ33KwajW2Mkc@mjV5?Pl1oJ4E-MV022D=S0j5EdOM~p7P~;#o z+G?BH%TH{oEGnY3K`a#&R#%yLF6u~?SBSlpdX`g4tuC?FkZ64 zM54EpF8wlZD)m^WwV#GWk14l`#=6jzHFdy57sQZ6StRCVhDrT|+0_2hypl5P zI8z=OBO8rKAFz>)t~QAUBOC1-Srp_VWUACj4Rfum=4LN?Bl}lt9E-G{)zpk!Z;FL2 z775lI=k}MiA`_HEOlPWrO43{fS=mt>g?N%zotnyx?DOACD8JOTwse3}i%&_eSnC)wx3;a^%c&W~S){Yc{H0Cv!DyVH>KS=uTwQZ( zixhcP)qY8lL02@cuCu)zrRb?uP3JXCZKx~lpZq9CKB;oLv|)^gsk5pI`&FiBwA2aH zYCGn1Y+1q>p>??$6+!>%jIZyU+g8?4J8wbc0~P!%&(1T0JfX7<4XcK<52>~KmopmK zsHNVvPL6L)rCR4sZ!;m;3hkU2v3>CZ?fUmLPJ%?9Vj3)&!8x)SWnVflRz_+J@_jkV z;>=3XmmFoAG3}i#rInR+9C00~+V)gu8$+2*jv{Gtw1=7-5%Z#>&P4-sVoUvrC|PW? zFKV4MwKSpi(cWGwEnaCryJm0o8Pm$MCXwl7Rm@Azdwi=DVr;`25~_kEnXFFTqOeng zoUE|Aj+8Xo(+@>Q^3&MFsJhmIkNf%b!o%q$_XZm+^4f&UTp*Z$G6VF@gU;3)z*#F&l-dg`N zjPFeUG8VnH{%d0VuN;8?#u)!k48Xr8Er|8nP_ z_5XAL{;!SkKL}$pTbq90kMY0l0Q@hH@n0|i|6;4+IQ*jq;Qv1BJ;Q&|0Q^53wBlHP*#P|OnjZFl!T|i6GmSm@v+%sN>A!c3{*?oeFJBmoWBpGV zfd9i{{7)T#f5|Jp{Hg)?KPJZiv;p`piSa*u0RAgt{GUDm|DvlO{%;Jxzpj_y^s63# z|N0pDH3RT(=6n$VmR~mj|EU=HqSMye|M@ZgXAQuAPmKTB1Mq)&jQ{xq@PBQL{{;i^ z|NR*MR}H}b@)-Zu48Z^0G5#e?Tbq9O#rXfu0Q^53Za3i zXWjG8|9vABvDf^}$As+FrE$WqR{q2?XfuoXZ2x042sA&*+>E%d;3t$arO0mCq4sH= z%rBEGpnJJJ2){V_1skpW^)4LJ{~3#)FJI1f5X_g~bvQsc+318_0YBhgf+r%j_4HK# zx&~Oi`SEuoUc~92kT2=rG@e9%*%H>*{~N%Jd|cVx==3jP5ci&%iBo~a$sgwAuXg@L z{sjDd`DX(Y`I5Hm{^aB@+<{R`D~JtUBC){T`~DlpTYt|Ew^#jF`Rad(^Y6!hu|y19 zqrIg6{rF#mH*w*A*@b_p^C{t%h2kn-)8$!UCjPyMUFqbvIsba`<*&nwIQfn5*buI8 z9!0(^a`)wb31o5d3(mA2msvKBi(S{8eh_|mA16QmAJ*e0M=bIu3cY*Q@E;2@q4eW_ z^*1e{S+DK<$i)x;czBMJpKyy4i!uKzPUKI*&zCRffX2yRRxQ5VI&FJv{(bo^0r|yd zfj8C@3v)P;Uy7eEU*b1bey#X&>w)d9`S<0251!+~|FT)gkVw4j${&$G1wUW@(*gOF z^;W1^D{ODgzc2sEfc%x8;sxA;&aOp%Iexx;ArneJ{cEJL4|kHir2l>S63ubpZ~Ui~ zKi~Nj`6uG%%RdVG31yC#|L$?}eR-CvV~Wc{=ik?VwexS*El1>8_S*&jar&=!`mg_z z^(^|IjGwRn{{-Y0w^@JMjRa63@Q;p|CHBldSNcBfha-%B%nU z_}munzS8%*d3#MV9EoOZEZtG4WZGYdLB;CGkHE zKVSZ2C*Kc$fmBRzvW}UZAO48}`SYFp%}zOyFLkXizda!T`3tR7v-X)3@a4}9$Zy-l zhQG}D75Ovp^X0Dz$lr94^=Q^bJ05cJ<;yvTaq<74lV9lkiu^P1^W`52&vEwGc8T?< z{Rqb)e)um7$gkPe#{VYgSHgc5e!l!2 zU|(_JU%$|L+>-ol;6E<>mAhrjuZfZ05Rkv}+gAQP_LBbh!(S7SU%b1Ozry*H@Xy50 z5C6h|{F+79qghApc*w<<-xZL*!O7?NuZMqM{)&M7{L8FRvli3dntxyZtpWKjf7yng z>u;%2eEClX*XB}x%l!Q3&=0q$ECmXEAm_L^X2b{b&_%UzwLVK(X69)Jmlib-&5CF5{=vz z?3*pW4Y)6VNkD$_O_rgouV?4WUlfqP*~za#{UA=l-;SRz|IUE?!sX%%_G5!#AMo$X zzbznt#_(+WOToV{U%tPIi~p6kTajiBep-6A{7LX1XaCPT`DMzWl2~^6#)7 zWz9Z2Km3;mmj>hyIn>IxdvDqN*9Att{Br~Hmp*ExZmIrj z56G`M!pbkC{C`P|{JR74^M7IGn{OBFt@-!Ee@8(6($Q8v$A3?Z{MQ2V+kRz*nr|KY zk^f3S{-$wOzKm~(6Z^XqKR^C=+accmHkk$M*cV_6`9B~2JrfaeiILJVhMQq8M*bo^ z`|?i+$glaG@t$zQ5cUE8e*BjPR;YYu!Ojo=V*&ZAzh>nRE49Aw#N`V7{P4f2@>vM(^JTFE+{y0S z4D*0LU;g@l{Flw<0I-9dtWS}@6hB}7W|i;k#8RXGypxqL-*K?><-Z@0pSaG&zY{3( zug1@pzdynkSNJOf3NsVjCB52dl^XMO}U-}{~8v>a$kxU$!4H`Z&qnhoKW~MU}mpUScaI=q-BT1TB;7od%_sAhV&vvh+ z4=8w>d#-R-xg^aFLj>UVVf;ky?hYPBuajm*k^py`G&6;9_{3}wNYc!#WxqF9DB_J7d!f89{rCTy_Op)_csn#EtSt_ zj*ss9qqua7M9%`pzutK~!Qo3Cu5#pc(#*W6gy@WmOkAExoW`ZclyJX_VY!FB?AuZf&mrVR1 z8w8R}NQ*MtB@@fCK_JP*Edl)20KPnc-yXoFg_7ysHtCQp_dSPeIj+7wF?ftVCf=84 zwUz7^Ww&JYi*p^l*ZfKB=5Up#*S#I?>vMp?Z%MDiQhJ$p#%>H*a*G^&ffBmwct^jh zhacx~ZO`dznU{`Qsfyyn5rdmZia^}idTW;qOdf7C^uE+Y;t_*$E5>?#&ft4x@ev@I zxGjKlZiq;d$nkM52~Ke?nU%VhDw*&G0i5S#)~ZFN_XaJ}^nU6py|iMpT{7Vc0q<2Z zflgnHOD244vqVYVEXVVcG$Rilh{t5YZ9w9^N@iJ`B}m480VaI>UY*is+ljnMnw=6Y z4gV+YuB8l_`((ORn)xc3@O$0%eKIQ-DZWQ01NW3n@1UCbI%!s&W@7S`Oz*s!`7W8* zClhc_NxO4p*1KfF>$Ifl3$kbhNGA3V;KKv>0RenO06#E*yUv;SHkmlsL+oSH?6R5t zDw#MW8w8R}92&sC62K1&;D-lr?#)Cb$;1)S1KlOlyXX>#+hn3B6Ts8!C}M`26|?r$ zo{r)p;M}%XkN*tN?>so3{^S5YEr2%!@a6#C5x_49;J*ssNfe!N;r$x$;naU@bP>@J zpua7E%i%zA{67fb+iV+8KLU81KBpV}I8CN>zxfJ-PfX*>TorV0fd8rhzS{7al;)%B zRW}CcKMvr7Q02tM!}bAuZ{RzjAG*v{kt3Z3UklKm;^@~odaZHi1n9dQeT~Qe8iz0S z`25u21s?rV4qxvC7CJrGJA9qPRiDp*$Hl`gsCwh@VZe7rzhJfFYxZl#{p0|BwWC*k zbZ_Qv;KOOW!Y$uT+!o*?^VV_tJn8rsyLd4BG(#WRKQ4~`ufQc8^HF|?!=rIuLX#qn z{-6Lp7P!Pi4eU#to4!e$9H5^bz|RcewE=uq06#B)UmU=%3*ff~@Rb4lfdDQ$G+)IaQRkLoW{=*$7i*t&k9Fh;|L3_x5OGpzsb|{FTjV>c-n0) zeR9xrj?+IkfFBXS%K~@}@ZmIGw~dp#FhIW~fL|NHe*oO13&JPP?5mCI3gE+OJW$4k z#l05b^FaU~^riUt*(HFF4&W1k52x`)UEf{@Tz_-(+4 z)A(n~+1L94`u_{ygK}-U6u2(c%`Sk40*|w|V}K8*@l{>_K0QGHjQ~C~fX@lw=LGQi z0sJE1VsFp8aLK;n;vNXl|22Rg3jcBOaA5#n8NisH`7*#h>-PrRV`2}l zvfSPQd};unAHW|8;O__U!5B)2(`Q%!KQ(~22k;96cy9o|DS-bT_;8xn(C;Vq#gIpw z{wD|UrND>NJcxb=@v*_9d5~*exm1cFw&64nqU-kWH+W4t9TndOLvO=r9^@WJu_y32 zJx>VGR~x)O&0p8cpBuohH2ADEy{_-O8F-xBzX9Jru`ErmeS;0RIv2;r28h!%Nq{=ON(3Y5qvpr5|qamb6@5 zFMa{=IJw_5^lfQ+UElq=!Q0cguDi}hc@f9wYXN+608arQPV-jE|5Age(sFgZ^&S{7 z8BX(7A3KWE3_d?iuj`Xj20t&2Yrpe)gP)(qbv^GB29M^gbe-)Rz~jREb^!k%fdA9* z?@G(nb+r3oXgiMoLBNO8yqM;T$^iXo0sPDWKGX2;N$aEQS}!yBrD01 zWB4pe^U-yq?-=~DG_LD8cilTa9u5aSoaWmn}(F73u*R}Q*vSmK5N z{~rbL2Lt$%0sPqj{<{GFhXDS10N)tEKMdgi4B$B!dXBTV9Rm1H0er6jUI2VJ&2Q>@ z&952!+H`v9I?9;=K8*pqGk{+dz#js>GtJM+p5o$G2k4&<;O_pB%tX4d5+;I~{F|+5c|^=&uRj%L4cc;F3?ZU#Ic# zOo0A1;QQ13wZ`oS0s1{LWwJlbYoi%y+z|%&UOT&?q^YB=8M~PF6EkD5FMogU$73t@dA0TR61|jUMdN5sT(ev4OlwI+6U0O+ z#$&JF*{yA0 zX-tuOGL&Pm<*9G@CF8KWK0sue0i0jpJY{TytDLfS1b4(y22jcfnUrSepUY5vMtw7m zA;?m@NY$QTvwBl2j#r2&4X_P!Lv6ckyFR+QytPAqDQrEQFqtb+jcwN3TUx7|TkC35 zO|2~n90$?f+MYoEYHnyTsWP^%wKHYzu%UgmD^c9;)3Fj`${%wcL8%@^AZGtB6<$^! z?1LP8`axX5mlh1K159Gs#T)zco;o1IFk(8!R!?nc8;x!5s~hIFr504TH?-kYjMjE! z(b8<5D4+v}We72}K2lPrxuHap-15gZ`X^P_Hz1{27sxK#aRZokncQ4Ey8^{`qAg|4q^O?TiK7?9-sglpdBivmMaYqY zZFcMEG}8zwZ<*IRr=kA%1(Rx%#j9h5b! zRD0B__0==mTWjm^-FJ=pAxv!p8+ax)^xdZa1 zo|rB=r8cozHOpS>&@bba9EgJ0pVm}2r=zl=y{bW?z#2LSqdp1-!Z$9hPN<0>C0wet z*1=9mNWj<{T43_@3j2gN;Cu=RU`BB1K{jkp zwI-zT05fiC!JhrDVl6@~o3f@<-hp#yTI9SO5XxyZX&^GPY+Mp+ykvpQSCT773va1J z+^*=E*+DAWF{9x#NFic{&zLb{2~}yEk1=o|8#@}ihNDf{fW6hLpiqDGGmZ43OukvR zZiE*~1f_Y!(~OnEU~+4FzjPTH$CVzVM~ey^1CZ3#P(LrDM6j62vs}7ZSRz^uw%9r* zQ67i4aZc?xuNg&T_Mt&0?~Z|)mP-5oWaRE~b*Tky4Ji4|;T`8T;WQd#?531!9B>X< z5ake5X{gFkAhw=cJ+rpHTFMfMb}FAk=MYI&=xnM_%paoCS|l0(=bMA z-Aw7n>!}y5?O$)NZ)neM+KXt|+gR61orQAO^hkkuM}w6josAXZmFvHXUZJ+pSkYX} zK*&wg_Ui!V;)EcGtV&5tHKWjtYoeOA5;qsoIWbzf=HgM+Q*nqEPD??bSx(WyoeRcR z=q2Z#zy@tdI?(DqsTx(qoN63{Ru8nPqob|1PAU$mXV6$MRkk~rOIqL(Ut;nIS=x?G z6v_b^Vsz4EZ*AvHfe6TW=UR%>WON|&jT39K{s2f&XB=`0(ZAH@KMEEpC*GA-*3Gp= zXp`wn;Ap`1+Pai&5jIUVO~R9_nk3UZYi=6b*3d9VGif>n%&As3n`<;}T>_+gH2;BP z)g;dsrB7Kx$EdEoK~6J~G>Kq2K+IST5(FnDO-P?Crf1NhwrFpdS=(IOQr9pu;1_2N zO|5bzf(~TFjHdmEPH!_2t77z!tbh5)Hk)@e*yenm|LQqyzC0WV)bF8rTRNN#{L&TK zG!#Y7_A(N=sb!04?=f)|Pu9qd9RoNy3wQKUcQ(obngar*$+-4T8Oy1 zw6(vwRB5-O!UCz((4(s*W8nNwsdUkz)JjCk)vc6B6@9X`VW4ctDj5E22ks%&qWH@?WzVFkUuq2Wz&kox&lH!1 z+G#**adxWFHbl@)u@dB`&RhMHqvwr%+*zN zGF}vJGe8dAwtUd#f8;bjlh*(85q|KUQKDv?+vmTGJFG8@CVWC)7O8(MpS0=go7zp6 zO0K3YEjL*{vkvK-rQMXw;1mT9=z~Q_Pu6sRY)>akNZ_14pRS+7`C4jQT05E>8rl+b zaPnMsFpPF|)?WI6K3_&r?5TX9${5XSjym~XGz{pBKCq3-mh{1V8R&Slm{QV8Dou}; zVx)F11`gBDsD)*w0bTN@`PCB5ZK=fU_S%*du69fnH`1d)SF^Q!jtsN4H_H*p=yB6s z+iav#ePT0VE+zLSTQhr;M^-21)`Ar@sOmThM+;+yt)bMmyi@5$bkR6zlUJYH zgmSebRo~QFI#YyWP7Q!I15q6l-uj(wk0r7*dd)opcvqAy}FqVvzuBln3Jk& zbO zQFkVt$VKc)Yf!+rjniT-h-&l_Z4_qbC;YZ``CWmW0UnaufZYO%? z|5L)*9)9NWxyrIhJVo@(=Xt`J&nq6E>m8priJtkqOZcsz)o^Wx?^(t9;mUJ(4&E`J zJqTxg_VxJOY`7*8qlljQ97{OsQ%d;L;Gue+xupa zU`qc3qG$fgiC%EEhaVCCBJp{MaE|}Agme5q<8amUWvAz>gp1q)7v49C&r2kCBjG}? z@w|;xesG%Zn!YCD`Y8Vt(F?BmaRK3+zSj}{JJ4%9+(`W8R?=SF zGNR}Bxtnm2yU2z20T0)B`-O*J?&#MN{(JCIeV!$prT=%Y3w&l8BA z_HD1NdpghxwmHIPdC4kUV(`xU|;1*yhQA@O0kvVXZaxiBvKqKotOsU)1` z)&}^@BAoS~Lwx=MIjZM5gfoBrjzV6up4SooH$`FRZX`aOUdssoD`+*nZV&KzGJro# ze3<`pM883m!S%O9&vIWSoYQMP@!@cN7{EUzKCF*vBtgEUBg@^%eWx$pnE&pCv;KQI zT=;N$$^PZyeEx?JALd_5xD@S`gN*mYbmDU>Ahm~Y626@9nZ!qKH@o`fY@%mAtsZ@W zR90~32I$ZC=!+eFPk??g(Mz~yxNuz+p#Kih%Wb3Me^Y?|R*(LAM}JpO&_C_b7d!eF1N6TqdWoM-7eB8B=-=?@mpb~l1N83_z1-F~eLe}$ ze@^tGkA642t!uc7J+u7`b-3gg@p`@Eze|9A7|{#=RZjnc0R2e9mxEsI|8U|Xa$k0G zzZ#%F&Z8gV`fn8h`cpjm@s9qi0DYZD-{$Ds0`#38{YpoFk%w<`xbD~O$N%LX{Ycjj zk^M--`Sjl>oa6Imk57%`bC-w9xlZElB|dBq4-wA%S9^RObbOxn@RuF_BJpAVuMp1s zW#1HWYKMhlTyT9JKEvVf5+COO5zz~&oTDo4^8kGk`-_NEx!T_t>To~)^NC*g|H1Je z7NFmU=*7;Rj$Zby5$DIxLXUo>qhAuBznbVJK0k2r zd1HY7heU7E*YUq2K!3MKUnrj7?hDZW+@qiG=pPTzKSlJS|8^1~aK8@FzvR*H;pkrt z(61+Y4)2Bl{og%$4ey5m`cFOj)h@g_xi%br`flfNiFP5)clkUoK)JZ z(`$O49iVUZ=tsKea|86}d-QGYxhFus*rR{YJzo`||BgqmTkhTzpug3lFUCGa;_eF2 zuk`4<9KGu0+vg)hFX`ouFH1Zb;Qutyi#@M%{9g>v|K6h?=03iDEkOSU(ThG6&VJqw z(7#LcqEC(E|4D%UbB}(dquw56=$X$!M1QjtW&XcH^s9krd=?VDgsaT)KbGjZ{+&WN*T3H+oa1x0$G^exZzlR5 zl0N4Uz1Z6f7jG95J?rzV!~J^vMWW|=eDa=P3McW;_2v&8uIY8X%U?etdalP;6VCOx z>}MuU_;YzxyqAFu&h_{SgtI>L2mwK-x0u{C7k)ZAHa{qLQs)`pks>tO{3Hu1o6_amI^>z@!lZBV4o zD}+}Oe*69r{~K|u`rjA89}eJa2tRUA#OGzg#}J+z9?2a`cpl+Q|7F5i&mTHm?EnAp zlR7}$eMJ8=JZrcf3(&76df}t)8Nwd~sC*7Oz*6Mk@+-p6bhzmAb4z0WYl)uqnN9dA zQ0n|s|Z!M}*%@`rJo+ZUL_0`gwrj|Rg@UA0z4)3oC z=kTr%@YzW89NxSUHZT(ZN0I$}nQ*p~LmaN+yNS@o6V|4%b4W=k&Ua=H{M9=YDN;vbGPJB3A)dBk1M9=ZuN;vDk zfcS7cUr0FX(@lI>pDP0NHxNC?^R0w)Jl{n)>v=!%mwfcQ!T5t)ML6sE81Z2}*97Q) zN%SoDWulk(neWCm{zy2>{WI}lxql1LzfJTk_g$jrdgmje=X&Qe!nxiVa61}9m9<5hPIP;km;4>#c-$wMD?wv&ckkef*=Mg=}+eJkGFwrk0 z`bU6ky!8_PC~z(JmJ%P9dmYhpJl{t2Y*%*?J=@7o31>b(BR*^=4-q|wcXfdNDWYdP zd4_P7`#a*pcJeCWoWARc58KIGL@#zC`{axJ2jR@;g8-jT0`#8~J=;lgq?IG}4yV@; zhfBWZc>5C3vz_ch^lT@45YBe8FY#fy2M|5S^Wj9#b~1+O*-j=9&V0T`eArGV5j}^u zEI@xU(X*XQBb?=)MSO-JPN&#tPt+04>Dx$r*iPDsUhHJNvy*cPXFlf#_*@*Izm(|N zPI`%+(`zZwbG%(g^lT^JBYL)zn+az-xt;j1+`EXL7F& zF$h-)`dv=H8V|qU;awj7l*5;KxUNTE?cslL^y@vm&*4T9yi;OG#UB2t z!)rWzI~Olq9zN9J%RKzc4qxrz`#XHShwFNDV>qPect>9#21WSS9bW9=y1u-|!{r=0 zaa|sMuESS*_(cwP%RUnF`bPZJp7VqmoZvj}H_V`RUPJ6b>D(}_p%*{Rj&3;7I}t&66Z&HJe3ZNT^!i?KE|*j<;c+{DZhAd^!m!|X;HU0jcO&=;{FMGM zhYSB7;ir14KBDIhF0F-hy8OWj{hjzJ{eN4Zu_6>si;p}KOj8e!Y+JnB76sjOZ>PZ6L+u|qUUmWD&frk8-z3erU3toiT)>$r~1z)oaN>k zt!%vgl<3vYC0vZFoe2IvM1Lgi#rb%tg|qOp2)`His%Hn`_YvOZaE(vZVte_SDpg> z(&3^%w;z7(aG(A+M9=N6R|w~N`8DFh?Z!8Vp5?wpxP(#rJMRVfaDHLAGRG%Q^k=y` zHzeU@{(3&H;LK+@@!@zo*x`P>9TuP;P4paZ;|XWE#}gmP*HiElH-+$uOxbo=bH(?8T>Wg?smAunc(UkAbPH^A9uJPKffYecr`jk&l4X`uU80{ zysY}1===$P;j`L3ce$(Bq2zI;|2^Sc&aWq&%lWq)F8sM3`3KQ+dCvCF{0m8c=KocS z2j+hq(KG*9gfoA&D+w?2XZvCP7ZM-l|2x8&|7(OZ|91(060$X(zfSS9k?_+9f12&&Bn;yI7~sEwaOU%&$L9;nCh-yPa!@x8xHo5_al;ce$d5p4e??AO#wca5-xli9iLvpS+4HG zA+K-2PxZepK!2md<-*~*necarkDklu)9(fvae{BcPv!0>Ae_dt+VKbrXVs6~uek*@ z%I9cDFZ|ynye)u#$KfjX7?-}<&h+he8S!DcTAujy_YnPB+^e2i7yEiX?dVk>jfdw5 z=XB)yi1qn|!ad8HD8*SWxA$1?NJlT>T85wUm$e+?{P^K^ z8uRhnXw4J8k$%#Iqzv`pqjN}y#*Lu>Q%Sj$*_=xzhJuf5t0NjfcxgX=FawiH1 zC-`Rk6fYMLPH;&(#djAFPVoQ3Z*QmHJ_5oiKEmPJo>hFBB}<&-u8LQ<=LQcy(>-%P zTIAL{`~r`@**!1z@RWPL&cn}l&o_H`k9+3)B62Txxb`(v|7#sCZ3c0QOPYy$!Cun1 zlv(1m{#E+BEu83cSH?!R#Q zP9S>0HGO9i{!il5OE~kn+QXI4BOX4?J#&5$KAO*8cJC$s3SNw#IIfpJ$Fqj(4Ud0` zp-v<=diYp}zvtn{IQ$b2Kg{7tiC{R5XXUSFQYx-|c60QqzqBpH?MZy3?Wg(=CtSj- zPQOd@`RMmg;*I-3<2^pw&zkJvs{c1VT=lPWxX2aVwSH|QT=aYyKXEq@AF)jh*AEF7 zdTr0#?%`U_{5Zh>5u%q{<-dk-;jet2BR-=4GWYx^qL*9cvx{rL2*!Mh2$wdU(w7j< z`J#$&;iL9n$xw{vz~>7v!0Uz_}4vL>$?XCXFXRF z&U!xWa9_{oh@SQQE#ac4%Kfv4s~v8?T@-IZuXebL!&R>K-wr06<9`(KVLKd4xX^1l zPV;b0_iuQ(mSYzXek%B=eJ&)N^}pWXqCe~TDA9BL^L~jO|1S|AEla>Mq5F1-y>a}% zM*KythU)_l*LeQi4y#SmH0Yi-aK@?~BOsb|&#* zxibl8xl27<y8(fZOwUfURz3?v) zVz`ezT;=}L;lBNEx4re}r_13E_tRwz;T%7_@1NirKe{eL3`|yTZvx!M9Tl$gbyM7dcwCSTpoIK&)@sJ@8~tW54rH}=&o|%_|blZ(*MfQjS0|ezd-3VK8uN-<5S!J@|xo( zMf4m$gQ@+=cwGQrLpa;#G>40xi(H)GHgSsl!HM|&C_c|JsKxguyn*oHzz4hM^F4e= z_q@nmC2ghe?1~dJmLji2uGBedZxTjvLVqCchnc6SURAu<;o2V;dOx{s$5N zCB*-BqF+Ju2NV7Cgv*$i>iH(&tpDePA42rHZ;j}4DB%YQ04JBP5Pmq}yAb|0!kPY( z0De8;Ecea;{xIRJ|A}N*oWD*B;I)KHQ^<@>`y>1`yk`?V z=Zg-am$`DK=l&Fz|5p(`Dy;i9>Nh&);J{V9;zm*9CCahxzmT zCgW0eiSy$@zAcTzd47}moFAb7VF2gwjs=aT@BgpiJ<>*JRy>ykaBi=1JWnHfNh6K7 zM#9;Ct|6S`S;{(bzP)|N!dZGQ58!tPa2?kd{;Vh4E$jI{@nJo`Ae`xWUV!7LJz#Iw z1n>hKe<_!4#&3vw9_g;4|IrlB$2nZe+hc%hdxq2T3B1#E`3BJ+3wo{BW_ftAdp?Ko zuMz)w4i|lR|A$Kmm$fJ=_gdn!0{5!t4MflN-7>LL9(9?pe4aq_x^!n@F*7B3}y3E|8~ zbQ33hINhfj)Z$#f{}18ZPI#Aark6gQIN{GY&sVZO2ODZj&-^DjT+&5;^LMepiG)kp z*5%@{*Wn_U`DmM2`8??O{3O6f*QpAh$rPW`M-%7cs|9d4%YBp5m*ZhKR}hIe#t$K! zUnK~pG5rMq4eT( z{49VEru1TZ?dJ-AuD=S1p39{Z3Fq?vG{Tvl^9#$(A$vHA!nFh8On)fhClEdNADR9X zqG$Yc!nr=ubIQa{I9)o3{t3L(^!*m$TyI`ZILp0(aE`ZI2UfX?D1eYv0eErVK|(|s{yiSy}?5+DYz4&V)z-0-O+{d)*!JLh_h``01*Z;_r- zmub3a{t~rLA^cR*ljZWb7V|lS@)7g-gmC7=;~~uFL&_J-htIQPK37xu&wThCI_AUY z$4#a1&fCsLv;>>&uqS|D9>A|9ob$^H!kPci0yxhDGW}CT&;0iyJK^x&MmXEmFA3-P zlrbf75)W)w4P-yehwY#FaC@Hl@b?H+6#oYpsa7uQ&;9gupi#S(u`F?-C+j0=Do$|L zr<-ur=kJ6wpPhvPoX=+x;moIn@M++wdKOtWh7X5}=L<#=eMf-4*Wud!`JT&HHxvCD z+^gKX2tSqh|AFw+2=60&2H|sYFHVBT@pBd79M7u>=lJ3AdFG?*!~J;fqWEM!w-7#^ z^!$);=?iN-d?`@=+(5XDNh6|=VXT~pGO>@Qv>w8PKo)<^7#D9@o5S0nMe3?(q|FjT;ASHIG0yH3E&R|@W%+} z@@);_Pm(^`KGpbn!G&vI(1?>bX8WIPP>ZvkJdVfsnF0EG!nr*#pKxy1Nm_^#eYicN z<*@3n^P&8`r1T4Z@51%EFo6?3JWu+mK`qYnq|!GLC-g(f&UL)bm%F2*S6usnI}sn2 zD{X#pLN9Hsf?egw_VMK^a?c>S(&iWE>mzwhoXXXBkhEj`eHRbJY-YygJ_D znl2BzbkXy#1-}_Tbrr%mlMw8MI1kR&ZmTn%M(54Q*frg zi{$Q#v`{@)dEvd@h4&XkzZ=nW|9yAD|4j5ezxFrcuYGE$mUx@+Vc?@q=TAldJ&6Ah zD#y5-**$>o8^8}DoXbhBUs(SVqTh@3;q|f1XPgU&=zk{BS2|qtY0~kXPWa94UM^?2 zd*O2newtoa5zg&8NegkxU&oscak$S%zmF08H2jqRB%){gl(vUB<-d>9U-GgzpZ`q4 znNO?7=K#m&T%zajE+YJK$WcA7_W0;H+Koidd{%h$+Wz2n*BavgYvS`w+^gIV2$wk> z#d$qH%RN&f22L)FHxqsq@#pn9%i%})pHK9huXX%c(nabKEw4%_f87jRT{-1b)~7N+ zKb`0~9luF9>&fdYPN#5vi|Dx>GQue)!Q*n6+Z$YN^Y}K?>$tA)Vfv2({I$&S>DN&D za(jCN;TiRdFo2T_x3{Md&icGgIP>3z;)nT9Ae{NnCY<^Im~f7t=Lu)}rol#iqd)7j zgmBj9al%=jw+UzdM{g7HXMK3x1=IHtJ?q2ky;+}98pmLL8VP6q%LuO~`{eP5NrXSW zU8FzbydIcwUJuOpSEMltC&|xvaR7fhfN!(CVUxv2*O&VIdA$V3=biz3K9O*a&-)2y zKCcj-#J!eRL&?sW50{fQM1Ky^*Ao6M!nyy*-y=(1qjDc3de-wz!dXxLZiw~V-$t{E zXSUm7!k@yu%DsqirWc*XiFl^}t3fT!^xGgT;(Yo$2GqCDhKmn)qxaKGOCUC;XqrPx&tw5Kj4j-G%FJ3mbaY|31Q(;a>Ut zf^epP#o@|d=X*8<=w28_=Q=&}9WL}NSK9pId_E&RJ_{Tlu>qxL zK1UPI`jmNmR3EXuI6mBeXFk0|&*^my;hbI%6aF>?Y5e>lfWJpL@7wSb!Z}~7JxjcC zzT_f5h%U+*Oh;N&7{s`US_urmR- zv0UHy+J-bZhDykeZ7L+T42MjcBPvC8Ohr=Rkl~<7sHhB4nMx|j#$k6(PNV@PLy6*? z=1dJbA)$~$DgEDjt>?M?&Udc&`oGt;rr)~P{oUX5zU!Ozx4+d4oc;sA`$B)VjSIca z={5%EbO(ZSx--Bz-EH8k!*Oux`S&2^c^S(?=Hd1j^PGj{8~wY2GmoE#pgK_hW<>ub zIQ8d-&#TF&AL@Kbnr8Tt@Z9_BO7IuKdq((>2p~=J^sKXviK!*^+)maxy*}L>_;7x!!^dkY-&)Xfy82tWnx;#AL+G{c^XYOu!s+^P z71e>qE2hF{KzQ!sVFCC+@a5q88A^$#3 z&f^~9I+sO_+yCM^=RocKgum;zNBDn?tIqPtl}TvdKz|~<_W3#r{V!={=8oqX@_X(3 z@AA)wzNGDUi>pQl%Vyp@8~fvP1M=yXhn_wap`Q)aiBA^e$>e@~o`>AnOX&hK*gP|yGC(cg@`4x6Fp@@FsjZP0T+j>kXiSU+fd-VS|R zaQgQEr~i-$=YAijI~n>rFx|(D`*`bXuX&yo$L+j?ewe-H`HFW2e{Vcrk^Q^DKBqd& z2{*5^pO>XP^w|PEefAnp>W@bBXWDjGQeO$%WAwiOoa3Y(_$uUY0)H-NcpQ)Q8~`8v zjlk_#xw}1J}z(pyzu0ctoF7EEt5Qe&l+*7&zDCCBQj8 zTY~=qbvOcE5xlmF8=9ueZ-X;`E%+#ZukxA7)dgoiHh>TFHwEW@WjkM*9{R?YGLrdl_ z4bHzuoMT)M^r-+nmk-sUSDqb~rxrMU>cWTf+aRLn@2YUR3nTjH;m`HLYVbd2om`F` z0`CM~OfwUj-cI*pne@w4ad6I8S>wq%SBdCrLH|y$_T?&n=Y{jz8a{gJzlYckoO!N= ze`omj0B8MsM|}E2-v{*>3eKF5MSPx&=wAcZF?O%RYWV1BjdR0YXd9qctp}!QD)k}w z`oP?0Gko-Y_wCd#q33?b-UvUC@VO+^LhWco|2sJAQ=xd~bIRT=+&t&d2%j9`uSfW9 z@ay3J8~F9$&HoTygr;%M@zdJ4ax#BA=vB}2Z9B3f^sHxBaQfT?AC;;`cDM_zFF5lI zfDgy*5a{JI#C+}mr_TuZFz5Z?%sC!D@-Hy|$CGtX}LD9>fqkNd!x=Lme5C#yvGaA?W#bDD9DALc0y&OBA&qddJWPYrP9 zsSh9KX%W%4g`RmjfHO~*2=58b@7IV3pAq3JjH_;%-}`NTSAlbW--QpC8=ru)4m%<~ zyP#(sz5{0+j>1Pd{r_-&1!vBzl0g)j`ki@-8&8gt($F(cd2r^b2_NNIXmzUt&OA-v z!@9KrXU>ihpH9#-XAf}Z>g^7!=}p8;o{ z0{F0QOTn4*jfl^i&@<;oaOT_&ALabQ>b3)%IrqVbIgfxdXI80T8d`E3o?^UEc#k=$aL!j>_%Q#_i2iQqnSTs8^G|}0)@uiCy*34$ zd1k?fdFDs-&q2@a#wFm)vl09N>h?MKsH~Iq{EzWuKYjx}{SQH}`OQ5q+=X@oob!7e zKAhiF>CESn(=BRT8N3*IBV=i}i*_-LFLvEyR7;LOt!KFo7PMBfp5j)$(`%-J8D zIq!kLa+a~2_k%O%RQNFGtcX4zdggo%oH>`mM|o;ko@L<7vkpGY^RI~hL+Ckfw}CUy zPWUKK8_V-8IP)BV5A&Re=nJ12OhYRaZp?EAIP;V-?#r2*Ezh~&%yR*Jn5S+;-vD~% zX$HN^GZ&nBUVsntyc*HJ4n6a{ z1(Gg=Z4^{Piy!v&lM4UN9dWSD>(D?i1^$T(ccSRIqT&1)mZSn zz>8$co0^s2c33P`U|cn*8uYgLmnv+>eN}&*v*X)uWsOVUJn(YnUp2w=jMs*b{x{Zr z+JMvl25_AN=K3+BcpFlxe2*<4ow;Uw?;QL+~OShc86*Yr(ml_AWU4@iX`< z&uYu_74*z=7$Ai1)PkOQnnAB9Ei6x~h`tjz z^Yj2`p26@}eack~ccI+|J@br(UQsTyJQE}OXTX`~Z{WIWH zJ)H>7{niEG+|OEJT=S)feJtlH@ch8NZIAeT56(LL9Pv2;J?np}EzmrVpZ9Q<@nk<% z2B*)337>~;z8XQ#oGrjv=gSg4`Q~#KIO}sQeCXdbqVEAcr#l$@sZe}h-Yx>?`CV^< z^Zc&O5xyIo=g$F0{%we-e1$;!|6T&J?p#>oa5?kaGgW${{IG_jp=>@ z{sZ`4^e=#~4|=cXTKH(birMk%_2A62A3mI~L(nVF7JGdJoIc0l!}&U;LiliK8b4ew zpJCj`;W7LCSC*+A%b$M6eSU}7 z>p|eG=WzJ2Uml3)Cqd8k_;hgQnF$~DufLuHJ?D1`IQ?INkH*_YS>Z0UH=w7_n+bhY z8=o5^`Y)kZ&V%-PC-fRy12xgm_JebNk0gAS+3VxrtbZyy<0o&{zo>EbBlV|4&*`2C zeqH#Ce^0AKc;g5kW8BBj)%JZE2R-ZXC^+-v!$Io&SsVgKF~(f5a*U5i&paK$+3#J!xg5I@{2tV^Klm!- z8Eo9gp_+Gv{h>=KRFC=e*j! zU!OzIoIimxXVJ>3bkmq+&dT82FRKY&A?swn)C1@ExeA;tvnlgY$T4b8z-cYvZ1CxQ)+t&_9cQyas#~c$b9F9P{Z8J$?Fs z)92QN&j$1PEA;fa8=O85fU_SbfwLc{fwLbM8u$8ivwnO5diLXbaQ5S7aQ5Rj;5?3W z6r9I73spPW@6?wDr#=Uqb*KT(I@ASc9oiUI9kl(^-nLt=gr0Ti4bD2;4$e9}0M49~ zz?pLzICCyE?)~NKuNR1%%KtwP~8C)|9w{g?1uP5fxMIe%0zH1Yf3 zzaULBe&kA{9znr5~)3Ww);_WA>GjW_?l$Ipz*XAFGwzj$im_k%A5*LJ=i zbL4f64hQb@wHtc&?@z|PzcwgTXumuUOZ3@#*G5FRjH2clzt?N611^TXIOgka zaQch|=Qx}RPW^mv>Q{hs+^zwq&->u?Ic(g&M@wwJjwiTZC&aJYpgN?#kM_N&W5-=o z&j(S5`rvwRV7RF@HtzNGy`}pi;>(jN+ zzlZt#8k|1GWDuI_#{Ch$zJ&WPHveqsxxI7&IQK`IgLAsAjq8E?BkjPsKhhCC+#eYN zPXDp+DH5i!$F@r+LO&7xI~jVvwr%)W>M?NoJPH0VzF)K9{|NjGz#j#F34BACo{!Jv z;MA`I*Z!uTceEaSGW<6g*Ms!k!cFZX=(*qeMS^SFSMB=*uVMUm<9d)!uEe1g)xsz= zjVtlX^dhtp;3vXspWiaZ-N(M>sS41`$Mw~~r(k}0T@KDyJ?O=K%>5D5WxqEAm)=_| z@TVPy$1C8^ey?B})!~M4^ZYf9Yks%db3N3t=hE{$w1(i@!gKez#CX!DCG^Xn?*Pv0 z2)zh?6Y9CzxcqsZp_Zj;$(-6pP!oSO+&m|bx6tQPi9?f~IrpY%hI5>xY}`rTGu+(2 z7&zxE7o2)sn^aBnm7ji=xf=~V&qH_`dUds*S1<>hK1<*;H|Ra*>k<81(7yxyCUCAd zKZTF-`1u3d!Se(6wgW!QvoE4Q0zJ<+NOx2)RsH(}jnCJq0-@=FIm?3I7~XSzW$<3$ zjllVRX=*&#UwP1XM;;!J;&|Zi3(UrJ7r=-6#r)lqYvF<xxnZ{kEph*GzE!-p6rp zeWrF!xC`xctyDr2W6q}F%){RoW}Y&(-<{0U0Q@@S{0BJm@c01p@cND1UOEN)N8}|U z{2cH`$WsfvK6q#F2H^Z11nT=k&pdY;*ZAS`b|my%-j0sw$3ed>c=`I5``uiQCHK4g zRSq8tZ3cW)e?PvFkLhwb$^CBTTm&C3Z(oe~`1L9^P3H9dZq-N2lR1`i9sHTouRkIE zu5k0l{c+~}7tB<=+WGbTm@<)Kdt@Yn6R9{yy{<^OfKd7jI`>3@y!q<$w*`%yS5w^LrdVT7LGocBjs;ln)R!I|gbNS>+CGmrmWSkqNKr&>Q2z$ZU&Z;RkFH$d-~HQ?MY*aFV| zkUq9-`VKY!on zsi5~d%!i)*`3U#x`D>coF4_P+w{s4IbGztwaO#T{Jvm>@a~3$~tEO@FOL{xDO6K$R z!8slp!)I=gdL5ca^sS+v5B>GfYaEWUad;y*r`sDoobG^#-mmMgI&*n40zS+$4*ce@ ztn>Mr0nX*^Kf$>j`wlq!>l1MHm!EI0ob0b%(Ep6Og{SEjBBol0+h}zhn)tME=ht&k zZPmo5>n_oR9*^i}K<|D+Q^Dy^PW{Y?{xNXs7a?PgJy(6KBx%wB{1khxhbzJL8E;eU ziI{Ff?Db-B`J7{~H-P&-g3x~O682mVrEFg0T@w6uaGyuLJs(`3SuGgao&@*x`mf-n zGt%@ufA35_dAbXY>k*+|RwVu|kN83B!2R|)J?K`-zK^G3xhkG(yjp@^Z9Ff*hZygc z;B$;WnBabW@L367%EtLi37%(sQ-V)1zB9pB8~-)I4;tt1!TCO5nC;YXEN{;fu=WXVn=IS9WqV_ddBxi~!j3VlUzKTk$_`_obS-P%|Vp6A zroGn>`s&bc0zV)8R2%Qgasl|2;5EQ!g4YD+-&JdYXWO`z|ApYA!Tp$k-hRlq&(}u# z%pT}3f{$M}UjB8!hsz)|@w(|}nLGZyEEhc6#;Nr6z#jy^82l0Ap7VgccM$se(Dy2y zc_DvaR*NhKZ@`80yQANU`^UjlCozS6k+cQXG|Z6PlICeZtJ zn_b`E^xdHMF|LmlfH#HDG4N*KydH0J@W<@?r#yZvNN@Y~V8wOI%?Wp*)wY$2Se}5j zQQ)l{hR3nSeZKU!QnmM?Z=FHu`>hGRuRpSFC8X)LfxfzN_xI&~Z|E -- Initial build. diff --git a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/vlineSplitter.cc b/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/vlineSplitter.cc deleted file mode 100644 index 569252656..000000000 --- a/examples/commandline/spear_qcp/target_algorithm/runsolver/runsolver-3.3.4-patched/src/vlineSplitter.cc +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2010 Olivier ROUSSEL - * - * This file is part of runsolver. - * - * runsolver is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * runsolver is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with runsolver. If not, see . - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -int main(int argc, char **argv) -{ - string s,timestamp; - bool withTimestamp; - - withTimestamp=argc==2 && strcmp(argv[1],"--timestamp")==0; - - try - { - while(cin.good()) - { - if (withTimestamp) - { - cin >> timestamp; - cin.get(); // skip tabulation - - if (!cin.good()) - break; - } - - getline(cin,s); - - if (!cin.good() && s.length()==0) - break; - - // we're only concerned by a "v " line - if (s.length()>=2 && s[0]=='v' && s[1]==' ') - { - istringstream f(s); - string word; - int len=0; - - f >> word; // skip "v " - - while (f.good()) - { - // read a literal - word=""; - f >> word; - if (word=="") - break; - - if (len==0) - { - if (withTimestamp) - cout << timestamp << "\t"; - cout << "v"; - } - - cout << " " << word; - len+=word.length(); - - if (len>100) - { - cout << endl; - len=0; - } - } - - if (len!=0) - cout << endl; - - s.clear(); - } - else - { - // copy - if (withTimestamp) - cout << timestamp << "\t"; - cout << s << endl; - } - } - } - catch (exception &e) - { - cout.flush(); - cerr << "\n\tUnexpected exception :\n"; - cerr << "\t" << e.what() << endl; - exit(1); - } -} - diff --git a/examples/commandline/spear_qcp/target_algorithm/scripts/SAT b/examples/commandline/spear_qcp/target_algorithm/scripts/SAT deleted file mode 100755 index 84c922296e75c139c1eb6d1d858285e889151503..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1492405 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±PhvRC8PkY5$Tb@FKvX1fz(Dp*$d;%;u>88pBYl$k|c zHNHkjz^7EmBpbs2c&BR#mR0R4_w+hYLT8;YE+$5m6QWA>Q>!C}0BZ`~6^WIVN4ruyo%r z<6&snW?B&;*h|5TmR-Eg>uCT&PPm&vRy79OBAH*4!h`LR>Xq9d2c2223e0~3|mvouwhdg9Xvy@vt*BI zuetD%${qJoUchO2I5<6&`5&GqHdgrkWmsxgQhy0o4b=*$T^0s)546U+r2cKhN@$n17 ze<*ZAqm|A@l1F*z{4>6@qS>22!%6&5*?@ZSCYMPl|50xzS7{_nu4NQ_#1If^M;Zcn zUo`~Iw41$}LdvFOQd{V<)(_$L#QIu`6OnY$B|a->E6*#Wj zE3)al$$38i4k(QwK7R~1ky0N`>b-SeV&h=8YdHSH&TraQF%K%vMcST^q;8)Zrjq)eSWRUQE!Gh@Rwz^R+YTo!&x{d(0M&=t(PmsuFLnD+0>qHz zOeW$*X+PYS!JmqfP(rjBeh|-s@ha=wTz>}bFL5{E<>fQktQ>3tKc`O_DlT>&nK!sS zoYweo&tOsIE{Er#@-{bm zPUrIh?AOj6`b&&8C1k2Y0F-eVsC)!#60#DqjD9v+T=y*YH!0!C4Gg zgW5WYDjreq^28gV#KQOYi$&^p&Hu7)zPw-joyzU>Sn4#nLFADRWOiv1SoJxTEuosK zU2Uj9Yu+u#gV9`)xs?2Jzqs4@5(XT`6`(hwLj zNoX2ZNkHrAYlugjeAIMv{BV6*J@L{|&n|gBkLYV^r93t>^fgIGR$!++{HTj&7)=Cw zkZ4(y;@rB;Uq*im6^OdZ18*JP^bkCZu5Ya6Sc6KFWy&~5Tg-mf(6zlp><+YrilWhq z-De>eOg%I0RKiNnzO|Z6O|>sT`Qu-r;lOcD*V#O^i=dW{DxaALV3O0dJJ;Itj{BT!?Di~$(_|yV{rJ~vF8e~ z+S|WA9B*eX^QPf7aFSh;-}`x2mb3XeX>jXTmU>r)IvxzW(@K6LP1&6maZg0iCD626 z>@F&G7nOL6D%?e7(kFSgC=x4nr4Obm6p=D0sBKv34Z^C&o zuCBh?6dGZw%6B4;0n?A&3k>K!=O)HRqa z(~vyww6OVt;ILPgd&LC`m%`!Go*xN6A7faWJf5UzH zCAKBv??2)hu8-+A*N5o(A+|4l()D^~MziviTd$vm+?;A=<@qOh`-uliKKYvc9fTnL zq-*y7dXri4*hdXN7v`biM{{k~?8ob(U}@Uep+n=%jY;hw@t6OdT<-1QKoH~WWAvZ8nm zLlRPyainpEMFI781J_9UqV(r=A$(d6uVedFs=E{hf3+?T@`{-HNO3YV2w}{|`KGnM zh>eP2P)Yv$X{6SjlsTAv=0oJSDQkXc()@62j^-*T8<;r1oZX}Yhno_af4rtT7pvy@ zhgf!)9-4>%d6@kfgNN7btl2nvZTtjHB=cL{MQwI$vtM)fDk0~UDbxMo*+*<`u=z|C zvyTqfM^P3+p78>PGHd=)#Cp6GD!k|&`dR07$PU9Gt-$G?)T6uY7D`R)q6|*1cnO(Q z4>`DEO@b5)lMJL-$b9Ft-66&JwmZlS55Drmb-$dS&h;ufXTP&m3-zki?HT<@?AF9U z;F{kq4tFAjoxtTk_vW{H)7#wb;~!=~#SW<7xHyI9`Yp{1mn9${lr1GI*?LCR6Q#{C zMET{~B9&c1$=70h9VYVzVmoNe)qH+Fs|PA2rCX0^YLgddDwlT#AQ$Kfrb<4+ z9HAz`UX;`(?fPhQ(Gr^4Zl4Qj_oQj&zbJ^ZO~aeo^pm{>e%EX(4iS67=EfQj^SYbU zj%F0QGr}s(vaN*$lqSMab9RJGW{| zg`W&t4d*B6!SItfj0GNJEz&7@wi0xtfUQ_nCZX4EteVoO(bYoft#{EG^XC~pGd$PV zR6#O20VPhJs6s;ZHJSeGvnOrzS!k>Ux!YA#TTR%~sOYw8EIO%bo$J+*bx^Ji0=2pW zqqjN9e}h{RgDjFBpr=Eym9w}@Px%8iySSXS>z?gf=*<~O8U7PBjz!h<_D#i2 z6~T>1z@cnWTq3`ov-CMiU_)K-p(A4SPnA&hIc?UMe(9gyZy9xilay9tD$-Pr)iA%p zCp*gKMAX}hrE8Wju{7z-b;@QlR~wJYplL~^{Vn6MJoTlA+FyJzt?o7se(mYmaX5SS z%5fyRYp0SwA%MhLrOK*ey&wvcX}&d zwzp+Y=F38R5gwM6*l4WAR(xa@Pcu~(qhcnV7j{3We60R~`Nzpg8;pO8h+f+mz~yN< zzZlu&DF0n6=nI>gv`;N4P8?9u^>n<cMn2&OZa z7-HICXZ;J>3()AX+UqSU7oPn;V6U2fMN<@u$^LmAZyCHiwQ_C!TTbFnJQaT6?|RD~ zH~hHkOhS<&X~0bF6(oIvW|iTE=N)kr%C5oRW+x%*X_w1=+k*W-Lx_L3Rc`WK*@#?I zVJ3_->X7Bm3PMOQlv{~K-z!N@t{NMn!1(*+iy3Q{7K&k1;v^f!Y;VIKd!$@6?O{{t zC7R*sYcn!L%r_xs5O(TkTZWrHw{n}j!AO(z4=WIbJ$4?XDB~E0?IqUJ5B`CQa1(3~ zRX*YNS(BJ~MBlBUC*&VnT9~0f&U{hgZW}I4`kL&-(^NtO5khAAF(J(S@8Z??t25_N zbz2{A$Uk}@+o^{9OHD^$ptr!<^47}9<4FUHI)8@C!rh;}f*+5PKFV&57*_4vYB z>>`n_>AC^9;-8zQ!V_Kpt}eKE9%cd<_2K>Kthh-p8OU_#h#n2>7q)$t)NhLY*t^TH z|a$GiQiAKL6LOF1x9&b~)&2>QhNd!X zwt`yBw`J#M{=k>H{w^oWsOZQq9@#pS7FKX90`Z|6nGb-pQ?NhZYW&+d7aa@UR$Q6e z^^=UQ($6ghq#)_UII3^gG*RZYmtu1|$sdw0od>x|cx`kIKOiLzz6aq^s7jfMl}T$^ z9WNv;*xf6T08JFGp@~Zzf===ZbAi=v)|-r3WhbOiveWisf>j(mx?Vs+Kw6$i4c2KUFB5^+1ezfWX`<30D5=-5rSq;|?-m5?9GodMp^cG9PE`a8^Pg%*d z3oHXm3I)T_a>h25L*gN!n;X7Pm$ubg|A_PeDazOhW|@|Tx!W|=k0A%% z(neaXE?NEzRIhn0bDA=-*Ie%c=SYlbf@EG6}@f*D974m2J+(S|2xTZIZ*O63Z1mhh4 zhR!^ZsYL&36q)rBV* z@aR$21c=o2BRw=~sE?BHW!xi1ae{{xsj*6hQ7&ZZCpI5f=wp73wUqf2FT1%j za-!xwuHXSkLjTB4)DoP|U>~c6n}yU27Bs_IycAqkNIM5_*ZLGvBuL=XnL4llcZYh|XqYFS(tUnE>=M zOGK+R=I+&>p~cZp(Spobq!@acY2nt$Zu-2bhW76EnH4vg%4(L)UYE9*E2t>Nj3UQ#*WuE$-^mEed%^#q4JbCiT(nUWw}pAMk?I=^ApJ0->ovMz77LPmYH)x*Hy1@5w$xNHk+w)& z2rGq_u>3*Z4KWor*0rwbIX7-}2egiM0D3sdcQA>|&OH zzTPQi?z`SurS+RGzsOrAC5)kH*UK9aX%Qpkjodq{0?g(q<&|p_JFihcEZEOY8zsHm zeFOz&BmGnEbd5%2u$uR8#j~08E}??~8=xJfFT%fExgmR|p$R;jTrH_++j!L%gS$01ue(>iJ~XvRG2aTGa~=1B#qgOX-ljw;kLwi!wdhpsX3rqLVO z^)Hoo0%NNYnPBav(x}sQ3sg$4`yR%7FaDC{#+g6X9q4>TaYK%b@W-8B*ZIQR3Xid= zb5Kv+;@CJBArFbv9%eupNcsL>AMe<)nOPc#&b zi~ZECjS_0$m((o#W1mdO)9cdXO*hA)nb zJ)?Lk*)8I$c1DWvpp^ma{WRTFN+@d-)$|d>v#ejoS`?veF@TIvVLs0BgVoXN*Dm_pPpB=X&ub0MCnS~@3A$H?;br?(STGhLH`rg}i&))|J z`->Z6Wny?LYBUlxBXa~&3VQjXR&&$T>=xG9Mp*Y-s9}4D9rr`=lnl!xW3=2WuIWEA z41=B&;E(lx!0a`H>0`%Zh%_}+ILQ|wkge@`s;xmoX-Z6w`F%+29WkutvpM%6-Rph! z*DUfe`8S#Y(5J_NzDD!9uaS|ZJNt2?btQaAUiThE>XCe!Bf23}`c-3)@q>NEk?4QF z=SUGfCZ<>M$?FF#QBNb70Qgh=1qotp&}`B zUh}5hP8hM9|FZC@;rrF1{9TO*r@rRL#r3I{ecV2#EmP(NPLkC${JS^sw`1qOSU{(9 z9o9aPf0S?R(?tG>AK3Y_0{*ytWWuY!h1~>3y3+$X&icQ&;Wn~nJe7`_^EjMK%VcL^CLUPI}B;L`DCKHf`fZYxTzaguYWsCVk)-btm$ zp?8ie=Wk#0gX9=+@6S$X_*(3khGSxt4adfgY^aKjZ#XeFuHiW1@FicMDfS2#g7h8c ztlriix;z|v*bDtHPnSctBH~*n*Ti2+T=ihk-FVGm-pmIT?R{fv+F)f*Z{RqHi%~#e zrK{hv73#RMC=jpk_E!~f=|17j+1Tx@o>4r?EB)^B(tSuzJiOOQn`+*iJ~t{K4aa$_ z8dv=dPi26HB(8dqtNVEd->mLfR2qi1oQ!X{{r${EiPk-}H-PAYb65Y5{W_!(>-6?aG&61>&@vm>m zjrM}8pL3;@2_T(Q7JAJux=WUG>oq^;F1ePAz9m2OQjB?RzldH>RTw@ z5~JKro@eW^$-7{Ej**s<;~e*FQR9R#83xMTE5`Eza@FuJ!!D5Z z-Cp5&;uKu+OwV$yU}@*<(xKUU@O1g$ubyd*CgI568oyIccgLDn+Ya1my|c{qm^Yrx zmKg3eNR^PU(eMYueD@cIzVR>Q&7NMvUqk-w8iv!5fBPE|DqWoUxj#ST*QdK}{v!f_ zpHbpf#x59wJ`G1b8H+2^f*Ad05wk}iMkI|-1h)gD`})a{eqfZ`vHffDcAiF8_4kHj zXW%wP7A3!_u4@to%1J8D;iM)s1Xd$BZfAK3JM2vSj{dbp@hgBc#UmXr7sbErx5ItQ zxos^oZhpSCIUa0s4E6US)xM(Z=@lGdnM8WC{Xw{^j)(Xg0k!1&Ql^)64Z35U>!e2f* z&3EGW2|mA%%{yB%au`{?Bv-B+un3N{hXtp0Ucc7Z3!)x$=u5Zns}|x>nAl!k|D<#B zldM+x8D>_fxc$mapBtRK@g5P8Uyzjj06jcTm@(@$J|vepp;X-_)t+lSP#V%A4Pm`-`ws$-^jB znLSBnxc;K4h36Yf`vbFtgBt<+Ieo?tAUMtkszJ34aR2a2{6~wU6%!ZX6N+*cHcrDT zuf5t>;um%+yWmEXTPSL>V}&WbkMiQeyI6bd^(|in|6;aqyKe;!rgE3$LY$z}~NUjZsUijJ%<#dFQ*^Vp5UEi*xvEDy_VdSSPbCl!d!>i*&mC)p0=j1N!N zlT1G_C|z@T$YfX2ecu>Oex-&ZYzVhW2a82wM~)b1RgA(BY=^)CCK>3se#cSja744ZE_!K=uN4G@5d+B*4D3!mt?+AEg8p{ zut_O8DL}A|B#6K6Zj4^j@luCdyk|h!pOMgvg1%9zpxXKI$HQ+vC8mp&f@+tFEj>OU zbzsZ!?`%)3uP_1?eW$55F>1gNp~yOS?EK+>Wy;joqP4!hk*_n?sl#o$z3hc)pzw^r z3gz?vr3hq$Y#B*q{k?nfPoQ&jI9*?Yj1efa2M3(@f8v1O9h~QYiQfL=0G|QUD00Rb zRWH))VBOZ+K@*G<(8_> z3bTc(t;%bQx=3%NyEair+?Hs;`fj^2mvATDtY~LgMuyu`Wm1RTS>~IT=(+j)^Zp}6 z6)U;C&KrwP5wG;7m)E2CO^?hkMoa8?dAosepevSOfQ>Em3r(Lus_Zj)znFC}%O4;I zb8Hloe)hLU!euu?hJ1NKk;J~z@!~G8y$ZJ)h2idRkGf4&nsw#|YG%C4QpUREh-_e1-kh_%vqk<{?QucqW{xUtvAz0T+ z%~6oy?2_I5%OBnzHU&0?>(|mA)6K)%(*wX8fjq1|rK0iRb0f6pWz(LqLtRFD9`QS^ z-L$7jR(*TO^pe)I)7_{@L%E!+@-z5LhHisDdAc~6! z+hp902Nw7Yaheatx1^d{q&udXPN7-M4wx#8GB__S_rGnh-;bvM3&*D)ucIK(67k9* z*p_mQ43W&r1v_$H&*Rr-=J6}qaKD(7=zvt{lmT=VdG~8`a)Wh|ZMUOy;9dQe`Nx82 zd)7Jm*~ELLmrub5UY@US(Q<+gVJp>dj=ve)5`4xzGgSF(*I;aa7hr8Is<-;)&#Hha zzH-3eD1h4GP%~W~pq4=cwCl^$b2odRdgXBa6AHShT5N0d7Rn2;6=`l_OwkY*6taLA z{S(D8;Cd{FX>qs?^WIQT&GS zAQ-<&pn~6!k-_QAbp~V_t4ek2vG`s!&}U3GiXv<@r72X@pk@<9LGHGE{7yIXUsycR zd4C}A-|x94eT@x#gZIs>8TK`jq3#S}@@{WSw4C?bR<^OsrmVF;c6w+u^TKeC-i3XQ zQyCtOEsXEd-Tdh$V;j)Zx91aE<&)LkQG+HyXIHOOh>(z1mWptd=@Xz62KCkR7M@Mg z3GR8b^8IZaVzI;aK!Q93(6%av{j=?8sxS@6&2OdwZbBMg-^sJ}iekesh_FT=s7rGb z#87=zSM{CA#}`KGJN2dCIb|;M=y>i;23GyN`zU=-v&MJW?6%%w<)M+c4R+Q$$c$6J zPdqHU_}ffo5Y+@niJeDTK{(jEuq*H=mK2(p&O;|GM1y&vk)6hm?hIf#`vvL}Lm*Ud!qQbZJ z44dNt5#xRgTW<$05^L-EmY!!+U5wY0Wb<8rJXUNDc%YMq&N3BlWrzBfQ($#Nm z1a?XKTb({Hg`fShdk+=vQMwXM1>NC?3fF|{tk5Ww_oyXO-%_&R@DXS&-O;duGnH6> zv3luW9p(&y0?PrzV5I1GPS6p(4Qs}r1Hm*EQ()wWEkm?7zoEM zH7ZT)qMCli@=@<#f*eiod?)!T_cdz@j$Cp6cp`(G<;xa#UYK#O4?0eIA9@Dj`;c!i zc22(M^7SW;e9nVyRfOM;f4w`WP+NLyKZL{;Q-ZG_5cDm*@HyNY7JFi3UFe~FDj zU|MeR%E0eso|?bybKR!dK#1^CHl3g8ATO=oss%qP%x zl4k9?0z8kfJQf*e_wHcb;^-}|-?^Yt&V?DNDJ*Y<+-=MG)iMelvxhT?gv57{9^)i8 zQoQ>l{s?b+xO0s0oM7l$7Ie-H+E14 zWuv;N*}@`2c?$l zk^7{J-{yX^-eMRcBk-E)Y3>U2FUvL*;LE?=?|0W9cYnY8`8LMyR$veB{=MOc#@f7p zO816w{PI7(?xXDm!*m~$%|9u6rjp4p`=D=>yy%cH&}V$-_A0k7vyPP@?dr%hz6?1{ z()1SHe?#~C>(lg)b-xH^VFhW0uK%QKoA&p*KZPTA$>$fkzFpV$yMNJr^Q-2&Gj;9h z+J3h{_XD~wN`Fh&9ohVMN9%q&t80|^8C_pwH}y}^^%J_bc^t3%Z{h>nX|*>u|?PS>$)htOV<$=?fpDn*Zt|bw{`x#?k^W>*)p^rC36e+x$WoO)?Y~w z$XVg~Eel2|?Ac>pn8+;HN&@iI=2|cnqQ2M1O*zstECKwD7BRnw52rrcV8am z?FHk{IRJlAd^7`S@cTEa%dDXx1BIE|=+aW>Gy5v!fiKU{(HSct>H1?Cg~oG`S*qZA zM*j2{g=1;U>UNCJkPbwU^lX{ZI?>(>x4N6|ROu_NQqezUQC4=KcXAZzhW zsm(hBD4R~w;BqzB{3f0*~2 z1X(1$j~Hn(`28P}$_{;^!zEwg4%uqAYRIR6OF)9=$wfAmOE4-!sGO+r3C1AQ&Iv(P z6&}ZE!<7TUhYfuiqUl$DVwTOcd30!Z7a(}%CMFDsm_A`EQ(sPg5g9OuSTjE%`@`ZB z>#Oaowjtg?{+GJ#WnHgv>b38jKqt+59k#}I^MH3u+anbt}X4g__?AgkAj-?^b@u}?B1%-&ZH)FkhVZgaQoPK zTfgI)nOB{r5{~8Q1AfDAXpGRmrMeGA$MKUs>^BDP5QHu!Pl2?_TH7~OkjYv|lI$*-!#6haxGiKK3mLJIDBeeQ?6ugY^XbaG%#pDeVT8InFyl0z9(o4S=k1w3sUkBGDpXLv z4|tZ-2elkf2d@;KDP_-+S{%hhgPSJAzn9e10iA0kyAW zy{)Lv-wfA?CB>u8SJu)Ak&K`*d4F`fd8tJLzzOuSCMr`;{QM@pyr=mcMheiC7~}8m zvHiu|r`>+$KgX=5Np%f2*uL za}R`sL*c>B%pe)m-#>q>`#|Mmo$u-H@|WEQCOo#hJnX*N zlNry? zao3~UH!UxY6}g{I_{enj&w9t`00VGPuLZ|vS=W0H;K9#Do>}iF{!yHG5AtX~lQ(OC z4si!6HwPaWy}2vxbiGZY)OQB`28}W8=vm$xEp~U%0X2W|+mL4H;xt@_^;Bz3+fX<%9>siT8osKdElwgaKT^AbJR5yPgQ zHMqJgte*(El?!XnA?O88A-OMKav0-%yZK9I-g8sg z*s%4FAu1J=VUBlFgub{-FFL*)xVQdzfa~~GSL!K@QR+;67A>I!#?e`G+PV=DbtSWt z=Ne6f#s|`;C-EHVpl26@_{z+zGxPBPqp zLO~&Q&eH!Rzs_9&Xy7dSEQDLx!Z=nx{)`utyXSgXqj_$}y3u7yj4ty`j8%+L`kD_F z48mO(JMT9p%1hVtdh$jvxNG~9<9Cs%Sxn1KHOaVzsn=4vBBpHCZt@ZQ6Q*XX10BX$ z(Dik?@#9smBEMXBB2}bfPO_8wq<*Nnb^e2sss`O>DmTQ>N&(EGEeER&_%=%d?99}F z#YOQ4zRa9Rk??KJ8muwit~Nwls?&)xt+qBWm?cJs($BH!jTE4tF5#IiBD+$2_3>(7 zlCS;Z`KI=eHEPM4L?1=JCm&lbezh=V5IyD|apW3n8|3{J@|Sa_7~Mmwrh1cQlr(ab zyL);x$79!{=6aJW#8t2FDkdj0Au|6?-F@QfoP|KW&zy>_%yAQ;ycF_yvnS3V#r<2kvF~7^-nm-DiwEa)Jy<3Mz>KP+F8pJ z%>hXDT5hYkeMh&T2D;@i-w^c92nYLsbsDi0a8sp37tuMT=PXuZFIuq;)5&5G7N*w<{77Z6N+&35oK({08I{Sv`J zPSkL`O9^WRIc?)UOl9iTXX@2kP_MtnU!f6o?wo*oRm6=Gc%KmS5vaK@<>;TbEo>-B zP;i(X^0mPk(dy2{(W-5b#p#+y{z&G+?o3o-Ij=sG55*3%qqA%J>F}1|=>${bqBVHB z6tLRh>53Xr9X!1x>*KUouSl#b>D=W~Xsp>Q#;3Lw(01$fq@~O*>owqvTGlu9+S!zZ zn#t7+UGdwd-^>`+uK1*s_8Tsxwrb6bmQp1=7Pp%EBrW$1)uoxb7~JhjDJ!amKJ_bX zW)Hv8<$k42I;tHEW=;`1TyMvU9Uu_N43NwbRLLL6d3&_n+uuwc+6l5S#DFaIbktbM z_CE9`rqGrAO}sJybTPQS>*4s7so6E5?T-W>8~;H4v$3-(w^1>Ri(nm}!DZmT{a0HE z#aDvcq_$n7O_u;4F35zf7ulz+8a#{E|dUVSzHYLi}lzTj1B{#AoA zEaO$qF7@^|`u2U!ip4ZyrSx$N(bZM_;ysn{oa$}tgx$1n=9vPKn>d;=`nh&UQ)nyv zy)eZw(!WU+A(eI0`gmHLhzCgv(BzMlmhAY8cUcJi*L|yUtvTE(@#=y8`Bmjt9|C=- zJ|`NqnzXoRI-1aqG|sF z4fdDZCDu>H8t7(S>&ui`m5N2b*vqv9vk|@gYVlX&DCC05fA3!y#fm?&ZNY)io6f4= z)*Gmf_mjgHz5N&7en@zA1_tm(^M$-O5sj>$)9jj(u_EIx1$|Uz280v3vPy(m7O2I(_G&;T4;1D)y!_#)?QhH!vw) z9s73w)O}=_mP5M4g`r(W%G2Id=)lBU0)t8uaEUD47>;l4{6JG+jDv_miH$`d4cz9C zNZDMS+sXpo6-o-$7EPo>^{5qWD20uN-Us^<4+jZe93wNl1*_BVzB z-KXv>nFR}e0l!w}t0c2e-?UFF^z>(BJLx;6PS*=+(3z!^E-P`m?&3PJup|)syo&~M za;e)=l3AdHXOl3Ygzcr1&MAq{HHj*f2&W>6UL{feL$TwDp3kj!W{EqcluX?zC7Cbi z)dR85`mfjr#jAGnY89{C#!|PjBvUeU`BN?#H@9m_xeXCwr<84)0X{`Af<0e7s8{d3 zIrM6E{;QVrc=gF;w#!aY}%}84bLYX90H| zDa+8Yy{XqBdt(Q&R)h-XHHH%}33ToMo^NQ?+77cZPc6e4H{h~)SE&N+KY*x2;_UEcra z|9Sb4vvZ$kW}bQGd7gP@#$6ybg~+aN7~bMcb#3+yknpg)4Ja>_!?FxGQk(z27#O zA`3#`C>UIurZJ*HZ^5pUtiqiezIPDz2zZMJq?2Zf>AW}n2CySrv7aHi6hN7C42PLM z0Xx#8De$hpSb$n{KkG7rVat)V>rGQ}$4Hc!0><)v4)cKULND2aD`2n?#90EQT>Bt~ zNn$Su98DXHyRZc~RobtKo$8kpFb)M^-obYc2X*B-dwe&J0xeBU;)+G(6s$O;E@>b_ z3WsvKC`+j)38q~|uD=cYJ69nJ%v=E{^&hhH0#`h}HPb{!mSaOqg=r07S*!AD>Q2{{ZoSk-MH_pR64q70VH`r|!f!rKLAU}dFlO&Kk;fPHj z8`+oWE7!bSrP2?4wN!U^0aEp6_S2Y11;DNX?ZyJNHn(sj$nMR^0;LHlJ;?Q_Gbp{3 z!ElD8g)|eC9LqueRmjkefm(t)SY>^!#gY!ALI@iLrm&F?A7NnuIFHB|wV;5|3ZR07 zl%@o}WRDJ$tMNFesk(FRL=^9;DpCSE7onMU?Pf=Cdha%MJ}L-YiXt!l1q45?A-TZ& z-~-8O`ygfMBt(GZqj^P$A@b*=nPtHTujS5OG1q2eBhgNdC&mzULx0gMAeO!LllZvS z67=}cODD?xrBSJJJ^B!Blil3_N9XRw+k?0obp)G;H}8V>LfB#ia2?xy?2y|Xwxhh8 zt(2bW;Ll2(SU6l8ux?=WPy%va2)6HDG%dwy+DN2j)8^Yn8%YEt^58ruNt4yKLGa`& zOK~9rHS0~SPz%`n$!Jr4!u+`4*!D#yvE>Y<)^$v9GH2_eS~d=5KE=vnxs>zvxsi^X ze#0@Na(+XkgB3TDxTFFIBSE4cR%jVKQEk#e;Co0f5Aqs^%y17y&Sz8*R3ZM?S z#tp`x#$7ObK)b;bY&Na~>$|LAAI^1|2+_j2-*SlmeS5}yW< zRu<1dcFCK*PM&rx6X+MfM!~op5unvyl2$(l=+-_2?qba|Q?P=n*b@##tqey^W7M>$ zsNaX99wB2=j;Ict%CA^WKE@$b7A!%mYKmA^MpRjU2uCfOj3^tT)=!5^7RZh{864q| zeRDNRU4I!WZ2t5@5R16r?NpE0gP=0jX@O^ciA~|b=)(1PG1(i1cY@P-ob!G}THzgF ziT(p_ayv?88C-Z#gd7iE$f!aWVncIy+!cGNH*b?oTD101NI zhPj}>u*-MD+$R(@IUF^YQR`_56vkr7-~gM$hcd22&k?)v6UOcdlT7S}k%Urt4K*tF zr5b(k+eJSYJ%E0GmaQs5jmE8j)3wuA&Kpm_5qiK5hM2-Dz};IHA4mh9gEvk4Ln zBTAWBAM87GO;(NVZMNk zodN(&v^*hMW!qu>7}GfH;G5mpugI8ej|nJ zuT5V`!nawdjF7tPAF|4mXsW*P;NQ`8M}@iQ+tiE758;G_bzj-1PA z_{ehRJ1@rJf$J@K1^I>xqsB-Gj^5sajeoISntRl=C+_^b$BS>}g1o8F=0iTGkq!?4 zL#(dskNK=kDk?#q11hx)6RWk> z?i*lSf#l>1ig6(ngK+Hvy2N#8?pLn&vHuMaG|1R6$AJiq0`R?)S4YWLotUpQ-?a+| z=uswW9IY(D1w+K0qD#?dxIweP>?<)TUrZ_xlZxaMr)M(Un1Xp*Or%vFtEt%^FNRzS z&ZWmzYJ?A&dY9lhM@VF-+-|Wi#~UR5=OQbl0O-)&K2ZLb5O2=Q@oAz~jCUGugHp(L zI^^vbp`lim{@fhoGI@p%2wO%}gD|~wUvTu^Hf)oCW2!lls68Q(46?V~wQJ5**t}8O z6XxC=3f|-UO0?DNa|B1*T4YIqUH-nP4;;D;V$;PK(!eg?*V?_Q=+;0R>J*z0cl>=@ z_26bSJ&ff;aNYHp-~A!c6)eoU4!Wyw424opw1-Xkvp zCLm}ngD?dVG%n3d^{ko7hoA{5W~xWcRPzuth0-=MEIl1C4vV@$lCr57{|=-q=y?#x-2m{7ACaP?683O#_}&(9=(s<RU0M1c^xT`)oS^*zuNZ3<@2(25;}2pL1py9cH+ zDd8n>ESleu^DdX1_eF>iA6^RnM?HZTcnh&IsAcDdnxO=CVi7m)!6&4@mHT{UhK?Yq z$jsYg+1TJoQmQgLw9zE6oZiY3@_nYCQ(IYjBgR^-o=XC2`~jtD6YXNJdaw;72FuO@ zlH(HG(w2%zcqdQI-du1tj0dWWP%q!y+z4&)ar~8d3ux9|Q-|YL9wxdoJbu2kb@; zQB|s%m=8M*!@1L326aw>oTFZBo9> z*==j%=$o;)IBp4daIt?Ue>JF{_qBoP8Lce+Cut2Pps0n?p2L8SwHcvd*uY^hQEHdC zIaeU#CWpAyp0|e9wmi_ zT+})QD$P-Flk0f|V$g7-|vltJK>^Oq=Iz_J8cv~+@ z)Z)HeTfJ&H)h(e`d((oqw5UfM7*bG|Ivlw}?zws51e1)gi))Q9NY%iT9&ZrMkrhim zkK@pn{Q@i2_c*ywH@v4E0~@DqNyLLH@f3h?`|=38R7mLexZ+X>r_3&4YcCPCw~RwCz(JMgLi zrd2*L&DS0K= z!D)`;o1iz1^OuO-*l7oaUQS8{8M-j>4LDZNw;*u^-5s{!iXDl%)gU68fch`2iL*!&eYT zgn|3j_9tWF7W-0HIdbZQWr-xX#kMwEZLq9gTuwdA)boi5wi=}hXb>lG_5)Mb!c;lK zl}Z&6;hoIBScD{9Xnzj_BrzG9v0||`N4M2Tyy5(ms3Wk;*dPIx;K1+@{ihy*9Xn7G zlo`siy5WU#a^g3YlT=yK2Wd>B;@#4oR{sFYjl5vg8V(CK)S35Q-WFi=5-G5T`tc{Y zzt1G%33s3$pZfs4ZLA>yTl?tU2+;QdXW2XE>4PtcQr?dWD}Ud{u>z*|KJZ(%`;k{1(7;T;t2Rr2MT#4Lx zTZ0rRQZC-(!ZSuL-bNGfU>B5U#ugxp%b>PJ?#|&UXJ}i-NL-&6mH#*4{Bnvuhy086QTJ)uwBr3b;93Ks{IcX-Kv96anCi6``Ucgg_Q`1B=2&5sBBonpr3UgAu3i z9bv8`w0p#pbAq_hOWGQ9OXLA~Z4Jt!pn=9o43U&OV5-dhB&YgayYDA4Z>f_z27#q{ zD1ZB27={5q<|)BZHWQVGKo=rvqBIWbWy}CU;HSu;Zghy<<8eQ1;61Cp7@O@#gcGch z^Q7uY-hey=T4$eWFoM}ufGLu$ELngVsh$L1Tmr{BsVX_KS6TWRgKK)OXMm{3Lav^0 zDmP#c5^t!FKF_HyJxn^y=OVvFioExN)7p+7qel5DX4rjvcX0TicHC;O!_*|_5Gp`z zY08pZ6swh<5;ZWf&DpUs9@7}-$}d4EIMQ@B6E$RDbBXC-H>=hs3K(J`Of9q7ZFoOc z=2!P6_o$ciuSbJ8BuY;aE`8KzkVGHk0WrQegH60N*d}hq2_XqtU=RbR!?vl!jtc{> zN1K9U@qE{uYjWBkfLhfDM-PjG7-}+xRvkDGj6AHf4ZMl0O2=~0)j2nu$;IACB`-Tv zPdb%K9tK>)ezfEZTeWM$+>>I1@moYtF6GE<1-Q9bSVA}F8y9^Q;_Ga?AjYEiat>MY zJKPD-E<3SCr(taB^7I4Leu1tMIilC zWHI0G6QWKTKgHzgxf7uHqRubzWmsO=;sjF3&0zI3_WEcYh22ltRHd{9=x^R9^aE4i znaeQ+wtZwxfj+?zHgVLP1iW7;_9Uo49<3Js{4wrAG8uxEUy8#Rh#yIytvCV~( zk<&pD2!8ZDVH&2jBs$*knHoB8K>I3Alg-@gHD?XRg)>HO;A&XOwcn8;c5PLb%!iK~ zro0_JSkx1HpwKWOEpYvp2!dwGbqHR(oiQEWLM&_I9Yu8k6eh zs_lKbOfGqjXbz4X8(=t5s2)nz2ap4Vn-)7tCAXopk*9*iZP1QZ?u1V@aLlkqkC#d# zZjGsJJ(Q()Fx@Bzgi$dDj#M!QC$^npjAPD~Q0J&^Nn#AH4NVtgoO1@_947LJF;Mhm ziZSVU(iZo)mB5@OTa;w7|E%>6nutSOFg5~4N$<2<$Vl^eUxs!|Y9SdTa(RXKLz3N2 z01$!cl_up`Jf(iCV@{FAO)=k4*Zq)ZH!_XXm4h(D-)iMiAK+F?0s=SfZ=k$Bv^qY`eWj9d;krlVEU;*f4i7oGofu3djh`jMKBwir~+o z-{k}ZGDJ^5Y45c!Iq$uYj8fY}uQ?D8YIB2KExQ!=f^AyHtsR)4Vrt4eQ2p0K&9Mtp znv8~RD{OTk(=Vn;3LuQqaP#|z;H7+^Lq(Mxl|smW;!`y6ce4(UUL zm^%ilq44=L765}kgj|5>zV<9I5Fybw?<@W9V9Df#jBBqzMDypE(+My)eLW>Sr_0E3 zT!R@Ciu!pts)5rzBPwb{IO;CuvLVW39n4J&y%@C-wrOZ)sJP3*c`8`kJMV%cIN`gF zXn^4(4dT|;Sh^5kjjC}`%o0|z1Pvt!@XNP3jX0!|=c%ELyOHOBM)DkxKOZloNxI?d z5!!fv6xRAC!7d=)pDVG;q+&HFKGs71Q4T=jn@OBku@by(CJ+tdjjuo+ z*!9bB9udX1?#4Wx7+%jiuI4xc@)Jav`|l{HD6hTMXmMl}B_(|bmQrM(>cgbhsB`U%5Y^Da9z72^n(XNVBUQZ=(1EQ^_ zOFL_k5zj+hG)LI5yh6g`@4I{z1SJAi3&2(|RO_`1L500u#8+9e8^MLW7_KaS7jEs` zO6J_-n=fBy$lKD~m(xZ2V=*8tl#w{LQ6B}a^72^_;Xa8r^#&xG$_y)5SQseuzv~m< z=q{R5aS`^lUbpzbxA+F@wVgIDpNm`chNwNX1(~imlqX2yrHS&Ck>&vMC&1 zc^69X4m>Q<0oHTbc${_#@Ttk*;%P`~>bvwBV6>^^`V!fLcRI~Z9T=SD38vze5yvoT zM0!)IxXI!FYSDLczE>VaTGg8t&n?a-IBcdeO#LD`xOfT*7Y|L4-*Nc*5^*(VH55O2 ztvNO8G0maTvI%!F6lzxQ^+H%&otslVPGs1?;+*IJJx2Yf7b2>2xIC|`1jZn4c?B9M z7LR1D=h7WjfEqHGFG50^25$rUHxD#BU=901UsrGSLI9>j(e$}pqD|f&<8kDPn7bMY z9GUQWtL6E`o@)O!b%_OVv5n@r-lqqqTBmuZ;52VtVm3xK_X4`I;Ho$`)gkG9T_Ou$ z^I_AD@iy&2Gvu*l&yFYOh8y&;uL%(!1k;%IQ85+A>N+53QkOii*Z5-is{gO%@sc? z$E)QV>wK;EjF4I@TT)e@AOAd4RK2+tc>fp$RH4u&4^%iiVa(zPb zdc79i-CR?#(00B5*@}hnW&Y(A3lqxxf2>$&zs3Jp#lplJ{J*YPm^9fh#P_IJ-IVOB zQdW;mu7q597mCZPt~fc&Iy=heiAE>Os2dT52!FH6jc%~LS0kW3EAY+FVL8OcOVd)& zI0v?V;?%m{BzHT5IL(8r>)##Aw}q79$Bj5-Qisvp3Z^f7Tb?YTW=90qu({0zYw}_4=s`6JVoBMQ6L-fUYx^GHs~h2ds)i zxmH;PUt|@0r!2T{tb$jV1w&ifuFqw`mv;@sT$xs_Z`aG2(jE=MabiCJ?_mM{gayKi z+htc()|lbp_`h25su>@64f)l&KCwWIGeIP0rZ=J(Lu1-d+ZR|B6VZ{|{*YDhC9+`O zJeC-W`>hq%+p5$pE9F77Gdf($k{ynPzg?fqO8a*W;AAr`M8UTZn zTqRd1bDi%%VX{y;+-vDv&gJ29#88?9c8>2I6<-_z*=R#&i=mC7^o9<<1;6qzpGXly zx#k-UaOx{C;>JU8S%6sly+v=)X}KmTX1nw`NW7@<`jWEYN?vBzA=m|Q0Xp{TJ20k=Mz_PO?e$0F`f&toIm{}rxL z369E;{BK?2Of-n&fx8{Tu6dSg1-DMt@Pq3a!m&Q9xaKL6uUQ~3D@anmzerj75(-i` z6}a9}Dqn|F@*Bme+O;?}qFpwR<&>tfI5ETWi=hm~nN%J!sKyCj$D$Jz$f;DOqTjSx zr?4=TptZbe5S9xTAhs#6w_~xxLouYnkpL>zIX|BQRVj}f2zq0fuv=n7DKPBO; zInp;c<3Y7O-n5dq@rdzvqVE#f|I(0c(aD$_>&0eDynC)X zfhU12D;m&2nwkcW+>g-IbUHajG&O@x&L&OGq>~d=Q_1SD;0)B%Y&tnTG<67_zmiUF zmdOntO)a34n?9OaMCV57ET*#_&IRe(I`Zq_0v2jR$=*8geoB;q?gP(SJcy|qIYL`zbLw(_-qNE9f zy><^)mo=Z_qxXKRGyTB%6nU8o7-^`OAsq7RY?`m7D}hR6uB$Z_50cT@oky;K`mU+nrjn|Bfa7PWJ+fE*4OI+wi=Wt(F5uBfQ6q(2?ea!rEZ{8$sH z>xQUQVL|_Pq*~89s6Dus{mC~1mf;$KYH<{Mr|WR?2~C5hlHo=#dmh_7pu@d zgp7p>oU_sAg^Nf*ZUiKs0S&wA%d@Q&ii!Y{&pmB>djnMMjTmMO@C6)THGBdnZYl=& zvNu8#KBWJ#1Ws(@NERFmg%gkQw);wBE(7y`b4p+VYVObs1_Pow2q$`|(DoR$;5()+ zTQrRK@=#dt;V2Y$b7U7A`Op=xU-WgG*kAL~!#hF)5o4w_eht)xle;@1`8=ericAyz zIT)jdYb`^qCPg#vW93~syf~BMntlZ$iFQimVxUvZ21@J+yqRvHf?l*4NM```=L-ZXcA>4>p5qw6&ToZiRngbUI zxxV)AMNHlBowT$97r0ZF7)s^Gd$!9-A%!HDa*I?*V!50YQb=McO(29Mmdi;Yg(Q~C zNg;(KmZl0*63gYJkaa4JNcm4lvMIO9g(__fNGpXTo6AWng(REHNh^gE|Cf{I2&v?d z=U9cx^?o@iq>#i?YA1vwmdi;Yg(Q|8aD_b#T)7tyPN^HysNIl)V&r_jyeJCiuAw9I z-wnmNFL-O4cGD@UJ_pB8edcWejWdQr*q8H+yc^`z!G*op1#`~kH78)(`tVvH8D!fc zddco<-9?SV7Z&<|qvDhcXNwBa#vYf;hXhX&M1Z-1q-wFmPa@z#^BuZcaP=j=j z_K>6s_4ox13;w)KT47URhqH6IOvE{=haSqehk z37slR{}5BqxZk3qF>Mr$@r*V39Td$|1th_~yTZkOhbmD$^^W@M*_iuVu#?)N_pU?H zgOGU{$E_H2Q{dg_J^CCF3L}Z(80})&@Qzz1#%Eaeh;PuJe1dG|B>PZm8j-($HS+VK zJD^q(ibN^gh<8ZxOa=-RDUam&WSBe;`5DOb!>dv1t5P5d&j%@A0R$(7D~v;0g!GwO z7j$2o{>)*(Zm!VW{dOXQ+$=2WL=SOZe>qtQ^bgi@4lC5-*--ZKZIKMXnT0b`z-PJ1 z`5H==M?B>|cgu0?a64U_l%;$RGg@Jz6~z<($00ZtPGaSM<}3`QA=myp_l1aX6^f5W zNiJ$jk+9SHKbq_RBSb`^VJ2GE@v=TS?rZj9w0J%_&bU*K+jNProsaJuLtrYxaf{*` zo~A#Y7T}Q*-Y+| zcM;IoP;wb28sCS!K;vSf@pV|gObefp(}2cxuY}Rq(hqdR-w+S0AI3&+2wOwg%U$f- z!&pmv%>=$$Sjr<5yi_a`_)A=^Dg$0vw}G{p_^B(4X^L!NxoLymkxR|*Lq6Y)L}zlT zpr7NgrAJ$I-%qrD@-_O|11zipE$H(V<9gV(d8<$LdRoV3+GC_IQC)v2ho2 zTZ{hTn@|)!YEAo)MeAps@Q^iq0QCL={&gdwLwI;nKlV}Q5e*sEyohviDIv|fn|L^k zXlCM}KK>8_h!-Z_#m38sKEuSzE1dcUtT2+E2da@7WRbWF{1{36@OQ+IXI~E27slIT z(ft|%wGUe?@wP=A(x-%~s3Zvm-k#KZozY5m)FCJ`C0PggHSxBH+_8zbv)HzDtT-|UN;xC{|M_xMwkln{B7_?TsD#zN2B5pS;eI< zN+1CwG#1iP{}>bLZUD#<>F%?TuHpEuVA&<@gKK~x7w@r<@5j-d#FnNb+Yck(u@Lfg zv3|GccdP^ciB9*8XQNuI4QAE|Bu~WVclm)Bna^Cl7VUhIf@m%U`w^TT*|0^%%7 zmbk~lF$f1fwGh1WZz$hvs2=>Va~nA-Tan$!1dMX|H*pWn^st2X^KkpD`;9;!TUqdj^u3^7p=&NQPDTIyP%YuIA^(N#=^@ z>tnH#3B{>7I#G%pAEUaV|zI0xyUHD=8=ZiHSiLl41sIt8%$K9NsDC4 z&TP)hw-XSWB>PP3|6JH@hfNcd&iIQa?&2~$6kqELQJT&=K{S=E~J&ptm|Je z3yJDR$^Z92UB+DG<2al8@gi$KQmHxwOh$%mE;{%fLPS9uvSabJxGA9wJ6E{br9b#~ zZ77)q^STXXX*S9cM{5oxpc69DlJ&ubfm(eDb8~1o+s3m38Vk? z`N=q&mTvPeM1Z{JJ@{Zli#9nGchF78GL?4x{pOy8UmMS$J7AWCIfFaEj^Or{!H0q) z(qXT+_z+4DrY69tZiEw;(e1F+r9_X&5e6RW$~11YLV3E4p*-1^ zY2;a<+>^}Y`K-v{tV|BIyl{yF6Vk7tXQ05sZb*O4D|HzCAm_IZPdQ}^-f>DP*+A76 zlAC77I<2H$jh6pR{G1^1QyWI540qR-0B_{gxzd;-uN6y1x;By%E<>JwC~8izioxYc zu}~Lw2hU*_Pr!0HD_b8+^u-9uB7t4hZSW8kQPrLRBUxe3(*OLNyl5^>CSpWyMxb?! z3?S0H%Xhj-w=TAFO-Dom>>+K_v(lrH<5D2URe->>^RSFB0ykq!LghQCM`JylBN+B# zmN*>NA~{!EiO_0EBPAdJB90IOUzthP4-7YfLgc3_0U`BUj2&Z^6^Z4cWU1$3?0ubM zpJgm>Dn=(|^cY#BZk!6jn-rF(%vGl3N*KSKSaIamS=~5|^~kCjd(bINqar`<&p9-{5tVF62E*= zbToe1`QrreOZ{sC25Oo-iGca6pY^evQ{zmILfZ4_Glt?qyvFX_G-G%FZyrhk`cQO9Yk@&^*zaEKq)87LdMOOJT{jKzyoq0av7wuv^w;yzw z{wex<>Pd`y*>s=03T_|xbc7$K``3_5tTH~K|Bv*WWt^t}RT(e)(-0JbioTd}y+4OLS^q2D7n<%r(Cw$&EaOi4E9p1;@d5h(5b@tj z|6ViiB)T0CgDv<6(LalR6aMSye?Z1d_)lL6|5cEvtg>bLCA^iUw|;O-$wsl`jhlhx>InQ2g*;O`(?V5^vmh~ z$aJ4i_pq-KZo+>c5B@UxE&ONv)QJBm-W+9Ml0Ms^(*Nf z(FS*demmVX`L^onPyfp&;5X~Kn*L4j!#;YW~2)q@GX>#KG=kaHy%M=DQpci{06 z{66Hd6&Gy9hAG??Ctz&ByP=A2@uxlHVDQzU;5!+tIo@nPefsozer)gLy<^)REVe5u ztxv?;i{4={E<#rVi3oU$zC64?X8S~@SJXy;ExynD#P-gjcV*JeNV*<@;ey^_is*vg zK){&=X=fJXe;`~4yDX{@|1UciF#G0CQ22*gnP=Y;ozZ`lQTVptj5&SWO6B)b{>-zR( z%hX{NxG|VGa6YcM3ns3*E!A;6aT&uPcmoF#<6aMk-~c^vAaUk9H1;&vRxJCJ`5X_~ zu8sHixZVvD%5U%{Ah*Wiut34a$XyftgiWNrf0VGk+)*t3QPxMC%L*p$yPy|BRw0Cf zyuWW0G^jiCm}O{c@!P;Z>{0P(R2mfKH@B~oqr5*MnEK8*6y3hxf-@z_H|F`g>Qelc zTfZgz)p`f#;DsdHhaNomvo)_iXZMA1Hk%D+>IVcD+MD;`Q0?xj0hRBG2H$`h-B~r) zR&hGcms)jWd}VcTAzm%1x)|t>2VEwct;GBTheV@=Ofc2y6y;B#BZD(-(5357ftkZK z0Td79_70LtS*$-*Y0QQ!<~$8OuqXGkQ9~A@GQodPC9e;-g3@!a*v4t@jg*1nZRxzNvLW=a z&}OVHG;w?#nG{kTFLV3c?}5MuEjBu=!>1Y@{aoBVd?TFm?0WOqhsB z{MyC4eff4-#hqymeLHsOnkzz}%U|A9YM%F)g8m(eUb!3akAo014Q&|>WkMCz?}g_+ zF(=J2zfiM3S>a2A4CTuWRYGV&72Jmka93dqns~-Rh){Y0H5?{~p*+_jkGe$;qxj(H z-{r;K*ztp;u(|WS-86GOz5{n_fV)m-z51|Zh%E46%Fu+vWssG{CAhj$T7v{QpM*S2 z;4)^P7vzl;hjRAjY;}F0lzxDu-0HJ)&_AaR3CCKlNB3_YetCPGaSdD{kdN=_40Hf` zoPP`G;Qj4kpsSLkB$$EQK256=6W}@t@XsOfyN(cKMZK5M#9anMKcfgv4j53^zLqMx z!L`1u2L=@2F4I53S4_w6=unTw`+Cwx8jADe8jkVXaq(phC$e|;xV){44#*jKbElZ= z!ZG=sVzR<913Sf}gkpS;gG}PRCe%TS2txW52q|bJeYWCD0kt`0M(M-9<$5`3SM++> zTFmwG%fVb7!|Nqh$5R7au{uU0>?qwX^AGBff3?g%B)ju^o7%JUdb=%wB}T8exQ@aH zaRl#0RJ|j%*6epq6+4b^W*3`J#5~l$V23|mv^cQQPm8TmGtxILI)b|#p@>_eBX~Bs zw3#xOdf0CMKVA!8yT5m^RB6ua5ZaD)BUsuS%7@a_%2t0uu(X%D*{St67wZocl{(wJ z4;F1_Ki{YX-llh}6+8Q>aE$*sTqPdP^yiM5DDZ_dDe5#JRyZTh<{4M+LM zM`gOYW2Rv;Q)(#F<&jJmL}jvf$W(bgywXNM-HhYN{9mw@{apE;FSFoXHr`g*>g!i? z$cb?H;t`Q-MkIt*2l8Lv93?*WZi>dIzv5796egub@mx+|RjR}RwdfMbd#Tg=O8;tf zOx*2bF~d?pPUI4(p0+ECJs85OCP6lAs@S?R!MyFPz^PRJ3^BzwL#c~jhW`@42XLAn_1(vNQtw3;nMu~fRL!C+h!F;$B&N0 z-z4Kt%{nJKK0gwl!}u=}d}#$vUvBZuzEu2Xh$467JOf7-Y@AJCVefMw}rkdr0*Ne`we}ArLRr%@)@zfS<-h3x=#8M;hRLrw9-#C z@23np3LMc&4{F|n^nE6M&6>BFKH5iVrAIXH5&EPVRp~L!%PoKaP3E=IgI4;5=KX@cnbLPi^B$t_F6le0c@NV^ zdnc{*sOCLNU#ax9YTj1*3Z(C(<~>Ot&9;=+N>3}Vc~2L>&nHS5ei44H>i28j{S3m+ z9SZ$i^L|d>*V1QbUW2|*rSB`v`xSlfOW)U;_iOs3Syt({n)h4!q?uId3C(+gz88=Z z)qjubzi0I?!O!q#86N72KfohliHx^uWNJx;@&4&Jp<3v+`+tn*BMRLP|LwRWztElP zAEA{x3*AnCF;JMe^qxKCT6|TD+;sJ zTKk#*wUmN-zq8;=e=1mJtg7(EyXqyjPU^TkM#&$4c1d|)a(FfXmneTc9b^dQ0ra$# zKjKzI=Eqph3rGMCag@s;m;} zm$7);O+9Qg)2K({aRYrQQS3N{`s4*lpFDgx~CfAhwa!4V#hHaxdqhG?8GvZ zghrylcG+M@$AWC656N3Id-gI9_Wf&ls~$y1%Uk0u zIct>lJL2Dzx6U=?E!;>+kxQw%(FSJ`#8>=OT`bz9%oQ1*yRKpTX8!mL{JD+olM<~( zU#tJjHvR9)uSc(Def!R+uVQZJ`j$QWU#~BEti_4EuhsntM1fQOBK1VVoNFxJuKzPP zS+3NuEB=|AM&i~CAl1GEAa>2MXU5^-9L@y$h7Pl%S&zc7QTk{c3R|?{zIUSelO(jY zYyRYoekOnN(Um5D@=MNkuH!G5&c!Ped>Qm`ou@~khsu^>%ibeu zZNEFRHs2A=XUz6QB8K5+yD$SXdq^Z=u;z*I6|3|# z(1d-0eYQQgYbIGhWbeBJ0y=g;F+|*YN(Tq|2b`yDwl%N9a?LO^mRtY^ty-EuFOx5^ z(z;jn2Izh^O6=J@*tS`;RVDGYLTUt{6G}K2I-%V;wdzS*@Xq$Rd(k&i8yM~n`NlSM zCNu&rqTUCE<+4BSfW91eT*)4pGXrxK)<_BQR9BdQ8x6RKg*$>U=}7k7EE5W50@7-WNF;BORuA80eY~QwpAH{EzoliGOC!kGncO66(j;KolZEpe}}E|Ol@4M zHqD{UqoM}a@bKt(f)D0a`B}{_wga8wz?<)OrSi{+!j;>08*qrKO)BN^D#-I1TAJD) zXElVpcvn90gxYPe+iC zHTPKtZa}dqFKueIC&k0We@F0En``G>1EwH+kth>z`K|%zPVo+RW&t!C<1nUUGRgj9 zb#pF8Qh9h6q3y;TgS;X1YT!Q(?26LemEsLi4w#-=Zk_mTc?&e?Z_Ak(QNnY?*dda73cA z`UZQTb>38%d{j!Ii&)gRU{s?2O1uMJr=?}#8B>#QaqXSc2N*W6z<#zLhpIu%zMs=9 z+FZL(oKkrTOG+@q#(B9f!M9>9CrwVRZD&+1_x}O4JXR|{K&eXQ`GC-@ig+F8r{yb= zxVe9*KH}RMH5w8{)3R_?dgVu`Q~mC=Qpp3QEOd3+z=E{1m8CBD#dno3^J+C?02%rj z)2VO4S+csS(OKvCuLcU_YcQVNOssAmV6KPg<8uL7+Ias zs(t66M^W04fwI=3fp`n(WWi2laWz_kAYej11}LA3XZ%xEyTo_bPRKe3A~4}WjM#*3 z$jIWnK=J6-v&|ZPUo)>b0lozuqTWa-ZV*bZ?RFy{l;rR`yJ z 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