Repository: LGE-ARC-AdvancedAI/auptimizer
Branch: master
Commit: 50f6e3b4e0cb
Files: 892
Total size: 14.8 MB
Directory structure:
gitextract_g8wyt_2k/
├── .coveragerc
├── .dockerignore
├── .gitignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── Examples/
│ ├── .gitignore
│ ├── 2dfunc_diff_mult_res/
│ │ ├── README.md
│ │ ├── cpu.ini
│ │ ├── exp_cpu.json
│ │ └── rosenbrock_hpo.py
│ ├── 2dfunc_diff_opt/
│ │ ├── .gitignore
│ │ ├── History.ipynb
│ │ ├── README.md
│ │ ├── experiment_auto.json
│ │ ├── experiment_bohb.json
│ │ ├── experiment_hyperband.json
│ │ ├── experiment_hyperopt.json
│ │ ├── experiment_random.json
│ │ ├── experiment_sequence.json
│ │ ├── experiment_spearmint.json
│ │ ├── rosenbrock_hpo.py
│ │ └── rosenbrock_origin.py
│ ├── 2dfunc_diff_res/
│ │ ├── README.md
│ │ ├── aws.txt
│ │ ├── cpu.ini
│ │ ├── env_local_template.ini
│ │ ├── env_user_template.ini
│ │ ├── exp_aws.json
│ │ ├── exp_cpu.json
│ │ ├── exp_gpu.json
│ │ ├── exp_node.json
│ │ ├── exp_node_async.json
│ │ ├── exp_passive.json
│ │ ├── gpu.txt
│ │ ├── node.txt
│ │ ├── plainGPU.txt
│ │ ├── rosenbrock_hpo.py
│ │ └── singleGPU.txt
│ ├── cai_eas/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── eas/
│ │ │ ├── client.py
│ │ │ ├── data_providers/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── base_provider.py
│ │ │ │ ├── cifar.py
│ │ │ │ ├── downloader.py
│ │ │ │ ├── svhn.py
│ │ │ │ └── utils.py
│ │ │ ├── expdir_monitor/
│ │ │ │ ├── __init__.py
│ │ │ │ └── expdir_monitor.py
│ │ │ ├── experiment.json
│ │ │ └── models/
│ │ │ ├── __init__.py
│ │ │ ├── basic_model.py
│ │ │ ├── convnet.py
│ │ │ ├── dense_net.py
│ │ │ ├── layer_cascade.py
│ │ │ ├── layer_multi_branch.py
│ │ │ ├── layers.py
│ │ │ └── utils.py
│ │ └── start_nets/
│ │ └── start_net_convnet_small_C10+/
│ │ ├── init
│ │ └── net.config
│ ├── compression/
│ │ ├── mnist_pytorch/
│ │ │ ├── README.md
│ │ │ ├── exp_activation_apoz_rank.json
│ │ │ ├── exp_activation_apoz_rank_dependency_aware.json
│ │ │ ├── exp_activation_mean_rank.json
│ │ │ ├── exp_activation_mean_rank_dependency_aware.json
│ │ │ ├── exp_admm.json
│ │ │ ├── exp_agp.json
│ │ │ ├── exp_amc.json
│ │ │ ├── exp_auto_activation_apoz_rank.json
│ │ │ ├── exp_auto_activation_mean_rank.json
│ │ │ ├── exp_auto_admm.json
│ │ │ ├── exp_auto_agp.json
│ │ │ ├── exp_auto_fpgm_aup_args.json
│ │ │ ├── exp_auto_fpgm_bohb.json
│ │ │ ├── exp_auto_fpgm_hyperband.json
│ │ │ ├── exp_auto_fpgm_hyperopt.json
│ │ │ ├── exp_auto_fpgm_no_expand.json
│ │ │ ├── exp_auto_fpgm_random.json
│ │ │ ├── exp_auto_fpgm_random_no_expand.json
│ │ │ ├── exp_auto_fpgm_sequence.json
│ │ │ ├── exp_auto_fpgm_spearmint.json
│ │ │ ├── exp_auto_l1filter.json
│ │ │ ├── exp_auto_l2filter.json
│ │ │ ├── exp_auto_level.json
│ │ │ ├── exp_auto_lottery_ticket.json
│ │ │ ├── exp_auto_taylor_fo.json
│ │ │ ├── exp_autocompress.json
│ │ │ ├── exp_fpgm.json
│ │ │ ├── exp_fpgm_aup_args.json
│ │ │ ├── exp_fpgm_dependency_aware.json
│ │ │ ├── exp_l1filter.json
│ │ │ ├── exp_l1filter_dependency_aware.json
│ │ │ ├── exp_l2filter.json
│ │ │ ├── exp_l2filter_dependency_aware.json
│ │ │ ├── exp_level.json
│ │ │ ├── exp_lottery_ticket.json
│ │ │ ├── exp_net_adapt.json
│ │ │ ├── exp_quantization_bnn.json
│ │ │ ├── exp_quantization_dorefa.json
│ │ │ ├── exp_quantization_naive.json
│ │ │ ├── exp_quantization_qat.json
│ │ │ ├── exp_sensitivity.json
│ │ │ ├── exp_simulated_annealing.json
│ │ │ ├── exp_taylor_fo.json
│ │ │ ├── exp_taylor_fo_dependency_aware.json
│ │ │ ├── mnist.py
│ │ │ ├── mnist_admm.py
│ │ │ ├── mnist_agp.py
│ │ │ ├── mnist_amc.py
│ │ │ ├── mnist_aup_args.py
│ │ │ ├── mnist_autocompress.py
│ │ │ ├── mnist_dependency_aware.py
│ │ │ ├── mnist_lottery_ticket.py
│ │ │ ├── mnist_net_adapt.py
│ │ │ ├── mnist_no_speedup.py
│ │ │ ├── mnist_pretrained.pth
│ │ │ ├── mnist_pretrained.py
│ │ │ ├── mnist_sensitivity.py
│ │ │ └── mnist_simulated_annealing.py
│ │ ├── mnist_tensorflow/
│ │ │ ├── README.md
│ │ │ ├── exp_auto_level.json
│ │ │ ├── exp_level.json
│ │ │ ├── mnist.py
│ │ │ └── mnist_pretrained.py
│ │ └── utility_functions/
│ │ ├── README.md
│ │ └── mnist.py
│ ├── converter_examples/
│ │ ├── Convert_Benchmark/
│ │ │ ├── .gitignore
│ │ │ ├── Benchmark.ipynb
│ │ │ ├── README.md
│ │ │ ├── repdata.py
│ │ │ └── script_mobilenet.py
│ │ ├── Convert_Profiler/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── convert_onnx.json
│ │ │ ├── convert_tflite.json
│ │ │ ├── download_test_models.py
│ │ │ ├── env_onnx.template
│ │ │ ├── env_tflite.template
│ │ │ ├── model_names_tflite.txt
│ │ │ ├── test_perf_onnx.py
│ │ │ ├── test_perf_tflite.py
│ │ │ └── tflite_output.txt
│ │ ├── README.md
│ │ ├── Tested_Models/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── conversion_jsons/
│ │ │ │ ├── convert_checkpoint_to_onnx.json
│ │ │ │ ├── convert_checkpoint_to_tflite.json
│ │ │ │ ├── convert_keras_to_onnx.json
│ │ │ │ ├── convert_keras_to_tflite.json
│ │ │ │ ├── convert_protobuf_to_onnx.json
│ │ │ │ ├── convert_protobuf_to_tflite.json
│ │ │ │ ├── convert_pytorch_to_onnx.json
│ │ │ │ ├── convert_pytorch_to_tflite.json
│ │ │ │ ├── convert_savedmodel_to_onnx.json
│ │ │ │ └── convert_savedmodel_to_tflite.json
│ │ │ ├── convert_pb_to_tflite.json
│ │ │ ├── create_test_models.py
│ │ │ ├── download_models.py
│ │ │ └── download_urls.json
│ │ └── dlconvert_requirements.txt
│ ├── decorator_example/
│ │ ├── origin.py
│ │ └── use_decorator.py
│ ├── demo/
│ │ ├── .gitignore
│ │ ├── demo.yml
│ │ ├── experiment_wrapper.json
│ │ ├── hpo_wrapper.py
│ │ └── origin.py
│ ├── early_stopping/
│ │ ├── README.md
│ │ ├── mnist_keras/
│ │ │ ├── cpu.ini
│ │ │ ├── exp_BOHB_bandit.json
│ │ │ ├── exp_BOHB_curve_fitting.json
│ │ │ ├── exp_BOHB_median.json
│ │ │ ├── exp_BOHB_trunc.json
│ │ │ ├── exp_random_bandit.json
│ │ │ ├── exp_random_curve_fitting.json
│ │ │ ├── exp_random_median.json
│ │ │ ├── exp_random_trunc.json
│ │ │ ├── exp_spearmint_bandit.json
│ │ │ ├── exp_spearmint_curve_fitting.json
│ │ │ ├── exp_spearmint_median.json
│ │ │ ├── exp_spearmint_trunc.json
│ │ │ └── mnist.py
│ │ └── quad_equation_min/
│ │ ├── cpu.ini
│ │ ├── quad_min.py
│ │ ├── quad_min_BOHB_bandit.json
│ │ ├── quad_min_BOHB_median.json
│ │ ├── quad_min_BOHB_trunc.json
│ │ ├── quad_min_random_bandit.json
│ │ ├── quad_min_random_median.json
│ │ ├── quad_min_random_trunc.json
│ │ ├── quad_min_spearmint_bandit.json
│ │ ├── quad_min_spearmint_median.json
│ │ └── quad_min_spearmint_trunc.json
│ ├── hpo_mnist/
│ │ ├── .gitignore
│ │ ├── MNIST Hyperparameter Optimization Demo.ipynb
│ │ ├── README.md
│ │ ├── demo.json
│ │ ├── exp_aws_async.json
│ │ ├── exp_aws_demo.json
│ │ ├── exp_aws_retry_job.json
│ │ ├── exp_bohb.json
│ │ ├── exp_hyperband.json
│ │ ├── exp_hyperopt.json
│ │ ├── exp_random.json
│ │ ├── exp_random_async.json
│ │ ├── exp_sequence.json
│ │ ├── exp_spearmint.json
│ │ ├── job_retries_random.json
│ │ ├── mnist_hpo_demo.py
│ │ └── mnist_hpo_fail.py
│ ├── intermediate_results/
│ │ ├── README.md
│ │ └── quad_equation_min/
│ │ ├── quad_min.py
│ │ ├── quad_min_BOHB.json
│ │ ├── quad_min_random.json
│ │ └── quad_min_spearmint.json
│ ├── job_failure_control/
│ │ ├── README.md
│ │ ├── experiment_extra_argument.json
│ │ ├── experiment_job_ignore_fail.json
│ │ ├── experiment_job_retries.json
│ │ ├── experiment_job_retries_ignore_fail.json
│ │ ├── experiment_no_nsample.json
│ │ ├── experiment_no_param_config.json
│ │ ├── experiment_no_proposer.json
│ │ ├── experiment_no_resource.json
│ │ ├── experiment_no_script.json
│ │ ├── experiment_test.json
│ │ ├── rosenbrock_hpo.py
│ │ └── test.py
│ ├── mnist_keras_save_model/
│ │ ├── README.md
│ │ ├── cpu.ini
│ │ ├── exp_random_cpu.json
│ │ ├── exp_random_node.json
│ │ ├── mnist.py
│ │ ├── mnist_wo_decorator.py
│ │ └── node.txt
│ ├── profiler_examples/
│ │ ├── README.md
│ │ ├── bench/
│ │ │ ├── download.sh
│ │ │ └── test_perf.py
│ │ ├── env_benchmark.template
│ │ ├── env_mnist.template
│ │ ├── experiments/
│ │ │ └── Readme.md
│ │ ├── internal/
│ │ │ └── ImageNet Experiments.ipynb
│ │ ├── issues.md
│ │ ├── mnist/
│ │ │ └── mnist.py
│ │ └── model_names.txt
│ ├── quad_equation_min/
│ │ ├── QUAD_MIN_Demo.ipynb
│ │ ├── cpu.ini
│ │ ├── quad_min.py
│ │ ├── quad_min_BOHB.json
│ │ ├── quad_min_random.json
│ │ └── quad_min_spearmint.json
│ ├── tf_flags/
│ │ ├── README.md
│ │ ├── experiment.json
│ │ ├── rosenbrock_hpo.py
│ │ └── rosenbrock_tf.py
│ └── tf_iris_diff_opt/
│ ├── History.ipynb
│ ├── README.md
│ ├── experiment_demo.json
│ ├── experiment_hpo.json
│ ├── experiment_hyperband.json
│ ├── experiment_random.json
│ ├── experiment_sequence.json
│ ├── experiment_spearmint.json
│ ├── iris_data.py
│ ├── premade_estimator.py
│ ├── premade_estimator_hpo.py
│ ├── premade_estimator_hyper.py
│ └── premade_estimator_wrapper.py
├── LICENSE
├── MANIFEST.in
├── R-src/
│ ├── README.md
│ ├── Rpackage/
│ │ ├── DESCRIPTION
│ │ ├── NAMESPACE
│ │ ├── R/
│ │ │ └── auptimizer.R
│ │ ├── man/
│ │ │ ├── get_config.Rd
│ │ │ └── print_result.Rd
│ │ ├── tests/
│ │ │ ├── testthat/
│ │ │ │ ├── test_IO.R
│ │ │ │ └── test_io.json
│ │ │ └── testthat.R
│ │ └── vignettes/
│ │ ├── .gitignore
│ │ └── auptimizer.Rmd
│ └── examples/
│ ├── exp_ridge.R
│ ├── exp_rosenbrock.R
│ ├── ridge.json
│ ├── ridgeRegression.R
│ ├── rosenbrock.R
│ └── rosenbrock.json
├── README.md
├── docs/
│ ├── Database/
│ │ ├── schema.dot
│ │ ├── schema.sql
│ │ └── sql_graphviz.py
│ ├── Dockerfile
│ ├── LICENSE
│ ├── Makefile
│ ├── README.rst
│ ├── _static/
│ │ └── .gitkeep
│ ├── algorithm.rst
│ ├── archive/
│ │ ├── Auptimizer-1.0-py2-none-any.whl
│ │ ├── Auptimizer-1.0-py3-none-any.whl
│ │ ├── Auptimizer-1.1-py2-none-any.whl
│ │ ├── Auptimizer-1.1-py3-none-any.whl
│ │ ├── Auptimizer-1.2-py2-none-any.whl
│ │ ├── Auptimizer-1.2-py3-none-any.whl
│ │ ├── Auptimizer-1.3-py2-none-any.whl
│ │ ├── Auptimizer-1.3-py3-none-any.whl
│ │ ├── Auptimizer-1.4-py2.py3-none-any.whl
│ │ ├── Auptimizer-2.0-py2.py3-none-any.whl
│ │ └── aup.py
│ ├── aup.EE.Experiment.rst
│ ├── aup.EE.Job.rst
│ ├── aup.EE.Resource.rst
│ ├── aup.EE.rst
│ ├── aup.ET.Connector.rst
│ ├── aup.ET.rst
│ ├── aup.Proposer.BOHBProposer.rst
│ ├── aup.Proposer.EASProposer.rst
│ ├── aup.Proposer.HyperbandProposer.rst
│ ├── aup.Proposer.HyperoptProposer.rst
│ ├── aup.Proposer.RandomProposer.rst
│ ├── aup.Proposer.SequenceProposer.rst
│ ├── aup.Proposer.SpearmintProposer.rst
│ ├── aup.Proposer.rst
│ ├── aup.__main__.rst
│ ├── aup.compression.rst
│ ├── aup.convert.rst
│ ├── aup.dlconvert.rst
│ ├── aup.dlconvert_API.rst
│ ├── aup.init.rst
│ ├── aup.profiler.rst
│ ├── aup.rst
│ ├── aup.setup.rst
│ ├── aup.setupdb.rst
│ ├── aup.setupdb.sqlite.rst
│ ├── aup.setupdb_API.rst
│ ├── aup.utils.rst
│ ├── aup.visualize.rst
│ ├── compression.rst
│ ├── compression_main.rst
│ ├── compression_utilities.rst
│ ├── compressors.rst
│ ├── conf.py
│ ├── dashboard.rst
│ ├── demo.rst
│ ├── developer.rst
│ ├── developer_guide.rst
│ ├── dlconvert.rst
│ ├── dlconvert_example.rst
│ ├── dlconvert_readme.rst
│ ├── early_stop.rst
│ ├── edge.rst
│ ├── environment.rst
│ ├── errors.rst
│ ├── experiment.rst
│ ├── hpo.rst
│ ├── index.rst
│ ├── install.rst
│ ├── prepare.sh
│ ├── prof_example.rst
│ ├── prof_readme.rst
│ ├── profiler.rst
│ ├── r_user.rst
│ ├── requirements.txt
│ ├── setup.rst
│ └── version.rst
├── oss-package.info
├── publish.sh
├── requirements.txt
├── setup.py
├── src/
│ └── aup/
│ ├── EE/
│ │ ├── Experiment.py
│ │ ├── Job.py
│ │ ├── Resource/
│ │ │ ├── AWSResourceManager.py
│ │ │ ├── AbstractResourceManager.py
│ │ │ ├── CPUResourceManager.py
│ │ │ ├── GPUResourceManager.py
│ │ │ ├── PassiveResourceManager.py
│ │ │ ├── SSHResourceManager.py
│ │ │ ├── __init__.py
│ │ │ └── utils/
│ │ │ ├── ResourceThreadPoolExecutor.py
│ │ │ ├── __init__.py
│ │ │ └── curve_fitting.py
│ │ └── __init__.py
│ ├── ET/
│ │ ├── Connector/
│ │ │ ├── AbstractConnector.py
│ │ │ ├── SQLiteConnector.py
│ │ │ └── __init__.py
│ │ └── __init__.py
│ ├── Proposer/
│ │ ├── AbstractProposer.py
│ │ ├── BOHBProposer.py
│ │ ├── EASProposer.py
│ │ ├── HyperbandProposer.py
│ │ ├── HyperoptProposer.py
│ │ ├── Hyperopt_LICENSE
│ │ ├── RandomProposer.py
│ │ ├── SequenceProposer.py
│ │ ├── SpearmintProposer.py
│ │ ├── Spearmint_LICENSE
│ │ ├── __init__.py
│ │ ├── eas/
│ │ │ ├── LICENSE
│ │ │ ├── __init__.py
│ │ │ ├── arch_search/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── arch_search_convnet_net2net.py
│ │ │ │ └── arch_search_densenet_net2net.py
│ │ │ ├── arch_search.py
│ │ │ ├── client.py
│ │ │ ├── data_providers/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── base_provider.py
│ │ │ │ ├── cifar.py
│ │ │ │ ├── downloader.py
│ │ │ │ ├── svhn.py
│ │ │ │ └── utils.py
│ │ │ ├── expdir_monitor/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── arch_manager.py
│ │ │ │ ├── distributed.py
│ │ │ │ └── expdir_monitor.py
│ │ │ ├── main.py
│ │ │ ├── meta_controller/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── base_controller.py
│ │ │ │ └── rl_controller.py
│ │ │ ├── models/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── basic_model.py
│ │ │ │ ├── convnet.py
│ │ │ │ ├── dense_net.py
│ │ │ │ ├── layer_cascade.py
│ │ │ │ ├── layer_multi_branch.py
│ │ │ │ ├── layers.py
│ │ │ │ └── utils.py
│ │ │ ├── run_dense_net.py
│ │ │ └── run_simple_convnet.py
│ │ ├── hpbandster/
│ │ │ ├── __init__.py
│ │ │ ├── core/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── base_config_generator.py
│ │ │ │ ├── base_iteration.py
│ │ │ │ ├── dispatcher.py
│ │ │ │ ├── master.py
│ │ │ │ ├── nameserver.py
│ │ │ │ ├── result.py
│ │ │ │ └── worker.py
│ │ │ ├── examples/
│ │ │ │ ├── README.txt
│ │ │ │ ├── __init__.py
│ │ │ │ ├── commons.py
│ │ │ │ ├── example_1_local_sequential.py
│ │ │ │ ├── example_2_local_parallel_threads.py
│ │ │ │ ├── example_3_local_parallel_processes.py
│ │ │ │ ├── example_4_cluster.py
│ │ │ │ ├── example_5_keras_worker.py
│ │ │ │ ├── example_5_mnist.py
│ │ │ │ ├── example_5_pytorch_worker.py
│ │ │ │ ├── example_5_run/
│ │ │ │ │ ├── configs.json
│ │ │ │ │ └── results.json
│ │ │ │ ├── example_8_mnist_continued.py
│ │ │ │ ├── plot_example_6_analysis.py
│ │ │ │ └── plot_example_7_interactive_plot.py
│ │ │ ├── optimizers/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── bohb.py
│ │ │ │ ├── config_generators/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── bohb.py
│ │ │ │ │ ├── h2bo.py
│ │ │ │ │ ├── kde.py
│ │ │ │ │ ├── lcnet.py
│ │ │ │ │ └── random_sampling.py
│ │ │ │ ├── h2bo.py
│ │ │ │ ├── hyperband.py
│ │ │ │ ├── iterations/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── successivehalving.py
│ │ │ │ │ └── successiveresampling.py
│ │ │ │ ├── kde/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── kernels.py
│ │ │ │ │ └── mvkde.py
│ │ │ │ ├── lcnet.py
│ │ │ │ ├── learning_curve_models/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── arif.py
│ │ │ │ │ ├── base.py
│ │ │ │ │ └── lcnet.py
│ │ │ │ └── randomsearch.py
│ │ │ ├── utils.py
│ │ │ ├── visualization.py
│ │ │ └── workers/
│ │ │ ├── __init__.py
│ │ │ └── hpolibbenchmark.py
│ │ ├── hpbandster_LICENSE
│ │ ├── hyperband_LICENSE
│ │ ├── hyperopt/
│ │ │ ├── LICENSE.txt
│ │ │ ├── __init__.py
│ │ │ ├── algobase.py
│ │ │ ├── anneal.py
│ │ │ ├── base.py
│ │ │ ├── criteria.py
│ │ │ ├── exceptions.py
│ │ │ ├── fmin.py
│ │ │ ├── graphviz.py
│ │ │ ├── hp.py
│ │ │ ├── ipy.py
│ │ │ ├── main.py
│ │ │ ├── mix.py
│ │ │ ├── mongoexp.py
│ │ │ ├── plotting.py
│ │ │ ├── pyll/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── base.py
│ │ │ │ └── stochastic.py
│ │ │ ├── pyll_utils.py
│ │ │ ├── rand.py
│ │ │ ├── rdists.py
│ │ │ ├── tpe.py
│ │ │ ├── utils.py
│ │ │ └── vectorize.py
│ │ └── spearmint/
│ │ ├── ExperimentGrid.py
│ │ ├── Locker.py
│ │ ├── __init__.py
│ │ ├── chooser/
│ │ │ ├── CMAChooser.py
│ │ │ ├── GPConstrainedEIChooser.py
│ │ │ ├── GPEIChooser.py
│ │ │ ├── GPEIOptChooser.py
│ │ │ ├── GPEIperSecChooser.py
│ │ │ ├── RandomChooser.py
│ │ │ ├── RandomForestEIChooser.py
│ │ │ ├── SequentialChooser.py
│ │ │ ├── __init__.py
│ │ │ └── cma.py
│ │ ├── gp.py
│ │ ├── helpers.py
│ │ ├── runner.py
│ │ ├── sobol_lib.py
│ │ ├── spearmint_pb2.py
│ │ └── util.py
│ ├── RestAPI/
│ │ ├── __init__.py
│ │ ├── server.py
│ │ └── templates/
│ │ └── home.html
│ ├── __init__.py
│ ├── __main__.py
│ ├── aup.py
│ ├── compression/
│ │ ├── Compressor.py
│ │ ├── NNI-LICENSE
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ ├── tensorflow/
│ │ │ ├── __init__.py
│ │ │ ├── compressor.py
│ │ │ ├── default_layers.py
│ │ │ └── pruning/
│ │ │ ├── __init__.py
│ │ │ └── one_shot.py
│ │ ├── torch/
│ │ │ ├── __init__.py
│ │ │ ├── _graph_utils.py
│ │ │ ├── compressor.py
│ │ │ ├── default_layers.py
│ │ │ ├── parameter_expressions.py
│ │ │ ├── pruning/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── admm_pruner.py
│ │ │ │ ├── agp.py
│ │ │ │ ├── amc/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── amc_pruner.py
│ │ │ │ │ ├── channel_pruning_env.py
│ │ │ │ │ └── lib/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── agent.py
│ │ │ │ │ ├── memory.py
│ │ │ │ │ ├── net_measure.py
│ │ │ │ │ └── utils.py
│ │ │ │ ├── apply_compression.py
│ │ │ │ ├── auto_compress_pruner.py
│ │ │ │ ├── constants.py
│ │ │ │ ├── constants_pruner.py
│ │ │ │ ├── finegrained_pruning.py
│ │ │ │ ├── lottery_ticket.py
│ │ │ │ ├── net_adapt_pruner.py
│ │ │ │ ├── one_shot.py
│ │ │ │ ├── sensitivity_pruner.py
│ │ │ │ ├── simulated_annealing_pruner.py
│ │ │ │ ├── structured_pruning.py
│ │ │ │ └── weight_masker.py
│ │ │ ├── quantization/
│ │ │ │ ├── __init__.py
│ │ │ │ └── quantizers.py
│ │ │ ├── speedup/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── compress_modules.py
│ │ │ │ ├── compressor.py
│ │ │ │ └── infer_shape.py
│ │ │ ├── torch_utils.py
│ │ │ └── utils/
│ │ │ ├── __init__.py
│ │ │ ├── config_validation.py
│ │ │ ├── counter.py
│ │ │ ├── mask_conflict.py
│ │ │ ├── num_param_counter.py
│ │ │ ├── sensitivity_analysis.py
│ │ │ ├── shape_dependency.py
│ │ │ └── utils.py
│ │ └── utils.py
│ ├── convert.py
│ ├── dashboard/
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── dashboard.py
│ │ └── frontend/
│ │ ├── .browserslistrc
│ │ ├── .editorconfig
│ │ ├── .eslintrc.json
│ │ ├── .gitignore
│ │ ├── .prettierignore
│ │ ├── .prettierrc
│ │ ├── Angular-LICENSE
│ │ ├── README.md
│ │ ├── angular.json
│ │ ├── e2e/
│ │ │ ├── protractor.conf.js
│ │ │ ├── src/
│ │ │ │ ├── app.e2e-spec.ts
│ │ │ │ └── app.po.ts
│ │ │ └── tsconfig.json
│ │ ├── febuild/
│ │ │ └── auptimizer-dashboard/
│ │ │ ├── 3rdpartylicenses.txt
│ │ │ ├── 4.3bf463bd37e16d085b6c.js
│ │ │ ├── 5.0e291298e9c49cb93c71.js
│ │ │ ├── _redirects
│ │ │ ├── index.html
│ │ │ ├── main.09d22743789a55973001.js
│ │ │ ├── polyfills.9cfb3f513e777138fb2c.js
│ │ │ ├── runtime.29a333e72cc7398676d0.js
│ │ │ └── styles.5a1d2b21684fc92c4f99.css
│ │ ├── karma.conf.js
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── _redirects
│ │ │ ├── app/
│ │ │ │ ├── @core/
│ │ │ │ │ ├── app-load.service.ts
│ │ │ │ │ └── core.module.ts
│ │ │ │ ├── app-routing.module.ts
│ │ │ │ ├── app.component.html
│ │ │ │ ├── app.component.scss
│ │ │ │ ├── app.component.spec.ts
│ │ │ │ ├── app.component.ts
│ │ │ │ ├── app.module.ts
│ │ │ │ ├── app.service.ts
│ │ │ │ ├── appStore/
│ │ │ │ │ ├── app-state.model.ts
│ │ │ │ │ ├── app.actions.ts
│ │ │ │ │ └── app.store.ts
│ │ │ │ ├── guards/
│ │ │ │ │ └── db.guard.ts
│ │ │ │ ├── main/
│ │ │ │ │ ├── containers/
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── main/
│ │ │ │ │ │ ├── main.component.html
│ │ │ │ │ │ ├── main.component.scss
│ │ │ │ │ │ ├── main.component.spec.ts
│ │ │ │ │ │ └── main.component.ts
│ │ │ │ │ ├── experiment/
│ │ │ │ │ │ ├── components/
│ │ │ │ │ │ │ ├── experiment-dropdown/
│ │ │ │ │ │ │ │ ├── experiment-dropdown.component.html
│ │ │ │ │ │ │ │ ├── experiment-dropdown.component.scss
│ │ │ │ │ │ │ │ ├── experiment-dropdown.component.spec.ts
│ │ │ │ │ │ │ │ └── experiment-dropdown.component.ts
│ │ │ │ │ │ │ ├── header/
│ │ │ │ │ │ │ │ ├── header.component.html
│ │ │ │ │ │ │ │ ├── header.component.scss
│ │ │ │ │ │ │ │ ├── header.component.spec.ts
│ │ │ │ │ │ │ │ └── header.component.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── sidenav/
│ │ │ │ │ │ │ │ ├── sidenav.component.html
│ │ │ │ │ │ │ │ ├── sidenav.component.scss
│ │ │ │ │ │ │ │ ├── sidenav.component.spec.ts
│ │ │ │ │ │ │ │ └── sidenav.component.ts
│ │ │ │ │ │ │ └── sidenav-element/
│ │ │ │ │ │ │ ├── sidenav-element.component.html
│ │ │ │ │ │ │ ├── sidenav-element.component.scss
│ │ │ │ │ │ │ ├── sidenav-element.component.spec.ts
│ │ │ │ │ │ │ └── sidenav-element.component.ts
│ │ │ │ │ │ ├── containers/
│ │ │ │ │ │ │ ├── create-experiment/
│ │ │ │ │ │ │ │ ├── create-experiment.component.html
│ │ │ │ │ │ │ │ ├── create-experiment.component.scss
│ │ │ │ │ │ │ │ ├── create-experiment.component.spec.ts
│ │ │ │ │ │ │ │ ├── create-experiment.component.ts
│ │ │ │ │ │ │ │ └── exampleJSON.ts
│ │ │ │ │ │ │ ├── experiment/
│ │ │ │ │ │ │ │ ├── experiment.component.html
│ │ │ │ │ │ │ │ ├── experiment.component.scss
│ │ │ │ │ │ │ │ ├── experiment.component.spec.ts
│ │ │ │ │ │ │ │ └── experiment.component.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── initialize/
│ │ │ │ │ │ │ │ ├── initialize.component.html
│ │ │ │ │ │ │ │ ├── initialize.component.scss
│ │ │ │ │ │ │ │ ├── initialize.component.spec.ts
│ │ │ │ │ │ │ │ └── initialize.component.ts
│ │ │ │ │ │ │ ├── interm-results/
│ │ │ │ │ │ │ │ ├── interm-results.component.html
│ │ │ │ │ │ │ │ ├── interm-results.component.scss
│ │ │ │ │ │ │ │ ├── interm-results.component.spec.ts
│ │ │ │ │ │ │ │ └── interm-results.component.ts
│ │ │ │ │ │ │ ├── job-status/
│ │ │ │ │ │ │ │ ├── job-status.component.html
│ │ │ │ │ │ │ │ ├── job-status.component.scss
│ │ │ │ │ │ │ │ ├── job-status.component.spec.ts
│ │ │ │ │ │ │ │ └── job-status.component.ts
│ │ │ │ │ │ │ ├── list/
│ │ │ │ │ │ │ │ ├── list.component.html
│ │ │ │ │ │ │ │ ├── list.component.scss
│ │ │ │ │ │ │ │ ├── list.component.spec.ts
│ │ │ │ │ │ │ │ └── list.component.ts
│ │ │ │ │ │ │ ├── main/
│ │ │ │ │ │ │ │ ├── main.component.html
│ │ │ │ │ │ │ │ ├── main.component.scss
│ │ │ │ │ │ │ │ ├── main.component.spec.ts
│ │ │ │ │ │ │ │ └── main.component.ts
│ │ │ │ │ │ │ ├── multi-exp-comp/
│ │ │ │ │ │ │ │ ├── multi-exp-comp.component.html
│ │ │ │ │ │ │ │ ├── multi-exp-comp.component.scss
│ │ │ │ │ │ │ │ ├── multi-exp-comp.component.spec.ts
│ │ │ │ │ │ │ │ └── multi-exp-comp.component.ts
│ │ │ │ │ │ │ ├── notification/
│ │ │ │ │ │ │ │ ├── notification.component.html
│ │ │ │ │ │ │ │ ├── notification.component.scss
│ │ │ │ │ │ │ │ ├── notification.component.spec.ts
│ │ │ │ │ │ │ │ └── notification.component.ts
│ │ │ │ │ │ │ ├── overview/
│ │ │ │ │ │ │ │ ├── overview.component.html
│ │ │ │ │ │ │ │ ├── overview.component.scss
│ │ │ │ │ │ │ │ ├── overview.component.spec.ts
│ │ │ │ │ │ │ │ └── overview.component.ts
│ │ │ │ │ │ │ └── pcg/
│ │ │ │ │ │ │ ├── pcg.component.html
│ │ │ │ │ │ │ ├── pcg.component.scss
│ │ │ │ │ │ │ ├── pcg.component.spec.ts
│ │ │ │ │ │ │ └── pcg.component.ts
│ │ │ │ │ │ ├── experiment-routing.module.ts
│ │ │ │ │ │ ├── experiment.module.ts
│ │ │ │ │ │ ├── services/
│ │ │ │ │ │ │ └── experiment.service.ts
│ │ │ │ │ │ └── store/
│ │ │ │ │ │ ├── experiment-state.model.ts
│ │ │ │ │ │ ├── experiment.actions.ts
│ │ │ │ │ │ └── experiment.store.ts
│ │ │ │ │ ├── main-routing.module.ts
│ │ │ │ │ └── main.module.ts
│ │ │ │ ├── material/
│ │ │ │ │ └── material.module.ts
│ │ │ │ ├── models/
│ │ │ │ │ ├── data/
│ │ │ │ │ │ ├── custom-icons.data.ts
│ │ │ │ │ │ ├── graph-colors.data.ts
│ │ │ │ │ │ ├── nav-items.ts
│ │ │ │ │ │ └── plotly-hidden-displays.ts
│ │ │ │ │ ├── enum/
│ │ │ │ │ │ ├── experiment-status.enum.ts
│ │ │ │ │ │ ├── experiment-view-type.enum.ts
│ │ │ │ │ │ ├── notification-type.enum.ts
│ │ │ │ │ │ └── theme-option.enum.ts
│ │ │ │ │ ├── experiment.model.ts
│ │ │ │ │ ├── nav-items.model.ts
│ │ │ │ │ ├── progress-bar.model.ts
│ │ │ │ │ └── resource.model.ts
│ │ │ │ ├── page-not-found/
│ │ │ │ │ ├── page-not-found.component.html
│ │ │ │ │ ├── page-not-found.component.scss
│ │ │ │ │ ├── page-not-found.component.spec.ts
│ │ │ │ │ └── page-not-found.component.ts
│ │ │ │ └── shared/
│ │ │ │ ├── dialogs/
│ │ │ │ │ ├── confirm/
│ │ │ │ │ │ ├── confirm-dialog.component.html
│ │ │ │ │ │ ├── confirm-dialog.component.scss
│ │ │ │ │ │ └── confirm-dialog.component.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── pipes/
│ │ │ │ │ ├── experiment-status.pipe.ts
│ │ │ │ │ ├── first-letter-uppercase/
│ │ │ │ │ │ ├── first-letter-uppercase.pipe.spec.ts
│ │ │ │ │ │ └── first-letter-uppercase.pipe.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── min-to-hour/
│ │ │ │ │ │ ├── min-to-hour.pipe.spec.ts
│ │ │ │ │ │ └── min-to-hour.pipe.ts
│ │ │ │ │ ├── notification-icon.pipe.ts
│ │ │ │ │ ├── progressBarWidth.pipe.ts
│ │ │ │ │ ├── roudNumber.pipe.ts
│ │ │ │ │ ├── sec-to-min/
│ │ │ │ │ │ ├── sec-to-min.pipe.spec.ts
│ │ │ │ │ │ └── sec-to-min.pipe.ts
│ │ │ │ │ └── truncate.pipe.ts
│ │ │ │ ├── services/
│ │ │ │ │ ├── api.service.ts
│ │ │ │ │ ├── color-scheme.service.ts
│ │ │ │ │ ├── helper.service.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── snackbar.service.ts
│ │ │ │ │ └── utils.service.ts
│ │ │ │ ├── shared.module.ts
│ │ │ │ └── validators/
│ │ │ │ ├── at-least.validator.ts
│ │ │ │ └── index.ts
│ │ │ ├── assets/
│ │ │ │ └── .gitkeep
│ │ │ ├── environments/
│ │ │ │ ├── environment.prod.ts
│ │ │ │ └── environment.ts
│ │ │ ├── index.html
│ │ │ ├── main.ts
│ │ │ ├── polyfills.ts
│ │ │ ├── scss/
│ │ │ │ ├── _colors.scss
│ │ │ │ ├── _helpers.scss
│ │ │ │ ├── _material-overrides.scss
│ │ │ │ ├── jsoneditor-dark.scss
│ │ │ │ ├── jsoneditor.scss
│ │ │ │ ├── main.scss
│ │ │ │ └── theme.scss
│ │ │ ├── styles.scss
│ │ │ └── test.ts
│ │ ├── tailwind.config.js
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.spec.json
│ │ └── webpack.config.js
│ ├── dlconvert/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ ├── __version__.py
│ │ ├── checkpoint_to_onnx.py
│ │ ├── checkpoint_to_pb.py
│ │ ├── checkpoint_to_tflite.py
│ │ ├── keras_to_onnx.py
│ │ ├── keras_to_pb.py
│ │ ├── keras_to_tflite.py
│ │ ├── pb_to_onnx.py
│ │ ├── pb_to_tflite.py
│ │ ├── pytorch_to_keras.py
│ │ ├── pytorch_to_onnx.py
│ │ ├── pytorch_to_tflite.py
│ │ ├── savedmodel_to_onnx.py
│ │ ├── savedmodel_to_tflite.py
│ │ ├── spec_utils/
│ │ │ ├── __init__.py
│ │ │ └── pb.py
│ │ ├── to_frozen_pb.py
│ │ ├── to_onnx.py
│ │ ├── to_tflite.py
│ │ └── utils.py
│ ├── init.py
│ ├── profiler/
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ ├── calculate.py
│ │ ├── compile_stats.py
│ │ ├── issues.md
│ │ ├── profiler.sh
│ │ └── statscript.sh
│ ├── setup.py
│ ├── setupdb/
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ ├── reset.py
│ │ └── sqlite.py
│ ├── utils.py
│ └── visualize.py
└── tests/
├── EE/
│ ├── Resource/
│ │ ├── .gitignore
│ │ ├── __init__.py
│ │ ├── test_AbstractResourceManager.py
│ │ ├── test_CPUResourceManager.py
│ │ ├── test_GPUResourceManager.py
│ │ ├── test_PassiveResourceManager.py
│ │ └── test_SSHResourceManager.py
│ ├── __init__.py
│ ├── test_Experiment.py
│ └── test_Job.py
├── ET/
│ ├── Connector/
│ │ ├── __init__.py
│ │ └── test_SQLiteConnector.py
│ └── __init__.py
├── Proposer/
│ ├── __init__.py
│ ├── test_AbstractProposer.py
│ ├── test_BOHBProposer.py
│ ├── test_HyperbandProposer.py
│ ├── test_HyperoptProposer.py
│ ├── test_RandomProposer.py
│ ├── test_SequenceProposer.py
│ └── test_SpearmintProposer.py
├── __init__.py
├── command/
│ ├── __init__.py
│ ├── test_convert.py
│ └── test_setup.py
├── compression/
│ ├── __init__.py
│ ├── test_aup_compressor.py
│ ├── test_compression_utils.py
│ ├── test_compressor_tf.py
│ ├── test_compressor_torch.py
│ └── test_model_speedup.py
├── data/
│ ├── config/
│ │ ├── test_read.json
│ │ └── test_wrong.json
│ ├── exp4.json
│ ├── exp4_no_name.json
│ ├── exp4_no_param.json
│ ├── exp4_no_script.json
│ ├── exp5.json
│ ├── exp6.json
│ ├── exp7.json
│ ├── gpus.txt
│ ├── nodes.txt
│ ├── plain_env.ini
│ ├── target_gpu_env.ini
│ ├── target_plain_env.ini
│ ├── task1.py
│ ├── task2.py
│ ├── task3.py
│ ├── task4.py
│ ├── task5.py
│ ├── task6.py
│ ├── task7.py
│ ├── task8.py
│ ├── test_script.py
│ ├── wrapper1.json
│ └── wrapper2.json
├── dlconvert/
│ ├── .gitignore
│ ├── Dockerfile_tf1
│ ├── Dockerfile_tf2
│ ├── README.md
│ ├── __init__.py
│ ├── data/
│ │ ├── .gitignore
│ │ ├── create_test_model.py
│ │ ├── evaluate_pred.py
│ │ ├── flag_names.json
│ │ ├── prepare.sh
│ │ ├── prepare_docker.sh
│ │ ├── pytorch_model.py
│ │ └── repdata.py
│ ├── dlconvert_requirements.txt
│ ├── pytest.ini
│ ├── test_checkpoint_to_onnx.py
│ ├── test_checkpoint_to_pb.py
│ ├── test_checkpoint_to_tflite.py
│ ├── test_keras_to_onnx.py
│ ├── test_keras_to_pb.py
│ ├── test_keras_to_tflite.py
│ ├── test_pb_to_onnx.py
│ ├── test_pb_to_tflite.py
│ ├── test_pytorch_to_keras.py
│ ├── test_pytorch_to_onnx.py
│ ├── test_pytorch_to_tflite.py
│ ├── test_savedmodel_to_onnx.py
│ ├── test_savedmodel_to_tflite.py
│ ├── unittest_tf1.sh
│ └── unittest_tf2.sh
├── setupdb/
│ ├── __init__.py
│ └── test_sqlite.py
├── test_BasicConfig.py
├── test_utils.py
└── test_wrapper.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .coveragerc
================================================
[run]
omit =
./src/aup/Proposer/spearmint/*
./src/aup/Proposer/hyperopt/*
./src/aup/Proposer/eas/*
./src/aup/Proposer/hpbandster/*
./src/aup/Proposer/EASProposer.py
./src/aup/setupdb/reset.py
./src/aup/visualize.py
./src/aup/EE/Resource/AWSResourceManager.py
./src/aup/init.py
*/__main__.py
*/__init__.py
./src/aup/profiler/*
./src/aup/RestAPI/*
./src/aup/dashboard/*
./src/aup/Proposer/SpearmintProposer.py
./src/aup/Proposer/BOHBProposer.py
./src/aup/compression/torch/pruning/*
[report]
exclude_lines =
pragma: no cover
flags
def main
if __name__ == "__main__"
@click
raise NotImplementedError
================================================
FILE: .dockerignore
================================================
docs
Examples/2d*
Examples/dlconvert_examples/Convert*
Examples/dlconvert_examples/Tested*
Examples/demo
Examples/cai_eas
Examples/decorator_example
Examples/hpo_mnist
Examples/job*
Examples/profiler*
Examples/tf*
build
dist
================================================
FILE: .gitignore
================================================
Examples/demo/auto.py
testenv/
.aup/
.idea/
*~
*.pkl
tests/EE/jobs/
tests/data/jobs/
tests/EE/Resource/data
.vscode/
analysis.html
R-src/*.tar.gz
R-src/R-src.Rproj
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
# OS files
.DS_Store
.Rproj.user
analysis_test
jobs
tests/mnist_compressed.pth
dashboard_logs
================================================
FILE: .travis.yml
================================================
git:
depth: false
quiet: true
branches:
only:
- master
- release
language: python
python:
- '3.7'
env:
global:
- secure: JbuCMpFomW4Yu+sMaBBvv7Jj1JyiQcCQAW7bhdJDil+YUdWEJmTjkF2pKwTQS5w6FUhZIHJ0cy3aJZibSrM2+YpAe3B8HMeLXAaYz9JKbC6Sp0KGr646YqoBNNP+wwrzwzJTPoV7YarViULFPYnUN8rcpp0ypaD9RrtqSN/IKUgxCeTNNcwJOLIr0SLqtmTgT2F2HzCbOkv2g84FqfCC8m2ISFyUgt8FqUD8bRfov++oHfiwSUDZHJaPkn1/NUG3qxrM85B6cC+iZH7MiudP2LF+frBJUtq4bimmjCDuQXX4c224LQC8hnG46sZGtH1i9PGp/wxdnO9gxDa+0ZIIbs86Uy6LrJptwZwWr/W4boian1JTLT8P6fPxuwDFejZwPVkyBGV1GF29pvVJ+4hRnJAKoUA/hg5IrEByXdMknkKh6yiMynmfNGEgCkAcqVPEcQie6u/nbLPqg0m3Pjs4GElEd0wIdqo9GBmK4q8FJGv05k98JX27Tm5gcwIps6qA5lPZoahOdy7GiBZFWdTfbgeah7tNjIqENbMhNFTS9Y1qy9gpgFZl8x9e0liF9OuTyTVlAmsHs9z1huuj8Bb6Eqbr8fvnVqAN9iHIBUWR7uGLeL4zwKmQZ5xiexba9LdnHuiaZjHzdzgA5SnVqXy/pabgCfoZ+wNiWcKu76mCjGU=
jobs:
- TF2TEST=1
install:
- pip install -r requirements.txt
- pip install -r tests/dlconvert/dlconvert_requirements.txt
- python setup.py install
- docs/prepare.sh
- tests/dlconvert/data/prepare.sh
script:
- pytest --cov-report=xml --cov=src/aup tests
after_success:
- codecov
deploy:
provider: pypi
user: "__token__"
password: $PYPITOKEN
on:
branch: release
distributions: "sdist bdist_wheel"
skip_existing: true
after_deploy:
- "./publish.sh"
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at auptimizer@lge.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: Examples/.gitignore
================================================
jobs/
spearmint/
================================================
FILE: Examples/2dfunc_diff_mult_res/README.md
================================================
# Return multiple results
This feature allows the user to save and track multiple secondary results along with the primary result for the HPO experiment.
Auptimizer still uses the primary result for the HPO algorithms, but saves the secondary results in the database under the table `multiple_results`.
## Usage
The feature can be used by adding the following parameter to the experiment configuration file:
"resource_args": {
"multi_res_labels": ["x", "y", …]
}
In the above example, {"x", "y", …} are the secondary parameters the user wants to track and record.
The user script would then return the results as a list including the primary result 'res' along with the secondary parameters as follows:
@aup_args
def HPO():
res = calculate_results()
return [res, x, y, …]
In the above example 'res' is the primary result which is always the first index of the returned array when using multiple results, and is used by HPO algorithm.
The other indices are matched directly with the array provided in the `multi_res_labels` parameter.
Hence, The length of the returned array from HPO script is '1 + length of multi_res_labels' parameter.
## Example code
python -m aup.setup cpu.ini
python -m aup exp_cpu.json
The code will create the job configuration file, interactively ask you to run the script, and ask for the result from your training code.
e.g.
# Job running path is Examples/2dfunc_diff_mult_res
# Config is at Examples/2dfunc_diff_mult_res/jobs/452.json
Job command is:
Examples/2dfunc_diff_mult_res/rosenbrock_hpo.py
Return results:
Then you should run the command in another terminal like:
./rosenbrock_hpo.py ./jobs/452.json
You will see something like:
#Auptimizer:232.78278278806914,-0.8297799529742598,2.2032449344215808
The first result is the main result, the next are "x" and "y" defined by multi_res_labels.
Once you paste the result back to **Auptimizer**, it will ask you for the next one.
================================================
FILE: Examples/2dfunc_diff_mult_res/cpu.ini
================================================
[Auptimizer]
# Auptimizer environment folder to be created, this file will be copied over
Auptimizer_PATH=./.aup
# Temp folder
TMP_FOLDER=./aup_tmp
# SQL engine
SQL_ENGINE=sqlite
================================================
FILE: Examples/2dfunc_diff_mult_res/exp_cpu.json
================================================
{
"name": "./2dfunc_diff_mult_res/exp_cpu.json",
"proposer": "random",
"n_samples": 10,
"random_seed": 1,
"script": "rosenbrock_hpo.py",
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"resource_args": {
"multi_res_labels": ["x", "y"]
}
}
================================================
FILE: Examples/2dfunc_diff_mult_res/rosenbrock_hpo.py
================================================
#!/usr/bin/env python
"""
Modified Rosenbrock function for HPO and aup
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import sys
""" # ver 1.0 - modify existing code
from aup import BasicConfig, print_result
def rosenbrock(conf, a=1, b=100):
x = conf.x
y = conf.y
return (a-x)*(a-x) + b*(y-x*x)*(y-x*x)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("config file required")
exit(1)
config = BasicConfig().load(sys.argv[1])
val = rosenbrock(config)
print_result(val)
"""
from aup import aup_args
@aup_args
def rosenbrock(x, y, a=1, b=100):
return [(a-x)*(a-x) + b*(y-x*x)*(y-x*x), x, y]
if __name__ == "__main__":
if len(sys.argv) != 2:
print("config file required")
exit(1)
rosenbrock(sys.argv[1])
================================================
FILE: Examples/2dfunc_diff_opt/.gitignore
================================================
spearmint/
rosenbrock_auto.py
================================================
FILE: Examples/2dfunc_diff_opt/History.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import matplotlib.pylab as plt\n",
"from aup.ET.Connector.SQLiteConnector import SQLiteConnector\n",
"import json\n",
"%matplotlib inline"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# connect to the result\n",
"sql = SQLiteConnector(\"./.aup/sqlite3.db\")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1, 2, 3, 4]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Get all experiments (this run is for iris using hyperopt, sequence, spearmint, random)\n",
"eids = sql.get_all_experiment()\n",
"eids"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(2,\n",
" 1,\n",
" 1564088286,\n",
" 1564088287,\n",
" '{\"proposer\": \"random\", \"n_samples\": 10, \"random_seed\": 1, \"script\": \"rosenbrock_hpo.py\", \"parameter_config\": [{\"name\": \"x\", \"range\": [-5, 5], \"type\": \"float\"}, {\"name\": \"y\", \"range\": [-5, 5], \"type\": \"float\"}], \"resource\": \"cpu\", \"n_parallel\": 2, \"target\": \"min\", \"workingdir\": \"/Users/jason.liu/PycharmProjects/CTE/Examples/2dfunc_diff_opt\"}')"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Show details in one experiment\n",
"sql.cursor.execute(\"SELECT * FROM experiment where eid = ?\", (eids[1],))\n",
"sql.cursor.fetchone()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" jid \n",
" score \n",
" eid \n",
" rid \n",
" start_time \n",
" end_time \n",
" job_config \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" 1 \n",
" 90036.000000 \n",
" 1 \n",
" 3 \n",
" 1564088282 \n",
" 1564088283 \n",
" {'x': -5, 'y': -5} \n",
" \n",
" \n",
" 1 \n",
" 2 \n",
" 40519.252553 \n",
" 1 \n",
" 2 \n",
" 1564088282 \n",
" 1564088283 \n",
" {'x': -3.888888888888889, 'y': -5} \n",
" \n",
" \n",
" 2 \n",
" 3 \n",
" 16184.062795 \n",
" 1 \n",
" 1 \n",
" 1564088282 \n",
" 1564088283 \n",
" {'x': -2.7777777777777777, 'y': -5} \n",
" \n",
" \n",
" 3 \n",
" 4 \n",
" 6056.493827 \n",
" 1 \n",
" 4 \n",
" 1564088282 \n",
" 1564088283 \n",
" {'x': -1.6666666666666665, 'y': -5} \n",
" \n",
" \n",
" 4 \n",
" 5 \n",
" 2820.587715 \n",
" 1 \n",
" 3 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': -0.5555555555555554, 'y': -5} \n",
" \n",
" \n",
" 5 \n",
" 6 \n",
" 2818.365493 \n",
" 1 \n",
" 2 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 0.5555555555555558, 'y': -5} \n",
" \n",
" \n",
" 6 \n",
" 7 \n",
" 6049.827160 \n",
" 1 \n",
" 1 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 1.666666666666667, 'y': -5} \n",
" \n",
" \n",
" 7 \n",
" 8 \n",
" 16172.951684 \n",
" 1 \n",
" 4 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 2.777777777777778, 'y': -5} \n",
" \n",
" \n",
" 8 \n",
" 9 \n",
" 40503.696997 \n",
" 1 \n",
" 3 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 3.8888888888888893, 'y': -5} \n",
" \n",
" \n",
" 9 \n",
" 10 \n",
" 90016.000000 \n",
" 1 \n",
" 2 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 5.0, 'y': -5} \n",
" \n",
" \n",
" 10 \n",
" 11 \n",
" 83492.790123 \n",
" 1 \n",
" 1 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': -5, 'y': -3.888888888888889} \n",
" \n",
" \n",
" 11 \n",
" 12 \n",
" 36170.830056 \n",
" 1 \n",
" 4 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': -3.888888888888889, 'y': -3.888888888888... \n",
" \n",
" \n",
" 12 \n",
" 13 \n",
" 13481.730834 \n",
" 1 \n",
" 3 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': -2.7777777777777777, 'y': -3.88888888888... \n",
" \n",
" \n",
" 13 \n",
" 14 \n",
" 4451.555556 \n",
" 1 \n",
" 2 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': -1.6666666666666665, 'y': -3.88888888888... \n",
" \n",
" \n",
" 14 \n",
" 15 \n",
" 1764.346289 \n",
" 1 \n",
" 1 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': -0.5555555555555554, 'y': -3.88888888888... \n",
" \n",
" \n",
" 15 \n",
" 16 \n",
" 1762.124066 \n",
" 1 \n",
" 4 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 0.5555555555555558, 'y': -3.888888888888... \n",
" \n",
" \n",
" 16 \n",
" 17 \n",
" 4444.888889 \n",
" 1 \n",
" 3 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 1.666666666666667, 'y': -3.888888888888889} \n",
" \n",
" \n",
" 17 \n",
" 18 \n",
" 13470.619723 \n",
" 1 \n",
" 2 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 2.777777777777778, 'y': -3.888888888888889} \n",
" \n",
" \n",
" 18 \n",
" 19 \n",
" 36155.274501 \n",
" 1 \n",
" 1 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 3.8888888888888893, 'y': -3.888888888888... \n",
" \n",
" \n",
" 19 \n",
" 20 \n",
" 83472.790123 \n",
" 1 \n",
" 4 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 5.0, 'y': -3.888888888888889} \n",
" \n",
" \n",
" 20 \n",
" 21 \n",
" 77196.493827 \n",
" 1 \n",
" 3 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': -5, 'y': -2.7777777777777777} \n",
" \n",
" \n",
" 21 \n",
" 22 \n",
" 32069.321140 \n",
" 1 \n",
" 2 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': -3.888888888888889, 'y': -2.777777777777... \n",
" \n",
" \n",
" 22 \n",
" 23 \n",
" 11026.312452 \n",
" 1 \n",
" 1 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': -2.7777777777777777, 'y': -2.77777777777... \n",
" \n",
" \n",
" 23 \n",
" 24 \n",
" 3093.530864 \n",
" 1 \n",
" 4 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': -1.6666666666666665, 'y': -2.77777777777... \n",
" \n",
" \n",
" 24 \n",
" 25 \n",
" 955.018442 \n",
" 1 \n",
" 3 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': -0.5555555555555554, 'y': -2.77777777777... \n",
" \n",
" \n",
" 25 \n",
" 26 \n",
" 952.796220 \n",
" 1 \n",
" 2 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 0.5555555555555558, 'y': -2.777777777777... \n",
" \n",
" \n",
" 26 \n",
" 27 \n",
" 3086.864198 \n",
" 1 \n",
" 1 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 1.666666666666667, 'y': -2.7777777777777... \n",
" \n",
" \n",
" 27 \n",
" 28 \n",
" 11015.201341 \n",
" 1 \n",
" 4 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 2.777777777777778, 'y': -2.7777777777777... \n",
" \n",
" \n",
" 28 \n",
" 29 \n",
" 32053.765585 \n",
" 1 \n",
" 3 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 3.8888888888888893, 'y': -2.777777777777... \n",
" \n",
" \n",
" 29 \n",
" 30 \n",
" 77176.493827 \n",
" 1 \n",
" 2 \n",
" 1564088283 \n",
" 1564088283 \n",
" {'x': 5.0, 'y': -2.7777777777777777} \n",
" \n",
" \n",
" ... \n",
" ... \n",
" ... \n",
" ... \n",
" ... \n",
" ... \n",
" ... \n",
" ... \n",
" \n",
" \n",
" 70 \n",
" 71 \n",
" 49418.716049 \n",
" 1 \n",
" 1 \n",
" 1564088284 \n",
" 1564088284 \n",
" {'x': -5, 'y': 2.777777777777778} \n",
" \n",
" \n",
" 71 \n",
" 72 \n",
" 15265.480262 \n",
" 1 \n",
" 4 \n",
" 1564088284 \n",
" 1564088284 \n",
" {'x': -3.888888888888889, 'y': 2.777777777777778} \n",
" \n",
" \n",
" 72 \n",
" 73 \n",
" 2452.924249 \n",
" 1 \n",
" 3 \n",
" 1564088284 \n",
" 1564088284 \n",
" {'x': -2.7777777777777777, 'y': 2.777777777777... \n",
" \n",
" \n",
" 73 \n",
" 74 \n",
" 7.111111 \n",
" 1 \n",
" 2 \n",
" 1564088284 \n",
" 1564088284 \n",
" {'x': -1.6666666666666665, 'y': 2.777777777777... \n",
" \n",
" \n",
" 74 \n",
" 75 \n",
" 612.082914 \n",
" 1 \n",
" 1 \n",
" 1564088284 \n",
" 1564088284 \n",
" {'x': -0.5555555555555554, 'y': 2.777777777777... \n",
" \n",
" \n",
" 75 \n",
" 76 \n",
" 609.860692 \n",
" 1 \n",
" 4 \n",
" 1564088284 \n",
" 1564088284 \n",
" {'x': 0.5555555555555558, 'y': 2.777777777777778} \n",
" \n",
" \n",
" 76 \n",
" 77 \n",
" 0.444444 \n",
" 1 \n",
" 3 \n",
" 1564088284 \n",
" 1564088284 \n",
" {'x': 1.666666666666667, 'y': 2.777777777777778} \n",
" \n",
" \n",
" 77 \n",
" 78 \n",
" 2441.813138 \n",
" 1 \n",
" 2 \n",
" 1564088284 \n",
" 1564088284 \n",
" {'x': 2.777777777777778, 'y': 2.777777777777778} \n",
" \n",
" \n",
" 78 \n",
" 79 \n",
" 15249.924707 \n",
" 1 \n",
" 1 \n",
" 1564088284 \n",
" 1564088284 \n",
" {'x': 3.8888888888888893, 'y': 2.777777777777778} \n",
" \n",
" \n",
" 79 \n",
" 80 \n",
" 49398.716049 \n",
" 1 \n",
" 4 \n",
" 1564088284 \n",
" 1564088284 \n",
" {'x': 5.0, 'y': 2.777777777777778} \n",
" \n",
" \n",
" 80 \n",
" 81 \n",
" 44603.901235 \n",
" 1 \n",
" 3 \n",
" 1564088284 \n",
" 1564088285 \n",
" {'x': -5, 'y': 3.8888888888888893} \n",
" \n",
" \n",
" 81 \n",
" 82 \n",
" 12645.452827 \n",
" 1 \n",
" 2 \n",
" 1564088284 \n",
" 1564088285 \n",
" {'x': -3.888888888888889, 'y': 3.8888888888888... \n",
" \n",
" \n",
" 82 \n",
" 83 \n",
" 1478.987349 \n",
" 1 \n",
" 1 \n",
" 1564088284 \n",
" 1564088285 \n",
" {'x': -2.7777777777777777, 'y': 3.888888888888... \n",
" \n",
" \n",
" 83 \n",
" 84 \n",
" 130.567901 \n",
" 1 \n",
" 4 \n",
" 1564088284 \n",
" 1564088285 \n",
" {'x': -1.6666666666666665, 'y': 3.888888888888... \n",
" \n",
" \n",
" 84 \n",
" 85 \n",
" 1284.236549 \n",
" 1 \n",
" 3 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': -0.5555555555555554, 'y': 3.888888888888... \n",
" \n",
" \n",
" 85 \n",
" 86 \n",
" 1282.014327 \n",
" 1 \n",
" 2 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': 0.5555555555555558, 'y': 3.8888888888888... \n",
" \n",
" \n",
" 86 \n",
" 87 \n",
" 123.901235 \n",
" 1 \n",
" 1 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': 1.666666666666667, 'y': 3.8888888888888893} \n",
" \n",
" \n",
" 87 \n",
" 88 \n",
" 1467.876238 \n",
" 1 \n",
" 4 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': 2.777777777777778, 'y': 3.8888888888888893} \n",
" \n",
" \n",
" 88 \n",
" 89 \n",
" 12629.897272 \n",
" 1 \n",
" 3 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': 3.8888888888888893, 'y': 3.8888888888888... \n",
" \n",
" \n",
" 89 \n",
" 90 \n",
" 44583.901235 \n",
" 1 \n",
" 2 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': 5.0, 'y': 3.8888888888888893} \n",
" \n",
" \n",
" 90 \n",
" 91 \n",
" 40036.000000 \n",
" 1 \n",
" 1 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': -5, 'y': 5.0} \n",
" \n",
" \n",
" 91 \n",
" 92 \n",
" 10272.338973 \n",
" 1 \n",
" 4 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': -3.888888888888889, 'y': 5.0} \n",
" \n",
" \n",
" 92 \n",
" 93 \n",
" 751.964030 \n",
" 1 \n",
" 3 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': -2.7777777777777777, 'y': 5.0} \n",
" \n",
" \n",
" 93 \n",
" 94 \n",
" 500.938272 \n",
" 1 \n",
" 2 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': -1.6666666666666665, 'y': 5.0} \n",
" \n",
" \n",
" 94 \n",
" 95 \n",
" 2203.303765 \n",
" 1 \n",
" 1 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': -0.5555555555555554, 'y': 5.0} \n",
" \n",
" \n",
" 95 \n",
" 96 \n",
" 2201.081542 \n",
" 1 \n",
" 4 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': 0.5555555555555558, 'y': 5.0} \n",
" \n",
" \n",
" 96 \n",
" 97 \n",
" 494.271605 \n",
" 1 \n",
" 3 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': 1.666666666666667, 'y': 5.0} \n",
" \n",
" \n",
" 97 \n",
" 98 \n",
" 740.852919 \n",
" 1 \n",
" 2 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': 2.777777777777778, 'y': 5.0} \n",
" \n",
" \n",
" 98 \n",
" 99 \n",
" 10256.783417 \n",
" 1 \n",
" 1 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': 3.8888888888888893, 'y': 5.0} \n",
" \n",
" \n",
" 99 \n",
" 100 \n",
" 40016.000000 \n",
" 1 \n",
" 4 \n",
" 1564088285 \n",
" 1564088285 \n",
" {'x': 5.0, 'y': 5.0} \n",
" \n",
" \n",
"
\n",
"
100 rows × 7 columns
\n",
"
"
],
"text/plain": [
" jid score eid rid start_time end_time \\\n",
"0 1 90036.000000 1 3 1564088282 1564088283 \n",
"1 2 40519.252553 1 2 1564088282 1564088283 \n",
"2 3 16184.062795 1 1 1564088282 1564088283 \n",
"3 4 6056.493827 1 4 1564088282 1564088283 \n",
"4 5 2820.587715 1 3 1564088283 1564088283 \n",
"5 6 2818.365493 1 2 1564088283 1564088283 \n",
"6 7 6049.827160 1 1 1564088283 1564088283 \n",
"7 8 16172.951684 1 4 1564088283 1564088283 \n",
"8 9 40503.696997 1 3 1564088283 1564088283 \n",
"9 10 90016.000000 1 2 1564088283 1564088283 \n",
"10 11 83492.790123 1 1 1564088283 1564088283 \n",
"11 12 36170.830056 1 4 1564088283 1564088283 \n",
"12 13 13481.730834 1 3 1564088283 1564088283 \n",
"13 14 4451.555556 1 2 1564088283 1564088283 \n",
"14 15 1764.346289 1 1 1564088283 1564088283 \n",
"15 16 1762.124066 1 4 1564088283 1564088283 \n",
"16 17 4444.888889 1 3 1564088283 1564088283 \n",
"17 18 13470.619723 1 2 1564088283 1564088283 \n",
"18 19 36155.274501 1 1 1564088283 1564088283 \n",
"19 20 83472.790123 1 4 1564088283 1564088283 \n",
"20 21 77196.493827 1 3 1564088283 1564088283 \n",
"21 22 32069.321140 1 2 1564088283 1564088283 \n",
"22 23 11026.312452 1 1 1564088283 1564088283 \n",
"23 24 3093.530864 1 4 1564088283 1564088283 \n",
"24 25 955.018442 1 3 1564088283 1564088283 \n",
"25 26 952.796220 1 2 1564088283 1564088283 \n",
"26 27 3086.864198 1 1 1564088283 1564088283 \n",
"27 28 11015.201341 1 4 1564088283 1564088283 \n",
"28 29 32053.765585 1 3 1564088283 1564088283 \n",
"29 30 77176.493827 1 2 1564088283 1564088283 \n",
".. ... ... ... ... ... ... \n",
"70 71 49418.716049 1 1 1564088284 1564088284 \n",
"71 72 15265.480262 1 4 1564088284 1564088284 \n",
"72 73 2452.924249 1 3 1564088284 1564088284 \n",
"73 74 7.111111 1 2 1564088284 1564088284 \n",
"74 75 612.082914 1 1 1564088284 1564088284 \n",
"75 76 609.860692 1 4 1564088284 1564088284 \n",
"76 77 0.444444 1 3 1564088284 1564088284 \n",
"77 78 2441.813138 1 2 1564088284 1564088284 \n",
"78 79 15249.924707 1 1 1564088284 1564088284 \n",
"79 80 49398.716049 1 4 1564088284 1564088284 \n",
"80 81 44603.901235 1 3 1564088284 1564088285 \n",
"81 82 12645.452827 1 2 1564088284 1564088285 \n",
"82 83 1478.987349 1 1 1564088284 1564088285 \n",
"83 84 130.567901 1 4 1564088284 1564088285 \n",
"84 85 1284.236549 1 3 1564088285 1564088285 \n",
"85 86 1282.014327 1 2 1564088285 1564088285 \n",
"86 87 123.901235 1 1 1564088285 1564088285 \n",
"87 88 1467.876238 1 4 1564088285 1564088285 \n",
"88 89 12629.897272 1 3 1564088285 1564088285 \n",
"89 90 44583.901235 1 2 1564088285 1564088285 \n",
"90 91 40036.000000 1 1 1564088285 1564088285 \n",
"91 92 10272.338973 1 4 1564088285 1564088285 \n",
"92 93 751.964030 1 3 1564088285 1564088285 \n",
"93 94 500.938272 1 2 1564088285 1564088285 \n",
"94 95 2203.303765 1 1 1564088285 1564088285 \n",
"95 96 2201.081542 1 4 1564088285 1564088285 \n",
"96 97 494.271605 1 3 1564088285 1564088285 \n",
"97 98 740.852919 1 2 1564088285 1564088285 \n",
"98 99 10256.783417 1 1 1564088285 1564088285 \n",
"99 100 40016.000000 1 4 1564088285 1564088285 \n",
"\n",
" job_config \n",
"0 {'x': -5, 'y': -5} \n",
"1 {'x': -3.888888888888889, 'y': -5} \n",
"2 {'x': -2.7777777777777777, 'y': -5} \n",
"3 {'x': -1.6666666666666665, 'y': -5} \n",
"4 {'x': -0.5555555555555554, 'y': -5} \n",
"5 {'x': 0.5555555555555558, 'y': -5} \n",
"6 {'x': 1.666666666666667, 'y': -5} \n",
"7 {'x': 2.777777777777778, 'y': -5} \n",
"8 {'x': 3.8888888888888893, 'y': -5} \n",
"9 {'x': 5.0, 'y': -5} \n",
"10 {'x': -5, 'y': -3.888888888888889} \n",
"11 {'x': -3.888888888888889, 'y': -3.888888888888... \n",
"12 {'x': -2.7777777777777777, 'y': -3.88888888888... \n",
"13 {'x': -1.6666666666666665, 'y': -3.88888888888... \n",
"14 {'x': -0.5555555555555554, 'y': -3.88888888888... \n",
"15 {'x': 0.5555555555555558, 'y': -3.888888888888... \n",
"16 {'x': 1.666666666666667, 'y': -3.888888888888889} \n",
"17 {'x': 2.777777777777778, 'y': -3.888888888888889} \n",
"18 {'x': 3.8888888888888893, 'y': -3.888888888888... \n",
"19 {'x': 5.0, 'y': -3.888888888888889} \n",
"20 {'x': -5, 'y': -2.7777777777777777} \n",
"21 {'x': -3.888888888888889, 'y': -2.777777777777... \n",
"22 {'x': -2.7777777777777777, 'y': -2.77777777777... \n",
"23 {'x': -1.6666666666666665, 'y': -2.77777777777... \n",
"24 {'x': -0.5555555555555554, 'y': -2.77777777777... \n",
"25 {'x': 0.5555555555555558, 'y': -2.777777777777... \n",
"26 {'x': 1.666666666666667, 'y': -2.7777777777777... \n",
"27 {'x': 2.777777777777778, 'y': -2.7777777777777... \n",
"28 {'x': 3.8888888888888893, 'y': -2.777777777777... \n",
"29 {'x': 5.0, 'y': -2.7777777777777777} \n",
".. ... \n",
"70 {'x': -5, 'y': 2.777777777777778} \n",
"71 {'x': -3.888888888888889, 'y': 2.777777777777778} \n",
"72 {'x': -2.7777777777777777, 'y': 2.777777777777... \n",
"73 {'x': -1.6666666666666665, 'y': 2.777777777777... \n",
"74 {'x': -0.5555555555555554, 'y': 2.777777777777... \n",
"75 {'x': 0.5555555555555558, 'y': 2.777777777777778} \n",
"76 {'x': 1.666666666666667, 'y': 2.777777777777778} \n",
"77 {'x': 2.777777777777778, 'y': 2.777777777777778} \n",
"78 {'x': 3.8888888888888893, 'y': 2.777777777777778} \n",
"79 {'x': 5.0, 'y': 2.777777777777778} \n",
"80 {'x': -5, 'y': 3.8888888888888893} \n",
"81 {'x': -3.888888888888889, 'y': 3.8888888888888... \n",
"82 {'x': -2.7777777777777777, 'y': 3.888888888888... \n",
"83 {'x': -1.6666666666666665, 'y': 3.888888888888... \n",
"84 {'x': -0.5555555555555554, 'y': 3.888888888888... \n",
"85 {'x': 0.5555555555555558, 'y': 3.8888888888888... \n",
"86 {'x': 1.666666666666667, 'y': 3.8888888888888893} \n",
"87 {'x': 2.777777777777778, 'y': 3.8888888888888893} \n",
"88 {'x': 3.8888888888888893, 'y': 3.8888888888888... \n",
"89 {'x': 5.0, 'y': 3.8888888888888893} \n",
"90 {'x': -5, 'y': 5.0} \n",
"91 {'x': -3.888888888888889, 'y': 5.0} \n",
"92 {'x': -2.7777777777777777, 'y': 5.0} \n",
"93 {'x': -1.6666666666666665, 'y': 5.0} \n",
"94 {'x': -0.5555555555555554, 'y': 5.0} \n",
"95 {'x': 0.5555555555555558, 'y': 5.0} \n",
"96 {'x': 1.666666666666667, 'y': 5.0} \n",
"97 {'x': 2.777777777777778, 'y': 5.0} \n",
"98 {'x': 3.8888888888888893, 'y': 5.0} \n",
"99 {'x': 5.0, 'y': 5.0} \n",
"\n",
"[100 rows x 7 columns]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# show details in job history for one experiment\n",
"history = sql.get_all_history(1)\n",
"history = pd.DataFrame(history)\n",
"history.columns = ['jid', 'score','eid','rid','start_time','end_time','job_config']\n",
"history"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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+5QuCV1IMP2oFBER72knZZFT0//aPOKco6mpyeswRFoVmqLcDo9w8fsU5eai/kjjEVIStf5WRET8ISs9i78d+htHG496HYqIb8VVgWtm3zazR+rr670OJayamhouuugivv/975Obm8t1111HYWEhOTk53HbbbaHrMjMzue222xg+fDh5eXns2rULgI8++ojS0lJycnKYPXv2cdvE/+pXvyI3N5fc3Fzuvffe0PsNGjSIWbNmkZ2dzTXXXMO6desoKSkhKyuLsrKyju0AiSnnJJ8DfPHInGgKbTLl0wK35bRsv8YoEiv8nptFYkl2j2waXSNv1b/ldSgivhVXjwlyzj0PPF9YWPiDNi9cuwDeq4zum381D7511ykv27NnD4899hgjR47k4MGD9OjRg8bGRsaPH88bb7xBfn4+AL169WLbtm089NBDLF68mN/85jcsWrSI0aNHc+utt/LCCy/w29/+FoBXX32VpUuXsmXLFpxzfP3rX+fSSy8lIyOD6upqfv/73/Poo49SVFTEypUr2bBhA8899xx33nknzz77bHT7QeJGcmIy3ZK6tcsIrt/X4LYscLWDssiZiTg3i8gpZadnA7Cndg+DegzyOBoRf4qrAjcWXHjhhYwcORKAJ598kkceeYSGhgbeffdddu7cGSpwp06dCsCIESN4+umnAXjllVdCn19xxRVkZGQAsGHDBq688kpSU1NDr12/fj2TJ0+mX79+5OXlAZCTk8P48eMxM/Ly8qipqemw71ti0zldz2mXEdzuXfy9BjfBEki0RBpdozaYEhER37jgnAvomtCVlVUref3A62fcXpeELkzqP4mcXjlRiE7EHzpngRvBSGt7aS5C//rXv7J48WK2bt1KRkYGs2bN4siRI6HrkpMD/6lOTEykoaHhS79fczsACQkJoeOEhIQzalc6h29c+A3OP/v8qLebmuTvNbgQGMVtbFSBKyIi/pGUkMTEzIls3L+R/Yf3n3F7h48d5j+r/pPRfUbzo/wfUXBeQRSiFPFW5yxwfeDQoUOkpqaSlpbG+++/z9q1axk7dmybrxkzZgwrV65k4cKFrF27ltraWgAuueQSZs2axYIFC3DO8cwzz/C73/2uA74LiXcLihe0S7vNI7jtsUNztCQlJPF54+cqcEVExFfuvOTOqLX1ydFPeOLNJ1i+Yzkz187kooyLQjnaL1IS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\n",
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(16,8))\n",
"plt.subplot(121)\n",
"for i in eids:\n",
" history = sql.get_all_history(i)\n",
" history = pd.DataFrame(history)\n",
" history.columns = ['jid', 'score','eid','rid','start_time','end_time','job_config']\n",
" sql.cursor.execute(\"SELECT * FROM experiment where eid = ?\", (i,))\n",
" label = json.loads(sql.cursor.fetchone()[4])['proposer']\n",
" plt.plot(history.score, label=label)\n",
"plt.legend()\n",
"plt.xlabel(\"Iteration\")\n",
"plt.ylabel(\"Accuracy\")\n",
"plt.yscale(\"log\")\n",
"\n",
"plt.subplot(122)\n",
"for i in eids:\n",
" history = sql.get_all_history(i)\n",
" history = pd.DataFrame(history)\n",
" history.columns = ['jid', 'score','eid','rid','start_time','end_time','job_config']\n",
" sql.cursor.execute(\"SELECT * FROM experiment where eid = ?\", (i,))\n",
" label = json.loads(sql.cursor.fetchone()[4])['proposer']\n",
" plt.plot(history.score.cummin(), label=label)\n",
"plt.legend()\n",
"plt.xlabel(\"Iteration\")\n",
"plt.ylabel(\"Best Accuracy so far\")\n",
"plt.yscale(\"log\")"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"sql.close()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: Examples/2dfunc_diff_opt/README.md
================================================
# Rosenbrock demo
## Setup
python -m aup.setup
To setup different resource configuration, see [resource example](../2dfunc_diff_res/README.md).
## Experiment
python -m aup experiment_sequence.json --log warn
python -m aup experiment_random.json
python -m aup experiment_spearmint.json
python -m aup experiment_hyperopt.json
Results are presented in [History](History.ipynb)
## Experiment Auto-convert
To convert the original `rosenbrock_origin.py` for *Auptimizer*, users can use
python -m aup.convert rosenbrock_origin.py experiment_auto.json rosenbrock
It will create `rosenbrock_auto.py` for user to use for *Auptimizer* experiment.
## Reset
To clean the database, use
python -m aup.setupdb.reset .aup/env.ini
================================================
FILE: Examples/2dfunc_diff_opt/experiment_auto.json
================================================
{
"name": "./2dfunc_diff_opt/experiment_auto.json",
"proposer": "random",
"n_samples": 10,
"random_seed": 1,
"script": "rosenbrock_auto.py",
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "cpu",
"n_parallel": 1,
"target":"min"
}
================================================
FILE: Examples/2dfunc_diff_opt/experiment_bohb.json
================================================
{
"name": "./2dfunc_diff_opt/experiment_bohb.json",
"script": "rosenbrock_hpo.py",
"resource": "cpu",
"n_parallel": 5,
"target": "min",
"workingdir": "./",
"proposer": "bohb",
"n_iterations": 3,
"min_points_in_model": "",
"top_n_percent": 15,
"num_samples": 64,
"random_fraction": 0.3333333333333333,
"bandwidth_factor": 3,
"min_bandwidth": 0.001,
"eta": 3,
"min_budget": 1,
"max_budget": 9,
"parameter_config": [
{
"name": "x",
"range": [
0,
1
],
"type": "float"
},
{
"name": "y",
"range": [
0,
1
],
"type": "float"
}
]
}
================================================
FILE: Examples/2dfunc_diff_opt/experiment_hyperband.json
================================================
{
"name": "./2dfunc_diff_opt/experiment_hyperband.json",
"proposer": "hyperband",
"script": "rosenbrock_hpo.py",
"resource": "cpu",
"n_parallel": 1,
"target":"max",
"max_iter": 3,
"engine": "random",
"parameter_config": [
{
"name": "x",
"range": [-5, 5],
"type": "float"
},
{
"name": "y",
"range": [-5,5],
"type": "float"
}
]
}
================================================
FILE: Examples/2dfunc_diff_opt/experiment_hyperopt.json
================================================
{
"name": "./2dfunc_diff_opt/experiment_hyperopt.json",
"proposer": "hyperopt",
"random_seed": 1,
"script": "rosenbrock_hpo.py",
"engine":"tpe",
"n_samples": 100,
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "cpu",
"n_parallel": 4,
"target":"min"
}
================================================
FILE: Examples/2dfunc_diff_opt/experiment_random.json
================================================
{
"name": "./2dfunc_diff_opt/experiment_random.json",
"proposer": "random",
"n_samples": 10,
"random_seed": 1,
"script": "rosenbrock_hpo.py",
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "cpu",
"n_parallel": 2,
"target":"min"
}
================================================
FILE: Examples/2dfunc_diff_opt/experiment_sequence.json
================================================
{
"name": "./2dfunc_diff_opt/experiment_sequence.json",
"proposer": "sequence",
"script": "rosenbrock_hpo.py",
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float",
"n": 10
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float",
"n": 10
}
],
"resource": "cpu",
"n_parallel": 4,
"target":"min"
}
================================================
FILE: Examples/2dfunc_diff_opt/experiment_spearmint.json
================================================
{
"name": "./2dfunc_diff_opt/experiment_spearmint.json",
"proposer": "spearmint",
"n_samples": 100,
"random_seed": 1,
"script": "rosenbrock_hpo.py",
"engine":"GPEIChooser",
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float",
"size": 1
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float",
"size": 1
}
],
"resource": "cpu",
"n_parallel": 2,
"target":"min"
}
================================================
FILE: Examples/2dfunc_diff_opt/rosenbrock_hpo.py
================================================
#!/usr/bin/env python
"""
Modified Rosenbrock function for HPO and aup
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import sys
""" # ver 1.0 - modify existing code
from aup import BasicConfig, print_result
def rosenbrock(conf, a=1, b=100):
x = conf.x
y = conf.y
return (a-x)*(a-x) + b*(y-x*x)*(y-x*x)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("config file required")
exit(1)
config = BasicConfig().load(sys.argv[1])
val = rosenbrock(config)
print_result(val)
"""
from aup import aup_args
@aup_args
def rosenbrock(x, y, a=1, b=100):
return (a-x)*(a-x) + b*(y-x*x)*(y-x*x)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("config file required")
exit(1)
rosenbrock(sys.argv[1])
================================================
FILE: Examples/2dfunc_diff_opt/rosenbrock_origin.py
================================================
"""
Demonstration code for Rosenbrock function
==========================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
def rosenbrock(x, y, a=1, b=100):
return (a-x)*(a-x) + b*(y-x*x)*(y-x*x)
================================================
FILE: Examples/2dfunc_diff_res/README.md
================================================
# Rosenbrock demo for different resources
## CPU
python -m aup.setup cpu.ini
python -m aup exp_cpu.json
## GPU
Using a file contains the GPU ID per line will create GPU resources accordingly
python -m aup.setup cpu.ini --gpu gpu.txt --overwrite
python -m aup exp_gpu.json
## multi-node
For multi node environment, first set up the ssh key (see [here](https://help.ubuntu.com/community/SSH/OpenSSH/Keys) as example), then either use IP for node, or use node name with IP in `/etc/hosts` file. In `node.txt`, each line contains `:[:port]`, where port number is optional (default 22).
python -m aup.setup cpu.ini --node node.txt --overwrite
python -m aup exp_node.json
Also, it is important to add `workingdir` for remote execution.
## Passive mode
Passive mode is used for debugging purpose. It helps users to run training code manually and check potential problem in
the training code.
python -m aup experiment_passive.json
The code will create the job configuration file, interactively ask you to run the script, and ask for the result from your training code.
e.g.
# Job running path is Examples/2dfunc_diff_res
# Config is at Examples/2dfunc_diff_res/jobs/452.json
Job command is:
Examples/2dfunc_diff_res/rosenbrock_hpo.py
Return results:
Then you should run the command in another terminal like:
./rosenbrock_hpo.py ./jobs/452.json
You will see something like:
#Auptimizer:232.78278278806914
Once you paste the result back to **Auptimizer**, it will ask you for the next one.
================================================
FILE: Examples/2dfunc_diff_res/aws.txt
================================================
AWS_EC2_ID SSH_KEY
AWS_EC2_ID SSH_KEY
================================================
FILE: Examples/2dfunc_diff_res/cpu.ini
================================================
[Auptimizer]
# Auptimizer environment folder to be created, this file will be copied over
Auptimizer_PATH=./.aup
# Temp folder
TMP_FOLDER=./aup_tmp
# SQL engine
SQL_ENGINE=sqlite
================================================
FILE: Examples/2dfunc_diff_res/env_local_template.ini
================================================
[Auptimizer]
# Auptimizer environment folder to be created, this file will be copied over
Auptimizer_PATH=./.aup
# Temp folder
TMP_FOLDER=./aup_tmp
# SQL engine
SQL_ENGINE=sqlite
================================================
FILE: Examples/2dfunc_diff_res/env_user_template.ini
================================================
[Auptimizer]
# Auptimizer environment folder to be created, this file will be copied over
Auptimizer_PATH=~/.aup
# Temp folder
TMP_FOLDER=/tmp/aup
# SQL engine
SQL_ENGINE=sqlite
================================================
FILE: Examples/2dfunc_diff_res/exp_aws.json
================================================
{
"name": "./2dfunc_diff_res/exp_aws.json",
"workingdir": "/home/ubuntu/",
"proposer": "random",
"n_samples": 10,
"resource_args": {
"shutdown":true,
"connection_retry": 5
},
"runtime_args": {
"prescript": "source .profile",
"postscript": "echo 'DONE'"
},
"random_seed": 1,
"script": "./rosenbrock_hpo.py",
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "aws",
"n_parallel": 1,
"target":"min"
}
================================================
FILE: Examples/2dfunc_diff_res/exp_cpu.json
================================================
{
"name": "./2dfunc_diff_res/exp_cpu.json",
"proposer": "sequence",
"n_samples": 10,
"random_seed": 1,
"script": "rosenbrock_hpo.py",
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "cpu",
"n_parallel": 2,
"target":"min"
}
================================================
FILE: Examples/2dfunc_diff_res/exp_gpu.json
================================================
{
"name": "./2dfunc_diff_res/exp_gpu.json",
"proposer": "random",
"n_samples": 10,
"random_seed": 1,
"script": "rosenbrock_hpo.py",
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "gpu",
"n_parallel": 2,
"target":"min"
}
================================================
FILE: Examples/2dfunc_diff_res/exp_node.json
================================================
{
"name": "./2dfunc_diff_res/exp_node.json",
"workingdir": "/home/ubuntu/aup_demo",
"proposer": "random",
"n_samples": 10,
"random_seed": 1,
"script": "rosenbrock_hpo.py",
"runtime_args": {
"prescript": "export CUDA_VISIBLE_DEVICES=-1",
"postscript": "echo $CUDA_VISIBLE_DEVICES",
"overwrite": true
},
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "node",
"n_parallel": 2,
"target":"min",
"resource_args": {
"max_retries": 2,
"reconn_wait_time": 3
}
}
================================================
FILE: Examples/2dfunc_diff_res/exp_node_async.json
================================================
{
"name": "./2dfunc_diff_res/exp_node_async.json",
"workingdir": "/home/paul/aup_demo",
"proposer": "random",
"n_samples": 10,
"random_seed": 1,
"script": "rosenbrock_hpo.py",
"runtime_args": {
"prescript": "export CUDA_VISIBLE_DEVICES=-1",
"postscript": "echo $CUDA_VISIBLE_DEVICES",
"overwrite": true
},
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "node",
"n_parallel": 2,
"target":"min",
"resource_args": {
"max_retries": 2,
"reconn_wait_time": 3,
"async_run": true,
"async_reconnect": 1,
"async_timeout": 100
}
}
================================================
FILE: Examples/2dfunc_diff_res/exp_passive.json
================================================
{
"name": "./2dfunc_diff_res/exp_passive.json",
"proposer": "random",
"n_samples": 5,
"random_seed": 1,
"script": "rosenbrock_hpo.py",
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "passive",
"target":"min"
}
================================================
FILE: Examples/2dfunc_diff_res/gpu.txt
================================================
0
1
================================================
FILE: Examples/2dfunc_diff_res/node.txt
================================================
jasonliu@ubuntu1
jasonliu@ubuntu2
================================================
FILE: Examples/2dfunc_diff_res/plainGPU.txt
================================================
0
================================================
FILE: Examples/2dfunc_diff_res/rosenbrock_hpo.py
================================================
#!/usr/bin/env python
"""
Modified Rosenbrock function for HPO and aup
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import sys
""" # ver 1.0 - modify existing code
from aup import BasicConfig, print_result
def rosenbrock(conf, a=1, b=100):
x = conf.x
y = conf.y
return (a-x)*(a-x) + b*(y-x*x)*(y-x*x)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("config file required")
exit(1)
config = BasicConfig().load(sys.argv[1])
val = rosenbrock(config)
print_result(val)
"""
from aup import aup_args
@aup_args
def rosenbrock(x, y, a=1, b=100):
return (a-x)*(a-x) + b*(y-x*x)*(y-x*x)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("config file required")
exit(1)
rosenbrock(sys.argv[1])
================================================
FILE: Examples/2dfunc_diff_res/singleGPU.txt
================================================
0
0
================================================
FILE: Examples/cai_eas/.gitignore
================================================
net_pool
arch_search
## Core latex/pdflatex auxiliary files:
*.aux
*.lof
*.log
*.lot
*.fls
*.out
*.toc
## Intermediate documents:
*.dvi
*-converted-to.*
# these rules might exclude image files for figures etc.
# *.ps
# *.eps
# *.pdf
/Datasets
## Bibliography auxiliary files (bibtex/biblatex/biber):
*.bbl
*.bcf
*.blg
*-blx.aux
*-blx.bib
*.brf
*.run.xml
## Build tool auxiliary files:
*.fdb_latexmk
*.synctex
*.synctex.gz
*.synctex.gz(busy)
*.pdfsync
## Auxiliary and intermediate files from other packages:
# algorithms
*.alg
*.loa
# achemso
acs-*.bib
# amsthm
*.thm
# beamer
*.nav
*.snm
*.vrb
#(e)ledmac/(e)ledpar
*.end
*.[1-9]
*.[1-9][0-9]
*.[1-9][0-9][0-9]
*.[1-9]R
*.[1-9][0-9]R
*.[1-9][0-9][0-9]R
*.eledsec[1-9]
*.eledsec[1-9]R
*.eledsec[1-9][0-9]
*.eledsec[1-9][0-9]R
*.eledsec[1-9][0-9][0-9]
*.eledsec[1-9][0-9][0-9]R
# glossaries
*.acn
*.acr
*.glg
*.glo
*.gls
# gnuplottex
*-gnuplottex-*
# hyperref
# knitr
*-concordance.tex
*.tikz
*-tikzDictionary
# listings
*.lol
# makeidx
*.idx
*.ilg
*.ind
*.ist
tex/rl-meta.pdf
# minitoc
*.maf
*.mtc
*.mtc[0-9]
*.mtc[1-9][0-9]
# minted
_minted*
*.pyg
# morewrites
*.mw
# mylatexformat
*.fmt
# nomencl
*.nlo
# sagetex
*.sagetex.sage
*.sagetex.py
*.sagetex.scmd
# sympy
*.sout
*.sympy
sympy-plots-for-*.tex/
# TikZ & PGF
*.dpth
*.md5
*.auxlock
# todonotes
*.tdo
# xindy
*.xdy
# WinEdt
*.bak
*.sav
*.DS_Store
/data
*/.idea/
/output/
/exp/
/backup/
# python
__pycache__
.pyc
================================================
FILE: Examples/cai_eas/README.md
================================================
# HPO for Deep Neural Networks
Demonstration using https://github.com/han-cai/EAS
The HPO engine is integrated into the Auptimizer.
The running script is a modification of `client.py`.
Run it as
cd eas
python -m aup experiment.json
Notice that the start_nets is needed in this folder although the experiment is running in the eas folder
================================================
FILE: Examples/cai_eas/eas/client.py
================================================
#!/usr/bin/env python
"""
Modified from https://github.com/han-cai/EAS
The file to run in the client side
Train the network and return the validation performance
"""
import os
from expdir_monitor.expdir_monitor import ExpdirMonitor
import time
import sys
from aup import BasicConfig, print_result
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
def run(expdir):
start_time = time.time()
expdir_monitor = ExpdirMonitor(expdir)
valid_performance = expdir_monitor.run(pure=True, restore=False)
end_time = time.time()
print('running time: %s' % (end_time - start_time))
print('valid performance: %s' % valid_performance)
print_result(valid_performance)
def main():
print("Starting Client")
config = BasicConfig().load(sys.argv[1])
run(config["expdir"])
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
pass
================================================
FILE: Examples/cai_eas/eas/data_providers/__init__.py
================================================
================================================
FILE: Examples/cai_eas/eas/data_providers/base_provider.py
================================================
import numpy as np
class DataSet:
"""Class to represent some dataset: train, validation, test"""
@property
def num_examples(self):
"""Return qtty of examples in dataset"""
raise NotImplementedError
def next_batch(self, batch_size):
"""Return batch of required size of data, labels"""
raise NotImplementedError
class ImagesDataSet(DataSet):
"""Dataset for images that provide some often used methods"""
@staticmethod
def measure_mean_and_std(images):
# for every channel in image
means = []
stds = []
# for every channel in image(assume this is last dimension)
for ch in range(images.shape[-1]):
means.append(np.mean(images[:, :, :, ch]))
stds.append(np.std(images[:, :, :, ch]))
return means, stds
@staticmethod
def shuffle_images_and_labels(images, labels):
rand_indexes = np.random.permutation(images.shape[0])
shuffled_images = images[rand_indexes]
shuffled_labels = labels[rand_indexes]
return shuffled_images, shuffled_labels
@staticmethod
def normalize_images(images, normalization_type, meanstd=None):
"""
Args:
images: numpy 4D array
normalization_type: `str`, available choices:
- divide_255
- divide_256
- by_channels
meanstd
"""
if normalization_type is not None:
if normalization_type == 'divide_255':
images = images / 255
elif normalization_type == 'divide_256':
images = images / 256
elif normalization_type == 'by_channels':
images = images.astype('float64')
# for every channel in image(assume this is last dimension)
means, stds = meanstd
for i in range(images.shape[-1]):
images[:, :, :, i] = ((images[:, :, :, i] - means[i]) / stds[i])
else:
raise Exception('Unknown type of normalization')
return images
class DataProvider:
_SEED = 88
@property
def data_shape(self):
"""Return shape as python list of one data entry"""
raise NotImplementedError
@property
def n_classes(self):
"""Return `int` of num classes"""
raise NotImplementedError
def labels_to_one_hot(self, labels):
"""Convert 1D array of labels to one hot representation
Args:
labels: 1D numpy array
"""
new_labels = np.zeros((labels.shape[0], self.n_classes))
new_labels[range(labels.shape[0]), labels] = np.ones(labels.shape)
return new_labels
@staticmethod
def labels_from_one_hot(labels):
"""Convert 2D array of labels to 1D class based representation
Args:
labels: 2D numpy array
"""
return np.argmax(labels, axis=1)
================================================
FILE: Examples/cai_eas/eas/data_providers/cifar.py
================================================
import tempfile
import os
import pickle
import random
import numpy as np
from data_providers.base_provider import ImagesDataSet, DataProvider
from data_providers.downloader import download_data_url
def augment_image(image, pad):
"""Perform zero padding, randomly crop image to original size,
maybe mirror horizontally"""
init_shape = image.shape
new_shape = [init_shape[0] + pad * 2,
init_shape[1] + pad * 2,
init_shape[2]]
zeros_padded = np.zeros(new_shape)
zeros_padded[pad:init_shape[0] + pad, pad:init_shape[1] + pad, :] = image
# randomly crop to original size
init_x = np.random.randint(0, pad * 2)
init_y = np.random.randint(0, pad * 2)
cropped = zeros_padded[
init_x: init_x + init_shape[0],
init_y: init_y + init_shape[1],
:]
flip = random.getrandbits(1)
if flip:
cropped = cropped[:, ::-1, :]
return cropped
def augment_all_images(initial_images, pad=4):
new_images = np.zeros(initial_images.shape)
for i in range(initial_images.shape[0]):
new_images[i] = augment_image(initial_images[i], pad)
return new_images
class CifarDataSet(ImagesDataSet):
def __init__(self, images, labels, n_classes, shuffle, normalization, augmentation, meanstd):
"""
Args:
images: 4D numpy array
labels: 2D or 1D numpy array
n_classes: `int`, number of cifar classes - 10 or 100
shuffle: `str` or None
None: no any shuffling
once_prior_train: shuffle train data only once prior train
every_epoch: shuffle train data prior every epoch
normalization: `str` or None
None: no any normalization
divide_255: divide all pixels by 255
divide_256: divide all pixels by 256
by_channels: substract mean of every chanel and divide each
chanel data by it's standard deviation
augmentation: `bool`
"""
if shuffle is None:
self.shuffle_every_epoch = False
elif shuffle == 'once_prior_train':
self.shuffle_every_epoch = False
images, labels = self.shuffle_images_and_labels(images, labels)
elif shuffle == 'every_epoch':
self.shuffle_every_epoch = True
else:
raise Exception('Unknown type of shuffling')
self._batch_counter, self.epoch_images, self.epoch_labels = 0, None, None
self.images = images
self.labels = labels
self.n_classes = n_classes
self.augmentation = augmentation
self.normalization = normalization
self.meanstd = meanstd
self.images = self.normalize_images(images, self.normalization, self.meanstd)
self.start_new_epoch()
def start_new_epoch(self):
self._batch_counter = 0
if self.shuffle_every_epoch:
images, labels = self.shuffle_images_and_labels(
self.images, self.labels)
else:
images, labels = self.images, self.labels
if self.augmentation:
images = augment_all_images(images, pad=4)
self.epoch_images = images
self.epoch_labels = labels
@property
def num_examples(self):
return self.labels.shape[0]
def next_batch(self, batch_size):
start = self._batch_counter * batch_size
end = (self._batch_counter + 1) * batch_size
self._batch_counter += 1
images_slice = self.epoch_images[start: end]
labels_slice = self.epoch_labels[start: end]
if images_slice.shape[0] != batch_size:
self.start_new_epoch()
return self.next_batch(batch_size)
else:
return images_slice, labels_slice
class CifarDataProvider(DataProvider):
"""Abstract class for cifar readers"""
def __init__(self, save_path=None, validation_size=None, shuffle=None, normalization=None,
one_hot=True, **kwargs):
"""
Args:
save_path: `str`
validation_set: `bool`.
validation_split: `float` or None
float: chunk of `train set` will be marked as `validation set`.
None: if 'validation set' == True, `validation set` will be
copy of `test set`
shuffle: `str` or None
None: no any shuffling
once_prior_train: shuffle train data only once prior train
every_epoch: shuffle train data prior every epoch
normalization: `str` or None
None: no any normalization
divide_255: divide all pixels by 255
divide_256: divide all pixels by 256
by_channels: substract mean of every chanel and divide each
chanel data by it's standard deviation
one_hot: `bool`, return laels one hot encoded
"""
self._save_path = save_path
self.one_hot = one_hot
download_data_url(self.data_url, self.save_path)
train_fnames, test_fnames = self.get_filenames(self.save_path)
# add train and validations datasets
images, labels = self.read_cifar(train_fnames)
train_meanstd = ImagesDataSet.measure_mean_and_std(images)
if validation_size is not None:
np.random.seed(DataProvider._SEED)
rand_indexes = np.random.permutation(images.shape[0])
valid_indexes = rand_indexes[:validation_size]
train_indexes = rand_indexes[validation_size:]
self.train = CifarDataSet(
images=images[train_indexes], labels=labels[train_indexes],
n_classes=self.n_classes, shuffle=shuffle,
normalization=normalization,
augmentation=self.data_augmentation, meanstd=train_meanstd)
self.validation = CifarDataSet(
images=images[valid_indexes], labels=labels[valid_indexes],
n_classes=self.n_classes, shuffle=None,
normalization=normalization,
augmentation=False, meanstd=train_meanstd)
else:
self.train = CifarDataSet(
images=images, labels=labels,
n_classes=self.n_classes, shuffle=shuffle,
normalization=normalization,
augmentation=self.data_augmentation, meanstd=train_meanstd)
# add test set
images, labels = self.read_cifar(test_fnames)
self.test = CifarDataSet(
images=images, labels=labels,
shuffle=None, n_classes=self.n_classes,
normalization=normalization,
augmentation=False, meanstd=train_meanstd)
if validation_size is None:
self.validation = self.test
@property
def save_path(self):
if self._save_path is None:
self._save_path = os.path.join(
tempfile.gettempdir(), 'cifar%d' % self.n_classes)
return self._save_path
@property
def data_url(self):
"""Return url for downloaded data depends on cifar class"""
data_url = ('http://www.cs.toronto.edu/'
'~kriz/cifar-%d-python.tar.gz' % self.n_classes)
return data_url
@property
def data_shape(self):
return 32, 32, 3
@property
def n_classes(self):
return self._n_classes
def get_filenames(self, save_path):
"""Return two lists of train and test filenames for dataset"""
raise NotImplementedError
def read_cifar(self, filenames):
if self.n_classes == 10:
labels_key = b'labels'
elif self.n_classes == 100:
labels_key = b'fine_labels'
images_res = []
labels_res = []
for fname in filenames:
with open(fname, 'rb') as f:
images_and_labels = pickle.load(f, encoding='bytes')
images = images_and_labels[b'data']
images = images.reshape(-1, 3, 32, 32)
images = images.swapaxes(1, 3).swapaxes(1, 2)
images_res.append(images)
labels_res.append(images_and_labels[labels_key])
images_res = np.vstack(images_res)
labels_res = np.hstack(labels_res)
if self.one_hot:
labels_res = self.labels_to_one_hot(labels_res)
return images_res, labels_res
class Cifar10DataProvider(CifarDataProvider):
_n_classes = 10
data_augmentation = False
def get_filenames(self, save_path):
sub_save_path = os.path.join(save_path, 'cifar-10-batches-py')
train_filenames = [
os.path.join(
sub_save_path,
'data_batch_%d' % i) for i in range(1, 6)]
test_filenames = [os.path.join(sub_save_path, 'test_batch')]
return train_filenames, test_filenames
class Cifar100DataProvider(CifarDataProvider):
_n_classes = 100
data_augmentation = False
def get_filenames(self, save_path):
sub_save_path = os.path.join(save_path, 'cifar-100-python')
train_filenames = [os.path.join(sub_save_path, 'train')]
test_filenames = [os.path.join(sub_save_path, 'test')]
return train_filenames, test_filenames
class Cifar10AugmentedDataProvider(Cifar10DataProvider):
_n_classes = 10
data_augmentation = True
class Cifar100AugmentedDataProvider(Cifar100DataProvider):
_n_classes = 100
data_augmentation = True
================================================
FILE: Examples/cai_eas/eas/data_providers/downloader.py
================================================
import sys
import os
import urllib.request
import tarfile
import zipfile
def report_download_progress(count, block_size, total_size):
pct_complete = float(count * block_size) / total_size
msg = '\r {0:.1%} already downloaded'.format(pct_complete)
sys.stdout.write(msg)
sys.stdout.flush()
def download_data_url(url, download_dir):
filename = url.split('/')[-1]
file_path = os.path.join(download_dir, filename)
if not os.path.exists(file_path):
os.makedirs(download_dir, exist_ok=True)
print('Download %s to %s' % (url, file_path))
file_path, _ = urllib.request.urlretrieve(
url=url,
filename=file_path,
reporthook=report_download_progress)
print('\nExtracting files')
if file_path.endswith('.zip'):
zipfile.ZipFile(file=file_path, mode='r').extractall(download_dir)
elif file_path.endswith(('.tar.gz', '.tgz')):
tarfile.open(name=file_path, mode='r:gz').extractall(download_dir)
================================================
FILE: Examples/cai_eas/eas/data_providers/svhn.py
================================================
import tempfile
import os
import scipy.io
import numpy as np
from data_providers.base_provider import ImagesDataSet, DataProvider
from data_providers.downloader import download_data_url
class SVHNDataSet(ImagesDataSet):
n_classes = 10
def __init__(self, images, labels, shuffle, normalization):
"""
Args:
images: 4D numpy array
labels: 2D or 1D numpy array
shuffle: `bool`, should shuffle data or not
normalization: `str` or None
None: no any normalization
divide_255: divide all pixels by 255
divide_256: divide all pixels by 256
by_channels: substract mean of every chanel and divide each
chanel data by it's standard deviation
"""
self._batch_counter, self.epoch_images, self.epoch_labels = 0, None, None
self.shuffle = shuffle
self.images = images
self.labels = labels
self.normalization = normalization
self.start_new_epoch()
def start_new_epoch(self):
self._batch_counter = 0
if self.shuffle:
self.epoch_images, self.epoch_labels = self.shuffle_images_and_labels(
self.images, self.labels)
else:
self.epoch_images, self.epoch_labels = self.images, self.labels
@property
def num_examples(self):
return self.labels.shape[0]
def next_batch(self, batch_size):
start = self._batch_counter * batch_size
end = (self._batch_counter + 1) * batch_size
self._batch_counter += 1
images_slice = self.epoch_images[start: end]
labels_slice = self.epoch_labels[start: end]
# due to memory error it should be done inside batch
if self.normalization is not None:
images_slice = self.normalize_images(
images_slice, self.normalization)
if images_slice.shape[0] != batch_size:
self.start_new_epoch()
return self.next_batch(batch_size)
else:
return images_slice, labels_slice
class SVHNDataProvider(DataProvider):
def __init__(self, save_path=None, validation_size=None, shuffle=False, normalization=None,
one_hot=True, include_extra=True, **kwargs):
"""
Args:
save_path: `str`
validation_set: `bool`.
validation_split: `int` or None
float: chunk of `train set` will be marked as `validation set`.
None: if 'validation set' == True, `validation set` will be
copy of `test set`
shuffle: `bool`, should shuffle data or not
normalization: `str` or None
None: no any normalization
divide_255: divide all pixels by 255
divide_256: divide all pixels by 256
by_chanels: substract mean of every chanel and divide each
chanel data by it's standart deviation
one_hot: `bool`, return lasels one hot encoded
"""
self._save_path = save_path
train_images = []
train_labels = []
if include_extra:
train_data_src = ['train', 'extra']
else:
train_data_src = ['train']
for part in train_data_src:
images, labels = self.get_images_and_labels(part, one_hot)
train_images.append(images)
train_labels.append(labels)
train_images = np.vstack(train_images)
if one_hot:
train_labels = np.vstack(train_labels)
else:
train_labels = np.hstack(train_labels)
if validation_size is not None:
np.random.seed(DataProvider._SEED)
rand_indexes = np.random.permutation(train_images.shape[0])
valid_indexes = rand_indexes[:validation_size]
train_indexes = rand_indexes[validation_size:]
valid_images, valid_labels = train_images[valid_indexes], train_labels[valid_indexes]
train_images, train_labels = train_images[train_indexes], train_labels[train_indexes]
self.validation = SVHNDataSet(
valid_images, valid_labels, False, normalization)
self.train = SVHNDataSet(
train_images, train_labels, shuffle, normalization)
test_images, test_labels = self.get_images_and_labels('test', one_hot)
self.test = SVHNDataSet(test_images, test_labels, False, normalization)
if validation_size is None:
self.validation = self.test
def get_images_and_labels(self, name_part, one_hot=False):
url = self.data_url + name_part + '_32x32.mat'
download_data_url(url, self.save_path)
filename = os.path.join(self.save_path, name_part + '_32x32.mat')
data = scipy.io.loadmat(filename)
images = data['X'].transpose(3, 0, 1, 2)
labels = data['y'].reshape((-1))
labels[labels == 10] = 0
if one_hot:
labels = self.labels_to_one_hot(labels)
return images, labels
@property
def n_classes(self):
return 10
@property
def save_path(self):
if self._save_path is None:
self._save_path = os.path.join(tempfile.gettempdir(), 'svhn')
return self._save_path
@property
def data_url(self):
return 'http://ufldl.stanford.edu/housenumbers/'
@property
def data_shape(self):
return 32, 32, 3
================================================
FILE: Examples/cai_eas/eas/data_providers/utils.py
================================================
from data_providers.cifar import Cifar10DataProvider, Cifar100DataProvider, \
Cifar10AugmentedDataProvider, Cifar100AugmentedDataProvider
from data_providers.svhn import SVHNDataProvider
def get_data_provider_by_name(name, train_params):
"""Return required data provider class"""
if name == 'C10':
return Cifar10DataProvider(**train_params)
if name == 'C10+':
return Cifar10AugmentedDataProvider(**train_params)
if name == 'C100':
return Cifar100DataProvider(**train_params)
if name == 'C100+':
return Cifar100AugmentedDataProvider(**train_params)
if name == 'SVHN':
return SVHNDataProvider(**train_params)
else:
print('Sorry, data provider for `%s` dataset '
'was not implemented yet' % name)
exit()
================================================
FILE: Examples/cai_eas/eas/expdir_monitor/__init__.py
================================================
================================================
FILE: Examples/cai_eas/eas/expdir_monitor/expdir_monitor.py
================================================
import json
import os
import subprocess
from models.utils import RunConfig, get_model_config_by_name, get_model_by_name
from data_providers.utils import get_data_provider_by_name
import pickle
class ExpdirMonitor:
def __init__(self, expdir):
self.expdir = os.path.realpath(expdir)
os.makedirs(self.expdir, exist_ok=True)
@property
def logs(self): return '%s/logs' % self.expdir
@property
def checkpoint(self): return '%s/checkpoint' % self.expdir
@property
def snapshot(self): return '%s/snapshot' % self.expdir
@property
def output(self): return '%s/output' % self.expdir
@property
def init(self): return '%s/init' % self.expdir
@property
def run_config_path(self): return '%s/run.config' % self.expdir
@property
def net_config_path(self): return '%s/net.config' % self.expdir
def load_run_config(self, print_info=False, dataset='C10+'):
if os.path.isfile(self.run_config_path):
run_config = json.load(open(self.run_config_path, 'r'))
else:
print('Use Default Run Config for %s' % dataset)
run_config = RunConfig.get_default_run_config(dataset)
if print_info:
print('Run config:')
for k, v in run_config.items():
print('\t%s: %s' % (k, v))
return RunConfig(**run_config)
def load_init(self):
init_path = '%s/init' % self.expdir
if os.path.isfile(init_path):
return pickle.load(open(self.init, 'rb'))
else:
return None
def load_net_config(self, init, print_info=False):
assert os.path.isfile(self.net_config_path), \
'Net configs do not exist in the given expdir <%s>' % self.expdir
net_config_json = json.load(open(self.net_config_path, 'r'))
net_config = get_model_config_by_name(net_config_json['name'])()
net_config.set_net_from_config(net_config_json, init=init, print_info=print_info)
return net_config, net_config_json['name']
def run(self, pure=True, restore=False, test=False, valid=False, valid_size=-1):
if not restore:
_clear_files = ['logs', 'checkpoint', 'snapshot', 'output']
for file in _clear_files:
subprocess.run(['rm', '-rf', os.path.join(self.expdir, file)])
init = self.load_init()
dataset = 'C10+' if init is None else init.get('dataset', 'C10+')
run_config = self.load_run_config(print_info=(not pure), dataset=dataset)
run_config.renew_logs = False
if valid_size > 0:
run_config.validation_size = valid_size
data_provider = get_data_provider_by_name(run_config.dataset, run_config.get_config())
net_config, model_name = self.load_net_config(init, print_info=(not pure))
model = get_model_by_name(model_name)(self.expdir, data_provider, run_config, net_config, pure=pure)
start_epoch = 1
if restore:
model.load_model()
epoch_info_file = '%s/checkpoint/epoch.info' % self.expdir
if os.path.isfile(epoch_info_file):
start_epoch = json.load(open(epoch_info_file, 'r'))['epoch']
if not pure:
print('start epoch: %d' % start_epoch)
if test:
print('Testing...')
loss, accuracy = model.test(data_provider.test, batch_size=200)
print('mean cross_entropy: %f, mean accuracy: %f' % (loss, accuracy))
json.dump({'test_loss': '%s' % loss, 'test_acc': '%s' % accuracy}, open(self.output, 'w'))
elif valid:
print('validating...')
loss, accuracy = model.test(data_provider.validation, batch_size=200)
print('mean cross_entropy: %f, mean accuracy: %f' % (loss, accuracy))
json.dump({'valid_loss': '%s' % loss, 'valid_acc': '%s' % accuracy}, open(self.output, 'w'))
elif pure:
model.pure_train()
loss, accuracy = model.test(data_provider.validation, batch_size=200)
json.dump({'valid_loss': '%s' % loss, 'valid_acc': '%s' % accuracy}, open(self.output, 'w'))
model.save_init(self.snapshot, print_info=(not pure))
model.save_config(self.expdir, print_info=(not pure))
else:
# train the model
print('Data provider train images: ', data_provider.train.num_examples)
model.train_all_epochs(start_epoch)
print('Data provider test images: ', data_provider.test.num_examples)
print('Testing...')
loss, accuracy = model.test(data_provider.test, batch_size=200)
print('mean cross_entropy: %f, mean accuracy: %f' % (loss, accuracy))
json.dump({'test_loss': '%s' % loss, 'test_acc': '%s' % accuracy}, open(self.output, 'w'))
model.save_init(self.snapshot, print_info=(not pure))
model.save_config(self.expdir, print_info=(not pure))
return accuracy
================================================
FILE: Examples/cai_eas/eas/experiment.json
================================================
{
"name": "./cai_eas/eas/experiment.json",
"proposer": "eas",
"script": "client.py",
"resource": "gpu",
"n_parallel": 2,
"parameter_config": [
],
"target":"max"
}
================================================
FILE: Examples/cai_eas/eas/models/__init__.py
================================================
================================================
FILE: Examples/cai_eas/eas/models/basic_model.py
================================================
import os
import shutil
import tensorflow as tf
import numpy as np
import time
from datetime import timedelta
import json
import pickle
class BasicModel:
def __init__(self, path, data_provider, run_config, net_config, pure=False, only_forward=False):
if only_forward: pure = True
self.graph = tf.Graph()
self.data_provider = data_provider
self._path = path
self.run_config = run_config
self.net_config = net_config
self.data_shape = data_provider.data_shape
self.n_classes = data_provider.n_classes
self._save_path, self._logs_path = None, None
self.batches_step = 0
self.cross_entropy, self.train_step, self.accuracy = None, None, None
with self.graph.as_default():
self._define_inputs()
self._build_graph(only_forward=only_forward)
self.global_variables_initializer = tf.global_variables_initializer()
if not pure:
self._count_trainable_params()
self.saver = tf.train.Saver()
self._initialize_session(set_logs=(not pure))
@property
def save_path(self):
if self._save_path is None:
save_path = '%s/checkpoint' % self._path
os.makedirs(save_path, exist_ok=True)
save_path = os.path.join(save_path, 'model.ckpt')
self._save_path = save_path
return self._save_path
@property
def logs_path(self):
if self._logs_path is None:
logs_path = '%s/logs' % self._path
if self.run_config.renew_logs:
shutil.rmtree(logs_path, ignore_errors=True)
os.makedirs(logs_path, exist_ok=True)
self._logs_path = logs_path
return self._logs_path
def _build_graph(self, only_forward=False):
raise NotImplementedError
def _define_inputs(self):
shape = [None]
shape.extend(self.data_shape)
self.images = tf.placeholder(
tf.float32,
shape=shape,
name='input_images')
self.labels = tf.placeholder(
tf.float32,
shape=[None, self.n_classes],
name='labels')
self.learning_rate = tf.placeholder(
tf.float32,
shape=[],
name='learning_rate')
self.is_training = tf.placeholder(tf.bool, shape=[], name='is_training')
def _initialize_session(self, set_logs=True):
"""Initialize session, variables"""
config = tf.ConfigProto()
# restrict model GPU memory utilization to min required
config.gpu_options.allow_growth = True
self.sess = tf.Session(graph=self.graph, config=config)
self.sess.run(self.global_variables_initializer)
if set_logs:
logswriter = tf.summary.FileWriter
self.summary_writer = logswriter(self.logs_path, graph=self.graph)
def train_all_epochs(self, start_epoch=1):
n_epochs = self.run_config.n_epochs
learning_rate = self.run_config.init_lr
batch_size = self.run_config.batch_size
total_start_time = time.time()
for epoch in range(start_epoch, n_epochs + 1):
print('\n', '-' * 30, 'Train epoch: %d' % epoch, '-' * 30, '\n')
start_time = time.time()
new_lr = self.run_config.learning_rate(epoch)
if new_lr != learning_rate:
learning_rate = new_lr
print('Decrease learning rate, new lr = %f' % learning_rate)
print('Training...')
loss, acc = self.train_one_epoch(
self.data_provider.train, batch_size, learning_rate)
# save logs about "loss" and "acc" if the option is true
if self.run_config.should_save_logs:
self.log_loss_accuracy(loss, acc, epoch, prefix='train')
if self.run_config.validation_frequency and epoch % self.run_config.validation_frequency == 0:
print('Validation...')
loss, acc = self.test(self.data_provider.validation, batch_size)
if self.run_config.should_save_logs:
self.log_loss_accuracy(loss, acc, epoch, prefix='valid')
if self.run_config.should_save_model:
self.save_model()
json.dump({'epoch': epoch + 1}, open('%s/checkpoint/epoch.info' % self._path, 'w'))
time_per_epoch = time.time() - start_time
seconds_left = int((n_epochs - epoch) * time_per_epoch)
print('Time per epoch: %s, Est. complete in: %s' % (
str(timedelta(seconds=time_per_epoch)),
str(timedelta(seconds=seconds_left))))
if self.run_config.should_save_model:
self.save_model()
total_training_time = time.time() - total_start_time
print('\nTotal training time: %s' % str(timedelta(
seconds=total_training_time)))
def train_one_epoch(self, data, batch_size, learning_rate):
num_examples = data.num_examples
total_loss = []
total_accuracy = []
for i in range(num_examples // batch_size):
batch = data.next_batch(batch_size)
images, labels = batch
feed_dict = {
self.images: images,
self.labels: labels,
self.learning_rate: learning_rate,
self.is_training: True,
}
fetches = [self.train_step, self.cross_entropy, self.accuracy]
result = self.sess.run(fetches, feed_dict=feed_dict)
_, loss, accuracy = result
total_loss.append(loss)
total_accuracy.append(accuracy)
# save logs about "loss" and "acc" if the option is true
if self.run_config.should_save_logs:
self.batches_step += 1
self.log_loss_accuracy(
loss, accuracy, self.batches_step, prefix='per_batch',
should_print=False)
mean_loss = np.mean(total_loss)
mean_accuracy = np.mean(total_accuracy)
return mean_loss, mean_accuracy
def test(self, data, batch_size):
num_examples = data.num_examples
total_loss = []
total_accuracy = []
for i in range(num_examples // batch_size):
batch = data.next_batch(batch_size)
feed_dict = {
self.images: batch[0],
self.labels: batch[1],
self.is_training: False,
}
fetches = [self.cross_entropy, self.accuracy]
loss, accuracy = self.sess.run(fetches, feed_dict=feed_dict)
total_loss.append(loss)
total_accuracy.append(accuracy)
mean_loss = np.mean(total_loss)
mean_accuracy = np.mean(total_accuracy)
remain_num = num_examples % batch_size
if remain_num != 0:
batch = data.next_batch(remain_num)
feed_dict = {
self.images: batch[0],
self.labels: batch[1],
self.is_training: False,
}
fetches = [self.cross_entropy, self.accuracy]
loss, accuracy = self.sess.run(fetches, feed_dict=feed_dict)
mean_loss = (mean_loss * (num_examples - remain_num) + loss * remain_num) / num_examples
mean_accuracy = (mean_accuracy * (num_examples - remain_num) + accuracy * remain_num) / num_examples
return mean_loss, mean_accuracy
def save_config(self, save_path, print_info=True):
os.makedirs(save_path, exist_ok=True)
net_save_path = os.path.join(save_path, 'net.config')
json.dump(self.net_config.get_config(), open(net_save_path, 'w'), indent=4)
if print_info: print('Network configs dump to %s' % save_path)
run_save_path = os.path.join(save_path, 'run.config')
json.dump(self.run_config.get_config(), open(run_save_path, 'w'), indent=4)
if print_info: print('Run configs dump to %s' % run_save_path)
def save_init(self, save_path, print_info=True):
os.makedirs(save_path, exist_ok=True)
save_path = os.path.join(save_path, 'init')
to_save_init = self.net_config.renew_init(self)
to_save_init['dataset'] = self.run_config.dataset
pickle.dump(to_save_init, open(save_path, 'wb'))
if print_info: print('Network weights dump to %s' % save_path)
def pure_train(self):
n_epochs = self.run_config.n_epochs
batch_size = self.run_config.batch_size
for epoch in range(1, n_epochs + 1):
learning_rate = self.run_config.learning_rate(epoch)
# train one epoch
data = self.data_provider.train
num_examples = data.num_examples
for i in range(num_examples // batch_size):
batch = data.next_batch(batch_size)
images, labels = batch
feed_dict = {
self.images: images,
self.labels: labels,
self.learning_rate: learning_rate,
self.is_training: True,
}
fetches = self.train_step
self.sess.run(fetches, feed_dict=feed_dict)
def save_model(self, global_step=None):
self.saver.save(self.sess, self.save_path, global_step=global_step)
def load_model(self):
try:
self.saver.restore(self.sess, self.save_path)
except Exception:
raise IOError('Failed to to load model '
'from save path: %s' % self.save_path)
print('Successfully load model from save path: %s' % self.save_path)
def log_loss_accuracy(self, loss, accuracy, epoch, prefix, should_print=True, write2file=True):
if should_print:
print('mean cross_entropy: %f, mean accuracy: %f' % (loss, accuracy))
summary = tf.Summary(value=[
tf.Summary.Value(
tag='loss_%s' % prefix, simple_value=float(loss)),
tf.Summary.Value(
tag='accuracy_%s' % prefix, simple_value=float(accuracy))
])
self.summary_writer.add_summary(summary, epoch)
if write2file and prefix == 'valid':
with open('%s/console.txt' % self.logs_path, 'a') as fout:
fout.write('%d: mean cross_entropy: %f, mean accuracy: %f\n' % (epoch, loss, accuracy))
@staticmethod
def _count_trainable_params():
total_parameters = 0
for variable in tf.trainable_variables():
shape = variable.get_shape()
variable_parameters = 1
for dim in shape:
variable_parameters *= dim.value
total_parameters += variable_parameters
print('Total training params: %.2fM' % (total_parameters / 1e6))
@staticmethod
def dropout(_input, keep_prob, is_training):
if keep_prob < 1:
output = tf.cond(
is_training,
lambda: tf.nn.dropout(_input, keep_prob),
lambda: _input
)
else:
output = _input
return output
@staticmethod
def weight_variable(shape, name, initializer):
return tf.get_variable(
name,
shape=shape,
initializer=initializer,
)
@staticmethod
def avg_pool(_input, k=2, s=2):
ksize = [1, k, k, 1]
strides = [1, s, s, 1]
padding = 'VALID'
# if stride = 1, keep the image size unchanged
if s == 1: padding = 'SAME'
output = tf.nn.avg_pool(_input, ksize, strides, padding)
return output
@staticmethod
def max_pool(_input, k=2, s=2):
ksize = [1, k, k, 1]
strides = [1, s, s, 1]
padding = 'VALID'
# if stride = 1, keep the image size unchanged
if s == 1: padding = 'SAME'
output = tf.nn.max_pool(_input, ksize, strides, padding)
return output
@staticmethod
def conv2d(_input, out_features, kernel_size, strides=1, padding='SAME', param_initializer=None):
if kernel_size == 1: padding = 'VALID'
in_features = int(_input.get_shape()[-1])
if not param_initializer: param_initializer = {}
kernel = BasicModel.weight_variable(
[kernel_size, kernel_size, in_features, out_features],
name='kernel',
initializer=param_initializer.get('kernel', tf.contrib.layers.variance_scaling_initializer())
)
output = tf.nn.conv2d(_input, kernel, [1, strides, strides, 1], padding)
return output
@staticmethod
def fc_layer(_input, out_units, use_bias=False, param_initializer=None):
features_total = int(_input.get_shape()[-1])
if not param_initializer: param_initializer = {}
W = BasicModel.weight_variable(
[features_total, out_units], name='W',
initializer=param_initializer.get('W', tf.contrib.layers.xavier_initializer())
)
output = tf.matmul(_input, W)
if use_bias:
bias = BasicModel.weight_variable(
[out_units], name='bias',
initializer=param_initializer.get('bias', tf.constant_initializer([0.0] * out_units))
)
output += bias
return output
@staticmethod
def batch_norm(_input, is_training, epsilon=1e-3, decay=0.999, param_initializer=None):
output = tf.contrib.layers.batch_norm(
_input, scale=True, is_training=is_training, param_initializers=param_initializer,
updates_collections=None, epsilon=epsilon, decay=decay)
return output
@staticmethod
def activation(_input, activation='relu'):
if activation == 'relu':
return tf.nn.relu(_input)
elif activation == 'tanh':
return tf.tanh(_input)
elif activation == 'sigmoid':
return tf.sigmoid(_input)
elif activation == 'softmax':
return tf.nn.softmax(_input)
elif activation is None:
return _input
else:
raise ValueError('Do not support %s' % activation)
@staticmethod
def build_optimizer(learning_rate, opt_name, opt_param):
if opt_name == 'momentum':
return tf.train.MomentumOptimizer(learning_rate, **opt_param)
elif opt_name == 'adam':
return tf.train.AdamOptimizer(learning_rate, **opt_param)
else:
raise ValueError('Do not support the optimizer type: %s' % opt_name)
@staticmethod
def flatten(_input):
input_shape = _input.shape.as_list()
if len(input_shape) != 2:
return tf.reshape(_input, [-1, np.prod(input_shape[1:])])
else:
return _input
================================================
FILE: Examples/cai_eas/eas/models/convnet.py
================================================
from models.basic_model import BasicModel
from data_providers.base_provider import DataProvider
from models.layers import ConvLayer, PoolLayer, FCLayer
from models.layer_cascade import LayerCascade
import tensorflow as tf
import numpy as np
class SimpleConvnetConfig:
def __init__(self):
self.net_config = {
'weight_decay': None,
'bn_epsilon': None,
'bn_decay': None,
'drop_scheme': None,
}
self.layer_cascade = None
@property
def weight_decay(self): return self.net_config['weight_decay']
@property
def bn_epsilon(self): return self.net_config['bn_epsilon']
@property
def bn_decay(self): return self.net_config['bn_decay']
@property
def drop_scheme(self): return self.net_config['drop_scheme']
@property
def depth(self): return self.layer_cascade.depth
def get_config(self):
return {
'name': 'SimpleConvnet',
**self.net_config,
'layer_cascade': self.layer_cascade.get_config()
}
def copy(self):
net_config = SimpleConvnetConfig()
net_config.set_net_from_config(self.get_config(), self.renew_init(None), print_info=False)
return net_config
def renew_init(self, convnet):
return {
'layer_cascade': self.layer_cascade.renew_init(convnet)
}
def set_standard_convnet(self, data_provider: DataProvider, conv_blocks_config, fc_block_config, weight_decay,
drop_scheme, bn_epsilon, bn_decay, print_info=True, **kwargs):
self.net_config = {
'weight_decay': weight_decay,
'bn_epsilon': bn_epsilon,
'bn_decay': bn_decay,
'drop_scheme': drop_scheme,
}
image_size = data_provider.data_shape[0]
layers = []
conv_id = 0
for _i, block_config in enumerate(conv_blocks_config):
num_layers, kernel_size, filter_num = block_config
for _j in range(num_layers):
keep_prob = 1.0
if 'conv' in drop_scheme['type']:
keep_prob = 1.0 if _i + _j == 0 else drop_scheme.get('conv_drop', 1.0)
conv_layer = ConvLayer('conv_%d' % conv_id, filter_num, kernel_size=kernel_size, keep_prob=keep_prob,
pre_activation=False)
conv_id += 1
layers.append(conv_layer)
if _i < len(conv_blocks_config) - 1:
keep_prob = 1.0
if 'pool' in drop_scheme['type']:
keep_prob = drop_scheme.get('pool_drop', 1.0)
pool_layer = PoolLayer('pool_%d' % _i, 'max', keep_prob=keep_prob, pre_activation=False)
layers.append(pool_layer)
image_size = image_size // 2
global_avg_pool = PoolLayer('pool_%d' % len(conv_blocks_config), 'avg',
kernel_size=image_size, strides=image_size, pre_activation=False)
layers.append(global_avg_pool)
for _i, units in enumerate(fc_block_config):
keep_prob = 1.0
if 'fc' in drop_scheme['type']:
keep_prob = drop_scheme.get('fc_drop', 1.0)
fc_layer = FCLayer('fc_%d' % _i, units, keep_prob=keep_prob)
layers.append(fc_layer)
final_fc_layer = FCLayer('fc_%d' % len(fc_block_config), data_provider.n_classes, use_bn=False, use_bias=True,
activation=None)
layers.append(final_fc_layer)
self.layer_cascade = LayerCascade('SimpleConvNet', layers)
if print_info:
pass
return self
def set_net_from_config(self, net_config_json, init=None, print_info=True):
for key in self.net_config.keys():
self.net_config[key] = net_config_json[key]
init = init['layer_cascade'] if init is not None else None
self.layer_cascade = LayerCascade.set_from_config(net_config_json['layer_cascade'], init)
if print_info:
pass
return self
def widen(self, layer_idx, new_width, widen_type='output_dim', noise=None):
change_out_dim, _, _ = self.layer_cascade.widen(layer_idx, new_width, widen_type, noise)
if change_out_dim:
raise ValueError('Can not change the final logits number')
def deepen(self, layer_idx, new_layer_config):
return self.layer_cascade.deepen(layer_idx, new_layer_config, None)
def set_identity4deepen(self, to_set_layers, data_provider, batch_size, batch_num=1, strict=True, noise=None):
"""
to_set_layers = [(new_layer, prev_layer), ...]
"""
task_list = {}
for new_layer, prev_layer in to_set_layers:
if new_layer.ready: continue
if new_layer.use_bn and strict:
task_id = id(prev_layer)
if task_id in task_list:
task_list[task_id][1].append(new_layer)
else:
task_list[task_id] = (prev_layer, [new_layer])
else:
new_layer.set_identity_layer(strict=strict, noise=noise)
if len(task_list) > 0:
model = SimpleConvnet(None, data_provider, None, net_config=self, only_forward=True)
task_list = list(task_list.values())
fetches = [prev_layer.output_op for prev_layer, _ in task_list]
statistics = [[0, 0] for _ in task_list]
for _i in range(batch_num):
input_images, _ = data_provider.train.next_batch(batch_size)
outputs = model.sess.run(fetches, feed_dict={model.images: input_images, model.is_training: False})
for _j, out in enumerate(outputs):
out = out.astype('float32')
axis = tuple(range(len(out.shape) - 1))
mean = np.mean(out, axis=axis, keepdims=True)
variance = np.mean(np.square(out - mean), axis=axis, keepdims=True)
mean, variance = np.squeeze(mean), np.squeeze(variance)
statistics[_j][0] += mean
statistics[_j][1] += variance
for _j, (prev_layer, new_layers) in enumerate(task_list):
mean, variance = statistics[_j][0] / batch_num, statistics[_j][1] / batch_num
for new_layer in new_layers:
if new_layer.ready: continue
param = {
'moving_mean': mean,
'moving_variance': variance,
'epsilon': self.bn_epsilon,
}
new_layer.set_identity_layer(strict=strict, param=param, noise=noise)
class SimpleConvnet(BasicModel):
def _build_graph(self, only_forward=False):
_input = self.images
output = _input
output = self.net_config.layer_cascade.build(output, self, store_output_op=only_forward)
if not only_forward:
logits = output
with tf.variable_scope('L2_Loss'):
l2_loss = tf.add_n([tf.nn.l2_loss(var) for var in tf.trainable_variables()])
prediction = tf.nn.softmax(logits)
# losses
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(
logits=logits, labels=self.labels))
self.cross_entropy = cross_entropy
# optimizer and train step
optimizer = self.build_optimizer(self.learning_rate,
self.run_config.opt_config[0], self.run_config.opt_config[1])
self.train_step = optimizer.minimize(
cross_entropy + l2_loss * self.net_config.weight_decay)
correct_prediction = tf.equal(
tf.argmax(prediction, 1),
tf.argmax(self.labels, 1))
self.accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
================================================
FILE: Examples/cai_eas/eas/models/dense_net.py
================================================
import tensorflow as tf
from models.basic_model import BasicModel
from models.layers import ConvLayer, FCLayer, PoolLayer, get_magnifier, apply_noise
from data_providers.base_provider import DataProvider
from models.layer_cascade import LayerCascade
from models.layer_multi_branch import LayerMultiBranch
import numpy as np
def get_block_by_name(name):
if name == 'transition':
return TransitionBlock
elif name == 'dense_block':
return DenseBlock
else:
raise ValueError('Unsupported block type: %s' % name)
class TransitionBlock(LayerCascade):
def get_config(self):
return {
'name': 'transition',
**super(TransitionBlock, self).get_config(),
}
@staticmethod
def set_from_config(config_json, init=None, return_class=True):
_id, layers = LayerCascade.set_from_config(config_json, init, return_class=False)
return TransitionBlock(_id, layers)
def prev_widen(self, indices, magnifier, noise=None):
super(TransitionBlock, self).prev_widen(indices, magnifier, noise=noise)
return False, None, None
def widen(self, loc, new_width, widen_type='output_dim', noise=None, input_dim=None):
return super(TransitionBlock, self).widen(loc['layer'], new_width, widen_type, noise=noise)
def deepen(self, loc, new_layer_config, input_dim):
return super(TransitionBlock, self).deepen(loc['layer'], new_layer_config, input_dim)
class DenseBlock:
def __init__(self, _id, miniblocks):
self._id = _id
self.miniblocks = miniblocks
self.output_op = None
@property
def id(self):
return self._id
@id.setter
def id(self, value):
self._id = value
@property
def depth(self):
depth = 0
for miniblock in self.miniblocks:
depth += miniblock.depth
return depth
def out_features_dim(self, in_features_dim):
out_features_dim = in_features_dim
for miniblock in self.miniblocks:
out_features_dim += miniblock.out_features_dim
return out_features_dim
def build(self, _input, densenet, store_output_op=False):
output = _input
with tf.variable_scope(self._id):
for miniblock in self.miniblocks:
comp_out = miniblock.build(output, densenet, store_output_op=store_output_op)
output = tf.concat(axis=3, values=(output, comp_out))
if store_output_op:
self.output_op = output
return output
def get_config(self):
return {
'name': 'dense_block',
'_id': self._id,
'miniblocks': [miniblock.get_config() for miniblock in self.miniblocks]
}
def renew_init(self, densenet):
return {
'_id': self._id,
'miniblocks': [miniblock.renew_init(densenet) for miniblock in self.miniblocks]
}
@staticmethod
def set_from_config(config_json, init=None):
_id = config_json['_id']
miniblocks = []
for _i, miniblock_config in enumerate(config_json['miniblocks']):
miniblock_init = init['miniblocks'][_i] if init is not None else None
miniblock = LayerMultiBranch.set_from_config(miniblock_config, miniblock_init)
miniblocks.append(miniblock)
return DenseBlock(_id, miniblocks)
"""
Network Transformation Operations
"""
def insert_miniblock(self, idx, miniblock_config, input_dim, noise=None, scheme=0):
assert 0 <= idx < len(self.miniblocks), 'Invalid miniblock index %d' % idx
if miniblock_config['bc_mode']:
# DenseNet-BC
if scheme == 0:
copy_idx = idx
copy_miniblock = self.miniblocks[copy_idx]
new_in_bottle = copy_miniblock.in_bottle.copy()
new_in_layer = new_in_bottle.layers[0]
pad_kernel_shape = list(new_in_layer.init['kernel'].shape)
pad_kernel_shape[2] = copy_miniblock.out_features_dim
new_in_layer.init['kernel'] = \
np.concatenate([new_in_layer.init['kernel'], np.zeros(pad_kernel_shape)], axis=2)
if new_in_layer.pre_activation and new_in_layer.use_bn:
new_in_layer.init['beta'] = \
np.concatenate([new_in_layer.init['beta'], np.zeros([copy_miniblock.out_features_dim])])
new_in_layer.init['gamma'] = \
np.concatenate([new_in_layer.init['gamma'], np.ones([copy_miniblock.out_features_dim])])
new_in_layer.init['moving_mean'] = \
np.concatenate([new_in_layer.init['moving_mean'], np.zeros([copy_miniblock.out_features_dim])])
new_in_layer.init['moving_variance'] = \
np.concatenate([new_in_layer.init['moving_variance'], np.ones([copy_miniblock.out_features_dim])])
new_in_layer.init['kernel'] = apply_noise(new_in_layer.init['kernel'], noise.get('wider'))
if copy_miniblock.out_bottle is None:
new_branches, indices = copy_miniblock.remapped_branches(noise=noise)
new_miniblock = LayerMultiBranch('M_%d' % (idx + 2), new_branches,
merge=copy_miniblock.merge, in_bottle=new_in_bottle)
old_size = len(indices)
indices = np.concatenate([np.arange(old_size), indices])
magnifier = get_magnifier(old_size, indices)
prev_miniblock_out_dim = input_dim
for _i in range(0, idx):
prev_miniblock_out_dim += self.miniblocks[_i].out_features_dim
indices = np.concatenate([
np.arange(prev_miniblock_out_dim),
indices + prev_miniblock_out_dim,
])
magnifier = np.concatenate([
[1] * prev_miniblock_out_dim,
magnifier,
])
prev_miniblock_out_dim += old_size
for _i in range(idx + 1, len(self.miniblocks)):
miniblock_out_dim = self.miniblocks[_i].out_features_dim
self.miniblocks[_i].id = 'M_%d' % (_i + 2)
self.miniblocks[_i].prev_widen(indices, magnifier, noise=noise)
indices = np.concatenate([
indices,
np.arange(prev_miniblock_out_dim, prev_miniblock_out_dim + miniblock_out_dim)
])
magnifier = np.concatenate([
magnifier,
[1] * miniblock_out_dim,
])
prev_miniblock_out_dim += miniblock_out_dim
self.miniblocks = self.miniblocks[:idx + 1] + [new_miniblock] + self.miniblocks[idx + 1:]
return indices, magnifier
else:
raise NotImplementedError
else:
# identity scheme
raise NotImplementedError
else:
# DenseNet without BC
raise NotImplementedError
def prev_widen(self, indices, magnifier, noise=None):
old_size = np.max(indices) + 1
prev_miniblock_out_dim = old_size
for miniblock in self.miniblocks:
miniblock_out_dim = miniblock.out_features_dim
miniblock.prev_widen(indices, magnifier, noise=noise)
indices = np.concatenate([
indices,
np.arange(prev_miniblock_out_dim, prev_miniblock_out_dim + miniblock_out_dim)
])
magnifier = np.concatenate([
magnifier,
[1] * miniblock_out_dim,
])
prev_miniblock_out_dim += miniblock_out_dim
return True, indices, magnifier
def widen(self, loc, new_width, widen_type='output_dim', noise=None, input_dim=3):
miniblock_idx = loc['miniblock']
miniblock = self.miniblocks[miniblock_idx]
old_miniblock_out_dim = miniblock.out_features_dim
change_out_dim, indices, magnifier = miniblock.widen(loc, new_width, widen_type, noise=noise)
if change_out_dim:
prev_miniblock_out_dim = input_dim
for _i in range(0, miniblock_idx):
prev_miniblock_out_dim += self.miniblocks[_i].out_features_dim
indices = np.concatenate([
np.arange(prev_miniblock_out_dim),
indices + prev_miniblock_out_dim,
])
magnifier = np.concatenate([
[1] * prev_miniblock_out_dim,
magnifier,
])
prev_miniblock_out_dim += old_miniblock_out_dim
for _i in range(miniblock_idx + 1, len(self.miniblocks)):
miniblock_out_dim = self.miniblocks[_i].out_features_dim
self.miniblocks[_i].prev_widen(indices, magnifier, noise=noise)
indices = np.concatenate([
indices,
np.arange(prev_miniblock_out_dim, prev_miniblock_out_dim + miniblock_out_dim)
])
magnifier = np.concatenate([
magnifier,
[1] * miniblock_out_dim,
])
prev_miniblock_out_dim += miniblock_out_dim
return True, indices, magnifier
else:
return False, None, None
def deepen(self, loc, new_layer_config, input_dim):
miniblock_idx = loc['miniblock']
for _i in range(0, miniblock_idx):
input_dim += self.miniblocks[_i].out_features_dim
return self.miniblocks[miniblock_idx].deepen(loc, new_layer_config, input_dim)
class DenseNetConfig:
def __init__(self):
self.net_config = {
'model_type': None,
'weight_decay': None,
'first_ratio': None,
'reduction': None,
'bc_ratio': None,
'bn_epsilon': None,
'bn_decay': None,
'pre_activation': None,
}
self.blocks = None
@property
def model_type(self): return self.net_config['model_type']
@property
def weight_decay(self): return self.net_config['weight_decay']
@property
def first_ratio(self): return self.net_config['first_ratio']
@property
def reduction(self): return self.net_config['reduction']
@property
def bc_ratio(self): return self.net_config['bc_ratio']
@property
def bn_epsilon(self): return self.net_config['bn_epsilon']
@property
def bn_decay(self): return self.net_config['bn_decay']
@property
def depth(self):
depth = 0
for block in self.blocks:
depth += block.depth
return depth
@property
def average_growth_rate(self):
growth_rate_list = []
for block in self.blocks:
if isinstance(block, DenseBlock):
for miniblock in block.miniblocks:
growth_rate = miniblock.out_features_dim
growth_rate_list.append(growth_rate)
return np.mean(growth_rate_list)
def copy(self):
net_config = DenseNetConfig()
net_config.set_net_from_config(self.get_config(), self.renew_init(None), print_info=False)
return net_config
def get_config(self):
return {
'name': 'DenseNet',
**self.net_config,
'blocks': [block.get_config() for block in self.blocks]
}
def renew_init(self, densenet):
return {
'blocks': [block.renew_init(densenet) for block in self.blocks]
}
def set_standard_dense_net(self, data_provider: DataProvider, growth_rate, depth, total_blocks,
keep_prob, weight_decay, model_type,
first_ratio=2, reduction=1.0, bc_ratio=4,
bn_epsilon=1e-5, bn_decay=0.9, print_info=True,
pre_activation=True, **kwargs):
self.net_config = {
'model_type': model_type,
'weight_decay': weight_decay,
'first_ratio': first_ratio,
'reduction': reduction,
'bc_ratio': bc_ratio,
'bn_epsilon': bn_epsilon,
'bn_decay': bn_decay,
'pre_activation': pre_activation,
}
image_size = data_provider.data_shape[0]
first_output_features = growth_rate * first_ratio
bc_mode = (model_type == 'DenseNet-BC')
layers_per_block = (depth - (total_blocks + 1)) // total_blocks
if bc_mode: layers_per_block = layers_per_block // 2
# initial conv
if pre_activation:
init_conv_layer = ConvLayer('conv_0', first_output_features, kernel_size=3, activation=None, use_bn=False)
else:
init_conv_layer = ConvLayer('conv_0', first_output_features, kernel_size=3, pre_activation=False)
init_transition = TransitionBlock('T_0_first', [init_conv_layer])
self.blocks = [init_transition]
# Dense Blocks
in_features_dim = first_output_features
for block_idx in range(1, total_blocks + 1):
miniblocks = []
block_id = 'D_%d' % block_idx
for miniblock_idx in range(1, layers_per_block + 1):
miniblock_id = 'M_%d' % miniblock_idx
in_bottle = None
if bc_mode:
bottelneck_layer = ConvLayer('conv_0', growth_rate * bc_ratio, kernel_size=1, keep_prob=keep_prob,
pre_activation=pre_activation)
in_bottle = LayerCascade('in_bottle', [bottelneck_layer])
branch_0 = LayerCascade('B_0', [
ConvLayer('conv_0', growth_rate, kernel_size=3,
keep_prob=keep_prob, pre_activation=pre_activation)
])
miniblocks.append(LayerMultiBranch(miniblock_id, [branch_0], in_bottle=in_bottle))
dense_block = DenseBlock(block_id, miniblocks)
self.blocks += [dense_block]
out_features_dim = dense_block.out_features_dim(in_features_dim)
if block_idx != total_blocks:
out_features_dim = int(out_features_dim * reduction)
transition_id = 'T_%d_middle' % block_idx
conv_layer = ConvLayer('conv_0', out_features_dim, kernel_size=1, keep_prob=keep_prob,
pre_activation=pre_activation)
avg_pool_layer = PoolLayer('pool_0', 'avg', kernel_size=2, strides=2)
transition = TransitionBlock(transition_id, [conv_layer, avg_pool_layer])
self.blocks.append(transition)
image_size = image_size // 2
in_features_dim = out_features_dim
# Transition to classes
if pre_activation:
global_avg_pool = PoolLayer('pool_0', 'avg', kernel_size=image_size, strides=image_size,
activation='relu', use_bn=True)
else:
global_avg_pool = PoolLayer('pool_0', 'avg', kernel_size=image_size, strides=image_size,
pre_activation=False)
final_fc_layer = FCLayer('fc_0', data_provider.n_classes, use_bn=False, use_bias=True, activation=None)
transition_to_classes = TransitionBlock('T_to_classes', [global_avg_pool, final_fc_layer])
self.blocks.append(transition_to_classes)
# print information about the network
if print_info:
print('Set Standard %s' % model_type)
if not bc_mode:
print('Build %s model with %d blocks, '
'%d composite layers each.' % (model_type, total_blocks, layers_per_block))
if bc_mode:
print('Build %s model with %d blocks, '
'%d bottleneck layers and %d composite layers each.' % (
model_type, total_blocks, layers_per_block, layers_per_block))
print('Reduction at transition layers: %.2f' % reduction)
return self
def set_net_from_config(self, net_config_json, init=None, print_info=True):
# load config and init (if exist)
for key in self.net_config.keys():
self.net_config[key] = net_config_json[key]
self.blocks = []
for _i, block_config in enumerate(net_config_json['blocks']):
block_init = init['blocks'][_i] if init is not None else None
block = get_block_by_name(block_config['name'])
self.blocks.append(block.set_from_config(block_config, block_init))
if print_info:
print('Set DenseNet from config:')
for k, v in self.net_config.items():
print('\t%s: %s' % (k, v))
print('\t%s: %d' % ('depth', self.depth))
return self
def widen(self, loc, new_width, widen_type='output_dim', noise=None, image_channel=3):
"""
widen_type: "output_dim" or "kernel"
"""
block_idx = loc['block']
if block_idx == 0:
input_dim = image_channel
elif isinstance(self.blocks[block_idx - 1], TransitionBlock):
input_dim = self.blocks[block_idx - 1].out_features_dim
else:
input_dim = self.blocks[block_idx - 1].out_features_dim(self.blocks[block_idx - 2].out_features_dim)
change_out_dim, indices, magnifier = \
self.blocks[block_idx].widen(loc, new_width, widen_type, noise=noise, input_dim=input_dim)
while change_out_dim:
change_out_dim, indices, magnifier = self.blocks[block_idx + 1].prev_widen(indices, magnifier, noise=noise)
block_idx += 1
def deepen(self, loc, new_layer_config, image_channel=3):
new_layer_config['pre_activation'] = self.net_config['pre_activation']
block_idx = loc['block']
if block_idx == 0:
input_dim = image_channel
elif isinstance(self.blocks[block_idx - 1], TransitionBlock):
input_dim = self.blocks[block_idx - 1].out_features_dim
else:
input_dim = self.blocks[block_idx - 1].out_features_dim(self.blocks[block_idx - 2].out_features_dim)
return self.blocks[block_idx].deepen(loc, new_layer_config, input_dim)
def set_identity4deepen(self, to_set_layers, data_provider, batch_size, batch_num=1, strict=True, noise=None):
"""
to_set_layers = [(new_layer, prev_layer), ...]
"""
task_list = {}
for new_layer, prev_layer in to_set_layers:
if new_layer.ready: continue
if new_layer.use_bn and strict:
task_id = id(prev_layer)
if task_id in task_list:
task_list[task_id][1].append(new_layer)
else:
task_list[task_id] = (prev_layer, [new_layer])
else:
new_layer.set_identity_layer(strict=strict, noise=noise)
if len(task_list) > 0:
model = DenseNet(None, data_provider, None, net_config=self, only_forward=True)
task_list = list(task_list.values())
fetches = [prev_layer.output_op for prev_layer, _ in task_list]
statistics = [[0, 0] for _ in task_list]
for _i in range(batch_num):
input_images, _ = data_provider.train.next_batch(batch_size)
outputs = model.sess.run(fetches, feed_dict={model.images: input_images, model.is_training: False})
for _j, out in enumerate(outputs):
out = out.astype('float32')
axis = tuple(range(len(out.shape) - 1))
mean = np.mean(out, axis=axis, keepdims=True)
variance = np.mean(np.square(out - mean), axis=axis, keepdims=True)
mean, variance = np.squeeze(mean), np.squeeze(variance)
statistics[_j][0] += mean
statistics[_j][1] += variance
for _j, (prev_layer, new_layers) in enumerate(task_list):
mean, variance = statistics[_j][0] / batch_num, statistics[_j][1] / batch_num
for new_layer in new_layers:
if new_layer.ready: continue
param = {
'moving_mean': mean,
'moving_variance': variance,
'epsilon': self.bn_epsilon,
}
new_layer.set_identity_layer(strict=strict, param=param, noise=noise)
def insert_miniblock(self, loc, miniblock_config, image_channel=3, noise=None):
block_idx = loc['block']
if block_idx == 0:
input_dim = image_channel
elif isinstance(self.blocks[block_idx - 1], TransitionBlock):
input_dim = self.blocks[block_idx - 1].out_features_dim
else:
input_dim = self.blocks[block_idx - 1].out_features_dim(self.blocks[block_idx - 2].out_features_dim)
assert isinstance(self.blocks[block_idx], DenseBlock), 'Invalid'
indices, magnifier = \
self.blocks[block_idx].insert_miniblock(loc['miniblock'], miniblock_config, input_dim, noise=noise)
self.blocks[block_idx + 1].prev_widen(indices, magnifier, noise=noise)
class DenseNet(BasicModel):
def _build_graph(self, only_forward=False):
_input = self.images
output = _input
# building blocks (transition and dense)
for block in self.net_config.blocks:
output = block.build(output, self, store_output_op=only_forward)
if not only_forward:
logits = output
with tf.variable_scope('L2_Loss'):
l2_loss = tf.add_n([tf.nn.l2_loss(var) for var in tf.trainable_variables()])
prediction = tf.nn.softmax(logits)
# losses
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(
logits=logits, labels=self.labels))
self.cross_entropy = cross_entropy
# optimizer and train step
optimizer = self.build_optimizer(self.learning_rate,
self.run_config.opt_config[0], self.run_config.opt_config[1])
self.train_step = optimizer.minimize(
cross_entropy + l2_loss * self.net_config.weight_decay)
correct_prediction = tf.equal(
tf.argmax(prediction, 1),
tf.argmax(self.labels, 1))
self.accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
================================================
FILE: Examples/cai_eas/eas/models/layer_cascade.py
================================================
from models.layers import ConvLayer, FCLayer, PoolLayer, get_layer_by_name
import tensorflow as tf
class LayerCascade:
def __init__(self, _id, layers):
self._id = _id
self.layers = layers
self.output_op = None
@property
def id(self):
return self._id
@id.setter
def id(self, value):
self._id = value
@property
def out_features_dim(self):
for layer in self.layers[::-1]:
if isinstance(layer, ConvLayer):
return layer.filter_num
elif isinstance(layer, FCLayer):
return layer.units
return None
@property
def depth(self):
depth = 0
for layer in self.layers:
if isinstance(layer, ConvLayer) or isinstance(layer, FCLayer):
depth += 1
return depth
def get_str(self):
layers_str = [layer.layer_str for layer in self.layers]
return '-'.join(layers_str)
def build(self, _input, densenet, store_output_op=False):
output = _input
with tf.variable_scope(self._id):
for layer in self.layers:
output = layer.build(output, densenet, store_output_op=store_output_op)
if store_output_op:
self.output_op = output
return output
def get_config(self):
return {
'_id': self._id,
'layers': [layer.get_config() for layer in self.layers]
}
def renew_init(self, densenet):
return {
'_id': self._id,
'layers': [layer.renew_init(densenet) for layer in self.layers]
}
def copy(self):
return self.set_from_config(self.get_config(), init=self.renew_init(None))
@staticmethod
def set_from_config(config_json, init=None, return_class=True):
_id = config_json['_id']
layers = []
for _i, layer_config in enumerate(config_json['layers']):
layer_init = init['layers'][_i] if init is not None else None
layer = get_layer_by_name(layer_config['name'])
layers.append(layer.set_from_config(layer_config, layer_init))
if return_class:
return LayerCascade(_id, layers)
else:
return _id, layers
"""
Network Transformation Operations
"""
def prev_widen(self, indices, magnifier, noise=None):
for layer in self.layers:
if isinstance(layer, ConvLayer) or isinstance(layer, FCLayer):
layer.prev_widen(indices, magnifier, noise=noise)
break
else:
layer.prev_widen(indices, magnifier, noise=noise)
def widen(self, idx, new_width, widen_type='output_dim', noise=None):
assert idx < len(self.layers), 'Index out of range: %d' % idx
if widen_type == 'output_dim':
assert isinstance(self.layers[idx], ConvLayer) or \
isinstance(self.layers[idx], FCLayer), 'Operation not available'
to_widen_layer = self.layers[idx]
if isinstance(to_widen_layer, ConvLayer):
indices, magnifier = to_widen_layer.widen_filters(new_filter_num=new_width, noise=noise)
else:
indices, magnifier = to_widen_layer.widen_units(new_units_num=new_width, noise=noise)
after_widen_layer = None
for _i in range(idx + 1, len(self.layers)):
if isinstance(self.layers[_i], ConvLayer) or isinstance(self.layers[_i], FCLayer):
self.layers[_i].prev_widen(indices, magnifier, noise=noise)
after_widen_layer = self.layers[_i]
break
else:
self.layers[_i].prev_widen(indices, magnifier, noise=noise)
return after_widen_layer is None, indices, magnifier
else:
raise ValueError('%s is not supported' % widen_type)
def deepen(self, idx, new_layer_config, input_dim):
assert idx < len(self.layers), 'Index out of range: %d' % idx
if new_layer_config['name'] == 'fc':
assert idx == len(self.layers) - 1 or isinstance(self.layers[idx + 1], FCLayer), 'Invalid'
assert isinstance(self.layers[idx], FCLayer) or isinstance(self.layers[idx], PoolLayer), 'Invalid'
# prepare the new fc layer
units = input_dim
for _i in range(idx, -1, -1):
if isinstance(self.layers[_i], FCLayer):
units = self.layers[_i].units
break
elif isinstance(self.layers[_i], ConvLayer):
units = self.layers[_i].filter_num
break
fc_idx = 0
for _i in range(0, idx + 1):
if isinstance(self.layers[_i], FCLayer):
fc_idx += 1
_id = 'fc_%d' % fc_idx
# change the id of following fc layers
for _i in range(idx + 1, len(self.layers)):
if isinstance(self.layers[_i], FCLayer):
self.layers[_i].id = 'fc_%d' % (fc_idx + 1)
fc_idx += 1
prev_layer = None
for _i in range(idx, -1, -1):
if self.layers[_i].ready:
prev_layer = self.layers[_i]
break
assert prev_layer is not None, 'Invalid'
new_fc_layer = FCLayer(_id, units, ready=False, **new_layer_config)
# insert the new layer into the cascade
self.layers = self.layers[:idx + 1] + [new_fc_layer] + self.layers[idx + 1:]
return new_fc_layer, prev_layer
elif new_layer_config['name'] == 'conv':
assert idx == len(self.layers) - 1 or not isinstance(self.layers[idx + 1], FCLayer), 'Invalid'
assert isinstance(self.layers[idx], ConvLayer) or isinstance(self.layers[idx], FCLayer), 'Invalid'
# prepare the new conv layer
filter_num = input_dim
for _i in range(idx, -1, -1):
if isinstance(self.layers[_i], ConvLayer):
filter_num = self.layers[_i].filter_num
break
conv_idx = 0
for _i in range(0, idx + 1):
if isinstance(self.layers[_i], ConvLayer):
conv_idx += 1
_id = 'conv_%d' % conv_idx
# change the id of following conv layers
for _i in range(idx + 1, len(self.layers)):
if isinstance(self.layers[_i], ConvLayer):
self.layers[_i].id = 'conv_%d' % (conv_idx + 1)
conv_idx += 1
prev_layer = None
for _i in range(idx, -1, -1):
if self.layers[_i].ready:
prev_layer = self.layers[_i]
break
assert prev_layer is not None, 'Invalid'
new_conv_layer = ConvLayer(_id, filter_num, ready=False, **new_layer_config)
self.layers = self.layers[:idx + 1] + [new_conv_layer] + self.layers[idx + 1:]
return new_conv_layer, prev_layer
else:
raise ValueError('Not support to insert a %s layer' % new_layer_config['name'])
================================================
FILE: Examples/cai_eas/eas/models/layer_multi_branch.py
================================================
import tensorflow as tf
import numpy as np
from models.layer_cascade import LayerCascade
class LayerMultiBranch:
def __init__(self, _id, branches, merge=None, in_bottle=None, out_bottle=None):
self._id = _id
self.in_bottle = in_bottle
self.branches = branches
self.out_bottle = out_bottle
self.merge = merge
if self.merge == 'add':
out_dim = []
for branch in self.branches:
out_dim.append(branch.out_features_dim)
assert np.std(out_dim) == 0, '<%s> require the output dim of all branches are the same' % self.merge
elif self.merge is None:
assert len(self.branches) == 1, 'Invalid'
self.output_op = None
@property
def id(self):
return self._id
@id.setter
def id(self, value):
self._id = value
@property
def out_features_dim(self):
if self.out_bottle:
return self.out_bottle.out_features_dim
out_dim = []
for branch in self.branches:
out_dim.append(branch.out_features_dim)
if self.merge == 'concat':
return np.sum(out_dim)
elif self.merge == 'add' or self.merge is None:
return out_dim[0]
else:
pass
@property
def depth(self):
depth = 0
if self.in_bottle:
depth += self.in_bottle.depth
if self.out_bottle:
depth += self.out_bottle.depth
branch_depth = []
for branch in self.branches:
branch_depth.append(branch.depth)
depth += np.max(branch_depth)
return depth
def get_str(self):
in_bottle_str = 'N' if self.in_bottle is None else self.in_bottle.get_str()
branches_str = [branch.get_str() for branch in self.branches]
branches_str = '+'.join(branches_str)
out_bottle_str = 'N' if self.out_bottle is None else self.out_bottle.get_str()
return '%s~%s~%s' % (in_bottle_str, branches_str, out_bottle_str)
def build(self, _input, densenet, store_output_op=False):
with tf.variable_scope(self._id):
output = _input
# in bottle
if self.in_bottle:
output = self.in_bottle.build(output, densenet, store_output_op=store_output_op)
# branches
branch_out = []
for branch in self.branches:
branch_out.append(branch.build(output, densenet, store_output_op=store_output_op))
if self.merge == 'concat':
output = tf.concat(branch_out, axis=3)
elif self.merge == 'add':
output = tf.add_n(branch_out)
elif self.merge is None:
output = branch_out[0]
else:
raise ValueError('Do not support <%s>' % self.merge)
# out bottle
if self.out_bottle:
output = self.out_bottle.build(output, densenet, store_output_op=store_output_op)
if store_output_op:
self.output_op = output
return output
def get_config(self):
return {
'_id': self._id,
'merge': self.merge,
'branches': [branch.get_config() for branch in self.branches],
'in_bottle': None if self.in_bottle is None else self.in_bottle.get_config(),
'out_bottle': None if self.out_bottle is None else self.out_bottle.get_config(),
}
def renew_init(self, densenet):
return {
'_id': self._id,
'branches': [branch.renew_init(densenet) for branch in self.branches],
'in_bottle': None if self.in_bottle is None else self.in_bottle.renew_init(densenet),
'out_bottle': None if self.out_bottle is None else self.out_bottle.renew_init(densenet),
}
@staticmethod
def set_from_config(config_json, init=None):
_id = config_json['_id']
merge = config_json['merge']
branches = []
for _i, branch_config in enumerate(config_json['branches']):
branch_init = init['branches'][_i] if init is not None else None
branch = LayerCascade.set_from_config(branch_config, branch_init)
branches.append(branch)
in_bottle = config_json['in_bottle']
if in_bottle:
in_bottle_init = init['in_bottle'] if init is not None else None
in_bottle = LayerCascade.set_from_config(in_bottle, in_bottle_init)
out_bottle = config_json['out_bottle']
if out_bottle:
out_bottle_init = init['out_bottle'] if init is not None else None
out_bottle = LayerCascade.set_from_config(out_bottle, out_bottle_init)
return LayerMultiBranch(_id, branches, merge, in_bottle=in_bottle, out_bottle=out_bottle)
"""
Network Transformation Operations
"""
def prev_widen(self, indices, magnifier, noise=None):
if self.in_bottle:
self.in_bottle.prev_widen(indices, magnifier, noise=noise)
else:
for branch in self.branches:
branch.prev_widen(indices, magnifier, noise=noise)
def widen(self, loc, new_width, widen_type='output_dim', noise=None):
if loc['multi-branch'] == 'in_bottle':
assert self.in_bottle is not None, 'Invalid'
change_out_dim, indices, magnifier = self.in_bottle.widen(loc['layer'], new_width, widen_type, noise=noise)
if change_out_dim:
for branch in self.branches:
branch.prev_widen(indices, magnifier, noise=noise)
return False, None, None
elif loc['multi-branch'] == 'out_bottle':
assert self.out_bottle is not None, 'Invalid'
change_out_dim, indices, magnifier = self.out_bottle.widen(loc['layer'], new_width, widen_type, noise=noise)
return change_out_dim, indices, magnifier
elif loc['multi-branch'] == 'branch':
branch_idx = loc['branch']
branch = self.branches[branch_idx]
old_branch_out_dim = branch.out_features_dim
change_out_dim, indices, magnifier = branch.widen(loc['layer'], new_width, widen_type, noise=noise)
if change_out_dim:
assert self.merge != 'add', 'Invalid'
prev_branch_out_dim = 0
for _i in range(0, branch_idx):
prev_branch_out_dim += self.branches[_i].out_features_dim
post_branch_out_dim = 0
for _i in range(branch_idx + 1, len(self.branches)):
post_branch_out_dim += self.branches[_i].out_features_dim
old_size = prev_branch_out_dim + old_branch_out_dim + post_branch_out_dim
base = np.arange(old_size)
indices = np.concatenate([
base[:prev_branch_out_dim],
indices + prev_branch_out_dim,
base[prev_branch_out_dim + old_branch_out_dim:]
])
magnifier = np.concatenate([
[1] * prev_branch_out_dim,
magnifier,
[1] * post_branch_out_dim,
])
if self.out_bottle is None:
return True, indices, magnifier
else:
self.out_bottle.prev_widen(indices, magnifier, noise=noise)
return False, None, None
else:
return False, None, None
else:
raise ValueError('Do not support %s' % loc['multi-branch'])
def deepen(self, loc, new_layer_config, input_dim):
if loc['multi-branch'] == 'in_bottle':
assert self.in_bottle is not None, 'Invalid'
return self.in_bottle.deepen(loc['layer'], new_layer_config, input_dim)
elif loc['multi-branch'] == 'out_bottle':
assert self.out_bottle is not None, 'Invalid'
if self.merge == 'concat': input_dim = np.sum([branch.out_features_dim for branch in self.branches])
else: input_dim = self.branches[0].out_features_dim
return self.out_bottle.deepen(loc['layer'], new_layer_config, input_dim)
elif loc['multi-branch'] == 'branch':
if self.in_bottle is not None: input_dim = self.in_bottle.out_features_dim
return self.branches[loc['branch']].deepen(loc['layer'], new_layer_config, input_dim)
else:
raise ValueError('Do not support %s' % loc['multi-branch'])
def remapped_branches(self, noise=None):
if self.merge == 'add' or self.merge is None:
size = self.out_features_dim
indices = np.random.choice(np.arange(size), size)
new_branches = []
for branch in self.branches:
new_layers = [layer.copy() for layer in branch.layers[:-1]]
last_layer = branch.layers[-1].copy().remap(indices, noise=noise)
new_layers.append(last_layer)
new_branch = LayerCascade(branch.id, new_layers)
new_branches.append(new_branch)
elif self.merge == 'concat':
new_branches = []
offset = 0
indices = []
for branch in self.branches:
size = branch.out_features_dim
sub_indices = np.random.choice(np.arange(size), size)
new_layers = [layer.copy() for layer in branch.layers[:-1]]
last_layer = branch.layers[-1].copy().remap(sub_indices, noise=noise)
new_layers.append(last_layer)
new_branch = LayerCascade(branch.id, new_layers)
new_branches.append(new_branch)
indices.append(sub_indices + offset)
offset += size
indices = np.concatenate(indices)
else:
raise NotImplementedError
return new_branches, indices
================================================
FILE: Examples/cai_eas/eas/models/layers.py
================================================
from models.basic_model import BasicModel
import tensorflow as tf
import numpy as np
import copy
def apply_noise(weights, noise_config):
if noise_config is None:
return weights
noise_type = noise_config.get('type', 'normal')
if noise_type == 'normal':
ratio = noise_config.get('ratio', 1e-3)
std = np.std(weights)
noise = np.random.normal(0, std * ratio, size=weights.shape)
elif noise_type == 'uniform':
ratio = noise_config.get('ratio', 1e-3)
mean, _max = np.mean(weights), np.max(weights)
width = (_max - mean) * ratio
noise = np.random.uniform(-width, width, size=weights.shape)
else:
raise NotImplementedError
return weights + noise
def get_layer_by_name(name):
if name == 'conv':
return ConvLayer
elif name == 'fc':
return FCLayer
elif name == 'pool':
return PoolLayer
else:
raise ValueError('Unknown layer type: %s' % name)
def get_magnifier(old_size, indices):
_l = np.zeros(old_size)
for x in indices:
_l[x] += 1
magnifier = (1.0 / _l)[indices]
return magnifier
def get_random_remapping(old_size, new_size):
base = np.arange(old_size)
indices = np.concatenate([base, np.random.choice(base, new_size - old_size)])
magnifier = get_magnifier(old_size, indices)
return indices, magnifier
class BaseLayer:
"""
_id, batch normalization, activation, dropout, ready
"""
def __init__(self, _id, use_bn=True, activation='relu', keep_prob=1.0, ready=True, pre_activation=True):
self._id = _id
self.use_bn = use_bn
self.activation = activation
self.keep_prob = keep_prob
self.ready = ready
self.pre_activation = pre_activation
self._scope = None
self._init = None
self.output_op = None
@property
def id(self): return self._id
@id.setter
def id(self, value): self._id = value
@property
def init(self):
return self._init
@property
def param_initializer(self):
if self._init is None:
return None
param_initializer = {}
for key in self.variable_list.keys():
if self._init[key] is not None:
param_initializer[key] = tf.constant_initializer(self._init[key])
if len(param_initializer) == 0:
param_initializer = None
return param_initializer
def renew_init(self, net: BasicModel):
if net is None:
return copy.deepcopy(self._init)
self._init = {}
for key, var_name in self.variable_list.items():
var = net.graph.get_tensor_by_name('%s/%s' % (self._scope, var_name))
self._init[key] = net.sess.run(var)
if len(self._init) == 0:
self._init = None
return copy.deepcopy(self._init)
def copy(self):
return self.set_from_config(self.get_config(), layer_init=copy.deepcopy(self._init))
def get_config(self):
return {
'_id': self.id,
'use_bn': self.use_bn,
'activation': self.activation,
'keep_prob': self.keep_prob,
'pre_activation': self.pre_activation,
}
@property
def variable_list(self):
"""
beta: mean scale
gamma: variance scale
y = gamma * (x - moving_mean) / sqrt(epsilon + moving_variance) + beta
"""
if self.use_bn:
return {
'moving_mean': 'BatchNorm/moving_mean:0',
'moving_variance': 'BatchNorm/moving_variance:0',
'beta': 'BatchNorm/beta:0',
'gamma': 'BatchNorm/gamma:0',
}
else:
return {}
@staticmethod
def set_from_config(layer_config, layer_init):
raise NotImplementedError
def build(self, _input, net, store_output_op):
raise NotImplementedError
def prev_widen(self, indices, magnifier, noise=None):
raise NotImplementedError
def set_identity_layer(self, strict, param, noise):
raise NotImplementedError
def widen_bn(self, indices, magnifier, noise=None):
if self.use_bn:
self._init['beta'] = self._init['beta'][indices]
self._init['gamma'] = self._init['gamma'][indices]
self._init['moving_mean'] = self._init['moving_mean'][indices]
self._init['moving_variance'] = self._init['moving_variance'][indices]
def set_bn_identity(self, strict=True, param=None, noise=None):
if self.use_bn:
if strict:
self._init['moving_mean'] = param['moving_mean']
self._init['moving_variance'] = param['moving_variance']
self._init['beta'] = self._init['moving_mean']
self._init['gamma'] = np.sqrt(self._init['moving_variance'] + param['epsilon'])
else:
# use default initialization for batch normalization layer
self._init['moving_mean'], self._init['moving_variance'] = None, None
self._init['beta'], self._init['gamma'] = None, None
class ConvLayer(BaseLayer):
def __init__(self, _id, filter_num, kernel_size=3, strides=1,
use_bn=True, activation='relu', keep_prob=1.0, ready=True, pre_activation=True, **kwargs):
BaseLayer.__init__(self, _id, use_bn, activation, keep_prob, ready, pre_activation)
self.filter_num = filter_num
self.kernel_size = kernel_size
self.strides = strides
@property
def layer_str(self):
return 'C%d,%d,%d' % (self.filter_num, self.kernel_size, self.strides)
@property
def variable_list(self):
var_list = {'kernel': 'kernel:0'}
var_list.update(super(ConvLayer, self).variable_list)
return var_list
def get_config(self):
return {
'name': 'conv',
'filter_num': self.filter_num,
'kernel_size': self.kernel_size,
'strides': self.strides,
**super(ConvLayer, self).get_config(),
}
@staticmethod
def set_from_config(layer_config, layer_init=None):
conv_layer = ConvLayer(**layer_config)
conv_layer._init = layer_init
return conv_layer
def build(self, _input, net: BasicModel, store_output_op=False):
output = _input
if not self.ready:
return output
with tf.variable_scope(self._id):
self._scope = tf.get_variable_scope().name
param_initializer = self.param_initializer
if self.pre_activation:
# batch normalization
if self.use_bn:
output = BasicModel.batch_norm(output, net.is_training, net.net_config.bn_epsilon,
net.net_config.bn_decay, param_initializer=param_initializer)
# activation
output = BasicModel.activation(output, self.activation)
# convolutional
output = BasicModel.conv2d(output, self.filter_num, self.kernel_size, self.strides,
param_initializer=param_initializer)
else:
# convolutional
output = BasicModel.conv2d(output, self.filter_num, self.kernel_size, self.strides,
param_initializer=param_initializer)
# batch normalization
if self.use_bn:
output = BasicModel.batch_norm(output, net.is_training, net.net_config.bn_epsilon,
net.net_config.bn_decay, param_initializer=param_initializer)
# activation
output = BasicModel.activation(output, self.activation)
# dropout
output = BasicModel.dropout(output, self.keep_prob, net.is_training)
if store_output_op:
self.output_op = output
return output
def widen_filters(self, new_filter_num, noise=None):
"""
Increase the filter number of a conv layer while preserving the functionality
Proposed in 'Net2Net': https://arxiv.org/abs/1511.05641
"""
assert new_filter_num > self.filter_num, 'Invalid new filter number: %d' % new_filter_num
assert self._init is not None, 'Uninitialized layer'
old_size, new_size = self.filter_num, new_filter_num
indices, magnifier = get_random_remapping(old_size, new_size)
# more filters
self.filter_num = new_filter_num
new_kernel = self._init['kernel'][:, :, :, indices]
new_kernel[:, :, :, old_size:] = apply_noise(new_kernel[:, :, :, old_size:], noise.get('wider'))
self._init['kernel'] = new_kernel
if not self.pre_activation:
# widen batch norm variables if use batch norm
self.widen_bn(indices, magnifier, noise=noise)
return indices, magnifier
def prev_widen(self, indices, magnifier, noise=None):
assert self._init is not None, 'Uninitialized layer'
# rescale kernel
self._init['kernel'] = self._init['kernel'][:, :, indices, :] * magnifier.reshape([1, 1, -1, 1])
if self.pre_activation:
self.widen_bn(indices, magnifier, noise=noise)
def set_identity_layer(self, strict=True, param=None, noise=None):
self._init = {}
self.set_bn_identity(strict, param, noise=noise)
mid = self.kernel_size // 2
self._init['kernel'] = np.zeros([self.kernel_size, self.kernel_size, self.filter_num, self.filter_num])
self._init['kernel'][mid, mid] = np.eye(self.filter_num)
self._init['kernel'] = apply_noise(self._init['kernel'], noise.get('deeper'))
self.ready = True
def remap(self, indices, noise=None):
self.filter_num = len(indices)
self._init['kernel'] = self._init['kernel'][:, :, :, indices]
self._init['kernel'] = apply_noise(self._init['kernel'], noise.get('wider'))
if not self.pre_activation:
self.widen_bn(indices, None, noise=noise)
return self
class FCLayer(BaseLayer):
def __init__(self, _id, units, use_bn=True, use_bias=False, activation='relu', keep_prob=1.0, ready=True,
pre_activation=False, **kwargs):
BaseLayer.__init__(self, _id, use_bn, activation, keep_prob, ready, pre_activation)
self.units = units
self.use_bias = use_bias
@property
def layer_str(self):
return 'FC%d' % self.units
@property
def variable_list(self):
var_list = {'W': 'W:0'}
if self.use_bias:
var_list['bias'] = 'bias:0'
var_list.update(super(FCLayer, self).variable_list)
return var_list
def get_config(self):
return {
'name': 'fc',
'units': self.units,
'use_bias': self.use_bias,
**super(FCLayer, self).get_config(),
}
@staticmethod
def set_from_config(layer_config, layer_init=None):
fc_layer = FCLayer(**layer_config)
fc_layer._init = layer_init
return fc_layer
def build(self, _input, net: BasicModel, store_output_op=False):
output = _input
if not self.ready:
return output
with tf.variable_scope(self._id):
self._scope = tf.get_variable_scope().name
param_initializer = self.param_initializer
# flatten if not
output = BasicModel.flatten(output)
if self.pre_activation:
# batch normalization
if self.use_bn:
output = BasicModel.batch_norm(output, net.is_training, net.net_config.bn_epsilon,
net.net_config.bn_decay, param_initializer=param_initializer)
# activation
output = BasicModel.activation(output, self.activation)
# FC
output = BasicModel.fc_layer(output, self.units, self.use_bias, param_initializer=param_initializer)
else:
# FC
output = BasicModel.fc_layer(output, self.units, self.use_bias, param_initializer=param_initializer)
# batch normalization
if self.use_bn:
output = BasicModel.batch_norm(output, net.is_training, net.net_config.bn_epsilon,
net.net_config.bn_decay, param_initializer=param_initializer)
# activation
output = BasicModel.activation(output, self.activation)
# dropout
output = BasicModel.dropout(output, self.keep_prob, net.is_training)
if store_output_op:
self.output_op = output
return output
def widen_units(self, new_units_num, noise=None):
"""
Increase the units number of a fc layer while preserving the functionality
Proposed in 'Net2Net': https://arxiv.org/abs/1511.05641
W: [in_dim, out_units]
bias: [out_units]
"""
assert new_units_num > self.units, 'Invalid new units number: %d' % new_units_num
assert self._init is not None, 'Uninitialized layer'
old_size, new_size = self.units, new_units_num
indices, magnifier = get_random_remapping(old_size, new_size)
# more units
self._init['W'] = self._init['W'][:, indices]
self._init['W'][:, old_size:] = apply_noise(self._init['W'][:, old_size:], noise.get('wider'))
self.units = new_units_num
# widen bias variable if exist
if self.use_bias:
self._init['bias'] = self._init['bias'][indices]
self._init['bias'][old_size:] = apply_noise(self._init['bias'][old_size:], noise.get('wider'))
if not self.pre_activation:
# widen batch norm variables if use batch norm
self.widen_bn(indices, magnifier, noise=noise)
return indices, magnifier
def prev_widen(self, indices, magnifier, noise=None):
assert self._init is not None, 'Uninitialized layer'
# rescale W
self._init['W'] = self._init['W'][indices] * magnifier.reshape([-1, 1])
if self.pre_activation:
self.widen_bn(indices, magnifier, noise=noise)
def set_identity_layer(self, strict=True, param=None, noise=None):
self._init = {}
self.set_bn_identity(strict, param, noise=noise)
if self.use_bias:
self._init['bias'] = [0.0] * self.units
self._init['W'] = np.eye(self.units)
self._init['W'] = apply_noise(self._init['W'], noise.get('deeper'))
self.ready = True
def remap(self, indices, noise=None):
self.units = len(indices)
self._init['W'] = self._init['W'][:, indices]
self._init['W'] = apply_noise(self._init['W'], noise.get('wider'))
if self.use_bias:
self._init['bias'] = self._init['bias'][indices]
if not self.pre_activation:
self.widen_bn(indices, None, noise=noise)
return self
class PoolLayer(BaseLayer):
def __init__(self, _id, _type, kernel_size=2, strides=2, use_bn=False, activation=None, keep_prob=1.0,
ready=True, pre_activation=True, **kwargs):
BaseLayer.__init__(self, _id, use_bn, activation, keep_prob, ready, pre_activation)
self._type = _type
self.kernel_size = kernel_size
self.strides = strides
@property
def layer_str(self):
return 'P%d,%d' % (self.kernel_size, self.strides)
def get_config(self):
return {
'name': 'pool',
'_type': self._type,
'kernel_size': self.kernel_size,
'strides': self.strides,
**super(PoolLayer, self).get_config(),
}
@staticmethod
def set_from_config(layer_config, layer_init=None):
pool_layer = PoolLayer(**layer_config)
pool_layer._init = layer_init
return pool_layer
def build(self, _input, net: BasicModel, store_output_op=False):
output = _input
if not self.ready:
return output
with tf.variable_scope(self._id):
self._scope = tf.get_variable_scope().name
param_initializer = self.param_initializer
if self.pre_activation:
# batch normalization
if self.use_bn:
output = BasicModel.batch_norm(output, net.is_training, net.net_config.bn_epsilon,
net.net_config.bn_decay, param_initializer=param_initializer)
# activation
output = BasicModel.activation(output, self.activation)
# Pooling
if self._type == 'avg':
output = BasicModel.avg_pool(output, k=self.kernel_size, s=self.strides)
elif self._type == 'max':
output = BasicModel.max_pool(output, k=self.kernel_size, s=self.strides)
else:
raise ValueError('Do not support the pooling type: %s' % self._type)
else:
# Pooling
if self._type == 'avg':
output = BasicModel.avg_pool(output, k=self.kernel_size, s=self.strides)
elif self._type == 'max':
output = BasicModel.max_pool(output, k=self.kernel_size, s=self.strides)
else:
raise ValueError('Do not support the pooling type: %s' % self._type)
# batch normalization
if self.use_bn:
output = BasicModel.batch_norm(output, net.is_training, net.net_config.bn_epsilon,
net.net_config.bn_decay, param_initializer=param_initializer)
# activation
output = BasicModel.activation(output, self.activation)
# dropout
output = BasicModel.dropout(output, self.keep_prob, net.is_training)
if store_output_op:
self.output_op = output
return output
def set_identity_layer(self, strict=True, param=None, noise=None):
raise ValueError('Pooling layer can never be an identity layer')
def prev_widen(self, indices, magnifier, noise=None):
self.widen_bn(indices, magnifier, noise=noise)
================================================
FILE: Examples/cai_eas/eas/models/utils.py
================================================
from models.dense_net import DenseNetConfig, DenseNet
from models.convnet import SimpleConvnetConfig, SimpleConvnet
import numpy as np
def get_model_config_by_name(name):
if name == 'DenseNet':
return DenseNetConfig
elif name == 'SimpleConvnet':
return SimpleConvnetConfig
else:
raise ValueError('Unknown model type %s' % name)
def get_model_by_name(name):
if name == 'DenseNet':
return DenseNet
elif name == 'SimpleConvnet':
return SimpleConvnet
else:
raise ValueError('Unknown model type %s' % name)
class RunConfig:
def __init__(self, batch_size, n_epochs, init_lr, reduce_lr_epochs, reduce_lr_factors, opt_config,
dataset, validation_size, validation_frequency, shuffle, normalization, should_save_logs,
should_save_model, renew_logs=False, other_lr_schedule=None, include_extra=True, **kwargs):
self.batch_size = batch_size
self.n_epochs = n_epochs
self.init_lr = init_lr
self.reduce_lr_epochs = reduce_lr_epochs
self.reduce_lr_factors = reduce_lr_factors
self.opt_config = opt_config
self.dataset = dataset
self.validation_size = validation_size
self.validation_frequency = validation_frequency
self.shuffle = shuffle
self.normalization = normalization
self.should_save_logs = should_save_logs
self.should_save_model = should_save_model
self.renew_logs = renew_logs
self.other_lr_schedule = other_lr_schedule
self.include_extra = include_extra
def get_config(self):
return self.__dict__
def update(self, new_config):
self.__dict__.update(new_config)
def copy(self):
return RunConfig(**self.get_config())
def learning_rate(self, epoch):
if self.other_lr_schedule is None or self.other_lr_schedule.get('type') is None:
lr = self.init_lr
for reduce_lr_epoch, reduce_factor in zip(self.reduce_lr_epochs, self.reduce_lr_factors):
if epoch >= reduce_lr_epoch * self.n_epochs:
lr /= reduce_factor
else:
if self.other_lr_schedule['type'] == 'cosine':
lr_max = self.init_lr
lr_min = self.other_lr_schedule.get('lr_min', 0)
lr = lr_min + 0.5 * (lr_max - lr_min) * (1 + np.cos((epoch - 1) / self.n_epochs * np.pi))
else:
raise ValueError('Do not support %s' % self.other_lr_schedule['type'])
return lr
@staticmethod
def get_default_run_config(dataset='C10+'):
if dataset in ['C10', 'C10+', 'C100', 'C100+']:
run_config = {
'batch_size': 64,
'n_epochs': 30,
'init_lr': 0.1,
'reduce_lr_epochs': [0.5, 0.75], # epochs * 0.5, epochs * 0.75
'reduce_lr_factors': [10, 10],
'opt_config': ['momentum', {'momentum': 0.9, 'use_nesterov': True}],
'dataset': dataset, # choices = [C10, C10+, C100, C100+]
'validation_size': None, # None or int
'validation_frequency': 10,
'shuffle': 'every_epoch', # None, once_prior_train, every_epoch
'normalization': 'by_channels', # None, divide_256, divide_255, by_channels
'should_save_logs': True,
'should_save_model': True,
'renew_logs': True,
'other_lr_schedule': {'type': 'cosine'}, # None, or cosine
}
elif dataset in ['SVHN']:
run_config = {
'batch_size': 64,
'n_epochs': 40,
'init_lr': 0.1,
'reduce_lr_epochs': [0.5, 0.75], # epochs * 0.5, epochs * 0.75
'reduce_lr_factors': [10, 10],
'opt_config': ['momentum', {'momentum': 0.9, 'use_nesterov': True}],
'dataset': dataset, # choices = [C10, C10+, C100, C100+]
'validation_size': None, # None or int
'validation_frequency': 1,
'shuffle': True,
'normalization': 'divide_255', # None, divide_256, divide_255, by_channels
'should_save_logs': True,
'should_save_model': True,
'renew_logs': True,
'other_lr_schedule': {'type': 'cosine'}, # None, or cosine
'include_extra': False,
}
else:
raise ValueError
return run_config
================================================
FILE: Examples/cai_eas/start_nets/start_net_convnet_small_C10+/net.config
================================================
{
"name": "SimpleConvnet",
"weight_decay": 0.0001,
"bn_epsilon": 1e-05,
"bn_decay": 0.9,
"drop_scheme": {
"type": "conv",
"conv_drop": 1.0,
"pool_drop": 0.7,
"fc_drop": 0.5
},
"layer_cascade": {
"_id": "SimpleConvNet",
"layers": [
{
"name": "conv",
"filter_num": 4,
"kernel_size": 3,
"strides": 1,
"_id": "conv_0",
"use_bn": true,
"activation": "relu",
"keep_prob": 1.0,
"pre_activation": false
},
{
"name": "pool",
"_type": "max",
"kernel_size": 2,
"strides": 2,
"_id": "pool_0",
"use_bn": false,
"activation": null,
"keep_prob": 1.0,
"pre_activation": false
},
{
"name": "conv",
"filter_num": 4,
"kernel_size": 3,
"strides": 1,
"_id": "conv_1",
"use_bn": true,
"activation": "relu",
"keep_prob": 1.0,
"pre_activation": false
},
{
"name": "pool",
"_type": "max",
"kernel_size": 2,
"strides": 2,
"_id": "pool_1",
"use_bn": false,
"activation": null,
"keep_prob": 1.0,
"pre_activation": false
},
{
"name": "conv",
"filter_num": 4,
"kernel_size": 3,
"strides": 1,
"_id": "conv_2",
"use_bn": true,
"activation": "relu",
"keep_prob": 1.0,
"pre_activation": false
},
{
"name": "pool",
"_type": "max",
"kernel_size": 2,
"strides": 2,
"_id": "pool_2",
"use_bn": false,
"activation": null,
"keep_prob": 1.0,
"pre_activation": false
},
{
"name": "conv",
"filter_num": 4,
"kernel_size": 3,
"strides": 1,
"_id": "conv_3",
"use_bn": true,
"activation": "relu",
"keep_prob": 1.0,
"pre_activation": false
},
{
"name": "pool",
"_type": "avg",
"kernel_size": 4,
"strides": 4,
"_id": "pool_4",
"use_bn": false,
"activation": null,
"keep_prob": 1.0,
"pre_activation": false
},
{
"name": "fc",
"units": 8,
"use_bias": false,
"_id": "fc_0",
"use_bn": true,
"activation": "relu",
"keep_prob": 1.0,
"pre_activation": false
},
{
"name": "fc",
"units": 10,
"use_bias": true,
"_id": "fc_1",
"use_bn": false,
"activation": null,
"keep_prob": 1.0,
"pre_activation": false
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/README.md
================================================
# Compression examples (PyTorch)
## Requirements
The base requirements for Auptimizer apply here.
Additionally, in order for the examples to work, torch >= 1.7.0 is needed.
## Example details
We provide an example configuration file and a corresponding training script for each of the compressors supported. Due to the fact that some compressors require additional modification of the training script, we provide individual training scripts for those compressors for clarity.
For pruners, we also provide the configuration files for both one-time compression and automatic compression experiments. For one-time compression, the configuration file is named as ``exp_.json``. For automatic compression, the configuration file is named as ``exp_auto.json``.
For quantizers, the configuration file is named as ``exp_.json``.
To further demonstrate the usage of automatic compression experiments using different Proposers and hyperparameter settings, we provide multiple examples for each of the Proposer. The corresponding configuration files are named ``exp_auto_fpgm_.json``.
In addition, to showcase the two use cases where the user can assign the same or different hyperparameter values for a group of layers, the configuration files ``exp_auto_fpgm_random_no_expand.json`` and ``exp_auto_fpgm_random.json`` can be used as references comparatively.
Finally, ``exp_auto_fpgm_aup_args.json`` and ``mnist_aup_args.py`` demonstrate how to use the decorator
``@aup_args`` to launch a compression experiment.
## Running the examples
For one-time compression:
```sh
python -m aup.compression exp_level.json
```
For automatic compression:
```sh
python -m aup.compression exp_auto_level.json --automatic
```
The ``--launch_dashboard`` flag can be added to the command if the user wants to show the dashboard while running the experiment.
================================================
FILE: Examples/compression/mnist_pytorch/exp_activation_apoz_rank.json
================================================
{
"name": "PyTorch MNIST Activation APoZ Rank Filter Pruner",
"script": "mnist.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "activation_apoz_rank_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_activation_apoz_rank_dependency_aware.json
================================================
{
"name": "PyTorch MNIST Activation APoZ Rank Filter Pruner (dependency aware)",
"script": "mnist_dependency_aware.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "activation_apoz_rank_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_activation_mean_rank.json
================================================
{
"name": "PyTorch MNIST Activation Mean Rank Filter Pruner",
"script": "mnist.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "activation_mean_rank_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_activation_mean_rank_dependency_aware.json
================================================
{
"name": "PyTorch MNIST Activation Mean Rank Filter Pruner (dependency aware)",
"script": "mnist_dependency_aware.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "activation_mean_rank_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_admm.json
================================================
{
"name": "PyTorch MNIST ADMM Pruner",
"script": "mnist_admm.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "admm",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"],
"op_names": ["conv1"]
}, {
"sparsity": 0.5,
"op_types": ["Conv2d"],
"op_names": ["conv2"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_agp.json
================================================
{
"name": "PyTorch MNIST AGP Pruner",
"script": "mnist_agp.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "agp",
"config_list": [{
"initial_sparsity": 0.0,
"final_sparsity": 0.8,
"start_epoch": 0,
"end_epoch": 2,
"frequency": 1,
"op_names": ["conv1", "conv2", "fc1", "fc2"],
"op_types": ["default"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_amc.json
================================================
{
"name": "PyTorch MNIST AMC Pruner",
"script": "mnist_amc.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "amc",
"config_list": [{
"op_types": ["Conv2d", "Linear"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_activation_apoz_rank.json
================================================
{
"name": "PyTorch MNIST Activation APoZ Rank Filter Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "activation_apoz_rank_filter",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2"]
}
]
},
"proposer": "random",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_activation_mean_rank.json
================================================
{
"name": "PyTorch MNIST Activation Mean Rank Filter Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "activation_mean_rank_filter",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2"]
}
]
},
"proposer": "random",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_admm.json
================================================
{
"name": "PyTorch MNIST ADMM Pruner (automatic)",
"script": "mnist_admm.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "admm",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2"],
"op_types": ["Conv2d"]
}]
},
"proposer": "random",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_agp.json
================================================
{
"name": "PyTorch MNIST AGP Pruner (automatic)",
"script": "mnist_no_speedup.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "agp",
"config_list": [{
"initial_sparsity": 0.0,
"final_sparsity": {
"range": [0.0, 0.9],
"type": "float"
},
"start_epoch": 0,
"end_epoch": 2,
"frequency": 1,
"op_names": ["conv1", "conv2", "fc1", "fc2"]
}
]
},
"proposer": "random",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_fpgm_aup_args.json
================================================
{
"name": "PyTorch MNIST FPGM Pruner (@aup_args) (automatic)",
"script": "mnist_aup_args.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "fpgm",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2"]
}
]
},
"proposer": "random",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_fpgm_bohb.json
================================================
{
"name": "PyTorch MNIST FPGM Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "fpgm",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2"]
}
]
},
"proposer": "bohb",
"n_parallel": 4,
"target": "max",
"n_samples": 5,
"num_samples": 4
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_fpgm_hyperband.json
================================================
{
"name": "PyTorch MNIST FPGM Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "fpgm",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2"]
}
]
},
"proposer": "hyperband",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_fpgm_hyperopt.json
================================================
{
"name": "PyTorch MNIST FPGM Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "fpgm",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2"]
}
]
},
"proposer": "hyperopt",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_fpgm_no_expand.json
================================================
{
"name": "PyTorch MNIST FPGM Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "fpgm",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1"]
}, {
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv2"]
}
]
},
"proposer": "random",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_fpgm_random.json
================================================
{
"name": "PyTorch MNIST FPGM Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "fpgm",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2"]
}
]
},
"proposer": "random",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_fpgm_random_no_expand.json
================================================
{
"name": "PyTorch MNIST FPGM Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "fpgm",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"expand_op_names": false,
"op_names": ["conv1", "conv2"]
}
]
},
"proposer": "random",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_fpgm_sequence.json
================================================
{
"name": "PyTorch MNIST FPGM Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "fpgm",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float",
"interval": 0.2
},
"op_names": ["conv1", "conv2"]
}
]
},
"proposer": "sequence",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_fpgm_spearmint.json
================================================
{
"name": "PyTorch MNIST FPGM Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "fpgm",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2"]
}
]
},
"proposer": "spearmint",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_l1filter.json
================================================
{
"name": "PyTorch MNIST L1 Filter Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "l1_filter",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2"]
}
]
},
"proposer": "random",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_l2filter.json
================================================
{
"name": "PyTorch MNIST L2 Filter Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "l2_filter",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2"]
}
]
},
"proposer": "random",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_level.json
================================================
{
"name": "PyTorch MNIST Level Pruner (automatic)",
"script": "mnist_no_speedup.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "level",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2", "fc1", "fc2"]
}
]
},
"proposer": "random",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_lottery_ticket.json
================================================
{
"name": "PyTorch MNIST Lottery Ticket Pruner (automatic)",
"script": "mnist_lottery_ticket.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "lottery_ticket",
"config_list": [{
"prune_iterations": {
"range": [1, 10],
"type": "int"
},
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_types": ["Conv2d"]
}
]
},
"proposer": "random",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_auto_taylor_fo.json
================================================
{
"name": "PyTorch MNIST Taylor FO Weight Filter Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "taylor_fo_weight_filter",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2"]
}
]
},
"proposer": "random",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_autocompress.json
================================================
{
"name": "PyTorch MNIST Auto Compress Pruner",
"script": "mnist_autocompress.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "auto_compress",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_fpgm.json
================================================
{
"name": "PyTorch MNIST FPGM Pruner",
"script": "mnist.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "fpgm",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_fpgm_aup_args.json
================================================
{
"name": "PyTorch MNIST FPGM Pruner (@aup_args)",
"script": "mnist_aup_args.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "fpgm",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_fpgm_dependency_aware.json
================================================
{
"name": "PyTorch MNIST FPGM Pruner (dependency aware)",
"script": "mnist_dependency_aware.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "fpgm",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_l1filter.json
================================================
{
"name": "PyTorch MNIST L1 Filter Pruner",
"script": "mnist.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "l1_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_l1filter_dependency_aware.json
================================================
{
"name": "PyTorch MNIST L1 Filter Pruner (dependency aware)",
"script": "mnist_dependency_aware.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "l1_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_l2filter.json
================================================
{
"name": "PyTorch MNIST L2 Filter Pruner",
"script": "mnist.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "l2_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_l2filter_dependency_aware.json
================================================
{
"name": "PyTorch MNIST L2 Filter Pruner (dependency aware)",
"script": "mnist_dependency_aware.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "l2_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_level.json
================================================
{
"name": "PyTorch MNIST Level Pruner",
"script": "mnist_no_speedup.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "level",
"config_list": [{
"sparsity": 0.8,
"op_types": ["default"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_lottery_ticket.json
================================================
{
"name": "PyTorch MNIST Lottery Ticket Pruner",
"script": "mnist_lottery_ticket.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "lottery_ticket",
"config_list": [{
"prune_iterations": 5,
"sparsity": 0.8,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_net_adapt.json
================================================
{
"name": "PyTorch MNIST NetAdapt Pruner",
"script": "mnist_net_adapt.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "net_adapt",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_quantization_bnn.json
================================================
{
"name": "PyTorch MNIST BNN Quantizer",
"script": "mnist_no_speedup.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "quantization",
"compressor": "bnn",
"config_list": [{
"quant_bits": 1,
"quant_types": ["weight"],
"op_types": ["Conv2d", "Linear"],
"op_names": ["conv1", "conv2", "fc1", "fc2"]
}]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_quantization_dorefa.json
================================================
{
"name": "PyTorch MNIST DoReFa Quantizer",
"script": "mnist_no_speedup.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "quantization",
"compressor": "dorefa",
"config_list": [{
"quant_types": ["weight"],
"quant_bits": 8,
"op_types": ["default"]
}]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_quantization_naive.json
================================================
{
"name": "PyTorch MNIST Naive Quantizer",
"script": "mnist_no_speedup.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "quantization",
"compressor": "naive",
"config_list": []
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_quantization_qat.json
================================================
{
"name": "PyTorch MNIST QAT Quantizer",
"script": "mnist_no_speedup.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "quantization",
"compressor": "qat",
"config_list": [{
"quant_types": ["weight"],
"quant_bits": {
"weight": 8
},
"op_types": ["Conv2d", "Linear"]
}, {
"quant_types": ["output"],
"quant_bits": 8,
"quant_start_step": 7000,
"op_types":["ReLU6"]
}]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_sensitivity.json
================================================
{
"name": "PyTorch MNIST Sensitivity Pruner",
"script": "mnist_sensitivity.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "sensitivity",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_simulated_annealing.json
================================================
{
"name": "PyTorch MNIST Simulated Annealing Pruner",
"script": "mnist_simulated_annealing.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "simulated_annealing",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_taylor_fo.json
================================================
{
"name": "PyTorch MNIST Taylor FO Weight Filter Pruner",
"script": "mnist.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "taylor_fo_weight_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/exp_taylor_fo_dependency_aware.json
================================================
{
"name": "PyTorch MNIST Taylor FO Weight Filter Pruner (dependency aware)",
"script": "mnist_dependency_aware.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "taylor_fo_weight_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_pytorch/mnist.py
================================================
#!/usr/bin/env python3
"""
MNIST convolutional network using pytorch
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
from __future__ import print_function
import argparse
import sys
import time
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
import aup
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25)
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return test_loss, correct / len(test_loader.dataset)
def main(config):
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=1, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
args, _ = parser.parse_known_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
kwargs = {'batch_size': args.batch_size}
if use_cuda:
kwargs.update({'num_workers': 1,
'pin_memory': True,
'shuffle': True},
)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('/tmp/torch_mnist_data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('/tmp/torch_mnist_data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1, **kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)
model = Net().to(device)
model.load_state_dict(torch.load("mnist_pretrained.pth", map_location=lambda storage, location: storage))
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
_, test_acc = test(model, device, test_loader)
compressor = aup.compression.create_compressor(model, config, optimizer=optimizer)
model = compressor.compress()
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
for epoch in range(1, args.epochs + 1):
train(args, model, device, train_loader, optimizer, epoch)
_, test_acc = test(model, device, test_loader)
scheduler.step()
model = compressor.apply_speedup(dummy_input=torch.randn(1, 1, 28, 28).to(device))
flops, _, _ = compressor.count_flops_params((1, 1, 28, 28))
flops = int(flops)
print("test_acc={} flops={}".format(test_acc, flops))
if "save_model" in config and config["save_model"]:
compressor.export_model(
model_path="mnist_compressed.pth",
speedup=True,
folder_name=config["folder_name"])
aup.print_result((test_acc - 1.) / np.log(flops))
if __name__ == '__main__':
config = aup.BasicConfig().load(sys.argv[1])
main(config)
================================================
FILE: Examples/compression/mnist_pytorch/mnist_admm.py
================================================
#!/usr/bin/env python3
from __future__ import print_function
import argparse
import sys
import time
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
import aup
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25)
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return test_loss, correct / len(test_loader.dataset)
def main(config):
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=2, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
args, _ = parser.parse_known_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
kwargs = {'batch_size': args.batch_size}
if use_cuda:
kwargs.update({'num_workers': 1,
'pin_memory': True,
'shuffle': True},
)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('/tmp/torch_mnist_data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('/tmp/torch_mnist_data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1, **kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)
model = Net().to(device)
model.load_state_dict(torch.load("mnist_pretrained.pth", map_location=lambda storage, location: storage))
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
_, test_acc = test(model, device, test_loader)
def short_term_fine_tuner(model, criterion, optimizer, epoch, callback):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target)
loss.backward()
if callback:
callback()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
scheduler.step()
def evaluator(model):
_, test_acc = test(model, device, test_loader)
return test_acc
compressor = aup.compression.create_compressor(model, config, trainer=short_term_fine_tuner, num_iterations=30, training_epochs=1)
model = compressor.compress()
model = compressor.apply_speedup(dummy_input=torch.randn(1, 1, 28, 28).to(device))
flops, _, _ = compressor.count_flops_params((1, 1, 28, 28))
flops = int(flops)
print("test_acc={} flops={}".format(test_acc, flops))
compressor.export_model(
model_path="mnist_compressed.pth",
save_best_only=True,
speedup=True,
**config)
aup.print_result((test_acc - 1.) / np.log(flops))
if __name__ == '__main__':
config = aup.BasicConfig().load(sys.argv[1])
main(config)
================================================
FILE: Examples/compression/mnist_pytorch/mnist_agp.py
================================================
#!/usr/bin/env python3
from __future__ import print_function
import argparse
import sys
import time
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
import aup
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25)
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return test_loss, correct / len(test_loader.dataset)
def main(config):
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=2, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
args, _ = parser.parse_known_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
kwargs = {'batch_size': args.batch_size}
if use_cuda:
kwargs.update({'num_workers': 1,
'pin_memory': True,
'shuffle': True},
)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('/tmp/torch_mnist_data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('/tmp/torch_mnist_data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1, **kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)
model = Net().to(device)
model.load_state_dict(torch.load("mnist_pretrained.pth", map_location=lambda storage, location: storage))
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
_, test_acc = test(model, device, test_loader)
compressor = aup.compression.create_compressor(model, config, optimizer=optimizer)
model = compressor.compress()
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
for epoch in range(1, args.epochs + 1):
train(args, model, device, train_loader, optimizer, epoch)
_, test_acc = test(model, device, test_loader)
scheduler.step()
compressor.update_epoch(epoch)
flops, _, _ = compressor.count_flops_params((1, 1, 28, 28))
flops = int(flops)
print("test_acc={} flops={}".format(test_acc, flops))
compressor.export_model(
model_path="mnist_compressed.pth",
mask_path="mnist_compressed_mask.pth",
save_best_only=True,
**config)
aup.print_result((test_acc - 1.) / np.log(flops))
if __name__ == '__main__':
config = aup.BasicConfig().load(sys.argv[1])
main(config)
================================================
FILE: Examples/compression/mnist_pytorch/mnist_amc.py
================================================
#!/usr/bin/env python3
from __future__ import print_function
import argparse
import sys
import time
import numpy as np
from PIL import Image
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
import aup
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return test_loss, correct / len(test_loader.dataset)
def main(config):
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=1, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
args, _ = parser.parse_known_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
kwargs = {'batch_size': args.batch_size}
if use_cuda:
kwargs.update({'num_workers': 1,
'pin_memory': True,
'shuffle': True},
)
def new_getitem(self, index):
img, target = self.data[index], int(self.targets[index])
# doing this so that it is consistent with all other datasets
# to return a PIL Image
img = Image.fromarray(img.numpy(), mode='L')
img = img.convert('RGB')
if self.transform is not None:
img = self.transform(img)
if self.target_transform is not None:
target = self.target_transform(target)
return img, target
datasets.mnist.MNIST.__getitem__ = new_getitem
transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
dataset1 = datasets.MNIST('/tmp/torch_mnist_data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('/tmp/torch_mnist_data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1, **kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)
model = torch.hub.load('pytorch/vision:v{}'.format(torchvision.__version__), 'mobilenet_v2', pretrained=True).to(device)
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
_, test_acc = test(model, device, test_loader)
def evaluator(val_loader, model):
_, test_acc = test(model, device, val_loader)
return test_acc
compressor = aup.compression.create_compressor(
model, config, evaluator=evaluator, val_loader=test_loader,
flops_ratio=0.5, model_type="mobilenetv2", n_calibration_batches=1,
n_points_per_layer=1, warmup=1, hidden1=32, hidden2=32,
train_episode=1, max_episode_length=1)
compressor.compress()
flops, _, _ = compressor.count_flops_params((1, 3, 28, 28))
print("test_acc={} flops={}".format(test_acc, flops))
if "save_model" in config and config["save_model"]:
compressor.export_model(
model_path="mnist_compressed.pth",
mask_path="mnist_compressed_mask.pth",
save_best_only=True,
folder_name=config["folder_name"])
aup.print_result((test_acc - 1.) / np.log(flops))
if __name__ == '__main__':
config = aup.BasicConfig().load(sys.argv[1])
main(config)
================================================
FILE: Examples/compression/mnist_pytorch/mnist_aup_args.py
================================================
#!/usr/bin/env python3
from __future__ import print_function
import argparse
import sys
import time
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
import aup
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25)
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return test_loss, correct / len(test_loader.dataset)
@aup.aup_args
def main(compression_type, compression_framework, compressor, config_list):
config = locals()
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=1, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
args, _ = parser.parse_known_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
kwargs = {'batch_size': args.batch_size}
if use_cuda:
kwargs.update({'num_workers': 1,
'pin_memory': True,
'shuffle': True},
)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('/tmp/torch_mnist_data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('/tmp/torch_mnist_data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1, **kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)
model = Net().to(device)
model.load_state_dict(torch.load("mnist_pretrained.pth", map_location=lambda storage, location: storage))
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
_, test_acc = test(model, device, test_loader)
compressor = aup.compression.create_compressor(model, config, optimizer=optimizer)
model = compressor.compress()
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
for epoch in range(1, args.epochs + 1):
train(args, model, device, train_loader, optimizer, epoch)
_, test_acc = test(model, device, test_loader)
scheduler.step()
model = compressor.apply_speedup(dummy_input=torch.randn(1, 1, 28, 28).to(device))
flops, _, _ = compressor.count_flops_params((1, 1, 28, 28))
flops = int(flops)
print("test_acc={} flops={}".format(test_acc, flops))
aup.aup_save_model(
compressor.export_model,
model_path="mnist_compressed.pth",
speedup=True)
return (test_acc - 1.) / np.log(flops)
if __name__ == '__main__':
if len(sys.argv) < 2:
print("config file required")
exit(1)
main(sys.argv[1])
================================================
FILE: Examples/compression/mnist_pytorch/mnist_autocompress.py
================================================
#!/usr/bin/env python3
from __future__ import print_function
import argparse
import sys
import time
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
import aup
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25)
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return test_loss, correct / len(test_loader.dataset)
def main(config):
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=2, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
args, _ = parser.parse_known_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
kwargs = {'batch_size': args.batch_size}
if use_cuda:
kwargs.update({'num_workers': 1,
'pin_memory': True,
'shuffle': True},
)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('/tmp/torch_mnist_data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('/tmp/torch_mnist_data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1, **kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)
model = Net().to(device)
model.load_state_dict(torch.load("mnist_pretrained.pth", map_location=lambda storage, location: storage))
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
_, test_acc = test(model, device, test_loader)
def short_term_fine_tuner(model, optimizer, criterion, epoch, callback):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target)
loss.backward()
if callback:
callback()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
scheduler.step()
def evaluator(model):
_, test_acc = test(model, device, test_loader)
return test_acc
compressor = aup.compression.create_compressor(
model, config, trainer=short_term_fine_tuner, evaluator=evaluator,
dummy_input=torch.randn(1, 1, 28, 28).to(device), optimize_mode="maximize",
num_iterations=1, admm_num_iterations=1, admm_training_epochs=1,
experiment_data_dir="./autocompress")
model = compressor.compress()
flops, _, _ = compressor.count_flops_params((1, 1, 28, 28))
flops = int(flops)
print("test_acc={} flops={}".format(test_acc, flops))
compressor.export_model(
model_path="mnist_compressed.pth",
mask_path="mnist_compressed_mask.pth",
save_best_only=True,
**config)
aup.print_result((test_acc - 1.) / np.log(flops))
if __name__ == '__main__':
config = aup.BasicConfig().load(sys.argv[1])
main(config)
================================================
FILE: Examples/compression/mnist_pytorch/mnist_dependency_aware.py
================================================
#!/usr/bin/env python3
from __future__ import print_function
import argparse
import sys
import time
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
import aup
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25)
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return test_loss, correct / len(test_loader.dataset)
def main(config):
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=1, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
args, _ = parser.parse_known_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
kwargs = {'batch_size': args.batch_size}
if use_cuda:
kwargs.update({'num_workers': 1,
'pin_memory': True,
'shuffle': True},
)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('/tmp/torch_mnist_data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('/tmp/torch_mnist_data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1, **kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)
model = Net().to(device)
model.load_state_dict(torch.load("mnist_pretrained.pth", map_location=lambda storage, location: storage))
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
_, test_acc = test(model, device, test_loader)
compressor = aup.compression.create_compressor(model, config, optimizer=optimizer, dependency_aware=True, dummy_input=torch.randn(1, 1, 28, 28).to(device))
model = compressor.compress()
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
for epoch in range(1, args.epochs + 1):
train(args, model, device, train_loader, optimizer, epoch)
_, test_acc = test(model, device, test_loader)
scheduler.step()
model = compressor.apply_speedup(dummy_input=torch.randn(1, 1, 28, 28).to(device))
flops, _, _ = compressor.count_flops_params((1, 1, 28, 28))
flops = int(flops)
print("test_acc={} flops={}".format(test_acc, flops))
if "save_model" in config and config["save_model"]:
compressor.export_model(
model_path="mnist_compressed.pth",
save_best_only=True,
speedup=True,
folder_name=config["folder_name"])
aup.print_result((test_acc - 1.) / np.log(flops))
if __name__ == '__main__':
config = aup.BasicConfig().load(sys.argv[1])
main(config)
================================================
FILE: Examples/compression/mnist_pytorch/mnist_lottery_ticket.py
================================================
#!/usr/bin/env python3
from __future__ import print_function
import argparse
import sys
import time
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
import aup
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25)
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return test_loss, correct / len(test_loader.dataset)
def main(config):
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=1, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
args, _ = parser.parse_known_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
kwargs = {'batch_size': args.batch_size}
if use_cuda:
kwargs.update({'num_workers': 1,
'pin_memory': True,
'shuffle': True},
)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('/tmp/torch_mnist_data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('/tmp/torch_mnist_data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1, **kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)
model = Net().to(device)
model.load_state_dict(torch.load("mnist_pretrained.pth", map_location=lambda storage, location: storage))
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
_, test_acc = test(model, device, test_loader)
compressor = aup.compression.create_compressor(model, config, optimizer=optimizer, lr_scheduler=scheduler)
model = compressor.compress()
for _ in compressor.get_prune_iterations():
compressor.prune_iteration_start()
for epoch in range(1, args.epochs + 1):
train(args, model, device, train_loader, optimizer, epoch)
_, test_acc = test(model, device, test_loader)
scheduler.step()
flops, _, _ = compressor.count_flops_params((1, 1, 28, 28))
flops = int(flops)
print("test_acc={} flops={}".format(test_acc, flops))
compressor.export_model(
model_path="mnist_compressed.pth",
mask_path="mnist_compressed_mask.pth",
save_best_only=True,
**config)
aup.print_result((test_acc - 1.) / np.log(flops))
if __name__ == '__main__':
config = aup.BasicConfig().load(sys.argv[1])
main(config)
================================================
FILE: Examples/compression/mnist_pytorch/mnist_net_adapt.py
================================================
#!/usr/bin/env python3
from __future__ import print_function
import argparse
import sys
import time
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
import aup
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25)
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return test_loss, correct / len(test_loader.dataset)
def main(config):
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=2, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
args, _ = parser.parse_known_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
kwargs = {'batch_size': args.batch_size}
if use_cuda:
kwargs.update({'num_workers': 1,
'pin_memory': True,
'shuffle': True},
)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('/tmp/torch_mnist_data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('/tmp/torch_mnist_data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1, **kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)
model = Net().to(device)
model.load_state_dict(torch.load("mnist_pretrained.pth", map_location=lambda storage, location: storage))
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
_, test_acc = test(model, device, test_loader)
def short_term_fine_tuner(model):
train(args, model, device, train_loader, optimizer, 1)
scheduler.step()
def evaluator(model):
_, test_acc = test(model, device, test_loader)
return test_acc
compressor = aup.compression.create_compressor(model, config, short_term_fine_tuner=short_term_fine_tuner, evaluator=evaluator, optimize_mode="maximize")
model = compressor.compress()
model = compressor.apply_speedup(dummy_input=torch.randn(1, 1, 28, 28).to(device))
flops, _, _ = compressor.count_flops_params((1, 1, 28, 28))
flops = int(flops)
print("test_acc={} flops={}".format(test_acc, flops))
compressor.export_model(
model_path="mnist_compressed.pth",
save_best_only=True,
speedup=True,
**config)
aup.print_result((test_acc - 1.) / np.log(flops))
if __name__ == '__main__':
config = aup.BasicConfig().load(sys.argv[1])
main(config)
================================================
FILE: Examples/compression/mnist_pytorch/mnist_no_speedup.py
================================================
#!/usr/bin/env python3
from __future__ import print_function
import argparse
import sys
import time
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
import aup
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25)
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return test_loss, correct / len(test_loader.dataset)
def main(config):
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=1, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
args, _ = parser.parse_known_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
kwargs = {'batch_size': args.batch_size}
if use_cuda:
kwargs.update({'num_workers': 1,
'pin_memory': True,
'shuffle': True},
)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('/tmp/torch_mnist_data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('/tmp/torch_mnist_data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1, **kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)
model = Net().to(device)
model.load_state_dict(torch.load("mnist_pretrained.pth", map_location=lambda storage, location: storage))
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
_, test_acc = test(model, device, test_loader)
compressor = aup.compression.create_compressor(model, config, optimizer=optimizer)
model = compressor.compress()
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
for epoch in range(1, args.epochs + 1):
train(args, model, device, train_loader, optimizer, epoch)
_, test_acc = test(model, device, test_loader)
scheduler.step()
flops, _, _ = compressor.count_flops_params((1, 1, 28, 28))
flops = int(flops)
print("test_acc={} flops={}".format(test_acc, flops))
compressor.export_model(
model_path="mnist_compressed.pth",
mask_path="mnist_compressed_mask.pth",
save_best_only=True,
**config)
aup.print_result((test_acc - 1.) / np.log(flops))
if __name__ == '__main__':
config = aup.BasicConfig().load(sys.argv[1])
main(config)
================================================
FILE: Examples/compression/mnist_pytorch/mnist_pretrained.py
================================================
from __future__ import print_function
import argparse
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25)
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
def main():
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=14, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
parser.add_argument('--save-model', action='store_true', default=False,
help='For Saving the current Model')
parser.add_argument('--speedup-model', action='store_true', default=False,
help='For speeding up the current Model')
args = parser.parse_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
kwargs = {'batch_size': args.batch_size}
if use_cuda:
kwargs.update({'num_workers': 1,
'pin_memory': True,
'shuffle': True},
)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('/tmp/torch_mnist_data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('/tmp/torch_mnist_data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1, **kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)
model = Net().to(device)
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
for epoch in range(1, args.epochs + 1):
train(args, model, device, train_loader, optimizer, epoch)
test(model, device, test_loader)
scheduler.step()
torch.save(model.state_dict(), "mnist_pretrained.pth")
if __name__ == '__main__':
main()
================================================
FILE: Examples/compression/mnist_pytorch/mnist_sensitivity.py
================================================
#!/usr/bin/env python3
from __future__ import print_function
import argparse
import sys
import time
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
import aup
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25)
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return test_loss, correct / len(test_loader.dataset)
def main(config):
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=1, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
args, _ = parser.parse_known_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
kwargs = {'batch_size': args.batch_size}
if use_cuda:
kwargs.update({'num_workers': 1,
'pin_memory': True,
'shuffle': True},
)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('/tmp/torch_mnist_data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('/tmp/torch_mnist_data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1, **kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)
model = Net().to(device)
model.load_state_dict(torch.load("mnist_pretrained.pth", map_location=lambda storage, location: storage))
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
_, test_acc = test(model, device, test_loader)
def short_term_fine_tuner(model):
train(args, model, device, train_loader, optimizer, 1)
scheduler.step()
def evaluator(model):
_, test_acc = test(model, device, test_loader)
return test_acc
compressor = aup.compression.create_compressor(model, config, finetuner=short_term_fine_tuner, evaluator=evaluator)
model = compressor.compress(eval_args=[model], finetune_args=[model])
model = compressor.apply_speedup(dummy_input=torch.randn(1, 1, 28, 28).to(device))
flops, _, _ = compressor.count_flops_params((1, 1, 28, 28))
flops = int(flops)
print("test_acc={} flops={}".format(test_acc, flops))
compressor.export_model(
model_path="mnist_compressed.pth",
save_best_only=True,
speedup=True,
**config)
aup.print_result((test_acc - 1.) / np.log(flops))
if __name__ == '__main__':
config = aup.BasicConfig().load(sys.argv[1])
main(config)
================================================
FILE: Examples/compression/mnist_pytorch/mnist_simulated_annealing.py
================================================
#!/usr/bin/env python3
from __future__ import print_function
import argparse
import sys
import time
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
import aup
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25)
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return test_loss, correct / len(test_loader.dataset)
def main(config):
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=1, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
args, _ = parser.parse_known_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
kwargs = {'batch_size': args.batch_size}
if use_cuda:
kwargs.update({'num_workers': 1,
'pin_memory': True,
'shuffle': True},
)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('/tmp/torch_mnist_data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('/tmp/torch_mnist_data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1, **kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)
model = Net().to(device)
model.load_state_dict(torch.load("mnist_pretrained.pth", map_location=lambda storage, location: storage))
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
_, test_acc = test(model, device, test_loader)
def evaluator(model):
_, test_acc = test(model, device, test_loader)
return test_acc
compressor = aup.compression.create_compressor(model, config, evaluator=evaluator, optimize_mode="maximize")
model = compressor.compress()
flops, _, _ = compressor.count_flops_params((1, 1, 28, 28))
flops = int(flops)
print("test_acc={} flops={}".format(test_acc, flops))
compressor.export_model(
model_path="mnist_compressed.pth",
mask_path="mnist_compressed_mask.pth",
save_best_only=True,
**config)
aup.print_result((test_acc - 1.) / np.log(flops))
if __name__ == '__main__':
config = aup.BasicConfig().load(sys.argv[1])
main(config)
================================================
FILE: Examples/compression/mnist_tensorflow/README.md
================================================
# Compression examples (Tensorflow)
## Requirements
The basic requirements for Auptimizer apply here.
Additionally, in order for the examples to work, the following packages are required:
* tensorflow >= 2.0
## Running the examples
For non-automatic examples:
```sh
python -m aup.compression exp_fpgm.json
```
For automatic examples:
```sh
python -m aup.compression exp_auto_fpgm.json --automatic
```
The ``--launch_dashboard`` flag can be added to the command if the user wants to show the dashboard while running the experiment.
================================================
FILE: Examples/compression/mnist_tensorflow/exp_auto_level.json
================================================
{
"name": "Tensorflow MNIST Level Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"resource_args": {
"save_model": true
},
"compression": {
"framework": "tensorflow",
"type": "pruning",
"compressor": "level",
"config_list": [{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2", "fc1", "fc2"]
}
]
},
"proposer": "hyperopt",
"n_parallel": 4,
"target": "max",
"n_samples": 5
}
================================================
FILE: Examples/compression/mnist_tensorflow/exp_level.json
================================================
{
"name": "Tensorflow MNIST Level Pruner",
"script": "mnist.py",
"resource": "cpu",
"compression": {
"framework": "tensorflow",
"type": "pruning",
"compressor": "level",
"config_list": [{
"sparsity": 0.8,
"op_types": ["default"]
}
]
}
}
================================================
FILE: Examples/compression/mnist_tensorflow/mnist.py
================================================
#!/usr/bin/env python3
"""
MNIST convolutional network using pytorch
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
from aup import print_result, aup_args
import tensorflow as tf
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
import tqdm
from math import log
import sys
import aup
num_epochs = 1
batch_size = 64
num_classes = 10
input_shape = (28, 28, 1)
def get_model(**kwargs):
model = keras.Sequential(
[
keras.Input(shape=input_shape),
layers.Conv2D(kwargs['conv1'], kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Conv2D(kwargs['conv2'], kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Flatten(),
layers.Dropout(kwargs['dropout']),
layers.Dense(num_classes, activation="softmax"),
]
)
return model
def test_model(model, test_dataset):
correct = []
for (batch, (images, labels)) in enumerate(test_dataset):
logits = model(images)
correct += list(labels.numpy() == np.argmax(logits.numpy(), axis=1))
return np.mean(correct)
def main(config):
(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()
train_dataset = tf.data.Dataset.from_tensor_slices(
(tf.cast(train_images[...,tf.newaxis]/255, tf.float32),
tf.cast(train_labels,tf.int64)))
train_dataset = train_dataset.shuffle(1000).batch(32)
test_dataset = tf.data.Dataset.from_tensor_slices(
(tf.cast(test_images[...,tf.newaxis]/255, tf.float32),
tf.cast(test_labels,tf.int64)))
test_dataset = test_dataset.shuffle(1000).batch(32)
model = get_model(dropout=0.1, conv1=32, conv2=64)
optimizer = tf.keras.optimizers.Adam()
loss_object = tf.keras.losses.SparseCategoricalCrossentropy()
model.load_weights("mnist_pretrained.h5")
def train_step(images, labels):
with tf.GradientTape() as tape:
logits = model(images, training=True)
tf.debugging.assert_equal(logits.shape, (32, 10))
loss_value = loss_object(labels, logits)
grads = tape.gradient(loss_value, model.trainable_variables)
optimizer.apply_gradients(zip(grads, model.trainable_variables))
compressor = aup.compression.create_compressor(model, config)
compressor.compress()
for epoch in range(num_epochs):
for (batch, (images, labels)) in enumerate(train_dataset):
train_step(images, labels)
acc = test_model(model, test_dataset)
print ('Epoch {} finished - test_acc={}'.format(epoch, acc))
compressor.export_model("mnist_compressed.h5", **config)
aup.print_result(acc - 1.)
if __name__ == '__main__':
config = aup.BasicConfig().load(sys.argv[1])
main(config)
================================================
FILE: Examples/compression/mnist_tensorflow/mnist_pretrained.py
================================================
#!/usr/bin/env python3
from aup import print_result, aup_args
import tensorflow as tf
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
import tqdm
from math import log
import sys
num_epochs = 14
batch_size = 64
num_classes = 10
input_shape = (28, 28, 1)
def get_model(**kwargs):
model = keras.Sequential(
[
keras.Input(shape=input_shape),
layers.Conv2D(kwargs['conv1'], kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Conv2D(kwargs['conv2'], kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Flatten(),
layers.Dropout(kwargs['dropout']),
layers.Dense(num_classes, activation="softmax"),
]
)
return model
def test_model(model, test_dataset):
correct = []
for (batch, (images, labels)) in enumerate(test_dataset):
logits = model(images)
correct += list(labels.numpy() == np.argmax(logits.numpy(), axis=1))
return np.mean(correct)
def main():
(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()
train_dataset = tf.data.Dataset.from_tensor_slices(
(tf.cast(train_images[...,tf.newaxis]/255, tf.float32),
tf.cast(train_labels,tf.int64)))
train_dataset = train_dataset.shuffle(1000).batch(32)
test_dataset = tf.data.Dataset.from_tensor_slices(
(tf.cast(test_images[...,tf.newaxis]/255, tf.float32),
tf.cast(test_labels,tf.int64)))
test_dataset = test_dataset.shuffle(1000).batch(32)
model = get_model(dropout=0.1, conv1=32, conv2=64)
optimizer = tf.keras.optimizers.Adam()
loss_object = tf.keras.losses.SparseCategoricalCrossentropy()
def train_step(images, labels):
with tf.GradientTape() as tape:
logits = model(images, training=True)
tf.debugging.assert_equal(logits.shape, (32, 10))
loss_value = loss_object(labels, logits)
grads = tape.gradient(loss_value, model.trainable_variables)
optimizer.apply_gradients(zip(grads, model.trainable_variables))
for epoch in range(num_epochs):
for (batch, (images, labels)) in enumerate(tqdm.tqdm(train_dataset)):
train_step(images, labels)
acc = test_model(model, test_dataset)
print ('Epoch {} finished - test_acc={}'.format(epoch, acc))
model.save("mnist_pretrained.h5")
print("test_acc={}".format(acc))
if __name__ == '__main__':
main()
================================================
FILE: Examples/compression/utility_functions/README.md
================================================
# Compression utility functions
This example provides an example training script ``mnist.py``, where the compression utility functions sensitivity analysis and channel dependency analysis are applied. To just use these utility functions, there is no need to launch an Auptimizer experiment. The analysis outputs will be saved to folder ``output``.
================================================
FILE: Examples/compression/utility_functions/mnist.py
================================================
#!/usr/bin/env python3
"""
MNIST convolutional network using pytorch
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
from __future__ import print_function
import argparse
import os
import sys
import time
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
import aup
OUT_DIR = "output"
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25)
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return test_loss, correct / len(test_loader.dataset)
def main():
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=1, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
args, _ = parser.parse_known_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
kwargs = {'batch_size': args.batch_size}
if use_cuda:
kwargs.update({'num_workers': 1,
'pin_memory': True,
'shuffle': True},
)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('/tmp/torch_mnist_data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('/tmp/torch_mnist_data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1, **kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)
model = Net().to(device)
model.load_state_dict(torch.load("../mnist_pytorch/mnist_pretrained.pth", map_location=lambda storage, location: storage))
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
os.makedirs(OUT_DIR, exist_ok=True)
# Example of Sensitivity Analysis usage
s_analyzer = aup.compression.sensitivity_analysis.SensitivityAnalysis(model=model, val_func=lambda model: test(model, device, test_loader))
sensitivity = s_analyzer.analysis(val_args=[model])
s_analyzer.export(os.path.join(OUT_DIR, "sensitivity_analysis.log"))
# ChannelDependency
data = torch.ones(1, 1, 28, 28).to(device)
channel_depen = aup.compression.shape_dependency.ChannelDependency(model, data)
channel_depen.export(os.path.join(OUT_DIR, "channel_dependency.csv"))
if __name__ == '__main__':
main()
================================================
FILE: Examples/converter_examples/Convert_Benchmark/.gitignore
================================================
.ipynb_checkpoints/
*.tflite
*.h5
*.tgz
pytorch/
================================================
FILE: Examples/converter_examples/Convert_Benchmark/Benchmark.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Using TensorFlow backend.\n"
]
}
],
"source": [
"\"\"\"\n",
" Copyright (c) 2018 LG Electronics Inc.\n",
" SPDX-License-Identifier: GPL-3.0-or-later\n",
"\"\"\" \n",
"import keras\n",
"import subprocess\n",
"from os import path\n",
"import pandas as pd\n",
"import json\n",
"\n",
"# see models here - https://www.tensorflow.org/lite/guide/hosted_models\n",
"STORAGE_LINK = \"https://storage.googleapis.com/download.tensorflow.org/models/mobilenet_v1_2018_08_02/\"\n",
"ADB_LOCAL = path.expanduser(\"~/Library/Android/sdk/platform-tools/adb\") # for MacOS\n",
"\n",
"ALPHA = [\"1.0\",\"0.75\",\"0.5\",\"0.25\"]\n",
"SIZE = [\"224\",\"192\",\"160\",\"128\"]\n",
"REP_DATA = \"repdata.py\"\n",
"\n",
"OP_TYPE = [\"float\", \"float16\", \"uint8\", \"int8\"]\n",
"\n",
"KEYS = [[a,b] for a in ALPHA for b in SIZE]\n",
"MOBILENET = \"mobilenet_v1\"\n",
"df = pd.DataFrame({\"model\":[], \"alpha\":[], \"size\":[], \"gpu\":[], \"nnapi\":[], \"op\": [], \"size_init\":[],\n",
" \"size_peak\":[], \"warmup\":[], \"init\":[], \"inference\":[]})"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"def preprocess(name):\n",
" try:\n",
" process = subprocess.Popen(\" wget \" + STORAGE_LINK + name + \".tgz \", shell=True).wait()\n",
" except:\n",
" print(\"Could not retrieve\")\n",
" return 0\n",
"\n",
" try:\n",
" process = subprocess.Popen(\" tar -xzf \" + name + \".tgz\", shell=True).wait()\n",
" except:\n",
" print(\"Could not unpack\")\n",
" return 0\n",
"\n",
" return 1\n",
"\n",
"def push_to_device(name):\n",
" try:\n",
" process = subprocess.Popen(ADB_LOCAL + \" push \" + name + \".tflite /data/local/tmp/\",\n",
" shell=True, stdout=subprocess.DEVNULL).wait()\n",
" except:\n",
" print(\"Could not push\")\n",
" return 0\n",
" return 1\n",
"\n",
"\n",
"def run_benchmark(name):\n",
" command_template = ADB_LOCAL + \" shell /data/local/tmp/benchmark_model --graph=/data/local/tmp/\"\n",
" command = command_template + name + \".tflite --num_threads=4\"\n",
" process2 = subprocess.Popen(command.split(), stdout=subprocess.PIPE)\n",
" output, error = process2.communicate()\n",
" output = output.decode('utf-8')\n",
" output = output.split(\"\\n\")\n",
" infer, memo = \"\",\"\"\n",
" for p in output:\n",
" if \"Inference timings\" in p:\n",
" infer = p\n",
" if \"Peak memory footprint\" in p:\n",
" memo = p\n",
" #output = output.split(\"\\n\")\n",
" #infer = output\n",
" return (infer, memo)\n",
"\n",
"def compile_results(infer, memo, df, of, op, alpha, size):\n",
" data = []\n",
" try:\n",
" idx = memo.find(\"init=\")\n",
" max_size = float(memo[idx+5:idx + memo[idx:].find(\" \")-1])\n",
" except:\n",
" max_size = float('nan')\n",
" idx = memo.find(\"overall=\")\n",
" try:\n",
" malloc_size = float(memo[idx+8:])\n",
" except:\n",
" malloc_size = float('nan')\n",
" idx = infer.find(\"Warmup (avg):\")\n",
" try:\n",
" warmup = float(infer[idx+14:idx + infer[idx:].find(\",\")])\n",
" except:\n",
" warmup = float('nan')\n",
" idx = infer.find(\"Init:\")\n",
" try:\n",
" init = float(infer[idx+6:idx + infer[idx:].find(\",\")])\n",
" except:\n",
" init = float('nan')\n",
" idx = infer.find(\"Inference (avg):\")\n",
" try:\n",
" no_stat = float(infer[idx+17:])\n",
" except:\n",
" no_stat = float('nan')\n",
" df = df.append(pd.Series([of, alpha, size, op, max_size, malloc_size,\n",
" warmup, init, no_stat], index=df.columns), ignore_index=True)\n",
" return df\n",
"\n",
"def convert(name, size, op):\n",
" json_dic = {}\n",
" json_dic[\"convert_from\"] = name+\"_frozen.pb\"\n",
" json_dic[\"convert_to\"] = name+\"_\"+op+\"_converted.tflite\"\n",
" json_dic[\"input_nodes\"] = \"input\"\n",
" json_dic[\"output_nodes\"] = \"MobilenetV1/Predictions/Reshape_1:0\"\n",
" command = \"\"\n",
" if op == 'float':\n",
" command = json.dumps([json_dic])\n",
" if op == 'float16':\n",
" quant_dic = {}\n",
" quant_dic[\"type\"]=\"float16\"\n",
" quant_dic[\"opsset\"] = \"tf\"\n",
" json_dic[\"quantization\"] = quant_dic\n",
" command = json.dumps([json_dic])\n",
" if op == 'int8':\n",
" quant_dic = {}\n",
" quant_dic[\"type\"]=\"int8\"\n",
" quant_dic[\"opsset\"] = \"int8\"\n",
" quant_dic[\"load\"] = REP_DATA + \" --custom_data '1,\" + size + \",\" + size + \",\" + \"3,1000' --undefok custom_data\"\n",
" json_dic[\"quantization\"] = quant_dic\n",
" command = json.dumps([json_dic])\n",
" if op == 'uint8':\n",
" quant_dic = {}\n",
" quant_dic[\"type\"]=\"uint8\"\n",
" quant_dic[\"opsset\"] = \"tf\"\n",
" quant_dic[\"load\"] = REP_DATA + \" --custom_data '1,\" + size + \",\" + size + \",\" + \"3,1000' --undefok custom_data\"\n",
" json_dic[\"quantization\"] = quant_dic\n",
" command = json.dumps([json_dic])\n",
" try:\n",
" process = subprocess.Popen(\"python3 -m aup.dlconvert -d \" + \"'\" + str(command) + \"'\", shell=True).wait()\n",
" except:\n",
" print(\"Could not Convert= \" + str(command))\n",
" return 0\n",
" return 1\n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for key in KEYS:\n",
"\n",
" name = MOBILENET + \"_\" + key[0] + \"_\" + key[1]\n",
"\n",
" print(\"Starting \" + str(name))\n",
"\n",
" if(not preprocess(name)):\n",
" continue\n",
"\n",
" if(not push_to_device(name)):\n",
" continue\n",
"\n",
" infer,memo = run_benchmark(name)\n",
"\n",
" df = compile_results(infer, memo, df, \"official\", \"float\", key[0], key[1])\n",
"\n",
" new_name = name + \"_quant\"\n",
"\n",
" print(\"Starting \" + str(new_name))\n",
"\n",
" if(not preprocess(new_name)):\n",
" continue\n",
"\n",
" if(not push_to_device(new_name)):\n",
" continue\n",
"\n",
" infer,memo = run_benchmark(new_name)\n",
"\n",
" df = compile_results(infer, memo , df, \"official\", \"int8\", key[0], key[1])\n",
" \n",
" print(\"Starting converted tflite benchmarking\")\n",
"\n",
" for op in OP_TYPE:\n",
"\n",
" converted = convert(name, key[1], op)\n",
"\n",
" if not converted:\n",
" print(\"Failed \" + str(name) + \" \" + str(op))\n",
" continue\n",
"\n",
" print(\"testing= \" + str(name) + \" \" + str(op))\n",
"\n",
" new_name = name + \"_\" + op + \"_converted\"\n",
"\n",
" if(not push_to_device(new_name)):\n",
" continue\n",
"\n",
" infer,memo = run_benchmark(new_name)\n",
"\n",
" df = compile_results(infer, memo, df, \"converted\", str(op), key[0], key[1])\n",
" \n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" model alpha size op size_init size_peak warmup init inference\n",
"0 official 1.0 224 float 0.78906 26.74220 33998.60 2196.0 33562.00\n",
"1 official 1.0 224 int8 0.87890 7.19531 24491.30 2017.0 23836.70\n",
"2 converted 1.0 224 float 0.79687 26.69530 34708.60 1929.0 33686.70\n",
"3 converted 1.0 224 float16 0.79296 34.55470 35102.30 2033.0 32647.30\n",
"4 converted 1.0 224 uint8 1.39060 8.44531 24231.40 3134.0 23624.50\n",
"5 converted 1.0 224 int8 1.39060 8.47266 27206.70 2890.0 23835.60\n",
"6 official 1.0 192 float 0.78906 25.14060 25100.70 1669.0 24425.90\n",
"7 official 1.0 192 int8 0.76562 6.64844 18825.00 2148.0 18390.10\n",
"8 converted 1.0 192 float 0.79687 25.37500 24657.60 1756.0 24129.00\n",
"9 converted 1.0 192 float16 0.79296 33.19920 26244.60 2075.0 25446.30\n",
"10 converted 1.0 192 uint8 1.39060 7.90234 18417.10 3117.0 18072.60\n",
"11 converted 1.0 192 int8 1.39060 8.06641 18780.40 2770.0 18272.40\n",
"12 official 1.0 160 float 0.78906 23.96480 18737.10 1639.0 18147.10\n",
"13 official 1.0 160 int8 0.76562 6.42969 14405.30 2022.0 13266.50\n",
"14 converted 1.0 160 float 0.79687 24.06640 18471.30 1768.0 19325.60\n",
"15 converted 1.0 160 float16 0.79687 31.91020 19203.30 1973.0 17638.40\n",
"16 converted 1.0 160 uint8 1.39060 7.23828 17993.90 2886.0 13290.70\n",
"17 converted 1.0 160 int8 1.39060 7.44141 21174.00 2649.0 20724.60\n",
"18 official 1.0 128 float 0.78515 22.92970 12444.20 1581.0 11945.00\n",
"19 official 1.0 128 int8 0.76562 6.20703 13523.20 2059.0 13424.40\n",
"20 converted 1.0 128 float 0.79296 22.69530 12496.10 1804.0 11959.30\n",
"21 converted 1.0 128 float16 0.78906 30.79300 13551.10 1828.0 12529.10\n",
"22 converted 1.0 128 uint8 1.28120 6.80469 8976.34 2915.0 8992.86\n",
"23 converted 1.0 128 int8 1.28120 6.92188 13422.40 2487.0 10108.50\n",
"24 official 0.75 224 float 0.76562 17.43360 21263.80 1878.0 21159.90\n",
"25 official 0.75 224 int8 0.82812 4.98047 17237.40 1988.0 16508.00\n",
"26 converted 0.75 224 float 0.78120 17.39450 22503.70 1607.0 21283.40\n",
"27 converted 0.75 224 float16 0.78120 22.75780 22192.10 2129.0 21192.60\n",
"28 converted 0.75 224 uint8 1.21870 6.03516 22660.10 2414.0 16021.80\n",
"29 converted 0.75 224 int8 1.21870 6.30859 15966.20 2838.0 15849.00\n",
"30 official 0.75 192 float 0.76562 16.13670 16253.70 1612.0 15627.30\n",
"31 official 0.75 192 int8 0.76562 4.30469 17908.20 1741.0 16690.40\n",
"32 converted 0.75 192 float 0.78120 16.42580 16129.60 1602.0 16212.00\n",
"33 converted 0.75 192 float16 0.78515 21.24220 15744.60 1838.0 15032.60\n",
"34 converted 0.75 192 uint8 1.21870 5.56250 12433.50 2599.0 12068.50\n",
"35 converted 0.75 192 int8 1.21870 5.62109 12045.20 2587.0 11737.50\n",
"36 official 0.75 160 float 0.76562 15.27340 11488.00 1807.0 11356.20\n",
"37 official 0.75 160 int8 0.76562 4.49219 9148.42 1905.0 9103.98\n",
"38 converted 0.75 160 float 0.78515 15.10160 11819.20 1928.0 11335.40\n",
"39 converted 0.75 160 float16 0.78515 19.90620 11790.90 1945.0 11200.20\n",
"40 converted 0.75 160 uint8 1.02340 5.09375 9288.87 2472.0 9114.61\n",
"41 converted 0.75 160 int8 1.02340 5.11328 9300.00 2543.0 9019.50\n",
"42 official 0.75 128 float 0.76171 14.07810 10167.30 1584.0 9786.01\n",
"43 official 0.75 128 int8 0.76562 3.81250 6325.96 1982.0 6249.11\n",
"44 converted 0.75 128 float 0.77734 14.44530 7544.64 1898.0 7422.35\n",
"45 converted 0.75 128 float16 0.78120 19.16020 8644.24 2002.0 8227.14\n",
"46 converted 0.75 128 uint8 1.02340 4.91016 6686.88 2458.0 5826.48\n",
"47 converted 0.75 128 int8 1.02340 4.85547 5921.82 2732.0 5877.53\n",
"48 official 0.5 224 float 0.75781 11.06250 12346.40 1864.0 11818.80\n",
"49 official 0.5 224 int8 0.76562 3.47656 9217.15 1663.0 9174.97\n",
"50 converted 0.5 224 float 0.75781 10.81250 12604.10 1658.0 11342.90\n",
"51 converted 0.5 224 float16 0.77734 13.45700 12766.20 1729.0 11792.10\n",
"52 converted 0.5 224 uint8 1.07810 4.51172 9011.44 2313.0 8938.94\n",
"53 converted 0.5 224 int8 1.07810 4.55078 9348.15 2308.0 9034.15\n",
"54 official 0.5 192 float 0.75781 9.57031 9739.80 1711.0 8417.13\n",
"55 official 0.5 192 int8 0.76562 2.90625 6879.19 1806.0 7067.31\n",
"56 converted 0.5 192 float 0.75781 9.56641 14427.00 1585.0 11264.40\n",
"57 converted 0.5 192 float16 0.77734 12.27340 9537.10 2055.0 9057.24\n",
"58 converted 0.5 192 uint8 1.07810 3.95703 6889.69 2352.0 6899.43\n",
"59 converted 0.5 192 int8 1.07810 3.68359 6866.42 2086.0 6884.52\n",
"60 official 0.5 160 float 0.75390 8.84375 6275.54 1826.0 6035.32\n",
"61 official 0.5 160 int8 0.76562 2.87109 8104.90 1843.0 7316.42\n",
"62 converted 0.5 160 float 0.75390 8.88281 6463.68 2811.0 6170.63\n",
"63 converted 0.5 160 float16 0.77343 11.58980 6799.38 1878.0 6314.49\n",
"64 converted 0.5 160 uint8 1.02340 3.66797 5131.54 2370.0 4970.17\n",
"65 converted 0.5 160 int8 1.02340 3.50781 5028.12 2082.0 4984.45\n",
"66 official 0.5 128 float 0.75390 7.90234 6759.62 1512.0 5552.03\n",
"67 official 0.5 128 int8 0.76562 2.24609 3510.57 1807.0 3630.16\n",
"68 converted 0.5 128 float 0.75390 7.99609 6622.07 1956.0 6976.16\n",
"69 converted 0.5 128 float16 0.77343 10.48830 4392.34 1880.0 4282.03\n",
"70 converted 0.5 128 uint8 1.02340 2.83984 3540.63 2307.0 3417.62\n",
"71 converted 0.5 128 int8 1.02340 3.18750 3475.96 2033.0 3435.91\n",
"72 official 0.25 224 float 0.73437 6.48438 6391.63 1487.0 6242.10\n",
"73 official 0.25 224 int8 0.50781 2.35156 4271.29 1878.0 4192.14\n",
"74 converted 0.25 224 float 0.74609 6.46484 4858.95 1478.0 4722.11\n",
"75 converted 0.25 224 float16 0.70000 7.48438 5223.81 1822.0 4951.89\n",
"76 converted 0.25 224 uint8 0.76562 3.60156 7467.73 2050.0 4353.84\n",
"77 converted 0.25 224 int8 0.76562 3.61719 4379.18 1977.0 4262.76\n",
"78 official 0.25 192 float 0.73046 5.38672 3590.72 1661.0 3483.75\n",
"79 official 0.25 192 int8 0.50781 1.98047 5196.78 1905.0 4826.97\n",
"80 converted 0.25 192 float 0.74218 5.38672 6775.37 1907.0 5215.93\n",
"81 converted 0.25 192 float16 0.74609 6.32812 5244.89 1851.0 4777.14\n",
"82 converted 0.25 192 uint8 0.76562 2.92969 4020.21 2084.0 3792.61\n",
"83 converted 0.25 192 int8 0.76562 3.05078 3359.21 2063.0 3350.96\n",
"84 official 0.25 160 float 0.73046 4.32812 2661.74 1759.0 2663.75\n",
"85 official 0.25 160 int8 0.50781 1.82031 2533.19 1761.0 2412.00\n",
"86 converted 0.25 160 float 0.74218 4.53125 3285.08 1705.0 2617.05\n",
"87 converted 0.25 160 float16 0.74609 5.35156 2636.35 1803.0 2572.78\n",
"88 converted 0.25 160 uint8 0.76562 2.35156 2531.91 2044.0 2494.01\n",
"89 converted 0.25 160 int8 0.76562 2.44922 2601.15 1997.0 2512.17\n",
"90 official 0.25 128 float 0.50781 3.79297 2639.18 1623.0 2469.44\n",
"91 official 0.25 128 int8 0.50781 1.55078 1830.96 1647.0 1757.80\n",
"92 converted 0.25 128 float 0.50781 3.69531 1923.96 1845.0 1857.89\n",
"93 converted 0.25 128 float16 0.75390 4.66797 2439.37 1901.0 2373.20\n",
"94 converted 0.25 128 uint8 0.76562 2.08984 1878.13 2049.0 1788.82\n",
"95 converted 0.25 128 int8 0.76562 1.95312 2336.19 2063.0 1838.06\n"
]
}
],
"source": [
"df = pd.read_pickle(\"./data_mobilenet.pkl\")\n",
"print(df.to_string())"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"import subprocess\n",
"from os import path\n",
"import pandas as pd\n",
"import logging\n",
"import matplotlib.pylab as plt\n",
"%matplotlib inline\n",
"%config InlineBackend.figure_format = 'retina'"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"def visualize(d, target, taget_label=None):\n",
" d['size'] = d['size'].astype('int')\n",
"\n",
" alphas = d['alpha'].unique().tolist()\n",
" labels = ['converted-float', 'converted-int8', 'official-float', 'official-int8'] # sorted((d['model'] + \"-\" + d['op']).unique().tolist())\n",
" shapes = ['ob','og','+b','+g'] # ['or','oy','ok','oc','+r','+k']\n",
"\n",
"\n",
" fig, axes = plt.subplots(figsize=(8,8), ncols=2, nrows=2, sharex=True, sharey=True)\n",
" for i, alpha in enumerate(alphas):\n",
" ax = axes[i%2][i//2]\n",
" for label, shape in zip(labels,shapes):\n",
" model, op = label.split(\"-\")\n",
" t = d[(d['alpha']==alpha) & (d['model']==model) & (d['op']==op)]\n",
" ax.plot(t['size'], t[target], shape, label=label)\n",
" ax.xaxis.set_major_formatter(plt.FormatStrFormatter('%d'))\n",
" if i==0:\n",
" ax.set_ylabel(taget_label)\n",
" if i==1:\n",
" ax.set_ylabel(taget_label)\n",
" ax.set_xlabel('input size')\n",
" if i==3:\n",
" ax.set_xlabel('input size')\n",
" ax.legend(title=\"Model alpha=%.2f\"%float(alpha))\n",
" plt.tight_layout()"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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O++8k6uuuqpQxx8VFcWhQ4c8Lmzff/89HTt25Pnnn2fevHn53t+KFSvYuHEjoaGhAIwfP557772X//znP/z1r3/1uEDv37+fVq1asXDhQipXrgxAfHw8zZo14/XXX2fYsGH4+fm56m/atInGjRu77SM9PZ1u3boxYcIEBg0aRL169VzbEhMTSUxMzPf4gUteHSA3P/30EwDNmjXzur1p06YA/PzzzwVqs2DBAn7++WeiNfGFSLkSEFCy7URELgeK3RW755Q9di9MvJ0fjz32mNvP1atXZ/z48R531Vxxsbu1Vq/L9AWsi4iIsBeTlJRkk5KSLlqvsABbr149a6213333nQXsyJEjrbXWJicnWx8fH/voo49aa6198cUXLWATEhJc7ZcvX24B2759e6/779SpkwXskiVLrLXWpqen2ypVqtjq1avbY8eOedSPjY21gI2Pj3eVbdy40QK2d+/eXvuYM2eOBew//vEPV1lCQoLHWAurR48e1t/f36anp7vKdu7caQEbGxvrdfyjRo3y2M/27dutj4+PDQsLcysPDQ21gN22bZtHmwcffNAC9scff8zXWD/99FML2OnTp7uVx8fHW6BAr7w4j79jx475Gld2Y8eOtYB98cUXvW7/97//bQH7yCOPuMoGDhxoATtlyhSvbYYNG2YBO27cuAKPJz+K+3MociXbtMlaKPhr06bC9RcREWGBdbYMxAK2APGAiJR9JREvKHa/OMXuhYu38/Luu+/ajz/+2O7atcueOXPGbt++3b766qu2evXqFrD/+te/3OqXZuye389hUcYDuuNGStTNN9/M9ddfz9SpU3nppZd45513yMzMzPNWy/Xr1wNZWW9voqKiWL58ORs2bCAyMpKtW7dy+vRpbrnlFgIDAz3q33rrrR7Py65atQqAtLQ0r3eB/PrrrwBs2bIlX8eZm//+97+8/fbbrF27liNHjnjMwn/kyBFCQkLyta/OnTt7lDVq1IgGDRqQnJzMsWPH3GZkDwwMpEmTJh5tGjRoAOCxZN7u3buZOHEiixYtYvfu3Zw5c8Zt+759+9x+HjFiRLHdQSMicilatoTIyIJNUNy5syYmFhFR7K7YvaQMGDDA7edGjRrx9NNPc80119CjRw9efPFFHn744UueC7O8UuJGStzAgQN54oknmDdvHgkJCbRp04Ybbrgh1/ppaWkAuV4UneXHjh1zq5/bbZF169b1KEtJSQFg4cKFLFy4MNexnDx5MtdtFzN58mQGDx5MzZo1uf3222nYsCFVqlTBGMOcOXP4/vvvOXfuXL73l9fx7dq1i7S0NLeLv7dl9QDXKgEXLlxwle3YsYObbrqJo0ePcssttxATE0NgYCAVKlQgOTmZ6dOnF2isJc35pe/8XcjJWZ7zy7GgbUSk/Bg+HGJi8rckuI8PlPGpvERESoxid8Xu3pRU7Ny9e3fq1avHvn37SEpK4vrrry/R/ssKJW6kxD3wwAM899xzDBo0iH379jF8+PA86zs/lAcPHvS6/cCBA271nO+HDh3yWt/bfpxtJk+ezBNPPJGPoyiY8+fPM2LECOrWrcv69es9vsicfzUoiEOHDnHNNdd4lDuPz9tfLPLrtddeIyUlhYSEBOLi4ty2zZw50+sM/2VpjhvnecntmVrnagbZn4ktTBsRKT+io+Hf/4ZHHsk7eePjA1OmZNUXERHF7ordf5M9di/J2LlOnTrs27fPbYWoKy12V+JGSlyNGjXo3bu3a/m4fv365VnfmdHP7cLinFE8IiICgObNm1OlShU2btxIWlqax0XQ237atWsHwLJly4rl4n/kyBGOHTvGPffc43HhP3nypOuW0oJYsmQJkZGRbmU7duxgz549hIWFXVJ2+ZdffgGgV69eXvv1JjExkZEjRxaon+JK3LRr147KlSuzYsUKTpw44TapXGZmpmuJyC5durjKnf+9YMECMjMz3WanP3HiBCtWrKBKlSqu3xURKX8efhjCwmD0aPB2KevcOetOGyVtRER+o9hdsbtT9ti9MPF2YaSlpbF161aMMYSHh7vKr7TYXcuBS6kYM2YMn332GfPnz/eYqT2njh07cs0117B8+XJmz57ttm327NksW7aMZs2a0alTJwD8/Pzo378/J06c8EgMrF27lg8++MCjj7Zt23LLLbfwn//8h6lTp3odx48//sjhw4cLcJS/CQ4OpkqVKqxbt87tls2MjAyefPJJjhw5UuB9Tp48mV27drl+zszMZOjQoWRmZvLQQw8VapxOYWFhgOcX5fz58z2WhHQaMWJEYSbMvGRbt25l69atbmXVqlXjgQce4NSpUx6/A2+++SbJycl07dqVRo0aucobN25MTEwMycnJ/OMf/3BrEx8fz6lTp3jggQeoWrVqkYxbREpHdDQkJsKmTTB5clYSZ/LkrJ8TE5W0ERHxRrF7FsXuvylMvA2wfft2tm7dSkZGhqvs4MGD7N2712OMJ0+eJC4ujrNnz3Lbbbe5PW52pcXuuuNGSkXDhg1p2LBhvuoaY5g+fTq33347ffv2pWfPnjRv3pyffvqJOXPmUL16dd577z23LOu4ceNYtGgRf/vb31i7di2dOnXiwIEDfPzxx9xxxx18/vnnHv18+OGHREVF8fDDD/PGG29w8803U6NGDfbu3csPP/zApk2bWLVqFcHBwQU+Xh8fH5544gkmTJjA9ddfT8+ePUlPT2fx4sWkpqbSpUsX118f8qtjx460bt2avn37EhgYyPz58/n+++9p06YNzz77bIHHmN1jjz1GQkIC9957L7179+bqq69m06ZNfP311/Tp04ePP/74kvafm+XLl7u+XJxfktu2bXO75XPatGlubVq0aAHg8WUybtw4EhMTee2119i4cSM33XQTW7ZsYe7cuQQHB3tc4AHeeustOnTowBNPPMGiRYto0aIF//vf/1i8eDHNmjVj7NixRXi0IlKaWrbU5MMiIvml2F2xuzeFibejo6PZtWsXO3fudCWctm7dym233Ub79u1p1qwZwcHB7Nu3j4ULF3Lw4EEaNWrkNQF1RcXuRbE0lV5l80UZXA78YrwtKei0detW+8c//tHWrVvX+vr62rp169r+/fvbrVu3et3XgQMH7EMPPWSDgoJspUqVbKtWrWxCQoJdvHixx5KCTsePH7djx461ERERtmrVqrZSpUo2LCzM3nHHHfZf//qXPXnypKtuQZcUzMjIsJMmTbItWrSwlSpVsldddZX94x//aJOTk13LBO7cudNV/2JLCjqXyLvmmmusv7+/vfrqq+2TTz5p09LSPPoODQ21oaGhXsflXApw8eLFbuUrVqywXbp0sTVq1LDVqlWzHTt2tJ999lme5+9SOc9pXq+cciu31tqUlBT7xBNP2IYNG1o/Pz9bt25d+9BDD9k9e/bkOobdu3fbuLg4W7duXevn52cbNmxon3zySZuamlpkx+mNlgMXuXxoOXARKS4lvRz4xSh23+mqfyXG7tYWPN52LnWe/dzt3r3bPvLII/aGG26wQUFB1tfX1wYEBNgbb7zRjhkzxh4/fjzX/ksjdi+N5cCNtUXzuIKUPcaYdRERERHr1q3Ls55zmTznnQtStsXFxTF9+nS3LLWUf/ocilw+2rRpw/r169dba9uU9lgg//GAiJR9ihfKH8Xul5/8fg6LMh7QHDciIiIiIiIiImWUEjciIiIiIiIiImWUEjciIiIiIiIiImWUEjci5cy0adOw1uoZWRERERGRMk6xuxQFJW5ERERERERERMooJW5ERERERERERMooJW5ERERERERERMooJW5ERERERERERMooJW5ERERERERERMooJW5ERERERERERMooJW5ERERERERERMooJW5ERERERERERMooJW5ERERERERERMooJW5EpEwwxnDrrbcWqE1GRgbx8fE0bdoUf39/jDHMmTOH5ORkjDHExcUVy1hFRERERK5kit1LlhI3UqI2b4Y33oAxY7LeN28u7RFJbkaMGIExhsTExNIeSq4mTZrEqFGjuPrqq3nmmWeIj4+nefPmpTqmwnyJiYiIiJQ1itvLF8XuhVNeYnff0h6AXBkWLYJRo2DpUs9tkZEwfDhER5f8uKR8+/LLL6lWrRoLFy6kYsWKrvLk5OTSG5SIiIhIOaa4XYqLYvfC0x03UuzefRdiYrxf/CGrPCYGpk4t2XFJ+bd//35q167tduEXERERkcJR3C7FSbF74SlxI8Vq0SJ45BHIzMy7XmYmDByYVb+0rV69mr59+1KvXj38/f0JCQkhJiaGTz75xK3eJ598QmRkJIGBgVSuXJnrr7+e8ePHc+7cOY99hoWFERYWxqlTpxg6dCgNGzbE39+fJk2aMHHiRKy1rrrfffcdxhjuvvvuXMfYokUL/P39SU1NdSufP38+d9xxB0FBQfj7+9O4cWOGDh3KsWPHch3T8ePHeeqppwgLC8PPz48RI0YQFhbGyJEjAejSpQvGGNcru9OnTzN+/Hhat25N1apVqVatGu3bt2fmzJlex52ens7o0aNp3Lgx/v7+hIeH89JLL3k9Z3mJi4vDGMPOnTvZtWuXa2xhYWEXbXvgwAH+/Oc/ExYWRsWKFalTpw733HMP69at86iblpbGX//6V6Kioqhfv76r/p133smqVavc6k6bNs11fpYsWeJ2zkaMGFGg4xMREREpaeUxbgfF7ordf3M5x+56VEqK1ahRF7/4O2VmwujRpXvr5ZQpU3j00UepUKECd955J02bNuXw4cOsXbuWt956iz59+gAwbNgwxo8fT1BQEPfffz/VqlVj3rx5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bduzZ0+7evVqr30kJCTYVq1a2SpVqtjatWvbu+66y/7www+5/rsdOnTI9uvXzwYHB1sfHx8L2Pj4+Isei5YDF7l8aDlwESkuits9KXb/jWL3kondS2M5cGOzvtDlMmSMWRcRERGxbt26POs5l33L+Sxgcdi8GRYtylo6MCAgK1Nfms/GipQVJfk5FJHi1aZNG9avX7/eWtumtMcC+Y8HRKTsU9wuUvry+zksynhAj0pJiWrZUhd8EREREZGyTnG7SNmhyYlFRERERERERMooJW5ERERERERERMooJW5ERERERERERMooJW5ERERERERERMooJW5ERERERERERMqocpe4McZMNMYsMsbsMcacMcakGmM2GGPijTG1c2nTwRjzlaPuGWPMD8aYwcaYCnn0090Yk2iMSTPGnDTG/M8YE3uRscUaY1Y76qc52nfPo34FY8wQx3icx/KVMaZD/s+IiIiIiIiIiFyuyl3iBhgCVAUWApOBD4DzwAjgB2NMg+yVjTE9gaVAJPAZ8CZQEXgd+MhbB8aYx4EvgOuAGcAU4GpgmjHm1VzavApMA0Ic9WcA1wNfOPaXs75x9P+aYzxvOsYXCSx1jFtERERERERErmC+pT2AQgiw1p7NWWiMGQsMA14AHnOUBZCVRLkA3GqtXesofxn4FuhtjLnPWvtRtv2EAa8CqUBba22yo3wUsAZ42hjzqbV2VbY2HYCnge3Ajdbao47yvwLrgFeNMV869+VwH9AbWAlEO4/JGPM2sByYYoz51lp7ovCnSkRERERERETKs3J3x423pI3DJ473ptnKegN1gI+cSZts+3jJ8eOjOfYzAPAH3syeaHEkY8Y5fhyUo43z57HOpI2jTTLwD8f+HsrRxtnvS9mPyVq7BvjYMe7eXo5TRERERERERK4Q5S5xk4cejvcfspVFOd6/9lJ/KXAa6GCM8c9nm3k56hSqjTGmEtDB0f+yAvQjIiIiIiIiIleQ8vioFADGmGeAakAg0BboRFbSZkK2atc43n/O2d5ae94YsxNoCTQCtuSjzQFjzCmgvjGmirX2tDGmKlAPOGmtPeBlqNsc782ylTUGKgA7rLXn89kmV8aYdblsap6f9iIiIlL+KR4QERG5PJXbxA3wDHBVtp+/BuKstb9mKwt0vKflsg9neY0CtqnqqHe6GPvI2UZERERERERErjDl9lEp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v/wlO+20U4/73lMf+chHOP3009lnn316tZ1169bR2NjI/fffz0EHHcR73vMeRo4cyYMPPsjXv/51vvWtb7FkyRIOOeSQXu2HJEnScDZU7tvBe/fe5L177zNxI/XQ//zP/wCw1157bVbZ6NGjGT16dI/b3Xrrrdf/34G+NmrUKEaNGtXr7dx2223cf//9NDY2snDhQmpqXp3VOWvWLGbPns3ll1/Odddd1+t9kSRJ0uDnvXvv8d6997nGjdTOzTffTH19PTvttBPbbbcdr3/96/nsZz/L2rVrgVfnl86aNQuA4447joggIpg/fz4RweTJkwH44Ac/uEEZbHqe7AMPPMBpp53GmDFjGDlyJKNHj2bixIncfPPN6+t0Nk/217/+NRdccAFveMMb2H333Rk5ciRjx45l+vTprFixopTPprN5shHBscceyx//+EemT5/O6NGjGTlyJHV1dVx//fUb1D3zzDM57rjjALjkkkvWfz5tr7t06VIATjzxxA1+8QNMmjQJgD/84Q+lvCdJkiQNXt67d85796HDETdSGxdeeCGf/exnGTVqFO9973t5zWtew913382FF17IPffcw8KFC9l3332ZNWsWixYtYvHixUyePJl9990XgPHjxzNr1iweffRRvve97zFp0qT1C5tVj52ZO3cuZ599NiNGjODkk0/mgAMOYOXKlTz00ENcffXVvPvd795k/L/927/xzW9+k+OOO44JEyawzTbb0NLSwrXXXssdd9zBQw891OMhnt3x3HPP8eY3v5ltttmGU045hbVr13LLLbcwZcoUampq1v9BrC4Cd8MNN9DQ0MCxxx67/hrVz7Gurg6Au+++m3PPPXeDPwB33nknAG95y1t67b1IkiRp4PPevee8dx9kMnPQPIDdgA8BtwG/Bf4MrAJ+BEwFatrV3xfITTwWbKKtycADwAuVNhYBJ22i/gjg48AvKv16BrgLmLCJmO2AS4BfAWuAlcDNwMElfV4PH3HEEdmVxx9/PB9//PEu65WFi0kups/a664lS5YkkHvvvXc+9dRT68+//PLLedJJJyWQl1122frzs2bNSiDvu+++ja51/fXXJ5DXX3/9RmWTJ09OIJctW7b+XEtLS2611Va5yy675GOPPbZRzH//93+vf75s2bIEcvLkyRvUWbFiRa5Zs2aj2HvuuSdramryrLPO2uD8fffdl0DOmjVro5jOdPaeqz9TU6dOzXXr1m3wvkaMGJEHH3zwZrXd2tqa73znOxPIQw45JD/60Y/mJz7xiTzuuONy6623znPOOSdffvnlbvd7MOjrn0NJveeII45I4OEcAPdOuRn3A5IGPu/bX+W9e9e8d+8d3f05LPN+YLCNuDkV+AbwFHAf8CTwV8A7gWuBt0XEqZnZfvWsnwO3d3C9xzpqJCIuB84HVgBzgW2A04E7IuKczLyyXf0AFgCnUCRhrgR2BU4DmiPiXZn5vXYxI4HvA28GHgK+CuxdeY8nRsTxmfnTLj+RAa66oFl3y/pz4bPqnMuLLrpog1Xkt9pqK770pS9x1113ce211260YFkZvvGNb7Bu3TpmzJixPmPd1mtf+9our9FZRn7ixInU1dVxzz33bHE/N2X77bfniiuuYMSIEevPHXLIIbz5zW+mubmZF154gde85jXdulZEcOutt3LJJZdw6aWX8vjjj68va2xs5L3vfS9bbTXYfn1JkiQNXIPpvh28d99S3rsPLoPt0/s1cDLw75nZWj0ZERdSjI55F0US57vt4h7NzIu700BETKBI2vwOOCozn62c/yLwMHB5RNyZmcvbhJ1OkbRZAjRm5ppKzDcpRgPNjYh7M/P5NjHnUSRtbgVOq76fiLiJIsl0XUS8vu37VO965JFHADj++OM3Knvd617Ha1/7WpYtW8aqVatKXxX+Jz/5CQBve9vbenyNzORf//VfmT9/Pj//+c959tlneeWVV9aXb7PNNl1e4/bbb+fRRx/d4Nz48ePXD5HclAMOOIDa2tqNzu+9994APPvss93+5b9mzRo+8IEPcPfdd3PVVVcxadIktt9+e+6//34++tGPUl9fzy233LJ+zqwkSZKGF+/dvXcfTgZV4iYz7+3k/NOVJMllwLFsnLjZHGdVjpdVkzaVNpZHxFXADOCDwKw2MWdXjhdVkzaVmAcriZgzKBI718P6ETrVdj7VNjmTmd+LiB8CxwANFCOLBq2OMvEDdVvBVatWAXS6avzo0aN58sknee6550r/5f/cc88BnWfeu+O8887jK1/5CqNHj+bv//7vGTNmDNtttx0A8+fP54knnujyGrfffjs33HDDBucmT57crV/+O++8c4fnq9n1tn+IuvK5z32OW265ha9+9av8wz/8w/rzb3vb27j11lsZP3485557rr/8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G0mqWbOmRo8eLcm11lFVxowbAECWvn376vHHH9ebb76p22+/Pat9/vz52rdvnyZPnqyffvrJZ+zq1av1u9/9TkePHtVtt92mdu3a6YcfftCsWbM0b948ff7557r66quz+u/fv1+dO3fW9u3b1bVrV3Xt2lW7d+/WsGHDFBMT4/McaWlpuvHGG5WamqobbrhBt9xyi44fP66PP/5Yt9xyi/79739ryJAhxb7+pKQkPffcc+rWrZvuuusu1a1bV1u3btXs2bM1f/58rVy5Uh07diz08UaOHKmkpCTdc8896t27txYvXqx//vOfWr58uVasWKFatWrl6H/mzBndfPPN2rVrl3r06KHq1atr7ty5+tvf/qZTp0553Vr08MMPKyIiQlFRUWratKkOHDigTz75RP369dOWLVsUFxdX7M/Cl6+++konT55UTEyM6tWrl2Ofw+HQzTffrKlTp2rp0qWFmhrty/z587Vnzx5FRUWpbdu2PvucPn1as2bN0s8//6zAwEBdfvnlioqKUrVq1Yp1TgCAN3ICcoL8lEVOIEk1atSQJK9ZXx5VJSegcAMAyFKvXj3dd999mjFjhnbu3KnmzZtLkqZNm6agoCDdc889Pu91t9aqf//+OnLkiGbNmqUHHngga9/777+v++67T/369dOmTZvkcLgme44ePVrbt2/XiBEj9NJLL2X1Hz58uK6//nqf4xswYIDS0tL03nvv6b777stqP3TokG688UY9+uijuu2223ThhRcW6/q7d++uvXv3eiUg3377rbp06aK//e1vWrRoUaGPt3LlSm3YsEGhoaGSpGeffVZ33323/ve//+kf//iHxowZk6P/rl271LFjR3322WeqXbu2JGncuHG65JJL9NJLL2n06NFZCYwkbdy4Ua1bt85xjIyMDPXo0UPPPfechg0bpmbNmmXtW7ZsWZG/scr+LdqWLVskSZdcconPvm3atJEk/fjjj0U6R3ZTp06VJP3hD3/Is8+ePXvUr1+/HG3h4eGKj49XdHR0sc8NAPgNOQE5QW5lnRNI0ltvvSVJuuWWW3zurzI5gbWWVyV9SVobGRlpAVR8mzZtsps2bSq140uyzZo1s9Za+9VXX1lJdvz48dZaa1NTU63D4bAPP/ywtdbap556ykqy8fHxWfErVqywkuz111/v8/hdu3a1kmxiYqK11tqMjAxbp04dW69ePXvo0CGv/gMGDLCS7Lhx47LaNmzYYCXZPn36+DzH3LlzrST72muvZbXFx8d7jbW4evXqZQMCAmxGRkZWW0pKipVkBwwY4HP8EyZM8DrOtm3brMPhsGFhYTnaQ0NDrSS7detWr5j+/ftbSfb7778v1FjnzJljJdmZM2fmaB83bpyVVKRXds8884yVZJ966imf5506daqVZIcOHVqoceaWkpJijTG2UaNG9tSpUz77xMbG2oSEBLtnzx57/Phx+/3339s//OEP1hhja9eubTds2FCscxdGYX8OIyMjraS11g9yAUs+AFQqpZ0PWEtOUBjkBKWfE1hr7bx586wxxjZv3tymp6d77S+vnKA88gFm3AAAcrj22mvVoUMHvfXWW3r66af15ptvKjMzM9/pxuvWrZPk+nbKl+7du2vFihVav369oqKi9MMPP+jEiRO64YYbFBwc7NX/xhtv9LqvfdWqVZKkw4cP+1yE7tdff5Ukbd68uVDXmZeFCxfqjTfe0Jo1a7R//36vp2Xs379fTZs2LdSxfH3T06pVK7Vo0UKpqak6dOiQ6tevn7UvODhYF198sVdMixYtJEkHDx7M0f7zzz9r8uTJSkhI0M8//6yTJ0/m2P/LL7/keB8bG+vXC/hNmzZN1loNGDDA56LEkrymhrdv315vvPGG6tatqxdeeEGxsbEslAkAJYScgJygvHz55Ze6//77FRgYqDlz5qhBgwZefapSTkDhBgDgZciQIXr00Ue1aNEixcfHq1OnTrryyivz7H/48GFJyjN58bQfOnQoR/+8pi83adLEq+3AgQOSpM8++0yfffZZnmM5duxYnvsK8vLLL2vEiBFq0KCBbrrpJrVs2VJ16tSRMUZz587Vt99+q9OnTxf6ePldX1pamg4fPpwjScv+5+w893WfO3cuq2379u265pprdPDgQd1www2KiYlRcHCwqlWrptTUVM2cObNIYy0MT0Lt+fvLzdOe13Xk5+zZs4qPj5dUvEWJhw0bphdeeEFJSUlFjgUA5I2cgJzAl9LMCVatWqUePXrI4XBo0aJFuuaaa4oUXxlzAgo3AAAv/fr101//+lcNGzZMv/zyi8aOHZtvf88v7z179vjcv3v37hz9PNu9e/f67O/rOJ6Yl19+WY8++mghrqJozp49q9jYWDVp0kTr1q3zSjg93+4Vxd69e3XppZd6tXuuz9c3i4X14osv6sCBA4qPj9fAgQNz7Hvvvfd8PonjfO9n91xLXvere54Uktf97vlZsGCBdu/erejoaJ+fWUEuuOACSa4ncAAASg45ATmBR1nkBMuXL9fvf/97ORwOLV68WNddd12R4qXKmRNQuAEAeKlfv7769OmT9ZjHvn375tvf881bXgnA0qVLJUmRkZGSpLZt26pOnTrasGGDDh8+7JWs+DqO5xf38uXLSyVJ279/vw4dOqQ777zTK0E7duxY1tTvokhMTFRUVFSOtu3bt2vHjh0KCwsr1rdQHp4nedx1110+z+vLsmXLNH78+CKdJ3uSdt1116l27dpauXKljh49mmPBxszMzKzHr3br1q1I55B+W5S4uI8A/+qrryTpvJ5cAQDwRk5ATuBR2jnBF198oV69eikgIECLFy/O8eSxoqiMOYGjvAcAAPBPEydO1EcffaTFixd7PVEhty5duujSSy/VihUrNHv27Bz7Zs+ereXLl+uSSy5R165dJbke7fjAAw/o6NGjXvdXr1mzRu+++67XOa666irdcMMN+t///pf1hIHcvv/+e+3bt68IV/mbkJAQ1alTR2vXrs0xtfrMmTN67LHHtH///iIf8+WXX1ZaWlrW+8zMTI0aNUqZmZl66KGHijVOj7CwMEneCe3ixYv15ptv+oyJjY0tzsK2WerWrat+/frp+PHjXn9vr776qlJTU3XzzTd7JUrbtm3TDz/8oDNnzvgcV1pampYsWaJGjRr5TDo9Nm/e7PPbs9TUVA0fPlyS9OCDD+YZDwAoHnICF3KC35R0TrBkyRL17NlTtWvXVkJCQoFFm6qWEzDjBgDgU8uWLdWyZctC9TXGaObMmbrpppt07/Cua4QAACAASURBVL33qnfv3mrbtq22bNmiuXPnql69enr77bezHvspSZMmTVJCQoL++c9/as2aNeratat2796t999/X7feeqvmz5/vdZ7//Oc/6t69uwYPHqwpU6bo2muvVf369bVz505999132rhxo1atWqWQkJAiX6/D4dCjjz6q5557Th06dFDv3r2VkZGhpUuXKj09Xd26dcv6lrCwunTpoiuuuEL33nuvgoODtXjxYn377bfq1KmT/vKXvxR5jNk98sgjio+P1913360+ffrooosu0saNG/Xpp5/qnnvu0fvvv39ex8/LpEmTtGzZMr34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O1zDnJ2dXaRdRQK0TH8AV1111Xb7mz59epE2q1evLvjmrbDSPsvBgweXuN/SpUsrNN7KeOWV1Krkfv36lSjbY489+P73v09+fj7vvvvu7h6aJNUINS0eAGOCnWFMkFKTYwITN5JUh3Vr040+2X12qk3f7L50a1NyLfnucMwxxwAwefLk7dbr2bMnkPrWpbgPP/yQ5cuX07lz5zK/TSuvJk2acPbZZ/Ppp5/y+uuv8/vf/56tW7eW+GYtM/Y1a9awaNGiSvVZWL169YDSv4n6/ve/T1ZWFm+++Wa575eTk0N+fj5vvfVWibLSPsvdKSsrq8xv3DLHkpZ1vGfmevETQyRJKTUtHgBjguKMCVJqa0xg4kaS6rgb+9xIVijfn4OskMXwPslvSFdel112GfXr12f06NEsXry4RPny5csBGDJkCABjxowp8od727ZtXHvtteTn53PxxRcnMqbMUZRPPfUUTz31FPXr1+eCCy4oUe+qq64CYOjQoXz66aclyr/++uuC9frllZk+vWzZshJlbdq04YILLmDu3LmMHj261ADno48+YsmSJQU/X3TRRQD89re/ZePGjQXXV69eXeSI0Kqwzz778M9//rPUsuOOOw6ARx99tMQ3r5MnT+btt9+mcePG9OrVa5ePU5JqqpoUD4AxQXHGBCm1NSZwjxtJquNy98/l0dMe5ZJXLiE/5pdZLytkMfZHY8ndv+qmRR966KE89NBDXHrppfTs2ZMBAwbQtWtXVq1axZw5c2jatCnTp0+nV69eXHfdddx+++10796dgQMHsueeezJ58mQWLlxI7969+dWvfpXImI499lgOOOAAJk6cyJYtW/jRj35U6mkFubm53HrrrfzmN7+ha9eu/Pu//zudO3dmw4YNfPLJJ8ycOZPevXvzxz/+sdx99+vXj6ysLH7zm9+wcOFCWrRoAaROzgB44IEH+Mc//sGNN97I7373O3r37s33vvc9Pv30U95//33mzJnDs88+S+fOnQE477zzeO655/if//kfunfvzoABA9iyZQsvvPACRx99NB999FECn1jF5ObmMmHCBH70ox+Rk5NDgwYN6NOnD3369GHgwIGceOKJvP766xxyyCH8+Mc/pm3btrz//vu88sorxBi59dZbi+wTIEkqqibFA2BMUJwxQS2PCZI4U9xX9XwB83JycqKkmm/x4sVx8eLFu7SP1z96PfYd1zcyghKvvuP6xtc/en2X9r8zZs+eHX/yk5/E1q1bxwYNGsR27drFk08+OU6cOLFIvWeffTYee+yxca+99oqNGjWKhx56aBwzZkz89ttvS9wzOzs7Zmdnl9pfXl5eBOL06dNLLR89enQEIhBfeOGF7Y79zTffjGeddVZs165dbNCgQWzVqlU84ogj4lVXXRXnzJlT7jFl/O53v4tHHHFEbNy4ccEYCtu0aVO8//774w9+8IPYtGnT2LBhw9ixY8d4wgknxLvvvjt++eWXJeqPHDkydu7cOTZs2DBmZ2fHYcOGxY0bN0Yg9u3bd7vjKWz69OkRiHl5eUWu9+3bt8Q4M8aNGxeBOG7cuCLXP//883jeeefFNm3axKysrBL33bx5c7z77rvjv/3bv8W999471qtXL7Zu3TqeeuqpccqUKeUe8/aU9/cwJycnAvNiNYgFovGAVKsYD5RkTPAdY4KUXR0TVEU8EGLqD7pqoRDCvJycnJx58+ZV9VAkVVLmeMZDDjlkl/e1aOUipi2ZxlebvqJpo6bkds6t0jXsUnVR3t/DI488kvnz58+PMR65O8a1I8YDUu1hPCBVvaqIB1wqJUkqolubbgZmkiTVccYDUvXh5sSSJEmSJEnVlIkbSZIkSZKkasrEjSRJkiRJUjVl4kaSJEmSJKmaMnEjSZIkSZJUTZm4kSRJkiRJqqZM3EiSJEmSJFVTJm4kSZIkSZKqKRM3kiRJkiRJ1ZSJG0mSJEmSpGrKxI0kSZIkSVI1ZeJGkiRJkiSpmjJxI0mq1aZOnUqvXr1o3rw5IQTOOOOMgrK5c+dy0kkn0apVK0II9OjRA4DBgwcTQmDp0qUV6nPp0qWEEBg8eHClxj5jxgxCCIwYMWKn2q1YsYJBgwbRoUMH6tWrRwiBtWvXMn78eEIIjB8/vlLjkiSpJjImMCaoqepX9QAkSdpVli5dyoABA2jevDlDhgyhadOmHHzwwQB89dVXnHrqqWzcuJELL7yQVq1a0bZt2yoecTIGDx7M1KlTOe+88zjggAMIIdC4ceMqG8+MGTPo168feXl5Ox1wSpKUBGMCY4KazMSNJKmEEFLvMVbtOCrr9ddfZ+PGjdx5552cf/75RcreeecdVq5cyU033cSwYcOKlN1yyy1cf/31tG/fvkL9tm/fnvfff59mzZpVeOwVtXnzZl577TVOPPFEnnnmmd3evySp9qgt8QAYExgT1GwmbiRJtdann34KwL777rtTZe3ataNdu3YV7rdBgwYF3+LtbitWrCA/P7/U55Ikqa4yJlBN5h43kqQa5/nnn6dPnz40a9aMJk2acNhhh3HLLbewadMm4Lt14Hl5eQD069ePEELBWu4QAoMGDQLgoosuKlIG21/P/s4773DOOefQvn17GjVqRLt27ejfvz/PP/98QZ2y1rN/8MEHXH/99Rx11FG0bt2aRo0akZ2dzSWXXMLy5csr/bl06tSJ7OxsAJ588smC5yrPuvp58+Zx5pln0qZNm4Jx/fznP+ezzz4rUXdnnmPw4MH069cPgJEjRxaMKYTAjBkzKv3MkqS6zZigdMYEtYszbiRJNcqwYcO45ZZbaNWqFeeffz577bUXkydPZtiwYUyZMoWpU6fSqVMn8vLymDFjBjNnzmTQoEF06tQJgB49epCXl8eCBQt46aWXGDBgQMEGhJn3sowdO5bLLruMevXqcfrpp9O1a1dWrlzJ3Llzeeihhzj77LO32/4Pf/gDjzzyCP369aNXr140bNiQRYsW8dhjj/Hyyy8zd+7cCk/FBrjyyitZunQp9957L0cccUTBpos7eq5XXnmFM888kxgjAwcOJDs7m3nz5vHwww/z0ksv8dZbb9G5c+cKPUdmDE8++SR9+/bl+OOPL7hP5r+JJEkVYUxQNmOCWibG6KuWvoB5OTk5UVLNt3jx4rh48eLd1l9qNftu667cZs+eHYHYsWPH+NlnnxVc37JlSzzttNMiEG+66aaC63l5eRGI06dPL3GvcePGRSCOGzeuRNmgQYMiEJcsWVJwbdGiRbF+/fqxRYsWceHChSXa/POf/yz495IlSyIQBw0aVKTO8uXL48aNG0u0nTJlSszKyoqXXnppkevTp0+PQMzLyyvRpixl9R1j6c+8fv362LJly5iVlRVnzZpVpP6tt94agXjSSSft9ueojsr7e5iTkxOBebEaxALReECqVYwHvmNMsGPGBLtGVcQDLpWSpDouhJKv8pRVhSeeeAKAG264ochpD/Xr1+fOO+8kKyuLxx57bJf0/fDDD7N161aGDx9Ot27dSpR36NBhh/fITKUurn///nTr1o0pU6YkMtad8dJLL7F69WrOOeccjjvuuCJl11xzDZ06deK1115j2bJlBder43NIkiqnJsUDYEywKxgTVF8ulZIk1Rjz588H4IQTTihRduCBB9KhQweWLFnCunXrEj+94c9//jMAp5xySoXvEWPkmWeeYfz48fzlL39hzZo1bNu2raC8YcOGO7zHpEmTWLBgQZFrPXr0KJh+vLO295nWr1+fPn36sHTpUt59913222+/xJ5DkqTKMCYwJqhLTNxIUh0XSznis7oe/7lu3TqAMk93aNeuHcuWLWPt2rWJB2lr164FqNR686uvvpp77rmHdu3acfLJJ9O+fXuaNGkCwPjx4/nkk092eI9Jkybx5JNPFrk2aNCgCgdp5flM4bvnT+o5JEnVS02KB8CYAIwJ6hITN5KkGiMTeK1YsYIuXbqUKM+cdpB0gAbQvHlzAP71r39V6FjPlStXct9999G9e3dmz57N3nvvXaT82WefLdd9xo8fX3DSRRIKf6alKf6ZJvUckiRVhjGBMUFd4h43kqQao2fPngClHhn54Ycfsnz5cjp37lwQUCXpmGOOAWDy5MkVav/xxx+Tn59P//79SwQ2y5cv5+OPP670GCtie5/p1q1befPNNwHIyckBKvYc9erVAygydVqSpMowJkieMUH1ZeJGklRjDBkyBIAxY8bwxRdfFFzftm0b1157Lfn5+Vx88cW7pO/LLruM+vXrM3r0aBYvXlyifPny5dttnznm8q233ioSrGzYsIGhQ4eydevWRMdbXmeccQYtW7bk2WefLVizn3HPPfewZMkSTjzxxIK17BV5jn322QegyGaGkiRVhjFB8owJqi+XSkmSaoxevXpx3XXXcfvtt9O9e3cGDhzInnvuyeTJk1m4cCG9e/fmV7/61S7p+9BDD+Whhx7i0ksvpWfPngwYMICuXbuyatUq5syZQ9OmTZk+fXqZ7du2bcu5557LhAkT6NGjB/3792fdunW89tprNG7cmB49epTYYHB32GuvvXjiiSc466yz6Nu3L2eddRb77bcf8+bNY+rUqbRt25b/+q//qtRzHHTQQbRv354JEybQoEEDsrOzCSFw4YUXkp2dvbsfWZJUCxgTJM+YoPoycSNJKqE6bkKYcdttt9GzZ08eeOABnnrqKbZs2UKXLl0YM2YM11xzzS49vWDo0KF0796dO+64gxkzZjBp0iRatWrF4Ycfzs9+9rMdtn/88cfZf//9ee6553jwwQdp3bo1p59+OqNGjeLMM8/c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\n",
"text/plain": [
""
]
},
"metadata": {
"image/png": {
"height": 566,
"width": 567
},
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"visualize(df, 'inference', 'avg inference [us]')"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABG0AAARsCAYAAAAkMmtiAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAIABJREFUeJzs3XlYlXX+//HnfQSRVHBBEjdwza1UdMrUICFxbDJq3FK/BuroWNPPtLLMUnDJpbTSmmqy3NKsrNQ2EiPAXEpFrRG0RUXFXVBcEZT79wdxxuM5KDsHfD2u61zEZ7k/7/vQubrPu89imKaJiIiIiIiIiIg4F0tZByAiIiIiIiIiIvaUtBERERERERERcUJK2oiIiIiIiIiIOCElbUREREREREREnJCSNiIiIiIiIiIiTkhJGxERERERERERJ6SkjYiIiIiIiIiIE1LSRkRERERERETECSlpIyIiIiIiIiLihJS0ERERERERERFxQkraiIiIiIiIiIg4ISVtRERERERERESckJI2IiIiIiIiIiJOSEkbEREREREREREnpKSNiIiIiIiIiIgTUtJGRERERERERMQJuZR1AOWdYRj7AA8guYxDERERkcLxA86Yptm4rAMBPVuIiIhUAH4U07OFkjZF5+Hu7l6rVatWtco6EBERESm4Xbt2cfHixbIO42p6thARESnHivPZQkmboktu1apVrYSEhLKOQ0RERAqhY8eObNu2Lbms47iKni1ERETKseJ8ttCeNiIiIiIiIiIiTkhJGxERERERERERJ6SkjYiIiIiIiIiIE1LSRkRERERERETECSlpIyIiIiIiIiLihJS0ERERERERERFxQkraiIiIiIiIiIg4IZeyDkBERERERERuLDs7m7S0NM6ePculS5cwTbOsQxKp8AzDwM3NjerVq1OrVi0sltKd+6KkjYiIiIiIiJPLzs7m4MGDXLhwoaxDEbmpmKZJRkYGGRkZnD9/noYNG5Zq4kZJGxERERERESeXlpbGhQsXcHFxoW7dulStWrXU/4+/yM0oOzub8+fPc/ToUS5cuEBaWhpeXl6lNr4+5SIiIiIiIk7u7NmzANStW5fq1asrYSNSSiwWC9WrV6du3brA/z6LpTZ+qY4mIiIiIiIiBXbp0iUAqlatWsaRiNyccj97uZ/F0qKkjYiI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Nmzg7NmzNhs2Z2dnW49d7969e4HGuFZ6ejq7d+/GMAwaN25sLc+9bnR0NNnZ2TYnSJ09e5YNGzZwyy23WP9dqQh05LeIiIg4reBgiIuDnTth7tycBM7cuTm/x8UpYSMiUpKmTZvGypUrWbNmjd1pStfq2rUrt912G+vXr+fTTz+1qfv000/54YcfaNGiBd26dQPA1dWVwYMHc/bsWbs9VrZu3cqyZcvsxujUqRP33HMPn3/+OQsWLHAYx3//+1+OHz9egLv8H29vb2655RYSEhJsllhlZWXx5JNPcvLkyQJfc+7cuezfv9/6e3Z2NuPGjSM7O5uhQ4cWKs5cfn5+gH2Ca82aNXZHtueKjIzENM0Cva5WrVo1hgwZwvnz5+3+bm+++SbJycn07NmTJk2a2NTt2bOH3bt3k5WVZS07evQoKSkpdjGeO3eO8PBwMjIyuO+++2yWmDVt2pSQkBCSk5P597//bdMvIiKC8+fPM2TIEKpWrerw/ssjzbQRERERp9emjTYaFhEpbY0aNaJRo0b5amsYBosXL6ZHjx4MGDCA0NBQWrZsya+//sqqVauoXr06S5YssZkZMX36dGJiYnj99dfZunUr3bp148iRI3z88cfcf//9fPHFF3bjfPjhhwQFBTF8+HDmzZvHXXfdRY0aNUhJSeGXX35h586dbNq0Kc9ToK7HYrEwevRoZs6cye23305oaCiZmZnExsaSlpZG9+7drTOG8qtr1660b9+eAQMG4OnpyZo1a/j555/p2LEjzz77bIFjvNrjjz/OwoUL6devH3379qVevXrs3LmTb7/9lv79+/Pxxx8X6fp5mT59OnFxcbz66qvs2LGDO++8k127drF69Wq8vb3tkikAwcHB7N+/n3379lmTTbt37+a+++7j7rvvpkWLFnh7e3Po0CHWrl3L0aNHadKkicPk01tvvUWXLl0YPXo0MTExtGrVip9++onY2FhatGjBSy+9VCL3XVY000ZERERERESK7K677mLLli0MGjSITZs28corr7Bx40YGDhzIli1buOuuu2zae3l5sWHDBoYOHcru3bt5/fXX2bFjB2+//TZjx451OEaDBg1ISEjgpZdeolKlSixbtox58+axceNGGjVqxH/+8x/rprWFMXXqVObMmYO7uzv/+c9/+Pzzz+nUqRObN2/OdwLraq+99hovvvgicXFx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PMowjDBgCnAF+AEY7WCqWLJpmov+/OdXgeaGYWwEUv4suwPI3Y1qommaG0s0aBEREREREScWHAzvvgsjR0J2dt7tLBaYP79sT4Zs3bo1b731FqNGjaJDhw6EhobSvHlzUlNT2bJlCx4eHsTGxtKlSxeeffZZXn75Zdq2bUvfvn2pWrUqUVFR7Ny5k27dujFu3Lhiialr1640a9aMFStWkJWVRe/evfH29rZrFxwczMyZM3n++edp3rw5999/P40bN+bcuXPs37+f+Ph4unXrxrfffpvvsbt3747FYuH5559n586d1KxZE8g5NQvgzTfHLlusAAAgAElEQVTf5Pfff2fSpEl88MEHdOvWjVtvvZXDhw+za9cutmzZwvLly2ncuDEAAwcO5OOPP+aLL76gbdu2hIaGkpWVxaeffspf/vIX9uzZUwzv2PW98847dO3alXHjxhEdHU2nTp04ePAgK1aswGKxsHDhQqpXr25tP3bsWOu/D3369MHV1ZUNGzaQlJRE7969+fLLL+3GCA4O5qOPPqJ37974+/vj6upKQEAAAQWddlYaiuPc8NJ6AZGAeYNX3FXthwNfAcnAOeAScAD4GLinmGJK8Pf3v+E57SIiIuKc/P39TSDBdIJnHVPPFiKSh6SkJDMpKalEx/juO9MMDMxd7Gz7CgzMqXcWGzduNP/+97+bderUMV1dXU0fHx+zZ8+e5ooVK2zaLV++3OzatatZrVo1083NzWzdurU5bdo08+LFi3bX9PX1NX19fR2OFxERYQJmbGysw/qpU6dav5N++umn1439hx9+MPv162f6+PiYrq6uppeXl9muXTtz7Nix5pYtW/IdU64PPvjAbNeunVmlShVrDFe7dOmS+cYbb5h333236eHhYVauXNls2LChGRQUZL722mvmyZMn7dpPnjzZbNy4sVm5cmXT19fXnDBhgpmRkWECZmBg4HXjuVZe97Bv3z4TMMPCwuzqUlJSzFGjRpmNGjUyXV1dzdq1a5uhoaHm5s2bHY6xcOFCs127duYtt9xi1q5d23zooYfMX375Jc+/27Fjx8yBAwea3t7epsViMQEzIiLihveS389hcT5bGKY2HygSwzAS/P39/RMSEso6FBERESmEjh07sm3btm1mHkdwlzY9W4iII7nHMl+7b0dJSEyEmJicY709PHJm1pTVHjYiziS/n8Pi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4H7gPiDXbM0FFlDSRkREREREREQkFwVN2jzqlShERERERERERIqowjrDqEBJG2vtFG8FIiIiIiIiIp4ZY7DWkpmZiY+PT2GHI3LDcSRtLrEdjNfp3S4iIiIiIlLE+fv7A3Dq1KlCjkTkxuR47znei1eLkjYiIiIiIiJFXKlSpQA4cOAAJ06cIDMzs9CWa4jcKByz206cOMGBAweAP96LV0u+l0cZY+YD06y1HxXG9SIiIiIiIjeKsmXLcurUKU6fPs3evXsLOxyRG1KJEiUoW7bsVR2zIDNt/gzUKMTrRUREREREbgg+Pj5UrlyZ8uXLExAQcNX31RC5URljCAgIoHz58lSuXPmq7ylV0NOjGhljHvFKJCIiIiIiIpIjHx8fQkJCCAkJKexQROQqKWjS5j6gcz6uU1pYRERERERERCQXBUnaPOqF8Td5oQ8RERERERERketOvpM21top3gxERERERERERET+oCO/RURERERERESKICVtRERERERERESKICVtRERERERERESKICVtRERERERERESKICVtRERERERERESKICVtRERERERERESKICVtRERERERERESKIN8r0akxpg5QF7jJWvvhlRhDREREREREROR65tWkjTGmEfA+0Dhb8YcX6toAC4CHrLXzvDmuiEhRlJmZSWpqKidOnODcuXNYaws7JJHrnjEGf39/SpUqRdmyZfHx0aRiERERuXZ57ZOMMaYWkATUBsaTlaDJbjmQCnT11pgiIkVVZmYme/bs4ffff+fs2bNK2IhcJdZazp49y++//86ePXvIzMws7JBERERE8s2bM23igeJAU2ttsjEmHujoqLTWWmPMauB2L44pIlIkpaamcvr0aXx9falQoQIlS5bU//iLXAWZmZmcOnWKAwcOcPr0aVJTUwkJCSnssERERETyxZu/QcQAn1lrk3Npswe4xYtjiogUSSdOnACgQoUKlCpVSgkbkavEx8eHUqVKUaFCBeCP96KIiIjItcibv0WUAfZeoo0hazaOiMh17dy5cwCULFmykCMRuTE53nuO96KIiIjItcibSZuDQI1LtKlP1mwbEZHrmmMPG82wESkcxhgA7SclIiIi1zRv/jbxLXCPMaa2p0pjzO1kLaFa6MUxRURERNw4kjYiIiIi1zJvJm1GAxnAcmPM41zYu8YYU//C83nACeA1L44pIiIiIiIiInJd8trpUdban4wxXYAZwNsXig3w44WvR4EHrLW7vTWmiIiIiIiIiMj1yqubLVhrvwaqAk8DnwDfAJ8Bg4Aa1tpvvTmeiIgUDUlJSRhjSEhIKFA/iYmJGGNITEz0SlwX27lzJ8YY4uLiCtxXREQEERERBe5HRERERCQnXt8h01p71Fo73lrb3Vrb3lr7oLV2nLU21dtjiYjcqIwxGGPw8fHh119/zbFd27ZtnW2vVCJEirbFixfzz3/+k5iYGMqVK4cxhtatW+e7v9TUVAYMGEBERAT+/v7ccsst9O7dm717cz5Acu/evfTu3ZtbbrkFf39/IiIiGDBgAEeOHMl3HCIiIiI3Aq8lbYwxUcaYppdoU8UYE+WtMUVEbmS+vr5Ya/nggw881v/8888kJSXh6+u1lbByDXrnnXd4/fXXWbVqFbfcckuB+kpJSaFFixaMHz+e6tWrM3DgQJo1a8bkyZNp0qQJ27dvd7vm119/pUmTJkyePJlmzZoxcOBAqlWrxvjx42nRogUpKSkFiklERETkeubNmTZJwH+MMbltNPwosNSLY4qI3LBuvvlmmjZtyuTJk8nIyHCrf//99wG45557rnZoUoQ899xzbN68mZMnTzJv3rwC9TV48GD+97//8fTTT7NkyRLGjBnDnDlzGD9+PIcOHeKJJ55wu+aJJ57g0KFDTJgwgTlz5jBmzBi+/fZbBg4cyE8//cSLL75YoJhERERErmfeXh51DhhojPnUGBPg5b5FROQiffr04cCBA3z55Zcu5enp6SQmJtKyZUvq1auX4/U///wzjzzyCJUqVaJ48eLccsstPPLII/z8888e2x88eJDHHnuMm2++mcDAQBo1asSUKVNyjTE1NZUXXniBunXrEhgYSHBwMDExMSxatOjyb/gi+/btY/jw4bRq1YoKFSo476FHjx4kJyfnuZ+4uDiMMWzfvp3XX3+dOnXqEBAQQFhYGAMHDuT48eM5Xnvq1CkGDRpElSpV8Pf3p0aNGowdOxZrrVvbxMREunTpQrVq1QgMDCQoKIhWrVoxbdq0fN1/XrRo0YL69etTrFixAvVz8uRJPvzwQ0qWLOm2d9FTTz1FeHg4CxcudJlt8+uvv7Jo0SIiIiJ48sknXa4ZNmwYJUuW5MMPP+TUqVMFik1ERETkeuXtpM044EPgfiDJGFPey/2LiEg23bt3p2TJks5ZNQ5z587l0KFD9OnTJ8dr165dS9OmTZk2bRq33347zzzzDM2bN2fatGk0bdqUtWvXurQ/fPgwLVu2ZNKkSdSqVYsBAwbQqFEj+vbtyxtvvOFxjF27dtGkSRPGjBlD+fLl6du3Lw899BBbt27lz3/+MxMnTizQ/S9fvpwxY8ZQunRpunTpwsCBA2nevDmzZ8+mWbNm/PDDD5fV38CBAxkxYgRt2rShf//+hISE8OabbxIdHc3Zs2fd2qenp9OhQwc+/fRTOnbsyF//+lfOnDnD888/z/Dhw93aP/744+zatYuoqCgGDBjAww8/zK5du+jVqxdDhgzJ9+twNXz//fecOXOGVq1aUapUKZc6Hx8fOnToAMDSpX9MqHX8uX379vj4uH7kKFWqFK1ateL06dN8//33Vzh6ERERkWuTtzc6SLfWxhljtgMJwPfGmLuttT95eRwRESHrF9+HH36YxMRE9u7dS1hYGAATJ04kKCiIbt26MWrUKLfrrLU88sgjHD9+nGnTptGzZ09n3ccff8zDDz9Mr169SE5Odv6yPXjwYLZv386AAQNckjRPPfUULVq08BhfbGwsu3btYsaMGTz88MPO8qNHj3LXXXfRr18/7r33Xm6++eZ83X90dDQHDx50SyL88MMPtGrViueff54FCxbkub+VK1eyadMmwsPDARg9ejQPPvggn332Ga+++qpbYmXfvn00bNiQxYsXExgYCEB8fDy1atXijTfeYPDgwfj5+Tnbb968merVq7v0kZaWRseOHRkzZgx9+/alUqVKzrqkpCSSkpLyHD9Q4BO8cvLTT1n/lNeqVctjfc2aNQH43//+d1nXLFq0iP/973/ExMR4M1wRERGR68IV2Z3SWjvcGPMr8AGwyhjTxVqbdCXGEhG50fXp04cPPviASZMmMXToUHbt2sXixYv5+9//TokSJTxes2rVKrZt20aLFi1cEjYADz30EG+//TYrVqxgxYoVREVFkZ6ezvTp0ylVqpRbUqBp06b07NnTbZnUDz/8wLJly+jatatLwgagdOnSDBs2jPvuu49PP/3U414oeREaGuqxvGHDhkRHR7No0SLS09NdEie56d+/vzNhA1kzSF599VXmzJnDpEmTPM6GmTBhgjNh44ipc+fOTJ06lZ9++okGDRo46y5O2AAUL16cJ598km+//ZYlS5bwyCOPOOuSkpIYNmxYnmJ3uFJJm2PHjgEQHBzssd5RfvTo0QJdIyIiIiJ/uGJHilhrpxtj9gKfA18bY/52pcYSEbmR3XHHHdx6661MmjSJl156iffff5/MzMxcl0Zt2LAByJqp4kl0dDQrVqxg48aNREVFsW3bNk6fPs2dd97p8Rfwu+66yy1ps3r1aiDrF3dPiYTff/8dgK1bt+bpPnPy1Vdf8a9//Yt169Zx+PBht02ZDx8+TMWKFfPUV5s2bdzKqlWrRuXKldm5cydHjx6ldOnSzrrg4GBq1Kjhdk3lypUB3I603r17N2PHjmXJkiXs3r2bM2fOuNT/9ttvLs8TEhKuWBJGRERERIq+K3oOrLV2mTGmJfAVMBlwPwtUREQKrE+fPvTr148FCxY4j19u3Lhxju0dMyBySmY4yh0zIBztc1rGVKFCBbcyx1HOixcvZvHixTnGcvLkyRzrLmX8+PEMGDCAMmXK0K5dO6pUqUKJEiUwxjBnzhx++OEHzp07l+f+cru/Xbt2cezYMZekTfY/Z+c4Zv38+fPOsu3bt9OsWTOOHDnCnXfeSfv27QkODqZYsWLs3LmTKVOmXFasV5sjWef4WbiYo/zipNblXiMiIiIif7iiSRsAa+02Y8wdwDzgDsD9OA0RESmQXr168dxzz9G3b19+++03hg4dmmt7xy/TBw4c8Fi/f/9+l3aOrwcPHvTY3lM/jmvGjx9Pv3798nAXlycjI4OEhAQqVKjAhg0b3BJQjpk+l+PgwYPUrl3brdxxfzkt88mL119/nZSUFCZPnkxcXJxL3YwZMzyewlWU9rRxvC7Z96zJznHiWPb9a/JzjYiIiIj8wZtJm7bATk8V1trDxpi7gP5AoKc2IiKSf6VLl6Zr167OI5m7d++ea3vHLJycEgKOU38iIyMBqFOnDiVKlGDTpk0cO3bMLXnhqZ/mzZsD8N13312RpM3hw4c5evQoDzzwgFvC5uTJk84lYJdj2bJlREVFuZRt376dPXv2EBERUaAZIb/88gsAXbp08TiuJ0VpT5vmzZsTGBjIypUrOXHihMvmz5mZmc4j3Nu2bessd/x50aJFZGZmupwgdeLECVauXEmJEiWcPysiIiIi4sprR35ba5dZa3flUn/OWvuKtfbyPn2KiEiejBw5ks8//5yFCxe6naZ0sVatWlG7dm1WrFjB7NmzXepmz57Nd999R61atWjdujUAfn5+9OzZkxMnTrglBdatW8f06dPdxmjatCl33nknn332GZMmTfIYx3//+18OHTp0GXf5h9DQUEqUKMH69etdllilp6fTv39/Dh8+fNl9jh8/nl27/vinLDMzk0GDBpGZmcmjjz6arzgdIiIiAPcE18KFC92ObHdISEjAWntZD2/Ytm0b27Ztcym76aab6NWrF6dOnXL7GXj77bfZuXMnHTp0oFq1as7y6tWr0759e3bu3Mk777zjck18fDynTp2iV69elCxZ0itxi4iIiFxvrvjyKBERuTqqVKlClSpV8tTWGMOUKVNo164dDz30EJ07d6ZOnTr89NNPzJkzh1KlSjF16lSXmRGjRo1iyZIlvPnmm6xbt47WrVuzf/9+Pv74Y+6++27mzp3rNs5HH31EdHQ0jz32GBMmTOCOO+6gdOnS7N27lx9//JHNmzezevXqHE+Byo2Pjw/9+vVjzJgx3HrrrXTu3Jm0tDSWLl1Kamoqbdu2dc4YyqtWrVrRqFEjHnroIYKDg1m4cCE//PADTZo04dlnn73sGLN74oknmDx5Mg8++CBdu3bllltuYfPmzXz99dd069aNjz/+uED952TFihXOpJAjufXzzz+7LNFKTEx0uaZu3boAbkmgUaNGkZSUxOuvv86mTZto1qwZW7du5YsvviA0NNQtMQPw7rvv0rJlS/r168eSJUuoW7cu//nPf1i6dCm1atXi5Zdf9uLdioiIiFxf8p20McZ8S9b+NLHW2r0XnueFtdbG5HdcERHxjjvuuIO1a9cycuRIvvnmG+bNm0dISAjdu3dnyJAhbnu7hISEsHLlSgYPHsy8efNYt24dtWvX5r333iMiIsJj0iYsLIz169fz1ltv8emnnzJ9+nTOnz9PhQoVqFevHv/4xz+49dZb830PI0aMoHz58rz//vv83//9H8HBwbRr146RI0cSHx9/2f298cYbfP7550ycOJGdO3dSrlw5+vfvz/DhwwkICMh3nAC33XYbS5cu5aWXXuKrr74iIyODhg0b8tlnn1G6dOkrlrT55Zdf3PbLOXTokEvZxUmbnJQrV47Vq1czbNgw5syZw3fffUe5cuV49NFHGT58OGFhYW7XVK9enXXr1jF06FC+/vpr5s+fT8WKFenfvz/x8fGUKVOmQPcnIiIicj0z+Z1KbYzJJCtpU9da+78Lz/PCWmuL5WvQIsgYsz4yMjJy/fr1hR2KiBQhjmOsHTMWpGiLi4tjypQp7Nixw7mMSa59eX0fNmnShA0bNmyw1ja5GnFdij5biIiIXNu8+dki3zNtrLU+uT0XEREREREREZH8U6JFRERERERERKQIuipJG2NMGWOMjoYQEREREREREckjryVtjDExxphXjDFlspWFGmOWAYeBVGPM694aT0RExFsSExOx1mo/GxEREREpUrw50+YfwAPW2iPZyl4D7gR+BVKA/saYbl4cU0RERERERETkuuTNpE1DYIXjiTEmEOgKLLbW1gJqA3uAvl4cU0RERERERETkuuTNpE0osC/b8zuAACARwFp7AviSrOSNiIiIiIiIiIjkwptJm3NAYLbndwIWWJ6t7DhQ1otjioiIiIiIiIhcl7yZtNkBRGd73gX42Vr7W7ayymRtSiwiIiIiIiIiIrnwZtJmCnCrMeY/xpjvgFuBjy5qcxvwkxfHFBERERERERG5LnkzafMeMBNoCrQia/+asY5KY0wDshI5SV4cU0RERERERETkuuTrrY6stelAD2NM36yn9sRFTQ4AjYGd3hpTREREREREROR65bWkjYO19ngO5YfRfjYiIiIiIiIiInnizeVRIiIiUsQYY7jrrrsu65r09HTi4+OpWbMm/v7+GGOYM2cOO3fuxBhDXFzcFYlVRERERFwpaSMico3bsgUmTICRI7O+btlS2BFJbhISEjDGkJSUVNih5GjcuHEMHz6cW265hWeeeYb4+Hjq1KlTqDHlJ/kkIiIicq3z+vIoERG5OpYsgeHDYfly97qoKBg6FGJirn5ccu378ssvuemmm1i8eDHFixd3lu/cubPwghIRERG5AWmmjYjINeiDD6B9e88JG8gqb98eJk26unHJ9WHfvn2UK1fOJWEjIiIiIlefkjYiIteYJUvgb3+DzMzc22VmQp8+We2LgjVr1vDQQw9RqVIl/P39qVixIu3bt+eTTz5xaffJJ58QFRVFcHAwgYGB3HrrrYwePZpz58659RkREUFERASnTp1i0KBBVKlSBX9/f2rUqMHYsWOx1jrbfv/99xhjuP/++3OMsW7duvj7+5OamupSvnDhQu6++25CQkLw9/enevXqDBo0iKNHj+YY0/Hjx3n66aeJiIjAz8+PhIQEIiIiGDZsGABt27bFGON8ZHf69GlGjx5No0aNKFmyJDfddBMtWrRgxowZHuNOS0tjxIgRVK9eHX9/f6pWrcpLL73k8TXLTVxcHMYYduzYwa5du5yxRUREXPLa/fv38+STTxIREUHx4sUpX748DzzwAOvXr3dre+zYMV599VWio6MJCwtztr/33ntZvXq1S9vExETn67Ns2TKX1ywhIeGy7k9ERETkWqPlUSIi15jhwy+dsHHIzIQRIwp/mdTEiRN5/PHHKVasGPfeey81a9bk0KFDrFu3jnfffZdu3boBMHjwYEaPHk1ISAg9evTgpptuYsGCBQwePJiFCxeyaNEit9kf6enpdOjQgX379tGxY0d8fX2ZM2cOzz//PGfPniU+Ph6A5s2bU7t2bebPn09KSgrlypVz6WfNmjVs27aNLl26ULZsWWf5sGHDSEhIoGzZsnTq1InQ0FB+/PFHXnvtNebPn8/q1asJCgpy6SstLY3o6GhSU1Np3749QUFBVK1alQEDBjBnzhyWLVtGbGysx2TI0aNHiY6OZuPGjURGRtK7d28yMzNZuHAhPXr0YMuWLYwcOdLZ3lpLt27d+OKLL6hevTpPPfUUaWlpTJo0HKRWHwAAIABJREFUif/+97+X9X267777iIiI4M033wRgwIABAJQuXTrX63bs2EHr1q3Zt28f0dHRdO/enT179jBr1iy++uorPv30Uzp16uRsv3XrVl588UWioqL4y1/+QpkyZdi9ezdz585lwYIFzJs3jz//+c8ANGrUiPj4eIYNG0Z4eLjLJsja40ZERESue9ZarzyA7UC/S7R5EtjurTGLwgNYHxkZaUVEsktOTrbJycle73fzZmvh8h+bN3s9lDzbsmWL9fX1tWXKlLGbPQSyZ88ea621q1atsoCtXLmy3b9/v7M+PT3ddurUyQL25Zdfdrk2PDzcArZjx4729OnTzvKDBw/a4OBgGxwcbNPS0pzlo0aNsoB966233OJ44oknLGDnzp3rLPv2228tYFu0aGGPHDni0n7y5MkWsAMGDPAYU0xMjD158qTbOPHx8RawS5cu9fRy2djYWAvYsWPHupSfOXPGdujQwRpj7MaNG53l06dPt4Bt3ry5PXPmjLM8JSXFVqtWzQK2TZs2HsfKSXh4uA0PD3cr37FjhwVsbGysS3n79u0tYEeOHOlSvnLlSlusWDFbtmxZe+LECWf50aNH7e+//+7W/549e2zFihVtnTp13Orycx95fR9GRkZaYL0tAp8rrD5biIiIXPO8+dnCm8ujIoDc/ysuqz7ci2OKiNxQ8rvUqTCXSL333ntkZGQwZMgQ6tev71YfFhYGwKQLG/C89NJLVKhQwVnv6+vLuHHj8PHx4f333/c4xoQJEwgMDHQ+Dw0NpXPnzhw7doyffvrJWd6rVy98fHyYMmWKy/VpaWnMnDmT0NBQOnbs6NIvZM0Uuni2SVxcHI0aNWL69OkeYxo3bhwlS5b0WJeTlJQUpk2bRtOmTXn22Wdd6gICApxLvj766CNn+eTJkwEYNWoUAQEBzvKyZcsyZMiQyxo/P/bu3cuiRYuoUqWKW8wtW7ake/fupKam8tlnnznLg4ODCQkJcesrLCyMrl27sm3bNnbv3n3FYxcREREp6q728qhSQNpVHlNE5Lpx/PjVvc4bvv/+ewCXZIgnGzZsACA6OtqtrlatWoSFhbFjxw6OHTtGcHCwsy44OJgaNWq4XVO5cmUAjhw54iwLCwsjJiaGxYsXk5ycTL169QCYN28eqampDBw4EF/fP/5pXL16NX5+fsyaNYtZs2a5jZGWlsbvv//uttwqICCA2267Ldf79WTt2rWcP38+x/1a0tPTgazlRQ4bNmzAx8eH1q1bu7X3tHxo06ZNzJkzx6WsdOnSzqVQl2vjxo0A3Hnnnfj5+bnVR0dHM23aNDZu3MgjjzziLF+5ciXjx49n9erVHDp0iLQ0148Hv/32G1WqVMlXTCIiIiLXiwIlbYwxF3+aKu2hDKAYUAXoQtYyKhERyYeLtk654td5g2Oz3kqVKuXa7tixYwBUrFjRY33FihXZvXs3R48edUna5LTfiiP5cv78eZfyuLg4Fi9ezJQpUxg7diyAc+ZNbGysS9uUlBQyMjKcmwfn5OTJky5Jm9DQULfNhfMiJSUFyErerF27NtfxHI4dO0bZsmU9Jkyyz1hy2LRpk9v9hIeH5ztpk5fvG+CyafPnn39O165dCQgIoF27dlSvXp2SJUvi4+NDUlISy5Ytu+xNlEVERESuRwWdabMTsNme97/wyIkBni7gmCIiN6z8bihcmBsRO5Iqv/32G3Xq1MmxnSMRc+DAAapXr+5Wv3//fpd2+XX//fcTFBTEtGnTGDVqFCkpKSxYsICGDRvSsGFDt5gyMzPdTpO6lPwkbBzjAQwcOJDXX389z9ekpqaSnp7ulrg5cOCAW/u4uDiXzXwLKvv3zRNP37chQ4ZQvHhx1q1bR926dV3a//3vf2fZsmVei09ERETkWlbQPW2mXnh8eOH5j9nKsj8mA68Bf7bWvlnAMUVEblj160NU1OVd06ZN1nWFpXnz5gAsWLAg13aNGzcGICkpya3ul19+Ye/evVStWvWSJxldSmBgIN26dWPfvn188803fPTRR2RkZLjNsnHEfuTIEbZs2VKgMbMrVqwY4D4DCKBZs2b4+Pjw3Xff5bm/yMhIMjMzWbFihVudp9fS2xzftxUrVpCRkeFWv3TpUiArTodffvmFevXquSVscroPAB8fH4+vmYiIiMj1rEBJG2ttnLX2UWtt3IWizy88v/jxV2vtc9baRQUPWUTkxjZ0KPjk8W9vHx+4CnvR5urxxx/H19eXESNGkJyc7Fa/d+9eAHr37g3AyJEj+f33353158+f55lnniEzM5PHHnvMKzE5ZppMnTqVqVOn4uvrS8+ePd3aDRw4EIA+ffqwb98+t/pTp0459+zJK8cyKk8b7YaGhtKzZ0/WrVvHiBEjPCYpfv31V3bs2OF8/uijjwLw4osvcvbsWWd5amqqy9HgV0pYWBjt2rVj586dzqPCHf7zn//w0UcfUaZMGe6//35neUREBD///LPLa2qtJSEhwePPCGS9bnv27LkyNyEiIiJSRHltI2JrrTdPohIRkRzExMC//w1/+xtkZubczscHJk4s3KVRAPXq1ePdd9+lb9++NG7cmM6dO1OzZk1SUlJYu3YtQUFBLF26lJYtW/Lss8/yyiuv0KBBA7p27UrJkiVZsGABmzdvpnXr1gwaNMgrMbVq1YoaNWowa9Ys0tPTueeeewgNDXVrFxMTw5gxY3jhhReoWbMmd999N1WrVuXkyZPs2rWLZcuW0bp1a77++us8j922bVt8fHx44YUX2Lx5M2XKlAGyTs0CePvtt/n5558ZOnQoH374Ia1bt+bmm29m3759bN26lbVr1zJjxgyqVq0KQPfu3fn444+ZO3cuDRo0oHPnzqSnpzN79mxuv/12fv31Vy+8Yrn717/+RatWrRg0aBCLFi2iadOm7Nmzh1mzZuHj48PkyZMpVaqUs/3AgQOdPw9dunTBz8+PlStXkpyczD333MO8efPcxoiJiWHmzJncc889REZG4ufnR1RUFFGXO/VMRERE5FrijXPDL34AdYD7gV5Xov+i9ADWR0ZGejybXURuXMnJyTY5OfmKjvHNN9a2aWMtuD/atMmqL0pWrVplH3jgAVu+fHnr5+dnK1asaDt06GBnzZrl0m7GjBm2VatW9qabbrL+/v62Xr16duTIkfbMmTNufYaHh9vw8HCP48XHx1vALl261GP9iBEjLFn7stnZs2fnGvt3331nH3zwQVuxYkXr5+dnQ0JCbMOGDe3AgQPt2rVr8xyTw4cffmgbNmxoAwICnDFkd+7cOfvWW2/ZFi1a2KCgIFu8eHFbuXJlGx0dbd944w17+PBht/bDhg2zVatWtcWLF7fh4eF28ODB9uzZsxawbdq0yTWei+V0Dzt27LCAjY2Ndavbu3ev7du3r61SpYr18/Oz5cqVs507d7Zr1qzxOMbkyZNtw4YNbYkSJWy5cuXsfffdZ3/88cccv28HDx603bt3t6GhodbHx8cCNj4+Ptf7yOv7MDIy0gLrbRH4XGH12UJEROSa583PFsZai7cYYxoB7wONsyWFil2oawMsAB6y1rr/F9o1yhizPjIyMnL9+vWFHYqIFCGOI5kv3rPjStiyBZYsyTrWOygoa2ZNYe5hI1JU5PV92KRJEzZs2LDBWtvkasR1KfpsISIicm3z5mcLry2PMsbUApLIOt57PFAL6JityXIgFegKXDdJGxGRwla/vpI0IiIiIiLXI2/uQxMPFAfusNY+DazNXmmzpvSsBm4vyCDGmK7GmLeMMd8ZY44bY6wxZloObSMu1Of0mFmQWERERERERERErhSvzbQBYoDPrLWej33IsgdoV8BxXgIaAieBvWTtn3MpPwBzPJRvLmAsIiIiIiIiIiJXhDeTNmXISqLkxpA1G6cgBl4Y5xegDbA0D9dsstYmFHBcEREREREREZGrxptJm4NAjUu0qU/WbJt8s9Y6kzTGmIJ0JSIiIiIiIiJSZHkzafMt0N0YU9ta+9PFlcaY28laQvWOF8fMq1uMMX8HygEpwGpr7Y+FEIeIiIiIiIiISJ54M2kzGngQWG6MSQBuATDG1AeiyNqo+ATwmhfHzKt2XLSXjjEmCYi11u7OSwfGmJzO3czLnjoiIiIiLvTZQkRERC7Fa6dHXZhd04WsPWveBv5K1h42P5I1u6Y48EBekyRechoYATQha8+dMvyxD85dwBJjTMmrGI+IiIiIiIiISJ54c6YN1tqvjTFVgVigOVnLkY4B3wOTrbWp3hwvD/EcAoZeVLzcGNMeWAHcQVZyaXwe+mriqfzC/5JFFjBUERERucHos4WIiIhcileTNgDW2qNkJUEumQgpLNbaDGPM+2QlbaIowrGKiIiIiIiIyI3Ja8ujcmOMKVMElyH9fuFrUYtLRERERERERMR7SRtjTIwx5hVjTJlsZaHGmGXAYSDVGPO6t8bzguYXvm4v1ChERERERERERDzw5kybf5C10fCRbGWvAXcCv5J11HZ/Y0w3L46ZK2NMpDHG7R6NMTHAwAtPp12teERERERERERE8sqbSZuGZG3uC4AxJhDoCiy21tYCagN7gL4FGcQYc58xJtEYkwg8f6G4haPMGJP9SPHXgT3GmFnGmDcuPJYA3wD+wBBr7aqCxCMiIlKUGWO466678tw+KSkJYwwJCQlXLCYRERERyRtvJm1CgX3Znt8BBACJANbaE8CXZCVvCqIRWadTxQIdLpRVy1bWNVvbD4GNwO1AH+AJoCbwCRBlrR1ZwFhERArdlkNbmPCfCYxcPpIJ/5nAlkNbCjskyUVCQgLGGJKSkgo7lCsuLwmjr776ivbt2xMWFkZgYCDVqlXjwQcfZPXq1VcnSBEREZEizJunR50DArM9vxOwwPJsZceBsgUZxFqbACTkse0HwAcFGU9EpKhasn0Jw5cPZ/mu5W51UeFRDI0aSky1mEKITK5lzZo1Y+vWrYSEhFzxsZ577jleeeUVypUrx3333UdISAi//PILX3zxBZ9++ilTp07l//2//3fF4xAREREpqryZtNkBRGd73gX42Vr7W7ayymRtSiwiIgXwwYYP+NuXfyPTZnqsX75rOe2ntWfiPRPp3bj3VY5OrmUlSpSgTp06V3ycAwcO8Nprr3HzzTfz448/Ehoa6qxbunQp0dHRDB06VEkbERERuaF5c3nUFOBWY8x/jDHfAbcCH13U5jbgJy+OKSJyw1myfUmuCRuHTJtJn3l9WLJ9yVWKLHdr1qzhoYceolKlSvj7+1OxYkXat2/PJ5984tLuk08+ISoqiuDgYAIDA7n11lsZPXo0586dc+szIiKCiIgITp06xaBBg6hSpQr+/v7UqFGDsWPHYq11tv3+++8xxnD//ffnGGPdunXx9/cnNTXVpXzhwoXcfffdhISE4O/vT/Xq1Rk0aBBHjx7NMabjx4/z9NNPExERgZ+fHwkJCURERDBs2DAA2rZtizHG+cju9OnTjB49mkaNGlGyZEluuukmWrRowYwZMzzGnZaWxogRI6hevTr+/v5UrVqVl156yeNrdik57Wlz1113YYwhIyODUaNGUbNmTfz9/alcuTLPPfccaWlpzraJiYnOe1q2bJnLfTr63bVrF5mZmdxxxx0uCRvHa1OqVCl+//33y45fRERE5HrizZk275F1jPZDgAHmAWMdlcaYBmQlcoZ6cUwRkRvO8OXDL5mwcci0mYxYPqLQl0lNnDiRxx9/nGLFinHvvfdSs2ZNDh06xLp163j33Xfp1i3rYMHBgwczevRoQkJC6NGjBzfddBMLFixg8ODBLFy4kEWLFlG8eHGXvtPT0+nQoQP79u2jY8eO+Pr6MmfOHJ5//nnOnj1LfHw8AM2bN6d27drMnz+flJQUypUr59LPmjVr2LZtG126dKFs2T9W8g4bNoyEhATKli1Lp06dCA0N5ccff+S1115j/vz5rF69mqCgIJe+0tLSiI6OJjU1lfbt2xMUFETVqlUZMGAAc+bMYdmyZcTGxhIREeH2Wh09epTo6Gg2btxIZGQkvXv3JjMzk4ULF9KjRw+2bNnCyJF/bMlmraVbt2588cUXVK9enaeeeoq0tDQmTZrEf//73wJ93zzp0aMH3333HR07diQoKIj58+fzyiuvcOjQISZPngxAo0aNiI+PZ9iwYYSHhxMXF+e83rHHTc2aNSlevDhr1qzh8OHDLsuxli9fzokTJ7jvvvu8Hr+IiIjINcVa69UHEASU8lAeQtYJU8HeHrMwH8D6yMhIKyKSXXJysk1OTvZ6v5sPbrYkcNmPzQc3ez2WvNqyZYv19fW1ZcqUsZs3u8exZ88ea621q1atsoCtXLmy3b9/v7M+PT3ddurUyQL25Zdfdrk2PDzcArZjx4729OnTzvKDBw/a4OBgGxwcbNPS0pzlo0aNsoB966233OJ44oknLGDnzp3rLPv2228tYFu0aGGPHDni0n7y5MkWsAMGDPAYU0xMjD158qTbOPHx8RawS5cu9fRy2djYWAvYsWPHupSfOXPGdujQwRpj7MaNG53l06dPt4Bt3ry5PXPmjLM8JSXFVqtWzQK2TZs2HsfyZOnSpRaw8fHxLuVt2rSxgI2MjLQpKSnO8pMnT9rq1atbHx8fl++btfaSY7/xxhvWGGPLly9v+/TpY59//nn74IMPWn9/f9uuXTt78ODBPMftSV7fh5GRkRZYb4vA5wqrzxYiIiLXPG9+tvDm8ihHEui4zTop6uLyw9baH6y1x7w9pojIjWLJjvwtdcrvdd7w3nvvkZGRwZAhQ6hfv75bfVhYGACTJk0C4KWXXqJChQrOel9fX8aNG4ePjw/vv/++xzEmTJhAYOAfe+GHhobSuXNnjh07xk8//bEqt1evXvj4+DBlyhSX69PS0pg5cyahoaF07NjRpV/ImilUunRpl2vi4uJo1KgR06dP9xjTuHHjKFmypMe6nKSkpDBt2jSaNm3Ks88+61IXEBDgXPL10Ud/rD52zG4ZNWoUAQEBzvKyZcsyZMiQyxo/L8aOHesyE6lkyZL07NmTzMxM1q1bd1l9DRgwgM8++4yMjAwmTpzImDFjmDVrFpUrVyYuLs5t2ZSIiIjIjcaby6NEROQKO37u+FW9zhu+//57AJdkiCcbNmwAIDo62q2uVq1ahIWFsWPHDo4dO0ZwcLCzLjg4mBo1arhdU7lyZQCOHDniLAsLCyMmJobFixeTnJxMvXr1AJg3bx6pqakMHDgQX98//mlcvXo1fn5+zJo1i1mzZrmNkZaWxu+//+623CogIIDbbrst1/v1ZO3atZw/f97jnjKQtRQMYOvWrc6yDRs24OPjQ+vWrd3aezpue9OmTcyZM8elrHTp0gwYMCBPMTZt2tStzNNrnRevvPIKgwcPpl+/fjz11FNUqFCBbdu28cILL9CzZ082bdrEK6+8cll9ioiIiFxPlLQREbmGBPkHXbqRF6/zBsdmvZUqVcq13bFjWRMxK1as6LG+YsWK7N69m6NHj7okbS6eAePgSL6cP3/epTwuLo7FixczZcoUxo7N2nrNMfMmNjbWpW1KSgoZGRnOzYNzcvLkSZekTWhoqNvmwnmRkpICZCVv1q5dm+t4DseOHaNs2bL4+fm5tcs+Y8lh06ZNbvcTHh6e56SNp9c7p9c6N0lJSTz33HPcf//9vP76687yyMhIPv/8c2rVqsW4cePo27cv1apVy3O/IiIiItcTry+PEhGRKyemav42FM7vdd7g+CX/t99+y7WdIxFz4MABj/X79+93aZdf999/P0FBQUybNo3z589z6NAhFixYQMOGDWnYsKFbTGXKlLnkWuPw8HCX6/KTsHGMBzBw4MBcx1u6dKnLNampqc5ZONl5ei3j4uLc+tu5c2e+4i2IL7/8Esg6KepiJUqUoFmzZmRmZrJx48arHZqIiIhIkaGkjYjINaR+aH2iwqMu65o24W2oH+q+l8zV0rx5cwAWLFiQa7vGjRsDWTMwLvbLL7+wd+9eqlatmuPMmrwKDAykW7du7Nu3j2+++YaPPvqIjIwMt1k2jtiPHDnCli1bCjRmdsWKFQM8z0pp1qwZPj4+fPfdd3nuLzIykszMTFasWOFW5+m1vJp8fHxynH3jOI48p2O9HeUXnxYmIiIiciNR0kZE5BozNGooPiZvf337GB+GRHl/M9rL8fjjj+Pr68uIESNITk52q9+7dy8AvXv3BmDkyJEuv8ifP3+eZ555hszMTB577DGvxOQ4gnrq1KlMnToVX19fevbs6dZu4MCBAPTp04d9+/a51Z86dcq5Z09eOZZR7d69260uNDSUnj17sm7dOkaMGOEx4fHrr7+yY8cO5/NHH30UgBdffJGzZ886y1NTU12OBi8M5cqVY8+ePR7r7rzzTgD+/e9/u83CWrBgAStXriQgIICWLVte8ThFREREiirtaSMico2JqRbDvzv9m799+TcybWaO7XyMDxPvmUhMtcJbGgVQr1493n33Xfr27Uvjxo3p3LkzNWvWJCUlhbVr1xIUFMTSpUtp2bIlzz77LK+88goNGjSga9eulCxZkgULFrB582Zat27NoEGDvBJTq1atqFGjBrNmzSI9PZ177rnH40lFMTExjBkzhhdeeIGaNWty9913U7VqVU6ePMmuXbtYtmwZrVu35uuvv87z2G3btsXHx4cXXniBzZs3U6ZMGSDr1CyAt99+m59//pmhQ4fy4Ycf0rp1a26++Wb27dvH1q1bWbt2LTNmzKBq1aoAdO/enY8//pi5c+fSoEEDOnfuTHp6OrNnz+b222/n119/9cIrlj8xMTHMnDmTe+65h8jISPz8/IiKiiIqKoquXbvypz/9iW+++Ya6dety//33U6FCBbZu3cqXX36JtZYxY8a47BUkIiIicqPxWtLGGLMdeNNaOyGXNk8C/7TWakdBEZECeCzyMSJKRzBi+QiW7VrmVt8mvA1DooYUesLGoU+fPjRo0IDXXnuNpKQk5syZQ0hICLfddht//etfne3Gjh1L48aNefvtt5k6dSrp6elUr16dkSNH8s9//tOrS2ViY2OdR2J7Whrl8Nxzz9GqVSsmTJjAihUr+OKLLwgODqZSpUr87W9/o0ePHpc1bt26dZkyZQqvvfYa7777rnN2jCNpExQUxLJly/j3v//NRx99xKeffsrZs2e5+eabqVmzJm+88Qbt2rVz9meMYdasWYwZM4bExETefvttKlasyKOPPsrQoUNdjgG/2saPH48xhiVLljB//nwyMzOJj48nKioKHx8f5s+fzzvvvMPMmTP5/PPPOX36NGXLluXuu++mX79+tG/fvtBiFxERESkKjLXWOx0ZkwkkWGuH59LmRWC4tbaYVwYtAowx6yMjIyPXr19f2KGISBHiOJK5bt26V3ysLYe2sGTHEo6fO06QfxAxVWMKdQ8bkaIir+/DJk2asGHDhg3W2iZXI65L0WcLERGRa5s3P1tc7eVRpYC0qzymiMh1rX5ofSVpRERERESuQwVK2hhjqlxUVNpDGUAxoArQBdhekDFFRERERERERG4EBZ1psxPIvr6q/4VHTgzwdAHHFBERERERERG57hU0aTOVrKSNAR4BfgQ2eWh3HkgBllhrFxVwTBERERERERGR616BkjbW2jjHn40xjwCf57YRsYiIiIiIiIiI5I3XNiK21vp4qy8RERERERERkRudEi0iIiIiIiIiIkWQV4/8NsaUBXoDzYAyZJ0adTFrrY3x5rgiIiIiIiIiItcbryVtjDF1gCSgPFkbE+fE5lInIiIiIiIiIiJ4d3nUa0AoMBaoBvhZa308PDzNvhERERERERERkWy8uTzqTuAra+1gL/YpIiIiIiIiInJD8uZMGwMke7E/EREREREREZEbljeTNuuB2l7sT0RERERERETkhuXNpM1w4G5jzF1e7FNERG5AixYtomXLlpQuXRpjDPfdd5+zbt26dbRr146QkBCMMTRq1AiAuLg4jDHs3LkzX2Pu3LkTYwxxcXEFij0pKQljDAkJCZd13YEDB4iNjSUsLIxixYphjOHo0aMkJiZijCExMbFAcYmIiIjItcebe9pUBr4AFhljZpA18+aop4bW2qleHFdERK4jO3fupHPnzpQuXZrevXsTFBREnTp1ADh+/Dh/+ctfOHv2LL169SIkJIQKFSoUcsTeERcXx6JFi+jevTs1atTAGENAQEChxZOUlETbtm2Jj4+/7ASUiIiIiHiHN5M2iWQd522AXhceFx/vbS6UKWkjIuJFxmR9tRf/rXsN+uabbzh79izjxo2jR48eLnVr1qzh0KFDvPzyywwe7Lrv/ejRo3n++eepVKlSvsatVKkSW7duJTg4ON+x51daWhqLFy/mT3/6E9OnT7/q44uIiIhI0eTNpM2jXuxLRERuUPv27QPglltuuay6ihUrUrFixXyP6+fn55zRc7UdOHCAzMxMj/clIiIiIjcur+1pY62dkteHt8YUEZFrxyeffEJUVBTBwcEEBgZy6623Mnr0aM6dOwf8sRdMfHw8AG3btsUY49zPxRhDbGwsAI8++qhLHeS+p82aNWt46KGHqFSpEv7+/v+fvTsPr7K69zZ+r4AgIgEVFUQMYB2DCtgRNQFTox6reNRayqlCpVDs6VGrHSzKYLC19mirtg5lpqWXWlvFquWAzStEUauC2Bps1QZQVKSKDKKgmPX+EZISMidPsneS+3NduWKeaa29TXTlm7V+i969e5Ofn8/vfve7imtqqmnz8ssvc/XVV/PpT3+aAw88kM6dO5OVlcX48eNZt25dk9+Xfv36kZWVBcC8efMqXld9aussX76c888/n4MOOqiiX9/61rd46623qlzbkNcxZswYhg8fDsB1111X0acQAkuWLGnya5YkSVL9JDnTRpKkak2cOJEbbriBnj17MmrUKPbdd18WLlzIxIkTWbRoEYsXL6Zfv35MmTKFJUsnOQvwAAAgAElEQVSWsHTpUkaPHk2/fv0AGDRoEFOmTGHlypU8+OCDjBgxoqIAcfnnmsyYMYNLL72UDh06cM4553DEEUewYcMGnnvuOe644w4uvPDCWu+///77ueuuuxg+fDhDhw6lU6dOFBcXM3PmTB566CGee+65Ri/JArjiiitYs2YNt956KyeccEJF0eW6XtfDDz/M+eefT4yRCy64gKysLJYvX86dd97Jgw8+yBNPPEH//v0b9TrK+zBv3jxyc3MZNmxYxXPK/51IkiSpBcQYE/0ADgQmALcCM/c4/lmgS9JtpvIDWD5kyJAoSbtbtWpVXLVqVYu1V1bNpsWaa5Ann3wyArFv377xrbfeqjj+8ccfxy996UsRiD/60Y8qjk+ZMiUC8bHHHqvyrDlz5kQgzpkzp8q50aNHRyCuXr264lhxcXHs2LFj3G+//eKLL75Y5Z7XX3+94p9Xr14dgTh69OhK16xbty5u3769yr2LFi2KGRkZccKECZWOP/bYYxGIU6ZMqXJPTWpqO8bqX/PWrVvj/vvvHzMyMmJRUVGl63/yk59EIJ522mkt/jrSTX1/DocMGRKB5TENxhXRsYUkSa1ekmOLJLf8JoQwFlgD3A78D5Xr3BwMPAWMqnqnJKm+Qqj6UZ9zqTJ79mwArr322ko7PXXs2JGbb76ZjIwMZs6c2Sxt33nnnezcuZNJkyaRnZ1d5fyhhx5a5zPKl1TtKT8/n+zsbBYtWpRIXxviwQcfZOPGjXzlK1/hlFNOqXTuqquuol+/fjz66KO89tprFcfT8XVIkiSpdoktjwohnAZMB/4KTAFOp2zGDQAxxhdDCMXAucCspNqVJKW3FStWAHDqqadWOXfkkUdy6KGHsnr1ajZv3pz4zk1PP/00AGeeeWajnxFj5Le//S1z587lhRde4L333uOTTz6pON+pU6c6n7FgwQJWrlxZ6digQYMqliE1VG3vaceOHcnJyWHNmjU8//zzHHbYYYm9DkmSJLWsJGva/AB4C8iNMW4JIQyu5pq/Al9IsE1JandiNdt6p/OW35s3bwaocWen3r1789prr7Fp06bEQ5tNmzYBNKnmzJVXXsktt9xC7969Of300+nTpw9dunQBYO7cuaxdu7bOZyxYsIB58yrX4R89enSjQ5v6vKfw79ef1OuQJElSy0oytPk0cE+McUst16wDetVyXpLUxpQHMevXr+fwww+vcr58p6OkAxuAHj16APDGG280ajvvDRs2cNtttzFw4ECefPJJunXrVun83XffXa/nzJ07t2KXqyTs/p5WZ8/3NKnXIUmSpJaVZE2bTsC2Oq7pAXxSxzWSpDZk8OCyiZfVbRX96quvsm7dOvr3718RsCTp85//PAALFy5s1P0lJSWUlpaSn59fJehYt24dJSUlTe5jY9T2nu7cuZPHH38cgCFDhgCNex0dOnQAqLSESpIkSS0rydBmDXBiHdd8DvhHgm1KktLcJZdcAsD111/Pv/71r4rjn3zyCd/97ncpLS1l7NixzdL2pZdeSseOHZk2bRqrVq2qcn7dunW13l++vfUTTzxRKbx4//33GTduHDt37ky0v/V17rnnsv/++3P33XdX1O0pd8stt7B69Wq++MUvVtSzaczrOOCAAwAqFTOWJElSy0pyedSDwPdDCF+OMd6358kQwteB44FrEmxTkpTmhg4dyve//31++tOfMnDgQC644AK6du3KwoULefHFFzn55JP53ve+1yxtH3vssdxxxx1MmDCBwYMHM2LECI444gjeffddnn32WTIzM3nsscdqvL9Xr16MHDmSe+65h0GDBpGfn8/mzZt59NFH2XvvvRk0aFCVAsMtYd9992X27Nl8+ctfJjc3ly9/+cscdthhLF++nMWLF9OrVy9+9atfNel1HHXUUfTp04d77rmHvfbai6ysLEIIXHTRRWRlZbX0S5YkSWqXkgxtfgqMBO4OIVwAdAcIIXwbOAU4D3gF+EWCbUqSSM8CxLu78cYbGTx4ML/85S/59a9/zccff8zhhx/O9ddfz1VXXdWsOxeNGzeOgQMHctNNN7FkyRIWLFhAz549Of744/nGN75R5/2zZs1iwIAB3Hvvvdx+++0ceOCBnHPOORQUFHD++ec3W7/rMmLECJYtW8aPf/xjFi1axObNm+nVqxcTJkxg0qRJHHLIIZWub+jr6NChAw888ABXX3019913H1u3biXGyMknn2xoI0mS1EJCTHCkH0I4DPg1kFPN6ceBUTHGNxJrMA2EEJYPGTJkyPLly1PdFUlp5KWXXgLgmGOOSXFPpParvj+HJ554IitWrFgRY6xrmXeLcGwhSVLrluTYIsmZNsQYXwOGhRCOp2xr7wOAzcDTMUZHHpIkSZIkSfWUaGhTLsb4V+CvzfFsSZIkSZKk9iDJ3aMkSZIkSZKUkEbPtAkhTG7krTHGOK2x7UqSJEmSJLUHTVkeNbWaY7tXNQ7VHA+7/tnQRpIkSZIkqRZNCW2GV3PsO8B/AL8FlgDrgV67rh0FPALc0oQ2JUmSJEmS2oVGhzYxxqW7fx1CuBg4Dfh8jHHFHpfPCyH8EigC7m9sm5IkSZIkSe1FkoWIvwPcW01gA0CM8Tngd7uukyRJkiRJUi2SDG2OAt6q45o3d10nSZIkSZKkWiQZ2mwBTqrjmpOB9xNsU5IkSZIkqU1KMrR5BDglhHBTCKHb7idCCN1CCDdTFuo8lGCbkiRJkiRJbVJTdo/a0w+BYZTVrPlGCGEl8DZwMDAIyARKgIkJtilJkiRJktQmJTbTJsa4AfgsMIuyMCgH+PKuzx2BGcDndl0nSZIkSZKkWiS5PIoY47sxxvFAD+B44JRdn3vEGL8ZY3w3yfYkSW3T4sWLGTp0KD169CCEwLnnnltx7rnnnuO0006jZ8+ehBAYNGgQAGPGjCGEwJo1axrV5po1awghMGbMmCb1fcmSJYQQmDp1ar3vmTp1KiEElixZ0qS2JUmS1LYkuTyqQoxxJ/BiczxbktS2rVmzhhEjRtCjRw8uueQSMjMzOfroowHYsmULZ511Ftu3b+eiiy6iZ8+e9OrVK8U9Th9Llixh+PDhTJkypcbQaMeOHcycOZN58+ZRUlLC9u3b6du3L6eddhpXXXUVWVlZLdtpSZIk1ahZQhtJUssK1wUA4pSY4p403Z///Ge2b9/OzTffzKhRoyqde+aZZ9iwYQM/+tGPmDixcom0G264gauvvpo+ffo0qt0+ffrw0ksv0b1790b3vbG+/e1vM3LkSA477LBmbWfnzp3k5eWxbNkyjj76aL761a/SuXNnnn32WX7xi1/w61//mieffJJjjz22WfshSZKUDkLZEJqYxkPoRoc2IYT/B0RgdIxx3a6v6yPGGPMa264kqW178803ATjkkEMadK5379707t270e3utddeFTN6WlrPnj3p2bNns7fzwAMPsGzZMvLy8li8eDEZGf9eJT1lyhQKCgq46aabmD17drP3RZIkSXVrSk2bYbs+9tnj6/p8SJLamd/97nfk5OTQvXt3unTpwnHHHccNN9zAjh07gH/XgpkyZQoAw4cPJ4RACIG5c+cSQmD06NEAfP3rX690DmqvafPMM8/wla98hT59+tC5c2d69+5Nfn4+v/vd7yquqammzcsvv8zVV1/Npz/9aQ488EA6d+5MVlYW48ePZ926dYm8NzXVtAkhMGzYMN555x3Gjx9P79696dy5M9nZ2cyZM6fStWPGjGH48OEAXHfddRXvz+7PLSkpAeCss86qFNgAjBgxAoB//etfibwmSZIkNV2jZ9rEGDNq+1qSpHITJ07khhtuoGfPnowaNYp9992XhQsXMnHiRBYtWsTixYvp168fU6ZMYcmSJSxdupTRo0fTr18/AAYNGsSUKVNYuXIlDz74ICNGjKgoQFz+uSYzZszg0ksvpUOHDpxzzjkcccQRbNiwgeeee4477riDCy+8sNb777//fu666y6GDx/O0KFD6dSpE8XFxcycOZOHHnqI5557rtFLsupj06ZNnHTSSXTq1IkLLriAHTt2cN9993HJJZeQkZFREWSVF2ueN28eubm5DBs2rOIZ5e9jdnY2AAsXLuTyyy+vFNw8/PDDAHzxi19sttciSZKULoqL//3Pt90GeXmwa6iUVqxpI0lqVk899RQ33HADffv25ZlnnqkoHHzDDTfwn//5nzz88MPcdNNNTJw4kalTpzJ16lSWLl3KmDFjKgUPgwYNYu7cuTz44IOce+659drladWqVXzrW98iMzOTxx9/vCK0KFefmTIXXXQR3/nOd+jcuXOl44sXL+bMM8/k+uuv584776z7jWikF154gbFjx/KrX/2KDh06AHDFFVdw/PHHc+ONN1YKbXr06MG8efMYNmxYtYWIzzrrLM477zzuv/9+jjvuOL74xS/SqVMnli9fzhNPPMH//M//8N///d/N9lokSZJSrbAQCgqgqOjfxy6/vOxzTg5MnlwW4KSLFgltQgj7AR/FGLe1RHuS1JaVFx2u77lUFycur49y7bXXVtrpqWPHjtx888386U9/YubMmVUKCyfhzjvvZOfOnUyaNKlKYANw6KGH1vmMmmbR5Ofnk52dzaJFi5rcz9rss88+/OxnP6sIbACOPfZYTjrpJIqKinj//ffZd9996/WsEAK///3vue6667j++utZtWpVxbm8vDxGjRpFx47+PUeSJLU9oeYhdIWiIiifdJwuxYkTG5mFEPKA04EbYozv7Tp2EHAfcDKwM4Rwe4zxyqTalCSlvxUrVgBw6qmnVjl35JFHcuihh7J69Wo2b96c+M5NTz/9NABnnnlmo58RY+S3v/0tc+fO5YUXXuC9997jk08+qTjfqVOnOp+xYMECVq5cWenYoEGDKpY01eaII44gMzOzyvG+ffsC8N5779U7tNm+fTsXX3wxCxcu5Pbbb2fEiBHss88+LFu2jMsuu4ycnBzuu+++ivo2kiRJ7VVhYXrMuEnyz2n/AwyMMX5/t2M3AacArwL7ApeHEJ6OMf6uugdIkupW3cyZdN7ye/PmzQA17uzUu3dvXnvtNTZt2pR4aLNp0yag5tky9XHllVdyyy230Lt3b04//XT69OlDly5dAJg7dy5r166t8xkLFixg3rx5lY6NHj26XqFNjx49qj1ePiNm9wCpLj/5yU+47777uPXWW/nmN79ZcfzMM8/k97//PYMGDeLyyy83tJEkSW1OTk7lJVF1mTat7YU2JwBLy78IIXQBLgAejTGeHkLoBvwNmAAY2khSO1EexKxfv57DDz+8yvm33nqr0nVJKg883njjjUZt571hwwZuu+02Bg4cyJNPPkm3bt0qnb/77rvr9Zy5c+dW7HKVSuXFhst3mdrdCSecwH777cfatWt59913OeCAA1q6e5IkSc2iuLhhgQ3A0qVl96W6OHGSOz4dBLy529efA/YG5gLEGLcCDwNHJdimJCnNDR48GKDKdtYAr776KuvWraN///41zihpis9//vNA2W5JjVFSUkJpaSn5+flVApt169ZVbKGdLsrr3tQ0+6Z8e/XqtvXesWMHW7duBeq35EuSJKm1KCxs2fuSlGRoswPostvXpwAR2D3P2gLsn2CbkqQ0d8kllwBw/fXXVwoLPvnkE7773e9SWlrK2LFjm6XtSy+9lI4dOzJt2rRKRXfL1bV7VPlW2U888USlIOT9999n3Lhx7Ny5M9H+NlX57JjXXnut2vOnnHIKAD/+8Y8rApxyU6dOZefOnXzmM5+pElBJkiS1Zlu2tOx9SUpyedRqYPcqk+cDr8QY39jtWF/gnQTblCSluaFDh/L973+fn/70pwwcOJALLriArl27snDhQl588UVOPvlkvve97zVL28ceeyx33HEHEyZMYPDgwYwYMYIjjjiCd999l2effZbMzEwee+yxGu/v1asXI0eO5J577mHQoEHk5+ezefNmHn30Ufbee28GDRpUpcBwKh111FH06dOHe+65h7322ousrCxCCFx00UVkZWVxzTXX8NBDD1FYWMjRRx/NGWecQZcuXVi2bBnPPPMMXbp04dZbb031y5AkSUpUNXs6NOt9SUoytJkH3BJC+AvwEXAccN0e1xwP/CPBNiVJpGcB4t3deOONDB48mF/+8pf8+te/5uOPP+bwww/n+uuv56qrrmrW5Tjjxo1j4MCB3HTTTSxZsoQFCxbQs2dPjj/+eL7xjW/Uef+sWbMYMGAA9957L7fffjsHHngg55xzDgUFBZx//vnN1u/G6NChAw888ABXX3019913H1u3biXGyMknn0xWVhZ9+vRhxYoV3HjjjTzyyCPMmTOH0tJSevfuzZgxY/jBD37QqNo/kiRJ6ayxBYXToRBxiAltPh5C2Iuy4OYrQAAeAi6MMe7YdX4g8Fdgcozx+kQaTQMhhOVDhgwZsnz58lR3RVIaeemllwA45phjUtwTqf2q78/hiSee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48c3obeSpFRzXOG4ojZJjiv69OnDt771rUqBDUCnTp2YOHEiUFbbqL1zpo0ktXJf/epXufLKK5k5cybnnntuxfE//vGPbNiwgRtvvJFXX3212nufffZZvvjFL7J161bOOeccjj32WP7+978zf/58HnzwQf785z/zmc98puL6d955h6FDh1JSUsLJJ5/MySefzFtvvcWECRPIz8+vto21a9cybNgw1qxZwymnnMIZZ5zBtm3bePjhhznjjDP41a9+xbhx4xr9+ouKivjJT37C8OHDOf/889l333155ZVX+P3vf88f//hHli1bxgknnFDv533nO9+hqKiICy+8kBEjRrBo0SJuueUWHn/8cZ544gn23nvvStd//PHHnH766bz55puceeaZdOzYkQULFnD11Vezffv2KsuJLr30UrKzs8nJyaF37968++67/OlPf+Kiiy7iH//4B9OmTWv0e1Gdp59+mg8//JD8/Hy6detW6VxGRgann34606dP57HHHqvXVObq/PGPf2T9+vXk5ORw9NFHV3vNjh07mD9/Pq+99hpdu3bl+OOPJycnhw4dOjSqTUlS83Bc4biiNi0xrgDYa6+9AKrM9irXnsYVhjaS1Mp169aNkSNHMnfuXNatW8ehhx4KwIwZM8jMzOTCCy+sdl16jJGLL76YLVu2MH/+fP7rv/6r4ty9997LyJEjueiii1i1ahUZGWUTMydOnEhJSQlXXHEFP//5zyuu//a3v80XvvCFavs3evRo1q5dy913383IkSMrjm/atIlhw4Zx2WWXcc4553DwwQc36vWfeuqpvP3221UGDi+88AInnXQSV199NQsXLqz385YtW8bKlSvJysoC4IYbbuDLX/4y999/P//7v//LpEmTKl3/5ptvcsIJJ/Doo4/SpUsXAKZMmcKRRx7Jz3/+cyZOnFgx8AB48cUXOfzwwys946OPPuLMM8/kJz/5CRMmTKBPnz4V55YsWdLgvzLt/pevf/zjHwAceeSR1V57xBFHAPDyyy83qI3dTZ8+HYBvfvObNV6zfv16LrrookrH+vfvz5w5c8jNzW1025KkZDmucFyxp5YeVwDMnj0bgDPOOKPa8+1qXBFj9KMJH8DyIUOGREna3apVq+KqVauatQ0g9unTJ8YY49NPPx2BeN1118UYY1yzZk3MyMiIl156aYwxxmuuuSYCcc6cORX3P/HEExGIX/jCF6p9/sknnxyBuHTp0hhjjB999FHcZ599Yrdu3eKmTZuqXD969OgIxClTplQcW7lyZQTiBRdcUG0bCxYsiEC8/fbbK47NmTOnSl8b6+yzz46dO3eOH330UcWx1atXRyCOHj262v4XFBRUec4///nPmJGREfv161fpeFZWVgTiK6+8UuWeiy++OALxb3/7W736+oc//CECcd68eZWOT5kyJQIN+tjdj370owjEa665ptp2p0+fHoE4fvz4evVzT6tXr44hhHjAAQfE7du3V3vN1KlTY2FhYVy/fn3ctm1b/Nvf/ha/+c1vxhBC7NKlS1y5cmWj2q5LfX8OhwwZEoHlMQ3GFdGxhaQaNPfYwnFF3RxXNP+4IsYYH3zwwRhCiIceemjcuHFjlfOpGlfEmJqxhTNtJKkN+NznPsdxxx3H7Nmzufbaa5k5cyalpaW1Tg9esWIFUPYXpeqceuqpPPHEEzz//PPk5OTw97//nQ8++IBTTjmF7t27V7l+2LBhVdagP/XUUwBs3ry52mJx//rXvwB46aWX6vU6a/LII49w11138dxzz/HOO+9U2dHinXfeoXfv3vV6VnV/nRkwYAB9+/ZlzZo1bNq0iR49elSc6969O5/61Keq3NO3b18A3nvvvUrHX3vtNW688UYKCwt57bXX+PDDDyudf+ONNyp9PXXq1LQutDdjxgxijIwePbraAsRAlancAwcO5K677mLffffl5ptvZurUqRa0lKQ04rjCcUWqPPnkk4waNYquXbvyhz/8gf3226/KNe1tXGFoI0ltxLhx47jssstYuHAhc+bM4cQTT2Tw4ME1Xr9582aAGgcd5cc3bdpU6fqaphv36tWryrF3330XgEcffZRHH320xr68//77NZ6ry6233soVV1zBfvvtx2mnncZhhx3GPvvsQwiBBQsW8MILL7Bjx456P6+217d27Vo2b95caXC1+z/vrnwN9ieffFJxrKSkhM9+9rO89957nHLKKeTn59O9e3c6dOjAmjVrmDdvXoP6Wh/lA+Hyf397Kj9e0+uozc6dO5kzZw7QuALEEyZM4Oabb6aoqKjB90qSmpfjCscV1WnOccVTTz3FmWeeSUZGBgsXLuSzn/1sg+5vq+MKQxtJaiMuuugifvCDHzBhwgTeeOMNJk+eXOv15f/TXb9+fbXn33rrrUrXlX9+++23q72+uueU33Prrbdy2WWX1eNVNMzOnTuZOnUqvXr1YsWKFVUGiuV/kWuIt99+m6OOOqrK8fLXV91fA+vrZz/7Ge+++y5z5sxhzJgxlc7dfffd1e6W0dS15+Wvpaa15eW7edS0Nr02Dz30EG+99Ra5ubnVvmd1OfDAA4GyXTIkSenFcYXjinItMa54/PHHOeuss8jIyGDRokV8/vOfb9D90HbHFYY2ktRG9OjRgwsuuKBiG8avfvWrtV5f/teymv7H/dhjjwEwZMgQAI4++mj22WcfVq5cyebNm6sMMqp7Tvn/cB9//PFmGVy98847bNq0ifPOO6/KwOr999+vmKrdEEuXLiUnJ6fSsZKSEl5//XX69evXqL8clSvfbeP888+vtt3/z96dx1VdJf4ffx0E0VTckJFcgMxyK5GsTA1DEsey0FwabQyybLRfU1pZZhmopNk327cZLZcyTawwK8IlUHMZFXVKsabFjdQsF1ILBTm/P5Cb1wuKcIELvp+Px30wnHM+n3M+l+74fpz7+ZxTmLS0NMaPH39e/Zwerjp16kTNmjVZtWoVR44ccVpYMS8vz7E9akRExHn1AX8uQFzSbb7Xrl0LUKrdJUREpGwoVyhXFCjrXPHFF19wyy234OvrS0pKitMOY+ejquYKr4oegIiIuE9CQgIfffQRKSkpLrsenKlLly5cfvnlfPnllyxYsMCpbsGCBaxcuZLLLruMrl27AvlbL95xxx0cOXLE5VnoDRs2MGfOHJc+OnbsyPXXX8+HH37o2AXgTF9//TX79+8/j6v8U0BAABdddBHp6elOt0Ln5OTw4IMP8uuvv573OV966SV27tzp+D0vL4/Ro0eTl5fHXXfdVaJxFggODgZcg2hKSgrTp08v9Jj4+PiSLGTrULt2bYYMGcKxY8dc/m6vvvoqO3bsoGfPni4B54cffuCbb74hJyen0HHt3LmTxYsX07Bhw0LDYoFt27YV+o3Xjh07uP/++wH4+9//XuTxIiJScZQr8ilX/MnduWLx4sX07t2bmjVrsmzZsnNO2FyIuUJ32oiIVCHNmzenefPmxWprjGHWrFn06NGD22+/nejoaFq1asW3335LUlISderUYfbs2Y5tOQEmTZrEsmXLePHFF9mwYQNdu3Zl7969vP/++9x00018/PHHLv289957dO/enbvvvpuXX36Za6+9lnr16pGZmclXX33Fli1bWLNmDQEBAed9vV5eXjzwwAM888wzXHHFFURHR3PixAlSU1M5ePAgERERjm/2iqtLly6EhoZy++23U7duXVJSUvjvf//LVVddxaOPPnreYzzdfffdx4wZMxgwYAD9+/fn4osvZsuWLXz++ecMHDiQ999/v1TnL8qkSZNIS0vj+eefZ/PmzVxzzTVs27aNhQsXEhAQwGuvveZyTGRkJDt37mT79u2OUHi6gkUpz7YAMeRv8zp16lTCw8MJCgqiTp06/PDDD3z66adkZ2dz00038cgjj7jzckVExE2UK5QrCuOuXPHtt98SHR3tyAMLFy5k4cKFLseePjl0IeYKTdqIiFzArr32WtavX09CQgJLly5l0aJF+Pv7M2jQIMaNG+fyDLa/vz+rVq1i7NixLFq0iA0bNnD55ZfzxhtvEBwcXGi4atq0Kenp6bzyyit88MEHzJkzh5MnT9K4cWPatGnDP//5T6644ooSX8PEiRNp1KgR06dP51//+hd169alR48eJCQkuOwuUBwvvPACH330EdOmTWPHjh00bNiQBx98kAkTJlCjRo0SjxPgyiuvJDU1lSeffJJPP/2U3Nxc2rdvz4cffki9evXKLFw1bNiQNWvWMH78eJKSkli5ciUNGzbkrrvuYsKECTRt2vS8znfy5EnHN5znejQqIiKCb7/9lk2bNrFq1SqOHTtGvXr16Nq1K0OGDGHIkCEYY0p8bSIi4jmUK1wpVxRt7969ZGdnA/DBBx/wwQcfFNru9EmbCzFXmDNvd5LzY4xJDwsLC0tPT6/ooYiIBynYarJ169YVPBIprtjYWGbNmlXknSVS+RT3c3jVVVexcePGjdbaq8pjXOeibCEihVG2qFyUK6qmisgWWtNGRERERERERMQDadJGRERERERERMQDadJGRERERERERMQDadJGREQEmDlzJtZaPXcuIiIipaZcIe6iSRsREREREREREQ+kSRsREREREREREQ+kH5JzXAAAIABJREFUSRsREREREREREQ+kSRsREREREREREQ+kSRsREREREREREQ+kSRsREREREREREQ+kSRsREREREREREQ+kSRsREREREREREQ+kSRsREREREREREQ9UqSZtjDENjTH3GGM+MsZ8b4z5wxiTZYz50hhztzHG64z2wcYYe5bXvIq6FhER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tpC09uxbYFpr7cSJEy1gAbtgwYKzjn3lypV2wIABNjAw0Pr4+Fh/f3/bvn17O2rUKLt+/fpij6nAO++8Y9u3b29r1KjhGMPpjh8/bl955RV73XXXWT8/P1u9enXbrFkz2717d/vCCy/YX3/91aX9+PHjbUhIiK1evboNCgqyY8eOtdnZ2WW+NWeBzMxMO3z4cNu8eXPr4+NjGzZsaKOjo+26desK7WPGjBm2ffv29qKLLrINGza0ffr0sV999VWRf7eff/7ZDho0yAYEBFgvLy8L2Li4uLNeh6dv+V3US9lCRAqjbOFMueJPyhXlkyusrZhsYWx+OJASMsakh4WFhaWnp1f0UETEgxRsnXjms7VlYetWWLYsf+tNP7/8b78qcg0bEU9R3M/hVVddxcaNGzfaIrbgLm/KFiJSGGULkYpXEdmisj0eJSIiZ2jbVkFKRERE3EfZQsRzVKqFiEVERERERERELhSatBERERERERER8UCatBERERERERER8UCatBERERERERER8UCatBERERERERER8UCatBERERERERER8UCatBERERERERER8UCatBERERERERER8UCatBERERERERER8UCatBERERERERER8UCatBERERERERER8UCatBERERERERER8UCatBEREanCjDHccMMNxW6flpaGMYb4+PgyG5OIiIhUTsoV5U+TNiIildzW/Vt5+T8vk7AigZf/8zJb92+t6CHJWcTHx2OMIS0traKHUuaKE+w+/fRToqKiaNq0KTVr1uSSSy5hwIABrFmzpnwGKSIiLpQtKg/lCmdVMVd4V/QARESkZJb9uIwJKyawYucKl7rwoHCeCn+KyEsiK2BkUpldc801bNu2DX9//zLv67HHHuPZZ5+lYcOG9OnTB39/f77//nsWLlzIBx98wOzZs/n73/9e5uMQEZF8yhbibsoVpadJGxGRSuitjW9x7yf3kmfzCq1fsXMFUe9GMe2WaQztMLScRyeV2UUXXUSrVq3KvJ99+/bx3HPP8Ze//IWvvvqKgIAAR11qairdu3fnqaeeqpThSkSkMlK2kLKgXFF6ejxKRKSSWfbjsrOGqgJ5No9hi4ax7Mdl5TSys1u3bh233347TZo0wdfXl8DAQKKiopg/f75Tu/nz5xMeHk7dunWpWbMmV1xxBZMnT+b48eMu5wwODiY4OJhj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\n",
"text/plain": [
""
]
},
"metadata": {
"image/png": {
"height": 566,
"width": 566
},
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"visualize(df, 'size_peak', 'Max resident set size [MB]')"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'2.0.0'"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import tensorflow as tf\n",
"tf.__version__"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'2.2.4-tf'"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from tensorflow.python import keras\n",
"keras.__version__"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: Examples/converter_examples/Convert_Benchmark/README.md
================================================
# Quantization Benchmarking on Android Phone
In this example, we use Converter to quantize and convert models into TensorFlow Lite format.
We ran the experiment for image classification task using different configurations of MobileNet and obtained benchmarking results by deploying models on the LG G6 smartphone. The results reaffirm the benefits of model quantization for edge deployments in terms of inference speed and peak memory usage.
The experiments are presented in the [Benchmark](Benchmark.ipynb) file along with figures showing model performance. To obtain the data pickle file, either run `Benchmark.ipynb` or `script_mobilenet.py`.
The script first downloads different MobileNets in the Protobuf format and converts them to TensorFlow Lite using different quantization configurations. The script also downloads the official TensorFlow Lite versions (int8 and float32) of MobileNet to compare the performance of the converted models.
To set up the mobile benchmarking pipeline please follow these steps.
### Setup
1. Android Studio / [adb](https://developer.android.com/studio/command-line/adb)
```bash
export PATH=$PATH:~/Library/Android/sdk/platform-tools/
```
2. Compile model_benchmark
See [step 1](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/tools/benchmark#on-android):
Needs Ubuntu system / Android Studio.
```bash
git clone git@github.com:tensorflow/tensorflow.git
cd tensorflow
bazel clean --expunge
bazel build -c opt --config=android_arm --cxxopt='--std=c++11' tensorflow/lite/tools/benchmark:benchmark_model
```
3. Prepare data on the phone
See [step 2-4](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/tools/benchmark#on-android)
```
Android/Sdk/platform-tools/adb push bazel-bin/tensorflow/lite/tools/benchmark/benchmark_model /data/local/tmp
```
If the device is not connected, follow instructions [here](https://askubuntu.com/questions/863587/adb-device-list-doesnt-show-phone)
Copy model
```bash
adb push model.tflite /data/local/tmp
```
4. Measure performance
See [here](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/tools/benchmark#parameters) for more control arguments.
```bash
adb shell /data/local/tmp/benchmark_model \
--num_threads=4 \
--graph=/data/local/tmp/model.tflite \
--warmup_runs=1 \
--num_runs=50
```
================================================
FILE: Examples/converter_examples/Convert_Benchmark/repdata.py
================================================
"""
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
Create a synthetic tf.dataset for inference speed measure
"""
import numpy as np
from absl import flags
import sys
def parse_arg():
try:
idx = sys.argv.index("--custom_data")
custom_data = str(sys.argv[idx+1:][0])
except ValueError:
custom_data = "1,224,224,3,1000"
return custom_data
def get_dataset():
custom_data = parse_arg()
print(custom_data)
batch_size, img_len, img_wid, channel_size, label_size = [int(x) for x in custom_data.split(",")]
labels = np.random.randint(0, label_size, size=batch_size).astype(np.float)
imgs = np.random.random(size=(batch_size, img_len, img_wid, channel_size))
class Dataset:
def __init__(self, data):
self.data = data
def numpy(self):
return self.data[np.newaxis,:]
for i in range(batch_size):
yield Dataset(imgs[i]), Dataset(labels[i])
================================================
FILE: Examples/converter_examples/Convert_Benchmark/script_mobilenet.py
================================================
"""
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import keras
import subprocess
from os import path
import pandas as pd
import json
# see models here - https://www.tensorflow.org/lite/guide/hosted_models
STORAGE_LINK = "https://storage.googleapis.com/download.tensorflow.org/models/mobilenet_v1_2018_08_02/"
ADB_LOCAL = path.expanduser("~/Library/Android/sdk/platform-tools/adb") # for MacOS
ALPHA = ["1.0","0.75","0.5","0.25"]
SIZE = ["224","192","160","128"]
REP_DATA = "repdata.py"
OP_TYPE = ["float", "float16", "uint8", "int8"]
KEYS = [[a,b] for a in ALPHA for b in SIZE]
MOBILENET = "mobilenet_v1"
df = pd.DataFrame({"model":[], "alpha":[], "size":[], "op": [], "size_init":[],
"size_peak":[], "warmup":[], "init":[], "inference":[]})
def preprocess(name):
try:
process = subprocess.Popen(" wget " + STORAGE_LINK + name + ".tgz ", shell=True).wait()
except:
print("Could not retrieve")
return 0
try:
process = subprocess.Popen(" tar -xzf " + name + ".tgz", shell=True).wait()
except:
print("Could not unpack")
return 0
return 1
def push_to_device(name):
try:
process = subprocess.Popen(ADB_LOCAL + " push " + name + ".tflite /data/local/tmp/",
shell=True, stdout=subprocess.DEVNULL).wait()
except:
print("Could not push")
return 0
return 1
def run_benchmark(name):
command_template = ADB_LOCAL + " shell /data/local/tmp/benchmark_model --graph=/data/local/tmp/"
command = command_template + name + ".tflite --num_threads=4"
process2 = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
output, error = process2.communicate()
output = output.decode('utf-8')
output = output.split("\n")
infer, memo = "",""
for p in output:
if "Inference timings" in p:
infer = p
if "Peak memory footprint" in p:
memo = p
#output = output.split("\n")
#infer = output
return (infer, memo)
def compile_results(infer, memo, df, of, op, alpha, size):
data = []
try:
idx = memo.find("init=")
max_size = float(memo[idx+5:idx + memo[idx:].find(" ")-1])
except:
max_size = float('nan')
idx = memo.find("overall=")
try:
malloc_size = float(memo[idx+8:])
except:
malloc_size = float('nan')
idx = infer.find("Warmup (avg):")
try:
warmup = float(infer[idx+14:idx + infer[idx:].find(",")])
except:
warmup = float('nan')
idx = infer.find("Init:")
try:
init = float(infer[idx+6:idx + infer[idx:].find(",")])
except:
init = float('nan')
idx = infer.find("Inference (avg):")
try:
no_stat = float(infer[idx+17:])
except:
no_stat = float('nan')
df = df.append(pd.Series([of, alpha, size, op, max_size, malloc_size,
warmup, init, no_stat], index=df.columns), ignore_index=True)
return df
def convert(name, size, op):
json_dic = {}
json_dic["convert_from"] = name+"_frozen.pb"
json_dic["convert_to"] = name+"_"+op+"_converted.tflite"
json_dic["input_nodes"] = "input"
json_dic["output_nodes"] = "MobilenetV1/Predictions/Reshape_1:0"
command = ""
if op == 'float':
command = json.dumps([json_dic])
if op == 'float16':
quant_dic = {}
quant_dic["type"]="float16"
quant_dic["opsset"] = "tf"
json_dic["quantization"] = quant_dic
command = json.dumps([json_dic])
if op == 'int8':
quant_dic = {}
quant_dic["type"]="int8"
quant_dic["opsset"] = "int8"
quant_dic["load"] = REP_DATA + " --custom_data '1," + size + "," + size + "," + "3,1000' --undefok custom_data"
json_dic["quantization"] = quant_dic
command = json.dumps([json_dic])
if op == 'uint8':
quant_dic = {}
quant_dic["type"]="uint8"
quant_dic["opsset"] = "tf"
quant_dic["load"] = REP_DATA + " --custom_data '1," + size + "," + size + "," + "3,1000' --undefok custom_data"
json_dic["quantization"] = quant_dic
command = json.dumps([json_dic])
try:
process = subprocess.Popen("python3 -m aup.dlconvert -d " + "'" + str(command) + "'", shell=True).wait()
except:
print("Could not Convert= " + str(command))
return 0
return 1
for key in KEYS:
name = MOBILENET + "_" + key[0] + "_" + key[1]
print("Starting " + str(name))
if(not preprocess(name)):
continue
if(not push_to_device(name)):
continue
infer,memo = run_benchmark(name)
df = compile_results(infer, memo, df, "official", "float", key[0], key[1])
new_name = name + "_quant"
print("Starting " + str(new_name))
if(not preprocess(new_name)):
continue
if(not push_to_device(new_name)):
continue
infer,memo = run_benchmark(new_name)
df = compile_results(infer, memo , df, "official", "int8", key[0], key[1])
print("Starting converted tflite benchmarking")
for op in OP_TYPE:
converted = convert(name, key[1], op)
if not converted:
print("Failed " + str(name) + " " + str(op))
continue
print("testing= " + str(name) + " " + str(op))
new_name = name + "_" + op + "_converted"
if(not push_to_device(new_name)):
continue
infer,memo = run_benchmark(new_name)
df = compile_results(infer, memo, df, "converted", str(op), key[0], key[1])
print(df.to_string())
df.to_pickle("./data_mobilenet.pkl")
================================================
FILE: Examples/converter_examples/Convert_Profiler/.gitignore
================================================
mobilenet*
input_models
output_models
*.tgz
Dockerfile_*
densenet*
nasnet*
squeezenet*
================================================
FILE: Examples/converter_examples/Convert_Profiler/README.md
================================================
# Profiling TensorFlow Lite/ONNX model performance for CPU
In this example, we show how to use Converter to convert models into edge device-friendly formats (TensorFlow Lite and ONNX) and then profile their performance (i.e. runtime and memory usage) on CPU using Auptimizer's Profiler package.
We use standard image classification models in Protobuf format and convert them to TensorFlow or ONNX. Once we have models in a common format, we can benchmark their performance using Profiler with different hardware settings.
By pairing Auptimizer's Converter and Profiler capabilities, you can significantly reduce the effort needed to prepare an ML model for edge deployment. You can simply download models from various DL platforms and libraries, convert them to a common edge-friendly standard and estimate the script's runtime and memory usage for your target device constraints.
## How to run this example
- *Step 1*: Download a few protobuf models for testing
```
python download_test_models.py
```
and create a new folder called `output\_models`.
The `download_test_models.py` script should download the squeezenet, densenet, and nasnet-mobile models into `input_models` directory.
- *Step 2*: Convert the models to TensorFlow Lite:
```
python -m aup.dlconvert -f convert_tflite.json
```
- *Step 3*: Profile the converted TensorFlow Lite models and the downloaded official TensorFlow Lite models released by TensorFlow for the same model architecture. Run the following to do profiling:
```bash
python -m aup.profiler -e env_tflite.template -f model_names_tflite.txt
```
This creates an output text file with the details of the model performance. The Docker settings can be edited using the `env_tflite.template` file and the test script can be edited using `test_perf_tflite.py`.
We show output from running the example on MacOS. From `tflite_output.txt`, we see that the performances of the converted models are very close to those of the official TensorFlow Lite models, which validates that the conversion from Protobuf to TensorFlow Lite works correctly.
You can similarly convert the models to ONNX and profile the performances. The sample conversion configuration json file `convert_onnx.json` and the Profiling environment file `env_onnx.template` are provided.
================================================
FILE: Examples/converter_examples/Convert_Profiler/convert_onnx.json
================================================
[
{
"convert_from":"input_models/densenet/densenet.pb",
"convert_to":"output_models/densenet_converted.onnx",
"input_nodes":"Placeholder:0",
"output_nodes":"softmax_tensor:0,ArgMax:0"
},
{
"convert_from":"input_models/squeezenet.pb",
"convert_to":"output_models/squeezenet_converted.onnx",
"input_nodes":"Placeholder:0",
"output_nodes":"softmax_tensor:0"
},
{
"convert_from":"input_models/nasnet_mobile.pb",
"convert_to":"output_models/nasnet_mobile_converted.onnx",
"input_nodes":"input:0",
"output_nodes":"final_layer/predictions:0"
}
]
================================================
FILE: Examples/converter_examples/Convert_Profiler/convert_tflite.json
================================================
[
{
"convert_from":"input_models/squeezenet.pb",
"convert_to":"output_models/squeezenet_converted.tflite",
"input_nodes":"Placeholder:0",
"output_nodes":"softmax_tensor:0"
},
{
"convert_from":"input_models/densenet/densenet.pb",
"convert_to":"output_models/densenet_converted.tflite",
"input_nodes":"Placeholder:0",
"output_nodes":"softmax_tensor:0,ArgMax:0"
},
{
"convert_from":"input_models/nasnet_mobile.pb",
"convert_to":"output_models/nasnet_mobile_converted.tflite",
"input_nodes":"input:0",
"output_nodes":"final_layer/predictions:0"
}
]
================================================
FILE: Examples/converter_examples/Convert_Profiler/download_test_models.py
================================================
# Copyright (c) 2020 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import os
import json
import argparse
import logging
import requests
import shutil
all_urls = {"densenet": "https://storage.googleapis.com/download.tensorflow.org/models/tflite/model_zoo/upload_20180427/densenet_2018_04_27.tgz",
"squeezenet": "https://storage.googleapis.com/download.tensorflow.org/models/tflite/model_zoo/upload_20180427/squeezenet_2018_04_27.tgz",
"nasnet_mobile": "https://storage.googleapis.com/download.tensorflow.org/models/tflite/model_zoo/upload_20180427/nasnet_mobile_2018_04_27.tgz"}
model_folder = "input_models"
def download_all_url(all_urls, model_folder):
for model, url in all_urls.items():
tarFilename = url.split('/')[-1]
# check if the tar file already exists on disk
# if not tarFilename[0].isdigit() and os.path.isfile(tarFilename):
if os.path.isfile(tarFilename):
print ("File already exists, skipping ", url)
continue
print("Downloading ", url)
response = requests.get(url, stream=True)
if response.status_code == 200:
with open(tarFilename, 'wb') as f:
f.write(response.raw.read())
save_folder = model_folder
shutil.unpack_archive(tarFilename, save_folder)
if __name__ == '__main__':
download_all_url(all_urls, model_folder)
================================================
FILE: Examples/converter_examples/Convert_Profiler/env_onnx.template
================================================
# Copyright (c) 2020 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
#User data Variables
IMAGEREPO=tensorflow/tensorflow:1.15.0-py3
APTREQUIREMENTS="wget"
PRERUN="pip install onnxruntime"
DIR=./
SCRIPT=test_perf_onnx.py
COMMAND=python
SAMPLETIME=3
OUTPUTFILE=onnx_output.txt
DOCFILE=Dockerfile
DOCKCPUS="4.0"
DOCKMEMORY="2.0g"
# Additional docker arguments could be passed as following:
# To run Docker container with privilege capability change to 'true'
# To use Volume instead of copying data with the current folder
# use the format "-v /path/in/source:/path/in/destination" as string
# DOCKER_ARGS="--privileged -v /data:/mnist_data"
DOCKER_ARGS=
================================================
FILE: Examples/converter_examples/Convert_Profiler/env_tflite.template
================================================
# Copyright (c) 2020 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
#User data Variables
IMAGEREPO=tensorflow/tensorflow:1.15.0-py3
APTREQUIREMENTS="wget"
# PIPREQUIREMENTS="ipython numpy"
PRERUN="wget https://dl.google.com/coral/python/tflite_runtime-1.14.0-cp36-cp36m-linux_x86_64.whl; pip install tflite_runtime-1.14.0-cp36-cp36m-linux_x86_64.whl"
DIR=./
SCRIPT=test_perf_tflite.py
COMMAND=python
SAMPLETIME=3
OUTPUTFILE=tflite_output.txt
DOCFILE=Dockerfile
DOCKCPUS="4.0"
DOCKMEMORY="2.0g"
# Additional docker arguments could be passed as following:
# To run Docker container with privilege capability change to 'true'
# To use Volume instead of copying data with the current folder
# use the format "-v /path/in/source:/path/in/destination" as string
# DOCKER_ARGS="--privileged -v /data:/mnist_data"
DOCKER_ARGS=
================================================
FILE: Examples/converter_examples/Convert_Profiler/model_names_tflite.txt
================================================
input_models/densenet/densenet.tflite
output_models/densenet_converted.tflite
input_models/squeezenet.tflite
output_models/squeezenet_converted.tflite
input_models/nasnet_mobile.tflite
output_models/nasnet_mobile_converted.tflite
================================================
FILE: Examples/converter_examples/Convert_Profiler/test_perf_onnx.py
================================================
# Copyright (c) 2020 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
# Compute the overall inference time for a given tflite model
import onnxruntime as rt
import numpy as np
import time
WARMUP = 10
ITER = 600
CONSTANT = 0.5
def compute(model_path):
now = time.monotonic()
sess = rt.InferenceSession(model_path)
input_det = sess.get_inputs()[0]
label_det = sess.get_outputs()[0]
input_name = input_det.name
input_shape = input_det.shape
if 'N' in input_shape:
input_shape[input_shape.index('N')] = 1
input_shape[0] = 1
print("input_shape", input_shape)
t1 = time.monotonic() - now
now = time.monotonic()
for i in range(WARMUP):
X_test = np.random.random(size=input_shape).astype(np.float32)
pred_onx = sess.run(None, {input_name: X_test})[0]
t2 = time.monotonic() - now
now = time.monotonic()
for i in range(ITER):
X_test = np.random.random(size=input_shape).astype(np.float32)
pred_onx = sess.run(None, {input_name: X_test})[0]
t3 = time.monotonic() - now
return t1, t2/float(WARMUP), t3/float(ITER)
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
print("test_perf_onnx.py ")
print(compute(sys.argv[1]))
================================================
FILE: Examples/converter_examples/Convert_Profiler/test_perf_tflite.py
================================================
# Copyright (c) 2020 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
# Compute the overall inference time for a given tflite model
import tensorflow as tf
from tflite_runtime.interpreter import Interpreter
import tensorflow.random
import numpy as np
import time
import os
WARMUP = 10
ITER = 300
CONSTANT = 0.5
def compute(model_path):
now = time.monotonic()
intp = Interpreter(model_path)
x = intp.tensor(intp.get_input_details()[0]['index'])
iy = intp.get_output_details()[0]['index']
intp.allocate_tensors()
t1 = time.monotonic() - now
now = time.monotonic()
for i in range(WARMUP):
#x().fill(CONSTANT)
x =np.random.rand()
intp.invoke()
y = intp.get_tensor(iy)
t2 = time.monotonic() - now
now = time.monotonic()
for i in range(ITER):
#x().fill(CONSTANT)
x =np.random.rand()
intp.invoke()
y = intp.get_tensor(iy)
t3 = time.monotonic() - now
return t1, t2/float(WARMUP), t3/float(ITER)
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
print("test_perf_tflite.py ")
print(compute(sys.argv[1]))
================================================
FILE: Examples/converter_examples/Convert_Profiler/tflite_output.txt
================================================
Usage stats for Experiment ran on : 2020-10-08 10:26:18.302189
NAME AVG CPU % PEAK CPU AVG MEM USAGE / LIMIT PEAK MEM NET I/O BLOCK I/O TOTAL TIME (ms)
----------------------- ----------- ---------- ----------------------- ---------- ---------------- ------------- -----------------
densenet 256.601% 260.97 162.9 MiB / 1.9 GiB 163.0 MiB 850.0 B / 42.0 B 0.0 B / 0.0 B 27640
densenet_converted 254.545% 259.71 162.9 MiB / 1.9 GiB 163.0 MiB 843.0 B / 42.0 B 0.0 B / 0.0 B 27635
squeezenet 272.655% 274.36 127.6 MiB / 1.9 GiB 127.7 MiB 773.0 B / 42.0 B 0.0 B / 0.0 B 3132
squeezenet_converted 274.83% 276.06 129.7 MiB / 1.9 GiB 129.8 MiB 731.0 B / 0.0 B 0.0 B / 0.0 B 3067
nasnet_mobile 155.766% 159.16 135.1 MiB / 1.9 GiB 135.3 MiB 839.0 B / 42.0 B 0.0 B / 0.0 B 24549
nasnet_mobile_converted 154.046% 159.38 123.0 MiB / 1.9 GiB 134.7 MiB 854.0 B / 42.0 B 0.0 B / 0.0 B 24515
================================================
FILE: Examples/converter_examples/README.md
================================================
# Model Conversion Examples
This folder contains examples that demonstrate Converter coverage and efficacy.
### - Evaluating supported model architectures
[Tested_Models](/Examples/converter_examples/Tested_Models) - This example evaluates common model architecture coverage by the individual conversion functions. It also summarizes known issues. **We strongly recommend that users review this example first before running their conversion tasks.**
### - Benchmarking quantized TensorFlow Lite models on an Android phone
[Convert_Benchmark](/Examples/converter_examples/Convert_Benchmark) - This example demonstrates how to benchmark quantized TensorFlow Lite model performance (i.e. running time and memory usage) on an Android phone. Specifically, we converted models from a TensorFlow frozen protobuf file to a quantized .tflite file, and benchmarked their performance on an LG G6 mobile phone.
This example shows that models converted and quantized with Converter match the performance of the official quantized
models provided in the [TensorFlow repo](https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet_v1.md).
### - Profiling performances of converted models using Auptimizer Profiler
[Convert_Profiler](/Examples/converter_examples/Convert_Profiler) - This example demonstrates how to use Auptimizer-Profiler to profile TensorFlow or ONNX model performance on CPU. Performance benchmarking scripts are provided for TensorFlow and ONNX.
### How to run the examples
If you have not done so, please install the packages in `dlconvert_requirements.txt` before running the examples included in this directory.
```bash
pip install -r dlconvert_requirements.txt
```
We provide specific instructions for each example in the respective folder.
================================================
FILE: Examples/converter_examples/Tested_Models/.gitignore
================================================
test_models/
converted_models/
*.tgz
*.gz
*.pb
*.h5
*_internal*
================================================
FILE: Examples/converter_examples/Tested_Models/README.md
================================================
# Model Coverage
In this example, we have evaluated the capabilities and limitations of Converter conversion functions by applying the tool to select models in various supported formats.
We tested the official models hosted/released by TensorFlow or PyTorch. Our goal was to cover as many models as possible. However, due to the limited availability of officially released models and the large number of model architectures, this evaluation does not aim to test all models exhaustively. Rather, its purpose is to identify and address common model conversion issues.
**We strongly recommended reviewing model coverage and known issues for the specific conversions, before you start using Converter.**
## How to run the evaluation tests
- *Step 0*: Make sure you have installed required packages listed in [dlconvert_requirement.txt](../dlconvert_requirements.txt).
- *Step 1*: Prepare test models.
There are two ways to get the test models: 1) Direct download via URLs and 2) Create the models using Python scripts.
To download models, run `python download_models.py`. This will automatically download models from the URLs list in `download_urls.json` and save them into a `test_models` folder. The downloaded models are in Protobuf, Checkpoint or Savedmodel formats.
To create models, run `python create_test_models.py`. This will create Keras and PyTorch models and save them into the `test_models` folder.
- *Step 2*: Convert test models to TensorFlow Lite/ONNX.
The configuration json files for each type of conversion (from one source format to either TensorFlow Lite or ONNX) are located in [conversion_jsons](/conversion_jsons). You can run model conversion with each json file individually to test a certain conversion; e.g.,
```bash
python3 -m aup.dlconvert -f conversion_jsons/convert_keras_to_onnx.json
```
In the json files, we set `skip:True` for the models that were tested but failed the conversion.
## Model support and known issues
Here we share the findings from the evaluation test for each individual conversion function. The model names corresponds to their names in `download_models.py` or `create_test_models.py`.
For the failed models, some failures were caused by non-supported operators in the conversion tool, while others by more model-specific issues that Converter cannot handle. Any model that is not listed below has not been tested.
If not specified, the (un)supported models apply to both TensorFlow v2.3 and TensorFlow v1.15. Please make sure you are using the TensorFlow version no lower than v1.15 to run the tests.
### Savedmodel to TensorFlow Lite
- Supported:
resnet_50_TF2, efficientnet_b1_TF2, lstm, gru
- Not supported:
Non-supported operators: object detection models from [TensorFlow 2 Detection Model Zoo](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf2_detection_zoo.md), transformer models
Model-specific issues: ssd_mobilenet_v1_coco, ssd_mobilenet_v2_coco
- Known issues:
- This conversion is only supported by Tensorflow v2.x.
- For some models downloaded from [TensorFlow Hub](https://tfhub.dev/) in Savedmodel format, the signature is empty. Conversion from Savedmodel format requires non-empty signature and tag.
### Savedmodel to ONNX
- Supported:
resnet_50_TF2, efficientnet_b1_TF2, lstm, gru, ssd_mobilenet_v1_coco, ssd_mobilenet_v2_coco
- Not supported:
Non-supported operators: object detection models from [TensorFlow 2 Detection Model Zoo](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf2_detection_zoo.md), transformer models
- Known issues:
- This conversion is only supported by Tensorflow v2.x.
- Conversion from Savedmodel format requires non-empty signature and tag.
### Protobuf to TensorFlow Lite
- Supported:
densenet, squeezenet, nasnet_mobile, inception_v3, mobilenet_v1_0.25_224
- Not supported:
Non-supported input type uint 8: ssd_mobilenet_v1, ssd_mobilenet_v2
- Known issues:
- Conversion requires correct input_nodes name and output_nodes name. When using the [summarize graph tool](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/tools/graph_transforms#inspecting-graphs), you can get the names correctly. But you need to make them into format `input_name:0` or `output_name:0`; i.e., adding `:0` after the name you found from the summarize graph tool.
- The model to be converted and the input should be in float32 format.
- Issue with Tensorflow v1.15: None dimension is only allowed for the 1st dimension of the input. When saving model to protobuf, please specify input dimension.
### Protobuf to ONNX
- Supported:
densenet, squeezenet, nasnet_mobile, inception_v3, mobilenet_v1_0.25_224, ssd_mobilenet_v1, ssd_mobilenet_v2
- Known issues:
- Conversion requires correct input_nodes name and output_nodes name. When using the [summarize graph tool](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/tools/graph_transforms#inspecting-graphs), you can get the names correctly. But you need to make them into format `input_name:0` or `output_name:0`; i.e., adding `:0` after the name you found from the summarize graph tool.
### Keras to TensorFlow Lite
- Supported:
VGG16, ResNet50, InceptionV3, MobileNet, MobileNetV2, DenseNet121, NASNetMobile, gru (supported by TF v2.3 only)
- Not supported:
Model-specific conversion error: lstm
- Known issues:
- Need to set parameter `quantization: opsset` to `tf` to enable successful conversion.
### Keras to ONNX
- Supported:
VGG16, ResNet50, MobileNet, MobileNetV2, DenseNet121, NASNetMobile, lstm, gru (supported by TF v2.3 only), InceptionV3 (supported by TF v1.15 only)
### Checkpoint to TensorFlow Lite
- Known issues:
- This conversion is experimental. There is no direct conversion tool available. Therefore, the approach taken is to convert a checkpoint file to frozen protobuf first, then to TensorFlow Lite. Hence, this conversion can be fragile.
### Checkpoint to ONNX
- Supported:
mobilenet_v1_0.25_224, ssd_mobilenet_v1, ssd_mobilenet_v2, lstm
### PyTorch to TensorFlow Lite
- Supported:
all **image classification** models in [torchvision.models](https://pytorch.org/docs/stable/torchvision/models.html#)
- Not supported:
Non-supported operators: fcn_resnet50, deeplabv3_resnet50, rnn models, resnet_3d, resnet_MC, resnet(2+1)d (the last three models all have 3D convolution layers)
- Known issues:
- To use the converter properly, please make changes in your `~/.keras/keras.json`:
```bash
"backend": "tensorflow",
"image_data_format": "channels_first"
```
- Models with `BatchNorm2d`/`BatchNorm3d` layers are not supported in TF v1.15.
### PyTorch to ONNX
- Supported:
all **image classification**, **semantic segmentation**, and **video classification** models in [torchvision.models](https://pytorch.org/docs/stable/torchvision/models.html#)
- Not supported:
Non-supported operators: rnn models
- Known issues:
- For supported models, all models can be converted using `torch==1.5.1` and `torchvision==0.6.1`, while some models fail when using `torch==1.6.0` and `torchvision==0.7.0`.
- Models with `BatchNorm2d`/`BatchNorm3d` layers are not supported in TF v1.15.
## More resources
Auptimizer model conversion tools integrates [TFLite converter](https://www.tensorflow.org/lite/convert), [tf2onnx](https://github.com/onnx/tensorflow-onnx), [keras2onnx](https://github.com/onnx/keras-onnx), and [pytorch2keras](https://github.com/nerox8664/pytorch2keras) packages. Please check those resources for more info on model support.
================================================
FILE: Examples/converter_examples/Tested_Models/conversion_jsons/convert_checkpoint_to_onnx.json
================================================
[
{
"convert_from":"test_models/mobilenet_v1_0.25_224/mobilenet_v1_0.25_224.ckpt.meta",
"convert_to":"converted_models/mobilenet_v1_0.25_224_ckpt_converted.onnx",
"input_nodes":"batch:0",
"output_nodes":"MobilenetV1/Predictions/Reshape_1:0",
"input_shape":"1,224,224,3",
"skip": "False"
},
{
"convert_from":"test_models/ssd_mobilenet_v1_coco_2018_01_28/model.ckpt.meta",
"convert_to":"converted_models/ssd_mobilenet_v1_ckpt_converted.onnx",
"input_nodes":"image_tensor:0",
"output_nodes":"detection_boxes:0,detection_scores:0,num_detections:0,detection_classes:0",
"input_shape":"1,224,224,3",
"skip": "False"
},
{
"convert_from":"test_models/ssd_mobilenet_v2_coco_2018_03_29/model.ckpt.meta",
"convert_to":"converted_models/ssd_mobilenet_v2_ckpt_converted.onnx",
"input_nodes":"image_tensor:0",
"output_nodes":"num_detections:0,detection_classes:0,detection_scores:0,detection_boxes:0",
"skip": "False"
},
{
"convert_from":"test_models/lstm_ckpt/ckpt.meta",
"convert_to":"converted_models/lstm_ckpt_converted.onnx",
"input_nodes":"embedding_input:0",
"output_nodes":"dense/BiasAdd:0",
"skip": "False"
}
]
================================================
FILE: Examples/converter_examples/Tested_Models/conversion_jsons/convert_checkpoint_to_tflite.json
================================================
[
{
"convert_from":"test_models/mobilenet_v1_0.25_224/mobilenet_v1_0.25_224.ckpt.meta",
"convert_to":"converted_models/mobilenet_v1_0.25_224_ckpt_converted.tflite",
"input_nodes":"batch:0",
"output_nodes":"MobilenetV1/Predictions/Reshape_1:0",
"skip": "True"
},
{
"convert_from":"test_models/ssd_mobilenet_v1_coco_2018_01_28/model.ckpt.meta",
"convert_to":"converted_models/ssd_mobilenet_v1_ckpt_converted.tflite",
"input_nodes":"image_tensor:0",
"output_nodes":"detection_boxes:0,detection_scores:0,num_detections:0,detection_classes:0",
"input_shape": "1,224,224,3",
"skip": "True"
},
{
"convert_from":"test_models/ssd_mobilenet_v2_coco_2018_03_29/model.ckpt.meta",
"convert_to":"converted_models/ssd_mobilenet_v2_ckpt_converted.tflite",
"input_nodes":"image_tensor:0",
"output_nodes":"num_detections:0,detection_classes:0,detection_scores:0,detection_boxes:0",
"skip": "True"
},
{
"convert_from":"test_models/lstm_ckpt/ckpt.meta",
"convert_to":"converted_models/lstm_ckpt_converted.tflite",
"input_nodes":"embedding_input:0",
"output_nodes":"dense/BiasAdd:0",
"input_shape":"1,53",
"skip": "True"
}
]
================================================
FILE: Examples/converter_examples/Tested_Models/conversion_jsons/convert_keras_to_onnx.json
================================================
[
{
"convert_from":"test_models/VGG16.h5",
"convert_to":"converted_models/VGG16_keras.onnx",
"skip":"False"
},
{
"convert_from":"test_models/ResNet50.h5",
"convert_to":"converted_models/ResNet50_keras.onnx",
"skip":"False"
},
{
"convert_from":"test_models/InceptionV3.h5",
"convert_to":"converted_models/InceptionV3_keras.onnx",
"skip":"True"
},
{
"convert_from":"test_models/MobileNet.h5",
"convert_to":"converted_models/MobileNet_keras.onnx",
"skip":"False"
},
{
"convert_from":"test_models/MobileNetV2.h5",
"convert_to":"converted_models/MobileNetV2_keras.onnx",
"skip":"False"
},
{
"convert_from":"test_models/DenseNet121.h5",
"convert_to":"converted_models/DenseNet121_keras.onnx",
"skip":"False"
},
{
"convert_from":"test_models/NASNetMobile.h5",
"convert_to":"converted_models/NASNetMobile_keras.onnx",
"skip":"False"
},
{
"convert_from":"test_models/lstm.h5",
"convert_to":"converted_models/lstm_keras.onnx",
"skip":"False"
},
{
"convert_from":"test_models/gru.h5",
"convert_to":"converted_models/gru_keras.onnx",
"skip":"False"
}
]
================================================
FILE: Examples/converter_examples/Tested_Models/conversion_jsons/convert_keras_to_tflite.json
================================================
[
{
"convert_from":"test_models/VGG16.h5",
"convert_to":"converted_models/VGG16_keras.tflite",
"quantization": {
"opsset":"tf"
},
"skip":"False"
},
{
"convert_from":"test_models/ResNet50.h5",
"convert_to":"converted_models/ResNet50_keras.tflite",
"quantization": {
"opsset":"tf"
},
"skip":"False"
},
{
"convert_from":"test_models/InceptionV3.h5",
"convert_to":"converted_models/InceptionV3_keras.tflite",
"quantization": {
"opsset":"tf"
},
"skip":"False"
},
{
"convert_from":"test_models/MobileNet.h5",
"convert_to":"converted_models/MobileNet_keras.tflite",
"quantization": {
"opsset":"tf"
},
"skip":"False"
},
{
"convert_from":"test_models/MobileNetV2.h5",
"convert_to":"converted_models/MobileNetV2_keras.tflite",
"quantization": {
"opsset":"tf"
},
"skip":"False"
},
{
"convert_from":"test_models/DenseNet121.h5",
"convert_to":"converted_models/DenseNet121_keras.tflite",
"quantization": {
"opsset":"tf"
},
"skip":"False"
},
{
"convert_from":"test_models/NASNetMobile.h5",
"convert_to":"converted_models/NASNetMobile_keras.tflite",
"quantization": {
"opsset":"tf"
},
"skip":"False"
},
{
"convert_from":"test_models/lstm.h5",
"convert_to":"converted_models/lstm_keras.tflite",
"skip":"True"
},
{
"convert_from":"test_models/gru.h5",
"convert_to":"converted_models/gru_keras.tflite",
"skip":"False"
}
]
================================================
FILE: Examples/converter_examples/Tested_Models/conversion_jsons/convert_protobuf_to_onnx.json
================================================
[
{
"convert_from":"test_models/densenet/densenet.pb",
"convert_to":"converted_models/densenet_converted.onnx",
"input_nodes":"Placeholder:0",
"output_nodes":"softmax_tensor:0,ArgMax:0",
"skip": "False"
},
{
"convert_from":"test_models/squeezenet.pb",
"convert_to":"converted_models/squeezenet_converted.onnx",
"input_nodes":"Placeholder:0",
"output_nodes":"softmax_tensor:0",
"skip": "False"
},
{
"convert_from":"test_models/nasnet_mobile.pb",
"convert_to":"converted_models/nasnet_mobile_converted.onnx",
"input_nodes":"input:0",
"output_nodes":"final_layer/predictions:0",
"skip": "False"
},
{
"convert_from":"test_models/inception_v3.pb",
"convert_to":"converted_models/inception_v3_converted.onnx",
"input_nodes":"input:0",
"output_nodes":"InceptionV3/Predictions/Reshape_1:0",
"skip": "False"
},
{
"convert_from":"test_models/mobilenet_v1_0.25_224/mobilenet_v1_0.25_224_frozen.pb",
"convert_to":"converted_models/mobilenet_v1_0.25_224_converted.onnx",
"input_nodes":"input:0",
"output_nodes":"MobilenetV1/Predictions/Reshape_1:0",
"skip": "False"
},
{
"convert_from":"test_models/ssd_mobilenet_v1_coco_2018_01_28/frozen_inference_graph.pb",
"convert_to":"converted_models/ssd_mobilenet_v1_converted.onnx",
"input_nodes":"image_tensor:0",
"output_nodes":"detection_boxes:0,detection_scores:0,num_detections:0,detection_classes:0",
"input_shape":"1,224,224,3",
"skip": "False"
},
{
"convert_from":"test_models/ssd_mobilenet_v2_coco_2018_03_29/frozen_inference_graph.pb",
"convert_to":"converted_models/ssd_mobilenet_v2_converted.onnx",
"input_nodes":"image_tensor:0",
"output_nodes":"num_detections:0,detection_classes:0,detection_scores:0,detection_boxes:0",
"skip": "False"
}
]
================================================
FILE: Examples/converter_examples/Tested_Models/conversion_jsons/convert_protobuf_to_tflite.json
================================================
[
{
"convert_from":"test_models/densenet/densenet.pb",
"convert_to":"converted_models/densenet_converted.tflite",
"input_nodes":"Placeholder",
"output_nodes":"softmax_tensor:0,ArgMax:0",
"skip": "False"
},
{
"convert_from":"test_models/squeezenet.pb",
"convert_to":"converted_models/squeezenet_converted.tflite",
"input_nodes":"Placeholder",
"output_nodes":"softmax_tensor:0",
"skip": "False"
},
{
"convert_from":"test_models/nasnet_mobile.pb",
"convert_to":"converted_models/nasnet_mobile_converted.tflite",
"input_nodes":"input:0",
"output_nodes":"final_layer/predictions:0",
"skip": "False"
},
{
"convert_from":"test_models/inception_v3.pb",
"convert_to":"converted_models/inception_v3_converted.tflite",
"input_nodes":"input:0",
"output_nodes":"InceptionV3/Predictions/Reshape_1:0",
"skip": "False"
},
{
"convert_from":"test_models/mobilenet_v1_0.25_224/mobilenet_v1_0.25_224_frozen.pb",
"convert_to":"converted_models/mobilenet_v1_0.25_224_converted.tflite",
"input_nodes":"input:0",
"output_nodes":"MobilenetV1/Predictions/Reshape_1:0",
"input_shape": "1,224,224,3",
"skip": "False"
},
{
"convert_from":"test_models/ssd_mobilenet_v1_coco_2018_01_28/frozen_inference_graph.pb",
"convert_to":"converted_models/ssd_mobilenet_v1_converted.tflite",
"input_nodes":"image_tensor:0",
"output_nodes":"detection_boxes:0,detection_scores:0,num_detections:0,detection_classes:0",
"input_shape": "1,224,224,3",
"skip": "True"
},
{
"convert_from":"test_models/ssd_mobilenet_v2_coco_2018_03_29/frozen_inference_graph.pb",
"convert_to":"converted_models/ssd_mobilenet_v2_converted.tflite",
"input_nodes":"image_tensor:0",
"output_nodes":"num_detections:0,detection_classes:0,detection_scores:0,detection_boxes:0",
"input_shape": "1,224,224,3",
"skip": "True"
}
]
================================================
FILE: Examples/converter_examples/Tested_Models/conversion_jsons/convert_pytorch_to_onnx.json
================================================
[
{
"convert_from":"test_models/resnet18.pt",
"convert_to":"converted_models/resnet_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/alexnet.pt",
"convert_to":"converted_models/alexnet_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/squeezenet1_0.pt",
"convert_to":"converted_models/squeezenet1_0_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/densenet161.pt",
"convert_to":"converted_models/densenet161_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/googlenet.pt",
"convert_to":"converted_models/googlenet_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/shufflenet_v2_x1_0.pt",
"convert_to":"converted_models/shufflenet_v2_x1_0_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/mobilenet_v2.pt",
"convert_to":"converted_models/mobilenet_v2_x1_0_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/resnext50_32x4d.pt",
"convert_to":"converted_models/resnext50_32x4d_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/wide_resnet50_2.pt",
"convert_to":"converted_models/wide_resnet50_2_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/mnasnet1_0.pt",
"convert_to":"converted_models/mnasnet1_0_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/inception_v3.pt",
"convert_to":"converted_models/inception_v3_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,299,299",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/fcn_resnet50.pt",
"convert_to":"converted_models/fcn_resnet50_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Segmentation",
"skip":"False"
},
{
"convert_from":"test_models/deeplabv3_resnet50.pt",
"convert_to":"converted_models/deeplabv3_resnet50_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Segmentation",
"skip":"False"
},
{
"convert_from":"test_models/r3d_18.pt",
"convert_to":"converted_models/r3d_18_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,16,112,112",
"network_name":"Test_Model_Video",
"skip":"False"
},
{
"convert_from":"test_models/mc3_18.pt",
"convert_to":"converted_models/mc3_18_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,16,112,112",
"network_name":"Test_Model_Video",
"skip":"False"
},
{
"convert_from":"test_models/r2plus1d_18.pt",
"convert_to":"converted_models/r2plus1d_18_torch.onnx",
"network_script":"create_test_models.py",
"input_shape":"1,3,16,112,112",
"network_name":"Test_Model_Video",
"skip":"False"
}
]
================================================
FILE: Examples/converter_examples/Tested_Models/conversion_jsons/convert_pytorch_to_tflite.json
================================================
[
{
"convert_from":"test_models/resnet18.pt",
"convert_to":"converted_models/resnet_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/alexnet.pt",
"convert_to":"converted_models/alexnet_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/squeezenet1_0.pt",
"convert_to":"converted_models/squeezenet1_0_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/densenet161.pt",
"convert_to":"converted_models/densenet161_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/googlenet.pt",
"convert_to":"converted_models/googlenet_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/shufflenet_v2_x1_0.pt",
"convert_to":"converted_models/shufflenet_v2_x1_0_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/mobilenet_v2.pt",
"convert_to":"converted_models/mobilenet_v2_x1_0_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/resnext50_32x4d.pt",
"convert_to":"converted_models/resnext50_32x4d_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/wide_resnet50_2.pt",
"convert_to":"converted_models/wide_resnet50_2_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/mnasnet1_0.pt",
"convert_to":"converted_models/mnasnet1_0_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/inception_v3.pt",
"convert_to":"converted_models/inception_v3_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,299,299",
"network_name":"Test_Model_Classification",
"skip":"False"
},
{
"convert_from":"test_models/fcn_resnet50.pt",
"convert_to":"converted_models/fcn_resnet50_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Segmentation",
"skip":"True"
},
{
"convert_from":"test_models/deeplabv3_resnet50.pt",
"convert_to":"converted_models/deeplabv3_resnet50_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,224,224",
"network_name":"Test_Model_Segmentation",
"skip":"True"
},
{
"convert_from":"test_models/r3d_18.pt",
"convert_to":"converted_models/r3d_18_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,16,112,112",
"network_name":"Test_Model_Video",
"skip":"True"
},
{
"convert_from":"test_models/mc3_18.pt",
"convert_to":"converted_models/mc3_18_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,16,112,112",
"network_name":"Test_Model_Video",
"skip":"True"
},
{
"convert_from":"test_models/r2plus1d_18.pt",
"convert_to":"converted_models/r2plus1d_18_torch.tflite",
"network_script":"create_test_models.py",
"input_shape":"1,3,16,112,112",
"network_name":"Test_Model_Video",
"skip":"True"
}
]
================================================
FILE: Examples/converter_examples/Tested_Models/conversion_jsons/convert_savedmodel_to_onnx.json
================================================
[
{
"convert_from":"test_models/resnet_50_TF2",
"convert_to":"converted_models/resnet_50_TF2_converted.onnx",
"skip": "False"
},
{
"convert_from":"test_models/efficientnet_b1_TF2",
"convert_to":"converted_models/efficientnet_b1_TF2_converted.onnx",
"skip": "False"
},
{
"convert_from":"test_models/ssd_mobilenet_v1_coco_2018_01_28/saved_model",
"convert_to":"converted_models/ssd_mobilenet_v1_converted.onnx",
"skip": "False"
},
{
"convert_from":"test_models/ssd_mobilenet_v2_coco_2018_03_29/saved_model",
"convert_to":"converted_models/ssd_mobilenet_v2_converted.onnx",
"skip": "False"
},
{
"convert_from":"test_models/lstm_savedmodel",
"convert_to":"converted_models/lstm_savedmodel_converted.onnx",
"skip": "False"
},
{
"convert_from":"test_models/gru_savedmodel",
"convert_to":"converted_models/gru_savedmodel_converted.onnx",
"skip": "False"
}
]
================================================
FILE: Examples/converter_examples/Tested_Models/conversion_jsons/convert_savedmodel_to_tflite.json
================================================
[
{
"convert_from":"test_models/resnet_50_TF2",
"convert_to":"converted_models/resnet_50_TF2_converted.tflite",
"skip": "False"
},
{
"convert_from":"test_models/efficientnet_b1_TF2",
"convert_to":"converted_models/efficientnet_b1_TF2_converted.tflite",
"skip": "False"
},
{
"convert_from":"test_models/lstm_savedmodel",
"convert_to":"converted_models/lstm_savedmodel_converted.tflite",
"skip": "False"
},
{
"convert_from":"test_models/gru_savedmodel",
"convert_to":"converted_models/gru_savedmodel_converted.tflite",
"skip": "False"
},
{
"convert_from":"test_models/ssd_mobilenet_v1_coco_2018_01_28/saved_model",
"convert_to":"converted_models/ssd_mobilenet_v1_converted.tflite",
"skip": "True"
},
{
"convert_from":"test_models/ssd_mobilenet_v1_coco_2018_01_28/saved_model",
"convert_to":"converted_models/ssd_mobilenet_v1_converted.tflite",
"skip": "True"
}
]
================================================
FILE: Examples/converter_examples/Tested_Models/convert_pb_to_tflite.json
================================================
[
{
"convert_from":"test_models/densenet/densenet.pb",
"convert_to":"converted_models/densenet_converted.tflite",
"input_nodes":"Placeholder",
"output_nodes":"softmax_tensor:0,ArgMax:0"
}
]
================================================
FILE: Examples/converter_examples/Tested_Models/create_test_models.py
================================================
# Copyright (c) 2020 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import torchvision.models as models
import torch
import torch.nn as nn
import tensorflow.keras.applications as applications
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.python.util import deprecation
import string
deprecation._PRINT_DEPRECATION_WARNINGS = False
keras_model_list = [
"VGG16", "ResNet50", "InceptionV3", "MobileNet", "MobileNetV2", "DenseNet121", "NASNetMobile"
]
classification_model_list = [
"resnet18", "alexnet", "squeezenet1_0", "densenet161", "inception_v3",
"googlenet", "shufflenet_v2_x1_0", "mobilenet_v2", "resnext50_32x4d",
"wide_resnet50_2", "mnasnet1_0"
]
segmentation_model_list = [
"fcn_resnet50", "deeplabv3_resnet50"
]
video_model_list = [
"r3d_18", "mc3_18", "r2plus1d_18"
]
class Test_Model_Classification(object):
def __init__(self, model_name):
self.func = getattr(models, model_name)
def create_model(self):
model = self.func()
return model
class Test_Model_Segmentation(object):
def __init__(self, model_name):
self.func = getattr(models.segmentation, model_name)
def create_model(self):
model = self.func()
return model
class Test_Model_Video(object):
def __init__(self, model_name):
self.func = getattr(models.video, model_name)
def create_model(self):
model = self.func()
return model
def create_torch_cnn_models():
for model_name in classification_model_list:
if model_name == "inception_v3":
x = torch.randn(1, 3, 299, 299)
else:
x = torch.randn(1, 3, 224, 224)
print("Creating model", model_name)
model = Test_Model_Classification(model_name).create_model()
model.eval()
y = model(x)
save_path = "test_models/" + model_name + ".pt"
torch.save(model, save_path)
print("Created Pytorch model saved to", save_path)
for model_name in segmentation_model_list:
x = torch.randn(1, 3, 224, 224)
print("Creating model", model_name)
model = Test_Model_Segmentation(model_name).create_model()
model.eval()
y = model(x)
save_path = "test_models/" + model_name + ".pt"
torch.save(model, save_path)
print("Created Pytorch model saved to", save_path)
for model_name in video_model_list:
x = torch.randn(1, 3, 16, 112, 112)
print("Creating model", model_name)
model = Test_Model_Video(model_name).create_model()
model.eval()
y = model(x)
save_path = "test_models/" + model_name + ".pt"
torch.save(model, save_path)
print("Created Pytorch model saved to", save_path)
def create_keras_cnn_models():
for cls_name in keras_model_list:
model_func = getattr(applications, cls_name)
model = model_func(weights = None)
save_path = "test_models/" + cls_name + ".h5"
model.save(save_path)
print("Created Keras model saved to", save_path)
def create_tf_lstm_model():
tf.compat.v1.disable_eager_execution()
# Create a test lstm network based on Pytorch doc
model = keras.Sequential()
# Add an Embedding layer expecting input vocab of size 1000, and
# output embedding dimension of size 64.
model.add(layers.Embedding(input_dim=1000, output_dim=64))
model.add(layers.LSTM(128))
model.add(layers.Dense(10))
# save checkpoint
model.save_weights("test_models/lstm_ckpt/ckpt")
ckpt_meta = "test_models/lstm_ckpt/ckpt.meta"
tf.compat.v1.train.export_meta_graph(filename=ckpt_meta)
# save keras model
model.save("test_models/lstm.h5")
# save TF savedmodel
model.save("test_models/lstm_savedmodel", save_format='tf')
# print model input/output names
print("lstm model input name:", model.input)
print("lstm model output name:",model.output)
def create_tf_gru_model():
tf.compat.v1.disable_eager_execution()
# Create a test rnn network based on Pytorch doc
model = keras.Sequential()
model.add(layers.Embedding(input_dim=1000, output_dim=64))
# The output of GRU will be a 3D tensor of shape (batch_size, timesteps, 256)
model.add(layers.GRU(256, return_sequences=True))
# The output of SimpleRNN will be a 2D tensor of shape (batch_size, 128)
model.add(layers.SimpleRNN(128))
model.add(layers.Dense(10))
# save keras model
model.save("test_models/gru_TF1.h5")
# save TF savedmodel
model.save("test_models/gru_savedmodel",save_format='tf')
# print model input/output names
print("gru model input name:", model.input)
print("gru model output name:", model.output)
if __name__ == "__main__":
create_torch_cnn_models()
create_keras_cnn_models()
create_tf_lstm_model()
create_tf_gru_model()
================================================
FILE: Examples/converter_examples/Tested_Models/download_models.py
================================================
# Copyright (c) 2020 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import os
import json
import argparse
import logging
import requests
import shutil
def download_all_url(url_file, model_folder):
try:
with open(url_file) as f:
all_urls = json.load(f)
except Exception as e:
logging.fatal("The url json file could not be opened")
raise e
for model_type in all_urls:
url_list = all_urls[model_type]
for model_name in url_list:
url = url_list[model_name]
# print(url)
tarFilename = url.split('/')[-1]
# check if the tar file already exists on disk
# if not tarFilename[0].isdigit() and os.path.isfile(tarFilename):
if os.path.isfile(tarFilename):
print ("File already exists, skipping ", url)
continue
print("Downloading ", url)
response = requests.get(url, stream=True)
if response.status_code == 200:
with open(tarFilename, 'wb') as f:
f.write(response.raw.read())
if tarFilename[0].isdigit():
save_folder = model_folder + "/" + model_name
else:
save_folder = model_folder
shutil.unpack_archive(tarFilename, save_folder)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("--url_file", required=True, help="Json file with urls for downloading models")
parser.add_argument("--model_folder", default="test_models", help="folder path for saving downloaded models")
args, unknownargs = parser.parse_known_args()
download_all_url(args.url_file, "test_models")
================================================
FILE: Examples/converter_examples/Tested_Models/download_urls.json
================================================
{
"protobuf_checkpoint":{
"densenet": "https://storage.googleapis.com/download.tensorflow.org/models/tflite/model_zoo/upload_20180427/densenet_2018_04_27.tgz",
"squeezenet": "https://storage.googleapis.com/download.tensorflow.org/models/tflite/model_zoo/upload_20180427/squeezenet_2018_04_27.tgz",
"nasnet_mobile": "https://storage.googleapis.com/download.tensorflow.org/models/tflite/model_zoo/upload_20180427/nasnet_mobile_2018_04_27.tgz",
"inception_v3": "https://storage.googleapis.com/download.tensorflow.org/models/tflite/model_zoo/upload_20180427/inception_v3_2018_04_27.tgz",
"mobilenet_v1_0.25_224": "https://storage.googleapis.com/download.tensorflow.org/models/mobilenet_v1_2018_02_22/mobilenet_v1_0.25_224.tgz",
"ssd_mobilenet_v1": "http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_coco_2018_01_28.tar.gz",
"ssd_mobilenet_v2": "http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v2_coco_2018_03_29.tar.gz"
},
"savedmodel":{
"resnet_50_TF2": "https://storage.googleapis.com/tfhub-modules/tensorflow/resnet_50/classification/1.tar.gz",
"efficientnet_b1_TF2": "https://storage.googleapis.com/tfhub-modules/tensorflow/efficientnet/b1/feature-vector/1.tar.gz"
}
}
================================================
FILE: Examples/converter_examples/dlconvert_requirements.txt
================================================
keras2onnx==1.7.0
tf2onnx
pytest
pytest-cov
torch
pytorch2keras
================================================
FILE: Examples/decorator_example/origin.py
================================================
#----------------------------------
def func(hyperparameter):
return 'training results'
#----------------------------------
import tensorflow as tf
FLAGS = tf.app.flags.FLAGS
# some arguments defined in flags
def main(unused):
return "training results"
if __name__ == "__main__":
tf.app.run()
#----------------------------------
================================================
FILE: Examples/decorator_example/use_decorator.py
================================================
#----------------------------------
#!/usr/bin/env python
import sys
from aup import aup_args
@aup_args
def func(hyperparameter):
return 'training results'
if __name__ == "__main__":
func(sys.argv[1])
#----------------------------------
#!/usr/bin/env python
import tensorflow as tf
from aup import aup_flags
FLAGS = tf.app.flags.FLAGS
# some arguments defined in flags
@aup_flags(FLAGS)
def main():
return "training results"
if __name__ == "__main__":
tf.app.run()
#----------------------------------
================================================
FILE: Examples/demo/.gitignore
================================================
env_template.ini
hpo.py
.aup/
experiment.json
================================================
FILE: Examples/demo/demo.yml
================================================
# The configurations that used for the recording, feel free to edit them
config:
# Specify a command to be executed
# like `/bin/bash -l`, `ls`, or any other commands
# the default is bash for Linux
# or powershell.exe for Windows
command: bash -l
# Specify the current working directory path
# the default is the current working directory path
cwd: ./Examples/demo
# Export additional ENV variables
env:
recording: true
# Explicitly set the number of columns
# or use `auto` to take the current
# number of columns of your shell
cols: 105
# Explicitly set the number of rows
# or use `auto` to take the current
# number of rows of your shell
rows: 24
# Amount of times to repeat GIF
# If value is -1, play once
# If value is 0, loop indefinitely
# If value is a positive number, loop n times
repeat: 0
# Quality
# 1 - 100
quality: 80
# Delay between frames in ms
# If the value is `auto` use the actual recording delays
frameDelay: auto
# Maximum delay between frames in ms
# Ignored if the `frameDelay` isn't set to `auto`
# Set to `auto` to prevent limiting the max idle time
maxIdleTime: 2000
# The surrounding frame box
# The `type` can be null, window, floating, or solid`
# To hide the title use the value null
# Don't forget to add a backgroundColor style with a null as type
frameBox:
type: solid
title: LGE-ARC-AdvancedAI/auptimizer
style:
border: 0px black solid
# boxShadow: none
# margin: 0px
# Add a watermark image to the rendered gif
# You need to specify an absolute path for
# the image on your machine or a URL, and you can also
# add your own CSS styles
watermark:
imagePath: /Users/jason.liu/.terminalizer/SVL-aai.png
style:
position: absolute
right: 15px
bottom: 15px
width: 100px
opacity: 0.9
# Cursor style can be one of
# `block`, `underline`, or `bar`
cursorStyle: block
# Font family
# You can use any font that is installed on your machine
# in CSS-like syntax
fontFamily: "Monaco, Lucida Console, Ubuntu Mono, Monospace"
# The size of the font
fontSize: 24
# The height of lines
lineHeight: 1
# The spacing between letters
letterSpacing: 0
# Theme
theme:
background: "transparent"
foreground: "#afafaf"
cursor: "#c7c7c7"
black: "#232628"
red: "#fc4384"
green: "#b3e33b"
yellow: "#ffa727"
blue: "#75dff2"
magenta: "#ae89fe"
cyan: "#708387"
white: "#d5d5d0"
brightBlack: "#626566"
brightRed: "#ff7fac"
brightGreen: "#c8ed71"
brightYellow: "#ebdf86"
brightBlue: "#75dff2"
brightMagenta: "#ae89fe"
brightCyan: "#b1c6ca"
brightWhite: "#f9f9f4"
# Records, feel free to edit them
records:
- delay: 476
content: "\e[H\e[2J"
- delay: 10
content: "\e]1337;RemoteHost=local\a\e]1337;CurrentDir=./Examples/demo\a\e]1337;ShellIntegrationVersion=13;shell=bash\a"
- delay: 12
content: "\e]133;C;\a\e]1337;RemoteHost=local\a\e]1337;CurrentDir=./Examples/demo\a\e]133;D;0\a\e]133;A\a\e[0;35m>demo$> \e[0m\e]133;B\a"
- delay: 1093
content: l
- delay: 96
content: s
- delay: 232
content: "\r\n"
- delay: 13
content: "\e]133;C;\a\e[34m.\e[m\e[m \e[34m..\e[m\e[m origin.py\r\n"
- delay: 5
content: "\e]1337;RemoteHost=local\a\e]1337;CurrentDir=./Examples/demo\a\e]133;D;0\a\e]133;A\a\e[0;35m>demo$> \e[0m\e]133;B\a"
- delay: 1112
content: p
- delay: 152
content: 'y'
- delay: 48
content: t
- delay: 144
content: h
- delay: 57
content: o
- delay: 128
content: 'n'
- delay: 135
content: ' '
- delay: 112
content: '-'
- delay: 200
content: m
- delay: 103
content: ' '
- delay: 162
content: a
- delay: 87
content: u
- delay: 89
content: p
- delay: 304
content: .
- delay: 191
content: s
- delay: 65
content: e
- delay: 111
content: t
- delay: 137
content: u
- delay: 64
content: p
- delay: 439
content: "\r\n"
- delay: 15
content: "\e]133;C;\a"
- delay: 130
content: "\e[1;30mINFO\e[0m - Using default user jason.liu\r\nEnvironment file, or hit ENTER to fill in details:"
- delay: 703
content: "\r\nAuptimizer Environment path - Auptimizer_PATH (.aup):"
- delay: 296
content: "\r\nAuptimizer Temp folder - TMP_FOLDER (/tmp/auptmp):"
- delay: 233
content: "\r\nNumber of CPUs (4):"
- delay: 207
content: "\r\nGPU template file or comma-separated IDs (none):"
- delay: 200
content: "\r\nNode template file (none) or comma-separated nodes (user@ip:[port] [keyfile]):"
- delay: 216
content: "\r\nAWS template file (none) or comma-separated nodes (user@ip):[port] [keyfile])"
- delay: 416
content: "\r\nUsername (jason.liu):"
- delay: 431
content: "\r\nOverwrite (y/N)"
- delay: 423
content: "\r\n\e[1;30mINFO\e[0m - Write env.ini to .aup/env.ini\r\n"
- delay: 138
content: "\e[32m2019-07-25 14:33:46\e[0m - \e[34maup.setupdb.sqlite\e[0m - \e[1;30mINFO\e[0m - SQLite3 Database is created at .aup/sqlite3.db\r\n"
- delay: 11
content: "\e[1;30mINFO\e[0m - Following commands have finished automatically\r\n/usr/local/opt/python/bin/python3.7 -m aup.setupdb.sqlite .aup/env.ini --user jason.liu --cpu 4 --log info\r\n"
- delay: 20
content: "\e]1337;RemoteHost=local\a\e]1337;CurrentDir=./Examples/demo\a\e]133;D;0\a\e]133;A\a\e[0;35m>demo$> \e[0m\e]133;B\a"
- delay: 1316
content: l
- delay: 120
content: s
- delay: 144
content: ' '
- delay: 472
content: .
- delay: 207
content: a
- delay: 104
content: u
- delay: 104
content: p
- delay: 256
content: "\r\n"
- delay: 14
content: "\e]133;C;\a\e[34m.\e[m\e[m \e[34m..\e[m\e[m env.ini sqlite3.db\r\n"
- delay: 5
content: "\e]1337;RemoteHost=local\a\e]1337;CurrentDir=./Examples/demo\a\e]133;D;0\a\e]133;A\a\e[0;35m>demo$> \e[0m\e]133;B\a"
- delay: 248
content: m
- delay: 88
content: o
- delay: 79
content: r
- delay: 65
content: e
- delay: 81
content: ' '
- delay: 191
content: .
- delay: 201
content: a
- delay: 103
content: u
- delay: 88
content: p
- delay: 264
content: /
- delay: 198
content: e
- delay: 74
content: 'n'
- delay: 217
content: 'v.ini '
- delay: 184
content: "\r\n"
- delay: 17
content: "\e]133;C;\a\e[?1h\e=\r[Auptimizer]\r\nAuptimizer_PATH = .aup\r\nTMP_FOLDER = /tmp/auptmp\r\nSQL_ENGINE = sqlite\r\nSQLITE_FILE = .aup/sqlite3.db\r\n\r\n\e[7m.aup/env.ini (END)\e[m\e[K"
- delay: 826
content: "\r\e[K\e[?1l\e>"
- delay: 5
content: "\e]1337;RemoteHost=local\a\e]1337;CurrentDir=./Examples/demo\a\e]133;D;0\a\e]133;A\a\e[0;35m>demo$> \e[0m\e]133;B\a"
- delay: 977
content: p
- delay: 185
content: 'y'
- delay: 71
content: t
- delay: 136
content: h
- delay: 48
content: o
- delay: 144
content: 'n'
- delay: 136
content: ' '
- delay: 200
content: '-'
- delay: 457
content: m
- delay: 126
content: ' '
- delay: 1089
content: a
- delay: 120
content: u
- delay: 89
content: p
- delay: 304
content: .
- delay: 350
content: i
- delay: 177
content: 'n'
- delay: 130
content: i
- delay: 230
content: t
- delay: 433
content: "\r\n"
- delay: 13
content: "\e]133;C;\a"
- delay: 122
content: "\e[34maup.utils\e[0m - \e[1;30mINFO\e[0m - Auptimizer environment at .aup\r\n"
- delay: 6
content: "\e[34maup.utils\e[0m - \e[1;30mINFO\e[0m - Use default connector at .aup/sqlite3.db\r\nscript name []:"
- delay: 626
content: h
- delay: 96
content: p
- delay: 88
content: o
- delay: 264
content: .
- delay: 192
content: p
- delay: 144
content: 'y'
- delay: 296
content: "\r\ncomputing resource for experiment [cpu]:"
- delay: 1128
content: "\r\nNumber of parallel execution on cpu, up to 4 [1]:"
- delay: 1159
content: '2'
- delay: 191
content: "\r\nmax/min the score [max]:"
- delay: 1361
content: m
- delay: 120
content: i
- delay: 193
content: 'n'
- delay: 591
content: "\r\nworking path, [default:./Examples/demo]:"
- delay: 823
content: "\r\nproposer [random]:"
- delay: 1385
content: "\r\n"
- delay: 113
content: 'number of jobs [1]:'
- delay: 1862
content: '1'
- delay: 224
content: '0'
- delay: 208
content: "\r\nrandom seed [0]"
- delay: 927
content: "\r\nstart adding hyperparameters, use 'stop' for variable name or ctrl+c to exit\r\nname:"
- delay: 1022
content: x
- delay: 273
content: "\r\nrange (separated by ,):"
- delay: 1512
content: '0'
- delay: 280
content: ','
- delay: 752
content: '5'
- delay: 225
content: "\r\ntype: [choice]:"
- delay: 1263
content: f
- delay: 144
content: l
- delay: 168
content: o
- delay: 64
content: a
- delay: 104
content: t
- delay: 80
content: "\r\nname:"
- delay: 358
content: s
- delay: 113
content: t
- delay: 112
content: o
- delay: 584
content: p
- delay: 288
content: "\r\n\e[34maup.init\e[0m - \e[1;30mINFO\e[0m - Write experiment config to experiment.json\r\n"
- delay: 28
content: "\e]1337;RemoteHost=local\a\e]1337;CurrentDir=./Examples/demo\a\e]133;D;0\a\e]133;A\a\e[0;35m>demo$> \e[0m\e]133;B\a"
- delay: 602
content: m
- delay: 64
content: o
- delay: 49
content: r
- delay: 71
content: e
- delay: 113
content: ' '
- delay: 2554
content: "\r\n.aup/ experiment.json origin.py \r\n\e]133;D;0\a\e]133;A\a\e[0;35m>demo$> \e[0m\e]133;B\amore "
- delay: 168
content: "\r\n.aup/ experiment.json origin.py \r\n\e]133;D;0\a\e]133;A\a\e[0;35m>demo$> \e[0m\e]133;B\amore "
- delay: 864
content: o
- delay: 303
content: r
- delay: 105
content: i
- delay: 136
content: 'gin.py '
- delay: 199
content: "\r\n"
- delay: 13
content: "\e]133;C;\a\e[?1h\e=\r# Demonstration code for 1D function, centered around a=1\r\nfrom time import sleep\r\n\r\n\r\ndef demo_func(x, a=1, b=0):\r\n sleep(1)\r\n return (x-a)*(x-a)+b\r\n\r\n\e[7morigin.py (END)\e[m\e[K"
- delay: 1609
content: "\r\e[K\e[?1l\e>"
- delay: 5
content: "\e]1337;RemoteHost=local\a\e]1337;CurrentDir=./Examples/demo\a\e]133;D;0\a\e]133;A\a\e[0;35m>demo$> \e[0m\e]133;B\a"
- delay: 2168
content: p
- delay: 241
content: 'y'
- delay: 183
content: t
- delay: 56
content: h
- delay: 103
content: o
- delay: 184
content: 'n'
- delay: 688
content: ' '
- delay: 136
content: '-'
- delay: 257
content: m
- delay: 151
content: ' '
- delay: 760
content: a
- delay: 128
content: u
- delay: 104
content: p
- delay: 329
content: .
- delay: 193
content: c
- delay: 55
content: o
- delay: 113
content: 'n'
- delay: 55
content: v
- delay: 217
content: e
- delay: 79
content: r
- delay: 168
content: t
- delay: 129
content: ' '
- delay: 656
content: o
- delay: 208
content: r
- delay: 111
content: i
- delay: 136
content: g
- delay: 88
content: i
- delay: 47
content: 'n'
- delay: 273
content: .
- delay: 208
content: 'py '
- delay: 568
content: e
- delay: 247
content: x
- delay: 120
content: p
- delay: 128
content: e
- delay: 97
content: r
- delay: 176
content: 'iment.json '
- delay: 663
content: d
- delay: 329
content: e
- delay: 415
content: m
- delay: 96
content: o
- delay: 255
content: _
- delay: 219
content: f
- delay: 87
content: u
- delay: 199
content: 'n'
- delay: 112
content: c
- delay: 457
content: "\r\n"
- delay: 14
content: "\e]133;C;\a"
- delay: 106
content: "\e]1337;RemoteHost=local\a\e]1337;CurrentDir=./Examples/demo\a\e]133;D;0\a\e]133;A\a\e[0;35m>demo$> \e[0m\e]133;B\a"
- delay: 669
content: l
- delay: 80
content: s
- delay: 120
content: "\r\n"
- delay: 14
content: "\e]133;C;\a\e[34m.\e[m\e[m \e[34m..\e[m\e[m \e[34m.aup\e[m\e[m experiment.json \e[31mhpo.py\e[m\e[m origin.py\r\n\e]1337;RemoteHost=local\a\e]1337;CurrentDir=./Examples/demo\a\e]133;D;0\a\e]133;A\a\e[0;35m>demo$> \e[0m\e]133;B\a"
- delay: 497
content: m
- delay: 79
content: o
- delay: 32
content: r
- delay: 89
content: e
- delay: 95
content: ' '
- delay: 280
content: h
- delay: 96
content: p
- delay: 64
content: o
- delay: 281
content: .
- delay: 184
content: p
- delay: 111
content: 'y'
- delay: 248
content: "\r\n"
- delay: 13
content: "\e]133;C;\a\e[?1h\e=\r#!/usr/bin/env python\r\n# Demonstration code for 1D function, centered around a=1\r\nfrom time import sleep\r\n\r\n\r\ndef demo_func(x, a=1, b=0):\r\n sleep(1)\r\n return (x-a)*(x-a)+b\r\n\r\n\r\ndef aup_wrapper(config):\r\n res = demo_func(x=config['x']) \r\n print_result(res)\r\n\r\nif __name__ == \"__main__\":\r\n import sys\r\n from aup import BasicConfig, print_result\r\n if len(sys.argv) != 2:\r\n print(\"config file required\")\r\n exit(1)\r\n config = BasicConfig().load(sys.argv[1])\r\n aup_wrapper(config)\r\n\e[7mhpo.py (END)\e[m\e[K"
- delay: 2905
content: "\r\e[K\e[?1l\e>"
- delay: 5
content: "\e]1337;RemoteHost=local\a\e]1337;CurrentDir=./Examples/demo\a\e]133;D;0\a\e]133;A\a\e[0;35m>demo$> \e[0m\e]133;B\a"
- delay: 808
content: p
- delay: 160
content: 'y'
- delay: 96
content: t
- delay: 120
content: h
- delay: 72
content: o
- delay: 136
content: 'n'
- delay: 127
content: ' '
- delay: 129
content: '-'
- delay: 215
content: m
- delay: 129
content: ' '
- delay: 327
content: a
- delay: 113
content: u
- delay: 103
content: p
- delay: 265
content: .
- delay: 1072
content: "\b\e[K"
- delay: 96
content: ' '
- delay: 1023
content: e
- delay: 216
content: x
- delay: 105
content: p
- delay: 127
content: e
- delay: 120
content: r
- delay: 152
content: i
- delay: 129
content: 'ment.json '
- delay: 816
content: "\r\n"
- delay: 21
content: "\e]133;C;\a"
- delay: 120
content: "\e[32m2019-07-25 14:34:52\e[0m - \e[34mroot\e[0m - \e[1;30mINFO\e[0m - Using default user jason.liu\r\n\e[32m2019-07-25 14:34:52\e[0m - \e[34maup.utils\e[0m - \e[1;30mINFO\e[0m - Auptimizer environment at .aup\r\n"
- delay: 5
content: "\e[32m2019-07-25 14:34:52\e[0m - \e[34maup.utils\e[0m - \e[1;30mINFO\e[0m - Use default connector at .aup/sqlite3.db\r\n"
- delay: 75
content: "\e[32m2019-07-25 14:34:52\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Experiment 1 is created\r\n\e[32m2019-07-25 14:34:52\e[0m - \e[34maup\e[0m - \e[1;30mINFO\e[0m - # Running Experiment\r\n\e[32m2019-07-25 14:34:52\e[0m - \e[34maup.EE.Job\e[0m - \e[1;30mWARNING\e[0m - \e[33mUsing default local path for script as ./hpo.py\e[0m\r\n\e[32m2019-07-25 14:34:52\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Submitting job 1 with resource 2 in experiment 1\r\n\e[32m2019-07-25 14:34:52\e[0m - \e[34maup.EE.Job\e[0m - \e[1;30mWARNING\e[0m - \e[33mCreate missing directory ./Examples/demo/jobs\e[0m\r\n"
- delay: 5
content: "\e[32m2019-07-25 14:34:52\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Submitting job 2 with resource 1 in experiment 1\r\n"
- delay: 1090
content: "\e[32m2019-07-25 14:34:54\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Job 1 is finished with result 3.041771513051445\r\n\e[32m2019-07-25 14:34:54\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Submitting job 3 with resource 3 in experiment 1\r\n\e[32m2019-07-25 14:34:54\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Job 2 is finished with result 6.635502080580377\r\n\e[32m2019-07-25 14:34:54\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Submitting job 4 with resource 4 in experiment 1\r\n"
- delay: 1100
content: "\e[32m2019-07-25 14:34:55\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Job 3 is finished with result 4.0554584276157115\r\n\e[32m2019-07-25 14:34:55\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Submitting job 5 with resource 2 in experiment 1\r\n"
- delay: 5
content: "\e[32m2019-07-25 14:34:55\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Job 4 is finished with result 2.973610247851776\r\n\e[32m2019-07-25 14:34:55\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Submitting job 6 with resource 1 in experiment 1\r\n"
- delay: 1100
content: "\e[32m2019-07-25 14:34:56\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Job 5 is finished with result 1.2505367316831435\r\n\e[32m2019-07-25 14:34:56\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Submitting job 7 with resource 4 in experiment 1\r\n\e[32m2019-07-25 14:34:56\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Job 6 is finished with result 4.970539001687499\r\n\e[32m2019-07-25 14:34:56\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Submitting job 8 with resource 3 in experiment 1\r\n"
- delay: 1103
content: "\e[32m2019-07-25 14:34:57\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Job 7 is finished with result 1.4111920738895818\r\n\e[32m2019-07-25 14:34:57\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Submitting job 9 with resource 1 in experiment 1\r\n\e[32m2019-07-25 14:34:57\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Job 8 is finished with result 11.963747115276082\r\n\e[32m2019-07-25 14:34:57\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Submitting job 10 with resource 4 in experiment 1\r\n"
- delay: 1101
content: "\e[32m2019-07-25 14:34:58\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Job 9 is finished with result 14.579520294401313\r\n\e[32m2019-07-25 14:34:58\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Job 10 is finished with result 0.8412697707277041\r\n"
- delay: 488
content: "\e[32m2019-07-25 14:34:58\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mINFO\e[0m - Finished\r\n\e[32m2019-07-25 14:34:58\e[0m - \e[34maup.EE.Experiment\e[0m - \e[1;30mCRITICAL\e[0m - \e[1;31mBest job (10) with score 0.841270 in experiment 1\e[0m\r\n"
- delay: 37
content: "\e]1337;RemoteHost=local\a\e]1337;CurrentDir=./Examples/demo\a\e]133;D;0\a\e]133;A\a\e[0;35m>demo$> \e[0m\e]133;B\a"
- delay: 2077
content: l
- delay: 88
content: s
- delay: 145
content: ' '
- delay: 167
content: j
- delay: 72
content: o
- delay: 96
content: b
- delay: 169
content: s
- delay: 135
content: "\r\n"
- delay: 13
content: "\e]133;C;\a\e[34m.\e[m\e[m 1.json 10.json 2.json 3.json 4.json 5.json 6.json 7.json 8.json 9.json\r\n\e[34m..\e[m\e[m 1.out 10.out 2.out 3.out 4.out 5.out 6.out 7.out 8.out 9.out\r\n"
- delay: 5
content: "\e]1337;RemoteHost=local\a\e]1337;CurrentDir=./Examples/demo\a\e]133;D;0\a\e]133;A\a\e[0;35m>demo$> \e[0m\e]133;B\a"
================================================
FILE: Examples/demo/experiment_wrapper.json
================================================
{
"name": "./demo/experiment_wrapper.json",
"script": "hpo_wrapper.py",
"resource": "cpu",
"n_parallel": 2,
"target": "min",
"workingdir": "./",
"proposer": "random",
"n_samples": 10,
"random_seed": 0,
"parameter_config": [
{
"name": "x",
"range": [
0,
5
],
"type": "float"
}
]
}
================================================
FILE: Examples/demo/hpo_wrapper.py
================================================
#!/usr/bin/env python
# Demonstration code for 1D function, centered around a=1
from time import sleep
from aup import aup_args
import sys
@aup_args
def demo_func(x, a=1, b=0):
sleep(1)
return (x-a)*(x-a)+b
if __name__ == "__main__":
demo_func(sys.argv[1])
================================================
FILE: Examples/demo/origin.py
================================================
# Demonstration code for 1D function, centered around a=1
from time import sleep
def demo_func(x, a=1, b=0):
sleep(1)
return (x-a)*(x-a)+b
================================================
FILE: Examples/early_stopping/README.md
================================================
# Early stopping of jobs
## Usage
In order to use early stopping to accelerate the HPO experiment,
we need to add the following lines in the JSON configuration file:
"resource_args":
{
"early_stop":
{
"aup_policy": "X",
"aup_policy_steps": Y
...
}
}
X can be bandit, median, truncation or curve_fitting.
Y refers to the interval of epochs/iterations by which the ES policy is applied.
In order to report intermediate results, "aup.print_result" should be called from the user script; e.g. at the end of an epoch, at the end of N iterations, etc.
Examples can be found at Examples/early_stopping/quad_equation_min and Examples/early_stopping/mnist_keras.
An additional parameter of "warmup" can also be used for all ES strategies (default is 0). "warmup" defines the number of initial iterations that should finish without an early stopping criterion.
## Strategies
We provide 4 strategies for early stopping: bandit, truncation, median and curve fitting. Please checkout [early stopping document](https://lge-arc-advancedai.github.io/auptimizer/early_stop.html) for detailed usages.
================================================
FILE: Examples/early_stopping/mnist_keras/cpu.ini
================================================
[Auptimizer]
# Auptimizer environment folder to be created, this file will be copied over
Auptimizer_PATH=./.aup
# Temp folder
TMP_FOLDER=./aup_tmp
# SQL engine
SQL_ENGINE=sqlite
================================================
FILE: Examples/early_stopping/mnist_keras/exp_BOHB_bandit.json
================================================
{
"name": "./early_stopping/mnist_keras/exp_BOHB_bandit.json",
"proposer": "bohb",
"script": "mnist.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"n_iterations": 50,
"num_samples": 64,
"random_fraction": 0.3333333333333333,
"bandwidth_factor": 3,
"min_bandwidth": 0.001,
"eta": 3,
"min_budget": 1,
"max_budget": 5,
"resource_args":
{
"early_stop":
{
"aup_policy": "bandit",
"aup_policy_steps": 10,
"bandit_factor": 0.5
}
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
}
]
}
================================================
FILE: Examples/early_stopping/mnist_keras/exp_BOHB_curve_fitting.json
================================================
{
"name": "./early_stopping/mnist_keras/exp_BOHB_curve_fitting.json",
"proposer": "bohb",
"script": "mnist.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"n_iterations": 50,
"num_samples": 64,
"random_fraction": 0.3333333333333333,
"bandwidth_factor": 3,
"min_bandwidth": 0.001,
"eta": 3,
"min_budget": 1,
"max_budget": 5,
"resource_args":
{
"early_stop":
{
"aup_policy": "curve_fitting",
"aup_policy_steps": 10,
"curve_fitting_max_iters": 235,
"curve_fitting_threshold": 0.95,
"curve_fitting_timeout": 60
}
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
}
]
}
================================================
FILE: Examples/early_stopping/mnist_keras/exp_BOHB_median.json
================================================
{
"name": "./early_stopping/mnist_keras/exp_BOHB_median.json",
"proposer": "bohb",
"script": "mnist.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"n_iterations": 50,
"num_samples": 64,
"random_fraction": 0.3333333333333333,
"bandwidth_factor": 3,
"min_bandwidth": 0.001,
"eta": 3,
"min_budget": 1,
"max_budget": 5,
"resource_args":
{
"early_stop":
{
"aup_policy": "median",
"aup_policy_steps": 10
}
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
}
]
}
================================================
FILE: Examples/early_stopping/mnist_keras/exp_BOHB_trunc.json
================================================
{
"name": "./early_stopping/mnist_keras/exp_BOHB_trunc.json",
"proposer": "bohb",
"script": "mnist.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"n_iterations": 50,
"num_samples": 64,
"random_fraction": 0.3333333333333333,
"bandwidth_factor": 3,
"min_bandwidth": 0.001,
"eta": 3,
"min_budget": 1,
"max_budget": 5,
"resource_args":
{
"early_stop":
{
"aup_policy": "truncation",
"aup_policy_steps": 10,
"truncation_percentage": 0.3
}
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
}
]
}
================================================
FILE: Examples/early_stopping/mnist_keras/exp_random_bandit.json
================================================
{
"name": "./early_stopping/mnist_keras/exp_random_bandit.json",
"proposer": "random",
"script": "mnist.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"n_samples": 50,
"resource_args":
{
"early_stop":
{
"aup_policy": "bandit",
"aup_policy_steps": 10,
"bandit_factor": 0.5
}
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
}
]
}
================================================
FILE: Examples/early_stopping/mnist_keras/exp_random_curve_fitting.json
================================================
{
"name": "./early_stopping/mnist_keras/exp_random_curve_fitting.json",
"proposer": "random",
"script": "mnist.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"n_samples": 50,
"resource_args":
{
"early_stop":
{
"aup_policy": "curve_fitting",
"aup_policy_steps": 10,
"curve_fitting_max_iters": 235,
"curve_fitting_threshold": 0.95,
"curve_fitting_timeout": 60
}
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
}
]
}
================================================
FILE: Examples/early_stopping/mnist_keras/exp_random_median.json
================================================
{
"name": "./early_stopping/mnist_keras/exp_random_median.json",
"proposer": "random",
"script": "mnist.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"n_samples": 50,
"resource_args":
{
"early_stop":
{
"aup_policy": "median",
"aup_policy_steps": 10
}
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
}
]
}
================================================
FILE: Examples/early_stopping/mnist_keras/exp_random_trunc.json
================================================
{
"name": "./early_stopping/mnist_keras/exp_random_trunc.json",
"proposer": "random",
"script": "mnist.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"n_samples": 50,
"resource_args":
{
"early_stop":
{
"aup_policy": "truncation",
"aup_policy_steps": 10,
"truncation_percentage": 0.3
}
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
}
]
}
================================================
FILE: Examples/early_stopping/mnist_keras/exp_spearmint_bandit.json
================================================
{
"name": "./early_stopping/mnist_keras/exp_spearmint_bandit.json",
"proposer": "spearmint",
"script": "mnist.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"engine":"GPEIChooser",
"n_samples": 50,
"resource_args":
{
"early_stop":
{
"aup_policy": "bandit",
"aup_policy_steps": 10,
"bandit_factor": 0.5
}
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
}
]
}
================================================
FILE: Examples/early_stopping/mnist_keras/exp_spearmint_curve_fitting.json
================================================
{
"name": "./early_stopping/mnist_keras/exp_spearmint_curve_fitting.json",
"proposer": "spearmint",
"script": "mnist.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"engine":"GPEIChooser",
"n_samples": 50,
"resource_args":
{
"early_stop":
{
"aup_policy": "curve_fitting",
"aup_policy_steps": 10,
"curve_fitting_max_iters": 235,
"curve_fitting_threshold": 0.95,
"curve_fitting_timeout": 60
}
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
}
]
}
================================================
FILE: Examples/early_stopping/mnist_keras/exp_spearmint_median.json
================================================
{
"name": "./early_stopping/mnist_keras/exp_spearmint_median.json",
"proposer": "spearmint",
"script": "mnist.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"engine":"GPEIChooser",
"n_samples": 50,
"resource_args":
{
"early_stop":
{
"aup_policy": "median",
"aup_policy_steps": 10
}
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
}
]
}
================================================
FILE: Examples/early_stopping/mnist_keras/exp_spearmint_trunc.json
================================================
{
"name": "./early_stopping/mnist_keras/exp_spearmint_trunc.json",
"proposer": "spearmint",
"script": "mnist.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"engine":"GPEIChooser",
"n_samples": 50,
"resource_args":
{
"early_stop":
{
"aup_policy": "truncation",
"aup_policy_steps": 10,
"truncation_percentage": 0.3
}
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
}
]
}
================================================
FILE: Examples/early_stopping/mnist_keras/mnist.py
================================================
#!/usr/bin/env python3
"""
MNIST convolutional network using Keras
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import aup
import tensorflow as tf
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
from math import log
import sys
num_epochs = 5
batch_size = 64
num_classes = 10
input_shape = (28, 28, 1)
# Load example MNIST data and pre-process it
# the data, split between train and test sets
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
# Scale images to the [0, 1] range
x_train = x_train.astype("float32") / 255
x_test = x_test.astype("float32") / 255
# Make sure images have shape (28, 28, 1)
x_train = np.expand_dims(x_train, -1)
x_test = np.expand_dims(x_test, -1)
# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
def get_flops():
run_meta = tf.RunMetadata()
opts = tf.profiler.ProfileOptionBuilder.float_operation()
# We use the Keras session graph in the call to the profiler.
flops = tf.profiler.profile(graph=keras.backend.get_session().graph,
run_meta=run_meta, cmd='op', options=opts)
return flops.total_float_ops # Prints the "flops" of the model.
def get_model(**kwargs):
model = keras.Sequential(
[
keras.Input(shape=input_shape),
layers.Conv2D(kwargs['conv1'], kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Conv2D(kwargs['conv2'], kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Flatten(),
layers.Dropout(kwargs['dropout']),
layers.Dense(num_classes, activation="softmax"),
]
)
model.compile(loss="categorical_crossentropy",
optimizer=keras.optimizers.Adam(learning_rate=kwargs['learning_rate']),
metrics=["accuracy"])
return model
class CustomCallback(keras.callbacks.Callback):
flops = None
def on_train_batch_end(self, batch, logs=None):
if batch % 10 == 0:
val_acc = logs["acc"]
aup.print_result((val_acc-1.0) / (log(self.flops)))
def on_train_begin(self, logs=None):
self.flops = get_flops()
def init(**kwargs):
global model
model = get_model(**kwargs)
@aup.aup_args
def do_train(learning_rate=0.001, dropout=0.1, conv1=32, conv2=64):
global model
model.fit(
x_train,
y_train,
batch_size=batch_size,
epochs=num_epochs,
verbose=1,
validation_split=0.5,
callbacks=[CustomCallback()],
)
res = model.evaluate(
x_test,
y_test,
batch_size=batch_size,
verbose=1,
)
flops = get_flops()
return (res[1]-1.0) / log(flops)
if __name__ == '__main__':
if len(sys.argv) < 2:
print("config file required")
exit(1)
do_train(sys.argv[1])
================================================
FILE: Examples/early_stopping/quad_equation_min/cpu.ini
================================================
[Auptimizer]
# Auptimizer environment folder to be created, this file will be copied over
Auptimizer_PATH=./.aup
# Temp folder
TMP_FOLDER=./aup_tmp
# SQL engine
SQL_ENGINE=sqlite
================================================
FILE: Examples/early_stopping/quad_equation_min/quad_min.py
================================================
#!/usr/bin/env python
"""
Modified Rosenbrock function for HPO and aup
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import sys
import aup
from time import sleep
def rosenbrock(x, a=2, b=4, c=5):
sleep(1)
it = 1.0
for _ in range(10):
res = x*x*a + x*b + c
res += it
it -= 1.0 / 10
aup.print_result(res)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("config file required")
exit(1)
config = aup.BasicConfig().load(sys.argv[1])
rosenbrock(config['x'])
================================================
FILE: Examples/early_stopping/quad_equation_min/quad_min_BOHB_bandit.json
================================================
{
"name": "./early_stopping/quad_equation_min/quad_min_BOHB_bandit.json",
"proposer": "bohb",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_iterations": 100,
"num_samples": 64,
"random_fraction": 0.3333333333333333,
"bandwidth_factor": 3,
"min_bandwidth": 0.001,
"eta": 3,
"min_budget": 1,
"max_budget": 5,
"resource_args":
{
"early_stop":
{
"aup_policy": "bandit",
"aup_policy_steps": 1,
"bandit_factor": 0.5
}
},
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/early_stopping/quad_equation_min/quad_min_BOHB_median.json
================================================
{
"name": "./early_stopping/quad_equation_min/quad_min_BOHB_median.json",
"proposer": "bohb",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_iterations": 100,
"num_samples": 64,
"random_fraction": 0.3333333333333333,
"bandwidth_factor": 3,
"min_bandwidth": 0.001,
"eta": 3,
"min_budget": 1,
"max_budget": 5,
"resource_args":
{
"early_stop":
{
"aup_policy": "median",
"aup_policy_steps": 1
}
},
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/early_stopping/quad_equation_min/quad_min_BOHB_trunc.json
================================================
{
"name": "./early_stopping/quad_equation_min/quad_min_BOHB_trunc.json",
"proposer": "bohb",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_iterations": 100,
"num_samples": 64,
"random_fraction": 0.3333333333333333,
"bandwidth_factor": 3,
"min_bandwidth": 0.001,
"eta": 3,
"min_budget": 1,
"max_budget": 5,
"resource_args":
{
"early_stop":
{
"aup_policy": "truncation",
"aup_policy_steps": 1,
"truncation_percentage": 0.7
}
},
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/early_stopping/quad_equation_min/quad_min_random_bandit.json
================================================
{
"name": "./early_stopping/quad_equation_min/quad_min_random_bandit.json",
"proposer": "random",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_samples": 200,
"resource_args":
{
"early_stop":
{
"aup_policy": "bandit",
"aup_policy_steps": 1,
"bandit_factor": 0.5
}
},
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/early_stopping/quad_equation_min/quad_min_random_median.json
================================================
{
"name": "./early_stopping/quad_equation_min/quad_min_random_median.json",
"proposer": "random",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_samples": 200,
"resource_args":
{
"early_stop":
{
"aup_policy": "median",
"aup_policy_steps": 1
}
},
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/early_stopping/quad_equation_min/quad_min_random_trunc.json
================================================
{
"name": "./early_stopping/quad_equation_min/quad_min_random_trunc.json",
"proposer": "random",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_samples": 200,
"resource_args":
{
"early_stop":
{
"aup_policy": "truncation",
"aup_policy_steps": 1,
"truncation_percentage": 0.7
}
},
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/early_stopping/quad_equation_min/quad_min_spearmint_bandit.json
================================================
{
"name": "./early_stopping/quad_equation_min/quad_min_spearmint_bandit.json",
"proposer": "spearmint",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_samples": 50,
"engine":"GPEIChooser",
"resource_args":
{
"early_stop":
{
"aup_policy": "bandit",
"aup_policy_steps": 1,
"bandit_factor": 0.5
}
},
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/early_stopping/quad_equation_min/quad_min_spearmint_median.json
================================================
{
"name": "./early_stopping/quad_equation_min/quad_min_spearmint_median.json",
"proposer": "spearmint",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_samples": 50,
"engine":"GPEIChooser",
"resource_args":
{
"early_stop":
{
"aup_policy": "median",
"aup_policy_steps": 1
}
},
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/early_stopping/quad_equation_min/quad_min_spearmint_trunc.json
================================================
{
"name": "./early_stopping/quad_equation_min/quad_min_spearmint_trunc.json",
"proposer": "spearmint",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_samples": 50,
"engine":"GPEIChooser",
"resource_args":
{
"early_stop":
{
"aup_policy": "truncation",
"aup_policy_steps": 1,
"truncation_percentage": 0.7
}
},
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/hpo_mnist/.gitignore
================================================
input_data/
.aup/
__pycache__/
logs/
================================================
FILE: Examples/hpo_mnist/MNIST Hyperparameter Optimization Demo.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# MNIST Hyperparameter Optimization Demo"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The MNIST database of handwritten digits, available publicly, has a training set of 60,000 examples, and a test set of 10,000 examples. The digits have been size-normalized and centered in a fixed-size image.\n",
"It is a good database for people who want to experiment on learning techniques and pattern recognition methods on real-world data while spending minimal efforts on preprocessing and formatting. For these reasons, MNIST is a very popular baseline dataset for AI researchers exploring effectiveness of their Neural Network architectures, hyperparameter tuning or optimization techniques.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this tutorial we will show how to perform Hyperparameter Optimization on MNIST using LeNet (http://yann.lecun.com/exdb/lenet/), a basic and popular neural network. \n",
"We modify a public implementation (https://github.com/aymericdamien/TensorFlow-Examples/blob/master/examples/3_NeuralNetworks/convolutional_network.py\n",
"), to find the best hyperparameter configuration for LeNet. We also demonstrate how to effectively switch between various HPO proposers such as Spearmint and Hyperband."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Running the Experiments "
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"We use the ‘mnist_hpo_demo.py’ file to accept hyperparameters from Auptimizer and run an Experiment. Since the values of the hyperparameters can widely swing the performance of the classification algorithm, finding an optimal configuration is very important. We define the range of hyperparameters for Auptimizer to consider in ‘exp_hpo_demo.json’. We choose the following hyperparameter ranges to optimize –\n",
"\n",
"1) Learning Rate: [ 0.001, 0.01 ]\n",
"\n",
"2) Dropout Probability: [ 0.0, 0.5 ]\n",
"\n",
"3) 1st layer Convolution filters: [ 20, 50 ]\n",
"\n",
"4) 2nd Layer Convolution filters: [ 40, 80 ]\n",
"\n",
"5) Fully Connected layer size: [ 700, 2000 ]\n",
"\n",
"\n",
"While we iterate over different Proposer algorithms.\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Each instance of 'mnist_hpo_demo.py' would accept an experiment configuration of hyperparameters, train the defined neural network, evaluate the network, and return the final performance statistics. The ‘exp_hpo_demo.json’ file contains code to perform HPO using AWS resources and consider 100 samples for each proposer. We can easily switch between proposers and see how they choose different configurations to optimize the final performance."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To run the experiment use command- \n",
"\n",
"'python3 -m aup exp_hpo_demo.json' \n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Analysis "
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"We next analyze the experiment results using the sqlite database for our experiments. The details for the experiment can be found inn the jobs profile, but here we aim to compare the proposers. Experiment 1 is performed using Grid Search, and Random, Spearmint, Hyperband, Hyperopt Proposers for Experiments 2,3,4 and 5 respectively."
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [],
"source": [
"'''\n",
"Connect to the Auptimizer database\n",
"''' \n",
"\n",
"import sqlite3\n",
"import time\n",
"import datetime\n",
"import random\n",
"\n",
"conn = sqlite3.connect('sqlite3.db')\n",
"c = conn.cursor()\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"'''\n",
"Function to compile and return details from a Auptimizer experiment. \n",
"Takes experiment id as input, returns score, jobs ran for the experiments, and end time for each job.\n",
"''' \n",
"\n",
"def graph_data(ex):\n",
" c.execute('SELECT score, jid, end_time FROM job WHERE eid = '+ str(ex))\n",
" data = c.fetchall()\n",
"\n",
" jobs = []\n",
" score = []\n",
" times =[]\n",
" \n",
" for row in data:\n",
" times.append(row[2])\n",
" jobs.append(row[1])\n",
" score.append(row[0])\n",
"\n",
" times = [a-min(times) for a in times] \n",
" jobs = [a-min(jobs) for a in jobs]\n",
" \n",
" print(score)\n",
" print(jobs)\n",
" \n",
" return (score, times, jobs)\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0.9895, 0.9893, 0.9677, 0.9602, 0.9852, 0.9886, 0.9685, 0.9698, 0.9899, 0.9908, 0.9738, 0.9701, 0.9911, 0.9899, 0.9699, 0.9699, 0.9875, 0.9907, 0.9672, 0.9697, 0.9892, 0.9901, 0.9597, 0.9636, 0.9898, 0.9914, 0.9755, 0.9628, 0.9914, 0.9892, 0.9693, 0.9737, 0.9904, 0.992, 0.9691, 0.9591, 0.9898, 0.9873, 0.9662, 0.9525, 0.9815, 0.9892, 0.9613, 0.951, 0.9905, 0.9892, 0.9722, 0.9591, 0.9876, 0.9922, 0.9446, 0.9528, 0.9883, 0.9898, 0.9641, 0.9619, 0.9905, 0.9893, 0.9697, 0.9629, 0.9891, 0.9908, 0.976, 0.954, 0.9911, 0.9892, 0.9598, 0.9606, 0.9878, 0.9883, 0.9621, 0.9584, 0.9871, 0.9901, 0.9663, 0.956, 0.9896, 0.9869, 0.9628, 0.9437, 0.9908, 0.9885, 0.9543, 0.9586, 0.9874, 0.9905, 0.9665, 0.9712, 0.9912, 0.9902, 0.9665, 0.9494, 0.9897, 0.9903, 0.9603, 0.9562, 0.9853, 0.9894, 0.9614, 0.9498, 0.9889, 0.9907, 0.9595, 0.9494, 0.9899, 0.9891, 0.9627, 0.9544]\n",
"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107]\n",
"[0.9619, 0.9742, 0.9828, 0.9662, 0.983, 0.9693, 0.9753, 0.9749, 0.9682, 0.9758, 0.9672, 0.9834, 0.9834, 0.9764, 0.9711, 0.9835, 0.9712, 0.9721, 0.973, 0.9769, 0.9898, 0.9687, 0.9678, 0.977, 0.9603, 0.9868, 0.9759, 0.9739, 0.9611, 0.9891, 0.9752, 0.9729, 0.978, 0.9828, 0.9913, 0.9774, 0.9657, 0.9671, 0.9667, 0.9791, 0.9742, 0.9649, 0.9812, 0.9289, 0.9847, 0.9828, 0.9875, 0.9572, 0.9715, 0.9867, 0.9771, 0.9885, 0.9789, 0.981, 0.9705, 0.9863, 0.9877, 0.9851, 0.9746, 0.9549, 0.9886, 0.9867, 0.9781, 0.964, 0.9679, 0.9641, 0.9631, 0.9904, 0.9864, 0.9709, 0.9701, 0.9602, 0.9897, 0.9878, 0.9747, 0.9912, 0.9648, 0.9821, 0.9842, 0.9841, 0.9709, 0.9722, 0.9774, 0.9819, 0.9698, 0.9647, 0.9802, 0.9668, 0.9766, 0.9811, 0.9837, 0.9871, 0.9708, 0.9727, 0.9826, 0.9768, 0.9554, 0.9612, 0.9824, 0.9801]\n",
"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]\n",
"[0.9886, 0.9869, 0.9802, 0.9821, 0.9686, 0.977, 0.9865, 0.9705, 0.9627, 0.9614, 0.9912, 0.986, 0.9685, 0.9595, 0.9877, 0.9904, 0.9897, 0.9575, 0.9849, 0.983, 0.9874, 0.9662, 0.9892, 0.9903, 0.9914, 0.9731, 0.9896, 0.9892, 0.9586, 0.9882, 0.9686, 0.9742, 0.9623, 0.9891, 0.9878, 0.9906, 0.9899, 0.9869, 0.9874, 0.9825, 0.9699, 0.9891, 0.9879, 0.9863, 0.9911, 0.9906, 0.9844, 0.9834, 0.9919, 0.9727, 0.9884, 0.9832, 0.9643, 0.9889, 0.964, 0.9888, 0.988, 0.9882, 0.9838, 0.9851, 0.9893, 0.9909, 0.9821, 0.9907, 0.9757, 0.9867, 0.9874, 0.97, 0.9908, 0.9892, 0.985, 0.9891, 0.9901, 0.988, 0.9886, 0.9837, 0.988, 0.9662, 0.9735, 0.9882, 0.9891, 0.9899, 0.9897, 0.9903, 0.9933, 0.9891, 0.9875, 0.9884, 0.9912, 0.9908, 0.9913, 0.9901, 0.9813, 0.9877, 0.9887, 0.9874, 0.9907, 0.9895, 0.9884, 0.987]\n",
"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]\n",
"[0.9865, 0.9888, 0.9604, 0.9827, 0.9897, 0.9713, 0.9831, 0.9824, 0.9876, 0.9786, 0.9817, 0.9782, 0.9631, 0.9725, 0.9708, 0.987, 0.9544, 0.9729, 0.9849, 0.9894, 0.9905, 0.9871, 0.9675, 0.952, 0.985, 0.9791, 0.9754, 0.9677, 0.969, 0.9712, 0.9767, 0.9719, 0.9631, 0.9851, 0.9787, 0.9724, 0.9738, 0.9822, 0.9435, 0.9738, 0.9748, 0.9775, 0.9859, 0.9863, 0.9764, 0.9736, 0.9759, 0.9724, 0.9857, 0.9821, 0.98, 0.9832, 0.9766, 0.9617, 0.9837, 0.9778, 0.9794, 0.9891, 0.9774, 0.9742, 0.9705, 0.9852, 0.9625, 0.9809, 0.9838, 0.9518, 0.9797, 0.9771, 0.973, 0.9747, 0.9849, 0.989, 0.9758, 0.9666, 0.9658, 0.9799, 0.9489, 0.9776, 0.9831, 0.9736, 0.9851, 0.9844, 0.9885, 0.9896, 0.9846, 0.9896, 0.9883, 0.9891, 0.9881, 0.9888, 0.9866, 0.9875, 0.9854, 0.9851, 0.9885, 0.9885, 0.9861, 0.9753, 0.9742, 0.9859, 0.9887, 0.9823, 0.9811, 0.9839, 0.9774, 0.9837, 0.9868, 0.9752, 0.9899, 0.9882, 0.9881, 0.9882, 0.9886, 0.987, 0.9887, 0.9871, 0.9884, 0.9911, 0.9901, 0.9852, 0.9878, 0.9788, 0.9701, 0.9858, 0.9871, 0.9871, 0.9834, 0.9845, 0.9725, 0.9873, 0.9851, 0.972, 0.9701, 0.986, 0.9645, 0.9909, 0.9878, 0.9883, 0.984, 0.9719, 0.9644, 0.9727, 0.9645]\n",
"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142]\n",
"[0.9682, 0.9748, 0.987, 0.9662, 0.9826, 0.9829, 0.9693, 0.9435, 0.9792, 0.9882, 0.9818, 0.9792, 0.9751, 0.9765, 0.9747, 0.9896, 0.9624, 0.9741, 0.9804, 0.975, 0.9903, 0.9875, 0.9865, 0.9876, 0.9858, 0.9898, 0.9875, 0.9865, 0.9814, 0.9873, 0.9876, 0.9867, 0.9857, 0.9557, 0.9832, 0.9846, 0.9835, 0.9823, 0.9764, 0.9761, 0.9642, 0.9882, 0.9911, 0.9677, 0.9826, 0.9866, 0.9754, 0.9852, 0.9815, 0.9902, 0.9678, 0.9712, 0.9862, 0.985, 0.9896, 0.9891, 0.9836, 0.9747, 0.9769, 0.9672, 0.9764, 0.9837, 0.988, 0.9845, 0.9831, 0.9907, 0.9913, 0.9883, 0.9905, 0.9845, 0.986, 0.9825, 0.9848, 0.9847, 0.9897, 0.9867, 0.9886, 0.9897, 0.9768, 0.9862, 0.9827, 0.9846, 0.9881, 0.9841, 0.9793, 0.9769, 0.9852, 0.9856, 0.9825, 0.9881, 0.9859, 0.9676, 0.9856, 0.9918, 0.9835, 0.9662, 0.9874, 0.9758, 0.9889, 0.9735]\n",
"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]\n"
]
}
],
"source": [
"'''\n",
"Compile results for grid search, random, spearmint, hyperband, hyperopt Proposers\n",
"''' \n",
"\n",
"s_gri, t_gri, j_gri = graph_data(1)\n",
"s_ran, t_ran, j_ran = graph_data(2)\n",
"s_spe, t_spe, j_spe = graph_data(3)\n",
"s_hpb, t_hpb, j_hpb = graph_data(4)\n",
"s_opt, t_opt, j_opt = graph_data(5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Visualization"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can easily visualize the performance of the hyperparameter optimization algorithms for our current experiment and how configurations can affect the final performance. In the plots below we compare job performance and time with accuracy achieved. "
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABJkAAANsCAYAAAD4MnkPAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAIABJREFUeJzs3Xd4VFX6wPHvmUlPIIGEFkACSAsEiPQOgi42EFARFYjo2hYV17K74mL5ydrQBXXtq1FBg8CKBUUFpZdApIVeAyEhIb2XmTm/P+4kTnogkwSS9/M8PEzunDn33Dt3yn3nPe9VWmuEEEIIIYQQQgghhKgJU30PQAghhBBCCCGEEEJc/iTIJIQQQgghhBBCCCFqTIJMQgghhBBCCCGEEKLGJMgkhBBCCCGEEEIIIWpMgkxCCCGEEEIIIYQQosYkyCSEEEIIIYQQQgghakyCTEIIIYSodUqpEUqpwxf52CuUUllKKfOlMqbaUFvbWRvs4+xU3+MQQgghxKVFgkxCCCHqnFLqlFJqXKllYUqpTfU1psuVUkorpa50cp9KKfWkUuqoUipXKXVaKfWSUsr9Yseltd6ote52MePRWp/WWvtora0X8/jaGFNtcNZ21gX7OE/U9zjEhbO/DhKVUi4Oy1zty7TDsnVKqTylVHuHZeOUUqcc/i5+L1dKuSmlXldKxdqDkKeUUgvt92U5/LPZ31eK/r6zTjZcCCFEnZAgkxBCiEbLHkxx6mfh5ZCFUsTxJLOUN4H7gBlAE+A6YCzwVR0NTVyiKjlmLhkyxmpJxXhdF7nOvqy0bOCf1ezzH0B/YCDG+8Zo4HcoDkr6aK19gNPATQ7LllzcJgghhLgUSZBJCCHEJceeRbOi1LI3lVKL7LfX2TNrIpVSGUqpb5RSzR3aDlZKbVFKpSml9iilRjvct04pNV8ptRnIATpVo79lSqlzSql0pdQGpVRPh/vClVLvKqV+UEplA2OUUjcopXbZ+zqjlHrOoX2QPZPgbvt9qUqpB5RSA5RSe+1jfrvUts9SSh20t/1JKdXBvnyDvckee0bAVPvyG5VSu+19bVFK9Xbo65RS6m9Kqb1AdumTXaVUF+Ah4E6t9VattUVrvR+YAoxXSl3tsN3vKaV+UUplKqXWVzYupdRopVRsqXE8ad/mbKXUf5VSrZRSP9r7W6OUalZqn7kopYaUyorIK8qsUEoNVEpttW93vFLqbaWU2wWMqYf9WEhTSu1XSk0o9Tz/Rym1yj6+7UqpzpSjdL8O21uU8TFQKbXTfnwkKKXeKL2d9r/XKaX+Tym12b7On5VSAQ59zlBKxSilkpVS/1TlZAja2w2yH79mh2WT7MdApfvNfr9WSv1FKXUUOOqw7Er7bV+l1GdKqfP28Tyj7MFbpdRzSqnFDn2V3sYwpdQJ+/adVBVktdj7Wa6UWmpv+7tSqk+p/VviuK7G81nu8Wu/f6hSaocyXvM7lFJDHe6rcMyqgtdqeftRGf6tjAyiDKXUPqVUrwq2P1Ap9a1SKkUpdUwp9edS++Yr+3OQad/W/uX14+BzjCBykRnAZ+W0exOYVtGxXsoA4GutdZw2nNJal9enEEKIBkyCTEIIIS5FizECGn5Q/Kv/7ZQ8CZoBzALaABaMkyGUUm2BVcCLQHPgCWCFUqqFw2OnY2TqNAFiKuvP7kegC9AS45f50r+83wHMt/e3CePX/xmAH3AD8KBS6uZSjxlk73MqsBCYC4wDegK3KaVG2bdnIvA0MBloAWwEvgTQWo+099XHnhGwVCkVCnwM3A/4A+8D36qSU92m2cflp7W2lBrXWCBWax3puFBrfQbYBlzjsPhO4P+AAGB30X4pb1yUb4q9v67ATRj7+Wn7dpqAR0o/wB74KsqIaAZsL9ofgBV4zD6eIfZteag6Y1JKuQLfAT9jPM8PA0uUUo7T6W4Hnrev9xjGc34xFgGLtNZNgc5UniF2B3C3fUxuGMczSqlg4B2M56AN4Au0La8DrfV2jGPy6lL9fmG/XeF+c3AzxjEbXM4q3rKvvxMwCuPYv7uSbcK+Dd4Yr7PrtNZNgKEYx1FFJgLLMF7XXwAr7c9bkeLjGlBU/XyWe/wqI8C8yj42f+ANYJVSyr+yMVf2WnXguB+vBUZiHP++wG1AcgXbHgHEAoHALcC/lD3gazfB3sYP+BZ4u0wPJa0ERiql/JQRzB0BfFNOu7PAhxjHfVW2AX9VSj2klApRSqlqPEYIIUQDI0EmIYQQ9WWlPcMgTSmVhnHCDIDWOh7YANxqXzQeSNJaRzk8/nOtdbTWumg6x232TI27gB+01j9orW1a61+AncD1Do8N11rvt2fpFFbRH1rrj7XWmVrrfOA5oI9Sytehv2+01pvt68vTWq/TWu+z/70X40RzVKnt/z97258xAgBfaq0TtdZnMU5OQ+3tHgBe0loftAeE/gX0dcyQKOU+4H2t9XattVVr/SmQDwx2aPOm1vqM1jq3nMcHAPEV9B1vv7/IKq31Bvt+mQsMUQ71W6rhLa11gsM2b9da79Ja5wFf88c+qMibQKZ93Wito7TW2+zP6ymMAFvp/V6RwYAP8LLWukBr/SvwPUbgosjXWutI+/OwBOhbzb5LKwSuVEoFaK2ztNbbKmn7idb6iP25+sphnbcA32mtN2mtC4B5gK6oE4xjcBqAUqoJxuuhKFhZnf32ktY6pfQxY3+N3A78w/4aOQW8jhHIrQ4b0Esp5am1jrdnzVUkSmu93P6afQPwoOLjujrPZ0XH7w3AUa315/Z98iVwCCMQWtmYq/NaddyPhRiB6e6Asj+uzGvPPqZhwN/s7xm7gY8omYm0yf6eZ8XIUupTup9S8jCCcFPt/761LyvPS8BNyiGDs5J2r2AE73YCZ5VSM6t4jBBCiAZGgkxCCCHqy81aa7+if5TNnPgUI2CE/f/PS91/xuF2DOCKEQDpANxaKoA1HCPbo7zHVtqfUsqslHpZKXVcKZUBnLK3CajgsUXTk35TxvShdIyTT8f2AAkOt3PL+dvHfrsDsMhhW1IwsjTKzVqxt3+81Pa3x8iAKHe8pSRRcl85amO/v0w/Wuss+9gCSz+oEtXdB2Uope7HqPlyh9baZl/WVSn1vTKmhmVgnOSX3u8VCQTOFPVlF0PJ/XzO4XZOZeOrwj0Y2SuH7FOxbqykbUXrDKTk/s+h4iwYMDJ/Jtsz2iYDv2utY6Da+62iYyYA47US47Cs9H4rlz2gOxXj9RGvjKmI3St5iOP22vgjs6e8MVbn+azo+A0stT3Fj61izNV5rTqu81eMjKP/AIlKqQ+UUk3L2e5AIEVrnVnJtpQ+TjxU1XWfPsMIVFU0Va5onOft43yhss7sQe3/aK2HYWRUzQc+Vkr1qGIcQgghGhAJMgkhhLhUrQR622uU3EjZKWqOGTNXYGQFJGGcxH3uGMDSWntrrV92aF9exkdF/d2BMU1nHMaUliB7G8epIKX7+wIjM6C91toXeK9U+wtxBri/1PZ4aq23VNJ+fqn2XvZsjIrG6+hXoL1SaqDjQns2xWBgrcNix6tO+WBMY4q7gG27KEqpERjTnCZqrTMc7noXI+Oki30q2tNUf7/HYWy343ejKzCmC12obMDLYbxmjOlTAGitj2qtp2FM43oFWG6fhnUh4oF2DuvwxJjaVS6t9QGMwMR1lJwqB9XbbxUdM0kYrxXHbB3H/VZiXwCtS43rJ631NRgBzEMYU7Mq4ni8mTC23/F4cxxjdZ7Pio7fuFLbU+KxlYy5Oq/VEvtRa/2m1rofxvS5rsCT5Wx3HNDcnoFW0bZcjI32bWiFMc23Mq8BY4B+1elYa52rtf4PRjHx8qZYCiGEaKAkyCSEEOKSZJ8ytRzjZDhSa326VJO7lFLBSikvjF/Yl9uniizGmNrxJ3sWkocyCjG3o3IV9dcEY7pZMsbJ8r+qMfwmGJkHefZgzR3V3OzyvAf8o2iqijKKLN/qcH8CRi2cIh8CD9izqZRSylsZhcgdT1ArpLU+Yl/nEmUUUDfb170CWKO1XuPQ/Hql1HBlFIn+P2CbNmo3lTcup7AHu74CZtjH6qgJkAFk2bNLHix1f2Vj2o6RAfKUMi7nPhpjelTERQzzCEYmyQ32mkHPAMU1sZRSdymlWtizbNLsi23l9FOZ5RjH+VD7/n+OqgNqXwCPYtQBWuawvKr9ViH7a+QrYL5Sqol9athfMV6HYNQrGqmUusI+xfQfRY9VRqH3ifYAWz6QReX7oZ9SarI9Q2eO/TEVTTWszvNZ0fH7A9BVKXWHMgqIT8UIlHxfxZireq2WoIxi/4Psx0g2xnS1MttvH9MW4CX7+1lvjGy4xaXbXgittcbYJxPstytrm4YxDfKpitoopebY32s97fttJsaxtasm4xRCCHF5kSCTEEKIS9mnQAhlp8phXxaOMU3EA3uRaPsJWVEB3vMY2QVPUvVnXrn9YUwjicHIGjhAxSe1jh4CXlBKZWLUyqmssHOltNZfY2S7RNinMkVT8tLjzwGf2qfo3Ka13gn8GWN6SypGgeqwC1ztbIyaL4sxTqJXA+swCnU7+gJ4FmNaUD/+mN5YZlwXuP7KjMXIvFiu/rjCXFFNnCcwAnqZGMG20gXHKxyTva7RTRj7NgmjRtgMrfWhCx2g1jod4xj4COO4ycaY2lVkPLBfKZWFUQT89tK1jqqxjv0YxawjMLKasoBEjMBHRYpqg/2qtXac9ljVfqvKwxjbeAIjI+YLjOLzaKMm2lJgLxCFURepiAkjIBWHcQyNovIA1zcYU9VSMWo+TdZ/1FQroZrPZ7nHr9Y6GSN78nGM4PJTwI32fVbhmKvxWi2tKcb+TsV4j0nGyBgqzzSMLMo4jHplz5YK+F4UbdSmq6wOlqNFGEXiK5KDEYg6h7HP/wJM0VqfqNkohRBCXE5UFT9cCCGEEPVGKXUFxnSU1o7TopRS64DFWuuPnLQep/bXGCilwjGuQvdMfY9FFE/3SsOY8nayvsfjbEqp54ArtdZ3VdW2mv2FI8evEEII4XSSySSEEOKSZK+l8lcgolTdHSEEoJS6SSnlZZ+6tQDYxx+F6YUQQggh6lxVV50QQggh6pz9pDkBYwrJ+HoejhCXqokY0zwVxiXjb6+qto4QQgghRG2S6XJCCCGEEEIIIYQQosZkupwQQgghhBBCCCGEqLEGM10uICBABwUF1fcwhBBCCCGEEEIIIRqMqKioJK11i+q0bTBBpqCgIHbu3FnfwxBCCCGEEEIIIYRoMJRSMdVtK9PlhBBCCCGEEEIIIUSNSZBJCCGEEEIIIYQQQtSYBJmEEEIIIYQQQgghRI01mJpM5SksLCQ2Npa8vLz6Hoq4AB4eHrRr1w5XV9f6HooQQgghhBBCCCGqqUEHmWJjY2nSpAlBQUEopep7OKIatNYkJycTGxtLx44d63s4QgghhBBCCCGEqKYGPV0uLy8Pf39/CTBdRpRS+Pv7S/aZEEIIIYQQQghxmWnQQSZAAkyXIXnOhBBCCCGEEEKIy0+DDzIJIYQQQgghhBBCiNonQaYGbOjQoeUuDwsLY/ny5XU8GiGEEEIIIYQQQjRkEmRqgCwWCwBbtmyp55EIIYQQQgghhBCisZAgUy2aN28eCxcuLP577ty5LFq0qEZ9Hj9+nMGDBxMSEsIzzzyDj48PAOvWrWPEiBFMmDCB4OBggOL7tNbMnj2bbt26MW7cOBITE2s0BiGEEEIIIYQQQojSXOp7AHXl+e/2cyAuw6l9Bgc25dmbelZ4/6xZs5g8eTJz5szBZrMRERFBZGRkmXYjRowgMzOzzPIFCxYwbty4EsseffRRHn30UaZNm8Z7771X4r7ff/+d6OhoOnbsWGL5119/zeHDhzlw4AAJCQkEBwcza9asC9lUIYQQQgghhBBCiEo1miBTfQgKCsLf359du3aRkJBAaGgo/v7+Zdpt3Lix2n1u3bqVlStXAnDHHXfwxBNPFN83cODAMgEmgA0bNjBt2jTMZjOBgYFcffXVF7E1QgghhBBCCCGEEBVrNEGmyjKOatO9995LeHg4586dqzB76EIymSrj7e190eMUQgghhBBCCCGEqIlGE2SqL5MmTWLevHkUFhbyxRdflNvmQjKZBg8ezIoVK5g6dSoRERHVeszIkSN5//33mTlzJomJifz222/ccccd1V6nEEIIIYQQQgghRFUkyFTL3NzcGDNmDH5+fpjN5hr3t3DhQu666y7mz5/P+PHj8fX1rfIxkyZN4tdffyU4OJgrrriCIUOG1HgcQgghhBBCCCGEEI4kyFTLbDYb27ZtY9myZU7pr23btmzbtg2lFBERERw+fBiA0aNHM3r06BJts7KyAFBK8fbbbztl/UIIIYQQQgghhBDlkSBTLTpw4AA33ngjkyZNokuXLk7pMyoqitmzZ6O1xs/Pj48//tgp/QohhBBCCCGEEELUhASZalFwcDAnTpxwap8jRoxgz549Tu1TCCGEEEIIIYQQoqZM9T0AIYQQQgghhBBCCHH5kyCTEEIIIYQQQgghhKgxCTIJIYQQQgghhBBCiBqTIJMQQgghhBBCCCGEqDEJMjVy4eHhzJ49u76HIYQQQgghhBBCiMucBJkuYxaLpb6HIIQQQgghhBBCCAFIkKlWzZs3j4ULFxb/PXfuXBYtWlSjPsPCwnjggQcYNGgQTz31FJGRkQwZMoTQ0FCGDh3K4cOHASNDafLkyYwfP54uXbrw1FNPFffxySef0LVrVwYOHMjmzZuLl586dYqrr76a3r17M3bsWE6fPl28zgcffJDBgwfTqVMn1q1bx6xZs+jRowdhYWE12h4hhBBCCCGEEEI0DC71PYA68+Pf4dw+5/bZOgSue7nCu2fNmsXkyZOZM2cONpuNiIgIIiMjy7QbMWIEmZmZZZYvWLCAcePGlVkeGxvLli1bMJvNZGRksHHjRlxcXFizZg1PP/00K1asAGD37t3s2rULd3d3unXrxsMPP4yLiwvPPvssUVFR+Pr6MmbMGEJDQwF4+OGHmTlzJjNnzuTjjz/mkUceYeXKlQCkpqaydetWvv32WyZMmMDmzZv56KOPGDBgALt376Zv374XtQuFEEIIIYQQQgjRMDSeIFM9CAoKwt/fn127dpGQkEBoaCj+/v5l2m3cuPGC+r311lsxm80ApKenM3PmTI4ePYpSisLCwuJ2Y8eOxdfXF4Dg4GBiYmJISkpi9OjRtGjRAoCpU6dy5MgRALZu3cr//vc/AKZPn14i++mmm25CKUVISAitWrUiJCQEgJ49e3Lq1CkJMgkhhBBCCCGEEI1c4wkyVZJxVJvuvfdewsPDOXfuHLNmzSq3zYVmMnl7exff/uc//8mYMWP4+uuvOXXqFKNHjy6+z93dvfi22WyuUQ2nor5MJlOJfk0mk9SGEkIIIYQQQgghRCMKMtWTSZMmMW/ePAoLC/niiy/KbXOhmUyO0tPTadu2LWDUYarKoEGDePTRR0lOTqZp06YsW7aMPn36ADB06FAiIiKYPn06S5YsYcSIERc9LiGEEEIIIYQQQjQuEmSqZW5ubowZMwY/P7/iKW7O9NRTTzFz5kxefPFFbrjhhirbt2nThueee44hQ4bg5+dXYprbW2+9xd13381rr71GixYt+OSTT5w+XiGEEEIIIYQQQjRMSmtd32Nwiv79++udO3eWWHbw4EF69OhRTyMy2Gw2rrrqKpYtW0aXLl3qdSyXk0vhuRNCCCGEEEIIIRo7pVSU1rp/ddqaanswjdmBAwe48sorGTt2rASYhBBCCCGEEEII0aDJdLlaFBwczIkTJ+p7GEIIIYQQQgghhBC1TjKZhBBCCCGEEEI0Gkm5SSTlJtX3MIRokCSTSQghhBBCCCFEozHntzm4mFwIHx9e30MRosGRIJMQQgghhBBCiEYhKTeJPef34GZyo9BaiKvZtb6HJESDItPlhBBCCCGEEEI0ChtjNwJQYCvgSOqReh6NEA2PBJmEUwwdOrTKNgsXLiQnJ6cORiOEEEIIIYQQZa2PXY+Pqw8A+5L21fNohGh4JMgkKmS1WqvddsuWLVW2kSCTEEIIIYQQor4UWAvYEreF6zteT3OP5hJkEqIWSJCpFs2bN4+FCxcW/z137lwWLVpUoz6XLVtGr1696NOnDyNHjgQgPDyciRMnMnr0aLp06cLzzz9f3H7x4sUMHDiQvn37cv/99xcHjh588EH69+9Pz549efbZZ4vbBwUF8be//Y2rrrqKZcuWMXr0aB577DH69+9Pjx492LFjB5MnT6ZLly4888wzxY/z8TF+DVi3bh2jR4/mlltuoXv37tx5551orXnzzTeJi4tjzJgxjBkzpkb7QAghhBBCCCEu1M5zO8m15DKq/ShCAkKIToqu7yEJ0eA0msLfr0S+wqGUQ07ts3vz7vxt4N8qvH/WrFlMnjyZOXPmYLPZiIiIIDIysky7ESNGkJmZWWb5ggULGDduXIllL7zwAj/99BNt27YlLS2teHlkZCTR0dF4eXkxYMAAbrjhBry9vVm6dCmbN2/G1dWVhx56iCVLljBjxgzmz59P8+bNsVqtjB07lr1799K7d28A/P39+f333wF47733cHNzY+fOnSxatIiJEycSFRVF8+bN6dy5M4899hj+/v4lxrhr1y72799PYGAgw4YNY/PmzTzyyCO88cYb/PbbbwQEBFR/JwshhBBCCCGEE6yPXY+H2YOBrQdyMPkgG2I3kFmQSRO3JvU9NCEajEYTZKoPQUFB+Pv7s2vXLhISEggNDS0TkAHYuHFjtfscNmwYYWFh3HbbbUyePLl4+TXXXFPc9+TJk9m0aRMuLi5ERUUxYMAAAHJzc2nZsiUAX331FR988AEWi4X4+HgOHDhQHGSaOnVqiXVOmDABgJCQEHr27EmbNm0A6NSpE2fOnCmzTQMHDqRdu3YA9O3bl1OnTjF8+PBqb6MQQgghhBBCOJPWmvWx6xncZjAeLh6EBISg0exP3s/gNoPre3hCNBiNJshUWcZRbbr33nsJDw/n3LlzzJo1q9w2F5LJ9N5777F9+3ZWrVpFv379iIqKAkApVaKdUgqtNTNnzuSll14qcd/JkydZsGABO3bsoFmzZoSFhZGXl1d8v7e3d4n27u7uAJhMpuLbRX9bLJYy43ZsYzaby20jhBBCCCGEEHXlRPoJzmad5Z6QewDoGdATgOikaAkyCeFEjSbIVF8mTZrEvHnzKCws5Isvvii3zYVkMh0/fpxBgwYxaNAgfvzxR86cOQPAL7/8QkpKCp6enqxcuZKPP/4YLy8vJk6cyGOPPUbLli1JSUkhMzOTjIwMvL298fX1JSEhgR9//JHRo0c7Y3Mr1aRJEzIzM2W6nBBCCCGEEKJOrY9dD8DItkZdW193X4KaBrHvvBT/FsKZJMhUy9zc3BgzZgx+fn6YzeYa9/fkk09y9OhRtNaMHTuWPn36sHv3bgYOHMiUKVOIjY3lrrvuon///gC8+OKLXHvttdhsNlxdXfnPf/7D4MGDCQ0NpXv37rRv355hw4bVeFzVcd999zF+/HgCAwP57bff6mSdQgghhBBCCLH+zHp6NO9BK+9Wxct6BfRie/x2tNZlZoYIIS6O0lrX9xicon///nrnzp0llh08eJAePXrU04gMNput+EptXbp0qZV1hIeHs3PnTt5+++1a6b8+XArPnRBCCCGEEOLyl56fzsilI/lzyJ+ZHTq7ePmSg0t4OfJlfrnlF1p7t67HEQpxaVNKRWmt+1enram2B9OYHThwgCuvvJKxY8fWWoBJCCGEEJeuuLRc3l13nIbyo54QQlyONp3dhE3bGNVuVInlIQEhgFGXSQjhHDJdrhYFBwdz4sSJWl9PWFgYYWFhtb4eIYQQQlyYLyNP89avx5g6oD3Nvd3qezhCCNEorY9dT3OP5sXFvot0b94dF5ML+5L2Ma7DuAoeLYS4EJLJJIQQokEptNrYF5te38MQAoDos8axWGi11fNIhBCicbLYLGw6u4kRbUdgUiVPf93MbnRv1p19SVL8WwhnkSCTEEKIBuW1nw4z4T+bSMrKr++hiAaswFpAriW3ynb74zIAsNhkupwQQtSH3Ym7ySzIZFT7UeXe3yugF/uT9mO1Wet4ZEI0TBJkEkII0WCcSckhfPMptIaM3ML6Ho5owF6KfIm/rP1LpW0SM/JIzDSCnRbJZBJCiHqxIXYDLiYXhrQZUu79IS1CyLHkcDL9ZB2PTIiGSYJMQgghGoxXVh+iwH4yn1sov0iK2nMk9QhxWXGVtinKYgIotEomkxBC1If1sevp36o/Pm4+5d5fVPxbpszVnoNb4lk6P1IugtFISJDpMhMWFsby5cvrdJ1BQUEkJSU5vd/nnnuOBQsWOL1fIUTj9PvpVL7fG0/oFX4A5EmQSdSixJxEcgpzKm2zP+6P2mAWm2QyCSFEXTuTcYYT6SfKXFXOUYemHWji2kSCTLUo9nAKSWeyyM+x1PdQRB2QIJOolNUqJ2lCiEuf1pr5qw4S4OPOo2O7AJBbICf1onbYtI2knCRyLJUHmaLP/pHJZJFMJiGEqHMbzm4AqDTIZFImegb0JDopuq6G1eikJxo1DLNSpV5mYyBBplo0b948Fi5cWPz33LlzWbRoUY373bBhA0OHDqVTp07FWU0zZsxg5cqVxW3uvPNOvvnmG8LDw5k4cSKjR4+mS5cuPP/888VtFi9ezMCBA+nbty/3339/cUDJx8eHxx9/nD59+rB161YAXn31VUJCQhg4cCDHjh0D4LvvvmPQoEGEhoYybtw4EhISACNDadasWYwePZpOnTrx5ptvFq9z/vz5dO3aleHDh3P48OEa7wshhABYHX2OqJhUHr+2KwE+7oBMlxO1JyUvBYu2kG/Nr7RQ7P74dHzcXQAp/C2EEPVh/Zn1dPTtSPum7SttFxIQwpHUI+RZ8upoZI1LWqLxo0x2mgSZGgOX+h5AXTn3r3+Rf/CQU/t079Gd1k8/XeH9s2bNYvLkycyZMwebzUZERASRkZFl2o0YMYLMzMwyyxcsWMC4cePKLI+Pj2fTpk0cOnSICRMmcMstt3DPPffw73//m5tvvpn09HS2bNnCp59+yuLFi4mMjCQ6OhovLy8GDBjADTfcgLfWO84UAAAgAElEQVS3N0uXLmXz5s24urry0EMPsWTJEmbMmEF2djaDBg3i9ddfL16nr68v+/bt47PPPmPOnDl8//33DB8+nG3btqGU4qOPPuLVV18tfsyhQ4f47bffyMzMpFu3bjz44IPs3buXiIgIdu/ejcVi4aqrrqJfv34Xs+uFuKzEZsaSnJdMnxZ9Km2XW2Bl64kkru7eqo5G9ofY1BxSsgvo3c6vztddUwUWGy+vPkTXVj7c2q8dp5KNLzISZBK1JTEnsfh2riW33Dof6TmFnEnJZWhnf7YcT5bC30IIUceyC7PZkbCDu3rcVWXbkIAQrNrKoZRD9G3Ztw5G13jkZReSn21Mk8tKlSBeY9Bogkz1ISgoCH9/f3bt2kVCQgKhoaH4+/uXabdx48YL6vfmm2/GZDIRHBxcnD00atQoHnroIc6fP8+KFSuYMmUKLi7G03vNNdcUr3fy5Mls2rQJFxcXoqKiGDBgAAC5ubm0bNkSALPZzJQpU0qsc9q0acX/P/bYYwDExsYydepU4uPjKSgooGPHjsXtb7jhBtzd3XF3d6dly5YkJCSwceNGJk2ahJeXFwATJky4oO0W4nKUlJtE2OowXEwurJ6yutK2L/14kM+2xrD571fT1s+zjkZoeHPtUVbtjWf73HHFmReXi8+3xRCTnEP43QNwMZvwdDMDkFdQjSBT8nFo1hFMktgrqs8xyJRjySk3yLQ/3qjH1Le9H1uOJ0vhb3FZ0VqTei6HZq29UErV93CEuChb47ZisVkY2W5klW17BfQCYO/5vQ02yGS1WTmefpwufl3q9HVdNFUOJJOpsbi8ziRqoLKMo9p07733Eh4ezrlz55g1a1a5bS40k8nd3b34tmOF/hkzZrB48WIiIiL45JNPipeXfhNRSqG1ZubMmbz00ktl+vfw8MBsNpd5TOnbDz/8MH/961+ZMGEC69at47nnnit3jGazGYvl8i/yVmgrxNXkWt/DEJeRQmshf133VxJyEmji1qTStscSs1iy/TQAqdkFdR5kysq3kF1g5ZvdZ7lzUIc6XXdNpOcU8ubao4zoEsCori0A8HQ13r+qzGRKOwNv94epS6D79bU9VNGAlAgyVVD8e7+9HlNRdqAU/haXi6zUPH77/BCnD6Rw3QMhdOrbor6H1DhYLWAygwT1nGZ97HqauDWpVtCohVcLWnu3vqTqMuUW5qMBL1f3KttWZVv8NhbsWMDh1MM8NeAppgdPr/kAq6loqhxIkKmxkJ9ua9mkSZNYvXo1O3bs4E9/+lO5bTZu3Mju3bvL/CsvwFSZsLCw4hpQwcHBxct/+eUXUlJSyM3NZeXKlQwbNoyxY8eyfPlyEhONL8opKSnExMRU2PfSpUuL/x8yZAgA6enptG3bFoBPP/20yvGNHDmSlStXkpubS2ZmJt99990FbV99yinMYUTECNbGrK3voYjLyEuRL7ErcRfdmnUjtzC30su2vvzjIaz2mi1Z+XUflM0vNE6Av9h++rK6vOxbvx4lI6+Qp6/vURwAr3aQKeUEaBukx9b2MEUDUzqTqTz749Jp4+tBq6bGyYHUZBKXOq01B7fE8+ULkcQdS8PF3czJ3efre1iNx1uhsOOj+h5Fg2HTNjbEbmB44PBq/0gcEhBySV1hbvyX9zBk8SgW7FhEUu7FXen7RPoJZq+dzZ9//jNZhVmEtgzljag32J+038mjrVh6Yg4oaB7oTZYEmRoFCTLVMjc3N8aMGcNtt91WJjvI2Vq1akWPHj24++67SywfOHAgU6ZMoXfv3kyZMoX+/fsTHBzMiy++yLXXXkvv3r255ppriI+Pr7Dv1NRUevfuzaJFi/j3v/8NGAW+b731Vvr160dAQECV47vqqquYOnUqffr04brrriueqnc5SMtPI7swm50JO53W52dbT3Hre1vIKbj8s7xEWV8d/oplR5ZxT697GN9xPBZtocBWUG7brceTWXMwget6tQYgM68egkwWI8i0Py6DPbHpVbS+NJxOzuHTrae4tV87erRpWrzc3cX4aMutarpcpv09Ly+tlkbYOGSn5/PFc9s4f6ZsRu7F+mDvB0z7fhqbzm5yWp/OVJ1Mpui4DHoGNsXVbByPcnU5UZve+OUIcyJ2XfTjs9Pz+eGdvfz62UEC2vlw+z8H0bF3ADH7k7FJgLT2WfIh7TSknKzvkTQY+5P2k5KXwsj2VU+VKxISEEJsViypeam1OLLqSclLIcW2F4vFnU/3/5drl1/LvM3zOJZ6rFqPT81L5V/b/8XkbyYTlRDFY/0e45ubv+Gtq98iwDOAJ9Y/QWaB8z63K5N+PhefZu74tvCUTKZGotFMl6svNpuNbdu2sWzZMqf0Fx4eXuLvrKys4ts5OTkcPXq0uH5SkXbt2pW48lyRqVOnMnXq1DLLHfsEOHXqFACvvPJKieUTJ05k4sSJZR7vOG0OIDr6j7TTuXPnMnfu3DKPudTlW403xKNpR53W5+7Taew4lcpz3+7n1VsqLwgtLi9RCVG8tP0lhrcdzsOhDxNxOAIwTkbdzSVTnm02zfwfDhDo68Gj47rwY/Q5svIL63zM+RYrvdv5GtP2tsXQt/2lXwD8ldWHcDGZePzabiWWm0wKD1cTeVVlMmXEGf/nXR5BtUvViV3nST2XQ/LZLFq0r3xaaHX9fOpnDqce5sE1DzIscBiP93+cLs26OKVvZ0jMScSszFi1tdxMppwCC8fPZ3FDSBtczEaGnRT+FrVp1+lUNh5N4u5hHelzAe/fWmuO7khgQ8QRLIU2ht/ahd5j2qFMiqDe/hzdkUDiqQxad/KtxdEL8u0n+3JlM6dZH7sekzIxPHB4tR9TVJdpX9K+atVxqk3rzqwDpfFKu5ukDEXXrrv48eSPfH3sa4a1HcbM4JkMbjO4TFmUAmsBXx76kvf3vE+OJYdbut7CQ30forlHcwDcze68NvI1wlaH8fzW53lt5Gu1Xp8pLTEXv5Ze+Pi5E3dUfthrDCSTqRYdOHCAK6+8krFjx9KlS+1+OV6zZg09evTg4YcfxtdXvgg4W4HVyECp7q8H1ZFjz7L4amcsX++S6ToNxbnsc/x13V9p16Qdr4x8BbPJjLerN2Bc5aS0b/acJfpsBk+O70aAjxGAyqqnTCZ/bzcm9g3ku71xpOfWfaDrQkTFpLJqXzz3jexEq6YeZe73dDVXPV1OMpmc4uReI4XfUuCcIEqhtZDj6ceZHjydpwY8xd6kvdzy3S08v/X5i54u4GwJOQm09TGmi5cXZDoYn4nW0DOwKS72ovKFkg3S4B1LPcZbu96qlynHhfYg5nvrj1f7MTkZBax+P5pfPj5As9Ze3P7MQPqMbY8yGSecVwT7o0yKU/sujdddg5Zv1HCTIJPzbIjdQN8WffHzqH7Qtad/T0zKdEnUZVp7ei3K0pyRHfry93HDObz/Gq7zfZuHQx/mUPIh7vvlPm797la+Pf4thdZCtNb8fOpnJq6cyIKdC+jbsi8rJqzgmcHPFAeYivRt2ZeHQx/mp1M/seyIcxIhKpOemINvSy+8m7mTn2OhsDoXZhGXNQky1aLg4GBOnDjB66+/XuvrGjduHDExMcyZM6fE8rCwMN5+++1aX//lKjkrnyXbY6pMBS8KMiXnJTsthTan0Eqvtk0ZGNScuV9Hc+J8VtUPEpe0PEsej/z6CPnWfBaNWURTN2MKl5eLcUXF0iejeYVWXlt9mJC2vkzs07b4qm4Z9RFkKrTh7mLmjoEdyCu08fXvl27gU2vN/FUHaNnEnftHdSq3jaerufrT5XIlyHSxCnItnD1svCdanPSl8UT6CSw2CyEBIUwPns4Pk37gju53sPLoSm743w18tO8j8ur5RCwxJ5Eg3yAAcgtzy9y/P87IjuvV1hdXyWRqFGzaxt83zuWDvR/USzC06OqFq/ef43g1vk8ci0rkyxe2ExOdzJDJnZn0RD/8WnmVaOPh7Uqbzr6c2pfslDHuT9rPjnM7nNJXg9PAMpl2n0lj56mUelt/QnYCB1MOXnA2kperF539Otd7Xaasgiy2xm1FZ/fCx92F+0Z2ImxoEJ9tTsKUPo6fb/mZF4a+gFVbmbtpLuNXjOfOH+7k8fWP4+nqyfvXvM87496hs1/nCtdxd6+7GRY4jFciX+FwyuFa25a8rELycyz4tvDE28/4MTU7VabMNXQSZBKNVlJWPtM+3Mbcr6Or/EJWNF0O4Fiac7KZcgss+Li7sGhaX9xdTPzli11VT++5RFmsNnbU45eJS4HWmue2PsehlEO8POJlOvn9EfwoymQqXbvlv5tOEpeex9PX98BkUri7mHA1q/op/G2x4u5qIqSdL73b+fJF5KVbAPyHfef4/XQaj1/bFS+38md9e7hVI5MpoyiTSabLXayY/cnY7Ce3zspkOpRyCIBuzYxpkH4efvxt4N/4euLXDGoziEW/L2LCygmsOrEKm677wE2eJY+MggyCmgYB5Wcy7T+bQTMvV9r4emC2Z4VI4e+GbfXJ1RxOPQDAybSKa1xeqCOpR0jPr/o9qtBqo087X9zMJj5Yf6LCdgW5Fn76KJqfPoymqb8Htz09gKuu7YDJVP50mQ4h/iTHZpGZUvPgx1u732L22tmXTEbiJSXf/j20sGEEmV776RAPLvm93oLrG85uAGBUu1EX/NiQgBCik6Lr9TvQprObKLQVkp8ejKebC0op/nljMNf1as2Lqw7yU3QSk7pM4n8T/se7496lk18n0vLTeG7Icyy7cRlDA4dWuQ6TMjF/+Hx83X15csOTFdYXrKm080a/fi098SkKMtVxXaaYjBjOZJyp03U2dhJkEo1SSnYBd320nSMJxod6VZkjRZlM4MQgU6EVLzcX2vh68vptfTgYn8H8VQed0ndd+3LHGW59bytn08r+ot9YfHbgM1adWMXs0NmMbj+6xH1ervZMJocP8KSsfN5dd5xxPVoxpLM/AEopmni41tt0uaKC2XcOuoIjCVnsjKn/wpel5VusvLL6EN1bN+GWfu0rbOfpaq46aJtZ/SBTen56ifcBYTi5JwkPH1eUSTktk+lw6mE8zB50aNqhxPIg3yDevPpNPv7Tx/i5+/H3jX/nrh/uYnfibqest7rO5xhX2yoaX66l7PtedFw6vdr6opSSwt+NQL41n9d2/BttNabunk4/55R+tdbM/HEm4fvDq2xbYLHR2teDqQPa879dsZxLLz9YEb3xLMd2JjJoQiemPNWP5oHelfYbFGJc2CUmuubZTNkF2eRYcnhn9zs17qvBKc5kahjfo/ILbZzPzOe3w/VzdcINZzbQ1qdtpZk8FekV0Iu0/DRiM+svo3vN6TU092hOQfYVeLsZF44ymxT/ntqXgUHNefyrPWw9noxSiuFth/PhtR/yw+QfmNJ1CmZT9S805e/pzysjXyEmI4b52+fXyrakJxrHtG9Lr+JMprq+wtxj6x5j+o/TJcBdhyTIJBqd1OwC7vhwGyeTsvnrNV2Bqi8Z73hVMGfVZcopsOJp/+C4unsr7hvZic+3xfDDPuf9AlpX1h0yrrSUnlP3dXy01mhb/U5D2XJ2C29EvcE1Ha7hzyF/LnN/edPlFq45Qm6hlX9c371EWx93FzLz6qPwtzFdDuCmPoE0cXfhi+2n63wcVfl8awynU3J4+voexRki5amyJpPNCpn2E8EqajJZbVZu++42Jn872al12S53VquN0/uTCeodgIubyWmZTIdTDnOl35WoCr6iDGg9gIgbI3hx2IskZCdw78/3klaHdbUSchIAaNekHWZlLvPrb4HFxpGETHoGGvURXYozmWS6XHm01pf91cuWHFxCUt458hJuAuBspnOCTOn56WQVZpGcW3WAp8Bqw9Vs4s8jOmHT8N9N5WczZZzPxbOJK/2vD8Jkrvo0oFlrL5oGeDilLlPR1NIVR1fIe2lpxUGmhjGNqKgG3dIddZ89kmfJY1v8Nka2G3lRBa17B/QGKJ4yp7Wu06ymfGs+G2M3MixwFGAqPlcA8HA18+GM/nTw9+K+z3dy6FxGjdc3oPUAHuj9AN8e/5Zvjn1T4/5KS0vMQSnwDXCYLleHQaaE7ASOph4lOS+Zf2z8R71kQDdGEmQSjUpaTgF3frSdE0nZfDijP3/qaVwyPruKIFPRdLmmbk2dOF3OipfrHx8cT1zbjT7t/fjb8r2cTq6dlNXakG+xsvWE8QU4z1K30/201sQ98SSnbr2tTtfr6HTGaZ7Y8ASd/Trz4rAXy/1CU5TJVFT4+1hiJl9GnuHOQVfQuYVPibY+7i71M12u0FqcyeTl5sKkq9qyal88KdmXTvZOWk4Bb649ysiuLRjZtUWlbT3dqqjJlH0etBXMblVmMu0+v5u47Djis+K544c7WH1q9cUMv8GJP5pGfo6Fjr0DcHE1YXHCdF+tNYdTD5Ob3YoJ/9lUYTaaSZmYeOVEnh36LPnWfE5m1N1lvxNzjKB6K69WeLl4lZkudyQhk0KrpmegUZPNxX4iXyiZTOV64fsDTHpn82U7XTw1L5V3d3+AJbM7IwOvQWvFuaxEp/RdFNCszmXGC6023Mwm2jf34qbebfhi+2nScsq+f2em5OPTrOzFEiqilCIoJIDYQ6k1LtbbddcYbj/9BN4u3rwR9UaN+mpwigp/l1Pj7XJUNE3ut8OJJGbU7RTAyHOR5FnzLmqqHEBnv854mD2Kg0xfRp5h9IJ1dRZo2h6/nRxLDoNbjQYoUxbA18uV8FkD8XIzE/bxDuKcMIvgvt73MaD1AOZvn8+J9Iqn216M9MRcfJp7YHY14ebhgpunC1l1WJNpW/w2AKZ1n8a2+G38d99/62zdjZkEmS4zYWFhLF++vL6HUamVK1dy4MCB+h5GGek5hdz13+0cS8zig+n9GNm1Bd7uRpCnykwm+zSZYP9gjqYddcoHjWMmE4Cbi4m3p4WCgoe//J0CS+1F2udtnsfzW58vd5rHhYo6lVp8pby8Or5aRPJ775GxahV5+/dTEHu2TtcNRtDokV8fwaRMvDnmzeJgUmmlM5le+uEQXq5mHh1b9qqTTTxcyKyv6XKuf3wk3DHoCgosNlZEXToFwD/aeJKsfAtzr+9RZVt3FzO5hZW8hoqmygV0hbwMqCTLZE3MGtxMbiyfsJyuzbry5PoneW3Ha1hsdf88XUpO7k3C7GqifY/muLiZnZLJlJCTQHp+Ora8NkSfzWDBT5UXIy2ashaTEVPjdVdXUZCppVdLPF09y2QyHYgzThZ7tTUymaTwd+VOnM9mT2w6L3x/6X1vqI739rxHniWXprk385fR3dBWb5JynTNF6Ly9n8zCagSZLLp4auYDozuTXWDl861lXxdZqXk0aV79IBMYU+ashTbOHqpgCvXS6RD9v0r7yEzJI/BcN5omt+K+3vex8exGtsZtvaBxNGgNrPC3xarp2soHq02z4ve6/X62IXYDni6e9G/d/6Ie72JyIdg/uPgKc1/viiUmOYf8Wvxe7mhNzBp8XH3o6hsKUHyu4qitnyfhdw8kO99C2CeRNZ5JYDaZeXnEy3i6ePLE+iecenGN9MQcfFt4Fv/t08y9TjOZtsZvpblHc/4+8O9c1/E63t79NlEJUXW2/sZKgkyiUlbrhQcNLsUgU3quEWA6ci6L96f3Y3S3lgDFV/OqqgaOY5ApsyCz+CSjJnJLBZkA2jf34tUpvdkTm86rqw/VeB3lic+K5+tjX7P8yHKm/zCdM5k1S2Vef/SPL9NVFlp2osxff+P8ojfJHHQzJztcR862uv2yatM2nt74NKcyTrFg1ALaNWlXYduiwt/ZhdlsOZbE2kOJPDTmSvx93Mu0rY8gk8Vqw2LTxdPlALq3bkq/Ds0uqQLgxxKz6NzCh26tm1TZ1tOtippMRUW/W3QH9B+/IpeitebX078yJHAIHX078smfPuH2brfz2YHPuO+X+6o1jaUh0lpzck8S7bs3w9XdbA8y1fz1X3SFGzeb8Xr67+aTbDtR8T4O9AnErMyczqi7qZ0JOQl4unji4+pTbiZTdFw6Pu4udGhuBJel8Hfl8u0ZsF9sP833e+PqeTQX5lT6KSIOL6UgbQCPjRpBgI872tKU5Hzn1P0o+q6RVVD11eIKrTZcXYxjrXvrplzdvSWfbDlVIqNTa01mch4+zct+9lQmsIsfru7miqfMHVoFeyIq7ePApjgUJkx5bkzucCttfdqyYOcCrLbLM4PN6Yqe44YSZLLZ6NKyCQODmrNs55k6+x4RlxVnfGa3GYK7+cKOc0e9AnpxMOUgyVk5RNnrU+ZX9sOVk1hsFtadWcfIdiMpLDRO0z1dy6+x1KNNU96f3o+TSdn8+fOdNc4GbenVkn8N/xdHU4/y6o5Xa9RXEa016edz8W3hyefbYnh19SG8/dzJSq2b41xrzba4bQxqMwiTMjFv8Dza+bTjqQ1POe1q4aJ8tRpkUkqNV0odVkodU0r9vZz7Oyil1iql9iql1iml2jnc94pSKtr+b2ptjrO2zJs3j4ULFxb/PXfuXBYtWlTjfjds2MDQoUPp1KlTcVbTjBkzWLlyZXGbO++8k2+++Ybw8HAmTpzI6NGj6dKlC88//3xxm8WLFzNw4ED69u3L/fffXxxQ8vHx4fHHH6dPnz5s3bqVtWvXEhoaSkhICLNmzSI/34g+BwUF8dRTTxESEsLAgQM5duwYW7Zs4dtvv+XJJ5+kb9++HD9+vMbbW1MZeYXM+O92Dp3L4N27rmJM95bF93nbg0zVnS7X078nAMfTarZdFquNAqsNL9eyV8a6LqQNM4Z04KNNJ1lzIKFG6ynPLzG/ADB30Fzis+OZ+v1UNsRuuOj+NhxJIsAeLMmrgw9ggPzjx4l78kk8evUiddjtnAy6nuTNv9fJuou8v+d9fj3zK4/3f5zBbQZX2tbd7I5JmcgpyGH+Dwdp6+fJ3cOCym1bH9PlCuwZFkXT5YrcOegKTiZls/X4pRFIycq30MSj/KvJlebpaqp8ulym/WS2pb0mVgVT5g6lHCIuO46xV4wFwNXsytzBc/nX8H+x9/xebvv+Nvac31PtbWgoks9mk5mcR8c+xrRFVzcThU7IZDqcagSZXK3t6N66CVc09+KJZXsqfE24mlwJ9AnkdGbdBZnO556nlVcrlFJ4uXqVyWSKPptOcJumxVfrKsgopF+eGUsd/Qp+ucm32BjSyZ/QK/z4x4p9xCRn1/eQqu2NqH+jbS60tk5gSr92NPFwRRc2Jb3AyUGmwqqDTEU1mYo8OLozKdkFfLXzjx+SCnItFOZbLziTyexqon1wc07tSy4bLLAWGlOPY3dABYEEq9XGgc1xFJiN7OncZCuPXvUoR1KP8O3xby9oLA1WUSZTA7m6nMWmcTErbhvQnhNJ2ew4Vbsn9Fprlh9ZzqRvJpFdmM1dwXfVqL+QFiHkW/NZHr2Dot8H6uKH1F2Ju0jNT2XsFWPJKTA+94rOVcoz9MoAFtzah8iTKfxtxd4ar39Y22Hc0+selh1ZxuqTNS8NkJddSH6Ohe9PnuefK6N5Z91xvP3qLpPpSOoRkvOSGdJmCAA+bj68Nuo1UvNSeWbzM5fMj6gNUa0FmZRSZuA/wHVAMDBNKRVcqtkC4DOtdW/gBeAl+2NvAK4C+gKDgCeUUk1rMp6NXx3h69d/d+q/jV8dqXSds2bN4rPPPgPAZrMRERHBXXeVfdMbMWIEffv2LfNvzZo15fYbHx/Ppk2b+P777/n7343Y3T333EN4eDgA6enpbNmyhRtuuAGAyMhIVqxYwd69e1m2bBk7d+7k4MGDLF26lM2bN7N7927MZjNLliwBIDs7m0GDBrFnzx769+9PWFgYS5cuZd++fVgsFt59993isfj6+rJv3z5mz57NnDlzGDp0KBMmTOC1115j9+7ddO584Vd1cKbMvEJmfhzJgfgM3rmzH2N7tCpxv6vZhLuL6YKmywEcTTtao3Hl2D+ovNzK/3Xi6et7ENymKU8s3+OUudaOfo75me7Nu3N799uJuDGCQO9AZq+dzbt73r3gYniJmXkcjM/gmmBjv9bFB7A1I4PYh/6C8vCg3VtvUlgAKBMxx3Ir/bBIzynkuz1xTvlAWXt6Le/seYcJnSdwV4+qv8gopfB28WZvfCL74zJ4anw3PCr4ZaqJh2udB5mKfp0rHWS6PqQNvp6uLIm8NAqAZ+Vb8PFwrVbbKgt/Z8SDMoO/fcpiBYWj15xeg0mZGNW+ZG2HmzrfxOLrF+NqciVsdRhfHf6qUX1ZObX3PCjj8uZgnIRanfD6P5RyiPZN2mO1uuHr6crrt/bhbFou81dVnB17RdMr6jSTKTEnkRZeRnDNy8WrxLRjq01zMD6T4MA/vrIciTzH1XluWHIb9/TKiuQX2vB2d+GtaaEoBbO/2FWc3XQp23FuB7+d+ZW886N5fFx/XM0mmni4YLM0JbMwxSnrKAoyVacmU4HFhpvDe/iAoOb079CMDzacoND+Q0JminFidyE1mYoEhfiTnZZPUmypgFdR5k1uCiSX/wNczN5kctIL+L2t8SNXWkIO44PG0zugN2/vervWLp1+MQ6nHOZQStWZ5KnZBfx2yDm1t4AGOV3OxWTi+pDW+Li71GoB8HPZ53hwzYM8v/V5egX04n8T/8eA1gNq1GdIQAgAa0/8Ma2qLurGrT29FjeTG8PbDi8+Vyg966G0iX3bMn1wB77ZHeeUiyj8JfQv9G3Rl+e2PseZjIt/3rTWLPvNqJd4IDOHAUHNAPBs6kpORgG2OphCXlSPaUjgkOJlwf7BPN7/cTbEbuCzA5/V+hgaq9rMZBoIHNNan9BaFwARwMRSbYKBX+23f3O4PxjYoLW2aK2zgb3A+Foca60ICgrC39+fXbt28fPPPxMaGoq/v3+Zdhs3bmT37t1l/o0bN67cfm+++WZMJhPBwcEkJBiZLqNGjeLo0aOcP3+eL7/8kilTpuDiYkS+r7nmGvz9/fH09GTy5GERuZ4AACAASURBVMls2rSJtWvXEhUVxYABA+jbty9r167lxAmj0JvZbGbKlCkAHD58mI4dO9K1q3EVtpkzZ7Jhwx9ZL9OmTSv+f+vWS2tufVa+hbBPdrAvNp2377iqOBBSWhOPqjNHiq4u18qrFQGeATUu/l1Uu6iiDw4PVzNv3xFKocXGI1/uclotj3PZ59hzfg/XdLgGgPZN2vP59Z9zY6cbeWf3Ozzy6yNkFFT/ShUbjxi/1v6pZ90EmbTVytnHn6AgLo52by7CtU0bCuwnbgmenck/Wn7wLy2ngGkfbuPhL3cRm1qzoN2x1GM8vfFpQgJCmDdkXrWvXOLp4knU6XP0aefLTb0DK2zn42FcXa5Or2Riz7BwLxX48nA1c0u/dvwUfY7zmfV/xZusfAs+5dQmKI9HVdPlMuPBpxV42d+TK8hk+vX0r/Rr1Y/mHs3L3Ne9eXeW3riUQW0G8X/b/o95W+Y5tY7BpezkniRaBTXF29fIYnR1Mzslk+lI6hG6NeuGxWqcMPcPas79IzvzZeQZfj1UfmZnhyYdiMmIqbPXTGJOIi29jIxYL9eS0+VOJmWRW2gtrscEkJ1ufH446+p7DU2+xYq7q4l2zbx47dY+7Dubzis/Vl6Lq77ZtI0FO17HZPUjyHU8N4a0AYwfrly0L7m2dKfUbDufY6/JdAGFvx09OLozZ9Nyi6chZqUY708XmskE0KFXAAAxpafMOV4NLTay3MdGbzyLl58r0a03gNL2q00pnhjwBIm5iXx64NMLHk9tiE6KZvqP03nglwcotFZe42bR2qPc8+kO5wUeiqZsN5DPkEKrDVezwsvNhZv6BLJqXxwZTr5yrtaalcdWMvmbyfye+DtPD3qaD6/9kLY+bWvcd6B3IM3cm3E4dX9xaY1a/46rNWtPr2Vo26HGZ0t+5T9IO2rbzKh55IwL8LiaXHl15KvkWnL55vjFXW0uLi2XGR9HEmEPMr0+64+LLbk1dUNryMmo/QvLbI3bSkffjrT2bl1i+R3d7+Dq9lezMGoh+87vq/VxNEbVm3dwcdoCjuHPWIysJEd7gMnAImAS0EQp5W9f/qxS6nXACxgDlPkZUyl1H3AfwBVXXFHpYEbc1vWiNqKm7r33XsLDwzl37hyzZs0qt82IESPIzCz7BWLBggXlBprc3f+YY+z4pXrGjBksXryYiIgIPvnkk+LlpU+ClVJorZk5cyYvvfRSmf49PDwwm6t3IufY98VcJrS2ZOVbCPs4kt1n0nh7WmjxG1t5vKsxPSnfmo9C4WJy4Uq/K2t86d2iQtmVfXD8P3vnHSdVfXf/950+s70DyxbK0rsI2BClWGKMRmOsUWMSfR6NXWMkxWiaPf7UmKomJiqxJdFYQDqICEjdhV3q9l5mZ3bqLb8/7tyZnZ26DfCJ5/XiBcwtc2fm3m8533POZ2xeKr/8+nTueH0XT39cxX3nTYq5b7JYXbMagGUly4KvWQ1WfnHmL5iWO43Htz3Ole9dydOLnmZi9sSE51tf1UpuqonZxerqxHAHf7f+5jf0bNzIiJ/9DNsppwDgDZBMnZkT6Nq0lRETwp91LfC9olEdwA1mkGD32rl97e3YjDaeXvR0v/z+Pr8Rt+Ri+UVTgjaaaEg1G/BLCl5Rjql2GmpoqoG+SiZQA8D/vOkob+yo5X8XjT8u1xMLTo8YHOwlgtWoxyvKyLIS/fvuboC0EWAJkAHuSCXTMfsxDnUd4oF5EW7vIDLMGTx/7vO8sPsFfr/n91R2VPL0OU8PySD3ZIWz00tLtYMFl4wNvmYw6REHKX93+V3UdNdw0diLOCQpGAK/211Ly1hX2cIP3trLyjuzyEoxhR1XnF6MS3TR7mkn15o7qGtIBEVRwkgmqyE8+Ls8EPo9tZeSyRX4XuQvgDrnRMArysG257ypI7jh9FJe3HyU08blxFwcOtF4/+j7VHSU4266gh9cOj2sjbEImfhQaHe3U5AyuOvXqsv5ZT9eyRuzz5FkBVkhzC4HcM7EfCYWpPHCusN8bWYhjgDJ1N9MJgBbuon80nSO7W1n7oVjQht6V0Or/QxmXR12nL3VTW1FB5OX5SE6/OjTZezN6jMzO382S0uW8tK+l7i87PKgQvBEoKa7hltX34pBZ6Dd087K6pV8ZexXou6rKAqrDzQjK6q6ZUj66qBdzq3aDk+iMfVAIMpKMI/um6cW8dpnNby7u4Fr5peE7acoCnavnQxzRr/mEa2uVh7e8jDr6tYxJ38OPz/j5xSlFw3Z9QuCQGnqZLZ3H2LJpHz+vbth2JVMFe0VNPU0ceusWwFCdjlT4nGPltvk9kkR1egGgpGpI8m15PY7g1ZRFN7cUcfD71Ygygp3j8/Hv7eLCeOy2N6h3uPGVFWR7uzqX6XL/sIrednevJ3Lyi6L2CYIAg+f8TBXvHsF9224j3989R+kmwZlmvoSfXCig7/vBc4WBGEncDZQD0iKoqwE3gc+AV4DtgART7aiKH9QFGWuoihz8/JOXMcUD5deeikffvgh27Zt47zzzou6T3+VTLFwww03BDOgpkwJORNXrVpFR0cHbrebf/7zn5xxxhksXryYN998k5YWtfHo6OigujqyCsnEiRM5duwYhw6ppMorr7zC2WeHbCMrVqwI/n3aaaoUMS0tLSppdjzx27WH+Lymk/935WwuCKwwxkKKyZAwk8kn+TDrzQiCwPjM8Ry2H+63taw3kiGZQJXAfuOU0fx23WGah6AE7MpjKynLKqM0ozTsdUEQuHry1bx4/ot4RA/Xvn8t/znyn7jnkmWFTYfaOKssL/g5hrMDtv/nP7T/8U9kXvlNsr55RfB1n1sks8CGojNw9LPwSmh2t5/rXlQD36+ZrxLRA63aJ8oi92+4n8aeRp5e9HS/Jg+tDi+dToG8dJg3JlIR0xvpgcyh42mZCyqZDJH347i8VBaMzebVrTVDIsMeDHq8Iqnm5O1yEGdVz9EE6aPAmqn+P4qSSSNltTymWNDr9Nw2+zaePfdZ6hx13Lb6tqSu8YsKLfx3zIxQv2sw6RAHmclW1VmFgsLErImBVXB1iGI26Hnyipl0uXz8+F/7Io4rTlOf7eNhmev0duKX/RTY1Oe/b/D3vno7JoOO8fmpwdd67CrJJPn+e+yU/YFKMoXanh9eOIlphenc+8Zu6ofYLj4U8Igentnx/9D5RjM9cxHn9sp5BLDp1Ta+dQgqzLW6WxFQJ97x1EyaHa4vyaTTCdyyaCxVzU7WVrbg7PSgMwjY0kzRTpMQpdNzaD7WHa5ACFMybYs4pmJTPYJOYMRcldgyZUNXS+h3vXPOnfhlP8/ven5A1zQUaHe3c8vHtyArMn+78G+UpJfw6oFXY+5/uNVJbUcgX2rIlEza76uoOVdfcPRuw2eOzmBiQRr/6GOZc/ld3Lr6Vs5acRZL3ljC3evu5i/lf2F36+5gTEVfKIrCf478h0v+dQlbGrdw/6n389L5Lw0pwaRB5y9GZ2rl7ElqsZHhVjKtrlmNXtCzaPQiIDRXSGSXg95jnqFTzObZ8mhxJ08yNXd7+M5ftnPfm3uYPDKdD+88ixKTibQcC3qDLtjOGwIkU0/n8Crkd7bsxCt5w6xyvZFhzuCxsx+juaeZhz556L8q8uB4YDhJpnqg9xM/OvBaEIqiNCiK8nVFUWYDywOvdQX+/oWiKLMURVkKCED8AKSTFCaTiXPOOYcrrrgiaXXQQFFQUMDkyZO58cYbw16fN28el112GTNmzOCyyy5j7ty5TJkyhZ///OcsW7aMGTNmsHTpUhobGyPOabFYeOmll/jGN77B9OnT0el03HLLLcHtnZ2dzJgxg2eeeYann34agCuvvJLHH3+c2bNnn7Dg7ya7h1GZVr4yIz7BBKo9KZlMJqNebRTHZ47HLbqpdw68JKvbr75fMqtfiycXoCjQ5hxcY9ziamFny84wFVNfzM6fzYqLVjAlZwoPbHyARz97FL8cfbCzr8FOR4+PsyfkqRYBnTBsHbCnooLG5T/CesopjHjwwbBtPqeLIv02LDovdR02FFH9brXA9/2NauD7soCabaBZH898/gyfNHzCjxf8mFn5s/p17J83HUWWzIzKiv9711V20vNWDSkyx7XCXKxMJg3XzC+hrtPNhoNDU5Z7IJBlBacvebucNiiLGf7taIC0kSElUxSSaU3NGqbmTI2QWcfCoqJFzEy9ikNdhzhmP5bUMV9EHN3dSkaelayRtuBrBqNu0NXltMpyk7InqSHGve7HqaMyuHPJBN7b08i/d4dXICtJV1fGq7sjF0qGGpp9qbddzt1LyVHe0M3kEWlhk33nF0DJ9NaOOhY9vvaEZCF5/VJY22M26HnuqjlIssLtr+0MEignC/6+/+80uRpxNl7A/edNiVBfpBpVkmmwVWj9sp92d3uwcmm8CnO+IMkUqQS5aMYoCjOtvLDuMI4OVTkgxFHTxkPp9FxQoKa8VzEIzd6VPxVaKnqRJSCJMvs/aaR0eg5KijqWsObo6Wp2BSd0xenFXDnxSt459A4HOweXdzkQaERHq6uV5xY/x9iMsVw16Sr2tO6hvK086jGr94d+27gFJvoDb6/fVzz5yNX+QpJDalRBUAPAd9fZOdCkqj07PB3c9NFNbG7YzPVTrmfuiLlUtFfwxPYnuPb9a1nw6gKue/86ntj2BB9Xf0yrq5V2dzt3r7ubBzY+QGlGKW989Q2um3IdOmF4prONLXkIgoJXry5gDHd1uY9rPmZuwVwyLeril0YyJaNkMhvV72DI7kdUkqnNlbiIgaIo/HNnPcue3sCmQ238+KIpvP69BZTkpNDV4iYj3xZ2jXqbOj5zDnP495aGLRgEQ9x8rpl5M7l9zu2sql7FisoVw3o9/20YTpJpG1AmCMIYQRBMwJVAWAkJQRByBSHYMvwQeDHwuj5gm0MQhBnADGDlMF7rsEGWZT799FNuuummITnfyy+/zOWXXx78v9MZ6pRcLhcHDx4M5iRpGD16NGvXruXgwYP89Kc/Db7+zW9+k127drFnzx527NjBggULIs4JsHjxYnbu3MnevXt58cUXMXg8+BvUQf59993Hnj172LZtG+PHq1aaM844g4qKCnbu3HnCgr97fGJSHmZIrppXb5n6+Cz1cw6mwlxIyZS440gJTKpdg+w4Pq7+GAUlLskEaqfyp/P+xDWTr+Fv+//GAxuiW4U2VKmTrTPLVHtKwqDlAUJsb6f2ttvQZ2Yy+pnfIJhCK7CKrODz6zC7jlBcbKAtYwLOXXujBr5rWRXeAazyvHfkPV4uf5krJ17J18u+3u/j67vcWA1WZCG+Gu3o7lYkp8hUnwHncSSZfIHKktoAoC/OmzqCnBQTr249cQHgLr+EoqikcDLQCNyo96TPpZJK6SPBlAYIEcHfzT3N7Gnbk1DF1BfuLtWu+db+L2SXlRA+j0hdZSelM3PDJtcGk37QmUOVnZWkmdIYkTIiar7MzQvHMrs4kx//c1+YsnNU6igMguG4VJjT7Et51lDwt0tUJ8yKoqiV5UaF8pgUWcEVyGSS/SfnKmlHj4+H36vgWLsLu/v4Kyi8ohzR9pTmpvDLr09nR3UnT606edYYOzwd/HHvnxBcUzi9cD6njYvM2cwyqX2iRkgOFO3udhQUxmaottR4Feb8gX7NFGWhwKjX8b2FY9le3Uljo5O0AVjlNOQWpZKSYQqqGYEQyTTmLFBkqA8FJR/Z1Yrb4WfqwsIgGZuSZ8TvlcLUULfMvIUUYwpP7nhywNc2EPhlP/esv4f9Hft5bOFjzMybCVt+y8V1B7AarDHVTKt7BX4nqqrr6vbxz6d30t2WgDjqRc4NVYW5g9ubef+FPSdEoSFKCoZebfilswsx6gVWbKul1lHLtz74Fge7DvKbRb/h3lPv5dGFj/LhZR+y5htreHrR01wz+RoAXjvwGnetu4tz3ziXJW8uYX3deu465S7+ev5fGZMxJtbbDxqdPT6qatQoiGrnfmB4lUxHuo5w1H6UxSWhMYfbJyIIYIkxNuuNoJJpCK8x35qflCLzyZVV3LliF+PyUvjgjrO46cwx6HRqNIu9xUVGnpoXpfXpilmH3qAbdiXTloYtzMibQYoxJe5+10+9njMLz+SxbY8lFfr/JZLDsJFMiqKIwG3AR8B+4B+KopQLgvCwIAgXB3ZbBFQKglAFFAC/CLxuBDYKglAB/AG4NnC+LxQqKioYP348ixcvpqysbFjf6+OPP2by5Ml8//vfJyMjI/EBg4Bk70bqil6J6WSByydhTdKTnGo20OON3yj7ZT8mnUpujMtQibPBhH8na5frvc9gSaZV1asYlzGOsZljKd9YT21F7Oo3Rp2RB+Y9wFfGfoXtzduj7rOhqo1phenkpqoDVjVoeWhXeRS/n/o77kRq72D0c89hyA3PW/F7JUDApNgpO2cCst5M5cq9XP/iZxGB79okpr8kU3l7OQ998hBzC+Zy/7z7B/Q5vH4JPZaEFXSajqgrfNN9ero9sQMRRVnk0c8e5Z2D78SUlPfv+mLb5UCduHxjbhGrD7TQZD8xoaSapbXfdrloAy5HQLWZNgp0OlXN1EfJtKZWrUnRe8CXDHRyNpJnBB8eXZN45y8gaso7kEWFsTPDn0WVZBq8kmlS9iQEQQhUJgpXWxj0Op78xky8osQP3gpNnAw6A4VphcdFyaSpU4J2OaMNSZHwyT7qOt10e0SmFYZyHTw9fmRJvU55mFfBB4rHP6oMkkuJVur9PokNr1VSvS9KKfsBQFGUCLuchotnjuKqecW8sO4w66tOnIqyN17Y9QIuvwtn4/ncuyx6bmGWORsUoV82k2jQ7rWxmSrJFK8ohz9wj/W1y2m4Ym4R2SkmOlpcpA0iA0UQBEpm5FJT0YGk9aUayVRyhvp3bcgyV76hnrQcC8WTs4NVGNPz1ffvag71hxnmDG6ecTOb6zfzSf0nA76+/kBRFB7Z8gib6jexfP5yzik+R91Q+T5pm5/l4sKz+fDoh3R4wsdKXS4fO6o7mR4I909EPNRVdlBf2UnVZ9ELFwThdYSUtQnCv/+zp5E3tsev+tV0xM7HL1dwdHcbPs/xVyj6ZTlMWZedYmLZlBG8vW8r171/HV3eLv607E+h7z2APFseS0qWcM/ce3jlwlfYcvUW/nbh37hv7n1cXnY5/7joH3x72rfR66KPVxS/n+ZHH8O9Z8+grn/DwVYUyUaBdTSu8q3ctO9dPN7hC6rW7PnnFp0bfK3HJ2E16pPKqrIOQ2xFni2PDk9HwhD8zYfbmFWUyRu3nM7YvJBV3OP04/NIZPZRMvlEhZRM07AqmTo9nRzoOBDTKtcbOkHHL878BVnmLO5dfy89/p5hu67/JgxrJpOiKO8rijJBUZRxiqL8IvDaTxRF+Xfg328qilIW2Oc7iqJ4A697FEWZEvizQFGUXcN5ncOFKVOmcOTIEZ58MvmVGbvbT3O3B4fHjyQnPyBdsmQJ1dXV3HnnnWGv33DDDTz33HNJnycZKH4fiixz9OhRcnOHN2R1oHD7JFKSVDKlmA0JrUleyYtJr5JMqaZURqWMGpSs25NkWVIIqZ1cg8joaXO3saN5B8tKVRXT9vePUbG5IcFRkGnOjNq5dHv87KjpZGFZKJPFakxQzWsAaP7Vr3Ft387In/8c67SpEdu10G+zYqdoTiEG2cO+fQ5219l57urwwHdtBaU/mUxt7jbuWHMH2ZZsnlz0JEZdcgRHX3hEGYPOGrfjEv0SbbUOLFkmsmUdrUdiTyhqHbX8bf/f+MknP+G8t87jD3v+QJdn4MRvKJMpdpdw9bxiJFkZ1jLE8aA9oynJ2uWCIZhRfu/uwL2fHrDTWjIigr9XV69mTMaYoIogWfhEGdE5iSbvfrpiVKz7IuPonlbMKQZGjA1fzDCYdMiSMuCSxJIscbDrIBOz1Im7v49dTsPYvFR+eMFk1lW28nqve7EorYhax/Dfmy2uFgQEcm0BBadBXaF1+V2UN6i/99RRvSvLhQbRyhBmZQwV9tbZeX1bDcXZ6iQgURteX9nJ3vX1vPfcbt59djft9bHVNclAs3nFant++tUpTCxI4+4Vu4Ykl3AwOGI/whtVbyDbF7CsbAYzizKj7pdhM4OUNmglk3a8trAV1y6nKZlikExWk54bFpRg8Ml4TIMLlC6dloPfI9FwKNBmaoRI2gjInRisMNfZ1EN9VRdTzxqFoBOCJFNWgaoq6E0yAVw16SpGp47miR1PIMnDT4q8sPsF3jn0DjfPuJkrJoZyHtWMKYWrOtvxyT7ePvh22HHrq1qRZCUYx+BN8My016n9/rG+Vfl6Q5bB54CUwJgqAcn0yqfHePi9ipjjmZ4uLx/8fm+Q4Pb2HF+FoiQrKAoYdOH348wJrUgFv0WS9Pz1/L8mFT1g0puYmTeTb039FssXLA+6CWKh+deP0vHSS3S9+dagPsO6ylayU0ycMmImRevLufzQetL/NXx2qtU1q5meOz0s79PVjxDvuOrtAUKzhbe541vm/JJMToopGPSuQctey8jXlEzqNfokmZRMMz3DSDJtbdyKgpIUyQSQbcnm1wt/Ta2jloe3PPxlPtMQ4EQHfw87vmg3icsr0tzt4WhbD+UN3VQ1O6jrdNHR48Prl06Kz6P4Akx+P0iwfp1/CD5jj09K2i6XZkkc/N23qsu4zHFfKCXT6urVKCgsLVkKqORMMiG9Jp0JrxTZCXxyqB1JVlg4IUQyWYy6IfWCd735Jp2vvkr2t79NxlcvirqPL0AymXQuPB4nstiK11rMs5dM5vxp4Xlcln4qmfySn3vW3YPda+eZc56JWsI+WXj8EkbBEhYQ3Bet1Q5kSWHy0iK8KLTv7Yy5r0ZW3TTtJiZmTeTZnc+y9M2l/PzTnw8o/DhYXS6OJLs4x8ZZZbm8vq0G8QRkpGiW1rQk7XLBTKaoSqYm9e+0wD1izQxTMnV5utjevJ0lxf0rvgDqdyn1TAZB5u97VvX7+JMZsiRTvbed0um56PpMZg2BAe5Aw79rHDW4RXewqqVPjLTLabhuQQlnjM/hkfcqqGlXn6mS9BKqu6uHvY9scbWQbckOEs42g0rOuEU3++q70esEJo1IC+7f0xVa+VbEE99/94YsK/zk3/vISTFz5xJVbZ2ofWyrVYmOBZeMpeVYNyt+/hlr/3YgjEzrDxIR3Bajnueuno3LJ3Hn67uQTmDxgad3PI2AiZ6Wc7lnWeyKxWkWA7KYPmglk2bNHJcZIJni2OWCmUxxFgoumzICHQJbmwZHfo+elI3eoKN6TyCXSbN2GSxQdKoa/q0olG9sQKcTmHz6KIAQyZSTit6gCwv/BpVMuPOUOznYeXDAZdOTxZtVb/LC7he4ZPwlwUpeQQQInnF7/838vFmsqFyBKIfGiGsOtJCTYmJ+oIhHokl9e4P6u0UEpveGRiBqJJM/vrXOJ8o4PCKbD0VO/iW/zAe/34vPIzH/q+oiiec4k0xajpqhl5Lpg6Mf8MKBB9DL2Yxy3xdU6A0lut56i86//x0MBryVlQM+jywrrK9qZWFZLjPyppPfqPYzI99+Bc8gzhsLjc5GytvLI+z5rn5Ef4TU20MY/B2whSdqy0RJiaqitLeo31tfJZPXL5OaZRlWJdOWxi2kGdOYmhO5QB0Lp444lRunfI/3j77P3/f/fdiu7b8F/6dJJovFQnv70Ei6jxdGZlqZOiqdMbkpFKRbMOp12N1+6jpdVDY72N/o4FhbDy3dnkEpWwYKRZJQAvktw0EyKYpCe3s7FsvgSlq6fWLSdrkUkwG3X4o7ePVL/qCSCdRcpqP2ozFDsRMhSDIZkyhLqpFMg1idWFW9ijEZYxifOR5ZVvB7pJDUPQ6MeiM+2RfxDG042Eqq2cCc4qzQdRr1sSt59ROKotDyzDPY5s4l/567Y+4XJJmEHu565RP2CD5EYwpltXUR+wZXUJIkmX792a/5vOVzHjnjESbnTB7ApwjBK8qYdFbcojtmVULNKlc6JYf9JgnPEUdQqdUX2mTjrNFn8bulv+Pti9/m/DHn8/bBt7nonYu4Y80dfN78edJtX7zqcr1xzfwSGu0e1lYef+uKRgQnE4AJCVb1HAElU1ovJVMvkml93XokRep3HhOo99fs/Bkoko33Dq3u9/EnMxoP2fG6RMbMjFSwGkzqcGKguUyVnerAPaRkUqKGGINaMevxy2eiFwTufWM3kqxQnFaMW3QnXHEdLFpcLcHVXVDtchBSMo3PSw0r6NB7pfZkUzK99XkdO2u6+OEFk4K250RKptZaBxl5Vk45v5RrHzmNGecUceCTRv7+k0/Z/sGxflsmg1bdOEUwygrSePhrU9lypJ3n1gx8cWcw2Na0jXW16/C0LuLSGRMpK0iLuW+6xYjkT6OlZ5BKJncrBsFAUZpaQyeZ6nKmGM8MgOBW99nU2EVtR3zrdjwYzXoKJ2ZxdG+b2seIvUim0fPA3YnYVMWBTxsZOzsPW7o6dtJIJpvJRka+NULJBLCsZBkz82by3M7nEtrLB4r1tet55NNHOKPwDH5y2k8irUiiF0bOAkXmKtFEU08T62rXqZskmXWVrSyamE+KWe2LEpJMdU6yRqZEBqb3RpBkyg1dQxxopOL7e8ML9iiKwvrXKmk+2s2S6yczqkxV23ldx3e+IMqalVn9bl+peIX7N9zPzLyZXFX0KJ8dkqjrHNrf17VzJ00P/YyU008n8xuX4z14EGWA85Q99Wphm3Mm5TMtewpFrbC+dAz+lFQa7v8Bsm9obXNBe34EyZT8grm2kDocSqZEqkyfJIcRihq6WlwIOoG0HHVOpy0meEVJVTJ1eodljq4oClsatjBv5DwMuuTGjBo66xei9Ezlie1P8Gnjp0N+bf9N6N83/wXD6NGjqauro7X15PDyDxSCAsgyPlHGKcm0iHLQfz8i3RwWrDfcUPx+xMD3aVAUBMPQ30IWi4XRo0cP6hyuftnlPqKmvwAAIABJREFUAlUOvCIZ1uh2qN52OYCyzDL8sp/a7toBrca4fWqHn4xdLmWQdrl2dzvbmrfxnenfQRAEfIHsjWQmA1oOlSiLwep6iqKwoaqV08blhIWMWoz6IVMyiS0tSK1tpN18C0KcqozeHnUgZhLcHKht5u4bz6bxL0c4/FkrZZeF7xvKZEp8jW9UvcE/qlTf//ljzh/4B9Gu0y9h1qmdrFt0Rw0hbDpqJz3XQk6ulb0mkVlOAwe3NTNtYWHEvj0+Vcmknacsq4xHzniEO+bcwav7X+UfVf9gTe0apudO51tTv8WS4iVxO9pE1eU0LJ6cT0G6mVe3Vgezro4XNLtcssHfIbtclN+7uxFMqWAJZOdYMqAtZH/9uOZjRqSMYErOlH5fp1eUyU+3MUKYTZ3nc9w+P1bTwGyWJwMURQlOwo7uaUNv0FE0OVLVZwi0ZQPNZarsqMQgGIKqDVGWY+bLAIzKtPLQxVO5543dvLjpKFPGhSrM5dnyYh43WLS4WhiZElJJBu1yoot9Dd2cVRZOwIUpfE4iJZPd7efRDw9wSkkWl84uZHu1qpxMtAreWuMgv0R9biwpRs68ooxpZxfyyduH2PqvI5RvqOe0S8dRNrcgqQpmQRVlgrbn8lNGs+FgG/9vzUG+u3BM0haSocLL5S9jEbLp6jyDu26MrWICSLcYUMR0Wt2DCyxvcbWQa8sl1ahmnMQN/g5Wl4v9PTo7VDLIqVf448YjPPy1aQO+ttLpOWx4vZ2uZhdZQZLJDEXzADi8oRxvTyZTzxoVPMYT2M9msJFZYKOzMdI+LggC9869l+s+uI6Xy1/mf2f974CvMRr2tO7h3vX3Mil7Ek+d/VR0C7zkhZEzIWc8i/Z9yMiyybx64FWWlCzh85ou7G4/iyfnJ6Uc8br8ODu9LLikkL1r6zi2t41Jp0WpeqyFfqcECOwE1eX8gbZkZUUzv5RCbeXedfXs/6SRuReWMm5OflBFdbyVTJraWa+Dp7Y/xUvlL7G0ZCm/OutXtHZL/H5dA29sr+OupfGfpWThb26m7vbbMYwYQeFTT9K9ciVdr72Ov74eU1FR4hP0wbrKFgQBzirLw9TmpdYH5UVmUpb+L3P/+Evannue/LvvGpJrB9UqNz5zPKUZpWGvu/tFMgXuxyGuLgeJK2VGK9QBYG91k5ZtRh9o37U5g0+Uyc00I4ky3h4RS+rQjpGqu6tp7Gnkpmn9L7q1s9aOs+EbzJj3V+5dfy+vf+X1YIXPL9E//J8mmYxGI2PGDF/lgROJj8qbuPmVHbz9v6czvZeaZLjhWLOWultvA2DM229hmTw4hcdwQQ3+Tt4uB6pSIhbJ5JN8pJpCYXbjM1VP+MGugwMimVw+Cb1OiLlS3xvaQGagdrk1tWuQFTlYVU5b0ZKSscsFiDWf7AuSTEfaeqjrdHPz2eGVAy1GPV2uoVnd8ZSrZYMtU+JP8n3dqvrHpHPxk6WlLDlzAm++tIm6zjwUWQmb5CSbyWT32vnl1l9yRuEZ3D779sF8jCA8fokcvQ0k1erWl2RSFIWmI3ZGT8zCbNDTYRaQdAb2b26ISjJpk42+58m15nL7nNv5zvTv8O/D/+aVile4b/193DjtRu4+JbYiLNmJnlGv44q5RTy39hAdPT6yU0xx9x9KBO1yyQZ/xwvBdDSEVEwAlpBdzuV3saVhC5dPuDypsM2+8IkyZoOOC0rO4eVDm/nbzo18d/65iQ88CfFxRTN3/2MXW364GJtJz9HdrYyelIUpCtE3aCVTRyVjMsdg0ptQFAV/n8pE0fD1OYV8VN7E4ysreffOSYCaVzZ3xNwBXUMyaHG1MCNvRvD/ml2uwd5Fq8PLtFHhWVU9XV4sqUZcPX4U6eQhmX7zcRXtPT5evnEeOp0QXAWPp2Ty9PhxtHvCiAOAzAIbF/7PDOqrOtn85iFWvVjB7tW1nPGNMkaNj55bpCGZPDhQyYfTxubw7u4Gut3icSWZFEVhZ/MunJ0TuXLuOIoC+VWxkGYxoojpdHk78Uv+YN/ZX2iqOb1OT6oxNSklUzySyREgmc6eNZIV22r50VemRK1GlwxKpufA63BsbztZmQGSyWiFjCIwZ7Bvl0RmgY3CiaHxqaZkshgsZObbOLanDVmSI6y3s/JnsaxkGS+Xv8yN024MErmDxTH7MW5bfRu51lyeX/x8UIUYAdGrEmbzb0a/702+aS7kN03bONh5kNUHRAw6gbPKcoOKkXjKkfZ6lUjLKUylZEYuB7c1I4lycNIdhEYypWokU2IlU7rFgN3t55PD7Zw9IY+6yk42vXGQ0hm5zLtInftYUtR778QomURWtf6GCsc6rpx4JQ/MewC9Ts/oLDhzfC5v7qjj9sVlETk+/YXs9VL3/duRe1wU//nP6DMzsUxQyStvVdWASKa1la3MKsokO8WEY+sxAOpGuimYeAqLv/512v/0J1LPWYRt9uxBXTuoFSt3NO/gO9O/E7Gtxycmrd4Okp5D5CgANZfVIBgSVpiLbZdzB61yEFLLe0U1kwnAGegjhxJbGrcAJJ3HpMHjl6ho6AbZwmNnPcW3V17HHWvv4JULXondXnyJmPg/bZf7vwyNDBlKxjoZ+OtCNiTZNTxS5sFCUZR++Zg1ybMzjlLIJ/uCqh6AMRlj0Ak6DncdHtA1unwStiQrRuh0AlajHpdvYIOEVcdWUZJewoQstdP1BRQhSWUyaSRTrwpmGwJVfs4uC1cLWI36IZPpevaVg06HZVL06j0afA6VbDELPSwZp9oXSkoMeHUpNJaHy8iTrS7X4elAlEUuHntxzOol/YVXlLH0CgjuC0eHB5fdFwxTTrcacI4y01LtoK0ucvVay2TSVrj7wma0ceWkK/n3Jf+mMLWQ5p74VW207ySZCcdc41H+ZVxO53GuMBm0yyUR/L2zZSd3bLwWwdAV/Z7sbgyFfkNY8Pem+k14Je+ArHIQsEYadNw45zxQdLxT9cXNZfro8GbEkU9woK2ajoYeuts8lM6IXuwhqGQaYBtQ2VHJpCyVKNKUuvGsP6ASD0unFKjEHjkYBMOwVpjzST46vZ1R7XKVLaoNZuqo9LBjeuw+UjLNSAIIJwnJdKCpm79uqebqecVMC1TIsiQxQWmrVSfCecXRrWKFE7L4xgNzWXzDZHrsPt554vOEBSYSVbbsjdyq3Tyx4Tl6XMc3ALzOUYfD3w2+Yr5/bvzQYVDbb8Wv3geDsW+2uFrIt6r3WqopPsnkE+NXlwNwdnixpBiZPDoDrygPSnmcnmMlpzCF6r1tIULEYAadjvasZTR15jDlzFFhYxy36Mait6ATdGQWWJElJUh89cVFYy/CLbqHrJy4oih8f833Afjd0t+Ra41TtEb0qNa/EdNh/FIuq9yEWW/mtQOvsWZ/C/PHZpNmMQYn9fGCv7Vg/NzRqZROzw0PTO8Nb6DQh2aXSyKTaeGEPFLNBj7Y20h3m5uP/rCPzHwrS2+cElxgM9vU8a3XdXyVTC6fB2vRK1Q41nH77Nt5cP6DYeOpK+YWUd/ljpop1R8oikLTQz/Ds2cPox79dZBcMgcqeg8kP6nd6WVPXReLJqjPnpbtVDeqFae/h4IHf4hxxAgaH/jhgOdB3sOHOXLp12l/8SXW165HVuSoGZADUTINZTaqTtCRa8tNSsnU1y6nKApdLS4yepFMvZVMqVkBkqlz6NvzLQ1bKEwtDFqNk0V5gz1o9SywFPH4wsc51HWIH23+0RcqeudkwZck0xcUg1W3DBT++vrgv09WkskrysgKSa90piZBMvW1y1kMForTigcc/u3uh9IK1PDvgfzWnZ5OPmv6jKUlS4ODPS3HKBklkyYl70sylebYKM4JZ/WtJv2QBQ56KiowjR2DzhZ/5cDnUMkWk+CCgIVs/LmTEGSJqlX7w/ZNVsmkSfothsHlgoWd0y9h1aufpUeMtAg0B/KYNJIp1WygJduAziCwP8okTSOZotnuekOv02M1WMN+v2hINpMJIN9xgBm6o0gdw18uvje05zMZu9yBjgMc7T6EZdQbuLxRBteOpnAlkzVTtSeIXlbXrCbLnMXs/IGtUHoDSqZsaya5xklUu7fj8BzfAf5Q4YijEr2liV9t/xGHd6tE5ZiYJJOmZOp/O9Xh6aDF3RIM/U5GlaFBU6K6/TA6bTQ1jv4H3ycLbaBdYAtZRTUl05F2lWSa0pdk6vKSkmFG1p8cJJOiKPz0X+WkWQzcuyxE4lsMia0/rYHQ77yi2HlEgk5g0oKRXPPwAtJzLRzbE38SmUzRAQ3plXuZ2nEMd9PgArX7iz1tain00tTJ5Kcn7hfSLEZkUb0PBhP+3epqDRKaqcbUuNXlgplMhtjErKPTQ2q2OTjRS8Y6Hg8l03NpOGTH2xPoXwJ9ZrnzXPT4mDQ7vH9yia6gKklTN3Q1RydTpuWqVr69rXsHdY0aah21nPvGYe7XXUBJekn8nTUlE8CZd5HZ08aFqeP49+F3OdjWyrmT1Oc/mUl9e70Ts81ASqaZ0ZOy0Bt7Bab3RtAul1x1Oa8ok2YxsnhyPqv3NvH+C3uQZYUL/2cGJmuojzQY9RiMOjw9x0/J5JN8PLT1fgyplXxl5Pf57ozvRiyoLptaQKbNyIrtg6sI2vnK37C/8w65t95K+tKlwdd1KSkYi4vxVvbfsrrhYCuKAudMUn8LT2UVyqh8PFYfB1z/QZ+ayshf/hJfdTUtTyRfPVyDa+dOqq++Bm9VFS2PPUbdilcYlTKKSdmTIvbt6ceCuWUYgr8B8q35iTOZxEh7u9vhx++RgpXlIDKTCRjyCnOiLLKtaRsLRi7otxp9Z02IAPaKMmcUnsGdc+5kVfUq/rj3j0N6nf8N+JJk+oLCFq9y0jDCV18HgRwmuSd2OfYTif5UboMQyRSvwpxP8oVVlwO14svBzoMxjogPtz/51QlQCZyBrE6srV2LpEhBqxyA162eJ5ng7952OVA7hk+PdHD2hMjME4tRN3RKpvJyrFMTV4Tw9rgRkDAIXggohDLnzSLLcYhjhz1hKw8GvQ69Tkg4sPZIw0EyycHJaDQlU9MROwaTjpxCdVCeajHQLcuMnZVH5WdNEYSg0+/EpDOFEZ+xYNKbgr9fLHj9EoJAUvZNi6Cey9cTu/rdcMDhETHpdUkRYRqpZkg5zLbO98I3yjI4GiPtcoDf1c6Gug0sKlrU77DI4HuLUvAal5Scg87cxIqdewZ0rhONLrfaxld27WPrlgryS9KCA8O+MAbaM/8A7HKVHYHQ7wDJJEqJVRkaUnq138XpxcOqZNJIpmhKpurOLkpzbKRZwmX/PXYvKZkmZJ1wUpBM7+5pZOvRDu47byJZveyudn8zppx1wbzAaGitcZCaZcaalrjdMZr05BWl0dkUfzEqWbscgNmhTgDcbTHCk4cJ+9r2ISgm0vWR1uVoSLMYUESViEs0OYsFl9+Fw+8IZqKkmdLiV5fT1KhxMgydHR7Ssi29JnqDm4yWTs9FkRVq6qyAAHoTfq9EZU0B4yyfYLWHt3tu0R0imQo0kin6/ZFny2NEygj2te0b1DVq2Ld/Ped/rjBub0finXuTTCWnw+h5XFVbgVfyYMzczuJJ6vNv1Osw6IS46r/2eic5hakIgoDRpGd078D03vBqwd+aXS4+yeQTJUx6gfOnjmB+u2rLW/adqcHvtTfMKUa8xymTSavKu63lEzyNlzIv98Ko+5kNei6ZVciq8mY6ewYWs9Dz6ac0P/ooqUsWk3trZHaXeUIZ3qr+k0xrD7SSm2oKWp+9VVWkTZmO2TuLo+J7tLpaSVkwn6xvXUfnq6/S88knSZ/bsWYtNTd+G31mJmPffRfLgnmc8/f9fLNrQlRCxO2TsJmTG4vodQIm/dCNwzXk2fIS2+VkJUIJr1WWy8iLRjLJ2DJMIDDkFeb2te3D6Xf22yoH4SSTZh2/YeoNXDDmAp7b+Rzra9cHt/s8Ip/+83BSC/b/rfiSZPqCwiL7WFKzDfdxrjDnr6vHHMi5OlmVTJqtLFkfc9Au54lPMvWd0I/PHE+Nowav1P8GUs2MSn4Sm2Iy0DMAu9zKYysZnTo6bIXE59KCv5MgmQIWQb+kHrP9WCduv8TCqCSTfkjsm/6WFsTWVixJkEx+lw+T4EIQCJJMgslEYWo3PZKVjoZwItSk1yVUMmm5EUOVA6EoCh5RCqqOYpFM+SXpwWyKNLMRp0dkyumj8PaIHNkd3sFHy3WKBZPOlPAe1dQ3yaz6mFEHhLLr+JJMPV4xKaschNRoorOMTzv/ylH70dBGVzvIfkjvlStjUQeTW+s24vQ7WVISKVtPFppdDuDq6ecB8Nb+L6ZlrtvrRJENLMm6HF1rCroxsdt8TckkDYBkqupUJwJaZblgOfYkSE+t/XZ4RYrTiql11A6brF1TpYSRTAHyuN7exdTC8DwmWZJxd/tIyTCj6E88ydTjFfnFfyqYVpjOlacWh23b3LQWc/6HdHpjP9dttQ5y46iY+sJua8Xe6oo7CO+PitJoV6/N13F82569bXsxS8VYjcll0KUHMpkgcWBuLGiTOk01l3QmUzwlU4eX1GxLsJLfYJVMBWPSsaQYOVafrqqYBIGD25vx+QSm2lZC3baw/d2iO7h4Y0k1YrYZ6GqJ3aZMz50eVJENFi3bNgGQ5k3QpkgiKFJQlYUgwJl3Mbm9hhFiHrbcrRTnhMYGasGT6Pe3Iiu0N/SQUxiytZdOz6G71R1JrvVVMvnjk0x+SZ3UZ9d4mOw3YC9LoWRqTtR9zTbDcclk8kt+7ll/D+vq1vG9Kffi75ofN1fvm6cW4ZNk3tlZH3OfWPDV1VF/x52YxpQy6tePIugi38cyYSK+6mpkT/J2LElW2HCwlYUT8tDpBGSPB9+xY1gmTCDbdymKIvL8rucByL/7bkxjxtDw4HKkQD5oPHS+8QZ1t92GuayMktdexTx2DFX3XkpNPix4fiPufeURx2jRGsnAe/QoZzaXJ6wQ2l/kWROTTH5JDlYS1NDVoo6lM6PY5byijF6vw5ZuGnIl05aGLQgIzB8xv9/H7qrtiiDhBUHgZ6f/jEnZk/jBxh9wxH4EgPrKTnZ8WE3TUXvM8/2340uS6QsK5Y3XuefzFXDsyHF9X399PeaA51nuOTlJJk3xk6wdbSB2OYDxWeORFTl8ApvsNfqTl8CC+ln6a5eze+1sbdzKstJlYeSBpmQSB6BkWl/VilEvsGBs5GDGatQPSeCgp6ICSBz6DWrOgFkXIJJ8oftx7JwCUGQObToWtr/ZqEu4ehu0y+mHRsnklxQUBVJMASWTGP7ciD6Jtlpn0CoHASWTx8/oSVmkZVsiLHNOvzNpksmsNwdJwlhQSabk7kcz6rkk1/HNZHJ6xaQry3klL3pBj67tSvSCieWbliPKgefbEfguoyiZVtdvxGawMX9k/wcnoBKKPkkODlLGZIwhXT+SY+7ttDiOb47MYCHLCj1+F4ps4mz/NwB42flszEmzwagpmfrfBhzoOEC+LZ8sixoS3C+7XB8lk1t0JxwQDxQtPZEkk0ZG2709EXlMrm6/+uxnmlF0AsIJXvB8ds0hmru9/OziaRFhu6KsDvRdMSa3fq9EZ7OLvKLoOXDR8M/2FSgybKiIXQZay7NJRsmkD5BM/uNIMvllP/vb96P3lwQthYmQZjGgSDZ06Ad8L2rPWdJKpgTPjNct4nOLpGUNnZJJpxMomZZDdVM2cqC/LN9QT/aoFEaOUqD2s7D9eyuZBEEgI98WU8kEKslU76ynw5OE+igBpL2qfV6xJyADelfK0zDhfKTciVzZ3oqsb2NT/abgJkucLEpHhwe/RwoqlEG1GIIamB6GYPC3ZpdLkMkkyVjafGx/7yj2PCNv9XQHK7r1hSXFOOzV5fyyn/s23Mfa2rU8OP9BFhdeCoAxTqj35JHpzBidwYpt/VsYkF0u6m69DUVRKHr+efSp0cdC5gkTQJbxHko+O3VXbRddLj+LJgbymA4fBlnGPGEi6foR5Mjn8M6hd6jqrEJnsTDq0V8jtrbS/ItfxDynoii0/va3NP34J6SceQYlL7+EIVut0Ppx+2ZeuC4HY1YOtTffjK82ZB+UZUUlmZKYK3R/+BHHLruc+zb9GVPD4CyIfZFvy8futcdcrNQKdfRte+wtLgSdQFpuaCytxVZobU9qppmezsjzSrLE7tbdyEr/26gtjVuYkjOFTEv8ohN90dLtob7LzSklWYFrDD3XVoOVZ855BrPezB1r7qDb1x20oGoRJF8iEl+STCcZ3KKbPa3xV24UWcb77r8A8B1Hokey25EdjhDJ5Epsl2t3Di1DnQx6BmiXixv8HcUuV5apBgsOxDKXbMehIcXcf5Jpbe1aREUMs8pB/zKZ+gZ/b6hqZW5JdlA90BtWox6/pAQniAOFp7wcBCGpyoU+j6TmMUFQyQSQs/BU0ruPcXhHePh3Mkqmoc5k0oi3WEqmlhoHsqwwYmxogppmNuD0imq+yekjqT3QSXd7aNDZ4+8Jq3YYD0a9MYlMJimpSR6AMUAyKZ7ju3rj8IikJllZzit5MevNWPVZTLfcyN62vby470V1Y3fgnuijZJKANa07OGv0WRHPerLQCMXepXzPLjobve0wb+/sPxl9ItHe40PGC7IJ+yEn1mwDzZYafrDhB0hyZFs0mEymys7KoIoJ+kcy9VailqSpWSvDZZlrcbVg1ptJN4WeVb1Oj1FnRtD5IivL2dX+LyXTjKIHvXzilEyHW538edMRLj9ldHAQ3Ruiol6rO0bgcHu9ExSSVjIpikKzWc3HenbN7/ng6AdR99MmG5YkMpmEDnViLh3HogNVnVVqdVXvKNJ1yRHFKSYDOkGHVZc1YCVTX2tmmiktgZJJC8uP/j06AwHb4ZlMg2c9S6bn4PWbaJam0FLdTUu1g6lnjUIoOlVVMsmh9+hNMgFkFljjKpm0XKbBWuY8ooesI2o2WELFiRSeLwWATkf5mG/zLVcNWfpUXjvwWnCT1aSLGfythX7nFKbS6VGJ0bRsCzmFqWpgem94u8FgBa1fj1NdTpIV0v1g3tZJ7uhUZlw6lnaXj8+ORSfjVCXT8JFMftnPDzb8gNU1q3lg3gNcNekqpEBbl6hC6BVzi6hsdrC7LrnxhKIoNDy4HO/BgxQ++SSmktj5WuaJgQpz/Qj/Xl/Zgk6AhWW5gWNVla15wgQsRj3pngtINaby1PanALDOmEHuzd/D/q9/070qUrGsSBJNP/sZbf/vWTIuuYSi559Hl6KOBb2Slw11GzhlyhKK//RHEEVqv/NdxA71d9TGjvHscooo0vz449TfeSemceOQBB1jtw6tclojumNZf7Wg7L52ua4WN+k5FvS97gFBEDAZQuPwlExzhF1OURQe+fQRrn3/Wv5a/td+XavD52BP656BWeVq1X5l/hh1Eb1v+zgydSRPLXqKOkcdD2x4AHfA5vklyRQbX5JMJxke2fII//Px/9Dti90RurZtRwyw3aL7+K2Oa6HfpjGlYDQmVDJ9XNHMvF+uptEef0VmqKHZ5ZIN/k5JkMmkKIo6yNSFT3CL04sx6AwDqjDn9knBkL5kYDUa+k0yrTy2ksLUQqbkhCuCvIEGUZEVpASEUG+Sqbnbw4EmB2dPjLTKQe/QwcGpmTwV+zGNGRPsiOPB51UwCoH7yxciPS2TJ1PgPEBnt57uttD9l5SSKZDJNFR2Oe37SDWpn0cL7dbQdEQdXBWMCU1Q0yyGIOk5+XRVcXPgkxBh1h+7nFlvxisnsMv55aSCdwFMSmAQ7j7+1eVSk7TLaSST2aAnm3mcX3o+L+x6gf3t+9U8JoC0EaEDrJnsNpvp8DujVnhJFr5g+G7ou/zahCUIOpE3ytcM+LwnAg1dbtB5MfhT6anpYcKskSxfsJztzdv5/Z7fR+wfrC7XT7ucT/JxtOtomKU3ZP1JfE9q6jZnQMkEUNM9POHfWkn5vrZSPWbQ+SIrywUGzykZJtDr0J0gJZOiKDz073IsBj0/OD8yXBYIrlK7YkxuW2viV5brC1EW6TA3o6AwkZncv+F+Xql4JWI/X5J2OUVRUDrVyZd8HEmmfa0qwfE95zruqrkNohCsfaHTCaSaDZiFzAFnMgVJJmt48HcsxUciYlar4haWyTQEWSLFU7LRCTLH3LMo39iAwahj4vwRUDQPPF3QHiqOEkEy5dtwdnhjEtNTc6aiE3TsbRtc+PeBln2MbVS/t4Qkk6Zk6qNcf9V1Km3kcKVfYFP9piCRbTHEVjJpJNNTR37FwhUL2dWyC1Atcw2H7OHEj9cB5lTQ6UFnjFtdzuuXuNBlQtALXHDLdBZPG4HVqOf9vY1R9zenGIfNLifKIg9seIBV1au4/9T7uWbyNQD4A+Ri34pjfXHxrFFYjDpWbEtOfdP++z/g+PBD8u+5h9Szzoy7r6m4GMFi6Vcu09rKVmYXZ5FpU39/b1UVgtmMqaQYi1GP32/l5hk3s7lhM5vrNwOQe8stmKdMpumnDyG2hxRqssdD/Z130vX6CnK++11G/uqXCMbQPGJr41ZcoovFxYsxjx3L6Bd+i7+pidr/+R9kt5seb/wFc7Gjg5qbvkPHn18k86orKfn739g7ZjYTdm1A9g7dAr/WBsVSZWptT1+7nL3VFRb6rcFs0AVVQqmZ5gi73O/2/I63Dr7FSFMeL3z2DAcby5E9nth/en3WbU3bkBSJ00YOLI/JqBeYU6IqoKK1j6cUnMID8x5gY/1GNh/Zou7nPr7ZyF8kfEkynWT41tRv0e3r5i/lf4m5T9dbbwb/7XcfPwLHV1cHgGn0aHQ2W8Lg71UVzUiyQnP38VUzuRI0zH1hMugwGXQ4vdEbClERkRU5Qt1g1BkpTS8dUIW5/iqZbCZ93EDWvuj2dbOlcUtYVTkNvVn3RGomLZPJJ/vYUKV2MAvLYpBMpqGpbOEpL0/KKgfg9QkoOj8KQpiSSdDpKClVO/Mju0IAaTeJAAAgAElEQVQdY38ymQaqZom4xsD3kR4gmfra5ZqPdJOeZ8WWHhrUploMODwiiqKQlm2haHI2+7c0IgdWjJw+J6nG5JRMJp1pSO1yhgDJpPMeXyWT0ysGVYeJ4JW8mA3mQMVDieXzl5NlyeLBTQ/i664DBEgNVQjDksHqFCtGQc+ZhfEHrvHgixJiPCd/DibBRp33cw63xra7nGxotLsRdH6KuiegSAqlM3P52vivcfG4i/nd7t/xWWO4FSaoZOonyXy46zCiIjIhe0LwNU2VEc9qoUHL3nN6RUakjMCgMwxbhblmV3OYVS4I2YTNLJKTGt5muHormQwC+qEkmf58Hnz6u6R2XVnRzMaDbdy1dAJ5adHbNY1cd8WY3LbWOrCkGINlpxPBI3mQ9H506RILU5ewpHgJj217jKe2PxVmgUg2+Ft2OMAXILi7jx/JtLdtL9mWbMb6HYz0HoED/0nquDSLEYOSOSi7nM1gCypWU02piIoY/J36wp8gx8wZsKSoJNPQZDIBmG1GRmY0c9gxi6ptzZSdWoDZZoTR89Qd6kLtRKSSSbWQ21uj33M2o41xmeMGTTId2rEGswhCehpSd4J+K2iXCymZZFnh48pONuVdxRV1+zEIel4/8DpAsI+JhoqqozgtHaxq/AiAnS07ASidEQhMr+ilPPI6wBwgcI3WuEqm2qpORkk6DDMySc+xYjXpOWdSHh/uaw4qiHrDYjMMi11OlEUe3PggK6tXcu/ce7luynWhbcE2PP5znW4xcsG0kby3uyGhZc5TWUXrM8+Q/tWvkv3tGxNen6DXYx4/Hk9VckqmVoeXvfV2zum1kOqtqsQ8fjyCXo/FqMPjl7hq0lUUpRXx5I4nkWRJzQF99FFkp5PGn/wURVGQ7HZqbvoOjo9XU/Dgg+Tfc3fEWHx1zWpSjClBe75tzhwKn3wCz9591N99Dy6Xeg9EWzB379nD0a9fhnvXLkb+6leM/OlP0ZlMbJuxCKvbieOjj5L6zMlAUzLFUmX6xchCHYqiYG9xk5EfJYjeoA+2+ylZZrwuEX9g/vX2wbf57a7fcr1xIc/8opWXH/MgnnM5lbNmx/4zcxadr68A1Dwmq8HKrPxZ/f6cu2o7mTwynQyrOneI1T5eMfEKLiu7jIqGA8CXSqZ4+JJkOskwKXsS55eezysVr9DujqygInV34/hoJZaZMwAQPcePwPHXq1kmxsJCdCm2hMHfmw+rcmDXAAKrBwOXv38kE6iWOWe0UueErGLRSIeyzLLjQjKlmPVBG2AyWFe7DlEWWVqyNGJb7wYxkepAUzL5JT8bDraRl2Zm8sjoK9nWIVAyie3tiE1NSYV+A/h8emRE/DpLxMpf/ukzSHXWcejTuuBraucW//q0Ff2hsstp72c1mTDrzWF2OUVRaDpiD7PKAaSajUiyEiTsJp8+EmeHl7oD6qC0P0omo96YRPB38nY5fUAVpfcdX5KpxyuSaumnXc6oVmXMtGTy0OkPcajrEM+1bIHUfNCHzqWY01lts3GadWTSNsSo7xv4rU29CDuj3siCUadjSN3PPz+vi3XoSYf6Lg/ovIyxT0Aw6xg5XlXaLZ+/nJL0Eh7Y+EBYH6U36EDov5LpQIc6UJuUFUXJlIRdTq8TsBr19HhFDDoDo1NHD5uSqdXdGpVkEkUjabbIz+3s8iIIYE1TVQeJ7HIOn4PrP7g+cc6fLKuT9z7BytHg8Us88l4FEwvS+NZpsa0l3sCk1h0jC6a1xkFecWrSJaG1NseQI2Fv8vDE2U/wzYnf5KXyl/jRph/hl/2B99UymeL3h2KvinK6RETBEGJf2z6m507HqATGB5uehiTyY9KtRgQ5fVAkU+97Lc2o9rtOX3SiWiO4Y6n/HB0edHoBW7op2NYnWnBJFqVZR7H7chG9ElPPClTgy52gFlSojUMy5cevMAdqLtO+tn2DCvPv2qk+J2lnnoncZY9/Lo3c6ZXJtLfeTpvTi3ne9eSaMllKKv889E9cfldUJZPda+fBjQ9SU92EJ8PO6195nQJbQbDAQX5pOpZUI8d6W+Z8zhDJZDDHzWTat6qWHkHBOjE0brhw+kjanF62RbHMmVOMiD55SKtgSbLE8k3L+eDYB9x9yt1cP/X6sO1aPlTf7LdoGJ+fisMrBpXAseDa+ikoCvn33pN0O2SeOCFoeUuE9YGFVC2PCVRiyzxRtXJbjSqhaNQbuXPOnRzsPMi/DqvRJeayMvLuuAPn6tW0//FPVF97LZ49eyh86kmyv3VdxHu1uFpYXbOahaMXhuW9pi1Zwogf/wjn2rW4Hv8lKErEXKHzH/+g+pprEfR6Sl97lcxLLwluaxgzhbbMgiDpMhTQ2qFYqkxNtda77XF1+/B7JTJjKJl8vTKZQFX9bqjbwMNbHuaMUWdwbZuap9V941f5+yIdh646jbx77o76xzRuHJ2vvoqiKHza+ClzCuYkVXm5N0RJZk+dndlFmUFXRizXgyAILJ+/nBEGta1rsbdF3e9LfEkynZS4ddat+CQff9r7p4ht3f/5D4rXS/bVVwMgHU+Sqa4OXVoa+owMVckUh2SqaXdR16l2ku4hqDjWH2iKn2TLfoJKMvXEUDJpJJNRHznBHZ81nnpnfYQFKhE8fgmrMfnrsxoN/foeVx1bxYiUEUzPnR6xzdtbyZRgkKlZBN2ih40HWzmrLDdm565lagymfGow9Htqckomn2hEEkQkgzXMLgdgW7CA3LbdNNe5cXWrv6HJkHzw91ApmTSiyGLQYTPYwpRMjnYPrm4fI8aEZ7mkWbSKWerEZuzMPCwpRio2qXL4/gZ/J85kkpMmmYRAMLAhTj7IcMDRH7uc2ItkCtyPC0cv5LKyy3jZW8vn6blh+1c6a6g3GlhsjK7SSxbRlEwA5405B53RwVvlnw1b5bOhRkOXG53gp6S7FEOhLZirYDPaeOLsJ7B77SzftDyoShEEAYNR1+9MpqrOKqwGK0VpRcHX+mOXA1X5pylRS9JLqHYMfSaToii0uFqC1b40uHwiXr8Rqznyc/fYfdjSTeh0AhgE9Al++iP2I3ze8jkb6zbG39HTBYoMzuaE1/3nTUep63Tz0MVT4+ajaAoZTxQFhSTKdDT0JG2VU88TaEdzVRJBQMfy+cv5/uzv8+6Rd/n+6u/j8rtCSqYEdl2xLTTB0TkSV3IaCjh9To7YjzA9dzoGxY+MDho+h6MbEh6bZjEg+9PjBubGQ19CM82kfvcOf/R2N2jVjZPJlJplRtAJQxb8raE0Q53E5xalkl8auEd0OiicG0aE9iWZNCtN31wmUZL5+9ZqfvX+fqblTMPutVPnGDhBb6w4gjvNhHnyZBS/HyVetbEoJNPqA2pOz5lTSmH+LVxdX4nT7+Tdw+9iMenD1Nsb6zby9X99nZWHVpHlKWDZnIVMzJ7IhKwJVHaqippgYPq+9qA6WVUyBUgjgzVmdbnWGgdNVV1sN4uYe41xz5mYj9mg44MoljmLTd3PM0S5TJIs8aPNP+L9o+9zx5w7uHFapKrIL2vqlsRkkHbPJlSZ79qNYcQIjAUFcffrDcuECUgdHYhtiYmAdZUt5KWZg7Znsa0Nqb0dSyDbqXfI+9KSpczKm8WzO58NLhxm33A91rmn0PrUU/gbmyj64x9Jv+CCiPdpdbVy00c34Zf83DD1hojtWVddRc7NN8N7/+Kqyo+DRYxkr5eGH/2Ipp/8FNu8eZS++UaE6t9qMvDp1IW4P/8cTz9sgvGQbkrHpDMFq6v2RbC/7kUo2gOV5aIrmXS9lEzqYu7eY/u5d/29TMyeyFOLnsKzdRuWKVOY/4PHUK69hB+P+ZymS08j97vfjfiTfe01eKuqqNuxkWPdxwZklatqduLyScwuzgq2j/EWzI16I9PSZgLwafW2AQWU/zfgS5LpJERpRilfG/81VlSuoNEZ3mF0vfkW5okTsc6ZA4DUj9Kcg4W/vh5jocrc6lJS4trlNBUT0C8FzlAg6GNOIvPI6xbZubKGFJMehye64kobIEYjHcZnjgfoVy6Toii4fP2rLqcqmcSkJqhOn5PNDZujWuWgn3a5wGrA4TY7XS4/Z0+IPQnXlEyDIRU95WoJ12RCv9VKXib8OhlJbw2zywGYSksZqdQDAkd3qxMUc5Ikk0VvQScMTfOordRbjHpsRtv/Z++94+Q6C3P/72nTZ7bvarXSqu+qWsUFuYAb4IIDxgTMBcL1BVOcUEwCP+6FcCHhAimEZpyEUOIEEsrF1zY4uBfAcgHbqitp1bVdW2d2dsqZ035/nDlnepNkxQE///jj0ezulHPe8rxPKSAknerT/GY5yCOZsteklM25OL57itRCxg7+rtcuJ3lqk0xa/Xa5qJbg3yIhZP0c2+XSDdjlTJtkKt4AfOLCT7DYFPi0N12gKHv05KOIlsUVlC6IGoFzbRUHYNoWPIFpc6cbLvlyx3gsRU9qEX7DDz2Fn0t/az+fvOiT7BjbwT/v+2f3cdkjnZaSaU3zGiQxd/1l9Po3KOAoUe17pTfSy/D88Flf9M1n5lENlQ5/4Rh4YHwey/DgkUs3cMmoSjB7UivIInKN4TuRJcprqmOTWbVCorZKZufQHH1dIS5eVb7e3IFDCqllSKbZsQSmYdUd+g25edPfLmHoJvPTKQRB4P3nvZ+/uOQveHb8Wd7z0HuIZpvDKpEjYAcLnzixG4CpkB954dyQTAMzA1hYrG1djwed0eYLIdhpq5lqIOJT0DX78zqdXKbJ5KRrUwFchWWl8O9ylpV8xGfThLIbupxd7uzcI83KBOct2snFN64qXHMsvQgmD0C2JCKlpfArOZLJ45MJNnkKlExPDk5y3dd/zafv2ce3fnWM1U32JnrPdPVCnEqYSk7RM5Qk3d+LFLHn2aq5TGVIpscPnmJbbwutQQ9c9D42mwrrxCA/PPhDvJJAWjOIZ+J89unP8seP/TERb4Q7z/8OWAIdS+y/2d/az/Hocde6vnxTO2pC51Q2kxF1Ps8u58vZ9orw4sMnkb0iuz16wTwT9Mpc0d/BA/smcsRVFt5g1v6TODuugv/z3P/h/mP385GtH+HWTbeWfY6jZKoV/A25+bImybRnD/7Nmxt6rY4KKV0j/Fs3TH51aIor+jrca9jJcnLKjvx56wlBEPj4hR9nOjXNXQN32Y9JEov/6q+JXH8dy37wfYLbS1tqp1PTvPfh93IqeYp/eO0/lGSmOui4/aOkr7qWdx98iKYnHkQbG+PkO99F7Kd30/bBD7D0n76F3FJa4OBTJHasehWCx0P0xz+p4xOqDUEQ6Ah0VFYylRl7YlP2PV1OyWQHf+cymQD+6Zl/ptXXyp1X34kvY5HavZvgxdsB+ORFn6Td386nnvqUO0/lI3LddQiKwtBP7JiZSxZf0vB73JVdl21Z2lz3+Gip9vudjy/w00M/rfrc31e8QjK9TPHB8z4IUBCwmj54kPTAAM1veQuC174xzUz1zePZhDY6grIkSzLVUDLtODLtThyNZAmdDTgnDf46SJxjO6d4+v8dYbEhVQz+djbo5eSXTsNcI5Y5VTcxrfpenwO/R8Ky6lsUPjnyJJqplbTKOcikdPtkndr5Kc573j8+iyDAZavbKz73bNjl0gMDeJYtQwrX3sxoqoGFRAYLUwmUKJkEQaBrywr86gzHdtqTY36rRSWk9NRZs8pBTsnklUUCSqCA3Jg4No/slQoqjiGv8TCP+Fx3aTemYXHg2TFUQ61byeSRPGTMOtrl6gz+fkyf5a/aWoma507JZJgWKc2ov13OVTKJBddjUAnyhbkEo2h8+fkvu48/NvQY2wyR1syZZdxVUjK1+lrZ2LYJJTzIvTtHz+hvnCuMRtO0qzYxobWUfu5v7Xsr1yy/hjt23uEG2sqK2FAmk2VZdrNca3/B47pZXZVRDFuJat8ry8LLSBvp0w5croRTSVs11BkstMsNjM1jWR4EqfQeS8TySSYB2RKwqljmHJVKbZIpax2rQ8mUMSz8dZRgOKRQukxJwNRwNvS7AZLJ2QyEuuxrZ248Nz7ftOYmvn7l1zkaPcrPpv4XHt+cOycBzKXneHL4Sb72wtf4Hw/+Dy7590v4l199DYDhzjRK4twQtU4W0MrwOhR0dCUEF/8xHHsCxnZW/dmIT0ZN28RQo5Y5RzVXTslUyS6nGSaSKFS0Jy3MqoRbsyST4iiZztIBoJ7m1X2/oXdDEZG55ELAgtEXMEyDjJkpKdRo7goQPZXi4MQ8f/Td57jln3+LZpi8YZNdeLE4sByf5Dvthrm9x5+lZxbCW89HasqSTNEqByRFmUwTsTT7Rue5al32uwi0Ipx/C++YHOFo7Chp5RDz7Oemn93EvUfu5dZNt/LjG35MZMF+vjO397X0oVs6x2LHAFi6vhVRFDixN3svq/Fcs5zsLUsyRSeTHH1hkp4LOlHFUkLx+k3dTMZVXhiaK3jcF7DvwbOhZNJNnZ8e+ik3rbmJ9533virPy7bL1WGXc96Hk8VX9vdNT5MZGWV6yfYSEq0aHIJIPVS9BXrXcJT5tF5olXNIpixR5ZNFUprhHvZu7tjMNcuv4a6Bu9y8Is+SHnq+8hV8a0sLFmZSM9z60K1MJCb4+6v/nm1d2yq+HkEQmPzAn/FCZx++b/wNx998E5kTJ1hy5zfpvP12BKl0/5Ccz9AaN5mW/ISvuYbYfffVjDWpFx3+KiRTGbtcdDKFKArumJOP/MPetNcez/xqhG+97lu0+9tJPv886DqB7TbJFPFE+Pyln+d47Dhff/HrJb9Pam4mdNVV+J94ni5Pu3v43wh2Ds3RElBY1hbIK0aoPj46OWed8iK++sJXT7tJ9HcZr5BML1N0h7q5uf9m7j1yLydiJwBbxSQoCpE/uAExSzJZ58guZ1kWmZFRPI6SKVBZyWSaFs8cneHS7MlpJRvaS4VkRkfKk4RXg1Mx3WzinoQXoxrJ1BPuwSf5GiKZHKVPQ8HfWQKnnoa5R048Qmegk/M6ziv772pKx58NmdbrDP4+eGqOTT1NJcG2+fA6SqYzIpn2153HpGUVWWlBwJJLlUwAoUu20z65k5GDs6gpvSBwsOJrMNJnmWTKKZmCcpCEnrtvTh2L0bUsjFi0WAxns4fyr8m2nhCdyyMc2DEGFnVnB3lED6qhVlXBNWKXS2ezVAyrMYvomcD5HIJ12uUyRqbELgeAlub8+WneHVnP/z30f92WoCPRI1wthNwT99NFLpOp9LO8qvcKRN8wP9836MrLX84Yj6aQzeyJrlB67QiCwGcv/izdwW4+8atPEFNjDSuZJhITxDNx+lsKSSatgVNwsK8Lh5BdGrFtd2c7/NtZQBbb5faNxvCIfjSzdFOYiGYINtljpig7weiVPx9H5Xg0erS6EsshmVJzVQOCATK6gbeOz9Gxy2llgqWnh+IoPommjvobN53fF+m03//cROH4fPnSy/nONd9BNRN4l97Jvw78K59+6tPccM8NvObHr+HDj3+Yfxn4F9J6mrf0vYWb2q7CEkXGW8GXPDcB+nun9rIssgyvGMKDZm/+L3iPbWt66mtVfzbsk0kmbQVgpc3HzFj5trioGkUzNbfVCXCVq5XscpphVlT+mabFQlQl1Gp/F2c7kwldLVD+uFhyASDAyPNu1ldALlRFelu8jA7Pc/3Xf83u4SifuWE9D3/sclc1rRkC69vWn3b49+hvnrRfyvarkJpsG5RZLdPLVTLZa4AnBu3v7uq1eff9xX/Ctck0zYLCAf0fibfciV/284PrfsBHt30Uj+RhZnQBSRFdu5Azxjm5TF6/TPea5lwuU37wt+wv2y6365EhBEmg+8JOBGkB1Sx8H1ev68IjiyUtc96gTTKfjYY5Z4xyDlgrwQ3+PktKptSePUy3bWLH0U5O7Kk/A0dubUXqaEetoWR6YnASSRS4bE3uIFUdPITU3o7c2grk1rj5a8jbt92Obup8c+c3q/7+2fQstz58K2OJMe68+k4uWHRBzdeeMgW+cOG7EdeuQ+7qYvn//Qnhq6+u+Pxdjw6xeHccOWXS8vabMRcWmH/ggZp/px50BDoatMslCbf7Sta2kM1G1UySWpLbn/oIqpTk2s4bWBaxMwMTzzyL4PEQOP9892cuXnwx/23tf+MHB35QUjoCEHnTG/HHM9w0vbzuvK587BqOsmVpM4Ig5JHw1cdH535a7ltFxsjwpee+1PDf/V3HKyTTyxjv3fRePJKHO3fdiamqxH7+c8Kvex1yS4urZLIy54ZkMubmsFIplJ4lAFWDvwdPxZlJZHjtentSPhPS4XSQUA0CilTXQJPMVmeGdaGiksmpf/eKpYsoURBZ2bySI3P1k0xOMLm/DjufAydfqtJrdJDQEjw1+hSvW/a6inavTFJ3m8zqtcuNROcrtso5OFMlkz43hzY2Vncek5qVvCcFAZRA2UVZYPvFdE7twjTh5N7pbOBgjdOJrF3ubMHNZMra5VLZ16lnDKaHF+gqsspBTskUTxeePK6/tJvoeJrOhd6GMpnAPoGshEba5VxVlHXumi0dksmxEdZC2kjbJJNHKrRvxu3F94d7r2NV0yo+u+Oz3HP4HgCuVtrtrJszQCW7HNiZUABxcS9PHX55B0WqusFkPIVoZhdbFQiPsCfMly//MtOpaf58x58jexrLZHJCv4uVTGdil3MWqk7F+NmCc4pbHPw9MDZPqz9U0hqpawbphEaw2R5DRad9r8rn41ihknqS8UT5OnL7CXmlIDUsc5phoci1P0fHJldO9Tg1HKd9SQihDlVC8e8LhvwEmzzMjpeS0ps7NnNZ8LOAwt8+/7c8NfoUK5tWcvu227nr2rt4+h1P88Mbfsj/vOh/0qtHkNvbiQckvJp2Viu6K8EJ/VZ1E4+gI8geO8z6wvfC/vtgprJNPuJXSGRJpnIKgMmT8/zoL3/D8d2lY4FDSjWiZMoYZsUNfTKmYpmWa5fznOVMJrSUTYwUw9cEHWth+DcuyeQomdKawZ1PHOH7A6NImsUtF/Tyy09cyXsvW4FHFgs2ehvbN3Jg5oAbFt8I1N17sASIbN6GGLFJpqp2OSc/K7v2eezAJD3Nfvq68g51mnrwbbqZm6NRVCsGscv5yQ0/YVNHLgNzZnSB1u6gq9DrjfTiET0MzubIjuWb2pgdSzA/nQJ1ocguV3h9J2IqB54ZZ+3F3eCX8C3+MT868TcFzwl5ZS7v6+CBvYWWOW/AscuduZJpPmN/drUOuRw1qlxPJpNDMhmVx8bU7j3MtdrqoImjjR0G+fr6XetbJTw5OMX5vS1usxjYdjlfX671tNwad0l4Ce9Y+w7uPXJvwXebj7n0HLc+fCsj8RG+edU3uXDRhXW97mTGIKX4aPrOXay47168K1ZUff7smD3Grpq38G/bhnfN6rMWAN4Z6GQ6WX7dUs4uF51MucH+xfDIIqqh8We//DMOzB4g0hrAl85dT4lnn8W/dSuir3Ad/rHzP8ayyDI+s+MzJePgyIZ2ogG4cGfjBxCxlMbhyQW29tr2Q0dBXW18tCzLJZkETeK2Lbfx6NCjPDb0WMN//3cZr5BML2O0+9t517p38eCJBxm8518xYzGa//AtAAierKrmHNnltBE7dLEeu9yOI/ZAdGV/J5IonPN2uVTGqNuKlsgGQgcyVkUlk+Ohr9RWsLp5dYNKJvvvNGKXc1RPtQi7X438ioyZqWiVMwwTXTMJNNWnZHKCvy10XlMljwly7yd9mg0m6QEn9LvOZrmYTQjEBRk8wRK7HIDS1Ul7u4DXSnFs11TdmUzFkv4zQS6TSSSoBN2TwMmhOKZpleQxQWkmk4M1F3QhKgJrJ7c3ZJcDqobPqlr97XKZLFllkLZbrs4BEq6SqT6SyVEy+YqVTFmSydu0lC+++ovMpmf57r7vsr5tPd3+jjNWMuXscqX3dl9LH12BRfibDnHvrpe3Ze5UTAVBQzYVdMFA1Sur4Da0b+BPz/9Tnhx+knkjWnNMycfg3CACAn0tfQWPazVCjIsR8soksuPqosAiFFE56w1zjl0uP5Mpo5scOhWnMxgpaWVLxrJzS1Mukwlwq5rLIT+vrerBRT7JtFBdop/Rzbo+R1fJVGSXM02L6ZGFhqxy+b/PK3lp6Q4W2OXy4TG7CU99igff8iBPvu1JvnHVN3jvpvdyftf5BeOwMT2D3N6G6rc3Akb0pbXMnUqcYjI1ycb2jaQ1Aw8agqPWedVtNgmxo9S64SDskzGNAIqolFUAjB+xx5rh/aVtYKdDMmlG5e85Pps9TMtaV9xN1NlqG6ukZAJYeiGM/JZUdn72Sj7u2zXKVV9+kr99aJDFS+z39f5tvbQEc2ssZwxNawabOjaRMTOuCqheGKZB+PA48z3NSKFQzi4XqyeTyUdaM9hxZJqr13WWHlpe+hFum53lS5lLUSffUKJ+nhldoG1JbuMsizKrW1YXvIflm2zVzMndp2xyy1Uy+Ura5fY8PoxlWGx9XS9pTUMKnGRBL7TFAVy/aRET8+mC7D9fVsmUPgskk3P9OddjJTjWN0WsQ8mUJaIyVeaZ1O7dRLs2Arksy3rh7etDPXIESy+/xp+cTzMwNs8Va3Nju6XrqEeOuFY5wG0dK17jvu+89xHxRvi75/+uRJkYTUe59eFbGZof4o6r7+Ci7ovqft3O3ing99R1aD43Yd9ja1MipmHR/LabSe/dSyqbdXom6PB3ENfiBXEPDortcpZlEZtKucH+xfDIAmPy93lq9Ck+s/0zdHW0ksge+Ouzs6gHD7p5TPnwy36+cNkXmEhO8De/LSRYn5n8LU9tEAj95iD6XOl9UQ17RnJ5TGArqGVRqGon1tKGa33PpHT++4b/Tl9LH1989osVc/N+H/EKyfQyxy0bbyHsCTP8w39G6elxPaqCKGJI8rkjmUbtTVFOyVTZLvf00RlWtAdZ3OwnoEjn3i6nGXVvRpNZu5w3bVYkmZzNeSWSaU3zGqZSU0jz950AACAASURBVETrVEEkXbtc/e1yQU99SqZHTj5Ch7+DLZ1byv67E/rdqJLJI5ts7W2u+lz/Gdrl3Ga59fUpmTLZhWIcBdEbLGuXAwht30775C5O7pvBKwi1M5mMlyqTScIv+13Fw0Q28HPRikjJzzgkU/E16fHLtG2QWT1zPj6rvpBq5zuslsuk6mbdmUxqlmRKiwKco8nUIdvqDf5OG2m8stetHHYxP2b/N7KY9W3ref/m9wNwde/V9ql76uwomcoRdoIgcMXSy5GCh3h4YLTmvfyfibFYCkHMIJsKhmDWJGbfte5dnNdxHsOpIVS1/o3M4OwgvZFeAkrhteycgtdjtQCbfHTscpIosTS89KwrmSaTk7R4WwrmgUOn4miGxeKmJlJ6CsPMXWvOotkJNpUcZUaV7z2eiSOL9jV+OFolQ6QBkkmronDJh5OhVEwyRU8l0TNmQ81ykJs3fZLPJpkmkmWtYapu4pN99IR6qm6k9Olp5PZ2jICd1aPNlZIzZxNOBtCm9k2kNRMPeo5kCnfB1nfC7h9CfKLsz0d8CiDQ6muvqGQCGD1UuiFyMpzySaaAHEAURFdJUgxNtyp+zwuz2XysrF2unk1UQ9DTrr2sBEsugnSU1IxNrjy0d5aP/mgXLUEPP3zfdj79dtvWHztVOH87bbVpzXRbcvdNNZbLdHjuMCtHDYT1NomdI5nqyWTy8syxGVKawVVrO0uf19GPtPYNvPbU/ei6jpGnHErOZ0jFNdp7CtU++Q1zYOdRNXcFOLE3e3247XKFSiY1pbPvl6Os2tZJc2eAoYUTCGKm5F6FrGVOKmyZ8/hkEM6OXc7ZQIeV6uOBE/wtNaRkKj/PWIbB/IFjLMhtyF6JyZPxms3I+fD292FlMmROlp8Tnjxkf/5X9OW+58zQEJaquplOAH5P+QblJm8THzzvgzwz/gw7xna4j8fUGO975H2ciJ3gG1d9g+3dpcRJNSQbiNbQMwbzM2msNg8BS+Dwi5M0vemNCD7fWQkAd8ai6VSpmknTC+1yyfkMumrQ1FF+jToh/oyE5xlu23wbf9j3hwSbvSxk58vkc88BENxe/rPa3LGZ9258L/ccuYcnh590H39m/BmOXbIMdJ35X/yiofe2a8he921emtvjeGWxKgnv5JspPolMSkcRFT538eeYSk2VzY36fcUrJNPLHBFPhNs63sLSwTmS11yMkHcqYMgKgnZuSKaMo2TqySmZLFUtORnQDJPnjs1wSTaPqcSucg6QVPW6rWiJqP35SUmDZMYoWCg4qNYuB7C6xQ6Zq1fNdDqZTI5KqNpnmdbT/Hrk11zde3VFq5yzyAiEHSVT9e9GEiSwBJa0empuVJxF4el+3+mBAZSlS5EipaRLOahxe7ETE7IkU6Y8yRS4eDvtE8+jZ0xCUb3udrmzhVwmk61kckimU8fmaerw4w+XkpdBb3klE0DkPBOP4SN9qL4QbCdXq1rDXEN2Oct+P0lBOGNSpl40apfLz2TSDCuXgZRVMhG2N6q3brqVT7/q09zcfzP4m20lUx0NjhX/bhWSCWzLnIFKxnOYh/eX35y+HDAWTYGYQTY9GKJVczMqCAKfuOATJEkwNV+/FXBwbrBExQQ5+X09VguAkE8uIGR7w70vSSZTqVXO3qz2NtvqmnRenlEiq2QKFpNM6epKplZfK4uCi6rPJ8lZELL3a43w74xhlrVvFsOZ57QiMno6G/rdSLMc5Egrn+yjtTuIphoszJVuilXd4B36PfDoX1T9ffrMDHJbO2JoOQBjowcbej2NYs/0HmRRpr+1n7RuFJJMAJd8GEwdnv37sj/v5Oo1eyqRTHEQ7KwqJxvSQTnVnCAIBJUgC1plu1yl7zmeJZnCLbl5zVtHCUbd0NO2xascltrKjdT4iwAMz+hs7Inw8w9dxsWr2oi0+xFEgehkoXLH5+bfGCwOLqbV19pwLtPgnicIpaH9ArttSgwGQRQx6spk8vL4gUn8isT2lRWaGVe8Bp8+T4REwRg5M2p/R61FhR79Lf3MpmcLNurLNrUxcjhOxvSBN0tKKYWZTPt+OUImbbDtGtsKfCx2AIBMmRy4iE/h1WvaeWDfhEvqCqKANyCfFbuckwlWU8lkOkqmOkimbIh1petRPXqUWY+999j46sUYmsn0SP22KJ8b/l1eCffk4CRdES/runPvyXmurz83P/nkypEQN/ffTG+4l797/u/QTd0mmB5+H8eix/jGVd84rcazREbHI4l1HRJEJ5NggbI2QlQ02ffrUaRIhMj11xO7/36MhTPLsXOaLsvly7mqtez4E5us3Cz36MlHGbbuw5vazm2bbwMg2OIlOZ/BMEwSzzyLGArh27ix4mu5bfNt9Lf087mnP8dceo60nmbnqZ2suOBKvGvXErv3vobe287hKKs7QwVWSZ8ika6y7nGaGiNtPjKqrWra1LGJd657Jz8Z/IlbhvL7jldIpv8CuHovmAJ8e+mRgpNAU/EgnEMlk9TcjBSyJ00xYP+32DK3ZyRKImNwabaFLOiV3Qyic4VkxmDd1FFmf/BvVZ9nWRaJeRVRFhBUE8XCtVzkox67HNRPMiUbaL9z4BBS1YK/Z9OzpI10xUpUyFMyNdWnZDo6lcCyZHpaaxMarpT4NE9H0/vrD/0GyMTta29W8CF7A6CVV9YFL7qIlvmjKKKBfypTc2Gd1usL/rYsi52PDDF8oPqJukNq+RSJgBwgoSWwLIuJYzG6VpYn1BRJxKeIZdV11qIUUd8ppl6sb8HoKpmqkkz12+VUy35NKVGsaC/LpHUO//YUj3//gLvgPhM0apdL67lMJshbFMYn7OwQn32irYgKb1/7dpq8TfZjllHWdlkvqmUyAVy06CJ8ko/mtiPcu3PstP/OS43xWBpBVG0lk2jVZavZ0rmF9nArsWS8rma3hcwCw/Fh1raWtvBkGrXLeWRU3XTJxN5IL8Px4erh2Q2iPMk0T8grszirkMi3EjhKJif4W86egqfLEMcOFrQFQkrItmDXssu1rcr+0Nmxyzkkk0Eh4TE1FEeSRVq661NOOiiwyy2yf9axc+RjcWI/70n/Czz9DZs8KwPLsmySqb0df7O96RsZOdDQ62kU+6b3sbZlLV7Ji6qZeNCQlDySqXUlbHgz/PZ7Zcl2hxAPy60ldjk1qRE9lWTlZnvjVqxmmkpO0eprRZEK592wEq6RyVSpWS6NNyDj8efGT08d1vG6YFnVlUxta8DXRPKUrUIyDA/Nfo+bVSTJIpE2H9EiJVOu4clEEAQ2tm9suGFu5vlnAVj8qisA2wEghcOYddjlLMnL4wcnuWxNu7u2KYHPVj40CwsFh2vOnFdOyQRwaLbQMmcaMJI5L88ul2uX0zWD3Y+PsHR9q6smPLFgE6yZChb46zZ1MxpNsXskNz/7Agrps6hkqpXJZDRQ3uBct5UKMdJ79jDX3IfiEdh0he2mcJTg9cCzahVIEuky4d+aYfLrQ9Nc0VdoiUwPDoIk2T+bha9KbIUiKXzs/I9xJHqE7+//Ph945AMciR7ha1d+jUt7Lq37teYjlTEI1Fl2Mjdu3z++dh97PAanDseYm0jQ8vabsZJJ5u+//7RegwOnhKBcU6Zjl3OaBB3CuKlMJtNTo0/hIYw0+1b38w41e8GyLeaJZ58lcOGFCHLltZ4iKXzhsi8wn5nn889+nhdPvUjGzHBx98U03fgm0nv3oh6tnJeXD8uy3NDvfNRSMqlZJVO4zQ8WZLKOnQ9v/TCLgov43NOfq7rm/n3BKyTTyxyWYZC4734Wtqziycw+nhl7JvdvigdRzzRU53m60EZGUZYscf9fDNqDRzHJtOPIDIIAF2dPfvyKRPIc20KSmsFlex5j8u9K/dH5UJM6pm7Rtcze6LcY5cO/a9nlugJdhJTQS6pkcqx15Uiw4tdZSXEFOZLJUc/UIpl+eWgKLJmuSO3NvVcWEQRIn4aSyYjF0IaH6w79Bsgk7UXYDD5kX6hs8DfYEvnAun4608fxTKTRatWSGvUpmfY8PsLTdx/hsbv2V1WEOQSHRxIJKAF0U2duaoHkfIZFK0rzmByEfUpZJVNCX+Bg53PMnVDLbtqKUYtkMkwLzbDqVjJpWSVTShAKgrI11eDIC5M8+K29fO8TT/Hwdwc48PQ493515xkTTQsN2uXyM5kgb1E4PwaRbihny8kST2cS/l0tkwlsVcf27u14Igd56sgU0wvnprihUYxGU0QCFrLhwRTrrzrf0LUOyVC4c9edNZ/r2MGKm+Ugr62mAbsc5MjIZZFlqIZaeOpqmvDCXadNIp5KniohmcZjaZa0+Alm7X754d+JmH2A4TQ7iXUomRYyC4Q8IdY0r+FY7FjlsP7kDEQW29ds4sztcpZluZlShlU4TkwNL9DWE0Sq87tw4AR/+2QfLYvsQylnI+TC0Lhl+iukhICtCjrw87K/y4zFQNOQ29sIt9qtVqfG6s9BbBSGaTAwM8DGdvs0XVVVJMFCVIrWAJfebluGn/9uye+IZE/FA1JrCek6OWRv1NdfthiPX2b0YCHJNJmcLFAxOQh5QpXb5fTK33N8TnVDvx3YTatn4QAwT/lTFqIISy4kNW1v8DVNLjnQaO4K2EqMPPiKQpY3tW/iWOxYRZKtLAYOkfFK+FbnKs3F5qbqwd9ZcufQbIbRaIqry1nlHPjtjWkTiQLiYWZ0AX/EU6JSdkmmvFym7tVNeLxwUr2gsF0u+zoOPjNBaj7jqpgAhhP2Z6ka5dc8r1vXhSIJBS1z3oDsbozPBM7nH/FUV5zrZv1q1Frtcqndu5lrW0f3mhYi7X5CLV5ONUAyiV4vnhXLUQ+VWpBfPDlHXNW5cm3h/aYeOoxn+XK3yRuqK5nAtt1v7dzKV174CoNzg3ztyq/x6iWvrvt1FiOZMdxm6VqYm0ggCBBo9bHXoyOIAgO/HsO3aRPe9euY+9GPq+6HysEyTaL/7x60iQnaA7ZwoKySSS+cr2OTKURJINxaOiYMxYcISd1oRu66cNS+scMjaENDZfOYitHf2s+fbPkTHjn5CH/7/N+iiArnd51P0w03gCQRu/feut7j0GyS2USmJA7Eq1Rvok7nKZkg7wBfCfDn2/+co7GjfHdf6bzw+4ZXSKaXORJPP40+MUHfH91GT6iHr+/8ujtQWB4PHkM/beVII9BGRlyrHFRWMu04Ms367ogb4BjwSFXVNy8FkqpO1/QIVipVMTcKcifN3avtzWWzWZ1kqkTeCILQUPi367NW6s9kCtRhl6uHZFKzA2GwzuDvA+PzCMh4lNonnoIg4JOl08pkajSPCSCTyAAmUcXimKCBkQGj/KYsePF2Og49jKBZvD6hoFW5Z1J67Uym4YOz7Lj7CO1LQyRiGQ7sqNwEldYNPLKIKApuWPfQYXvTUS7020HYK5e0y4FtqRns+A2KV+Shbw+432klOHY5tUx+A+QRI/VmMmXVIUlRQI1HOfriJA99ex/f+8Sveejb+xg/GmP9pYt5859t452f244kCdz3tZ3MjJ0+0RR37HLe2oo63dTRLd21ywGkM3l2ufDi8j+YPZU+k/BvZ9NWzZ70mqWvYcGYxJInuX93bTWTZmg8Pfp01XbAs42xaIq2sIVkKpiiULfioSkQJiiEuOfIPTVDeis1y0EeyVSnui5UlGHWG+kFihrmhp+Dn38UDj9S1+8seD2mxmx6lq5AV8Hjqm66rZFAQfh3IqoSbPK6p7VydgzPVMkodJVMLavRTI3h+HD5JyZnINAGoa7adjm9tl0uP6/NQMutMSyL6eE47Q3mMUGhkskfVvAFFWaLSfGn72CZfpx/avsEtK6CfXeX/V36tG0vktrbUZrtzc7c5Nm1Q+bjeOw4CS3htoVlMvb3KhVbwrrPg9WvhWf/oeSQw1EyeYUWFrSFApXb5Amb5OhaEWHxmmZGDhUS2+VUc2BblKoGf1exyxVv+LxKbSWTbuo8NfpU9VY3N8OoSlnGkotIzdu5npoulyiDmjsDRE8VZnYVK6M3tW/CwmJgpr4Q43gmTueJGIk13QhS7u9JkaYamUz2PPnoIfs5V1YlmWybbLOQKAiDnhlN0N5TWszR7GumM9BZkMskSSK9y0xOqOdjOeog2QtaGtMw2fnwSTqXR+jps+enjJFhPHUcyxLRLb3sd9MUULh0dTu/2Dvufqa+oOJujM8EjpKpVvFIQ8HfNdoO5/YcIenroKff/ry7VjQxcawKUVgGvr4+1DJKpicGp5BFwXVfOFAHB/H2rSl4rEQZXQRBEPjkRZ9kWWQZX73iq26j7OkimdHrdjzMTSQJt/sJ+BWSInSsbebgs+MYmknL225GPXiQ9O7ddf9ty7KY+Mu/ZPxTn+LUF79EWAnjk3xlVcrOd+3Js8tF2v2IZUjv4fgwYakLNe8zDLXYY9PsC/Y+IFAhj6kYt2y4hS0dWzgSPcKWzi0ElAByezuhV7+a2H0/wypqK4xNJe0mxzzsGi4M/XZglwRV2W+5SqZCkgnsWIRrl1/Lt/d8m2OxY3W9l99VvEIyvcwR/endSC0tNL32ddy2+Tb2z+zPVSQqHjym/pJnHlmmiTY25jbLgZ3JBBSQOKmMwc6haMFgHfhPsMtlkmma5uyFtz5Z2bbhtP90r85Knk2xrHLEmcSdzXo5rG6xSaZ6Tgqcdjmfp/7bzw3+roNkqqS4gvzg72ytfQ2SaSGtI6LUXR3s95w7kklNaXiEFHQ/ykemfmU/WMEyF9i+ndbpAQKdC2zQZJ65t/LArxpqVZJpfjrFQ9/eR8uiAG/+s210r27ixYdOVlSFqZqJLzv5BmT7vhk/HkP2SrSVWYg6KM6ZcbCgLZDyxLnmAxuZG0/wwD/uqapIc0hHx/ZZ8vqyE2lddjlDR0Vg+exGtLHbuOufAjz4T/sYPTTH2u3d3Pixrfz3v7qU17y9j8VrmmnuCnDjn25DEAXu++pOt2K3UeTscrUXXI5iK98uV6JkKgdXyXT6JFOmhl0O4DU99sKzu/sY9+6qTDJZlsXjQ4/z5p+9mQ88+gF+cbyxMMszwXg0TVPQQjYVLFGou4VK9kjIpkJQCfKV579S9bmDs4M0eZtKiBvILVrlOvI8IKdwc0mmsE0yFeQyTezJ/vLyp//V4FQ3F2/8nVZG574usMvFVDf0G0DJjveZKsrefLscVLFgF5BMtZRMlQOhHTj5SQAImmtXjM+kUZN6w81yYI+jsiAjizKCINDSHShsmJs5Cr/8a55SLmEgfBlsvAlO/Lrs+9Gn7aBzua2dQMhHSpFIz04WBK2fTTjZP46SSVPtuVUulzt02ccgMQW7Cu35kWwmkwd7XMnP4Zk8GSfS4ccXVFjS38L8VMrNTYIqJJMSrthcVO17XphNE2otVjJVz2SaTk3zvoffx22P3sZvxn9T8Xk1lUwASy8klX1pmbJKJj96xnQzMp3XB7kGPOe7qDeXad/ICyybtPCdd17B41IkUj2TyVBBVHh8cJpNPU10RaocOPlySiaHeDANk9nxBK095e1k/S39JQT88t4kSbOVqZksUaf4wVA5+sIk89Npzr9mmUtWH5o7hImOmbKdBQX3bh6u39TNyFyKfaM2GeMNKmctkykgB9yCgkrQXbtcPZlM2ebNMnY5YyHBqah9Ly3JkkyLVkaIz6ZLssyqwdvXjzY6WpJNtGt4jo09TW6GmvM3tZERfP2FByD5YfSVsKFtA/e/+X6uWHpF3a+tEpKZ+kuM5iYStC4KuOHknVvbURM6R1+cJHLDDYiBAHN1BoBblsWpz3+e6I9+jLKsl/hjj6FPTNAR6CjblKmXscuVa5ZTDZVTiVM0K90FIe+hZvseix4cQmpvx7tmTcnPloMkSnzhsi8QUkJctfQq9/GmG29En5wk8cyzBc9/4vsH+eUPC++9nUNR/IpEf1fhHFeridrJt4202e8zU3TY+8mLPolf9vMXT//FWbXt/1fDKyTTyxj67Czxxx+n6Y1vRPR4uGHlDaxsWskdO++wF1deD4qhveRKIX1qGiuTKVQyBbNKpkRuUf3bE7NkDNMN/QYI/CfY5VpnxhCzZI8+VZlkSszbE1RzVwA5IGeVTKWfZT3kzerm1cTUWNnmhWKcTrtcLvi78meZv7mu+JxUNoQ6pIBQ2y6XyOhIglK3t9hu82p8QE0PDKAsXozc0lL3z2TSJoqYRvANE3OyCSqFf2/bhqAoLJ17lt0enb2PDrP3yZHyr6VKJlMmrfOLf9gDFlz3wU14fDIXvmEFC3MqB54uTxiouoE3eyrrKB6mjy/QtSxc9qTHQdiXa8zKx0JmgaASZNn6dq5691pGB6M89q8H3DrVYji5HmqF/Aa1hsWrAIaKP/oqrh18H0JiAyt653jT7Vu45a8u5fJ39NPT3+JmbTho7gpw48e2IggC935tZ10Wv2IsqDo+Rawr38FVUMjewsZDy7IzmcIVSKas9eFMwsxrBX8DdAW7WNu6lmDLIXYNR5kpY5nbP7Of9zz0Hj76xEeRBAmf5OPAzEubQZOPsWiKpoCJbHpAqn6ilw/ZI2JoFu/f9AF2jO1gx+iOis8dnB1kbcvaso1ijdrlQkV2uUXBRXhED0PzeSTTeJZkqrApqwY3iDlQZKnQTbx5SqYCu1w0Q6ApNxbL2fG+qpIpa5db0bQCAaF8LpOh25bOQBsEO86Kkil/bBAEzR3Dp7Kh36dDMhWPoy3dQWbH7Tw6LAvuvx0kL9/wvt8eeza+BSwT9peGtjpKJrmjnYBHZt7rw7+gc2L+RMOvqx7sm95HWAmzPLIcAE2zrxnJU2ZeWHYpLLkQdnyjQEnrKJkE0x5X8m0mkyfm6Vpmf6Y9/fa/O7lMjmquHMkU8oSqBn+Xy2TKpHXUpE64iGSqlsm0e2o3N99/My9O2mHdMbWa8idL2lZT//ZcQCpbRpLRFHc+dNDUZd8/+Za5Yrtck7eJZZFl7J2qj2Q68dvHkU3oedWVBY9LTZGamUyW7OXFobnyrXL5yCqZIkKOZIpNpTA0sySPyUFfSx/HYscKDn16F80CJieOZMcG2YdlwYsPn6C5K8CKzbmDWyeXSk+uBCqTTK9f34UsCvxH1jJn2+XOjpKpVh4T5IK/6zkoqGaXS+/by1zTGhQlVz7gKMAbyWXyuuHfhZa5lGa61lYH6uFD2Z8pJJnc9cQ5cmckM0ZdJUamaRE9laJlUdC19HkW+2nq9DPw6zGkUJDIG/+A+V/8orqKjyzB9IUvMvfvP6T1ve+h97vfA9Nk7kc/psPfUVbJlMmzy1mWRWwqSXOZZrnR+CgWFi2exWiG5RYteYMykiwyPzZH8FWvqtoyWozeSC+PvfUx3rnune5joauuRIxESixz6YTmOlgc7Bya47wlTSVrS68s1cxkEiXBzbctdhS0+9v5+AUf58XJF7n7cHmF7u8DXiGZXsaI/exnoGk0/+FbAJu1/dDWD3Esdoz7j92P4PHiMfWK0s2zBW3Uljl78jOZAqWZTDuOTqNIAhetaHUf+8+wy3VM58gDfbLyKW9+MGug1UuzKbBQpn67Hhvammabea9aO52Fo6qotwEP7ElYFoWqn2U9ZJgj8fT4ZWRZrK1kUnUkQa5IUBTDq4inpWRKDQw0FPoNkFEtBFHFkqdJWdnvTStPMol+P/6tW2kZ3M0jfo3udS386seHOLazcMI0LRPVUPFLpacwlmXx+L8eYHYswetv3UBzNtRwydoWFq1s4oUHy6uZ0prpnoAF5ACSoRAf1+iqYpUDe+NcTlmX1JOuVL1/ezfbb1zJ4d+e4ul7ygcdOtdtJaLQmUjrUjLpKnJ6CbqgMdb/cS7asI8la1urkmUALYuCvOljWwG49yuNE03xtE6oDqscFJKtvvxFYWrOPqWOVLLLnbmSSdVNBKH2wvo1S17DKfUgiEnm877jU4lTfPqpT/P2+9/O0ehR/vxVf87db7ybvtbC6uuXEvNpjbiqE/AZyKYCUvVsgnw4lrC3rXobS0JL+PLzXy6rNtFNncPRw/S1ljbLgU0ySaKAVKeSqbiNURREloaXFtrlHCWT3ngOlkMQlLPLeWURf9YqVKxkCjbnxmKP11Ey1bbL+WU/S8NLy88nqWx+j6tkqnyQYlmW3TpWQ01QsFEVNdfKMD28gCAKVRWXFX+nkS6YM1sXBVETOqm4Zqt+jv8KXvc5xs0me+zpXAed68ta5oyZLMnU1kbAIzGvhAilbDL2pcDe6b1saN/gtrTqqv35yEqZNYAg2Gqm6EnYn9vU+BQJjyRi6fbG2AnMTc5nWJhT6VxuZ9q0LQ7hCyqMDtrf60xqBgurhNAECCmhKkqm8plMC7P29R4qtsuVyWSyLIufDP6EWx68BUVUuOOqOwBI6FXGa+d+qtQuB+CLkArb946akUqVTNm5ND/821WN5I09jYR/J3btBKDt/ELrjdhUyy6XRsWDZcHV62qRTFkVPAvuumdm1P6s2iopmVr70U29wEbjF2IsUgY5eTBH2A1ntjA9kmTbNb0IeePgvul9+MQIZsa+PiqRTM0BDxevauOBfbZlzhdUUJNaxcOoerGQWaiZxwS2kkkShboIA5dkKqNkSu3eQ7Slj57VTe4BVsfSMKIsNGSZc1ri1EOF86ijRi14bNAhmQrnpzMtt2kUyYxel5IpPpPC0E2aFwXccHJVN9nw6h7Gj8aYGV2g5eabsVSV2H0/q/h7LMvi1Je+xNwPfkDrLbfQ+fGP41nSQ+jKK4n+5Cd0K23lg7/z7HLJWAY9Y5ZVMjnK4jafvQZzyClBEAiGRNKGp648pmIElEDBdSZ6PESuv474o48WKNc01SC9kFsHpzWD/ePzbCnKYwLHTlwlazWp4w0qbplCpsxa/cbVN3Lhogv56vNfrasM5XcRr5BML1NYlkXs7rvxbT6vQDr42t7Xsr5tPX+/6+/Bo+Ax9JecxNFGbdKmvJIpt/h4+sgMW5e2FCh0At7Ts0+dLgzTrXyTPwAAIABJREFUoic6juUsDqsomZKxDB6fhOKVCLf7aTEEFspsAGq1y4FtlwOqNwJlkcrYk1q9GygHtQg7hwiqZvXKpAwUn4QoCkgesbaSSc0qmcwGlEwNXo9GPI52cqhxkikjoEr2e9YtEw0qkkxg5zIFho4R1JJsedtqupZHePh7A4wfySlX8mu3i/HCAyc5+uIUF795Nb3rc2o9QRC48IblLMypHHy2NJsprRnu6VJQCdKRWIplVs9jAgh5lfJ2uYy9EXWw7ZplbLq8h12PDLH7sdIMl1rB365drp5MJj2Nkuki5ptiXhYwk/Wrflq7g9x4+1Ysy+Ler+4saROqhoSqE6qzZcX5Dkva5eazSrPwovI/6GYynYGSyTCzAfjV7+3Ll1yOiYkcOkQyo5PUkty5605uuOcGHjj+ALdsvIX/uOk/uHntzciiTF9LH4fmDjUc3Hk6GI/an1/Aa5NMoizVfZDhNKgJhsTt59/OkegR7j1SGsA5ND+Eaqhlm+XAsf7UPz7mlEy519kb6c0pmfQMTGaVYHUS5vlwSKYSu5xeZJfLKpkyaR0tbbjNcpAj4PQKJJNu6qT0lHtvV8z5S9rWMQKtEOq0g6crhJk74bu1lEyO+g+KlExDcVoWBdzX3ghUXS1SMmUb5o6PwkOfht5LYNstqJqZG3s23ARDz0CsUGWqT0+DoiA2NRHwSMSUJprSQt35PI0grac5PHeYTe2bcn8/q2SSvRXm1r7roL0fnvqqrdLKIuKXMTSbZHKuISePqTNbOCKIAov7mhkdjBY8r5yNNOwJu+2kxchkCc9ixOfs1x4uCf4ubE9K62k+s+MzfP7Zz/Oq7lfx4xt+zPld5wOF5GkJ3Eym6jmG6cgiZMsio1slc02o2YusiAVKJkdZm/8aN7VvYjI1yalEdfWeZVn4BodYaLczWvIhRezg74pjqZ4hacp0hL1sXFx9jkZSMOQATXmZTDOjNjFbqY2xXPg3apzl3heYHE7YFjDFx4uJmwg2yfRdVDhfDcwM0OFZDaY9r+erJ4vxhk3dnJxJMjA2jzcgY1nlN8ONIJ6JF6w/KkE3rbrtzkoVu9zsrkOk/B0s2ZgbeyVFpLM33FD4t7x4MWIohHqo0C7lzNn5UA8dQgwGUXoKD6R8/xlKpjrGXqdQobU7WBBOvvbiRUiyyMCvRvGtW4dv83nM/bh8ALhlWUz+9d8w96/fp+Xdf0TnJ/8/BNOAf3kjrVf0Y8zNcd7ehbLB3/l2Oecebi7TLOfMxx3eQpIJwGsmSHtbCGy/uOb7rQfNN96IlU4Tf/BB9zEtY5JO6O77HxibRzMsti4tdVB45equDDWh4QvIeB2SKVV6TQiCwGcv/iyqofKl33zpTN/Sf0m8QjK9TJHevRv18BGa3/KWgscFQeAjWz/CWGKMOaJ4TK0qiWOaFk98/8AZhe46SqZCkqlQyRRNZtg3FuOS1W0FPxvwyGXDtF8qpDSDZfMTJBf3Ivh81e1ysZydoaUzQNgSWEiWbsRVQ0UWZfdUsxxafa20+lrrCv9OZoyGmuUcBDwyyTrsclWVTGkdr19m5jvfQTS0qq1oYG/aFNFTMc+nGH5FaviUJ73f3vw10iwHdrZDQs5tGFOiUNEuB+BZZreztKbnMUR4w5+cR6jFy3/8/R5ms1khzmarmGQ6sWea535+jL6LutjyuqUlv3vpula6VkR44YGTGEWqDyccGOwTl0Xx5QAsWlH9NDDsqxz8nR+6KQgCl93cx8otHTz108Mcfr5wAe6STBWIwobscloKb3oRMf8UC4KM1aC1rHVxkDfdvhXTyBJNk/URTQuq7oY710K+8rDALhfPEoCVgr+92e/jTJRMmlFXXfzG9o2E5Gbk0H4eGfo5N9xzA/+4+x+5fOnl/OzGn/Gn5/8pYU/OotTf0k9Mjbm2rZcSY1H7NN3j0ZFND6LcuJJJz5i8ftnr2dyxmW/u+mbJJtVRZZVrloP6GtHy4Vwb+XNNb7iX4fiwnYcwPQhOrtzpKJlSk3hED83ewhNPVTPxynl2uez7dFWy+ZlMioSBhVZhg5LI5sk5VpTVLasZmh8qJYddkimrZIKKuUwZvT7bodMEpwheEDV3DJ8airu16Y2iWMnkNsw98SP7MOAPvg6imFWDZceejTfZ/x0oJCb16Rnktjb7xNsrE/cEaU7LDEyffZLp4OxBdEt3M4AA9Ez28ylnlwO7Qe3Sj8KpfXDkUffhsE8hmVIKAnNPnZxHECj4XHv6WojPppmfTrmbuHLtcmFPGMMyCgLmHVRWMtlzWtlMpuymfnRhlHc/8G7uO3ofHzjvA9x51Z00eZtchV4li579hx2SqUomE5AKduA3LZYYo+5G2IEgCjR1BojlHTxIooAiCQXrCec7qaVmGlkYYdlIBn3dypJ/kyIRMIyCqIcC6GlUS2ZFe7DE+l0Opq+5QMk0PbJAc6cfuYJSfVlkGR7RU0QyLbA8bBchnNw3w8RMhNHMJrZcGkbKI0CSWpJjsWO0SqsQLHtezyeIi/H6DYuQRIEH9o3jDWRt82domYtr8YK5qRL0OrLgHFSyy1mWxdiQfa07tlIHXSubmDwZL1lvVYIgCHj7+kgPFpJMzhiej/ShQbx9fSWHRY66rt758EyRVOtrl3MKFZq7AgU5lP6Qh1XbOhh8bgJNNWi5+e1kjh4l9fzzBT9vWRaTX/4ys3fdRcu73kXX//pf9nsf3wXHf0mgZRbPypWseewIKT3lzlUO3HlGFolN2t9XOSXTcHyYsBKmyWuTt/lKIc/8KTLBNjx52b9nAt/mzXiWLyeaZ5nTVQNDN9GyBz1O6HdxsxzUVjKpSR1vQM4pmSoU8CyLLOODmz/IIycf4YmhJ077/fxXxSsk08sU0bvvRvD7iVx/fcm/XbL4Es7vOp9RfQTF0Kqy6gtzafbvGGd4/+xpv5bMyAhSRzuiL7dIKQ7+fvbYDJZFSUODP1sDaZyhRLdeJFWd5fMTpJcuR+7oqGqXS8ZUt2WtvTuAgEBitnQDohpqVaucg9XNqzkaLW9ZKvi7GaOhPCYHAa9UV/B39UwmHY9fZvbf/h1BTdZUMi2oOorYQCaTR2r4lCc9YG8UGlUyqbpCTMktsNKCWDH4G0Dw25Oez8ig6ib+kIc/+PAWRFnk53fsIhFVc0omKXetz00kePh7A3QsDXPlu8pnyAiCwIVvWEF8Ns3gsxOF7y9Pjh2Ug3TFVyA3myX1xsUIZ4O/i0+dFrSFkmYXURR43XvW072yiUfv2u/me0AusL6mkqkOu5yhpvGpHUT9kywI0mkRMm09IW782FYMzeS+r+4kNlWbaFpQdVetUgtlSaZMnpKpUvC3JIMnfGbB34ZZkjdSDqIgsrltO0rTHr47+Nd0B7v5/nXf58uXf5kl4SUlzy97+v0SYSxmLxJlOYNsepBqVPnmw1Ey6RkDQRD4+AUfZzo1zV0DdxU87+DsQWRRZmVT6SYQToNkyo6n8XySKdJLxszYqgcnjwlO2y7XEegoufftTKZSJVMiWypRQDJJAjr2aWo5OBt5RyWwpnkNhmVwPHa88IkFJFP2dL8CyVRvtpWzUQ3IYQTBtuAnYirJ+cxp5TFB6bwZavGiKBazQ1Pw6o9DR9a+oufZVdpWQfeWEsucPj3tKlL8HomYJ0goadmE0FluXXSCpfOVTKZmXzNCNSJl01sh0mOrmbKI+GQWVKMgMHfyRJyW7iBKnjLT2UCPDM5VVM1BjoCcz5TahCoFf8dn0wii4K51HHiySqanR5/m5vtvZiQ+wh1X3cGHtn4ISbRfmyjY13bxprIA9bTLASl/M37LZJt4uKxqtrnLT3SykDzzFako17auRRZl9kzvKf7xAgwc+DXt89C87aKSf5Oa7MMEs1L4t54mg1KffRywfM22kim77pkdW6holQOQRZlVzasYnM2zbalxWkMxQq1eTuyZ5sXdIbxCnPVbCi3i+2f2Y1omTdJKJOxrsRzh6KA16OHilW38Yu8EvqA9RqbPMPy73kwm3TTrCv2GXPB38TyjjY4xIy/GK5u0LS78m4tWNGHoJtPD9R+ge/v7UA8VKoKdMdyBZVmohw7j7S+1cnskEVE4l0qm+uxy0Ykk/ogHX1DJNepm1/YbXtNDJm1w+PlTRK67FjEcLggAtyyLqa98hdnvfo+Wd/w3uj79qdw8d+xJAITMAi3vfAehI+OsHrVK1EyuYlay1YiiLJSQ2mCTTEsjS3Mqxez3bek68sQxVKXprKm1BUGg6cYbST3/ApnhYSzLQs9+b+kF+x7YOTTH4iZf2XD/eoK/vUEF2SMiiEJFkgnglo23cOGiC+suUPpdwisk08sQZiLB/H/8gsi11yKFSgdzQRD4o/V/RFLI4LHSVZVMarayVKuSA1EL2sgonsWF7HJxJtOOIzMEPVJJDaTTBnWuLHOJ6DxdqTmM3hXInZ01lEyqq2Rq67I37am50g2IZmpVm+UcOPaGWk0CKa3+WtJ8BGoQOPUEf6tJW8lkxuNIll6VZLIsi4Sqo4ieuu1yXlki1WDwd3r/fuTubuTW1tpPzkPG8BKVkwhkrzGhupJJDNjfsU/PuJNHU4efP/jQZtSEzs+/uZv4gv3zzgmumtT4xT/sRVZErvvgpqq2kd4NrXQuC/PCgycw8mTfac1wlUw+2UfXwnLE7gob3UzCDb4NeWVMixKLZEJLlJWryx6J6//4PJra/fziH/YyM2ovvpzroWLwdwOZTPHpJCISUd8pkqKIWC0QtgraekK86WNb0DIG935lZ0mtbDEW0qdHMjkNjgVKplAFuxzYGRtnEPytamZdSiaAa3pvxEj28s6Vn+IH1/+ALZ1bKj53TYttmT4nJFM0ZVt5hYxtl1PEgrrhanBO7/UskbKlcwvXLL+GuwbuKliYDs4NsqpplRtKXwxNb8wu58wz+UqmZRFbuXgyfhIm9oISsC09pxH8XantyyFIvJIXURBLlUx5G3tZEtEE3IVuMZxqelfJVKlhrhzJlKiuZKppl8t+JiElYiuZNJOpoWzod2/tDWWl35mvCBUyCVqkIeaEfrjsdiC7oSu2eW28CcZehNlcZo0+M43cZqukgx6ZuCeAN62hZVJnvSJ679ReFgUXFWQiGZnsNVPhegVA9sDFH4KTO2DoOcBWMsXTmhuYa1kWkyfn3TwmB63dQfxhO5dpMjmJLMq0+EotHGHFJvycayUfGb1yJlOw2VOSm+eRBOY8D/DBRz9Ih7+DH97ww7KNWEElWMMu57TLVbfLpWQFHwLbhMNlVbPNnQHmp1IFc6e3iOD2Sl76W/prKplO/fbXACzZflXJv4lNtoqiYi6TrqLiqU/ZCwj+Fptk0g0yaZ356XRVkgnsXKZCJdM8gi/M8k3tDA3McvyYh02BX+CRCtddjj00LKxAEe3Pu1Imk4PtK1s5Pp1AyI6Rzp7gdFFvJpNmWMhinUqmCna55K5dzLX0s2iZvyCXCk4v/NvX14cZj6OP52INCkhuQJ+YwJyfL8ljAnvv5VPqt4+fKVJanXa5bLMc5NRWzl6he1UTrYuDDPxqFNHvp+nGG4k/9BD63JxNMH3t68x8+zs0v/1muj7zmcKDlOO/tP+rxml6041YAR/XvmCW5Atpes4uF5tK0dTuL6sCHI4P0xvudQ/inHs7PTCAJz6FiXjGJGg+mt70RhAEYvfeh6GbrpPZ+Rs7h6Js7S1fNuStoeBOJzS8Abs51eOTqpJMiqjw3dd/l9cvf/3pv5n/oniFZHoZYv7BhzCTSTfwuxxWNq0kI4PHrN4u59xMlRa29UAbHUVZUni6LkgSgs+XI5mOTnPRitaSRY4/e8J8rhrmUoezC/IVK20lUwWSybIskrGMuwlwpJ1atLxdrpoFzcHqltUk9STjidJcnnzU2xhRjIBS3S5XT/B3JqWj+CTMRALRMqoGf6u6iW5aeCRPQ0qmejekDtIDA/jWN2aVs/QMGdPPgpKkSVwBZO1yVRbDDjHq19UCWXZHb5hrP7CRubEEz39/AtGU8Mk+TNPike/tZ34qxbXv31TSzlMMR800P53m0HM5NZNtl7PvC2teJqBFsDorvM79P4OfvBtmj7mVusW5TMV2uXz4ggo3fHgzskfk53fsJj6bdq+HSqcorl2ujmsyOmlfYzH/FClBQCxzol4v2peEedPtW9HU2kTTaSmZZG9hQ1F83G7jkqvcy76mM7PLlcl3qITzOjaTPPnHrA29pmaGU9gTpifUw6HZl55kGo+mWRTxkdJSiJaEVONELx/5SiYHH932UXRT55s7v+k+Njg7SH9reascNK5kkiURnyIW3CsOyTQ0P2SHfndtsDfCdY5l+ahMMtlWC0EQCMgBV1XgVGu7SiZdpWn+ELpguQRcMRwlk3NvL4ssQxbkyiSTvzXPLlfeRunYoWoRn849E1YiCIKGqhuuQqB9yenb5fIVoTzxBVo4xpzQ51qrNMPCsorGng1vtv87cI/7kDE1jdRhK5kCWSUTQCjFWbfM7Z3eW6BiAjAdIqWWonnbu+3GsR1fA2w16nxapzPQyVRqivhMmvSC5jbLORAEgZ6+FkYH55hKTtHh7yhrz3cIyHL2Nc0w8cil40h8Nl0ydyW1JPvNO0iF7ufa5dfyb9f/m3u/FCOoBGsomZyw6hp2OT2NTwqwTTzszof5aO4KYJoW8ekcaeKVxZIN/cb2jQzMDJQtFHBf0r79GJJAcP3Gkn+TIg7JVGHuMlRU5PoyCgHB30wTCVIZg9kxJ/S7elB+X0sfM+mZXBuxGgdviOWb2jF0E1mG84L/UUKI75vex6LgIkQrgiLWVjJBXouxx7420snT38RblkU8U69dzqw7k0kUBWRRKLHLzbw4iOprpXdrqbo31OIl1OJl4ngDDXP99pyTzstlKrDrgpvZ5OsvPz/5lXOTM5vRTTTDIliDZLIsi7mJpGtH9imFB/uCILDh1YuZPBln8uQ8LTe/DUvTiP2/e5i+4w5mvvUtmt/6Vhb97/9duA7RUi5ZjhpHCgWRb3g9lxywmBkrdGxohl14IokCsckkTWXymDRTY2xhjKXhpe4ayVHRJ555Fq9qH+4Vt7+dCZTubgLbX0XsvvvQ8kig9ILGZDzNaDRVIoxwYGfWVbfL+bIWVI9fRq2RddZIY97vEl4hmV6GiN59N54VK/Bv21bxOYtDi9FkAY+hV621d0gmTT09D7FlGGjj4wV5TA7EYBAzkWAilubYVKLEKge4fuJz1TCXOWy38Yir1lS1y2VSOrpmukomX1AhI4AVL98uVw/J5DTM1Qr/rjfMrxgBb33B3zXtch4BLAvJ1KqSTI4qwCt56pZ5+htslzMWEmROnGg4j8mIz2GikJFTdCr2z6aEWiRTvl2u8DX2rm/jyj9aS/RohiuOvgOv5OW5+45xct8Mr357H4vXlJ+IirFsUxsdvWGef+AkZnaDZ9vl7O977qT9HantFdQyzgn1/8/emwdJkt33fZ+8s+7qu2d67p1jd2b2ALG4QYAEQFICKAAkxAAkU6RlSjYlhxWWJStCEaItSnZIVFCOMGVSoiXRlERQlkCQlCiChsggCeLgAljsOTO7M7M7Z/dM31XddeWd/uNlZlV1ZdbRPYPgCvP7Z4Gp6u6srJfv/d73fY/meuIzs9eXqek2h9LVyzM5/sz/8DSO5fGf/q+XCSyxuGUymbzxmUz1TbFBr5vrWDKoBwCZQKTEfOJ/fBuO5fEff+4lOs10AKB1UE8mx4fd+9mm33GZlYMZf48RFx9X7Ms27tx4durbkzC3Uu9wuGpiW+K7UHUZLwjxUkxZ91avJ1NcR0tH+fOP/3l+843f5Or2VTY7m2x2NjP9mEDEX4/LCAvDELvtUjTUPpBpPj+PoRjc2Y2YTItP7YvJFIZhKsgUhmGf2XIvyNSuO2iGgh6P2Vf+HU/99scJCPAz5sd4Ix+zVTRF40TlxOB60t4GvSjSvPKzgDRELjeZ8XdJr3SZTHcbVOZyiefEpGV7NkYMPKx8C77+z5g+sUirGSZxz6lS3eoxOPouuPTrAIRBgLe9jTrTBZkaEcg07+YeaMJczaqx3Fzu82OCrlxuFJCCUYS3/0W4+gWwdijHTKb8HOvtddZvC3bYXiYTwNK5KVo7DvX1dmqyHHSllGkJc5meTDWL4h7T789f/zzr/rdQap/gZz7wM4mnWFrltVFyuThdboRczutg6CXOSCsUwsF1uroQJcz1+PSZmjwQI/7k7JO03Ba3dm+l/h3bt5l6c4PGiVlkY/D7iuVyfqZczsYOx5fLyfmpxPh7c1ms3yOZTNHcl7CZ7AYYJZbOVTEKKhe+SyUnN8RGv6cubV7i4sxFsc5EINMoJlMMOoQxe+QAnkyWb+GF3vjG3xOwUXVVHgCZVq6L72jp/Ezaj7B4qjIRkykOUYrT4/bO4UDi2ZTGZAIiJtPD92SKmUi5EdYanYaL3faoRkwmETxCH0By7l2LqJrM5S/fwzh9mtyzb2fjn/wTNn/hn1L5s59i8af/LtJe1tndr4uQDEkBOwor+NEfQw1A+a3f73urG0RS3RB21jtU5gbngtXmKl7ocbR0dMCDq/XccxQXBfjbTFGTHKSqn/wk7vIyjW+9lPyb1XJ56U62HxPEnkzp33MQhDgd4ckEAmRKM/5+VI9Apj9xZd+4QeeFF6j+2U8NRT4NxUAz82i+P0IuF4FM+wR5vLU18Dy0FDM2OZ8naLf56hviNOa9jw2CTLGM4dsFMvk33sRSNMwjS6jzcwStVsK26q2uZ4YAjyRJoq2DlEIldn13LE+mU1XhL5IaO91Tlrtf4+/hIFNi/D1E2md3PHRVTJxy4GVueKCb1GRMwGQyJzzlsV9/DcKQ3KR+TDXhMeYoFku5GGSSR8jlIjqx5ww0MwCPv+cQh75X4ezms9z4rMsLX7zN+e8+zMUPjG9EKEkSz370BLsbHa59U7ALrJ70pLWbu7iyTbuU4ZEWN5WtTUp7YtkhkjAOYTLFNXukxJ/+ySepr7X5g39xHSVQH4gnU23Lx1Kb2FobRw7R3YOBTCCYZB/7q0/RrNn89s+/kjpXNezxvAmga2JsKAaaIqPKUiSXu5dt+h2XWT0Ykykj4Smt8pr4POM+L2enznJr91YmWPig6t5Oh8PVHJYdg0z91PZhpUXv3fsd/uWn/jJlo8zPPv+zCRsrK1kOBP1+3A3Kta+v8st/+2tUNYVmz7MiSzJHS0e5vfW6aJIXnxQggTcZk6npNul4nYG0ry4DMAKZtHwiK2rW7T4/JnbvI4cekuTiZzCZYuCgF0A+XT09uJ60t0SyHAgfsfxMNpNpTOPveKNaNSpIkk/bcdi822B2n35MIDb7pmKC78J//GtQXGDq/T8IQC0KWsgEuC9+Sphob1zFr9fB9xNPpoKhsmuI+e+iduyBgkyxDCubyTT6sImFaC3bvS+YTB2P+dw8Ha/Dyo0tZFVKBSGOnItkGyv51GQ5IJEppTOZBj2ZgiCkWbMHmExrrTUUdLzad488YR/NZBrT+NvroBlTyFLIYmNQ7hanUfWmjqZJk56cE9/NKxvpvkyvr1/m5P0Q9cn0nkIpx55MGWuXZ9EJtbHlcnJ+KjH+3lppopkKpZnhrOfEYy9mpjpNMEqomsKP/vR7eO/3R+Ojxz+ubtVZbi5zYfaCkEZGcrlh6XIAuYhd6kcst4PIkeI5ahwm06RsVE2R++RygeOw0S5gKC7Th9L7ncVTFZrb9tjsF6VUQjt8GPuqOKzZO4cD2Fevoh4+hFJK/4ymNsiuexjVisgDo/YK8Vw6HTGZJEnCVPv7cCOvceYdC1z75hpOx2PqM3+O0LKo/PAPc+jv/b1BgAngxpdAVuHYuwUIClTPXuDVUwqzX3yB0O2OI9cL0GSJ1o6N5wZUM0y/gT1MpoDAsui88ALTTwsA8EEymQBK3/d9yPk8tS92Axk6TZeX7tZRZYmLS+kJkrFcLjXJMwJqYzN9I6cOlct9J9cjkOlPWNU//3lQVSqf+MTI9+bzFQw/HCpFS+Ry+/RkcpZFlHAqkymfJ2i1+Oqbm0wXdB5fHJyUE7ncELZVx+vwg7/xg3zt3tf2dY29Jd26wZ3SAnlTQ50Tp4Fpkrl2LGcodxsj25TROoMbgHGZTGW9zEJ+YWTC3IHS5YZ817Zvo8t6ZtMYhgJ912QxFuRRTKboOzPVCeRy2mTG34np94RyOSc6hQxCmDbF5kPI5bKb4V65XNaGufwuj8sLX6H+psehxyp84NPpp1nD6uTTs8weLfL8F24R+EFfutzqjR1q5fu0g+xkGwDam5QiFkQvO6PjdQjCYCTIBHD08Wk+/ONPcP/6Du9Y/ugQkGkCudx2yI4pWBOuFKAE1sSb9rQ6dLrK9//EBdZv7fKf/8XlhAUGYqPseEECuo2qvYy+XHzyuHs/2/Q7rgPK5RzPH3tzkvhFDZkbe+vc9DmCMBgrwXK/FQQhqzsWhyo5bFusHbEEbhyQKX7vXq+3ilHhJ5/6SZ67/xy/dPmXAB6YXO7OlW0822daHkwyPVY6xp3YOPvQUxHINBmTKcuIeW8qY07NJRu+9o6dHGCIN4sNrSy5mT54Sbqc1g8yrTRX+j1x2lsCWIqruADNdFl41/h7OJAQPzNVU5zq7u622N209u3HBD3pcl/7JwIw+ujPMnVMrMm11b0g055n5vwnAAku/TrepjjEUmdnovfKNHQxl5+VDvH69usPzFD11c1XkSWZCzP9AEU4CchUiuaYxj3KOY2O6ydr1L2bNWaXin2JYXFV5nMUKjr5jbnUZDnoApBpTKY0FmVn1yHwQ0rT/QBQza5hyuWh6UlxFdTCcCAjSZcbDqy0vTaKMUcQSszVXx543SxqGAW1z/w7zXz3RPkERa2Y6ct0/YXfx3Rh8R3vT31dGcOTyQrVsQ8LMKvkJAfX7rC10mTmcHEkcFc1q8zn5weYTCDug6xHm/QeKVzdKhXTAAAgAElEQVTsx3Rx9iKuH2CM6ckUJ/m5hKiafCAmU+wFNg7I5Afh2HI5iJhMvV6Wr73Gdvk0i4tK5v1cOCUAw4nYTOfOYV8X9z1t/rGvXcM8m702fbs8meID5ZEgUzSXTh3qshFz+iDb6sJ3L+HZPte+sUr5Yx/lxOf+PYf+fgbABMKPaelZwf62u/PN8++fJ1dr0/i9Lmjj+sGeZLlBZuSdxh1ABHIYPUymzosvEjoO0+/7LiTpwTOZ5Hye0g/8ALtf/Ubyb1bT5cU7dZ44VE568701LEkwlpzGZvp6TsUZIZf7Tq1HINOfsCp+8IPM//W/npzaDatCUTSEtpX9UFrNyPh7n0wid0UkMulHBjXRcqFA0G7xtTe2eM9jM6lGb+NIQu4173F793Z/2sY+S7tzk1vlRfK62gWZUiRzMZMp32PM6hcUDDvs2+CCiH4fx/gbhC/TqIS5juOT0/aRLqcrtIex1kak4PluQOCHqIgxIfvOUOPveMOW08yxjb/NyKgzGDNN0LpyBXV+Pvmuxi0n8lMIfINStOHoKOpwJlNPulwakwnADiy+cvLXePrTc3z0rzyVuhkYVZIk8Y6PnmRnvcP159exo3Q51/HZWm7SmF7LNlJNQKatHrlcd/FK24gOq7PvXOTQ6QqHGo+NBpnGkcvVJGq5dQxZBwksSToQKNNbp56Z4wOfOcutVzb50r/tJsDE43BsJtMekMnUFRy7A+3NMZhMlYMZf08gl9MVGUWWJpLLAQ/Vl2mzaeP6IUtVEzcCv7SEyTT6OpWoMUtbbz597tMcKx3j6/e/zmJhMYkxTitnApAp3mBUZHnAv+x4+Th3rS18SYH58wJkmtCTaa0tWEKDIFM/A7AXZGrt2BQqPXNx9Iyokk2QMeemMpmmhPl3n7n1AMg0n8lkip/tcY2/pyKQqbkuNpNzxw7AZPJsTM+GL/0MPP6D8MQPUp7NoagytfviPsWSjgH/m9IinHg/XPo8fnRIFPdEkiThFsXYOR5O4wTOWKmu49Srm69yqnJqUD4WA+mj5HLxtQM0VpODgqIyDaFEfdlKlcqB+FwLZ8rM108wlxv0/4JsuVwYhqnPTGM7MnTfw2TatrbJKRVcPxy5Vo+Wy40HMnW8DqpS4Gp4hKntQZAJBJtpFJNJlmQuzF5IUgD31s4LYjO58M4PpL4u5fOgqtmeTJ6NFUwAMuUEA03q1NhaaTFzZLy1+dzUua782W6A0TMu4nvpdgGkGFQ7P3M+AhTH82QyeyLtjYKWqBv2U3Gq4XhMphB1AiaTrvQDiuvPXcExqhx9WzaTfO5oCUWVJwOZzp7FvnGTwHEG5vDQcbBv3syUysG3z5Opk4BMw/ue2mobzVD6mLOmOmhbMX+ixOzRIpf+SOzpck8+iaRkAFidOtx7EU59UICfPSDT1tuOU5822P7sZ5N/i+VysdS1ksFkMhWTudxcX7pc64+fA1Wl+M53ki/rD5zJBFD55Cf7Ul07TZdXluuZUjmAvQl4vRWb58dMplHG39/J9Qhk+hNWhXe+k5mf+G/Gem+xKCjznVa2ZCVGXPfLZHKXl0GS0A4NMgDkfJ7OTpPVXYv3pUjlYDyQKTY/TKOAT1JerYZa3+Z2aZG8rqDNi0Ytjck0YMwKUFSRgcZ2/yTn+M5YcjkQvkw36jeGxiq3HW9fTKbcCLncKMZV7IOhhqJhln1nKJOpmYBMBq4/XmNiauNLawA6ly9jTiiVA9isb0R/p0w5kk50VGOoJ5Ok66Cqfelye8vyLEIp5PQ75zCLQ5KERtTJp2eZWSrw/BduYUfpchu3dwmCkM7MdnbTHjeVra3E6LqZAjKNw2SKa+ZwkanWQiIj21vJRm9EU+10PNpthXpug0ok2+g8QJAJ4OIHj/D2P3WcK1+5x/NfuAV0x+F+0uVANIW6Fc0Bo5hMuSo4DfD31zDs9XcYVpIkkdeGP9O9dbR0lJyae6gJc/d2xPg7VMklIJMepxKN4UOhpXgyJa8pGn+r+iP8xBd9PrQxHFR2/fFS+tq7DruRUXBJGgSZjpWP4RKwNndaeMYoB2Ay7dn4701ljOVyYRjSqjv9IFPEZNIkiyBj7mm5LRRJ6TPLjhPmrtd6JHOpTKYsT6bJjL+nTAHeOBviHs0dQC5n+Rbmna8L9s9HfxYQBr/VhRzbA0ymlOu7+CnYuo73pvDSUGa6PYZfFNd1yBcb+gdh/h2GIZc2Lw1I5QDCGJichMm0ey8JbzDkKtXOPIED88ezk7kKxyHvlpnupMvlcmoORVIGeqVuhHj/QV/MCtgrl6tbdXKKuA5nhNfaaLncmOlyXgdVMrkdLmJ20kHR6kKenfU9IFMKuP3U7FNcr11PZfEor92gU9QHwmrikiQJpVzO9GQKPZt2ML4nEzmxUVUauzgdj5nD463NZ6fOcmPnBq5ri018r89i7G/V8/kubV3iRPkEZb2M4wuz6pyaG5vJZLk+ZkE9kFwuHnfjeTIFEyWE6qqceMgBLF8Ra/bxd6Qb0gMoqszcsRKrN8aX7ZvnzoLv49y4kczhMQhv37wJnodxLhtk+nYxmWIlwSjj79pqi6nFfB/by9QHgTBJkrj4gSW2Vpqs3Rxxv25/FcIATg6CTLOFeb70rjyd57+F9frrQFcut7PeQValAQ84EEymI6UjIo2tx/i79dxz5J58EqVYoFA1aD4EkCn/jmeR5rq938ZWm5bjZ5p+A+w1J+8tO9pXG4Ue4+9HIFNqPQKZ3sJVKYmma7e1kvmervH3/kEmdWFBbND3lFwo0I5Og953Ot2YLz+GXC4BmVJieScp5w0hI7lVPkReV0bI5RzUXmNWQCmJz7iz0Q9UjCuXA7EpcAIn0R+n1X7lcgVdxfGCTAPeUWBYjLRrgZjEpZFMpugkRTPGZjLl4vjUMRbhoN3GuXFzYqkcwM2a2FQ13TkqEchkqfpQkAlAyuWidLn064sNcHPqcBPTUSXJEs9+9CT1tTanLBlTVZJGyJttZMsP+uRyYgFr2PtnMoFIutH9HH4zfbofl+0Qn1LVzXWqEQulLT9YkAngXZ84xbl3L/KN37rJla/eS5hcpUmNv9UuyJRsakpjyOUgAQUmrUmMv0EAx+PKS2VJ5kz1zEM1/75XF6fih6u5hI1kRODeeHK5GGTqfqYwCGj84R9y+8f/a+b/6s/wAy+EfP/l4QCu549nGtt7gl0I5VS5HMDtmRPRBZoTyztjkGmvGfNemWlezdP22thtD98L+g8wLDGeTKkzlMlU1PulNkeKRzAUo18i2d7eAzLNQWsd0rwjvIDDnkzzzeHj2fIsdFlPWKH+tkNxyiBXGm/d21thGGJ5FkZ9GZ75r/rA3alDhRRPppT18ImPg6TgXRFx9OpcF2TS8jlcVafYDihppQfiy7TcXKZu1xPPn77yJ2Ay6XkxjzRWKUdzls4U8y0xFudPZAN30pJ4/rTV9M2PJEkU9eIAk6krixyPyVSzaxRUMdeNAo8LWiGbeQtC0iUpwh9sSHW8Dopk0CSHmnGgWJ3P06zZSb8qEp4Gr+/i7EW80OP17df7/n2zs8nS7RbWuaNDJWtKpTJELmdho40lHweEjx+gRwD92Eym6XN4gceN7StAmMjlgO446wGQLm9e5sKsOJCzPQHC59Rc0rNkVRwyY7k+Rl47kFwuHneNV+WBPnlveX6IMolcTpH7+rK1TRmTTiorprcWTpXZuNPAH/NgM2Yp2VevDoDcsVdTVrIcxL6j307j71EgUzdZLq6clp7yfOYdC2iGwuU/yt4zAsKPScvDkXcIhp3XEd56CEbv75y3kUyTWsRmiuVy9fU2ldlcqqrl7u7dZD2O77e3s4t16RKF97wbgOKU+VCYTJIsY777vQBousx2NC++7dhU5s8kYyLlu47JG73G327HT/Vv+k6vRyDTW7iqJXGy2urcy3yP1YyNv/c3KborK6mm3yCYTF6zzVI1x7Hp9HSSbyeTyYqS5W5Hcjm5UkHSddxUuZxNodzfQOuRh8buRj/92PGd8UGmSN6Q5ZviB2GfR88kldzLDABnbCZTRK+WPWfowhxv2PKaQRAGQ9lZcfU2NKPKev0qBMG+mEx3mqJB3HaXqOYiuZyqD5XLgRizIl0u/XPH1HNzxKnsOPXY2+aoHsrzHkvFUCRWb+xQmc9hFNXRcrnWZsLc6U2X2xtzPk5NHxZNb7iVPjYSkGkE2yGWMezk1qgaorHuSDJYtbGvZZySJInv/QuPc+z8NH/42ausvCZM0ieRy8mSjCqJ95u6Qt6O5oCRIFO0udtnwtwkcjkQz/Qk1Puz02e5Vrv20JqZGGRaquYSoChhMo0hl1MjkNlzAwLbpva5z3Hjz3yc5Z/8Kzi3bjH/N/8GxtmzzHWGg0zjejKt3dxJGtocDDKZVLFxu1OKQBlV3xeTqWJUBuaEvVKLmMkUN8m9UuwYtDSlNoGX/t213NYAeKzICqcqp7rriWcLpl1s/A2CyeRZqcCo6we8y1K5/pu3WLmW/ZxavoWpmhR08RnlWnAg0283cAkJMX0XCv0HUFOLBXa3LDzHH86iLMzAY9+Ld/MVJF1HLnbvTV5X6eSLBPUdzs+eT/xqDlKvbgj5VRqTSYrN9sfsAygdhsb95KDAdTUOt04Rqv7AhrC36sYGDX0b+272XFfSSgO9khuNqb3PTHPbQjcVjD0JgdvWNkUtAplGPNcFrSBSxbLWf88emSwXg44KBo0wh+IOekpBN2EuBjCymEzxd7RXMnfp5tc5sgXFZ7KTmUGYfwdZcjnfFiDThHI5tSnW0VHJcnEl8udo3PWDTP1MprXWGhudDS7OiNTD+DDDVMzRcrloTrbcACOvJiyM/VTDaSAHCq98bpPnf+f20Pe6foCW5feTUr3pcu7WFlvGERam/ZH+VosnK/ieSMMc6++cOIGkaVjXrvXM4dEad+0akqahH89mT4nEw2+n8Xf2XOBYHs2a3efHBNkBPLqpcvZdi1z/1vpwRtvNL8Gx94j1Mh6XEZtpLjfHtu6Q+9gPsPNb/wm/Xk/kcjsbnVQ/piAMWG4uc6wcg0zifmuXXoIgIP9uATIVqsYD92SKS3vmWQDycod2w6Ga1zgxk52qaQxRZXTlcuK7MXIqQRAOVYZ8p9YjkOktXNNlof237XTqMXTjSvdt/L2ygp5i+g2CFSJ12rzv9EzmQhADI8NO67c6WwDDKdljlH39Oq6Zp1GsosgSkiShzs1lMpn6TpoRmwKXsM94EiYDmU5VTiEhDcZORxVP/Ps1/gZoZ3yXtm8PBUecBGQSDZzs2amylrgSmVK08RjH/DsGz8bZOCem3/sAmVY7YiGyQoOiLiQEHUUbyWSSCwVMz870ZLI8CwlpbA+uYSXJEuc/fJTZQEa912H1xg6LpyoJ4yG14nS59iaKLJHX+xOz9gcyRakjtfSxYXt+FHs7vJmrrbWBkB1zk6opNrkPg8kEoCgyP/DfXmT2SJHrv3GTRU8aXy7nCW+y+PPkNJmiE8vlxvBkgn1/pknkciCCESZJ3jw7dZYdeyfxCXrQda9ukdcVcnoIvvgcMZNpnNhmSZZQVInd577JGx/6MKs/9b8g6TqH/9HPcPp3/zMzf+kvoS0t4W1tDf09TkpSVlqt3thl9lgJ1VAwfWkAZJrfWcEMAm7rEailmiKWeYJab68P+DHBoNQrr+bpeJ0EZCqmMJkKUpMwY+5puI1UhuKZqTPd9aQdpVLulctBqmTO9QPMEAjhd//lZTqNDF+2KAkup+ZQfR21KTF39GCm3wBGGPZ7zYBIigrFfJKW7tRXFz+Fv9NCnSr3zU95XaFtFPFrNc7PnOdq7erY4RRZ9ermq5iKyWPVxwZekyaRy4HwZWrcpxyBO7uWx2L7JNZUPfWUP67Nzgb3Ktep33AIM7ySSnppgPUdS940dZDJtJfFZHkWHa9DWYs8PUcwQOK1JrM/86yRDC/LtwgJkdHZJY/sNCAY/LvVBQGu1Neiwx5NTp135vJzLOQXEpBp+36LMAxZ/sYfAHD03R8aej1ypYyfmS5n4zCJJ1Mkl2srFKeNAUAvq46Xj6PLOtdiL9I0JlMkn7+0JfyYLs4KkCmWE+fU3EiQKad1e3CzoGGlJCiPWw2nQd4Vz/PK1eGHS14wHhs1Lk2RErnc6pdfxtXLHLkw2pt28ZRYs9fGlMxJqop+5jT21WtJHxjPP9bVa+inTyNp2Ycg3y5PpnGMv+ODv6mFQSZT1np94bsP47sBV59bTf+ljVXYeF34McEAyBSvhc4nP0JoWdQ//+siDVaCnY1OarLcensd27c5WjoKdFnz+UsvIpkmuWeeAUTat9Px9q28GVol8ZwarQ18S0jlhia4D5XLRaFI+a5cDnjky5RSj0Cmt3CVi6LRdJ30ZBnoZTJN/tCGjoO3toa2lK5t3w41TM/mfY+lS+WgC4y0xpHLHZDJZF+/Tm3haB/jIQtkau3Y/SfNQMnUqMsh22uDcrlxPZlyao7DxcPc3L2Z+npnzMSItOqywtLv5SgwLJ4YlahBlT0LPyOiE3oMl/cDMo0x3qzLl1FmZ1HnJzP9Bti2xTU7EhRNVTRbigLOcKBSyeXIDWEyWZ440R8FuIxb8+en2JQD3G9t02m4AmQaZqQa+1u0xCa8ZKp9G+f9yOXMgoZlNlHr6cCU7Y4HjOystcnnOwSyT9UUp7dtST6QUfaw0k2Vj/33TyHnVH64ZaCMSfPf+7zmNIWyuyH8eHLZ9GigCzLt8zPZnj+ZXE6T6bjjNybnpgSN/2H5Mt2rdzhczdHxO6i+mEvM3HhMJvvmTe7/r38XyWrReukS5sULHPvl/4eTv/55Kh//eCK5Vmdn8LY2h/4u1w/Q1eHPoO8HrN/aZfFUmVxRQ/NCLLdfTiyvXeao53E36GGiZHiTZVUmyJR4MkVyOU2Ax6l+fxHLqCg1wAtT59yW20oFj09XT7PeWWfH3hF+TDBo/A2p5t+2F2CEEoW5HFbL4/d++UoqeGF5FoZqYCgGM+3DSEgHYjLF/m9mCsg0tShOj2v3W8PlcgCPfwzPVlGM/rGXN1QaZgG/XufCzAW8wON6/Xr67xizLm1e4omZJ9DkwQ2mPIlcDgSYvXufcsRk2m05lBvz1MsZG7uo1tvrbEzdwW57bN1L74eGyeXSPJkG/JhsMbeVjclApkz2rWt1mTcZFQMhUmjQDHNIhKlJsJU5MTbizbOhpst+AJ6ae4pXN17l2jdX+bc//XXuXN7GeuVVQgnKz7x96PUo5Uo6yOR7SIGHHepjp4TGa4psmcyOyWICUGWVx6qPcXUnMq3vfU4kKZL2ivt2efMyiqQkiZwJk0kdh8kUscs9/8BMpqbbpOSKcdPYstjdzP7bAmTaH5Pp7otCznXiA0+M/LnilEFxypjI/Ns8czZTLmeePTP8Z79Nnkzj7BVi2fEgk0nO7MHnjpZYOFnm8pdX0nv/m38k/nsyHWSKZeObSwXyzz5L7Vd/Fc/1KIcyvhukMpli+5AYZIrvd+nKS+Tf/nbkqDeIvZwehmTOtaM5srOF5oW87Ui2H1PvNWaly6m6nASd6Kb4joaBTM7yMkFn+LP6X2I9ApnewiWbouEJvPRT4TAIkwXF2wfI5K6uQhCgZTCZbrdD1DDg3ceyjSwVWZi8DWUyWeL6D+LJFIYh9vU32Jo70kcvVefm8NbTQCaHQrm/YSwYKnU5HNCau4E7EbOlrJczP0tXZz15ulxuhPRwFBgWT4CKJa5NcsVEnuXL1HQ8dEUmr4nfOY4vU04bX1pjXbmCeeH8xIDOensd19dBtgglAWTm1ByWrIxmMuXz5H0n0+zU8q0D+zH1lhOEPGd6BG1xPxZPlRNZTWp5XSYTYUjRUPvS5eJxVdDHZzIBdEo19Hr6ptH2grH8J2prbXIF0ZjHyWCdh8RkiqtQMSh+/yEk4IV/c4327ugxOAAy6QoVd0v4wowaa7lYLncQJtP4AHJ+QibTmSnRAD8skOn+TodDFbFxUQOxQTYjX5lh3i3u+jo3f/hT7PzGb6DqCvk//YMc+8VfpPDudw8838rMDP52jTCFyRCXN4Zcbmu5iecGLJ6qkCtqqF6cRthzP++/wrFQ43b7vvj/qrk/kCkl7SuRWmjddDkv8NitiWe47xAjYjKVZTGu0ubcptNMTW2Kzb/frL+ZDjIVYpApjckUYoQwdaTA+3/kNHcub/Pi794ZeJ/lWRiKgamazLbEodJBkuViM2IBMvX/nup8HkmWqK22BySHA2VW8IIyqrTdx3wp6Aq7WgGvXuPCjGDCHsT82w1cXtt+LVUqByAFLgEKyGM+26VFaK5R0sXY31ltIwcK9/LDU/DW2+t4i2KMrFxNB7qLWpHGHrlZvDnfC3CnMZm2LcGGq+gCHBm1VsdJewdhMnVBJp0G0SbUGgR5NEOhOGUk/n+GJmNlgGAXZy+ytrPBV34tskm4vEnx2j0ahyoopeFjN9OTKWI5Ck+mMbdHRgU31AjdAtMTgEwgfJmuRdHuGHt+tmeuurR5idPV00lv4vgCZJrE+LvjiHQ5zwnw9gmSNJwG02HXlH55CJvJ84UZ9Lilqwp21Jfdv+9j+rtURgABcS2eqkyWMHfuHN7GBs62eBYMVcGr1fDW1zHOZvsxQeSj+O00/h7C4N5ebSPLEuW5/p51FBB2/v2Hqa222bybsk+58SUBnC4+Jf7/XiZTtBZudDaY+tEfxV1Z4fgbL1GO2tSsZDmgj8k0Ze1SuH+Hwnvfk7wvPphp1iaTtI9T8ZjXdtdQkXjqcHa6LfSEGKWs1Xbbwyx0DyNiJlOW+XfoOLz5ke9j+5d/eT+X/pauRyDTW7gkPVrY/fSJ3u54hKF4ADwnyKRfZ5W7Ik4TslI6rjfFQzsjj9D0j0hFi5lMB5HLeRsbBDs7rM0s9SH/6vz8AJPJsTw82ydf7QeOCoZCXQlobll992oS428YnsbSdmOd9f6MvyEbZBrFZHI64ueUtmjulOh0NktH3LI9CoaSnOyOw2TKJfLI4aejgWVhv/nmvky/r2xdQfdMVEVcT8FQMFWTtix35WYZJefzgsmUsQB3vE5futNBy3J9Xtd8tKqOZihMHy5S0Ap0vA5BmHKP4g2wZ4HTomhqfcbfscxuEiYTgF3ZxWiU8VPAtVguN6zCUMhIzbzY+ExFTKaGpD5UkAmgY8j8esHG2nX57Z9/eSSVei/IZGoK08Gm8EkZVYlc7tvjyTSJ8TcIqcxScYlr2w8HZFqpWyxVc7TddgIy5czRxt/OjRuEnQ5Hfv7nMWarhGY2CKrOzILv49ez77Hrh6gj/DxiI/3FUxXMooYczTnNXqbn6qscy82x3FjGD/zIk2l8kMkLPLasrbHlcgC7tTZGQUWNgVvfBa9DqOiUJdGop8mUm24zk8kEkc9fKpMpWy7nREwmM69x4QNLPPZdczz3H25w/83+Z9b2bXJqDkMxmG0dwVV9ilNjsnZSKpHLBQGY/YdQiiZTmctRu9/qylWGALOepaIqbbjzx8m/5XSFHT2PX99hqbhExagcyPz7eu06tm9ngkxy4OClMJwyq3QIQp+iJ3qzTsTMuWFcGeqntt5epzyTozyXy9zAp8nl0oy/XcfHarqUpvu/x1rkoRfP4SOZTGokl/OGgUwjkuXidTnUaYQRyJQRrlCZzydMJlNVcLyAIKV3fXL2Sb5r+fvp7LiUZkxuvrbOyRUPLmQng8WlVMoEu7uDQHc0N0wkl5Nl1oIzgDwRkwmE/HnLbbApywNgLKoJbocwDLm8dTmRyoF4rjVlTCaT3mVkxBvj/Zp/N5wGVV/MPYoqD/V5Gze8IS5dkXC9gMDz2QpmmC+2xz6AXDxVoVmzx/bzic2/wygsyFBl7GvX+17LKlMVEs6HbfLccXwkaXjqb321TWU+h7LnQCY3AmSaPhSxE/ce2oWh8GM68d0Qr78xwy4CmWbzQsK43l6n9OEPoS4s8PZv/R6FiCBXTWEy3dm9gyqrLBaExYsqS7xtU9z72I8JuhLzh8Nk8pGlAL0h9oNnK9l+TNC972mecHbLTfyYoAsyuZ30e+5FvY5SHQ80/S+pHoFMb+GSDAEoKMFO6oQXG7vFTcakpmTO8jJAKpPJ9nyu7ogHKmgNZ4/kdfWhy+XsyPT73tThfpBpbo5gd5fA6iLj7Z0InKj0N18lUzCZAi+ktdOdfCeRy0GUxpLhudMeMzEirXIj5HK2b2PI2ddpd1wkCeSW2GDIUVpElvl3y/YpGGoCXI3DZEpOzUac9Nivvw6+T24ffkxXtq5g+Dl0OTYmj+RykjRSLiflc5jDmEyRXO5BleX6hBIc+ehRPvIXzyPLEnk1T0iYfgLZC5K1NymbKs1e42+niSZrE4GeAE5VGHburA82pPYYPkKtuoNn+2g5cfIXG39vy+a+AZlxq2F73FdDvu8nLrBxp8Ef/MrrQ99v+3aSLAei4ZoJtgS7YFSZ+2cyBUGIF4QjDdR7a1LjbxBspoeRMGd7PptNW8jlephM+byavJ5V3pqQaulHj6BqylDmrDorNineZrZkzhlDLrd6Y4dCRRcpaEWdMKLDJx5mThu2rnO8ego3cLnfuj+xJ9NmZ5MgDDJBpg/feR713/wS0GV8NOqd/rUlYmyEUydRJfG30+TrWUymxcIiBa3A9dr1dJApNwWymiqXczwfHWFQKkkS3/ujj1OaNvjP//JSn/Gr5UdMJkUwmXYL42/w0ipOeExjMoGQzG33yuUyWCOh7+M32qgFGS59Pvn3gq5SU3MEOzvg+1yYuXAgkOnSZr/nTW95foAaugSTgkyA2l6joCu4GzaS6bOl3h9gIfXWRmeD+fw8R85WuXe9ngquDPVk6pl7mnGy3NRgshzAVCyXGyNdDkYwmbQRIFMEhISBToOI6WBnm3/HTCZziPnuknuKp+5/D5zb4fz7D9NcdTG9ArPPvmfgvXtLLpchDAmae3pOL2YyTQbhlRsAACAASURBVCCXgwhkgumlyRjGifxZ1wefE80Ez+Ju4y67zm6SLAexnHg8JpOuyEhSnC4XzeX79GVqOA1KvvBjPHZhmpWr9UywxQ2CkQcFfdepyjh+wOo3ruKqBZYeG8406a2FUwIIGZfNZJ4TQJJ0SzALTU3GviYOboxzI0AmPXtMPshq2T55TRk6D9dWW6lBAlnG33FpUZjHwIHd9g3Yudv1Y4IeJpNYx3JqjpJWYqO9gaRpTP25z/DYncvM7TZRNLnfizCqu427HCkeQZXF+JMkie/afAMnX8R8oiuJLEQHG82HADJ5to+qhOjR3Kn5w0HCxAw+g8lk5LvrgTGCyeTXIpBpaoRdw3+B9QhkeguXbIgHUvPdRHLWW3ETGTcZk5qpuSsroChoiwsDr714p86uJB6yoD18Yz/stN4LvORk7SBMphhkulNe7ANw1DmhH+5lM6Wm/xA1rrKYeGLJXJyqNsmmfpjnTqKz3ke6XMEYLpdzfGeEXM5Hz6kE7ciTKYillBlyOduj2AMyuf5oLX8uOjUbtXHuXBEbgv2Yfl/eukzZM1FVL/qbimi2JGksuVzOczIb63iz9aAqbkSqSwVOPSPGYsx4SAUiPQvizUxra1Aul8F2GFX+lPhbWyuDQK7wZBo+HutrYjyrhniOYrlcXdIfOpOpZXsUdIXHnpnj9LMLrL45/O/tBVtzqsxcuD3a9BtAL4g47n18pnijN7bMgsjAeEIp87mpc9zavZVs5B9UrUYR3IcqJm2vjRqI5z4f+coMa6rdVQFwqAsLqLo8FGRSZgRA4g8x/x4nXW7tpjDSlyQJs6gRWuJvJh5m61cgDDg2/zQAdxp3JvZk2miL8b6QH1wDbdfng8svEnz2XxE4Tve5rjv9jbYdjaWZMwnItPf+hGFIw22kPtuSJHG6ejpiMkXG373eYrIsJHNpTKaOj4RELk7ByWt8/1+6SHvH4ff/9WvJBtH2BDCroTHdPkQtf8CkVy/b+Btg6lCBnfUOVvRdZYHcfq0GQYBy/HG48h/Ajw4WDIXt6H77u7tcmLmQsJH2U69uvsqUMcVSMe1ALcDAJZgkDKIcpVju3qec05BrDuYhCaTumNpbYRiy3l5nLj/H0rkpnI7HZkpiVlEr0nSbfUzY2DBZ7wOZxL3Y68kU91szOfEcZh24xDXSk8mzRzOZekCmjpwtlwOozuewWx5W08003w3DkOd//Q6B4vPamS9x5Jx4HuqVMxx+1/cOvRYQnkzAoC9TNG7tcIJ0OWDLO4mEl6TjjVtJwpyuDT4nag48qwuAznQBUNsb3/hbkiRhVu34iVGxtU9fpqbbpOAK5uix89O06nbqwRXsh8kkPJnuPHcDgOPvH+6N1FtzR0soqszqzfHWbWV2FmV6GjUCmQxVwb52FWVqKtkzZFV8kHpQX6atlSZf/bXrbC6ng60d1yM/RCrn+wE7653E4663Rkn6tNhDyNoDitz4Q/Hfk9/T/bc9cjkQvkwbHTGPVX/kR/BklbnNLSpzOaQUieTdxl2OlLqKmDAMeWr9OmuPXUTqASI1XcHIq7QeQsKc6/ioqoQWkRmGpuvR7ePSjb/TmUwD9zMq/xGT6VG9FUuKQSYPlhvLA6/HpxWxJn9SXyZ3eQXt0CEkdXCi+9obm9iRV0/QGg4ODZPL1awaISFzuTk6Xgc32N/iZ1+/jjIzw6aaT2RlIORysAdk2o2MWfcwmYoRkwlESgJ0JWIPTC53ACZTXhsulxsl63M6ngCZGv0gU5Ynk5DLqYkf1STG36MWYOvyZbGgL47BLumpMAy5vHmZkquDIlgj8YleR0KwF4aUnC9geHai/R+4Lu/BejJZSUR39/se6nHhWVCJNjptATLtNf7eD8hE1SaQArbvDf5N2/NHAiOxfEExxEY2ZjLVJJ3wIRl/x9W0PIqRZEs3FbwRJ4h7mUxVpU1OcgjHYTJJkpDM7eMzxcDlJEwmU5tMLgdiYxKEQTfW/gHVSl3MebFcTgk0JLV7cjvsmfbW1pDLZeRcDlVXhrJm1VlBt/c2h4BM3nCQqb3rsLtpsRAlC5lFjcANUMNuYAH3Xwbg2NH3AYKyP6kn03pbjPcsJlPJ7YBjY736avJcd3Y98inJctLsGbQEZOq/P07g4AVeKpMJSECmsLUpxqeyh1VTnINWiidT1PT2+kcsnCjznh96jJsvb/LKH4i+wfItTMXE2gAlVNkyDwYc9zOZBkGm6cU8QRBi18SakgVyx2w39fwHhE/dLWFKm9dUthUxT8cJc17o7VtG+urGqzw592Qqa8ByfTS8yUCmiMlE4z5VXUFr+kwfFdcbj6m9VbfruIHLQn6BpQg0SfNlKuklQsK+9SNNLteIfE2KKXI5RVKYimSMo+LYR3oyuZ2xPZkCX8NW4k1r+hiLgZr6erunn+h/Xm6+tMnd12p0nrnDy81vMXusCKHN1sw5zDOjwQmlGoFM9T3X4O3DkwmouUfJqWsDsqVRVTWrzCt5wWTS93oyGeBaXNq6hKEYnJ46nbwUp5iaiplIU4eVqSnC+LsQM5n212c3nAY5p0ShonfHaIZkzvMnYzJpiozrB9y71SZnbTH91OnRPxSVosrMHSuxNiaTSZIkjLNn0W8LQMtQZayr1zDOnh3J4EwsIfYBMoVhyJ0rW/zHn3uJ//fvf4OXfu8uL3xx0CMPRJ8/zFZjd6NDEIRMHUpnMg2T9OkReDVAPLj5JSgvwUxPwmYGyBTPY+rMDC+efhbTcimUBveKYRhyp3GHY6Vjyb+5d+4w166xcnLwkLlQNR4Ok8kJkFS5CzI1R4BMw4y/Wx5GryfTCONvvyaekUdMpkf1lqoYZNIzQKa9crmJmUzLy5mm3199c4vDh8RJWNAevrHP6UqmxCuWyh0vHweGnJaNKPv6GxhnztBx/H4mU5Rc1mv+3ZXL9TeNRUNlVw5B6oJMcbM8kVxOLWR+jvg+7MeTqet3NEQuN+Q67Rhkau4BmTLlcgJk0qINzVhyuXFBpiuvYV64MLEkY629xpa1hemZBEqY3BNTNekQiMSaIVp5OZcTIFPGxt7yH7RcTtxbs6dhjZv21DHiWlAR5oi0NymZWlf+gzhJnNSPCUDXNVr5WjqTaQy5XH2tg6rL+KpgUuTUHKqk05BUws7DZTLFjDoQzWQwDsjU8xzMBALM8ApjApq56r6YTLYfG0FPYvwt5sZJ/B3ihKEH7ct0vx4xmaq5hMmkanLPid4w4+81tAUBxGi6nMmOBNGUAkMT5twR8dexLCKOr84VI/+oUOqCsquvgllhfvFt5NQct3dvi41b6CeMmFG11hYMrSyQqRiB2u1vfpOcmkMKJZxG0L+22F2QKUsuF6eFZQHIZ6bOULfrbLVW+6VycRUXUuVyMciUz/eDUk9/+Cgnnpzha59/g/Xbu4lMuHFPzPGbkSx2v9XPZEqRy0UbIyc6sc7yMYuBSPXC94JeSiRzBUNh1xC/I06YA8FynbSaTpMbOzdSpXLiswTokkc4iUS5MA+SDI37HApVJODwSSEzihkAeyvetM3l5ihUDKoL+VRfpnj+75XMuV4MMnWfmca2hSTtSTlEGH9XjSo5bbTXGowjl7PHTpcLfA038njKZjJ1E+bidbO3n3Adn6987jrThwucet8UdbvOG7vXMVtvUJt7AkkZPf8qZQGwBbt75nm/15Np/Hm84cxTVZcTsG+SOquWuGrowi+utzTBZLq8eZlz0+cSb8wwDPuMv0cxmYCIyfRgPJl0O0e+rFNdyJOv6KxkeId5Qdg3HkeVrsq4bsCGVWJOr/UxXMapxVNl1u80Mg9N95Z57izmym3kMECXJezr10dK5YCeMTn+d+27Aa997T7/7n/7Br/1cy+ztdzkXZ84xaln5rh7ZTtVFtuy/b4Qo71Vuy/WnjQmkzlizY6ZTK7Vsw4FAdz8skiV6+3LtQIg9YFM87n5PkbmHz7xQQJZJ1xbGfA5q9k1Wm4rMf0GaP3xcwDcOT4IMhWnjIfjyeT4uDJokbfcSCaTmi2LtNsuZg+TSTNHyOUeMZke1Vux4kho3YNbO3cHXo+R2kQuNymTaWUF7cggyNS0PV6+W+f8KSEhGMVkGpagFINMJyonxO/ehy9TGATYbwiQqeV4A55MAN569/SwVbdRNDmhOMZVMFRCCaSSllCAY2bVJOlyBa2A5Vt4weCEEzdL+0mXi+VyrX0bf3sYMcikaT1yufTfJzb3ykRMpjhdbhg7I7Bt7OvX9y2VA1D8HJ4iUYi+65yaoxMGEAYw5DrlfB4lCPCd9PdYnvVAjb9jqq3ZAzwMbdo9S5wkAbQ2KZoqTcdLmpC2294Xk0lXdHbya2ylMplGy+Vqa22qC3mcKApeV3Q02aQhq4QP2ZOpD2TS5JHecrbXDzJN+WKjaufGBJnMyr58ppJI+4k8mVSCcDJ/hyPFI+TU3ANPmLsXMZkOVczE+FvR5ISZNcy7xVtbR50X64EywpNJLpeRNC1TLheGofAcGXIfV2/sICsSc8fEhjtXFHNULuiRy62+AotPIckyR0tHhVwuZlyMKatab6+jympiktxbtucLJhPQ/sY3yGt5TLcIIameTMyczpTLxXNBFoCcmH931jJApnS5nBdtInqZTCBO8j/84+fJl3W++M8v4dtgKibbyx0cxaKmHQw4jtkVpqSm+vXEPiJ+3UFTJJSMFCpvU2xk1MXD8PjH4LXfAs8hpyvsRgmbfq3GYmGRaXN6XyDTla0rhISZpt+266PjEk7iyaSoAmhq3GcuWmpOnRHspiwm017W3NK5Ke6/UR8IayhGjJdeb6fEk6kHrGtuWxSqxgC7pm7XmTKnhm6ieiuRy2X4TE6SLuf7Op46yIzordKsiSxL1Nfaqdf4whdv09i2+MBnzvL0gkjA+twrv8Kh9WtY6hytndHPthyBTINyuV5PpvHmcavpYntFZtWb+5JQnZNy3NDUQTsC1cTzOry2/VqfVM4LQsKQPuPvUYcUIqWv68k0aoOdVQ2ngWqZ5MsGkiSxdHaK5WvpvkzeiIOCvaWrMjNNG082OHR08h5s8VSFwAvZSJGYppVx9iyKY7PY2kJZu0fY6WCOMP2G8XrcuKyWy/O/c4t//Xe+FsmT4UM/9gQ/9r+/l2f/9AlOv30eq+WyfmsQcO243tDD6O1VsWakSTRzIw57FVVGVqR+4sHaq9DZ7vdjAiHHNkqDTKbOevK9vzl7HMco4r/2Km98+COs/cOfofPyy4LFtCuYWsfKXSZT67nnqBWmWK8OHt4UqsbYBu6TlGf7dADNbQMhnVFMphio23MPfTfAc4I+TyZZltBMJdP4269HTKZHINOjeiuVbIqJWHVM7jRSQKaWCz0nWd4ETKbAsvA2NlKZTN+4uYUXhDx9VnicjGIy5Yd4MiUgU/kEwICh5Tjl3rtP2G5jnDkdUUy7AI5SrYKm9cvldhwKFX2ARRNvZMO8wu5mP5NpUk8mSG/K2gfwZIq14FmA3Sg/oZjJ5LdaqNUqSgwyDTP+1tXkd44jZcyit/ddx7Vr4Hn7TpZTJJkgyOHIcqJZz6t5rDC6L0PMv+W8OHENO+lj9kEbf9sJk6lHLjfKk6kwK3yZ2puUDJUw7MbZNt1mssmYpHRFZ7uwyu5mZ4DRaLuj0+Xq6wJkihthQzEw5BwtSd6XtGySatpduZyiyfje8GSXvUymqifmmI4x3GshKXN/TKb9eDKNagbTSpEVzlTPPHiQacditqhjaopgMvk6mq6gKjKqLI00/lYXBMik6fLQAw1JklBmZjLlcn7PJiqrVm/sMHeslCS4maUeJpPlCabS2uUkhvl4+bhoduNxMaZkbr29zlxuDlkavBbb8SnGINOLL5FDp+AIZlUfeyRO0SrOYUct116mV7zuZYFMj1WFfOENpz6EybQuTqN7yo++h70HKiAkht/3ExdobNs889qfwpANNu822crfw2P0gcKwSuRyGYC4ZiiUpk3CHXcowB0DkcrMLFz8lHgu3/x9CrrKrh55MtXrSJLE+Znz+zL/fmXzFYBMkMlyA3Q8wkm9+sqHYPc+VQvaCszOVhPD3LQaAJnOVnFtn43b/ZvmkiZAmt5eKU7p6wVmG9v2gOk3CLnclDmVzFPOCJBJl3VUWc3uzcZIl4vXOs9T8dW8YHllpMspikx5LhfJ5fqZTDsbHV784h3OvGOBpbNTPFZ9DFMxufLHX2Ammg/vXRu9HimVLLlc5MmENjaTKWYHL6nXM1nmw+psqOBJEjd2bvS/oJrc8Np0vE4fyy5mS8VMJmCkF1lOU7AcH91UkaT9MZm8wKPttpE7Gvmy6IePnJuis+tQWx3sZdwJ5XK6InO+LsbEsXcen/j6YlbruObfxlnBCD6xu0rwZpQyd+7c6J+L1+wh6+HORps/+rdX+Vd/+6t8/T/cYGapyJ/5a0/zmZ96J0+89xBKNK6Pnp9GkuD25cG1UDCZssdgbbVFccpAN1Pm9hgIG+HL5PZ6CN34kvjvyQ8OvnkPyDSfn8cLPOq2eNZcL8AxSpTf/XbMxx9n+7Of5danP8ObH/4I7f/zn3HqfsiRovBkCoOA9nPP8caRx7G9wT6uWDVoN5zUJOSDlGv77AYSEiG64o+Uy5kZIHzsZ9bryQTC/NvO8mSq1ZHy+cRH+TupHoFMb+GK5XKqncvwZBLmZLFe1B0RK99b7r37AOhHjgy89vWb2+iqzJOnBTNgHJApK10uNiyP9br7YTLZ16NUiNNCLtc7MUuyjDo72wcytXftAT8mEBpcVZZwcwo7623CMNyfXG6IUeZBPJlkWRg4tu3Be+kHPl7gjTD+9tBzCkGziTI93ZXLZYyLxJNJGZ/JpMgSuioPXdysywcz/T5tLuFj0Jbl5LsWTKbovgyRXMr52HQ0nWL+wD2ZvNiTqTvVZo6PMBT+FlpOAE2tLUpRAxGzM/bryaTLOlu5FQgZ8GVyvGCoxMt3AxqbHaoLeeyg61FmKCZtWUZ2dodKFA9arT1yOUIIhiSD7AWZyq4AmVpjg0yV/cnl9uHJFI/fSc2/44S5BxmjfK/e4VBFjP2YyaTFclRNyWQ8hJ6Ht7mJGsnlVF0ZKVlQZ2Yy5XKxiXEWyOT7ARu3Gyye7KYPxXK5fCgJT6atN8SGcVEAB0dLR1luLuPFXkYjEpk+9w+f51v/3y3WO+upUjkAv9VGCQNyTz9N2OmgX7uTDjLFTCajQkNOl67H614WgDxjzjBlTPFG0E4HmQrzQgbY6Zeu+FYEfKaATACHT1d518dPcnzjSfLXl9hcbrBdWMULDnaSnMjlhgDiU4fyyE1vKMDtbWwi5XLIhTyc+h4BAF/6PPkeJpMX+V1cmLnAm/U3x5IP9dalzUscKx1LwgwGP4vwZBqQM2XU7u/+Lm98+COE+QVorJJv+qxrYkz3GuburfVOVy4HsHQ23fMm9u3q7ZUS4+89TKbS9GA/sG1tM2VMdRmKQzbLIEDhYT6Tk6TLeZ6Goati05ohlwNh/l1f6wzI77/6a9eRFIn3/rBg9mmyxhMzT3B82abYXEYzZJYzPIJ6KwGZBphMYn2zw/E9mTYjkGlBu4nTnrx/PRfd/oFDA83kMuI56k2W6wUU4wOxUWM+9mSSZAkjr+3Lk6nlttD9HPhyEpqzdE4wM9Ikc54/uVxuyZXJt1eZfudTE19foWpQnDZYvZE9rnrLOP0YoSRxunEf59o1kCSM06N9oJKDoYw1u77W5ld/+utc/so9Tr99ns/81Dv5+F97hmPnZwYOts2CxsLJCncuDYJMe/cyA39ntZ0qleu9xmFsK81Q+tehm1+C2bPd0ILeMkp9oHA8R8VzmeOFSAEUnzjD0X/6C5z96lc49A/+AfqZ01R+88v8w1/28X/kv2P9H/8f7P72b+PX69w8dj41dKBQNSDs2po8qPLcAFsC28ihS+5INp+mSAKQ3bOXiQHavexgPadmezLVa6jfgSwmeAQyvaUrlstpjsG91srA61bLxSxoqNFENQmTyV0RoFUak6nWcpjO6+Sqka79gHK5klZKkk72kzBnXxenENKpU3hBODAxq3Nze+RyzkCyHIhmqmiqWIaEYwmkO2ZuTGr8nfVZOo6PLGWn6Yz83YZCOwXAcYLRBuVOx8PQJfB91Jlp5EjOlyY/CsOQliM294kn0xggE4CpykOZGdblyyiVCtrSGGlfe67pyuYVLqgC+GyhJt+1qZp0ApcQhpp/xyCT1E5vyjp+5wF7Mg3K5TKZTL4LhEJ6kJ+F9mbC4IkT5prO/tLldEVn3RRsx617/Y3wKE+mnY0OYSi8MpxozOiyjqnk6UgSUuANZY8dtBqWADsB1KjxHwZi2L7Nwt0Wt//CjxF0OhSddbbDIu1gTLnLPo2/nZ4T5nErt0+Q6dz0OXbsnUzpzX7qXr3D4Wp306KFBlp03w1VztyMeltbEARoC+LQQdHlkQcayuwMfgaTyQ0G/WV6a2u5iecGSWw1CFYOQEmSBSC7+qp44VCXyeQFHveDCFwawmQKgpCN27vceHGD9XY2yBQ2BBBZ/NCHAJBeukLejUCmSgqTySzTlNNDOEYxmSRJ4vTUaa5LHuSnB99QjK5xjy9TEH0PaUymuJ7+yBHuVl5H/doSnhNQK67jhQ+IyZRhZA5CMqc0vaHyUm9rC3Um2pypOpz/OFz9AgUlwFJ0Qk1P/C7Oz5zHD32ubl8d+zotz+Kl9Zcy/Zggin6X3C4LbkTZr72Ou7KCr85i12uobZ8VSfiu9Rrm7q319jrT5nSy3ubLOtOHCwMb+BiI3HW6m769xt9hENKs2UnoS2/V7H4m0zhS3YJaGCKXGy9dTkLC8RWx1hjlTLkcQGUhz856G0OVkmu8fWmLmy9v8o6PnqA41f0uLs5e5My9kPa0ydK56UyPoN6STRNJ1wc9mSJw1GH8dLntlSaq5pGXazityb3MjjsOepgCMqk5LkkuRa2YMP2hB2RS5aSXsEaA5rmegAkjr2Ltg8nUcBrkHTHnxv1zeTZHccpIB5mCgMd//zdY+Z/+RvKMDitdkshpVWa8+6j7NEhePFVhbcyEOTmXozl7iJONVexr19CPHUPOjT5gNEcwmTbuNAi8kB/6m9/Fh3/8PDNLw5nnxy9Os367QXu3f85tu16mJ1MYhtRW24nsOPMah/RJuql2QSbPgdtfS2cxQSqTCbpJmWEQIIVdryelXKb6Q5/k2C/+Iv/uH/8Av/pD0xjHT7D1S7/Evf/5bwFw98T51NCB+IDmQfsyubaPA3TyJfSgM5LJJElS1Pf038MYoN3LZNJNJRNk8ur170ipHDwCmd7SJUkSoaahOQbb1sYAZTYGmTQjZjJNAjIJ0EpLYTJ13ABTk5FMEyRpbLlc2qn7ZmeTmdxM0jjtRy5nX7+OeugQtp6L/l7/w6/Oz/UzmXbSmUwABV2lGeE0OxudfcnlhoFMsZxvUsPruHIZ0sMYAMoCSMIwxOl4aLL4WWVqeqjxd8f1CUL60uXGjYfO6cpIkMm8cH7ie3C/dZ+aXeMc4hSlLmlJkmBOzeET4oIw/84oKWoiJDu9KXvgnkzRIu9/9cts/t//HBiS1hOfRqo5KMwITyajH2Rqua39GX8rOnVjHVWT2V7p/7u2N1wuV1/vGkzaEVvMUAzyWg47/rF9MH/GrabtUeplMpFtVg8RyHSzRvub38R+4w3y9jpr4fT4aTD7NP6Om/9JDGMn8XforTj6+mpt/A31sArDsJ/J5LXRQyMB9QxVzmxYvdVVgITJpOnCk2kYy0qdmRXgVEp1TYzTx+Re028AI68hSVCRY5DpZQEKzIr7FDNl73jR+jIEZLKaLmEIG3ebbO/WWcgvpL+xKZpu/cQJjDOn8b71UsRkCsmXewBNa0c804rGrizu74BcbgSTCeB06QRvaAphLg1kiq5xD8gUurFcLntMOqHD75/+FchFkqTSFv4B5XKxJ5ORkiwX1/ShAnIAUylSxLi8zY0kjRCApWfBaVIOtkXvUSonG9j9mH//3Is/x5a1xSdPfzLzPbYn5HKSOh5I7UfjItRnWW+Ia1+RAyw3GDDM7a2N9sYAoLl0bor7b+70zXcJk6lXLuf3A7OdpovvBZT2gExe4LFr7zJlTo3ltRZXXssfKF0uZgjbbtADMg1jMuXF4VdbXFvbcvnyv79GdSHP0x8+2vfep2af4sxKiPf4SZbOVtlZ74zl6SJXyvg7e5lMYtxaaGMfFmyutChUPSQJvGZ2YmZWqXaDxyQzBWQyuKQEnJ853yfXtXtApvGZTN352yjsj8nUcBrkXTH28mXxfUuSxNK5KVau1Ql7zKuFr17I4VefY/cLX+Dmn/0RrNdeG/r71R0XZJ35dEx/rFo8WaFZs2nWRifuAWwvHuPEzn3sq1fHkspB75qd/tzEyWhZANDeOn5RzBF3rvSPnfYQuVyzZuPafmqyHHSNv4fK5QwFJwaZVp4XCoC9fkxxpXgyQY+/XDS24kOp3nozWOP+9zzBsX/xzznzlS+z+Pd+moWf+jvY1ZlUgDuW+D5oXybP8XEIsQsVNLc10pMJRC83ADJFAK0xEZOp/h2ZLAePQKa3fIW6gerqhITca97re81qeQJkiiaqSdLl3OVlJE1LjLP7fq/rY2oKkiQh5/NjMJkUvCBMpUZudjaZzc12E1P2JZe7nvgxxX+vt9S5Lsjk2j6O5acymSBKmFPEYrlfkCk+XUplMrleH6tl0iroajeiu6dGXadr+4QhqFIEMvXI5dJMemN5VtFQkt85jicTRKdmGYtbYNu0r76Of+bEWL+rt+LNwwlfjJXtUE+YILHErSNLounNKDkvFmXZHnyPF3i4gftgmUyejyJLNH/nd9j8hV8gDILsdLl446uZCZOpZIqFrGl7eIGH5Vv7ZjKFUkj1UG6QyeQON/6ur4nrjJlMEqDKKnk1jyNFz/RDApnCMExkm0DiZTDM/Nv2bIxoqDp37mBaJYVGEQAAIABJREFU66yGU+P7HpkVYQw9ZByl/91oQz8BkykGxLPSN7MqBpkelC/TruXRcnyWql25nBYYCQvWGCKXc9cEsKFFnkyqLiSNw4BAdWYGb3s7FYgaJZdbvbFLoWr0baBlWcIoaBQlWcyPq6/C/BMQsULi9NI7brSpHAKYt3fFa2EQUqrNJw313pIaoulWKhXy73gH1gsvUXaqhDkPuffa7V2I4uJ3FDF3eXvGYgIyDQGQT+cXaMsy97UUsCNhMvWzZEInwJdIvKvSyvItOnqD4sfqPP7eQ7QLDQKcA0kxLc9CC0Ex0yVo0E1FmvGzDxv8zS2U2R55YEF8FyVPAEteqYxfE/97Pj/PbG52bF+mb65+k1+58it8+tynec/h92S+Lzb+lsZkMgW7YlwE+hTrrpDfrCoBDcsdMMztrdj/q7eOnJ3CcwLWeoyB03qlxKcnGneNbbHJ3stk2rF3CP9/9t482JLsru/85J55t3fv22p5tXR119Ld1VJLSC0hqW0hkA2GwZpgkWDYBHjYg7EcBAMRNnYYjMNgNs+MhwGDWYwGEAg8ZiwQ2EISjdStXd1V3V3VXfurevty99znj5Mn75Z5l1fVDvdQvwhFl967L2/evJnn/M73fBdialYt9VrzwsnjYq5cLgoh8qdKl3N0BzeIRP9jV8bOGdLMOEwkM1uf2WZ/o8Pfes+ZdKNB1putcyzVYeVL38HKuWyJYVZplTnC/WEmkzT+NqaSPcdRzM7tJqWFRC3Qmvy+I+U2OKuXRhh4nm5ySVcHpHLQx5jVep5MnXD8XGUZvY0/u3gwJlPTb1LwEyZTpddnrpyt0W35A6EiYQI4ma0G9mteQ+z7XPumb2bvj/849/jGDcECW340B9Sfonq+TNNJ5raXjrPc2MK7cQNrCtNvIDPxsL9auy66paU2JZNq8VgJp2JyfUgy1x4jl9tLPLBqGabfMJ3Xo2FpvXS5Kx8VPmkPPJn94iGQadERwJiUyymJt1LWZ75Zv8nxigCG9VqN2rvfzfy3fAuWrmX6wZVeQSaTS4xXqqC79amA1iwGd54nk+noeN3s6x3u3mcy3a9XaSmmieGLpnPYlymVy1nSbHR6kMm7tYpx9GhmjKgEmQDUYnEik0kmqWXt1m93tu8KZIqDAO/ll7HOnEkXasN+R/rSEuHeHpHnpckjeUymkq2zo0SgwP5Ge8DoeNoa58k0SWc9qRwzG8CZ5B0l0XdDcH3Q52toYT6TqZ0Akv1Mpqnlcka+0fvqlS+ihhH/RZ19cXxh6wK6onPEFff7TqgMMJkAOoo6lVxOy/Bkktfwnnoy+RG2rhL7PnG3S7C+jqEamKpJKxhq2iWoodsjnkyNrj8xgWpcyfti7og9kjDnBtFY/4nd9TaFiikm0TjERE18Ogr4qgSZXhnzbzeICKK4Z/w9gckURAFBHGAm/YN/4wZWe531uDY9W8hOmoEZgbMek2l2udzULKukymaZo8WjXNq5NyDTnX1x7x2t9phMRmwOMJmyqO0gkuWA1PhbAhrDbJ3+0hcXwPeJhhd59Et/ssGHtSv7HH5wlCHjlAyKsUKz68OdL6Z+TCCaYkd3uOEl9+kYJlO/bOFI/aFcuZzalCBThcITTxC12yw1q4SFoWN364K5AeyrcyiEBJ3B10ySywGcMcUC+qUsKZsEmVqDIJPix/gTbkc3uRblEzpf8e2PoGsGihLMlHg4cszQxY4Ri5Ockrvw1SAfZAq2tgaZTAnIVAzEYt4vllMmk6IonF84PxXI1PSa/JOn/gnHysf4R2/4R2Nf2/UjDAIUY7oeQDKZIr3Chn8GsxThqgLIHTbM7a8saebRs1VQBj1vLM1CV3UaXm/RN8z+ayYg07An025XHGfeFmw48VxPIZczipn9TPocTZEuZ+t2L2RiglyuuizmaXfXpRTB/me2ePB1S5x4dNSPzLwh7vmFx76ExZUSVlGfSjKnzc2NejIlPcBc6PDiX9/h2rNbbK826bb8TGBwf6tD4EWUDslAkYOBTOfMeba722kQDsCLcZdAUQaS5WDQ+DtlMk3YEHH6QKaDejLVvXoqlyv2bdJm+TIFKchUp/Alr+fUB/8Q5/HHufNjP87aP/9J4ox0X2W1QbG5iv347IEwshaPl9AMlbUpJXPrS8dQiSGOsc5NBzI5E0y1m3supao1NVNfURVOnp/n5sUdouS7jaKYjh/mplDLZLl8JtOUIJPcsL76UTjyODg5bJshkMnSLOasOTbaAjBX5cbQEMjU8Brsurspk3jgkEa2BN8q6miGmjLC7kXFcUzgRbhxjFeew2jv0WlmP9Mj5zg0PrqtxJOpMMpkcu8zmUZq9hz1+/XfV5kWhi8e7FvNUZDJKupiYabMyGRaXc2UyoFgPkg0Xy0UiCeATMU+35HqEPAumUyO7qAq6sxyOe/GTWLfxzpzhvVkEVkcGpiNhH8bbm7SbotBOVcuZ+nsd3xKNYv9rQ763cjlhkEExu9OTFMFU8tkMkkAKO88JY3TQHyefrlc1mJQMpkGjL+jGUCmnMnN3RY7H3/V+gJf3bzN0dL0vkwXty9ypnaG+I44j3oUUbB6nkwgmUyT0+VUd3QCk5TzeyqXCwQgK5sq7+pVjCNHKBiFDCZTQvHWEyaT16CkievY7AYpyHQQJpORxG+XDhl0nvboNDycskmUMAzHejKtt9OdZTcOMRVxzUtWkTCRX75STCYpExyRy+UsjNLnILmnvevX0e0t1pmnOAuTCcRnKh+e+lz7ZQzTlhwLZpXLAZydP3vP5HK398S9fyTxZJLpcimTKcObQFawsQ6GkTZRutmXoJZzq2oLAjgItrZGdvjkIqoY1WH/Fsz15qHWvktju8tr3zE6N9klA6fRxeysiSjmI4+nv1MUhRPlE1x3k93iMSBTJwGZ9IIAmfLkclorAZkqFQpvfCMAlU4Fvza04OtjMtX1GmXFw28OznNNv4mlWakfT1Y9lEjtXgoa/O3hX1oVMW4MyeXwI8IJ000qbUuAaEO1QG0k8/zB5qpu0MWKoxRcyyq7aODpUMmZVmLfJ9zbQ1/oB5kEyOB4O8AibqFCuN3re84vnOfjqx+n7bdTxmhW/eynf5Y7rTv85lf95tjXgRjDTQLUCebWsiSTKVYrrPslqksdaKrUu/6AYW7N7i06/Mhnp7szAjLZRYPFYyVWX9zlia85BYh7uWJWBnqllP2nDzGZhtLldl0BBFQTIN0c81z3V9Eost5aH/1F/5w1piSTaU8ymYwybF/Of7+qiW6qdHddvqxjQAxv+4ZsU+ZwRzzT+uIiiqqwcqbGrWlApkoFf2PoMwUucQxPthb4y98ZHFt1S6NcsyjVLIo1m1LNwk0kN3Mrc/AiRO0DMpmcZWhf4tLupZQl8lwovt/Hag8PvLzf+FtuiMlnOK9sQ6Wb/J1d0FM2xizV9ASTSdWVAY+3yoJDZdFm9dJuKmUMohgjDNC7HbRaDX1hgRO//mts/NzPs/Pv/z3d559n5Rd/ESORWIdBhNsyOLz/EuGDf3/mc5Ol6SrLJ8qsT5kwt7bQ85y1p2QyWRMAnNZedzD4YYo6cX6BFz6xxvrVOkdOV9P+uZizVthda2MVdJxy9nwxzeaVYSfG324Tbn0K3vJD+SeYAQovOUtstjcJoxgjFoCatGaRdTNJPT9eHpS4AliamqluURSFYtWiNaXkcZqSzPduHBGU57A3twn9iMCLRs554Bwz5XKJT+9wupydLZeLg4CoXr/PZLpfr85SLRMzAEOxBphMYRjhd0PsooGiKMInw50hXe7WrUzTbxASoH4mUzhBLtcztx18ADtBh6bfZMFZmJxgklPuZdGoCCZTvlwOINjcTJlMeXK5siXkaHNLBfY3Oj1DbXUGuVye5w4kuxN3AzJlm6inTCY1e3KTg5+eNIXafG2sJ1MrlcvNbvztGFru7qi/KyjR+3bMb138ramOB2In4sL2BR5deBQvGeTrQTSQLgfQUZSpmEy6NzqBpYlIE3ZlZ6lu4kEhQSb32jVASCpHfBT6G/ZkMVWJRLPUdIOU5XcQkEkuIAuHxPWSsctykh8nl9uVIFMc48UhVgIylY0CoZI80wcwyp6mWn1gJ/QZf+csjORzYHhi0eVdfRmFeEa5XNIMDH0m3wsHfCeG60BMJuNgxt8gJHPX6tem9kobV6t74t6TcrmO30GLdLQEMBJyuexz9NfXMZaWUtZrGjQx5jPpiQQqyDD/lgvm1zz3s/CrXwF+71ldT2QQ/X5MspySiRXB4XbC7jo8GEl/onKCG5IpMJbJlLBXT/ssN0+yYCxmvk5LkqTUuTn0pSXMBx/ECCq49tD4089k0ubRFZdgaGOm6TcnMhQrXpflIOAlNyOVT1EEm2lILqcGMcGEhKfUpDsB1y3NQlH8sRHdk8oNXawoGstkAmjZKiU3+5kKdnYhjtGXRplMlifmkW6hNGAq/OjCo0RxxAs7L+S+58dufYwPXv4g7z3/Xl63/LqJn6XrR5iKjzplupxkMjXdIq1okaU5ca6NhMkEjPgybXe2iYkzWXMrZ2usXakPSCxLRmmAyeQNyeWaOy6GpY1IOiSTqWYJgEssoiZ/zwW9kLlpls5ZU6TLFfRCj8lkV8amyymKQvVQgdVnt3nE19HOz1FZzGYYyzFEWxBjysq5Ko3tLvWt8eweba5ClOHJdMd/lEqo8eQ3nuHrf/QN/N1/cJ63fv1pzr/tKPNHiridkBsXtvn0f77Gsx9dRbc0qsfF96bMyugNPAhdzhbFZtvl3R7w9py/x3wYcsQaZED0G3/LZ3aSJ9OA8XfRwGsHY+eyrGp4DRyvjFMxRlg6K+dq3L68R5QcMwgjykkYiFYV56/oOof+1x9l5Rd+nu6LL3L167+e9qc/DcD6tToxGm1/F5+D98cAhx6cY+NGY2LCKcBGaQHXsFAcB+P4KBCSVZOkaM09d8CYfpo6/sg8iqpw/YK4l/PWMrJ277SoHS7msqWcaYy/rUTedeMTEAX5fkwgxnGvAVHveMuFZTY7m/hhTBKeiWkPjjc3GjfE58sCmTJYQrJKVeueMplk6FU3jgkrVYxk7JmUMGdnsK267QDT0VHVwWtvOiJVd7g3lZJcrXYfZLpfr8JSLQsjDJgzDg2ATCmlLzEn0y1tauPvqNUi3N3NZTJ1/RA7WZSqhQJxa5Lxt/QdGXz/7Y4YUOXOTckozSyXcy9fFtGjDz44Vi4H4G9spLGY+UwmjWY3YG7Job7VSYGVWeRycrGQb/x9d0ymrN2JSecpaZxa0ozo8/MoxKhKnOlv00quZcHU0BU9SYaZlsmk5u6gSJDJWVjiDy/9Ydr0TqpbzVvUvTrnF8+nn6UdR+m9le7oKep4JlNi/D0OZLrX6XKDTKZrQI6Rqt/XsBfEM1FIvEfqfUymgxp/AzjLYmKUkjk5yecBI92WT7fpC5ApcPEUBUsV17xsFUFNEv1eISZTsw/shMlMph7IJO4/76bYSVuL56cHmZxRuVy35fP+f/pJ/uT/+GJKaR957wN5MiUg04xyOYBztXNEccRLey/N/LfDdWevg64qLJbE+NEO2qihjmFMwWRa30ilctDHZBrzmfRkQRhsjwImqRwkaEBzDb7w/vR3a1f2UXWFpeOj4IVdMjCCmGPuy4AChwa9TE5WTrLa3SKAiZ5MmqHirmyjxwbKZjbTxWg3iRQVtShAX/uNbyLSSnSNoYXrAJMpAZk6g4vCptcca/otTmybM57PS63b2b8vLo8wmZQgJpoAMg2D66ZmgepP/7xklOu3seMo/dx5VTeg0I0yZQthcm9I8ECcXAl0G72zjaJA2ykR7u8TJ4ufRxeE1CbP/Hu3u8tPPPUTnKmd4Qdf94NTfZauL5hM2pRyOclk2twWY9aRoghRqXf8tNcZTphbb4vvLRNkOlcjDKIBn5mSWaLh94FMqVxOfNeN3S6leXtkEToilzPUTF+U4crdAJyRydSV0uwJxt8gJHPtPY89NSI4nb+xEuxsg6qmTIGVs9P5Mqlz2Z5MF9vvxFdiHn3yKIcfnOPMGw/x+r9zgifffYav+t7X8I0/9ka+8189yff971/Gt/2Lt/At/+xLKdXm8GMNdVaQKWGj1QrLLDvLA75MF9xtzrseytBY5fVJI9O+Z0K6nG1odAMRxmAVdOIYvO5svkwNv0HBr1CsjD4HK2druO2A7Vvi8/hhTEWCTEMyocrf+3uc+r3fRSsWuf7e72Tnt36ba59bhzjiukEmu2WWOvxghSiI2byZL8eU5QZwZ/kk9iOPZFqDZJWhKahKNoATRzHtPW9mJpNdNDj8YCX1ZWqn/Xe22Gh3vZ162mWVNaXxt++GcOUvQTPh+Jfmn6DcLOhjTy45IinTjyLMuHfM/pJr0iyQydTye4pi1bqnnkxSxePGMVGlipGMZZMS5ixdG/me3bY/At5DL8F1mM0U7opx6D6T6X69Kku1LcwooKwfGpDLyYdHgkyGqU4tl/NkslxOxHx3SC4Xtsezj/rlcv0l9eey8SoaxZnlcu7lyxgnjqM6Tnr84lDCgZ7I5SSTSdUVrGL24F1MmUwOnYZPpz1ehpZVlmahKVqmh0HbC3GMg6tUC6ZOK+N7nGT8nTKZ5MQ/L5pMVY0JM+Vy4j1KlkjCMzVzeuPvHCAMSHedv+4N30E37PL+F96f+brhkouGRxcexUsYeZ7Su7ek2fpk42/xOst3CYaaGUk5d7R768lkDcnlgAlyOUd4MgFqZ5uSpdPsBumzUTQPZvwt3liwG3cSJpMERvI8maTpd+1QAYIurqJgKOL+LZkFUKCrKK+4XC71ZJLG3xOYTHoyFoQ7+0SBIjyZZpbL9RYMn/jgSzT3XG5c2OYTf3wl88+8A8jlUlr7jMbf0Gf+fQ98mW7vdTg8Z6Mlu3Ntv40aaj0mU0azJStYXx8CmSZ7MmmJz06YkTAnFxkayff11C9BKK7P2tV9lo6X0/ugv5ySgRbEPOC9DPMPjrBoTpRPEMQhd3S996xlVLvhUaiY7FTFfLp9NXs8MTstunYhXcgrr3kCAL01lB7Wx2RqajUMxSXoDnkyTcFkor3NaT/g5fp1wijjXi4dgubge2tBTGRMAJmScU+yImzNRlGCsbvgk6rjNbHjeCKTaU+P0ULSzZ/+CrZEfzDgyaQoUFhEaW+LEAyrBFFElHjrLBWWWC4sZ4JMcRzzU5/8Kfa9ff7lk/9y6jldpstp5pRyucQQfmsrRCHiiCnGi0Y3SE3kpWGuLMlsGjb+Bjh6poqiwNXP9/6mbJSH5HIRhqak92JzpzvixwSw44pNHimXGwce95f0ZBoBA+XGyBSeTCJdLtmgtMoQemMZhVKi/RHHZ9xyM9zeQZufT0GC+aNFnLLB6ovjAR+tMkfUbBL3GZ+7nYCXu2/lRlkZK6MB0DSVyoJDqWZhGzr7FFHdGedBCbSZJc7On02DHFp+iyveDo+53shY5fV5MqUM7onpchpxLO5luSbotmYEmbwGJb+auUF7LDFclzLFIIqYS5lMo4tr68wZHviDD1B6+9tZ/Vc/z7N/cYXlzc/zfPUwfnDwwAHoN/+e/F24QciffM33sPKzPzP18RVFyQ23aTc8oihOzatnqZOPLbB1s0lr3x3LZOq2fDp1b2x6XcpkGkMsMGzBvImufByOvxnMMbJhOY73SeaWC8tsdbZw/QAzRy53o36DRWcxU5JsGdnG3wClmmAy3U34RH/JXsRXgGoVIyEzdJrjN86zjL/dJFBruFKQaQi8lWse/W+oJ9N9kOlVXpptY4Y+RXWZW41b6UMpNdcpyGRpUxt/+wnIZI5jMvXJ5SYxmfLkcllMpoPI5awzZ5Lji8/nDPlIaPPzoGkEm5u09z2KlXxTvrKl0/QCKouioezuJOyEGUAmRVFyI387XnD3TKaMBekk428JMmmuGFzl7pKmxJkL9mGZkqmaMxl/5+2CR3v7RAqcPf46vvz4l/P+59+fbSg6VBe3LmKoBmeqZ/Bc0NWASIFCcn6pJ9MEuZximkSajh14IztmrwSTSXgy9eRyXiKXK+rFUfnBsCcTpObfTddPX38gJpPaSwhcWCn2mEypxCsnxUQmy2UwmWTjsKcVXjHj79Ywk8kYz2SS36HWN9Z5TY21eD43cnikhoy/Vy/tcvGpO7z+nSd4zdtX+Pyf3+DFp9dG/mzStcyqQhqKMPuC/nj5OI7u3JOEudv73dT0G6DruSixipHK5bJNOuM4xt/YSJPloCdpHDffaHNzYkzOkssFfSCTqsPuNXj+PxIGERvXG5lSORBMJiWGk+E6HHntyO9PVIT56LOWKWQqOdWpC5BpPbpNs7TNncvZ97bZaeHavUY/OiVYNJWNoXvDrafAZcsQTCbfHQTsm96UIBMmXuSlXhcDVRplMulhTDwB9JTG33Lcs3UbVO+umUxWHI/1ZALY0UW/Io1s+0veGwMgEyShCJs4pkbDEtc/2O2xVvLMvz909UN8+PqH+YHHf4Bz89PFlYNkMvmoU8io4zgmTPy2tndg3tmm6Ip+qtH1Bwxz+2sck8lydM695QjPfnSVrYQpUjJLI+ly/WmMjZ3uSLIcwF53j7JRTj36ZvFkCuNw1Punf2NkTPWny1mG2gfk57OZHnv7Cu/8zkdZdZSx92KwvY2ebJqB6L+Onqmxeml37CJVq4h7s9/8+/K1GgEWq9XZejTH1NiPi+gZhu5jSy7arTJna2d5ef9l/NDn4vZFYuAx1x2QC8OgLDvte6YAmSBJkk3WBO6MvkzCk6lMIQNkKlYtIW+UIFMYU076sDyZkFYuc+x/+zfsv+fHCBSLEzc+zAu1k1OlHY6r4pxIHp0mYc4NItyF5VxrkLzK63El+2ZWJhMIXyaAGxe2c1UZIPyYAGpH8kGhaY2/Afw7l+HUGKkcZIJMS4Ulwjhku7ODXB0Ny+VuNm5mmn5DNoAjq1i1iIJ4ItNo2pIqHl+JoVrDSHrpSXK5LBA+l8lkSybT4GcK7jOZ7teruTRLMJlsZZl20E6NHVMmUymRy5na1Ewm/5ZkMmUPvJ0BkKkwMV1OAhWTmEzDjdOkijwP7/r1Hsjk9iRe/aWoqojM3hBMpjw/JnmucQxWoqn2dkWTMotcDvLp5YLJdHcgU9sPR5qnScbfUmKmd+sopomWSDw0JSLMmiyHQCZDM6Y2/nbGgUz7dZo2WIbDd73mu6h7df7g0h9MPObF7YucrZ3F1Ew8T8UwBr/rdEdPM8fK5QAiy8YOvZFdlHRH/16CTNKTyRfXzl9dJXLdbCaTZGAZNhQSiUh7i5Kl0+gGtLyDG3/L+9cNXeZXSmzfbhFHcY/JlLMQ3Vtvo6oK5UUbgq4AmRKPLske29FL/+3lcjkLI/kcaK6PkrDWvJZFS6/MwGRKFsbdPQI/5C9/50UqizZPfO0p3vbuMxw9U+Uj/+EFNq4PNrHuATyZNFXB1FXa/uxMJk3VOF09fW9Apr0OR+d6973niflD65fLZQB7UaNB3G7PzGRSVBV9fj5TLieTidQ4hKOvh4Uz8Fe/wNZN4bORBzI5yVy3ELtEh14z8vvHFh/jofIJfnJxnkvNDJAmqXbdxymbbLQ36C7tcOfKfqZE0u628JweMORq4n47tNoH9IQ++O0UbFF1A5SAYCi8oelPJ5c7rYtmP1MiWToE7W3xnknpIcRTMpnkGOHotvBkuhuQKehgR5NBpq0knXL3ThbIlDCZFoYSxYpL0NqkaGrUE6C735fp/MJ5ru1fG2D6bLQ3+BdP/wteu/RavvOx75zps3Q9IZdjih4gbrchDImB7brGcnUftbWGpirUu+J7kYa5/bXZ3kRX9QEz8P5629edxi7qfOS3nyeKYspmeTBdLoxTkCnwQjoNn3JtdB7b7e4OvMe0nkxpmMnw3DptupzfwdJsgigWILy8L8ZI5opzFufefBjbVMey6sLt7dTjTdaxh2s0d132N/PBF21OnEPUBzI9f+0IVf0mnfJsSyNLV9mniO5PBjYGqg9kOlc7RxAFXK1f5cKWYOKddz0YApC8jHS5SXK5/kQ0uUB2Z2UydZtYfpFCJbvHXDlb5fZLe0ShSISVTKZxDI4wjLncXOHoERXj3V/LljN3V6mWsg4/WGF9ioQ5N4hm2hSSlRdu09wVz8OsnkwAi8dKFOZMrj+3k6vKANiVyXJj5HKGpqKrytieJwVFYme8HxP0Pa99TCZHAOJrrc0ek2koXe5G4wbHytlkBVNTiWJGFAXQu373ypdJejL5gFKtpUymaeRyI+ly7QCrMMpkshIm03DCnJyb/qamy90HmV7lpVgWdhxgRgKokRpYidBKWdhMTKZbt1AcZ9ALoa/6U2fUQpFokvF3jrntVncLVVFTE8pZmUze1asQhtgSZPLzB2Z9eTmRy3m5fkzQk+WoSWqDvysGz1mYTCCYKu1gFHy7F8bfcTyqBx9eKAyX1wlQdYW4WUctlcAQn09TokxPpjRdLjlXU5uNyZSblrVfp+GI4z2+9DhPHH6C37z4m/hh/mAfxREXty9yfuE8hD5uaKLpSQLVkCdTx7DGMpkAItvGCdyRZiZlMt3DdDlpkh95nrjmcYx3/Xp2JHTasNsiSlZRobVFydbv2vhb3r9+6LNwtEjghjR2uul9NA5kqiw5aJoKgSvkcgkrSl7zXbXwysnl3EG5nD6JyZQ8B5obpOOC79WwDWP6RbNuiZ35zh6f/s/X2Ftv82X/08MYpoamqXzV9zyGUzb40C8/OxB335/6M0s5456XCXW2JhLm7oZWHkYxa31MpjiO8RKmjdEnl8tq/oN1Aajoh3oMDOnJNMkDUFtcJMxgMslFlBqHwiviyX8Ia8+y9qlPAWIBkVV2WdyXnahCZ+H8yO8tzeKX//bPUYgivv/6B7nTvJN5nHbdpVARIJN21MPvhimDpL+cbguv0AOG5C72qWvrPQmObMoT4FJTVWIlJvAHv6+ahFCqAAAgAElEQVRp5XIP2mJOzgaZloAYWgKcicIIPQZypLCyhsc9x7BBDdI0qoNUN+wkTKYJcrkoItIVdu+Mjtnh9hZqoZBKnNMqLkJ7m4Kps5uMheHuoPl3TMzzO88D4n7+ib/+CbzQ46ef/Gl0dTa5uhd4qEos7sUJFSZSua69gBdoLB8KUOp3KFtaKv2Vhrn9tdHeYMlZQlWyvyu7ZPDku8+wcb3Bsx+5lWn8LUEmudDNk8sNgkzTezIBGXOW3BiZzGQypRzTUHtA/gRfJnGO44GwYGcHbX6wV105K1gDq2NS5tQ5AVZLX6atW0029qucKj6VJohNW4qiUFfKWDODTMm4YlVS+fOLOy/y3PZzHDFrLETRiKSwf54xVANd1adgMol7o+uHafT6SMKc34GP/Sx84t/Cxf8Itz4N9TuQSHPlgjwXZDpXw++GbNxoEIQRFTdfLifrxU+u0a57PPGe1xK/51tAUdLgh7upQw/O0dx1aU5IKHODcCZ5uyw7x7T6bphMiqJw8vwCN5/foZWMFVkb0rtrbTRdpbww/pkTm735z3bKZNIX4eiXjD+5lMnUu7+l9HejvSGMv9XeJiCIeWWjvZHPZEruyay+Ql6/1u69AZlkLxIoMdp8DcNvA5OZUlkMbpnaPlz5nkwJyHSfyXS/Xo2lWiZmFKANgUwjxt+mhj9lupx/exVj5WimpCxMIs9TT6ZigajTSY03syo1tx3avd3qbFGzamiq+H3RKM7EZHIv9ZLlQMSAK0r2gllfWkrkci7FMUwmyZjoEuNUTMJ9DQUFXZmtMc1jMnXugfE3jEoPJxt/h1iOTtxqoZZKKIqCYpqohJkL9pYbYBsqetK4Wpo1FgjqL8fQ6AbZZq5KvUnD6S1ovvux72ajvcGfXPmT3OPdbNyk4TeEqWtnDy8qoCVxFiNMJt0UzIExFdkOduCONNeyUXMmUP9nKdePsHWN2POxTokIau/aNRzdGQUhZaOo26Cq4MxDe4uybQgmU3I/SQbRLCUTAt3QZWFFLGa3V5s99k1OU50my0GPyZSATFIut63ar3i6nHwuVV2MSZOYTIrroy8vozkqXseZHchxqmxvxnzuz25w7s2HOf5oT47hlE2++vtfS7fp86e/8mx6Lm4gfFGGU0cmVcHUDpQuBwJk2nf3R+Q3s9RW0yWIYo5Ue3HYWpSA0Ml9kZWyAuCvi/ftl8sZyTM5Kd1HX1ggyPBkknI5NQ5B1eA174byUda/cIlSzRqJZZclmUzdqEKj+kjmaw6Xj/F/rm3Sjjy+/y++n/0hD5UoEo2nU9bZ6mxROSk+y+0MyZzjtgkKPcC3tecBIZVmE/fFxMBXgq/JTrChKkQq+ENDactrTcFk2qFQWOJY6Vg+kwmgJb4Tryu+L8WcIJcLB+VygskU0vam21TIPGbgCk+mCcbfbhARlXV2sphMm1upd9dAJXK5gqGym5zzcMIckErmPnDpAzy1+hTve8P7OFk5OfNnCWVIxBTpctKPqV4W77O8YkDQ4ajtUe/0mEzDz+tGZyNdtOXVmTce4sT5eT75/1yh5NZoB+3Um8sPIsw+028gUy63291NN/Vgek+m3MTcaZlMQQdDSdILpScTjJXLybKN8UymYHsbfWF+4GfVQwUKc+ZYkEmrSJBJnMPzT91GVUKOOp85ELulpZSw/AN6MlllHph7AEM1uLR7iee2nuOx5B4a9phMjb+TPtfRnVEZ41ANMJmSBbLbHmIyffa34L/+FPzZj8Pvfzv8u6+An38YfmoZfuExvJvXAShe+QA8/X9Ba3DsTg3XX9xNjb+jQhHFGGV9gBhrP/fhGyyfLLNyroapaQOf726q58s0/v6STPNZK893tLnnoqoKhfJsm9KyTj62gNcJ2EtMy/OYTNVDhYl9hpXDtpKVgkyH3wTahPVNjicTwFZHMJnUoR5ynOk39DbjssYf6Wl1z5hMfZ5MZqGAWnAw1WAKJtPg+BjHMW47wM40/k7u3wwmk2LbaejQ37S6DzK9yksxLawoQAnEJCvNv7stH1XrmRcaM6TLebdWc6VycqHRYzKJaPO4mz/JpXK5oQFvq7OVSuUgSZebwfjbvXwZdB3zpJiMW25IwdAywTF9aQl3cwe3HWRqytNzSM615QZUlxziPT2JdJ5x4ZjhyeQFgkZ8b0CmITO6KYy/TVsnajZRS2JhpJgmGmEOkylMrwWAoc4glzM1wijO3JFS6y2ajpKe51uPvpWH5x/m15/7daI4u7lIqeOL56GzixcXUBIJiPS0Sb0JNGMiyITtYIfe6A7FKyCX6wYhVuLJZJ0VO5Xe1WvZIKRs2OWucHERWluULZ1G16fpNynohRSUnaUsNZF/Rh7zR8T3v73aGiuXi6OY/Y1OH8gkPJnMBMiUYNeuar1ycrlugKr0mmTdGA9gyO9QdT0Ux8aoCE+mcWb0WRVZVT7yxddiFnTe9o2nR36/dLzMl3/HI9x5aZ+P/74Au70DUu8d8+BMJukt8+LuixNemV+re2IRs1IV933bb6NHcnNiWiZTD2TSpvBkgjEgUzJuqNKTSTfhrT/E2u48hw7lH1NuqGxEh2no89kv0m3O+j6/tPQObjRu8MP/9YfTsRPEbn0cQ+QEhHHIoeUFKos2d14avb8Lbpuw2GPqtPZdKAQoxDSfeUb8UC4iE7BF1xQCBYKw97xFcTQ1k4nCPKdrp3lpdwzI1ExApqTZnQQyDafLFZI4+mZGAue01Ym8iUymOE7kuhUjlYD0V7C9PerHBEIuF3SZN322kpCGsM+TacFZ4EjxCBe2LnCzfpN//el/zZce+VK+6eFvOtBnCfzk/phCLieZTPXKSVQiFo6LhfdJY3+AybTV2RqY7zbaGxwqHBo9YF8pisLbv/kcxDHaUysQk27KeWGUMjKaO+J7K+d4MvUzmcwcGexw5crl/L6NkZzyQ58gDtAVcf1smS4HUzGZxno8ttvE7TbawuB9oigKK2dr3Lq0l8vylHK5sL5P6Ee8+MwaDy5dR9G8AwEPbbWEHU5ONBuoVC5XQld1TldP8/Sdp1ltrnK++pD43ZAULk3fTBbpjuZM7ck0wGTq96OJInjmV2DlDfCjV+H7/gq++ffga34O3vrDcPKtRL4Ynwov/x586Efh6V8eeI9CxWT+aJHVS3up8XdUzpY2A1z53Cb7mx2+5CtPinCZ5JrfC5Bp8VgJTVcnSua88GAgk63nezIVqibKjBtNso49Mo+qKjSviuc6a62wuzY+WU6WY6rjPZkCMff6S2+cfGIZINNCwqrd7GxixqAOzTM3GjeAnhfiyCGNfFCxUDFRFO5ZwpzfJ5ezdBW9VsOMPToTPZkG+x7fDYnCOFMul2v8vbv7N5bFBPdBpld9KZaFEQa4vs6SszQgl7OLRgqOGKaa6lInlb+6irmSZ/otHjhbl0wm0XyMk8xZuoqiMLKQ2u5sD4JMZolu2J06xcy9fBnr1AMoZiKT8IPUCHq49OVlOq0ksWGCJxOIxW1lyYG6ObNUDrKZTPLzOzmxpNOUBFWGQSbJ4MgDSNx2gOkIkEkrimZBMU3UOJ/J1L+LMotcTk7aWYt6tdGm4fQYV4qi8N2PfTfX6tf4yI2PZB7vwvYFTNXkoepDAmSKiqkEpCBBVEkb1/WJcjlsByfw8uVy9xJkStJ0Ys9Dq9XQl5bwrl2joBfwI3+QHZY27MlipiBkISVLyOXafvtApt/QAx+90MN0dMrzNju3m2N9hBo7XcIgorqcgF5BB1fpAYSpXE6xXjHj76YbpAmH0Gf8nZNAk96jXRfVdjALXfxdP9dDIa+eq7+D9foyT37jGZxS9vN/5o2H+JKvPMGFj63y3MdWD0y9L8wIgA2cQ02wOO/Gl+nOnrjvpVyuHbTRI/GZpb+SlNVE0eB1DzYSkGm5J5eTTCZ/gpm5trhAuLU1sggMElasEicgE9B66JtpRMsc9p/KPZ6T7B7fCI+nMsvRN9VBUXmTUeWnn/xpPrvxWX784z+eMkKk/NE1xdi9VFji6Okqty8PLlbjOKbod4iKg3I5tQxrVWg+/UnxQ8nUsCTIpOIqGkGoQ3K8TtAhJh7/bMdxAjItcKZ6huv166PjcSn5DhLzb+kNoU3Y1EiZTAm7tJiA3E1v/MJ1/DG9JF0un8kURDFRDFrVoNPwR3aVg62tUT8mECATsKzW2cMAXR9gMoHwZXp261n+8VP/GF3R+cm3/WSuFG1SRQdgMjXKJ5kzmmjVIwAc03sgkzTM3enupH+32d7MTJYbrsqiw5v//oOE1xwe2n59CjL1G383dlxQRiU7cRxnyOWm9GTSE7ncCPu2T+KdU/JvdLWPyZT63k0hl0uY0VkV7AhwcZjJBCLxrFP3UqPk4UqNv/f3ufKFTdxWwCPLF3BjMzdtdVy1tQp22BSAzbTV58kEgpkqZZ6PVRNz+iGQSc7Zcq5xjMkgU3+kvWao6KaK27/AvvIR2H4J3vS9UJiHw6+Bc18FT/wDeOc/ha/7FeJYjC+Ff/hfk95k1E9v5WyNOy/t4XqhYDJVskGmOI757J9dp3qowKnXifveSJh4foZHz6yl6SrzR4uZMuf+cpP031krl8m06x4oWU6W5egcfmgO72YrfZ/+CvyQxlZnOpBpDDgLYGx9HgB/YdS/cPTERkEmQzOYt+fZ7m5ioqRJtLJkOEUek0n2nFnjj6qpFCrmPWQy9Yy/LUNFW1jACNsTmUyCRdmXPpmw/zKNv/Pkcnt7f2P9mOA+yPSqL8UyMUJh0nmsfCxlMrktP02RANCn9GQK63Wiej2XySQfuAEmE4w1/xZpaxqtIZBrq7PFgtNrImWTPU3aGAwmy4EAXvJYQvrSEp4pmopxemnJ3mm6AXNLDmrbwmZ2eVKW5440970XTKbWkFxuGiaTVdAJE7kcSCZTkMlkarlB6ncEs6XLyYkxa4LTGx2aTm/XHOCdJ9/J8fJxfu25X8vcdbywfYGH5x8WiTjdPbzYITLEufWfo6M7dNUpmEyFAlaYDzI52j2UywVCWhr7PoppYD7wAN7Vqz2Pi/6mvT9dDqC4IJhMtk6zKzyZiubsfkwwCDIBacKcm3oyjd6TextJisnhQSZTj/GQMJkwXlHj735GnZbI5YKc5ik1QHU9VFPDLLj4ex1KSji1J1Njp8snV9/O8fJLnH3TeHbBm9/1ECfOL/Dx372EsuXO7McEUDD0EfnrtFUxKxwtHuXSzsFBptsJk+nIXAIy+e1ULqf3pcsBI4mM/vo6WrWKavWeZ9lsTmQyLS4R+366MJcldzbVKEhBprVVcX0O7/4RbGaztnTVR8NjIzqcyiyzX2hD6PJVp76KH3njj/Dn1/+cn/nUzxDHMe26GEdburifDxUOceRMlW7LH/ANCptNtDgiKvUzmTz0Mlw8odD97OeEhHyIyWSoCl1VI4jNtGGX3jpj5XJuAyIfCgucrp4miAOu1a8NvqYoQaZBJpNqjb8nO0EHVVHTxLGimTDaJoH1Y8qNg8T4O5/JJMdfI5mPhxPmwq0t9KUMJlOSvLmkNmh5EVq1Srg3KIs6v3ieW81bfHbjs/zYm3+Mw8XDB/4skQRSpvFkqjeIUWiUT1BT9qAiQKaj2m5q/C0Nc2XwSdtv0/SbmclyWfXaLz+OfTjmbde+nt09cX95Qc/4u7nTpVgxB/xRQLCegihg3u4BMrN6Mo2yb4fmrIySAIiGlb5nlpFwXonQgeyxJEyCA7T5UZBp5dx4XybpyRTV6zz/1G1K8xbHSy/joh+IkdrVK6jE4M4wF7oNQIHk+kpfJoBH5x8W/xhOlwsHN4ZszZ4IMkkmsJzv7aIxKJd75lcEeHv+f8z8+ziOUbpJ4EfFAqcKndHrunKuSuBF7N9qiXS5uWyQ6dbzu2zeaPD6v3silX3dSyYTCDbT9mozl8kmmZQHYTJZerbfUWvPpVi9u03Kk48tEO/5lCIoDAFge+sd4hhqRyb3gZM21sy1pwHw7KOTT8ocBZlASH933S3MeHQz42bjJhWzwpyVfQ9M+r6LNZvWBE+taStlMiniu9Nrwvx7crrcIJNJPjN2cZTJpGkquqHiDqXLCSZTPqPv/+91H2R6lZdqWRihT9sLOFY6NsRk6pM7mRq+l+2T01/+apIsdyyPyTQIMskEp0nm3wVLp9OXoBTH8YhcTjYy0/gyRa0W/q1bAyBTy81PbtOXlnAt0XRM48nUdAPmEgZHzZuu+euvgl4YiaiX7KN7ATINs8Lc0EVV1FzvKK/bYzL1g0xqFGSmyw0v7k3NnCldDkZBpqjbRfMCmoXB89RVnfeefy/Pbj3Lp9Y+Nfg3ccTz28+nPht0dnHjAn4CMvXv9Di6Q0dTwRt/L6qOg5PjyaQp2szGsOOq64dYhmAyKaaJeeoU3tWrKUAzAEQGXVAN4UMD6W5hydZpeeF0Mec5JUEmCUbOr5TYW2vTTRbjWTu3e+vi3KqHiun5eQqpgatkMu2jicV0dHfRw1nV7Aap6Tf0pcvlSDy80BOsj04XRQ0wSwHEcLi7O5UkLY5jPva7l4hjlbcv/d8TZbKqqvB3v/tRyos2S19sUJ1RVgtg34VcDsTC5G6YTKt7HUqWTiW5zp2g02Mypelyg4sUWcH6xoBUDkSzpWpKJnjdXzIRKhgy/5ZyOSUO0mdh7UodVVNYcm7DU/8m83jK5gs46j574SLN7hiQSTNTBsZ3nP8Ovu3Rb+P9L7yf37jwG3QSJlNdEyyT5cIyR0+LeeP2Sz22TGcn+Xe5D2TaczErChdPKMT7dSHnHmIyaapKW9EJYpM4AYPkwn3ss91OrlFhQTA6YVQyZxbEYiA5rmyIdXsyk6lfEl4yxdh0ULlcHMd04xBL0UDL9mMBUuDAWhTgQ3/CXOx5hPv72eEjRdEzLCp12l6AXquOMJnkfPEVJ76Cr33waw/0OWRFM8jlomaDVuEwoWYxF25BWYBMh9gbYDIBqS+T/O+0IJOqKpx6l4PtF7j4/wqQxQ8jDF1l45d+ie3PvpDpx7SXsE2rVk+6IYxt78aTSYJM+ddmGGSyjT5PpmnlcnlMpm3xnGbJKiuLDqWaxeqlHJDJNFEch/pWl5sv7PLIW46ghC7d2DgQ8OAmyY8z+RO6DXEtVPF+Uv78QOUByjJhdjhdToKzUi6nOxPT5YYj7a2C3ltg71yFS38Gb3hv7vfYDbvYbgkswYTCqWV+zpUzNVCgfr3BnNuCuWyZ0Gf+7DrFOZNzb+qBvxJ0cO8Bkwlg4ViJTsOnvZ/dt0om5UE9mYb72ziOae7dHZMJBMgEcCbSU09UWb1kuSlBpry+Io4xbn8cYDqvXk0HozDyvC4VltjpbmHGCvrQZsaN+o1c02/o6ylynu1S1aK5d3BfwP5KmUyI+0ybn0fv7E/lyRRGcZqAJ9l/WUwmAMPRs5lM9+Vy9+vVWoppoYcBHU8wmdZaa/ihn8rlZOmWRhzFRDkyE1neLQFS5TOZkp2QZFGqSbncGCYTjJrb1r06fuSPyOWAqXyZ3CtXAAZApo4fZBrlgZBzuAmTqVDJnwSKfZ5Mc4uisZrrTqaxjxxnjFzOPgA9V9Y4udw476gBuVxZgkwGapzDZPICilbvPA3NmCldDkblcjLFpVvUR87zXaffxYK9wK8992sDP79Wv0Y7aKeLhqi1SxA7uIl0oTAMMinqRCaTWihky+XC7oH8t/IqjmO6foSjxBBFqAnIFO7vU2qL53CAyeR3B1N6iovQ2SMJOqTuNtNmf9aS8kQpRV1YKRJFMe1t6ceSATKttTFtDUeeQJIuZyXgkvRk2ouT7+AVYDMNg52KoqDqSq5crht20UPE9cbFKCcMmMbWVJK0lz+7ybUvbvGmh19iLroy1TlaBYOv/v7XokQxT65NZvAMV8E4uPE3wNn5s1yrXxvwFpql7ux3ODJnp/e9kMsNezJlU9uD9XX0w6NsL91Qp/Jkgh4bQZbfny6XAL7rV/ZZOlFGe+O3wBd/D/ZvjR5w7VlstU47rqTpmNlvbA8kNv3IG3+Er3zgK/n5z/w8n772BQC2lDU0RWPenmdu2aFQMQfMv12ZZlYSc4rvhnidALuic/FEch2f+VQfk0nsZBqaQhOTGI1wX8japmIytRNpVWGBU3On0BQtx/x7eVQulzMnpp8ldAcSNcuWZLRlsyOidptb73sf/p3sdD65GWFPAGXk+GvPmeimOsAUC3YkeJAx9yZyuVq8T9sL0eaqBLuDQMITh5/gfW94H//sLf/srsfz0E/mvSnkcmG9QSNZWM35a2I8t6sssT1g/A1CIgezg0wAh47P8YWjH2Hzcz63XtzFDyNsQnZ/5/20mlGmH9OOK67pgCeTlu21NlwTmUxj0uUkyKQirp+lqwJ8NKZLJbXHMZl2BPiqZzCZFEVh5VyN1Ut7xFGOL1OlwtVt8dw9/NYjEHTpxvqBgAfPSNgKGQyf/D9qDLD9JJPpscXHRMIpjKTL+WGEpipoCQPI1qdnMnVSkKmPyfSpfyeSbN/4Xbl/3/SaFPwKWjG5jnY1UyJvlwwWVkrUbzSpeC2UDJBp/Wqd1Rd3efydJ1L5O/Q8pvx7xGRaOi6+1zzJXM8qYPZ+3NZH/Y68bkjghhRrdwcyzR8tEloqDwXZfkwo9CwMxpQzBpxl8wWMtvBM8rtT9h5WeYTJtFxYZs/bxoh70npZNxs3OV7JlspBH6iYI9ct1qx758nkRSi6AkkwlDZfQ2/tTJUuJ84xAZlSuVz25omVBTLt7qLfl8vdr1drKYlMwe90OVY+RkzM7dZtuq1gAGTq+WSMH1Akk8k8lgMySaPgGeRyIAa8frncdkc0B8PG3zAdk2k4WQ4S4++xcrk5FCVOU4iyqmxLJlOYMpkqnYzd1AlVNIoEUTAAzMgJ/q6YTFZ2upwbumO9o7xOgCWZTP2eTJGf48kUDnoyqbMzmYZ3UeRus1scPU9Ls/jWR7+Vv77912kqEAyZfgNeXUxyHd3C1NV0Nw8kyKRMBpkcByv0RphM3aB7T/2Y0kWUknjMmCbmA8KkvrQmFp8DTXvQGZQdFBaBmEVNfJ7G3TCZ1EEm08JRcZzulgSZsuVy1UOF3iItSZcz9UEmU4Pk968QyDQMHOu6OpbJZCV9g4qLWRL34FJ9cyLI1G35fOz3LrF0oszjj+4k7KzpGt75I0WunnaouvCR//DCRMZof91NuhyIhUkYh7y89/KB/v72Xjf1YwLo+B30UDKZEkmGkb3r6K+vYyxngEymlia65JU06x02/5YgkxIJkCkMIjauN0Ri0Ft+EIhFzPZwrX0RW2tDZE+Qy5kDCzdVUfnpJ3+aJw4/wccuPYWiw4a/xqKziKaKIImjZ6rceannyyRBJiXxdWnti+MVKhZbcwrR4SXazzyT4cmkUE8W28GeABlmZTKZmsnJysn8hLmEySRNTU17svF3v3xZejK1c9gR7ksv0fjQn9L86EdzjweDkujM48jx0dCpHS4OmH8HmwJ4lGy3gUqYTHNxHTeIUGu1ESaToRp812PfRdW++x3kWF6HKZlM7dIRlDii0BHfL+Uj1MIdml5AFMVpz7PR2Rj476R0uf4qmSU+c+xP0eci/vJ3XiD0Qx668xJhvU5HLeUmywGDcrmc1MjhkhsKI1YGwWSWlwRAlDiRy8lNtoxFa1ZZRj4QJlmQmYw3hC9Tt+lnphcCqJU5rncOc/yReSoLDgQundg4kLeeb0qfqQMwmZKq2TW+97Xfy3vOvafHKspIl+uXZdu6PQOTqV8u5wv/ys/9NjzytVDJl001vAYFv4xRTt7XqeYyto6drdG+3caMI5QMBsdnP3wdq6Bz/m8Nvl8qn7pXTKYVCTJl32Mpk/IA/ltZnkxNmep4l0wmRVFoLRgcc1XCoWuxu9aismCPADpZZRsq3by+4vn/hKGIZ9cfN1f2V8bzuuQs0fB3MWMw+hizfuhzu3U7148J+jeu8plMXicYMdI+SAVuiKL3Nsz0+XmMbp3Aj8auiYfZVt12wmQqZm/cmLY2cL5xGBLW6/eZTPfr1VuqJRrW0O1yrCQkbrcat0aYTGlc5QTzb//WKmqxmOrVh0sOWnby8E1j/A2CIdQvl5N+BFkg08huWUa5ly8L0/M+WV/HGyOXW5jHteawtWBs8oOlq2iqQtMV1y8wPEqdnKSiMZVFL7+XcrlMJpOaPblFUYzvhhimQuz7Qi53/ROoSoQa+jnpcqNyuQGT6jE13NDICpOFmZtjpPyec++hZJT49ed+Pf3Zxe2L2JrNg3MPis/ZENezq4xeR0d36CpMNP7WikWcwMX1ByevbtBNgZN7UXJiKpAsmg0D69Qp8bPbouEf9GRyB0Gmomica4pYqDb9ZrqjPGvpqo6CkoKeMgLX2xaNRlajtbve7iXLAbHfSUAmcY00VUPDpCkNdV8B8++mG6TAryzNUAlzGpNu2MX2xfOtxC00K0ItFpnf38xvuJL6xB+9TLfp845vfRi1UIU4EjvNU9ZGWeHKIY1Lz6zz+b+4OfXfZVHvZ6lztSRhbudgCXN39jsDINMgk6ln/A2Du46x5xFub4/I5cTfqbm+WelrJsjliIUn09bNJmEQCZCpegJe843wmd/osXtkrT2LU1RxYmUyk2mI9WVqJr/4jl9kiSM0tF2+sPWFgbSvI6erNHddGgnzz0vkctI8uJ2ATOWaeH79156h/elPE3f3xPslLBhdVdlFvCbYF5+74SdMpqlAJjEXna6ezgGZlqCVgExJQ2w445lM3bA7wGSSrMdOzsI16iTX4Oq17OPJAIUJ3nb9yZZzSw77m73FdLAtQaYMTybDAbNEJRLfQVyupHPLK1FxkGyujJH+yQrrDdzSIWza0E0+T+UIlWCTOIamF6SGucNMpknpcv1VMkoEmo/95Xvsb3Q4difgkaufxzdKRKpBeX60F5Ag06Dxt4ofxiOG/sOlqRqO7oxuAPodIT9V85cSPZCpj8kEAqro4/EAACAASURBVHidRi6XwRqRFexsi37Vzt4cWjknPuutF7LZRbvzD9OhwCNvFbLGOHTpRgfzZAqkDHEWJpPbgCEG4w+9/od43fLreuywISaTF0QDIJijTzb+HmUy6XRbATz7+2Jz6M3fO/bvG34Dx6tgl5Lr4tRyP+fKuSpxGFOvnEIdYjLtrrW48vlNXvNlxzCH5nUJnN0rTyarYFBesKdgMh0gXS7DVFuybu6WyQSwO6dhxLA2lGq6e6c9lR8TSCZTzhz83AdRTr4F3dLwpgyEymMyxUSY9NaYALdbt4niaIJcbjzIJL1z7wWbyfdCSIzlLUNDq81jJGPZODbTcN/jthJPphwmkznEZArrdZEGWr3PZLpfr9LqMZlcjpUF4HJz5xahH2GX+uVy05mx+rduYRw7lksxl4OW9MKZlsk0vFsvQaZ+429pajyVXO7yZayHHkLRegNbe4xcTtF1/NIiNuMnY0VRKJo91pVbrON0ZjRt27xEcUMs+FoX/wguiP9VrvwJX61+kuWbf5r+jPULMx26YPTkfP01jskkBz1DTYxgS0X44+9Dad1BC73p0uXugfG33G32StkNYdks8+5z7+bPr/85N+qCyitNv6VPktsS91mHeMD0GySTiYlMJq1YQI8j/O7g5DW82LrbSnfKkFHipgBFdR1rVSwaBwBVvwPGMJMJ5iLRaHSCg6fLKUkqnGSjabpK9XCBcFcaxg9OBb4X0txxB0CmIOgQKQqW2fuZqdrimsMrwmQaNqCX5x7kNCZe6FFOdszVqInizGGcOEFtd30sk2n10i4X/+o2j3/FcZZOlFN50yyfyQsi7hw1eej1S3zij17mzkvTLXzvlsl0vHwcW7MP5MvU9UO2mh5H53r3XdvPl8v1A8fBplgk64dGZT5TMZmqVVDVFFCQNcxkWktiqA8/mDAF3va/gN+CZ36190dRJECmuQKFWKE5rnHWrJGFGwgT9cdLb8Cz2lyrXxtglhw9M+jL5CfSX2no2Uq8I8oJWNd67AHC3V2863cGEtYMTWFPMpnqAiRrJR5y4+VyPSYTwOnaaW41bo0yS0qHenK5doBPjDlBnu0G7gCDU/6742eDTHECnrjXrmYfT6bVGePH0jR0wFApzlm06r35JdxKDJ0XMkAmgOIipUAscsPKHOFeflT9XVeaoDYFk6nRoOvMU1C76XWifISSJz5Pv2ROgkyb7U0KemGmDYRyYsTrHtnh4S89zAObISu3btO1xGKmVMtgMrkJyGQNpsvBdOyRLAsAsTEyHkxMAZAEZJJWC9iVqdLlshb0ssLtnVwWE0B53qayaOf6Mt0snMeIOjz4ePKsB90DezKFppTLHZzJNFCaAYo26skUjoJMk5hMchMp9WQqGsJf5plfhUOPwYm3jP37ulun4JcpzCXPgF0Vc2MG01eOlbvVsyOpWp/78A10XeW17xj1fNVUBUW5N+lyshaPldi6+QrI5Qxh/N0/5jSTXupumUwAmwWFSIHrF3obMFEUs7fRpnZoOsuEXE+m9Yuw+Tw89nUYljaReJBWDpMJwIjB6AMNJyXLwWTjb5kC3q7fvS9T4IWQhMZIuZyReOaOBZmkXM6XcjkfRVUGWFsDr3f0AeNvufmh1e4zme7Xq7QUUwxoUddl0VnE0ixWt0WT2W9OlsrlJjGZVldzTb9h1JNpWuNvx9Bou6Mg04HlckPJcgBtNxyJ/Owvz65i+ZOZCWXbSE0624V97FZ+DHNmfeA7KD79KwC0PvSj8IH3wgfey+s/+Q/5t+a/4fhffH/6M37762Y6tDPG+NvKoaynIFMCdmilErS2URQfJYPJFEUxbW9ILjeD8bfdF5fbXxJkCsr5i49vfeRb0RWd37jwG4RRyAs7L/RMvwGvJSbybhyPMJlszaZNJECmMTInoyjuWb81uEC713I5+aw4KZPJRNF1zOPH0W6tAcPG3+7gQiZZUFaifSCmE7QOzGSCUaBw4WiReC+h/w411fsborHtB5m85FxNvQ9k0hxcNWm0Xgm53JDxNyRMphy5XDfoUgrF69WgDuWjmCdOUN7JB5kCP+Qvf+dFygs2b/ofBNMsBZlmWDB4QYRpaLzj2x+hPG/x4V+7MDG9BMTY2PHDiWyCvNJUjTO1MwcCme7si8XJRCZTKpfrXUN/XTAwjCwm0xSeTIqmoc3PE44wmSIUBUjS5dav7FOqWb2F8/IjcO6r4elf7pn8714Fr4k9P48VKzQ7Y8YqPRtkAghbCudWTlMxK5yaO5X+fOFoEaugcyfxZfL3xL2uJ4xfGbVcWxBz2P550Vy3Lt3uxbUjjL+7cRLV3RDHkvPdRCaTqqeA1ZnqGWJiru4PAT2lZfEc+l267QBXYaL0R3rRyZL/zvP4ijpibMhlMoVSLjd+MdS/yCtUTQI3TKUGkt2WKZcDKC5RDJLvoliGMBxJKbxXFcsxcwq5XNho0DWqFHQ3ZXxRPoLtbqESDZh/S5ncent9Jj8mEPOxpVk0vSZv+4YzhErI6spX49ZE75blybTb3cXSrAG2buqLMsGkH7ITcwm6E8E3CTLFkWQySblcZfp0uVzj7+3U2y2vVs7VuH15b2R87TQ97sRHObL3xZ43kO/iYhxIQoUjNybuEcgEggU5BPa6Q3I5R3fohOM3Ty1dRVH6gnuKOoEfEay9CG/6HpjgW9ZotjAii7JMTnOqQJzJRLMKBlYpTECm3uK6udvlxafXeORtR3HKoxuiYiNsurTDaWvxWIm9jXbmuqefSTlr2cYoCydlMs3dPcjUCiPqJZXrz/XmxsZ2l9CPpmYy5abLPfeHwoPr0XdhWtoMnkyjz+tyYRk10tBRsfr6NLlJfKJycONvyXSb+vzGlO9GxFoPZNLn5zH8ySCTVOxIckW3LVK680gYw8bfMvX0PpPpfr1qS0nkcmrgE0awUlphIzFDHGQyiYdlXOMfxzHe6irGSr42O52kkodvWuPvoqXT7pfLdbcwVZOy0ZtgpwWZwv19go0NrLNDIJMXjkR+Dpy7Vsbo7OT+vneuWsoUajm7mO3CiDZ6bLW2KJ54qzinb/hV+IFPwg98kv/0tg/yd9yfYfvbPyp+9pYfguYauJNBNVnCh0ihPWw6GHq5TCY3BZlEs6w6NngNFCVADV1hCN/3+VqJ31Op3/hbnd74Oy9dToJMYSl/8bFUWOJdp9/FH7/0xzyz9gydoJP6MQF4yU5wK4ooDLHWHMOhEyfvOYY+rif3bDgMMoX32pMpeVYQ/1VM8f2Yp07BTWGaOwgydQZ3hRPvkWK4B4pPRHR3IJM2CDLNr5RQ2iElVR2ZNHvJcr3vyk0W9Gaf+bil2XhK0rzPsoM7RcVxTNMLKFujTKY8uZwXeSnIpPi7UDmCeeIExZ0NPNfPBHI+86Hr7K23+bJvOdejfDtJczwDcOYmMgbL0fnK//kx2nWP//Kbz09kWDgJUyuX2j5FyYS5Wdkcd/bEc3KkL3ZZgEyJvEsfZDL1L0aDDbGZkS2Xm8xkAmH+PezJ5IURhqaiJCDT2pU6h04NsUmffB90duCzvy3+/9oXAXAOCdlLtzlOLpcPMrUbHksLNf706/+UH3zdD6Y/V1SFIw/NcTuRL4T1OqGiYpYSQ+R9F91UqSTJnfV5C/3IEdov74wwmdxYXJcgAUWafhMFZbypf3tbgM7JcyoT5i7vXR58XSn5LlobuG0fV4kHfOuyqhsMMjjlGCjBouGS4Il/6xaxNzonuMm1tc0xi2cGF3lyYSYXasHWFmqplCuDorCI7Ym53C+K6zvsy3SvSgmnN/4OGi26apGi4RN1Jch0GDUOWWA/ZTItF5YHmEyzgkwg+qW6V8cuGYStZ6lXHuD6mXeJt8wy/u7uULNrA2N9nqF/VmUl5gqQafyc2fGHQCYJ4Fjl6dPl/DBzbAu3t9EWxtsZrJyt4bYDtodkU5eeXidG5cjqX6U/i8MEZDoAu0U3C3Ric0a5XHNgfBgpw+6Zqyflh/EAcGxrk42/FUXB1rW+dDmxNnCNo0J+PKH29sS1m6smQLg9XhpYsDvUKw+glnuL68//l5vEMbzunePZLdMY0U9bi8fKEMP27dEeu59JOWtl+Y4291ycsjFgZn7QankBzZrBzu0WjR3x/c+SLAdiQ3oEPI5juPBBOPW3obSMYd8lk6mwhBGKsdt0es/MzcZNHN1hwc4HgHs9Rfb7mwlbyJvWM2pMBV6YgkwyXU7K5Tqt/HVNFpMpL1kOwLKHQaaEyXTfk+l+vVpLTeRyZujTThLmdhIqf6bx95i4ynBvj7jdxpyKySSOp5gmGAZRa4LxtznIZNrubLPoLA40PI7uoCrqRLmc+5LwouhnMkVRTMcPR4CH9LMFEb5iYTQ3xx4boGTpKdDScHZQYjX14piq3AaFkljstEpLYud9+RHWrFNcjo9hHD0vfrbyJeL1e9enPzYiYa49NPAO70b3lxz09GTRoJoyItxHTRYE/WwmKRUcZjLJZLJJNaz/lxXu7eGbKro9nl7/3vPvJYxD/vkn/jkA5xf6QSbxWZrxKKDo6A7dZAE3bJbZX2aSrhe0/9swmaxoGGR6gPDmKkoUZ6TL/X/svWmwLcldJ/bLrL3Odve39vJ6RepWayRoIVCDjARiMTEIBoYR9uABA4Ec4XF4mQl/Gn+asSMcjvDEMGEHBCJswDOeAAY8oEEQNAK1llFLArVaTav3fv36vXvvu8tZa69Mf8jMOlV1qurUufd0TLT0/l/ei3vPPadOLZn//OVvycvlxATtREMQKqnYZ5TLAeIa5tkJyhjzIipMvxXItJdjMslGNn+f2ZqDhMpzvmYmkxel4ByLxt9LmEydVIx7ND4Bepdh3H0XaJpg1x8uNLD+JMJX/vh1PPS+C7j7nbmG6IxyOdU47d3Tx3f/xAN47ZkjPPNkRRJarup81laphzYfwjAcZv4ubetNCTJdyTOZYg8WHGgGzfzrqvwTkgMJMu3VyeWWfx8BMhXlcknKYVACsBSzyMXkJJhL5VTd9T7gng8An/sXQBoD+18DiAb7opi7GlNjdGth4QaIOSSYRHD7JnpmL5Poqrr04AaGBx68cQQ2HmNqOLCkfNkbhugMrEzy7aU+3Me/A97rM/AcU0GnFKqlTWYSZIqE1xolDe2YAplkXe1exf/yGwzsX/9B8XUdeS2mtxEFksm0BGQK05JcTgJOdUymTAbGGKI3Fr3HMiaT2bwYyhZ5uoaOkkbIuPHk+Kjaj0lVZweWBJkCV5zf9HSFxX3L4pzPQaaGYA1VMw8AIeg4DNxXnkxi0+4iOZ0zmZxdHAfHSFiCQ+/wTCBTz+xlG3LvePmP0fVew5j1QNOw0ph2GA4LUjlguS9KvqrlcqU5q6IUAMIk+J8BOK3lchSM57za8h9/cgJ9q5nJdFX5Mn1jfn9wzvHcZ29iy/HROX4FPBbjBUlCRDibXM4xNYzQAV+XXA6QSZjFsSpK0gUmU8KSpf1Z3qzagjjvwbd9DDCXy69UsMHmpjxWR95HNawtl5yAUx0jT/QKwSzG1z9zEw8+vof+Tn3/Z2p0vXI5mTBXBhiB+T1vameTywHFjaHZMMx8hM5bfpQi3BXjzXUpmTvdFz3Z5sWWcjldQ5QyJPnzeeuvgZNXgEf/DgBIudwqxt/F53XL3oLBxPNvleRyd/Xuakz2VM9YnVTXsNbJZEqRyl7G1MpMpvrvX2ZbhSWv43KZjgDt1GbmXC53h8l0p96mpTyZTJYgiFNc7V7FeCwenlWNv+MbYjFkXKlOlgNyTKYcWq+57lK5XDmm+8g/yqRyPIrwxi9/HKe/9dvo6O5S4+/wxcVkuWXJbUrXaw73wZPmQbVj6VkzOLYFKJU3JW2sNAYSHx1LyihyrKzsGBU4siGSxjC83u69ZVV5uERpVAsyqdhNXTYrVJOgByIQaWqaX7TPmUzzScPSrNZMJqshXc5ztcYUPEBQbD9yz0dwY3oDju7g3v698+8SiMF7kjJ0rEWQyefy2kb195CSy5WBUT/x4Swxq12lsiRGrjyZxPNo3nsvEEXYG5FSulxpV1gzAHsgduw1ce06SxZuTWVpVsG8ffuyeK89tjgNDA88dDetgpmjApny18/VXaQ0ASfa2o2/lXlzpVyuwZPJTaVfXHQsmUziObs0O14APr1xBMY4rr17t/hGaqd2he8UlgxZH/vQVdz72A4+93sv4fD1+sVUnQR2lVLR1984Xc38W8nlLg6KTCabu1myHJBPlyvK5YhpVu7SGSatDBQol7azXSmXM3QKsAT7p4KlcPG+Cl+8J/5bYHwD+NrvALeeAXa/Dc5A3NOJ3zDGa9aC8TcggCnOAbdfPT5dfkD6Mr04BB+PMTGcbKdzNorQ2bBgazYICLzYg/v440g9hmgyfz9dI4ghxrDYF+NPK0N/76QAMqUvvYL79hl6Xy+BPF0FMh0g8lOEhK8ul5Pyp6iOyeTN58Lo1UVfpkCOE7bZLDPP5HIGnZu8ysVsevsIWp1UDgA6uzCDEwAcvisWkm8FkylMGEzIMbMNyBSJsarjACwIBPumdxEAcIGcYhLOmUyMMxz7xzj0D1dKllPVNbqYRlPE+/u4dvwGyMYRNMJgh6eCsVCq0+C0YPoN5GWwZ5XLha3lcqkE/7Pe0Rq0ZjIBi0xPnqYiHrzpPoEwEN644BZ8mQ5fm+Dk5gz3X5H9z3gMsBSExQj52eRytqFhyLtgXkuwk0u5mdWwcaTb1elyeSaTYh4uS5jT6XyT+MafAgDC+388+318eIjX/rP/HPH+/sLf+mNx325symNVTN8aQM2NboHwFKeH4pp97dM3kIQp3vuRexqP0dTXK5frbdswba3SlyljUq6JyTQbhmvxYwJED65vGOhuWZlk7nR/BqdnNIIchWOUfopB/nw++7tCdv1tPwpAADnt5XKSyZQbW3Sqo8/F2GXnAiauT643mn4DOQCnpk/ImExrAJmSKAXTlGyUgLouDI0B4AimDUymsvG3lMvVlekoYEz0H3O53B0m0516m5byZDJyTCYeytjp3GCkvDWadpfjN98U79XEZEpSvJ8+h87T/2J+DB13ufG3pRd8R478o8z0O97fx/TTn8bBP/2n+PjvR/Cnzc1i+MKLoN0u9IsXs58tS25TNHwzGCI5bpbMdS09k8udWoIZMG4LMkk6aUcuUvNNmReJHShd7UIpkOl0NSaTUwEyNRp/ywFPl4CGpsldO8SgkfheRSaTeH3ecNnQDKQ8RcqWD/hNcjnP1VqZa//8oz8PAHjH1jugUXlNOYdSZ4zTFG7JENrWbPhMLuEazL+NXrXEc91MJjV5mrzIZFIJc/eOzFK6XIX0wN2BHp6AroHJZGhGgZ3Q27LBNGAnXZ4sBwBhBcjkGA4IjRAb/bUzmRTQ262Sy9UxmdIAbiJZlloK9C7BvFvQ8y/PjhZAJnXfL1Dcz8BkChNWkFkQQvDh/+IdcPsmPvVrz2ay1XKpMavJmHxZPbQlQKZVfZluDn3sdK3CcfuJDwvFmORKJtP+PvSLFyt3KzVzuScTAOjbO0iOjwtSmFjK5QTItAmqE+zeVbHb/8D3C9Paz/7vQi538V2we2LOY02xxzVyObURUeUXAgC7d/egmxQ3XxqCT8aYmm52XmbDEJ2BCUIIHN2Bl3jovO994n3fnB+LTglk+CESLwAYwzSaZkbOteUdZ8lyADB7Skh83MPSIl3J5aYHiIO0lVxuwfhbjs8Rq/FkCnIg02uvLb6ffGbsJhkQgCidy+XcTC6nmEzH0HcagJfODghP0McMni2ZqW8BkymMGQwir18L428vFeeu1yNAmgJxDPQUk+kEY3/OZAKE3DFhyUrJcqp6Zg+TeILpn/85AOD2u96N777vAPe98u8qZYxKLpcvxYhpJZczKjYAY3+5XC7xxQZHItSeGQvH6gHRFFjSU1RJdQEJKjIGbQmTCQCuPLQhfJkka+K5z92EblBcu0+MF+lolI0JZ5XL2YZkMrUFmaIZAN7MZDKcxXS5CuNvoAXIpDx6khDWy/8WABDS+bnzvvg0/C9/Gf5Xn1n423AirlFXyaqXbMIYk1O4kzdw9OoUcZjimSdv4N53bWfs6boyddrKhL5tEUKwfbVbmTAXZZ5wZ/dkygdhTE/Xx2TyohSuZeCeR3dw4/lTpDETyXItpXLiGEt9OGPA138fuP/D2VyysvE3TxdAzx4X95ACmVKW4sbkRqPpN5Az/q5lMimQ6fxyuThKkZLitTa2BjBIvCRdrgiECU+mJiaTDCjyFcg0BDEM0E479tk3Y90Bmd7mRaUnk8kS+JFgMtmxGIjylOn1MZkYPkqfgv7U/zY/hgomUxKneOWvb+PLf/waOOMLC6k8k0ntQHY++L1479c8/MA/exLR9Xp2T/jii7AeeKCwuPEzkKkaZVY0fCsaZ6lIddW19IxFMdZOwPU0M0JeWnJnrmOLQTzflPlRUjQm7+wAhrsyk6lj6vCi4sAbpVEtQKIkZpqUISrAgmgAlaaS+UW7+u7ldDkArcy/DY1Ao6QwAQPiOk9dspTJBADv2H4HfvFdv4iPveNj8x+GE0TMhkYZJlG6ACi6hgsOjpAQIKoHmTRpVp/JGWSt25NJTe4KZKJ5TyYAdw/1kidTBcjU2QGZHcG15TU5hyeTRa3C9SOUIHA1bJXmcM45RoeLIFMkm90866FjdCTI1Fs7yKTAzgWQyahnyURpBEeCZlTnQO8S9AsXwAxDMJnK7Dr5Pnq50bT6AMhKPlNhki40rHbHwEd+4VFMTkJ8+reer/QVUaDseeRyfbOPS51LeOFkNZDpzaGPKxvFe86LPZjMLjCZrArgOD48gFEhlQPEpkbcxpNpZxs8CAqswijhYiHKEhycDLB3d6/a54IQwWa6/bxIVLv0GJyueMaY3/DZul0JMvkSZKpjMmk6xYVrA9x6aQgymQgmk66Bc47ZaL7AcA0XXuzBuPtu6A6D99p8DtA1ikSBTEyw/9oxmYpyuakEmbq3veI91ZHAzPQQSXg242+DGgAniGvGeh4EgsG2vY2wiskUinHAsivYZ7nKy+VMW4NuUszGc0+mRkNn+T13yBgTS3rsvQVMpiBJYcrAjGXG3zyO4WtdABxuTzwvLAiAzi44odgjp5gEcyYTAHz9SKTLKtBpleqagsk0efLPcbO7g+DSVVzZjbF39NeVINNpcLool6swMK6r+nS55jnTSzzYui1B+Jz/nzLEX2L+XTX2AMi83PQlnkyAMP+OgxS3rwvQ48WnD3D/t+/B2RX3aDoaZezGM8vlDA1j3gFvy35V37tRLmctpsslDIZWtJkAsNSXyTY04X/z9X8LOxS9fjCbT/7RKy8DAJKjxf44nnIwks5ZHE6zJxOdjmFNr+P4jQm++uQbCGYx3vuDzSwmADDWLJcDhC/T0ZtT8JIf43nT5YD5eiaJUwTTGN3N84NMnHP4sr+955EtxGGKmy8Pcbo/a236XThG1VfceBoYvZFJ5QDBFmrNFFL3ael57UiQyZEAy6F3iJjFuKvfDmSqYzIRSqCvYkzeUEnIkND5WAIA+uYWTBY0hrOUx8dwFsNuYjJJ1n0kE+aS01NoGxuNssFv9roDMr3NK5PLpQn8OMHV3lXYSQfE4NDVA/Xyk9Ahmvi4YSET3bgBbTAQ6WM1FcYp+jQAiWdCGgaAdjpgnoc0Znj1q7fxp5/4Oj7xj57Cv/8/v4Yv/P4rOL45LfiOJCzBaXA6B5nkDuTuxz+Of/NLD6FzGuDVn/wpTD796YXP55xXJsspiVctk0nS8M1ohOR2s29JJwcyRSwC70UY3W5mamUlB2BXsrTyTZlXBkYIATbuXtmTaVUmk5LLaYEEmYj4LpRyEAkyVXkydUueTABaSeaEySSt9GSaOqRW1leuf/jef4gfuveH5j8IhohYB6alJuGS8bfa0SNERJzXFM1ApgomUwuWVdtS9H6z5MmkbW2B9nq4clIy/i57MgGAuwN4J3Cs84NMZeNvAPAdikGEwkLVn8QIvaQCZAqy91HVNRyARgj13tqNv6d1IFOD8XeQBnBjCTJpHOhfAqEU6YXLuDw7XmTXKSaTWZoKKZWeIWfzZMrXpfsHeP+P3YeXvnyIr3/m5sLvnWxsPN+O3cObD6/MZLo1CnBpUJSIeokHk1vLmUwHh5Wm3wBgGBrSFqCZih9Pc75MCWMwKJAygsPTLi5USeVUvfOjc0boxXdlGyuk6bN1s4bJJH5WBzIBIp776MYUqRdlTKbIT5BEbA4y6S68xANhKdzdALOX50wtIyeXS7gFTA8wjabomg07/IwJk3MJMjHPg/+lLyOwNZhRijRvnK6bwi9ldogkSBERXliQVlWZwUkIASUG4jomk+eDOA7Ma/ciem1x7sqYTHazRCDMMQkIIegMLHjDECwMwSYT6LvNnkwAsI0xZroNUPrWgExxCkvJ5ZYYf6fTKQJrC47JYLjimWJ+AGg6SPcCrtAhxnJXXvU+zx4/CwBn9mSKJiN4X/gC/sOlR2AaWubRWQaZwjSEl3iLcrlV0uX0TpF5C7Qy/g6SAI7uLDA9M8PrJZK5KqkugOy+15akywHC/BsAbnzjBC9/5RBxkOKdH7gErS+OgY3Ha2AyUQx5B6St8XcGMjUw/vQqJhOHmTu+9iCT7Mm++KuwdsS4HXrzBXb4kgKZjhb+lnsUkeVnHn2ZJ1PNnK9PRoB/C5wBT//hq7h0/wCXHlguGVp3uhwgfJmSMMXoqHh+zpcupxgu4j0UA3MdTKYoZUgYR8fSceVhweT9xuf3EXoJNi+0Z8Q45efm2d8VQPnDP5y9ZjUmUzUo7DBxXW3J8HljIiTcy+RyGiXQKckYrVVl2lomPTtPxVGKpMRk0ra2YCQe/EYm05zpyRlH6CewGuSKlqNApjmT6VvZjwm4AzK97WvuyRTDjxiudK/ASlxwSz6400PgN38cxvO/AwBImphMb95slMoBArnvPhDxlgAAIABJREFUU0nLDUZIE4bb3QfxFf4d+MQ/+gw++X98Da8/e4wHvn0P3/m37xMvmyUZIOBHKU6DU3DwBSaTtrGBw3ddxq/+Nw/AuHIFN37547j9K/8SPBdHnx4dIR0OF0CmpXK5USio2tFkKZOpZwu5HGMMYRqCDOL2nkxyADbcTZjULMrl4rTIZALOBDJ1akCmJuNv3aDAbAroOkgijpFoPJPLpVVyuZwfT8ZkapswZy7Gp6bDISY2zzw/Vi7/FBF3YVoEsyip9GQCAJ+QRuNvBTKhzGR6i4y/DaY8mcQ5JITAvHYNF47TklyulC4HAJ1twDuCZYrzfl7j7/L1m9gEZjqXCgHVyXLA3AhY3QsA0DM7ICREoK2fyZTJ5cqeTA1yuSiNYEuqCNE5MBC7aezyFVyaHS3uhsuGVq9iytiD1t+Jc74gY8jXe37gbtz9zi089W9eXKDu58fG89SDmw/itfFrtYbNVcd8c+jj8kbxnvNjHwYzoeeAt/JilHOO5OCgFmTSTdoyXU7MAfmEuThlsDSOo/gaUkZxsZwsly9NB/6T/1HINy69G5pGwQwCGjek7Ol2pSeTJ31HloFM4MCUbkvjbzpfYAzmTCY/9oFwDHcvQjr2M1mZTudMppjbwPQQ03ja/FwHQ4CzDGSaffGL4HGMv/kuES6xYL7dvYB0fAiecIRk+SIqTMMFcF2DiZRXj/Us8EEdB+a991Z7MknQwLKbm+uyJ4o7MDEbRUjlIrcRPHDFfbNFxpjFHNrGRmayus4SnkyKydQMMrHJBIG9jY7LQSXIlJmk9y7isjbMmEzbzjYICJ47eg7A2UCmrtHFtedH4HGMz114p0hklHNMGWQ6DQTwsQgySUlKC/ZIx+jAT/yiZD4JWnkyObojALv8vVjDjCiX+psyM1rZHjQy3mS5fRNblzt484UhnvvsTQz2HFx6YANUgkzpeJwZbIc4myeTI+VyJGw5D0YtmExGjSdTTgKrepZlIJNjarjq/Q3w5pdhvv9nQch8AxIAQslkSitAJng6Ujs3ZhqOACxqWFv6dIwgPgXVCFjKW7GYgPWnywHAzlUxtpZ9mdaSLpeBTNLOYOP8/aPqAxxDg2nruPzABl58WoRsbF5qDzLNmUxMSFKf+33goY/MGYSASJcLknaptNnzWgSF7VS8H7XEe1yfCGXGMrkcIJ7tJoDbtHVEbUGwmuKMI40ZYhT9CfWtTejhuJHJNJccMiGB42jlyZSBTKfDb2k/JuAOyPS2r7knUwIvSuAaLvpsA7EhgaCRaECJfwrdoI0ShmT/FvRLF2t/D4idvS4ivB6+B3/22y/jN/7xU3ja+BAO9Ltx33v38KP/9bvxc//rE/jQ338H7nmXmPxDL87An1mU4MgXk5jyZMqDTF2jixv9GPf+q/8Hg49+FEe/8iu48fH/StCZkUuWe6gIMrWRyzk9EwQcyWEzyNSxdDAOTKMQHBx0I8H4KFig21ZWjgJdppf7FRIvbNwDnK5q/F0tl8sv/gu/8xOYrg42nULrdEBkY0AoQCLFZMpFsVbJ5bT2cjlgHjusijOGdDzGyOatmUwL5Z8iZC4MS6TNlAE7tVDyKWk0/qaOXFTnvEViFiPhyVqZTGoRlYFMxnwHxLz3HuwexSXj7woTVXcH8I5hGnIhe14mU+n6jeQtc/zmvAEbHsoUkzKTKV2Uy/XtLkBjzLTu2o2/6+RyeoPxd5AEsBICEID0L2SMB3rlLimXKz43yjdIq1qI2xutv1OccnBen+RFKMGH/8E7YXV0fOrXni34DKzDkwkAHt56GClP8fLw5VavH/sJvCjF5bJcLvGg8zLIVNwVTYdD8CiCcaFOLkfBGEe6ZPGqTHvzu+dRwmFrHPvxwwBqTL/z9bd+BvjHr2Q+WsTSYCQ88/9bKK2GyTSJoBkUhl3PYrhwrQ+qEUzsqwJk0rWMJdvZEA+Tq7tiwReO4e6J33lPPy2+r0YUZCGZTC1AJk96CCqQ6anPgtg2XvvAvQCAeAFk2kM0kulRSzyZYhYj5enCmKwRC0kNyMR9H9S2YV27hvTkJJubVQWRBJmc5sV/5lknj6+zYWE2CucyqMZ0ublczosSATK9RUymufF387yVjicI7C10exqILZ4pFsg+rHcZF8hpxmTSqY5tZxuHvmBVn1Uu9+7nQ9CNDTy3dW8rkGnLKkrLlsWI58s1xHwwS0phFUZzWIYCmcKEZYs2APPF7pKEuXomkwQjt5bL5QDBZnrzhVPcemmEd37gMggh0AZSLjfMeTLxs8nllPG3lmP5N5bqFZtYjDXpcvnjUz1LUGPUn71O1/CR6R8AZhfkb/09WK6RLbB5HGesxOT2Isik+Ra4W/pOzkYtk8mYjjE2LVx+aAPbV7u459HlQCCw/nQ5ANi61AGhpNDjAOuRyyngczqUwSxrYDLNShvm9zy6nc1lq3gyFYCw1z8rJOU5qRwgmEyco3bTrlA1oLDFxM8nTDzH1yfXYVCjlc+cZWiNoKJhknPL5ZR6JyLFa61tbsHwhks8meZMJgXINnsyFX2k7jCZ7oBMb/uaezLF2SKlyzYQ6HJAncikiHAsYqUbUGE286B1mpkSQcywP/1e/OHpP8ErX5/h3sd28H7nS/i+V/45Pvyz78A9j2xDk02jQnxDL8lJQtIMZFJMpuT0FKAUtN/PfAaobePS//zPcPF/+ieYfu5zePUnfwrBN74xT5Z74IHCcS2Xy4n0H21raymTSYErJ9IY2tgA0oRhOmzBEMhRoF3DLTRkXpRkA39Wm/cA4WglqVGdXK42Xc5PYTk62GwK2u1mixZCOTQZe1vNZCoafwMrMJlKIBMbjwHGMLTTVp5MleUPEXEXumS2dGrkcj6hjcbfislEcyCTMs18K5hMelpkMgHC/Ls/jBDP5HPKeXXD3tkBWAJXPs+NspolZdJFJtOpnC+P35zfp8N9D5pO0d0qngv1t/nr1zNdEMIxpO7amUxnkctFaQQ7FgEq5NK75n9z112w0xjhQVEqq95HL8vlgJWYTIoJ0LQr6vZNfOTnH8Ho0MNf/uu5rG0dnkxALmHupF3C3JtDcf+XmUxe7EFLjbncGkLmRcm8MU8OxXmsZzKpoInm5nUulysymRzKcBjfj47b0ueCzo+V2hpcTrI5YfHg6j2Z3J7Z6J9gmBp2r7oYDu6XIBPNQCZlXu0YwvgbwRhmL4W20YX39JfER1MBgGoGQcJNYHqAWTxbAjLJcyPNWmdPPQX38ccRXd0DI9VMpmgi5qGIoBFkqhv3dNLAZPIDKZcT3nJl8+9Qev/ZTvPiP0wYdEqyIIxO38JsFGWAYyPIJAG3S/oUXpRC29zMZPfrrEAafzOiCwltQyXjMUJrE71NM9vIyHz/ehexw08w9ucLGgUsbdlb2fy6SvVpB+99mUP77veDUQ2mRkEM6UkWVoNMGyUJY5UMtq7UPVr0EWyXLidApjKTSYLHy+RyTUwmXc+AomV15eENsISDUIKH3y82U7WMyTQHmaJzGn8DaNfPtfJkqgCZ0pInk+wZ/Ab2NgDs0DG+J/qMAOXtPqyOni2cozfeAGTqcpVczggdULd0j9gbtZ5M5nSMmdXBD/3io/jx/+49c5ndklp3uhwg5qKNCy6O3iiCI+eRyy0wmU4l03wNnkxqI8yVfY8C6HRLW+n9s3S5OBVSOaMDPPiDhddkHkJtgJwakMlIRD99ysQ8dWNyA1e6V+aBPQ3VKI/c/xrMg/+AaNzMdFxWqgeJUCGXC8btjL8TlklL7c4qTKbTO0ym/9gHcKfOVwVPJkWzTDuYUDnJjaUHSDiBbtFGTyYWBCDOEn19nCJJ++jQI/z8xxm+/x+8E5cHHuAtpjcojW7oJRkg4FeATOlwCG0wAKEUXaObsTsIIdj82Mdwz//9f4GHIV776b+H4e/8LrTNzQUqvd9CLtcZmNB3d5fL5eTgfipBJlMC0a0kc6phqmIyxQxOmWm1IXXLK0jmquRyURrVp8v5MUxHRzqdCZDJPwX6V4RcToJMVelybm6Ruapczja0gvxH7TIPrfTsbCH/FBHvgFjivipf66zZos3G38Q0kVANJJw3b0pi5JTlaucoBbJpFSCTWqC5+xLEUIveKiYTAJuOAa7VstXaVJVcbgqO2CA4uVlkMg32HNBSYxiyRZBJ7W4fw3rrQKayXK7B+DtIA5gRB9GYSB6TZd4tnjN2o7ggzzyZqhYV9qA1+KuYAHVMJlVXHt7Ed/zIvfjGF/bx/OdvAZgz8s4rl7u7dzdszcaLwxdbvf5mHciUeNCYXgDeCCGw9PmuY3Ig6Pv63jKQqfk76VtbACFIjuYgU8KEXM5nA3Tc1f0YdFeHw0h2/yy+wBIpOWnx9944hDtY/nxduGxh0rsbM7MrQKahYjLlPJliDwjHIAToPPYwvC9+EZzzDFDRTA0JcRFPbsFP/GbwOAOZthHdeBPRa6+h+z1PwHZ7GPYI4uslkKmzh2gqpT+ENxp/q3GvPCbr1ESK5XI5AAvm30HswWQc1FnmyVQEHdwNE0mYwt9vwWTSTcDewK42eUuZTKE0/uYtNkamhxNwqqG37YCWmUz9S+jzCUJ/3g8oidxZpHIAsPviEboBEH7XewEIILiWyRQ2y+XaGn8DRZ/JtulyQi7HiiB8SyaTVcNkSk6OoW9ugiwB/1RdeXATIGLRrqStRNeFp+iCJ9PZEsdGXIJMbRiwbUAmwxZejbmKk+Iz7WjSi3IJk+mDk08K6efjvyg+1jUQSiZTphB4+OEFkIkxDjNyoXdLzFBns/J7Mt+HFkeY2l1YrtHI/CjXutPlVO1UJMzNgwfOky4n7snpMIBhaRnIcJ7KrD/kfb9xwUV/x8bmBXclA2n1bAdhADz3/wkvJrPITm8TCDV/wzqQyQFDiuNY3DfXx9dxd7/Zjyl7S4PWJ1se/g0M4iGa1asS2pT6bhEvembqW5vQkymSmNWui/Pm5OGsDZNpni7HGUM6GkHbvAMy3am3cSmQyWBJNjjpkY0hjhGzOMdkmsBYxmQKAlCnWfMbJAyMGbDpBFoiFpW001lIlwPEACZ030W53HEgmshtW8nlRhna2zE6CNJAHLss9z3vwbXf/R04jz6K8IUXYD344MJg6y2Vy4VwB5YAmQ6XG38DwEg2iPaWeExGhy3Mv0tyufyunx8lBeAGwNy09rQ9yOSYemFBmrCkUvKQHZJiMk2noL2uMJEdXAXRtQxkKqbLpeiYWgFkUMBC/ro0HqOhFXYe1QJg6uAcTKZTRMwFseVirpbJ1Gz8DQCxYUEL5k2Z8jNYJ5NJNe66pM4XQCa5QBvsy8ZH+SmUQS65Y2+QKcCsc6VUmJq54NcTJinCjlZkMh0sJssBQCSvff4+c3XxuhNqiR3XuLnRXaWmYQJDIwu7ysuYTGYQgGoMuDhnMtn3yufs5o3C69UuV6Unk7NxBibT8t277/hPr+HKQxv4i3/1DZzuzwqhCOcpjWrYc/cyEH9Z3RpJkGmwmC5HE63AZAJEQ6ia6liCTLVyOXk+lzGZiK5D29hAkjP+jhMOkzJE3IVltpAol8p0dTicZGD54sHJ+7f0LHjjGE6vBci0w8CpDt3ZAyEEs2EEy9VhyOvo6HMmEwC473kMycEB4jfegCHHVKpTJNoAs6mYn9sxmbYxk6lynSeegKu7OBgA0Y0yk2kPYSSbYzQDn4rJVPbJM4gFjrhScsg9IZczr14FNA3Rq68Vfh/GHiy+JJodYnzMPy9q4T+RwPtSQ+fODnbJGLMwhba58ZYxmUzE4C2YRpNj8Tz1L/RAbGX8rZhMwj/L8Oe9x64rmExnkcoBwObTLyHSgNPHhHWAoefkcnFLuZx8TtuwRzK53ILEux3IFCYp7ILxd7XHS7mq4uIBID06bmX6nb1P18BH/stH8MRPFVnwdNBHOlqDJ5OpYQT5HLcx/25l/G0vpsuVvP+ywJOkYe5NY3xg+Af4PB4DdgXj1e7omVwueuUVAID7vvchOT4u+KDORgEoKKxuqbeukctlvZ61Ouva0AjiZPUxf1ntXO1iehoW/HfChAnz6SUbQ1Vlm8XEw9kwXAuLCZgH76i+gBAhtf+en35opfdRm1e9m58VPX9JKgfkQaYWmzk1xt9aYiHWQhz5t8E5x/XJ9aWm36pMrQFUnB7CIP65jb/VRlcAXphvtK1tmHIsq2MzaZTA0AjCJEUgmUwFT6bxLeC3f0r8C9H3UEoQ+SnYZAKk6R0m03/sA7hT5yvVVJipkMtxzkFCDYE+w/50H5iImx/RVCQJ1CxkOOfCa2EZkylKkHILJvGzCYa6LngYgifFwYBQAtPVC3I5xWTqGb1sQZ+enma6VdVsz0qeOvruLu7+jU9g73/477H9S7+0cFzKo2jBWBtAmjL401gwmfb2ljKZlDxnGAiAyNkwQDWC8VEbJtNEmB0ZrpDLNaXLATkmU3tfpo6pIUpZpl1X7JQm42/TUZ5MksnkbIHYbi2TqVOSKK3MZCoZfyey8ZiskC63UMEQIXfBJLjkloy/M0+mJcbfABAZFrSwQi63Tk8maXTKowqQ6R4Bemyo1EIFzpTT5TqiiSZkBs7O18RYmrUAEoYxQ9LTcXJrBsY4WMowuu1jY68eZMqDhKrBPebyZ2tkM02DZEEqBwgmE0t4pUdakAYw/QBU5wWQqXP1ClJCQW++WXi9AqsW0uUA6cnU7vuU/WWailKCH/j5R6CbGj71a8/ClMBh2S/qLDWwBhgvWbSpenMYwNAIdrrz+yphCSIWgaTagoQwb9KZ7B8AhEDfrV4gt2UyAcKXKS+Xi6Txt0qSXLXsrgGHoyBNKpQaf0qSOW8SNZp+q9rpBgBncOWCfSY3MFSpdDm1eHYff1y8/9NPZwsaalDEWg/TmQDr2jKZpk99BvrlSzCvXYNruNjfBKIyk6l7AREXz+9ZmUwmtQAaVzJcWBCAuA6IacK8enVRLpf4sDkDzCUgU1zcWe5IFtns2AMdDEDNJdeis4ttMoYXpdAlk6mVge0KlXkytZizJqfiXPYvD7I+imeeTAJkcoM5yLTnnJ3JxDmH+4Vn8ey9BEMi5mThySTAsDKT6SQ4ASUU/RKgocarWjZBriqZTC3S5WqZTG3T5fTigj776JOTVqbf+XrwOy5gsFuc27T+QPiKpXlPptXlco6hzZlMK8nllnkyldLlEgZTmx+f6qMXkv/y9fwfYRDfxm+xuVzKco1MLhe+/AqMy5dh3nUXEMcFn7WjE/Fd7EFpLq7xLFRgr+esDjKZuvbWMJnukubfOTbTgnxzhSrfk9PTcC1+TADgx0W5HABcfmADl+5vJwvNjlECKhff+CMhTX3gwwuvUf6DrXyPakBhLTEQayEOvUMcB8fwEx9Xe80BUtlbGg3G37NDmMRH3G7JUVtqzRtyvsBkMmJxPyyTzAm5nLgudj5d7tZfAy/+CfDl3wAgQ30cHZGfZGCrfseT6U69nYsQQZG2mJDLRUEKcIJAn+GN6RtzkCmcQDfr4yp5GAKcZztwdcXiAAmzYVIvm2CoKyZW5i1OcmoiU3I55cm0nTMFTYdzB37VbE/jRfkdMQxs/8IvoPvEBxZ+15Qu548jgGPOZDo6KuzUlEstbMfSs8cyTPR3HIwOW4JMVg8gBB190fh7AQRzNkWztYJczikxH7LUrxqGUJgDmYRcbgg4myB2B1SaUqd54+9ocXGfeTK1Nf7WaaEpTDOQqR4MW1bMO0XMXXDZqJY9mRSrxjfsRuNvAIhNC1o03/l7azyZUtiGljX8eZCJOg78nS52b0diYaR2IcufL+VyjM/AUutcppgGNSqYTAy8ryONGca3fYyPA7CUVzKZQnmvFJhMcnf7BPJarNH8exomC1I5YM6SKZtKpyxFwhLofgiiE2B7vmPtdmwcuJswDm4W/ibJ5HI1IFNLE1fVGDct6PPV2bDwnT90GcdvzjA69OEYi2mMZ6m+1ceoZbrRrZGPS4OiLDJLKEroIpNJ17LFaHJ4AG17u3BP50sBVG0S5rTtnYJcLk4ZbMoRchfWGUAmp2tCB8FkUnPd9EWQiTGOoCXIpPsTdGc30SOi6Z4NwwwgAcQz4cVzJpP58CPQtrbgffGL0DWVfEiQkC6mnmBwLWUy6TY4DHif/wK6H3gChBDBZNogSG/fnjNmAMFkYgpkOpsnk6lZICReWNgDUi4n+4SqhDk/9WGBLPUwKi/y1CLNGwbtwIPODrb4SMjlNjfB4xhs1oJtvEIFcQqTpEuT5QBgOhb3+uDqVo7JVASZuvF8g0sxmc4CMoUvvgh66zaefpBgLMEKS6cZMFcGmYbhEBvWBigpAcdGe08mBTJl7Ow0FrLT8sZIqWqZTIYjzPNaGn8vMJmOj6FttzP9biptMJDpcueVy2kYZp5MLZlMmtXsaaXLdLkceBolZ2AyffFXMbIu4Y/jd2dArO3qGTsjfPklmPffD31XWljkJHMnJ+L6dEqMVziblWBakoFMzSBzVTV69Jyjdq6KY8n7MoVJfRrssjI0Ao2SQrpcd00g07Kk7LblGBpMxLiy/yTwjh+tvM8M2ee3ksvplhgHy2mQCUdMY9z2b+ONidjwaMtkykvwF2p6Gyb1ESXnkyAq9Y7Pitdb29qCoZhMDQlzlk6l8XcFk0mtM/7qt0WCH4T5dxQkGdh6h8l0p972RSwLDlL4cZohsoE+w43JjYzGh3AMw9Jqm37VpE5pM4hAo6mIkSd+tstPO6KhrQKZ7BKTyZPpcsqPCSiBTIrJtETuVC4vSmFqtLKhno1UxLTwZEKaIj05qX2vjmTITKRnj0lNDHYdDFt5Mk2yHbqyXK6SyUSIYDOtwGRyM8BO7kJVpH7lK/ITWI6OdDabG3+7W6BOtz2TSVuNyeSUmEx5udxZQaZ4KoDHVC7U6jyZAt1qNP4GgMS0YeQ8mZSfwTo9mUSaDp2DTEZRchFe3salYy4W9nUgk0xHSxCAM7teAtSiLM1auH5hkoLIVKzjm1MMD2Sy3MUqJpP47Com0ymXz906mUxhsgAkAnNAqJyIop4DIwiF7DdnPGnpFDc7O7BKIFMaM1CNLPhPAcjSytp8pyhLqmk/pdoviMSxoxduwa3wWTtLDcwBRlG7a3Bz6ONShVQOAJCSaiaT/J7xwQGMvfrFccZkagGc6dvbWaIYIEAmiyom0+ry0E5fPGfjUU1QQwYy5Z7/aQzO0QpkSscjbAxfwgazkCZM+P1tFJlMMYsRBzJgwRnAffxxzJ5+Whh/Q8rl4GAqX9PMZDoB3G34zzwDNpuh8z1PiM8xXBzI/jW+kZOBdvcQSUbFMiaTGvfKY7KlCSZTUMFw4d6c8Wxeu4bo9dcLmzZhGsJu0VqGCSswRhQbbDZjzX5M2R/sYMDHmEVzScK6fZnCRMjllplbA8DM4zDiKUzXzM4PU+ESfQEybaXH2VhxHk+m6ZN/DgD4ygMEE2m0bmg0s0+oSpfbtBZ31DMmU4t0qY4umUwqzKRuziqVn/hwdVcw1/JMJkLEhlx50VqqfMKTKs45kuNj6FurMZmqSuv3wcajglzuLOCDnWcytfVkWiYpM2wAHJDzNudcyOVyxt+WZoGAzDcIyrX/LPD6Z/H1K38XKZ/Lk6yO2ABmSYrolVdh3Xdf9tzlfZlGQ3G9+4NST+BsCFYLK6X+nYrv7rtnYTKRt4TJ5PZNuH0Tx3kmU4lJuUoRQuRGKgNjPAsWWkd54fpApg/Sr8JMpsCjP1H5GtNWaWgte4+q5zVhSDSG294cZLqrd1ert2sEFWdCLpdyA2l0djqTSlQPSkwmATItZzLZhpZ5MmkGzXob8eayXxrfAF4RY7JiMin1xp10uTv1ti8FMnlRmiGyiRnhxvRGjsk0bWQyeVOBuv7pwWcaP0tLpoi4A6Mgl6tnMpmOXvBk8qIUx/5xxmTinEu53NyTCahmMjWVFyWVUjlA+DEBYqdU3xO7h02SOcWeGEs5laVZGOw6GN/2l9Pxw3FGK+0YnawhY4zDj9NF429A+DKt4MmkQLA2TKY0ZkhjNmcyubZgZzgbIG4PmmRp5D1uBMhUPJdKLhe3iebFYrpcOhwClMKzzw4yKTPbRB7aAsikPJl0s9H4G5AgUzxfhGaeTGuUywVxCkvXwOMYxDAW/JSSq3u4fCoB1bqG3XAAo4MYIcAsTM6hTzc1EzGLwXhuQZgwaBsmQETCnAKZKuVyXIJMOfNxxR57S0CmIEGvgsmkKb+f0sJIAWhaEIF0i7uohBDc7u3AuX2r8AwncZq930IpkKmF9EEtgFZZnNhjId0bHngClF0HyGQNWjOZbg4DXKkw/aaMAmwRZLKNvPH3YW2yHNDekwmQcrncoiZJOUwwJLBgnmGoGEjgbDpeAjLlAFdvLP7fxpOJjcfYGL0EDQS3r0/glRYYit3n+acZU6HzXe9HcvMWrFeFwS4xKGLYmEaiYV/KZHK3MH3qKUDT0Pmu7xKfI5lMQClhriCXQyGJqlyhZG+UmUyWZoOQuBJ8EAEhksl07Rp4GCK5dSv7fZBGsMnyBZLwZJrfY6YtJJp+KO6JpdXZRZeNEIZh1siv25dJyOUSkBZMplmgwUkF60MZf3PFZLI3kFALF8gQk0DMofcN7oNO9SwVcpWaPPkkjEffidMewUTeQ0IuV81kOglOFky/AUDXKHRKEKXLx54FT6Z4OciUshRhGgq5XJIuytCsfgtPpkUmE/c88CBod58sKTroIx3O0+ViGBkYvErZBsV41XS5Jb5lmUej7A/iVMxd+XlGAB52PZPphX8PAHjl6o8DAAI5JtsdA+DA7LUb4EEA84H7oVWATJOh3HjaLI1RKqmwNOerZzA4i1zuLWIyAcKX6XYOZIpSdiZZpCq1kepPInDG1wcyZUnZ52PwWDrFj2pfgKdvANc+WPmalYy/gUqQiaZAQhkO/UNcH18HJRRXulfavV2T8fdUyOUAID54rd3xVZSS7HvfVatEAAAgAElEQVSseL1ppwOTi2feb5TLUQQJQ+DFRRYTMF9nGC7wld8EIBL7Qj/JwNY7TKY79bYvappweIIgTrO0iH7PxY3R6/MdlXACw6K1Hhmfev7fAQBOSfPiXIsVkyknl1NMpgrzbyWXMzQKU6OZXE4xmbjvg0fRApNpGq0KMgmz6qpSTCa3b2UeIo0gk2TxTCMxwBmagcGegzhM4ddJMFTlGgclm+CcZzvCTpUxsGIytfSTyOJTSyBTFXgTyihN0yTgQQBNgUfOJojbA5HAQX4xOA3TBbmceu/WcrmKdDne74KTs3syRTPZBMoGsMy2yjyZNGOp8Xdq2TCi+SL0rZHLzZlMVbIiftdluCEw23+z3pMJADrbCHkEntrnBpmAuXk75xxRwmDZOvo7Dk4kk8nuGLC7i0a3IU9hghbAMrXwGKbyZy3T2NrUNKzxZNLFZ5XNvxUrQ4sZaH9x8XE02IMZeAW2QxqzatNvQOzUAq2AszBjMrVvWsntWzDDIUZHIRxjTUwma4BJNEHKmt8rZRz74wCXNkpMpsSDxsS110vjqaKNAyJdTq8x/c7/bRtPJm17B8zzMjZtlDLYciy0zsBkGijZVd1YrS0ymfyxmiNaMJlGY/TGQiL28l/dBkt5ZloN5GS74TBL0Or/yI+AOA7sP/w98SKdIOEmpvLrLQeZtjH7zFNw3v1uaL35/HIgcYM4DzK52wh5F5Qm4AQwGmRrtUwmvZ7JxIKgIJcDgDBn/h2yGFYrkCkteJgRQtAZWAgSM1vsNlZnFxQcWjh8y5lMxFg+Z3mJCYeL/onYJSYTIQicPVwkJxjLMfxq7yo+/7HP47Hdx1Y6pvjgEMEzz6D3oQ8BAMYZyDRPl2MV6XJVIBMgAItWTKayJ1MLJlOeIRzK+bBQdn95upxeTPIChB8TIMx7z1tavyiXg26fKWDD1Cg40RBqnfZyuaUgk7zvZH9QJ8t2dKeeyRSMAN0B6QhpoXqm1YJ5+g0xlln33z9nMt2eg0yzUYRQ89DvlI5VzY+l75oOh+AgiM/kybRGkKnUT+/c1cXprVnWN5zHkwkQc30Qp1m66NqMv9ckl6OJhx+gX8bzm98H1AQXZJ5MbRnyFSATSTgSDbjt3cb1yXVc6lzK7DWWlanRernc7DaMntjoi/ZfaXd8FaUANC8tXm9CCKy+GLua5HJifEwReslispxiMj3208DzfwTMjiWTKc3mojtMpjv1ti9iWbB5Ci9Ksodla3OAG2PJjunsZelyVcbfcRrjk38jmt8xaU6HMuIZEm7DpDm5nGIyVfghWB0907I6poZxOMM0nmYgU/YgtvBkaqpKvyNZs1EIEMDtG9B3xcKoKWHOMTRQAsyiOXjT3xFN9WiZZC4oMpk4hBzKb5o4Nu8RoIh3vPi7ilLgipJOKQZHFUASSZDJoOJfasrmydkC6QxAAGhaUXpUJZfLPJnaGn8vpMuNwPviPjkryBRKI9+IiOahfC41qsGkJnxNX2r8zSwbZo7JpJrhtYJMifRkiqtBJnq32O3xX325Pl0OANwdeEjAmVUfy96iFANJgZIZMGJQbF/uZEymjQsVx5AmiMBhkmr22Eg1dGv0ZKq6D4E5k6ksl8uYTDEBHSwaUg83xLMfX59LU9OYLWcytfhO6lyuwmRKDg7gegcYHkdCLrcGT6aBOQAHXzp+Hk4CpIzjcpnJFHvQmbhPyuCbSJdjYGGIdDiE0cRkyjyZ2snlAGSSuThlMKWpu2mvvtjb3JKN47RmrFLPeJJjMk1WAJnGYzAtxcwkeOlLwri7s1H0ZAIALxhl0mmt38fgx/429E//CXrRDEQjSFIDUwkALTP+TngfwXPPoZPzI3R1FxMH4K5dNP+mGiK6CZ2GMOqkoLLqwHVHt6UnU/EZ43EMxHFOLncvABTMv0OWwKbLFxkL8ikAbk9HqHWgb7cBmcRrnHgIbUMymYbrZTKFcQqTJCBL5HKcc3jchauJ80k0DcQ058bfAGLnAi6Q04zJBJxtvpl++tMAgMH3/wAc3cmedTPPZApLnkzBsFIuBxRlsE1laRY0os0ltTlQpq4U8CE8mSqYI1Z/qVyOUrKwGFVMG31Nnkw8DME8CZ61kEZWFSFEeOtpvXbzYDRtTpYDBJMZyAA9BcCUAyYc3cl6mIUKp4DVzfywVC9qSQPj6ctCamvddx9otwtiWQUmUzCO4RkT9MpG/o68n0obS+npKQKnA6KvzsQxNHou38n5QSTAP393xi4BgO2rXbCU43RfXOeq8WeVckwBMk1P50qJdZQfpaBkNel9Zb3wKbgkxFcHH6p9yepMpsXnlaYcnFIMwyFeGb7SWioHiDTeSlCRMWB2G+ZFEZATH7a3EymX6kFm6eL1NrY2YfCw2fhbMrjDWQy7U7qnYw8AAR7/BYDFwDP/Lyxl/H16Cui6sCj5Fq47INM3QRHLEsbfMctApgubO7gxkxT2nYeANIRuAEm4+EB/8tVPYjIWDf5oCchkycnOtEghXQ7AfJLOle0K6iDnHK6pYRiKHahtWy4sJLVWL6fLrejJNIuSWnqpN4rg9ExQjbaSyxFC0LF0zKL5Lq+SD6kJqrZyu1OZh0E8y1gKlUDYhoxXb2n+nflbxcvlcpHcNdUhQSZDXn9nE6QjFtGaBiQLcrmadLmWTCbHECkhqVwspsMhWK9Te5xtSunGfSiQafF6O4YjQKYlcjlmOzDjnCfLW5Iux2DrGlgNk0m/Jq579OqruYa9olHp7MBDCjAL07CdXLGqMjaaBGPy7JvtK12MDj0c35xVmn4jCRARApMW71/F2pik6weZJmG1XE6XzXJaijpWTTZNAbJxceHvRlsCFIlyIFMSswWD66wyOcDy73QWT6b48BCuf4jRSQzHoAjWxGQCsFQyd3MozlUZZPITH3otk0kYfyuAXt+rB5mMzJOpnVwOmJvNxinPgUyrtygDyc4KZzWArC6fxRyTyRutAjKN4Fsuxj06X2DkmEyZbDeaZEwmANj8mZ8BiSL84OtfBNcI4pTOQaYlTKbZ9RTgHN0nnsh+7BouQAiSSzuIbhQT5iK6CYP6jabfQH26nKPZAE0WjL+ZBE2UXE7f3QV13YL5t89TWG1ApgrQwbGB0Oy382SSIJMbn2RyeyVRWFcFCYNNlsvl/EkMRnR0zPn4TBxnbvwNIO1exAWcnouNCgCTJ/8Mxl13wXrwQXSNLmYSZDL0arlcylIMw2Etkylv6N9UhBC4hjsHsLONkfrFtS83exzDkfLx0v1o9YEW8l4BcOeY0YrJ1AaMXFLaQDyj6VgexxlBJkBsrnlavyWTab4hWVsZIF4EmYzSebQ1u57JFE0Bs5v1jYrJZEsm0+yNA2g7O9A2NkAIgb6zg+Todu7PGXxzjJ5ROtZsfiwzmU7hO90sSXOVMnWKhHGwiuTYleroBdFPHzyb/WjnijT/lpK5StBzhbLlpsucybSe3lF4t+pnYtMV6uu/h9vYxPPWo7Uv0XQKSslqnkxREWTSGAeTaYcvnL7Q2vQbaAC4gyHAEhhboo+Lj95cfE3LUgDarEIeqW9uwmB+/YZUdowpgiomU+QBZge4+Chw+b3AX/3m3Phbeg2f+zq+zesOyPRNUMRS6XIJAtlYX96+iEniYUQJsPMgAMDQUqQJKwzgjDN84tlP4H5bsCqGqGeAMMbhyt1fo+PkjL/rmUymo4MlHEnM4JoaJrGYkOqYTGf3ZKow1ZY1G83Tf6hpQhsMGkEmQEjmPAlCGJqB7kCDRUK8+vklQFBJLgcIkEmZYFce44YclFv6MmX+VmF7uZzO5AJck5OJuwXSFU2nRhjS3AK3Sqa0uvF3keKeDodI++J8nIktlESIIvmeEAZ+WsUOvaM78CldKpdjtgMrx2TIPJnWzGSylFzOWFx0OVfuQqQByevX58wrY5FFlDjbCAjA1+DJBMx9tdTCwtIpti53wLkwQKwGmUKEhMAixe+RLah5LFhYa/RkmtUYf1Mll6thMtGEgvYW435n2xfAQRZApuVMplXkcu2mVM55xmSKQo4e1eDF51t8AquATOJ+uzyoZzKVz4slZTXJgWDvNHkyaSt4MqmFYp7JpMs/s+zVFwGGrSEFR+zVgUzyGc8lLXqTCJpBM/lAU7HRGJ7VgZeL9C4bfwOAF00KTAX7oYeA93w7fvSVzwEUSBKCKSXQCa1nd6YJEAwxe3kMbWMD9iOPLHxOeHET8fUiyBSiB5PMljLr6uRyjmGDkBReSXbFPHHfKLkcIUSYf+dAphAMdgu2apVcxdEiRNZGa08mAOinQ6ArEl3XzWQK4hQWSZamy01OxHns2PPeito2mD/viUj/Ei6QU4y9s5vYstkM3ue/gN6Hvg+EEPTMHrwkZ/xtLIJMo2gEDl4PMhntJUoC1FJyOfn8VMxZqrxEfH9bs6UHV+n5aiGXA5QfXE4uJ8eKtTCZ+uIZZWOxeKbn6AFsQ8OMdNt7MjUxGIH5WCX7A8XyKTOZbL0BZJIG40qqqNiJasHs3TqGdf/984/c2Sl45KUzAs+YLLItM7lc8bsmp6fwnG6jF1xdqfHq3Obft/5a/OvNA342LjjQDIqjNxTIdD65nCMtIaanIahG4FRYDJylvCg5t1QOwRh44U/wl8YH4Cf114EQAsOu9+pdqAq5nJYC0MV35+ArMZnMOpBpKjayzL6Uy53UK0+WVRIxgAAJFvszlTC3PF2OIfTiDJjNKp4JPyYAeO/fBw6fgxkfIQpSJCen0DYW+9BvtboDMn0TFDUtWCzOjL8tV8ddg6sAgBu6Duw+DADQNdF4JLkB5dNvfBqvjF7BD18UlMpT6tf6eYQJQxdykut2Fz2ZKoy/1UQWzgTTaByLQb8OZHJ0BxrRVvZk8htAJm8Uwe3Pm159bxdxg1wOECCTH8/Bm+BLX8LuG5/H9ZdmmQStskrpcoBIY2mMJVUgU8uEuU4pXU4triuZTHKhZSi/Gk0Ops4mSFc0aBpJMyZTkjKECVtY3K8KMimzTj8HMiVdu/Y4l1YwzMxsPcYqZVSA3NGjdCmTCbYDO3nrPZksXQOP4komk2t2sb8F4PrNRiaT54qJiqfnA5mU5DGTy8VzYGT7yryBbGYyFc+7RjVoMJEiALcHawOZUsbhRWlmwp8vJeNKS7vvQRKAcA6SzI1382U4NsbdjaJcLmnwZFrB+DtaUS7HRiPwMITriXFokJC1eTIBWJowd2skQaaSJ1OeyWRUejIxxPsCZDIuLmcyVcmzy6UAheQoBzLJPzOd1VsUQghCjYDV7c6q8Sf3/PvjCG7PbLXrmI5GmJkuwo35osIdVMjlYq/AZAIA7Sf+Li74pzBu30QSc0wJRZcshgLMD+wUnAPTv7mFznd/N4iWS2OTn+Pv9RHfuFFIeIt4FzaZLGUy1Y17rvSGm4RFZjOXHkNKLgfIhLm8XA681TgqmAQlIBM+Us0C67cADyTItEXG8FMOrd9fvydTzATItITdMjkW56nbm38fattz428A2uAyHBIhmNYn2y6r6Wc/Cx5F6H7ow+LzzDmTydQoqCkXe/F8nh7KPq1OLtfoi1KqQmJu5snUwGSSwIdOxP1QzWRaDjIpgFtVKkEmbXsdxt9izEwnM6Sg0Cs2hNqWbVBMabcdo7eNJ5PyaEyKEveVPJnCKWD25j1ZJpcTc6t/PIF1/33Zy7XdnYInE/c0RJYHvTT3Z0ymsifT6RCe3T2TeboCz84NMt2UIJM/f9aoJmwBikymsy+BbUNDkAhPps7AAjnD962qpg3z1vWNTwJpiKesDxZSnqvKsDTEbfvKEsjEUgaNAzTHhL2rv4JcTq8x/p6Jvsjoi3ssHrazEqmqOEpFj1chQdS3t2D4oyXG3/N0uUVPJn8Osj/6dwDdgXn8FXDGEY2m3/Km38AdkOmboohlwUgT+HGKYBrD6hi42pUgk+UCfcFSMohoPFTjzznHr3/t13GlewWP9b8NABDpyNJKyhXEKToqYWqwIRaUjOWYTFXG32JiCr0YjqnBS2uYTFIuRwhBx+iszGRqksuJiOn5IkDf3VvKZOpYOvxkvss7+8xf4sLhl8BA8epXa/6WpQLZznkyAWKxoQAhx6g4Rrsv9O0ryuX8klzOovVMJk3uKFIqF1bOJkhXNGgaEqSScTCTAGRdutyqIFOeyRRJkOlMnkz+ECGTLDfOqg3UIZstgrkhX01xx4HOGZJAnI8gDaATHUYLmUfbCpO00fi7Y3Rwc4tAu3HQ6Mk0k/eTybRzeTKVzdvnnkwaNvacjCFUBzKFhMCsOD8GtQEaCZBpTcbf6ntWG38rT6YipT4KRjDk6SHO4iLXNjQcD/YK/jVpzLL3WyjDEYBEC+AsykkP21R8IJoo1xf/diO+nnQ5s71crmfp6NnF6+klOSZTZbpc2orJRHUCQhbZZlWlbwlAITmey+UU4fIsTCYAiHWA14FMenHhBggmk9NCKgcIT6ap4YB0NHQ3LTg9A1oOzMmYTLEHWMWdTPN7P4hDZwPOy18HZ8BUc9BtMsn2jhEOdaQjD52cVC7/OdPdLngcZ9cFAEJmw8UI5pKFTx2TqSMb52kJrC/L5QBh/h3fuiV+x1LJNG0BMsWL8gUrEb1HaLbYAXY2wUGwTcbwohTaxkYmv19XBYlIl1vOZBLjd3cwf56I42TnCwDMTdGHpaNbOGtN/+xJ0MEA7re/FwDQM3rwUwky6QSQAEmeyXQSiIV2E5OpLcjkGu5K6XJzkEncX3Z53laL1iWhJ2pBryo5PgHt9UAr5tVVS5NsiXQ6Q0zMc/v0jNFdo/G38mSSoQg1jNnGdLlIMZnKcjnls6nBvK/IZFKeTFGQgMQamFPR9znVcvL09BQz++xyOQDnN/++9VXxr1cEdHeudnF8YwrOeeX4s0qpcJvpMFibHxOgkrLPlyyHZ38PGNyN15x3Lkiey2VYKzCZzG4BZFJ/p+XYjKvI5WqN3hWTaVOsUSI/PnNvmYQpNLlmWmAybW5BD0aNnky2QRHFCeIwzYDZrKKZkMsBYlPykY/CPPgiACAce5kNzLdy3QGZvgmK2BbMNIYfiXQ529VxtSdBps5mNpEZRKZzyYHhSwdfwjNHz+DnHvk5ELnYDg1gHFXvLAVJCkf64ZiDDYAzIJqCyoazismkJrLQT9AxNfhsCAKSNTxZzONg3lQWKNktq47JxBiHP44Knhn67u5SkKln6wiUNJAamP7FX6I/fg1WcIIXPl+jD1aDr2Qy5f2lGo2/AeHL1FIup1hGChBSwE8VeKNYV7pcLFDiA9QAzC5oT4FMccZkmkXVi3u1i7WKJxMgQCYWBOBBgKhr1R7n0vJPEXEXlHJ4SboAgmWfqzsIWoBMRMo9gom4z4IkWCuLCZgvoupAJld3cWsLMPdPwP1609GpJRaTfc4KprGrVhkozMvlqEaxebEDEGBjt57JVOW1YmkOCImQmv21MZlmTSCTkmKV0+VOXoIlTw91Fr+DY2g47O0U5XJql6uqCBGNQyu5nDiXbZlMyaEABOzgGJQCbsjXymQahs0N2e1JiN1+BWsunqfL1TGZksMDENdtNLQkhECvCZpYeK1pgg4GSI+OwRhHyjiIYjKV6ektixkUpE6qpy8ymQTbtT3INDEcWIaO+799D5fuL+5WZkymNFhgMhmWiT+69l1wbomx3td66Datr71jTPfFuNT5wAcKv1Lj1XBHgse5hLkoMWGRKbb05gCEMAmhU32BpeCa4r1VwqoqNcfTPMh07V6Ac0SvXwfCMUJCYOsVY8jCZ6cLC3pTLiRC1EuwsqIaInMTOxhjFibQNjfXzmQKpPH3MibTeH8KLfFhb8y/N7XtjPkFAPam6MnI5GwgE08STP/iL9D94Pdmpspdsws/EXOHoYnkT2KaBZDpNBSAx5ZdzQ5r68kECJ/JWdI+XU6BTBRy7i+Pj3YfYMnSoA7lf6MqPTnOwOnzVubJpECm8wAPuoYROmJB3AScpbE4f8uMvzO53PJ0uVqQKZwCVm/ek0UKGKDQdSA2XFgP5EGmXaSnp+BxDF8GIsCt2NzSLSEVyi3+OedIh0NM7TPK5bQ1gEwsBfafEf/3iyDT9tUeglmM2TCsHH9WKVsaQs+G0dqS5YDmpOx2b3ACvPxnwCMfhWPq7UCm1p5MfXHfyrWR8nLSDDubQ9Tas9Xb6eIc8vKzMhPrM0OGtUTcAY5fav2++YqjNNswKz/b2tYmjGS2xJNJAwtl2m1VupyRm+ve+7OwmLjngll0h8mEOyDTN0VR04KexoLJNIthdw10jA62OMUN28kmMl2aeiu3/V9/9texZW/hxx74sSwFpRFkiufR0mZPLjCCoUhSse1KJpOZMZkE0yhkI2zam9mAlA6HoL1eIYmiY3ZWlsvVUUz9SQTOi4au+t4ukttHiwNbrjqmnjGE6JuHiF57DYMf+WFcOPwK3nhhVK3hzUCmRU+mRuNvQEjmWsrlbIOCEMCXgFCT8bdiMtFAnE+KmWBNEQL0BZtM43HGOFCL+7IcjRACk5qZn8/yY1TUbJY1/mFXHN95QCbTopjFrJa15hiOcBVbIpcj0qw+HIvzEqTrB5mCOM9kWgRnFJOJpAzRvgQ9K/wtZqb42a7GMV2DJ9Oi8beYBi7dP8D2lW61R5GSy1XcYya1ARoiNtcnl8uYTBVyudp0uZOXcyDT4rV0TA23ujtIj4+RTsVYlSYNnkyAkAS8Bcbf8f6+OE7O0OtwmF66lNbeplQC0HiJBCVM0ko2oJd4sJg4dwueTIZI04kPDmHs7S2VlukmbeXJBIiEueT4GLGUfFHpI2HaZ2MWMpNCq2NR1XgytQGZOGNg4zHGugPLoHjiJx/ED//yuwqvyXzKWLSwiNQpwafu+U4QyFhl2kE3bbju3jFmtyxY998D48Je4VeUUDi6g9Nt6bWWB5liDRadYY8037thGlaGHXTlmOOVJDiqT8iDTNa1a+IzX30VLBghpBSW0QZkWpSrmHIH25u1exYie7vAZFq78XfMYCKujQBXNT7yYAcn0HpzZgpx7ILxNx1cAgBos/0zHYv/V3+FdDhET0rl/n/23jRmtuQ8D3uq6uzd/e3fXWbuMpzhziFFKeIMJUakqT9REiBSJEWJf9CA4cQBYv9KAkSJgfywYAcIAgSIEQSOESWwZSMQJCWSDQmCLckeUrYobsOdQ84+d+767d191lryo6pOnz59zunT/fVI0eW8wGBm+uvlLHWq3nre53leQBeyUjEDmQDNbJdVkMkYM7cbf/f3ZGqWyy0HmVgbyGSfjyXzlW0Xb4MfHYP1MYfvEaUn0zhBAfdyPj0ew7ka6rmlCzir5YqtUcrlasbfDd3llhl/15lMAOBRAe4M4D09k8tZ031+clI2RGCDlly5tj6qJIHKMoz9ARhd/Tra87pUh7mjH+jNf7ADxPOMsoObet9ydGdyablc6FIkmcDkdNNMpvZO2b3iu/9UA7fP/oJmWy0DmQJnNU8mQI8pzAgL1GU4DA9xJbxSrn+9vq7Ng2vyECAM3o4uhBcq1GbuawTPJaj5nTqo6OztwS0m4IVq7YTruxTIrY9ZnckUA15lrbv1E/AMYSKf5mXX0x/meBdkegyC+Bpksp5MgWlNekNI3GFsxmSCTg6KTOJ7J9/DH7/9x/jchz+HwJklQ5nbvkFJcgHfrDWerWLbDnODQYsn07xcrsB5KZUDNMjEapTCoTtcw/i7mWJqF8nqIuAcXgGKorPqOfAdZGYznv/rPwEAHPyN/wJXL74FpQhe/VoDE6qWOJSeTBXj7zaZF3Zva5BJLl9cbavcab7c+DtPuDazjfX1ZHJctp6lW9rTgqq83Ax2yZQ85q3MZEoKUV7n1FBN1wKZ0jPkMoIXMsRZuzFiyEIkkDrJa/EWA2abpGxiQCaebrSzHKA3UYGrmUxNtH6XuXhwaBhid814akjYJ6aKfoUJjC8hl7MA0aInk76Wn/rF9+Ln/6sfa/6wMf5uAplCJwShOQqnZ+vmHmG9pzrlcnUm09nrFZBpMdEJHIa3ByZpeUsDurq7XBfI1JfJtJonE38w84QbDSScWCDns26M64ZDHYzc0VJPpozLxmONixgR0fNWK5PpwYNOqVx5LC5rTdwW3ru/D358hMJ2KeQELklAnfVAJuLTUjq5EHb+MUwmKRXSniCTnEwApXDuBK0blFIuR8gCk8mhBOf+EMlTmjmgUh/DDuBentxDfORh8BPPt/7W8RYFGCuZTKKQEILAIzGu0O5xkPCkcT4eeta8fJ4dYfMEywQFAO+26ZL5+mvIDHPAtxKCjmjq7uSeaQBmetZvnRHhHvYsyPQOMJmyUi7XvWZNTlINMm3NQAMazMvlMNIgkxc/qH+8V4z/8I9AXHdONrnlbSGTOu8qQaY6k8mATDt+c1W91Xy3Iebkcit4MhGln61FuZwFmZotGmwENUkf3yCTiY6Mafw0RXZJkClwGU6VGftda2GZK/Y0/jbXus34O3TCUvra+FsVJlNSAf5dmYCHW3AOD2c/eWhApkdHmJr82R22XJNwZ47JJIxcdewN4K7jybQJuZyVyj39Gd25UMwWggPjPXn01qRx/lklApdBFgI8lxtmMjU3POkd3/4tYO8Z4PqPlObkXeH6DPmqIJPZI1oGFPUoboxu4Omdp9s+2fx1bfd7+hAYHIL5DqhDUGCgwcM1gmcC1LVMpkXjb8/MZ23m375DQYoWkKnOZCIE3gc/o38X3rtMJrwLMj0WQXwPjBfIuUQ65fAHLqAUnsxS3CG8XMgcpR8mngn86jd/FQN3gF/6wC8BAGSaAJSCs265nKv0wuEainHZYS6KuuVyUy2XE/RiHmQ6PV14EFcFmQohUQjVSDG17UWrxqzOFb2g8oftkrlR4CAXORzqYPrC5+Hdvg3/mWdw9YNXEfEz/ODLDYliC8gUF3E/uZzISsO7ZRF5TsmO6jT+Tjj80IGYTABKQcQ5EOnkjBBjl6oAACAASURBVET6HjKZlRv2mSdTC8i0Rnc5m/jHZoJey/jbMplCt2zx2vi7TohEmQUzb5dcWrP6/B2Uy9mWzbq7XPM5n1/RYyS/d6I3Mg3skIlh+V0jxaWMv+1mspC17nJmAXZcBi9sSW6sXK5hsxWwEKA5Mmf4ZyKXc9qYTBd3SpCpugm2EXoMbxnJSP7Gm+V3dDOZ+vlM5VyCEvQ2O63652xFAmTCQdTMzP8yseVvLfVkaqvixjxGCD0mm7rLKQUUDx7AqbFqEJ8A//NHZ4arWI3JxA72IY6OUdhkUxB4ZArQ9TYBLGDwpDYmXQhnHmRKJwWUAsLR8nlJXOi18dwJWzcoLnPhUgcxJYtMJrM5PH32RwEAz3yfYpi3bA4BTF/8NiAJhp/56ca/R26EqUrhPvFE2WHOsld9EuMAS8aByBrnvZGRy9kOqzZst7QqU5AOBnCuXkX+2uvIYm3QGnrdDA0uNKC6MAaP7oMpjul51vzBWqjoAPu4QJxzw2TadHc5CVcVS+Vyk3OOIDsBHVZApjCASirsEjfEmAwRZqt3SlJKYfyHf4Dok58EG84AvKE3BFc5AF4CD8TzoLJ5udzQHbauu3VT7a4YuINFkKmju1zJrjEgU6NcDljaYS6oMZnE8QnYBjrLAQBhDHQ0gogz5HAvDTycSLPp7Fo3+jKZnGYmU71AELCW7nJS6I2wV+0uN7uOLJtADPfmWKklk+noEeILY8Ww1XJNwt258+SGSXjhD+D8eXWXu/ei9rK6+Un9/xWwzwsdbB0EmslUiN5FoaYIXQaW6uPcNJNpbePvyUPgtReAZ38eIAR+TWbaFF6wovE3UI7f3ORp1KX4u//238WvfOpXVjpcOx8sgNyTR8BQ79M830HuXgGO1wOZilyAGL/R+v1mu7twzXzWZv7tOwyM6+KXJXDMvrwGMgHwPvozAADuhAsEih/GeBdkegyC+j5YUYAoDSoEAxdITnGjyHBPJOAmCXCVBm7unT3A77/x+/ilD/wStjy9yKs4AQK9yW2Xywm4Sk9+7mje9I8OBs1yuVC/P0s000jVQaazs4U2j6t6MnVJ0WyyWvdkAgDe0WFu4DMUMsdQeoi/+KcYfPrT+vXnn8Ph2/8Gb790Wi7AZdQ8mSxtdL67XMsmfkdXg/v6MkUe6yeXizm80IGcTEGHQ5D0vGQyWZ8gKjLwYp7J1OR55FK3N8hkE7UqkymJGByy6P/RK5JTZDKCH3m60tPlyWRBpg5fJmbkcrmRTSUiWYnmuyy4kOBSaSZT0dxdDgCwNUQ69JDfP5tR42sRm432dZpfSi5nTc1LJtMqEi+eISeA1wAyRW4EQnOkzlbZDOCy0SmXa2IySYF0fK9TLhe4DG/4BmSaYzJ1JHThTj/jb6GZQX26kwFA8fBBCa6PggKQwLYkmzH/9rd7gkwNcrkiRkj0s7HIZGIgSoI/fAi3zmQ6fQ04f3Ou2uh4DLynBNDZP9ByObu54AQ+jYF15goAzADa6bTJS2Te+NvO432YTOJcX9dT1s5kAoCQ+ogJXfRkMhuv+KpmtXzsewSDIpvzh6rG9OuvgDgK4fM/0fj3yIkQ8xjezRvI79wBMPPh82iMA6wnl4tMzlAHmZrkcoA2/85efw2pMTz2vW6vmVnTgflrKI6PEdAMcU+QCYNDHJDzksmksgwy6fb3WSXSQsBB0Wn8ncUF8lwtMJlInckE4JQdYJh3+0E2Rf7qqyjeeBOjn/7s3OvW95GwFK7ZSBHPXTD+bpPKASt6Mhm5nFKqsyOqjf5Mpu75KnBnIJPiHOLsDM7+ZuRygJbMiWmOFO7ljL9dimNhQaYOwLO3XM48Z+Y5bO0u52q53IIFhLWdqBh/V+VTzuQEvHYMFmQSR0eILzJIIjEcthTganI5C/JeeIP1jL834cl090Xg2kfL7pOL5t8jHN0ZX1ou57sMvmk+Mvz/i1zuu7+jvXKf/QUAGgjbqPF3DWSyTCbmM1wbXMO1wbWVDreVuTZ9CAx1juEGDLl7uDaTqcgEYOVytZzHyuUAtJp/+w4tQaalcjkA3r4+bu4Ec+vBD2u8CzI9BkE8H5TnCMz6EgxcYHwfNwsOAYX7+RgAKUGmP3rtX4ERhs996HPld8g0BTXV/zaQKSskqGJwaA4a2falBmRqYTJRRuEGTMvlXAqwMXb9WQVKnJ0tOPCv6slkq/9NAE7TBsK5oqvwXebfQ9+FIgWefRNQWYahBZk++UlcffgVKAW88tUaSGVlhmYipoQiciJj/M21j3BbArNrQKaevkyRNy+XawNv8lQzmeRkAjocaCPE0DCZHAcgGmQSZiHqYpCsJJfzZsbfFmQaR81yq16RnCHHCF7kYtpR6dFMJrOx7ACZHMNk4lP9nk0zmVKzaHZ1lwN00n52NUL+4KLV22JiQLNrSDfTXW7Bk6lHQmO7yzVsKAYGZIrpsGwGcNmYdMnlrPF3tUJ38hpyyeGZpK9JLhe6DOfUA9vfR/HmCkymPnK5QixIGLqCP3gI94Y2yBx5+n7sSrIZ829ve7lczrDs6hHzGIHSz8ZidzmK7WwKcA7nSg1kspsmORufmsnUE2Q62Iccj5HFifkay2RaD2TyTcVxoRAAAMwBCC3ZAckKIJM0INPEDTs3oxHzmplMxqdEmkduZ+LiqTdIaXRaj8l37iF6grZ20YrcCEmRwL15qxzTZUdRkmJPdYNMKU/h22f6wXeA3/tlQMqZrxRvkcvVQab3PIX8tdeRWrncEkPjtrmHHx0h9EQp01kWdHiIbRIjTpKyWLVJNlNeiKVMpvGJviZheqylV/bYghqTCcCFs4+t4girxvgP/hAAMPzsPMhkPdhA03L+oQ1yuV2/C2RazZOJK67zAOs5tMSTySEOhGju7lRuWpcwmfyKpE+cngJKbYzJBBiQKSmQq8vL5R5x82z0ksstM/62rEvTXU60dJczQPGCZC6zINNIy36InvsBgJ+egsWnKOj8/WMlk+kI8XmOxBlj2Cbrq8vlzvSzd/7nJZeTUpt+X/8RIDJjvmb+fXBziPNHCYhQl2KthS7DUOpz3HR3uSYlQa948090N/ErHwLQE2QKnNWMv4EZyGS7y3nrPTP2+jcymQZ6n+YFDAXbBU5e7bTAaAueS8AUd+rPDd3agiv1M9Mql3Pp/N66GsUUcOel4b5RAwgnBJus5yP1OMW7INNjEMT3QTlHJC2lzwHGd3GD62TzTnwX8EdwhF7Iv373G/jZ9/4sDqOZDlulCVgYwqNeJ5OJKgcuKyrtS7vlcoBGf7OYgzkpCBXYcisg0wbkcnZj1sRumZ5lCIbuXJvyksnUCTIxEMLxsZcFSBAgeu4TAIDgQx/CFhljxKaLkrmG6pSt/MW5NtptZTps39T/Pnu961TL0EymGcjUBt7kiYAXaZCJDUxr3XB2vQkjYCIpN+y2u1yjXG4F4+9qdzkLMk3DZrlVr0hOkWMIL3CMJ1PzIhw4ARJZQAKd5t/OQC8MxTvkyWQTOa3bbweZIifC8aGP7GjamqxbVt91Nb1cd7m68Xcx6y63NHim5XINxzh0I4DmiKn1ori8ZK7LG6yRyXT/G0gJQVCYan4TyGQSIXbjxgpyOVOpXdJiOxcSfhcjqhb8wYMZyOToJGdPkI2Yf2/720uNv3PefLwJT+ArfY+d2rjwHYZ9c2+da20g02x8Oh7rL5fbN15ZRyfmayg8sj6TKRjqz52dt0jRnKA0/o7HKzCZjFxu4kadG5SIuojpIpPJyim5WQcuIhfv+aYDTBbl1/lbb6E4TjB8pt23pcpkEmdnEONxyWTKGMWu7AZcUlGZ9/7wV4Av/m/A+F45T9c3raVcLpifB/z3vAfy4gLpsema6M+zk+tR7WxZfvd0CpUkiCLaWy7HjKRCTY9KacImfZl4YY6jw/h7fKyvUaPxd43JNPWvYE8er3wckz/4AwQf+Qjca/NMActkok4KZsYWcedBprPsrJPJtJInk/Ebm+QTw2QinSyvhCcInKA0m16Yc4J+nky66YA+Rn6i5whnb7/XMfcJtrMNmXCk6nJyubAKMnUxmfK+cjnzXYY11mX8DWCxw5wt+HhDEELgO7RcY/JXX4VbJMjl/BxLfR90awv80RHG5wli72IGZtYj3J07T2u8f+aE6zGZHGv8vaY34ckr+pyf+HhZTK0zmfafHAIKOBT0kt3lKEZqsyCTkAppIdu9W5fF3ReB6x8v/9caf3c1OXJ9Bl5IyD5+kHUmkwGZnDWZV14pl6vkPUoZJtOhOT4HORkCIgfO+ik9qsFzAeVYkGn+OAkher+MdiZT4DAE5j4vdLstkgW5sOszAAqcBWB3/vnKx/u4xbsg02MQxNeL/FBWdKOGyQQAd8Z3AG8IVxjWkXDwVz/yV+e+QyYpaBRiy99q3aCkXADKhedywBvpavASuRyg2z5mMYek+nuHrk54VJ5DxnEjyJSJrDegYcGWpol5ep5jsD2fBNEgAB2NlsjlHIBwfOQHGQbPPw/q60WEOA6iH/9xXD36Ku69fI7JaWVRbwGZpsUUcbFEZ+1FGrnvzWRySkAoF3krCydLOLzAgZxOtA9REZdyOX0+1DCZanK5BhBnLePvXECcnoFEERLKZ1XzVSM5RS5DbfxdtLd4LZMtQjqZTK7xtRCGOfFOMZm0J1O7XC50QzzYdyDGBYRqYTIVE4QK2MXFpZhMrd3l+iRaRaK7yzVICofeAIRkGGPWcfKyMWnpcggAlBGA1DyZHnwLOWUYmIp5fRMMzMYkefIm8rfeghQ6sVpq/C1551gCNMuzL5NJZhnE6Sm8G08CADxkoD7FnqSbYTL1lss1G397CMBcClKrRPsuxUGiv3dBLtfEZHJX6S43q57rr6Hw6fqeTJHxVzo/bQErmLcglwt7yeUMyOSFneBsRBxt/F0DWygloAQQRK/VX386wO6bDPnL3134jukXvgAAGHzwysLfyt9xI8RFDPfmLQC6w5wFmRLmYEcukctx48l0+jrw0u+ZH35UztP1Tasqjb/nny/vqacAAPmbbwMA/LDbi6JsOlB59uy9j7ZcTM/zzo2RDXdLj0M1OYJj8gi+QSaTNK26u4y/LZMpSE9qTKYQKsugKvLhNLiCPXW6ckU+/c53EH3iEwuvDz3jt+lkZQGLeB5Usapcrj+TCdDzBHiqwdoOiXDCtQzd3u8FJnfP7nKBS2cMHDNOnIPNgUx0axsi5UiUc2kJ1cNiBU8mb4nxN6V6rjKssdL4uy6XK5mHNalohckEWGaL/o7slVfg8CmEwIKs2Tk4AD86wvQ8Rex2gEzBjmZzmFxdnJ0ClOLcCXv7E1bDgmd5V8fNrrCegNc/PstzG5hMAHAoyCW7yzGMJIFfK2JfJiwA2GYH0RnZGDh+WbO47DF6DFJ1e1x5gf6tXpK5mvG3Zc26wXrrdKPxd3quASXLZAoZCsOuxtHLK/9GkQkoy2RqyPWCbfPstMnlXIpAETgeBavmeILr46w1uSCUwGUK3Anh3H0BGK/XTfRxiXdBpscgLAAyNGhwMHSBi3s4FAIudTXI5I+QFrqC9oHRh3Br69bcd8g0AQlCjLxRB5NJAsqD60q9+PlbvZhMQeQgiwtwor83Ynry56biuAAymYW3ry+TlXg1yuXOM0Tbiwmic+XKEiaTg+vnUxyecAw+/VNzf4uefx4H39f09Ze/UgGqGhIH240lzcWiH0E9dm6t6MnUg8kUW+PvKWhorkM0Y5IRh4HydMZkyjhoi6xvFePvmf5flr5bGc/WZjKp5Ay58EE9pk16O4y/AQMydRh/lyCTAUbnKvobiLTCZNLG382V8IEzwN19vQDmF83jY1pMMSQMW+L8UsbfJcgk15HLme5yDSDTVjAAaIGxTQQ2xGQKXLpQsQV09clx6DzIdP+bSKNdREKPiyYmkx2T6vqT4PfuoZiY9trLQCZgqfl3JmTvqqgFty2TCYIj3Auwu0lPpvwcUrUnlhlvlsslPIEnfTgN9HffoTMmUyvIVPH78FboLmc2jMWx3kAKfjkm03BLzzMXbYwYJ5gDmZhLy2S7K8SFPv+xuwRkAkVCF7vLAdr82xDu8N3bPkCA099erHhOPv8FuCPAu3G9/XcqTCYAyN+6Uyb+E+JhW560fhbQ857PfOBL/wcAA+rER+VcaP3bbMg0BQnDBUau9573AAD42/r+hUuZTItzDz/W+clgLwLPRC8Jh7etNyI0eWeYTLKH79D4OAUlEh5SEH/2PusLpypspjy6CgYF1cBcawuV51BFAba9OJasp6brzNZl4nmQhsmklNJyuS6QyaW9PZksc2rKpwZk6l7PS5Cpba3pLZebAWHCMJnYJplMW1sQiUSi3EubQU8QQFUKsI3R15MJMHNVzfi7ti7aAtkCk8mCdyYnrXpb5a+8ApeYJiDxfF5hQab4okDsjjuYTPO2Gfz0FGx7G4XCesbfl/VkuveiBoQPPzDLc2uMstFeADdkuCLopU3eh5LAH63XAbUprPVHW37bGfe/CUBpFpcJu0Z1mX+7BtDqJZmrMZnSpICC2qxczkrHh1fM8TkohLnGR6vLz4pcQFL7e4vH6e7twJFZR3c5hkAC7gKLyewvasbfAOAQDu5EoC4HXvwnKx/z4xTvgkyPQRBPL/QDac3JXGB8DyzcxZPDJ3FnokGm3yjegiAcH9/90YXvUHECGgTY8rbaQaacQygfJSmjosemUdTKZPJCLZfLjT9ESPXCZKm1dQd+Wy3rK5mLzaIZNcnlGphMgJbMLQOZ/q039fFZP6by+J5/DlHyELtbAj/4chVkutAMLzp7rEomU5+OEbu3e9NBI4+VrIdMNIM3SinkiTX+noBaE+Uqk8l1wHgCUUgopTDNBAa+0yjrWwVkmi1uwoBMO51g2LIopjEUKOAaenKH8TcAvcHrYJ94JZNJv8fS+jcVZaXeYZ2eTJEb4c6uSfo6QKYBcTEUZ8i4XDsB86g+BrtxzCtsq6XBUxSEwG8CmbwBCFE4gjnHHt3YlsUk441SORvMpTW53LeQR7sYcv2ZRk8m8/zx65pBlL6hjZI7mUw1WXBbrMJksp3l3Cf1caiiwOAgwJ6gG+kut+1tQyrZCdJnXDZupuIihqv8RjN032HYTy6gKIWzX9vg2c2MqMrlVvBkMt8njo8BBfCCXsr4e2RYSZOLNpBpxmRKLnJEI6+Xabu8uAAcBxnzOuWREQgSQhs9V1xKYO9y5nrInspx9i9fnCvSqDxH/Cd/gsH1AmTQbnAcuRpkcm9quXXx1pslk+mM+tgW3SBTJjIExAG++g+Bax/TL06PSiZTLmsgUxI3sgTdJ58EXBfqvn72m2S1c7/bIJfjjzRANbyiNzJ9JHPEmPuy5LgsVm0SZFIWZOpYt8bHKSKago1Gc2PIAt1VyZwwxrjZ6du9j8GOCxotbmZsQY45s98gvg+V6+cw5jEKWSz1ZCqE6iWXicyGaloYkKmjsxwwA5nSNmk2ZRoAWSKXC1yK3HQktGCks0lPpu0tiEwhk+yScjkKBQoV7PQz/l7GZAKaQaa+TKaK8bc+PlayZbJXXkW0p5+1+gbbOThAcfQI2UQg8S5KcHEhAgsy6XMVp2dgu7vgQpX+c6vErLvcmnK5e18Hrj2r5a3+ll4/anI5Qgi2rkUGZLqc/9ZIEribBJkMmyhaRy5XZXGZqHqjtoVbMpl65B7eAAApx2+WCBToWahs+romJtPE7KnM3O4FDHkOLX9cscOcFBKSK0ja3F0OAJzdPXhi2mn8HSgCp16EsnYc3uK87KKACEcgt38S+NqvLbVbeJzjXZDpMQhbPSs9mYYaZMLoCTw5ehJ3xneQeQP8I3kK5Uhss8XFWVcoDcjUQl3mWYxChfB8k0gF2/NyuThupLj7Ay2Xy5SRWihj0NnGZHJXYzIlZee2+UlASaU3EE1MpsPDTrncMHDwY29c4OGBD88k8OX5fPCDoNvbeIK/ioevX+D8kVnYs4uFytTAGSDmMeJCLK9O7NwGzu/0otJHvlNuSHORN4I3VmftW08mC8yEs/tPPRfU+KgILjs39x71UMh+EkZKCQKXliCTs7ODTDZ3MuoTeayTfWUAgTZPpjLZInRmTNoQfhiAEwr5jsnlrAeFNf5uYTK5A7w1KgAC5GfNC9GkmGDIfISFfl7WlczZMWJlqHaj1wcc4UUCTgg8Z3FBHZhF9si6GW+CyZQuAZkcCm4Tk+kxML6LNNhCIIz5bYdcLr/6BAAgMdKeTqq7ZTItOadVPJkKCzJduwYw3X1w60qEkSKYxut7btnYNiySLslca3c5HsORXiuT6SA9h9rd000D5r6wyfibzZuzd4Q1m1Unx3AAKEUvZfy9NfCQQSEet1zPmidTH6kcoOVyajTS7aG7ussppT2ZGjpGMkpQmI+60oX6KIGMc5z/s39Wvid+UYNOwyvjOeZpPSJHy+XYaAS2s4P8zbdKJtMRBhjys871JOEJgvF9vY5/9r/TL04flfN0XmMyqSRtBHAJY/Bu3QJ5qDe1yxirzUwmDTKNntCASC/zbwPAuekR2LY1/t4MyFQICabM+OkCmU5SRGo658cEoGykMmf+vWXmnuM7vY+jBJkGg4W/2VxpDmSqGH+fpHqDvcyTCejXNt4WADXIlK3MZGpkc/tbvbrLAXrNEsfHgOuCbi0xzV4h2PY2IIGcO5f06dHHKf3t5XK5WkGyNZyg7C5n71Gd4Wtzl1a5nAGzqt5W2auvILym55as1oXTOTxAepoAEph2yeVswdLsA6zHKpeq7KS5SlyKySSlBpksyEKI8YxaBNoHV0IcCAJvjWO0EbgUQ0XA6mbQl4guf9mlce/rwPAaMJqxjKveqG3hmjyrl1yOmGYWZr3PU46cLI7HvmHX0Dkm5dTsyyyTKWDIUw4cvG9luZyV64uSybR4XdneHtxsjHTSvN5okAlg9U7HtojdxGQSGYQ/BH7sc9on7I1/vdJxP07xLsj0GAQ1nkyRAkCMxnZ8Dxhdw43hDbw1fgu/4xQ4JhJh4IM3TCYyTUDDSHsytTCZRHqBXIbwQ/OgBjtzcjlwDlUsJvW+kcvF4hRKMkDp5KsNZLKJzDjvrm7ZKOVy7vwkkEwKSKmamUxXNJOpzfchUhwfeTvBSx/YWfgboRTRJ34c+9/REoeXv2Ko79l4AWSycrkk58urEzu39Cbt4m73+6ArHXNMJrqY7OWGAl0ymUoGWoXJ5HmgVk9fSEyz9s4WLnMX5BNdYU0HSyYTX5PJpBRys/mWxsBvmSdTskQu5zkUieNDJRoYbWvlvW6UcjmiACk7jb8vkMDdosjPmhf5aTHFwAnhiSk8FGXntVWDEgqHOuU9zLhm39Ae3gm5WVD9hsq1vebHdiXfkFxuWF/UK8EcCmkBjAff1MfoDRBxCrhuozyx9Ak70GyC9C39nHWaVpYgU/fGNecCfm8mk06inGvXQFwXqiiwd80wBI5ajKpXiGUgk1JKG3/XQBKllAaZhNvMZHK18bfYa2DWNBh/u15/Tybq+6DDIeTJCXwzJV/Gk2ngO0ioak0cwfw5uVwf029AG3+rgZ7jO+Vy0oBMDeEyCq5bE8CRHqLbO/CvhTj9tX9crkfTL/wxwBiiqxkQtcuCIjdCIQsUooB76xaKO9qTyQ0YHmEECrlQya9GxjP4D18CrnwEeP/PaDBlegSXuoAiKBaYTEmjFBXQHeaYGb/LAPsmTyZxdAQQgtGT+nzjPubfwQ44GPzsBMR1QUejjTGZ0kLAhxnPS7rLBeJizo8JmMnlqkwmuq2lj8Xp8jXeRieTyYBMhM2uFfFcqEz//2mqGSZ7QTtQWUpWegDCc55MRTIzpm6JpUwmQEtKe3SXA7Tshx+fwNnb68U87BsWsBL55dgtJVvWW8ZkWixItoYbzLrLtcjlSpuAene5ksmkzy80hT85nYLfvYfBTQ1IZLXiBjs4QGokSkmn8fe8XE6cnmomk5SXMv5eC2Q6fU1f14onEcK9xvkvvBLCAwGdri9P9wlBpAjoYH3mWz2S4hJyuXsvzknlgKptRRfIZIpvvTvMjWogk1pLGglUjL+rc8/EKEzK7nKO9lXaf9/KcrnC7JF4h1yO7e3CyS6QjltAJlcbf1O/9tkukInHEG4IfPhnNZj8tX+00nE/TvEuyPQYhGUyhYqA+KaD2cU9YOs6bo5u4iK/wN8XD/EsV4jCoHzwqqGSdKlcDtkYhYpm3hVVuZypsjVJ5oLIAc8lpsUZlBiVlRTbargul7MLWm8mU4tcztLtBy1MJpXnWv7QEP43vwZPKLz0vubkbPDc83Be/zauPBnMJHMNINPKcjmgl/m3lctJqVqZTLai7fkUMo5BXbN7mwOZfBADMvFCM5naQCaP9pfLAYaanc9AplzkCBDi9//Bt3B8d4U299kYudQJOzeASLjM+HuJXM53KFLHA+IYXHIIJcrPbiLKyq3SY7OrBXkmMrg7BNlxM+tiUkzK5H4PF7i4RIc5n/kzT6ai2fy5KXKT5DaNM9tx6FgYlHtDxt9N5vM2mFthMt3XIFPqBgg4aWQxAUBgxkwcDkFHI2R3NTjczWTqKZdrkZ81BX/wACSKQIfDEmQ6uG42b8fvPMjUZvieiQxSSVDptHsyJecodrtApnlPJsF7dq2BkcydnsC3nVwu4ck0ChzEBMinLYCssybIdH4GOdQbti6JQCh4K8jkMAIuAeIoONLDcHCAvY+HyL7/fSRf/jIAYPKFzyP8yPvBXNUNMplnT/sy3UT+pgaZ/NDBA2EA0g7/n4zHCKZHwPN/XVepB4fA9AiEEFB4i3K5NGl9vvynnoJzUoBK1YPJ1CCXOzoG29vDcN8Armc91hpCcEa2EeSzXEJsyPg74xIuzHhuKY4UuUAyLnRnua35tZ8YJpNMZs+0v30NXFGI8xXkcianIg0gE6MMDAEYqwBZFSaTBZmWyeUA9PJluiyTqXG9qWxa26LOfhFxjwAAIABJREFUZGIblMoBANvSz4rKyaXkcvazhbe1xJNp0h9kqvjH5ULCZWShMGQLZItMJuv9NO/JlL36GgBg8LSRjteZTAeHyIzfV6cnU7k+GpDJ5HqFUHDXMP6edZdbA2S6Z+RiVaAl2msE+7wDPW7FSf+iaT1Iao4x3BzINDUEgLYiamvkUw3AXJ8HmaoNeNpiJeNvwDyvet+Up2IjTKY5FuX0oW4qZRi8rs8ABRTbH9B/W6GIac+Jk/nfq4aztwe3mCJpA5mMXI7Uc6IOuRzLp+As1PLCj/4C8O3/dyPF17+I8S7I9BiE9WQKFAF8ql3vpw+B0XXcGGpD0Acqx18bx7pdZROTKUlKudwknzSaxqp0jFyF8EKTcFXlciYBktPFjb0XmopIPIXio5J5tIzJ1NuTqUUuFxu6fZtcDkC7ZO6Lf4zEJXjtdnNyFj3/PADg5vAEx3cmOL0/bQWZ4iJGkotWYKSMHQsyLfdligwQlHLR6slkvTlcov9NmbnvVeNvPwA1EhfLZBq2UHU91l8uB+gFLss5xMVF6ck0mh7g5a88xKtfa/fDWojkFJkxlebm0NqAsFWYTCnzgDRFInRitllPJrOJMiBTF5MJAMi2RH6cznUisjHNpxiahG+fjC/XYa4CFGZc9JYG5Fw/140gk/Xp4OlcM4DLxCTlGHUxmdyK8ff9bwGj68gBBJw2ynmACnWcS3g3byK9ZxhFncbf/UCmJmZQWxQPH8C9cgWEEAMy5bjyhJ7zij4b6yWx7RmQKV8CMtU2U7G5x0w0g0yB6S6X7zaAHg1yOWuo3teXiR0cgJzOmEweiTtbx3fFwHeQEAWedINMUiqk4/4gkzy/gBhY6UkHk0lwxEAjU9ah2gMHjoQjPIwGV7F1awq2vY2TX/vH4EdHyL7zXQw//n7zZd1MJkBvLt2bN1Dcu4dsWsALHdyXRk7UAjIppZDKAj7zgI/+R7PfMsarlHjgan48tsnlAG3+TSVweIalgH2jXO7oCM7+PtyAwfFoL08mABizHUTcgEw7OxtlMnlLmEwT21kuOQIdNjOZVDrb+I8iH4+ws1LHIctkYg1yOQBgiADWLJc7zfR12QkWGdk2ZiDT8o39vCdTogGQjkiKBKEbIuMCDiXN7BZ/q1d3OcAymY7LbpSbCmuqLotLdhwzOV7hbi2Xy/k9/JgAI5ebMZma5O02j1gw/s4nAGHlfbKeTNkrWna09YGn9eHEi55MeQkydXgylUymUyilIE5PQU0+vw6TyUrs1mIy3X1Rg8GHH6oc324jk4luexBQKI4vATIlel2Ta3ZWawq7l1m6V6jH/W8BSs6zuDADZ3sZf/fNKyugcJEJ5FC9/SgXvsqCx3NMpodAdFCymL3QyPm2ntF/X0EyZ5nUBbRMvWlMst09uMVkAWgtj9HI5VC/J6Xx9+K8zNIxCmJyih/7K3qu/NZv9j7uxyneUZCJEPIzhJCXCCEvE0J+ueHvtwkhf0AI+QYh5F8SQm5U/vY/EkK+TQj5LiHkfyGb5MY+ZkGMXM5XVHvWTB/qCWd0HTdG+pI+5Yzw0+encD3WyGSSaQoahNjytqCgGgEeksRQYPAiCzJV5HIDCzItbux948ofTxNIPiyZR+LsDCQMy+54Nlb1ZIrN5Bg4bUymxQ2Ee0VTMZvMv5VSyL7wBXzrlgdBmzcf/vveC7a3h8O7fwoQaDZTi1wuFSmmeb6cybR9AwDpzWQC9KKUi7wRZLJMJkfp68CcQi/CFXon8QNQPmMyTTPRyiBZxfgb0AuIvLgAlCpBpmiqE5DTe/3uLQAgPUMu9THnZsZqu5YWKIopW8pkShwfSOIyMduoJ5OVgywBmSygqkYcqpCNoOeUTzEwsq09crG2XA6Yv4dtvjxNkZkKadM4sxvKaZEA4RIvip7RxagDNPtIVJlM1z6qOwQWAAmb72NYoY67t28he6g9YDbSXW4lJtPDsjubZTJFoYsLKsE3ADJtGWlEO5OpWbpiq+BE0EYJoVfkGPIU6XYHyCSqcjkjHekpmXP290HOTksmk5bLrdldzoBMsk0C4PiAyJBOCt2tctRfLsctyNQll+M5FGmQr0BvpLiUUI7UcrnhddD0EbZ/8Rcw/hf/Ame/9f8AAAYfedJ8WQ8mUxHDu3kLEALZeQwvdHCvZDI1F1Kys9cBAMH1H521YR4cliCTAw98Jbmc7jD3xMlyJlNT0wF+fATn4ACEEAy2/X5yOQATtoOhBZl2dzbGZEoLOQOZWsDOC8M89M/vgS4wmYxcrsJk2gpcPFA7YNMVQKYOJhMAMBWC0ArI5C4ymbrkct4qIJNjCwqGydTgOVaNmVyuA4TvIZezuV1aCPCTYzh7G2YyjQwgm3eDx8sisNfS3V5u/L2SXG7WrKNpnWn3ZDJgltk+WSZT/sqrgOti8N5bIJQsdpc7PEBuihW5H7eDxsFMLienMVRRgBiQiV2CydTHH2wh7n0duPJh3dTBRtjMZCoAnFCF7NH6zGFhQAlRl1FJoc2ee3ir1sP6rLZ5jrZGE4sLVXC2hyfTGnK5ItNMprXlcgb0yeaYTI9KPyZ9fAYEi57SL6wgmbMFroKo1vnH2duFW0zBuWosiLkgcECg3No5Ws/XmoWEUgosPgdX5h4+8WNajv7VH07J3DsGMhFCGID/FcC/C+DDAP4yIeTDtbf9TwD+oVLqYwD+NoD/wXz2JwF8CsDHADwL4BMAPvNOHetf9LAgjQcK4RLtxwQAo+u4tXULN0c38Tf3PwGqJBwXKLL5CVwpBWWZTGaD0mT+TTPDZohMYhHu6K4XRTqTy8XtIFM6TaH4qETrxdkZ2O5ihW1VTyYrRatTiG2SGrV0lwOaQab8tddQvP02XnzagZTNkz2hFNFzz0F+6fN44r07ePnLD6DS8ULL6oGjzyURSbnJbQ3HB0bXgdMeTCazCMWZaO3aZplMjvHgoTTTlZ1qB5wgmhl/F93G3y51S6lVnwhdCjrW48iCTMFUX5+Te+0A0EIkp8gNkykjeuwuNf52Z9W/prBMJpKlM5DpHfBk8o3cgrjtcjkAKLb0Pchfe23u70opzWQym4RdjDHO1pfLecybyeVWYN/k5hp5DaDrrIoaG3bj5ZlM0yXd5RzLZOIZcPQScPVZLRstZoa79bDVwSSX8G7dRn5yXn5XazBHm6ZukMnE79+Hc1UnURZkIoTg3AFUm1H1ClEymdpApqJZuhJbUJazxmvinmhQLtlu2ODZ9WLO+NswmToS3Go4B/tgZzW5HFmvSuw7FCkDVNayWWE+wNOSIr+KJ1MRWpCpo7ucleI1AN2MEnChIJmArwK4w6tAPsHuL/4cICWO/t7fA9vdRXDNzEc9mEy6w5wuKKXjFG7AcGQabJRGqrXIvvx/AgCC25+avTg4BGIDvhIPAvPjUaVJO5PpqacAAE+c9DH+njVGsCEeHYEd6HONtr1+xt8AYncPI2me5Q0ymTIu4BkWMFrOZ2xAJu/8LtgCk8l2l5utQ1uBgwdqD27cLmGsxzImE0UARWe/QTwP0nhjnqan8KhXztFNMWsjvvw5daiDgAV6XPN0OZOplMuJZtNvYE5+03qMrvVuERDHJ2D17paXDDo055Gv3ykLqHTzYiPN8m9gJgNYDWRywjlPpiZpUmt3uWyiPWFMBMb4O3vlFXi3b4G6LvzQaewul3tbACkQhB2dN5mjvz89gzjTYI4a6Xnnz9T4WykNMtVAFkTNxt8ZF3jIJOKH7TnishDmmuV1kOn1LwC//TeAN/545e8sjb9XZTLdfVF7GI2uz71c5jy9usutDjLxTKAgan25XOW5LmPysOwsB8zkfLl/XRedVugwZ88ph2otArI9zWQCFrssAgA1+ZJslcvNz8sqScCyKSSozn0I0Qbgd78KPPh272N/XOKdZDI9B+BlpdSrSqkcwP8N4Gdr7/kwgD80//1Hlb8rAAEAD4APwAXQf1X+IQvryeQRBuEYPyYA2LqO0Anxuz//u/h39nV7YteRC2itynNAKW38bSiyTb5MxBhYekOTsFQ6L5VyuXgxqQ5M94U8ERpksnI504miHqETghHWn8lUNPsdTc9z+JHTaGJrQaaigTkyeeEFAMDXnqaQsn2yHzz/HPj9+3jP0w5O78c4nuwstKy2gFnK435mfru3V2MyFXypXM4x1XRKkrnOcgBAwgGI0O/jhcQ07/BkWpHJFHoMbKKTfwsy+RO9QTt9MIXsW61KTpEpcx3NS8uMv1PH65bLMe3JRNOkTMzeCU8mryeTKRkZ4KcGMmUiA1ccUaTlAfuXZTJV5XKF6M2+yc2muUsul/Bkjt14mRgvNf4mmsn06Hsa2DBMJq9QrZvgqgmmd+smpNLn3slkAuZkwW2R8X7XUkmJ4tEjuDUmEwBMPAI2Ea3NCPqGy1xETtQql7NV4vrxWrkcOGlkMjEDMk23mkAmw3yteTIB/ZlMbH8fbDJGKK25bdKv+1JDEEIgXAIiVHPy7PgAz2eS6h4gk5IScjxGHupntpPJZLpBlde0Ei6j4FJCsAK+CoGhHgvetoPhZz8LVRQYfOpTIIaFUvXQq8eMRTiFd+sWACCPCzg+wxQBCho0M5l4hvQb/0Sfh+l4BkB3a5vq++xQD7Iml5NJWsrA6sF2d5H7CjdOsNSUuS6XU0q3pncO9Lo82PExPevHZEq9XexIK73fBd+YXE7Cg5lrW+Ry45MUlBJ4Z/cXmEzWu0pVjL9HgYsHahdh2t7Zth42p2pjMhEZQpF2udxOsNN5P8qNXs+NfeRGmulepJ2eTEqpGcjUxWSqdKtqi5LJdDGGyjLt37bBYJE+D5r3LxY0hS0kxs6WVhO0FUqz8UKu2BqOX3aXK8SKTKZ8XpZnO/7mr7wC/5n3AgD8gYOstrlmu7vI/G1QTNv9mGwYb1bLIFSmy6OzxtxNCIHLyOpMptPX9Rpdk4sh3NNgaD4/D2dc4hFTyMYFkrbmEEsiHxfIoJCjtl5b5lR8vPJ3JuvK5e59XZ977TmvMgDb4jLG3zyznkyXZDJV557pw3kmk8kD84IAu08BR+uATO3rNdvbg2f2mk0gEyn0/RX1cyzlcvPzsjg9hWNBYSOpxMf+Y60i+eZv9D72xyXeSZDpSQBvVf7/jnmtGl8H8PPmv/9DACNCyL5S6t9Ag073zD+/r5T67jt4rH+hw3oyuXCQM8wxmcowC5rDxELSbVvs0iAoF5QmkImZxMUbWJBpZvrXZfxtmUyeCOCorTkmk9MAMhFCMHAH/T2ZMt44KcfneaMfE6CNyulg0Mhkmr7wArz3PoPjHQUuOqrVxpfp6vQlEAr84OJHGz2ZAICrZLlcDtAd5np4MtnznWZiqfG3YyZDimRhw0J8H1RYTybR2V3Oox4KsZonE5voxciCTM7EgJFc4aJvJ61Ey+UIAWKTfETLPJkcr1MuRwhB7gagWVpKWjYrl9Nj3DXMjjaQyR5vHBYgvoP89dfn/m6fgWF4AEUY9sgY48t4MtXlcsvYdSYyc4265HKZTHoBMssi5xI5lxh2Gn8z8EJqLwIAuPZR/RzkcqlcLi0EvFu3IKlrvmsZyLQcOMt7Sg/F6SlQFHCuzEAmcH0/k4CACoX4YgO+TP52DyZTzZOpqIBMDdeEHuu5cjJqAD0auss5K3oyWZ+V7UKfv8/W98sAAGWruE2bCEczmeIVmExyPAaUQmZBpi5PplyvqQubPhjjb6EMyBSUIBMmD7H3Vz4HABh+5jN6kxJsd/pSlUymIoZz5QqI5yHPFVjAABAk/kGzJ9N3fhuZ2QzNMTgHB3rezKdwiQeJOsiUlIbW9SCiwGRH4Yn2ZnZl1Nl0cjKZAw8G2z6mF3kvwDXz9xAhBYoEbHcHKo4hs8uNHX2MFU+mFuPv8XGKwbYLArXAZLKyQpnMxkDgUjwiewj4RSfTtho2p6JRM5OJqBCSVJhMvqc7/UqJ0/S0UyoHVMx3e4JMtpmJZjK1F2YykUFBablc11oTbOsx15Fb2M8WR4Zlt2Hjbxo4AFFgKzTDaApbyIipAXbaZNb5KnK5UF9raFlRE8jkUAcudRc9mWoG44HLwNMU+VtvwX9G+zEFA3dBLkcYQzHYAxMdfkw2Qt1JzzII1ZbO6S8DPKzMZLr3df3v63UmkxknNTZTxiUeMv0bR3dWaEJT/Y6LHGOqFk21LStvDduA6TpyuSLRxbY6iwv9mEyU6vW+P5NpBgqLXCK/DJOpPvcopbvLNTCZipQD++9bCWSyLOpMqdb8jG1vwzG+rMmkYQ6y+9X6LWkx/uZnZ2AmX7aFfkR7wF/758Bn/1bvY39c4s/b+Pu/BvAZQsjXoOVwbwMQhJD3AvgQgBvQwNRPE0J+qv5hQshfJ4R8mRDy5UcNYMEPS1Dfg6AOKGHIKDTIRNjcg2qrGS7jC0m/TYKs8TfQIpczyb83MotORY/dxWSyxm0+j+DTbcQVTya201ylHbrD/kymvNlHaHqeNfox2XAODxdAJjmdIv7SlzH89GegSAGxMLPMwnvPe8AODyC/9kXcfP8WXk4/BeUtejIBAGjWE2S6DVy83ZlwASjPN8kFUpE2M5liDkIJaKqvI5PjOdNvQHeiIQa4SVKOQqhO42+ueKMpfFP4LoM7nZfL0YsA+0/qZPmkry+Tkct5ASvHTpv0sKzoMXehelWPwvXAKnK5ZRKPVcJ6MnnSMpmaN4qlkSqj8K5uI3/t9bm/22dg4A2BaA+HZIzxJT2ZMiOfzLjoL5czwFRXd7lMbIbJZBsDdDOZjFzu/jd1JWnvaaQ8hVvIVrmcywgYJUhyAbcCMjUxHeeihwSwzSujHvyB3vCXcjnHgcoN7T7Unz97sIKUtCW2/e3GORxolioBM9aN5GhkMuFIz5UXwy6QqSqXW9GTyUiltgQHgYLL1h/nAEAC40fRlDg6PiDyEtALe4BMwnQiTQPLZGoZN1IiMnNPk1zOoRSFVChoDlf6s6rt5AEGn/wk3vNbv4mtf//f0yBTh1QOmO8uRyiFc+MGCsnAzByetYFMX/z7SHY182kOXLc5w/QILgsgSQ5R6Q6okna5HLIxzncUrp/0AIZqvmDcgAfOoQYao21PSzF6VNeLYL88ZptPbEIyl/EKk6kDZBqO9LXuw2QihODCMabVthi4JGwzFRq1XHcZQGJeLgdohvppetrZWQ6oyuX6PadDd2jkct3d5aoM4azoWGsso6eDzVSOk2PNDtm08TcRGZgrwXLRu/DSFBZkmlIzFpoKLkrpc/WawZtXf/bncPabvzV7wQlKkKnN+BvQz/Eik2ky9zuhy7B3+gCQEt4z2kjZjxblcgCQ+ztw8vNyT9AawY6WyxkmkzSd+tYx/gY0w3bl7nL3XtRSqqsfmX/dMvfjDpDprfVApvg8x5QqpHWZqR3HaxTbklw/Jyv5Wd3/FqDEIsCGfsbfgJbMFX3zSn8E5BMoziELiQLrd5cjhMBz6GzuySdaGlphMnmWyZQK4OB9wMmrvf2uuJHLp2gHjwmlCML2XEGa7ygWmExmbV9gMp3NmEzVa/rEx7W89Ics3kmQ6W0ANyv/f8O8VoZS6q5S6ueVUj8K4G+Z186gWU1/opSaKKUmAH4PwE/Uf0Ap9b8rpX5cKfXjh4eH9T//0ATxfXDj/ZNRpTuXDK+W7vwAymqGQwsUtaTfGlPSICzbXzcymcxk6lq5nO0sUZXLNTCZgkhv5nweIqA7pVyOm3anTTHwBr09mZKiuXObBpnakyDn8BD84TzINP3iF7UR7099CiACnLc/IoQQDJ57HtM//SKeeTbChbiGh+P5TYFlMhGW9aPA7t7WNOvzO51vmxl/c2383ZDs5QmHFzDIqV5EqbyY3TN7Dp5XMplig7p3yeUA9JbMhS6DH5sFfGsEFBQkcfHUR3WCuBrINIIXukhygcBtX4QpoQhYgIQ5MzprSxReMAcybVYuJ+AyAmJM1ds8maxnV0wI/Ov77UwmdwgSHeCQjS8ll/OZXzP+7gsytcvl7HXjMi2p85cJ2z2vy5OJuUYu9+Bb2uyTMuQih5vL1k0wIaTsruMcHkL6+n2dnkxAr3Pqa/xdGJDJvXZNH1NFLscH+nw3BTIt7y7X7MkkC9V4TfjDh5g6AeL6GOAZYMZHI8jU05PJ+qwMBYfrcJBLJmQssEymBpDJejJd5GAOLaulXSHO9ZqYBEvkcvkYkUmCm+VyBFxIFCRbAJkAIPjwh0EoXRlkAgB28zYUKKjxj8iCfV0ZrsbbXwHe/jKyj/ycPo8quF4BmTzqgxBeAkJKKcg0bWUKIjvHya7Czlg15gFzb+VyrtuPMOABqzCZAPTqMCcMyCQmj8p8YhMg03x3uRaQ6STFINKgGhu1MZnm2SUT31zjnh3m5HQKEkV6TDSEkgEESUrWF7UgU5bhJD3BbrAMZGrwRemIyI16dZezgEfkRN1MJsu06fBlsptleazBAmfDTCbwDNRTcPL+hZemsEbLY1gmU4P5dxHrHK+BySSzDNlLLyF7udJBywlKuVxXMSNk4WKjgWxRLndjrOcZvwSZXKTxYk6RsQH89AzDFjBs9sPzcjk5snK59ZhM7rpMpisfWgQ9LXO/zmQqBBIKhNsejtdkMk3PMiQOQVIvoqSXYzL1KkZXw5p+16WC6Gf8DWjJ3EqeTACKiT7Py8jlAMCv3m8r7R5U5XIVz6iD9+lco4elSPkZAKlUnczjcKT3qE0gkzLf0QgyEbZQgBBnM5Apa+tu+0MU7yTI9CUA7yOEvIcQ4gH4TwD8TvUNhJADQog9hv8WwK+a/34TmuHkEEJcaJbTu3K5liC+j8KAGTEAXNwFtuYN4OzE4LIcPJ/3/bAtdmkUdnoyMcvOCA0ro0ku18BkYi4FcRQ8HiGiu4hzASUEpGlt3xQjd9SbyTTN+AKTSSll5HIdTKYrVxaYTJN/9QJoFMH5+EcBADnvnvCj55+DeHSEG+E9UBT4wevz51OCTL2ZTLq6vEwyN5PLtXsyZSmHHzmQE0O3FyeLcjnXAzFynalpY9sllwPQ2/y7BJkYQxF52E51cn1wc4Thnt+/w1x6hoxuwwsd7Rm1hEocOiFSxpbKEQovgJunSAxVdrPG31o+Zb0xljGZYkrhPXGA4u23IfPZ9bUb/6E3BAYHOKDjEoRZJ1zmVjyZVuguJ6yEaXGcMcrA4CFXKZS/pcG9FWSV9egDMjklk+kbwDX9rKYihZOJ9k0w9GYlKQQIpSB7OpHp58nUzmQSUoHL9u4l1eAPdBJV7y4HAHTAIAhwugmQyeuQyzW0jwf0ppBKCiWbmUz8wQOcRtvz7YaBmR8TAIgKyFTK5foymQyLRUj4bjFfJFkj3Kg9cSw9mS5yRFsdxraVEOd605AYenzr/U4vEJr1NWmYgxgl4FIhIymYdHW7ZpCyq1sZfUCmilwOAPDEU/rf5toX4eEik+lP/wHgDZE9rXupzM17xvsN00fwqQfQoqyCqzwHZDtTENkYx7v6vLMaWL7w1hrAXTKZKp5MAHqZfytzzNn5fbBdw2TaQIe5lAu4xGy8GuY9waUuZPl6zNM6yOS6AKVzxt8AkPpmA3Vxt9dxyDguu/c2hRIBFHjJUK0ymc6ys/4gU8PGXk6nuPvf/DKKBzMPqZlcrru7XJ3JFLQ9L7ZZSkeHObtZVqcaLNi08TdEBuZJuDnfiFxuTDrkcpbp0gQyGbakquQAurtcxZOphTUSuuHifNNg/H3r4gFAaWnUr+Vy83MkzwU48RBOz5Z7MhkmEz891bleqMfq2h3HnBVBJqW08XUDk2cml5ufD+xY339yiKM7/YrZ1ZBSYXqeI3PRwGQy43gNJpNuYrRGZ7lo33Snng+PUVCCRUlfLVzfWc2TCUAxtiDT+nI5QDOqy6YDdg0czkgjM88oI5cDgOOX0SdsJ/VEdue6wbZpXtPA6CsMUJSRGkM3j7Xpdy13EKenpQ9u/i7I9M6BTEopDuBvAvh9aIDo15VS3yaE/G1CyH9g3vaXALxECPk+gKsA/o55/TcAvALgm9C+TV9XSv3Td+pY/6LHPMgkdYVs1AIykQxQ2uTZhjR0bhIECJ0QDnGa5XLmI15oHtZKe28SBDqhagCZAED5Ar4IMXR3kRRCSw+UKpPCeqzkyZQvMpnSaQEp1HIm06NHJeCmlMLk8y8g+smfQG5ONi+6H5GB8WXiL34Jt/yv4eWXfaiKvMAyVUAzhG6PxWPntv73EqTeAi1j4z3RJpfzQgdyYphMJF00/va8km2TpN2b+1WZTIFLESZjsO1tFLLAdmo8V66E2Ls+WInJVGAEL2SIs2bWWjVCJ0RC2VK5nPACUCmQJvr6bNSTiWvGlU0W6RLj75gQeDeuAVKieGMGME5yfWyRGwHRPvYwxjhdH8DxqV/pLid6t2vukssBgEsDgObg3vINw7KY9JbLcQ3+XHsWUkkUsgDLefsmGEDoUaQm8SC7eqNyWeNvmxD3kss91Am+9Z4h3gxkCj0XUw843xSTqdWTaV6qZCMuYjhS31+n3kkFmoV1Fu0sbkara0VTd7nenkz6mgSKwHMKLX+4RHiGGdYOMmlPpj5SOWC2AZz6ETyHtgNT2QUiswa0Gn8LiYzEYMLRFPpBg6wtPlkKMpWebuZ3yFVte0nMppSHh7qKz82cPT0CvvWbwI/8ZSSGGTPHgh0YkCk+gu8EIKQoq+Cld2ObXC69wEOzvNRlv/Woy6f4kZFBHVgmk74nvcy/zYakuHi4YSaTrDCZFtfXyanOpSJHX9sFJhMhoEEAFc9v/LPIeHD1ZTLFcckUb/y70Mdm8yULMmXpFJNi0lsu17SxT1/6Ps5/+7dx9uu/Xr42cAa9usvNgUydTCYrl2tfM8oNogGZnJa8ce3gGmTyiuJScjmXUTiU4Awm72tiMpUg06IMTZQgU2XcO4Fmb0jZyWQKWFAWzMpYMP5muDl5CPrEk6Wc048cZDGfy1utjHg0OceQNXuBlREuU4OxAAAgAElEQVTuak+m0zOw7W0I6HlxHeNvwIBMq8jlzt/Sc1yDJ1GXXA4Artwc4vRerAtWK0RykUNJhbyST8y+fH0mU5I3NzHqjLvNpt/AjL29jMnkBWswmcZ6HGsm0/pQgscqcrkmJpPPAAItnT54v37x6Pu9vpvnAoQAqRCt4CwAuHu7cETayHrOEwEFhbQOMhXTBakcYJhMJcjU85o+xvGOejIppX5XKfV+pdQzSqm/Y17775VSv2P++zeUUu8z7/lPlVKZeV0opf5zpdSHlFIfVkr9l+/kcf5FD+K6KAyYMVESGN9dBJlMNcIx2n1emVBkJXkkhGDL32qUqlHzEc+CEBW5HCEENIpaafLCzTGQIwy9ENOMl5XGNibTKp5MSUN3ubJr0BJPJpUkJQiTv/wy+N17GH760+WmOu+QywGAe+sWnOvXEX/1G3hf8AVMJwT3Xp1t7mylmdCecrmtJzUF87Qfk2lsgJSm1vJZwuEFGmSiA22cvcBk8jwQA4SkaT+5XF/z79BliNJp6ce0ZZhM24chdq8PcHo/hpQ9OmklZ8jUAH5PJlPgBEgIWSqXE75OsrgxJ98kyJQtMJmWGH9TCu+m7vJUZQFU5XKI9rGjzi/tybSOXC4z4EHTONOvByAkR+5YkGn9Td5kCdgJaGColGFd+1hZxWc5b/cuAUq5HABgWyegS/OjYEcnji0+AHkLM6gpigcP4BwcgDjm3CpMpshjuHA2xGQycrkm42Sb0AU1cC3hiWbWoJ3JdD7cXWx1XvVSaZTL9UvgaRSBewE8ReA7+aVBpmjgQEIhGTcZfweAEojPs16m38BMLjf1wu7nJr1AZHzrmj2ZNJMpITGobSwxvLrYBS4+XvDQW/wuBz7zSwaD3NfrPr3QoA0PrfzNVIi/8n8BIgee+8/KZ2bB+Nu832e+YTLp+131bmyMbIwHuwSKYEH2u/DWmlE+P3oEMFbmA7Y4FPdgMlEDMonxI7BdAzJtgMm0zPh7fKI3EpEynkmjRdCAhGFZxLPhhDtI4a3gyTRtNf0GAMn1/bA5m11rzseaHbaUydTRXc6CHRe/+7vlXKLlcqYA2NOTKe30ZLL+RcuZTOTsDHR7u3U9XTt4CuZJ+EVxKSYToNeYU9MNt3Ed7GAyiSYmk81LRIa8xfgbMNe5avyt1ILxd+gy3Bo/ALn9VPlaMHABNS/tKb3qsnPsFUuudbgD8BTi5Ahsd7f0U/ozM/5uM/0GOoy/9Xg8uDmClKp/wdPExIDf3KeLTKZ0fSbTNBetTW0ao0iBR99tPncTQTXnaYnV5HJ6nsvtvomoy8nlXDa731OzBlY8mQghcH2mmUyDfb2H6Wn+zTMJx2fIRbdcztnbhVtMGgtS2bRARhrmxyLRpvy1EKencI0P8btMpj9/4+93YwNBCAEPNasoE6mu7o+uzb/JMpkMyFSdUKrd5QBgy9tqlMtRSUHBZ5V/5gLuoJxMaRS1MpkKJ8VAbiF0HcS5KCuNm/BkmmaLFFPr5dDJZLqiJzIrmZu88HkA0CCTZXvkpLPDjfZleg7xN7+Pp7wvwXGBl780q0ivLJdjDrD95FImk/2ucdZuWp0nAn7kQEwns7bTDSATFXUmU/NxusYo2W5OlkXgMYzyKcj2NnKRYzs9AB1IeIGDvesDiEJifNyjw05yhlyG8EI9dqKW47MROiFiguVMJjPeC+MbtUlPptSwhJaBTJRQhMzHlBJ4NzULocoCKI2/3QEwOMBIjRGn63dOWgSZ+lXNctM1rM0c3WchQDMkzFRNLwMy9fJkokaZRYArH0YuchCpQAuhWZUtUQWZyGgbRBYQR0uaRljG5hIj7V5MpvsPSqkcMC+XizyGE6q7LopVPSlqse1tg0veyKQpmVesBszzGANiihG1hEwJAX50hPGwiclUBZlmSZpbGn/3r+Zlo204hG2EyTQIXKQUSBoo8BY0SMY5olF797Zq2A3g2I26n5slTCaHUeRCIEUMYuXYg5qsLY+158MSJhOgPW/s76gdDbiQcz2mZemx9FBLGb/8q8DTfwk4/EC5IZ0D172Brs5OjxAwy2TS97vq3dh83mNMPILxjof8tdc6j1kzW2ZjTBwfg+3tgpgx6QYMjkd7eTJ54QipciEmj8putRthMnEJv8P4265doTLNLUaL3jU0CEo7Ahuj0MUjst8fZFoilxMGZLKsV+v/dz7VQOMykGnWRnzxOVWGKZ2/9hqy730PgJHL2XHdsWZWQaacy1JKthDl/Nqe79nPsvNTOHsb9mMCSk+mIM8uDTIFHsOEu3rMdMrlFseLNOwQOSeXM9e4SFYz/uapNoSueCoFFLgxeQRx66nyNd8wPqsd5iy46+UX2FmGvxjbDHFyDLa7UzYK+DNjMt19URdm66bfgAZB3QEQ1+RypovgwQ19bVbtMDc91c+FCuhGu8slOUe0CpPu4bd1YaeJxWWiF8i0qvE3gMLYcOS4HJPJdypyuckjAGQm2zbh+WzWBGL/fSvJ5VyPLS2ost09uPkY6XhxvUljjpw1gExWLlcLcXYGf6Tn5PwSBeHHJd4FmR6T4EbX7nCzYdp6Yv4Njg9QF47SE0ORV5lMVi6nF7M2kIlIBsZqlcVgu5xMaRRBtYBMKZsiEEMMfD3hlSBTC+15FU+mpMEsb3qmj3Ow081kAlCaf09eeAH++98P99q1EkgR0lnadSV6/nmIiynkmOP2B4d4+asPIc0i6TMfFNTI5XouHju3l3oyuYzCYxSTXN+7JhlTlhRGLjcFDQ04EC3K5ajZGBbm3i01/u7pyRQ4DFt5DGxtIxUpttMDeOZ27123HeZ6sDaSU+TCn4FMPeRyKSGz7g8tIQ2TqTBMpk12l9MeFKxMFrsqrxH1ERMCtr0D5/BwboM2z2TSCy9N16/Sz3WX66ou1yJXvPx8UwQsBKEFEmaqppcw/+4rl1OKQO6+F/CHSHkK32AJNGzfkPkuK5NCNdgClRz5G0tMJC1j8/9j781+ZbvuO7/vGvZU45nuRInkvaIkavYg2VIbbdkNww1L/dKNJEDHaCQBAiTp5M1+cJD8CX5ynpKH2A0EyFvs5CEWEiTw1IEkm3aTMiWTsihKJMV7z7lnrGGPa8jDWmvXrqo91TmHbZi+vxdK955bZ1fV3mv4rs/3+2t4T2XGUY+Fljg5LjvLAesiU+gxnBIJrTRmp/3amzeVa+BQZ5krr3ezu1wRY2S7Im2STOL0DJASi/HBdkDwmsi0+jtW2uX6bxjS8R4o8RDw7MaZTKOAI4ZGOq+zy4VQmiKZFxi0HERUS82uQDwPMXgnyRTqNrscQaESFDQHFIGUypJMFbHTnbz3EZm8wSq0fWwGWHpqrFjKWbMWJ8Cb/6fpXPrz/wWA1WHB1rg3PAKWT434RCvB35XsxtrKZsgIwdW9YQ+SaTX2aCmRvPoqvAerNQshBMNpgLiHyDQMPJxiCrJ8auazwcDkw9yw0kLCJy0k01kKECBwh2yjbdGAROFW8Pck8vBE7QGzHUSmFrucEOb7mxfrJNN8YUWmLrtcC8mkstXnP/vDbwAwIlMiEiiglWRy935pl+vsLtdMMnnMNPvgs4vSVnurZe1yYZG32mr6VOhRpEKVNrKtaiWZzN/prEoy2c9YpMiFgteXZHJZeZXfMzp/Ak9JFB95sfyzwGbXVXOZ4plt9JHPMF10jN+RE5nOwff3UUgrMt2AZNqpu9zjV03odw1VAsCsebdIJmPfnN4dgHt051wmRzLpiG13brtBdzlzYL7DvPd+c+i3q9Cj2zmKG3Wt4G9rAy5umMm0lsG1PDHf10bTDz+qZEYdfbK3Xa7IJHjAOg9UmSWZklm69XdZXEAwUkMyNdnlLuDvTcF9+iz4G89Epg9NFcEYREuMlEGkt0gmQoBgBE+bice1dgRQBlO6xeM4GG9lMmmtQRQHpxsL9mhvRTINh5ANdrklm8MXIQY+wzKTnXa5oTdEJrNOa5bWGnGdXc5Okm0bCH7XikxPTyAXC8R/9VcYffUXAVRyhzTvDFoefvnnze88CfCJL95BMi/wk781nwkhBD4b9CeZACsydXdPiHyGZW6+uyaSyWUyUbdh3yKZPFCtQCEgEysyNdjRnFWqt13ONyKTHk8MyZTcQXhgPoN9JzK9332CpOML5MIzwd+Z6AxGjHiEBLpbZLKiqlgu4FEP/IbkRLXSQq1lMrWJTEPmY0kpwEP4Dx+ubdDiIgYnxhKDoVlc+9l5wyt1V8CqmUyqdyZTaZdrEpm4IZmW1JFMzUHZXdXXLgcA8u4XAJjndSUytZNMqV0s6GgEpgrk77QLuuVJe8N7cqeufT7L4vgE3t1mkukYZiF10w5zE7txqxeZGjKZRIyB7Yq0STKJE0PZxNODZpIpmKwFvnP7+sUOJFMymgLUg88ygPYjjJpqFHDERGNZczoJ7iNVY2gNROP+djk6nSKTHc9NdgUKIGJhQ/A3hdAJBDXPociVsQcsjo3FBTBWOaC/yGQ39HlhNnbsqQ2VHluSaXFsAr+nLwCf/DUAWJFMmw0PhneA5SkGXghCJJZ2DCvtck2kYDZDSggWD6bI3367lQCuNh24+t//D2R/+wMc/mf/6fpl7AW9gr8jn+FMT0DsZ8b292+FZMqEQkikuQ9rqIz5eYrhxAeJ56DDYUlhVYuG0Vbw9zjkeF/tQ+9ilxs22+UKa2cq7XKBE5nMGusgbCd/nKhSZ1FyJJP3wgulZa6aI9iayWTv/cjra5drnzMCTuHNr24/9Bso7XJMa7D0ZmNvmYET7u1sl1PzOrtcVF5jLlXjYcYWyeTW8BWSafD4XfNXH3lh9e8GNruuQnwuZzkADa9YYDTv2CjbNaW4ugLb24co7XLX217u1F2uDP1uFlkQ7dVkMpn7kVKCw4+OcPz2bhmSy8sUlBGwqIYSukF3uaTY0S73+DVzn+292Pgjkd+HZOI7i0x5bHOHyPUFRcCRTJVMpkoeU3l9AUPh9mFHHzfzWY/cT5FLeD7tPFDlBwfwimWtXS5dChSshvTMY8DfFplc13Q/4s/scngmMn1oSnhDUJ1hKpzI9Nz2DwVjeMpMcNWFvy4xeGuX87ZJpkwoaO2D8Y2HJtwrFwd0MIBe1k/Qc1yCFz4ijyPJRaddzrVN7aKZ0kJBa2wJD/FVDj9kpWWjrkq73MlTLL/5TaAoMPzqVwGsRCatjLDRVt5HPgLvaITlsY8Xf+o5eAHDD/5yla/hk8iKTD0nj/0XDUpfbKvq1Rr6DMui3i6nlUaeCgQRh5rPwZzFrCb4GwAYinIx3Bn8vUN3uXG+hBxPEScphsUeokNzHUHEMdwLcNFFMokcoiigtGkznhQSww6xLuQhEiiDEYuWaw3NBCGT+FbzmAAzIYUeKwWEdpLJMxlSPIT/6NEWyTTwBiZk2JJMQX4Dkon6KGQBrXV/u5zWyK340ZTJNOADEJpjQeykexORyT5vbdlbTJvvVR59zvw6uSKZSFvwt8fKoE7tBaCqQPHOu+0XVIpMDSST67rZsahWSQI1m7Xa5Z4aPuDGuUxT35JMeQvJtPHdx0WMoRUJN4O/xbERmdLpQU13OTtXRHtrmUyEEnCPQu5AMi2GUygWWJHphna5gCOhQNxAMiXKfEa9M5lmM7DptPu5sYvfqvhTLY8SFHoJYQ9sRC4NySSz1XOzi8jEVySTCxr1js0hBRnae+2Hfwz86M+An/vPS0LMtTvfGvuGd4DlU0Sey/qxeU9undBECqYzZIQifu4AKo5LQriuMhtgrOIYT3/ndxD+1Bcw/trX1t/X1O8V/D0MGM71GDwxax+2twd5cRvB3xIRlY20zvw8xfgwhJzNQSfbeUyAtcttkEzj0MOx3jdzfIsQ56qLZCoKG/xt7XKuycTCWoS67HLchlW32eX2/sU/R/GTnyD9znfKnMkFpf27y7XZ5bzQkGItdjnAkJ7B4gr88IOxyzHfHj7Mrt+0AqjYkzpJprrgb0sybXaXA4AibQ3+HvDBOsmUO5JpJTIFPzHjwvL+qhNZMHQk03omkzcgoFohmnU8g+EetAbk1Rxsbw/C2uUY/ffQXW72PhCftmYSITqo7S7nRIdHP3WE47dnuHraf85dXGYY7gUIfb4dql12l7sC1G6293hXu9zjV41VrqU7ap/gb8/a0doOBspyJFNic2uJvhH951dFpuXTtc5y5fWFfN0uBwBn3blMIpfgzi7XcjDEDg6NyBRvf05ZXEBysr3uKZLG4G+2v48g4s+Cv/FMZPrQVOGNwHWGfWkXp5skEwAEE3ArMtUFfxPbNWYSbItMaSGhtQfGNx60SuclOhhAxtuiUCISLOgMJOcY+hRxISEuLkE8rzFrwJ2WOQS8qeLcTIyblFCRSXgtdhvAkFckiiCePsXyT/8MdDTC4Gd+BkAld0jzXkHLg5cOET8NwHyO5z6xh8c/WG3uOAmNXa43yWRPma7ea/2xyGeIi3q7XJFJQBvMVC4XoIGdhDZIJrcgZSSHsiGfnXa5nt3lIlUgUAJiOC7tP6Oj1XUePBh0By6ml8iVuReCiBucuOOkJ+IREm3v7zaR0oqqahkjYreXxwQY8TPgFDrvJzItKQW8CP6jR5CXl6XdY1ksjVUOKEN5J+qqc9HQVB7zkMmskWapLZEhJwQcFKzBwjT0jMg007eTyTT0GWjLIpWnRvSQB58BYEkm+5g2dr/C+qmeECb0O3+ngxoMVw0O6qovyeSEGu9+RWTiHrQQ9to4MgoEI+/6Hea+8d8Cr/9v7XY5J4rVkkzmWdu0yxX22rPpYXPwd3SwJjK519klkymO9gFC4dPkxiLTOORIiEZWl8nEA8TKfK+DSc9MpqsrsMmkOzA/mwGUY+ANGzKZCIROIZgjmeQq6NQFdMc72OUqmUwmA0LDf2LuaR5GQDAFXv99Q5387H+yuswmu9zgCFieYmjtJ8vM5j054rkl+DulFOlzRgRos8w5kuDs934P4uQE937rt7a69Q2nAZazvHPjM/Q5zjCFl94uyZQWChEVJnuypuZnKcYHIdRiDlZjlQPqg78nIcex3jcdAHuMk23B31JpSGkzmTa6y8XLSxAQTPx6AaxaPq+31Di73OTrXwfxPMz+8Btlx9yYdpBMfYO/ASO4tNjlAGBINYJ48cGQTNJkMgHmOb9JhW5TH+01ZDLZ91lrlzO/W1dsiuVnLJLW4O9tksmKTBWSib/3YzyNpkgr4mBgSabqOBlf5aAjjZwDwWXHXBTtQRUEkOp2gr85RS57iB2AEVmAdpKpzi5XKPj2oODlL98HCPDGt/p1ewRM18vRXlAv4KQzS+Hqznt6s+KsO3O0LJEBx99rf+/ol8nkhwxK6X5ZkKXIZO6Xm3aXC3gl+LuBZPJDtso3OrIiU4/w7yJT8KxdbjODslou+FsIrBrK2MpiAeXR/na5C0MyeSF/lsmEZyLTh6YKPgBXGe6Tc2hvsDp9r1YwhifNpLeWyZQmACHl4mTim+5y1cVdmktIHYLxjcE/2gMSSzINh7Uk01lyhoybPw9BoDWQn52D7e01toEee2Yg6yKZYvs+NgUcKRUYb5/kCCHgd+5AnJxg8ad/iuEv/AKIZxaUJcmkvU6SCQCGj8ZQOUX6xhu492iCiyfL0o/LSQhC893scgBw+aP23xlwJEX9RsH97sBlMnENsGDLt74imXLovIDHSOMixgV/9xaZEtt9YjjG8tRMSJM7q8XNwYMRLp4s11rnblVygVybgdxkMnWf9EQ8QmozhFrDv+3psI5vn2RKC0sy9clkItwu2AOEn/60+fff+Q4Aczo9dOGClmQ6IPNOC2dTBSyAhkZcmOvqJzKlyAiBT5o/96E/AGiOhfLMAuuGdrm2PCYAYAtDH4n9lwFYksnelm12ueqCSwoF5lHk73aJTO12OZdR1LaIAYxVDkAryQQAo6Pw+iTTv/tfgFf/145MJgmPka2T5ljECO2ztk0ynQCcQ0wbgr8JM4vPLZGJotihPfTCiuBcxDfOZBr6xi6Xb7TnBgCwALEyv2sw6ZfJJGdWZOraMKczIJiYBgR13eUYRQGbyYSKXQ5YhX9f0y6XJQIeVeDnT+HJwpwwj+4C0MDn/8O1TL5MZAhYsD0HD4+A+BRD7gKljUjS1y6Xf9ScRLeFf2dC4SCd4ex//l2M/+k/xeBnf3b7fU19iEyuTrAbKvIZTvUEfn4BaG1JplvoLickQirMvLlRSmkszjOMD6NukmnLLmdJJqAzl0lr3Rr8XUgFKB8A2eouF8cz7AV7jQcD1VqzrFR/v80GYodHGH71q5h94xsY2gOZJaG9ustx+BBKN5NMABBOOu0vh/Ye/8AymTzz/uUNSSbTXEI12+XyhSG3aj47ZUkmVdR0lxNZa/B3xCMksnKvlSTT6t6k7/4I747urYkOoc1kSpfV4O8MGEhcDgHvsuMgMNyDzMw1sf19CJfJdN3gb0aR11B1tfX4NYBQ4P7nm38mOtiyy+VydVAw2g/x/Kf28ea3nrSvRSu1uMgw3A9M/lZ1fpMFIBLTvAfY6bCtKfqjsU7+xjTaaKO4YNc8HQc9nhW2elnmKAO8YSmgFLgNu5z9vcuna53lyusL2SqTaf+RWW/0EZksyZR3kkzGLgcA6WL1HGitDeHn1ZCeNXY5labQSWJJJvbMLodnItOHpgoWwlMJ7pELqOG9enzSH4ELKzKtdZdLQaKoXGxO/AmklmsCTxrPUehoO/+ySjIN67vLnSanyJgNgdTmd+QXF41WOQDlxtoh4E3lRKZNa40SGrSHus7v3sHy29+GOD7G6Je+Wv75KpOJ9drQD563C7tv/znuPZoAGjj5kVmsUISgLOuv9juS6aI9KybyGBJhAxo3vhg3uJWZTFyaDcbGfVG1y5FCNFJM1d/R2y5nRaZiOEZ8bq5nemc1KO8/GEDkqmwFXVvJJTJl/o1nQ+O7SKYBHyBxm92WXCZnq9JJgqBlsXydcvaAUmTizdc8IMwu2CNEP/UFwPMQv/KXADZIJrtBPMSszC3atZzdbW5P14M+aLZIkROCoIUsGfsDEJKZ5zHau5nIlLffhwDA5kYYkqHZ0GYyQyDMArGvXU4WEtznKH78Tjst0RH83ZtksrlGvCGTyTUGCA8CXJ5cI/hb5GZj8eT1UmSqa+DQZPeKixgRrMi0cV+I4yfgd+4g9L16kSkYG/LohiTT0gp6PF/ekl1OmwPleON54SFia5eLetrl1NUMdDopQ2MbK5sB4WSNMFr71ZRAIi4zmQpnlwPWRSZC6w+LNmrdLifgBwREa9yPz82c417bBn67SkRSL64P7wAyx8jezovSLueyG+sFD5nOUBCA3DkCCcN2kqlQ+KX/7w+giwJ3f/M3an/GdYbt6jA39Lmxy6kMyBe3l8lUKIRUAnz7/oivMiilMT4MjR29kWSqC/7meOJEpo5cJp1lgFKNJJMZeyh8OtgimZJk1mmVc7VGE1R/v6WbaeBj8vWvQZycYPzGTwAAS0o6u8tFPEJhN+7tJNO40y53YNej7APpLpeCBVZkurqpXY6aOSbab+4u59ffL3JeZ5db7y7XFPwdshBCCRSuw+dGFzutFPSPf4R3xvfWhBHmUROOvBb8nUNFOS6HAD3v+DzCKWRuu//tr+xy1w7+3qW73PuvAkcv12bjlDU4MHuUinVt86Dg5a88wPwsxfs/6B43tNZYWrtctEkJuc/creF3yGXKhIJUun+shqO4WjrLAUZk6mpe5Nn1VpegX1YwRpFJaEagCeBdU1AEKvbIPDbrl+G2Xc4PKnY57gP7D3ewy9E1UbGu2N4ePGFFpuXq2StS04gFfk3AexGbzoWVqsbAPMtkMvVMZPqQVEEjeMKITGJYY5UDDMkkjKIvNkimqsXEhcZWNyhFfIVcD7ZOuBHumUW1kqCDIVRN8PdZcoac24BqmIlHXFw2dpYDUG6su0mmerucFAqsB6XB79yBPDOnxsN//Ivln2fKLmyV10tk8oIM/h5D/O1v495D8/m5MEGqjcjUu8YPDA3SEf498BlSe1q4uVlwJJMfUqjlEowVW1Y5YJ1kgpCtOTi7Bn+HiZlw02iE9Ewh4XOMhqv7bNVhruU7Ti5QWLoCPoXW6JXJlGlhUoRaRKbydDjNPgC7nLR2uRzwPJCWSXgIWpJMNIoQffaziF95BYCxQDjrKJiHwp/igMx6WTjrygmFjk7oSzLlhMBvFZmGAC0M9VfpOHmdWqQC4y6RafZDAIC0wlImMvgu+Lup+xWAyKcru1yhwCMParmEPG8JU/dHZsPfSDL1y2Qqnhgc32voLudoTH8vQDLLd+9M4nInFk8QpHNEPGokmepoxUQk8GE+u81xvjg+gXfvHgJOazIo5ua0nHm1JNMu3eViO+6zbHFLdjnzv5PFhjDOfSRqD4wZFL9PydkMbDLttstZkmngDWqDvzml0EhXwd9ZVWSyWX7xmRmve1AoA29QUiN5IuBHhky4vzwz1/noq8Bn/8WWrSKTWX1HTWvLHdncs6V9Dzpdz27crMyuFwI/hP/ii60k0+HJO/jMa3+Cg1//j+G/WB9aO9xzIlP7oUboUZzBinHLp2B7U6jFYn2jfo1KC4mA1JNM8zPzWYwPQsh5G8lUZ5fzcIx+IpNbTzUJe4XdPIZ0sEUypcm8v8jk0dpMJpVlJvScc4x/+ZdBwhDRn9gDENusoqmcyOQ2Zze1yx3aA0d+dNT6c9eqKsl0dTOB0jSXsIct2QyQG+O4E+VrSlmKqq67nCoSCNWcf+PWgGUuU75ulxNPngBJgnfGd7fG8HDAkVohXmuNeJajCBNcjghw3kEFUgZpbfJ8fx9C3Sz422cUhdjBLtdhF0N0AGi1RhVtHhR87GfuwAtYL8tcFguIXGG0F6yska7cGsGJTDuQTI426k0yvf+qsULvP2r9scijt0syAUZkyhXACTglrdEGnS/lKMqlnftqSCY/Ysir+7CjT1h4/8oAACAASURBVACnP+h8bZFJUHv415ajSBhDaM8SquHfZRi+XzM+FvGWM2TVNd2ITM+6yz0TmT4UpbVGjgC+jHEPF8gG9+p/MBjDK8yEsUkyVReOzsPvFi0AUCRXyFUEvmljcaf86ZUhmZIEeiPs7jQ5RWZFJt/+WmUT+JuqbyZT0mSXE7rTLgcYkQkAgk9/em3zt7LLmW54nZXNMXg0RPzKK/B9gv37Axy/bSYcoo1drndRCuw9D1y2k0yDgCO1wdZbJFPsrHoS0BqU5luh3wBAPJfJpACpWjt67UoyeUvz3SXRCPkFcBWerl3nfk+RKbMik7AnY12TcGRPV1NCWu1yxAbYkiT7QO1y1GvPfBmAIiG0nLAGX/oiktdfh0rTdZIJgAgPcEjmmGf9hL7Nup7IlCEjBEFLt69JMAQh2oQEh9MbB3+3kkxSgM3etv/TjDWZyhA6kanJzgOzARBKo5AKslDgA/OzrblMhKwRm5vlTl2bbKauxPEJ6Gi01imKeB4gBLRS5X3N98znfPlkR8tcNXfi+HVM/EmtyJQ3iCSJSBBo83lsk0zH4Pfu2c1oTfC3I5k2NlTc241kSmzeC0vnt0MyEbNZSTa7xvAQsdzDYKgbLdvV0lIaYmUyKfOEGiubAeG0kWTyGIEkCQRzwd/WWkP5usjUwyoHrJNMWSwQTMx3+GB5ZjZ6v/xbwH/0b7b+XSrT7c5yQCkyjS0R4XL/VLye3bj1tq3dKWAB/IcPkf2oWWT659/+fRTBAEf/+l83/sxwasaqrvBvQggWzK4llqfl4dVNs3VSIRESgW18GyV9Oz6wJNO4nkyhUQidbHeXO+lJMjkyvKm7nGsXH7DhlsiUJUvsB/1EJp812OXSDCQwlko6HGL0T34Z+KNvgipdHow0lROZ3Oas3S437bTL7dmMIf6BkEwZiG/GAXUbwd+OZAK250InytdULclk1zPCEoVN84xb95S5TBskU/bWWwCAd8d3tzJ6goFXZjJlSwElNXI/xmLMIU/P2t6uuTZpx+01u9z1hAePk34k0/yJIT87SJ7ye6iEf28eFHg+w0tfvIu3/vKkU2hx49HQikyZUFDOZuc+8+nuJFNc7CgyPX4NePCF1tBvoCJ6tpRnD1ryHUimPCfQjNzIKgdUgr8XNo+wtrschxIa0tFER58Azn4AqPbrLXIFwp3I1L4+C0Zm3VVdKzgCmgYb46PIzYHaBkFX7ZruR7z/5/khrmci04egikxCEwq/WOI+uUAWbj+kAIBgDJafAwRrp8sqSUCibZGpSjLl8xkkAvBwY6NZ6bxEBwNA661F1Wl6ipyZP/PsBKSv2kWmsW8zmfJ2kmnZYJfrSzJ5tsPc6Bd/ce3PV3Y5D4s+G/psjuFLB1DLJdLvfQ/3Hk3w5O0ZtNbQKgDoDiQTYHKZukgmjzWGtzoF3dMWd6fJShCsVEkyEQkojWFL6OCuwd++FZmWwRDiguIqfLom5oRDD4Opj4s2kSm9RG7tcrIUmbqDvwGYjm0tJJwfBRCEgqS3LzK5bha6yFvzmABgAIIlJdBWxIm+9CWgKJC89h0si+WKZAKgokMc3MAu5+6TZSky9bfL+S0i0zgw13iVLZqzKHrWMhOtYifO3wKXZsPhFh1VkqlpEwysNjpJISEKBW9s7q38x+2CbrWL5mY5m0nXIsYJNdVyGXBaiHJxqcdWZDrZVWSqnDgffxfTYNpAMtWLTHERw9fm/qjrLsfv3UXAWZlBtXpBZ5djNyaZMvvssmR540ymUWAymQBstyZmPmK1hyjqd2Ku7OaPTSfIih7d5RzJJGpIJkagkEDa7nJFLs3BwvDutUSmyIuQyhRCCeSpQDgOIYMQD5bnjdYawGYy1YkE1q4wkub+S5zIlCYAY+U9u/V6VuQIWQj/0UMU7/2kliZa/Nm/xeff/xt891f+g/aDJmuXiztIJgCIfSs8LE/B7WuKG+YyZYUyJFONXW5mSabRQWBIpnG9aEAsyVS1405CDxl8pN60M5Opi2RyY0/Ehlt2uTxd7kgy1WUyZaDB6h6ZfP3rwMUVPvtjbSzeXvNYm4p0nWRqsxMH406SyYlMH0jwt0ghPA5B2S3Y5SzdUjaM2JgL+5BM+TbJJDPrBugQmUqSaSP424lM74zvb1l/wiEvN9RL200u9mdIJiHkxUVJ2zaVlHbc3tsrg7+vn8lUb93cqvdd6HeHyORy6Cq5THUHBZ/6yn0UmcQPX23uigmYznKAyXJy64ny2XH38DVIpjhzrowehyuyAI6/2y2woV8mk1+STD3XlcEYRUGgOLlR6DdQseqWJFNddzkrgrnrO/yE6cZ61d4ZWGQSxMIGXXEGzjafrolM5n+zgK03RnD7iga7HN/fhx9yiExC9bV+fkjrmcj0ISj3UPjFHAEpsAyaRSYilvB8tqbWqzQBDWvscpVJP1+YBaQ/2FiUhlWSyTxwm7lMp8kpQtsmlUmYtr3zWS+SyS2cmsrZ5TZJJiUVaB+S6cEDAFjLYwLWu8st+pJMn3wOALD89rdx79EU6aLA7DSFVj40Sfu1B3W1/2JnJtMw4MgbRCbnBeZ2wcH0ssEuZ78XokFUc2c5YHeRidl7ZuENoBcMV9HTLeLq4MEQ549bNtPJBXJt7oWCms+vTQgDKiITbSeZfEaR8AAsyetP9K9ZWusy90blPUQmraEIQWYJscHP/ixACOJX/mLdLgeADA5xQObXtst5tlPSsnCZTP27y7WJTO4zX2TxjUmmeVfw95O/BiOWAnEkk8wQOJKpo7scYBsZFAreZAgQguKd9sVK23tyJ/VdJFNxcrxGSwIVkSkvEHnmPasBBSHA5a7h31WRyeYyXeX13eU2RRKpJFKZwlNmHGGV+0IuFlBxXNrlmjOZ6uxybKtbS1sJZa6LLG9ulxsFHIl9G8l80y4XIlZ7GET9rs2FAVNnl2t7bmwmU2PwN6XQNIVvx9pShBvdrWQyne9EMgGGYMgTAX/AER/dx4PlaauFM5VpvU3YikwDe8CTSDNW6CQBDcNG8iuxc3XAAwSPHgFSIn9vvUOqlhInv/3beDI8xI9/8eut78sLGbhPOzOZACD17Ny2fLoimW6Yy5QKCR8NdrnzFOHIA1c5IGULyRQBUgKVTbob2xbeHUNjtFQXyeSIj4iPyvxK1zFWZMmNM5lUbkim8tq/+lWQ4RC/8DfaZjLtQDK1CbM97HKTdIaCcdCG/KsblchQEB9pOLxx8LcRmVRzll+LyFSSTNXuclbIk5ZkatrUb5FM+cJ0v7Jiff7WW2D7+8hH4y27XDDwSmtQPLM2We8K+Z6lyNvs5ACk8AEC0PH4318m0+NXAZD20G9gRfBXSaaaOfC5j+9hfBjijW+2C78rkslHZOeBkgxLN0SmXUimXexyT98wIkuXwAZs01Y15UScnexyBYVipDMmoPOlXPC3m/saussBlcyossNcs2VOSvue7X3YdZ3R1Iyx6bJql7P7qIiv2+XcvmKDZBIVkimIzDj/D51meiYyfQjKPRSenaQXwbYSDKCc2LhP1rrL6SRdz2SqIZnEwiw4/eHGZrwykbrTtjqRaWIXYbRQGIgURMrWTKaQhWCE9e4ut53JpMF6DH7jX/1VPP8//Y+INrrbOCFlwP1+1Eg2Az86hP/xl1bh3wCO376CEj5AVG+bGQAzScWnQAvJFfkMhaq3y5Wd7axdg+rFWmchVyXJRAEo2m6Xc5lMqp9Vi86vEPMAydwsGGbh6ZYYtv9giIvHy2YBLrlARs19klkqIeo46XFUUkJoayZT4FGk3AfLxK2STG4THnomk6lLZBra9ZS719lkguDllxG/8goSkazZ5ejoDg7J7Prd5ailA1xXwh26y9Xmt9hyG91Fsbx58HcXyfTkr8GYbTdd1IhMHXY5YEUy8YDDe/Cg3S4HNLejRpVkal8ciuMT8HvreXmlyFSsuk8mUmN8FOFiV7ucO6k9/ARw/DqmfhPJJLdEErcx8bQP7tE1IUEcrwLLA27shqK6CVgL/l4fG3YlmYjN4qCLm4tMoUeRWWG6unC0F4ZETTEI+41lznrFptNuu1yFZGoK/iY0hW9DIEo74ejeevB3zXhdVwPbRjkuYmSJQBByLA/v2eDv5o1eKtJ6ksmKW0FqRIuSZLINQmpLa2R2rA1ZCP/hQwDbHeau/uAPkH3/+/g3n/1n8KL2ZguEEAynQS+RKQ8cyfS0PLy6schUKCMy1ZBMi7O0zGMC0EgyuU6X1Vwmj1EMfIYrfgjM32+9BmW79TZmMtnncMC37XJM6N52ubUOT5XSWb4mMtEgwPhXfgVfflMj1rRX8HfWh2QKJ2YcaTmIGyULzMNxL3vrziUzFMRDFg5vbLOMPIZcKsjArY03iLpsXlrYqqW1LgUuVSWH7NpEFe12uXLdU7XLVcSs7K0fInjppe0cIQDBkJd2OUcOXrEziH2b5/T0tPU9y4yBRQSEkHJuuLbIxAhyoboPZR+/ZsSGms9yrdw4mlRJpu2DAkIJXv7Kfbz35kVrM5rFhRWZpkFJMpWfpxNKx/dtl93+Y9Cy4cC8tvpSXJXXawv/3j34e4JCckh6s85ygLmfC6mh5pZkqgn+dtdXCjZHnzT/Pf1+4+sKK5hpZ5fraHLjHe6Dy6SWZOLRRni6y1r0Nuxya8HfVtz9B57L9Exk+hBUZtXWwG7s5rxdZPI8Uj6AgFkAVe1yzqq2JjLFlnwYbSx2qnY5RzJthH+fJWfYH01BKQEpFKZWOGkjmQghGHrDtVyoumrqLieFAu2xgaa+j9Ev/dLWwsWJTMMgMmHGbaV1OaEPf/7LiP/qr3Bw5IP7FMdvzyCltSh1CGZrtWfDUFsscwOPQcF+9zUkE+UEJLW4PctrSSZaikwaWtNbJZnIfIa5N0B6aU/GoktQsv6dHDwYoshkOXFvVXKJnO7DC21bYHQHf6+RTC0ik88oUmZEpqhlsbxruQVHyBl0XvQgmczPVzekgy99Ccm/exVM6jWSiY/vYB9zLNLrhdq673AlMvWxy2XIybaQWS230V3m8Sr4exdyz5bWutsud/w62MFHAVQymSoiU2OLdayLTLJQ4B6F//DFbpGplWTqzmTSUkI8fQq+STL5TmQqysVgnEvs3xtc3y738B8DT9/A1B/3tsu5e49JD6zGKgcA/N5dhHZhnjeKTOuL1F27y1GhAS2h4/TGIhMhBGHIoRnZymRSNDAiU9RXZDJzIXPd5ZqeG6XWustlMoPYpLsYBWiG0BEnJcl0x7Rw1nrnTCbAzC95IuFHHIvD+7i/PGt9BhuDv3kABFMEdsOUWlp2s0HIWhUJUlhxnYfwH5kw2mqHObVc4uR3fgfhT/80/uTB53uNPcO9oJddzgsGiMkAiM9WJNPFzUSmTEj4KBozmVxnOQCNJJPrdLnZYW4ccpzTo26Sydnlhu0i05CPVtQ35wAh8IXuTTL5dYQinF1u/f1P/9nXMUqB0Xu8F8nk5sPW7zsYm3Dmlm7Co2SGq/ADoJgAkzsID1k0hJrdUGSy42fmWYGnp11OJwkghJm/imKVbWrFI2eXayIySrucrAR/W6uc1hrZW2/Bf+kl0xFtY0wOB15pl3PP2yU9hTow63tx2m4hkxkB9831OpLpuh3H3Dzq8sYa6/1Xe4ks5bq3wy4HGMscNPD9P29+LpeXGaKJD8ZpOWeXJFOZgzVpPZiqq6RhL1Nbj18F/DFw8LHOHw35Bm1VU9cJ/s6FD8luxy4HAGpxYpwxNaL+imSyc+ng0PxsS4e5IjP3o2bu97RfJz88gJcv1qhn90wEA74hMjm73GYm0yXoaATiefAtyfQPPfz7mcj0IaiSZLKT2QVvWJzaCYdzvW6XS+I1u9zQG4ISurZBETb0M5xsTPJVu1wDyXSWnOFocIRgyKFzjbFFDdlee3vmsT/uFGaSJrucUL2Cv5sqkxkooRgGPhZ5xyBRJICWQDDG4Mtfho5jZH/zXdx90eQyCWEDTG9bZAo4YG1DjjJylScCQcShLIFGPdXaXY4SQGveurn3rF2qtBJ2lLq6xDwYQliRKRtuY+gHD8w90xj+nVwgJ1MEES8D2PtmMnUFfwceQ8IDeJm4VbvcimRiPUkms5iqWmsGX/oidJri4TEwqrQ75uM74EQhX14vb8QJRckuwd9FYuxyLSST+8yXwopMqlid9uxQmVAQSnfb5e6+BGBTZNImpJY1b2ZCtyjMJYRQYB6F98ILPTKZmoO/sx6ZTOLsDJASXkMmE4RYWfkKib27A1wdx6Z9bt9Kzo0w88JXAJljIiWusqutE+E6kcTde0x58DbG0uKJEZmcXc5cY43IxPi2Xc7bjWSiQoOqHHKpbpzJBBjLnPAI0vm6mJSmDBoMkd9zLLMbTzoeNwanA7CbZA0Ek237ii2PERCWIrRiaLFGMp3YjlT5ziLTPF5CKw0/4pjt3UWgRCuBkMmsedwbHiGMzRiTiXW7XP2LzZHZg5qABWCTCdjhIbIKyXT2u78H+fQUe7/5mwAhvcaewdTvDP4GzPw/I5NbJ5k8FFt2Oa015o5kmvUjmXS6Gf7t4SndN+TaZvexSnUHf1uSyRthns+htQmy1x4Hl9jBLkfXM0ds6SwD8dff//Af/SMsQ+DeW7xXd7kq2dt8Afbzy5oPFQfLK1w22MxuXCJFpj0Ug9GtZDIBQMLse6olmbbfh6PiXPe8MpeJBwBIf5KpqJJMZu0gz86grq5WJNOGoBgMOUShIHKJeJaBexSX6hzk0Aq2px0kU6rB/ALQehX8fQO7HLC6t2trcWIowK7OcoDdo5BtkqlG9JzeGeDBx6d445tPGkmqxUWGke186V6jJJncQVQw3jmbcie7nAv97iHkbQlhNeWXwd87ZDIpH4J029C6yn3fen5S21kOQCnY5G7fSojtMNcsMrnDLWXvw675hu0fwCsWSK5WY3UWF6CMIAgYpG0YYy6k3i4nL1dd0901F9eMtviw1DOR6UNQTmTiqZmkLljD4tRO5B5Xa6fLxi63WixQQjH2x2skk0zN7xjsbSym6uxyFZJJa43T5BSH0SGCgQeVSUwsycRb7HKAEbu6MpmWuQSnZGvi7Rv83VSFKuBTH5PA67bLOUQ2GGPw8z8HAIi//W3cezTB6btzyMxZlHYgE/atyNSSyzTwGUAEPOpvk1iJgB9xqIUNy/R0fXc5JzJRQMPDyG/e1BJC4FGvt+1PXl5iGQyh5gVUUADh9qLh4IFZBDWGfycXyDGCH/Eyf6trEi4zSrrscowiYz68XN2qXW51ckuhix4kk91krJFMX/wiAOAz7+qSEjJ/YRegy/ZFX1OVIpMwG7fWhb8r112u5TNa5cLEzYGnPcrZABvFzsUJsDgGv29w6apdbiBZq1UOqCGZOIX/wotQV1ftm9Iewd9ti60VDbQhMnHzPnVRYOCtSKa9+wOIQpUho70quTDP+L3PAQCm2RK5ylcn27YyIbfGSyeEMMnX8pgAQJysrj0og05dm1BpTvWCie0ut2mX241kYkKDQEDECmDtXRn71CjkKGpIpsSGNQ2CZltEtZyNRQzN5rDR+uPmgnCyZmOrlrHLZYi8ARin63Y5LVc5E31FJvt7Fgvze4IBx9W+WawX7zYfUiQiaR73hkcI7eY4V5ZkarPLVUQmJ1z5Dx+WJFNxfIKz3/1djL/2a8BnvmCus8f8PJwGWM7yTuvM0Oc4J1Ng+dTkRkVR2ennupUWElxv2+WSeQFRKEMyLbpIpm27HABMQo4nyrZWXzZTIqXI1Bj8bT6XkTeC1LJ8jrXH4UngIOxnuQw4q83BUdl6JhNg1gzf/bSPj/yQQhXNa6NYxGsiUyvJFNp1ZUuHuXA5w7lfL7bduESGDD6KaHQrmUwAkDArJFVpFlkAIqntLudCv7kNNi9FJkIAHkJZ22pXd7lyvM8WhnaBscoBgP/Sx2qDoIOBPUCMBeJZjsHUx0IswA/NekN0iEwiFmC+BIoYhSWwrku4uH/XGv79+DXz3x7B16DU7FMsyaS1bj0o+NRXHuDyOMbxj+rvg+VlhqEVmaoHQwDM+M98wAt3JpnK9W0bxQ0YUfrJ6/0oLmDb0ldTzDM5kDtlMukQmoob2+Xc96CXJ7V5TECFtKra+Y4+2SoyucMb5Q4/OshZdrAPr1gina3WXGksEAw4ApuXWdJMTcHfFxflIYcfOpLpWSbTs/p7XlWR6UyNsBQNX6vLZOKyRAkBa5cL1xePE3+yJjLpzEx4o02SyRuYzUVD8Pe8mCNXOY7CI/gRh0wlJpkjmZrtcoBZOHWTTLJWdFBS97LLNVUmM/jMxzDg3Xa5CiLL9/fhv/gi0jfexP1HUyipMV6Yz2wnkml4x+QdXLaLTIQakWnrkhzJtLRhoF0kEwChPRzQdvrEZz4K2dNicnmJOBqCLATEKK49NQ9HHqKx10wypZfI9BB+yMtOgoOO4O/yRI/x1kyrwKNIuIcw17csMm2STO2b5YE076t6f/A7d6Cef4BPv6PXMpkwNAtQGne3Fa4rR7w5kWmX7nJeD5IpFWnFQru77cAJuo0i05O/BgCw+y8DqJBMIkMkKEjDZqy8TrcBSAW00mAehf+iCelstcyFU0CkQLEtSmRCwWMEtKVlczXXqFqrTKYCnFH4jBqR6a75PHcK/04uzDN+9EmAepguzT2yaZmrW2A7gZNICr5JMh0fg02noGFY/ruSeijHPmeXq+kuV/TI17DFJACqIVNA4+Yk0zDgyDmQLtaFcTdFDbx+tJ2jG8TAPIuNz43bJAeTUnjdzGXijILQFAM+XM+scqe4J9+zF7ejXW5pN6ERx8Weea383fca/12jXQ4AhncQLs/tz626yzXa5bIrQ44CZc6T/+gh8rd/BAB4+j/8DiAE7v7Gb6xEh46MDMCITCKTnVkhg4DhTE8Ae8+zvb0bk0yZqCeZXF7LGsk0aSKZzOe12XF3HHp4LO042ZLLtLLLtZNMLuLAHcopTuFJYC9oX2O5MiRTXSZTBhpu3yNvfjaEXxAs/uRPG19z2y7XRjLZz6Ih/FtrjWB+hTNvtFsDlb4lMmSaQwxHN85kKjf1ippNaPWwpTpebpQjmdgde5BUDf/mAXTRYZdjm8HfK5Ipe8sI18HHP26y6jbyt1xjnjQuEM9yRBMfs3yG4XAPdDLpzmRaZGCBApLLkmRiLfNhWzkRrTX822USdYV+u4oOSpKpy97+0hfvgnkUb36z3jK3vFyRTOEm2ZvNVwLidUmmrnHx9E0jVPahuFARPVsOewgh8ELeO5NJ+0ZkAopbsMuZf0+WT2s7ywGV7nLVA//DjwOLJ43CtIuEkTa/s6vJDT+wJFMlvzFbFgiHXvlvyzGyzGRanw8NyWTG3DL4+5ld7ln9fa90WcCjElQrnIh9JE32BJfJxMRG8Pf24nFTZFL2gR2NNgQNQsrBtM4ud5qYyekoOkI44CgSgT3RT2Tql8kkau1TUiiwGyjsucwRsADDgHeHLFdIJsC8LzWfl+HfB8triEyEmPDvVpGJlyTT1vVbkklakoly3Rr8DQBS+zig7dfoU793JpO8vEIajcFiiXy0aMz0OXgwxPn7LSSTiuBHvLRGdnnWS5uKF3RmMiXcQ1DgVjOZym46NvibdpJMNnBz41rzz72ET72nMWLbJBNL2ru9NJXbVKZit+DvnBAELZ+RoylSmazoxuuITF0k0/HrAAD23GfNpVVIpkjQbpLJ5R65YHyPwX/Bikw/bhGZWt5T3pbRY6s4dpazjZO6isjkri/JBfbumU3lTiJTfG5EJu4Dd17GdG5+56bIVJvJZO89Ihj4Jsl0fFISWO59rlo2u03TqLa7nLPeyRorTl1xpU2GgjYWjJvWKOCIid4imeK5syb0G5Pl7ArE95Fz8301PjdVkqlJZKIEYCkiPoQXVEgvd4r79A3z3x1JprgqMo0OoAhpJZkykbWSTHx5CmhSkkw6brfLpda64Q4TgocPIc/OEP/FX+Dq9/8A+//qX8F//vlyfOxrlwPQGf498Bme6klJBbH9vRuRTEoZ2oHrYouom59ZkalCMjV1PKNNJFPk4T3hRKbj5uuIY4CxRhrW0R5jm//jOswJTsFFf5KpLZNp0y4HAKcPAywGwOwb32h8zW27XEcmE9AoMqn5HEwKXAajfp3Hdi2RIdUe1HAENZ+v8pCuUVFJjtiDvapdzmVO+dv3iyOotuxygNnM9iSZVsHfi/Jzzd/6IehwCH73LkJeRzJZ6mIpsLzKEY05hBIY+SPwo6NWkklrDblIjciUXq6Cv68rMvUimV4FDl5aHWh11eCg/B667O1BxPGxn76Dv33leGveErlEuiww3F8nmcrPM52t7uWdSSa59pqNtQvFhdX9WBfsXy0vYL1JJsEm5hCIZDcWmdz9TOOnjSSTo4LWSSbbYe6svsOc2+MK0q+7HDs4gFcskaWr7zxzJJM7XHP3ZGmXayGZnolMAJ6JTB+KSpcFfM8syE/lHuImhNmeani0WCXva70V/A0Ykakq8CiLZbvJaK1s0G9d8PdZYk4Wj6IjE54WCxyIBIrQxtM/VyO/m2RaNpBMUugb2eVymcNnPsa9RKb10yk6HkMuFhjuBRjtBzhK7Emz2EFkAozI1GGXI6QAJ9ukTJbIMpOJBB4IRSvJBAAKHBPZLup5rJ9dTksJNZshiybgqUI6nDWemjd2mNPaBH/LAEHEykymqOOkp1xs8XaRKfAoMo8hzLeD029S7lQr4IZkckJCUw0KG4y+ca8vP/sCRikweLdCLQ3NAtTPricyeXbDlOwc/N0hMtkNdS6T1cJvhwWWq06R6cnrwOQjYBOzAa/a5cKCbI1jm1UlmQCUmUwgBPk7LblMYbPIVGc/2yxxfAJwDna4LhyQTZHJY4hzieGeDx6wHUmmy5WQfO9zmJ4bgaF6WADUt28uhRBJtkgmcXxcEZncYmsz6HRsMpQ2RCZnveuby+RJcge2awAAIABJREFUQNngXLG8OWY+Cjhi6LWOMQAQ24ymiLePd67UbAY6naw6ZTV93yXJNC3Fn2Qjm8yRTBEbrtsJR5ZyK0mmnt3lnJgV22c64sgIw/nwoJVkSmTSPO4NjkDiM1D4EHa8V2kKMmgYA9JZSTI54cqFf//kt34LbDLB0X/1XwJAP/uULWdLWXaEfw99jhM5Mh1ZlQK/IcnkrpGpYivcuo5kYg1rmVXw9ybJxPF+bseqFjFeLZegg0FjRzVHMk3s2m5emOsRHIg0b23WUK2As1qRSeXbdjkAGBCKv36ZYPHHfwy52F7XFKqAUKI/ydRhlxNnZg68DEbreXC3VSJFqjnUaAJoXQa6X6fCalv7TaGhhWRa2eXMHK+qIhMPDUmLHbrLVYK/Xeg3IQSRz5A2kUzLAvEsA7Ma2MSfdIpMaj4HlLIk0wUKpY0l+JpdAHuRTI9f6y2yADAkk7XL5T1Iyk995T6yWODt76y/byd2lySTExTL+XC2upd3JpkEGO2RVff+q4aQO/x4r9ddkUztz40XsFX3to4qiN3rIGvtYNqnAs4QIAfL57uRTK7DXIPIJKxbR9qPsysagu0bu5wQBMKOWemyQDD0tg/XmoK/Ly/LGBjXXe5Z8Pez+ntf2VIgsCLTBfaQNmGRzi5H83Jhq/McUGot+BsAJsEEs8qpki4AQJXe2LWyLcu7SKbAdrDYFzHyaAjSEVo38kadmUxJLmvtU6pnd7mmup5dzolMo3KRcvTiGA+Ka2QyASaXqS3422YyMVJDMsWFyWSaz0FdiHKdyOQ2udoMniN5OySTnM0ArVEMj0AAxMOr+nbZMCRTnkosLzdeN5sDWiIXXpnJFHms1ZYEVEUmvzX422cMCecIC3wgmUx9SaahMO97k3i4fPk5c52vv7X6Q0syBfn1TundpjKz32GXOALAhKISAt9rtqK5zzzXaasg01WlXa4p+Pvke8C9z4IyCkLJevC3IKBRu10u3BCZuEdBgwD8/n0UXXY5oHbR2BoEbUscH4PfubM15m2KTAOfISkkCCHYuxtdzy4HAPc+i+nCjL3bJJPcQsfd2KQLAr7RXa44OS674oVeE8k0NsRHA8lU9Mxl8hSgLakolzdfnA0DjjkUikyWC0cASGY5GAr46GmXu7wCm067rV49SCZCBAgVCNkA3Kcotuxyf2P+uyPJlMb2mQ45ciFxPjlqvKelkhBKtJBMdwAtwcBRqEp3ubA7k8mNMU5kEu8/xtF/81+DTc0z1CnUVS/DkUwd2WSRz3AiJ+b+Sy/B9vYhLq9PMrkxnOu81i7nhwzBwMyv8LxaIQaoBn9vd5d7bLMaW0WmOG60ygGrjfjErj3cwWDBgEj3zzQLPFpLjugsBwm2568hgD//DIVOUyz+6I+2/t4JHWuZTK0kkwv+rheZ5LkRCC6Dca2t76alZWZFJvM53sQyF1UzcDZJpja7nBUs+Z0akqkqMjUQGQELQECMZR2wJJNRi/K33kLwkmmWEXp0S6hzh8fxLEe2FKADux70HMnUnBvmiEHuO7uculFOTyfJtDwDrt7tbRcDsEEydYueH/30AYZTH29+6/Han7suyCXJtGlFq9rl7L4IPam4OJcYeKxbnHv8qrEJ9myMUc2hbCs/7E8y5cTcVxTxrdjljmCftwaSiTEK5tF1kmn/EUAYcPr92n/j1hzueKnrUIPv78Oz+810YdYe2yST/f01wd86z6GWy5Jk4h4D5eRZ8Pff9QU8q5tXuizg1jhzvVdil1tlffseycqFrcsKoDUk05pdThAwktcPgFaxJ74PeN4ayeREJhP8zZElAtMiRhJ1t6Ltk8kU5wIDr8YuJ/Wt2OVG4e4kExuNIS1Gv//8CFPFEeXdnfK2au9Fs6ltWIQOfA7QolZkylLTylotF2ABM/lO3vYGgVAKeB6sxoSBaD/B85mPQnVnMrn20dri+ovovPHU/OCBWURvhX+74NmCWZFJYtiRxwSsFlsJ450kU2pJJpdncBu11V3O67DL2UXhpgh5ceDhdAzoV7+7+kMvREojRMU1u8tZa6XrENhPZDLB336LEMcoA4OHQlVFplsmmWQBPH0TuPtp8zs9ukEyobddLrOLFUfa+C+8gPxH1yWZVOfnWJwcb3WWA2pIJn9lZdi/N8DFTiLT+Upkuv85TO3itpddzgohWhDwymZQFwXk6Rk8myXlxKm0rmWzy2SqEIlOsOprl/M1oCJzj4pbEJlGAcdM2VPJCs0Uz3NEfAYi+wd/s8m0e4NSdheqBH9viEwS5neGbAjuVUimYGw2k/PH5rOsCQeuKydmuRNTP+IopMbF3l3k79WTTO75b+suBwAcHEKbza6Ok611wuoFZ6vgb0cyffSjAGPwXnwB+//yX65+1H2GPZoODKf2gKYHyXSq7ecVn9lMpusLBW4Mp7rYCv6en6UYH4YghEDOZ2DjcePGcEUybQZ/ezgTPUimOG4M/QZWbd6n9l5xdrmcaUSqRyt0WwGnyKWC2uhmqbMMtI5kUsB3P2qaAcz+8A+3/t7Re5EX9bNHOtGliWQ6XZFMdcTVTUsXGTJ4wNiJTNcP/17LwNnsSlodLzdKzc3vZJvB34AJkrZ2uaZNPSEEIQ+NwCeFye3xx5CzGcTTpwhe+lh5fVt2OUsyXT6x88DAjJVjfwx+5wiyJZPJiUylXU5peD26njVVZ3e5xzaPqWfwNYA1kqlPN1hKCT755fv48XfPEc9W34MTu8vucm4+dPdkOlvPZNLKZGP1qDirPzBfKyVNLuUOFFe4OWc3lLHL9ZtzC5gxien0dkQmYsfAhu5ygBHB8qoIxn1zEN8Q/u3m1YLo8ve0FfE8+J75Hl3OcbYsEAy81eGaW8eUmUyrsVlYcpZVGlr5IX8W/P13fQHP6uaVLgoE3AwOSzJpVqwZB7wBOJLSLueyAja7xjiRyVmYtGSgpOE00drlAIANBlDLdZLJox4m/gT+gEMrjVFRYNlDZBp6Q2Qyaw2ajnO55WFWSpehvtetXOXwqY9RYBbsrX7mjYUDHY+h7KnU6CNGQLm7ePEaIpPJimmimYxdToBi/cRSSQWRyTKTifqklmJyRT0Pym5Ig45r9FlPkslZFXzzmczC00Z0f9+KTFvh38kFpOaQkpQiU13+1ma5xVbaEfztM4rMo+AKCHX/BXlXVe0Bqsg7u8v5IgcH2dqMLsQSbzxPUPzVa2tWwpjvYySvZwVx30Emc3iM9ArnVEUMQUinpdCjERQyCBdUftuZTGdvAaoA7po8Js4ppLXxZjKDX+hOu1wZ1JmtSCbAiky9SKamTKZuu9xmZzmgnmRyhwR79waYn6drBE5jFakRVEuS6fMrkSmvE5nWx0xHHqhCr5FM4vQU0HrbLlcGnVby6Kj9ztTqevkOJJNSGr4m0EMzF4nF7YhMlzZYv5rLFM9yDPgc6JsvN5uBTSbdG5QKyVQSlRt2uULbAF86sMHflbbMbpE9ODT/v0d5zINHPRR2MRsMOHKhcLl3B/LsrNbO5DpQtQV/A4APWopMxlbfTDKlhICAlEI28X3c++//O3zkt397bQzcxS7nhQw8YN2ZTAHDOezmbvkUbH8f6uoKWlzvHjJjuDZ2uU2S6SzF+MBmLc0XoA2d5YDV4Z1K1++BScghwaA2g6E3Si2XrSST24jv2bWHs8tlVCHU/YPzmyxKqiGTaagVFoRg8rWvYfFv/+0W+VMlmdI+5Jo/AkBWa6mNkudVu9wHsGkTKTL4IGPzOcrZ9QXKsEqObNnl1vM7qyVnc5AoKkXFTZLJCeJtBxoRjwzJ5ISNYITsLUNC+yXJxLbWs37IQCjB+RMzVsjQPG9jfwx2dAQVx2uHx9USVZEpuYCQ+kYkU2d3uVJk2oFkivaNxUlkFZKy/fn41FceQCuN7//5KgC8JJn21kmm0j1StctVOm/3qbjosb49/Vszz+8gsIU9SSYv5L1JpkKZeYBjeXO7nFcRmRpIJsCKYJtU0OEnmjOZ7HvJ4USm7vEwsha3dJGbXL5UIqyQTOXYUyxNBmUlr88drFezhv2IP8tk+ru+gGd180qXBQJuBr+ETFq7CMAfwUMCpTSkUGVWwCYGP/bHEEqUiwUpOShtEHscFgqADAdbdrmj6Mhs/G2b1IGUWATdrWhH1k/eZpmro1uUO4W8weBX2uXsJmnRhjxuBn+PR9BZBp3nCO6EkNB4sPjY9exyQGMuk7PLbYpMudts2Ewm6tWHfrsivg8p7Sl4i70M2MEuZ0UmSocoKLBks8ZT82jsIRzWdJhLL5GpYflelpmozd+qfU0eIaGsm2Syk0fYr2Fer1rZ5Rh0XnSKTKRIEBG2dX8s8yV+8NCHfPoUxbvvln+eeXuYqKtrddlxIlMu8355TABy4bratL8Pj4YgNEdcdtW5gchUZ5c7sUTXvc8AABgnkPazzoQRmbrscq6DW5ZvkEwvvgB5cdHcvrpcMG4TZLnsJplMrtH2AspRbiuSiSMuViITNHB10sPS5a7LiUyjOwgHd+CDrJFMQipIpbeuNy5iMMIgC7WWyVQ8MQtsd+3Nwd9VkWk1VvIdMpmy2D6EoxCEashFPwGorYYBx9JOA+mmyOTFpQWlq9TVFdhaJlNLdznCAG/QaJcT2oxzxi7HVnY5YJXL1NMq52rgDVCkCoQau2MuFa72zWsVP9mmmZylpi34GwBCUCjk0EpBp2mLXW6GlHmGIq2IYwe//uuIvvCF9R/dIfibEILhxO8V/H2mKyKTXehftx19KiR82Pt4M/j7fCUyGZKpmThzZKXeJJki85oqmNyQZDL3zjRcD/5OqUAo+y/vy+d6gzjUWQZS011uqDQEAaJf+1WgKDD/f/7ftb9ft8tJBJy224AoNYd0DXY5cWYolCt/+IFlMmXaA52a71Jd874BKrRsXfB3ZteyQU3wt6XiHDm23l0uBO3RrCPikfnss1XAeG5FJmeXi2pIJkIIggEvafI8MN/f2B+DHxnB2eVibV2321yHxNjllAK/Ad1SCp5NItP7rwL7D1dzcp8a2HkxPu9NUh48N8TdF8d441srkWl5mcEPWRlEHVatkcA2yQT0JrrjTHTmjV5HYHP3Yy+SqWcmU64trYrFzYO/GcMRsc9bQyYTYESwrcyoIysy1VgS3eGNe4r6kLPh2HZfXhTIYzP+B4NKd7lq8Le/nccEAGxvdaAfPBOZnolMf99LKY0sEQi5mRTyNpIJAIIxPJiJpMhks13ODpTOMqeUB0IbHhYXcKc16GCwFfx9FNkcGev79oTG3O8hMnndIlOSS0QbdjmX03LT4G9jlzOLwWWbwp/Njc3BYvXU+foXC2Ra4ynTuL94dD27HNBCMnHznWxQOFXbhFosQLlqJZmI7wPCbMBYl8jE/F7B3+WAiwAzz5JhDSIFIQT7Dwa1drlcm4HcD01WzW4iE11hrbXvhSLxPwCRqcygMJlMXSITRIoh4Vv3x6JY4J2PmXsp/otXyj/PgwPsY3atxTanHJRQFCrv11kOQGbFry6RKaARQDPEWc0Jbs9apAKUNIS7H3/PbOBt4CPzKEQlk8nLVaddDrD5Ixt2Oc91mHvn3YZ/5IJpG4K/WxZacrGAWi477HLmmY08WnZR3Ltn7v3Lkx7itNvIVMRkcv9zmOp1u5wjFTa/+0QkGPABRK7WusuJ4xMAKK+9PfjbiUyrh8kJVn1orNgi6tSnYKGCuAWRaRRyJBaXTyqvl5QiU3+SiVbtck0LVneSTcjKLrchHhfa2l7IYL27HHB9kYkPIFMNPzKZHrlQWByY16oj9BzJ1GyXM4v9AIDUeZkp1GiXS2dIvaBXtl0p1PUkjYd7QaddblC1yy1PS8vCdTvMZYWC79I8KlmCWVwgTwRGh/1IJuK6y9UEfwNA4U17BX83lduIT4IhGGGY53NorZFQCX8nkck+17LSdVjrZrucPZQSLz+E9/zzW5a5NZGp6CY9AZjnpjH4+xRqPIWi2xTOrZTIkYODTQyxepNMJkfLJoU0a2ORlFa39uDvOehkXK4X1EZ3uT4kU8hC82y7LnbBeNXZ9DmT8Rh6FKlQW4dUwYCXpE4amH/vMpkANIZ/l3a5yQhIL1FIfe3OckCP4O/Hr+1mlQOMXQ4AkotedjlXL3/lAc7eW+D0PfO9LS4zDPdXY5zHKDgl5rvW+mYkU584iMevmegLF3rdo7aEsIbyNu1oLVUoc4/6enEjQRGwJFNHJhNgM6M2D/uPPmEOiq62121FrkApQW4FqK7ucgAQTs13my2L0jIXDHl98Le33VkOMJ1Ny2uO2DOR6e/6Ap7VzSqPBaCBgJgNqmCj5kwmAAjG4Mr+bC5XdrnN4G9/XWSSygdYw6AfTs3pdWFCKjdJpsPILJidyMSlxlVLiLArJzK1iTPLfJtucRaam4pMHvMwsoN+ay5TNl9bNLhFp5rPEecSj5nC4eIjWLZYt2or2jc5Wpf1JFPoUYAUIHqTZLIKvBWZGCtaT32I75eLIJJ12+Xa7IuuSpGpYLhiGqlIW+1WB8+NcL7ZYa4qMlmSadjUdWyjSpGp5TMPPIbMvlyQ3bxduqtsjWTqJzINqLdFPCyLJRbPTcH29hD/5V+ufjw8wD6ZY55dTxkLWGBJpn7PRyE6rDW2Qh6C0AJxLrazKHrWwn7HtafeJ98zHVXspo9xClms7HJerjrtcoARsAoX7GsXYP6LDwEARVOHOS80QnJj8Hfz4lDYRT6/WycymRtwZZfj5SHB3l0rMvXJZdokmQAT/l3kuEpXG+2m0OVYxIhYBFGoNbtc9v03AUrhfeQj5t9tnui5TZM/qieZXKe4HiSTE5l4yMFDCTFvp1f61ChgpcjkSCalNJJFgchPe5FMWkozjvaxy1VOsj3qgRO+9VwXdv71ibPLVT4bK+707SznasAHUJkZ8wFDuCwPLclU02EuK6mIhmc6OgBAEGkNhaLRVr96wTkySzJ11Uro7HdgMJj6ncHfQ5/jAnYOXp6uSKZrdphLCwmvJJlW78l1lpscms+hi2QijIH4PnS6KTKZOTvn41sJ/g44w9AbYp7PkYgEOVXwdtBitmywAFAUgNb1djkrRi3FEpOvfQ3Lb30L4nzV8dSJTAM+sI0GenzXwbg5+PvsHNoSAh8IySQzZPDB95zIdHOSqQz+BlbzRnW83LyEmbmX3Hph3S4XgMn24G+ghmQKxtCZ6XBLuBkbIo9BKl3meblyHeYAYMnNPekymQBANOQyyctLwPNAJ9MPPvg7Pjfr4V06ywGr8TQ538mu+8mfuwfKCN74pqGZlpcZRnvr67nQY+aezBcwmzE7Du1KMhUSUZdd7n0b+s36xzuUomeP7nK97XKFzd/D/MZ2OZ8Zu1zBR2ad1Xh9NSTT4SfMf8+2c5lEJsEDhlwoeIx0NgwCgMG+eS6TRYHMkkzhwNs+XCuSrYzbOpLJD/l6R7x/gPVMZPp7Xk5tDa2nldCo3S4XjOEp24Ekkyu7XE3wNwDTYU4pCBU2i0wVxZ4O6u1ygMEOAUBTH5c9RKahpZ3mLcF5cU13OSVvxy4XsACjwFzzLiKTa2cs5wskucT7XMGTAcRZ/4wEACaTY++FRpKJEAJKJfQGyeREJn/AIZdLUJqtTnLqXsf3oXOziNdZ+6bLp/1JJuUFYKnGBVElGdZUBw8GyGKxFrKI5BKZWolMcS67cWJbEY+QErTa5XxGkfrmHvGLWxSZKhtRXRQgfkuXHykAJTCk/rZdrlhi6I8QfemLiF9ZkUxqcIhDzLBIricyedQzJFPPzzLraZcLmSWZcnuCe0273LhJSDz5XmmVA2zwtyOZRAqey067HGA2AS4jqMxkev6jAOqpj7LCejqrK/i7FJlq7XLNwd9+xDGY+mUYa2sldoO3JjJ9HhMlcbU8XrtWYLvTU1zEGPIRtNJrwd+z/+v/xuBLXyrHtLC01VRIJn9kOt04W1FNJpPokcnkMpNYQMBDdUsik2fGgcrrp4sCWv3/7L1JsCXXfeb3nSmnO733ql4VCiCqAA5AQaQAUgBpqgG2KDcptugOR7fldrsjOixvvLIjvLG98Kqjw9HhZS8c4fDCXsiW2mE77IW6I8zJkiiSEkCIIkhhBjEVhnqvqt5wpxxPHi/OOXkz783pvldclFj/zQPqDTfvzcyT53zn931/hcCNe2UyWcsVm4y7FyilnWxCCHzhb9zXiclkElTb5e4KySQCqITCKYlMajgCnUyQ3Ni8pjuDvxkH/F34SgIkRWpyndrscjHj25FMPUXuwcTF4jRutQf7DkMGjtSZAItbxcI4/eijXq+xXlGWr+xypeDv2R39fOybyQRoYa4u+BsAIjZsz2TqssuZDTXBKEbOCPN0jqPoCCkHeNb/mebwNfEYK5KmrnPewGw0LdMlxv/eNwEpMfv2t4vvr5NMXe3DAXTY5e6A7Ok5zF3PZFIKVOrgbyfwdXzAeTKZ+FomE7B6bpTHy7XKTe6bFfVUXBaZfFBzz4o2kskGfycrMUvFcaXDbUG2rBFhdn7uDQUWcg5GGHzul0im+g5z8uQYfGcHxHRwS+9S8HctyXTz5/rrA09ufq+t7Px3eVQ8u/qMP95Q4JFfv4g3XrgJKXPMj+Mij6n4GaEp+4LCc89IMsVZEc9RW3kO3PzZdllUWEUErJ/v9XJchiyWUHn3uGFtdR5OehFCbeUKin1yitBpf+Y5fo0IZomumvDvNJEQDq3NoGwqcWEXPFsimieIjH1fd5dbsxO32eUqJBMvnCW/qnVfZLrHqxCZlPZL+8g77XI814NhluQlDH6NZCrb5ZI5UuUDTeJ5qb03HQwKu5zMJY7j4w27XMoDHPXo5jUSWrhpIpkymSPJ8o3ucoVd7jzB31IHf1t8dbENyTQ0JNNck0wfGXGO3uq2CG7U7rXGTCYAIDSDyhvsch7TdjkSddrliCGZ1rH+9RJM9M5kSi5eBQFwG7IQ7ZqqNvw7PEZC9YC96i63BckErFqN1pQrKGIz93J67uD0qSiVIAQQUICU7SSTISkC5taSTAMxQPD0M0jffx+psS4huAiPpFjMz7bb6jIXWZ72t8vZTnS0o0ueCHQmU11XnZ41jxpotXgOHL8LXFqJTLwkMmVJDJqrZjtPqXyxysGxtCMNAvBLl9o7zJXaIJerK/g7XbOclWslMul7KhCsQqLuXg62s8uVxeTLn8VE5jhdrnagm/JwltkSA6LHMEsfxW+9heQXv8Do73+j+LmNbjrxdDX22YVTiXRcZTL1t8u5LgFzJeSsX15SWw1cBkUA5jNEM/33w5n5rL2kF8mUFyLTpHuBEk0Bd1L8b8CDYsFd/IghmQTxIRxaDUUvB39vUQEPgIQWJJPevaVwHn4YaY0F1B5Tqyg02EegJEBTxHN9DTZ3l5shYrwXybRNJhOgRaYsyVvzQuxzOnb2gOVtuJ/+NOhkgsWP/qLXa2wcYyrhEHMdl8R1SzKNLthMplkryQToXKb14G9rl1uy4bnscqnMwahu4DByRpgncxxHx0iZJsb71soOUrLLmUwg4m6O+wNjr1+kC7iPPQbnk5/E9N+uLHOV4O9M9lvktdjl5J07oEZkuuvd5QzVFytNLLDJ5FyZTJQSOJyu7HJAiWSaos4qB+hriY4bSCbhgeXmOdxBMkVZVLLlDZEncUUo9NbDqk3Z+XkwdjBNphg5umsi290FKG20y2XHx/pnTGyGPGfwt31/td3lDl/VXy9/brs/WkMy9RI+ATz+5QcQzlK89/M7WE4TDHerY6AnqH4u2M/cW8tkqpkz1FVdE6NKHf1C01LbUlzQz+1W+ACaFAL6NemwXegGmJ7rXAN67LnYR2Ry2SYVNLio55o1IpMlmWwmXJ/ie7vg6QLh8bLIiHQHNZlMDXY5EgQVQVcHf9/vLne/7uEqRKZcL2R85J12OWE6U2mSqd0uN0tmQDJHonyANwwmpfbeZZLpOD5GrnJc9KoiU8YD3O4hMg3MTdyUyWQDchvtcudQ2G2GkO1yNesUmVYTTWbb4M5mCFOJE6qQiRju7S2CCm3tXNMkU8Mubp3IZEkmQSUgJRjPujOZjMiURe30gMOcQnRoK3lygnhP59zcIapTZNozItPxusjEjNXS51jWWCObSotMuSaZGj47h1GEBr7gPVu39qk4y/VuZmozZvqJTHWZTAMxQPDMMwCA8K80zUSH+n6Kp4dnOj6HOUjV3bfLBVyLTIvCLrf9bvAiyepDv2+9pr+WRCZtl9MPfUvgrYvldeUJhsz8Xtka1tlhLrhQtEEuV3+SqV93uTCVBbUxuRzg+Kx2uYuP6UymZLVgaiJxlukSQ2pFJv296f/7LYAQjL/+9eLn7AJg1V2uJLDX2uUsydS9MIysOO7CkEwRVE2g5zZlx2/msYJksrRk4KbFArOtbD4LLdvlujKZTAUi2BCPk9xknBFNMuWZKujbs5JMvvBBE14imXS4u3j4E0g+2BSZOkkmQItMeQpCUkSGZGq0y0VTRIS2/z372g00XVMFEz1+toV/242m0NkDFrdBGMPg7/wmFj/84ZkaJFRIJlYlmZig8EcCKsuglstOkol6XmPw94I0N0hQUmdmdnWXs3aVoRhils5wHB8jYwDdwlZmr+eyRanYgHQ3z+nA3DeLdAFCCMbf/CaWL75YCOpnI5ma7XLZ0RH4L4tkMvdCDE0s0MkY8uTsJBOgNzKK4G9gNT4n80aRKZ/q4G9LPtuNBwAA98AtydSyqC9IplLwt4qTepFp7fqwdrlg7GCezou4CsIY2IU9yMZMphNtTzU5jFmeg5+DZLKkVq1d7vAVvZHS0uq+tux5WG5nlwOAa5+7AG8o8Nfffh8qVxskk29JpniNZHIG+pnYc7OtM3P0Ixv6vb3I5AvWK5MJQK/w7ySSYCRFcDeCv7nOZFqIdot4bfA3IabDXB3JpJuY9M6EA8D29uCkC4SnIeKFDf7mK4LbivANJBPfqa4qiUINAAAgAElEQVTxXF/b5frQYX9b677IdI+XFZl8qbFwT2Xtg0lZZEok8rB+h7KcyaTCKRLlQzVNCtftcoZkuh3qh5IlmWxHhoz7OGI+sqZgP1O2u1xTlpFV5tftcncj+Luwy5ljbieZqrtT1IhM+WyuQ3wJEF04xeD44vYHsnNVo89NuyEkhWwgmbjx8FORd3SXEyCpnsDIuENkov0zmcLRFQDAMc0glWwVKYKxAzfgOPq4tCCLTgqRSWcy9Q/+9rinRSaoRlrB5RSxowd/fpdJJhv6DaCdZDLB5AHzau1yQzGE98R10CAoLHN8pHNb0mk9vt5VDnMMydTXLmd3UNtJpqETADTR96W/A4TbT9RnUVYIA5U6fEV/XbPLWbGILM2ue5Odp1S+YIU4VaYdxbUOkcnfBZabHXY6M5kOD8Amk9pQ8nWRyXMYlFotAHYvB4gXWSW0uraWR3oxXG6owB1M3B1M89X135bJFGBofk1/b/atbyF4+mnw/VXHF26CTivB34XIZO1yZ8tkshkIVmSCzM8VwAusRCa4DNFCf4ZWZPJ92VNkKpFMXQuUcnchaOF1/b6O5RIqZ1BKlILRrchkFk8t9ua6CngAmopCZIoLkukq0g8/gsqqzy8b/N0qHA8uYiCTNZKpJZOJ0uaMp/KPbhG8C6zahS9awr/tHGDJd4CFHheHzz2H7PAQ8RubC5CuitJSdzlezWQa7XkghEDONLnQRTIR3y8yrWwNHAZKgBkG+pqpEVPtBmBr8LfMi0Xe0BkWJFPCAZL23zhx6+xycYtdzgTmLzI9Nxt/83cBpTD71rcArHeX62lXcccrGqRUeZIgn04hjG2ry/azdVmSCQ5cQcHGkzN3JbTlWXKkyS63VkopQzI1d5djKobT0aWvIJmScibTul2uFExeKndgSKaJg1kyw8hZzWn5xf3mTKY1kimV6lw5Pa2ZTIev6o2mtk6FdSUCna0WrrrLdXWEtcU4xWNfvIybb+tn0bDGLhfV2eUIabTY19UizjBoy2T6+Kf6Pew/3uvv1R5jSwkzhvbJZUojCcFSDFR4frsc15lMc9G8EQ5oEazWznfxsXqSKZEQDtPjT88NDba7B5HOEc2SgmTySt3lCmE2DfU1VX69k+MiC9CW43FA9ftM/7bWfZHpHi+rtvrJu/qryrBMsubdO2cInmnBIotlscNG1hZAI2cEAoJpMkU6nwOgIE0D4LpdzpBMVmSywd+EEjhCIeMBZk5QkEhN1UkyJfUkU25IJtpEXvWoRGqSyVp35m3hbVFVZGJrdjkAyPcXGM4vbN9pYNd0mDt+t+EHMkhZff/2NbiZ6FGuWkkm6jigif5ZmeWtHZe26S4X+vuAIFhSg522LGgIIdi7MlgjmU4Kuxx3qNnp2cIup0r+6YbXTO0hddgEG+snfwC8+4PKP0WphMfZSmQSLZlMZoIbcH+DeLAkE+Ec/he+gOWLOvzbm2jaIZudkWSiDqRKend3stRDF8k0dAYgpGSXi+sXT201jxtEpoNX9EN955HinxjXdjmlFEhiqLE+djmH1QrRzrVHIG/fhpw3hMUHF1bZR6XqIpnSg8NaigmoIZlEKc8DpQ5zBx3XZ3is7/G1yfdk8ABCqOIcNk2wl+kSAdHjLRcM8dtvI37zTYy+8Q2sl8tpNfi7D8nUgz6wGQieq8A8/fOyoW1237LXUu5QhLM1kinIC4qhrWw+iw7+7rB6xacVksmvua8juYDKPWR5DrEuwl35PPDl/xz49Nd6vkNdgQjAM7eSyeQwAufqw0CWIb15UPn5zuBvQItMWQRCUiRm46hNZIpIBxllfzSVoAS9O1ANLMnUEv5tF2dzvluITINnn9W/94M/7/U668dYdJcrB3/fiQqrXD7X8xK7qdRU1PM2gr8JIRi6HKdqAEDVEjx2s66LZLKLvJEYYZbMcBxpkum8IpPNaay1y5n5ghVQ3U9+EuLaVSx//IL+d3PNe9zTmy7n6C4nTaC4c1HPI+968LfZhIqxssudV2TyBdNi2DrJtBatYCtfLIE8rwR/V7rLcQ9MSQSsnYhYBX+vutipNbuc39BtzDOZTIOxi3kyXxOZLrZ2l2O7O/q9hifIpAS7K93l1t6rUkZkemL7P0pIYXdPthS5AW2Zs9VJMpXGf/g7vUgmmSvEWd5ul/v4JeCBz62yD7eo4hhbyopMfYKq01hCsAwDtTy3XY6rFDtkgRlr31hxrJ1vI5fp08Ds4w2BOo0lhEs7u/9WjmVvFyJdIFxKRMsM3KFggq4I7iL4e7EhMsmTk6KraXHMfv/P9G9r3ReZ7vGKFikIAVypJ5GukshVS/tPdwSR68lRWuout75bRgnFUAwxjadIZnbwbCAZ1uxyKo6hsgy3lnqyZ0kmABBMIhUBTt1Bp0fYYx444Y0ik6WL/PVMJvPez2qXU0qtRCY7eT1DJpOcrUQmcSUBAcHBe1tOXna05awu/DtXORTJkGXVB1Mc6sERllITeXvwt3BArV1OOa0eckH7ZzIt+Q742AFherLeRcLsXhlsZDLFZKx91Wb3orPFqyktMplz1tKdMPX0tVIOq9+qvvvPgR//z5V/ijNtD+hFMhkhcCCqxINSSpNMZtcz+OIziN94A/LkBN7E0A7L+klfV7nMhVRZ70mWFRW7zt/IkEyLODVjgtKL7i1q0SQyHb4M7F8HShg+E9ouF8sYrlkPNtp5SuULhtxMNHlJaHOu6nstrQlKBqAnqcujDftll+c/u3mzv8hkxptloq/dVYe5js6U4VGtkDyZ6Pc0NbluTRPsZbaEDyMyObQgEka/83WslytYPcnENkUmSgkoJ70ymeIwQwoFjxuSCUB2+5wikyFRpSBFd7lwmoBxCsejvUgmm89Cx+P20GqlNqzTgdgkmSK5AHIPmVSFCFdMnLkD/P1/CQy2zGRiAUTmVLrLOZxCfOJh/f9r13RkFtY+b7lfBvsIZATQFMlCv4daUjDPdfA3OjKeTFmypY3IqBzGxJJMzefKExSEAFO6AyyPINMUb7wpwR+7jvkPftD4e23HuLLLrRZ1lmQCSoHw43aRifjeRvA3oC1zx7n5PGssc/nSikztwd9lkmmWznAUH0FyWs306Sh3PdAfK5KGrpNMuURg5gBli7f/5FMIf/oSlFIIs1B3V6TcPA97dpeT8cY9mRmh2TVh7vFdJ5n0e0mUtsux8Rjy9GxdCW15wjRwcCfQKHuJZKoTmabWkjtqzGQCgAFrX6z63NeUYjLXZCl3kccNmUwtJJPNZLLVJDKpXNOmbHdXCypKgmeLc7W1bySZTj/QVP+l62f7w/4esDze2i4HAPtXR9h7UD8fh7vV+8EVVAuf63Y5oDfJZJ/3jSRTnmuR6QxWOUAT0l3irOP1J5mSKIMQOQYIz22XIwt9XU1ZN8mkX7upw9xblX/OEgnu6O5yfTdU2Z4mmeJYIV6kRRg+pQQOK22u1dnlrG20VAVZ/Csc/n1fZLrHK5qncH0KZsKlXTPBbxRw3DE4MYJCLPUOGyG1C+GxO8Y0mSK2O/s1O1oAViRTeFLsuuXLJQ6WWvi6FKz80w5STTKJoD07Cnq3b+AMME/qRSarzK8LD6vg77Mp7FmeQUHBZS4YJQgc1myXKxYWqwcy4RwkCJBPdSaTJyi8B/XC1CK3vasQmTYDia3Yk9WQTI7PkS/058ZEO8lEHAfMkkxKtIpMfUgmpRTkyQkWGMLZcQBivM0dJMzelQGieapJgzwHTm4gIRO4HtM5P0B3i1dTPvcR5kZ5aAn/TgqR6QwkU5Zo+9Ta9al3btmqO0+ryGRJpgCRjJCZ+zfJE2R5VtB8wdNPAwCWP/lrBLtasCA11q0+JZiARNJ7kpXIfiLh2B2CEIVpHJboxu2u93nUkMl0+GrFKgcA3JBMZZGpsftVqTzBkJtugmW7nHNN32vJe00i0wVAyY331Bn8fXhQ21lOHwADCKl0lwNW4/f4ogdKCU66cpnCk1oheXLh0wCA049/AqA5DyfMQvjQkyYuKKbf+jb83/iN2rByj9O1TCYzqbYk05qdVjisl10uDTPEBBA0X4lMd84mpNryhbYlpcx0lVMKy1kCfyxAuNsr+HvdLtdoWUnmgMqrmUw1wd+hXEJJD6nMt+q+11YBhiCgEHbn1NrlrmqRKblRzWXqa5dzlQIhOeKZoXbqSMF0AUAhMs/Mroq3mPQDenHBXYZli12OEIJAMJzQMQCFt370Dv70D1/H9PPfRPjiX229iRCVg78N7ZUmEuEsrXSWAwA67CKZNu1yADDyBI6kWajUiUxG2Ouyy1nyY+SMsEgXOI6Owd0AkBJK9ruu3IIeKdvlDMnkrJ3TLMbAbPpURKannkJ26xaymzcRpmEhYPYmmWxg/hqRYGlG/9K++Xu/LJLJgcsp6GSM/PS8djmmmyNQqseDsl3O3bRXFtbL8QSEMYDzje5yADDqEJlsJlMeTQFXb1DpTKbN7nLrZIslmdYzmQAjMt25s5GRl081rcytXQ6Al03PZ5drymSqyWXcqoI9bZcz109fuxygx5cvfP0q9q+O4A2rJJG/YZcrjQc9SSb7vG8kmY7f0SLWlp3lbHk2iL6liuDvHplMaSwhRI4hOb/IhIUm8k9oe2atpYLS9bVY0WGuKjIVmUwd87NyaZFpgUxSLKcpvMFqLuqW5z3pcjP4u5ZkMo04foXDv++LTPd4RcsUnpfrM0kIHLOwbhxQ3BGEEZnSOEe+DEF8v3bSPHa0yBTOzC5mEyVAmX5wRifFhMiKTHveXmVxylWM1Bkipy3CTamGYtjYXa7TLnfGwa8gN0w3rYHLm0mmLAbydGN3ig2HkPOZCavmGAx9HHsH+Pjtfp0mivIm+uFdQzJZC8w6yZQsM7g+X+H8Iu8WmSok06YlyJbO88mQq+aJngpDyDTDInXh77orkakjr2P3ir52jj9e6AlFfIpEXNad5czuSmuL11J53EOqpN6LbiGZlJDI6BlJJvNwLEI2TUWpJZn0vUhEj0wmQyzZBakVVq3I5D35JIgQWL74IoQ/RqwEWHg2kcllLvItSKY477Y7AsDE1cc6i+aFyJR+8E7v4F2lFOZJDck0v6UtMJc+W/lnWiGZzD3fyy5HoWQOtiYWiIetyNTQYa5og7z63JVSrXY5laaQt+9AXGogmQjRNJPJzLFjmR3bKKMY7/v97XJrNdnXn9np4d8AqO/spZRCmIZwoT87decQ8WuvYfyN36l9KdcuoIBOuxygRas+IkoSScREQRB51+xyeqOCI6JAniskYYZwmiAYOVo8aLEG25LTKYjrgrpuO7W2nsmBepJpmc2hchcyV1tlVrWVJ/W9Rx2FPFfIch38zS9fBoRAuiYy9Qr+Di7CM2JCstCL4Fq7nHnfEdpz94rX3qLbD2DO4dhpJZkAIHA5jqHHnZd/qDe48muPQ6Uplj/+ce/XA/QY7hJLMun3NN/oLNePZKK+B1Vjxx57HHcy8/m3kkzNdrmkFPw9EiPkKsdH84/gGLG9L8200aIbKESODbtcFoEDcAmrXNv+U7qtfPjSS1q0NsJIb5LJirNrn0V2R89H+MULcDit0FZ3pYpMJlFkMuWLxUaO2TblC7bq3ubvluxy9d3l8jUqjjhO9dyZudOwS2Qy93McTwFDIulMpjLJtJYvY2pyyQelBHsPDjYzmfYvAmm6kZGXHev3VQR/A/Dl7FzB37Zb4kZ3OZvLuH9WkmnXBH9LCEa2tvRd/80r+I/+2y9urJWKvKN4CoBUM7d6k0z1a5miPjah32foLAdo8ap38HefTKZYggtgiPBcgiIAPb8DcEw6RCYzL9wgmfYe1V9P3q38cxZLCJf1z4SDjg5xqL7HTg+XBckEmG7UmdRgQboExOpZqLJMB/fXBH8D2D4m5W9R3ReZ7vGK5ik8R1vmiCPgmAl+IyXkDsGJfnhpu1xYG0gLGJEpniKcmWyfoHmyo0P/ToufyRcLHC4PKxQTAIgsQmYX1D0mCwMxaM5kiu3CbM0ud87g76JluxHHRm0iU+F9r+5O0dEI+Wyu25IKhoEY4HD0Lg7fnW3f7Wb3GnC8ufC1x5mkm3Y5x+eQhcjUTTLxsshU00HLlhXe0rw5/FuenCB2d6FAMLjggfTIZAKAvSv6ujj6eAHc+Ev93uiOFpmKh3A/kingWrCKCCmEnLoiLEXsUORnyWSam5yTtZ1XHfzNetrlTHc5s2toJ+1WWLW7idR14T35pA7/JgQnZAQRbylYmnKogxxpb5ogMefanvum8s1Dd5osAX8H6YLirf/4v8Ds29/p9TrLREIpbIpMhy/rr2tZDGWSyTG3Z1+7HKSqUEwAwIYDsIsXkbzfIDLZjl8l0i81uRFNi+bs9m1AqUa7HKDJRytI2ryM8vi906fD3LLJLqcz3U6PdDBmXehymqfIVAbXWHeiF/S9N/qdBpHJLvSUqi6aiuDv6rjOHIa0D8kUaZLJITmYowBGz22XA7RlLjRvN5ynWEwTBBPXiEw9SKbpKdhYj++tE9aaTI6Ab3aXC7MlVO4hzdVdI5k8Q8QoJytoFMEoCGNwHnoIyY0PKj8fZREooeC0ZTwd7MM1z6pkqce42vvLjH9xLvvZ5dL+k/7iUHbcVpIJ0Au0O2qMO+lVfPy+vp+ynQdAPA/zH/xwq9eLM4mBocPB9bg3u1MVmQqSqSOTibSQTIdpm8jUTTKlWTX4GwDem74H4el5WF+RyYrklUwm27FzfX5o7pkBdSobgN7jj4M4DsKXfrYmMvUlmcx9s5ZPJY/0GMD2LmiKsi4Q+jxVyWRiYBMtVFq66CzliRI5YgKx9Xg5LwijctnXoiZEngpRZGIBKBa0fexyABAms+J18p6ZTLsPDPCf/au/i90HAyzSxYZdDsBGhzl5rAUUbZfTzx8/m51beBCMbEZ+HL4KjK60NrFpLX/XBH9vP/60/tkik8lQamWBrSfJZEn9xvntRz/VjT32z5BHhZ7d5bYI/k4iCSEAl2QrMf6sZTZrjzpEppVdbu31uKvnHmsNotIi+Hu7TQ3X19fu9E5UdEQHtBAfZ7kWpVVesctZ8ZXt1gR/477IdL/u4YoWKVymF8jE9SCMVaHZLjcCJTkYXwV/N4pM1i5nOjfxYUurXn9i7HIrkqlOZOLpHJl5EHbZ5QC9yN42+NuKTGcN/rY2NCuKtJJMcQ0iC00y5fMZolTCdxgCEeBg+B7iuSwmq71r52oryZRk1ds4CQ3JVLY4iObJP3EccJlCUQWJbrscgNZcJnlygqWv0fbRvt/bLjfYceB4TJNMN14AgouIM2FEpirp0VXFZIuSVrscpRliQYtd461qZkWm6qTY7tzaFsR9RKaBmdDZbj32mh+UkNzgmWcQvfIK8sUCUzqBlzSLgW0lmIBC1t8uZ7KtuuxyVtibJQvAmyA6EYDMEf7kJ71ex95jG3a5w1f118tVkolxQzJlMTxzOTYGE5fKFwxKqkoeky3n6lWkjXa5TZKpq1NNdqCvkUa7HHQu07pdrjwh3L0c4PTWEnlbG9zwGAhqRCZjQTk9eUcfr80UKpEFVth0oO/P6Ed/Bv+ppyCuXKl9qSL4O9E2qZXIZP5mvm6X60cyZYZkcmgOQgA+HpzbLgfo8XsB/b6jeWpIJgFwT9sfZfsEMD89BdvRn2NrO+SCZJoU/2SDeMsbC8t0DkgXmcwhbCbTOUUmR+rrXoqsIADscYqHH94gmSIZwWNeey7SYB+eOe7UhlDXzRXM+BeprF/w9xb2BVvBxGkN/gb0Au0wH+Pl8HdAqYLjc4RLieCLX8Tiz7cL/47SHANmzokZ96ZWZCrscoY+6RP83UAyHSTmmVizEO0d/M2rItPB8gCub6xSvUmmtWBblOxy65lMRUdUtzI3I44D77Of3SCZor4txO04srZpk92+A+J5oIOgV5esrcvOoxQ3wd9a6JEnZ89lqpAjJhAbaajHmxqSaT3fi7juRvA3AAxoe2ff4jNPpgVRo+1y3ZlMgG7UYM9pWWRiRmRaz2WSJ5ZkWtnl/Px8JBOgc5k27HKHr5wt9NuWCf6O0/4Ud5/ybCZTVEOpmc33rgYoYRfJdPPn+r3z9jlY8zF2B387TSJOTaVRBubqzzBQZ8wztTXXItNtTFp/zAo2tXY+J6jM85VSyGIJ7mqL2zb2bN8ISypXcAclksnOeyy9WZqbyzLRVz6s+5lM90Wme73iRQaPzgExAHVdCDNhbrPLAYDgqgj+JkH9wsza5RLT+tkZtizgNkimBpEpmiGj+oG57GOXM21568p2p1v3MeeGLjhr8LcVUOyieujyZmtfqYtHuehoBGlIpsBhGPABDkbvAgBuvrNdTs2H6VP4v9/4fSyOq+KUPc4kZZUFaLyWyUTH7YM3hACXmSYHOuxywtAKXSJT6OvzvnPJByH9SCZCiA7/vrkAbjwPXP0yklDC8TgWSX3+VlMVIhOhrXY5kBSxYGezy81v6q+1mUzl4O+WbiCGIAvMLm5oJvDrJBOgRSZkGcKXXsKc7cBPzzYJdpkLRdJ+Ey2letvl7Ge+TJeAt4N4qt939NprvY6rEJnWSaaDl4Hg4qq9uykmKLIsRySjgmRqEszL5TkMXAG05v07164heb9BZLKkUIn0WwVp11+X6YGeQIkHHqj9vn5RURP8XSWZ8kw1i9NpqAPk60gmKzItDgCZ1drlLGkjcn1+szdeq+0qZ0vv6MnNsa/JLucwyB45KjK2djkzfu8MIe8GyeRyzIy9dzlNEM5T+GOnEA+6OszJ02kxhsaZbJ6w1pFMIoBUspJjt8hMdzl59+xyjrFdSZEU16QlXNjuzobNJc7ibupocLEQmWS4ABEChNfstMdTSACZkp2WaP3acqs8FECHfy9O41YKOHAYbqdDvB5+FZ9+ZIbxRQ/LaYLhc88iefddJB982Pv1olRuiEyzowiUkqK7lCwymVo23wDQoJ5kGvsCH8VWZDojySRXwd8jsZqDeNuKTGIzB6ewy9VkMgHAgHkbVlD/yScRvfwykmRZIZm2s8ttkkx8bw+EEBOy/Muyy5lMJkMt5ufoMOfx0qLe39GbAA1zRf1almRqssv1D/4GgChZFK+j4hi0ZHn0GzKZbFmRqZrJpDcNN0SmCsmkF9iBnIGdk2RyOKuSTLkEbr1+9jwmQNvd8wyI53dXZHJK3eW8tbwtf0dTL0k7Fdc5v519vMpnPcsxCoaw4xmzDcmUxhLMXEd+fk6RaXELS/hY5O0CWnF8dSKYGKzEH+jIFKVQZDL17S4HAN5oNd5VSCYrcFtiqkwyGUGa3+8ut1H3RaZ7vKJFCg8nwOgBENcFz0yb5qadUbM7wXmOLLZ2uWaRaZbMkNgubkHLYtmb6Ewms+uWzE5xFB1tiExseYKc6EVeH5JpIAbNmUxxfUeGVfD33bHLaZKp4VgbJg5sPEJuustZu9xR8DEoBw7e6T95SWOJ7/30SXycPIGf/NvqYt0ep1K8MmFIwgxOoDOZCCOgw/ZORZILiDwDYQQSXivJZIWGLrtc6O+Dc4LxrgfQfiQToMO/jz6cAUdvAw9/yVBZDGEXTrxWdgHVRTKBJogdBnWW4O9ZvV1uRTLpz4j2IZmMyGSvdfu1TDL5X/g8QCmWL/4VlnwHA3k2kUlQSzL1uD/yDImZL3aSTKal6yJdAt4EyanZxXnttV4W0XnUIDI17GAyTgEFRMn23eUY6klH59pVZIeH9aKjtcuVSCY7Ee4mmVrscqIsMlm73GpSsnNZf65//Z33cfRRzVho79ea4O+AB+CgOCUKuPNmYTUpH69dKHKlzy+TaWMeE2CzCfJNq7DtwrUhMtFepI4WmQAO/bN8Z9TYNnub0q3i9fs+OVhC5QrB2CkWbl0d5uR02s8uZ4UCt2qXA1afse4aOdci0120y/FMn7uMx4WFsxCZRuMN608ko+7x2NuBq/Q9koeL5nsrniE2RNQvi2QaTFxkSd4aShs4DPSOh0QN8Nlr7yMYOwinCQZf+QoAYLFFl7k4yxFYkckIZ7M7EQa7LqjJcslnU9DBQAc1txTxfN1xd41mGHkcN2MBBdIQ/G1Ipo7g7yKTqUSe+IERSvra5YoW3SWRKbHd5TYzmQBgwP2CvC1e9/NPQcUxxu8fwxd+kVl3HrtcdueoIGk8a1m5m2XeT05dEELAjKAszyMylbt5WbtcQ7RC+bUsFUdctxr8bbvLkXaSqZj3pMtV8HeSVITCFclU/znOjCAydlbHyU1nv+xWPcnEd3eKTY5BPoPYMu9ovRxGqiTT8bv6PJ01jwkoSGQen2w0vjhPeVx3MFPRdPPc2s7bHblMdn673im7qPkBMGyeQ3Qeo+jOMqOMggnaGfytlEIaSVAzT/PvAsl0Snc67+vG7nKAtpOWRCY73xC2u9wW9kh/d/Wc84I6ksmsFcRqXM4aSCbhMhBy3y53v+7RklmONJbw8jvA+EEQ1wXLuu1yACC4RJpIqGVLJpM7RixjhCYZPwhaFvgm4M5OiE5PNeVxOagOjGyub0ZXrUiktupjl/PXHhi2PTk9427Kul1u6DLM44aHexPJNBxBzucILckkBlAkh3dlO5Hp+T9+G7O5wGXxOl7+y6NK+GlBEyleEeySUML1dCYTdUmxw9RUGWUQuQThFJIO2jOZetjlMmOXG19wETi8IJm6RAoA2L0yQDiXCPMR8PCXkUSayloUwd/9u8sBQEhI5eGzUSRFzM9KMhmRSSaVhWqcSngVkqlHJpOrz5GlSurscmw4hPfEE1i++CJCsYuR3LJToSlBdce/XhOtLEJiFpB97XJhFgLOEPHUtI8/PUV282bnS9WSTHkOHL62YZUDViJyFJe6y/UUmbgiIHUi01UT/r2WYQNAC+mEVUi/olNNw05ZenBTB+vvNN+DdXa5smi8f22Eh5/Yxct//iH+9b94Hv/6XzyPH//bd3B80yzwCpFpk2QihGDsDHFKKXDwcm2LjLwAACAASURBVG0mk73muNTnN3jiMxAPPdR4vC5n+n03kUxr9jMu+nWXy+Nck0zUHOO1BxG98ca5slEAvTt8Yo7JinTB2F1ZDzpFpvVMpv4kU0H3mc84zEJIJQHpIZN5iWQ6n8jEjMiU8Kiwy1khkY6GyOfzitAbZT1EJkrhmHPblt2IaKqz79BvI+EsmUzBRL+/tvDvgcNx5U6OXfERrvhvIxg5WE4TOI8+Cv7gFSx+2F9kilIJj1qSSb+nxUlcaV8uZ/POPCZg1YxArdFMY09AKqrvnzOTTJuZTADg+1oosVlvXcUZBaOkYpdTTXY5+8zi/sYGoP+kDv++9M4JAh4gkTmU2uxmWVtuPcmU3dEkE4Bfjl3O3P/KiInWGivP0WHO4+t2uePGaAWgJFgaUnCTZNLjSNDTLhemi0rwd/kc2vGriWSyIlP5eqLDIYjr1pBMxyCOAxIEetFNBQb5HPycHcccvmaXs5b585JMAJzk+K6STPaZnUenm+fWzr07cplag79lqq+fQbPlvvMYe9jlAC2KdJFMMsuR5wrU1QKMd26S6RCnbBdJ1v661npWe3xrdjn7Mzr4u4U+rqngwuq6r2YyGaHOjnlik2Ra7y5HCIHj8/vd5e7XvVnRQj9wvOymIZkcMNMtJ0wblFMrMrFMd5eLosYdSruTsUwVKBL464RBuXxjlzOZTNNjbRMpk0wqScCW+mb0FDm3XS5MJTxBi51FW9J0lztr8Pd6d7mhtxI5Nqox+HtoSCbdXc5SHvxKils3Zr3sI4fvTfGz793Ar31pgq9P/hVyqfDX315ZeSzJhHyVWZSlEjLLtV1uvtCh3x1BiQnloFBgnCCjg1aSSbC+drl97Dww0AKgyWTqs8u996AWVY7zRyEv/TqyJK9kMjW2eF2rviKTQoJI8POJTEClw1xkWnT3EplsvoWxNRUkk0FyyxM9AAieeRrhSy8h4RMMEHYukOuKQoDQniRTFiMmBAykPSQYpc88W0IRgmQq4H1C3xd9LHNWZBqUx5mT9/RDvWZyySsik4IiZHNBVHecDgMHQGomwuKqDspO3nt38xcJ0fdS2S5n828aJjHZwSH4pUut2TcVkakm+Fs4DP/+f/kF/Kf//bP4yj95DG7A8cK/eQd/9M+fx//+372AF793CyfZA43h/hNvD6eMAzd/viKZWI3IZIKVd77xtcZjte81yuTmoqnIZNokmbpEFJnlUFIhJoAw+UmjZ78ApCnmf/pnrb/bVUNXYJrkYILizkf6Pg3GYkUyddjl8tMpqMlpSc7QXQ7YDPRXuYs0V6VMpvPRGTQx1CANi3NsCRc2Gul29qUxLpZxcb+2lVe0lY+aBdx4hsg8g3sFf2856QdQWNQWLeHfO7HCbgR87uJfgSxvIZhokQkAhs8+h8WP/qK4z7oqynL4hcikn3nLaYLBZDW+5LNpZx4TABAjzq1b5kYmY0Q641qRSS2XegEvmgnypCR6lu1yg4EVmfo/HyotugHkUbvINBSDDbscf/BBsP2LePC9OXzu14rajWXF2TUyWN65A3bBiky0kcA5c5lnKDGisxWU5el5MplKtj5/R4+Jdr7g1AR/T2eFTQ/QFvu67nIB7eguZ0kmGZWCv5NKh0BKyWrBXFNWZCqTcYQQ8IsXkd2+VfnZ7PgYbGdHP9+I3tAcqPm5g78dTqvd5azItP/42f+omQc76cndtcvZvxXV2OV6kkzWLhfU2eUW5jMf7p/5GH3BkOVqs2PfWgmXFc6VpipEHnNNefIMeablmt/CjO92kkxcUE0F9bDL2fkGd+nW5KzY2wMzXZ7dQdUup7MozeuU7XLWNlqzmeh4/D7JdL/uzbIik5t8CIyugDouaGpJpoYb1hkAIOA0Q5ZIqChsnDzah0yYApTE7btR3g6QLkA9k18w1QNjmWTKTk7AzUDgKdI7+DvJk1pRYxFntWTL3e4uN3B5YeXZ/OHNhQWgJ/YqjpGEMXxDMgEALoXIM4VbH7Tvzucyx5/8b6/BHzn4O//k1zEZLPHYQx/i5e9/WEycV3Y5UXyWVjF3jV2O8ry1sxwAJER/howTSBq0B38b4a2cMbJe2fEJQv8iJpcHejFB+5NMe1f053Q0+E2kmZ5cl7vLbZ3JRGmrXS4nCSIhztZdblaic0qIf5RKeLzUXa5lkWAnuIHZZWsjmQDAf+YZqDiGsGuSxfZ2IkaEJpl6iUyaZHI6BCZgtaCOZYjs5k3kGcH4c/p9xa+/3vn79h4blYO/bdviGpHJ3t9xksBNAeK57UHGpjyh7bqomQg71zTJlDbmMu1Vg787SKbs4KDVKgcARDgbIlMdiTqYuHjytz+B/+C/ehq//y+fxXP/+DMQDsPz38/xh7f/R/wf/yvBT771HqJ5dSE9cXdw6g0NyaRFkvLnZHPA6HuHoDJptcoBpcXoBsnUZJdjyDoWhnYSFhMFTgyhev2T4Pv7mH33u62/21VDl2EWp/CHAsc39f3lj0qZTC1Crcoy5ItFYaGJs7z5ORhPNenmlML6rV3O3Nez1GSvwL+rJBNSBkkyhFhuBH9b2qZMhPWyywHwzLODtGxGIV6RTL88u5whmVrCv/duJsiIwuMPvQ8sbsMfOcilQrzMMHjuOeSLBcKXXur1enEq4dsFPbcCV6xtlqZ6k0wmjmA9/Hvk6fslaxCZ5GLRGvoNNJNMw4E+b30zmQB9vZRzcAqSaX2TxGYyiWCDZCKEwH/qKVx9X4uYVmjpRTIxoYmdePVZKKWQHR2BX7hojpFpgftulr3/jUDD7kImU2VRb+dfJyZ8vy74e02wpI5bfP4Aiu5yHmk/n0Umk4wBZwillMlkqt7rbUHQRfC3qB4nv3ixtrtchd7wdjC8C8HfYj34+/AVYOdabWe+3mXOg5uc3t3ucnbjM5lt2uV6kkytcRAmGPs8JFNb2Hu5HI912uXs95Wnryn3LpBMC767GfS+VoQQCI/3sstZcpobYWib8832diHMPVBvl6sJ/j450c0Jap6Rjs/vB3/fr3uzYksy4RgYXQFxXVDT0aqc6VEpQgB3BE5ipLFEHkYFzr1elmSKMwZCE3htu4+enoRT6Afjcqp3+8skkzw5gTCT7RGlvfBNu8iuy2UKE1lLtuTy7naXG7kciczrB8EWuxwAkOWisMsBQHJRT1wO3m6fwPz0ezdw+8ZckwsDB9i/jmf2vg2Z5fjr77xfOc6yXc4u1jTJNAdjaafIFBsCgTOCrCOTqY9dbnYcQ1GOnUuBXtBukck0HCoIEuKIf7YYmF1fB38TojH0PlXs6HG3Nfg7R4KYi7OTTHZSUaLtbCZT3ssuFwKEFcHfZeKBEbaxaAuefhoA4H6srzu1qO4s9ilKBAjJ0SuWwJBMDukhMpkFdZxHiH/xNgDAu0Qgrl5F9Gp/kqlilytEps0shsIuVxKZ+pQvGDhI7dOPjUZge3tIGjvMXajcH4lsX0SlhwcQLZ3lgCrJRCmptr9uqOGui6f+3sP4vf/mafwn//QjPDv6X0A5x1/8P7/AC3/8duVnJ+4EU+EBB39T2x3NCiDqg1tgJIfz8MOtr1208m0M/q6KXNxhnSJKvLQi04pkItzB8O/9u5h///u1wcl9a2gaB3gDURCkfTOZrDBT2OW6usu5I/2MNbVBMhlCkSqdyUQZBeXk3MHfKqJIWIQwW24Gf5vFa14SmeIs7hXS7RnxmyZxs10uniE2Ake/4O/t7XKWIGqyyyVhhuDjGK87OdzJGFjeLix2y2mCwW9+GWAM8565TFGWw7etuZmDNJZII1n8TUCLEH1IJju/Wr+Gx8b+kfBRLemglstWqxxQDf72mAduxunRQJ+37UQmViGZVKJtVhvCvaVvxbB2XuY/+RQuH0mMQ1L8Pa+vqOiOKna5/PQUyDLwEskU33WSyZwXc+0SxwHx/fPZ5crh2pZmOXlPf20I/qbjEjnkOMjTTZLJ78hk8pkluM3rZBmQ5xtzkLaW9nUkEwCw/YubmUzHx1WRyd/FWM3B7wLJlKyTTOexygGFXc7PTrduPNBW9lyTeHZ2kimuj/4AsBKZzpPJVGPDr6s+djkr8ihHX2vueUgmmQHLIyzEhV5Za1oEq1nbOoNauxwVBDJXW21q8AsXIMy45m6ITLIkMq0EJWmIvtpj9huO+Vek7otM93BFc2NDojNgfAXEdUDMg6lVsXZHECRGlkjkYQjSEvwNAIkUAEnbO4QYxZ6kcxDPQzw9gce8SnigPD4BNxjiiLHmjm2lsh0u6nKZbOe29ZKZAgg2bHR9a727nLXv1B5vPNM7+GuTazbSx00Xc/iCrcgad4rBjouDd5snMKe3Qvz4j9/BI09exKd+wyCyl65jZ/4jfOaLl/E3f/YBwllSCf62omJcEpnkbAoqZG0gcOUtwIhMHDr4uy2TifYQmab6YTHZ97Unmdud9e4FCLn5M+zyD3CcPFgRzJZxBl+w3ufUCh4RdxpJplzlyJEg5GcgmfJcP/wvfEr/v1lwpzIvHmr9MpligHsbAcGLdIGBGGxM8PneHpxPfQr+R3qyF5+eQWRS+sHJeI8d4SxCSgjcHiSTvcaTPETyi7cAAO4whvf444jPapc7sDuYmxNzSzIlSapFph55TIDeeWQKUA0TYefq1eYOc0F/kkkpZexyXSSTgMpWY0vg8OZNgpoasVv4/OCP8R/+109h78EBFtPqvTlxJzilBJh9DBYdbQhiy2yJC6cK8mQJ3maJNlVMttatwoXIVL2uuKCdIopF4GOiwEhe/L3R178OFYZY/OhHncfVVAOXQ+YKzsBe91TnO9jxqE1kslkLO6Xucm2ZTGuLjEpOGVYkE1cBMhvQ3UOE6yqVAAkPscxWJJMVH+yGh+2GBmiSqQ915AX6+UOTBLShCy2iKWJjHehFMqUtn2FDCY+BuwzLBrvcGz8+AJEKPxEp8uACsLiFYKTH3XCagI3H8J96Cosf/LDX68WphEulpt0IKcStsl1OzvuRTHZ+lYfrdjl9Pca8OZOpS2RKshXJRAgpaKbRGUgmx97X9vXjpN5+bEkmZ4RltkSuqve299SvAwAuvXu6ssv1DVr2xhW7XHak5yJs70Lxd+46ySStXW71XtlkstGRcZuqkCOWZjm1JFNN8PdsBjYq2+Wcap6WebZ2ikxm4RsRArhD5A0dAvVGRnvw98CpUnTaLrce/H0CtltaXPs7GGEBfu7g7xLJlCXAnTdrm39sVWaz1ZfTu2uXEwwOUlAZnzmTyUZ/sLrPbWFFprPb5azI2yXQCo93ikz2+7kZ14SsjzPpVcvbABRCZ68y9jQen9tAWjUEfyszNm4jKrLdPThWZCrb5awIX2eXOznZyGOydZ9kul/3bEVLQzLRmbbLuS6QJOC0w4rmjiBIpIO/w/bgbwBIpQNFsnaRyVsNpnQwQLqY4lJQzSKRJyfgZtd8SGhzOHmp7KSpLpdpYfKO1ktmORijvawzdVVnlwNWi+DqD2/uXgMri4KIlvAdBkooAq7x8gceHePgnfoJjFIKf/ZHr4Ewgt/6p4+t3sP+E8DyDp756hhZmuOn331/JfTkJbvcckX/5PMZKFfdJBPR59XhQAanH8nUYpebhfrvTS7phxA3YkYfkgk3/hJ7/H0cnborkcljWKayd2c5oGSX425jJpM9zxE7A8kUHmti48KnzR/TEzMr7nqCFZPEzkwm4YFRTS3ZneF5Ot+wytkKnnkG/rvv6864R+9td9wACIw9siPfQR9fpEkm2m11ZJSBQSA1JBMLOBidwX3iOpL33y+6JTXVPM4gGKlOAA9fadzBtCSTFZmaumSulyaZgLxhIuxc6xKZSsHfLd3l8ukUKoq67XKcV7JifMF6WYmLCo91OLEI4A1EjV1uglOl/+3C4s1Nkild4suvK0gqIIbdn+FGdzlr02FWZKrPZGrrMBiHK5LJdpcD5Rh86Uug4zFm3/5O53E1lSXjGPSY5Xl6QV6ITC2ZTNYyQ3t1l5sCNsPIVEEyZdVMJkY8ZLlF+vt132urNMqRsAjLdEUy2WuSGUIin5fsclnUKz/JC7RNicdx42YU4ikiV49VvYK/TWbdNkUIwWDs1JJMSin8zfc/BNl1cJMppN4FIDpFMNCvYe3lg+eeRfTyy4Vw0VZRKuGRrAj9tn9jg2Qa9yeZVFTdyLAbZBFrEJl62uWcErE9ckYgIBgNtCjTt7scULKDmNKB0TXjvu0uZ+MUsrUNmuufRk6AC2/fWdnlepNM44r1XN7Rgj6/eGF1jL+ETKYcBEyURKbx+Hzd5azIlJTtcuaZUksyVa8l3V2ubJfT11CXXc6KvCEhgDMqMrnWz2NbgPosmcHnPgSt2vz5xX3I4+PKs0oeH1fbtns7GOMuBX9bkunOW/qZcl6RiXHAnSCQ063Hn7byBMMIZv64Nv7DGZpmIV3B3/VrGQB3xS5X11CkroTL6u1opbJUjnRcZIrCyc5BMpn3FjoXOu1ygBZskjoRTARVu1xsRSbTkGIbkqnBLufZeU8R/F2yyx0fFxtRG8d8P5Ppft2rZRcTLplru5zxcftORycBdwSOJbIi+LvdLidzF5Jk9SinLWOXQ6Q7zGWLRcUqB1RFpgHpn8kE1JNMYQPJlGeqv1Xu9pvAH/zDyu7ZOsk0ahWZZrWTBjrUxz3IouIYB2KAZbbE5UcnmN6Oiolrud54/iZuvHqM3/yHn8Jwt3RejF1ol7yDzzxzGT/70w8Rzst2uU2SKZ/PwXoEf4dmGBCMIFNCW7jSerKnT/D3IvNAIYtdX87MMfUQKnDjBeyOllhOM8yOouK9LOOsdx4TUBaZRKPIFJnJ8pI5QJputeuLucljuvAZ/bUQmYw9oG/wdxYVtp1ABJXFaLPI9DRYFOHt48twfvaH/Y+5KH09U9pjsp5ZkaklV6r8l6mHTMWI33oLzqUhSHQK7/p1QClEb7zR+rvzKMPA5SthNYv1BPNyvcjESySTk/brLAfYTCaCvGGIEFevIvv443qLls1kMoJJ0hJsmx7ooFfxQA+SqSwyOayXAF/U8kgvZAjRItNiTWRyJljIGCmAS8u3NgSxZbbEl1/LgckexKCHhcq2bI6n+tq1XdqK7nKbdjmlgFw2i0zJckUycViSiYEIgdFvfxXzP/mTCu21TVmRiXz4DgDAMTv1VkAo7DI1VbQWr2QybU8yWULRbpZwEiA1n4e2E55v4ZyGElIkWGbLYnFWkEw2k2lassvJuJcg5BqLhkiT9uBvmxnTK/h7e7scoMO/6zKZDt6Z4s4Hc3jXxwABYlcLEgHX77cI/37uOUApLH7YTcXFWQ6XpKvQb0NQBWP9mSmlNMk03CL4e41kKvLX2LBBZOpjl1uRTICeL03cCbh5za3scqKag6PiGNSpI5lMdzkzP1zfAIwc4P19YPzWQSFatW5QlssbV+xymRGZLMnkCdaLeNiqsggpcSq0FRuPzxf8bUWmrGSXO/2glnoH9DhDx6tF6mbwtxGZ0CEy2ZgAqkkmK1TVZTI1iUzzdL5hlQM0yQSs6DIlJeTpacUmpLwJxlhA3E2S6ZbtLHdOkQkAgl0M5fTuZjIJhiEx8+V1u5wJQ+/sLhfXr2UA6OBvZ1ghZ85yjEBL13FTjsuQdjhMCrscp5jDhziPyGQordjtZ5fTJFO3Xc6SwbkZGnuTlADY3l4hMjnBOskkG0km3kAyufe7y92ve7WiRQpKcwgSme5yLvIk0e0q2wYTZwihFnr3NM9B/frByz5o8txDhrw9k8lfeY/pYAC1WG6KTMfHoCqHcCkCUCx6WEKsyFTn/W+2y+X9Q7/f/lPg7T/RthxT65lM7SRTTdgfVjkYQRrBNzsUAzHAIl3g8qP659ctc+EswQ/+z7fwwCfH+NzfXWshvm8esIev4ZnffQRZIjH/KzvhLZFMkRWZGPJFCCq6g79DrEgmmZtBtYFm6hP8vVBDDHkMYiYanEnd0ayLLFMKuPE89j6hr6Wbb5+a96IzVVpFzrUSVIASipCJRrtcITKZ97SVZc6Gfl+skkx2AuyKvsHfJZGJB5Xgb3vtr1fwzDMAgB8d/hq8g58AN17of9wAiLIiU0+7HAC3R2g7ADjEA6EJ4rffhvvgLmBFJqDTMreIs2oe0+03zQ5mE8mkr6ckzeBmCixo3/W35QkKDkA2XI6O6TCX3rix+c3ggibYzMKqrXtSdqAnUN3B31WRKejaJFiv8LgQkr0BryWZAGA6vIQr4VsbE2x18xYe/xAg+5eLjn1tZUUWGU6rAntD8LftoNZmCStIJgAM5veNaDX82tcgT0+xfPHFzmOrq4HLQXMJ9ou/0cczMzvDhV2upVOmyWVhE5vJ1NFdbu1ZYEkmS3vYzRJhgr+BfplVXRWHGaRIsUyXhXhVBH+bDY8yyRTLuJ9dbviAPt4kbdyMQjxFZEiLfiTT9nY5QId/19nlXv7zDyFchsl1/dwIhb4XXHUEykghMnmf/SzYZIJFj1ymKJVwkFVCv+0xACbEO8t6kkzGLrdGMhV0AR3q0GBZvW/yZR+SSVVEppEzwq63W9jcKparjiqy1uzrx3GDXc6QTGZjcbG2yAyzEG89SDB440PE5vX7k0yjCslkRaYik4mzX0p3uQSicozswgXIo2aiu6vsXDlM5Gr+tbilg6vX5kEqz/WG4KiayVQRmZhApihctJ9PTjkE4YZkWolM6+exK5NpPfQbAPi+EZlMLpOcTgGlwHZW88vc28UYS5zXjVbpLnf4qqaB7IbeecrfwzC/23Y5WiKZasYDb6cHydQiMs0PgOHZKSZ9jP2Cv/tkMtnvSwrM4YNn57DLzXXcQ+L1JJm6gr/N5p/t1iqtyLTF+aaehwdPforP775XiecoCG67AS/K3eXaMpk0ydRGcv9trvsi0z1c8SKFJxKQwQWAuyCuAxXHCJwOu4U7AlcLyDSHAmkM/uaUY8ADqNxHSvL20OWyXS4IQKO40lkOMAn8vg83EPBUt6oOrIK/6zOZGuxyMgfrGzxoffKzj4t/WrfLDb0zkExmwjBIQwRmgA9MN5b9ayMQSnDwdnX38gf/15tIogxf/WfXC4GmqNEDmha79Sr2Hhzg079xCelLQ7hpAOS8+CxtgK5gCirLQEW3XW6p9PEJqiBzc44bcpnsZ5LK+smOkhJLsYORv/qsKMtAVA8S5uhtYHELe595FABw04Sju75+f4MeeTG2CCHwuY+Q8UaSKZT6YbE072kry5xtR7xhl1uJDipJACFA2jqtpFERIGhFSEAHBK9nItgSV64gv/QA0lscqTMB/uJ/6H/cAGBEJkJ7LECyGDElvToDAoDLfIzjBfKTE7ifuARkIfj+Huh4jOi19g5zs3WRqaWzHAAwbtu/Z/BT2jiOrZcvdCZTo8j0iBaZai1zlgo0uUwrkmlzbMwOtBDZK5OpFPK6vV3upLjHvaEmmcoTGisyne5/Bg/Gb29MuPaeN4TZeA+8aaJbKvte82hdZKq3y1lbYxutY3HylCowtbLLAZpAIZ53ZsvcyOV4+vB1iFMtLrGjj3TeCu9DMukxmvWxy8WnGzvZVshZJ5kY9ZHllmSi5xaZkjCDElKTTA3B3+XucmEW9qKO+PABUKUg0qzZjhrPEJvPsutvylwhlepMJEEwcbE4jSvXdrRI8eaLh3jsS5cxHJoOdFxf72SpO8wtZ0bsZwyDZ5/F/Ec/7JzwR2kOh2RFB8LlaaJD+U2ul8236kMy2TgCFdWTTHNixvm4uunUh2RKsrxCJv6DT/4D/N5nfg9EGDFsm0wmVs1kUnFc30yhyGTSn/Ny7fkaZiHefJCALSJkZgztLzJNKlS5vHMEEFLknbiCdi6Ut64sRgpRuSb5xYvIbm2fd2jLLy/qncFqbKyzys3ngFKV4O+N7nIAIjhwO0gmAPCoQERoZyZTk1g3S2btJNNt/bnIYy3ClbNocm8CShQG6nxt7Svd5Q5f1dmXot/zvbX8XYzV7K6KTL5gGFmSqWbDuQ/J1BT9AUBbys5hlQPWguhbSni8u7ucWQdJBsyVX1A/ZypDMqX+fj+SyWPFRnr1GwGgJGAAgZUQZu1y2z1vdrwYj8hXK/9mu2+qZKEpaNMwSUkJOZ1WxNZyOT5Dnqui6civWt0Xme7hihYZPLYERlcArB5Mbe1JAQDuGCLXD3LJnALnrquxGAK5h5SS9tDlwi53CukJuHFea5djuztwAw5X9bTLtWQyNan/2i7X89I+/UB/LYlMltKx1M6wNfh7WjtxYIXItGaXS5cQDsOFhwYVkun9V+7gjecP8BvfuIYLD9YQLIRomulQ0yDPfPMRIKV48uZXAdCiO0USZgBBEbCuSaZ2u9xSGeKI5sis9N9FMjXY5bKTU4TeRYxGpV1BloGgh8hkiJzR9afBHYo7Hxlk1efmIbzdg8LnPkJKgaR+whObyXJoFoJnEpn2PgmAFGRLNZMpAW2jmABDMukJYCCCop38Ils0kkwAQJ/6Aq7feQ/vPfKPgVf/GDh+t/ehKyP49SWZYkL65WlBi0wPn5jzdk3TeCSewrt+HdFrr7b9KuZRjchEBXCxfgfTkkxZKuFm/TOZLMmUkfqFpnP1KgDUd5gLtG3DirB2UVaXyVTY5S61B3bWkkxbiUxHq/bMA4Fcqspu5MQsBk8vXMND6bvwWfV9P/DCu/joskDOHHCnB8lk3qvaEJnM/blBMumfb8sdsiST4nT1+2ZhRn0fw688h9n3vgeVbz9RG7gc33jvBXCu37eTTLH88Y9LmUzNC7cik2kygVLKiEz9SSabtVYmFH3uw6G8EJmEe367XBxmgJsbkqmayUQ8DxACubHLKaV62+XI4CJcpSCyvNkuF00RG8tkFx1ViLJnyEQZTFxkSV5ZBL3+/E3INMdnv/JQ8XyYczPZX95BMK7ST4PnnoO8dRvx682Ctz7PEm6JZFqexggmTrH5k8+sjbKHXc6STGuk7IbItLYQzZdL0EGzysDhpwAAIABJREFUyKSUQrJml/tHn/lH+P3P/j6Io8f4daGirYqdevv3m+xyaQgwBwNz769T5mEW4o2H9OdEXtX04Hnscmx3F4Tp3/c4Q5arggK8K5XFiCEq1yTf30c+m525q2Wlmxchq03YutBvc1+uB3/n5eyjXCGGgKO6RSafckSUAO64MZOpLVJjlsyKeXe5rMgkTfh3nciUmWfNID+H8ACTyVSITK/cHascAAR7WmTagorvqkom07pdDuhFMjVFfwDQBNw5Qr+BsujZEfztMuRSQbYIPpYkyhnBHD7YeUSm+SHAfVBnCNnjvnaagr/thqwRvO2mTWaWrNuKimxvTwvcpbJClYwXVatcQfQ1kEwGUvhVDf++LzLdwxUtUnh0WohM1i7XuUhxhxC5fpBL5rZmmUzoEEQJpF1kkPC07Sc8QeJSeAlwebBJMrGdHbiBgJOjVwelrkwm/7x2uTqRqaG73LxOQe/IZAqyqDjGAV+RKg88OsHBu1PkuV4Q/tkfvY6dywGe/t1rzcd66br2pyuFCw8NIR85xq9//FsYUYZlqo8tCTM4LoNamoBZrlZWxoZaKLPjTfIVsR/Wk0xFJlODXW564w5y5mC8t5rUUCqBPiTTjb8E3AnI5Sew+8AAUJqCYJy2e9YbqhCZmjKZpJ5Ahqbtb77cxi53oH3y7kh/LexyqwwKlabteUyAEZn061vSDTAkU0MmEwC4zzyN3XiON8VvAYQCz/9PvQ9dWVqN9HjoZTFSEIieIpPPfXzi2HTmeFQTaYhO4V5/HPEbb0LJ5nFpkWQFNQhAW1gvPlbkoqyXvcfTVMJNSe9MJodQUBA0vXs2mYBNJkjerwlVt4KtEZnaMpmyg0Owvb3Oa2BTZNquuxzC41XnnKH+rMqWuYJkGl+BgxTXyGqsSw8OcekXd/DKkxNkiQTvMfkuRKZ1qzCrt8tZOqptJy8JM+ScQHC66k5X6mg4+trXkB0cIPr5zzuPb72CxSn+nZuvIP41vVBx8xCL518obKqtJNPpFMTzQB2nyDqqFUiU0mNAzSIjEMGKZErnGIkROCMVu9x5gr/zXCGNJKiTr5FMZqFPCNhwCGnsclmeIVd5L5IJg30EMgeXqsUuN9NdPNHdQbSwE5+BJLCh29a6ppTCy9//EJcfHWP/6qggAabUPO8WtxCMHYSzksj07LP6Wy2WuVQq5AoQSFck0zRBMF7dx5YK69NdriCZ1kQmSnWTg2khMlXJ5q7gbytSOjVzM2rGnDIh2VUur2Yy5UmTXS4GuF9YQetEpo8uAGrgg7/2cvG3+x3ESFsHzRggj+4UVjlgZUPrQz30rizSIhOvikwAzkwzWeq/oK4sTV5HMs1sc4Fmu1yS5YjgwOlBMvmEbdjlNjKZ+PaZTKwgmTTFW3TeLHWXk47+b1/OcJ7Swd9KRx0cvdNIM29byt/FDu4uyaRFpvORTO12uUNg2E5Ddx+jfr+ddjnP0OEtNFMaS1BKkCiFuTqnyGQENMfMO5IOkcmSVhskqnED2GiMLJGgnCDJmxuztBXb20V2vC4y6b+Rx4u10G97HzR3lwPwKxv+fV9kuocrWqTw1LG2UsHsVqQpAk66g7+lntBI6rQuznZy/bDJWI8FvjcBohNEDuClqM1k4juaZOJSVUkmmRV+2sqhMhec8I2JjFIKiyTDoKm7XN/gbysyTat2OZvpA6xIpm3scoQxKN/HII2KXYSBsxKZLj86RhpJHH+8wAv/5h1Mb0f47X/2ePsib/8JvaA0HRmWT92AIz18MRFYlkgmG/oNANR3aoMmyzU36XgUEpk9DU0kE2snmU4+0L+3c3ml9BOaFRat1rrxAvDwFwFKsXdFD+J2gG46123lcU9PthpCzG1OSnQmkunm6sFfypGIS910VJp0i0xpuCKZ1jKZ2kSm3S99Ub/eL24Cn/s94Cd/UBseW1fK5G4RsgXJ1GdBCsDnAR4+jqD8APwTmghCeALv+hNQYVhPB5mqJZladjCtDUumEm7asgheK2Uya1I0W2bEtWtIa+1yhmQKqyJT3SQmOzjozGMCNkUmrytTr1xKrYK/gcLOUw7/tl1CT03HqU/Jd4rvzb7zHRAFvPmFfWRJ3o9kMmMUWR/7WoK/gXaSKQkzqEJksiTTaiwcfvWrAOeYffe7nce3XuL/+xa4ynH6xWcAAuw8NMHy+edLwd8tmUzT04pVDmhA75OFxvVrFhk+91f3dTLHwBmAU1oK/qbnIplsECr1tHXJTtSdEuFCRyPkxuJlxfVedKI7wsh8PLWkoMyAdIHIdBbsIplaP8OOGuyYfCQT/v3xWyc4vrnEZ7/yoP6+aQwxVYEmII3ItCx1pBOXL8F97DHM/7xZZIqMEFYWmRanCYLJ6vPKZ5Y+2YZk2hQzfYdhqjZFJpUkUGnaapdL1wLeK6/pbG+XW89kUnHS3F2Ou415mWEaQhECPPEZuG9q8ro3yWTvH0MGZ3eOwC5cLL7dN1tmq8pixMqp2uUuWZHp9pn+pN1YLMgRu9FXM1esJZlcB8iyYlMmyXJEyoHIu8k0H0ZkcofIGzKZPGf7TCbquqDjMbI1kqkceJyaMPiBPHtnPsAGf0vg9usAFLB//Vx/z1bu7WFEQvh9mp70LE9QjIjNZDobydTYXU6meq5xTrvcNt3lACBpCf9Oo/+fvTcPsuS47zu/mXXXe6+v6Z7BYAYzuIiDJAAeIAhYoETJoGRda1rk6nBYilg71gpTsrXhWK9trWTZcmhjQxH2UhK5h9YO7Urr2CPIFRmSlqIpaUlKIgGQAAkeOEjcc3fPdPc768zM/SMz61W9Ol93Y0Mjze8fEv16XterV5WV+c3P9/tjsFxJFE7ggVa4TDqXsgJqAaetc2RmPVsUma0iyZREHJZtNG4CNpW5vlHKZNObSzyezkUt5MTWGpLJyUSmv5rh3zdEpuu4wmkCh18DVuQkS+9W9KlozWSylIeYG3Z9a2IAq1zevLHZgURx14BwiKnFJclUkclkrK3D8U0YqZgvpMIh8Ku3A7/5XuDrHyssUggh6Nm9kl0uSjm4QA3JJLqRTCyZE0wLJFM+g6anMfwlRCYAYF5P2eVU8LfZyxYbOvz7G5+/gGf+6HW8+dGbcfObmrOTdIc53W0jXN3Hxc0XcN+UIpjJcxYFKRzfXCozYqLabFGVhcJh1mYy6YVJwqvzfPYvyb+7dnOuUwpJMmGjtoJ96b2/5d0AgPWTcnKtB+ggZvCX6C4HyMVdSEitXU4HfwdUk0xL5AiMr2TiLpw+EKkFXFq0y7WTTFEpk4lxma3SZJdbuV0Sb+LyJeDhD8lJ+dO/3enQ9XchSJdMphDxEiKTb3k4vRuDnT4LoifW4RDuPXcDAKIX6sO/C5lM4UjmpdV0lgPmJFOactiJ6GyXS9VEJmnIZbHPnKmxyxUzmaIGkSnZ3oZ1vH1ySCwLiA8Y/J0EAIvKIlMVyWT7SGDibPJy9tr405/GzgkXs9MbSBPWKZPJ1TasuEZk4sVjNztlMjEwk0j6ZsEuB0i6rPfQQxj/x88sFaAphEDye5/AsxtnsX/6FP7OLz+CW991BtG3voV0pBYGjSTTEMaq6iyXNExYdZ5ODcmUD/4eWAPZyZMfTfC3xvBNhyJIg5JdDpBiiBZG9LinO3A2FiHoJer54Ff8vurUF1IDBKTU9rx0rE3nsKV6q3PBBwC+8fmLsD0Tdz4o5xm+Ja+XWcKB3iYw3YG3YiMYJxB8fs30Hn0Us6efBp/W2KjVMZoiZ5cbRRlJBeRIppWKReVCEcsCKC0Ff8tjNjDkSkjKiUx6w6NRZEqV3bKqXbxpAoSALyUy0WyTBFB2ubrgb9PNNkGqMpkAwLzvXrjnXoaTxt2/b33/KMtceu0qzI05yaTfJzxKkolFiGAWSSZN7RyUZNLB34skU4UNLct9Wy3a5YC5SBgzjhgW7JbgbwBwQSTBbfUg6jKZagLUhRC1mUyAyqpSIlOq7XK5xbUWmVx+FCQTz+IhjopkSh15rAMcLjMqX612OU+ui6o20XXVkkxTdf0d0i5XIutqSotMzSRTKkUmJjAWHmh8iO96ugP0j8M2O5JMan5YOj5tX9MiU8xgOXPRfFl7pLm1hfTqVcSvzWl2fQ5FPFvoLFe2jRYO7QbJdKOuxxJCIJxou5wimWwtMvHmwcQZwCRyh0Pa5eoXkAM1AUrasmUAOZgG+xgbKdwY2HCLWUCZXc6zQFNJIgkhgJ0XZGjq/uvAx/8e8GtvA77wG9mkq2/1S3Y5LVBVZjIxDlo18Vqs8SVA8Pn/VxWzuLDLaxoUnmWUM5nSWE64qnYvADC/h14azO1yuWDnteM+HN/ENz53Ad7Axl/7kTvajzfXYQ6QxNWLdzwOWwC91+TgmpFMUyUyddhpnagcJgPKx2wM6u1yahFRRzKNrkUgPMHgltx3T1II3nL9nP8yAJGJTBnJpPDdxmDEmvJMDwFEvV1Oi0yqzfiinaGxJlcWSCZll0u0XY6Cx3FzZzkASINSdzktRDaRTIbjYN9dgXFtG7j5bcCt7wEe/x9LHYqqii9llwsRUQKry4IUQM/ycXo3QXjqlnkOxXQH9p13AqaJ8Ll6kanQXW5bty1+S+3vmxnJxGElonoRXFF6FyxuEZmSS5fKizR3VdoTc3Y5SgCzIq8uvXIF5k03tR4PsSyIdP5dtDZuyJcmDnV3uX6ZZOpbfVBCMUwneJWcxi1KZEp3djD78pfx9fv68E1fkkydusspkimeVmQykVq7XHN3uQTMIDBpnmQq3juD730f4tdeQ/zii63HqCv4yleRvvIKPn323ZhGKVa3PPQefggAMPvK1+QvsXo6gA9HoLqzXJPVS+fIVDwLfLNol+tZPRiUgOlMJoseyi6nJ6+WRyXJlJYJFzoYZMLIUiQTAC9R33dVdqMe9yiFa7qtHUTn3TcPlskESLtcMI7x0le2cffDN2XdC/UmxCxOlcgkM5k4FwhnufvhPY8CSYLpk9VdOfXcyRKSZGKMIxgn6OXsclqw07b4piKEgLouRAXJ5NoG9rgat6pEpga7nF6QWRXXIyGk3KGspWyTLnSXC0viBAA557Fc+Oq5WdVdDgDc+x8A4Rx37p/vvsjT44kSbdm1XRjHjmUva5IpOmKSKRBmKZMJOLjI5C0SV249yaSz0miOZMrsjopEipm0y5mdSCYp+oLSLJOJLhBprkUrNzIiFiHhSWUmE6BFJh38vQ/iOBmpBwCJCt330kOKTIYiPbeflTThxu2Hej9dsbLzrYjDkVb5sgyKFRoioU61td9dk5RrVH9OpMhUMb9VroX/v0gmnR/U1GEuCRksx0TCOCbwQA5NMm11J5nUeqAU/r1ol4vkhtlB7dlrP/ajMHo9nPuZnwGbqPgHNT6IuMYuV9tdruaY/4rUDZHpOq005uBMwCVjYCBJJo3E9sGaMz3sASwiJzxtwd/9RL5nancgGZRdbmREMDlgpvNF3DyBfw1OzwRJBcAVCXBNLRr+7qeBn/g/gPVbgf/4C8C/fQvw6f8afeqURKap+nyHsstpq9zWPfOW9JAP2sVuWj3HLJNMenCtIZlS1y8Ef/uWj4hFSHkKQglO3ConFe/5sbvg+B1EvP5xuSOmSKaIRYjWh9geEGycjxAHKaIFu5yx0pzHBAAjViSZUud4rV2OEgqTmvUi05DBC67C2sip+iSZCxt1de4JuXg/9U4AwHrOLse4QJjwg2UygctJMS8/NPViK6Rysrx08HdeZIqLJJNjGhBxl0ymaC4yqewWLUQ2kUwAsD9Yh3NNTYIf+RlgdB547pOth647CPIOu6KyvTOB01Fk2kgtHJsIBCfPABu3ycnRc78Hattwbr8dYQ3JxLikL7NMpm2Z5dGFZELEQAUaiczCR9KdGJtEplvPApwjOX+++AI15KQxI5kYbJOWFtc8jsF2d2Ge6EIymQW7nGfLHTjGOxA7WgxusMtRQrFir2AYDfFtnMHN4UsAIK1nQuDLb7aVyMSWCv42kkl57KMmwBftch1JJkPRNxWZTADQ/57vAQhZyjK3//GPgfo+nr7tnRir8dt761tBfB+zLz0lx5y0fuHGRiMYK4pkatoVzUim1dJLBRtsPEHf7sM05i26zUMGf2uRyXZNzNJy8DcAGIN+Jozohgdt+UnZ8SuRqZIUVOJaRGirVU7+7YPb5SzXgOkYmA1jPPfFS+CpyKxywHwuMIsZ0NvK7HKAzFTS5b3znSCeh+mf/XnNMcrrz1AiUzCS13PeLqcFO6MDyQRIy1xViLRvG9hlDSJTA8k0t0VWz3WkyNRhjFdVaZer6y5nOvBMD5TQykwmAOi/7e0AgHv2Xsvox/aDUOczGoNHEfhkArMgMulsmaMjmUQaIuTF7nLGxgZgGIcgmRYW9U12uYoQeT2X15scccoRwYIl2kUmlwsEKt5C1NjlPMtQnR6L51HPs1fs6uva3NwE25nb5Yz19cKzL1YElHtIkckyKBgXENvPAZt3A8ZyG4x1FVsqmPyQdr7FWqcBIlojCGdEd7VlTgih7HIVY6IWmQ6ZyZQRgB2Cv4FmkimOGGzXQMw4JsIDSaaV8+vW4gyYXVUkk85a65YZFS8eX2aXk2ORnstEB7TL2adP49SH/zvEr7yKi//sn0LwXFfZpIZkuhH8XVk3RKbrtPQiwqWTYiYTgB7hLcHfA1iaZKLNwd9+Kh9Osd3dLrdL5WQqv2jPEvjXpV0OAByhiKRrLwLEkIvSu78f+M/+APj7nwXu+j7g8f8Bve3nML3wFHDh6ez99Oc7lF1Oi0ynH5QigZowxzzOuqjp6jsGJovqvl5Y1IhMseujlwSF7nLAPMPggcduwUM/fBvueEdHFHahw5y29V04ZcNkwNc+e16STK4JpjOZVps7ywHASH0sQy0OmbMJzKpFJkB2mItqdv/HUwI/vFrYgRUkAW+zy517HDjxVmk9A7Cy6cGwKGzPzCZqy2YyeYYSmYBKmklPhgNDi0wdSaZoIq+XgXrw54K/wxzJ1MkulwRZa96e1QMTDLvhbvbfTTVZ3YS/rzIj3vR9wMYdwBc+0ohlAwBjSmQSHe1ytLtd7qar8roYHj8ld/Ue+HHgW38IjK/AvfceRDUkkxaNCySTPQBWb6n9W9k9HsvP2zX4W5NMYYOIM+8wVxH+7R8rZDJVLZjTbTk5tDpkMkFlMmkbmN9x11H+krpPVSC5HlvzdjkAWHPWMIpGeE6cwWp6FZhew+gPPw37ttvw8kYCn/YgBDoHf9tIQHlcIzLVkEwNnycKUqR00S5XPBbr+HF4DzyA0Wc+03qMAMAmU4w+9YcY/MD3w+z3MhKVWBb8d75TkiyG00FkUiRTk9WriWSyillrfasv7XJZJpMBlvCCpWuZilTWg+0bmKWzjPLI03W0P8ieCXrs7iIKAYCTqi5/VaSgHvdIN9HqMMHfhBD0VmxM9iI8+6cXcfLO1UInVteiIASYRSngb9aKTNS24T/0rtrw7zCzy8WA6WA2kucrH/zNR2PAsqqDsSuKui5EhV3Oswzsp7YUO3O5LdrK10QyJQ1WXUAKC0tlMlnF4O9au1wi6VtCSIHS05WJTMdvxvTYCdy79zrMLmQ5kOtSPAK7JoV841jeLqcIoZbF6DIl0gjxQvA3oRTmsWMHFplKi/os+Ls8PvDRGCCkQMURS9vl5DiuM5nMmoYr+fKEQEjUM74uk6mmpf0oluNY3QaXubVZyGRatAgl1EUoLLhpt3zIutLXtDjKznIAQksKAX1+tCLTCg0Q1IlMmmKryWVqiv7AVItMh7PLEULgWrR78HcrySTtchOoZ0IDpVVbs13pJMlnMrXYYO1Wu5wcezK7XNKwMdRSvUcewYn/6p9g8kd/jKsf/e+zYyRJUMpkIpZV2wn0hl3uRl2XpRcRLh2XMpl8wtqDv3MkU9PizEuUCut2IIOUXe4akRMkPs2JTDmkUFM7jiBycXntRUkv5VHTm98OfPDfAz/3DPqrZzAJ94D/+buB3/oB4Mo3MztJrV2uk8h0Tv7vaRmirGmmxUwmAOi7ZtkupwfWKh82gNjx4SdhwS4HzDMMzrz5GN71g7e1WgwKleswp9tQ8zUb232Cr/7R6wgmCRzfBFeIJ1071vKGeZJJfj5mH6slmQAZ/l2VySSEwCS24Ytx4TNxJGDMBK9bRLEUOP8UcObh7EeUErz1u07htvs35aIBWD6TyfIQKjqrKvxb2+VC1d2nM8k0ka3p0deZTCs5kUllMpldM5nCjGTSGSnbMzmxaBOZwrVNDEa7UpygFHjkQ8DFpyUV1lBaZGK1/dXyxxchIiTrKthWWzvyPF/bVATPO35KouLP/O9w7r4H6fY20t2yFVN3bsxEpitqctlwbxBKQE0CEmmRqduiWWcyRbx+QmOdlZlX1eHfG4VMprrQbwAwj3cL/oYQANPCuSYyOnw/mcgkJ/vUoHB8E+G0+G9XnBUM4yG+yaRol77wBcy+9CUM/sb3IWAhfMhFRTeSyUC/rpuOYZUzmbqQTLMUiaEsXhWZTLoG73sfomefQ3z+Qutxjj71/0DMZlj7wAfQcwxMcxPn3rsfQvzSS0jjZpGJD4dZVkqjQBKpRVXFs8AzvbldTpNMlCLh8+BvoDkYvan05NXzHXDBEbCoRNcZKwPwkVxYZXamrmH+SmSqtsspkgndRKvDZDIBMvz79W9ew3AnwFvec6rwGiEEvmVgqkmm2bVMGApGxcV5/zseldbLc+fKx5iRTClg2FkGVC9PMk3GMAaDzs9u4rmVwd+uZWCWCkWBL5nJ1BD8DQDEtpYM/pY5OPo5LaKoxi5XpG9LUQZpAEooLGrh2pm7cM9efbOH8kFokmmEVLUQz5NM2rLSZqtZpkRS7i4H6FyWg4lMhBB4ltHJLsfGY9B+H4TmRC41l9d2N91dzmiw9uryOEOg3kqLVIvzEDcLJi+OOTr7tC6TydjcBJ9OwWczsP19mOtFeiNhHEP0YB/WLmdSDDADHV04UpEpUiSTf8Qk0woJMDsgyaTXMr0mkumQdjlACtptDUU6BX9HKSzHQMI4pocRmXICmhaB2kQmq9Yup8bJuBj8rcdyu6vIvVDrP/VTWH3/+3H1ox9F78k/BQCQZFawy6V7ezDW1mqfBTdEpht1XZbOGHCNmdy1w/zB1BMMCSujsFnlSaYWu5wTqx0Rp8ME2F0FohG2IQecfLBmHinUYc5uRjK9BBy7s/o9125B7+Z3YLJxFvjeXwEufwP443+dkQ9VPmZJMnW0y3kbkgABgPFFAMjEm3z1bDNbCGelB9Yakil0PPTSMBvg6lr+LlVb98rJ6Phylh3l2wa+sQZE0xRJyJRdbgwQAbKy2fqW+3pNp4Sj1F6vzWQCJMlUZZebDWMwGOgbRUGHIwGEWf8AufINibmqPCZdj37wTbjnkZNy0YBqQbGpPNNDoBesFeHf2i6XEAfCMLqLTNpaOajIZEo1yaRFpgZxRoiCyKRFJS0y1eUi6Eo2j8NJI/ChWpw88BNyMvvFjzT+u1SJTHU0WuEQk0AGf3fMb9m4MkZsAJf1ruzmm4AzjwBf+Z15+PfzZZpJW1H7rinPy/Y3G61yugyTQueXd7XLsViTTPUTGmNtDXQwqAn/PpaRfpJkahCZOtnl5DWiLXNZnkcXC9WsaJcDpGUub5cDgFV7FcNoiK8lktAa/+GnAM7R/973IUgDeESOTV2Cvx2Lok+0yLRIMhm13eXqMpmEEIiDFAmV+XfNItNjAIDJH7db5oYf+zjsO+6A97a3oe+YmV0OAPx3y7FmuuPWBn+LJAGfzbJw50arV0smU5AGWaD/wBrApASpFgmy83OwhbOevPo9eY8GSVCaVNP+AHw6hWBseZKJyQUqpxWbBFpcB+80Rhw0iFVXb9VGEjG4PauSAPZsU9nlNoF4At+V19xsQWTqPfooAFTSTJo+oVyRTKo7XT74m4/GoIP2PCZd1PWqg79ttfCrEZlIB7tcnchELTuzS3UpLZbr9+VxXE1q5XIE8zmTuoI0gGd6IIRg+/SdOBYMkVy+XH6fqtLjSTgE25VCvlmRyXSUJBPSqFpk2tw8cHc5QOUe6TEvs8uVrxk+GpW6FOp5Qz74O4IFk9c3Kcj+Lkuhf0t//4tEmrYvLop1YxXiXB/8rbKqrl2TJNNakWRKmcBQ9GAnh+0uR/AmopwGRygyzUw5Pnvp0YpMAxIgIDX3agvJNGtYy2C6I0l5u34c6FquVd9RUFeWydRklwsZbNdEzPic3jqIyJQT0PTzKm4jmeoyo7TIlLfLFYK/DyZ1EEJw07/6l3Dvuw/ev/kVnBldBk0X7XL7taHfgNwwtxzjRne5G3V9ld6Zc/uOpBgwD/72FKFQSzM5/Yxk4kazXc5WJJNwOliJ3DWEENhV7Tzz3bqyNo85u5wrCGZR0iwyARhYA0zTAPhrPwvc9h5g75XG4G+Wdgz+Hp4HVk9nJJgWDxKWlEimgVuRydQiMgWWh14SZgp3z1R2ufQQIlOuw5zOjvJtAxcox5m3KMuMZ4IP92BYHMRvtsuljGOsgr+pEo6Ytd5IMlmGhbgC2x7uyO+97xYXmVzEEMKsvx7PqQDWBZFJV+NDuKE800OgLWEVdrkwDWFTByAGhOuBdw3+zkgmLTIpu5wQ2UPcMTvY5VgicWFrHvwNADuB3D1tI5nElvz72QTe7gEP/l3gud8Hdl+u/Xcp04uJ9l3uJF0uJLh3YReXNoAxy53Lt/8kcO1FOH21IH3+hdK/0/dWzzHlfRjsdeooY1pzkalr8HeqJh6zhk4mhBDZYa6KZPJyJBOrJpmSK93tcosikx7TZskSJFPuPnf7FSKTs4r9aIirWMUo3sDVT34B9u23Q9whiS0XWmRqHzdlN506kcmqsMs1k0xpIumJiKh8mZpMJkDaGJ277261zEUvvojgmWew9oEPgBBSIlHde+8F7fcxu2wANffBPHexZtODAAAgAElEQVRnMZNp+e5ys2SWjfs9qwfTmAd/z8/PwSah0UzdO+r6D9hM2g5zpQURPp0uHfztMnl9plXPLSWMhIJ1IqMOY5cD5rlI9zxyU6W1s+cY8+BvALbYAzVJSWSyb7sV1qlTmFTkMukx3OAxYFgZyZS3y0mSqVseE4Da4G/PUp0kF0UmtUFnNNnllN2ybpee2DZEsgzJpEO1uWxEkKZZBEOh0mhu8TZ7lXY5TeVevFlu4AXPfK3bQbjzTKb0qrbL5UimTBw5SpEpRCSsknhsHt86sF0OQJFkyuxyFSTTaAS6WsxyK3WXU3Y52sUuxxIERNFoioRanIfUBUGPkzaRSXfdu4pUNfLJV8o59tGHHR/eLncXPXqRKSA+EmHATQ53fIs1wAyTOpGpI8lUSepPrsgs1iOobKxpqCyTqckuF83tcoHKMz0YyaQ75x2fE4odM5na7HKHzWTKF3UcnP7Ib4B4Hn7pid+CmIZzUQvSpVOXx5QdnmvcCP6+UddXja7KCctgYz4R1x0kXNUlrBaNdFZymUx2Y66AGVNwMMDqsAD31rBtGAhtOcFts8u5giDZPy93xo7Vd1fr2b0M48X6rcDeq3MLVZVdbpng79Vb5q3oR3OSqVPwd8PuNQAEtgubp1l442Im04Eq12EuYhFc04Vvm5jGKd71g7cBkDuufLgHaoksq6WupjEDIxSCEFBFIKTmmvJLV9vbbKOaZNrfltfI4gYvQwJwq0FkelyG16+ernx5jhMvJzK5hotUqHjruDqTyVG7+dxxC6JoY5XscgNpCUsChAmHbVBQSiDiOOsSU1maoFggmXZm8uHbFvxtqM5lYd469NDfl4vzJ/6n+j+b6Hax7RPWSGXJLGaU1ZV7/irObxJMotz5fsv7AXsA86VPwDxxAuHzz5X+naYEB44pO8oAnUQmw6SgqbJ7NhCZ+WJq9zZoIJkAwD57tiaTaUOSfkIgUt/3YqVXroC4bvcW58iRTFpk6iI6BLvy+sllBLg9q5TJtOqsYhQN0YsDXPrsADyIcerf/hsESgx0hfz3XTOZ+rUiUzmTyVD3Q52IokmcmAhll9OqYfWxDB57DMFTT2fZIFW1/7GPA6aJ1b/5nwAok6jENOE/+CCmF1FLMrF91Vp8TYlMSYNAEo5krk4FfeibPkIWYqSEqIE9KAZ/HwHJZJgUPU9OfMN0VhI+tSDCRuOlg78dJTKxKpFJE5yCLRn8fbCp58qmB0JQssrp8m1T2iJ7krggyjK3KDIRQtB79FHMHn+8ZCnTx0hZDBgOZqMYbt8q5DwuSzLVBX97hyCZ2u1ydrk7ZkNlAg5jtQQMgBJ9uzifmaWzTGS6tHUGiWEieOaZbgdh+TKfMxoh+tYLACGVJFObrWapYrEkmRbEY3NrC+zatULnz2XKzS/qV9T1qucM+T8/LpNM+rwXu8tZMFgHkimNkQJIeCIzmUwTxCzOnepa2muSqSmTCQDSK5ellXgxk0mRTNZhSSaT4m5yDtzygdUzh3qvfEWpwD76cNJqweeg5YsZxuKgJFMDqT/ZPhKrHNCNZDJtCpAOmUyutMtFhxGZMpJpK5tDtWYydbbLMWWX080RDid1WCdOwP6VX8XWbB9X/syFoPNnXRvJBEjL3A273I26rmp4NYBvjmCtzZFxLRa5apJfLzINYJAUBAzM6TXmChiJgcSIAKMLybSKK6aBUK1HiyRTzi7Xmwd/013Z6aiJZOpbfcQ8lovi9VuBNARX1JHvVNnlePfg79XTkgBxVjOSqdIu51RlMjUHf0/Uwk939VnMZDpQ9bekXWfnuSw7SiP3J25bwY//4kN404MnwMb7SmRqHvxmcQoQAmFaIMpGkZorcqFXYTEDpOCQsHIm03A7ABEM/bXiuUtFDAir/gF37kngzLtr83f0ea8MRmwoPdENKMkw2nzJ71mJTK63nF2OWnN6RH//8QRRynKtTuMswLOyFkQmbafsmslknzopD+f1XAe0lZPAWz8APP07tZOaRJNMHXZF40Qe46LoWlU8DGFc3sWFYwST/DVu94C3/gjw7CfgvOkORA0kU989iMikSM6udrmknWQCAOvsGSQXLpRzTfwN+d0lM8SMV1p/0u0rME8c75TZUiKZdChrJ5FpryQkV4pM9irCYIR/8cRvge2nOP2efbhvujMbixyVrdC1u1xf0apdRCYAMGxaK6JoEifKRKZULjRrzt3ge98HCIHxn/xJ5esijjH85Ccx+O7vzhao/QoS1X/3u5EMBZLd6hbMfKREpi52uWgkz0XFMev7+mogRbGe1YNFCVJFMlkdgtGbKgpT2L6ZkZAhC0rCQ0YyTcYZyeQZ3e4Xhyu7XNXCMRoDhCLkScfg78PZ5d786En82C8+hLUT1Qs63zYQJGkWIYDpVfgDu5TJBAC9R78DfDrF7KtfLfxcP6dIzi6Xp5gAeR6XJpkqg7/NapGpC8mUapGpqbvcAUSmhGfiFHEqhMMkBNR37Vt+iczOk0yBILiweUt3kYkQwBkg2d7G3v/5f2Hlh3+oQNpndrkjJJlIWp/JBCEqMwS7lFzUqzHv5P3AT/9pIXdSFx+NQVcW7XLqnit0l7NBu2QyKRE5TEPZIbBioysjmRaeMW2ZTJpkil6U8/bFxTXjAiP0YB2SZLIMirvIeUTrd2VOjaOoKGXYF3048dGKTD0xwxg1IpMzkM+zWpJJzW+tGrvcIUO/dcng7+Y5DyHS2lVnl2OMg6UcthaZDG2XO4CoON0GDBtwV7OukW12uSwzavH4DEvOydU8P4m5sssxOBXdfw9S3tvfjt984Icxvexi+/e/kf2cqUymprI980Z3uRt1fdVoJ8CqcRkYnMx+lolMKsC5difcdABqwRQJuNPs9SUxRWyEAO2wAHc1yST/s9Bdbn8fUAn880wmAnNP2XoaRCa92J4kEykyATBHkjDwKyarnIn24O9wKAdGTc8MbsoymRKelMiNQRXJ1GKXmyjxQItMR5LJBGQd5kIWZplMKReIGcexU30YJgUfj0Et3ioyaQFHWBaMVHWXM9Xnqcllsg271i7nhddgr8/RbyEEUq7sclXX4/CCDGCvscoB84lQ7wDB3wAQEFob/K0tHqnjQnTtLje5Iq1y+sGlJ2TRGGEy7zYmkqTZLqePySza5bTI5FvN92bv+BYSYmB2/mLxhUc+JB+2T/+vlf8uToXMyOowYY1VSHAXa038yisgQuD8JjBbvMbf8VNAMoO7wRG9/HJphz2zy9mmDP3unwB67aH1hkVBVcZUZ7ucWqBMW9Bs+8xZgHMkFxfOr6+Oa7aLKGFwKnbJkivbsE6Ud62rKusklNnlcq3Y2yrYL93jVZlMK9YAP/t7HPdfexnBf/oYeltT4NqLWdczR8hr0OyQXdAc/F0tMpm2USui6B2+kGDeXa7CKpf9/bvugnXLLRj/UXUu0/j//SzY3h7WPviB7Gf9ivG797Acc2YvVVuDmQrKLmcy1ZBMzmr55ygH+vftPkyD5rrLHc4uFwcpHM/MxouQBRUkkxyj+HicNTzoTDKpzqDVIpMU16o2ZqrqsCSTaRmFjnKL5dsq4F3bruIx/FUH0yqR6eGHAcPAdMEypxdihCVZ8HdvtTiOs2UzmXyvMvjbs2nOLpfvLqdIpoYog0iTTHXd5Ww7C37uUvM24jwjaKrtciGgrume1WyXCxOO8zfdgfCb38zGt9ZyV3DtD5+FSFNs/ezPFl/q2Iq9cwkBynQmU3F+YWTWsINZ5jx7gRw5eX+lCM3GZcFy0S6XKJKJpGFr91g3kd9dkAayQ2DFHEQv6sOFRf04HsMgRvb9LZaxvg5QiujFF9V/VwR/ix7M+LCZTBR30XMI1+4+1PssVpRy7KEPKzpCkYkzeGKGEa8hOQmR93cdyRQ1zG+PkGTy7Ha7HCCFnLrgby0+WY6JhAlEhhoDD0Qy7cjPRsjcqtsiMlGDwrQokirrme0DSSDXHLEimZLqzMyDlGNSfOHW+7F+5xS7n/4ahr/3exCcgw2Hpfug9G9vkEw36nqr0c4MK+RCUWRSDxNHk0wtHeYMkYBbLQuzxEBshOCki8i0iu08yTQtZjKZKoHftA1Qk8AVgDN6WaKOuc+xWHpXZRpPM5HJHsuuMFV0C0s5jJrdvayGiv7QItPKyQLJVGWXi1JeDFNXu7ioEQPGatLNxnJ36EjscoDqMPe8JJmonS1K8yIOn0xgWKKQ1VJVE43FWjaI2gFLqXpw1OQyWdSqtFoNr0zhzbYLqn7KUwgIgJvVu4+6E1qDyDQ9oF0u200lpDr4Ow0ziwez3eW6yw1yWTtaZIxGiBKWTeB40pLJpLtaqXtQLxJ3gh24hguLNnd0W+u7uOqtIb54qfjCyQeAW98jLXMVxFmUMlBYlTRa6XeVENWFZNK7mxc2CWaLot6pdwJb98LlzwNpilhNUnVldjlNMnWgmABFMulMsSXtcq0i01mJ6ZdymTQ5NLumSKYKu9zlyzA75DEBeZJJE3vy/bpMCDHbLYtMfRNJxMDUhE0Igdt/+3N45HmBf3f/ezH6nr8pf/HKN7IFoqlolS7B35ZBMKgN/jYrrzmrgWTKRCZokok1ikyEEAweewyzLz6e5Sbla//jH4N54kQW7gzMSVSRW6A5d98Nw6WYvlI9QWZDuVAyVnUmk7LL1WUy1XQZXSQU+1YfJiUlu1wSHdwuZ7tGJlJHbFYO/tZ2ufFk+eBvrjKZwgp7YjQGnNXCWNpUjZbDI6ierTYzMvvEFP7AqiSZjMEA3tveVgr/jlIGAg7CkznJtFoU0HiFMNBUxK2xy1lyg4g5ayW7HPH9QsexxUparCDLk0x6oddml4sykqnKLhemYfbsjVKGS6fugAhDhN/6VqfjiMM+9r50CWsf/ADsM0WrlHPUJJOax8hMpuJ5tLZUyPUBRSbXop3GcD4awSiRTNouV8xkIhC1GXLyYGN4qXw9TEPwOKqMw3BraNlxPMbAru+aSAwDxrENRN/+NgDAXC8Hf++LPoxkLLsGH7B66R62yAjTtTcd+D2qSpNM5lGKTEpgGYqGtZS3Xk8yJTV2OZbIjd5+t3lEW3kd7HKADNeus8vpn2u7XGwcJpNpO6O0sqYDHWywlmsgrjo+ywfiqZzfCWSZTHYVeXyAciwDPolw4h1D+PecwaVf+EXMHn8cYKyVZLJcs0xf/RWpGyLTdVhpwjAZxlgxiySTnhDYvCWTCVAiUwTWEtaZJCZSIwTrIjKpTCbiyklpftGeLiCFtmfCEQT++FXZ3a1hMlUgmVZvAUDgT87BoKRysiq7y7Vc2pnIJFt6Y3ASGMnFepXIpFurFyxz0bjWIgEAIyUy8cmCXS49hF0OALbukYJGOieZgCL5wKczULM7yURsC1TtcDMtMs26k0xCCAx3QnjBTuF71gsaIWoymc49KR8ON91Xe4wZTrysXc7I2+UqMplYkNFOie10D/4eXyk++DORaYIo5dkETsQtJFNaJJn09bEb7rZa5QBgzbOw7a+BX7lUfvGRnwVGF4BnP1l6KUo5CLqRTEuJTC+/BBgUl9YlTVEoQoB3/CQcLq1y4XPFDnMZyWQRYOd54MRbWv8eILUIQ8jxpmnXP1+ptsul83bdVaUXOKUOc5pkCnYrM5mEEEi3t2F16CwHIMvLmGcyadG4Y/C3vygyye9K00y7v/W/YO2Tf4Y/eJDgd++9D/H6nRItv/KNbCyyhRyrutjlCCFYM9SCuaNdzrSNWlJHY+QB+Nwu1yAyAcDgfe+DSBJMPvf5ws+TS5cw/dM/w+rfej+IMR8v+o6JlIvCTimhFP4ZH7PXqsdjtmiXS1q6y9Vk82WEYpAnmfLB383d99oqmqWwvbldLuZhyS5naLvceDQnmToGfzvMQGQCSVix2A4lyRSycDm73BFN/BfLtw3ZedZW42c8hb/qIBjHlfd6/z2PInz2WaTXrmU/CxMOWzVPEdTCbFQkmUSayq6DS3WXcyEqni/6Xk+sgXxGKYGAT6egvWaSNQv+riOZnCVFppxlhWuSyV74ToXo3F0OkOdy+7Sk1MOvdQv/vvqlBIQSbP6Df1B6zc3RVkdSaqMnrslkAg5BMnVY1Is0ld91iWTSGw95u5zadKrJkJO/OIGnhHRJMlV3CJxnW5WDv9uyIM3NrSyrsCr4ewh174UHt8ytjuUm1HjliEWmhGNPDGC8ASLTHmtYS3lrDSRTTWObLBj7aOxyTofgbwCNdjmdhWQ5UmRKDyMy5SitzKrboWuk7ZrVx2f5QDJDEmvayqjt/nuQckwKFxEIBU79k78N49gGzv+jnwNQFltL/9YzbpBMN+r6qfG1EBDAqnFFEjiq9MPEVjvJzSTTCgwWg7dMDBNmgdEIKTrQN+4arpgm1p0eQGnJLpd/ILk9C64gGExfawz9BuYhhJNkIruarNyM3uw8fMso7bhwLiB4B7vcUJJQc7vcSWByWdpjWFKagGuRaRwuikz1u5n7RJNMcgC2qQ2TmEdAMsnwb53JNA8Knh8bCyKZyeQ2K+yTTGSyQRVmzYharNeQTI7hlCiYYJwgiTm8YKfQKUXnf0CY1Yj7uccl5WLUUzsHDf7OJrqEVAZ/h2kIX9vlLGcJkunygsg0R4bDhGUPNZnJ1EAjaZJpwS4HyIVoW635Fna8NZCd7fKLb/peaUH94kdKeH2UclBU02iLlSiRqZNd7sWXQE+fQmoSBKziXN7/47BXKIhtIHyhKDK9NnkOjjuENXxNTqI7dpQhJmAoO09Tl8x8aZEpJc2tsI1jx0B9v0wyaTpwtltJMrG9PYgkgXm8I8lkV2cydbPL7VXa5QAgnCQY/v4fYPtXfxX8ux/Gbz9GQcwAtu0BW3cDl/Mkk/w3XYK/AWCNhmAwsms3K2rOu8PlyrQokhaSaSaQE5maj8N72wMwtjZLlrn93/1dQAisfeADhZ/r8buUy3T7CpIRQ5wPz1fFlV2unMlURTINa0mmkl3O6sOk+eDvo7HLacE8FmW7HFV2OTaeIGQhLGrBaDnH2fGnApEFREEVySQJrohFnYO/CanPETps+Y4h7xs9fsZTeAMbQqCUUwYAve+QtNv0C1/IfhYmLBOZotQHZwL+ynz84xNJJi9FMnkueBgWSDpAChEAEJtzGhaQG3S0IfQbaA/+pgfNZEp5RtCU7HI8LXRE7Vk9JDwpzAfyIlOUckTHTsA4dgzBV9tzmaJvfxvDb06x/oBT2ZnTNChMSo6OZFLP4Eq73CFFpi6L+nkHy+K1RBfschGTmUwAZCZWXUXjBZEpyhoC5curIZkm8aQ2j0mXubkJMNWBsSb4G0AtudOlBkpkGvbrYzQOUtouR8O9Vtth9zeV92yjyOSutXeXW9xEzYKxj667XNjhGWM5RjvJ5BhImAA1LTnWHrS73ALJ1EU8tuo6tVnSLqeP0bSNQkbqYcukBH3VMMvcPIFbPvKRrClAl0ymGyLTjbpuargjd8RWjeJiV4tMlnrgz5p2wp0+DB6BtSweY+YgpTFS0WEBbvdxxTBw3HBAfb9kl8s/kBzfhCcEVoKLjXlMQE5kyjrM3YbV8EIl2cLVINXaXW54Xu7m6/M3OCknULOrtXY5AHKXVJcOe62pPaLCG9VEghAigzKPIJMpBcDA4RhOJr4USKYgBnUtwGwmUDTJRG0bJJaTlzQTmRpIpgWBYn9bXh9+sAMzN+Dq3xPcLE+44ilw6WvALQ81HuMsSqWtfcmHRZbJRGll8LcUmdTCrKtdLo1l+/pBLm9HC43RGGHKciRTi11OW8rUhN0xHFAiP2M3ksnGjrcGc/cqBFs4t5QCD38IuPgV4PUvFl7Sdrkuwd+R+v66kUwvw75ddjiMq7rg9I6BvPkH4a4miJ59NvuxEAKfHf63cLf+ZKnQb0DmaVKhRKYl7XIMzbQnIQTWrWcRv/Zq8YVcJlOclkmm9IrsPri8XU6e687d5YSQ92hJZJLnY+/JZ3Dxn/9z+A8+iN4v/zwEISBGIBeTW3cDV7+VkUyGFpk6kEwAsEJDhEavTHFSc94dLlembYDVLLiiTGTisE2VydQgOgOSQhr89b+Oyec/n9mQBOcYfvz/hv/ww7BvuaXw+5UkKgD/DnnuZk88UfobbDgC8bzsHs7scrWZTM12uXzXSMsoB3/XiXBtFQfF4O+UhyURx+jPg7+7CkK67IQjtoA0vFZ+UWcypVGWb9dURxnEWlW+bcp5j2nLazGeZqHdix3mAMB9y5thrK1h+oX5GBmmDL6pLLWJPKd+jmTSwgAdNC/G80VdTy7MF3KJtDU21CKToj/4bAbaEPoNyI5jQEPwt2VntrcuVRSZ5D1VssvVdETNz2mKIhODYxnwHngAQQeSaefXfwPUMXDsvvqxzzHbA4w7l/o8VcHf1LZhrK6CNXSxbCrPMjL6sa703LAU/K3OuybKiiRTA3EdT+DyoshUotFQH6Cu7XJNpcO/gWqSaV+TTDXkTpfqD7+NfdHD1N5s/+UlStvlCIsq6fYDleoyvXtAkknPi0vrmYxkOqrucrSUwVVVtSIO5plMtmsiYRy2IYP6lw7+5lx+PiWg6TlUF7tcLclkS7tcqmznliO7yx0VNUsIwYqRZH/LffObcfN/8ysgvg/7ttuaj9kzkSYcrKXZzF/GuiEyXYc1uiofMivG5fmCB/PdDy0ytdrlWATWtHhkKWLugtMEseggjFCKbcvCCZhSZCqQTMMiyeRbWAUDBWsXmewcyQQA67diPb6YCT+FQ1YIeSe73MrNc5ueJsLGl7Kso8IxuA12uZraVTtP+dyQKrx86eodQ6x2APJ2ualS8EWSQCQMht8+6c9EJscBVaIHI+rfNWUyLQgUY3VNusHVSrschFXeRbnwNCBYYx4TIDOZqqi1tppnMtUEfzMZ/G0ZBLHldrPLTdXuUpVdLpbB365FpejDWEapVNbChJ0Qgp4pJ2hdRKaBa+KqvwbCefVu6wM/IQWIL3608OMo4TBIN5IpUmPJ4v2wWCJJEL/2Grw77wIAxLzmXL7jJ+GsBAif+2a2q38tvIZYjGFYEyUyEWkJ7VKGABUWhEGbBb1csYQDBgFIe+6RfeYskkW7nKYDZ9cQpaxEjSRKZOpsl1voLueYFJR06C6XzGQ+x2J3ub58v4sf/Xdwbj2L0x/9CNZX5LEQYyZ39gYngck2AnVfGEyOb10ymQBghYQIaQVpUWuXayCZZikIJQg4h0m72eUAYPDY+yBms0wgmD3xBJILF0oUEzDfJBgvTJ6dm9dhuMDsySqRaZjlMQFQ+Q41AklTJlMu0F+H6haCv51D2uVCBtsz4ZouCAgSEZZyKIhtg7gu2EgGf3cN/QYAJxUILYBV7cRHYyR2H6lIuwV/J0c36a8q35YdvRgX0jKXzDKRqSqXiVAK78F3YvbUU4VjXDHldzELVfZQTmTiGX2yhMjkqSYgC7lMuqNUqC3q6hzz6bQzydSUycSTA2QyJWxul1sU7pPiM0tf29ncDAsik/q+vQceQPzKK7IBTE0FX/86xp/5DDa+63aYpJ6McC2jk62mU6lnYCzKJBMAGFubb2gmExtVk0yVwd9CXYNpg3AYTeAJeV00ZzLp3L+F4O+ku8hEPK9ED6dMYKRJppr5Y5fy9r+FF8QtiNkR0UaqooRjH81xEMu/qbLLpV5mgS5VA8k0jVKYlJTv44mcRxyVyORZRqeOtXYTyaSDv10DKVMZis5geZIp3JfPefXZNKHY5b5uI5l0gxFJMh2dXQ4AVk01nqr5+coP/ADu/tKTpey4xbJVs6u/ijTTDZHpOqzhTgDTZPDosLiLbVkAITDTLna5AYw0AiMNi+B4jFh4YDRBxKvbPOeLC46rlOI4R4FkEkKU7HKOb8LXuGqLyFTaLVu/FWvpVaya5RtWk0y0ZuKV1fD8PI8JyLKt2PBC5YS5rxYCZbtcg8iksmL4eH7uqrqxHKSiTelVtw0bvlpABSo4mCmcn/ba7UM6+Nt0bJAkAqEEaUrkIDqrniRUkUyBsiLYybjwPWe/J8yyNUmHfp9+V+MxzuI0+4zLlN6tn1lupV0uSAO4pgvHNBCZdjeSKXvw50Qme26Xi1IG1zSyyWFzJlNxwg7M6asuIhOlBNNVOeFLFsO/Abmz8+DfA57/A+DaS9mPo5TD7CgyJbybXS5+/XUgTeHdeacMFec1SP/t3w33ZB98GiJVXdteGb4CQAoguPJNYOM2eexdypD5UqKqC1JNpSkHUbv/bbYL+8wZxBcuZFi0/JumnDQGu8rzX1ycpFekELk8ySTvIUKInBC2WUL0JHmBZDIm8uest45bfvM3Yayuom/1QUCkyGQa8vpNA8xC+btEi0wdacE+CRCQiu/IsGrscvWZTDq4OuH54O92IaL30LtAV1Yyy9z+xz4OurKCwfseKx9vDclETAf+SYHpE0+WrExsNCos/mo71QjRiWTanm2jZ/Vk8wtKkPDD2+U440gjBsczQQmFZ3pIeCh3mBeKDvqyuxzrFtKty44ZYgvgScUiKRwhcuRY1dUu90aFfgPIUb2pfIbFkxzJVL049x98EMnrr2ficJQy9DTJpESmvF1OCwO0311kIq4c1xc7zGXUou7SpGiHTna5tNkut2x3Of29xCxnl1ukYFpIJiFEiWRyLQrvgfvlx/v612v//s6Hfw3G2ho23nefFG1r7EyuZbwxJFPF2GdubSHdfuMymfhYWXIHi8HfWmSS31+ccoSZXa6JZBrDLWUyVXSXM+tJptZMpi0556jqqJVykctkOiDJJASc3RfwLX46o/WOqmLGMdQiUw2pv3QpimcMr14k0SRTxTU9ixk8u2IT9Q2wywUJKz3nFqspkymJ5plMMeMwM5JpSZEp+2zzvCnbpN1Iprrj05lM2tJnU9n99wifNwNDzR9yDbPy2Y91Zbs3RKYbdR3V6GqIVX8G4gwKtgJCCIjjwMzscs0iE20RmXgwQio8JAljiwYAACAASURBVIQhZO0i0264i5QAxxkD7fWyRTufTIA0XbDLWbC4uvyWyWQCsg5zZ40yxsyWscvpPCYgE5nikQwELwd/y/M0zSv8DSKTEAJTJpA6bhb8DeBo7HIAIiXMOYZdCv7W4h7tt+f6TKMUlACG44DHMUyLIk1VYHidXY6WRaZwkoBAwEyDwu6/zmQS3Crvopx7Ati8u7UD3ixm6C0Z+g3kMplMp9Yu55oubJMishwgSdozLMZKZMp3l7M86duKJMnkWDR7n6r2wVnpXeHcA0tP2tsmerqiY/IhnVy6WP0LD/3nkgp56rfm/yZl3UkmRaW02eWil6SIZd9xJyziIhFR9WSGGnAe+T4AQPiU7OqkRSbQKbD9XGerHAAIQ0iRye0uMrGYgeqd3BaLkn32DJCmSC4tiHj+hiKZeIlkSq9ckWL/ZjfUf1FkAmQgcKtdTu8U5+4ftr+Pnf9ShlH6H/zbsE7Kcc2gBlyjB2KovB5l95zNrkrxKZWfwegoMg0ww6xKZKJGZXc506b1wd9hCsc3ETMOS9vlOpBMxLbRf+93YfInf4J0dxfjz3wGqz/0Q5W2yYxEXbSQmy56N6VIL19GosJsdfHhsCgypayawklmkshsIZlm6Sy7r01KIYTMEDQMCkrJgexycc6+AMjnC0NYGQZt9Adgk3Fna5suK00RmQBLKxb+0RihGr+6BX8fXUZGVWnRJoiZJJniWU5kqhZc/Hc+KF//8pcByLDqgSaZInlv5u1y+nl+EJJJhEWBIHt2E00yKbvcdNrdLlcX/H3Q7nIJh4h1d7mFcXUhR3CxmUnMY3DBM2FVk0zuW+8DCEHwTLVlbvrEk5j++Z/j2E//NIy1Y3IMqAm4dkzamKW3VDXY5QAlMh0i+LttUZ8JlnUkU84uF3cJ/o7G8BRNE7JQZjJV2OUoJbArzmPnTCYA5lo57DhlHEPR3J24tUYXYcQjSTIdVcC7qijlmFI1Pz0qkknds2Ph15NC7pp8RsTlddQsTqvzRqc7cgOz64ZbSzlZ2HvzObUausvFC3a5A5NMFZSWY9JumUyeWd1dTtnlskwmZZera4xwkBrQaP63lignI5n+6nWYuyEyXYc13Amw4g5LXYUAaXsiSQxCWnbp7b4Smeon84l6AKbgCDqITFdmcuA4Hkegvg8xlRMPjUgvkkwQFkZktVVkcAwHJjFzmUy3AgDO0PLDn6Ud7HIsBUYXiyJT/zgAgngkA2DLmUyaZMpNVBtEpphJZD/1emB5ksk8ArscgHjjVnmc0TQLcZypwVUHk9J+ezDpJErRc8xs19OwqLQT+eu1k4Sq7nLhNIFNExi9XoHeyZNMBTKDc9lZ7kyzVQ6Qwp63ZOg3kMtkMq3a4G9JMlGEKruq1TI3uSz/t5/LZCJEhn9HE4TJQUim+SRQL0i7kEwAwDflQzq9fLn6FwY3SRF379XsR1HKYdLyd1hVkcrXaSWZlMjk3H4bLOoCJKrdhXT/xk8DEAj/9BMAgJeHL8vPQqbA7kudO8sBgDAYCEygYx4TIEkmPT60LVZqO8x5G1km0+LiJD53DsbmsebQ91xViUy+bbR3l9P3Z45kuvhP/xn4ay/DNIHUWy38umcMFMlEs8ndLNiFb/lIEw7T6p6V00OAaaXI1NBdroY+iANp90qZyrfqKDIBwOCxx8D293H5l/4lRBxj7YNlqxxQQ6ICgOnA35L34fSJJwsvsdGo0MSglsJRmRxtJBMwt36bijTK00wHIZmimRKB1STWN30wRJV0C10ZgKvg766d5QDAjFNEFkECVszeSCOARYjUhLs7yfTG2eX0c3oas2zRYbkGTIvWkkzuvfeA+j4CZZmLUoaeoTpQziyYjpGJeEBOGFgik0nbzsp2OXm8E1LsyNUt+Ft1l2skmbqLTHYhk0nZ5UqZTLojqrIRLpBM2n6bbfAoUdHo9+DceSeCZ8rh30II7Hz4wzBPnMD6T/z4XKwNq3NenA5ZR52rIfgbUCLT1aut9EdVOZYBIZoX9bUkE6WAZWXfX8w4Uqrur0aRaaG7XI1dDigHQTPOMEnaRSZDiUxVYccJOwKSafs5AMC33wCSKUoYpqa6vg5h5yu+qRwPxvDqM488da4qcplmMSuHfgOq+9rRdJYD5mNNG11nOQaSmEFUWP8ySkjZ5eyDikw6bypHadkm7XRfS5KpwS6nNmss26gkzQ9TfVq0y3Ut21MNHm6QTDfqL3oJITC6GmDFulbZnp44DhDH8C2jhWRakXY5UX8DxmrHLiFAyGZIKxYP+dpWeTUnIhlayWZy4sH25GBurM0n7I5vAiA4R29vfE9AElp9u18imU6J8sKaM00yNVzak8tyVyEvMhkW0D+OeCKJhcVJ+IkVF65F8fzl3GDa0F0uVAMd93tZhgNwRJlMAKI1ufh1JjtZ3ogOes+636w0dzwAJMnUz0QmRTIlPFtEV5VllCmYcJLARlyaeOhMJpPaRcT96rfkJKQlj0l/rsOQTIFpl0IeGWeIeQzP8OSOnvq+Wy1z4ysASNkn76wouxyXk8ulRKaDk0zu2ioC26u2y+ny1guTmyjhsKg1z8uqK86RCLVz1UoyvQzr5ptBfR+O4QE0zkTPxaKn7oG9YSP6xjMA53h5X4pMKaZggnfuLAcAgnIAFojbfdHMEp6Rjm0ZBdbZswCA+PUi5QL/GESwi5SLwk5Z+MILGH3qUxi8972djycToxZEpnaSqWyXC555Bqvvfz/cgYNwWiQ3HDpQwd9GJpIG4Z4UJmLWOY8JAHwEmIoKOy61ajOZ0hpSJ5olsFwTXEjCByzpLDL1H30UxHUx/sxn4Lz5XrhvrqbgKklUADAd2L0A5tZWKfy7ZJdLy50E5QtqMeyull+DJD8NIs+tvq91WHOWy2QbteenqfSkVT5P5yRTlchk9Adg4xEiFi0lMhlRIrvLEQpMc/SwWlhEqnFBp+DvpByUf5Tl5+1ydh+Ip9J+umJjNq4WXIhpwnv72zH70pxk6hnyOpkGBnorxbEvI5mWCf5W2TViYRNDBzCPK0WmFpIps8vVBH/bFsBYuSlETeXbiPNakUk9M9QGjhZQM5EpnYtMjAskTGTWLO9tMvx7UbCZfO5zCL7yFWx+6EOSQswaaVSLTK5Fjy6TSX2eWFiVxIO5uQURx1mnyWVKL+qbFs5sKN+XrpTHDmpZGVEWpxxMb0Y1dZeLJwW7HI/iWpHJtYoB6tNUfoftJJMUPhY7ywEy+DuBCWH1Dh78vSNFpm+JU28IyRSY6lwfoV2OEwMBnGaSCagU3gJllyvV5EoxluGQNQ97byOZDEAAScVnSSIGQlS32Mwut3Jwu1yBZDI6iYqWayKNebbWm78g7XJ6s+aou8sBOZFpSZJJbwJFN0SmG/UXvWbDGCzhWDUulQJfATkp4FEEz27O9BB2HzSNkQpau0sTT+RDJ1KXyThuHkh0i+YT4biSZDIXussBwOvi1sb31FUQZ3qbmMHFSX6l9HvaLkeb7HJDaYkrZDIBwOAmRGMpXC0uqi2D4h1n1vHkK+rBxJm0YNWITDOVjyR6fbBFu1x6BCTT6ikAgDu+PA/+VoNr1v1mtTwJWKxpnCeZ4jnJ5DWQTNRGwpPCdRNOE1hsVhaZ0nmmT2EHRecx3fJw6zHOYnagTCaLWjCJicAwSyKTFlg0yRQYXUmmKzJsf7H7lTNQwd/SA57l63TpLncIkmnNs7DbW0dSRzIBBZFJCIEoZbAqLI+lYhEiRbZ0scvZd0jbq0NdEBpj1jD+OHffhXA7AV75LF7WdjkCjCgFjncnmbjBAGKWA2obKk14Zgtryz0yt7ZAPK9kpYK/ATGV3bb0TplgDJd+4RdhrKxg6x//487Hk5FMudyntvFbHrwmmebPAR7HoL0e3L5Vatnu0F6ZZIrH8C0fScI7d5YDAF/MMEaVyFRDMjVmMjE5sQWUXa5bJhMgs/96j34HAFQGfuvShMskWrBMmS6ISOE/9BCmTxZzmcqZTDV2uRaSiRCSCd4ZyaQaTsxFpoORTFpkypNMHFHlgpkOFMmUhtnxdCkaS5EppGS+Aw1kIkBozrtjttUbbZcrWMftXmaT9ldszIb1453/rgcRffvbYPv7CBMGX4lMsyktWOWAfCZTt40AYN75cpFk0sc7YZa8d8IhhBDKLtce/G0ZpJY+1J3hutJM+nuJUw4RKpFp8fm1QN9mdjn1fM2LTFk3RvW+3gMPgA+HiF99NXs7wTl2PvxrsM6cwdqP/C11IM0ik+wud7QiEzNsGLR8Hs0tKagcxDKnF/VN4zgbjwBKK79roiIMAJWTpe+vFpLJEQIEBGEq7XJVmUwASrl/en7fPZOpPL/UdB3c1YOLTNvPgfdOYA8rWbj9UVWUcoRaZKrJHF26whGY2QdA6q/LBpJp2mSX6x8hyWR3m/PY6llZZZmLwxSWI/Oj4sPY5abbcrxzc+4Ws5t4bLs1x7dgl5t3lzu6502PqLF0iecnkAv+runa95e5bohM11kNdWc5nK8hmWyIKJaLlIZJqzA8UBYDIFlL78VKJnLiEELeIKO4eTfnyuwKDBBsTPdBe/6cZKqyy1ny2C7wU43vqatv5UgmQnABx3E8LdMbmV2uabc0E5lOF38+uBmxmkRXddN66LYNPHd5hFGYzAfVGrtcRiH0+qXg7yMhmdTusT26UOpGxffkZ6Br7Zkwk4gpkcnKSCZpl9uoz2RSgkOSa1UeTBJYybSWZHKMhV2ec09KsaYljws4OMkEyMluaBglu5zOitKZTDNtl5u2kEx1u0t2X5JMCYdrGdnkkFhNJFNxVxiY7wx3Fpl8C9veWn0mE1AQDFMuwAW6iUxpiFgtYJoWkIJzxK+8Aud2SSW6pg/QCLOo/oHqPvheJFMTkz//97gyuwwWSdFj33KBjXa6URcnDCAWiNf9oc8SnlE7bYsVQogM/160y/nHQNQ51Qv6vf/wHxB+/es48fM/XxDU24qYcnwtZDJ16QSTBX/P7zkRxyC2DbdnlUgmi/Tn3eW8dcCwMYsnByKZPD7DuIpkMuq7y3EmyruPkEKJqSaOy9rlAGD9R38U9h13YPWHfqj2d/QkfrI4MVVjmf/g28GuXkX8sqTqRBxDzGagqwskU9WENZL0SV0mE1AWj7VdLuXzdssHIZn0zqjOfPAsD4JElbSQMRhkwd/LkEwkiKTIRBZEplCLTFIk/YsQ/K1JpmmUyp3teC4yBTUkEyDDvwFg9vTTiNI5yTSbEfRWi+eKj8egvV6nwFdd8+Dv4iaGlwkRXC7Mw6G0qnHeSjJlmSh1f3OhQ1lb6WsmSnOZTC3d5bQgUUUyaYLHVd+3e78K/85Z5kaf+hSi55/H1j/8h3Ois8Uu90YEf3NafT8cRmTSi/qmZwwfjWEMBpVCYd7uGKcc3Ohgl4vHIIYD13RV8Hd1JhOgz+P82HQcxYrdHLNA+334Dz4I/53vKL2mRXN49d3UWmv72YxmPnqSicGwHDlfO0KSiSn6q/a7PhDJtH1kod9Abqxpo7e1yFQRrp2EDJayDhftcvVB/ZU12ZFWQDofv7oGf+vjixePz/IBwZCE8p4xbXrk9myfRIjgFI67S90I/r5R102NduRDfJW/VJllRG0HIopaFykCLgy1yKzCIgEgnsq/NVOtU0c1O0u6rsyuYNPwYLAI1HXmJFNml8uJTKmkkC6wbjjoojjzmjiOY0mVyNTBLjc8J/93dUHgGtyEeCYnE1WT8Idu3YAQwFOv7s132WpEpqAgMhXtcrNkdiCPf760SOLsnQMhBH4uKJjvK5Fpo/0BNY2kgJORTCaVLUC1MMHLg74W4PIiRThNYIajWpHJNZziDsq5x6VVrkMGjMxkOrjIFBCjFPwdqomaa8jucjOqOgG22uUuF0O/dTkDiHCMmHG4Fs26wjTb5QJpL8pRG1pk0sRDW615Fi46q0ib7HLuWiYy6XwIu0smUxplIlOV6KoruXgRIgxh3ykFQ9/0JMnUMP44b3krAOC1r34OAMBmtwIAhsdulUJFx2K624fX3SPPEg4rC/7usHN25ozsnpcvbx0kmcJBDNukSC5exPaHfw2973wPVn7wBzofCwDZFRTlTKZOwd+ml4mUgjEgTUEcLTIVJzQW+iA0kBNDQoD+CczSmexIFvPOod9gKRwRYiQqRAVq1gR/y2u8KpcpClIYauJoHUBk6n/nd+KOP/j9QsOB0mFRgp5tlLrLZQHG77gPADBVljmm7DHGSj6TqaZTTQvJBMzv64ElnxcZycTndrm653BT6Z1RnfnQM3tSZKohmdhYBn93CenOKooQW5BUY4VdTotM3YK/39hMJr8Q/N3PNhf8FRuzUf145953H4hlYfalLyNMGDxtl5uIMsk0Hi+VxwTkg79russlTIlM+/PGHa0kk+gkMmnrW1uZBoWh2ohndrlakkl+nkW7nA4AlySTvNd14LBzxx2gvR7Cr8nwb5Ek2Pn1X4dz113FMVPPqerscqZx5HY51Fy7mch0tdxkpq20TbCZZBqXQr91EdvOuvzFKQf0nLSpu1w0AZy+3FxLQ7npUGuXK5JMe5GcI6w0jGOA3Hg5+7/9Dla+//vLn0fPFxtI+MbiHNh+HuSNEplUYxZ460cX/B2NcyLT8plM05hlpG1WLJEi2GIswyFK34dtOZRaRKoimZKIzUWofHc5iEzQ71TTct5U1+BvLdiURDA1FqWqg6dlG0feXa5HIoRkiWenqhvB3zfquqnh1QCEAIP05dpMJhFHsjtR0w6KsOciU10ngZl8fawWE13scifULgi1KXgQQHCOdH9fIsG5h6kTSZpoO+3WgWlgD7KdFsYFXmVbWI8ulNRz3tUu566VBaKVmxGpXekqe9Dbz6zDpARPvLLbmWQyBgOwSZFkYoK15+G0lBZ47L3XAM7h2cY8k2lfToiMjXYBb6qCv6nOZLINZZfbAASvnOhZyiqmRQohBMJJAnO2Vy8ymTm73PQacO1F4JaHOn3WIGHVOHGH8iwPAaVlkklNlj3Tg21QTKm2y3UhmW4q/9wZQKhrwukc/B1lk3VdPVOKJV1JplXfxo67Bra/X2/189alyJbGiNR3YBkdSKYkQEQICACzYdEfvfgiALmIAOTCo01kcu+5BwBwbU8tQFL5b/fXTtf+m6piRIl5bnfrSppwWHb7AkCXffYMknPnitkm/jEAwBomcAyCy//qlwEhcNO/+KXO4dm6MrtcXOwu126X2y9sNGQdDR1HikwLdjkTPmAE8024/gkELIJneWAJg9XVLqeeAyNeIzJVkEz6vRdpHcEF4nAuMplG9+5yy1bPMTEpBX/L+9M6uQXz5EnMVPh3JjItBn9bFQJJlslUvzjT9rSeXSSZtB3kyIK/LR+CRJU5PcagDxGGiOOgE3UEyLFdhBHCjGTKi0zyc0fqu+qWyXS0k/7F0s+JefC3fPZ6KzaCSVJJ0gHqnnngfsyeekqKTJQh4Q6SGFl3Ol18PF4qjwnIkUyz4hjtmFQ2aYlZRjLpjY7WTKY2ksnSJFN1V72qclT4rohiwLLKtNaCyGRRCza1swiAPMmkn/f6+yaGAfe++xB8VZJM+5/4BJLXXsfWf/FzMug6O4g2kokeHcnEWkSm40pk2j6AXa4DLctHo9priThFkollJFPD3DEaA7YUmYI0AI/jWrucaxWDli+pPNKbehXzm46VcCEtnAs5kJ1r/1UgDUCO3yvJFna4zdjFykTuhu7JS1c4glDrgNpndhvJZC087zQxeoQiUxb83ZVkqiDR45DN7Wp5uxywnGVusl1yBHQN/tbW+njx+FROUhpGMCwKQsmRk7MeIgToHs2gy7Ao/s6/fgT3f89y89u/DHVDZLrOarQToL9mw0DckMkkg7+bBhMhbBjqAZtGNV1/MpFJTnba7HLbs20cd6TwRW0CCAERBGD7+zBWVwsTCWf2KgDgalK/+5yvntXL7HJBwvC6OA6Lh0V8HwBjHbrLDc+X85gASTI1ZNB4toH7Tq/iS692EZnkAGgM+oXQSG2bOKxlLrOhJQEwPIdejnxgwz2ACJC19snCRAd/W/NMplRnMgGVu1H63MQ5kZIzAWu6WysyeaY7fwAvkccESCHMX9zp6Viu4SIgKGUyBUxOhl3ThWNRTBXJJJpIJs7lw7GSZOpn14QkmbRdrqHDWBIAVvGBlZFMHYO/1zwL274858mlmlwmbz7ByXaXu4hMimRyiNkonMQvSYuRtsv1LB8gcXYPVJV54gSMtTVE01UYAnhg9VYAwP5guUkVo/JvEK/7oi9NeJY70EVkss6ehUiSYgc/Je5skDGOfenzmHzuc9j6uX8E+3Q3+2++ssVgnmSyjMbzJw9+t7DRIHL0gdu3EM2SQocYKnogRMw3C/onMOMxfNNHGnMYVQJKVanrfMjrgr/L51S/96KQkkQMEHJTAtAkE3tDRKa+a2KyeE7VYpmwCL2HHsLsySchOAcbys0GI2+XSw7WXQ4ok0xHHfytRSbPkAKvXWHlon35t+k06CQIAeqaZAyxRTChzkImkwr+VuRhF+EqrhPqjqj0c0IGf/eycb+3YuP/Y+/NgzXN7vq+7znPedZ3u7eXO92z9KzSjDYGodGMAIEiQzZblEspIIaKzGIjJ64ighRUTIiJiZMqXHaCYiMb4jgpu4hjbEOZJUUQiZ2ASmEkDatgRqPpWXu6p++93X3vuz7rOfnjnPPs2/ve9yp06f7+6eq7vPddnucs3/P9fn4QMtbd+Lvvewr+H/8xxHIJl8ZYcHlvleNyJ3Eycb8oMhFCMjaOs1MSmdqdTGHMYTVAv4H143KAFITChKuYVY04UdMRVbuzgTKTScXlcp+3++ST8F98EcnxMQ4/9ffhPPk1GH74w8W/ocXahk2rzYytM5maRCY6GIA4zmZxuV5Mpg4nk/rsooRna4W4xckUzgF7JN//cAnEccrmqnt++ed2fXEdBASXvM1Fpjjhkm3lbBiXU53lsPdOWEa/+NQ6lbpRvXNb7C53nI79jdelPQKI0dBdLq52l9Ng7FOIy3U7mRriaJDCk5mKTMpJabffr7W1OKgIaDYzEPRgcDU7mQbq6xFMy4AQAmGyXZHJQQAf7XzSpppcdAtdSr9a6kxkustqerjCeFd9bI1MJgn+1vDpuuKJAYN3xOXUqe9KsTd6iUyudCbpPQJfLpHcOaqID/b8Rfn9mCGpaZVZrqE5zCzZYYzXhRqgbr9S+DnNl+oWmWoU5dG9nQyapx8+hz+8doRgoSaLho2FjuGY4zFEFKX28zIoc9PSAoEtBHDwgnSuaSfT9BiUCZCaOGW5pJNJxuV4FGXd5fTv1pz26OhUpGIx2jHBaphM+nm6ppNNwG88Kzej935t5/NLuEAQc3jlk56eJeNypCIypXE55sAyKOZEOZnaRKbVHYBH9Uwme5w6PBzTgIi0k6lFZIr9ipNpbfC3Z+JQiUjxWw2RuZxgmIlMdi8mU0AIzI4Nf3D1KowLF9LPfmh1O5kIIbCfeALmLQMPRBG+i/0RAODY3Wn8ndqnmDqZ1onLJTAtI3MQdJR1RXeYy0XmlJPpUnQL9/zjvw/n3e/GuY99bI1nnpW+RgpMpr5xudwckHLALOlkEqLYzcRQraXT2PPoHixFIkWmiPd3MqnF5J24zslkyHukVKzByaSfnxaZMibT9oWIYZ2TSR8mxCG8Z55BcucOgi+/lB4MFLvLNbhwgikAIuNZDVW+r400LqecTC1g9LYKVzGYRVMGoW04AA0lQL1UxlgKI8Yi6O9kUu7IgAFTwwWWNXE5ov9237jcVwj8bQ6AJATiEK5yI7VF5rynngKSBA+89TJcmmCpRKZyXG4TJ5NmG5XjcoDc/C3zTqY0LtfNZKqLRepKRaZoHZHJQBBx8DCoj1lpkanEEUwPAOvA37nn6D75NUAc48Zf/wnEb72FvR/+4eoBRq/ucttlMpEGkC8hBOzixROBv9sEsWR6XBhj8kUtO+sul/BsrdDWXU45mRzDQeDL64j0ZDJdn1/HnreXOtU3qSgRMCmVB1ubOJm0yHTxceVk2m68KB1/Wronr/+gMxAljDYKioSkcdhyLcKkeoh6Ck6mFETfcZhhtoC/ZVxOiTyq8cDaTiYh5Ouri8v1OfRLRbDSXK7GpNiPwGyKKBEQAls91HBEgCXWj8t9NdeZyHSX1fHBCpOxuhHbmEwdmxTOjZyTqUlkUowf0i0yLaMl5tEce+oUhDL1u4uFdDKVQLjm0Qsg4LBFPzfBwBqkcbllkOANLTLdebX4utTJMG054cPxGw0i06XOblrPPHwOUSLw6nXlamhiMulY0kR+X3OZ9CbjpB3mtEPIEgLYf77gZOKzKajJa0XIci1S8LcFRBEMRrLuckDtaY/eTOi4nAYMW9G8IjL5iQ8CAs+yMov7G88Cl58sLFSbSgtnlcx6z3KZixVEY1zOMRzYpoEZ0UymllPCufrMa0WmEUi4AAEvOpla43I1ItOG4G8AiG70EZnUwt+QTKZWNlgcICSA3cJjAoDw6tU0KgcAQ2sA0BCLcievUjlPPIHdmwEeChJ86/WfBxMCR/Z6XTti1e2DrhGXSyIOZtLKSW5TWQ9eAQCE+Q5zykX6kT/6LOjsGJf/m7+5Fgg4X7Xgb6vHaX1JZMpfc85QXs/5yBxJ5DV1HCpQ9fAeLCHgGQ7iMFnbyXQnsavXT2NcTjOZiq9JO3GIVWYybb7RaaqBxRqZTIh9DJ6R8d3ls7+TxuXouBSXa+ouZ49bYaCpk0mxO0yqwd/y/TNtuhmTaRWnLiYAsAwXhCQwaPWxtPuGLcPeTCbdDS0wGabUKoG/5XUUqNfSKy7XJNRtqRwmxeNloJxMABAt4I3l620Tmdz3vhegFI/d+DIcEqci0zacTLr7JV/ViEy6k2QqMvVzMnWDv3UMt7/IZKkOT8JvEJmieidTHfhbz/f5e8ZV8O/Zpz8N7+s/ObieEAAAIABJREFUgMEHatzM1JACYSv4e7tOJmI23w+bikypc6QlAsSnM9BxQ1zOstKDgyDmMBmTongr+Fs5mUwXsb9MH6euykymG4sbuHd4b+tr6qqYK06Pu5NG9Neq/eeByRXAGcMyKKJ4y3G5SI3hLY1t1i5/CupM1OO3XJc1wluccIR1h6hz1Tl7sMXucj2cdUDOKVTbXa7IZCrG5dpNCGn5R1L8LwlolnJRdlUa1ys7mVRcLgqkk6lO5D5pOfCxFGci0zp1JjLdRRX6MVazCOOhsvg2MJl4KMHfrXG5hHaDvwMBRgLYzIFFrVbw982lHBT3RjIuQhWQly+XUmTKiw9CgNx+CYxFcATpjoVAxgxCHiJMpEPimlCDb0lk6gR/+1O5OK4Tmcb3doKO3/fgORACvHFD2Vk7mEy2ilskSmQqgzI3rTQuN9hTTqacyLRYwDBFp8gUxhxhwjG0WLoQMQyiwN9KwKxp85oymdT1oyMIdd3lwiSEw5xsQx/MgTd/F7jSLyqnX5O3KZOJuViBS3dFDkhcdTLJx291Ms2UyDSqsZNbQxAIeAgKTKbayIGuyK/E5b7pvm/CX3j8L+CBUU2cs6YmroVb7gSCEERN8O8UOnkn1/Gn2iGwUqq7nN1ysimEQHD1KuxHs45wE1vGsqZBi2AHgD3+GKxYYLR/HlY0xYQL3BHrdd+IIO8DYbdvyPIVRxyM9ReZ2N4eiG0XO8x557G4aeGdr70K/6PflTKmNilCKcBYJS4XJaK9hfPydr3IpMDfAIod5lS87TjIiUyUwhMCcbi+k2kunKxltS7DbOwuB1TjcqnTKo3LnR6TaegwzCsik1owJiHM++6Def/9WDz7OSTHmslU6i5XB0cPpq08JqCuu5x6P/JxuYbYelsFqySFigKATZVIS6vcFjqUQqwXiN7d5fR4GJlMuj3L4G/Dgi+0cN3DyRSdLvibUqKipkkmMoVLeGN5P6xaRCZjOIDzjnfg8f2rsGmCRaKcTHVMpgZhoKmIoZpr+NUx0dWiSZnJ1OFkCuN+4O9143JBrOJybU6mnPOnOS4nrwsnd8+wCxdg3i/XXns/9EPNT8QZN25a0+d4wuYpAFKRyWDN8zS7eHEz8HeP5hLJbAZj1BaXk+N3GCvXGnPbRSYF/nYMB7G61pqZTMUufdfn13F5cLn1NXVVnAg5trUwiFpr//m0s5zJSC/RYZ0K4kS+j+45KfjURLvXKiGAYArqdjiZgNoIoebmVg5RdVxuq06m7m6HQFd3uRiWI6NoWVxuTSfTXAm2g5q4XB8mk3JSVbvLyfEyDmSn3NS1v0WRyeY+Fmci01p1JjLdRTU9lJPL2FMg6Vomk+xI4VlGO/g7oaAd4O8oBAwawDYNjO1xq5NpfykHxUtjGS2hVE6OMi5XAkIvbwH+MQyTwxHSmdRVenE+j+ZYRTECWAjcexpFpkbw9/RN+W+dyOTuIlQb6qYF88Q18cSlMW4etItMemFh78jJhyv4d+pkOqHIlIK/z78d2H9edaNSwt5iKeMnHV26MpdQTmSiotPJlHaX006mFpEpSAJYhiUjZMEC+Kf/oRR8nmhuN54v7TyoZNZ7lsMcKTIBhe4XKZPJUEwmTgDGmuHZQHa61OBkAoAhVidyMl0eXsaPf+DHW0Hb+drxTMSUIZrsIlojLueozXUrgD4OEBDS2lku3j8An89hPZI5mUa2jmW1X+N3HpDXinEghekdamUCSM9K1IJbtJxEV34n4jAsQ57k9uDgEEphXXmgEJfjxMVbX9jBYmAj+dj3r/Wca/+GaVacTACa3ahCyHszD/7OM5kGVScTT6TQod/jyDuPmBB4PEYcJTD63mNq8zeDW+3yRBmQ1IlMauFaer/Tlr6mHK8zJtMpxeWaRCZ1HXkfeAbLz38eyZHcDBTick3Qau1kaikN/tZOpir4e9O4XFRwMplUxbJIVVjQr8ULsufTVTreFTETc2KWmExTwB4VXKFd1SjUbbFciynwtxaZFnBH3XE5AHCeegpvv/0anDjAku+CUpLeS4AU1ZPZLOVbrVPEdRudTGlcLvbBp/La6+4ux2G2bKC0SLSWyGRKDk5zXC4AQKSYrMozvYqTyWFOrZMJAHa+8zux+7GPwX3yyZYn0iIyKUfGViJzsY8AFuyWOD67cOFkTqYGBo4IQ4jVqlGwJLadjumSv0XleNXlZLKGcJiDRIlMTUwmCf5WHE+e4Obi5hacTEK6NNM1xxoiUxIBhy+mItNpMJnCNC63C0CkbsyNK1oBPAbzpJOpFUhf42TS+4RK9+TFgYxfW/0xAF3VB0QPtIO/dXe51IG7SVxuoQW0okvL6t1druH5qWREFHKYdl5k2t5awhIBFtzcjsD9VVJnItNdVNMDOWlMbDVQ1ThVqJqYXNNoPUHhfgCDq7hck5MpBAwawjEpxla7yJQ6mXYels+DyMfO4nI58eGW7EZlOEw5mboX17ql+yJcYKFEqXB8pTEu1+hkOpZd7WrB34QgULbXprgcICNzR3eU1baBw6Ffk6tedxqXY9thMqVxub13AIcvYmjSDPy99EF7AOb0hmtos9Rab1BRAn/XMJlK4O+iyFQEuQdJAJvaGBsRfir8b4HXPwv8B/8QePDre73OzMm0eVzO1+6Y3HtedjIFiQD1vHYnUx+RiazgMCPHx1lPZFq3Jq46od+5gLgzLneUigKu2ly3cpmUk6ntXgivqs5yj2Ui09jSItO89nd0vbYTI6bA/Ytd4P6nMXHO4ShY7+STq5bZ3OrfLSuOVVyuTyRNlXnlQUSvZ3G5w3/4PyOcMbz2dffC6tgM9ilimhBxtmjSzr3GMTycS7G2BvxNbRvOUP5+3snEE+VkUnG5pVoYe3GMOOSp26izUieTW10UUqPVyZSUmUyqO5pgefD3KTmZ7Jq4nFEUmQbPPAM+nWL57LMgnlcA9zfG5fo4mUoxWDNlMmknk2ThiR58wsKfXiXFuJyKtgvUOZnkGDUI+rmOgCzeFVkmFsSQB0S6TXkwA+wRgiSAQYxOYZzz7YNY62pgGxn4GwCiBSyHgdlGp8hkfu17YfMY3s0DLJNdeGMThGYHVsL3gThe28kESC5TGfwNIFurqbUHn96SP98jLrd98LehnExhg8i0knNWjqM0YLm4XCQ7F1JCa51MAHDh4z+ASz/+X3Q8kVFrXA5AL9dDZ8UBImK2XpPs4kXw2SyNjvYtO2Xg1I/huuswbXQymQXwt8WodD53MZkU+DtRz7f2c0QR/H2wOkAs4pOLTAmXTiZ3AyfTratyTtMiEzNOwcmkRG59OHNSLpOaCw13AkbJ+k6mpvXtfH+rUTkgF5fr2G8ZjIIyUjEfcK4czw5LD0c2An83QM1tRhF2QMkB2amNUlJ1MqnxXq5ljFRAbePWrVsmX2EJJ523z6q7zkSmu6iOD+UCZczUTVoDySU5JlMQ80aotvBXWVyuqbtcZIDSCK5pSJGpJS6nnUx7o3sBcwAKOcHFt25DBEHR4aJFJs+BLQhWLYByXXknkx6Y48mDVSdTF/j7+A35b52TCUCoNgttG+v3P3QODl8iMYeNHI5lFMNiFKZajCaz7TuZGGFge+8EoiUuYz+Ly60CULe7A4IW6wpOJqKcTAaTk0eP7nL+IgKBAIuXFYt/kASwDQvf8+pfw9fxPwY++nPAe7699+vUr2lgnyAupze9UbbA1yKTy1zYCiRKXRd82fK5zG5KUdGuERZzTia7r5MpWp1YZDINiqHNMBufb+4uZ08AkGJcTsX0+ohMbRvSQHWWsx7J4nJ6Qz0L2+NyL85fwxsXgcenc+Av/yZ29t69tsiUqM9U9HwfeSwAIceHvnE5ALAefBDh629AcA7/S1/CrX/0jzB4DKCXsBVnBmGsAOjNAMYNY6O+L2vB3/VxuUSBurWTaamuWTdcIQ4TsL6vQ4tMqBOZGuJypnYy1TOZROpkOr243KDVySTfO+/pZwAAy+eegzHJBHMhRDO02j/udDLpuJzuLmfQopMpZVateXIfruJCXI4R+Xo4qYpMxkjF5fx1RCYp4iamhQUoIHh27SkHl5/4sA27tQMlgHTDeJpxOSAH0lbjkHawemOrU2TCe6S7xnr9AEu+A6/MY5rKa38TJxN1HIgGJ5MfJem9zI/l+9sH/N0nLqebjvQpy1BMpsbuckEl4l2Oy2mXXNDgZOpVzrilu5yK/fTYkHZWEiCE2TqGs4tys7+um8ntcFxlzQXqryWa6y4XJvm4XMO8ynnqZHKZCx4okakF/B1zGcm+Pr8OALh3cDKRKeJCujSdZid8Y+3/ifw3dTKRU+gupw4KdArkpFwmvSeyx1nstalqnEyZU7+GybTFqBwg14oGJb3uG9M2KiKOFp0sx0hj8qZBs4P23k6meqi53RPoTwiB6RiIVmUnk2IyRQKmTXPzzfZkDotLJtPWGg98FdSZyHQX1fRgBdtjcJIDeepVEymQTKYwBx2sH1D4ys/A3w3KdhQzEBrD6RGXu7m4iZElT1Dg7oBCsRzelPG0ishEGdhwAEdkYkdb6cW5jssBAHYeAmbXCyc7iRpYGsHfx9dkK9E6rg6A0JF/p20R/v6HdzHECivafNK4ChN4lpF2oeHz7TOZLMNKJ+QryetZXM6PYXjdm2694RrYRrqgNCgHTwQ4V0ynmpOeNC6XczKZBgeBqJy+htEK9vwmHpo9hx+N/2OI93zHWq9zoV5TxU7cs6TIFEEAhbicn2ROJlvZsqnnpd2Uamv+Vr2LCSg4mWxmpNGndidTdcG+SU1cE3eG5xDduFFv4027vWRxOc8swtvrn5/sLme1iUwvXwUdj9OFOJBFcRZh+zX++2+9iFcv2LjwlhR+d5ydteNy2skkWpga+dKbeGbRTrdnvqwrVyCCAPGNG7jx138CxngM74M72MVcxhhOWOW4nNMF6UxFpnxcLusuZ7kMhJJCXC6MCKhw0/d4pU7+3MCHEFmkrbPUYnIBpwo6pQwQiYzz5Uo/dnmu0R1iuIo3m4zK0+xTcTLJxXEh4leKy5n37MF66CGA80JULl2wbshkOu+eByMME+VWMdX8lOScTEDzXNxUZfC3oZxMHFUxI2My9YN0A1lcLrEs2aUTyDYJwQywxwjioB/0O9r+or+uBrbqtKo3P6rpgzfqFpmi0QSvje6Bce0QC36+IjLpeXwTJxNx3Vo3jJePywHgsyNAMZzaKoz7iUya69On9EaPB35zdzlWFZl0I5O8yKQ3sxuJ8C1xuT5d23pXHCCA2SqEsT0tMq3HZTINAoOSZieTcrfThu5yxJJ8VaAcl2sQDfWa0pYik+5M18Rkyu8Rri+kyHR5eFImEwejJMeBXOPQ6OAFgFDgwtsBKBD01kUmFXlOuyevIYLVlZ+JTHaXyKSdTLm5Uc/vFSfT4mDrIhOgXZM9Imk2qziZNKPJtI2ck4kAzJJjQl/w98GX5GFUCfdiG1IY6hNFMx0DYXnfqLvLRVBOppY5e8NiyQpL2NtrPPBVUGci011U08MVxhdc1VWovj09sS0gjuGpMaspisb9FSiPAIhGJlOYWOAk6RWX21/u4x5PbcCdHVAhJ7zoupy8WL673K2XgN2HYQ8s2D3jcgO1IZqH81SUoudkNA9HOVZK3CMuN76vkfkRWDrO0LzJ2Rs5uMcOMeXNC+tlmMAzjbQLTXIK3eUc5gAXHwcA3Bu+Cj+SzrUk4J08ByA7RRnmnExU8YtSLlPNJJyCv3Pd5WwjAigtLkwjH/4bvwMrWOBfP/4T+KXkm9a2P2te1+AE4G8OgZCgEpcjkLyhtMWp66adfWprdrNRnMycTL7kT4V9RKZVAaC6ae14Jg68HQjfT1kylXK0yFSMy3UxmcIOkSl86SrsRx4puBhSITVuj4RePbqKV85NYBzdRnx4iB17B0fB0Vp5d6E2kLynyJR3OjrWOk4m2WHu5k/9Lfh/+Ie458d+DNFoF7tktpUWuWWRSS86G0UwLf7m43JR5mQihMAZsIKTKUw4GAbpOL4U8nvOUm5813EyxWwAAVplUOhxs+RmykSUalyOMoJEXT8m1Uym04nLAcA83/o4B/7W5T0j3UwFHlMb36EHk+kjj3wE/+wj/wxjS/5cHfgbqL4/XVUWmVInU43IRAwDwnMxCEQvfhKQdduMbTt7xKXabAczwMmcTF0VnER0WKM8y5BrBEs7maSL2Jt0i0x+xPHF8w+DXLuDJd+FNymOK2nXwTW7ywHKyVQTl0u7fGmRaT4FHQx6OMNEaxRkU/B32BaXi/xCZzkgczIJIWqdTM4mTqbWuJx8zVtjMonuuBywvpOJEAKH0cY5Jm0u0CgyFcHfJqNyIx01HITpeLo9gsOc1MHWxmQC5DV/Yy6j9lsBf9MNwd/7fwKceyQVCyxG2xtfrFkJl7Bq6WRS8+aJ43LqGnXGcK2a+TBf7o6cF3OHb81Opv1KnGwb5ZhGPyeTY1TA35qBZDpGMS4HyPu1j5NpeRv4g/9NphlKKRDbNCAEqs1EaspyWE13Obm3imKiustt2TnLORgPsDpzMq1VZyLTXVTHBytMLrryRvXqRSY9oXhE3gRNiqtY+SAAGI2au8slNjjl0snUQ2Ta89Sg6ExAuRxw6p1MV4Hzj8EdmBL83eOkbWjKU8l5NE83XuyCEplykbkU/N3mZGqIygFAaHmgQoB1RH3udSMcRrZ0/NTUKkrgWkZqeecqLscog23YazGZ/BdfxGsf+4uFDidhEkonkzMBxvdhz38FALD0Q4gYoIPulu6L1MmUj8vlRKaGNq9lJ9NqHsFCVFwYxwHwzz+GcHUb9u7DePWBj8rXsiZHQbuzTsJkAgCf0KKTKfbhMAeEkMyJ4rrdTKYmJ5M6NR9iBZvl4nJmSyv2OKgs2DepHc/ETVtuUFq5TDkn00AxjKKku7uc1eK2Cl5+GVaOxwRk7/mqaTEMGT86DK7hzfNyUeu/8CXs2DuIeIRlhziVL67uU94CJ89XrMZDw6RwTdr7RMq6IkWm2W/+JgYf/CDGH/lzWLEd7GJ2Kk4mrwv8rcXfGvA3VSfXzsAsOJmCiMMkgzSSqMcgWwkJ/Z1MU8RqPK6Av40Okan0fuu4V7poZTout/1IlY7cFpyzJSYTAAyeeRoAQPOd5ZpcOKq7UJeTyTIsPH7u8fT/rCEu13TgU1dJzBFHHLabvVcG5L0a1zCZAEAM3LWcTClDyHIQEDXXpU6m4xT83U9k+srE5TxLORRz4G9AOpnaussBcr30xfOPgIcCKz7GoNxZTnF0jA1EJuI69eDvMpNpPuvkMQGaydQiMpknYTIFoE6Tk6l4MDIwBxCQAtPWnEzOpNnJxE7DydQiMl24IH90E/h3C/ePz5TI1HAtkXJcrgv8rcRUqESBqYbgJiZT3hH25vxNnHPO9W4I0FQRF9Ldoq7ltZxMuc5ygBQwtslk0q6oApNpi3E5h3U4o2uEt1UdkymJ5PM6BSeTY9LWruO6TNuogLV1fM6yWXY4sq7I9Ln/UR72fuMnKt/SY1llTdHw/EK/FJczTIAyxDEFs43sUGNbzlkdCYZVdXCfVWOdiUx3SXEuMLvl55xM9e3pdf7ag7wJGp1MqxWISWHSEHHdwjYOEXIHMZEZ5rE9xjycg4v6Qb8gMrk7INEUMM2qyMS5EpkexWBkwQDBYtGfybSIFulrcvbU5jYnMvGEgzLSfAp4/Ea7yGQ6sIUA0ZDnhjpvhjhKHLy4Xz+wyrgcAzGk0JTMsgXTwBysFZdbfuELWH7+8zj82Z9Lv1Y4Pb74BM6vJBtncUd1pumxCJ7XOpkU70o7mericorJFHG5gfUXEUzhZwvjOAB+4WPAlz+N4PwjsCcP9G6fWnntWwB/A5BRj7yTKfHT76WLYMft7i7XGJeTm8whWSknUwgwJtvTN1W0Sk/tTlI7roU3LLmoi9pEJv8onRzXicvZDQvP+M4dJLduwX6kKDJp/syqRSy6ubyJBD7CK+8EAARfegE7thwj1uEyaSeT6OikqEs7mZi5HpOJXboEYpogrotLf+NvgBCCJZtgl8y3ApZcu7vcqsbJVOKAOUOz4GQK4gQWGWZMJvX52Mq9tw74m6suaVUmk/ocSuKlwShAqk4d7cQpnIyeEpNppJohFLhMWmyJc06mp6XIZIwzJlPjglV1F+pyMpVLnwDHJ4jLaZ5VIS6n2isnon4jygcOvDXA35ohxB0Xke7Sucg5mewxgiTotTnd+qK/oQYWkzFrHZdT4743seAvojRSX1daZAqV46zKZDqBk8n1GuNyBSfTYt5bZDJbwd/ygGMdkclisuOYCIJ6lk/NwUh+bbaKV3DNopNpIxHeHsvPraZTpZ1z4Jy44gC+MFvdqMa5c4BhbCQy2ax5jkn5Xk1OJtsqdJezUyZTg8iUCh5DOIYDS4tMLUwmQF7zNxY3TsxjAoCEK/B3C9OztqIVcPtlYO+d6Ze23V2uMP7YExnNO6mTKY3LjaSg2CaQ1EQIa9e3WsTfMvgbQO81TxuTybQzIHs6/vQRmcIF8OzPAo//2YKYqEvf130+c8sxag9kBBsiTgyYFj0ZE66u1DyyhHPmZFqjzkSmu6Tmt31wLqSTaXW7PS6HvMhUL+BwfwVqGjCJX+tkEsEMofAQEqRxOQGBWVgdSGIe49A/LMTl4B/B8DxEb0kYcSoyTa8BSQCcfwzeUHXGWnQ7mXTrZwn+jmEzCmN0j5x0806mSDRH5XgCTK+3ikwBs2EKIVlPLTUmK8zg4nOv1E9SyzBOM+90NEqdTMD6IpNe3Nz5hV9IRbvUyQQAe+/AzvwVUHD4bylRb1yFwper4GRSp56G0CJTIq+xPuDveQSLr+TCOA6Bf/49wJd/A/jITyNwJrANu3dni8pzDLPnuEnpzc+SkoLNfBWv0siIXgQLp8XJFMzlSeGoSWSSG5oBVnAU+LuLqSFPhU/uZBq7Jl6j8u83wr9LTiYtMp0kLhe+LIVN+9FHCl/XcTk/aRbsnj+U8P+Hr7wL7PJl+M+/gIlyY20iMiW0xTGWqzgVmQy4Vn8mEzEMnPuev4jL//VPwrr/PgDA3JhgRFawSbdI3vn4FSeT6i7X1BShDvwdaAaH/LycQVlk4rBpTmRSiyZzoZxMfWN/wQzc0k6mGvA3UHEyEULALKMiogSrBLbL0oWldYoikx5DiiKTukdzGzd24QLO/aXvx/jf/Xey55megpfeo1xcYp0qg783icsFSmTKg78B+dlHDSJT4tnw/DWcTCvdBt1FQhIIECkyCaFigqPecTn/K8Rk8uwy+FvOve5IftarafN6I4g5Dr0dxLvnAQCDUlxOz+Mbx+VqDjH0xk8ooZIvqw006irqYDJpzmK+oUBX2aqNOA87usvlSo/382hejMvFElZNaXvsr7b0/VTjZkq7y20B/C1iH75grdckoRTs/PmNnUxNXfD6OJnAOXgUZeDvtu5yOi6nwN/ayUQ7mEyrKMH1+fUT85gAGXXSLs26bmqNdfiibCpw8Yn0SxbbrpOp4KTUkb4TO5nUfsjZzMmUOfVzY7juvtZ0oHmCcrq4Uaosp47JVI3LpQKy3QzqT+t3/4lct3zjD9V+W9+DfQQcy2HV7nIAYlOuIVkuLre17nLKEXsWl1uvzkSmu6SmurPcBafVyaTjco4SC5pUa7HyQWwDDD7imu5y8fwYAgZWkAOTFnnqInOHq0NwwQtxOfjHIAMPSFQ8RXfqUZ3lcP4xjPSir4fIZFELjDLMQ9ldzrMM2UZ396GiyJRwGE0Lr/lNuYFpEZkiw4ItBDBr2KyrYtEc3Bzh2QaRaRUmqRvBGA1TYChQ7MbSp5LDQ1DPAyEEBz/zKQCqaxvNnEwGD/AA2Ud4IMUxOqkXIfO1SDu3GRUnUxqX84+lOJcrU20kwySEEEI6meKlFJn+5fcBL/468Gf/DvDU96eA8hQyuebCcBUmoGTzjYneTK0IqY3LAdkJCredZieTdrYNG5hMzEZCTIxyTibaFpUTojZ6sEnteCauCQvEshDdaBBHSyLT0OrZXY42i0zBS1cBANajjxX/lN5ktIhMz157HgDw9Q+8E84TTxScTMd+f/g3SeNy6zmZDJNmLJSetfcjP4LJt31b+v85VZ0o1+yIV1fENIG8kykVZRsWM6sjwBwURMoyB6wSl4s5HGOUMZmUk8lcqijvGk4maCdTBfytRJjaDnO0xskUwXJZ5ugxyKkxmbK4XI2TqSS23vOjP4rhhz6U/r8xLpeeZE+wTlXB3/Vg9LaqczJRoVymDSJT7Nnw1mEy+SuAEFDbBWgM4Z6TJ+3RSgLe7RGCOIDdQyxvFOq2XJ6lwN/Mlk0+NPhbRd+W02ZhXW/AknvlWsYtxeWSDmGgrYjr1DqZHEuySAJiA4YNvvR7OZnCREhOT9Pf24jJZCgmU1APjK7rLsekILaMlsW4XJRsLigqxmGtyJTG5U6+0RNx0MlkAiSXKT5cX2RyzBYm03Qm3c5u/RpAi4SRH0AItaFnTnN3OR2XU+BvU/3ZrrjcKkzw1uKtrTiZ4nzHQ3fSPy63L9cDp+pkKo/h3rntMZnsMRzLgN/2fNd1Mp0W+Lunk6nMPErjcs4Gcbk4BD77M8CVbwCuPFP7I1oM6vOZS2ZUdY0RGZP0+YfJtuNy8r5bwj6Ly61RZyLTXVLHB0pkOmfJjX8Dk0lPKK6QN2CTss59H9S2YGJZyd4CQKisvEtCUicTUC8y7S+l8p46mdwdIJjC8OTig45GGZvmltyYSieTnESDZbfIRAjB0BwqJ1OSKf8lkYnHMi5XW8fX5L+TBxr/TmAYsASk46nt+QQzDCe7+Pwrt2tBxakQBtnuOMk5mTzmrQX+jg8OYV65gt3v/m4c//IvI7h6teJkAoC3k2tIbktxjO5e6HzceRDDNAhslolMhrpu0rgchLzecpU6mXiIOORIIg4znIEuXgNe+DXg3//bwNM/AEAByg2nsKBXPaBwAAAgAElEQVRZpxZBgoHFOiGoTVVgMpXicvr03TLkc0vanExadGxyMgEIjAGGxAejBCLqcDLpDjFb6C6345oIOYFx6VIHk+kIQRTBoASu2UNkinwEhDa6FMKXr4K4Lsx7iyeg+j0PecOJK4A/2v8yROLgGx56CPYTjyN4+RVMIDdW6ziZECiRCf02rnFOZOpsOdxRMyoXNNQ/YYcaKCdTWBeXa3AyLW9XDhp0tELHI6STKU7HpyBK4BkjHAfHEEJkTib1J9bqLmd3xOXqRCaLVphM2slUjcttX4gYKZFp6ufmG8ogc3ztbd4bo14bOpmq4O96MHpb1YlMMScQ3ETUcO9Fnrl2XI66LmzmgJAI3LsgN0G51+0nfi/R6isVl/MsQzbBEJBcJs1kSkWm5jFPCxf8nFxfmcfF2DyfzQHTBHHWH7ep49Y6mbz83OhMwP2gn5Opg8kExgBKwdcRmVR3OeH79cDohu5yQC4ul3MyOZsKijp+WrNxtTeM3teVUEymLrcDu3Bh7e5yAFrnmGQ2hTEeN65t9DgeqMYMJtMiU8NYFRSZTGlcrgP8fbC6BT/xt+JkirlIXZpNjWMqxRPguX8sD6fPZ9H7bXeXqzQeaHDqr1X+VB72UAMO6+Ad1TiZ9EGvm79PtJPpFOJystlJTxGn7GTqjMu1dJf74r+UKZYP/nDjj+hYWx+XkNnkZKI5J9O2u8vlmUxnTqbedSYy3SU1PfRBDYKhqxaPHUwmW4tMTdDB1RLUtsCIj7hGZIqUKDLnBK4CfwPAtGYg0SJT5mSSgylxlHBRgH6/JAfl0SXYnlwch8t+iwUdM1uGcab8a5FJbaSSuCUudyxbpbfG5UQCC6TdycQ5EM5w/tx57M8CvHarKkwsc04mOhqCz4pOprXicoeHYBcv4vzHfwDUcXDwd/+edDKlTCYJlH0buYbkjjwFMXa6J6hFEKen+5rfQFXHKdldTgmZpdMeDf6OkiiN47A7r4KGh8C/91PAMx9Pf1aLYZ0t2RtqGcbp+7hJpUwmStITbUA6mVImk7peEstuFplSJ1OLyEQ9TKgPQkh3XE7Hc3rGVtpqx1PsjYt7LXG5HQACxJ/CZrQSeWx6jiFp3pAGL12F/fDDFe4UowwUZqvI9Mb8VdD4HlyauHCeeAeQJBi+Ka+zdUQmQzkDOFmfyeSYsqV9vKElf0oU82V5a6PfzxcxTYg4G4c7u8vVuFl1LIaqe9kZmhIOrYSLIOZwjRESkchxVDmZKJef7zrd5bSoUtlAGfVxOUDCrZuYTHozkYpMRr/44zo1UffJdJUTmQhp37ipaoRWawF+XSaTjsvxEvh7DRE+qBGZooRDcAshr3c7hK65Hvh7tQJxXfnzNELsnJdxOb35t8cbgL9Pn8kEqLnGGqSt3fuITHojmigel3jh9wvfT2ZTGMPhRocetMHJpOc3zWXiftjPyRR3MJkIkfDoYA0mk0ERxTFEFNWzfBq6ywF1ItMJnExatK3pMOessRntKhH5CvzdvsZgexc3isu1uWX58bTVEUdSJ5O8ZixDd5drAn/re1J2l0vB3x1MphtzeaB63/C+1tfSp6JEZNdk37jc//sp4PXPyrVjbtw3je12l6uM4Q2NbdZ70OP0Wt2EybRSWI1CpDRda54C+Fsx17pKgr/LcTklMjkGony8HWh3MnEOfOaTwN67gLf9241/cx3wt2VLJ1P5gD8yxunz33qjiTQu52yn6cBXSZ2JTHdJHR+sMDrvgAaaxdHOZLLVIr8JHCtWPohjSyZTUHUShQoGeyTkZmxsNzuZbi7loFiIywGgjpwwjN3cZujWS/K0gpBUZKqzPdbVyBoV43KAFJmiRQojTRLeIjJpJ1NLXC6JYFPWzmRStuTLe/L1fu7V6kTlR9lzNIYjJCeIy8UHB2AXLoCdO4dz3/u9mP3Gb+D8a8fZwt4eIRzeh7fTaxDH8n2g57rz3PMgThfk2ppNk7zIpD630mmPQQ0wwhDyEP6x3MyY8wPQB94DfOA/Kb4PyjGkF9JNfIKmWobJxjwmICcyGSzdbADFuJw+xUxsF4ii+nhBV1wOgE89jIly1nwFRaaJK/9OfGGvHfwNgAbHBZGpjckkIh8hCMyGDX/w8suwHn209nsmcRA3RHYA4Di+hgu2dBQ6T0iRlF2V9+c6IhONItAkBO85lSUlJxOAdot7Sx0TtRE66UIVAExWYDKZBoVpECybFjOrO4BXFJl4EEjRxMxEJgBYzUMkXCDmAgMmn/NxeIxVvIJJGKAiVus4mWiXk6mma2E9k0mDv+Vi0aLk1JhME1e+H8er0nNjVn+RqSzEnZDJlDmZNo/L5ZlMUSwAbjcKvKHLMPDXcDL5K1DHgctcEMKxsneB5WEhJhIkQS/Rausg1oZKXYBBLLlMJSfTatYiMqnnGNIBzGiG4He/UPg+n81Bx+tH5QCAOC5EEEDw4j2jN/tL7WQK4v7d5TpEnHyHsj5lmxSmiseTnt3lNJNpEZdEpoifTlxui04mxH5ndzlAxuWSW7cKBwF9yjGbOT3JbNYI/QYykSlUTiaLdXSXC+rjck1MJn3d7a/kmuHyYAtOpoSD6UMnd6c7Lrf/PPCv/ybwxEeAJ7+r8K3tO5lKjB73HLA8oZNJNT8A0M1kskYSNu4X43KVpjaLA9m0wOp2M65brtUvLmc5BpKYF5okhMqMYFpGLt5eEplqUh148deBwy9JF1OLOL8O+Nt0ZMQ4Lu0nYsUmZRY9te5yS5wxmdapM5HpLqnp4QoT3VkO6GQyWUpkamyf6vugjgNGglonkxaZZmBSZOqIyzHKsOuo56QUe2orkWknx6u49RJwXjJcbHWynNTYHutqYA4kXDLnEsLuQ/JfFZnjsYDRFpezJ60bAs0QanUyKcX+wvkL2PXMWvh3PtJ3EvC34BzxrVtpG91z3/e9MCYTfPjXr2dxOQDx+cfxdvImxFR1lzvfna9fBDGGqZNJiUyqY1wcJa1tXk3DRBj78D/9dwAABgfola+t/FyYhLCZXYBMrlMF19oGlYpMzCmCv5MM/K0noUid+NVymWZvSahxQ0wVAFbUw5DIBaAIo3aRST+XLTqZ/J2LiPf36xfCarxg4RFsZqSbTN0hsK7i2AdvcDIl8wXiGzdgN4lM1AFHUOsSuj67DU5neHgsgeHmAw+Aeh6iL30ZI2u0npMpjEFEjCSuWdzUviZ5/TGTwulyC3XUMdRGaFtOpqj4WbgtGxTZ/KHkZFLCpnZZOAN5XQSLOF24DdU4fhwcYxkt4TEHsVjDycS5FJnUGNocl6s+b2ZSRDknE0844iCRTCZtv6ei+DhbLJsZcE0DR+V4NnMqTKZy6dPfZibThnG5Sne5deJyipGRE5kC5WRq4qH5DgXjgBn1u1/4ygf1XLjKvTKzd+QmKNdVSUeiu6oSVzmlGtjyvl6ESSEuxywDlmNgedwSl4s1y9KFizmWXyiKTNLJtJnIRF35HomSm0mvE3ztZAp5Z1yOK9G4DfwNbCAyMQOWEojr43LN3eWmwRQRjwpOps3jcmrNWONkslMm0zZEJsVk6rgm2cWLgBCIb693oOCaRuOGlE87nExKHApXcmyydHe5JJDjcLmCmRQxTE85mQQEJTI2Wffc1Px3sJJr3XuH2+guJyRXD0g72jZWHAK/9HE5dn7kkxUBYvvg79IY7u6e/IBINT8AlJOp7ZqkVB7Al5hMnl26R+b7pxKVA6QQ1ue+MW1tAMh+NgoSMNsAoaQ+Lsej6mGNEMBnfhrYuQK866Otf3OduJzlVJ8fAEREfhZmPi53JjL9/1pnItNdUtPDFcYX3Sy65DXE5UoiU7OTaQXqujBJgKgG/B0u5EJopk55tMhU111uf7mPPXcPlOhW8MrJZMkBKI3LxQFw9HoqMunFMa9pRVlXQ3OIRbTAIswcOGWRKYk5aNPC6/haq4sJkJwhy7CBaYMjBEhFJuKM8P6HzlVEJs4FVlGSCitGKS7nmV5vkSk5PgbiGOziBfVYI5z/+A/g8ReXeOBq9pjiwhN4lFwHnWuRqftUahEk6YI8FZlUfCpucTIBMjIXvvSbWL0s4wSmv6icvgoh0lifPn3chMm0DZHJN+1G8Lc+2YoVDLs2Mje/KaNyLScxK+JhpJxM3XG5LTKZlMi02DkPcI54f7/6Q6nIdCxPqxW8vc3JFCrAaJ3IFL4iO8tZpc5yuizqADSsdeL831e/CAB48tLbAMjuPeb99yO6cQM79k5vkUkIARYkoCJG0nPS15t4g+WcTBtuVu4IHZc7uZOpVmRq6363ulNxs4qg2BFKi0z+PEoX2CMzJzLFS3jmAPE6TqZoAUDA8LTIVAZ/tzOZktx7rZkKeSaTAX38fjpul4lrVp1Mhr15XG5DJ5NenKfi2rbicjEHuN0qMgGAmM9rv18uvlqBOC481ZZ+aqq25Hpz5qi4XA/wd/gVisu5pnw/lmFcEJkACfJe9nAyraIBPGuF+PqNtKMrcFInk5pfSiJT/gBGWGOISHQ6mdKYZafIZK4pMlGY6t6tjVnVdJfTItMtX4rtGfj7BE6mlu5yWhDaxkaPJAHCPnG5i3LTv25kzjHpyZ1Mhbiceu/r3EzhXDpgCIFryO5ywmzmWTrqs7kd7GNkjtLmPiepiOfA386OfJ5RA6j8t/428NYfAt/2SWBYFVVkXE7UMk83qUpc19uVwkFT/LDXg07Ta9U2aTeMvhQhXIYxPLMkAi72TyUqB3SsKXJlqn1BPjIX+Qks/fW4NP40MdRe+yxw7fPAN/yngNF+cLQO+Nty5PMISymYmMqxiKm4HCU5t9VJS+E2fGFtpbPlV0udiUx3QfmLCMEyxriHk0kvDMxYLqTbwN/EHUgmU83pabSUC5MjInk6LnPBKGtkMt0zyMWzFJOJqtNZpuNyd16VbUqVyEQpQWwAoufpba2TaedK9tiQIlOzk+mNbpEpCWEzF5i/VX9aBBR4FE8/fA6v317ireNsotKnoRmTaQwRRWmL8YE5wDJegovu160XNXqRAwC73/3dOBpRfN2/ej6dgOk974BNIjhHb6Z/s6vmBSZTUWRK4pzIVN5E8wRWtER0+yr8t30nAMCY36qcvoZcPpZtnMDJFCXF9q5rVhaXM4vg7wKTSU1YZouTaX6zFfoNAAu4GECJTFGHk0l3iNlGdzkVlzsaybbbtZE59VlaKi6nhaM2JlOYKOAorcblgqsS4N/kZLINF4SGtePP5998AQDwzQ+9O/0a29tDvL+PHXsHx0G/7nIRj2BFQopMPWOYKZPJMja+JnUtuYkVnFMTmTyL1cflhKhnMpWEzVRkWkTpAntsyQMAHZfzzCFiklnMO0uNfYY6SKjEX1ORqT4ul3cyBctMJAkTAcugIEKLTNt3MgENIhNbR2SqczKRtNte39KREu1kooyAkPXjcqZd5HmEysnkJ/Vx7KWjutrNOtpNqxIrGZfz1OZ2ypSwquZbYQ3XAH9/ZeJy+uBkWXIyATIy1+pkUvfbMhpiOJLXxPK559Lv85M4mRw51pfh3xnkPwE35GN3ikw6XtqxgaLmek4mi9HUyVQLjK7pLucyFwQEt1ZFkelkTqbmuJzNKAip6Wy5QREF/u6Myykn+boik2s2c3qSWbuTSTvJ4lU+LtciMgVKZALgmhL8zVvWTvq6uxO+tRXoNyDjvywFf1cZRGm9+Rzw2/+djMi949uq30c21m7LzVSJ67rNTv3+D5rF5VxTArF1x9DaKkUI8+zWtE7TyWQavboymkrEKTiZ/Dj9up63zHxcDqjer5/5acC7ALz3P+r8m/rz7iPgmE1OJtU8xrTkZ7HVuSbvZNpCZ8uvljoTme6Cmh7KRcnkopsNiA1MpjR/HYWt7VP5agXqedLJVGOdD9VCXItMhBCMrXFjXC7lMQFZXI7Jx02dTLnOcroSgwA9b9haJpPlSU5OQWTa3MkUJAEs05Mn8cuGbiJBFhV4+mH5OeS5TOW2pHQkJ37tZtItf1dNrWhzlYpMF7JucdR18Wvf5ODilw+x+O3fBgBYl98l/+Z8H9REBcZcV4sckykVmZQ4GYdcOdJI0cnEOfArPwgrXCG49DXwL32zfH7RAnRQXBhrl4xt2Gk0aV3XyDKI003DJqXdSkvDLIK/k6qTKRWZFjUbtNnNVug3AMyJBw95J1MLvFhvans4ALpKO5luefI+q4V/K+HXjo9lJKIH+DuIs8+vXOHVq4BpwnqgvlOjyzzpZKrZNH/p9lVAMLznnofTr2mRaWJPejuZgiSAHQEEvLeTSf+cYVK41mbuuvTvxwmmdLwVJlNzXK4m+hjM5PhU012O5kWmYU5kirTIlDVwWEZLeKaH2FIutzVEJuqMYDG6Zne5IpMpzxSKEgUx1iyn0xKZPBNHFZGpD/i7IeoVqLhEj/G28Cc1+FttoAghYHYVjN5WGpqeryiWItMqbhCZ1K3Me4pMfLUC8VwMlLBwbKiDhNvSyRhbHrjg64G/Tzkupw8lFoFyMuUOF7yx1cpk8uMEBiFYRkOMdhLQ0QjLL2QiU3ICJ5OOyzU6mcIEXG2SynNpuTInQTuAnFhW2hCgT9mMwk7jcqVDEiFqu8tRQuGZHg5Xcr2U7y63sZOJOTKeXhOXI4TAZnRjll5aQoAkQT/w96ZOphbnCJ/OYEy6nUyxr+JyRofIFGZdPx3DgZkAvOV1aYD6cbS/lagcIMVPlgd/A9XIXLQCfumvAKNLEvbdUFpA3RaXqTKGpziIE3CZ/MzJ5PRxRlecTEl1fTvf71xrblqOSbuFMGROpjCHUomCJHM41cXlgKKT6a0/Al76TclpNbsPUy3W36FoNjmZ1PjJbAk43+pcow4rlnDO4nJr1JnIdBfU8UFeZLojc9cNDAh9+iSCQJ6EN7TAFqsV6GAEk/jgnBQAb0B28/qEplGnOpFJCIGby5tFkUnH5ZgcbDOR6SX57/ksYsNNCqMnH0I7mRZhXHS36A5zAHgiQOsWNsFcvnc9nEyWOg3CrCEyF2RdPN55eYyBZeDzucicXlRkcTk5ACdT+XspKLNHZC45lAs3IycyAcD/9STB8uII+5/8JATnMPYeVy8gBrX73daLWieTXNAkEZeRFXcn20RzDvzqDwK//7/CdM8hvPAY/HkEy6GggldOX7WAYRt2uqBZW2QKkzT+sEkxymBSEytWA/4uMZmC1MlUF5d7q3PiXwgHnpC/2xmX0xbyHpNvVzmmAZtRHDhaZKqB1rtaZJLd5SihKby9qUIVucmzv3QFV1+G9eAVELNeSHMMB4QGcqNXqpur1+GRSzBykSi2dxHx4SF2zElvJ5MWmSiSCgCyqfQmnjG6ccdDXWHCMafjU2MyeZZRH3fWi+ISH0xExbicPVCsl0UWl9tRC+Kj4EjG5ZiH2DwHoKUrZ75yLk6b0Zrucu1MpryIokUmy1MiE6PZ752ik2laB/7uZDI1dZebrs1jAqSLl5IM/C2fhrF2XK4sMoUJB4TdeICxULdyMusZl/N9UMeFZ8lx6lg7lm6/AgDwVVOAXuDvbYNYG6rQmdEsO5ns1u5yfsQxMSg4GAZeBPfr3lvgMvHp5k4mopxMfFUSmdLucjE45PtIO5pdpJu8HuBvviaTKY3LOaXPtOVgZMAGOFhJAcZjch3gR8nmTgJC5Oa9oWOV3ZMt01pJBAKBsAeTydhUZGIytsNLm3oeBPJQoMVxXhGZ8k6mughaMAfsofq7srtc0vK6KCWwGMU8OcC9g+2ITDHPg781bqEkMv2fPwnc+jLw5z+VuZ1qSgsYUXJKcbmG7snrPei04GQCOta4dU6m/Po2ieR6+7Ticj0RATqOlo/LhX6SsZCSJidT7n79zCelw/f9f7nXc7PXEJmsGmYUAEREjrGmZZxM5K6raAUBggDmWVxujToTme6C0k6m0XkVzXB3G09O9SaDB4E6Ca/esEIIGZcbjMCInMDiEhcp9BNQxEiQnXiMrXElLjeLZljFK9zj5TbgpgdQE9RQIpOOy916SVoncyfwxCIwek4iQ3OIiEdYRUGR05MTmZKYw6g73ZsqrsKk3nmhK0xC2Dr60MRlyolMzKD4ugd3C1ymzMmkwN9D5WRSHebyLX+7KlYiUz4uJ4TAEiFe/Y5nEPzJ85h9+tOAPcSbuAgeUVCn3wZtHsQYaiYTpQBjoJG6HvQk5O7KTS3nwK99Avi9nwc+9J/DGl2S3eUWERxXObZKcTk/1iwBC6ZBYFCyEfj7JE4mQJ6s+tRIF2YxjxHxKN0YpZObWjxXmExxKIWEUXNnOQCYCQeOCIAkliJTgwAjH1N3lzu5kwmQbqZDzkDHY8R1cTlmA+YATjxLF9SWYbU7mdT36kWml2A/+ljl67o805NxudLnPfUj+OQGLrlXik9vbw9IEtwTOrjj9ztZDJIAVgwQsqaTich40knjckHEMTcmW4rLWbVMpnqRSbtZy93lisKmYVBYLlNMJvn+eKbsFKbB367pIjbGYCTq15Y95+K0WQ3Utqu7XO69DgpOJiE3J9oBdUpMph3XrAd/bxqXyzE51i1m0DR2AACmRdeOy9k1IhMVNpYNTqaZLV8Hn68Rl3NdjJTIdAQtMr0MMBcB5OP1istFpbbXp1TanVsGfwOANzYRLJvjtUGcYFdde54bwXvqKYQvv4xYdRbjy+WJnUzCLwoEmSjGwYUSmVqmDvk8ezKZbHvt7nJW0sBkSues6sFIk5PJOYmTwB7XxuUA6cg4cWRFCct94nLUsmBMJumhX99Ku+qWxkk+la/LaLmW9PtfEJlSJlPNeBVmcTlGGeyEIul4XbYVIBKrrTmZ4ryTKY3L5ebzV34LePYfAE9/HHj0w62PZWmMwdacTKWDgpbGNr0qiaVLUneX09zRtZxMpfXtQomYpxSXc3smCprA32mMLimNP2WR6fYrwB//EvDU97UKiflaB/ydOplKh5hpExMjUSLTduNyxPRgsX6Rw7OSdSYy3QV1fLCCO7akilzTVShfemISQajaVdY4maIISBLQ4QSm6oZVhn+HAWBSHyCZDXRkjypOpv2FhAwXnEyEAO4OKJWLm0Jc7nxxY0osA6znulqLMwlWVZFp+iYQB0jihhP54zfkv33icsqJ1cfJBADPPHwOX7o5w52FfL16ktHPMXUyzYoi0zKq3wjkKz44BHHdgoAT8xgCAne++T2wHnsUB//D34WIY7xKr4BHBNRpcdCoEkJgESapkwmQbgoS5ZxMgGrzegv43/8z4Hf/CfBNPwL8Wz8Gi1qIkgj+PIQ2TzQ5mRzDASFyU7/u4LwIT8ZkAuSid0WNNC6nY3xlJpOvBB9RFpn0xN/hZJpytfgO5xBhWIguVaplwb5J7bgWjlYhzMuX6+NyAODuwkum6eu1DKsd/J3Ux+V4ECB64xrsBug3oK5xElacTL/3xgGIeRuPl8YBc0+OHxcWBpbxElGNSFGuIA5gRwJ0DZEpjjgYo/J61AuuDeNyYcKxNE7PySTvlxYnUx2TqcRRcQYMqxz42zYNTOxJBv5mnhKZ2kWWtHJjn81o9URP744bwN+1TibXkO3YDZITmb6CTCbD6h+XqzCZjlPn7rplUlLovijjhCeNywkYsBvnlrklHz+Z1m/ey8VXKxDXwdCSY/sRUWPa7EYK/QbQC/ytT5Z7iZknKN2taRXGMk5fcjIBaIR/+xHHRC2LBx6H99RT8uefew5cwdLbODptlYK/S06mvKOSc3n/UKP9OtCbvC5xhFgWRLCGyGRQWLwhLtdyMDIwB7itNuuucucG0Qk3efaoNi4HKLbMSd0EcV5k6n6exsULGziZ6oUHvRZsdzLJayFSLE/ToNl6oc6pGGRxOQBwEoq4Q+RzXOkavjzYEpOpDP4GMlHFnwL/6q8C5x4FvvUnOx9rHRB0n0o7hOr3pIk52vsBi00fsrhcy/PVTibFUi2gPwAZlQNOzcmkDQNdB2u14O/WuFwJ/P3Zvyfn8A/81d7PbT3wd4OTiTsAOBhWCOJku06mcAFYXv2656wa60xkugtqerjC5II6wajpKpQvvTAQYdDYAluDjelgDKaEoPIJahgCzJCLDc0uqYvL7S9rRCYAcCYwmPz9lCd066WKyERtA1bP+zXtfkH9alwOAjh6Azzm9XG542vy3z7d5WzFIuoSmdTzefphCVz+wmty86cjihn4W/4cVxGFdZ1M7MKFwsLcV0Bmy3Rw8ROfQPjKKzj+5V/BNfYgeERheP0grAkXRZHJsoAohMFotml3d4GX/x/guf8F+OAPA3/mvwQIkS4YHsJfxLBNBa/16plM2gnTxgirqyjhCGN+ou5ygBaZSBqX01ESffquJzefyedZAX/PlWjTKTKpxXcwU06mtricWrBvobscoFgzywjmpUv14G8gJzJlTqaoBtCsK1RCT9nJFL76KsA5rAboNwAMrXrw92defR6ECLz/vicKX9dOvXMqxdOHy+QnvozLUb4W+NtQi8yTOpnCmGPJdrbHZIqLwkxnXK7SXS6ocMCcgYkgB/62GcXEmqTgb5e5iMkADH6n0AKgKDKZ6zOZkohDKPdO5mQyc3G50xeZVlFSXCQyp55xkqsg5rDqBBL/aGORqexkknHC9eJytlscG8MkAYWDkIe19/bUlO8vXzMuN7JVdzlBs8/GHqVjfD/w95YX/Q2l5wvpZBpKx4py57hjOZY1Reb8KMFYLYs9j8N917tAHAfLL3wBiRKZ+jTVqCvq6rhcUQDMmEwxOFfuZ6Mec6CrEldpqLW7y+WdTGXwdyoyVT/rgTlALNR7rLvLxSdkojiTlrhcTVR33VKvJ4DV67pkFy8i3l8T/N3gHOnjZNKHVNxX2AFGM4GvbqzOgb8BwE4ooqYmOKqYJefZ+4b3tf5c32oFf/8fPyYPgz/6c1L87SgtYGwN/N0Ul9t0/s65eoE1mEw8Sjlxq3JcrueB5qbVl42agb+zcSj046y7nB5/WNnJNPp3uioAACAASURBVJVC2e/9vIS6j/uLl+uAv9Pucqviz8bCAiMBSOwjiHi6tt9KRUvA9Ood3GfVWGci011QxwcrjC+qE4xlu5MJjAGUyrhcwyZFgyeJ68JUi4CoHJeLKEwlMtn5uFxJZLq5vAmgTmTaweghivs/9TOwH3tMLhbmbwHnixtT5hiwRT/1WoszxKiJywHAnVebu8sdX5Msq1H7oJd2lxvutYhMUxkJVPyRr7l/Asug+Nwr0tGwKoG/jVJcbh0mU3xwUIB+A0Wg9uhbvxXOu9+Ng0/9DN4yHkASkU5oKIDUYTIsiEzSTWHk2Sme5LXgGz8BfMt/JV1qAEzDRJiEWM1D2MqKVo7LBSUnjGMaa7lGygD1TctlLlaEZC1IS6fveiJaKTGlAv6eyWu8q7vckXYyBTPwqIPJ1LJg36R2lEOD3Xu5Pi4HAO4OhnyWiUy03ckU5LoDFr7+4ovy6y0i08gaADTEvORk+oOb8nffs/e2wteZcjKNZ/Ln+4hMYRIqkQlI4n6R2zhKtiYyBTGHb06kmyVp3xR2FWEMiGOIXEdL12L1ItOyPi4n3XMlJ9PQLHSXsxnFjr1TBH8TD4yEsoNiV+VEJocZLd3lakQm9b7H6rloJ5OpnEymcfoik4bkF9xMzAJaYqOAdmXULJdO4mQySHoiLJ/Gek6mYFnvZGIq0lbHZZqyEJwASY+4nBAii8spkWkVBzLyDgD2KBtLe4K/7U27ja1RDjNAiGwaATXX6gMGr0NkCmKOIeQc5w3lwYv75JNYfeG5TBgYDWt/t6uoo+NyRUHTYhRMRcl5rEWmdidnJa7SUMRar7tcgclUEZnUXFFzMKLXNEAuLhfxzbvLAR1xuS1s9NTrCQXrJYaxixfXdzI1zDGZk6klLqfe/yTIdZfTDMc6JlM4S5lMAGAnBHHHMEpNOc9uo7ucEAIxF1nLeH1Y6x8BX/p14Pd/Xh5UPvD+Xo9nb9vJFJfiupYn11+bOpm0y25dJhMArI7AucAyjOudTKfVXU4fqHYcymXg73x3uSTr6qbHH1oWmWbA7/wDOZ9+4yfWem76c+kTg60DkwNAxE2YJATCpeout2WRyRpIJ9NZXK53nYlMf8oriTnmdwKML6jJZXVUAb7mixAic/hB2Bi30C10qevAVC6lMnA0igwY6jRNT5Rja4xZOAMX2Q3W6GRyd0DCY4y+5Vvk/2s6ywGA6TKYIJgtuxdCQ1NOoIT6xbafqcj0CpIm8PfxNWB0bwamrSkhBIIkgGmYkr/TxmTK25JNA1/7wA4+96p2MhXB3zSNyyknk+out4j7O5nyFeZYOYQQXPzhH0J8/QZ2XjqSTKZh9yJ4oSaPvJNJtztmJk03gnj6rwB/7r+X9ubcKb5FJc/HX8SwqLxOmpxMegPS1s63rrRYN+iAoHaVy1z4QHp6pDdGmslkUCIX+VpkKsflUidTO5PpKFGL8nAOEUZfWZHJkyKTefleJMfH4Iuaa8vdwUDMU9HYNuxWJlOonBAWLb6O41/9VbC9PSkeN9TIHoAQgVlQfC9fPX4FAMFD44cKX9fX+OBYXjPrOJkMA73B30nEU7HDyQOCN6gw5ghMJTCcpEMNkPK78m4mz2roLqdPhstMpjCoXHPOwCx0l7OZgbE9xp3gDlbxSsbliCPjcnqB21YVJ1MT+LveyQRkrtlgFYNZFIZBFZOJnDr4e+zK97kA/+7lZGqAGJ9AZDIoKYC/Tdvo7WSaHq7gzyOcu7co7IcJh0FVJKwmMhckIULXBJ/2EJnSwygnZTL5sQ8MtMg0Tl21veJyTULdlotSGc1eaiYTkEbmtMi0anEyDRLAJCtYtrxWvPe9D/4LL6QO0U2dTMStB38DSPmZPJZzLCXt66GwHFdpKLq2yJR1l6vMX1rYaHAy6XKZCyGEdDKd5PNui8ttA/y9ZlyOXZTNKYToD6JucrfouKoxaR479PvPA73e6+guF8wL61IrIQg7XhYxj0CEhV275eC6Z2lXZupkolSOjbdeAn7lB4F73gN86K/1frw0PrU1J1NSjeu656pg8t4PqMbQUlyuk8kEAP4RXru9BBfAA+dyyAR90HNa4G+r38GaaRXjcoILRGEuLlfubskcOWdPrwOf/5+Ad/75iqGgqyglsAza6/MmVHZjLcfl4sQEIz4QLU4e1y1XuARMt37dc1aNdSYy/Smv2S0fEKqzHKCYTM0iE6AWFkHQGLfIO5mY2sBXwN8JS8HdGmg3sSfgghcWr/vLfezau9WTTEed8OtKO8sVN6aWJ//+8XF3VGOgF4w0SOGeAKS1lDnAnVfBYw6j7nTv+FpnVC5WGyPbsKUgNWtg2+Q6Suh6/8O7+OKbx1gEcdZdTsfllMOHb8JkOjwsQL+Bqngz+IZvgPf003jXc88ijBjopebNv6556mTKBmHd7tgwaRY/uv99wPv/UkFgAqTAlUQccZDAUgviTpGppZ1vXS3UBnsrTiYIKTIJkW6MXCOb3G1GsRISfl6Jy81uAiCdE/+dWMflpv27y21NZLJkXO6yFMKit2quXXcXYzHvB/4WIhOZcnG58NqbWPz2Z7Dz7d8u3TcNNbHlNX4cZGLX8TLCNLmOEdurdKMipgnj/Hk4d1bq97o7zKVOJkaQ9NxwyLicvJ76dlppqiBOEFi6g87JInOpyBRm4odnGVhGSXVTs7otYxGseH3VM5lMBf7OeBQTe4L95T4EhHQycUs6mZrGu3wFU8kFMcz6E71W8LdyMoWZk0k7caJEWdtP3ckk37MC/NuwJNy/pWo71XCuWlhvGJejpbicRSuO4qa69oIUNe9/vLgWCBMOUzmZ6uDffuIjcs1e4G+9TqCOm3J2/CTIiUwjBHGRb9dWX6m4HCCbbqRxOSB1sXoj7WSqX28EEYeXcHj0DqA653nvfwrgHPPf+m0A23AyVV0ojuJnCnVZUtIuekZlV0ZDEXM9kcliFKa6d2ljd7kakYllIpPDHESJgBA4mZPJGQMN84Bt0pPDd9O4XDf4G5AikwjD1NHWp5rmmMwV1wb+ltdqokUm1iIyxYGMYeXiclYMhKxdEOPGbZji3FY4aVowZ3nh090BvviLUsj56M9W5qy20i69aFsiU118yju3hbhc2cnUwWQCgNUR/uS6/P133ZubPxYHsiOmNaj55ZNXFs1tn2fKIk4UJoDIxei4ACHyoET+ApEC5+//U/m+fPCHNnp+67iELNsoxPkAIEoYTBIAkWIynSSuW66zuNxGdSYy/Smv4wO5IBlfcOVCOJy3x+WgO4ooJlPNBiplMjluZn8sM5liC5SpzjE5JxOAQmTu5vJm1cUEVLoopE6mcw8Xf0yJTNOWtsK6RqackAn1i8IDpcDOgx1xuTd6Qb8BLTJdAmY1reCBipMJkFymhAv87ut3UiaT5kYRwwAdDNKIQl8mEw9D8ONjsIv1TiYt3mg302A5BY0FjIvd+Xot4JSZTFw7mTpiGxa1IHwFkOZqM9IQl0uZTKz+emyqZVDs0rdpSZGJA4IDcZAxmXKLZYtRhIkAdd0aJ9NNwDufbjrqSgiB20mJydTqZGpesG9SmjUjLspIXy38W4tMahHYKjIlEfQ2LC8gH/2LfwEQgp3v+PbW5zNWtv1pzsn0R28eg9r7uDJ8qPZ32N4ezNvyHunlZIqlk4kxukZcLnMymUYWU9mkgpgjstSi8YTw71RkirLPw7UMCFHTbWV1p3YOEEH1mnOGJkI/SWOqmsmkxx6PeUiEqZxMPeNyauyTi60y+LvNyaRFJvk7+e5oX6m43MSti8v1YzJVFqzhDIA4UVwuzsUj14nLXXvhNryJhd3LRWE/ijkYVdG2mkiNH/uIPSt11bZV6nj23HQMkE4mdejhTDInU8+4nLXNk+WWGtiGnIc1+yVU0G6TwvYYltP6OFoQJ3ATSJFJubPcJ58EGMP83/wbAAAdb9/J5KkDmER9/lS0H0BVmChNf9OywKP14nJWY1yuncmky2VuMyh/nbLHcrzh1XvC3qqTqSeT6YK87teJzKXultJ9nSgnYdu1pN9/EebA3zqqGJWuoUDdz7l1qRkDgdE+Lyb0FgzefmjdtyJeilABmXPnz/w4cOndaz2eFlC3FZeT8anS+OPubi0u17u7HAD4R/jj68dglOBt9+RE6/n+qbmY5HPsf7Bm2UZ66KH/tXJxOZOWXGH2SMaSH/kwcO97N3p+ch3e7742HaMQ5wOAOKHSyRQu6g+GTlLhArAGsrPlmcjUu85Epj/lNT2UC73JRTeLZHjdIpNmMtUp1vm4HFOdyCpOJu4A6hSkTWTaX+43iEyTQhcF3HoJmDyQZcpVuQO56J/PejiZckwmt+xu2X0IuPMaeFwTl+McOH6zt8hkUhMY3ys3jrWAxarI9L4Hd0EJ8PlXbmNZ6i4HyMichq26zAUB6RSZErWYMRqYTHmHiffe9+L1J94n/9awu/uNdjKVRSYRKidTxyBqGRZIIH/X1CJTg5NJiznytLb/4KzFusEJnUwOc7CCur6jZSUuB2QbZup5FTAr5jc7QYxRIjDn6qR6NQWSpAJhLlS8AgxbCqRbKM2aWe3IayW6USOQuruwSYSBije2dpeLfYQkE6MAQEQRjn7xFzH80IdgXm5nOHjqPp+H2Wb2D67dBrUO8O69eqcd27sIckuKS31EpiBYgnHAMI3e4O+8yARkMZVNKog5YvuEHWpU6Wsl32Gu8dSxUWQK/j/23j3Ituyu7/uutfbzvLrvo/vevvMevUYzkhCRhACbh1WOzBsZjAyOeZQflMtxUjbgCnYFpzDl2KacUK6QKocyDsF/RKBEIBLAQABDhQABjIQ0w4w0GkbMfXfP7e7Tfc5+75U/1lr7ufbr9DmtG6t/Vap7daf79O5z9l6P7/p8vz8Qu26XAwD/VFJphiCZVI3MEaKEDctkykSmtuDv+rxjmMoupyGZYi7Q+8+JyGT3yGTS2OUUqXuW4O+kSDIxxD0W/5xz3HzpEI++5VKNQAiTFBZtJpmCJEAytnvRGOowijhONlYGiV/KZBoW/H0+djkAdbtcgRoezaxGksmPUlgRx5gdCsINYl5znnsW8X1hJ2U97Oi6IqYp8jI1JJM6EOR+AEI5SNxOmqmNWL9Mpu5Onapsg8LK7HLDRSaDGjCpmdEcZ/q8r75ZHAz93H9RIyMdk55dfJD3bkzMPEeopRRRPkRkaiSZTuYglgVaFfIKpQ4eUi3JVLmHFFVTIJmMmHeKTCF5ABKf3SoHNJBM198OPPUVwJf+l4Nfb/3d5TTjj3vp7CRTrbtcv0ymF+7M8cbdSXleWZyPyNTnYM0skEKKaCra5WpWXeXu+LN/b+XrG0QyOUa9u1xMCyTTmu1ykSdJpjU0Hfg8qguR6SGv430PhklFlkBD6+pqEVu0rR1ZDSSTsss5LkxXTHJRcaMVB4hSF1w+nyosTnV3mwc9SCZ3G+BJ3j749Ze1Ht3RREyki5PuhdDEyjOZajk9l58Cf/BZpCkHq04ki/sCJe4QmVQ3noxkAvQWEo3INLENPHdjC7/7Jw/gh4mgRwvXwabTzKJACMHIHHWKTPHBAQC0Bn8X64Wv/jbElGWWqbbSB3+LBWmfLkcWs0ClyGRFp4Bp1iiKKnHlmjRrI9unsuDvNWQyeWrTGy6y0/fixsgyxKKVjkbgVZLp5G5n6LcfJziBEFb4Qmw+O0mmNVFMALDtip81n14CCEGsIZkSKS7MIO5Di7Z0l4uDmsh08qu/huTgANt/+YOd1zMyhOB4Gubv5e/ffAWExnjrlTdpv8fc3UWyvw+HOTjyu0WmaCkELMNknaKoqmJ3OUAJn8MXDJxzhHGKxFEi03pIJkRluxyATLTOqqH5gwj+rpNMABAs8iYOJZHJGImcKgPDRSZd+G4mMtXvK7OaybQskEyq9fWGM5m2pchUsssZdj+SqTqvqJPsle1y1eBvWp6HG+rB7QW8kwiPPlO/B6IkhSVJJp0d2098pCMn65TWVoq4oa4rDl44RViyy81qTRTaSgh157PkHNuGOKQwy5lMAOBOrebucnECFpVJJgAYvfvd2d/7ZB7qihAC6jjgGpLJkaJYulyCWujMiQkludlpl7Pt4d3lpNBLK4J19oxogr+VyJSFfmf23DNs8t7xQeAr/4EIjP7QXyl9huskmXgPCg8oiExyXdanmuiWZH7SScQRQkTX0ajYXU6JTBWRVB3m2EWRKUXAmseTZbREjBOk0ZpEJkkylQS7b/gfgO/4KECH3wdKQF1rJlOVRh1dXv2AKKiSTD27ywGSZJrj2RuVe+D0/sZCv4H8fuwVru3USSYlMsUpr1OUW48Cj70XeOrLV74+S3dw1fS1DkNYscvFMRVUdrREECUX3eUegroQmR7ymh+IznKEkFxx78xkssGD3C5XzfRIi8Hfrpi0ilkQyXKOGDYSJgKR1aQxs8skU5REeOA/wLWRZgNeGEzBubDLXakTDBOZkeAtukUmi1qgxABokJ0QZXXpSSSh+L1qdrnjm+LPrcdaX79ECE1viH9sFJnqC4QveuoyPvbaEY69CCOTlU6Z6XRasiiMjXF/kWmnLOIVg79LX//kG/CdX/UDmLz//a2vC+Qik55k6t60W8wCC8TizIgWNYoJyAO2M7tcg32z8RrXmcmkwi4KJFMxR0RRGdR1693lTu91hn77UYKFEpmWQmSqbvhLFXnaxfqqlXXNCjmM3d0spLb0I6W1awpxH7ba5WIfgbx/lUh4+FMfgnFjD5Mv+7LO61HdhpYFy84LB58GADy9/bT2e4ydXSSvP8AlY9aLZFIik2mbvUmmJC6LTE3NETp/tjy1TZwztkGWldvlCiSTtInWwr8bSKY0DGv0gSKZItnJTdnlsp9huIjCBIZl5F0U26ogMjmGRjTuZZeTJJOfZJl8dbvcZmxVMx3JxGzxczX0lSptntCZSSaCpJDJZFoi+LsrWDjLY3qmvg4I44LIVCGZ4jRGnMZIx26WD9hWKjuISpsXgYkwDfJNkD0dbJc7j+5yAPI8yiz4O597R1vNIlMcJKAJwZjmJBMAjN4lRCY6GrVm0XUVcd3skK96vX6UIF0sQC1azrPUlBInrY4W9cQyh2UyMQozicGJyCcsVVsmU0VkWgvJRAjwld8PfN2PAC//X8D/8vXAQoj5jknhn7m7nPgc0r4i064Ume4Pt8vVgr9P5q15TKqIbQOSZBJ2uYbucsouVySZohQebR7T7i7E2jYJtzuvo08pksmkhXuSkFqeZ9/aRHe5ul3usphPB4S5Z+XPAWrmtlqrh8hkzwAQLI4PsH8SlPOYgI3b5YZ01DVthlCSQkrMsWQmU5ikMKoU/jf/OPBXP7Ly5w0I8bjv5206RnZ9qqIIMKVdbu3d5cIFYI0kbXVBMvWtC5HpIa/jfa/QWa4vySQzmSwDnNeD6FTXGOq6MEbitYvkSjQXC5yY0VJwY9Uut++JybbRLgeIxdLiQAQ4akSm6VR2wukhMhFCYNMRCNOQTJeeRMpV+9/KbX38mvizp11OiEyKZNLYjoJ5jWQChMgUxCl+55UH2QZRFZ1OShaFXiTTvhKZ+pFMI8vAA2uCPu6fUykqTqyiyGRm3eW6Nu0WtcBCsQg3gznouC4yKQFDEUPCmrQCybQGkclPY3BAiExJ3S5XJJlKwd9pKib+DpIpiFIkYIip05Nk8ksn5WetrYzQCGFev64VmULZCW2a5iJTs12uTDKFr76K5W//Di598IMgrPvzUJsNdY8/WIR4EAmx96nZU9rvMXZ3gTTFI9GkV/B3shSvbdom0pQjTbsXinGYaOxywxcM6qSe2qoN8roymQokkxx7a80bvAfaDqO6HLBMZFrmGSmzgkA+MiXJZBsDSCbx/bZuo6dyy1q6y0WF7nJ1u5xKPt4MycQowdQx6nY5QG+NlqUVSM4qMlGKqBL8DY5Ogf/miw+wteNierm+2Q8TDofpSabsWZ+Me5JMuV0OAAg3EaVhSWQaFvx9fna5kcVEpl+WyVSwy02txu5yTE6eI3ZUEZn+E4CQlfOYVFHH0QZ/qwNBQTIZvUWmPnY5JEmpa2VbGYzC4RES06yHQWfNKurzljpUUARrnsm0BlHx3X8N+OC/Be49D/yb9wOHr8Ix2dk3eplo1i+Mmo7HII6zUiZTPfj7BKzHvZQd/FEiQpaZBYDUycuMZMpfk0Up/BaR6dbpLQBA5K82flVLiUyMri4yFGvtdjnd+DO6LBwXQf8w9/wFT4RVTj4nyvHRar+XHfcOXxf30LN7hXsgicTc3hHNcJbq210OkHa0Gsmk5usUVs0uNymRdKvUkM5tpib4O464JJm89XeXUyST2V8Iu6gLkemhLs455gcetpTIpLBOzQajWMS2kfoB3AZUV2HwxHVBnSkYwtLDGspTzqBJZJID8v2lyChotMsBAvtu6CwHADPZUjhY9lsEmcStB38DgmSC2ODU7HIZydRhl0sKdrlZA8nEudYuBwDveVJ8Li/dO4Frla+BTaalhf3YHGMRd4lMYiIyLlc7CCl8uioy9W/JnpNMle5yMpOpqyW8yUwYoSSZlkdakqkW/D2QGlmqazxj8LdjOODg8AkBwqZMJkEykfGoHPztHYqNb8fErybG2JyAL8XzkVmgdBX7okvXmioTmbwIxo09xBqRKTDF8ztOxfNtM7vFLlfIZKIWDn/6w4BhYOubvqnX9ajNhpeIjcknbh2DWvuYmtvYdvQnp8auGEdu+E4vkimW2VmmzJXrY5mrkkyr2uXUIsM2mAiFXx4Ofo1iZSJTYTOofZ7TVEsy8SQB4rieySTtcokfw5Ltm6t2uShMwRy7p8iUC+y2odnoZd3lmkkmJWCHyxi200QybUZkAsSzoheZmi1z2jyPM4pMJiOIS3Y5aUUImu/jNElx69NHWqscIO5Lm0mKsEIyqXEPkxHSk5NOYionnsU4RYmFiBfscs5sIMl0jnY5yxAkbNZdLp9rR1sWQj/RWsIN+d6P6IOSmMK2tmC/+c0rd5ZTRVxHG/ztSvIqXSxAnfWJTIqmHUIzuTxBohNeMlGmPm/V7XKqacyaPu+3fh3w7T8rum/9+PvxWPjyGkgm+fv0yBMDxCGnsbMzLJMpo1uqwd/zXoIlsSyQKMxtkYToGxUEkkyUm3yepqBJiiVtXlffWYg1gh+sR2TKgr975Fv1qfV3l9OMP8oVsoplrtJl2mAUJiPwu0QSdxvLY3GAXLLLLaQNc5N2OaN/8LdZDP5WmUxOi11uDWUx2tseaRXsfKqikMtMpoW+WceqlcQit9Ea67MoL6qxLkSmh7iW8xBxmGK2M5RkssCDIOvKtazYLVSwMXUcwJ7CIAFiPz/FDU/FgiygRmmRMDbHYIRlJNO9pdiUXBt32OUykameyTQZmQjBMztHV9lkC8Q4rU8W208gkSST1i5nTTs3AyVRxL0kbBTzCskULUUYpUZkujy28KZdMcmPTA3JVLAojM2xNjOjWPHBAdilSzWxoplkashw0dQiiGEbtOSfp1JkMnqITBa14ERjMel4C9BRveVqkARghMGQG0YhMvUfnBdyE1ALeR9YGcJPSNkux/LFckYyuRWR6VSKjB0ik/q9EnMCLm1crSRT5G/GLreMYF7fQ3T3bm0T6RtKZBLPr0nNVpIpIIBFDPAwxPFHPoLp+94Hc7cfyq1Otn0ZUPqJm0eg9n28ocEqB+SZF9c8q5fIlMjPyVIiU497K45SGEaRZKIriUxqkWEZVCxUz0gyKWtKkWRydM9zeCLGn4plWm0idd3lACDx8wV2yS7HXCRhAtOxBbGn6eRUquC0I/hbPqsdJFMSpUjiNLPLhUkqNlIbzmQCWkSmlvDvdrvcanYTg5aDv80K6aWr+589QeQnWqscIDZkjpwXmkgmOpkAaVq3BVeKV0QmBgtxGorOQX/2e4A3vA9BEsAgRjbGt9XaT5ZbKmt6kgV/lzOZANQsc5xzmPK2EHa58vy6+z1/D1f/9t8+03VRx20M/vbDBOliCerYnSJTqKxJfUgmDBOZHB4jMTQHJFnwd11QrNvl1kgyqXriS4C/9ksANfBtz/8tfBE+eTYBouX3aaqhIlNGt9RIpr52OQuIwnK2jOlousvJtaUUVdXnHRhp40HS7dPboDAQBZOS2L1qaYO/z1AZybS2TCYNjTo6g93drzsaHKMHGe1sI1w8wKOX3OxwEEB+yLNBuxylBJbRb81jOjkppLfLredzLpZt9g/+1tnl4jCFQUPwYLleu5yaS01Xrnsu7HJ960JkeohrfiAmktlVuRn1HggPsNV+mkYtYZdzGjzCKniS2DZgT2ASH5FXFJnEA7WkRolkIoRgak0zkUmRTPpMpoJd7vWXxXVvPV77spFlICAYIDJdBjOP6yi3NUI6Eha3ul3upqCYOrzCmchELfG10+t1kik7MdIvEN7zlJi0qsIImwqSSW38+wZ/q4237jqrIpOy6CkCqK1Og7gU+g0AxCx0l+sR/O3EE9hjQyD+GrtckASl3CjXZAiTtJRD0lZemIBRcuaJIqNqKAHCBbzYK4lfQLW7XGEToD7/aXcmEwCk5hip10Nkiv21Bn9PbAOMEhx5Icy9PfAgQHJYpms8JkQmN85JprZMppAQ2NTEyS//CpKjo16B36qyzYYkmT7+2hFMZx9vvNQiMkkB68op6SUyKbGcOVJI6UMyRSlYYUxbNZMpJ5moWKhuIJNJSzKpE9cqyRRIAaHSrci0GJhJkfp5d7QiyWRTB5wDbDQWxJ7XQmRVKE7bYIhTXt6g0Da7XJ7JFKiMKGmXixMuNicbzmQChCB7tCzc96wHyaTL81BCgCafr08ZjGSn/0Dx/Wm+H2++KD7/R96iF7aiJIVjWLCo1UgyUUnjqEYUTZURz454lg1iIeGhsET++f8GcC/Bj/1eod8A1nuy3FFjW5JMhgOAlEmmmV5kCpMUYzk1jdhhbv2UNfmKMv8Z5QAAIABJREFUr8Dsq7/6TNdFXVcb/O1aDEtll3O7RaZIidw9RaZ0iMiUJohbRab6vKXm2I2RTKp23wr89V/BwrmOnzD/OZJPfGT115IkExlw2GPs7AwK/lZ0Sy34++QEdNYtMlHLAqKoLDLpSKZK8LeaDyIGeNVOdLJuL25jYlwBsIZ8K+TEUS2rZ8U6F7ucmkdXIZGDee3g2rFYtwDhboN4R3iuGvq9kOLleHMiE5AL2l1l2gxhQ/C36C63GZJpSPB3EqVI5H2XphxJnMJkKRI53q9N5M5EppG06l6QTH1rozM+IeSrCCEvEUJeJoR8v+a/P0EI+VVCyB8RQv49IeRR+e9/jhDyscL/fELIBzZ5rQ9jzffFjb1VJJncS51iCbFtpEHYmOmR+j6I44BQCtgzmCRA5OWLkPBUTEonxKwtEmbWLCeZFvdgMzuz0ZWqape7/BTA6qedjknhE47E77fRs/glEPNIi/on0ycAAMyskkyvdVrlgEp3OQCY7gEnFdtRJjLpNxbvlSJT1c5HJ1MgirLJf2z2Cf7er3WWA+o2NFVjS/9562oRxLVcK2JZSKMQhskQ9wj+duIRrBETiH8DyVTs4KYshH039YswxshidUFxYKmFr6dIpsSHzezS66quFtR1KySTOl3qssuJ94vbU/DPgchECMG2a+JoGcG8sQcAiG6X712fOIg4g5uI57c9+NtDSAhMauDwpz4E8/HHMf6SL+l9Peo9D1NJMt29DU6XeHqrRWS6chmgFNunHPNwjpS334MqQNcYSjIV7XKrikxJgWQanZ1k0mcyKRK1cH0NNGvaQDIBMpcpyBfYjuFkz6WVij9N9fy2WebiQAhRWXc5zUlzS3c5w8y7y4VSZMoymR4Ku1wbyaQRSPxjceCjmdf6lEHLwd+ZXa4l0+Pmi4e4+tgE7kQ/toRy8T8yRzWSSVnbVBZMMm/PIVHEDZW5jYzYSFAmH9VY2qfO0y4ngrRTJBziMypmMjWITH6UYpwSgHA45HStmXmqiOtog78VeSXmUlfcWy12RvXMmV3B36YimbozL1VZPEbMGkQmamjv95pdLgv+3oBYvPUIfvm9P4GP8TfC/tm/AfzOv1rtdRTZN0Rkunp1EMkE1OcYzjmSkxOwaQ+7nGmBhGFZTNTa5VTwtxibU7nODI2CTbZSd07vYNsU65pVcgmrpcayWmv7FcvaRHe5JrvcKodEmgZAjkk738vY2oIdn+DZPU3oN7BRkgmQe64eayWVycQ513eX24DINCT421JWe7l3VIczhpEiDcTeam3d5dQhhbTLdVoiLyqrjc34hBAG4H8E8NUAngXwbYSQZytf9i8A/CTn/B0A/jGAfwoAnPNf55y/k3P+TgDvA7AE8MubutaHtY4PfIAAsyuFTKaOPCYgt8u5DRk93PcyBF7Y5XzEQb4IiZZiUpoTM/PwqiqKTPeX97E72tWLAGrw9Y8aO8sBYnMcMSDtk1YNgKWXABJpSYdkKrrHsSaSqaNq4s1MJzKptqUNJNOTDSKTOj2Wlrmx0cMut79fC/0uXmedZBI/c1HtRqWp0yDRikw8jATJ1JXJRE040QTmmIrTV00mU5iENZIJ6Bc6CADLIDlz6DeQZy95hGZ2Oaci8NiF4G++gsikFpHcmoL7YkJSC3xtRd5aRSYA2BqZIpPpuhSZ7pStnkHCcYQx7KggMqWhPpslDhAQgsdfp/B+/w9w6YPfIkTpnmVQAxQmojTA/kmAff9PAaBVZCKGAePKFUyPI6Q8xUnYTlooIsBwxXPQRTJxziXJVAn+XsUuV9xEja6s3gZZlr67nBq/C8+zEplGTXa5+sbYGZtAWF5gz+wZCAiMVObYTeR4dnq39v1ZVShOZQUpneplIpMm70a+73GUk0xKZArPVWSyVshkarDLrZjHBAjKIUoqwd9oJpmiMMGdV47x6Fua7fJRksIyKEbGqEYyqXnDkJvbtCP8O7PLyeBvk1hIeFmsCOKgV+i3+Nrzs8uNigG31qjcXW4m7YQVkSmIEkxSAsNOQAgvBX+vq6jjNgZ/B3Eq59KxCCMOmz+foSTTELucncaItCRT0Dhn1TOZpF1uQ+QacbfxHeH3w3/DVwH/7r8CfuUfDe8QJkkmOtAul56caIXCpqqJTL4PRFEvkonYNkhcIZlMt95dLjwRWVlSAFSfd9xGMp3exiVbCBqrHLRUK5ZUprEm8SETmdZFMunsuqMzZDL5cxH8XSi3RyTEYTrCFlloSKbzEZn6rnlMmwFczNehLxqm0EJO1rrExGINCv5W1j1p5cuEMBNIM5Jp3Xa5EWyDIUp4b0fG53tt8ljpiwC8zDl/hXMeAvgQgG+sfM2zAH5N/v3XNf8dAP4SgF/knLfvyP8jrPm+h8m2nW+KGlpXV4vaTklkqmb0pEsPxJWLBXsqSKaCxSqUVNMht0p2OUBsTk7kZuPe8p4+9BsQdgd7SwzeD17R5jGpihkB73mSQhLx+6v2q8VKxkJIYqQwyIdLQRn0EJkU1ZEJI9M9YH6nvHjpsMvd2Hbxhp0xdqblxZjy3ycnYuHYRTJxzpHsH4BpSKbsOmmVZFItz/uRTBO7/NmSSiZTWzCsIpkMlzTa5aqn3MoP3/fUbBklZw79BgokE82Dv6sbo4xkGo/AoyhflJ/cEyfhHV0z1MKC2BNwXwxV7SRTsNZMJgDYdk2RySRJpvhO+RkJohTHfAI7FlYMi1pIeYqYa0RJaZf78v8QAKbZO/C7WAZxEHEPn7x1DGqL09+ntvSd5bLv2d3F6Fgs4Lssc6pLpumK97krRyyNZfebavB3T4G7WGEi7uEsk8k71AorfSsjDnR2uaibZGrKZAIAZ2KAhry0Udmyt+AabvaemBMplqjTVF1lArvqLift2MVFIaUAoVq7HKFENBUokEx2gWSyjPPNZMrGN7Vxbs1k0tnljs4kMlWDv7syme6+fIw05o15TOo6G0kmKaKZM3HNxYxAXaWeDxhGJoAa1EaKCv3Tk2TinItQ8nMjmQrWcWtcssu5M/H7eCcVkSlOMeYEli3f/w2QTLQp+Fs+S8liIahnoNUyFyUpCOnu5KUaAfBogMiURIh0dF7kNb4nTSRTdf24rnJMhgAWbv2n/xPwru8CfutfAp/9rWEvEvsIYdZzelpKxRcMCv+uCA/JXDx3bNY9dhDLAo2jCslk1zthBqelNQrvIJmiJMK+t48rjlgrrCNjRgnm5pqyeiglMChZr12uKnqqPL02m3jjCx7XM5l6CDj3IgczLPDsXmUPcXofMMd5jtyGqi+9nVnj/ARRkGSiDpATs+sui9Hen3fx+oACyWQSpJJcXZ/IJIVac5QT3Bfh371qkzP+IwBeK/z/m/LfivVxAGr38hcBTAkhVypf860A/teNXOFDXvMDD7Orhc2wd1gLfNUVsW2kYZhtUqr+29T3QWXOAqyJIJkKX6M2AIdp3S5XzWRqFJkAwN0SbWeToJFkAoDEICBhP1U4jcXErBOZ0rHoCEeDgmd+Ltq0YuuxztfOurYV7XLRIheWgE6RCQA+9N1fgn/4Nc+U/o1OyjkYI3OEMA0bQxnT+Rw8ihozmSxq1Qiy0RC7XKi3y6lMJnAgTVpEJioymYwRGu1yYRKW8jrUQrrvqdkyiDGyz75I1dnlHFYlmVhmlwPyzko4vdurpaxapBFnVhCZ2rrLeWvtLgcA2yMLR14owuJtG1Glw1wQJzjCBGYoNi/qPlddFcvXFyCNCd71h0vM3v/+WofDPmVRBwkCfFyGfjvMwfVxe7aVsbsL+1BsCDtzmWSzAjYWn2UXfRdHahFSJplWOcVVmyiLUUEygXfmqLRVRjIVbC2Ozu7cJDLJTUW1uxwgSCYalUNPtywhMilrFpvK16tm0BWrMvbZOpIJEAKR7p6CoHXiMK3Z5eKEiyBRJU6taEHrU1uuiSjh+WZAHSo0kEycc32eh3+8ch4TIIO/B9jlbr70AJQS7L2xeXMaybBTHcmkNpvWlvis1YFHU6VF4hlizOekQjIlQc/OcpL8O7dMpsKzY47zk2gI0tkZm1geV+1yCcYpge3I33EDJBNxXC0FM7IYjDQWhEsPkSlMhF2ly0q+CslkpTFC3e8eB41zlsUsjIxRlvemhOdNiYpK8A1SAH/m74p/PPzssBeJA4SwBtF1xq4SmfrnMlUtVOmJWD+zXiSTBVrLZHL1mUxWXWSKDD3JdHdxFxwcu66Yj1c5aKmWCv7uEj6HlMno+rrLxUmd/GOGOCgYapfL8gmrdrnu9cRrng2bxNgbV9bXp/eByeY6y6nqI4QBOSkUBTEiP85EHWCDdjlzSCaTPKDKcqOkhdgimT16iIDcWpldbpSvey4sc71qcyu5fvV9AH6UEPJdAH4TwC0A2SdHCNkD8HYAv6T7ZkLIdwP4bgB4/PF6qPT/3+t438MTbytobssHwN47O78v6y7XkMnEPS9D4BXJdFKYZBR+eMI5Hq2STNIuxznH/eV9fei3KmcLuPMx8fcWkYmbBNTrN7DE4RZg5O1Xi5WMrwOIwJb3ALxN/OOx1DlXsctNxSkPTu7kWGwPkWlnWl905ySTtMvJk79ltCwF8apS4ZLG1fqkEyahdmGvRKPv+/DH8Q8/8onG6wOA0zDG1759r/RvxDKBJAGTH3kSpWANi0QTFqyEgtopuO9r7XJBEsCmOpGp32e9CONal75VKhOZmCWCv6nXYJdLQOTvkXoe2NaWIJk6Qr+BIsk0lYSN1U0yrfmkfNs18al7JyCEwLx+HdHdqsiU4piPM5HJlLkbYRJm3eDy6/Px+MsUrp8OCvwulkkdgIT4vVcfYDJ5gKe2ngIl7QsTY3cX7A//AABwHLSLNkQ2KzBHUmTqWJwo0qkqMnmRyB0Ykv0VJIVNc4bcv97LzqwrnV1Ohd73Cv6Wm0jakMnEYl7a8G3b2xib40zQMEYjsRlvJZmqIpPc6NU6zJlakgkQuUxxmBTscgxpyvNF6znY5VQnxqNlJIgXNRZU6QBZYdIgkPjH+RyxQhmUlDZQXXa5my8e4trTs2xxratIig8ucRu7y9lblxEh3+w2VWmdAMBiNjgJS89KEAe1sVRXmch0Xt3l5LyxCBXJVBbU3JmF5Ykmk4kTOE4EBNiQXc7JbIjFckwGR2aC0ekWMEcnydRllQPy8UCJDn3KTCL4VENTxH7rnPWv3/+v8chUnCNvmmRSz6IfpcBVedC5aBm7dBUHCGEMEj5XJpkKG1KVhUb7ZDIpksmokExBhUIMTkokU9ohMt1eCCv9tZFYM68jYyZas10OyLv+rqPCpsYD7uXhdrlwIbq8OnWR6XjZLui+cirGJuIdlamlxf1eB5pnrb4Ha0pUCv0EoZ/ALMw7UZJi1jIPrVoWY71FpqpdLiOZLJodKmzKLgdo1j0Xpa1Niky3ABTxkUflv2XFOb8NSTIRQiYAvplzXjy+/iCAn+Gca49FOec/BuDHAODd7373f1QGyShIsJyHdZJp1McuZwNJAoeKt6Rml/N9kJHKZJrBIAHiwjscBSlM6sGPHH0mUzDHcXCMMA07RKbt/OFsEZlgUrAkQZqk9c5wheKc43ThgIwY7i41djn3GoCbYMvC5vr4pvhzFbvcrCAy7bxF/N0vW0b6llpQpAW7HAAsooVeZJKLmKbg72roNwDsbTn4/q9+Bvfn/RaU3/DOG6X/r0QRRsR9E0cprAbYhgYmgATUkAvjsT74u3idasHZO5MpTHB5fPaFvtoE+aYtSCamz2QKZCYTgDz8+/QesPcFnT9DTdrMnSKKxaSn2/BnFfkiW2GNtTUSdjkAMG7sIb5dJ5k8jGEErwLI73O1+SxVHOC5TzAc7Np45j3vWel6HOaC0BB/8NlDTN94H09vf3Hn9xi7OyBHc7CE4dBvR9hJIO49YyQzmTrES/XfS5lMFkOSckQJh9URolusMsl0hlwHWYp6K4pMgCAcaiSTNa11vkozkkmTyTQxYcSAXchQ+Jvv+Jt44D9AfKoWZgyYXhuUydR4okeNZpHJElbczC43MrPNibDLnU/wNwAcexFubLuFTCb9uNkokPjHwM4zmu/oVwbrH/ztLyLc/9MTvOdrnmx8vSQVOREmoxjRUe35UcHf9uwSIvQgmTwfpEAy2cwGSCxbQ8uxPPGyzmJtFWyYbKlWiWSyxjXBZjSzaiSTF8QYcwLXDoXItMHg76qo7VoMrsoIml7qJTL1yURZiWRKIgS656+jWcXbd96e/X3Tn7dalwaR/HzNMXA6LJAbcYAA5qBrVOuxISKTY5bb2iuRqQ/JRC0LLInKn7XpAosKSRWcZqHfAMDl3BgaRGuXu30qRKYb4xsAPruW4G9FMq0zq8cy6FqCv3MaVSN6upeGk0wN2ayuSXGvZR0SJSleOjYABmG33iqYe07vt++T1lSOSXFw2p3batk5KRQFCawCybQpu5xt9hcV1WFL6EmSSd7DpmWsX2RSjSNk8DegIbgvSlubFJl+D8CbCCFPQYhL3wrgrxS/gBByFcADznkK4B8A+DeV1/g2+e+fdzU/EKcPWWe5yBMWmx6ZTCr81eHioavb5by8s4U9gUl8FPc2YQBYNIQfW1lHMFUze4aYx3h1/ioAdNjlpN/ZmrQq9MRmABKEXgJn0jwo/O//4Rb+5MDDo3tX9XY5cxvATdBFQcs8vgmRnn6j9vXVClONXQ4QuUyqepBMumIy+DuRp8eKHGnKZVI4dlPwt45kIoTgb31Fc/ZVV9FMZBKDZ9wiBtFQikyQbbEbSKaJmZ+uqXtpiMj02KU1kkyGDUQefO5jq5KlohYzalOVLgoiU4/TJXUSaLhbCFOxyGonmZrzLVatbdfCSRCLDcj1PSx+q5xREUQplnwCGpTtcuq+L5b/yk3cuEvw7//iDXzZit39HOYC9Bh+7MPE63hq1p7HBAiSCQC2F912ORpEiCwKQ+av9CWZSplMBeFzSCcStfAVduICybRiZSRTXBWZjLrIpDloUDa7puBvCmBE8kXis1dED47PflJcs2FRcZ8PIZlMJTJVSSbWIjIVSCYCWDbDQo4HJiPnQzK5OckkLkq+ZzqxFcWQ980Gf7dlMt3+1BHAgUff2kzKRYWOhyNWz2QKpIjhjGdYmGZ3JlPFLmczB4SE8AsBukEc4LLdTe81vocbqpJ13BoB83IThNHMwr1XyyTX6bHs/OoEQuTZUPA3kgSIIqAwP4yKItPsklgxe83jX9aNsaPUHJQOEJmMJEbYJDL1zBH0oxSUCFpvE+VUx57Jzgokk49goF2OXb4MMDZYZDoq0C3quaPTHnY5ywaLI1jFazQcsX4oVnhSWqfwUJJMDcHftxe3QUBwY3IdwGfXEvydKJJpQIOQrhIZPWfnBzIaVTf+bD8O3Pn4sBdsOGzusqK9sr/A64krRKbq8316H3jiS4ddxwrlWgPtcn6CyI/hTPKDrc11lxPr8DTloB1jh1Ww8wFALG1zhs1A5CHx2rrLZSSTm60ZL+xy/WpjMz7nPAbwdyCsbn8M4Kc5588TQv4xIeQb5Jd9JYCXCCGfAnANwD9R308IeRKChPqNTV3jw1zH+2JiyEimLIujXyYTADhywV6zyy09UBX8bdgwWIQ4zm+FMCSwjBB+lGhJJgD49NGnAXSITGoBfuUNQMtGldkSfV42t9m9deThB3/ueXzRk5fxxiuP4d6i3mo7UREbpzfzfzy+KcQiXUveSimiw6Tya5VNqthhLpiLSd4YtgBVC4qMZDJykklXmV1Ok8lU7dq2riIVkamVDPHkfUFUm+vu7nLOgEymw0WIVw8WePTy2WkfddLuGcIu5yc+XFZ+Xdug4BzgjspkWorTwfBUEB4dpTZRzJ2BJ1JkMhvuuTQVIcNrz2QSP2/uRTD39hDv75fImCBOccQnoOEJkERZcHyoCTw+/PVPImIcL39xNwHYVI7hgtAgC/1+eru5s5wqdb9fOaWddjkaRIgtllk6u4K/k8wul49pQ3PCVKnTNovJ7nLAam2QZRFDdgWqkEyuVUHbvQfagwa1qdAHf4v7wtU1EczCMpnYpLRmMpUX1tliq/q+s2a7nGnJ4O9lDMtmIDQPvzYZBZLNi0yzAskEoAfJpKEy0lS8H2cJ/qYk68gkLqPZLnfzxQcwbIZrTzYTtGrDbTKiz2SSJJNruKDTaXbg0VR8WbbLOcwBaCzoEfUzk4F2uQ3Zp6q1PRLPwcFJIA65KvPsaGbVusstJNk0diT5sQmRSa67qrlMjsngyHGYXpJzfgvJFPQkCVYhmcw4hK8VmZq7y9WvL4FjskEW5CFVW0uMd/NOsH0rCRFwY5DwSSiFceXKGYO/FcnUzy7H4rgS/O00BH/nolXRLqee+2LdPr2N3dEuJrbsvLsGkSl6iEmmnEbVfNa7zwKHr5aaA3S/oBToK+N/lxXt+dvHOOaS+vcLIlMSibl9vNnOcsAKwd+SZDLtsl1uE93llCjU5zPP7XIVkskxQKWwujZ7dskuV7DqXlRnbfRYiXP+C5zzN3PO38A5/yfy3/4R5/zn5N//N875m+TX/A3OeVD43lc5549Iyunzrmokk7Ji9Mj8UOGvqitFdQJJfR/EyTe4pgHECQOX6H4YUZhGIkQmTSYTALx8+DIAdNvlgE4E1JCDRbDUb0zSlOPvf/jjSDjHv/iWL8De+Lq+u5ycSNhJIW/++LVeVjlAbLZNaua5MdZYdMg7qZBMAykmQNrJCMmCv4uZTLqKD/aFH19z2tU3bHVoZSKTjEVr3bQH4jOjqZiYdXY5Py53HhoiMv38J+4gTjm+4Qu6CbSuUpsgzxAYrR/X7XJqcottKTItl/middIjkykWJAy1p1D7xkaSSeHr6+4uN8o3z8bedYBzRPfy010R/K0WOMeZAFgVmZLTBeb/7yv4o2cAvtXeVa+tRsYIhIZgtriGp7e6RSZTkkyPBqNOkomFMRLLyMikLpIpGx9KdjlJ1w20CyjhwTIqmUwrli6TCRCL1mVYGBcbOoxmmUwNwd8A4Kb1RWGWU7UKyZQttjR2uUQ/ljOTZcHf1kji7kkhyyMjmTYnRii73FyJTKynXa44F4anMpPjbCRTXCCZmEEBorfL3XzpEDfeuN2YkQfkJJMK/q4SDMo2Yxs26HSSHXg0VepX7HKGDUJSnAb5eNG3u9x52+Ue2XZBCPDa4RIwR7UN5GhmIQ6SLM8DALwT8flPbDk+b8Iulx1ilDf+rskwUiTTlqSXW+1yvNd7mYtMzQd41TKSCAHRPH8t3eWqpQ3KX2NlY4+iCSa7K9jlfPjcHBxGb+zsID4YQjKV1+CKZGK9SCYLRhKV30vTybtdqaoFfyu7nJ5kurO4gxuTGzkRtoYNc7yJTCYmsjLPWhlJqRO5rz0LgAP7Lw54QflsDiSZXrg9h8fk514kmZT9cfLwiUyhH9e6y0WbsssNyDvK7HxZdzl5/zlWQWRal11OBX+PCwT3BcnUp85nxr+owTXf92A5DPZYqscNXYV0RSXJxINAoJFhecGf+uUTSgXlKCU4jA1YRoKUo9ZdbiYH1c8cfQYEBFdHdTtXVm5PkUl2GAobRKaf/O1X8f985nX8wNc9i8evjHB9fB33lveQVFqGp3KRTU8+K7o/AIJkGiAy1RbMs721iEyEUtDxOMvByDKZYv3pSXJwAOPqVe1JYLVr27pKLUiJFJnaSKbUk/dFItHvBpKp+H4qaqTPhv6jH7uFN+1O8Oze6t2bVFFCYTNbnM6GsrtcLZNJ5qGoVvKel1MdPUkmx6CAPQXvssspkannqXDfUpvnIy+CuSfEubgQ/h1EaX6K5h02ZjLNf/7nkQYx/u8vIGcSM8emC5AQVy8dgRGGx6fdzRmUXe66Z3WLTEGMxDazjXd3dzm5CCksPNyBOWGqwuLJqDUR1MNZMpmaRCZdJpOGZs26yzUEfwOA00UyTa+JxXN1A6MqOBGh3nLsaQ7+brbLmTKTKfBi2HLcVyfg1nnZ5VTwtyfFko7gb63VSwkAZxGZKsHfhBAYFqvZ5U4PAxzeXeLRZ9rn/jAjmShGphCZinNkkARghMGkJthkiuS0wy7nle1yrnyf5mF+MDI8+Pt8lpyOyXBt6uC1B54M/q6LTADgFcK/g7l49qb2EgDZyD2oSCbul5+xkWXAyTKZZmJMaROZNkgysTiCTzUUbkt3uWr5UbLRkPcaRTkebpdLIx8+zMHXaVy9Oqi7XJVGTeYnIK7bbqeXRWwLRhJ3d5erkExd3eVun97G3nhv5flPV2ocX6dF0jRIyVK8arWK3LvCOo57L/R/QV+fyeSYrFWwe/72HNd25XqySDJlB5qbF5ncSkZYUxW7t4V+OZMpSvlaxcTsZw7o3MZMCspIZpdTXeZM2wKT93x1/7pyRUuAMIBZg4Swi7oQmR7aOj7wMdtxc5FBWTH62OVkLkcaBFn3pGJxzwcdFUgmU/wMpQSHsQnD1HcHKdrlLjuXc2uZrnqSTGqzobPLfWb/FP/s372Ir3zLDr71PSJH/vroOhKe4MArT/SJ9G6z8FBsxtIUOL7VW2TSBmpPr9czmVYQmQBhmVOnWN2ZTPtgmjwmdZ0bIZmkwMJ4N8mUevK+jMRCmI4bustpRKauU5Sbh0v83quH+MAXPrI23N41XCwZA6KFIJlYE8kk7QwDSSZlD4A9ze1y5ywyKYvI8TKCeUPkiUV3CiJTnGJO5Gmnd5h9NlGaP3eccxz+1Idg703w6UfomWyZY3MMQkOMxg/w2PSxrJtdW6nMi52F0WmXM8IEqW1k3eI6M5nkwooVcuaGhtGrUgsMy6DCCuxe3gjJNKrmJyz1djmVuaIN/pYik6V5e7LTP0UyAc22EzX2yWeyOfi7pbuczGQK/RiWEpkK4sh5iEwT2wCjpGCXk/d4UyaTboOirINnIpnKwd+AshOWP6hbL4m5v0tkKmUySYtw0SrjxXlXTTqbIp23i0zcK9jqAYzX2V9SAAAgAElEQVQkeXni5yJTb5IpE+rOxy4HAI9fHuG1B0shMsU+UBDcXCkyLef58xaeir9PrKUQUjdg9SKO3i7nmoVMptFI3Fddwd89GhUMFZk452BxBA+az2lAjmAQp+vb4GmqRkVPdsXY2EBQ6orHPgI+LPgbEM0pBgd/l0Sm414UE6BIphilt9KwyyJTmgg7aJFkkvbp2CA1kSlJE9xb3JMk02p2cV0pKtNYp12Orae7XKvIfelJIdzdHyAyZXa5Kskk7H3VcR0Qz9YLd+Z48pEbAEiFZJL307nY5Sj8OAXn7eJdZpfzkzrJlKTiUGjNpT6fIeHfYUYyyQOzkQ3KY5iI12iX8wQRS0jzuueitHUhMj2kNT/wsFXtLAf0C/62VdvasNadiHOO1PPKdjkVOCqV4CixYVhiAKripUpkeuA/aM9jAvKskqtvav0ytQnyF+UNVpyk+J6f/jhsg+Gff/M7MsFhbyI20NUOc5kdhsTCY708EBuHrcfQp7RZR9Mb5ZyS4GRwZzlVbDLJTo+L3eV0Fe8faPOYGq9zDaUWpJR3k0yJB0Q0APxmu1xNZLLUhr59Avnox0RI6zqsctnPNlx4hIrgb41dLpvc5CI6XRRFph7B35Fsj2tPoQy+jSKTIkXWLTK5OaFhXhfCWHS7KDIlWFJ573pHmUBcJJn8T34SwQt/jO337CGkNMttWqW2nDFAQyTGXTy11R36DcjMi50dXDklOAzau8uZYQpuW6Bys5VE7YsmNT4YZp1kqjZH6KqwumgdXcnH6FWKMYCQ9u5yaSpOP3V2OWmPaMtksjT7LxXuL0QmlUHXITLJag7+NoBUb88xLIooTBAsCyJTMZNJCQEbFJkIIdhyzULwtyKZ6tklQEN3ubWQTBRxykuLfcNktUymmy8ewpmYuPpIu3W1+D6qQ4yiHbs4HrPJNLNuN1XVLjeS79OpHL845zVLdFNlQt0GhYdqPXrZFXY51Sa8QDONMpEpH/uiRYwl4bBJmFso11yKDONeeeNf7C5HMpGpmeQMewZ/06EkUxSBcA6PNGQy9eyIummSKbfLFUgmcLHm61k8ChDCGBwObOzsIHn9dfC4n6BVpVvS+Qloj85ygPj8KDhsWpjbTFeI8UpQC6Xt1c7HB5XJxGy31l1u39tHzOOSyLQOkineRPC3sSaRqU3kpgzYfQa49/yAF9QHf7cdpN468nDsRXjrI9tCnCqRTJLCm+jX/Oss18w76raVIoW80xA85ZnoBGzSLtewpmgo02GZ5TkKEhACMFvMfS6C9drlLPG69hotpp8PdSEyPYSVphzz1z3MdgoT+oBMpswuFwb14NgoApKkdEJpSG9rHCYA5whTG4akm5zKQzottEm9Nu7YfL/la4C/9D8De+9s/TJHbo59vzxp/6vf+Aw+/toRfugDb8O1WX69KgeqmsuUSpKJIhIi07HMZjqLXW56XbT1VkE7ZyaZxIJAdV1rzmQ6yNrlVitIAth0k5lM4nNoE5niJYdvLMBlF4em7nLF91MN+G0LGs45fvYPb+HdT1zCY5e7W2P3Lddw4VGKKFwi5nEjyRRa8qRZ2eWo2euZy0LyFcnEKEjTYkvZcjaUyXS0jEBHI7CtLUR3yySTZyiRKSeZiplMhx/6EMhohK23byEgOBMxd206AyEch9Gt3iITICxzWycJjltO8jnnMMIUqWMVSKb2hbK6n4u5NrnwOTyTiZJC/sTojCQTIYJmqmUyGTnaHhyLHCDN/ag2kbrucpbDkILD0HTpicMUIPI9Uah+K8mUL6rtYhvxYjGzRI0UyzAZkkhkMlXtcqXucmSzS5Mt18xJJmpAhiFpvzbPZFqvXU4Fp8aFU29DBqOr4pzj5kuHeOTNl0A6bChBgQhTHTWL4d9BEmTjngj+7shk8jzRDU3WWC6yTwIhkERpBA6e/aw+13ZedjlAkEx35z4iJueRwlyb2eUK4d/JMsaCcDAe9moUskqpmIJUKzKJa2HjcT+SaZBdTk/pVUsRkR5hSKs0Ruw/3CQT0J4pV604QLCKXW5nB+Ac8YN+9mjXZAiTNGtwkJzM8+7OHaXGc7fQICAXxeU9FMjnWJPJxBynRjLdPhWHeDfGN8AogcXoWkKM4w0Ef5tsXcHfHSL37nPA/T/u/4L+HKI9aln4bxPtXrgthKnnbsyEy6NEMsn79pyCv5uusVqmzbCUDRGUfQ7YnF1uOMnESplMhs1ALDEfuQjW211OHtxc2OWG1YXI9BDW4ihAGvM89BsQp+SG0+skiRQzmcwyyaQwbVLIZDIdsaCKggQ88hByF1RaSqp2uak1BYGYRFpDv8ULA2/7pk7sfCwDYL2CyPT87WP8y1/9NL7uHXs1ouX6WJy4V0WmGsl0LLvMDbDL1ex/sxti46NOyIL5GUSmSWaXs5gFgxpakolHEZLDQxhX9acamwv+Fr87lRRC3DIJxUsO31yAL/Xd5VKeIkqj0nUSQgSq2xaMeGeOT98/xQe+8JGVfw9dOYYDnwC+zMBqymQKwQDDyO1yk2u9bBNiUc0AawKeEpC2yU0t+tbcXW7qmCAkb81u7O0hLpJMUVlkqgZ/J/M55j//C9j62q8BMxJEwJmIOWXZSXnaK/RblbG7g8lxiKPgqBHpjtMYdsQB2wJlVHQp65nJxDQkU7UDZ1eFcVrenIwunymTCRCWuXomUyE0toVmVZtIatU3xzEHfIIGkSmBYVJBiapumo0iU3nsq7URV9WSySRIphShlzSQTLEQfTbUlUrVrCgyESI7NjWQTJHGLrcGkYlJEboY/m3aDFGQv5/H9z2cHgadVjkgF+tsg2obS/ixn2X5sYJ1u6mqdrmxXHuchmL8Ula8fiTT+dvlHrs0AufAYSQ3RwWSyZ2YAAEWBZEpXcY4pRwsDTcS+g20B39nJJPrdpJMUcLLHceafp4UmdK+djm5PgypUd/cR37/7nJRutHPmlECk5F87FGb8wG5TDz2EcAabpeThHlfy5waJxV1NYRkUp+fg8L8VM2Qy0imeiaTqSGZbi+EyKQcAXbHmqxvbSL4214XydQlcu++Vdw7i54knDpsrhwktpFMz9+egxDgmetTkVdbJZnMcYlG21TleWb9RKbFsbyXzsUuN0zAMW2j1F3OsFhGrrokWN8YFOZE7IVdblhdiEwPYc33xSJuVrLLPeiVxwRUMpkqmR5qcUPdXBTIRKYwQXRyDICCSJHJrYhMlFBMpHrfaZfrWa7NwMHhe+I6gzjB9/zUx7E9svBD3/i22tfPrBlcw62LTCr4e3xpJZEpTBtIJgCYi4n5LCSTCFvNT4/H5lgrMsWvCyKijWTahF1OofW5yNQ80IfLRJJMepFJWbCq19nV4vWjH7sNgxJ87dv3hv8CLeUaLjwAfpS38S5WRjKlKajr5iJTj9BvQNkDaCYy0VaRSS4O17yRYZRg5uSbZ3Nvr5LJlCAyVGeTw8wKF6ZiA3L80Z8D931s/+VvRRwtEZMzikxmfk8MEZnM3V04Rx7CNNSGlgIyCyYG4MhNs0GyTLamUiSTURjTpnLsm/v9uy8BQmQqnZKdMZMJ0ItMI8vIu8tlIlMDyUQIYNZFpiBO4BEOqnme4ygVCzNAWP4I7c5kkqU2ubVTcGqIlsyaUplMgddkl4sFPbjh2i6KTOLCgKSDZNLa5bZXvoacZMrfP/X+qLr5Yr88JqAe/A2USSY/8UskU7pYgCf6sZgnCXgYluxyY0l5LqTIFMhxrE/wd81eeg71+BXxHtwP6iITZRTuxCyRTPAT+AwgSSSC/DdQTcHfjBKM0xCx7QgC1tluJZnCuF8LcWIYAKX9M5lUYDQz6xu9uL/I5MfJxq2RtsE0JFP/rCQSByKTaWh3Obku6ysyVYWH5ORkAMmkmjYURHtFQEcVkqkoMkUhiG3DNetdJhXJtDfey65vHSLTJoK/LWNdJFOHyH1NhX/3tMwFc21shrqXdO/nC3fmePrqGCPLqJNMp/fPxSoHDGt2YjpGRjIpu1yScnCOjdjlrJVIJvFsxEEC06IZcTRaO8kk5sJMpLsgmXrVhcj0ENbxgZgUyiSTPotDV8VMpmonAbW4KdnlXLFRi4ME0VwgndwSC7MqyQTkuUzrEpnGtokYQCC7BPz3v/IpvHTvBD/8ze/ApXF9sUcIwfXxda1djhoE5PKTuchkjnu/b3q7nKSoTu6KjnVnsstNSqfHY2Nc2gSoUp1LjN3zJpmkyCQ3iG12uWARwzcWgOeD2LZYzBZK0THVDYjT0tkiSTl+7mO38ZVv2dF+7mcpkcnE4af6jVF2OhGloKMRUm8psml65DEB0i5nMrGghwnSNgGrxWHPfIshtT0ycbSUi4K9PUR382ckiFOYpgnYWzWSKbp/Hwc/+qNw3/UuuG97DuEASqGpikLek1tP9v4+Y3cX5qkPI+aN4d9BEsAOASLHMWbS/t3lCpuKKxPxHhyc9MwsUT+/KjKpTKZ09YWHEJnKBJDYAKTCurJsJpnSIACxLG1QfhCn8AkHCTUikySZAAgCabxbzqArvVB57DMYhUHJwOBvCnCApzyzy4XVTKYN5jGp2qqJTC0kk04gyUSm1btfqs1YkWSq2uVuvniIyWW7vBZoKF3wdymTqdAJjk3FQVF6qrfMZYdRBbvcVNoQFnL8UoTEw5rJ9Ngl8R7c9eQaRtNhbilFJs45iJ8iMIjIcTxnkgkApmmISAp5fexyfTdQxLLAw34ieiptVgEzy891moictYeEZAJkgHGxuxwwrMNcIjKZVrLLob/IpHJN1bonnc/BZv3GjcQQIpNdFJmqGXIqH8gqZzIR24bDHHhJXWS67FzO5ue+Le27Kt5EdzlGSx04V61OkXv3OfFnX8ucf6zdB+SCYv2aX7g9x7M3JPlaJZkW98/FKgcUrabd76tps4z2tOzyodAm7XJ9KSHTYXmWcJAIIUyuq6csBFvXvViyyzULiRdVrwuR6SGs+b4HSgkmlwoLneWDXtkwQDmTqRr8rbIASnY52Wku8kOEMqeBqxMUzaJw3SLTyGaICRAECX7/1Qf4sd98Bd/6nsfw555pfv298Z7WLscYFd0iVCbT1qO9rRdBEtQ7YCmS6eS2IFDSaHWSaTpDcnKSWYBG5khPMh2IxUsTybTp4G8myZY2kilYxAjMJcjSb8xjAvQkU9MJyu/+yeu4O/fxje9cr1UOkCITT+HJe6EpkylIhMjEl0uRxdVbZMozKITI1PLFWXe59W9ktlwTR56yy11HOp8jORX3WBDLcHK5wFGfTRD7uPMDP4DU97H3Qz8EAAjlNa7DLrfr7pay3LpKLeIvnQJHgd4yEiQB7Dgfx5hBO7vLJRq7nMkotkcmDk77ZZaoEna5osh0GeCJyE1atUxDG/wNyFPHVrtcpO0sp67VIwB0IlORZAIEEdCUa6IR2G2DNgR/N2cyqVIkU1zNZKKbt1QJMbbwXjO7JZNJI5D4x+IA4wzZPWqRHlVJJnmfpqnIY3r0mcu9umzmJBPJRaYKyaQEISpJiqZcJnUYRQqHURNL5TwF2esByCx4bfW56C63O7VhGRS3FvJzq4hM7jQXmfxFBMKB0IS4DzaVyeSq7nJ1QnOcVEWmeaNo3Tf4G1AiU0+SSdpuI2qUg20HzlnBOZFM2WbUngrxZUAmE0mClexybKDI5GbkQwLOOZKT/na5VHa+tLnOLic/E03wNw9CENuCa9btcncWd3BjnEdQtK3JhpQiMte2scc6u8tpLM/FmuwKQvj+AJJJc8DQlHd0tAxx68gTeUyAhmTaz2m8DZdrdWejqrIchliKOMoulx8Krd8uZxUOe3t9vWMg9CTJpNYy0tY2Y/07TXaWzi53Efzdqy5Epoewjg88TK44oMVFhPdAbA57lNps6O1yimTKTygNufCJF0uEckOamEpk0pBMEhPtzGTqWSNTRE17fozv/fDH8ci2i//6655t/Z7r4+u17nJpnIpOU5eeFBTTg1d7W+WABpJpcg0AEaf7qm3pit3l6HQKxHGWe9BolzuQJFNb8PcGSSYiN1tNZEiapAiWMSLLB/ECvcgkNyLV63QkmaGrj/7hbYwthj//1vXcV8VyDRc+EvhUBdq3kEyuK074l6/nImNHBXHeTae/yLR+kqnYNcvcEwvJWIZ/Z9foXioFf2//8h9g8Ru/id3v/V7YT4uA7rBBJBxSyrLz1Hb/0G9AkExAu8jkxz7sKLdpGr1IJtF9hFYWR1fGFl5fDBOZtCQTcKZcJr1drigyNTd/4JJkarpWj3Ig0JFMqaCLVKlGB9oX0ohMJtOQTKy1u1z2vW2ZTBuuLdfE3I/ycONqW/BCaQUS/+hMeUxAwS7XQDIdvHaCYBnj0bf0JXG7u8vldjlJMp3Mta+lshuLtvqJ7NrjS5JJHSS47OEM/qaU4NFLLl5TOlplrh1t5SKTsoTEFhUk06a6yznKLle/10ZpiMAsiEzgQKjPzRKZKBsQmaRdLmSVTKasWUXf7nLpxj9rx6T5Ro8QQYIs+tvlaKKCv4ddJ7UssK0tJAf98nsye1KYiq61SdLbLhczMRZaRTJUfQaRIpl0wd8BqCVJJo1dTuUxARUi7AwVJRwmI70E8b51Lt3lAHH/XHsOuPdCzxfUd5lWjUSqlIsK/X52T36PIplU5uTpvXMTmZwKWddWxY5y6u9qvlqbFa1QWTZqT3rNdBhCKYLFgcxkks/HjA2j01srWmQkk8EoGCUXdrmedSEyPYQ13/ewdbWCJXuH/TOZsuDvsNydCPnihpZIJvHwREsP4alYlEameI1zIZksAzHh+OSfHuFPHyzx333LF2Bit280ro+u48A7KHXGSuJUdEm69KSgCu4/f3aRiRli8J/fLrQtXZVkEouARFrmxuZY211OnZAxjcikC9ReV2Wb1CgEIc3B3/5Cdp+zAlA/AB2Pa1+jNiB1kUkfMulHCX7hE3fwF952PZuo11mCZEoQyAVQNZMp62ohSab0VH7WA0gmdXKbcgZCW/KB1OJwzd3lAGB7ZBUymYRApnKZArXwlyKTyUzsHHE8/hO/itF734tLf/U/y16nSSQcUuo9HpLHBBRFJt4oMoXLU1Ce23mYQTuDv5MoBVMh14W6OrFXssuVFqxqbF6zyFRaECqSSZMDJDYVTSKTyGRKg/pzJ+xyPUimJBKB9ZWFtW3Q+okea7PLFUim0edWZOIcOJEWbSEy6YXGRrvcGUUmXfC3YTFEkji7+aL4vPvkMQH5+2gX7XJFkin2C3Y5MYclDeHfqeoaWiCZZvJZ86QYl9nl+pBMXSTBhurxyyP8yVw+71W73NSCNw/BOc9EpsSm4l7fmF1OdZfTiExxgECuu7J7y9OPf1HMe5NMdIDIlCqRiZpnJJlS7QHlOquUyQSITJu+JFMSg/AUITcyO9uQYjtXBwR/S+EhTjJRty/JpOxyVolkkp9BjWTKx2YeikMH1yiTTJzzGsnkrItkSlIYTR11V6z1dZfTdAit1u6zwP6L/SzvfgPJZOgFnBfuSJGpSDIloYhOSCJxgHTedrkelrRi2Lf6e2aXW/NnDQy3y1mOIRpWcY4oVHY5sR+Z0GEHh60VeSWBXRDcF3a5PnUhMj2EdXzgYbZToEM4F5uXntlCarPBgyDrTqQsWmpxQ4rB3xMhfkSej1Ce6IdysaNT/rftbYzNMSbmejohjGwmM5kS/PU/8xTe+/SVzu9RHebuLfOQ2iThuV0OEO2+tx7rfR1hGta7ywHAdE+STGcTmehEfJ/KwWgimZKDA7CtLe2mUYlqm7TLIYrALNZIhvgLGQxuh6BeqCeZUr1IUSXrVP36i/dxEsT4wAasckAuMnm7gpBzPvu7pf9ebMdORi7SxTCRKYiTbPLmKQOhLQuVbMG+AZHJLWcyAUAkO8yFSVlkYpziP/8/U4AQ3Pin/60InJW1jvtMjQ+rikyXT1rsckuxOWZyHGNmP7sc0ywyr07twXa5IE4qJJMUmbyziEyWNvgbkN3vvEOxkWB1EYZHYTPJFAm7HI/FQqxYNZJpcl1s1Kp2t4ziLI99jsn0drmG4G+zIDLlmUzqZJScq8gEAMfKMmfYgmDRlFYgWYPIpAv+Nq3cnnDzpUNc2htjvNVvY68N/o4a7HKVuahaXNOFdmZJkkmOX+ogoWo91pUi/9ZJOfSpxy6N8JljKeLVMplsxFGKyE+wmMvP3mFCbNxQ8DdhTJBFGrucE4fwjIrI1JDLFCUpTKPfe7kqyVTaRKkcwd6ZTMn5kEzFsWcIyaTu4RVIJkBYuuP7Pe1yyp4UJkjmct7qSzLJsdBKiplMcrObZTKpsbmYySSCvx2jTDK97r+OIAkqJBPr1Wms81pTDmPNFqr1dZfrIXLvvlUIdsd/2uMF9V2m1WftV675+dtzXJvZuDqRz7dypfhHeUe7cw7+9nuRTPlcrDKZirbsddfQ4G/TZgAXeUyRCv6W89SEDmvm0loFuxzQEBNwUdq6EJkesgqWEYJFjFmRZAoXwn7QM5OJlDKZDCQpz04DUq9+QkndKQgSxJ6PcClPKSVloTuN+s7nvhM//OU/vLYF48gSmUwzk+H7/sJben2PEpmKuUxpLDeRSmQCBpFMjTa06R5wcqdxo9W3couCeJ2RMcIi1tjl9g/AdpqtcsDZCJOmUptUHoYwjGb7kX8qBm9uRWB+1GqX69td7mc/dgs7Uxtf+oZugXGVcg0XfhJg+WV/FwDg/NoPAX/8f2T/3SqRTGOBtQO9u8sFUZqdYnHOQMjnSGQaiUDjNOVCrKEU0d0iycTEAsc7xOFP/ls8+xrH89/+xTBv3Ci9TihFQtWBbpV6bPoYfvBLfxBf/4avH/R9bHsbMIwOkkk8Q8ZITPx9MpniKIWhWWTuTGzsryOTCdiYXW4Zxq0HDWpToSsV/A3kz66qOEoqmUzXBAVa/T0axj7b0JCJLZlMrCBoWY7KZKqSTJvP7clEJhX+zdpJpppA4h+fKfQbyE+C47Rul0uiFHc+fdSbYgLKwd8mNWEQo0QylYK/JUmRNpJMylafj+3qe/0qydQn+Psc7FO6evzyCPf8puBvcQ8s52FGMsFh0i63GZEJAIjrakkmJw7gyQyefBOqF5mGZjKlYb/xLesuRyvd5bKOqD1FpnMgmWqB1UNIJvn7rJLJBEiRqW/wt6JbopxkYj1JpkgeKJhF+3G1u1x4ChBW+mx4EIhMpgrJdOdUrAUemeSHeevMZFp3xzHVXU4dkq9and3lAGGXA/pZ5hrscur1qwLOC7fneO5G4VBC0cjeUR5W3/NA86w1pLucZddJJjVfbcYup0im/t3lACEyxWEKoxj8TTdjlxPXyS4ymXrWhcj0kNX8QEwI5c5ycsHft7ucYQCMiUymTLUWD4TOLkecKUziI/IjhJ7MKGCq80T9Fnli9gS+/NEvH/Bbtde1qYOdbQdv2Zn2XpjoRKYk5iJvZbqXLxIH2uW05MasKjKttrnILQri9Hjb3sahf1ibQOODAxhX9acaijDpY1EYWiWRyWq2HymSKXWkyNRil6uecuvQ7ONlhF9/cR9f/44bG+lYAeQbpGN1XTvPAh/+LuDFnwfQ0F0OEGRHjyq2bOYpaReZBp4KD6kt10QqbUDEMGDs7iK+XchkMgXJFNw9wf6P/Aj+8C0m/uRLn6i9TqDuszOImYQQfNObvgljs35/dH2fubuL3aXR2F0uWmhEph7d5ZjGinl1YuHEjwd1C8moMFWZXe713q9RLZ3IpKyjmV2uYQ7gYQvJJO1yQP7sqorDtNRtLxNVq7lMLSKTPvhbb5cziyKTq+lWc04k0/ZIvFdHcr5rtcvpBJI1kEzq1L/YPcmwGDgHbn36EHGU9s5jAsokEyEEruk2k0xqLpo3iEyaLrRqzlFj+6Dg70Jm3XnWY5dd+LDAQUSHoEKNZuK6lcgUU7mRikPA2JzIRB1HG/xtRz6WrB/JFMabyWRSdrmAmWWaYMDBSCoPNTctKtoGLVt+xrvA8qBR4C5VkWRaQQwzdnYQHxz0Ej+KOT3qeaOzfmNHLAPoreLvlAV/y/EqOBEUU0EEV5lMruGWSKbbi9sAROMcVevKZIoTvtbQb0CMZZyLzsNnKSUItAojO8+IP+93iExxKO4fzSFD9lkX7ks/SvDy/mmexwSUSSYljD6M3eWkiEMNIqJIsFm73NDgb1MeVEV+Iqz/Vm6XG6/LLheHYl1iFQ5czAu7XN+6EJkesjreFxPC7GpRZFJdhfqRTICgmXgQZoPeUrbGzu1yhde3pzBIgNgPEfni606pymTa/MKQUoKnr00wZFmnFZkSmclEGbD9uPjHdYhM0z2xeVRY65ntcmKhsTPaQZAEOInKC/14f7819BvYEMkkw955GLZm3GQkkxPDaCCZwgaRwjFZ7ZTnFz95B2GS4gNfWKZp1lkqH+hBIARb55t/HNh7J/DT3wm89Itlksl1kXoBANIrjDFJOaKE5yRTSkBIS2eLWL72BoRCtXk+zsK/9xDdFc9IIOkbbs5w+7dnoOMRPvSN2wjS+gYklHanTdgy+5Sxu4urC9ZIMkVLIdSaUmQy+tjl4oqgIuuKRNgfLPqffAVRZZPnbInT5DPZ5bqCvw8baVZ1ct10rZ681JrIFCUVu5wSme6Vvq5ZZNIFfxstwd8Fu9yobJfLu8udo13OK9jlmoK/dQLJOu1yhUwmZSd89Y9eByHAI2/u1+wDKNoOxec5MkYZycQ5L2cySYu8mouqpbPLmdQEOM3moKF2uc8FyfTY5RE4KBLm1rvLzcTzspyHWMwD+IYkETYY/A0IkYlrSCYrDLBU420Pu1xfkkCITP2sIzwQY2BIjQrJ1D+TqRc1soaqNRGZ7IqIhD40qbx3Q26sTDLxMEQ61wfnV68TUCKT+DyHk0xFu5wSmaR4FJwCle6taRiA2LYkuH2kXLxPimS6MVl/JlOUcJhrFpmK67KzVBAnMBlpF8GcGbD1ePYMi54AACAASURBVLfIlMVmtHSXK6xxP3XvBEnK885yQJlkUiLTOdvl+nzmKuy7GAC+Sbvc0OBvRTKFfiztcgwwLMRgGJM1kUzqcKJCMq1DmP18qAuR6SGr+YGYOLaKItNyGMkEyLDHIMg3KXLQ4369uxxsSTIFMUI/BUMEn3MYlKwdf22qPpkqxXINF1v2VllkilIwlVNw6UkABJj1Fy60wd+AEJkA4PVPiz9XJpnKwd87rphU9pc5ds05FyTTTjvJtJFMJkLkRjcUn0cHyUScBGYQg441drmG7mQ6NPtnP3YLT18d4+2PnG3T1lZKZDr0hWDrjHeAb/8IcP3twE9/B+xXfkVctySZeBCJjmE9WlmrTbai/ngCEBLlnUOqFXtiobiBfJJtuXlWhIa5dz0P/pZh1Qe/9AL8QwvX//7fQTBzEVUFgTRBCPE7fS5FpksnzXa5eCk2jOZYLK6Z2TP4W7OhUDkJQ3KZwiQth4gSIgSgdZNMprLLye5yLSRTc/B3i10uTCvB31JkOmkSmSrB39VcFKCVZFKZWISSTNyK5PdbjAoK4XMlMiX6RWlNIOF8vcHfaZFkEv/26h8dYOeJGexR9/ijKrPLyTl7ZI4ykilKI3DwTBAipgniuhlVW63MLlc5QCDczKy0yoZT7dSpq8+lyAQAIXPzgGRZo4LItDwO4VE5hm8w+BsAyGiUde9TxTmHGXo4pd0iU5JypByD7HK9M5mkrS6qZjIpkalHd7nqfLipcqqdLcdyzbToYZk7q11OkuZ9LHNugRxJFck07ScyhXIsNJKW7nLhSSmPCRBiocpkAvJn9dbpLUzNKaYFUapmO1yx4jRdO4muxrKz5jLVGnU01bVnu+1ybSKTvJeKAsTztyuh30Alk+l8SSa1bunzmStxySpkMym7nLmB8Vx0J0TvjDBFMvmLCGnCYdqShCI2RpsUmS5Ipt51ITI9ZHW878GZmJmVAEBOMvXMZAIEyZSGQaasL6XIlJFMxfwOawKDBIiCFGHAYTEffrR5T32xDIshDodNJNdH13F3WchkSjiomuQeeZfoFtFzsZikCWIeN5NMALD/KfHnyplMKgdDLHZ3RlJk8vKFSrpYgPt+N8lEN9T9Ri5IjZZNu3cagZkUhsVgBsmg7nKuVT4BuH3k4XdeeYBvfOcjGw2FrYlMhiMW8t/+M8Dus2Af/g68j30cYZKAjkfgCQd3+0366vdRi1WeAJTyRjICcbCxTcy23JQeSZLJ2NtDfOcOeJoiiBJcu/MKDn7mtzB7YonZFz8Hi1mlDo3q+lQXvk0Qc33K2N3F7CTGkd8uMlkjsbhmBkXamclUoXZkXZ2IZ36IyBTESd2uMrpy9kymuCzO9LXLpWEAYuk/qzDJ7XJBjWSqBn83kUz6pgf67nLNmUyK1LFclj3v9e5ym593qs+JyGRqIpkqgmJ4KoiJs5JMtE4yKdLr5IE/KI8JqJ8wF0mmzNpWeJ7ZZJJlxFQrs8s5ZQGJwEQoycdBJFOUbCTDo6tmjontkQkPtghvLZQzMUEI4J2EWBwHWP5/7L17sGXZXd/3XWu/z+M+uu+9/Rh1SwJJaGYEFkKGkXk4GHCKp4DilYKAXXZRpEwScGwTJxUHqkyVU06wLROTOFWmKhbYf6QSm7goHJfLBDsgKYAw0QuBhDQ90z3dfbv7vs45+7lW/lhr7efa++x9ztn7nhndb5VqZlr3cfo89l7ru77fz4/Kk/Q4aHW4sKqo61bA3zwIQDnHOZG/V21gNde/wuelhYhtdQd/V6bLKSbT8vtBdj/se7ocrSaZgHZcpgL4e7W6HNDNZFpECRLFZGppMkWpyZS7bpenywUXlesyDwJQyWQCss//g9mDAvQb2KDJlGwe/G1tMMnUykw8ekEcJscNnxdfXjM1dTnToLAMUjhI/cT9M0wdE3f2c4Z9Icn0WFS8nM0MUlomx6QgpKXJJJNC+Slz6fWnh7ocIQS2QRF0TDItzsTrpe6fPlx4qFl/d5W6b1yBv1fSlcm0Zfqa7307vuenvqL4hymTqXtdrlC3AMAWCxDXLUySUkmmOGIIQw7LCOHHSe8nUXk1mRp1ujW+VWIy5ZJMf/qngB/99dY/Sy2ctaDjHXlTPv40QK2VDQI6HgOEZHU5TZJJLVrMSwB/AwoSKpNMNU69P4vgTSy43IQZcz34u+ZxumZx2uGv/HvBCHj/u/urygE5kyl4BpOY2RRBb08YTYfvxC+YP4fnjn8zTfkxp118Wd2s0+lyCQehPEt/lFUah7pJpZvnharL3QaPIiRPn4IHAb7yn34A5rVd3PyKU2DxDLZhp69VqthHKA2Ay0wyOYsEs3O9aZNIZpa9ySTTefuTr1B3MupdW89kMk3wqPgY1HS5RRCIBWnNPaCRySSnywHCIE6/h/OMY6Bkj8QGt21drpwmABqny6nf5XjVk1FzwLqcaxmwTYqzQl2uJskUlepyKl2yNpOpCv7OT9/rajJFCQMl2c/NJ5nUIIZ86ohOp7VJJlXnKiSeAVDYiFSSSWNc1UkYdcMzmQAB/55xt1KXo5TAm9ppkumCMplk6rku57kV8Debi9fpXN2XqCE+h5okU5h0q6t0YjL52XS5wsa+A0ew1SSvDahijqgkSJsJc/LzEMFcqfaTmkzHx0u/Vj0PizABOzsHHY0EN7WFtEmm8nS58AKwy0kmceigDGDFZbo/u1+oygHCBIsSvjb3KGZs48aDs6kkU9vBA0cviHuQai1of1h9kgmovi8/fv8Uz9/aAc1X9dKk4om41w5UlQMgeX1GodJXJ5Vgytfloh7rckDNwVWN1OOan8lDVWUyEQceNpVkkveNMvj7ymRqpSuTactkOQZ2D0ub9pTJ1KEu54i6nFfqCHN/UTmdBDVg0RhxxBGFFLYRwS8vrHuW2CR2O025Mb6BB7MH6X8Lk0m+panRCd5ZxxACkCWZTj4vNlkrJm4IpaDjcQp/VEmm40W2UMlMJv1Np66GtikVkkw1yTL/IoIztjCKxXPdyWSSNwF1gf5nH30VX353D2856AaH7qp8kqlS7xhdA374n+OPyXP43j/6KZDZPQAAs9tNuquaTKzZZIr93pJMu55kMqUmk2CX+a+8ih/82K9i+vBV3PovfxyGzQH/BI7hpAZr9vi2IMkk3//kiT7JlMhNmTMWi7XW0+U0xnlqMs26JJk0TJTRtd6YTPHiFACvr8stmS7HCGA6RoHJxGIOcFTTXZOjBpOpuJmppAkAYcTX1OXU78onddMEDpVJph5TJHnteVaWZDLd5iRT/rVOT7L7AH+L32OYFLe+qNvPD+PidKeROUo3l7pqmzGd1k+XW4jvI6W1AoWNOFeXM6kJo0XyrHWSoAfd2R/hLLGyzUJO3o6N08cLREGCM3BxDe8Z/E1cr1KXYzPx2GbUzt4P7p7WZErrpS2fT9qlLpdOlzOLlZUO0+XUvb3vJHylqqs26q2STOLvwwxnpQS1eSRNpkfLDS1KSQopT87PQXfa4xYCaTIZce6+YJiC/1dIMhWvyywMUyYTID6rnHM8uHiA2+OiyeR2qE81qY8kU9eR9nVqbXLfeEH8s6ky5+tTvUp5kylhHJ967bxYlQOkibybTZcbqCpXeIwt6l5pXS6fZOqxLgcAdgcDR02onZ+Jz7N6vAs4m0syKYM9dzAskkxXdbk2ujKZXg+aPxMnFR0WPsR2hMmUjsDO6nLEq6YoTDNBFBGEkQHbSsRI9oGTTEnXutz4Js7D8/S0liUcdMULX6N54+2Lk03OVq7KKdGdbGE/tsbwTA+P5tmiKJEnY5cB/gYySKhhGbWbdv8igju2MArl5ImGupyOyQSIBc0fvHaOT712ju9893OV79+0Gk0mABhdw39q/TQeO3dAf+d/BgAws52pm4FOZV0uZiDGMpOpnyRTypqZKyaTMEhPfuVX8J2f+be4/6e/FZOv+3rxxYtnsKilqcvlkky6ZN8AUot452ReZUYB6SbNGYvFWxNDTCmJWMoEysuzDYxtY4Ukk8Zk2jCTSUXbybz5oIGHYT34WzFSxmbBZIrkPcEsL74nN2uYTCSd3JI9Ps2CkNbX5ZTJl08yRQmDSYk46R2IyQSIz0o7JlPJINlUkqmhLnfzi3eLCbMWCksw6GV1OZFkqgN/L0BsG8QoPgaD2EiQ1eXaVOWAy2MyAYLLdBLb4EHVZBrv2Dh+RTwHp1y+zkk4APi7WJdTSaa56WZVG3d3SZJJ83z++t8CfvWvFf6I2E4nJhNxHICQGvD38tdbbbD7ny5nIIwZmErguHtiqnAHJhNb8XWm4zGI67aqywHZwBN2fgajk8kkPn9mORlqeRmTKTivgL/VIIi8yXQWnuEiuqgmmUpth1UVMZ5e0zYl9R6PkvVSVq2nMV5/u7j/NMG/1bpOU5cDxBpXfQY+/2SGeZhUTSYA8HZlkulxqwEzm5RIMrWfLqfYR0AuydRDXQ4Q1422pqJ6fLNTud5V+13uwOUbmi6nErD5upzVPm31ha4rk+n1oAYWR50Ukym/qQcEa6EcgQcAy2SIYoIwNmFbCfwoGZ7JFLFWI2GVyhPmCkmmjmoEahMCTOUo+xWh30rGZIrkIqsoHI2OikkmaTIZNSZTn+BvoGWSSdblxpFYUHRKMuX4BP/s916FQQm+9ctuVb5/08rX5eo2Rr61i39w5+dA9wWXhpntNpHlJBOLEhCKhrqcD1jtNmddZZsUY9soMJkA4PyXfxkPxtfx6vf/hQw6uXgmkkxNJtMl1eWsI7Ho2j/nOA2qGy21SbPG4gTXNJebTCLJpL+mHUydbuBvbZJJMpk6XMPy0plMKtq+jMsnRlbX1+UAwaDxL7KEkXq+Kkmm6Q19ksmZAqWFpfZEjxq10+WoQUENUkgyRUkugTMQkwnQmEyNTKYe6nIN4O+uVTmgupEq1OU0/CQ6nTQkmXztOsEkNmIurhf5aXXLJOoql1OXu3PNwwV3EQfVaqC3YyOYic/EOTg8EwKq1yf423Nrk0y+aWfTV2tMpihW0xg1a53f/cfARz9YqKuqCnwbsVwict3pcn2vH9UhaPo4CRHw74s2dTnx9+Et379lEUJgHh62Npk8OQkvOT0DbTlZDgBCLtPiJVafSF5Ko1IL/g5Ac+DveTxPU/+3xiUmk1ncI6yqOOkB/L2xJFNS5OrVybSF0dRoMqkkk/7671o0NewU9PtFncnk7kkm08PBTSbHop2STIW6nDK5zZ7qch2g2pZtAEQMbwAAU4K/59yGgw2ZTBrwt3tVl2utK5Pp9aCGqUJ1Io4tmUwyTqjqcnNNXQ6AaXHEsYEwsWDZkEymYetygKxwtJS6WRZMphXjuo11OSCbUrdukqlUUTjwDgrg7/jxMWBZMPb0o6uHSTIJk6l2upxMMnnSZCIakylMQm2VIj8t659/9FV87dsP0rpSn1KLrZjFtRsjxzTwFFPQb/tZAAC7/mWtfnYKOlXT5aJY1OVCPe9EJJn6MZkAYG9kp0wmY29PVF4oxf/wnh+ANRmLU1DTS5lMOpMpkB+jywR/A8C1C+hNJjViXW6E1XTKJpO6LskEiMpcW5MpYRwx41WTybsmzJU6c3GJiGUBmlHjI9sADVokmWrA30EsWD3e2KpJMpXrcg0mU0na6XJGfV0OEAcKxSRTrmYxEJMJEPwy9TmB4YjfrUlgVXgeqcmkv0a3lWJa5JlM126P8fxX38I7X+puvJfH2numlyWZdHW5yRTJRX1dTpt4JjYYz5JMba8PrTd5PejutRHmcJD41euxmjAHABeUY2TI922v4G+vmmSaiddpYTitk0yV68/pK8DZK6IW+Nr/l/5xp+lyMgFjUFLc2HeZLle6H/YlZY5UJsy1STKpe94ahyhdTCZlPCTn5zCm7Q8qI8YRUBNGmRdnuiKNxbmoy+WYTJzz9H6QTzLdvxD8y+cmxeS4QhhspC634SRTajIl6z22TknKZRPmltTllKEIAJ94cAbLIHj7keZrvT1gfiz2dwPX5TyZrFsmVUez8yaTYij2lGSyjfZJJkIJLMfA4rwI/p5zBzbrEfxt0Y3A8r8QdGUyvR60QpKJyrqcqs88k/UZ5uvrcpZFECUmwsSB7UBOlxu2LgegE5cpTTLJCXMs3kBdrq4elCaZ1jOZjMmksLA/9A6L4O/jY5gHB7WcgKVm2JpSk2gMiyLWXOgZ4/DnEdyJBVftzzR1OT/2tY9RGZf/9tOPcf/UH6QqB2RJpvK/56VubvSa2OAxq21dLksyccaAJGlRl+vPZNrNsWYIIdj5tm+F9WM/jk9ef0u20PL2Gkym4NKTTHRnB9y2sH/B04mABS0CMCKNGQiGDefIqhMaJXGTyWS3NpnCtB5ZMuFHkuG1IpdJl2QCRJ3BDCSbqgb8zZrA37Hg67kTu2AyZUmmcl3uhjBI88mP4ExvMsnKSsHco2ajyfS29x7h7osZ7yxKcgmcAU2mHc8qgr+BjD2TU291OQX+ztflLAN/5j9+HpP97tf3KOGFdMvYGmMezcE519fldqbppNOytOxGABZ1Xn91uf0R5typgL+Bosk0IxxjQ97z+gZ/l5NMsi63MJ30QBCuZLaUpJIEdvlA7d6Hs39/+UPpv3Y1majtVBOKqprV4nkZqi7npin90oS5DtPl1kmsmYeHrcDfQMbpYWdnMLokmWKGiJqgcbku5wpWTOyL5F3u2qxea5JLMi3iRZZkKk+XMxWTab1kRsRY64mHbaWM+LDD4bNOgW5QR52OXgBOX87MpMoPOxPrtxp8iWMZhSTT24+men6auwc8+SPx7wOCvwFZl2szXc6pr8u1qh+uIDFMpP170XYMzEt1uQtmw+abYjKpJFOeyXSVZGqrK5Pp9aD509qaRJ2I44CHgsm0P7Jw/0ScnLGaxaNpUcTMQsg82A7FIkzSk6IhpDY6XSbMHY2OQEDSm+dadTm2pIY23WSSKVvYV5NMj2t5TED/4G9q2anJlGhOOsJ5DHCIJJNct9YlmXQmk+r//9P/9x48y8A3vXBjs3+BGuWNpdokk0xlqPpf+bS5TtnIZpot8CiyWHVZUd9JJguni2xTcftv/k2w7/8h+RjlZ9rbBxYnjdPlTGKAksu5RRBCQA6uYf9cn2RCECC0SWrGqs99U2UuDvXgb0AkmZ5ctNuIqc2XFvwNrMxlIrbeZBpZZs5kqhqfPEmAOK5lMoUxg2NRWZerJpkqxttEfibzaaaaJFOlsgIIk4gzgOlfi6//wXfi7X8y+9wX6nLJgEkmz8aJPHxJN5vlzwI0GxRlMq1ZnVan/tGa47mVBPg7Mx5G1ggcwmDSTZczplNw39caEGy+ABlpDqOoDQ7xHlrECzgtN+mdNnkb1u09Dwu4MGSqKy9lMhGDYEEAl0pztGfwNw8CcSAhpepyC9NemmSK6phM9z4iEqq7d4CXfyv7ffLgqI1YIJhMlRHdsS9SPy3SC4OBv1NzpDRhrtV0OZmEXYON2KkuZxtpkol2SDKFMUNkmKDl+4Lpib+DOgjQmEw0z2RKRJLJNVzsO8V7yKaYTAnbPPjbSZNMG6jLtU4yvSj++fhTNT/srPHa71lGCs3/xP0zPY8JEAd96vN9GeDvlibTtdtjHLwpS8r1XpczukG1LTdjTVqOgThhmG0yyaSpy12Bv9vrymR6PWilupwDFoibze09D69Kk4kvfFDd4tGm4KBgsGC7pqjLdQSPriO1SazjAOlkUQsH3kFWl0s46IoXvqUJoQ0lmcocjKPRERbxAjM5+UYlmeo0XF3O0CaZFnIj7k4suHLdWsdk0iaZ5Ov8qdfO8R++eANjZ5gNZf7Eve703TaKJpNa+C9TIcmkTCaDF5MgecX9MZkAWQOaFxelQc4IA5CaTE3T5Rw6zJSvOhlHR9i/AE6C6mk+8UNEOXNEGSV1sHrOeWOS6frEwdN5iLjFYjZLMmmYTIAY1LCCVJKpXPnzbANOVD/RLH3P1SaZRIrEHVsIFzGY/DuqQQtW+To/VSZTLhFQV5dTlZWoZDIBjWmmvKKEZwvWgZlMszARi+bGJBMrVn/8E7HgXNOIMDV1uXUkwN/ZczcyxXVsHs31SaaJeD3zjEAl5vugbnWdYFMXnKyQZIoub7qcbVJQZwKL+RXj05MmkzO2AAKMqNw49JxkArLKL5BPMrnZaHFvT/B2kuLnqNZkevlDwJveC7z5q8W/y+sIsW2AMfAy10cjLk0muzxGPA5aD6sYPMmU3+xNDoXJVGNwp1Kfc2uNJNPBAdj5eSWVppNrGgjCCOz8vFuSKWEIqQlSTjIphpw6yMrX5eSEwPx0uUW0wP2L+7g1uVVJybvWZupyUcI3XqGy5eCBtZlMEWtf3zx6Xvzz4cf1/79/Vgv9BrJq5KMzH8cXgZ7HBBTr1pNhDlvTX20ZWLQ40CeU4D/6G19VPBTquS7nWO3rckBx8p1pGwgTBh82LNbukHipdHU5acJ3YQh/oerKZNp2MSbrcl2TTHZ6s3luz8slmXwQzeLRzHVubc8U0+UGTTJ1r8sBojKnTCa2RpJpaUJoQ0wmBf5WF6cDTxhKasLcMpMpNcN6ApMS2waLRJKJxRy8tAHyJSTVnViw5Sa1brpcU5IJAN7/5cNU5QAJUJYLrvokk4jA0olYsOk2XzqlYOWCyYRLmS4HALtexmRKH6NciKcLLW9/6XQ5+5JNJufGTVy74HqTKYgQ5YDVhjQp6pJMynyqSzIdTmxwDjydLz/xVyf1WiYTsHqSSVb/UNoMjmwDbnwiDCajaspmJ9f1TCbHNOCOxc9Xn+E0yVQGf6dJptdyP6TOZFJJptx1OzWZ9PDvsqrg76Gmy4nfc7aIMlNBZzKVDRL/dO2qHJAHf2/IZIpZoUI1snImk0xu5BOdarOrg3/zhT7xbBsOOBFGaBAH7cHfZaNuYDlyQEB6Ki2lkkzWRLwXXKJMpj6TTOI5Y1qTyc5MJvUeKyViQx34O5QcpjtfCdx9SXCJnn4WANKBAG3STAoY7ZhGMT0SL1pXywYHf+ev+eMjcQ3xq/eMguTnnK5jMh2KilObNJNnG+CzOcA5aJfpcrIuR8pJJjVdTnEfHY3JlGcyJT7uz+7j9rg4WQ7I2FaLFoyeJsVJMUm5CanDh3XTnp2SlLt3hWlXB/+uqY4rKSbTxx+Iz+0LtxqSTEpD1+XsdkkmnXqvy5VTlEuUr/KZtjDH53BgJptKMs0AahU4fY5lgPP1px5+IejKZNp2BWeierAikwmQSaZnC3DOwRZzPWvBzT5A9siR0+WGZzItmxBVljKZOONgCV8b/F1fl9vMdDk6nQJxnJ5iHo7EzeV4cQyeJEiePk0XLzoFSQBKKEzSz0YsD/4GUEkzqViqN7HgBOL/4151oVZrMsmF5/Wxja99W72Z1odSk6khyRTGLDXN6nglZamT1HxdjtruEpOpv5PyvZGF03kxEROk6RtVl1vOZLosHpOSc+MW9mvA3zSIEOcMS3NJkkklJOtMaAWfPz5vbzJVk0zSZFqRyQRTfKbLiQPPMuDGZ7X3AJZuKpqYTBSu3Eirz7C61laSTBN5rWuVZKqpywEdkkwsG4c8KPhbPF8ni2h5kqlcl9uAyZSCvzdUlyuDv9MkU7wkyXRWvU6xhb4u5xgOCIkQxAx+oufulSUMqcurywHAaCxfrxKXSZlMpjRgh6jLqYRYvo7NZjPAMBBRs1iXAyqVuQz8nVvrvPq7gs1z5yXg7vvEn0kuE+lgMrEwX5fLbULjoHX6NoiHSTI5uqloakrXMi6T/Jwba9TWzSNlMi3nMrkWBZmJz1kn8HfCEBsmeFi6LpmOMP5UWjqXZFINBuLY6VpnHs/x4OIBbk+qJpMnDxn8NdNCMeMwNg3+NjY4Xa7t+5FSkWZ69MmaH3beuA9wJe/oE3Ky3PNtkkxD1+XM1cHVfdflbHP1JJPliIPiOXdh8LgwZXNlRYtCVQ6oOVy7klZXJtO2a8no6joRx0nH1r5p38MsTHC2iMEXPojX1mQaMMlkdWcyAZnJpDaXq4K/l9flJCxx3STTVKZk5OnxkSduLo/njxE/eQIwBvOwOckkFvr9XOCFyRRl9aPS6+GrutzYgi1NpkhTq6xjMqn31Ld92a2Nj7tdpuVJJrGwJoYBOh6D1UxeKkvdrAtJJsetny4X+a2m9KyqPc9CmLACY6Gy8HeFyaSty0ULUZe7pMlyStbRIbwQuDipnhQbQYQ4V7U0llw/0iRTTQX4YCpNphbw79q6nLsnYFxrJpnKXCbPNjBKzhsmy0nIe910OVkVyJJM4uvjOiaTty+MnvNykqm6WHYszYQndeKnmdSmU7EulwyYZBKP8zRvMpWYTJlB0kOSSQP+XkeFRBiKJpOuZq3uRbrrXF1dzjFcgMYIIta6LqdMkcuqywHAZCper2BR/Lu6IwuUEhijcpKp/7pcJck0GgGEVJNMJZNJJQkKSaZ7EvT9pvcCB+8Qn2HJZVLXBdYqyRSCOLa4Fxbqcu05ghmjcJgkU8EcGcsDumUT5mIfIUzY1urXmi5JJtcyYMzEeoB2BH/HhoappabLhTomU3boYFADNrXxzH+GZ8EzrcmUmnVrJpnK159NKJ0ut7bJ1HHwwNHzoi6nq0MtrcuJlNAn7p/h7rURdtyaRLhKMlnjQhJtCClG2CqK+67LdYRqW9JkopTAkOa4D3lIoBn20FnhDLBLJlO67rmCfy/Tlcm07VKn4p2ZTHYhyQQAr54sahePZs54ssYe/HjY6XKqspF0YDIBwM3RTfiJj2dzEY9euy5XN13u2hcBf+a/AV74jpV+vpI6PVYVhYORMJQeLx4jkZNKjCVMpj4TJpUkU1g2mbK6nBUk8C0g4tXEgp/42sf53J6H//wb3o4f+w++uIdH36ylJlNudCqdTFrX5dSi2jVpupAnjlcP/u5QPVhFavOc5zJVxkp7+0C8gA2KmMVgvMjfEEmmyzWZzCNhwIaPH1b+i6f+SAAAIABJREFUPyOIweycySRNClYzhUaZpcuSTE9my02mWvA3peJ5nbdMMj38OPB//BjwR/8a4LzWZBrZBibsrLYynd9U6B+vmN6WmkwS/h3XJZkoFZU5lQZgTLBhGpJMhclEiqnU8hSxWpcbiMk0kibTPMo20KUkU2qQWD2YTAr8vYwf01IC/J0zmXJ1OR34m05lkqmuLqeZQuuaDghhuAiD1uDv2uTfgNrdFRu6h8dFA5hQgq/9gXdg513i/3eomjbYL/gbEHB1JTabpSzAapKpWP1Kp8vln897HwEO3ykOIykViaYVkkyqLqf4hNkvbZ++HSrJpA6sghWTTBGsdLO4ihTWoFVdzjJgzMWGt0uSKYwZEtNMDxJSWZ5IWKi0dN5kkut+VZ92TRefPRXVyVvj4mQ5IEMY+GumMuKEp9e0TSlNMq1bl4tYt9f66EWx97qorj2Wgb+VyfTx+6f1PCYgSzINXJUD2oO/dQpTk7unJFNH8LctDxsV8iWIRV0OgPiMrKtoXptkWpdj9oWgK5Np26WSTB2ZTNRxgCQBj+PUZLp/fA7EsR78nTOZ6MhDwviwTCZlaqzAZAKAB/LU3VgV/L1suhwhwNf9lYzNtKKoOj2WC/upNYVjOCLJJE2mZeBvh/a3+VeTaLL6UfH18GchqEFgOYYwmWxUkzCQSSbNopRSgp/8pnfg1m5/SZ46qVP3utN3NV0OUID2lkymOIFBCUyDpgYBcT19XS6JxUa6RybT3khjMlXqcsK0tmXapFCZU0ymLTGZcFw1bcwwQeJkJ4RGev2oqctFzUym6xPxuW9TlwvLz2Ve3rV2SaYkAv73HwX+/T8BPvjdwC98NchrHwWgM5lMTHlTkimrR+gkqgIG3Ek5ySSNN91zMjnKmEya03IlV3ei17EuVzBHBmUy5ZJM6rpfMpkqnxtAmExr1qaBzGTaVJIpTHjBeFCm+jyeY5EsYFO7MC3SmKoDDw34e7HQJp49aVKd+fPaSnRZmcF9eXW5/X3x2Xn8pPrZfNfXPQeyLwHg6J/JlIG/cybTfA5D1rTb1uWyzwwD7n1Y8JiU7r4EPPlDYHbcrS4X+CC2ZDKVp8u1vGcpU5tu2HAoK93oFZJM8p6xbMJcEiCEtZYRZly7BhhG6ySTtZAmU8ckU2JYqXGUynSKSaZCXS5jMgHSZDoRJpMuybQp8HfM+MbT6ZtIMok0asfBAzdeEP/UcZmW1OU8y0CUcHzuybyexwRkSaaBq3KAMpkY2Ao8wEiyt/pqU3QFf6skk+L6hjHDgiuTqTpRtLPCeTXJpMMEXEmrK5Np26UmFXVNMskbDA8CPCdNptceiZ9FNEwmM288yRPMIetyyzaJdVInMw/PxckVXfEmt7QutyEZEvqYyIU9IQSH3iEeLx6n3X7zsP6mM1SSqa5+5F9EcCcWCCEw/Qi+hSrTB/2bYavIkxU1r2axbOeSTMZk2qEux9KpeemG3x3pp8tJAG+f0+V2PcWayV6XyumyMpnk5KIgXxNS0+XW4FVsQspkok+qEFczTMDyJpOp6p36hXKaZKoxmaaOCdukrepyteBvQEyYa8Nk+s2/Dzz8GPA9vwi8/38EwEF+/4MAAP6bv1BIQ3m2gV1+UVuZTk+ua5JMYVyqy6VJJvFcVZJMgOAyqVNczWm5UrrYyj/vChjf0mSKGc9ORVmcfX/PKtblVJKpCAutTGUENpZkUvySzYG/kwKMtZxkKpv+WZKpmrisSzyP5LXzLFgI8HeLutxQyZYmHV4T17unz/RA6LTiRaTB2+M6QA/+nqUm0zyty8lNaLkuJ03J9LU+/rT4mjtflX1RjstEbJmQbF2Xc9LqeKo4aJ1k8geaJKg1R7x9MXWjRZIpgL3W4ySUwrx+vXWSyfKFydQF/B0mDLGpq8t5ksmkrs158Hfx0GFkjvB4IR6jHvwtnoNFxxZBWTHbPPg7NZnWSDLFjIPxjqDqI2kyPSyZTIyJ53zJdDmlF59rk2Qa3mTy1qh7xWzzUwTz6gr+VkwmtY4J4gQLlWTaRF1Om2TSTNW9klZXJtO2aw0mEyB6+NfHNmyT4vFjsVjRLR6t3Bh65oob1rDgb2lqdK3LySTTo3NxE121LrcU/L0hqclleQPjcKRMJvF3MA+u135/29PjlR9fajLpmUyLiyjdrJpBXJtk6vtxrqJ20+XEgpVOp0gu2t2g8vyylI/jjvRJJpWU6NHA2cvXgKQqlRVpMjmy0hTlJ4GpJFOPlb42UiaT/bT6OlghA9ckmZKautyyJBMhBIcTB4/XYTIB4jq9rC735DPA//3fAc9/O/Cu7wa+/IeA/+Q3Qb7uJwAA/N/9PPB3XgR+9a8CTz+LkQnskhmYs6f9cVmSqWm6HIXlGKAmKSaZCEB16c/JEXDewWRaF/ydpjKGZzKdzKOsHlUyzCsGCecbM5kIIbAMskHwN68Ff+v4SXUDDngU1SaePWmOnwUz+InfarrcNtTl9mRd7uRUbzKlFVgMAP6Wh3isAP6eg45GcjLVEiZTOcmkeEx3Xsq+6Pa7hVH28m91r8u5TnWjFy9a37PEJMH+DyhVhbV47aGCy9SCyeRza21ulHl4iPi4TZKJYhSI19voaDIxnclkuaLCqAF/q/p0vi4HACY100Ezhb+DQWEZZEN1uc1+xtV7PFojMZLer7vsZ8YHImFUTjKF5wB483S53KHNC7ca7hMqOHAJJlPKM1shvSaSx/2lFLuCv9V0ubQuFw1Ql0uvPVd1uWW6Mpm2XepU3NVvMOqkTjG474NSguf2PDx+Ik0mHfg7N4Y+ccUHalDwt2IydbzoXfeuw6QmHl1IntGKdbmUydSzyWRoOBgH3kFal6PTqXb6n1KYhMMkmeTzWEkyzSJ4snZj+BEWNhBp2CtBUj05v2wtM5lUkolzDjoZa0d76xTELGcyyQ2/N6kxmeRNbwCT6WRRZTKlm1AZ1bbkprqYZPIRSmDoZYqOx4gdE+7JojApDwDskIG72ftr2XRKdV2pSzIBwMHExvFFi7qcjomitMxk4hz4Fz8hNoDf/LezPycE5E3vEV/yPf8r8OJ3Ab/9i8AH3oP3f/K/EL/X1i9Y02lCDUwmxzRACIE7tgrgb9M29LH36U1gfixMn9Rk0oC/zez0MJWxbl1umPuOZVCMbaM5yZRuUORjCmdiitcGTCZAwFM3l2QqLv7zSSbdJDhiGKCTSSWxqcwPXeJ5JE2mE1+kn1qZTAOBoJtEbLG+OT+rTqoEsiRTajL1eZDjqrpcEfxNx2MB5FVJJnsiBgnUmkzytb73EZGgvJ7jHJoO8NxXAC9/KE04tjWZiO3ALsN3O0yXGzrJFJTXjJND4GKJ8RMHCGB2Mx40Mg8PW06XMzCOFgAh6UFjGwkmkwVWmS7nimtVeCE2wLlrpkq2qkMHte65ObpZqMuWH99iA+Bvc8Pmg0kJCFkvyaStPLfRjReqJlPDvVBJYUauj23c2Gm4jri74n/XLoFPKj87q8C/y1NMNy3HNBAz3vrwpZpkYlhwuRaKNgH+ngP2uPBHV3W59roymbZdi2eAs5st3FtKnWJk8G8Xz56KhSHRAD3NibpoMoRyMTHsdLnV6nKUUNwY3cCTmWAtrJNkMogBs+dTdKrhYByNjnC8OEZ8fNzIYwL6TwipjapJxOtQnS6XJZmMRQDfJtq6XN10ucvUUiaTScG4HMU7mSLpMF1O3XR4JDf8o6l+ulwkNxZ9mkyqLldIMqlERpHJ5MjHW2QyBVsxXY4QgvjaFHvnDOdR8bWwIg7kTKa0Lldz04+X1OUA4PrEwZNWdTmZetDF7xWTSTeVBgB+75eAP/4N4Jt+GtgpQlhT8Pf0zcB3/gPgJz8GfO1fxo2z3wcA+K4eEJqBv+umy2XvT3dsZXW5kNUmuzA5AjgTbBMFsNclmbRpgu5JJvsSmEwAsDeyRa00ZTKVkkzlupza8G/KZDJIahqsq/Li3zVcEBDM4zn8WJ86otMpkrOyySSuUbrE81hWjp9KGHUrJpO69gyYjK5IJj1mF3Umk6yOQtXlejzISZNMxbqcSjKldTlKxWa2zGRS4F31Wt/7sKjKlc3iuy8BD34PhIhrUSsmUyjrciYtmjcdpssFAw2NqYXvTm4sTTLxOJBJpnVNpoPWTKZJtAAZj0E6pH3CmIGbVhX8bboAuDjQKF2X00MHlWSS6x0djyn/+NZNZYga1WZNJkJIAWOwilau6x69ADz6VHFKqjTXG+ty0ux44fZOM7eIGsBf+gjwlT/a7XFtQO4aJlMfibW8ulYkLQX+trN00QLyWhVugMkUzerrclcm01JdmUzbrvlTYNSNxwTkxtbKG87tXQ+nz8RiUjc1xpITL2ziI1Bp7UFNpuYR5E26MbqBpxeiVkjXMJn6TjEBEBNkCCkYGAfeAS6iC4SPHqZjcevUt3lDLPEcULkw1SWZFECY+mFtXc6P9dPlLlPqRK+WyZSDTNLpFKx1XS6rB1SSTOXJUQMwmVyLwjZpicnEQEju9FsxmWSyqmgyLRBSuhWvH7u+h/0LjtPcRoslCZwYIF7VZKq7fiRpXa7+miaSTB3qcrqfNboOJIEeOHnxCPiX/zVw908B7/lzlf+7Ml1uehP4hr+Bf/ln/zX+fPhXcXL3z2ofT/qes/Uso0AymQAUk0xRki7MKprclI/5YUsmk8ZkajldrspkGs5k2vEsnDUmmUoblE2bTJRsEPxdnC5HCMHIGqVJJp25bmiSTHwh3ru6utzYFn92GojnoZ3JdPl1OXUSHczPK6lIIMdZQ//T5bIkk6YuZ5emPrm7teBv26DA7Bh48kdF6LfS3fcBLAY5/Yz4HUtMJh7HQByDOIJVVNjkdZkuF7FBUmu2QUFIabIlIGpOS5JMLPIRYBN1uSMkT57A/4M/aPw6T5pMmLSHfgPiXqOvy8nP5uxxoSoH5JJM8sBQrXd0k+Xyj2+dJBPnHEkP4G9AJszXSTKVJ+u21dELInn+7HO5H6YOXJqSTOL3vNA0WU5perPXa02d1oG9RwmDtWJjpI2c3Dq8jdIkU2/T5RbZ501KmeiVFOWVKroymbZdi2edod9Ari4nT7mf2/cwP5PgQR34eyxufrbhp93sIZlM1CQAqa+7NOnW5BaeLcTJqrFiXLdvoLYSoVRUFHJJpkNPGEvh44etkkx91+UAgMraQP714JzDn8VpkokspMlUk2RqA4UdUgr8XctkykVg6WQMvlhUJn3pFMRJ+llRi0E6mgLg1biu2sT2OF2OEII9z6owmRyTZidrzg5ADNihzmQK5HS5yzeZ6OEBrp0Dz4Jn6Z9FCwnNz13HMibTatPlAOBg4uDJRbh04kq6IdUtqBU7Tzdh7tf+ujCfvv3viZRCSanJFBffc7a3g3/DvhzzRL8hKo+s1j1e2xDf604s+DPx2RZJpppN1uSG+Od5s8mUTZfTgb/bLcCimInNCeeiijagybTrmbIuJ5+7ZdPl1CZjY0mmzdblyu/JkTnCIl5owd+AgBAnJSaTAlLr6nITW/zZaSiMj25Mpsury6mTaDOZ49m8ek33owSWQUDVgUmfBznaJJOsy1lGMV3g7gKLIkcqktw5y6CiKgcUeUxKd/4kAALy5JMAlieZ8tcRxzSKxnGn6XLJIKk1QghcU5PAmUgmU12aFACPfAQbSDLtfuf7YR4d4fM/+EOYffgjtV/n2QbGkQ8+bl+VA8SGntu2frocIEwmp2Qy1TCZnps8V/t7XItWzbpOj1O+J3uYKNiV0VPWWnU5oFiZ85ebTIrJ1DhZ7pKlHuNKTKbSYcam1TUllDKZBq3LyefvKsm0VFcm07Zr8VRUMDqqWpfz4MRq8lV1sUCcMUziwzai9MIzZJKJEALTpIhXOE25ObqJ07m4+K9cl2PhYAwaOp0UeD8KxsiOn8I83I66nMHF6xDnbkLhIgZnPE0ykbkP3ypBowHELEbM460wKfJSJ3p1z58tbxxhzGDIE8fkQlN5K0mc3Jamy43kAqM8YS41mfqtou2NrGJdLkqKiyxCAG8PtkzcFNJoEvx92XU5ALBuHGH/AjjxM5PJv5C139x1LJsuV5NkUlMDl5hMMePCdGhQI0h0JKH9ZS7TH/4r4GP/G/C1fwU4fIf25xJLLJbKxqZaEC4iff2MhcuYTNmmz51Y8CV3Ko5YfZJpKk2mlkmmwgZF8UHaMpkSLhat6uuHrMt5tgR/y/d6UmMyqdc6TTJ1YyTWyaKbBH9XWRkqyaQDfwMyyXRWnC6nmEy6xPNUjnI+DyWTqc10uejyp8uBUiSGhxECvPy0mjL0Vfom6T/JRCwLoBRMJpk4Y+Dzea4ul/vceHtaJpNBiZhOeO9DwtS9/e7qL/L2gaMXQB6Luq2aOlan7DriwK6Av9tPlwsilnJp+pajM0fGRwLg7+urkYCoy4Ww1mbL2Hfv4i3/5Jdh3riBe3/xL+Ls135N+3WuRTGJFmAdTaYwYeBWzXQ5QCTZ7HJdTs9kujVZkmRaI5WRSKO8lyTT2ibTitefw3cCIMUJc8HyutyX393Hj7zvzfj6dw4P9G6rkVxTzILV6nLWEHW5TSSZ1q3LcV5Tl7tKMrXVlcm07Vo5yaTqcjLJtOelk6R04G8QAouEsM0ovWkPtVBQMmy6Ul3u5vgmOBMnKNtelwMgeT/FJJMTcpCFD2NJkilMwl6B2mmSiVeTTAvJcnEnFjgXj1eXZFL/vQ0mRV6e0VyXy5JMScbOamEy+XE2XS5dqI/lJrQM/1ZMJqu/JBMgNs95s0QlmQpy92AH4qSnCP4OEKB/CH4buTduw4mB82cP0z8LZmKhl6/zmMuSTGGLJNNUvF+fzJorc41JJk+TZAougH/xk8DBlwBf8xO1P7dSl5NSC8J5jQHPSwyOvBjjiBJeYDIFsxiccwH+rjtIGMsF8sVr2XvY1plMuiSTYjK1q8sJJhPJmUzD3Xd2PUt8TtS1qlyXKxskG2cybSbJFCcMjKNywjwyR5jHcyziRT2TqXSN400mkyNNpqh7kmnIZLRO3BphBB/3NCZTmkZVSbY+08KEgLouuEwysbl8vhX4O78G0tTlxDTGHPT79rvr7yd3XwJ5JE2mlkkmIqfLhQkTqU7OO02X8wdKMgFijVplMslr16yhMherutz6j9O6fRtv+aUPwn3Xu/DqT/5lPP3HH6w+Tgn+TrqaTDEDTDGMpVDzbEoylQZBqPXO7XE9k8mxNM9jB0WsBKPfoCyDrsWtWznJZI+B/bcAjz6e+2HLk0wTx8TPvP9d2HH19fVt0NgW9+hZ0O4gKK+h6nJtGWGWNJlSJlOUYzLpsAVdFAeCTWnXTZe7SjIt05XJtO2aP80qGB1EHNn7lzccYTLJKo9m8QgAJo1gWSyXZBr27WFaxsomk8HEhWad6XJDmSJ0Oi0mmbxD7MlU5zImU/9JJslbYorbkr0eiuXiji2xEU4SLDTgb2VYbJ3JpKbL1Zy+F5hMEzneu43JFCWpIZsmmcZyExqWTKZ0uly/z83uyCpOl8txeVJ5+3BC8cYrTAiMfQTgW/H6jW/dAQDMX3s1/bNgJjZdhpfd+JeBv5MW4O+DsViUPz5v3ow1MmZUkmmRJa/wb34WOL0HfMcHGl/3OpNJTYKpNZkakkyKZaEW2O7YAmMcoZ+IulxdkslyRVrn4pFYWFsj7fAJyxDTfwqLLUPV5dotYGMVv7+MJJP6nBgWAFIFf5c3KD0wmTYB/lZ1lXIywzM9zON57X2jnKoF8nW56jphR/7ZLBKbrTbXiHAb6nIAqDvBiCxLMvVflwNEZU49z2wuMQYyybTIJ5lqmEyWIQ2xV39XQL/rdPd9IIm4h6mhFHUq1OWsHHyXxWKj1ZIjmE/29i3XotWN3liuoS4a4N9xIEymDaX1jb093P3Ff4TJ1389Hv7sz+LRz/2dgik0tk2MowVCd9zwU6oKYwao6YD5+4IyFZNAy2QilpUCxtskmdw1TSbFlTP6qstdBpMJAG68WEwy+fVDMF5PmsqK2cUqJhPjvdblbF06uunr5d8lP10uggFOjPVNJvX9V+DvlXVlMm2zWCIWGCskmWiJyXRz14WbKCix3mTaHc+wt8cupS4HiJTBKkymm+ObMLi40KwzXW64JNMESW5hv+vs4mAu3fiD5eDvPmt9aV2OVZNMfi7JxGZiYawDf2+ryXRnegeu4eLA06fF8kwmQyaZkvPlE+byBo6aAlObZFIn5T0ymQBIJlMe/J1UN3nePiz5+PJJJhb7iAkGq482aXrrLgAgePha+mfhXDzmvMlEKAE1SD34W56KNZpMMsm0DP6t2DfayTFlJtOrvwN8+H8C3vsXxLSnBimTCSWTaSynpxRqNDmlDA6NyVSejqZ4av5FJMHfDdf4yQ3gXCaZahbVhBAxiUo3XS5pO11OAmMvwWTa8SyEMRNsBTUWPKeKoSinqjXVJbrINEhaNVlH6cSx0uJ/bI1FXS4OtKkjY7qD5OKisCHO6nLVr1d1uVnc3mTaCvA3AGqPsWeEeOWZxmRS6Zs4AAjtPM2382PxvBT8re6ldDySSaY8k0lfl7MNCjz4fWEyNJpML4HSdtPl0pqV7RQ3UXG3iahBLtnbt5zGJFOTyeQj3ACTKS/qunjTB/4e9r7v+/DkH/5DPPjr/1VqDF2f2JhGCyzsbvf9IGaAOnzIv37516J0beZhUEi1Pjd5Dtfca7g5uln7e7w1mUyq8tsb+Psy6nIAcPQ88PQzWQI9OAeIUWH0vN6k1hQrmUwx67Uul4K/WxqLtmvAm1rYORSfLXG/IeI1WrcuV2sydUtbfSFruNXclbrLPwXAV2IykRKTybUMXDPFYkMH/gaAb/vp7wcMA7/8uw8ADD9y2LRXYzLdGt8CZeKtTFeM6w5pMtHpFOwzn0n/mxCCu+EOgONLZzKpjaoCoOqSTN7EApuJpEZTXW4b6lZ5ve/2+/AbP/AbS6fLCfB3h7pcPskkT4vJRBrDlbqcTDL1OF0OyCU0pLSny94+nGefBkpGYRj7gLkdr599Q7CB2KOs+hDOxGtijooLPcOsN6njkIFQAqNhEXwwaWcyBXFSz/Jw9wAQkUBNIuBX/jNh1nzjf9v4M4H6JNOOPKk7W9SZTKFgbFnVeH55fLziqfmzqDnJBAgu08UjYfo0nNwKSHC+LteeycQ5R5jW5dTo9iHB3+L5OF1EcE07S7JIlZ8/+KfCIN5QEtGkNE0hraN04piGyfTKxSu10+XodAJEEbjvp4dPTXU5Bf5eJOK61q4upzZ5l5tkgj3GNSvSJpkCdQ1Pwt5TTIAw8Fhal5PT/MZjeLGBRViqy0UzcS2RCcEwlkmmex8WX6ObLKe0dwdk7zYA3r4uJ6fLAfK1I91MJn/gJFMFvptWfevrciQJN1aXK/xc08TNn/lpmEdHOP75n0f89Ane9Hf/Lq45Bs7jAA871uSjhKUHfzwIgIlMLRVMpmKSiYVhwWT6rrd/F77li74FllFf33ItIx34s4oi1h/42zIpwjWukWsNHjh6QaT4jv8AuPUnRKrXmYr77etYY0cxmVary/W5N0wN7pamJzUofvhn/1QaMFDrM2KN1k8yKZOqAv7WTNW9klZXSaZtloLHboDJBAAHpvhA6KbGAIDheTBsG740eryBk0yGZayUZNqxd+BScfNeNck0ZF3O2JlWKgq3AvGaLJsu17cZllZuogjUJEhym8c0yTS20oj/QmMy+YlYlG5bkokQUmswAUW+jDEVC7d2JhPLTZeLANME8WSdphb83bfJZGMeJukGT8tk8vZhL8Qpef41DKLtef1UfZQfZyDtSCaZrLLJZNF6JlPMGlNMgEh/GZS0SjLVbk4MU2wM50+A3/p54OHHgG/571vVq2pNJmmEnNUAyVkQgNi2NllVXmCnSaZZJJlMDc/J5EbGZGowmSqVFdq+Lqd4RMW63HD3nb2ReKwC/q1JMkWlDYp/urGqHCDqhjHbRF1OccKK74GROcIsmsGPfS3LT5fYVOaHLvHsSnM8YOK61gr83QTKH1L2GDtGhHtPq2Otg1hew5NwkJHixPVS8HeaZKqrywGFNFOUcMFEufchYO/NYgx60+96i0hQsiXg73xdzs5vorY5yWQZVfju6JpIozUkmUgi63I9GJ+EEBz++F/CzZ/5Gcz+3f+Dz//In4Px2n0AwAntdk8NEwaoA+O8SZg/pCqx8ngQptOlAYAS2rjuAST4e4UDXqU+k0yOQRGuYYCtlWS68aL4p6rM+WcbS7FephzTgG1QXKwA/h6qLtelImnaBog0ONP1meVtoC4nOSalJJM2wX0lra5Mpm2W4nqswmRKTz+yG9O+wRAaWVe7TpdZl1uFyUQIwb4pjLi16nJDTZeT4O98ReFoYSOhgLFfbyiqqW1DTJdjYVhhZPkXEQglsD0zq8tZ1bqcMizanHJvk4pMJmEytavLZYtqHobCLFCb8kpdbhiTKZ/QUI+xWpfbgy0ZA3mTKZIm4TYkmeh4DN81YD7JJmDF8r1njYonuE112yRiMJdcGygluD628eRiOZOpcSrR6Bpw/6PAr/8t4PlvB57/tsafp1RnMlkGxcg2aqfe8TDSQr/FYxXXcvV4vUm+LseW1+UUk2lJkqlQWekA/lbmiGXmTKaGE/dNq/A5MZwGJlMuybRBk8k0aMozWUd1dbmRJUymkIU1SaZqYlOZH7okk0UtgNPMZGqTZIoaQPlDyhpjQn28erKoTPTz1fTNOOgV+q1UBH9nSaaRrMulawM1xTBnMoUJg02JgH4vqeACAHnr+wDCwc8bQNjI1eVct1hZ6WwyDZlkMqpJJmoAo4NGJlNqMvVofO5///fhTX//Awg+/Wl8/od/GADwrKvJFDNQW1eXy302K+DvANTu9nvWZjKlhwU9MZnW2Myr711pkuC1LxbXg0fSZArOG6HfryeNHQMXQbvhHHlFMYM5QF1u1clt4vojK41R9UChk9JyDleTAAAgAElEQVTmQfVeKEymq7rcMl2ZTNusxepJJpqefmSn8ns0gW/YxSkVGvlxAoOSXt1qnUxrtbocAOxaYjFGVwR/h2zIutwESJK0lgAA1+YEZ2PSaAAOMbUtNSfDUCRD8tPlZhHcsQlCSLow9hvA39tgUnRRnsmUbr7Om5NMnPNCPYCHoXgOU5OpOB487fb3nmSSm+e5Mpn04G9bXgvyTKZgy+qOi10H9rNZ+t+xTNFZ46LxQc36JFMSLU8yAcD1ibNekgkQ8O9Xf1ssTr/5by/9nUp1JhOQm4KmEZdJJp38JiZTmyRTNAfOHjQurCsnein4e/m1XFXFTEouhcm054nn7WQeigpchcmkmS63QZPJ2Bj4u6YuJ5NMgP6+oZJM7Cy7Tqn7Up1xCW4h5Be1P7OsIE5gGxS0hypNJ9ljjOAjYRwPTouvc5pGHaguRzw3BX9zZTKNRnAtA4znTvHTJNNJ+r1RzHCHPAYuHjZX5ZTuvg+EcvAnLzd+WTaVzClWVlKTafnzIu6HmgONnuSYVL8ZnRzVT5dLYlCebJzJpNP0G74Bd3/xH6XX9GN0N5mIXW0lFF6LMvi7xGRqI2EyrcNkUtfxzT+flkHWqhSvxYQzTDEVNjWZzt5AJpOJ2SpJpoTBHmS63GrvxxQNYY2AcLb8G5pUU5cD1ETGqyTTMl2ZTNsslWRapS6nEim5G9MUMXzDwtNZ80m9HzG4lwDpNFZMMgHArrF+kmmwulxaUcgMjJ1zhmdjDr+0yclrCPMmM5kimGbx9QguonSTqkymQGcyxdsJ/l4mJ59ksm0Q2wa7aE4yZVWQXJLJtsQi0LCBUFOXI7T3tEa6eVZJphomkw2xeItyqZNgADOzi4L9MUa5TWG8EO89e1Q0mZqSTHHEmg0VqYOJjcdLk0wNTCYgY+h9408DO/UTfcrKTKZqzWzHtXDm1yWZQi30G8hPlxOP1x6ZAJF1uWVJJlXDOb3XnGSq1OXaM5kK5sglM5lgOhomk0itpVXEHupymwB/Bw1JJiVd6kix5/L3IrYQfCYt2B4AgQ0Oab61BH9fNvQbAGCPYDNxHblX4jKladSB6nLUzcDfSakuByCrLmnrcgxfxj8l/uPO8iQTjp4HMQj401cavywdIODYuRHdSTasogVPKGYcjA83mbg2gTM+rE8yyXVUX3W5skbveQ/e8ksfxKdeeAkf23mu9fcxxhEznlbf1FARAMXXosxkajh0qJNriQluq16L1HW8t+ly64C/o+IarbNuvJCry52+IepyADBxzJXA33HPdTn1mVz1NQ/UEIce63LAVZKprbbgzn+lWq3DZDJNwDQLdbkJjxEYFl49aY4Q+tFwnfq8THs1JhMATE1x4WdYMWKZBMMlmVKodGZgjM4CnIwJjhfHtd83xNQ2YmVJJtMuJZkuohQcrOpyiWsVDIqhHmcfKo8lpZMJkiVMJvW1+boclc8h7Im+Lmd6vYMjC6wZ1E+Xs+WaMp9kimT9cRumywFAfH0H09PsPcakyeSMi4s9w6SI10wyHU4cHJ+3STI1XB/f9o3Ai98NfMWfX/r7Clo1yRTWbyrKTCFKCZyRidlpAHA0g7/VlCbw5eDvWFOXSzrU5S6JyVQxmTRMpoJBsum6HKUpNHcd1SWZ8iwWXV3O2Knei5i/0FbllCgyg7wt+PvSeUwAYE9gJuLaca80YU4kmVRdbljwNy/V5QBkE+ZqmEwvJp8UiYqj51v8MgPEtsBPHjR+WQb+duAYuTSBqoy0SDIpw2eoJJNbx0WZHNUzmeK8yTTM+9J529vwkR/6SXyOtwd/qwMCQ8dkKoC/i/fBMpOpjZS5uWplrt+6nNGJz1PWWkwmQMC/z++LQ/8l1fHXkyaOiQu/u8kU9l2Xs9ZMMsnpv6Iutynwd53JdJVkWqYtuPNfqVaLZwBI1svvKGrb6cIBADwWITBs3F9qMrHLMZnWSDJNDHHhfxw2jK1t0JDT5VKodI73Y5/M8WwCPF7UcxOGmNpWrsuVp8uVk0zMtatJJvb6NJnyTCZA1BqX1eWCqLiA4VGYbfidaY3J1P/zojbPJ3Px2mgrXt4+CACLGNlryDkCaRpuS10O1/exe8ExlwsGZTK54+Jmv3G6XNsk01TU5ZoqxWGyhMn0VT8KfO8vAh0XYsQU5ozOZNrxLJzWTJcrTxPKqzIdDaIyd/FMfEbNpuv8JAcUbjSZaHHKSgfwdxTnwN/KlBowyTR1TRCSZzIVDcaKObvhk2zLIBU+0CpSdZIy92hkZotjHfibasDffL6onUALABTiumBRC5Qsf48Lo+6SJ8sBgDUCiRYwKa9MmBMVL1WX658JJsDfJSbTaATPXp5kChOGd0afBN703taGLHVc8NlpdnCpEfNzJpOVuxeqz8QSeDQwPOTdsWhDkukxoLuOSyO5byZTWdfHDp7OQrCWpnJmMqk1Wb4ulwd/b4bJBKxhMvUI/rYMsl6SKWagRFayV9HRC+Kfjz75BmMymZiFq02X67Mup+5hq8LeBRrCkHW5dZNM8vu1SSbjarpcC12ZTNusxVPA2+u8WVEijlO4MTlJBN+08cqzJSbTJZ08CpNptQvLmIyRkBgP5w9X+v4h63K0VJfjjIGenOFkDDye15tMgySZFGQyCuWmvThdToGD1elr7NkV8Leqy22NSdFShbHNAIzJFMmSupzqZKtFGgvzJtNOdbpc5LeqHayr3VEZ/K1nMgGAQ8zMZEoiBHL9sC0moXF4ADsBTh6LugfzfcQUcNzijb9pulwSJa2STAcTG0HMMGtgwwUR6wViTCgV6VOtyWTWTpfjQVifZNLwKAomU6skE5ZMlzNKdTkF/m5hMjGVZLocJhOlBDuulUsylU2mnDnLeS9Jpk2CvytMpnxdTpdkmlQPPJjvayfLpd8jk0xtBzssBeUPJXsMAo637tLKhDkxXU4mmQY4BBDg72y6HHEcENNM7yPzssm0yJhMZniOO/HngDtf1fr3EXcMziQsvEZpksm2i6neDkymNNk7WJKphosyOQLiRbWuDqSfccFkGs78vD6xkTBem0gtS32mDXdZkqlUl1uByZTWNFdcfyuT2+qhLueYdM0kkzC56+q/S3VDmkwPP/6GmS4HABN3S+tyayaZwjjZ4HS5BpPJuqrLtdEW3PmvVKvFs4zvsYKI4xSYTDTwEZs27p/Uc38Akc4YapGQl2EbSMLVLiweHYORBK/NX1vp+0M25HQ5ubCXBkby7BmQMJyMSaskU5+bf2oX63IqycQ5F0kmaTIlsxlACIguySTNsDbjrbdJ1STTFOyiGRzox2oSYwn8DYjFXxn8HS8G2cRMHRMGJbm6nCZNIBOSNqHZaxj7iORibFtMQvuGSNScvvrHAAC+8OHb1ffXJpJM18fitWmqzIWJxrDbkIhl1dblak2msL4ekZlM2WvvTixcPBP3gEYmk7efTdpakmQqTpfrzmQSdbnhmUyAqJaezBWTSWMyqdc6mou/0ybB3wZJjbZ1VHgec8onmXSmEBmNAMMoJJnYYt5YlzOI+Iy0vb4HatF/2ZLw1rftUX2SyZJpugHB35xzsPkcdCReJ1WXSz9P9lh8HnJJpi8KPwkK3s1kGk3BGAVe/q3ar0mZTLnpcoLJ1H5YRVqXG5DJFMRJNXk6lga5jst0CXU5QAyVAIAns+Y6tlJqMtkak4nS7NpcSTLVJ1vrpF6vVUHGiuXUR5LJNtZlMq15aL7zHODsiomxLHrj1OXs1epyfU+Xs431TKb0YGijdTkN+PuqLtdKW3Dnv1Kt5k9X4jEpEccuMJm474N47tK63CJKBgM35qXqcsum3+nkEg8JjfHarLvJxDkflMmUgr/PxMI+PhYcpvOJ0S7J1KNJUajLmRkjK/ITsITDHcv/Xy6MHdNFVGKvDFHr60PlqRZ0Mi6c8OtUZt7wMFpSlwta1Q7WFSEEu56Fk4V4LbQbPU+aTCBZGi32EWyZyeTdFLDU2YN7AITJFJrVx2c2JZliBqNFBfhgKk2mhglzfSWZgGaT6TyItWBWUY+oYzJVeRTe2ELkiz9vNN4IERPmgBWny3Wsy10CkwnI8a50Sab8pCy10d8k+JtuGvxdPK3PJ5l0hxOEEBiTYi2YL/zGupwpTaa2hx3bA/4WG4W37gCv5JhMnPPMhE+CwcDfSBIgisBms9Rk8spJJkLE+y1nMn1J+AkwUOC5r2j9+4jjglu7wMsfqv0alksyqQOXwnQ5qwV/K6qa2n3KMSkYR3X62ORQ/FM3YS5flxvwfXkg106Pz5sHSyhFJSZTYbockK0jSqYHDwLQjkymdetyyig3e2AyWQZdawLn2tcfQgT7TH123kh1uRWSTGHCYPVYlzMNCoOuXpFM69mWt4G63EwcOmjWJJUE95W02oI7/5VqtXgGjFZPMlHbKTCZmO/D9EYtwN8sZQMMKVVnqdsoNolyA5yylUwmBa4eui6nkkzxY2Eyseu7jUmmYepyciphKcnkz8Rz5E5EyoDN56DjMSxqVepyfuL3/jj7kGlQUJI7QWxTl2tMMk2rcf1o0WqxvgnteSKhkTCOKOHVhb9hAfYUNnLg75zJtC2v3/T2mwEAi9fuiz8IAoQWgVlKvBgmqWW6JRFrNXnyYCJeuyaTSSSZ+rk+1plMO64wbs41E+bEe66OyVStyzmTjDnTmGQCcibTCuDvFiZTmOTMkUuoywE5k0nLZMptUHowmUxjM3W5KKm+zkApyVSTPKLTKZLzLHG5rC5nycRv67rctjCZpMl0d8JxfBGmG6xseAOV4O8BTCZPPHfM99N7qXgMmtpSyWR6Pv4k7jtv7VTbIbYNbk2B+78rKtsa8SAELAvEMLIJTwnrlGTSMeD6VGqOlGsrLZJMzLBXr1CtoFWTTKankkyla79aR2hMprr7QZ3WBn+ndbkekkzrTpdbNqijjW68ADz9jPj3DV7/L1MT18QsTFozwpRixnt5nfNaZ3JbNl1uLJJnLQaQ1Cpa1OItBIvyqi63TFcm0zZr8XT9ulyOycQXC9hjrwX4+3LqcupUfZUJcyxmIBQrmUxDJ2/oaARQmlYU4mNhLFkHh43T5QYBf5smQKlMMmX1o8xkkiaUPH21jWpdbohaX1/Kb5jpdLoU/K0WZel0uShKuVb66XJBq8X6JrQ7EptntUDTLvzlhLk0jRYHCLcsybR3+60AgOih4K0RP0RoVzcHTUymOGTN/CGpQ7kReHxRf9ocxpeTZAKg5XmwpulyurrcOGcyLdsMtjGZrFKSKZ0ut9xkUsBYu5BkuiSTSctkyiUAfWnEbDLJZJC1TumVwjTJVM9kqkvAlq9zy+pyymRqn2TakulyljBynhuL50qxKVX6xjUNCf7u/7pHXPH8soUPNmuoywFFk4kleCH5A3x+9KXdfp9jgxtj8fe7/1Ht1+QTkWmqN0oyU6rVdDm92dmX1OFOBcCreHK6CXOJMpmGXZ+oA4wnDfeWvNJ0oo7JBIjXg1qV16VpEESdsiTTatcidR03emAy2SZFzHhnM0RpI3VdBf8G3jBJpokjXvMu8O+EcSQ9M5mA9YzFIGZiOqaaCLdOZS6ca6tygNorXCWZlmkL7vxXqtXiZM26nFOoyzHfhzMd48kszKaXaORHyeVMl5MLrFUmzCUxAzFXM5lUimOoTTWhFDRXUYgfC5PJPbqJR/P66Xhpkon2uzgitg0eRoVpf4sLaTKp6XJyYWxTPZPJpsOeEm5K+ZsbnYzBLi4a65uFDQo0SaaKybQYzGRSSabGEb7eHmzOCkmmUL5sQzHKlml/9wYuXIAdPwEAkCBEpDGMDMuoZzLFrBX4+9pYbQQa6nI9bpprk0zSZDrTTJjjYVS7qdAZjAWTaVmSadomyVSeLtcB/J3kWB7bYDJpmUzlutxq0151MihJx3+vI2VUVcDfuSSTZ+iNI2M6LdSC+cJPkzY62fL+0wX8vU11uVsj8VwpLpOfT98MBf6Wzy9fzMWBjUwyqQT5PKwxmR5+HCP4eGXS0WSybXDIv1cNl4kFfnodKcB30yRTm+lyxUOXvqXM80oCZ3QAgIgJc2VJI5kPbDLtjWxQ0nxvyUulPE31XtHV5UrQb/V1dYy+Oq0N/maq9txPXQ7AyvDvINoAQ7FgMr0xmExjR9xnZ0H71zxl//VYlwPW4x2lHEWVQFqnMhfNtNBv4CrJ1FZbcOe/klZJJKDB69TlHLtYl1ssMNoRi5n7p/VpJn8TF+UVpE7V4xXg30nCYZrGSuDvNCE04KbamEwy8PfxMehohP39W41JpiHqcoAymULBuFFJJmkyqelyClZqG5rpcknwukwxAcWbmzGZApyDzepvUn6pHsDDEMSSm3hnKmHBuRtR5A9nMo1snCxCbZollbcHh7EckynYurqcZVg4mVLQ42cAABpEiDWbGMMkSGL9hj2JGMwWG13ToNgfWc1Mpr6TTHG3JJOoR+jHrqtNX/7xFk2m9ZNMFfguIQAxRFR9iaJCXe7ywN+ni0hsOitMpp7rcnQ93ohSLfi7ZZIp6TBdzjK6gr+3pS4nnosjR5iZ95TJpNKopjEc+Nst1eVKTKZFxWSS0+XufRgA8Or0T3T6fdS2wWMGHLyjlsuUB0YX4LtxAIBkrLUGDZ1kysyw0mbPMMXaWZdkUqbZwINJDEpwbWzjeNaSyVROMkWl77NcwC5V5TiXTKauSSYF/l61LqeYTJt/3dV7aWWTaVN1OaU3ynQ5aTJ1mTCnDkT6rsvZa5hMoXq9ZXJ1/SRTjclUTnBfSasrk2lbtRAbqrWSTDkmE48iII4x2REnH02VuSC+nCSTShrEK9zoWMxgWgZOg1PMO15U1AZ7yHqQWNirJNMxzMNDHIwOcBKcVEDaSkMlrpTJZFgUcVw0mdIkk2IyGZY2ydQnnLxPFZJM0+IUQJ18TZKJ5pNMQDHNFPuDMZl2VZKpaeHv7cNO4sJ0uW2rywHAxY4N86l4HqkfIXaqRkTeFC0rjpJWdTkAOJg4OG6As/aZzCCm2bkux8OwdlOhDDGaqzG4kw4m080vE5N1FONEIy18l5odmUyXm2RKGEdIrCV1ObnR33BdbrPg7+Lr6eXSJ3WmsQB/56fLLQSYukYqSWt3qcttRZJJXM+nNMDYNtIkU8ZkkuDvFmbKulJ1RLZYFMHf9hIm070P4xHfx8y73en3EUvc03H3JeDehwDNRMO8OVGA76r0bYtk8tBJpsaa1/iokck0BOC9rOtjp3OSyVZJpkpdzq0mmaII4Lwzk0nLAusgZT6YPdXlAKxcn9pIvd3bB6byM/eGqct1N5lS47OHxFpejmmsUZeT95tN1OWieUOS6aou10ZbcOe/klabMJlyTCbmi9ObnT2x8X31WXOS6fXGZEpiBssUF82uaaahEkJ50Wm2sI+Pj2EcHuDIExu5ujTTcEkmKzWZklAkFPxZBEIAeyTB37MZ6FjW5ViVybQtKZiuKiaZlMlUz2UKloG/gSL8Ox4yyWTh3I8xj8Qioo7JZCXR1ptM/p4H90QsFowwRqKpeVFTMJnK9UbOOFjMW4G/AWky1WwEOOfypGzoupz43J3pwN9BAGLVTZerPlYvbzIt2wx+yTcDf+2z2lqGkjolLqQJDKuY4KtRoeZ1iSYTAPjcEumr3Aa8cAquNvob3GRsDvwtfkb5taaEpkZTXb2NTqdI5DWOcw6+WIA01OXUtd1qmfzdSF1lE5J1ORLNcefaqJpksigQh8PU5WSSiZfA37YcPlFMMu0VTKbf4e+A3dHEIbYNFoXA3feJn/X4U5WvYWFQqN2m8N04aH0wohs00KeUOaKFBE8Oa6bLXU5dDgCuT+zWTCa1ybZtC6C0Ol1ucgPYKZqNTBpRqzKZVq3/pODvHpJM6meumvjcWL396HnxzzdcXa6DyZTW5bYT/J1NCt1UXa7JZFodTv6FpC24819Jq/lT8c+1TCY7ZTKxhTCVdq/tgJLmJJNgMl1GXU4ymVapy8Ucjtzcd+UyDQ3+BtTksozJZB4c4nAkxu4+Wui5TEM9TmqpupwBzgHGOPyLCM7IShMRbD4HkXW5cvLKj/2tMii6yM6dTqgpgPkqSVl+aWSzqMvJv7s8Oa8kmQZkMgHA4/Og8BgL8vbh5E2mSEyXMwitTG+7TEXXJhidheCMCZPJqaYNlEnNSpU5BQNvw2QCgIOpgyc1lYawhn2zKRHLEqfRJTWDv+tBr0GcVB5rp7ocIaJ60qACv0WJGq2muqjNiUkvc7qc+LwuEvn5yHGZUr4DIDbnprvRJKJFSTr+ex3Vgb8BwWUiILV1cGNnKthzjIm0BOegnn5hDQCuNK1M0jbJtCV1ObVZCGfCZHqmTKbcNTwZZrpcAfw9n4OOxWMjhGBkm9UkU+wDzz4HnLyM307e3nkzrziLuPuS+AMNlylflwNyBy4d7lnKpBjq9VZmVrckkzhwJTVTo/rU9YYDjLJSk8kystcvr+/4APDd/0vhj1RzoTOTSZeg66CYqbpcD0kmY70k08aSxze/VIDWv5CTTFtel4sSDs4hOIppXW62+gNpqMu5loEo4RtJIr+RdWUybatUkmktJlOuLidNJmvk4caOi1dP9GNso4QhZvxy6nL2ekkmV0aEH84edvreyzCZaA62Gh8fwzw4wIF3AAA4njcnmQapy0VhuilPQgZ/FhVqNmw+hzEea8HfYRK25nVsm/KnE7RFkkmdgquNKIuiXJJJLkbyJlPk145E3bT2RuJxPDxTJlNNXY4lCBN5PZBJpm2Bfisl13dhJBzJyQmsIAHTmEwqqRSXFicKXr80tSN1fWzj+Fy/EUhB2j1toohlVTcTEKwWyyAVk4knCRDHjdPlyq97cbrc+n+PdBJVwWSyVqjLXQ6TSRl4Cy5/b5zdG4MoX5c73fj4aoNScI61F6pRwmBQop3uNLJGcE23dhADnUwBxsDmc7C5MF5U0kYnTxoOFmmZZNqaupzcdIQz3Nkf4d7ThTz5ziWZkoGSTDIpxi7ORd11lG1kXMuomkwA8If/CgDw2+wdsDtu5lUFHvtvFQkYDZcpP10OkBu9iEmOYHtDUfwdhk0yaVlCk6PGJBOxLiHJNO6QZMpdG4ltV8Hf3n5lf6AqdZ2ZTE1mXQtFyfbW5TZmcn/NTwA/8n8uPXR5vSg1mfwV6nJbCv4uMCjTulzzNPVGRbPMrCopW/dcpZmatAV3/itptdhAkinHZFJ1OeJ6eG7Pw6sn+ghhITo+sMx1mEwJh+s4ICB4MHvQ6Xsvoy5nyLoc832w83OYh4c4Gom63OOFZmEEYd6YxOw9YUJsG0yCvwGxSV9cROnmlHNeSDLpwN+v3yRTxmQyZJKpsS6Xntw21OWUycS55FsM8z7bHYnX6+GZX3iMBbl7sDlHqDgVcYAQBM6WvX7kQCym40ePYIYM3K03mcomtfrvtkmmw6mD8yDWblzS0+WB63KEEOy4Fs7KJlNaj2gwmUpGkmFRmI4BQgC6gcViWpfLP18tmUzFupz8u9FhDzj25OdkliiTqZRkytflNmwyqZP/eM00U5iwWk7GyBw13ttS9tz5ObhaJzTU5Vx5/TJamEyF+sJlixoikRPOcPeah0WU4PgizLh6Bgc4Gwj8LQ4a4idinafqcgDg2bRalwOAP/y/wE0Xn+BvWTHJFIpk4t2XtCZTfrocILkoiUoytTsYaRwy0YNcXYpSaXwoqurluoxc79FL4EYeTOzae0tZYa56SBy7ymTSKE0ydWQymQaFZZDVk0w9gr/Xny63IZPb2wfe/L71f86WKK3LhSvU5XoafKJkr8g7Sq8/Fs0lV9epyy1qD4VTk2lFY/YLRVtw57+SVimTafUkk2AyiRuTSjJRz8XtPQ/3a5JMasHlXUKSKa3Lrcpkskxc966vXpcbML1BZV0uPhapJfPgAPvOPiiheDTX1+WGMm/y4G9APLf5JBP3fYAx0NEIFq0Bf78BmEwqydRUl1MbKEIIOGPFVIlTqsuxWGxiWi7Y15Wqyz1SJpPuM+3tw+YcQT7JRAls2j/8tovMI2HA+q++AjPhYBpTJf9+zStLMrVlMomfras19M0cqTOZAJG4qSSZ1Ml1TZIprEmRuGMThm3Uplu6SLvRa2sy5Wtel8xkulB1OcVsKRskPZhMyhhal8vUBLdVSaY6GVORuEzOz9NafVNdbiQX3bSFyVSoL2yD7LEwma6Lv9/LT+fZwRqR779BwN/i9UiePhH/nUsyjSyzOl0OAP74N5DcfDcimN1NJmlScM4Fl+n0ZeD0lcLX6OtyksnU0pDxowSE9A8GVlJmVm2SCahOmJOfbzrQAI68DibieXzaYsJcvpqtEAbLxILmQ4cmuaax+nS5HsHfziaSTNvAhNsyTd0V6nJpYq1/JtMqr3femE1NprXrcjVJppQHd2UyNenqk7etmj8Vi+01IHOCyRSI5EmaZBIm04PTBZgmop/Vfy7BZJJ1udWYTAyGQXBzdLO7yXQp0+UmQJIgevllAIB5eACDGrjuXm8Efw9h3qj+f5pkChP4F5nJlFYqxmPBZCqNKn+9m0zpdLmJTDKdN9flVGQ/TZXUJZlUbHegE9S2dTmH84yrFQcICGk9OWooOTduAgDOP/tp8Qdu9fFtKsmkNgLHmlpD70kmu95k2tGYTCxlcNRPl9ObTBasltP2lkm70TPamUzpSGTjMplM4rp2HheTTGqTV2AybTrJJBfr65pMUcJq35Mjc9RYX84nmVjuMKpOI1kzonz5/VJVCbYiyQSI6kM0x519sQF55VlmMnlUvv8GBH/Hx9JkyiWZXNvAXFeXi32Et78SQHfwLrVtAbSP4xyXqZhm4kEAmruuOpasy6npci0UxGJozCbM6zZSBnctkwkALkrJ8NhHBFMAtQfWdXlvaVOZS+81si6nhvg0icuv6VqXA8Saf1WTqc+Ey9bU5d5gckwxQbJTXS41Poeoy3V/LxaSlOvW5ThfCv4Wv6q+ji8AACAASURBVPOqLtekLbnzX6mixTMRz1zjZk0dJ11Y5E8on9v3ECUcj7Un9cOOoM0rTSKsUpeLOahJcXN883UxXU5VsYLP/jEAkWQCgAPvoLEu5wywAFZJpnyyzJ9F8GRdjs3EyQAdiRrGGyvJZGRMpvEIIATJRTP4O50sJ80Bohav5elyqoYzFJNJbp4fnjfU5bx9WBwIlFEYC/D3EO+zLhrfvAMAmH/us+IPNCaTWZtkSgr//zIdpBuB+iTT0HU5QJhMZ6UFoeI31dUjxHS56rXcHVutTbdl0jOZ2plM28BkGtmCd3UeyedD3g8q1Z8ek0zrwr/DmNVu8F48eBHvOnhX7fcauQEHPHcYVaeRTGISLN+kDz1tbKnsMRBe4E3SZHr5yTx7jFS+/wYBf0uTSZNk8iwKP59k8vbSf13cfC8ArMRkAuRByI0vFWabxmTKX0dsQ4G/20+X86MNTfJqKVc32VJpIgap6JJMEcxLeU9eVynZ2XLDKH+gQRxHy+orK6vLdX8PezZdmcmUMP7/s/euUZJkB3ngd2+88lVVWa+enu7p7ulB0kij1zw0M0IICQGLkJfXgrwSCPlwsAFxvDJ4F4wxwgsCw8H4rO1lDT5wDAsWsrCFVys45mAQmD2A0YxGI2kkkDTSPHqmZ6Tp6qrqemRmPO/+uHEjIiMjIiMiMyJuVcd3Tp/ursrsis6IuHHvd78H74ioQMkUtsuVI+KlyYSTDIQQ9HWlXLtc5Xa5ckqmqU2NSNFDKdhjACw1+Dts1W2VTFk4HQlmpxHj3YXymIBw4eGZ1pRd7rw/Ybi6P8Ytq9OThyCfoIFBOZoBVBSu40HxSaa/eO4vwBjLvZvWVPA3AFhP+iTTNp8QnemdSVVi1atkCu1y5rEN1/ZmlUy9HjRFg8tcuJ4Lxc9SsVzrZGcy+Q9SQgjoYADvKP0hZToZSiY9pmRyhJKpHpn+amCX80nUFLucwRgs5vB7xjFhA9IpmYaDLRx0gf6VK1CQnBmjpIwfRZVMmxl2uVqCv53kSd9aVwtq1wXEznV68LcbZC9EsX62P0PGlUVyu5yaq13OdiLV14GSqd4NDkIItyIKkskng007RpBUFPwNVKtkevc97858r3gWeYdHIH6obZZdbqD7JBMrQjJJoiTQe4A1QldXsL1i4Jm9UdCspaM+JRNRFBDDgLuTYJfT1aARFMDUNXe8fQ+AxwqT3KLx1LMsrpq6cP8MyRRvqTRUxR/vxrnnoqbtBcRPHTByKZlmSSYTeiPX5Fa/uJIpCP7OZZfLVrZmYRG7nO2yyhrHAiWTW/zYXI/Bdpk8449kWOloODLzf6712eVKZjKJZ7ZG+WYBUcormcT75imZ2kymTLT0rqwY7y2UxwSEvmxmmfDG0eBvftNc3Zu9+cLg7xOWyeT6drn+WYydMQ6sg9zvbYJkUqIkE6VQNvi5nqdkqieTSfOVTHx4ONrnE5dEu5yfYxUN/564k8wMEJlhiEYdH9QPaE/DxA7tSDP5OIrK85dM/1q0/dyjmj4bhRKsdlS8kKlk4sHfALjt0RnDpIp0JNNaZw27KwB75jkAye1XgV0urmRyimYypdvlgvaSqkh4VU1XMnXUUsHfSVk9X/XWF+Fb/sHdCx4sR3LwtxYqkzJgux6o2AFvyC4H+HlXtr8pIUim6K4oY5xkWnJ99XKDv8tdk6GS6QDeZL5dbq3DrV1jc/4cwYy1bzYOP5MJAC5u9HBlN1QydYh/b9U0D6CdDpzdhOBvTcEoGsYrSKbNF8PUOdlTJvgbCJWPuPRVwJc/DTzx34LXMNOcGkcMrXgmk+k0o2RKJEf6QskUt8uZMKE1qmRKUsnGEapGSHK7XAKYyGQqGPwNAF1dWSj4uwoVE4Dg+bVQRo8s449k6BsKjsz5m0ECtdrlSlyLgdJcUbgLSOf26FIQWU4pJFNHy1BRtgjQ3nmyYrS3sJJJ+LKZaU5NHs8N+QTyuf0kkklU0NZPMlGVAGQ2UyUPonY5AIVymZqwy4m8H/OpJ6FsbIAo/PPe7m1jb7I3k3MkjtOgNeRFxJRMR3s+yZRglxOkV9QyZ7lWrSHqy0RUyQQAymAl2y6XpGTSIjv8xgpgCrucTzLVGDg67OnB7lMiMaL1oBOxU2jxCThVpLM7Do0h9gYEygv+oqwzazkM7bYxJZOf8abk3M3saApWjJiawIdVsf0nT/A3Y6HqRSw80oK/00JPFZVC1Zczxifb5ZTc7XLBgrlhkmnfEkomfp9OWSPtMW+/kzb4m6UGf89DVMmUxy730jPc2v3IU/Mn7/LZ5QYByXRhvYtndscwRVg1q5dkIt0u3OuzSqaOpkwrc9QOX+hcfHDaXlrkZwmSyfaf0w/+AHDmZcAHvhv40mP8e5PJVJZPUIJRoF0uuulSBygl0BWKSdJCT9V5M9+MkmmCCdMaIR56uoKORhNVsnGYvjqREAKat10uyGSqP/hbrSjsXRAaVokxUrpMOMnQN1QcF1Iy1WOXM2Lz8LwIzre4t7VuebucaKVLs8tlqShbBGjvPFkx3gN6iyqZQpJJTB5pt4uVjoaVjoqriSSTUDLVf2kQQqBqFI5VQhYbscsBxUimZpRMPGzVee75II8JALa722BguD6+PvOe+pRM0yTT8V5MyXQs7HIRJVOEZDrZmUxxJdNKtl0uYg+YscsBvGEusMvVq2QCwnp2IGWiRQh0fwFheRYPRaUUWg0NS0UwNIbYHwDED4qOLsoE0oK/g3a5AkHXWytGcrtcpPGnChBNzySZHI9hFBkfQyVTWvC3W7lVoJPUskJVTsrMge0yaUimPUEy+WNZaJdTuIoJqC74e0Elk+16hcOgBahhgGgavKNDeCN/Myrh/hJ4ycZL8D9s/Qj+5ovn8IUX0gl4QEK7nNYLdqgvbvTw/I0xDk2Hh1WL67WmPDra6YTq14iSqRdXlBACvP39wJt+ItwwKK1k8p/TnTXgHR8EOqvA+94KtvsUmG1PZzIJy4ozKaRkqnuDMggoT8LgzEwmE3NNmExt5JokhGCzb+S2yxn+eSY52+XYInY5XcG45ILZXkBJOQ+6vwFbLqNHsvFHMgwMVdp2OdPxpjbU8mBmE1DrLWCXE0qmlHa5Nvg7F1qSSVYsOZNJTB7FDuX5YTdZydRg8DfALXNF7XKMMXguA1V5uxxQkGQS7XI1qm/E7jEQ5jEBnGQCkNgwV1smk6Yn2+X6ye1yQPgZMsZguuapyGQCADroZ9vlIvYAL5FkWmmUZBLNWUD6wkT3H6KBkonQWhRzRdBTe9hfCY9f7c0++NOCv8XflQKL8K2BnrgQEIuZsqqReZgX/A0AB5Pw+0FldYqSyUppl1smxL8/3S6nFVAy+TvgDQV/A1zxtzsRdjmhZIrsilZGMvnB3wsrmcIFaRnQ1VU/+NsnmTKUTIQQ/JM3vh2aouG3P3ol898N7HKyKAkidrnbNnrwGPDEtWN+jh3/fq9RySQwFfytx+xyAPAVbwJWz5VWEogyiimiYu088N2/CzhjsN96K39dZ1rJZAUkU97g73qVTMB0WccM+mdm2uU8e9KYXQ7gz5ad43wkkyCO87bLLZbJVM6iBHAlplqVXW6Bdrmq22BPOoqTTPXY5XSVgrGwfTYvZkjFhexyIpMpWcXZBn/nQ3vnyQh7wm+MRUmmaCbTZAxiGCA+A31+2MXV/cnMe8b+Lnm3IZJJ0WhhksnzJ+iKSrHV3YJKVDx//Hzu95uuCZ3qtdXuAqFdDsC0kqnHSaZro9lcpjqDvz3bDpVMPsnUHcTscv1eoHgRSiaHOfCYd4IzmRS4HoPjP0zn2uUi7V3JSqbVsF1OZDLV1C4HhCST4cvuk6DrUZJpAosS6ZRohBCY6yGxpHQT7HLqcoK/AWCzn6xkEgRkVUrPeXY5ALgRyWUKr7k0JVMdJFOKksktYZcjFKh4lzQJa10N101BMvlKpuiuqMhV6wyT3l4aqrK84G9tgYm/MhjAOzgMshtpwv0VxdbAwJtffha/+8izmRYbU7ZMFL0f2CAubnBi5wsvHHE1qm+brzOTSSBKOAm7nJewwLKDMOhi5zqIToirYc68DHj7fwDb4WQhjYwVQY14gXa5OpSTcXS0jFa0wfaskqlxksnInckkNjPyt8tlbzpkYaFMJo9VpmQKGjgXsU+1JFMiuF0uP8kkFLfV2+XKETgz53spdrlWybQI2jtPRoz3+O8L2uWimUxsPJma1JwbdnF1b5bhnTQ8KVQ1CregXS5QKigUClV4Q9sov5LJdu3alTe03wsWU3G7HIDE8O+6FELCLieC2I/2/ODoHlcYRNvl4nY58btsJEVehE0m/Jriwd9Hqa83bTcgHII6eS1yjvRBuEAN2uXq+2yEXS5rkiWUTKZrAo4JC/VaR/PC3giJWa23MvN9JUXJ5PgT52J2OT3ZLuf/W0LCv2wQTQNcFyyhSScgmUZRkkm0yyXbG03bS24VXCLCdrlo8LeaS8lkxUmmBlRMAP9sZ5VMNdjlJAj+Briy1j06hDceA4oCaPPtsu948BIOJg5+/1PpGzrS2VX0Pif9GcMFn2S6uj/mY7hQMtVll/PD1WmvF2z+AdwuByQvsIRdt6g1MrDLJYVH3/5V8L7xF/nrPvM7gaLQUBU+3hVQMpmOV3vUQkcrpmRi9gQW0yofF9OwmaKSjcNywsbIvO1yYSZTve1yjudVmMlUXsk0iTeEtphCYSWT3warVkwyiXNeVFk31S4HLMkul53J1LbLZaO982TEmIfbLq5kEnY5E95kMrVbdn69i4OJg8PJ9O6I2WC7HMAXgoWVTP7AR/2d3LP9s4WDv+teVBNCQAc8l0ndDkmmze4mCEgqyVSXkgmOA7GOHh/aMHoqqP9g8UYjQFVBdD343ERQ+cRfoMlIUuRBvJZUGQzgHSWTTJ7HsHNkBjamZCVTNPjbn+DnDFFdBoZdfixZE2pD95sO/UwmkxApzx/bDMdDpUQmUzG7nIG9kT2zeyrIx6pIeBEaz5zZid9qR9jlwu8FmS4JiwrGmK8sqHhCqCRMtnKSTI7LIna5ZkkmCz6xEmQyRXZFKyKZtCCTaXG73CIWTmV1xQ/+HoN2OrlUva+9YwN3bPfx2x99OvU10ikJtB4ABjgTnF3tBNee0YCSifjlBaQ/PZYJFfmMZQ6hkqlsJpOXQlSw27+Ov+7ap4A/+DGAMW5ZCZ5Zee1y9SuZDHWOksm8EaqIATDRLlfxQjkNmwMD14/NuXkzlhslmbRc7XKBXa6skqlEHirAx/HK2uViG39FECopJSG5JcPAVzLlzT6y3HJKyqIwSp5zMz4+RjL4CmNe8Hdrl8sFSZ78LaYglEzdBYO/daFksvjkMUIynRvyPz8Xs8wFwd8N7TwqmlK4XS6euXJL/xbpSSaAExjAdCaTSlWsd9YT7XJ1Bn8DAGXhJFfkMQHcLkd7PRBCUpVMHeVk2uVmlEyDFTDLSpycP7FzhIOJg7svcAuNaO5JDf62JVUy+bXslmsB9gQWmJRKNCVCxhr92Sr5NCWTGE/UAsTQ1oD//3dj2Rl1ZDIBSLTMJdnlshYVjsfgseqOVUA0PJUL/o4ocNxmSSZTkEwxJVNHo8Bkn3+vIiVTGStIFNYCwd8AH+fcwwN4ozFILx8JTgjBdz1wEY9e2cdfP3eQfFzSKZn4MxfWMRRKcN6fB3U0GpCLdQZ/A4ASy5fr+kqmJOtSZlNoBmaCv2MIimFe9mbg4V8D/uJfwVApqFuMZEprs6wS3F6YoWQCgOPIfEqQTA2p9Tf7vPE1ulmQhChxTA0jZ/C3xTcA1eLjqKHRwMlQFLbrBYT5siH+3XLB35KR3JKhb6jwWPJYkwQRI1H1nEJXEzauciDMURSZTL2QLCoKkeWUomTqJCm4W8ygvfNkxGg5SiYazWQajUG64UThfEAyTUsJJ7YHSqpnqtOglshkCkmmUMn05dGX4bF8/47t2o0sqkX4d9QuBwBnemcaVjLxxRZxbYgNbdEsB3AlkwgqDTKZ/OBv05+UyqiEyYNgd8IO7XIAEsO/P/YUJ4Nfc4nfp6GSKWI1mQr+9ifsDWUypUH3c2Ys69hXMsl5/rStbYg7WusmBH+rySSTY3sglARKvDwQJFPcMlebkimBZFrt8oVDYiZTgpKpzjwcQ6PTky1FDYO8MzCTyUSbISOGvYiSyb9Pa7HL+bv/y8hkWij427cFe5MJaCf/+PTW+26DrlK8/6FkNZOUmUxAkJMnLHOGpjQQ/M3nY2lKpiRVSfngbzEXTCZ+A7L6vncCr3gr8Mc/hVde/wMYKEa88Qy4+jOZUtUEA59kOorkMjnNZzIBmJvLZMaCv/O2y9ESKiaAbyxbjge3hKrS8VhldjlKCTSFLKZkakmmRAw6fE6R1zIXtMvVlMlU9Jwvt10um2QSRFuqirIFgJZkkhNLymQikUym+ORRkExXZ0gmXkFbZwh2FKpO4RSU7EaDvwHg1v6tcDwH18fXc72/KSWTIDCUGMm01d1KVTLVZpcDgEj49wzJ5FcuB+1y7jTJJKMSJg9CJRO/BhWfCEyyzD3y9B7Wexoub/HPQhADNG6Xc02+gAkymepTeQ17vl0uY+Kv+wtnc7wH5nAlk4wk01p/Awd9wFKBToKEWdhlk4K/i6iYAN4ABAA7sewMq6RdJS+Ixid9SSTTirDLRUmmjKDX0O5V/aLPUJXpyVbuTCYWKnAatsvZUMBAIiRTzC6nGLkDkPNCTNbLLOyisJ3FgneVlVV4frtcVrNcHMOejm965a340KPPJQbIytcu548b/u52QDKptIHgbz4Ho70UkilBXVDWriJyAlOVTGIc6XaBb/tl4PIb8LWfey++Xvk4f0HOjRFul6u/XW6+kilKMpk8k6khdd1myrMljihxTHQDzLbB5mS3Mcss1SwHhAq6MsoM2/UqJR50hZZTMtmSKSklw8Dwc1fnqOoE6rbLFVcycZFE0HS4DLtcCsmkKhQqJa2SaQ4kefK3mEIFmUxsPD153F4xoFIySzI5bmN5TAAnisoqmYRS4WzvLADktsyZnhnYvuqEssItP1G7HMDDv3fGO1NfY4xxJVMNUn4akdYHJFOCXQ5A8LnZLl/4nnSSKaxjF3Y5TgS6CeHfj1zZw32X1gNCNjGTyc87gnUU5kLUSjL5SqYMkkXv8HHGnuzB9q1CMp6/oTHE7gCYaMlKK0IIFJXCS7DLFWmWAyJKpsPp3WbTcfkkpmK7HBJIJoUSrHTURCVT0u51oLqqYdEXNFEJUBVw59vlHNeDLkEmE79PCDyqB2TD1AJlcmPpKiYgnAwvxy5XfuJPVwbwRiO4R0e57XIC73jtRRyZDj78yedmvidf8HdolwPChrmOpkQy82oO/u5PqzJF8HeWkqlsJlMqyRQERuv8//+292F/cAd+Vv11/oICSqa654+8XS5loZegZCKu1aiSabOfT8kUD/4GkjcfovDM8iRTRy2vzHA9Bq2iTCaAB90v1C4ni5JSMvR1/rw9NvPa5fhmSFXWSIHALleQwBFKykAksZBd7pjnp2b8Xw2VtsHfc9DeeTJivOfvmiYzqHkRNopYPPg7MnlUKMGtw06iXa7T4K6jqitLscsByN0w15xdbgDS7c5MMrd727g+uQ43YjdxPAespqyc6IRUNMyl2eXE8cTtcjKSFHkwm8nk2+WOpu1yu8cWnrh2jPsuhWrDgGTSYnY5gDfMORO+iFbqW0gP89jlfDLbnOzD9BVpTZCu8zDsDLE/ILC09MwvJcFu6zgllEwrKXa5iu0gWXY5gId/H0xm2+WS2sBmmlYqhBG3rFAtt11OFZM4z22MZBLh/Q7VZ+1yGq2MZBLqo0WDv23HW6jxUCg2nWvXCtnlAODei+u485aVxADwIIhVFiWTmFP5u9sX1iMkkyBFaw7+jiuZOj7JNErKZHLK2uXEuJKmZPLtcoKg6Kzhzx/8t3gem/zvOTZGPI/542PNmUxxFWUUfX8D7zhKMjWbyRSoZI+zlUzTwd8Z7YARMMsGMcpdv1lZYPPguNXZ5YAFlEytXS4Txe1yHhRKQCskFIFI8HfBc27a7vR9rfV5NmSODa8Z2OO5Ck5DU9rg7zlo7zwZMdrlKqYFLWs0sMtN4I3HM5PHc2tdXN1Ltss1BVWjJYK/p+1yAcmUV8nUkF1u5eu+Hutve9uMNXG7uw2Pedid7AZfC7KOalj8R5toEpVMSZlM7mnJZIq1y6XY5T7+NLe03ncpVBumtssBvGGuQBX0srAWBH9n2OV6fCFhTW7AklzJ9Od3EfzZK9Lb7xSVzIwfruUWVjL1dQUdjc6QTGZkd7kKzCOZ1rratF3OskAMI9HeXKeKhNedR0kmpaRdrplnj8guc4g+Y5fTlepIpmUFf5uLKpkGgmTaKWSXA7iC8B2vvYhPXz3Ap57dnz4ux4WmkMqapwojyGSKKZmasMsJJVOKXW6SoGQK7CoFxyA6R8nkTWIkEwA2OIvvsf4RRhfeCJx95dyfIY6t7vlj5kJP6wDGGnAUxg9QQTI1pK7b6PNzkUvJJOxyRvb5E+CZTCWVTOK6K0Ey2V5ks6AC6OqiJJMkSkrJMDCEkik/yVRHXm+oZCpul5tSeQqSyC6hZrJG4fMiBTMK7hYzaEkmGTHeWziPCYiQBcIu152ePJ5f7yYomZonmZyCDzlhj6H+wDQ0hugoHTx//Hyu99fV2hbH6pu/Abf84x+b+fp2l+++RcO/61QIRUNChQKkO4jZ5UQmk2iXiymZOjWTKctCvDo1zS73saf3oCkEr7otXHh6iSSTb88wD5shmXIomYwezwSzrIPgPMpIEg6NIf78FRS/80YlU8mUFPxdVMlECMHWwMD1hEymKndF85BM0+1yVmpddZ3NOol2uTztco4cdjlDVdDVFNhEm1Iy6Qrlu7YV2+UWCf5mjC0c/K2s+mT6jRuF7XIA8G33nEdXU/Dbf3Vl6uumXX8QdCYCkklkMvH/q6HRMPi7Jrsc6cyxy2W1yxVVMokNxzl2uehYYqgKvsBuw1NveR8wvDj3Z4T20rozmWiQ/ZWIwXaoZPJcUOb4mUzNLH9UhWK9p808W+KwIwT8PJJQgC1il8sInJ+HWpRMZexydmuXy0LfKB78vUj2X14EBTwlSKapcx3L4CsE+3ium4jPe1olUxbaO09GjPcWzmMCwGtMVTW0y8WUTOeHXXzpYBLUUgK+Xa7BAVnRFbhWQSWTf/yK/5AjhOBs/2xuJVNdgdp5sd3zSaZI+LdQCtWx+I+GhKpzlEzx4O86j7MKhO1yfHIS2OVi7XIff3oPLz+3NkXIJiuZeO4WzEOeyVRjsxzA/z89XQkrXROg+9ebZR5KrURbM8JFftrxqZoyq2RyvEDlWASbAwPXJFMyrXZVHIzDCaFQMiWhTqvSTMOTouVSMs22yzVDMgGcwLOgTWUyBQvRypRMiwd/ux4DY8UtVFEIJROAwnY5gNs4v+XV5/DhTz43Zec0G7BPZSLWLrfW1XBps8dtc64JEKU2Nd284O9RkpIpsMsVDP6ObDgmQdiwaGQsCVS9OXfqJw3l33Q0JfjZieifCZVMgkBuUMkE8GfL9eMCSqY550/AWyD4W8xlSgd/S6hkskpmmN0sWClMMnn1kExauUymmTgDzR/vSyuZskmmjqa0mUxzMPdqIYS8mxCyOOPRIj+WRDIBfAckbJeb3v0/N+zCY8CXDibB1+RQMnlgLP+kO7DLRSY3t/RvwZePv5zr/U3Z5dIgj5LJSm+XSyGZTlsmkyKUTJFMJsvx8Mln9/GaS9P3KLNsQFFAotkowi5nHfJ2uZp2yaM4N+xis59+fetRJRPzd6MlPH9DYxj8OU0pp6gkGA8EHKt48DcAbA/0xHY5mZRMzDSDvJU46mzW4Xa5uJJp/gSRV19HM5mS/y91YNjTYEKbsssFC+aqMplE8Pec1qgsBAupBa5L0XQKALRbjgh/x2svYmy7+NCjV4OvmU79bWOZiNnlCCH4r//wDfi+r74DcK1ax+e04G+RyZRkWxJ2laLtv0RRAEXhz6gECPKCROaIRXNRxHjTqZm86WgUtsvSidqoksm3gzeZyQQAm/3ZZ0sc0Q0NogslWrY6lJlW6vNgHoJWw4KbvAAfx6u0UelqWSVTm8mUhb6sdjmlrF0u9rxZxC5nj3MqmVq7XBby3Hm3AHiYEPIfCSHfSJrqtr+ZIDKZlgBiGPBGx4Btg/ZmlUwA8Nx+hGRqul3Of/DHLS9ZCOxyEYb9XP8cnjx4EgfWwdz3W64lVdDxVpcv+psjmcKQ0LiSiXke2Gg0a5cTJJNzskmmeCYT0TSQTgfeUViD+pnnbsB0vKk8JsBXlcStS3rULmfytoqa8b6/+yD+1294Ser3dZ+wMScHsPzhXSbSVWBVXwVB9vEltVO6jgdVLz6mbQ2MlEym5oK/Z0gmKz2Do367XCyTKUfYpuVEJq0NZjIBPPzbZOqUXc5QFYCxypVMi9jlbJ9UXWSHWVldDf5MuuUsva+6bYhXnF/Fb//VlWCTqGrlX2Ek7GwbqsItkY5VWx4TkB783c2wLS2iJCC6nm6XM4UKN6Jk0oot9Jpq8gqtNSmLvf6ZsF3Ov7etBtvlAPhW7HlKpnDBPK8dUGCRTCZh0xxZ+QiHKNzoZkEF0JSy7XJ8/GmXrcno6QoIKWaXq1KxJiDGkMLB3/FNQKFEssfJb8hCLrtcRulACwA5SCbG2HsAvBjAvwPwPQAeJ4T8HCHkKyo+tpsTjC0tkwngJJO7f4P/OR78HZBM4Q3YtF1OkBpOgd2UwC4XCT59251vw8ge4Rce+oW577e8ZjKZ0qApGobGcMouV6eNaVrJNN0u5434tSImxqpvbzmt7XKAX+8dscs9khD6DaSQTEHw9yF/0DWgZDq71sFqJ313U6EKVAbY5gFMiUkmhSpY0VegEAVaiuIlLZOpjF1ua2Bg99iCF9khNx23DDGPcgAAIABJREFUHruckzzpW+1oGNtuMPnykq45H+I19bTLxcJ3aX67nC6RXW7saaHaQUxYnQlXuUga/G26fHFdNAw6CmELBsrZ5QTe8eAlfO7Lh/j4FT5GSpfJpKi8udc6mv2ea9ZKMoXB39NKJk2h0BSS3C63QCZKJslkCbvcdCYTkJ9kmtSonIxCzFdTF3uDM8Bkn5OIrix2uflKJiuiGgnnZPPa5crb5YSqJcmmOQ+26wWqzCpQvl1OMiWlZCCEYKCrhexydWwaGErJTKb480abVq4WQg67HG/VbZVMWch1tTC+LfUl/5cDYB3ABwkh/7zCY7s5YY/4g3BpSiYd7g1OMsWDv88N+d+vTpFMbu1y5yiE4qCIksm1p9vlAODlWy/H973q+/DhL34YH3n6I5nvN11TOlJku7fdmJIpGjIpPlOhZPJGfLCmfT74EkKgUx22r1oQZJNsn2dexDOZAEAZrEzZ5R55eg8XNro4szp9P3GSKUZ+BEqmI76LWnMmU15oAEz7KCCZZD1/6531TAJMUWfbKd0Swd8Ar5p2PYb9iHKo8opudY6SyW8LFLk3zJyfyVSPXY4m2OVsvmmSAZkymYZdDWNPCZVMtk8oTvjzE53VjHeXQxD8vUAmkwiDXij4O0oylQj+FviWV5/DwFCDAPApy6Es0HvJQbBOvXa5MPh7diHT1ZREJZO1kJJJy2iXmwCUAmp4/4UNT/kWUeJ1dW9Szs0S6vP4ARxfC+9tpjWqsNvsG7gxtjOJE9tlwTHSnO1yXsbzYB76/tz7uISSqfLg7wXa5aQiuSVE31Cls8uVVzLFNgEXssuNQpIqBW3w93zkyWT6IULIIwD+OYC/APBKxtgPArgPwHdUfHw3H8Z8BxDd5SiZqNEJSCYSy2Tq6So2+nqMZPIyQ4KrRhklk+fO2uUA4Ptf9f142cbL8N6/ei+uj6+nvr+pdrksbHe3sTPaCf5ea/B3hGRSdQq9owRkExvxwTqaI6ErekAuTZwJCEiq0kR2JCuZVgK7HGMMH3t6D/ddnCWBmWWBarHzQyknmkyRySRn655BKCzrGLY/f5DJPhrFmrGW2iwHpCmZ3FIk0+aAT9ajlrmqg4wDJVNK9oZQpAnLHA/+lqVdLhb8DQAsexyfWpxIoGQ69tRQ7eD4z8KAZBpmvLschPVgkeDvIAxaLT/5J5oG4mcxxecJRdA3VHzbPefw+489j/2RJV/wN8DH46Sdbbdeu1znpS/F8G+/Fb3775/5XldXEjOZrGgbY0FQLdsuRwxjylZUNJOpKSWTOM5MJRPAc5l8laJDdSgVKm/mYXPAr7O9UfL5cD2eMaX7io4idrm058E8dIVdzizRLud5UCq0UWkKhVXCUjxV3tAiEX1Dkc4uF2YyFbsWZ+1ywh5dxi43mrspbKhKSzLNQZ6rZQPAtzPG3swY+0+MMRsAGGMegG+q9OhuRox2+e9LzGQKlUyzO2bnhh1c3QtvQNN2m22XEyRTViVtDGJRqWjTkwaNavi51/8cjqwjvPe/vzcxTNzxHLjMlY5k2upu4YXxC8Hfmwj+9iwLF166jhfff0vwPffYVzJFciR0RZ9qlzMU48R64OOZTACgDPqBXe7ZvTGuHZq47/ZZEpjZdrJ1yVgBzAPeLteAXS4PdKLA8iyp7XIAD//OOjZVnSWZXLtc8PeWIJkOQ5JJhuBvADgQJJNpBsrDOEIlUx3tcnG7nL/QnGOZs2bscs1tcAx7GkaeCs8WdjnfajHxc/2qCP5egl3ODhqUFvvslBVu7V3ELgcA3/XAJViOhw8+8qycSgKtx/M24nDNmoO/u7j1Z34G6vrsXK+rKYm2pUXsKkTXwew0kml2HClql2tayZREygHgmUwAb5jzlUysYaXulk8yxTP/BASxFwZ/52uXWyyTyQ+BLqFk4jbO6uZ8hkphlbAlSamklAyDjoajnMSi7XoL2bLzglICTSGFlUyWExNJiEylquxybfD3XOS5Wv4AwK74CyFklRDyIAAwxv6mqgO7aTH2P+qlZTLpcPf3Acza5QAe/j2VydRw8HdglysQpha0yyXIyF+0/iK8+55340+e+RP83hO/N/N9QY7IZg860zuD3fEuPF8JUOdxRnfN7nztrfiad7w0+F6gZIqQTBrVptrlZCUo8kClBITElEwRu1yQx5SkZLJT8nGMFZ4B4kzktcsRFRYh0tvlXrn1Srxs42Wp31e0hODvkiTT9go/l9ciCwGr4kyCMHQ/RcnUTVAypQV/24u3juWFodLpRZ5QJM0J/5bJLrfW1WBBgzcV/B21yy2fZCKEQKFkoeDvsrX2cVBBMi1glwOAu86t4p6LQ7z/oSuY2BJmouj95EWHY4UKvIbR1VWMU9vlSpJMhgEvzS5nmTMKtnDDJa9drulMppTjHAi73AsSkUz8519PyWWaIZmMfO1ynlXeLqdQgo5GS2UyOa5XqcKldLucjCS3ZBgYSiG7XFklZVGUUQnNKJnK2uU8jzsP5tnlNGVqQ7rFLPKMCr8CIJqSeOR/rUUVCOxyy1EyUd0Am/Cd2SQZ/DmfZGKMwXE92C4L2k2agKqWt8ulhfu+86534t4z9+LnP/rzeP7o+anv2R5/aMtmD9rqbsFhDvYmfoCqUDLVsNMakkyzExpvjl3OdM1MO5PsIITMWH948DcfAj/29C4Ghoo7z67MvNezrECJMoXALievksmgGiwAtuRKpne9+l34pa/7pdTvKyoN2iYFHNuDWmJMS1oIcHVLk+1ynIQ5mPBJoWeZqcHfoV2ujkwmBY7HnyEAePA3kKlkcj0Gj0Eakom3y2lccYhIiOiEb9JUQTIBfHFne+UnqmLxtSiZKHKZFrHLCbzjwUt44toxPvelQ/mUBGkkk2vyUHAJ0NVociaTs2Dwt5ltl4sizGTKa5erz54bxVzFVaBkCkkmr+HzLKzY14+TlUkizF+fCf6uzi4HAH09fz5PFLZXrZJJU0jQolkEUtp1JUNfV3E0yXfOnZrscgAff4rb5dwUu1xBkkm8PpeSqSWZspDnaiEs4jPybXLNzQRPOwK73PLa5QRod3aH8vywi2PLxY2xjYl/szRql9NL2OVskcmU/JBTqIKfff3PwmUufvIvfzJQBwH1trYVwXaX776J8O8m7HJJExovyS4XCf4+6UomYLbJRBkM4B1xkumRp/dxz8VhYp5DYrsc4NvlfCWTKqeSSVe0KSXTST2HcSWT5zF4LiulZFrtaFApmbI08EyU5uxys0omOzP4m5DFFS55EAR1BiSTP0Xw0sdxYfMKsoQ8t9ng754OCxqY4xPmwmpRoZIJALQFlUy2UD0seF3SVR5snjRPKIpvetWtWO2ocDwmn5IgS8kkySZAV1fSlUyL2OVSM5nMqWY5oHgmkxnMH+s93z1f/X4wTlH56D2+0XN8LchkalrJJDKZ0pRMIsw/UDJp89vlmOMArgtaUskEAD0j2aY5D65XQ/B3GSWTjEpKyTDo5G+Xs2qyywHCIrlgu5yiA0RJLnrIgshw0ua3y6UqKFsAyEcyPUEI+QeEEM3/9UMAnqj6wG5aLFnJNEUyJexQnh/yCeXV/XFwszRql/MXK4Xsci4DpQQkI8jxwsoF/Oj9P4qPPv9RfOCzHwi+Xid5UwRnenz37dqIk0y1Bn8H4cMJJFOCXS6uZJLtsywKXscePjjoYAXe8TEOjif43JcOcG+CVQ7wF/ypJNMhV0hocqq8dMWARQgsye1y8xBvlxP5TGWCvyklftX0dPB3pXa5eSRTZzaTaabR0IfIj6ojH20myyzIZEq3dwQkE40qmZp79qx1NZjQQNz67HIAoCp0seDvgKxbUMm0wpVMyyCZOpqC77jvNgD1K1vmIlXJVG/wdxa6mpqoZFrErpLZLmdOZmy3qkKhUCK9kunOsytQKcEnntlPf1F/myuZ/HubNEwmrhgqdIViJ6ddLk+7HPPzmtLs03lQRsnEGA8pr9Qupyjl2+UaXM+cBAwMNXcOV712ueIqoZn5GSF+Bl9Rksl/PswjmXxLX1LebwuOPKPCuwC8DsBVAM8CeBDA91d5UDc1xnv8wl7SYjS6O0USg799kmkvQjI1uPMobC3xXJUseI4HmmMR+dYXvxWvP/96/MtH/iWeuvEUAAQKHNmUG1vdLQDAzpg3zNWqZKIU0JInpKl2uUgmUx2WviqhK7N2OQD41Oefg8eA19yeRjJlKZkOpG6X05UOzAjJJNv9kBfxdjlBOKVZaedha2BMLQSaDv7uaAoMlYYkk2Wl7lzXmUcxY1lR5tvlxG69Jkm73LCrwYIKOkUy+e1yil7ZvaspZEnB3wsqmQbcAkwWDP4WeMeDFwFISDKlLTpcUx6SKUvJtIhdbk67XBxFgm2DTKaalfA9XcUrzq/hoSd30180ODOVyQSt2TkKIbMbGFEEJFOBdjmRt1U2kwngqrCiSiYxjqsVtvVpKlkgk0my8Ucy9A1ul8tDlNRtlytCLHoeg+UmnG+9BMlk5bfLASh1bd4smHu1MMZeYIy9nTF2hjF2C2PsuxhjL8x7X4uSuOXlwKvetrR/LrqrkRj8vc4nlM/tj8MK2gbtcqqwyxXIZHIdBiUHu04IwU+/7qehKzp+4s9/Ao7nyGuX63G73AsjfqsFSqaasqNoGskk7HKR3e4ZkumEqmAEDG2aZBKtS59+/HlQAtx9IbnKPJNkEjZYSUkmQ+3C9u1yBAQqOZmOaFWj8FwG5itDxDgixpWi4CRTA0omJ10BtNbVQrucaQZWijhm8gkqxEz4bmCXyyKZYgocz2k0eHmtq8FkOihzAM8LrRaTG1zFVJEiTKV0ScHfCyqZVv3g74R5Qhm86MwKfuqb78Jb77uwlH9vadAHGXY5OeYB6ZlM5UkmOscul5TloxdQEwgVYxP2yAcvb+BTz97IaJjb9tvl/HxSCZ7DmwMd1+eQTFoskymrXS5QMi2SyVRA1SLg+HlyaoU2csOPMCiqGLEct5bii5OMgcFtzXnu87ra5YDiwd+C6JlZv2rdEnY5//Xzgr8L5tbdjJh7tRBCOoSQv08I+WVCyK+LX3Uc3E2Ju78L+OZ/tbR/bp5dbrOvQ1cpnrsxkcIupwR2uQKZTK4HmnPgO9M7g/e89j341M6n8Buf/o2QZJIs+NtQDKzqq1OZTDrVa7G+AOl1x95oBKJpU2SKTvUgQN1yLekIu6KIZzLRPlcyff6J53Hn2VWsdJIXwpxkSgn+FvJbCSa3SdC0bqBkMpT6rrNlQyiWhJrJ9Xfhy2QyAZxkErkZrsdtAZUGfysKQEiqkgmYJpk8OyOTyfZq2zCYUTIF7XLpC5YZcqThTKZVX8kEAHBN32oRIZkqwuLB39P5LWUhlEzLsMsJfM9XXcYrb6vusysFvcdJpviCVaLg715Ku5zlstLnmWjzMplmn01FclEmjgtNIYl5hVXj/ts3YLleumUupmSiEjyHN/sGrh+n2OViYf5EVQFFyWyXEyTTQplMuoJRzjp7gRlFagUQn4NdkIxvlUzzMTD4My+PTdJyvVoyHoHiwd+p7ZZav3zw95w2aGHFbBvm0pHn7vv3AM4CeDOAPwNwG4DDKg+qxfIQ3dVIao0hhOD8sIure+PghpYhk6mQXc72oKj5B763XH4L3nz7m/HLn/xlPLbzGAA5M2i2u9tTdrk6j5HoemLdsXc8mrLKAYCmaAFZN3EmUn6WRcAzmWbtcleuXMN9l5JVTIBvXUpTMglImslkaP0g+Fs2wrUIBMkkxg9nYbucjmtHJhhjMzkZVYFoGpBBMq12NRxMbDDPA+yUHDD4qqsKd5ejCHf08iuZHC/JLtfgBgclwf3J7Mm0Xa5CkklTyEKZTMsK/u7ecw+699wDZZg+xp0K6H2AuaF1SsC1pVEydTQlI5OpvF3OS9g4AvyWykS7XH41gWl7jUUt3H/7BggBHk6zzPXPcDWxr2AjDdvlAKFkypfJBIh2wAwlk7DLLZrJVFTJ5BNildrlFEEylam0bzOZstD3SaY84d+Oy8IMxYpRNPg7bNOt3y5XtAXvZkKeq+VFjLGfBHDMGPtNAP8jeC5TixMAsatBDIPvkifg/LDrB3/77SANMv9BJlMRu5zLoBSceL3nwfdgaAzxrz/+rwHIZ5cDgK3e1rSSqcZjTMtv8I6Pp0K/Aa5kEnY5y7VOPsmkUFiRh4awy5HREV5zKb31MdMuJyBru5ze94O/5bwX8iJQQgolky3scuUmmlsDA5bj4dB00icxSwbRtFxKpmBRkWKP4Ha5mjKZtJhsvIhdLlAyNZvJBACKv/i0LN4uM2WXqwiqsqBdLt7SVxL9Bx/A7f/h/YFl89RC55sGM5Y5R6JMJk2B5XrBAl7AXkBJQAwjVQnDM5lm/+/FMpncxqIW1noa7rxlBQ89lUIyDbYBMODgKgBA0Zvf7Nn2rdhJFrDA+hN51mTZHQHAM7OfB3lQpl1OEORV2uUE2Va8baxtl5uHQQGSidvl5Az+Du26y7DLieDvbLucEGS0drl05Ln7xFNpnxDyCgBrAM5Ud0gtlgmxq5FklRM4N+zg6v442DlrUslEVQIQTIX3zoPneIXtMMPOED/9up+WNpMJAM50zwTtck0omZImpN5oBNqPkUxKaJc7jZlMdMAXJT3HxH2X0lsfmWUl5+NMkUxyfja62oVFqW+Xk/MY8yCwy8WUTGpZJdMKP587h2atSqYskmm1o3KSSdgjMpRMtdvl7DjJlP7/mLHLuXbjJJOqcxLYmtRIMlE5gr9vGojGIDtGMrmWRHY5fj9NYvMge4FMpiwlDJtMEm1WukpzW0Em8frwmvHA5Q088vTeDDEHgCuZAGD/GThQoKXk2NWJzYEO0/FwnJK9BYTB30B6hIEAsxa3y/V1FaOCSiY7rkitAAHJVEbJ1GDG7ElAaJebTy5aCxQPFEXR4G8zbX6m9WfH+nkoqGRKzYJrkYtk+lVCyDqA9wD4MIC/BvALlR5Vi6VBSKBJRs7CuWEX1w5NHEz4gqBbctd/GSCEQNUonAK7Ka7jgZZ4wL3htjfgO178HQCAgTYo/P6qsdXbws54h1t1as46SlUyjUagvWl2/7QFf89kMvkk01nFwW3r6fcRS7MuTdnlJFUyKTpMQmBSBbqkRFgeqClKpkUymQBg58hKn8QsGXmUTAdjJ6Jkyshkqi2kMyYbD9rl0sfxGXKk4UwmAND8XBrb5AHBhlaHXY4G1sEyCMi6dsc+H3T/+ZWkZJLFLufPweILfstlpc8z0ZPLPADeTJZkszJUmnth36SSCeAk08hy8ZnnDma/OfBJphvPwoImhbpls88/76Tw7/CeDue1RNdzBn8vksmkYmJ7hey7gtRTKrRRCWKjCOnAGGvtcjnQN/jnc2SmzzkEHJfVRjIVDf4Oleax8633AHtc7IeL12t5SaZWyZSGzBkdIYQCOGCM7QH4/wDcUctRtVgahHQ2S8l0fsgXvk/t8ElXU756AVVTCmUyuS4rnbny4w/+OL7+0tfj4urFUu+vEtvdbdiejRvmjfqVTBntcnElk0Y1WN7pIZnS2uVe3EdmILaXh2SSlMDRqQ6bEFhUOdHnbyb4e0kk0/UjExt9fm5rsctlBLyu+ZlM7sRfVKS1y7kehklB9BVAqF+DyZbIVnLT/x9OYLOQI5MJADSDPwttk+9k1qFkUhZUMlmtkqkYApIpYqFgTC4lk7ifYrEBi2YyMcsCY2zmOcbb5VIymU6Kkul2bmV/6MldvDreANvnbb248QxMWUimga+SPTJxaXN64y5JnZhldwTC5rm0jL48EITDyHJSC07iqCP4u0xVfJLlsMUsVjrCLjd/Y38Ru25RcBVlfrGBICCX0y4n7HLZJNNal98jB+P5BN3Nisy7jzHmAfhHNR1LiwpAcyiZBMn0RUEyNSwvVXVajGSyvdIkk6EYeP3515d6b9XY7vGJ0QvjF2rPOspSMpF4JpNyujKZ4kqmaxbgEoqLnfTdvSCEOSnPRI+o5GTNZFJ0WAAsSk928HesOCCwy5Uc06ILAVkymVa7GhgDjo75xCldyVRfHoWYbO2P/TGDCiVTRibTTLtc85lMhsHHNtviSqYusXnrmNTB3367XEsy5UNAMh2FX/McAEwaJZNQk4/smJLJKb/Io7rOyTRn+t9kjPF2uU4CyaQVzGRqcEF/ZrWD2zd7yblMQslkHcFkWtAK1SSiKtk4UoO/MzKZmLl48HdP5+NvkVwmxxPB3xVmMpVQMoVtY+24mIV+znY5xhgcr04lU34VJZBxvhexy81xHoiNx92UlsgW+exyf0wI+RFCyAVCyIb4VfmRtVgKgkymLJLJtwB98QU+6Wr6AayoFG6Bh5znlrPLyY7tLieZdkY7XCFUowomTVrP7XKzwd+2a4Mxhok7kTLfqgi4TDe8/j5+ZR/Hage3qunXpCAFkpVMq+GfJW2X0xUdLgHGVDnR50+0TAoFk+vvhJVVMm30dBACXDuywp2yinfriT6fZAKAwxuj4PVJsByvtrFcTLaCtqQcwd+WhMHfRscnmUwul+8zf3JaaSbTosHfLlRKQBuojj+RELvTUbucaJqTZOzr+vdtvGHOdr3Sdl3xbJp5rts2wFiqXS53u5zjNb5Bef/tG3j4qV14cdJWHwQbPBPJlExJDXOmm0IyZbbLiUymBYK/fXIzT529gBi71Arn4GXa5YIgaAkIRZkRtMtNss95qFir0S5XQGxgps3PytjlDp4DBrcAGc4FAFj35z17o5ZkSkOeq+VtAP4+uF3uEf/Xx6o8qBbLQx673Nk1/r0nT6qSyWGlF5EyQ5BM18bX5MlkOj4G7c9mMjnMCULUO6qcREpexAMHH3l6DyO9g6GXFbopdhHn2eXk/GyE+uywu3ailWiKP6EUdrlFlUyqQrHR07FzVF/wNzQNzEmf8AnV0NERJ5nSgl5Np7y1pih0lWK1o4b5Irna5SIKHM8DwBonmbr+ZszxiH+2fearXYwq2+UIbG+R4O/6dpdPBYSyNFpr7StxZbHLCSXTOGIX8bzFlATCVuvFnuteRrabriq51SNcOdnsgv6ByxvYH9n4wrWj6W8Q4jfMARZTpSCZQmI+PZPJiAR/z2uXW04mk7DLFVEy1Rj8vYxK+xZT6Ov52uXCNtga7XKFSMWU8631+PjuFgi03/k8sPWSuS9bMVSolLQkUwbm3n2MscsJv9psphOCPHY5Q1WwvWLAdDwQ0rzsXimayeR4UE7hJHuruwWAk0yma8Kg9U2AaUqTSaKSySe/jmw+sTvJditgdvf2Y0/vgfUGwChdchuSTAmqEiNql5OTZBLn8NDoQVNOboV5EPw9Y5crv/jZGhi4fmTWJr8n6rx2OX5+RkdCyZTWLldvEO/WwMCOkI0r80kmERirqSR8XcOZTB1/bDs8OgQAdF1/sVpxu9xCSqYFLFQ3JZLscoJkksUul6BkEkTkIu1yAGZyfdiEW0NJggLmpCmZHry8CQD46JMJljm/YY5nMjWvbjFUBSsdFdcTrDZ2dGz0Mc8u5y2BZMprnYpCjOOV2uVKkUytXS4PFErQ05W559ypXcnEN3sZy/dsTLfLiTbRArlM1x8HNl8092WEEAx7OnaP20ymNMzdNiSE/J2krzPGfmv5h9Ni2RAPnCwlE8Bzma4dmuioSma4cR1QNRosEvPAcxmoevom2T2th4E2wLXRtQaCv/WZHU/mODy7IcEuBwCHFl+YnWQlDDCtZJrYLj7z3A1oqwN4h4ep78lUMmk9gFCAedKTTEfW0Yk+f2IccP2cmkWDvwFga0XHTsQuJ0O7HACMjsZYgRztcgC3f+wc5lcyWdHFSUAyNatk6nUFyTQCsIGuV4NdTqELB3/rEiyaTwxELXU0DDawy8kx9iUpmaaUfyUgxon45pFQwCQpIjnJlDeTqfkmrwsbXdyyauChJ3fxztdemv7mQJBMujS19tsDAzsZSqap4G9dh7e3l/pvLSeTqbiSSVyXaoV23aBdrkjwd0329tOAvqHOVTIFz+sa1dGAIK/nn8NMuxzASabOKubi+Dow3gO2XpzrODf6GvZbJVMq8lwt90d+fTWAnwLwLRUeU4slQjxwSHc+yQQ0b5UDfLtcgYec65QP/pYdW92tBu1y0wtdz7eQJNnlAE5QAKg1O6oKGKoCx2NwPYbHrt6A7TL014dwj45S3yNIJppEMhESWubmBAk2BUEUHtknm2QKlUx8/BC2OWUBEnqzb/jB33KQTKtdP5z1aOy/Pk3JVO+ib2tghLvygizKaJebtssJkqlZFd3AH9uOjzm5ZDg+sSxx8LfleNBbJVN+aELJFFGmBnY5iZVMzmJ2FaGyjef6eBnkRLF2uWaDvwGuLHjg8iYefnJ3VgHRl8suB3BiPimTyXI8UDK9oOftclmW/cUzmQIlk1VAyeRVTz4YrZKpUqzkIJnCxsN6njVFGwXT2+USMviysPN5/nsOuxwAX8nUkkxpyGOXe3fk1/cBuBfAYN77WsiBIJOpm13FKMK/uxKE5Kml7HKnc5K93dtuRsmUIM0OSKYUu9xpUjIB/KH1saf4zuH6mfXySiYA0H2SSXIlEwBoDS/0F4Egm6PtcpQS0AUmwFsDAzuHJixXeP4rDv7OqWSaHPskU8KigjEGy61fyRRmMol2ufTNAjvRLteskqnf4wTEaOznE9Zil6NBrkkZLBIGfVNC1fn1aScEf8til0tQMgVB+UsO/hbkBElolyuSi5JXcVA1Hrh9HV86mOCZ3VjY7yC0y8lyv2z2DVw/TlAyJdzTuexylAJq+TE0UDLlqLMXqCP4Wy9IOAAZGT0tZtA3VPnscv5YkpfkTs3gCuxyOcO/rz/Of89hlwN4OUybyZSOMlfLMYDLyz6QFtWA5rTLnfPDv2WYJChaseBvz2Ggp/RBst3dlib4OySZppVMgpQ4tDkJc5LbyYDwIWU6Lh55eg93bPXRHa5jNrYsAAAgAElEQVTCy1AyefNIJuPkkEwnmSQUtrjALmd5UPTFxoatFR3Hlov9ESd+mlYyDQwVlADmiGepJNlczLRdvQqx2TewN7J5RofIVsoM/o5kzAgyquFMppUBH9sm/lin29UrmVSFLGSXs12vDf4uCr2fomSSY+xLUjJZTuR+KQGaRjLNscvlyUV5bn+Mo4kjxYL+AT+X6aGnYrlM/YhdThIL1eaAW7Hj4DlrcZJJy26XMy0Qw1go7kKEQBdRMgXjeIWZTKXa5Rp4Bp5U9A1FOrucUdAimcsulwc7j/PnwPBirpev9zXsjdpMpjTMvVoIIb9HCPmw/+v3AXwOwP9T/aG1WAaIkc8ud863y8lQ96lqFG5rlwPASaad8Q4m7qR5JZNvIaH9bCVTR5GTSMmLqBf841f2cN+lddDBCtyjo9TJ9lwlkzHgVowKJ2KLIBrWfqJJJjUW/O14pZvlBLYG/PN4/gYndSoP/p5DMhFCsNrVApIp6ZpLnXBViC2/knt3ZEUymebb5TSJMplWBlykPTH5rqdmH3DVS4U212UEf8uizDgxSCOZZFEyaUmZTIvZf9KUTEFgdJJdTgufhWn4/JcP8e2//JcwVIpvvft8qWNbJl58ZoBhT8NDT16f/obfLseDv+W4XzYHBvZGVhCeLZCkQqXz7HKmmWzXL4CeUTyTSVh9a1EylbLLNb+mkR0DQ8PRHPVa7XY5MfbYOTPh7JQ4g8J2uceBjTtyb3it93TsHVu5A8pvNuSZ0f2LyJ8dAE8zxp6t6HhaLBli4kA72ZNkYZeTIpOpoJLJdb2FMldkxnZvG6bLJ4H1kkwamMUHTrEz5h0nK5niwd+nRcn02S8dYvfY4iTT/gBwHLDJJLGpUZACafk4MFakVTEB09fWST5/oZKJjx+u7S5MQG/7JNPVfU48NK1kArhlztoXrVBJSiY+MauTfNj0P6frRxbODIRdLoeSSSWAyH9rmGQSmUz2hJ9r1T7kKqYKyzBUhQa5JmVgueVr7W9axEmmIPhbjrGPUgJDpdOZTAvaVQTJNFPoEbSSJbXL+ZaVFCvcQ0/u4u/95sPoaAp+5we+EnedyxGsWzEoJXjNpQ08/FQsJNtXMvFMJjmIh62BDsaAvZGN7ZVwHOc5azElk5Ztl2OWuVCzHMDz8VRKCrXL2Z64LiskmZQyJFNrl8uLgSFfu5w45/nbLV2olECJB9CXscuduSvvYWKjr8PxGA5NJ2j+bREiz9VyBcBHGWN/xhj7CwDXCSG3V3pULZYGdX2I3le+Ft277858XRD8LcHDV9GV3O1yjLFTb5cTqNsuB8YAJ3zw3GyZTH/5xR0AwGtuX4eywu1uaZY5EZIuwlVnIDnJFL22TjLJRCkBISHJ5NgeVH2xMW3TV+g8J0imiidZeUkme5yhZLLrDz3d7PPjuH4UVTJlZDJF7T+SKJmIosMDAfH4wlu1Diq1ygGARslCmUyW41Z+TZ46aD2p7XIAz2VKUjItSjIVsctlKUj+8DNfwnf/u49ia8XA7/7g66QgmAQevLyBJ3eO8cLBJPxiJJNJFgvVZt8n5mO5TEnqRKLPNv5G4ZmLk0yE8Dr7IkomocJSKlRpByRTAcVn+Axsfk0jO2RslxP3aF5ikRedJByb7m+K57HLuTaw91TuZjmAB38DwP5xa5lLQp6r5T8BiJ5l1/9aixMAouu49Bu/gd6992S+bq2roacrUimZ8sgPPf+ho5zSSfZ2LySZ6iRvkvIb5rbL2X67nEQT9TIQk5L//sXrWOtquGNrANrnNhr3MI1k8u1yaUqmW14J3JJ/d6RunJZMJkIIFDVUQrr24lZaYZe7usdJplrsck72hGW1o8GZ+IvDTLtcA0qmYzNnu5w/aaUkksnULMkEQmBDgwE+4aZm9SSTqtCF7HK2y1q7XFHog+lFh2TB3wDQ06YX+0Hwd+l2OfFMj7XGCiVTQm5nNJ8wivf91dP4wfc9grtuXcUH3/U6XNjILpapG/df3gAQy2XqC7ucLo26RWxgxBvmEkkmw+Bq6hTVI89kWvz6zRMCHUUQ/B1XkCwRC9nlJFjTyI5BJ3+7XJWKtSiEKmjnKD2HLArL8ZLjXoTVPY9dbu8pvuG1mZ9k2ujz49xtw78TkefuUxljwafn/znXSEYI+UZCyOcIIV8ghPzjhO9fIoR8hBDyKULIfyOE3Bb53kVCyH8lhPwNIeSvW/VUtSCE4KVnV3B2rfmKdVWftrxkQbyGnlK73FZ3K/hz7UomTEvr0zKZguDvU6JkEhPQT1+9wa1ylICucJLJO0pumJubyfTGHwX+zv+7/INdEqKZTCdZyQRwy1xol/MCC11ZiIXAtSMTukIXClbNA6KqoX0sBWtdDa5PMkGbVc+FVoH6dnGFrfDaYYRkyrLLeQyaQvjnGSiZmt91tokOAxY/1+aNGkimZQR/n87nX2XQ+4AV2TCQUMnUiSuZHJGJsmwlk3h2JQd/A+GCnTGG/+OPPo/3fOjT+Jo7z+D93/cgNvryPS9efm4VPV3Bw09GSKbOGibGNr7MhtKoW8QGRnwhbSWE+aedPwFmWaAJ57AoerqCUc4cHACwvcUUdnlQjmRq7XJ5MdBVWI6X+fnWbZe769wqKAE++cx+rtebjpt8rovY5XY+z3/feknOo+SZTACwd9ySTEnIc7VcI4R8i/gLIeRbAezMexMhRAHwbwC8BcBdAL6TEBLfyv8XAH6LMfYqAO8F8POR7/0WgF9kjL0MwAMAXshxrC0WwP/9vQ/gn35T82oL1WejHWv+A8XzW6ROa/D3md6Z4M+dGu1WSbue8+xyR9ZpUTLxa8ljwH2X1gEgsMu5hykkky0m6ifTkz2VyUTlWzQUgaLRMPjbXjz421AVrHZUMFbPhDWPXW61q8HNaBOyGtjFXe2qUCnB9eOcwd/RBiVJ7HIA4BANOvymrEkNJNPCdrk2+Lsw9B5gRZRMAckkz/jd0xVMkpRMJc91qE6eJjTE32lWJpPtwXE9/Ph/fgz/50cex//8mtvwq++8Dz29+fs1CZpCce/FdXw0SjIRgj9+04fwG+5bpCEetlKUTLY7e0+L85PWMMeWYJcDuJJpVEbJVCHRrVACSkq2y0lCKMqMvuG3Cmac90XtukXR01XceXYVj+YmmVLscgHJlEPJtPM4/33rRTmPMkIytUqmROS5Wt4F4J8QQq4QQq4A+DEAP5DjfQ8A+AJj7Alf/fQBAN8ae81dAP7E//Ofiu/7ZJTKGPsjAGCMHTHGcvYPtiiL1Y6G7oL5JctAEN6bI5fJFX5wSSYNy0Zf66OrcnVZrUom3/YlyBMA8Ea+kilOMongb7/u21BPNskUndzde5GTTHQglEzJDyqxu7hou0tT0CKLqxOvZFLpdCbTEoiWLT+UtY7FPNHzkEwqD3pNud6asMsRQrA50HH9yAwX61mZTK6cJJNLdRjE5gRdLSQTheux0u00SaqHFnOQFvwt0bOrG7PLVZXJ5E3SCwTE+HFjbONd73sEH3j4Gbz7a1+EX/iOV9WWzVIWD1zewOe+fIgbkXrxQ7oKG6o0FqrVjuYT89PEkZkU/D1HyeQtIfgb4OTmcYFMpuC6rLg5V1dp7jp7IKNtrMUMBh3+3M2yzC1q1y2Dey4O8Yln9uHl2IQxbS+ZUFQNgNDpTYU0XH+cFwQUeOav+0rO3VbJlIi5dx9j7IuMsdeCE0J3McZexxj7Qo5/+zyAZyJ/f9b/WhSfBPDt/p//JwArhJBNAC8BsE8I+c+EkEcJIb/oK6OmQAj5fkLIxwghH7t27VqOQ2pxEiAWhU4Oya5YTJ7WdjkgDP82aJ3tcgmZTMcjkE4HRJm+FU9b8Ld4UCmU4O4LQ/7nwYJ2OckRPWcn/fypESXTMuxyALDlB7TWqWTKIh3WuhqoY+cgmerdNNjsG3xXnsTIowRMtaLJkskEgCk6dNj8s6uBZBKT9rJqplbJVAJaX/rg7442bZezfNV2ZXa5DJLphz7wKD7y2RfwM9/6cvxv33Bn5ZbhZeCByxtgDPjY06GaSdShy6JuoZRgo69j5zBHJpOIMDBT7HLLymTSVYys/Eom16teyQTw676oXU5TEtrGWsxgIJRMGee9brscANx9YYjDiYMndpKzUKMwHTf5OUgIH+9z2eUeL2SVA4DVjgqFklbJlIK5Vwsh5OcIIUNfTXRECFknhPzskn7+jwB4IyHkUQBvBHAVPFhcBfDV/vfvB3AHgO+Jv5kx9quMsdcwxl6zvb0d/3aLEwolIJny2+Wo5Ltqi0CEf9e5+E8kmUbHMyomYNYud9KVMOJB9fJzq4Gyj86zy51wkum0tMsBAI0GfztLIplW+GdSi5JJZCw56RO+1Y4G3XXAEvKYgOhiqt5xcXOgY+fY4hM7qmYGfzuuB10sTCTKZPIUAwZsDBQHcCa1BH8DKB3+bbuzqocWc6D3uX1CELmCZJIp+FtXMElol9NLbqgl5SwCof0q6dklFD/7Yxu/8o578c6vvL3Uz24Cd18YQlMIHopY5ppQeM7D5sBIbpebUTLx+V9qJpNpLkVJ3TNUjMwC7XJ1kUwFlUxJn2GLZAi73NFEHrscANx7kW/yPnplvmUu1S4H8PDvvHa5AlY5gCu413sa9kZtu1wS8lwtb2GMBWeYMbYH4G/leN9VABcif7/N/1oAxthzjLFvZ4zdA+An/K/tg6uePuFb7RwAHwJwb46f2eIUQFSO57LLOafbLgeESqZ6g7/54jXeLhdvlose16F9CIUoQRD4SYV4UIk8JiBs1Euzy3knnWQ6RcHfqkbhCbuc5S7HLjeo0S7nE0dZlrm1rgbNc+CltBk2tZjaGhjcLgcAVMsO/na90HIjXidDJo5qQIeNdcXf+awhkwkIA3SLwnJau1xh6D1+zQlySdjlJBr76rLLMd9mlaRO+ortAd74km38++99AN/4iltL/dym0NEUvPq24VTDnCUhybQ10LGTI5MpOH92OsmUFN5eFD1NyVS0xBG2hFZslyusZEppG2sxA6Fkks0ud8fWACsdNVcuE2+XS7kG4xl8STi+Dox3CzXLCaz39Db4OwV5RgWFEBKMXISQLoA8I9nDAF5MCLlMCNEBvB3Ah6MvIIRsESJ09fhxAL8eee+QECLkSV8L4K9z/MwWpwCBXS5P8Lcrgr9PryRWNMw1rmQ6HiUrmWioZDrpBAUAnFk1cOctK/hbrwwn1URRQHs9eKdUyaRSFQT8HjrpdjllRsm0+ERTkEx12Czykky658BLIWWas8vpYYgtVedkMrFwwipRJhNROzBgY0PhWTXoDCv9eYJkKq9kYq1drih0bn8OLHMS2uW68Xa5RUkmRQEUZarMA+D2q7Qsn2FPx29+7wN48I7NUj+zadx/eQOPPXsjsH+ZjgeFEqnypDb7+qySKZFk8p8LKcHfnpV+HougZyjFlEwuAyGo3Jamq8XtcjKRiTIjsMtlnPcm7HLUj6z4RG4lU8p8R+sD9hyS6boI/S5mlwN4LlObyZSMPFfLbwP4CCHk7xJC/h6APwLwm/Pe5CuQ/hcAfwjgbwD8R8bYZwgh74201X0NgM8RQj4P4BYA/8x/rwtulfsIIeQxAATArxX6n7U4sSiTyURP8cNENMzVSeDQEnY5BnbiCQqAt1r84T98A+6/fWPq63RlBW5aJpNtA4oyk1d1UkAICc7jST+HihYL/l7C2LA5qM8uhxwk06qvZHLUNJLJt8vVHHC7OTAwtl2+qKNKZrucJWnwN9UM6MTBkPqT0s5qpT8vsMuVVTK1wd/FofuKXEEyOSa/9ipWYxRBV1Nj7XKLL/KIridkMplLyfKREQ9c3oDjsWCRKiPxsDkwZtrlkqxe1Jhvl1tWJtOx5eQuIrA9r/LQb4Bf90Xb5WQ717Kib/B565GZ0QbbgF0OAO65MMRnv3QwNycs897WuvNJphLNcgLrPQ37rV0uEXNndIyxXyCEfBLA1wNg4KTRpTz/OGPsvwD4L7Gv/dPInz8I4IMp7/0jAK/K83NanC4oBZRMgV2uRgln3bi0egmUUAyNanfUo0jKb/BGIygrswuuqD3uNCiZ0kBXBvAOkwMImZUewnxSoCs6TNc88XbHaLvc0oK/B/UGfwMAy8hkWutq0FwbjtJN/H5TthBRyb1zaOGiMt8uF5B2QfB38yQt1TowYGEtIJlqCv4uoWRijLXB32UQ1Fr759i1pFIxAUBXpxjZLhhjIIQE9/QiOTM0kWSagC7BZiUj7ru0DkqAjz65i9e9aEtK4mFzoGNkcWK+p/MlWVbwd3Ym03KUTB7jJE0nhwrYcVnleUxACSVTWttYixmsGHzOcZShZLIbsMsBwN0Xh/AY8Klnb+C1GYrKzHtb780P/r7+OLdLD3PRG1PY6Ov4eA611c2IvKPtl8EJpr8Nbl37m8qOqMVND1VrM5mieNOFN+HD3/Zh3NK/pbafmWyXS1YyEUICYqKjdOo5wAag9AfwjtJIJisMbD6hELbHE69kUnm7nOcxeC6Dqp+wTCY1byaTC1tJ3idqyi4nPqedY18ZkkEyOS4LrGJBQLgESiZV70CHgzXUlclUPvhbhO7qp3iTpRIEdjl/PHctqUK/AZ7J5HoMtn9dBIu8BaIBuJJp2m6VZZc76VjtaHjZrat42M9lkpF4EGNmVM2UlLMWtstVa5fr68I6lS+XyfUi43iFKBr8bTpu7UrekwqhZMo653YDdjkAuPsCz0b9xJxcJtPO2GyJt4kmYedxYOOOUhtdQz+TKa/672ZC6tVCCHkJIeR/J4R8FsAvAbgCgDDG3sQY+79qO8IWNx3EorBQu9wpJpkIIbi0WpxdX+hnBiRTuND1RsmZTECoYDrdSqYVuFkk0wlXMgly6aSTTKpvlxMk9TII6O0mlEwZJNNKR4Xu2rBSSBnTFk1U9bfLAf6CiaqAmx0kKqNdTtW7MGBjlfiT0srb5coHf4ud/dYuVxC6/xyL2uUke3Z1/cW+yGWyl6BkSrfLnewxPwsPXN7Ax6/swXK89JrzBiHUn9cjmS6WO6vKIIFdbva5wFwXsO2l2OV6fvFONHQ+C3ZNdl2tTPC3ZOdaVqgKhaHSzODvpuxyG30dt2/28OiVvczXcbtcWiZTTrvcVvHQbwDY6OlwPJb5+d2syLpaPguuWvomxtjrGWO/BCB/GlyLFiUh7C1unkwm9/QrmZpAkpKJpQR/A6dHBZMFbpdLD/4WwZwnFYIg1GRo+FoAisaDvwOSaRl2uRWRySRH8LemUBjMhUnSlEwuVEoqD2ONYzPYlZ+vZJq2y8lDMmlGBzqxsYKaSKYFlExhrX37/CuEIJNJYrucr+ge+4t92/UWDlgmuj5lgQd8m9VpJplu38DE9vDY1RtSEg+bfV/9ecgVSoyx5OBvLd0uJ762jPMo6uzzNsw5LqvlOWMUVjLJp1qTGSsddS7JRGsIeE/C3ReGePTKfqZSyFrELufawN6TpZrlAB78DQB7x20uUxxZo+23A3gewJ8SQn6NEPJ1AFpNdovKofo7KXmUTKFdrr00l4kkkskdjUD7/cTXC2LiNJNMyiDbLkdT6uRPCk5N8LefySSKA9QlkEw9XUVXUxZSEeRFQDIl7FhHYTAHE5I8iW5qMbXZj+zK5yCZQiWTyGRqnmRS9S46sDFgI348WjKxvrSfJzKZWiVTfdAEySSxXc5XdAslk+UyaAoFIYva5WLtctbpVjLdf5kXeDz81K5fay/XvRKoP/2GOcdjYGxWsRbOyWbtcqJxjiwjk0kX1qmcSiavHiWTXljJJF/Iu8zoG+pcu1xTrYz3XFzHC4cmnr8xSX1N5r09zy639zSfq5RUMq33+Jxtb9Q2zMWResUwxj7EGHs7gJcC+FMAPwzgDCHkVwgh31DXAba4+aAWCP4O7HLtJHupCCY0vpqCWRZg26D9m1jJNMiwy9kn3y53Ws6hovFMJkFSL4NkAoC33ncb3vCSraX8W1nIo2QCAN1zM0gmF0aO0NZlo6MpGBgqdo5MQNEy2+Ucl4UhooGSSYKdZ8VAT3Fxx8DhKqYFFvV5sEjwt9UqmcpBKJmEhcIxJVQyccJVtCrZ7mzjWFEk2+WsU9suB/DMozu2+3joyd1sS01DCJRMfiZTEPAeu6epka5k8kz+taVkMhnT19081BX8XbhdzpaPUJQZfV3F0WSO8rihddbdF3jp0aMp4dqO68HxWHm73M7n+e9bLyl1fELJtNuSTDOYe8Uwxo4ZY+9njH0zgNsAPArgxyo/shY3LahCAIJAiZCFmyH4uwnEpdneiA/Q8zKZTjpBkQW6MgAbjxMX/94pyGQK7HInvl2OwJ2yyy1nUfEz3/YKfOvd55fyb2UhP8lkY8xSSCa7OVvI5kD3M5mUUKGUAMv1wp1RiexyUA10YONS367cKgdE7HIllEwijLWpyf+JRWCX83e3XYuTohKh6ytKJnZol1u02SmRZJpMTrVdDgAevLyBh5/axciST93S1RX0dSUI/k7LvslqlxPqpiYymRzPqy/4u3Amk1yEoswY5LDL1d0sJ/CyW1ehqxSfeCY5l0lstqTb5fp8jE/LiLz+OP9980Wljm+jJ+xyLckUR6HRljG2xxj7VcbY11V1QC1aEEKg+rkq8+D6SqbWLrdcUF0sdH2S6ZhPxtPscgHJpJ7eyaoyWAEQfhZRnIbgb13RoVN9ITuGDFA1CsYA25f7LyOTqU4QPR/JpLoOjlMe4UnBsXVhs69z6wdVw9a4BEztjEpGMsGzgfFeTSSTH/xdRsmUonpoMQdxkskx+XmXCGKxP/YV3UmNY0VBdG2GpPAscyk2K5nxwOUNHE4cfPb5Q+lIJoBn2Qm7XNo9HbbLJZBMvl1uKZlMenElUy12ucIkk3yEoswYGGpmDleTdjldpXjl+bVUJdPcohNheU9TM+08DvS3ge6w1PGtC5Jp1GYyxdHegS2khKopgRIhC57PYJ/mdrlGEOTC5FQynRKrVRbogNdeJ1nmmGWfCpLpNJw/xd+9NMd8wrQsu1xdCJVM2btiqmvjOFPJ1Mwu7tbAwM6hBVAtO5PJidrl5MlkCsiG4xfqIZn8ibvrlQ/+bjOZCkLR+bU2pWSSa/wWwd9isW8tocUr3S538sf9LNx/O89lGtvy2eWAiPoTXIEDpJNMyXY5oWRaQiaTUSyTyfHqs8tZBYh4y8motG8xg74hr10O4Ja5x67eSLRMinsm0y4HZJNMJa1yAA9NVyhplUwJaO/AFlJC1fMqmdrg7ypACJmakOa1y+mSTdSXCbrCSaakhrnToGQyFONUnD9F42OBNXL8v5+sx1xAMjnpEz7meVBcF0de8v+NZzI1ZZczQiVTBsnkRANjpVIydfjvR9dqIpmEkqm4Xc4Mgr/b518hEDIdButa0imZOqJdLrDLsYWVGVQ3ZoKjmWmCduT6vy8bt633cH7IF5oy5vRsDQyeY4d06w9RFEBVM9vllqFIK6pksl0PCq3+MzVUCsvJX3AuY5OgzBgYKo4yiEWnQbscANxzcQjT8fDZ52fn35aTwy4HpJNM1x8vbZUDAEoJ1ntam8mUgPYObCElFI3CzeEJD+xy7U7u0hGtO85rl+sonXoOrgEoK9wu5yaRTHarZJIFIp/txCqZVJ9oybDLiUXFMaNwUnb2mppgbw107B5bYFTJJJmm7D8ykUyCaD2uh2TSRCZTCbuc3QZ/l4feB2xhl5NPydSLZzItxS4XPtMFmHn67XIAcP/t6wAyFqINYmug80ZOZDdGEl0PrHFRMN9CR5eQySQUdLmVTC6DVlcmU04injGGiaSqNVkxMBRp2+UA3jAHAI8m5DKZPvmY3i7nb45bCSTTaBcYXS/dLCcw7OnYb0mmGcg32rZoAW6Xy6Nk8hwPlBKQGh5yNxsKKZnoTaBk6vtKpqO0TCa5gmOL4jtf+p344ft+uOnDWBhCuWT6/vgTq2TKIpn8hYZNVRwmSNzNBq0Cm30dHgNsZJNMdmK7nAQkk1AyMbdWJVO54G+fZGo3WYpD70WUTKZ0JFNol4sEfy+o2ObP9OlxxbNOv10OAB64vAkgw1LTIDb7BnaPLXgey7ynqa4n2qjD4O/FzyOlBD1dyZ/J5Hk12eVI7ty6p66P4DHgtvVuxUd1etA3VIxtN3HTCliOXXcRnFvrYHvFwCcScpnm2+UyMpl2ROj3YiTTRo9vrrWYRjszaSEllNzB3x5oa5WrBNEJ6TySSfObeU6DEiYNirDLHaXY5bSTTTK9evvVeMvltzR9GAtDKJmsk6pkykMy+eSvpai4MZ59XZNV3ZsDPgZYHp1DMkWVTBJmMgFt8Pdpht4Pd7YlDP4W7XLCLldFJhNzHMBxltJKJjseuMxzmWRUMm0OdLgew/7Yzrynia4H+UtRLDOTCeAquuOc7XJ2XcHfigLXY7my6z71LCciXnVbuSDnmxEDgz97086743rQG7TLEUJwz4UhHn0miWTylUypdrkMkkk0yy2oZFrva9g7boO/45BvtG3RAnxh6OSxy7ksWFS2WC6mlEx52+VOMclEs+xypyCT6bRADZRMfiaThDvXWchDMomGIZuqOJgkkEx2g+1yA34fWB5NrQxmjMHxIosTz/8/UAnO1RTJVP0ipQ3+bgj6IKJksqVTMhkqBSHAJKpkWjbJtMRWMtnxFdv/P3v3FiNJmp73/fm+OGRVZlZ3V3fPTs9yZlbLJSlpJc7s0vQKoiztCgZkUrBJS4Jgcm3Ahi9kQJB9YxqQbFiw1hBkWDJgAZZt6EKQdSUIBAQIBgEZoET7wjBA2uSOTFPk8ry9270704fq7jpkxskXEV9k5DmrKiMzIvL/A4juruplR09mR0a88b7PO9CXf+At/dBnTvd9KHNcYf7Zm9HqIlOvN9eJJk3G5UywnfdwP/R1sWJ0qipJs7JQXif332OTDXNf/+aZjgKrH3h7WPdhdUZZZFryuu97XE6SvvD+PXg8YnIAACAASURBVP32J+dzAdvrt8sVHW2LxuU++Ua+pOTeZ251bKf9UC8Yl5vDlQkayQ/tRtvl8k4m3sZ1qK47ZrvcpMi0bFzOUmRqhLlOprBd54frdTIFSzqZ9pnJlJ8DrlZ0MrmunalxOePlgcz75u2rk+nmwd90Mt1A0J9kMjVwXM4Yo+PAK8flxvHttztVP9MllflMh5DJZIzR//Iffkl/+gff2fehzHk4yN97n7wZa7QiZ23RdkCpWizcVpHpOp1M6U6KD+6zYpNcpo8ev9Qf+vTdvRdF2mRQFJneLCkyjfcc/C1JX3wvLxD/8uPpbqbRuuDvYEXw9yffkO5/r+Tdrov6dJAXmbLs+g+Luox/gWik62QyeWzWqcV0J1N+cjbHi2fcD2K7XBjKBMGKcbnu/t3bxJvtZOrkuNwkk2lRkWkcp/sblytumEaJWVFkmunASeNmjMpJOx+Xc/8N4ht1MuX/GzKZbiCsbJeLm7ddTspzmarb5W57k+c+092NkCtOmI5vl2u6spPpvNLJdJ3g7y1mMkl5wWHzTKbddDL1NuxkipNU/++3z/TBu/Wfu7tkeLS6yBTvOZNJkj54966skX7p95YVmZZc86wbl7vlqJwknfYDRUm29L/foeLKBI20eSYT43J1scF08Lft92WWrKoNbH5jfOR3d7uclHczLRqXSzuwXa4rZrfLte38cJ1Opsj6enW5KPg72duq7tN+KGuki8ROxuBmzBeZkmYWmXp3av/jvOIGbVng6iqrNlFhjWqRqYGdTFKey3RZGZe7bceaDUMpy6Q4P2cc0rhck7kR42dvxis3RtolnUy1ZDJtvF1uN51M5bjcmvPkN777RldRqg/JY7qWTcbl9v05M+j5+oG3T/TL35wtMt1wu1wSS89/e0tFpvzf8MsLcpmquDJBI/mhVbJBu27KuFxtZjOZzGDxqJx0GJ1MkmRPhkpfv5n6WpamEkWmxnCdS+PLWNYzsm3bPOmKTAuyN5xyu9zS4O/9jctZa3R/0NNlokmg9wx3oxD4dDK57pSbBH+vuiHFGq7IlGVS0vxOpu0Ef+d/R5fpVhYnDmBcrslcYX4qk2lZJ9PCcbli7HFbnUzh5p1MUZIp2MFnrHvvR2s6mSah33QyXccgLDqZFmyrlVwm3P6vpb74/ql++fdeKK10/q4fl3OdTDNRFy9/N38QdsvNcpJ0v+jgZsPcNK5M0Ei+v2EnU5LJY7tcLUwYKo2mO5mWKYO/bbcvVr3BUOmbmSJT0XFCkakZyk6mi7h1m+WkPDtEQZBvflrCZamk/vJMpn0WHh4OQ13Ey8fl4nLMq5LJ1ITQb2n3mUxbCf7mM/Dagn5eZEqKfz8NfEDSD6vjctvIZMr/jlnxuV6Oyx3Adrkm86zR/UGoT87H67fLrchk2taG237vGp1MaSp/B+efTTuZvv74TCdHvn7fg8VLarDYyZpxuW0sHtiGL753T6+uYv32s0nBaO24nN+TjJWiy+mvf/Lr+Y8Pf+DWx3Wv6GR6Tvj3lP2/Y4AFvPAamUw8xa1F/tQsvwBPLy6WbpaTKsHfDXwavE325ETJbJGpDE/lQr0Jyu1yl3Hr8pgc4/urx+WKJ9e9/tHcdrk4SZWk2d4ymaR8/OMiNpMb+BmuOOJbOpnK4O+U4O+dCof5U+xxcT5vYJHpqBL8HcW3H1cpi0zj6SIT43L792DQ0yevR5Muz0WdTL3e4k6m8Uim18sfUGzBdTqZknQ3W8dcgXVdJtNHj1/m2T1t62Des0ELxuUk6Yvv52OQ1VymUVGIX/o5aEzxUGFmXO6Tb+Q/Pvy+Wx+X62R6SZFpyv7fMcACfpBvl1uX1J/EqSxPcWsxOy63USdTh7fLSW5cbjqTaVJk2s5TRNyOKywlUdreIlMQbJTJFPaP5jqZ1raO78CDQU/nkdYHfzd5XM54+UhVzcrg71uMywVLsvKwgnttL1/kPzbwAclx6Omq0skU3LJre7bItO0sH9zcg2GoZxt0Mi3OZBpv9TXs966zXW5H43IbdDJdRYn+5ZPX+oA8pmsb9PKHUqs7mfZ/r/W5t4Y66fn6pd97UX5to2ueoD8f/P3sG1L/oXR8euvjuu86mc7JZKriygSN5NaOJ2ueWhD8XZ+pItOacTkX/N31IhPjcs1XPR/4QUNGsK5pfZEpvzk86h/rVROLTMNQb1YUmcbx7LhccusVwlvjlhcc3c2fgNbM3Z/dNPjbty3MHWsCt3Hosngi3sBOpn4l+HscbyOTyeW9uU4ml+XT7YUdbfBg2MszmZLl528TBou3y41GWx15HIS+xnG60TlpV8HfvQ06mX71ySvFaaYPyWO6tp7vKfSs3iwZk4yS3XSsrWOt0Yfv3ZsK/x5vcs0TLigyffKNrYzKSfm4oTXSCzKZpuz/HQMs4G4O4/G6IhPjcnUxYTBdZFoxLueKS10vMq0cl9tSHgJup9q91PVOpuPBfJGpvODaY4Ht4bCny8QqW9fJ5DWwk8kVG3YwKiflGVyBZxTdMJOJUbkbCof5j5fP8x8b2MlUHZcbbzOTyRWZxm5crnkFtkPzYBDq2ZvxyuBvu2xcbjSS3WJ4ez/MPzsuovXdTFGalSO/dSozmVYUmT56fCZJdDLd0KDnrRiXu/35Z1u++P49/cunr8sCvMugXDku6jL4qj75xlZG5aS8+HXaD/WCcbkpzXjHADOqIy+rpAnjcnW5zrjc6dGprLG62+v2EyR7kncyVcc43X8jSydTI0x3MrXzI25dkcmNuQyGxwvG5Yp1vnsdlwsVyy4P/i7yh/wmFpmqnUy7+iOtvWHwdzNyMlrJbRxy43IN7GQ6DqbH5W5bULRLMpkYl9u/h8NQr0ex3lzFS7sTTbBkXK7IZNoWl89zsUH4d97JtMPtciu6q77++KUeDnt65y6deTcx6PmNH5eTpC+8d09JmulffCsvKo7iZP31TtCfDv6+eC5dfLKVzXLO6YAi0yyuTtBI7uYwXvMkJYmz1nYrNJ2dG5db3sn0lfe+on/8E/9Yn+p/aleHtxfe8ERKU2UXk7Zbgr+bxVYu0Nva5bi+kyn/3vHwWK9mVg6v3bSyAw+HPSXyZNI4XxE/w43LBVPb5RpSZPKKjsRdFpk8s/LmaZl9bxFsNZfJdFF0MjWwyOS2yyVppjRbHAZ9HXOZTFcUmZriwTB/DZ6cXS19nZdvl9tyJlPRyXS+Jvw7Ld6X/g4y4TbtZPrw3btbC0A/NMMVRaa4IeNyUl5kklTmMo3idP31zuy43LPfyH98uMUiUz/QCzKZpjTjHQPMcIWjjcblGnLi6xrXyZRl2dpMJmusvvfu9+7w6PbDDvMRi+rIHEWm5nHnD5ft1jZri0xFB8LJSV9nl9FUZ90o2v/GsQfDUFFWXPSl8w8KXEGlbL9PYsk2JD/LmLybaYdFpsCzNw7+bsoIQ+u0Ifg7yItM7sb61kWmohCRjmY6mfjs2rsHxXaqJ2eXS8/dS7fLjUZb7aQehJt1MrmNmLvocAnXBH+/GcX6zY/fMCp3C8Oev3BcLssyjZPbZ8Jty4NhT5950C83zI2idLNOpuq4XLlZbjuZTJIYl1ugGe8YYIZfPElZF/ydxqnsLTeuYDEThMqiSNnVlZQkKzOZDoV3kheZqhvmUopMjeM6mNrdybT8YsXdaAwHx0rSbGoTUBPG5Vwnk6SFI3ONzmSSJK+30yKTZ005QngdYzqZbm62yNTATqaj0FOWSa9HecH5tjfzJliWydS8AtuheXgy6WRaWmQKAylJlMXT59RsvP3tctL6TiY34ruLDpdwTfD3v3h8piyTPniv25ENdRosKTLF6cyijgb4QiX8+0bjcs++IdlAuveZrR3T/UGo5wR/T+HqBI3kb9zJxHa5uriiSXKWzz2v6mQ6FPbkRJKUVIpMbnSJIlNzlJ1MLR2lNUEgxcsv8LPxSCYMdbdYm1sN/27KdrnYXV6k8x1ZUeJuTho4LidJf+w/kX7wz+/sjwusuXEnU1NyMlqnBUWmfhHe7/5937agODcuRyZTYzwc5K/Bd15dLe1OnH39nK1nMrlOpjVFpvI8vsvg7yWdTB89zgsOH9LJdGPDI1+vFxWZkt0VEzf1xffu6emrKz05u9zsYcvsuNwn35Duf3arW23v9UO9vJjuLD90DbqqAyY2zmRKGJerS1lkepl/eFNkkuyg6GR6M2m7nWyXa95NyqHyiu5GL2zICNY1Gd8vi5eLpMWT67vHeX7Q2WWkT987llTpZNrjdrl+6Mu4bKMVnUxhUzuZ/sRP7/SP8z1bPi2+jm2stT9YgSsyNXe73HFx/nLh/rfPZMr/TbouyWw0loJAxmvnebJLHgzz64c0W/6AwHWcZeOxVLkeyzOZtnf9UWYyrRmXi4vz+E6KTGs6mT56fKZ3T491f8B12E0Nw8WdTOPZzuMG+ML7p5KkX/69l3km07rrnUXjclsclZOk+4NA4yTV+TjRsNeg65k9as47Bqhw43LrOpnSOJMX8CS3DnNFJsblJuNyb6qdTIzLNY1XXHD4Le1yXJ/JlBeZ7lSKTM64AZ1MknTkbnpWZDJNxuWSZhWZduymwd/jLWwcO1gtCP4+KjuZ8hu/297kLdoux1bUZuiHno6Kh6urgr+lSaaWk7+O2xyX26yTKd7huFzgr94u9/XHL+liuqV8XG5VhmJz7rU+/84dhb7VL33z5fXH5ZJYev5b0oPv2+oxnRad5S8YmStxdYJGciNwSbT8wjvLMiVxKtug6nqXuKeeyYt8nIBOpiXjcpErMgV7OSbMc52Qbd08uX673FgmDMpOpqaNy0lSr1eskU4WjcsVRSa/2sl0uN0U/q3G5dr5Ht87vycZ2+jg734xtnRW27jcFaNyDWGM0YNiZG55JlPRyRTNF5m2+ZBrsGEn0+RhwX47mZ69Genxi0t98C55TLcxPMq3y6UzXbVNHJcLfas//Ok7k06mteNyAykZ5Q+0Xv5uPsa/xc1yUqXIRPh3qTnvGKDCbYVaNS7nToQewd+1mO9koshkh3mRadG4HE+Em6Mcl2trkSlcv13OhtPjco7bLrfPcTlJ6h+5TqZF43L5uTtoaibTjvnW3jj4e9/FxNYyRgqHlUym5j0kOHadTFdFkem2wd9znUzbDYzG7TwsRuaWF5mWZTJtOfh7w0ymsvhgd9DJVLz3FxWZPvpWnhvKZrnbGRaB7xcz911znccN8YX3TvXRt17qfBRv0MmUxwlofF7LZjlJOi1GNQn/nmjWOwYouJvDVZ1MaZx/wFkusmthGZebY/vHkjHT43IRwd9N04Xg71VFprQI/r5ztKDIVGQy7Xu1fd91Mq3aLmernUyHW2QKPHOjTKYoyRp34d8qQV+6ym9Q5TWv2OIymV5tLZOpGLdiXK6RHgyLTqal43JFplYR2O5ko9FWM5lC3yrwzNTW0kUm43L1P+g1xij0rMYLOj4/+uaZjJF+kE6mWxkUY5KzuUzjHXasXccX37+nqyjVb358rp6/QSaTlI/MPSuKTFsfl8v/fdLJNMHVCRqpzGRaUWRKiicaBH/Xw12QxozLlYy1ssOhktdvyq9Ngr+b9yT8ULlx29Z2Mm0yLtfr6eTIlzHSq6vJRWE5Lrfnv3v/OL9hSuNNxuUOPZPJ3mhcbhyney8mtlo4kFT8d2/guJzrZNpe8HfRCVNk+qTjkczR0a3+f2J7HhSdEMEmwd8V2WhUfm9b+qGviwUh0FWu+3JXhe7Qt4s7mR6/1OfeGhK2fEvuv9/rq+nXPS47j5v1WfOF9/LOtSTNNstkkqSo6GTqP5D697d6PC50/sX58mu3Q9OsdwxQKLfLrQj+LotMjMvVgu1yi9mTodLXBH83mSsytbWTSZsEf4ehrDUa9vxGZjL1j/Ob1zdXV3PfY1xumm9vFvwdJenSG1JsIKx8pjUw+HvSybSd4G/jeZLvMy7XUA9P1nUyzY/LZVlWZPRt93UchN76TqbiPO7tYLucVBSZkuljyrJMX398Rh7TFgyXdDI1dVzu3dNjPSy6/9Y+VHPn+vFFXmR6sN08Jkm6cxTIGjqZqpr1jgEK1jMyZnUmkysyMS5XD7bLLeYNhkrPJ51MKUWmxvHLcbl2hklv0slki/GIu8fBdJEpasa43ElRZHr55mLue+5pNONyOf+G43KjOG3cCEOrhMPJzxtYZHKr5CfB37d/rU0YTopMV1eMyzWI62Ra9oBg0Xa58iHXtjuZer4u1xSZdhn87f6cKJ4+Tz45u9Inb0ZsltuCto3LGWP0xffz1339uFxx/+LG5bYc+i1J1hrd64dkMlVwd45GMsbIC+zKcTmXyeRRZKpFWWR68VKylieeBXtyMj8uZ62Mf7g3yU3TiXG5ePmoQjYayQSTItNUJlOSh0Ebs98LwsFxHrT56s3l3PfiNJVnjax7Ap5Gh11kslbxDTuZ9t2x1mph5cFJA4tMR1sel5MkGwST7XLjEZ/rDeK6MtZul6t2MhX5THaLmUyS62RaNy63u+BvyXUyTZ8nP3qcPwSlk+n2ynG5UTvG5aTJyNzGwd+vvy2df1xLkUnKc5leXjAu5zTvHQMU/MBTsmpcLnHjcryN6+BuYpOXL2X7/b3ftDbF/LhcRBdTw7jiUlvPDcZf08kUTcZc7hwFc9vlmlB4OOnnx/fqYvG43NRT0QPPZLp58HfayAv/1nA5HTaQdnSjfB1z2+W28O/ahKGyiHG5JnrgtstdZ1yuKDJtvZMp9HUx2qyTaRfB31L+32U2k+nrj8/kW6M/+M6dnRxDl7VtXE5S2cm09tzoxuWefD3/sYZxOSnPZaKTaaJ57xig4IdW8YKQP6fcLtewFs6uqI7LMSo34Q1PlL6Z7mSiyNQsbc9kWrtdbjR5z909DsqbUCkfoeo1YEzwZJCPy72+mO9kGsczxZE0luz+j3lf8k4mgr93zo3LNTD0W8pvnHxrttrJNDUut+WtZLidB4P8fRgsGYsst8uNJ9vl3Ojc1jOZeus7mZJ0tx0ugbe4k+kPvHNSdv3h5to2LidJH7x7T/3QK7sAl3Ljck8+yn98+AO1HM+9fkgmUwVXJ2gsL7BKVsyET4K/eRvXwV3QpK9fE/pdYYdDJRSZGq3sZArbeW4wQSAlibJk8fkv3y63ZFwuThpReDgpzhmvzhd1Ms0UR8hkUpTeZFwuI/j7NtzT7QaOyjnHgVdmrm3j37UJwzJHMN9Kxna5pnhYdjItLpgs2i7nCk7bLhb2Q18XGwZ/+zsK/u7NbJdL00wfPT7TB+QxbcXJUfvG5YY9Xz/3n35ZP/Wl91f/Rjcu9+SX82uN08/Ucjz3KTJNad47Bij4gbcyk4ntcvWqBoJSZJrwFmyXcwU5NIMrMvktvQE3QfHEekkuUzYalYXNO8f+TJEpXb9pZQd8P/87vL6cLzLFSTY9YnHoRSZrrt3JlGWZxozL3Y7LZGpwkeko9Mqbvu11MuXni3TMuFyTnA5CWSMdLTl/l8HfCzOZauhkGq3uZCrH5XaZyVQpMv3Os3O9vor1IXlMW9HzrTxrWjUuJ0nv3D3eYFyuONeffyydflby6rlmPx2EenEeKcuu35ncRYd7VYfG88PNgr/ZLlePancO43ITdniibDxWOh7LFqMHNmjuTcoh6kLwt6R8ZG7BzUM2HssW4xF3jwNdRalGcaKe7xWZTA0YHSiKRhcLxuXmsoQOPJPJ964f/B0VRakm5G+1lhuh8Jt7/u6Hntz9yjbGVUyvN7VdjnG55gg8q7/9k19cuimtzGQa7SiTaV0nkwv+3tl2ueki00ePzySJTqYtMcbkge8zWVy73iJYi6DyoLymUTkpD/4eJ6nOx0mZcXXIuDpBY3m+VbxqXI7g71oZOpkWssM8x8PlMmUR43JN47KY/JbmNEwVmRZIx9OZTJL06jJ/+pgXmxpwTiyKRudXo7lvjReOy7XztdqGmwR/d+LCf9/KTqbmdvMcV85h2xiNNGFQFibycbnm/t0P0b/14af1/oPF11tmwbhcbZlMxXa5VR0Zuz4HzW6X+/rjlzoKrL7/U8Od/PmH4OQo0Our2U6m5o7LbczvSSrepw+/r7Y/5nSQX5e9IPxbEkUmNJgfWiWMy+0NRabFvJOiyFSMzLFdrnk608k0ni8yZWkqRdFku1xRZHIjc+O4Gdvlyk6mBUWm+U6mQx+Xs9cuMrkn+k3I32otV2RqaPC3pKlA42281q77NktTZVG09eIE6jP5XKhmMuU/t1vuSDsOfWWZdLXiGnySybSjcbkFnUx/+NN35XMO3JpFY5JNH5fbiDGT831Nm+WkPJNJErlMhRa/Y9B16zKZJtvleBvXgXG5xezJiSQpeV3pZArIZGqSB98z1N1PHat/p53Fv1WdTO6mYpLJVHQyFRvmmrJdzmUeXFwtzmQqNyhlGUUmz5QX8psqL/ybUFBsq7KTqbnn735Y6WTaRiZTUBSZ3HmETqbWMNZKQTC1XW4S/L39TCZJKzfMJbsel6t0MsVJql/5NqHf2zbo+XOvedyVrlk3MvewviLT6SD/LHlxsXw78CHh6gSN5QWrM5kYl6uX8Typ2HJCJ9OEHUyPy6Vsl2ucT3//Pf17X/ujCnoNKLbcQBkkHy8vMrkn13eOpjuZRg3rZBqPx1NPn6V8XK58+p0V32vwjX7dbhL8PaKT6fZaNC7nWSNvC1u8TBgqi8aTwOij5v7dMc91ojl1ZjJJ0sVoeWSF24i5qw6XXqWT6de/80ZXUaoP3yP0e5uGPX9uXG7sOtba/lnjNszVmsnEuFxVy98x6DI/tEpWZTJFjMvVzRVP7IAik1OOy71hXA71MH5+gb+wk8ndVMxlMrkiU7J+08ouFBlLvlI9n7ngiqqZTGk89fsPke/ZsitgU66TqRGvdVu5J9sNDv4+LjqZttVFYMJQ6XistDyPUGRqExOG5Wsn1ZvJJK3uZHKF8W0UPzdR3S730eOXkgj93rZhz186Ltf6BxrhQDq+L/Xv1/ZHuCLT7DXPoWr5OwZdtnZcLmFcrm6TIhPjcs7cuBydTNiyzcblJtvlpEqRKWpKJ1N+XL5J9Mmb6VymqDouVxaZDndcLrCm7ArY1LgLORn7FhaBwS3oZNrW62zCUNk4qq0DBvVyr58zeeiw3U7QfrEZ62JFkakc2d1RJlPg2fLP/PrjM9058vX7loSk42YGC4pMblxuV2ORtendkd76/bX+EXeOA1kjvSSTSZJ0uFd1aLy143Iu+Lul4b5t4C5cGJebmNsuR5EJW7aqyDR5cu0ymfKP8elxuQZ0BRVFI1+LikypTo6Ky48kmvr9h8j3rLIszzjZtCsgKjIJW/90eZ/C4nPNa+7523Uybet1Ni74243LbTkwGvUyvd5M8Ld7HWvqZFoxLhfvOJNptpPpg3fvyZiWFz4aZtjz9Xq0ZFxuRx1rtfnTf7P26wzPGt3rh3pOkUkSnUxoMD/It8stW6FaFpnaXl1vMBsUnUwUmUrecHZcbrz1p4g4bCs7mSIX2Jv/2+z5no4Cq1dFjsIoThrSyTQpMj17Mzsul00ymdJk6vcfIldYuk7495jg79srt8s1t9AyGZfbUpGplxeZUjqZWsmEQVkglFTb61hmMq2IrNh1h0tYBH9fRYl+7elrffAueUzb5sblqvddbry99QW9dz6Q3v587X/MvX6gF+cEf0sUmdBgfpi/PZN48YV3OS7HRXZtXLeEochUMkEgc3TEuBxqs7LINJp/cn33ONBZsc1kHKfqNaG706sUmc7nO5nCuXG5BnRf7YnL24mvkcs0Jvj79to0Lrel7EkbhspGI2U1ZfmgXjac6WSa6WzdFrddbvW4XH6+2u24XKZf+fYrxWlGHlMNBj1faSZdVaZI4iRt/6jcDt3vh3pBJ5MkikxoML+4uIrHi4tMSZTKWCPb9hbOBnMXLh6ZTFPsyVDp60knk6XIhC3aLJNp8p67cxTo7DJSlmWNG5cLbbagkymddGaQyVR2dSXX2DA3Cf7m8+/G2hD8HdQ1Lnclie1ybeO2AzrZaJQ/+Npyocd1Mp2vWr6TZrJGO7sGdx26/8/vvpAkNsvVYFgUF1+PJtceUZKR/XcNp4OQ4O8C7xo0lstaSpbkMiVJxqhczcrgbzqZpniDodLzSidT0NybFLTQykym+fGIu8eBXl1F5QhVk8bl7oaZPpktMsUUmapcJ9N1wr8nnUwNKCi2VXAsyTQ6k6m/7XG54jM9PT/Pf824XKvk2+WmM5nqeA3LTqbRik6mNN3pWntXaP2F33mut056enTnaGd/9qEYFlmJ1SyucfWhENY67Qd0MhV416Cx3LhcHC1+kpLGKaHfNWNcbjF7cjIZl4sixuWwVRt1MlUKm3eP806mUdykIlP+dzgJzfy4XJpNVrJTZCpv1OIbdDJta4zqIBkj3X1POnm07yNZ6sh1Mm3p37Q7b7jPL4pM7TIb/J2O6ikyHfmejFndyRQn2U7DoN1nxv/9uy/04bt3258R1EAD18FWKS7GSTr5vMZap4NQLy6ipXnCh+Rwr+rQeGvH5eJUlhNfrRiXW8wrxuWyLKPIhK1bnck0Hfwt5Wtzf+07rzWKmlRkspKMhqHWjMsR/H2r4G+eMN/Of/S/TwLAG2jrwd+uk+n1q6lfox3cuKOTjcZTnwXbYq1RP/BWdjLFSbrTIlNYjIE/Ox+Tx1STYS//HH59NXndGZe7nvv9UOM41cU40aB3uNc1Ep1MaDDXpRSvGpdrws1Uh7mtaXQyTbPDE6Xnb8oigCsKANvgug2yaP4Cv1xZHU53Mr26jDSK84JNIzKZJMkLNAykT97MdDItHJdryDHvAcHfe9S/L/nN7eaZjMtt52beFZWSIlPQ0snUKrPb5bLRSLam8PZ+z1/ZyZR3pO5wXK5yvc9muXpMxuUm1x5jOpmu5bSfn2PJZaLIhAbz12UyRSmb5WpWZjLRyTTFDodKXr9ZGMIM3JYJvJyGmQAAIABJREFU8gu9leNylZvDO0e+Xo9iXRWjxY3YLidJ1tcgyDuZplciZ2QyVZTB39fIZHKbnbY1RoVmcuNy2+9kKsbljsi1aZPZ7XJpTZlMkjQIvZXb5Xa9daxa6KCTqR6u8+Z8PDsux+fMpk4H+Tn25cX89duh4V2DxvKDNZlMSUrwd81sGEpBwPa0GeW4HEUm1GDVuFwZ/B1Oj8tlmcqA7UaMy0l5kcnPn4S+Lp6MZlmmKE0VzmUyHW43YBn8fY1MpnHRtUYnU7dte7ucLUarkjd5J5OpqQsG9TBhqDSaHZerqZMp9KcCoGfFaVYWyHfBfa69d/9Y9wdcc9WBcbnbuz/Ir2WeE/5NkQnN5YfrMpkygr9rZoKQzXIL5ONy58qu8jXQbqwQ2IbVwd/FiObMdjlJ+vh1XoBqzLic9dX38vO3y2VK0kxZNgm7JpNp0sl0veDv/PcGTSkoohZulfzWgr9dJ9MrNy7HzXqbmDAsc/kkNy5Xz2vYX9vJlO10jMr9G6CLqT6uyFQdl4sYl7uWe8W43AvG5Qj+RnN5a8bl0jiVpbpeq6MPP1BaFFIwYYdDSVL84oUkOpmwXauDvxd3MknSd8siU0POi9bXcXGV8ezNSJ99OJgUR8hkKrmO3Oga43KT4G8u/rvsuK5xuTev83B+n9uANpndLpeNRuX1yLb1e77OLpeP/MRpWi4t2AX3b+BD8phq0w+LrYIzRSafe62N3XdFJjqZKDKhudaNyyVJKo/1zbW6/9Wv6v5Xv7rvw2gc7yS/qEue50UmxgmxTZMi0/xFSjmiWQmbn+1kakxOj/V15OVFJTfKN1ccSaPy9x6qoMxkIvgb0+raLpe8ei3T67EGvmVMGCgb5xl3xhil0VhejZlMT15eLv3+rseoPn3vWIFn9COfe7izP/PQGGM0CP1yvF3KX2dX7MZ6d44DWUMnk0SRCQ1WbpdbNi4XZfJDLrCxe3Z4IklKnj+TRCcTtst4nmTtknG5kUwYTt0c3i07mfKuw8aMy3m+jm0xLneeF8CioshUFsII/i7Dc91/m024EQaKBN3mikzhlh6oTYK/X/NwpIVMGEppKsWxFARFJlNd43K+LlZsl9t18Pfn3hrqV/7ajzbnIUpHDXv+XCfTnaPD/Xy+Ls8a3T0O9ILgbzKZ0FwukymJl4zLJYzLYT/KcblnzyVRZML2mSBYHPw9ng96vTObydSUrDrrK7TTmUwud6gMjCWTqezquk4m0zhO6WI6AFsflwtc8PcbNsu1kC3O/a6jNc9kqqmTqedNbRmbtevgb6lBXbodNuh5U4HvUZIxLndNp4OQ4G9RZEKD+es6meKMcTnsRTku94IiE+qxrMiUjcZz77f54O+GfLRbX14W6+5xoE/eTHcyBXPb5RrSfbUHngv+vkYmU5SkhH4fAM8a9Xy7tYJitZOprq1kqI8rEqaVIlOd2+UuVm2X23HwN3Zj2Jsdl+OBxnWd9kPG5cS4HBrMekbGrMhkilN5XGRjD+xJPi5HJxPqYoIgH4mYkY3nxyMGoSfPmgZulwukNNaDYVh2Mo0Zl5vjWzcud41OJi78D8bf/PMf6g99+s5W/n+5TajZeMxmuRZy1xqukykdzz902JZB6GmcpMVo7vy5Jk7TnXcyoX7Do/lxOYqJ13PaD/WtFXlmh4KzAxrLGCMv9BQv2y6XpLJ0MmEP3Lhc8rwoMlVCmIFtML6/dLucDaZvKowxunPk61nx5Kw5nUyelCZ6OOgt6GSiyOS4/xbXC/7ebegu9ufHP/y0PvfWdjaIVXOYTE1jVqiPWTAuV1smU7HOflkuUz5GxTV41wzC6SJTzLjctd0fBHQyiSITGs4PrJKV43K8hbF7nstkek4nE+phgkDZeEGRKZrPZJImI3NSszKZlEZ5J9P5bCaTG5cjk+kmwd/jJCWfBNdWPXcwLtc+ZSfaaKQsy/KHDjVul5OkiyW5THknE0Wmrhn2fL2+mrzm4yWdbFjutJ9nMmXZ5g+Ouoh3DRrND+zqcTlOfNgDc3wseV6lk4kiE7ZrafD3aLSwqHmnUmRqzBiVVx2XyzuZ3LhcMDcu15ARvz0IXCbTNcblIoK/cQPVc0ddHTCoz9S4XBRJWVZbR5rrZDpfkstEh0s3DY/8qcD3PJOJYuJ1nA5CjeNUl0vuXw8FZwc0mhdYJcvG5chkwp4YY+QNh3QyoTYmXBL8PY5WdjJ51jTnwt/6UprowaCnFxeR4iRVVGwLDWfH5bzDHTn13Ha5awd/c+GP66l+Vtke2+XaprpdzoV/19WRtq6Tiayebhr08nE514VDMfH67vfz8+zzAx+Z412DRvOD5ZlMSZyRyYS9scOhsosLSZMWdmBrlm6XGy18v7lOpsbkMUl5kSmJ9HBYXHBdjMtwazKZJgKCv7Ej051MjMu1TbkdcDRWNsq7Q2vLZApXdzIlaUbwdwcNe76iJNOoeCDEuNz13evn12Mvzuev4Q4J7xo0mh/ahUWmLMuUJHQyYX/chjlpOkwV2IZl43LZeCy7YDzizlFDi0xprIfD/HifvRkrSl3wt8tkosjk3yj4mwt/XJ+xVvLzf2uMy7WPG43LxpMiU22ZTL11nUwEf3fRsByTzF/3mHG5a7s/yM+tLy7oZAIayw+s4gWbLbI0kzLJo5MJe+LCvyXG5bB9y4tMo5Xjcj2/QdlGRZHpQVFk+uTNqByXm3QyEfxN8Dd2yX1e1VWcQH3KTKZorNR1MtWVyeQ6mZZslyP4u5sGlSyuJM2UZmJc7ppOKTJJosiEhvMCb2EmUxLnT3wtJz7sSbWTiSITtm1p8Pd4vPD9VhaZmrJZTpoK/paKTqbZcbmk+DsS/K34Gp1MEeNyuCEb5OeKuooTqM/Udrkyk6me64+yk2m0ZLscWT2dNCxe99ejqHzwQdfs9ZySySSJIhMaLt8ut6jIlH+NcTnsi3WdTNbK+IfbhYF6mCBQFs9f3Gej8cKbijvH+XuwWeNyXj4uN6h0MiWMy83yim6A+BqdTFGcceGPG3FFajKZ2qca/F33uNy6TqYoScs8OXTHsJcXMs9HyfznNTZy9ziQMdKLCzKZgMby1haZOPFhP7yTvMhEFxPqYPzlmUyrOpkaNUJVBH/fOfblW6Nn52ONZ5+MUmQqL+CvHfzdpNcareGKS/aIIlPblMHf42rwd11FpjWdTCmdTF3kOtjOR/F85zE24lmje8eBXtDJBDSXH1olC56ipMWJz3KRjT2xw3xcjiIT6rA0k2k0WhP83aCxM5uPyxlj9GAY6tmbkeK57XJkMhlj5FlD8Dd2ouxkYlyudcpMptFY6agYl6vpdQw8q9CzyzOZCP7uJBf8/XoUMy53C6f9UM/JZAKayw88xuXQSG5czgTz6+SB21q1XW5lJlOTzonWL4tIDwa9IpNpybicadBx74FvTbl5bxN0MuGmGJdrL1Mdlxu7Tqb6HnT1e97S7XJxmpZ5cuiO4dFku5z7vKaYeH2ng1AvKTIBzbUuk8ly4sOeMC6HOi0qMmVpqiyK1myXa9DHuvWkNP87PBiG+uS8UmTyK+Ny1pfMYZ/LA8+WXV6bGEVJs15rtMakyMRnV9uUnUzjcRn8XeeWwEHo63y0YJqg2DrmkcnUOZPtcpNxOZZMXN9pP9DzczKZgMbyQqskSpVl0xffabFdjk4m7MtkXI5OJmzfwiJT8etFhc07xw0clyu2y0nSW8Oenr0ZlZlMoTdTZDpwnjUbB39fRYleXcV6OKRIgOtzn1l1FidQD9c5nY1HtWcySXku02U038nkui4JhO6eQRH4/vqKcbnbOO2HZDLt+wCAVfxiHXcy082UJIzLYb9s0clk6WRCDRYWmcptQguKTEWLey9o0DnR+mWR6cEwzLfLFQ8IfPcEPE0oMim/WYs3zGT6zqsrSdKju8d1HhI6ypLJ1FrGGJkwVDYeK3VFphpfx35vcSeT67ok+Lt7PGvUDz3G5W7p/iDUi4vxXJPEIeHsgEbzg/yp/OzIXOrG5dguhz3xykwmikzYvlVFpkWdTL5nNez5zRqhsr6UuCJTT1dRqrPLSMZUxizoZJIk+XbzcbknZ3mR6Z27R3UeEjrKfWYZtsu1kglDpaOxspEbl6vvGmQQLs5kKotMjMt10qDn63zMuNxt3OuHGsWpLqPFwfmHgHcNGs1b1snkxuU48WFP7Anb5VCfhUWm8eptQv/Ov/qe/uTv/1Ttx7axaifTIP938vTVpQLPyhiKTFW+t3nwt+tkevsORSZcn/vMYlyunVwnUxn8XeM1SH9JJtNkXI5r8C4a9nzG5W7p/iAfbX1xcbi5TFzZodH8MD+xxTOVYLbLYd/KTiaKTKiBCQIpipRlWVmQmaysXvye+y//zc/v7Pg2UikyPRzmN7RPzq6mn4pSZJKUdwRct5PpEZ1MuAGX4cO4XDuZXm9mXK7GTqYl2+WS1I3L0cnURcOez7jcLZ3283+XL87H+p57hznazh06Gq0clxvPjsvlH3CMy2FfLEUm1MgEReElnlzgZ1FRZGrLVigvkLJEyjI9KEKqv3N2NR0WS5FJUj7uGG/YyfT07EonR76GPf674frYLtduJgyUjUb5uJzvy/j1nQf6oa/z8YJOJtfhYrmN7KJBz9P5KCnH5ehkur7Tonv7+QGHf/OuQaO54O/ZTCaCv7FvFJlQp3KLUGVkblUmUyPZYtNdGutB0cn0ndej6bDYNJ78vgN2vU6mS/KYcGNsl2s3G4bKorGy0aj2xSOD0NPFaHkmk0cmUycNe75ej2JF8cw2WGys7GS6oMgENNIkk2nZuBwfcNgP43my/X57bvjRKguLTGMX9NqSm0PXoZTGZSZTkmaMyy0QeHbj7XJPz67YLIcbm3QyteQ8gikm7CktMpnqfg37PV8XUaJ05tzkui4Zo+omNy7H63xz9weTcblDRZEJjbask6kcl6O6jj2yJye1bnbBAVtQZNpFBsdW2fzvoCTSUeDppBjvYlxunu+ZcgRlnaevrvToDgUC3Iwti0x0w7WRCUNlozyTqe4i0yD0lGXSVTz9oJcxqm4bFEWmMa/zjd09DmSM9Jzgb6CZ/HBxJhPB32iCt/+L/1zBp79n34eBDlrVydSawN5KJ5MkPRiGej2Kpy9Y04QikzYfl4uSVN99PaKTCTc22S7XkmI1prjg72w0rj1Xq188GDgfJeqHk/O0O1f5jMt1EuNyt+dZo7vHgV4e8LgcV3ZotMm43LIiEx9w2J87f+pP7fsQ0FGLM5lWb5drnEomkyQ9GPb0O88uyGRawLebBX9//HqkLBOZTLgxxuXazYSB0jdvikym+juZJBUb5iZ/ljtX0eHSTcOer3Gc6rKIKmFc7mbu90OCv4GmmozLTbfqpi50kE4mAB20sMgUuUymlhSZvGJczhWZioyCkHG5Ob5nNspkenJ2JUl6RJEJN3T8xR/S4Mt/QnYw2Peh4AbycbmR0l1kMhVFpvPR9DW4O1dRfOimQdHB5rpwKCbezL1+cNDB31zZodHWjctZikwAOqgb2+Vmx+XyG6KA4O85gWc3Gpd76opMdygy4WYGf+RLGvyRL+37MHBDNqyOy9VdZMrPzXkn04TLj2O7XDcNiyLTiyJPiHG5m7k/CPXtl1f7Poy94V2DRvOWBH8ncSpjJMsHHIAOmhSZJhf3qctkasuYiyseJfmF6sNhXhwjk2mebzcL/n76Kr9gZVwOOEwmDJVG43xcruau1kGv6GQaz3QyEQjdacMjV2TKrznoWLuZe/3woDuZODug0fwyk2lmXC7OGJUD0FnGX5HJ1LYiU5qfv924XODPdDJ5FJk2HZd7enapo8Dq7nGwg6MC0DRuu1w2GtW+BKLsZBpNdzKVq+150NtJk3G5/PqDYuLN3B+QyQQ0lvWMjFncycSoHICuWr1drp3jcg9PinE5SybTLN9aJRtmMr1z91jGcHMHHCK3XW4XmUyDclxu+kFvRCdTpw2LDrYXZSYTnzc3cdoPNYpTXc78+zkUnB3QaMYYeaE3X2RKMjbLAegsE67IZApa0sVSBn/nf4cHAzKZlvG9Dcflzq7IYwIOmAmDPJNpHMnUPC7X71W3y00kBH932rCXf3a/vIjkW8NDjRs67ef/HZ8f6MgcRSY0nh9YJQuCvxmXA9BVk06mycVJFo1lwrA9F3yznUzDBeNyCUUmSQrsZsHfT86u2CwHHDC3XS67upLdUSfTbCaTK4j7luvwLhpUOpnoVru50yIi4MWBjszxzkHj+YFVPJfJlMryBAVARy0al0tH9Y9HbJXNL1TLTKZyu9zsuJy36yNrHM8zZc7JMmma6buvKTIBh8wVltLz89ozmY4CK2MWZDIVBXEymbrJbZc7u4wYlbuF+67IRCcT0ExeYBdkMhH8DaC7lmUytSaPSZJsMS5XbJe7dxzImrxrp8S4nKQ8pypa08n07HysKMnYLAccMBPknwHp+XntDx2MMRqE/vx2ORf8TQGik1zwd5aRu3Ub5bgcnUxAM/mBp3jBuBzB3wC6amGRaTSuPYNjq2bG5aw1+gOP7uj9B/3J76HIJEnyvfXB30/PriSJTCbggFUfNLjsvjr1Q28uk4ng724LPKtecY/Fa3xzp/3DHpfjyg6N54dWSTxdZEqTVB5PUAB01LJOJhu0t8gkSf/rf/yvaSpSKk0oMmmz4O8nZ5eSpHfuHu/ikAA0UPVBQ92ZTFLe1XI+mulkKjOZuA7vqmHP1ygeK2DJ0o3dPQ5kjPTiIlr/mzuI8iQazw+s4plW3STO5AW8fQF0U7lBLp4UaLIdrKzeKm++yGRnN9WQySSpCP5e18n0Ku9kevtui94DALZqupOp/nPBok6muNwux3V4Vw2P8s/vgHD3G/M9qztHAZlMQFN5gadkJpMpD/7m7Qugm4xfZCJUg79bl8k0X2Saw7icJMmzRkmaKcuWF5qenl3Jt0YPBxSZgENlq0WmXXQyhQs6mVI3LkeXS1e5zYKMy93O/UFIJxPQVH64KPg7JfgbQGctz2RqUYHBFY+SFRdYFJkkTW7WVoV/Pz270tt3jmQZUQEOVvUzYBcZff3egk6mYlzO41zUWW7DHONyt3PaDw42k4m7dDSe5y8Zl+PEB6CjFheZRjsJet0at11uZScTmUzSZOxkVfj3k7MrNssBB67azbqTTKYF2+XK4G9GqTrLjcv5vMa3ctoP2S4HNNWiTqY0YVwOQIe5ItN4Jvh7BxkcW+OyltJk+e+hk0nSJEA3SpeHfz99daVHFJmAg2aCPWQyjWYzmVJZI7oqO2xQdDKF3Gvdyukg1EsymYBm8hdkMiVxKi/gww1ANxljpCCY2S43amkmE+Ny67jci3jJuFyWZXpydqlHdygyAYesOiK3k3G50JvrZIqTjNDvjhv28odEPrlbt3J/EOo5RSagmfxgUSZTJo8POAAdZmaKTOm4ZZlM3ibjchSZpEm2ics6mfXqMtZVlNLJBBy4XY/L9Xv+XCZTlGQK6GLqtDKTiXutW7nXD3QVpbocr+jo7qha3znGmB81xvyaMeY3jDF/ecH3P2OM+TljzEfGmJ83xrxb+V5ijPnl4v/+SZ3HiWbzQqskSqe27qRJKkvwN4AOmy0yZaOObZfLMikjk0mqBH8vyWR68upSkvTO3eOdHROA5rFTwd+7yGTyFCWZxvGkAJ6kKZ1MHTegyLQV9/v5NduLA+xmqu2dY4zxJP0dST8m6fOSfsoY8/mZ3/a3JP2DLMs+kPQ1SX+j8r3LLMu+UPzfj9d1nGg+PygCUSvdTEmUEvwNoNPmikzj8U7GI7am3C63pMjkik8Umcpw1WTJuNyTsytJopMJOHDVBw27yWTKz8/VTowozcrCOLpp0snE63wb94oi0yGGf9dZnvySpN/Isuy3siwbS/qHkn5i5vd8XtI/K37+zxd8H5Af5HPB1ZG5JGFcDkC3mSBQFk8KNNloJNulTqayyOTt5ngazPdWB38/LYpMbJcDDttUkWkHDx0GRTbPeWVkLk7ScsQX3cS43HbcH9DJVIfvkfTNyq8fF1+r+rqkP1v8/M9IOjHGPCh+fWSM+UVjzP9ljPm3F/0Bxpi/UPyeX/z444+3eexoEK/oZIrHk4vvNE7LrwNAFxnfn+9katV2uU2LTHQyrQv+fnJ2JWOkt05a9PoD2LrqiNxOMpmKTqaLqSJTxmr7jmNcbjvuD/JsyhcXKxagdNS+3zk/LenLxphfkvRlSd+S5PoxP5Nl2Q9L+qqk/94Y87nZ/3GWZX83y7IfzrLsh996662dHTR2yw+LMYI4f2ukSaoskywtnAA6rDoul6WpsihqVyZTGfy95OKKIlPJdQVES4K/n55d6q1hjwt+4MCZoNrJtINMJtfJNGJc7pAwLrcdblzuxQGOy9V5ZfctSe9Vfv1u8bVSlmXfVtHJZIwZSvpzWZa9LL73reLH3zLG/LykL0r6zRqPFw1VjssVnUwus8Ij+BtAh00VmYofW7VdzhRjcOmSrSru6xSZygv5ZEnw99NXI0blAMiGQfnzXXweuE6m2XE5gr+7bXhEJ9M23DsOdOfIV7zks73L6ryy+wVJ32+M+azy4tJPKu9KKhljHkp6nmVZKumvSPp7xddPJV1kWTYqfs8fk/Tf1nisaDAX/O0ymdJiwwVFJgBdlheZ8qdf2WgkSbKtCv62krFkMm3AjZ7ESzOZLvXZh4NdHhKAJgoCyRgpy3aS0Tdw43KVTqY4zeSTydRp7nWnyHQ7vmf10X/1b+z7MPaitndOlmWxpL8k6Z9K+lVJ/yjLsl8xxnzNGOO2xX1F0q8ZY35d0tuS/nrx9T8o6ReNMV9XHgj+32RZ9v/VdaxoNs+Ny0X5B1wS59VgxuUAdNlUJ9M4Lza1alxOkmwgJYzLrVMGf6/IZHrn7vEuDwlAAxljys+BnXQyLQn+pvjQbYzL4bZqvbLLsuxnJf3szNf+auXnPyPpZxb87/5PST9Y57GhPebG5VwnE8HfADpsqshUdDK1KvhbygtIBH+vVXYyLSgyvRnFen0V6+07jMsByB82ZFEk49d/7iw7mcbTnUxsl+s2xuVwW7xz0Hiz43JlkYnqOoAOqxaZ0tZ2Mq0qMhU3LV6w+PsHpOxkWjAu9/TsSpLIZAIgKe9g2lU+X9nJNJqcx6MkpcOl4wY9T8ZIPaJJcEM8PkTjuY6lpMxkKsblOPEB6LCF43JtymSSJG+TTiYymYKikylZ0Mn0nVd5kekRRSYAkkwYyEa7+SzoF9MEU51MSUaHS8f1fE//41d/SD/0mdN9HwpaiiITGm/SyVRkMiUEfwPoPhMEUpQXYtqbycS43CZcJ9Oi4O8ndDIBqLBBqLS3ZGvnlvmeVc+3U5lMUZrpOKSTqet+7Aff2fchoMW4skPj+eHiTCaCvwF0mQn8uUwmu6MRia2xgZQsKTK5QHCKTOXoyaLg76dnl5JEJhMAScW43JJNlHUY9Pyp7XJJSvA3gNW4skPjzWYyuXE5OpkAdFk3tst56zOZKDLJc8HfSzqZTvuBjgLGCgHknwMm3U0nkyQdB97MdrlMPsHfAFbgyg6N54Uuk6kYl4sZlwNwAKrB353eLkfxxN2wLe5kutKju8e7PiQADWXCUHannUyeLiuZTFGSliO+ALAIRSY0nrVGxiwYl/P5gAPQXdOdTPmPretk8gIpjRZ/j0ymkhs9SdIFRaZXV+QxASj5Dx4ovbra2Z/XD32dV4O/00y+5UEvgOW4skPjGWPkhZ7ioriUJozLAei+6SKTy2RqWZHJ+pOxuFkUmUpl8Hcy353w9OxKH753b9eHBKChHn3tr0nZfEG6LoOep4vRzLgcnUwAVuDKDq3gB1bJTCeTR+gggA7rfiYTRSYnKLoCZsflrqJEz87HekToN4CCf7rbtfL90Nfz88vy11GSlucsAFiEMwRawQ+s4iKTKWVcDsABMIsymVq5XW7ZuBzB347nOplmcla++yp/3R8xLgdgTwahp4tq8HdKJxOA1SgyoRX80Cu3yyWMywE4ACYIpDRVliTKRm3tZCL4exPLgr+fnOXdA2QyAdiXfs/X+aiSyZSkZY4cACzCGQKt4AV2EvwdsV0OQPeZIC8oZXE8GZdrXScTmUybWBb8/fRVHu5LkQnAvizqZPIsnUwAluMuHa3gB1aJG5crO5n4gAPQXcbPiy9ZFClz43JBsM9Duj7P32C7XMv+TjXw3BbVmeDvp2d5kenR3eN9HBYAqB/6uhgnSosiOMHfANahyIRWyDOZpoO/LZ1MADrMFZSyKFIWjWXCUMa07MJ+5bgcmUxVvjWK0tlxuSsNe76GPf4bAdiPQS8fab4sHvZGKcHfAFbjDIFW8AKvHJObbJdr2c0WAFxDWWQaR0pHo/blMUl5AWlp8HfxdTKZJEm+tQs7mQj9BrBP/TAvcp+PYyVppiwTnUwAVqLIhFbww0knU5pkMkayhA4C6LCpTqbxuH15TBKZTNfge0bxbCfTqyvymADsletkuhgliopCOMHfAFbhDIFW8AOreJzfqCRRyqgcgM4zoSsyjZWNxu3tZFq7XY4ik5TftMUz2+W+c3alR3coMgHYn9lOJmmyERMAFuFOHa0wlcmUpIzKAei82U4m28YikxesCP4mk6nKt0ZxOhmXi5NU331NJxOA/RoURaaLcVIWwtkuB2AVikxohWomUxpn8gLeugC6bbrINGrxuNy6TiYymaQi+LvSyfTxm5HSTHqbIhOAPeoX43Lno1hRyrgcgPU4Q6AVZrfLkccEoOtckUlRpHTc1nE5j0ymDfnedPD3k7MrSaKTCcBe9cMik6nSyUTwN4BVuFNHK3ihVRKlyrIsH5fz+XAD0G1lJ1Mc55lMrexkClZsl6PIVDUb/P20KDI9unO8r0MCgKlxuTL423ILCWA5zhBoBb8Yj0uiVEmUySP4G0DHGT+/sC+3yxVB4K1C8PfGAjsd/P2UTiYADTDpZIoHtuIKAAAeWklEQVTLQjidTABW4U4dreAH+QdcHKVKE8blAHTfVCbTaCQbtrGTyV8xLkfwd1XeyTQZl3v66ko93+pev4XFRQCdMegV2+VGiZLiHOVzHQ5gBc4QaAU/zN+q8ThVEmeMywHovpntcq3MZPL8FdvlYklGYuxC0nzw95OzKz26eyRj+LwDsD8938qavJPJnaMCtssBWIErO7SC2yYXR4mSOGVcDkDnVTuZ0q5ul/Po0nF8z053Mp1d6tEdRuUA7JcxRoPQ1/loEvztUWQCsAJ36mgFNy6XuHE5ikwAOm5qXG4ctbOTaV2RiVG5km/NVCbTk7Mr8pgANEK/5+WdTEUhPGBcDsAKnCHQCn7ZyZQqidguB6D7TJAXlbJxnslkem0sMgVSlkqVDp1SQpGpKvBsGaqbppm++2qkR3fZLAdg/wahr/PxpJOJ4G8Aq1BkQit4odsulyhJMoK/AXSemclksq3sZMq7UBd2M6Xx5PuQZ43iYj3484uxxkmqR3daOCIJoHP6PU8Xo7g8R/lk6QFYgTMEWqHcLjdOlZLJBOAAmHB6u5xp63Y5aXH4N+NyUwJvEvz99OxKkuhkAtAI/dDX+Tguuy0DOpkArMCdOlphalwuZlwOQPdNdTJFLc1kcsHeSzuZKDI5vrVKihu4J0WRiUwmAE0wCD1djJNyOYHPRAGAFThDoBWq2+XSJCP4G0DnGT8vwKTn5/mv27pdTpLSZP57aUKRqcL3TBmq+/TsUhJFJgDN0O/5Oh/FZbelz3Y5ACtwp45WcJ1MSdnJxFsXQLe5TqayyFSMz7WKKyIly8blyGRyAs+WobpPX13Jt0YPhi0sLALonLKTieBvABvgTh2t4IeTTKYkzuTx4Qag44znSdYqPX8jSbKt7mRiXG6davD3k7MrfeqkJ49uAQAN0A/zTqZyXI7gbwArcIZAK1QzmQj+BnAoTBAoeZMXmVqZyUSRaWOBZxSlk+DvR4zKAWiIQS/vZHLjcgR/A1iFO3W0ghe6cblESZzKEvwN4ACYIKiMy7Wwk2ll8DeZTFXV4O+nZ1d6h81yABqiH/qK00yXUZ6vR/A3gFU4Q6AVPM/KWKNolCjLRCcTgINggkDpG1dkamMnU5G5RCfTWr5nFCWpsizTEzqZADRIv4iteHWZ5+sFjPICWIE7dbSGF1iNLvMnKBSZAByCqU6mXhuLTIzLbcoFf7+6inUZJWyWA9AYgzA/V58VRSby4gCswp06WsMPrMaX+Y2KZRYcwAGoFplsKzuZinG5pdvlKDI5njWK01RPz64kSW/focgEoBn6vbyT6ewiP5czLgdgFc4QaI1qkYlOJgCHYLqTqYWZTGUnUzL/PYpMUwJrFCWZvn12KUl0MgFojNlOJoK/AazCnTpaww89ikwADooJfKXldrk2FpnWZTJ5uz2eBnOdAd9+mReZyGQC0BQuk8kVmXzLdTiA5ThDoDXyTKZiXI7tcgAOQRAoi/KL+lYGf5fb5RiXW8cvOgMev7iUMdKnTigyAWiGQY9OJgCbo8iE1mBcDsChMUFQ/twS/N1pQdEZ8M3nF3o47Cnkcw5AQ5Tb5a4iedbIGIpMAJbjCgat4Vc6mTwCBwEcgGqRqZWdTGXw96IiU0KRqcJta/rmi0s9IvQbQINUO5nYLAdgHe7U0Rpe4Cm6ysNjGZcDcAimikytDP4mk2lTbvzk8fML8pgANIrrZHp9FSugyARgDYpMaA0/nLxdGZcDcAja38nEuNymXPD3s/Mxm+UANEo/nJyrfaYJAKzBWQKt4QfVIhNPUQB0nwkmhaVWdjIR/L0xv9IdQCcTgCbxrNFRcR1O6DeAdSgyoTWqRSZLJxOAAzDVyVT5eWuUnUzJ/PfIZJriV27c6GQC0DSDopvJt1yDA1iNswRawwsn2R0EfwM4BK6wZMKwndt81mUyeRSZnOqN29sEfwNomH4vP5/7dDIBWIM7dbSG71c7mfiAA9B9xs+LMK3MY5Iq2+UYl1snmOpkOt7jkQDAvEknE9fgAFajyITW8Aj+BnBgyk6mNuYxSQR/X0O1k+kRnUwAGsZtmCP4G8A6nCXQGtPB37x1AXRfdVyulVYVmRKKTFVuBOVeP9BxZTwcAJqgTycTgA1xp47W8IPJRbdlHhzAAXBFJtvWIpO3rpOJYorjOpnoYgLQRK6TKaCTCcAanCXQGj7jcgAOTKc7mRiXm+I6mR6xWQ5AAw16RScTD3oBrMGdOlqDcTkAh4ZMpsPhgr/focgEoIHKTibLNTiA1ThLoDW8yricx3Y5AAfAhG3vZHLb5WaKTGkqKaPIVDEZl2OzHIDmoZMJwKYoMqE1yk4mIxlCBwEcgEknU1uLTMXDgdlOJvdrMplKPp1MABrMdTJ5XIMDWIMiE1rDKzKZPM/KGD7gAHRf6zOZjMm7ldJo+utlkYlOJudzbw31E1/4tP74Dzzc96EAwJxBsV2O4G8A63B1h9Zw2+UYlQNwMPz8Y9qGLc1kkooi07JOJi5DnKPA09/+yS/u+zAAYKF+L78O9+lkArAGpWi0hhuXs4R+AzgQre9kkooiUzL9NYpMANAqdDIB2BRnCbSG78blKDIBOBCt3y4n5YWkZHZcLpl8DwDQeC6TieBvAOtwt47W8AJXZOLDDcBhaH3wt8S4HAB0QLldznL7CGA1zhJoDZfJZGnTBXAgXJHJtn5cbrbIFE2+BwBovLKTiUwmAGtwt47W8APG5QAclkkmU4vH5byATiYAaLmyk4lxOQBrcLeO1vBCxuUAHBYT5B1M7Q7+9hYUmchkAoA2cZ1MBH8DWIezBFrD86yMNYzLATgYnQn+XtrJ5O3+eAAA1+a2yzEuB2Ad7tbRKn5g5QV8uAE4DJNxuWDPR3ILNliwXY5xOQBok37PbZfj9hHAapwl0Cp+aOXx4QbgQJggL8LY1ncyJdNfo8gEAK0Selafvnuk7zk93vehAGg4ru7QKp5vZQn+BnAgJp1MLc5k8haNy5HJBABtYozRz/9nf5JxOQBrcXWHVun1fQU9MjwAHAbb7+c/DgZ7PpJbsL6ULhuX43wOAG0R8qAXwAYoMqFV/vV///MKj3nbAjgM4fvv693/+X/S8Ed+ZN+HcnMrg785nwMAAHQJV3dolbfeP9n3IQDATp185Sv7PoTbsb6UUGQCAAA4BPQ8AgCA+qzqZPJavDUPAAAAcygyAQCA+iwsMrngbzKZAAAAuoQiEwAAqI8XzAd/J8WvGZcDAADoFIpMAACgPtabdC45ZDIBAAB0EkUmAABQH7bLAQAAHAyKTAAAoD42mIzHOWUmE0UmAACALqHIBAAA6mP9FeNyBH8DAAB0CUUmAABQH+sxLgcAAHAgKDIBAID6LNouR5EJAACgkygyAQCA+iwM/iaTCQAAoIsoMgEAgPrYgEwmAACAA0GRCQAA1Md6C7bLMS4HAADQRRSZAABAfRaOy7kiU7D74wEAAEBtKDIBAID6uODvLJt8jUwmAACATqLIBAAA6uMKSVk6+ZrbNkcmEwAAQKdQZAIAAPVxhaTqyFwaS8aTjNnPMQEAAKAWFJkAAEB9XO5SNfw7jRmVAwAA6CCKTAAAoD6umDTVyZRQZAIAAOggikwAAKA+ZZEpmXyNTiYAAIBOosgEAADq47ki0+y4HKHfAAAAXUORCQAA1GfhuBydTAAAAF1EkQkAANSHIhMAAMDBoMgEAADqU26XI/gbAACg6ygyAQCA+rjspblOJjKZAAAAuoYiEwAAqI9XdDLNFpnc1wEAANAZFJkAAEB97LLtcozLAQAAdA1FJgAAUJ+yyJRMvpYwLgcAANBFFJkAAEB9XJEpoZMJAACg6ygyAQCA+pSdTLPB3xSZAAAAuoYiEwAAqA9FJgAAgINBkQkAANRn4Xa5hCITAABAB1FkAgAA9XEB33OdTAR/AwAAdA1FJgAAUB/G5QAAAA4GRSYAAFAfW4zLsV0OAACg8ygyAQCA+pSdTMnka2QyAQAAdFKtRSZjzI8aY37NGPMbxpi/vOD7nzHG/Jwx5iNjzM8bY96d+f4dY8xjY8z/UOdxAgCAmnjLxuXIZAIAAOia2opMxhhP0t+R9GOSPi/pp4wxn5/5bX9L0j/IsuwDSV+T9Ddmvv9fS/o/6jpGAABQs7KTaXZcLtjP8QAAAKA2dXYyfUnSb2RZ9ltZlo0l/UNJPzHzez4v6Z8VP//n1e8bY/4VSW9L+t9qPEYAAFCnhcHfEeNyAAAAHVRnkel7JH2z8uvHxdeqvi7pzxY//zOSTowxD4wxVtJ/J+mnV/0Bxpi/YIz5RWPML3788cdbOmwAALA1rmOJTCYAAIDO23fw909L+rIx5pckfVnStyQlkv6ipJ/Nsuzxqv9xlmV/N8uyH86y7Iffeuut+o8WAABcj8temtsuRyYTAABA19T5GPFbkt6r/Prd4mulLMu+raKTyRgzlPTnsix7aYz5o5L+uDHmL0oaSgqNMW+yLJsLDwcAAA22cFwuppMJAACgg+q8wvsFSd9vjPms8uLST0r6avU3GGMeSnqeZVkq6a9I+nuSlGXZv1v5Pf+BpB+mwAQAQAt5blxutpOJIhMAAEDX1DYul2VZLOkvSfqnkn5V0j/KsuxXjDFfM8b8ePHbviLp14wxv6485Puv13U8AABgD8pOJjKZAAAAuq7WK7wsy35W0s/OfO2vVn7+M5J+Zs3/j78v6e/XcHgAAKBupnieNTcuRyYTAABA1+w7+BsAAHSZMfmGubngbzqZAAAAuoYiEwAAqJf1J51MWUaRCQAAoKMoMgEAgHpZf5LJlKWTrwEAAKBTKDIBAIB6ef5ku5zraCKTCQAAoHMoMgEAgHpVx+Xcj16wv+MBAABALSgyAQCAetlgUlxyAeCMywEAAHQORSYAAFAv60uJ62RKJl8DAABAp1BkAgAA9bLe/LgcmUwAAACdQ5EJAADUywsWFJnoZAIAAOgaikwAAKBedtF2OYpMAAAAXUORCQAA1Mt6kywmikwAAACdRZEJAADUywaTrXIEfwMAAHQWRSYAAFAv6xP8DQAAcAAoMgEAgHotLDLRyQQAANA1FJkAAEC9PIpMAAAAh4AiEwAAqNdUJ5PLZAr2dzwAAACoBUUmAABQLxuQyQQAAHAAKDIBAIB6WV9KXJEpmnwNAAAAnUKRCQAA1Mt6ZDIBAAAcAIpMAACgXl6wIJOJIhMAAEDXUGQCAAD1sv5kTI5MJgAAgM6iyAQAAOpl/UkHE+NyAAAAnUWRCQAA1Mv6ZDIBAAAcAIpMAACgXtaXEjcuRyYTAABAV1FkAgAA9VrYyUQmEwAAQNdQZAIAAPWa2i7HuBwAAEBXUWQCAAD1sh5FJgAAgANAkQkAANRr0bicF+zveAAAAFALikwAAKBethiXyzKCvwEAADqMIhMAAKiXKyilyWTLHMHfAAAAnUORCQAA1MtzRaaYTCYAAIAOo8gEAADqVXYyRRSZAAAAOowiEwAAqJetdjKRyQQAANBVFJkAAEC9qplMrpPJcAkCAADQNVzhAQCAerkiU1KMy1lfMma/xwQAAICto8gEAADqZWeCvxmVAwAA6CSKTAAAoF5ekP/oMpkoMgEAAHQSRSYAAFCvuU4mb7/HAwAAgFpQZAIAAPVyRSXG5QAAADqNIhMAAKiXLcblyuDvYL/HAwAAgFpQZAIAAPUi+BsAAOAgUGQCAAD1KotMCZlMAAAAHUaRCQAA1MtzRaaITiYAAIAOo8gEAADqxbgcAADAQaDIBAAA6uWCvtM4H5mjyAQAANBJFJkAAEC9XFEpiclkAgAA6DCKTAAAoF6uqMS4HAAAQKdRZAIAAPXyquNyFJkAAAC6iiITAACol61ulyOTCQAAoKsoMgEAgHqVRaaETCYAAIAOo8gEAADqVRaZGJcDAADoMopMAACgXuV2uSgvMrmMJgAAAHQKRSYAAFCvaidTQicTAABAV1FkAgAA9ZrbLkcmEwAAQBdRZAIAAPVyRSUymQAAADqNIhMAAKiXne1kosgEAADQRRSZAABAvaaCvxOKTAAAAB1FkQkAANSrDP5OyGQCAADoMIpMAACgXtZKxjIuBwAA0HEUmQAAQP2sL6URRSYAAIAOo8gEAADqZ/2ik4lMJgAAgK6iyAQAAOpng0omE0UmAACALqLIBAAA6me9Yrscwd8AAABdRZEJAADUrxyXi/OuJgAAAHQORSYAAFA/L8iDvzMymQAAALqKIhMAAKif9aToqvj5/9/evcZqVpV3AP8/55yhor2gQkgFVAykdmLlEkohpUhsP0BrpKVN0bQpMW0MSZtaU2OgNW1qYhpT0qvGRhEvicEatC2xxsYgXvqhCEq5SWkJFgFBpqlob4EOffrh3cO8nM6Ahz37vGe2v18ymb3X3mfmmQ8r681/1npeIRMAwBwJmQCA6a1tJHv3hUx6MgEAzJGQCQCY3tqupZDJTiYAgDkSMgEA01vbSPY+uv8aAIDZETIBANNb37CTCQBg5oRMAMD01jaWGn/ryQQAMEdCJgBgemt2MgEAzJ2QCQCY3touPZkAAGZOyAQATG9tPdn738O1kAkAYI6ETADA9J707XJ6MgEAzJGQCQCY3vqu/T2Z1netthYAACYhZAIApre2kTz+2P5rAABmR8gEAExv+YickAkAYJaETADA9NaWjsjpyQQAMEtCJgBgesu7l+xkAgCYJSETADA9IRMAwOwJmQCA6a0LmQAA5k7IBABMz04mAIDZEzIBANPT+BsAYPaETADA9JaDJTuZAABmScgEAEzPcTkAgNkTMgEA01tfPi4nZAIAmCMhEwAwPTuZAABmT8gEAExPTyYAgNkTMgEA01tzXA4AYO6ETADA9ByXAwCYPSETADC9J4VM6wd/DwCAw5aQCQCY3rqdTAAAcydkAgCm57gcAMDsCZkAgOlp/A0AMHtCJgBgenYyAQDMnpAJAJjeE82+K1nz8QMAYI58ygMAprc+HJeziwkAYLaETADA9PaFS0ImAIDZEjIBANPbFy6t73rq9wAAOGwJmQCA6T2xk2n9qd8DAOCwJWQCAKbnuBwAwOwJmQCA6QmZAABmT8gEAEzPt8sBAMyekAkAmN6+Xkx6MgEAzJaQCQCYnuNyAACzN2nIVFXnV9VdVXV3VV12gOcvqqrrqurWqvpMVR2/NP6lqvqHqrqjqi6dsk4AYGJrjssBAMzdZCFTVa0neWeSC5LsTvLaqtq96bUrknywu1+e5K1Jfn8YfzDJ2d19apIfSXJZVb1gqloBgInZyQQAMHtT7mQ6M8nd3X1Pdz+W5MNJLtz0zu4knx6ur9/3vLsf6+5Hh/HvmrhOAGBq6/tCJj2ZAADmasrw5rgk9y3d3z+MLbslyUXD9c8k+Z6qen6SVNUJVXXr8Ge8vbu/tvkvqKrXV9VNVXXTnj17Dvk/AAA4ROxkAgCYvVXvEHpTkldU1c1JXpHkgSSPJ0l33zccozspySVVdezmH+7ud3f3Gd19xjHHHLOddQMAW/FEyLRrtXUAADCZKUOmB5KcsHR//DD2hO7+Wndf1N2nJfntYeyRze8kuT3Jj01YKwAwJY2/AQBmb8qQ6cYkJ1fViVV1RJLXJLl2+YWqOrqq9tVweZKrhvHjq+rI4fq5Sc5JcteEtQIAU9rXi0lPJgCA2ZosZOruvUl+LcnfJrkzyUe6+46qemtVvXp47bwkd1XVPyU5NsnbhvEfTHJDVd2S5LNJruju26aqFQCYmJ5MAACzN+knve7+RJJPbBr7naXra5Jcc4Cf+1SSl09ZGwCwjdYdlwMAmLtVN/4GAL4T2MkEADB7QiYAYHpPhEx6MgEAzJWQCQCYXlVS63YyAQDMmJAJANgeaxtCJgCAGRMyAQDbY32XkAkAYMaETADA9lhb15MJAGDGhEwAwPbYODLZeNaqqwAAYCL2rAMA2+Nn35Mc9cJVVwEAwESETADA9jjx3FVXAADAhByXAwAAAGA0IRMAAAAAowmZAAAAABhNyAQAAADAaEImAAAAAEYTMgEAAAAwmpAJAAAAgNGETAAAAACMJmQCAAAAYDQhEwAAAACjCZkAAAAAGE3IBAAAAMBoQiYAAAAARhMyAQAAADCakAkAAACA0YRMAAAAAIwmZAIAAABgNCETAAAAAKMJmQAAAAAYTcgEAAAAwGhCJgAAAABGEzIBAAAAMJqQCQAAAIDRhEwAAAAAjCZkAgAAAGA0IRMAAAAAowmZAAAAABhNyAQAAADAaEImAAAAAEYTMgEAAAAwmpAJAAAAgNGETAAAAACMJmQCAAAAYDQhEwAAAACjCZkAAAAAGK26e9U1HBJVtSfJvauu4xA5Osm/rroIOIyYM7A15gxsjTkDW2POwNbs9Dnzou4+5tt5cTYh05xU1U3dfcaq64DDhTkDW2POwNaYM7A15gxszZzmjONyAAAAAIwmZAIAAABgNCHTzvTuVRcAhxlzBrbGnIGtMWdga8wZ2JrZzBk9mQAAAAAYzU4mAAAAAEYTMgEAAAAwmpBph6mq86vqrqq6u6ouW3U9sNNU1QlVdX1Vfbmq7qiqNwzjz6uqT1XVPw+/P3fVtcJOUVXrVXVzVX18uD+xqm4Y1pq/qKojVl0j7BRVdVRVXVNV/1hVd1bV2dYYOLiqeuPwmez2qrq6qp5lnYEnq6qrqurhqrp9aeyAa0st/Okwf26tqtNXV/nWCZl2kKpaT/LOJBck2Z3ktVW1e7VVwY6zN8lvdvfuJGcl+dVhnlyW5LruPjnJdcM9sPCGJHcu3b89yR9190lJvpHkl1dSFexMf5Lkk9390iSnZDF3rDFwAFV1XJJfT3JGd78syXqS18Q6A5u9P8n5m8YOtrZckOTk4dfrk7xrm2o8JIRMO8uZSe7u7nu6+7EkH05y4Yprgh2lux/s7i8N1/+exYf/47KYKx8YXvtAkp9eTYWws1TV8Ul+KsmVw30leWWSa4ZXzBcYVNX3JTk3yXuTpLsf6+5HYo2Bp7KR5Miq2kjy7CQPxjoDT9Ldn0vyb5uGD7a2XJjkg73w90mOqqrv355KxxMy7SzHJblv6f7+YQw4gKp6cZLTktyQ5NjufnB49FCSY1dUFuw0f5zkzUn+d7h/fpJHunvvcG+tgf1OTLInyfuGI6ZXVtVzYo2BA+ruB5JckeSrWYRL30zyxVhn4NtxsLXlsM4FhEzAYamqvjvJR5P8Rnd/a/lZd3eSXklhsINU1auSPNzdX1x1LXCY2EhyepJ3dfdpSf4zm47GWWNgv6GHzIVZBLQvSPKc/P8jQcDTmNPaImTaWR5IcsLS/fHDGLCkqnZlETB9qLs/Ngx/fd820uH3h1dVH+wgP5rk1VX1L1kcwX5lFv1mjhqONSTWGlh2f5L7u/uG4f6aLEInawwc2E8k+Up37+nu/0nysSzWHusMPL2DrS2HdS4gZNpZbkxy8vBtDEdk0TTv2hXXBDvK0E/mvUnu7O4/XHp0bZJLhutLkvz1dtcGO013X97dx3f3i7NYUz7d3b+Q5PokPze8Zr7AoLsfSnJfVf3AMPTjSb4cawwczFeTnFVVzx4+o+2bM9YZeHoHW1uuTfJLw7fMnZXkm0vH6na8WuzKYqeoqp/Mon/GepKruvttKy4JdpSqOifJ55Pclv09Zn4ri75MH0nywiT3Jvn57t7cXA++Y1XVeUne1N2vqqqXZLGz6XlJbk7yi9396Crrg52iqk7NolH+EUnuSfK6LP5j1hoDB1BVv5fk4iy+AfjmJL+SRf8Y6wwMqurqJOclOTrJ15P8bpK/ygHWliGwfUcWR0//K8nruvumVdT9TAiZAAAAABjNcTkAAAAARhMyAQAAADCakAkAAACA0YRMAAAAAIwmZAIAAABgNCETAMAhVlX/8RTPzquqj29nPQAA20HIBAAAAMBoQiYAgAnUwh9U1e1VdVtVXbz0+Hur6m+q6q6q+vOqWquq9ap6/9L7b1xZ8QAAz8DGqgsAAJipi5KcmuSUJEcnubGqPjc8OzPJ7iT3Jvnk8O5XkhzX3S9Lkqo6atsrBgAYwU4mAIBpnJPk6u5+vLu/nuSzSX54ePaF7r6nux9PcvXw7j1JXlJVf1ZV5yf51kqqBgB4hoRMAADbrzffd/c3stj19Jkklya5cruLAgAYQ8gEADCNzye5eOi1dEySc5N8YXh2ZlWdWFVrSS5O8ndVdXSSte7+aJK3JDl9JVUDADxDejIBABxCVbWR5NEkf5nk7CS3ZLFz6c3d/VBVvTTJjUnekeSkJNcP7/5QkvcNwVOSXL7dtQMAjFHdm3drAwDwTFXVKUne091nrroWAIDt5LgcAMAhUlWXZtHI+y2rrgUAYLvZyQQAAADAaHYyAQAAADCakAkAAACA0YRMAAAAAIwmZAIAAABgNCETAAAAAKP9H67DCKuK7JmyAAAAAElFTkSuQmCC\n",
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"'''\n",
"Plots for the experiment results\n",
"'''\n",
"\n",
"import matplotlib.pyplot as plt\n",
"\n",
"plt.figure(figsize=(20,15))\n",
"\n",
"\n",
"plt.plot(j_gri[:100], s_gri[:100])\n",
"plt.plot(j_ran, s_ran)\n",
"plt.plot(j_spe, s_spe)\n",
"plt.plot(j_hpb[:100], s_hpb[:100])\n",
"plt.plot(j_opt, s_opt)\n",
"\n",
"plt.legend(['y = grid','y = random', 'y = spearmint', 'y = hyperband', 'y = hyperopt'], loc='upper left')\n",
"\n",
"plt.title('Hyperparameter Optimization using various proposers on MNIST')\n",
"plt.xlabel('Jobs')\n",
"plt.ylabel('Accuracy')\n",
"\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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/lzu/TNmzCAiIoK5c+ditVqJjIwkKiqqVLvhw4eTlZVVavmCBQsYNWpUsWV///vf+emnn2jTpg3p6en25VFRURw4cAB/f38GDhzI2LFjCQgIYMWKFWzbtg0vLy/mzJnDsmXLmDp1Ki+//DJNmzalsLCQkSNHsn//fsLCwgAICgrit99+A+D999/H29ub3bt3s2jRIsaPH090dDRNmzalc+fOPPHEEwQFBRUb4549ezh48CCtW7fmuuuuY9u2bTz22GP861//YuPGjQQHBzu/k4UQQgghhBCiDDnmHKLOR3Fvj3sJ8AqgS+MukvEkRB1UbwJP7hASEkJQUBB79uwhMTGRfv36lQrSAGzZssXpPq+77jqmT5/O3XffTUREhH356NGj7X1HRESwdetWPD09iY6OZuDAgQDk5eXRvHlzAL744gs+/PBDLBYL58+f59ChQ/bA06RJk4qtc9y4cQD07t2bXr160apVKwA6depEbGxsqW0aNGgQbdu2BaBv377ExMRw/fXXO72NQgghhBBCCFGZree2YraaCW9vzKjo06wPP57+Eau24qFkco8QdUW9CTxVlJnkSjNnzmTJkiUkJCQwY8aMMttUJePp/fff59dff+W7777j2muvJTrauGSoUqpYO6UUWmumTZvGK6+8Uuy+06dPs2DBAnbt2kWTJk2YPn06+fn59vsDAgKKtffx8QHAw8PDfrvob4vFUmrcjm1MJlOZbYQQQgghhBCiJjbGbqSJTxP6NusLQFizMFYeW8npjNN0btzZzaMTQhSpN4End5kwYQLz58/HbDazfPnyMttUJePp5MmTDB48mMGDB/PDDz8QGxsLwLp160hNTcXPz4/Vq1fzySef4O/vz/jx43niiSdo3rw5qampZGVlkZmZSUBAAIGBgSQmJvLDDz8wYsSI2tjcCjVs2JCsrCyZaieEEEIIIYSoEbPVzOa4zdzU7iZMHibAyHgCo8C4BJ6EqDsk8ORi3t7ehIeH07hxY0wmU437e/rppzl+/Dhaa0aOHEmfPn3Yu3cvgwYNYuLEicTFxXHfffcxYMAAAF566SXGjBmD1WrFy8uLd999lyFDhtCvXz969OhBu3btuO6662o8LmfMmjWLW265hdatW7Nx48bLsk4hhBBCCCHE1ee3xN/IKsiyT7MD6NCoA428G7Hvwj4mdJ3gxtEJIRwprbW7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\n",
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"'''\n",
"Plots for the experiment results\n",
"'''\n",
"\n",
"\n",
"import matplotlib.pyplot as plt\n",
"\n",
"plt.figure(figsize=(20,15))\n",
"\n",
"\n",
"plt.plot(t_gri[:100], s_gri[:100])\n",
"plt.plot(t_ran, s_ran)\n",
"plt.plot(t_spe, s_spe)\n",
"plt.plot(t_hpb[:100], s_hpb[:100])\n",
"plt.plot(t_opt, s_opt)\n",
"\n",
"plt.legend(['y = grid','y = random', 'y = spearmint', 'y = hyperband', 'y = hyperopt'], loc='upper left')\n",
"\n",
"plt.title('Hyperparameter Optimization using various proposers on MNIST')\n",
"plt.xlabel('Time')\n",
"plt.ylabel('Accuracy')\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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f//9LFy4kPj4+AJHG5Vk5FNhQkJCrjiniIiIiIiIiMiVUvHJRWPGjGHGjBlkZmayePHifNuUZORT3759efvttxk/fjxLliwp1j4DBw7kn//8J5MmTeLEiROsXr2au+++u9jPKSIiIiIiIiJSGBWfXFSjRg2GDBlCWFgYvr6+V93frFmzmDBhAs899xw33XQToaGhRe4zZswYVq1aRVRUFC1atKBfv35XnUNERERERERE5CIVn1zk8XjYsGEDy5YtK5X+mjZtyoYNGzDGsGTJEvbs2QPA4MGDGTx48CVtz549C4Axhrlz55bK84uIiIiIiIiI5KXik0t27tzJrbfeypgxY4iMjCyVPjdt2sQjjzyCtZawsDDmz59fKv2KiIiIiIiIiFwpFZ9cEhUVxYEDB0q1zwEDBvDdd9+Vap8iIiIiIiIiIlfDx+0AIiIiIiIiIiJSdan4JCIiIiIiIiIiZUbFJxERERERERERKTMqPomIiIiIiIiISJlR8UkKtHDhQh555BG3Y4iIiIiIiIhIJabiUxWVlZXldgQRERERERERERWf3DJjxgxmzZqV8/NTTz3F7Nmzr6rPyZMn89BDD9GnTx+efPJJNm7cSL9+/ejevTv9+/dnz549gDOiKTo6mptuuonIyEiefPLJnD4WLFhAu3bt6N27N1999VXO9tjYWIYOHUqXLl0YNmwYhw8fznnOhx9+mL59+9K6dWvWrFnDlClT6NixI5MnT76q4xERERERERGRys/P7QAVwse/gvjvS7fPRp3h5v8r8OEpU6YQHR3NtGnT8Hg8LFmyhI0bN17WbsCAAaSmpl62febMmQwfPvyy7XFxcaxfvx5fX1/OnDnD2rVr8fPz47PPPuM3v/kNb7/9NgBbt25ly5YtBAQE0L59ex599FH8/Px4+umn2bRpE6GhoQwZMoTu3bsD8OijjzJp0iQmTZrE/Pnz+dnPfsaKFSsASEpK4uuvv+a9995j5MiRfPXVV7zyyiv06tWLrVu30q1btys6hSIiIiIiIiJS+an45JKIiAjCw8PZsmULCQkJdO/enfDw8MvarV27tkT93n777fj6+gKQkpLCpEmT2Lt3L8YYMjMzc9oNGzaM0NBQAKKiojh06BCnTp1i8ODB1K9fH4Dx48fzww8/APD111/zn//8B4B77733ktFSt912G8YYOnfuTMOGDencuTMAnTp1IjY2VsUnERERERERkWpMxScodIRSWbr//vtZuHAh8fHxTJkyJd82JR35FBISkvP97373O4YMGcI777xDbGwsgwcPznksICAg53tfX9+rWiPqYl8+Pj6X9Ovj46O1p0RERERERESqORWfXDRmzBhmzJhBZmYmixcvzrdNSUc+5ZaSkkLTpk0BZ52novTp04fHHnuM06dPU7t2bZYtW0bXrl0B6N+/P0uWLOHee+/ljTfeYMCAAVecS0RERERERESqDxWfXFSjRg2GDBlCWFhYzlS50vTkk08yadIknn32WUaMGFFk+8aNG/PMM8/Qr18/wsLCLpku9/e//50f//jH/L//9/+oX78+CxYsKPW8IiIiIiIiIlL1GGut2xnKXM+ePW1MTMwl23bt2kXHjh1dSuTweDz06NGDZcuWERkZ6WqWyqIivG4iIiIiIiIiAsaYTdbankW18ymPMHK5nTt30rZtW4YNG6bCk4iIiIiIiIhUWZp255KoqCgOHDjgdgwRERERERERkTKl4pOIiIiIiIhUe5meTGLiY8jy6I7dUraC/ILo2ajImWpViopPIiIiIiIiUu29s/cd/rjhj27HkGogonYE74953+0Y5UrFJxEREREREan2Vh5aScvaLfnT9X9yO4pUcQG+AW5HKHcqPomIiIiIiEi1dibjDDHxMUzsNJEu9bu4HUekytHd7qTM9e/fv8g2s2bN4vz58+WQRkRERERE5FLr4taRZbMY2mKo21FEqiQVn+SKZGdnF7vt+vXri2yj4pOIiIiIiLhl1ZFV1AuqR+d6nd2OIlIlqfjkkhkzZjBr1qycn5966ilmz559VX0uW7aMa665hq5duzJw4EAAFi5cyKhRoxg8eDCRkZH8/ve/z2m/aNEievfuTbdu3XjwwQdzCkoPP/wwPXv2pFOnTjz99NM57SMiIvjlL39Jjx49WLZsGYMHD2b69On07NmTjh078u233xIdHU1kZCS//e1vc/arWbMmAGvWrGHw4MGMGzeODh06cM8992CtZc6cORw7dowhQ4YwZMiQqzoHIiIiIiIiJZGRncG6o+sY1GwQPkYfkUXKgtZ8Av688c/sTtxdqn12qNuBX/b+ZYGPT5kyhejoaKZNm4bH42HJkiVs3LjxsnYDBgwgNTX1su0zZ85k+PDhl2z7wx/+wH//+1+aNm1KcnJy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\n",
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"'''\n",
"Plots for the experiment results\n",
"'''\n",
"\n",
"def max_so_far(arr):\n",
" mmax=-1\n",
" new_arr=[]\n",
" for _,x in enumerate(arr):\n",
" if x>mmax:\n",
" mmax=x\n",
" new_arr.append(mmax)\n",
" return new_arr\n",
"\n",
"plt.figure(figsize=(20,15))\n",
"\n",
"\n",
"plt.plot(j_gri[:100], max_so_far(s_gri[:100]))\n",
"plt.plot(j_ran, max_so_far(s_ran))\n",
"plt.plot(j_spe, max_so_far(s_spe))\n",
"plt.plot(j_hpb[:100], max_so_far(s_hpb[:100]))\n",
"plt.plot(j_opt, max_so_far(s_opt))\n",
"\n",
"plt.legend(['y = grid','y = random', 'y = spearmint', 'y = hyperband', 'y = hyperopt'], loc='upper left')\n",
"\n",
"plt.title('Hyperparameter Optimization using various proposers on MNIST')\n",
"plt.xlabel('Jobs')\n",
"plt.ylabel('Max Accuracy')\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: Examples/hpo_mnist/README.md
================================================
# HPO on MNIST data using Auptimizer
This folder contains demonstrating how to use Auptimizer for Hyperparameter Optimization on the MNIST dataset.
The original source code is from [Tensorflow Tutorial](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/tutorials/mnist).
We modified `mnist_with_summaries.py` to use **Auptimizer** for model tuning.
**Tensorflow** package is needed for this demo.
Using the CPU version will slow down the demonstration to more than 1 minute per job, but it should not be a limiting factor.
## Changes
We need to parse the input for hyperparameter values. So we add the following lines
to parse the values original stored in `FLAGS`, which is commonly used in `Tensorflow`
for parameter sharing.
config = BasicConfig(**FLAGS.__dict__).load(sys.argv[1])
FLAGS.__dict__.update(config)
# for hyperband only
if "n_iterations" in FLAGS:
FLAGS.max_steps = int(FLAGS.n_iterations)
## Runtime
First, we could test the script with a testing configuration `demo.json`:
python mnist_hpo.py demo.json
Once the script runs smoothly, it will print
#Auptimizer:0.9592
At the end of the run.
Now it is the time to run **Auptimizer** for a large scale experiment.
Set up the **Auptimizer**, if you haven't done that by `python -m aup.setup `.
Then run as `python -m aup `
The following table lists the minor differences in the configuration of HPO algorithms.
| Name | Comments |
| ---- | -------- |
| Random | `n_samples` specifies the total jobs to run |
| Sequence | `n` or `interval` specifies the grid for each parameter |
| Hyperband | `max_iter` specify the total iterations for training and `engine` specify the underneath proposer |
| BOHB | Uses many extra parameters. Refer to the documentation for details. |
| Spearmint | None |
| Hyperopt | None |
## Analysis
`MNIST Hyperparameter Optimization Demo.ipynb` presents detailed analysis of different HPO proposers under similar experimental settings for mnist.py.
## Additional Examples - Running with AWS and Job failure capabilities.
We provide further examples to show the full array of Auptimizers capabilities to handle large scale HPO in an asynchronous cloud based distributed setting with job retry and experiment persistence capabilities.
- `exp_aws_demo.json` : Demo experiment to run MNIST HPO using AWS instances. Ensure AWS instances are registered as resources and have the correct working directory.
- `exp_aws_async.json` : Builds on the demo experiment, with a larger search space using asynchornous connections to AWS instances. Refer to the documentation for more details on async parameters.
- `exp_aws_retry_job.json` : Builds on the demo experiment, with a larger search space on AWS instances using job failure and experiment persistence capabilities. Refer to the documentation for more details on job failure parameters.
================================================
FILE: Examples/hpo_mnist/demo.json
================================================
{
"name": "./hpo_mnist/demo.json",
"drop_out":0.9,
"learning_rate":0.01
}
================================================
FILE: Examples/hpo_mnist/exp_aws_async.json
================================================
{
"name": "./hpo_mnist/exp_hpo_demo.json",
"workingdir": "/home/ubuntu/aup/mnist_try/",
"proposer": "random",
"script": "mnist_hpo_demo.py",
"runtime_args": {
"prescript": "source /home/ubuntu/.bashrc; source /home/ubuntu/.profile; source activate tensorflow_p36",
"postscript": "source deactivate",
"overwrite" : true
},
"resource_args":{
"shutdown":false,
"connection_retry": 5,
"async_run":true,
"async_reconnect": 60,
"async_timeout":1000
},
"resource": "aws",
"n_parallel": 5,
"target":"max",
"n_samples":100,
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
},
{
"name": "fc1",
"range": [
700,
2000
],
"type": "int"
}
]
}
================================================
FILE: Examples/hpo_mnist/exp_aws_demo.json
================================================
{
"name": "./hpo_mnist/exp_aws.json",
"workingdir": "/home/ubuntu/aup/mnist_try/",
"proposer": "random",
"script": "mnist_hpo_demo.py",
"runtime_args": {
"prescript": "source /home/ubuntu/.bashrc; source /home/ubuntu/.profile; source activate tensorflow_p36",
"postscript": "source deactivate"
},
"resource_args":{
"shutdown":false,
"connection_retry": 5
},
"resource": "aws",
"n_parallel": 1,
"target":"max",
"n_samples":6,
"parameter_config": [
{
"name": "drop_out",
"range": [0.5, 0.9],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.01,
0.05,
0.001
],
"type": "choice"
}
]
}
================================================
FILE: Examples/hpo_mnist/exp_aws_retry_job.json
================================================
{
"proposer": "random",
"script": "mnist_hpo_fail.py",
"workingdir": "/home/ubuntu/aup/mnist_try/",
"resource": "aws",
"n_parallel": 4,
"target":"max",
"n_samples": 100,
"runtime_args": {
"prescript": "source /home/ubuntu/.bashrc; source /home/ubuntu/.profile; source activate tensorflow_p36",
"postscript": "source deactivate",
"overwrite" : true
},
"job_failure": {
"ignore_fail": true,
"job_retries": 3
},
"resource_args":{
"shutdown":false,
"connection_retry": 5
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
},
{
"name": "fc1",
"range": [
700,
2000
],
"type": "int"
}
]
}
================================================
FILE: Examples/hpo_mnist/exp_bohb.json
================================================
{
"name": "./hpo_mnist/exp_bohb.json",
"script": "mnist_hpo_demo.py",
"resource": "cpu",
"n_parallel": 4,
"target": "max",
"proposer": "bohb",
"n_iterations": 16,
"min_points_in_model": "",
"top_n_percent": 15,
"num_samples": 64,
"random_fraction": 0.3333333333333333,
"bandwidth_factor": 3,
"min_bandwidth": 0.001,
"eta": 3,
"min_budget": 10,
"max_budget": 100,
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
},
{
"name": "fc1",
"range": [
700,
2000
],
"type": "int"
}
]
}
================================================
FILE: Examples/hpo_mnist/exp_hyperband.json
================================================
{
"name": "./hpo_mnist/exp_hyperband.json",
"proposer": "hyperband",
"script": "mnist_hpo_demo.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"max_iter": 243,
"engine": "random",
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
},
{
"name": "fc1",
"range": [
700,
2000
],
"type": "int"
}
]
}
================================================
FILE: Examples/hpo_mnist/exp_hyperopt.json
================================================
{
"name": "./hpo_mnist/exp_hyperopt.json",
"proposer": "hyperopt",
"script": "mnist_hpo_demo.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"n_samples": 100,
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
},
{
"name": "fc1",
"range": [
700,
2000
],
"type": "int"
}
]
}
================================================
FILE: Examples/hpo_mnist/exp_random.json
================================================
{
"name": "./hpo_mnist/exp_random.json",
"proposer": "random",
"script": "mnist_hpo_demo.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"n_samples": 100,
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
},
{
"name": "fc1",
"range": [
700,
2000
],
"type": "int"
}
]
}
================================================
FILE: Examples/hpo_mnist/exp_random_async.json
================================================
{
"name": "./hpo_mnist/exp_random_async.json",
"proposer": "random",
"script": "mnist_hpo_demo.py",
"workingdir": "/home/ubuntu/",
"resource": "aws",
"resource_args": {
"async_run": true,
"async_reconnect": 60
},
"n_parallel": 4,
"target":"max",
"n_samples": 100,
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
},
{
"name": "fc1",
"range": [
700,
2000
],
"type": "int"
}
]
}
================================================
FILE: Examples/hpo_mnist/exp_sequence.json
================================================
{
"name": "./hpo_mnist/exp_sequence.json",
"proposer": "sequence",
"script": "mnist_hpo_demo.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float",
"n":3
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float",
"n":3
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int",
"n":3
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int",
"n":3
},
{
"name": "fc1",
"range": [
700,
2000
],
"type": "int",
"n":3
}
]
}
================================================
FILE: Examples/hpo_mnist/exp_spearmint.json
================================================
{
"name": "./hpo_mnist/exp_spearmint.json",
"proposer": "spearmint",
"script": "mnist_hpo_demo.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"n_samples": 100,
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
},
{
"name": "fc1",
"range": [
700,
2000
],
"type": "int"
}
]
}
================================================
FILE: Examples/hpo_mnist/job_retries_random.json
================================================
{
"name": "./hpo_mnist/job_retries_random.json",
"proposer": "random",
"script": "mnist_hpo_demo.py",
"workingdir": "/home/ubuntu/",
"resource": "aws",
"n_parallel": 4,
"target":"max",
"n_samples": 100,
"job_failure": {
"ignore_fail": true,
"job_retries": 3
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
},
{
"name": "fc1",
"range": [
700,
2000
],
"type": "int"
}
]
}
================================================
FILE: Examples/hpo_mnist/mnist_hpo_demo.py
================================================
#!/usr/bin/env python3
# Modified from https://github.com/aymericdamien/TensorFlow-Examples/blob/master/examples/3_NeuralNetworks/convolutional_network.py
# The trained model is evaluated by a combination of accuracy and FLOP.
#
# Original License
"""
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
All contributions by Aymeric Damien:
Copyright (c) 2015, Aymeric Damien.
All rights reserved.
All other contributions:
Copyright (c) 2015, the respective contributors.
All rights reserved.
Each contributor holds copyright over their respective contributions.
The project versioning (Git) records all such contribution source information.
"""
from __future__ import division, print_function, absolute_import
import argparse
import os
import sys
from datetime import datetime
from math import log
import tensorflow as tf
# Import MNIST data
from tensorflow.examples.tutorials.mnist import input_data
from aup import BasicConfig, print_result
# Training Parameters
num_steps = 2000
batch_size = 128
# Network Parameters
num_input = 784 # MNIST data input (img shape: 28*28)
num_classes = 10 # MNIST total classes (0-9 digits)
# Create the neural network
def conv_net(x_dict, n_classes, reuse, is_training):
# Define a scope for reusing the variables
with tf.variable_scope('ConvNet', reuse=reuse):
# TF Estimator input is a dict, in case of multiple inputs
x = x_dict['images']
# MNIST data input is a 1-D vector of 784 features (28*28 pixels)
# Reshape to match picture format [Height x Width x Channel]
# Tensor input become 4-D: [Batch Size, Height, Width, Channel]
x = tf.reshape(x, shape=[-1, 28, 28, 1])
# Convolution Layer with 32 filters and a kernel size of 5
conv1 = tf.layers.conv2d(x, FLAGS.conv1, 5, activation=tf.nn.relu)
# Max Pooling (down-sampling) with strides of 2 and kernel size of 2
conv1 = tf.layers.max_pooling2d(conv1, 2, 2)
# Convolution Layer with 64 filters and a kernel size of 3
conv2 = tf.layers.conv2d(conv1, FLAGS.conv2, 3, activation=tf.nn.relu)
# Max Pooling (down-sampling) with strides of 2 and kernel size of 2
conv2 = tf.layers.max_pooling2d(conv2, 2, 2)
# Flatten the data to a 1-D vector for the fully connected layer
fc1 = tf.contrib.layers.flatten(conv2)
# Fully connected layer (in tf contrib folder for now)
fc1 = tf.layers.dense(fc1, FLAGS.fc1)
# Apply Dropout (if is_training is False, dropout is not applied)
fc1 = tf.layers.dropout(fc1, rate=FLAGS.dropout, training=is_training)
# Output layer, class prediction
out = tf.layers.dense(fc1, n_classes)
return out
# Define the model function (following TF Estimator Template)
def model_fn(features, labels, mode):
# Build the neural network
# Because Dropout have different behavior at training and prediction time, we
# need to create 2 distinct computation graphs that still share the same weights.
logits_train = conv_net(features, num_classes, reuse=False,
is_training=True)
logits_test = conv_net(features, num_classes, reuse=True,
is_training=False)
# Predictions
pred_classes = tf.argmax(logits_test, axis=1)
pred_probas = tf.nn.softmax(logits_test)
# If prediction mode, early return
if mode == tf.estimator.ModeKeys.PREDICT:
return tf.estimator.EstimatorSpec(mode, predictions=pred_classes)
# Define loss and optimizer
loss_op = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(
logits=logits_train, labels=tf.cast(labels, dtype=tf.int32)))
optimizer = tf.train.AdamOptimizer(learning_rate=FLAGS.learning_rate)
train_op = optimizer.minimize(loss_op,
global_step=tf.train.get_global_step())
# Evaluate the accuracy of the model
acc_op = tf.metrics.accuracy(labels=labels, predictions=pred_classes)
# TF Estimators requires to return a EstimatorSpec, that specify
# the different ops for training, evaluating, ...
estim_specs = tf.estimator.EstimatorSpec(
mode=mode,
predictions=pred_classes,
loss=loss_op,
train_op=train_op,
eval_metric_ops={'accuracy': acc_op})
return estim_specs
def train():
# Build the Estimator
mnist = input_data.read_data_sets("./input_data/", one_hot=False)
model = tf.estimator.Estimator(model_fn)
# Define the input function for training
input_fn = tf.estimator.inputs.numpy_input_fn(
x={'images': mnist.train.images}, y=mnist.train.labels,
batch_size=batch_size, num_epochs=None, shuffle=True)
# Train the Model
model.train(input_fn, steps=num_steps)
# Evaluate the Model
# Define the input function for evaluating
input_fn = tf.estimator.inputs.numpy_input_fn(
x={'images': mnist.test.images}, y=mnist.test.labels,
batch_size=batch_size, shuffle=False)
# Use the Estimator 'evaluate' method
e = model.evaluate(input_fn)
print("Testing Accuracy:", e['accuracy'])
return e['accuracy']
def get_flop():
run_meta = tf.RunMetadata()
with tf.Session(graph=tf.Graph()) as sess:
x = tf.zeros([1,784])
out = conv_net({'images':x}, 10, False, False)
opts = tf.profiler.ProfileOptionBuilder.float_operation()
flops = tf.profiler.profile(sess.graph, run_meta=run_meta, cmd='op', options=opts)
print("Model FLOPs is %d"%flops.total_float_ops)
return flops.total_float_ops
def main(_):
log_dir = FLAGS.log_dir + "%s" % datetime.now()
if tf.gfile.Exists(log_dir):
tf.gfile.DeleteRecursively(log_dir)
tf.gfile.MakeDirs(log_dir)
acc = train()
flop = get_flop()
return (acc-1)/log(flop) # metric used in AMC
if __name__ == '__main__':
if len(sys.argv) < 2:
print("config file required")
exit(1)
parser = argparse.ArgumentParser()
parser.add_argument('--learning_rate', type=float, default=0.001,
help='Initial learning rate')
parser.add_argument('--dropout', type=float, default=0.1,
help='drop probability for training dropout.')
parser.add_argument('--conv1', type=int, default=32,
help='Conv1 layer size.')
parser.add_argument('--conv2', type=int, default=64,
help='Conv2 layer size.')
parser.add_argument('--fc1', type=int, default=1024,
help='FC1 layer size.')
parser.add_argument(
'--data_dir',
type=str,
default=os.path.join(os.getenv('TEST_TMPDIR', './'),
'input_data/'),
help='Directory for storing input data')
parser.add_argument(
'--log_dir',
type=str,
default=os.path.join(os.getenv('TEST_TMPDIR', './'),
'logs/'),
help='Summaries log directory')
FLAGS, unparsed = parser.parse_known_args()
config = BasicConfig(**FLAGS.__dict__).load(sys.argv[1])
FLAGS.__dict__.update(config)
# for hyperband
if "n_iterations" in FLAGS:
FLAGS.max_steps = int(FLAGS.n_iterations * 100) # 100 times n_iteration units
print("FLAGS => " + str(FLAGS))
val = main(config)
print(str(val))
print_result(val)
================================================
FILE: Examples/hpo_mnist/mnist_hpo_fail.py
================================================
#!/usr/bin/env python3
# Modified from https://github.com/aymericdamien/TensorFlow-Examples/blob/master/examples/3_NeuralNetworks/convolutional_network.py
# The trained model is evaluated by a combination of accuracy and FLOP.
#
# Original License
"""
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
All contributions by Aymeric Damien:
Copyright (c) 2015, Aymeric Damien.
All rights reserved.
All other contributions:
Copyright (c) 2015, the respective contributors.
All rights reserved.
Each contributor holds copyright over their respective contributions.
The project versioning (Git) records all such contribution source information.
"""
from __future__ import division, print_function, absolute_import
import argparse
import os
import sys
from datetime import datetime
from math import log
import tensorflow as tf
# Import MNIST data
from tensorflow.examples.tutorials.mnist import input_data
from aup import BasicConfig, print_result
# Training Parameters
num_steps = 2000
batch_size = 128
# Network Parameters
num_input = 784 # MNIST data input (img shape: 28*28)
num_classes = 10 # MNIST total classes (0-9 digits)
# Create the neural network
def conv_net(x_dict, n_classes, reuse, is_training):
# Define a scope for reusing the variables
with tf.variable_scope('ConvNet', reuse=reuse):
# TF Estimator input is a dict, in case of multiple inputs
x = x_dict['images']
# MNIST data input is a 1-D vector of 784 features (28*28 pixels)
# Reshape to match picture format [Height x Width x Channel]
# Tensor input become 4-D: [Batch Size, Height, Width, Channel]
x = tf.reshape(x, shape=[-1, 28, 28, 1])
# Convolution Layer with 32 filters and a kernel size of 5
conv1 = tf.layers.conv2d(x, FLAGS.conv1, 5, activation=tf.nn.relu)
# Max Pooling (down-sampling) with strides of 2 and kernel size of 2
conv1 = tf.layers.max_pooling2d(conv1, 2, 2)
# Convolution Layer with 64 filters and a kernel size of 3
conv2 = tf.layers.conv2d(conv1, FLAGS.conv2, 3, activation=tf.nn.relu)
# Max Pooling (down-sampling) with strides of 2 and kernel size of 2
conv2 = tf.layers.max_pooling2d(conv2, 2, 2)
# Flatten the data to a 1-D vector for the fully connected layer
fc1 = tf.contrib.layers.flatten(conv2)
# Fully connected layer (in tf contrib folder for now)
fc1 = tf.layers.dense(fc1, FLAGS.fc1)
# Apply Dropout (if is_training is False, dropout is not applied)
fc1 = tf.layers.dropout(fc1, rate=FLAGS.dropout, training=is_training)
# Output layer, class prediction
out = tf.layers.dense(fc1, n_classes)
return out
# Define the model function (following TF Estimator Template)
def model_fn(features, labels, mode):
# Build the neural network
# Because Dropout have different behavior at training and prediction time, we
# need to create 2 distinct computation graphs that still share the same weights.
logits_train = conv_net(features, num_classes, reuse=False,
is_training=True)
logits_test = conv_net(features, num_classes, reuse=True,
is_training=False)
# Predictions
pred_classes = tf.argmax(logits_test, axis=1)
pred_probas = tf.nn.softmax(logits_test)
# If prediction mode, early return
if mode == tf.estimator.ModeKeys.PREDICT:
return tf.estimator.EstimatorSpec(mode, predictions=pred_classes)
# Define loss and optimizer
loss_op = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(
logits=logits_train, labels=tf.cast(labels, dtype=tf.int32)))
optimizer = tf.train.AdamOptimizer(learning_rate=FLAGS.learning_rate)
train_op = optimizer.minimize(loss_op,
global_step=tf.train.get_global_step())
# Evaluate the accuracy of the model
acc_op = tf.metrics.accuracy(labels=labels, predictions=pred_classes)
# TF Estimators requires to return a EstimatorSpec, that specify
# the different ops for training, evaluating, ...
estim_specs = tf.estimator.EstimatorSpec(
mode=mode,
predictions=pred_classes,
loss=loss_op,
train_op=train_op,
eval_metric_ops={'accuracy': acc_op})
return estim_specs
def train():
# Build the Estimator
mnist = input_data.read_data_sets("./input_data/", one_hot=False)
model = tf.estimator.Estimator(model_fn)
# Define the input function for training
input_fn = tf.estimator.inputs.numpy_input_fn(
x={'images': mnist.train.images}, y=mnist.train.labels,
batch_size=batch_size, num_epochs=None, shuffle=True)
# Train the Model
model.train(input_fn, steps=num_steps)
# Evaluate the Model
# Define the input function for evaluating
input_fn = tf.estimator.inputs.numpy_input_fn(
x={'images': mnist.test.images}, y=mnist.test.labels,
batch_size=batch_size, shuffle=False)
# Use the Estimator 'evaluate' method
e = model.evaluate(input_fn)
print("Testing Accuracy:", e['accuracy'])
return e['accuracy']
def get_flop():
run_meta = tf.RunMetadata()
with tf.Session(graph=tf.Graph()) as sess:
x = tf.zeros([1,784])
out = conv_net({'images':x}, 10, False, False)
opts = tf.profiler.ProfileOptionBuilder.float_operation()
flops = tf.profiler.profile(sess.graph, run_meta=run_meta, cmd='op', options=opts)
print("Model FLOPs is %d"%flops.total_float_ops)
return flops.total_float_ops
def main(_):
log_dir = FLAGS.log_dir + "%s" % datetime.now()
if tf.gfile.Exists(log_dir):
tf.gfile.DeleteRecursively(log_dir)
tf.gfile.MakeDirs(log_dir)
acc = train()
flop = get_flop()
return (acc-1)/log(flop) # metric used in AMC
if __name__ == '__main__':
if len(sys.argv) < 2:
print("config file required")
exit(1)
import random
if random.random()>0.75:
raise Exception('failed', 'test123')
parser = argparse.ArgumentParser()
parser.add_argument('--learning_rate', type=float, default=0.001,
help='Initial learning rate')
parser.add_argument('--dropout', type=float, default=0.1,
help='drop probability for training dropout.')
parser.add_argument('--conv1', type=int, default=32,
help='Conv1 layer size.')
parser.add_argument('--conv2', type=int, default=64,
help='Conv2 layer size.')
parser.add_argument('--fc1', type=int, default=1024,
help='FC1 layer size.')
parser.add_argument(
'--data_dir',
type=str,
default=os.path.join(os.getenv('TEST_TMPDIR', './'),
'input_data/'),
help='Directory for storing input data')
parser.add_argument(
'--log_dir',
type=str,
default=os.path.join(os.getenv('TEST_TMPDIR', './'),
'logs/'),
help='Summaries log directory')
FLAGS, unparsed = parser.parse_known_args()
config = BasicConfig(**FLAGS.__dict__).load(sys.argv[1])
FLAGS.__dict__.update(config)
# for hyperband
if "n_iterations" in FLAGS:
FLAGS.max_steps = int(FLAGS.n_iterations * 100) # 100 times n_iteration units
print("FLAGS => " + str(FLAGS))
val = main(config)
print(str(val))
print_result(val)
================================================
FILE: Examples/intermediate_results/README.md
================================================
# Intermediate Results
## Usage
Intermediate results are non-final results for each job of an experiment used
in order to better tell the evolution of a job through time. Each intermediate
"result" can be one or multiple values.
In order to let auptimizer know that intermediate results are to be tracked,
the flag "track_intermediate_results" should be passed in the experiment config
under "resource_args":
"resource_args":
{
"track_intermediate_results": true
}
The script used in the experiment then has to call `aup.print_result` each time
an intermediate result is to be saved. The last reported result will always be taken
as the final result of a job. Please refer to `quad_euation_min/quad_min.py` for training script modification.
The intermediate results will be shown on the dashboard if tracked.
## Examples
Take the following main function in a user script:
def main(x, a=2, b=4, c=5, n=10):
it = 1.0
for _ in range(n):
res = x*x*a + x*b + c
res += it
it -= 1.0 / 10
aup.print_result(res)
1. If "track_intermediate_results" is `false` or missing, all results except the last one will be discarded:
```json
{
"proposer": ...,
"script": ...,
"resource": ...,
"n_parallel": ...,
"target": "min",
"n_samples": ...,
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
```
```
sqlite> SELECT COUNT(*) FROM intermediate_result;
COUNT(*)
0
```
2. If "track_intermediate_results" is present in the config and is set to `true`, then the values will be found in the database (200 jobs with 10 intermediate results each):
```json
{
"proposer": ...,
"script": ...,
"resource": ...,
"n_parallel": ...,
"target": "min",
"n_samples": ...,
"resource_args":
{
"track_intermediate_results": true
},
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
```
```
sqlite> SELECT COUNT(*) FROM intermediate_result;
2000
```
================================================
FILE: Examples/intermediate_results/quad_equation_min/quad_min.py
================================================
#!/usr/bin/env python
"""
Modified Rosenbrock function for HPO and aup
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import sys
import aup
from time import sleep
def rosenbrock(x, a=2, b=4, c=5):
sleep(1)
it = 1.0
for _ in range(10):
res = x*x*a + x*b + c
res += it
it -= 1.0 / 10
aup.print_result(res)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("config file required")
exit(1)
config = aup.BasicConfig().load(sys.argv[1])
rosenbrock(config['x'])
================================================
FILE: Examples/intermediate_results/quad_equation_min/quad_min_BOHB.json
================================================
{
"name": "./intermediate_results/quad_equation_min/quad_min_BOHB_bandit.json",
"proposer": "bohb",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_iterations": 100,
"num_samples": 64,
"random_fraction": 0.3333333333333333,
"bandwidth_factor": 3,
"min_bandwidth": 0.001,
"eta": 3,
"min_budget": 1,
"max_budget": 5,
"resource_args":
{
"track_intermediate_results": true
},
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/intermediate_results/quad_equation_min/quad_min_random.json
================================================
{
"name": "./intermediate_results/quad_equation_min/quad_min_random_bandit.json",
"proposer": "random",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_samples": 200,
"resource_args":
{
"track_intermediate_results": true
},
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/intermediate_results/quad_equation_min/quad_min_spearmint.json
================================================
{
"name": "./intermediate_results/quad_equation_min/quad_min_spearmint_bandit.json",
"proposer": "spearmint",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_samples": 50,
"engine":"GPEIChooser",
"resource_args":
{
"track_intermediate_results": true
},
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/job_failure_control/README.md
================================================
# Job Failure control Examples
This folder contains examples that demonstrate the different scenarios under job failure conditions and how Auptimizer handles them gracefully.
The examples can be broadly divided into two categories -
## 1. Job failures based on incorrect or incomplete experiment configuration (For debugging purposes only)
`experiment_no_*.json` files demonstrate an ill-configured experiment setup. They will result in standard error conditions and Auptimizer will report them, providing users with detailed description of the failure.
- `experiment_no_nsample.json` : Auptimizer Error ( KeyError: "Specify number of samples to randomly draw")
- `experiment_no_param_config.json` : Auptimizer Error (KeyError: "Specify the parameter configuration 'parameter_config' to be searched")
- `experiment_no_proposer.json` : Auptimizer Error (KeyError: "Specify the optimization 'proposer'")
- `experiment_no_resource.json` : Auptimizer Error (KeyError: "Missing required value for 'resource'")
- `experiment_no_script.json` : Auptimizer Error (KeyError: "Missing required value for 'script'")
- `experiment_extra_argument.json` : No Auptimizer Error (for extra variable c)
## 2. Experiment persistence and job retries upon job failure
The following examples show how Auptimizer provides control for allowing job retries and experiment continuation upon job failures.
- `experiment_job_retries.json` : Auptimizer retries failed job 3 times.
- `experiment_job_ignore_fail.json` : Auptimizer ignores failed job, and continues the experiment
- `experiment_job_retries_ignore_fail.json` : Auptimizer retries failed job 3 times. If all retries fail, Auptimizer ignores the failed job and continues the experiment.
Auptimizer uses the `job_failure` variable to control this behavior, with the following parameters:
- `job_retries` : number of times to retry a failed job, default is 0. Preference is given to a different resource, if multiple resources are available.
- `ignore_fail` : whether to continue the experiment if a job fails, default is False. Currently [BOHB, EAS, Hyperband] proposers do not support experiment continuation upon job failure.
The examples use `test.py` file, which will fail to return any result when `time=3`. When we run the base case `experiment_test.json` as:
python3 -m aup experiment_test.json
It will print something like:
2018-10-17 12:02:02 - aup.EE.Resource.CPUResourceManager - CRITICAL - Failed to run job:
******/Examples/job_failure_control/test.py
******/Examples/job_failure_control/jobs/79.json
2018-10-17 12:02:02 - aup.EE.Experiment - CRITICAL - Stop Experiment due to job failure
Best job (80) with score 4.000000 in experiment 14
## experiment ill-configuration
Other `experiment_no_*.json` files demonstrate ill-configured experiment setup.
================================================
FILE: Examples/job_failure_control/experiment_extra_argument.json
================================================
{
"name": "./job_failure_control/experiment_extra_argument.json",
"script": "rosenbrock_hpo.py",
"resource": "cpu",
"n_parallel": 2,
"target": "min",
"proposer": "random",
"n_samples": 20,
"random_seed": 0,
"parameter_config": [
{
"name": "x",
"range": [
0,
10
],
"type": "float"
},
{
"name": "y",
"range": [
0,
10
],
"type": "float"
},
{
"name": "c",
"range": [
0,
1
],
"type": "float"
}
]
}
================================================
FILE: Examples/job_failure_control/experiment_job_ignore_fail.json
================================================
{
"name": "./job_failure_control/experiment_job_ignore_fail.json",
"proposer": "sequence",
"n_samples": 10,
"random_seed": 1,
"script": "test.py",
"job_failure": {
"ignore_fail": true
},
"parameter_config": [
{
"name": "time",
"range": [
1,
10
],
"type": "int"
}
],
"resource": "cpu",
"n_parallel": 2,
"target":"max"
}
================================================
FILE: Examples/job_failure_control/experiment_job_retries.json
================================================
{
"name": "./job_failure_control/experiment_job_retries.json",
"proposer": "sequence",
"n_samples": 10,
"random_seed": 1,
"script": "test.py",
"job_failure": {
"job_retries": 3
},
"parameter_config": [
{
"name": "time",
"range": [
1,
10
],
"type": "int"
}
],
"resource": "cpu",
"n_parallel": 2,
"target":"max"
}
================================================
FILE: Examples/job_failure_control/experiment_job_retries_ignore_fail.json
================================================
{
"name": "./job_failure_control/experiment_job_retries_ignore_fail.json",
"proposer": "sequence",
"n_samples": 10,
"random_seed": 1,
"script": "test.py",
"job_failure": {
"job_retries": 3,
"ignore_fail": true
},
"parameter_config": [
{
"name": "time",
"range": [
1,
10
],
"type": "int"
}
],
"resource": "cpu",
"n_parallel": 2,
"target":"max"
}
================================================
FILE: Examples/job_failure_control/experiment_no_nsample.json
================================================
{
"name": "./job_failure_control/experiment_no_nsample.json",
"proposer": "random",
"random_seed": 1,
"script": "rosenbrock_hpo.py",
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "cpu",
"n_parallel": 4,
"target":"min"
}
================================================
FILE: Examples/job_failure_control/experiment_no_param_config.json
================================================
{
"name": "./job_failure_control/experiment_no_param_config.json",
"proposer": "random",
"n_samples": 30,
"random_seed": 1,
"script": "rosenbrock_hpo.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min"
}
================================================
FILE: Examples/job_failure_control/experiment_no_proposer.json
================================================
{
"name": "./job_failure_control/experiment_no_proposer.json",
"n_samples": 30,
"random_seed": 1,
"script": "rosenbrock_hpo.py",
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "cpu",
"n_parallel": 4,
"target":"min"
}
================================================
FILE: Examples/job_failure_control/experiment_no_resource.json
================================================
{
"name": "./job_failure_control/experiment_no_resource.json",
"proposer": "random",
"n_samples": 30,
"random_seed": 1,
"script": "rosenbrock_hpo.py",
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"n_parallel": 4,
"target":"min"
}
================================================
FILE: Examples/job_failure_control/experiment_no_script.json
================================================
{
"name": "./job_failure_control/experiment_no_script.json",
"proposer": "random",
"n_samples": 30,
"random_seed": 1,
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "cpu",
"n_parallel": 4,
"target":"min"
}
================================================
FILE: Examples/job_failure_control/experiment_test.json
================================================
{
"name": "./job_failure_control/experiment_test.json",
"proposer": "sequence",
"n_samples": 10,
"random_seed": 1,
"script": "test.py",
"parameter_config": [
{
"name": "time",
"range": [
1,
10
],
"type": "int"
}
],
"resource": "cpu",
"n_parallel": 2,
"target":"max"
}
================================================
FILE: Examples/job_failure_control/rosenbrock_hpo.py
================================================
#!/usr/bin/env python
"""
Modified Rosenbrock function for HPO and aup
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import sys
"""
from aup import BasicConfig, print_result
def rosenbrock(conf, a=1, b=100):
x = conf.x
y = conf.y
return (a-x)*(a-x) + b*(y-x*x)*(y-x*x)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("config file required")
exit(1)
config = BasicConfig().load(sys.argv[1])
val = rosenbrock(config)
print_result(val)
"""
from aup import aup_args
@aup_args
def rosenbrock(x, y, a=1, b=100):
return (a-x)*(a-x) + b*(y-x*x)*(y-x*x)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("config file required")
exit(1)
rosenbrock(sys.argv[1])
================================================
FILE: Examples/job_failure_control/test.py
================================================
#!/usr/bin/env python3
"""
Job will fail when sleep time is 1
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import sys
from time import sleep
from aup import BasicConfig, print_result
if __name__ == "__main__":
if len(sys.argv) != 2:
print("config file required")
exit(1)
config = BasicConfig().load(sys.argv[1])
sleep(1+config.time/10.)
if config.time == 3:
exit(1)
print_result(config.time)
================================================
FILE: Examples/mnist_keras_save_model/README.md
================================================
# Save best model feature
This example showcases how to use saving the best model feature. This feature allows the user to save the best performing model after running the HPO experiment. This is achieved by running the training script again using the best hyperparamters obtained during HPO the experiment.
The model, by default, will be saved to path ``aup_models/models_/``.
## Usage
In order to use this feature, please add to the configuration file:
"resource_args": {
"save_model": true
}
Depending on whether ``@aup_args`` is used, the training script needs to be changed differently.
Please check ``mnist.py`` and ``mnist_wo_decorator.py`` for examples of adapting the training script
with and without using the decorator, respectively.
## Run experiment
To run the experiment on the remote machine, make sure to change the ``node.txt`` file and "workingdir"
in ``exp_random_node.json`` to the your own settings. Then run
```sh
python -m aup.setup
python -m aup exp_random_node.json
```
To run the experiment using the local cpu, please run
```sh
python -m aup.setup cpu.ini
python -m aup exp_random_cpu.json
```
================================================
FILE: Examples/mnist_keras_save_model/cpu.ini
================================================
[Auptimizer]
# Auptimizer environment folder to be created, this file will be copied over
Auptimizer_PATH=./.aup
# Temp folder
TMP_FOLDER=./aup_tmp
# SQL engine
SQL_ENGINE=sqlite
================================================
FILE: Examples/mnist_keras_save_model/exp_random_cpu.json
================================================
{
"name": "random_wo_decorator",
"proposer": "random",
"script": "mnist_wo_decorator.py",
"resource": "cpu",
"n_parallel": 4,
"target":"max",
"n_samples": 10,
"resource_args": {
"save_model": true
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
}
]
}
================================================
FILE: Examples/mnist_keras_save_model/exp_random_node.json
================================================
{
"name": "random_w_decorator_remote",
"workingdir": "/home/usr/aup_demo",
"proposer": "random",
"script": "mnist.py",
"resource": "node",
"n_parallel": 4,
"target":"max",
"n_samples": 10,
"resource_args": {
"save_model": true
},
"runtime_args": {
"overwrite": true
},
"parameter_config": [
{
"name": "dropout",
"range": [0.0, 0.5],
"type": "float"
},
{
"name": "learning_rate",
"range": [
0.001,
0.01
],
"type": "float"
},
{
"name": "conv1",
"range": [
20,
50
],
"type": "int"
},
{
"name": "conv2",
"range": [
40,
80
],
"type": "int"
}
]
}
================================================
FILE: Examples/mnist_keras_save_model/mnist.py
================================================
#!/usr/bin/env python3
"""
MNIST convolutional network using Keras
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
from aup import print_result, aup_args, aup_save_model
import tensorflow as tf
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
from math import log
import sys
num_epochs = 5
batch_size = 64
num_classes = 10
input_shape = (28, 28, 1)
# Load example MNIST data and pre-process it
# the data, split between train and test sets
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
# Scale images to the [0, 1] range
x_train = x_train.astype("float32") / 255
x_test = x_test.astype("float32") / 255
# Make sure images have shape (28, 28, 1)
x_train = np.expand_dims(x_train, -1)
x_test = np.expand_dims(x_test, -1)
# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
def get_flops():
run_meta = tf.RunMetadata()
opts = tf.profiler.ProfileOptionBuilder.float_operation()
# We use the Keras session graph in the call to the profiler.
flops = tf.profiler.profile(graph=keras.backend.get_session().graph,
run_meta=run_meta, cmd='op', options=opts)
return flops.total_float_ops # Prints the "flops" of the model.
def get_model(**kwargs):
model = keras.Sequential(
[
keras.Input(shape=input_shape),
layers.Conv2D(kwargs['conv1'], kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Conv2D(kwargs['conv2'], kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Flatten(),
layers.Dropout(kwargs['dropout']),
layers.Dense(num_classes, activation="softmax"),
]
)
model.compile(loss="categorical_crossentropy",
optimizer=keras.optimizers.Adam(learning_rate=kwargs['learning_rate']),
metrics=["accuracy"])
return model
@aup_args
def do_train(learning_rate=0.001, dropout=0.1, conv1=32, conv2=64):
model = get_model(**locals())
model.fit(
x_train,
y_train,
batch_size=batch_size,
epochs=num_epochs,
verbose=1,
validation_split=0.5,
)
res = model.evaluate(
x_test,
y_test,
batch_size=batch_size,
verbose=1,
)
# register the saving model function
# add model as argument
aup_save_model(save_model, model)
flops = get_flops()
return (res[1]-1.0) / log(flops)
def save_model(model):
import os
# dummy folders as example
os.makedirs('f1/f2')
os.makedirs('f1/f3')
# actual model
model.save('./f1/f2/mnist.h5')
if __name__ == '__main__':
if len(sys.argv) < 2:
print("config file required")
exit(1)
do_train(sys.argv[1])
================================================
FILE: Examples/mnist_keras_save_model/mnist_wo_decorator.py
================================================
#!/usr/bin/env python3
"""
MNIST convolutional network using Keras
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
from aup import BasicConfig, print_result
import tensorflow as tf
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
from math import log
import sys
num_epochs = 5
batch_size = 64
num_classes = 10
input_shape = (28, 28, 1)
# Load example MNIST data and pre-process it
# the data, split between train and test sets
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
# Scale images to the [0, 1] range
x_train = x_train.astype("float32") / 255
x_test = x_test.astype("float32") / 255
# Make sure images have shape (28, 28, 1)
x_train = np.expand_dims(x_train, -1)
x_test = np.expand_dims(x_test, -1)
# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
def get_flops():
run_meta = tf.RunMetadata()
opts = tf.profiler.ProfileOptionBuilder.float_operation()
# We use the Keras session graph in the call to the profiler.
flops = tf.profiler.profile(graph=keras.backend.get_session().graph,
run_meta=run_meta, cmd='op', options=opts)
return flops.total_float_ops # Prints the "flops" of the model.
def get_model(**kwargs):
model = keras.Sequential(
[
keras.Input(shape=input_shape),
layers.Conv2D(kwargs['conv1'], kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Conv2D(kwargs['conv2'], kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Flatten(),
layers.Dropout(kwargs['dropout']),
layers.Dense(num_classes, activation="softmax"),
]
)
model.compile(loss="categorical_crossentropy",
optimizer=keras.optimizers.Adam(learning_rate=kwargs['learning_rate']),
metrics=["accuracy"])
return model
def do_train(learning_rate=0.001, dropout=0.1, conv1=32, conv2=64,
save_model=False, folder_name=None):
model = get_model(**locals())
model.fit(
x_train,
y_train,
batch_size=batch_size,
epochs=num_epochs,
verbose=1,
validation_split=0.5,
)
res = model.evaluate(
x_test,
y_test,
batch_size=batch_size,
verbose=1,
)
# this means we are in best job context
# config also contains save_model flag and
# folder_name path
if save_model is True:
import shutil
import os
path = os.path.join('aup_models', folder_name)
if os.path.exists('aup_models') is False:
os.makedirs('aup_models')
if os.path.exists(path) is True:
shutil.rmtree(path)
os.makedirs(path)
os.chdir(path)
save_model_fun(model)
flops = get_flops()
return (res[1]-1.0) / log(flops)
def save_model_fun(model):
import os
# dummy folders as example
os.makedirs('f1/f2')
os.makedirs('f1/f3')
# actual model
model.save('./f1/f2/mnist.h5')
if __name__ == '__main__':
if len(sys.argv) < 2:
print("config file required")
exit(1)
config = BasicConfig()
config.load(sys.argv[1])
val = do_train(**config)
print(str(val))
print_result(val)
================================================
FILE: Examples/mnist_keras_save_model/node.txt
================================================
user@192.168.XX.XX
user@192.168.XX.XX
================================================
FILE: Examples/profiler_examples/README.md
================================================
For more details about how to use Profiler and run the examples in this folder refer to [Profiler](https://github.com/LGE-ARC-AdvancedAI/auptimizer/tree/master/src/aup/profiler).
We have also provided detailed experiments we performed using Profiler to estimate the performance of various edge-devices under [Experiments]( https://github.com/LGE-ARC-AdvancedAI/auptimizer/tree/master/Examples/profiler_examples/experiments).
================================================
FILE: Examples/profiler_examples/bench/download.sh
================================================
# Copyright (c) 2020 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
wget https://storage.googleapis.com/download.tensorflow.org/models/mobilenet_v1_2018_02_22/mobilenet_v1_1.0_224.tgz
wget https://storage.googleapis.com/download.tensorflow.org/models/mobilenet_v1_2018_02_22/mobilenet_v1_0.75_224.tgz
tar -xvzf mobilenet_v1_1.0_224.tgz
tar -xvzf mobilenet_v1_0.75_224.tgz
================================================
FILE: Examples/profiler_examples/bench/test_perf.py
================================================
# Copyright (c) 2020 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
# Compute the overall inference time for a given tflite model
import tensorflow as tf
from tflite_runtime.interpreter import Interpreter
import tensorflow.random
import numpy as np
import time
import os
WARMUP = 1
ITER = 500
CONSTANT = 0.5
def compute(model_path):
now = time.monotonic()
intp = Interpreter(model_path)
x = intp.tensor(intp.get_input_details()[0]['index'])
iy = intp.get_output_details()[0]['index']
intp.allocate_tensors()
t1 = time.monotonic() - now
now = time.monotonic()
for i in range(WARMUP):
#x().fill(CONSTANT)
x =np.random.rand()
intp.invoke()
y = intp.get_tensor(iy)
t2 = time.monotonic() - now
now = time.monotonic()
for i in range(ITER):
#x().fill(CONSTANT)
x =np.random.rand()
intp.invoke()
y = intp.get_tensor(iy)
t3 = time.monotonic() - now
return t1, t2/float(WARMUP), t3/float(ITER)
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
print("test_acc.py ")
print(compute(sys.argv[1]))
================================================
FILE: Examples/profiler_examples/env_benchmark.template
================================================
# Copyright (c) 2020 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
#User data Variables
IMAGEREPO=tensorflow/tensorflow:1.15.0-py3
APTREQUIREMENTS="wget"
# PIPREQUIREMENTS="ipython numpy"
PRERUN="wget https://dl.google.com/coral/python/tflite_runtime-1.14.0-cp36-cp36m-linux_x86_64.whl; pip install tflite_runtime-1.14.0-cp36-cp36m-linux_x86_64.whl"
DIR=bench
SCRIPT=test_perf.py
COMMAND=python
SAMPLETIME=3
OUTPUTFILE=out.txt
DOCFILE=Dockerfile
DOCKCPUS="4.0"
DOCKMEMORY="2.0g"
# Additional docker arguments could be passed as following:
# To run Docker container with privilege capability change to 'true'
# To use Volume instead of copying data with the current folder
# use the format "-v /path/in/source:/path/in/destination" as string
# DOCKER_ARGS="--privileged -v /data:/mnist_data"
DOCKER_ARGS=
================================================
FILE: Examples/profiler_examples/env_mnist.template
================================================
# Copyright (c) 2020 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
#User data Variables
IMAGEREPO=tensorflow/tensorflow:1.3.0
# APTREQUIREMENTS="curl vim"
# PIPREQUIREMENTS="ipython numpy"
DIR=mnist
SCRIPT=mnist.py
COMMAND=python
SAMPLETIME=3
OUTPUTFILE=out.txt
DOCFILE=Dockerfile
DOCKCPUS="4.0"
DOCKMEMORY="2.0g"
# Additional docker arguments could be passed as following:
# To run Docker container with privilege capability change to 'true'
# To use Volume instead of copying data with the current folder
# use the format "-v /path/in/source:/path/in/destination" as string
# DOCKER_ARGS="--privileged -v /data:~/Documents/Data/mnist1"
DOCKER_ARGS=
================================================
FILE: Examples/profiler_examples/experiments/Readme.md
================================================
# Use Profiler for On-Device Resource Footprint Estimation
Profiler can be used to estimate model script performance and resource requirements for a target device on the development machine. Given a set of models and the resource constraints (i.e. memory and CPU) to reflect *target device specs* or *the desired resource budget*, Profiler can help with the following **two scenarios**:
1. The ranking of the runtimes and memory usages of the model scripts measured using Profiler on the development machine is indicative of their ranking on the target device. For instance, if `Model1` is faster than `Model2` when measured using Profiler on the development machine, `Model1` will be faster than `Model2` on the device. This ranking is valid only when the CPUs are running at **full utilization**.
2. The runtimes and memory usages of the model scripts measured using Profiler on the development machine is related by a simple **linear relationship** to the usage measured with a native profiling tool on the actual device. In other words, if a model runs in time `x` when measured using Profiler, it will run approximately in time `(a*x+b)` on the target device (where `a` and `b` can be discovered by profiling a few models on the device with a native profiling tool). The strength of this relationship depends on the architectural similarity between the models. In general, the models designed for the same task are architecturally similar as they are composed of the same set of layers, making Profiler a useful tool for model selection.
To support these claims and showcase Profiler's capabilities, we have conducted extensive experiments on three devices: LG G6, Samsung S8, and NVIDIA Jetson Nano. For anyone interested in conducting similar experiments on their own devices, we have put together the following Experiment Guidelines.
## Guidelines for Experiment Setup
The goal of the experiment is to explore the relationship of model runtime and resource footprint measured using Profiler on the development machine and model runtime and resource footprint measured with a native profiling tool on a target device.
**For the first scenario**, to get a performance ordering of models, the user can acquire the CPU and memory constraints of the target device, and run Profiler on a desktop CPU with those constraints. Please make sure that the models used in Profiler match those to be deployed on-device (e.g., both are TFLite models). Our experiments show that the output from Profiler should provide the relative goodness of models for CPU-based devices.
In some cases, when two models are very close in performance (e.g., with peak memory usage difference of ~10MB), a flipped ordering might be observed on-device compared to Profiler output. However, the performances should still be close to each other.
**For the second scenario**, to acquire a linear relationship between Profiler estimates and on-device performance, the user needs to profile a set of models both using Profiler on the development machine and with a native profiling tool on the target device. Below, are the general steps to conduct this experiment.
- ***Step 1***: Prepare the models and profiling scripts for both Profiler and the target device. The models running on Profiler and on-device should be in the same format / generated using the same framework. The scripts running on Profiler and on-device may be written in different programming languages, but should implement the same functionality (e.g., both running inference using batch size 1 for 500 iterations).
- ***Step 2***: Acquire device resource constraints (i.e. number of CPU cores/threads and memory limit), and input these constraints in the Profiler user variables in `env.template`.
- ***Step 3***: Run experiments.
- Run the profiling script using Profiler for all models on a desktop CPU.
- Run the profiling script using a native profiling tool on the target device.
- ***Step 4***: Due to possible variances across experiments, we recommend running Step 3 multiple times and taking the averages of the measured metrics for further analysis.
- ***Step 5 (optional)***: Use any regression analysis tool to establish the relationship between Profiler outputs and the on-device measurements for the profiled models.
## Experiment I - Profiler and TFLite Model Benchmark for LG G6 and Samsung S8 Deployments
### Experiment Configurations:
- ***Models:*** InceptionV3, SqueezeNet, MobileNetV2-0.25x, -0.5x, -0.75x, -1.0x
- ***Profiling script:*** Profile inference performance. Run inference using a selected model for 500 iterations with batchsize 1 on randomly generated input
- ***Framework:*** TFLite
- ***CPU:*** CPU = 1, 2, 4 for Profiler, use Thread = 1, 2, 4 on the phone
- ***Memory:*** Max memory is 4 GB for both phones, but the memory variable is set to 2 GB in Profiler, which simulates a lower memory limit.
- ***On-device profiling tool:*** [TFLite Model Benchmark Tool](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/tools/benchmark)
### Results:
The experiment was conducted 5 times. We measured the average inference time on one input sample across 500 iterations, and the peak memory usage. The averages of 5 experiments are shown in Figures 1 and 2. The formula and R-squared value for the best fitting line are also shown in the plots.
Even though Profiler only outputs the script's total execution time, users can utilize the Profiler environment and measure the execution time of different functions in their profiling script like we did in this experiment. An example script for measuring the inference time is provided in [script](https://github.com/LGE-ARC-AdvancedAI/auptimizer/blob/master/Examples/profiler_examples/bench/test_perf.py).
||
|:--:|
| *Figure 1: Model inference time measured with Profiler on the desktop vs TFLite Model Benchmark on LG G6, and Profiler on the desktop vs TFLite Model Benchmark on Samsung S8.* |
For both phones, a linear relationship can be established between the inference time measured in the Profiler environment and the inference time measured on the phones, for various numbers of CPUs/threads.
||
|:--:|
| *Figure 2: Peak memory usage measured on Profiler vs LG G6, and Profiler vs Samsung S8.* |
The peak memory usage does not vary much across the number of CPUs/threads. So we only present one representative plot with `CPU/thread = 1` for both phones. Again, a linear relationship can be observed. The dot on the right-upper corner represents the InceptionV3 model, in both Figures 1 and 2, which has a significantly larger resource footprint than the rest of the models.
## Experiment II - Profiler and Jetson Stats for Nvidia Jetson Nano Deployment
### Experiment Configurations:
- ***Models:***
*Image classification models*: ShuffleNetV2-0.5x, SqueezeNet, MobileNetV2-1.0x, ResNet18, InceptionV3
*Video classification models*: 3D-SqueezeNet, 3D-ShuffleNetV2-0.25x, -0.5x, -1.0x, -1.5x, -2.0x, 3D-MobileNetV2-0.25x, -0.5x, -0.75x, -1.0x, -2.0x
(model credit to [Efficient-3DCNNs](https://github.com/okankop/Efficient-3DCNNs))
- ***Profiling script:*** Profile inference performance. Run inference using a selected model for 500 iterations with batchsize 1 on randomly generated input
- ***Framework:*** Pytorch
- ***CPU:*** CPU = 1 for Profiler, use 1 CPU core on NVIDIA Jetson Nano
- ***Memory Limit:*** Max memory on NVIDIA Jetson Nano is 4 GB. The memory variable is set to 4 GB in Profiler.
- ***On-device profiling tool:*** [jetson-stats](https://github.com/rbonghi/jetson_stats)
### Results:
We ran the experiment 5 times. For this experiment, we measured the script's total execution time (direct output from Profiler, denoted as `Runtime` in figures), average memory usage, and peak memory usage. The averages of these measures across five experiments are shown in Figures 3 and 4. The formula and R-squared value for the best fitting line are also shown in the plots.
||
|:--:|
| *Figure 3: Image model performances measured using Profiler on the desktop and Jetson Stats on NVIDIA Jetson Nano.* |
||
|:--:|
| *Figure 4: Video model performances measured using Profiler on the desktop and Jetson Stats on NVIDIA Jetson Nano.* |
Note that separate plots are made for image models and video models, respectively. This is due to the fact that video models profiled here are architecturally different from the image models. As we have discussed, Profiler estimation is only valid for models with similar architectures or layers.
## Conclusion
From the experimental results shared above, we have shown that Profiler can help users build a good estimate of model runtime and memory usage on LG G6, Samsung S8, and NVIDIA Jetson Nano, for some popular image/video recognition models. We hope that Profiler's estimation capability can enable leaner and faster model development for resource-constrained devices.
Profiler might have limitations for certain models/devices, resulting in inconsistencies between Profiler outputs and on-device measurements. Therefore, we welcome the community to run experiments on their device of choice and **report any inconsistencies or limitations via GitHub Issues**.
================================================
FILE: Examples/profiler_examples/internal/ImageNet Experiments.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Copyright (c) 2020 LG Electronics Inc.\n",
"# SPDX-License-Identifier: GPL-3.0-or-later\n",
"# ImageNet Experiments with MobileNet Inference using Profiler"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"output_type": "error",
"ename": "SyntaxError",
"evalue": "invalid syntax (, line 1)",
"traceback": [
"\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m We performed many ImageNet Experiments in the inference mode under different Compute constraints using Profiler. We used different MobileNet versions for the experiments detailed in https://www.tensorflow.org/lite/guide/hosted_models.\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
]
}
],
"source": [
"We performed many ImageNet Experiments in the inference mode under different Compute constraints using Profiler. We used different MobileNet versions for the experiments detailed in https://www.tensorflow.org/lite/guide/hosted_models.\n",
"\n",
"The source code used for Inference is available at 'https://gitlab.lgsvl.net/jason.liu/compression/tree/compression_profiler/src/mobilenet' \n",
"and specifically 'test_perf.py'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The experimental settings used 'Mobilenet_V1_0.25_224', 'Mobilenet_V1_0.50_224', 'Mobilenet_V1_0.75_224', 'Mobilenet_V1_1.0_224'. The CPU contraints were 4, 2, 1, 0.5 and Inference was performed with 500 and 5000 iterations over a single validation TFrecord."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We recorded the time taken for the inference process to finish per each iteration under these conditions and observed the following results"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Iter = 5000\t\t\t\t\t\t\t\t\t\t\t\t"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"| Exp | Mob 1.0 | Mob 0.75 |Mob 0.5 |Mob 0.25 |\n",
"|----------|:-------------:|:-------------:|:-------------:|:-------------:|\n",
"| CPU = 4 | 0.02016684028 | 0.01380343905 | 0.008244011551 | 0.004547368029 |\n",
"| CPU = 2 | 0.02899219721 | 0.01863248097 | 0.01087483355 |0.005579292277 |\n",
"| CPU = 1 | 0.05820066958 | 0.03714312666 | 0.02166576314 | 0.0111540261 |\n",
"| CPU = 0.5 | 0.1165416765 | 0.07581892986 | 0.04407729727 | 0.02251805596 |"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAFzCAYAAADi5Xe0AAAhZUlEQVR42u3dDZBVdf34cfjxg0CmoRqIgQgqEWigLOiBCGqCYWooevAX/eo/UJAmBdG4gk9AKURqSuv4zzKd0X85SslDDyz2QzBDYFQMRbCEkhB8AORpQZZddGE//9/3zNw7u7AsLOyF5e7rNXMG793D7nI+3nvee+65Z1sFAABNqpVNAAAgsAAABBYAgMACAEBgAQAILAAAgQUAgMACABBYAAACCwAAgQUAILAAAAQWAIDAAgBAYAEACCwAAIEFAIDAAgAQWAAAAgsAAIEFACCwAAAEVm179uyJq666Kn7xi18c97F9+/bFK6+8ct5urMWLF2f/tvqW+++/v866R44ciUcffTRuueWWuO666+Lmm2+ORx55JI4ePXrc5y3UugBAkQfWli1bYvr06bFmzZrzdmPdfffdpxxY8+bNO6X1CrkuAFDkgfX0009n95/PgTVr1qy49dZbT7reP//5z+zf+uMf/zj+9a9/xZtvvhkvvPBCdjvdnz5e6HUBAIHV7FVUVJzykaLf/OY32bopgmpLMZTuv++++wq+LgBQ5IFV38tar732Wv7jVVVVsWTJkrjpppvi2muvjTlz5sTvf//7LGpqu/fee+PGG2+MnTt3ZkeSZsyYEb/73e/OyobavHlz9n2n859OZvbs2XH99ddHTU1NnfvT7RtuuCFbCr0uANCCA6uysjLmzp1b73lFKbgOHjxYJ7BSfNVe/6GHHmrw+0nrn+i8qdzypz/96aT/rtWrV2frrly5Mn79619ncfejH/0oO2q0a9euOrGY1vvlL39Z7+e58847s4+nf3eh1gUAWkBgJSd6iXDhwoXZ/SnC0pGp6urq2Lt3bxY96f4HHnigTmCl+372s59l66R3zp3s3XNNFVi57/Oaa6457u//8Ic/jFdffTVbr7y8vMGX63Iv86V3VBZqXQCgBQdWuuRAemdhaWnpcS97Jeldeylo3njjjTqBtX79+rO+oe64447se0mXREixk04wf/nll+Ouu+7Kvqf0b0jSkbljw7C2dH/uCF6h1gUAWnBgbd++/aRHl9Kybdu2OoGVjl41Fym00nlh6ftKR+DS93YqR5pSpBVqXQCgBQfWv//971MKrE2bNtUJrMacZ9RULxE25MEHH8w+z/PPP599b+m/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\n",
"text/plain": [
""
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.display import Image\n",
"Image(filename='exp_img/5000_1.png') "
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAFzCAYAAADi5Xe0AAAnSElEQVR42u3dC7hVZZ0/cBwGHpQpakb//kGSLngLmxp5vGTS/wmGmaIsu9A0jT5p/4rS6B9BZUQXSFNL0czUtHGyKS0u3sBEMEWkvHQ08QYFIXjhoNzlcoAD5/33W8w67bPP3vvsc9iHy+HzeZ73Oey9Fnutvda79vru933X2t0SAAA11c0mAAAQsAAABCwAAAELAAABCwBAwAIAELAAABCwAAAELAAAAQsAAAELAEDAAgAQsAAABCwAAAQsAAABCwBAwAIAQMACABCwAAAELAAABCwAAAELAEDAKrR69er05S9/Of3oRz9qNW3t2rXphRde2Gfe/B133JGta6ny85//vMW8O3bsSPfee2/63ve+l772ta+liy++ON1zzz1p586drV63s+Zty/PPP5+mTp2avdb48ePTN77xjXTNNdekRx99NDU1NbWYd+7cuSXf91e/+tX0rW99K/t/f/jDH1r8n9/+9rfZPLG+5WzZsiWb59JLL21zfX/2s59l877yyiuOxP1AqePXPgTYywFr6dKl2Un/4Ycf3mfe/HXXXVd1wLrpppuqmq8z5y0nwtNdd92VvvKVr5R9P7/4xS9ahKxyAau4RNgTsCh3/NqHAHs5YEUrSjy/LwWsiRMnpu9///ttzvfHP/4xW/fvfOc76U9/+lPavn17Wrx4cfY4no/pnT1vJbNnz87m//rXv57uvvvu9NJLL2WvtWHDhvTQQw9lLVIxfdasWa0C1rx581q8VrScbdq0KQtREdjOP//85pNnrQMW+4998fgFELD2wQ/oCBHVthTdeOON2bwRggpFGIrn41t8Z89bToSp6NaL7sVly5aVnCe6dSIoRQBraGioGLAK/dd//Vc2T+w7AUvAErAA9rGAVaobLIJBLk76M2fOTBdddFEWBC644IJ0yy23ZCGo0A033JC++93vppUrV2YtTxEYfvnLX3ZoPZcsWdJmWMhNmjQpawUqHscUj7/97W9npbPnLefXv/519j5iPFlb88W2evnll6sOWPlr5/N0dhfhT3/606xVMdYxwuc3v/nNLDheddVVaeHChSWXNWPGjKzexHxRN6I1r3D8Wlt1ptq6FyLAxjpHC2PMO2HChGzdHn/88Vb777777kuTJ0/OutXiffz4xz8u+R7a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q6MBK8T2j9sDXH755c13W48TYlzJVngz0M4MWPmJOO6NFS1+EexiG8aJvDBItlVnqq17sY7RqhnLivcb2zi6cKMe57f2yG9QGy0u0boVV1LGPoiWxQhojzzySKv9UO3y2xLvOb/XVKn62551KqXc8dsZAauW2wWgywQsAAABCwAAAQsAQMACABCwAAAQsAAABCwAAAELAAABCwBAwAIAELAAABCwAAAELAAAAQsAQMACAEDAAgAQsAAABCwAAAQsAAABC/aYm2++Of3zP/9zeuMb35hOPvnk9IMf/CA1NTVl09asWZP69evXXPr3759OOumkdN1112XTN2zYkD0f8xWK6WeddVZN1u+3v/1tOu6441o9/9nPfjY99dRT2foOHjw4velNb0ojR45My5Yts707oK3tmG/v3KRJk9IPf/hD9Xs3lduO+fb+3e9+l975zndm6/+FL3whbdu2reTrTJgwocV7efe73+3DDQEL9pbLL788nXbaaenBBx9MDQ0N2Qf6u971rnTxxRe3OAHFiSbs3LkzPfnkk+nYY49N8+bN6/QT0JYtW9Kpp56ajjnmmBbPx3rEet57771pyJAhWRjYvHlz+ta3vpXe//73297tdN9991Xcjvn2jmCydevWbHqsx74esPbl+l1pO+bbO/bFW97yljRz5sy0du3a9IlPfCJdcsklJV/vox/9aLr//vt9qCFgwd62fv36NGDAgLRw4cIWz//hD3/IvimXOgHlPvaxj2Unmc4OWHEC+spXvtIqYBWuY6GXXnopHXnkkc0tFLZ3xxRvx8J1HD16dPrMZz6T/d2XA9a+vr0rbcd8HefMmZPe9773NT8f7+XEE08s+Xpve9vbsv0GAha009ALN7WrtGXWrFnp7W9/e8V5ik9A27dvz7osjj766FRXV9fuE1B8Ey/sxsjLbbfd1mreRx99NL3nPe9Jzz77bKuAdcUVV6Rp06a1+j933nln+td//deabO+Xh53YrrK/b+9K27Fwe+cn8fHjx9c0YDXe87ftKvv79q60HfPtHcv54he/2Px8dA/G60XLVqFYzwjEEQwHDhyYPvjBD6YlS5b40ETAgr0RsH75y1+m9773vVWdgPLyute9LhvH0tljVOJEMmzYsPT000+n5557rlXA+tCHPpRefvnlFs+98MIL2Rii6NrZFwPWvry929qOpbb3vh6w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6NoBa9y4cemiiy6yZwAAAWt3NTQ0pDFjxqRu3boJWACAgFULZ555Zho5cmT2V8ACAASsGqivr8/+nnfeeQIWACBg1VI1AWvFihWprq5OURRFURSlZiXyxQEdsAAAtGAJWACAgCVgAQAIWAAAAhYAgIAFAICABQAgYAEACFgAAAhYAAACFgCAgAUAIGABACBgAQAIWAAAAhYAAAIWAICABQAgYAEAIGABAAhYAAACFgAAAhYAgIAFACBgAQAIWAAACFgAAAIWAICABQCAgAUAIGABAAhYAAAIWAAAAhYAgIAFAICABQAgYAEACFhp7ty56dhjj00HH3xwOuuss9LWrVtLzjd69OjUrVu35jJ48GB7BwAQsIpt3rw5HXbYYWnatGlp9erV6fTTT08TJkwoOe+wYcPS7Nmz7REAQMCqZMaMGemUU05pfvzEE0+kI488suS8ffv2TfX19fYIACBgVTJ58uR09tlnNz+O7sHo/tu0aVOL+datW5d69OiRhg8fnnr37p2GDBmSFi1aZO8AAAJWsUmTJqVzzz23xXMHHXRQWrVqVYvn5s+fn/r06ZP93bBhQxo7dmwaNGhQampqsocAAAGr0KWXXprOOeec5sd5C9aWLVsq/r/GxsbUs2fPtGzZspLTV6xYkerq6hRFURRFUWpWIl/sFwHrjjvuSKeddlrz4wULFqT+/fu3+f8igHXv3j2tXLlSBAYAtGAVirFWhx56aJoyZUrzVYTjxo1rNd/UqVPTwIEDswC2fv36NGrUqDR06FB7BwAQsEp54IEH0vHHH58NXh85cmR264bc4Ycfnl1pGCZOnJj69euXevXqlUaMGFHTZjoAgC4VsAAABCwAAAQsAAABCwBAwAIAQMACABCwAAAELAAABCwAAAELAEDAAgAQsAAAELAAAAQsAAABCwAAAQsAQMACABCwAAAQsAAABCwAAAELAAABCwBAwAIAELAAAAQsAAAELAAAAQsAQMACAEDAAgAQsAAABCwAAAQsAAABCwBAwKqBuXPnpmOPPTYdfPDB6ayzzkpbt261dwAAAaujNm/enA477LA0bdq0tHr16nT66aenCRMm2DsAgIDVUTNmzEinnHJK8+MnnngiHXnkkfYOACBgddTkyZPT2Wef3fw4uge7deuWNm3aZA8BAAJWR0yaNCmde+65LZ476KCD0qpVq+whAEDA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\n",
"text/plain": [
""
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Image(filename='exp_img/5000_2.png') "
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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CwBAD5aABQAIWAIWAMC+ELAAAAQsAAAELAAAAQsAQMACABCwAAAQsAAABCwAAAELAAABCwBAwAIAELAAABCwAAAELAAAAQsAAAELAEDAAgAQsAAABCwAAAQsAAABCwBAwAIAQMACABCwAAAELAAABCwAAAELAEDAAgBAwAIAELAAAAQsAAABCwAAAQsAQMACABCwAAAQsAAABCwAAAELAAABCwBAwAIAELAAAAQsAAAELAAAAQsAQMBqPzNmzEh9+vRJhxxySDr77LPTtm3bKi43bNiw1KNHj0Lp16+f2gEABKxymzdvTkcccUSaOHFiWr16dTr99NPTyJEjKy47cODANG3aNDUCAAhY1UyZMiX179+/8POzzz6bjjnmmIrLHnXUUWn58uVqBAAQsKoZN25cOueccwo/x/BgDP9t2rSpZLl169alnj17pkGDBqVDDz00nXbaaWnBggVqBwAQsMqNHj06nX/++SWPHXDAAWnVqlUlj82ePTsddthh2b8bNmxIF154YTrhhBNSY2OjGgIABKxiY8eOTUOHDi38nPdgbdmypervNTQ0pIMOOigtWbKk4vPLli1Lc+fOVRRFURRFabcS+aJbBKzJkyenU089tfDzvHnz0tFHH93i70UAO/DAA9OKFStEYABAD1axmGt1+OGHp/HjxxeuIhwxYkST5SZMmJB69+6dBbD169en8847Lw0YMEDtAAACViWzZs1Kffv2zSavDxkyJLt1Q+7II4/MrjQMo0aNSr169UoHH3xwGjx4cLt20wEA7FMBCwBAwAIAQMACABCwAAAELAAABCwAAAELAEDAAgBAwAIAELAAAAQsAAABCwAAAQsAQMACABCwAAAQsAAABCwAAAELAAABCwBAwAIAELAAABCwAAAELAAAAQsAQMACAEDAAgAQsAAABCwAAAQsAAABCwBAwAIAQMACABCwAAAELAAABCwAAAELAEDAAgAQsAAAELAAAAQsAAABCwAAAQsAQMACABCwAADo+gFrxowZqU+fPumQQw5JZ599dtq2bZvaAQAErNbavHlzOuKII9LEiRPT6tWr0+mnn55GjhypdgAAAau1pkyZkvr371/4+dlnn03HHHOM2gEABKzWGjduXDrnnHMKP8fwYI8ePdKmTZvUEAAgYLXG6NGj0/nnn1/y2AEHHJBWrVqlhgAAAas1xo4dm4YOHVr4Oe/B2rJlS8Xlr7/++tSvXz9FURRFUZR2K5Ev9qmANXny5HTqqacWfp43b146+uijxV8AQA9Wa8Vcq8MPPzyNHz++cBXhiBEj1A4AIGC1xaxZs1Lfvn3ToYcemoYMGZLdugEAQMACAEDAAgAQsAAABCwAAAGLDnTTTTelE088Mfuj1u985zvT5ZdfnhobG7Pn4saqcf+vvLzlLW9Jxx57bLr66quz59etW5c9Xn4D1nj+Ix/5SLu8vwcffDD94R/+oYqyX2Quu+yy7JYpb33rW9OAAQPSwoULVZZjRSau9B4zZoyK2g/3gRkzZqQ+ffpk7+vss8/O7l1ZybBhw0reY9xnSsCiQ8Qd64877rj08MMPZzdRffrpp9MJJ5yQLrnkkpIvTHwxwq5du9JTTz2V3v72t6fp06d3+EEzrtzs3bt3Ouyww1SW/SLdd9996fjjj89CVdxKZfjw4emUU05RYfv5sWLr1q3ZvhDrF7D2v30g2okjjjgiTZw4sXBbpZEjR1ZcduDAgWnatGl6sOhYa9euTQcddFD2h6yLPfbYY9kZQKUvTG7QoEHZ32vs6ID1pS99KZ177rkClv2iouXLl6eePXsWzqDZP/eJs846K7uVTvwrYO1/+8CUKVNS//79Cz/HezzmmGMqLnvUUUdlxw0Biw515513pne9611Vlyn/wmzfvj3rin3b296WHnnkkbq/MBMmTCjpns3L7bff3mTZOXPmpL/9279NL7/8soBlv6ho0qRJ6aSTTlJp+/k+kTeYF1xwgYC1H+4DEd7OOeecws/5n7aLXu5i8fpxQhaBL+5vedppp6UFCxYIWLS/m2++Ob3//e+v6QuTlwMPPDAbd48dOt9hO+KMJL4gf/VXf5WeeeaZtHjxYgHLftHE0qVLs7lYMfSAfULA2n/3gRi6PP/880seO+CAA5q8zuzZs7O2JP7dsGFDuvDCC7Mhzv2pB1zA6iRTp07Ndv5KVq5cWfGMpFyMw8fz5V2u3/nOd9IZZ5zR6vd26aWXFsbQBSz7RblFixal97znPem2225TYfYJAWs/3wfGjh2bhg4dWnKCHq8Tr1dNQ0NDNuy5ZMkSAYv2tX79+vT7v//7TcbUn3jiiaz7dOPGjS1+YUJMYHz88cebHOjiao1ytXb5xkTmSsvtz2Pn9os95s+fn020jQM+9gkByz4wefLkdOqppxZ+njdvXta73ZIIYNHLtmLFCgGL9he9RHFpa3SZxs4WExbj57z3qJYvzGc/+9nscvnoaYqrOeJKrz/6oz/KrjRpD3qw7Be5NWvWZJd9z5w5UyXZJwQs+0Am5lodfvjhafz48YWrCOOWHZUCW1yVHgEsAuN5552XvZf9iYDViWLsObpnY7glzk5iEuNVV13V5L4m1b4wceYSV/r9yZ/8SbaOGNO+9dZb2+09Clj2i1xcDl7pjDZeC8cKAWv/3QdmzZqV+vbtm/WmxRWlEd5yRx55ZHalYRg1alTq1atXOvjgg9PgwYPTsmXLBCwAAAQsAAABCwBAwAIAQMACABCwAAAELAAABCwAAAELAEDAAgAQsAAAELAAAAQsAAABCwAAAQsAQMACABCwAAAQsAAABCwAAAELAAABCwBAwAIAELAAarFjx47U2NhoQwACFkBbHHvssemhhx5Ku3btSn/5l3+Ztm7d2imvO2zYsNSjR49C6devX+G5GTNmpD59+qRDDjkknX322Wnbtm1tfg4QsAA6PWA1NDRkQaezAtbAgQPTtGnTmjy+efPmdMQRR6SJEyem1atXp9NPPz2NHDmyTc8BAhbAXglY73//+wu9ScuXL0/Tp09P733ve9Mf/MEfpDPPPDOtXbs2W37OnDnpfe97X+rfv396xzvekbZs2dKq1z3qqKOy1yk3ZcqUbN25Z599Nh1zzDFteg4QsAD2SsAq7sF6/fXX09ve9rZ0xx13pPXr12fDeZ/85CcLASuWmzp1alq5cmXJuiZMmFAy7JeX22+/vWS5devWpZ49e6ZBgwalQw89NJ122mlpwYIF2XPjxo1L55xzTmHZGOaLdWzatKnVzwECFsBeD1jXXnttNsSWi+G3CETbt2/PAtYf//Eft+k1Z8+enQ477LDs3w0bNqQLL7wwnXDCCdkE+9GjR6fzzz+/ZPkDDjggrVq1qtXPAQIWwF4PWBdffHHFnqiXX345C1gxGb49xWsfdNBBacmSJWns2LFp6NChhefynqgYimztc4CABbDXA9Y111yTXYVXSQSs6G2qpNYhwnIRgg488MC0YsWKNHny5HTqqacWnps3b146+uijs/+39jlAwALYKwErvOUtb0lLly5NixYtyobw7r333mwI78Ybb8zCStwnq1rAqlUEsd69e2chKOZ4nXfeeWnAgAHZczFn6vDDD0/jx48vXA04YsSINj0HCFgAey1gxUT2GKp74YUX0j333JP69u2b3VPq5JNPTnPnzs2WaY+AFUaNGpV69eqVDj744DR48OC0bNmywnOzZs3KXjsmwA8ZMiSbA9bW5wABCwAAAQsAQMACABCwAAAQsAAABCwAAAELAAABCwBAwAIAELAAABCwAAAELAAAAQsAQMACAEDAAgAQsAAABCwAAAQsAAABCwBAwAIAQMACABCwAAC6p/8PUna0/E1hinMAAAAASUVORK5CYII=\n",
"text/plain": [
""
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Image(filename='exp_img/5000_3.png') "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Iter = 500"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"| Exp | Mob 1.0 | Mob 0.75 |Mob 0.5 |Mob 0.25 |\n",
"|----------|:-------------:|:-------------:|:-------------:|:-------------:|\n",
"| CPU = 4 | 0.02051069524 | 0.01426426142 | 0.008495391378 | 0.004515069188 |\n",
"| CPU = 2 | 0.02922545242 | 0.01855015136 | 0.01072093597 | 0.005292796298 |\n",
"| CPU = 1 | 0.05900840658 | 0.03781192797 | 0.02081295101 | 0.01101532969 |\n",
"| CPU = 0.5 | 0.1174173313 | 0.07161711947 | 0.04177759899 | 0.02059194843 |"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAFzCAYAAADi5Xe0AAAhcElEQVR42u3dDZBVdf34cYgfBDIN1UAMRFCJQANlQQ9EUBMMU0PRg7/oZzNQkCYF0biCT0ApRGK6reMvy3RG/+UoJQ89sNgPwQyBUTEUwRJKQvABkKcFWXbRhf38+56Ze2cXlmV32YVl9/WaOYP37mF3OR/vPe8999yzbQIAgEbVxiYAABBYAAACCwBAYAEAILAAAAQWAIDAAgBAYAEACCwAAIEFAIDAAgAQWAAAAgsAQGABACCwAAAEFgCAwAIAQGABAAgsAACBBQCAwAIAEFgAAAKrqn379sXVV18dv/jFL0762IEDB+KVV145bzfW0qVLs39bTcv9999fbd1jx47Fo48+Grfccktcf/31cfPNN8cjjzwSx48fP+nz1mddAEBg5W3bti1mzpwZ69atO2831t13313nwFqwYEGd1qvvugCAwMp7+umns/vP58CaM2dO3Hrrradd75///Gf2b/3xj38c//rXv+LNN9+MF154Ibud7k8fb8i6AIDAalGBVVpaWuejSr/5zW+ydVMwVZXCKd1/3333NWhdAEBg5e+r6SWw1157Lf/x8vLyWLZsWcyfPz+uu+66mDdvXvz+97/Poqaqe++9N2666abYvXt3diRp1qxZ8bvf/e6sbKitW7dm33c6V+p05s6dGzfccENUVlZWuz/dvvHGG7OlIesCAAKrToFVVlYWhYWFNZ6DlILr8OHD1QIrxVfV9R966KFav5+0/qnOm8otf/rTn07771q7dm227urVq+PXv/51Fnc/+tGPsiNMe/bsqRaLab1f/vKXNX6eO++8M/t4+nfXZ10AQGDV+SXCxYsXZ/enCEtHpioqKmL//v1Z9KT7H3jggWqBle772c9+lq2T3mV3unfaNVZg5b7Pa6+99qS//8Mf/jBeffXVbL2SkpJaX9rLvSSY3lFZn3UBAIFVp8BKlydI7ywsKio66SWyJL1rLwXNG2+8US2wNm7ceNY31B133JF9L+nyCSmM0snoL7/8ctx1113Z95T+DUk6MndiGFaV7s8dwavPugCAwKpTYO3cufO0R5fSsmPHjmqBlY5eNRcptNJ5Yen7Skfg0vdWl6NSKdLqsy4AILDqFFj//ve/6xRYW7ZsqRZY9TknqbFeIqzNgw8+mH2e559/Pvve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\n",
"text/plain": [
""
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Image(filename='exp_img/500_1.png') "
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAFzCAYAAADi5Xe0AAAuPElEQVR42u3dCXxU1f33cSx/eKE8lvZffHhACloRULCLvNzF/yOU1uJaW1proWKr4oaPCFqkVAtFUYuAS12wtWotKIsLQYHgwhIFMS4gCApCWEzYk0BWsvyefE9yhpvJzGQmGSAkn/frdV6QmZuZm3vPnfu955x7ppkBAAAgqZqxCQAAAAhYAAAABCwAAAACFgAAAAhYAAAABCwAAAACFgAAAAhYAAAABCwAAAACFgAAAAhYAAAABCwAAAACFgAAAAELAAAABCwAAAACFgAAAAELAAAABCwAAAACFgAAAAELAAAABCwAAAACFgAAAAEraNeuXXbnnXfa3//+9xrP7dmzx7Zu3dpg/vjZs2e7dY1UXnzxxWrLlpaW2jvvvGMPPfSQ3X333fbAAw/YW2+9ZWVlZTVeN5Fla7NlyxabMWOGe61Ro0bZn//8Z3vyySfto48+svLy8mrLLly4MOLf8sc//tHuvfde93uffPJJtd9577333DJa32gKCgrcMhMmTKh1fV944QW37N69ezm6jgCRjkn2IQAcQQFrw4YNLiB88MEHDeaPnzJlStwBa+rUqXEtl+iy0Sg8zZ071+66666o6/if//ynWsiKFrDCi8IeAQvRjkn2IQAcQQFLLS56vCEFrDFjxtjf/va3Wpf74osv3Lr/9a9/tS+//NL2799v69atcz/rcT1fl2VjSU1Ndcv/6U9/svnz59v27dvda+Xm5tqyZctci5SenzdvXo2AtXjx4mqvpZazvLw8F6IU2EaOHBk6eSY7YOHI0RCPSQAgYB3hAUuBI95Wpeeff94tq8AUpOCkx3XFX5dlo1GYUreeuhczMjIiLqNuHQUlBbDCwsKYASvoX//6l1tG+4OARcAiYAHAERywInWZKUR4Cghz5syx8ePHu9Awbtw4e+WVV1wICnr22Wft/vvvt23btrmWJ4WLl156qU7ruX79+lqDhTd27FjXYhQ+5kk//+Uvf3GlLstG8+abb7p10xix2pbT379jx464A5Z/bb/Mwe4ifO6551xLodZR4fOee+5xwfHxxx+3NWvWRHyvlJQUVxe0nPa3WvOC49dqqwfx1idRgNU6q4VRy44ePdqt26efflpj/7377rs2ceJE162mv+Ppp5+O+Dck8v7hXn311agtnZs2bXLP6bUSXadwsY7JSPtQf0NJSYmrk6rDeq/p06e7VlVZtGiR3XfffW6MoLreg8d3MrYLABCwEgxYOqHqxB1prJA+iPft21ftxKoP5eDyb7zxhntOj9c29uj1118PvVZaWlooaOgEopO0Tho6ufjA4k8KWu6JJ56I+Pdq4Lie19+RyLKxTJ482S2nE2oi4glYftyZDxCHImDp5O+7NINF3ZXBAdY60eqGgEj7bubMmXHVg0Tq0+eff+5aCqPVF7U6ehoPF2kZ/Q3BQJPI+0fiQ5TCSzjVXz3nWzXjXadkBSy19kbaLwsWLKjxuPajbvZI1nYBAAJWgl2E+oDW4/rAV4uErpJ3794dOploIHfwxKrHHn74YbeMWjV8y0aiAcu/b6QTrK7Cv/76a7dcdnZ2zK493yWou7ESWTYWtRBoOW2LZAQsbSNtL7Ue+HFdPuQdioClnxWwPv74YxdC9ff/85//rNYaE9wnjz76qAsRxcXFLnD40KU7KmurB4nUJwVZhb/PPvvMrZcCgZb1rzFt2jS3nB5Xa5ru5MzMzHSvWVRU5P4e1Z9//OMfdarP0Tz44IMu7AcDilqr1MqmMJLoOkUT7ZiMtA8V2rQPV69e7VqttH90nGgd9G96errbhgppWv9gEEzWdgEAAlacAUsnCZ3g1MUR3qXmW1t0stCJNnhiXbFiRb3XU91Aem3dUadgpJOGTuDqYtF7aJ1EJ4xYJwA97q/+E1k2FnWfqCQq3rsINUjeO1QBK/wkvnPnTvf4M888EwqBChXqogu/e82PX9Ng/1j1INH6FI1aU/T6CoGi5X1Li0JDtOk2kvX+/gYHtbB5X331VbWbGuJdp2QFLP38/vvvV1vu5Zdfdo+r2zlSPfRTgiRruwAAASvOgKUr73gCge8q8ydWXfkeLApaGt+j99GVtt4rnlYphbRElo3F33Hox7fUN2AprOk1NcA9fPC9Tppa5u233476uvn5+aEWo7oGLNWLILVg6PGnnnrK/ey3nQ9csUSrB4nWJ0+BV2FNIVOtVn7/B9dF6+l/X2FBoUDhNDh+qK7vH+0Y8i1oMmvWrBrhPJ51SmbACr8w8F2UatUK+vDDD93j+jeZ2wUACFhxBix/VV5bWbt2bbUTa6QxTIl2Ecbir8zVguBbbzR+KhI/rkrdI4ksG4u2XTwnHLUCaaxNIncRRjvJhrdCBKlLL9rksfEGrPCTvlpcgttKrYf6OZ6bFqLVg0TrkwKD39aRSjBg6e/xXWXBZRRs1D1Wl/eP5bHHHnNdbwqi2lbqNlaXZlA865TMgBU+TkqtaXp848aNMQNWMrcLABCw4ghY/qQa7wSchypg+a48P8hZd8GphPN3BurOQS+RZaNRt2U8dxH6cS0aZFzXgOXn7fr3v/9d6zI60R6sgKUbCxJtwQqvB4nUJ72/ute0vIKL6oZufFBgzcrKiroufh4yjSXSXXNaTuOQtC6J1udYfNftqlWrQttfd+pFEmudkhmwwvdhvAErmdsFAAhYcQQsdYGp+0qDZ+Pp1ogVsBLh58DS4OnwMSHqDlMQ0pgQ/V98117wrjLxY4P0vJfIstGo68uPw4rWiqVxNzqJaj1991tdApa2pVpA1FoS7U4utSrFO6VFXQOWfvZfBRS+rAKE/k5/go5WDxKpT9p+eg11q4XXAY0d0nMajxeLfk8hzA/AT7Q+x6K6p9fS3YT6qiTtI22H2oSvU0MIWMncLgBAwAoLWP6kpVaZ4IBcf7v4I4884roS9GGsE6emEfDz7fgusGQFLPFdQzppq/XE3xml9QjvqtIYEz2mVjLNn6VuG/3rW82CXRuJLBuLph3w3T1qodI66rW0fTX3kYKInte8SV5dApb4yUc1iN13OWpgsrrQ/Pc1KszVdvdjfQKW+K5Z7RsfWDR+x+8rP6g9Vj2Itz75Viq1+GzevNn9vTk5Oa71UH+rntONEKLn/Xgx/Z72Q3CGfoUHH8YTqc/xXFCoxVMtogqCQYmsUzTRjslkB6xkbxcAIGAFBMdhaJoAP2BWrSa+qybSfD7BsSTJDFg6cfuQEl40eWX4ycmfZCJ9H2C4RJaNRic8HziiFb1PcCqHugYsBado+8BPZRHPmJ76BizVBT/APNLfGk89iLc+6f19mI5Wgt25PoRGKhrsnej7x0Phw/9uMKwkuk7RRDsmD0bASuZ2AQACVoC6LnQnlEKNSnAiRH1w6ypaJ1ddeevEplvk1fITfkWfrIDl11VX1moh0Pvq/TWLeKQraQUZTROgZfz8Q7rzLjhXUV2WrY3G3+gEp+5MP+eQWi0031F411ZdA5Zom2p6gEmTJoVmW9cJUXeyBScDPZgBy5+INTeWWvwU7LQNdSIPBsna6kG89UnrqJZKvZf+Xm1jdeGqbvrpOvyks2pxUeuW7qTUPlDLogLa8uXLa+yHeN+/Nvqb/VxTkepkIusUSbRj8mAErGRuFwBokgELAACAgAUAAAACFgAAAAELAACAgAUAAAACFgAAAAELAACAgAUAAAACFgAAAAELAACAgAUAAAACFgAAAAELAACAgAUAAEDAAgAAAAELAACAgAUAAEDAAgAAAAELAACAgAUcMtOmTbMf//jH9r3vfc/OOusse+SRR6y8vNw9t3v3buvQoUOodOzY0c4880ybMmWKez43N9c9ruWC9PygQYOSsn7vvfeenXLKKTUev/HGG23VqlVufXv16mUnnXSSDRgwwDIyMtjedVDbdvTb2xs7dqw99thj1O96irYd/fZ+//337YILLnDrf9ttt1lxcXHE1xk9enS1v+Wiiy7iww0ELOBwmTRpkp1//vm2dOlSKywsdB/oF154oT3wwAPVTkA60UhZWZl99tln1r17d1u8ePFBPwEVFBTYueeea926dav2uNZD6/nOO+9Y7969XRjIz8+3e++91y677DK2d4LefffdmNvRb28Fk6KiIve81qOhB6yGXL9jbUe/vbUvTjvtNJszZ47t2bPHrrnmGnvwwQcjvt6vfvUrW7RoER9qIGABh1tOTo517tzZ1qxZU+3xTz75xF0pRzoBeVdddZU7yRzsgKUT0F133VUjYAXXMWj79u3WqVOnUAsF27tuwrdjcB2HDh1qN9xwg/u3IQeshr69Y21Hv44LFiywSy65JPS4/pYzzjgj4uv98Ic/dPsNIGABCepzX15CpTbz5s2zc845J+Yy4Seg/fv3uy6Lrl27Wnp6esInIF2JB7sxfHnttddqLPvRRx/Zz372M9u4cWONgDV58mSbOXNmjd9544037Kc//WlStveOvmckVI707R1rOwa3tz+Jjxo1KqkBq+St/0qoHOnbO9Z29Ntb73P77beHHlf3oF5PLVtBWk8FYgXDLl262M9//nNbv349H5ogYAGHI2C99NJLdvHFF8d1AvLlu9/9rhvHcrDHqOhE0rdvX1u9erVt3ry5RsC68sorbceOHdUe27p1qxtDpK6dhhiwGvL2rm07RtreDT1gHSnbO9J29NtbXZx33313teeOP/74GuuzfPlyd4zo371799qYMWNCXboAAQs4xAHrrbfecieTSHbu3BnxCj+cxrXo+fCuiSeffNKuu+66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\n",
"text/plain": [
""
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Image(filename='exp_img/500_2.png') "
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Image(filename='exp_img/500_3.png') "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Caveat - "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"An important factor to note about these results is that the code used reaches a threshold at CPU=4 where despite availablability of further resources, The average CPU utilization is throttled at (270% , 300%) range. Hence the code cannot scale beyond more computation resources. This is reflected in the results as well, where CPU = 4 becomes an outlier/"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3-final"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: Examples/profiler_examples/issues.md
================================================
# Known issues
+ Remove any dangling Dockerfiles in the profiler directory before executing Profiler as they might interfere.
+ Once Docker has started running, if you hit Ctrl+Z, you will only exit from the Profiler script. Docker will continue to run in the background until completion unless you stop the Docker process separately.
================================================
FILE: Examples/profiler_examples/mnist/mnist.py
================================================
""" Convolutional Neural Network.
Build and train a convolutional neural network with TensorFlow.
This example is using the MNIST database of handwritten digits
(http://yann.lecun.com/exdb/mnist/)
This example is using TensorFlow layers API, see 'convolutional_network_raw'
example for a raw implementation with variables.
Author: Aymeric Damien
Project: https://github.com/aymericdamien/TensorFlow-Examples/
"""
from __future__ import division, print_function, absolute_import
# Import MNIST data
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("/mnist_data", one_hot=False)
import tensorflow as tf
# Training Parameters
learning_rate = 0.001
num_steps = 2000
batch_size = 128
# Network Parameters
num_input = 784 # MNIST data input (img shape: 28*28)
num_classes = 10 # MNIST total classes (0-9 digits)
dropout = 0.25 # Dropout, probability to drop a unit
# Create the neural network
def conv_net(x_dict, n_classes, dropout, reuse, is_training):
# Define a scope for reusing the variables
with tf.variable_scope('ConvNet', reuse=reuse):
# TF Estimator input is a dict, in case of multiple inputs
x = x_dict['images']
# MNIST data input is a 1-D vector of 784 features (28*28 pixels)
# Reshape to match picture format [Height x Width x Channel]
# Tensor input become 4-D: [Batch Size, Height, Width, Channel]
x = tf.reshape(x, shape=[-1, 28, 28, 1])
# Convolution Layer with 32 filters and a kernel size of 5
conv1 = tf.layers.conv2d(x, 32, 5, activation=tf.nn.relu)
# Max Pooling (down-sampling) with strides of 2 and kernel size of 2
conv1 = tf.layers.max_pooling2d(conv1, 2, 2)
# Convolution Layer with 64 filters and a kernel size of 3
conv2 = tf.layers.conv2d(conv1, 64, 3, activation=tf.nn.relu)
# Max Pooling (down-sampling) with strides of 2 and kernel size of 2
conv2 = tf.layers.max_pooling2d(conv2, 2, 2)
# Flatten the data to a 1-D vector for the fully connected layer
fc1 = tf.contrib.layers.flatten(conv2)
# Fully connected layer (in tf contrib folder for now)
fc1 = tf.layers.dense(fc1, 1024)
# Apply Dropout (if is_training is False, dropout is not applied)
fc1 = tf.layers.dropout(fc1, rate=dropout, training=is_training)
# Output layer, class prediction
out = tf.layers.dense(fc1, n_classes)
return out
# Define the model function (following TF Estimator Template)
def model_fn(features, labels, mode):
# Build the neural network
# Because Dropout have different behavior at training and prediction time, we
# need to create 2 distinct computation graphs that still share the same weights.
logits_train = conv_net(features, num_classes, dropout, reuse=False,
is_training=True)
logits_test = conv_net(features, num_classes, dropout, reuse=True,
is_training=False)
# Predictions
pred_classes = tf.argmax(logits_test, axis=1)
pred_probas = tf.nn.softmax(logits_test)
# If prediction mode, early return
if mode == tf.estimator.ModeKeys.PREDICT:
return tf.estimator.EstimatorSpec(mode, predictions=pred_classes)
# Define loss and optimizer
loss_op = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(
logits=logits_train, labels=tf.cast(labels, dtype=tf.int32)))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
train_op = optimizer.minimize(loss_op,
global_step=tf.train.get_global_step())
# Evaluate the accuracy of the model
acc_op = tf.metrics.accuracy(labels=labels, predictions=pred_classes)
# TF Estimators requires to return a EstimatorSpec, that specify
# the different ops for training, evaluating, ...
estim_specs = tf.estimator.EstimatorSpec(
mode=mode,
predictions=pred_classes,
loss=loss_op,
train_op=train_op,
eval_metric_ops={'accuracy': acc_op})
return estim_specs
# Build the Estimator
model = tf.estimator.Estimator(model_fn)
# Define the input function for training
input_fn = tf.estimator.inputs.numpy_input_fn(
x={'images': mnist.train.images}, y=mnist.train.labels,
batch_size=batch_size, num_epochs=None, shuffle=True)
# Train the Model
model.train(input_fn, steps=num_steps)
# Evaluate the Model
# Define the input function for evaluating
input_fn = tf.estimator.inputs.numpy_input_fn(
x={'images': mnist.test.images}, y=mnist.test.labels,
batch_size=batch_size, shuffle=False)
# Use the Estimator 'evaluate' method
e = model.evaluate(input_fn)
print("Testing Accuracy:", e['accuracy'])
================================================
FILE: Examples/profiler_examples/model_names.txt
================================================
mobilenet_v1_0.75_224.tflite
mobilenet_v1_1.0_224.tflite
================================================
FILE: Examples/quad_equation_min/QUAD_MIN_Demo.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Minimum of quadratic function Demo"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this tutorial we will show how to find the minimum of a quadratic function using Auptimizer. We also demonstrate how to effectively switch between various HPO proposers such as Spearmint and BOHB."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Running the Experiments "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this experiment we try to find the best X such that f(X) is minimal.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Each instance of 'quad_min.py' would accept an X that calculates the value of the quadratic function. The ‘quad_min_[proposer].json’ file contains code to perform HPO using CPU resources and consider 50 samples for each proposer. We can easily switch between proposers and see how they choose different configurations to optimize the final performance."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To run the experiment use command- \n",
"\n",
"'python3 -m aup quad_min_[proposer].json' \n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Analysis "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We next analyze the experiment results using the sqlite database for our experiments. The details for the experiment can be found inn the jobs profile, but here we aim to compare the proposers. Experiment 1 is performed using BOHB, then random and finally Spearmint."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"'''\n",
"Connect to the Auptimizer database\n",
"''' \n",
"\n",
"import sqlite3\n",
"import time\n",
"import datetime\n",
"import random\n",
"\n",
"conn = sqlite3.connect('sqlite3.db')\n",
"c = conn.cursor()\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"'''\n",
"Function to compile and return details from a Auptimizer experiment. \n",
"Takes experiment id as input, returns score, jobs ran for the experiments, and end time for each job.\n",
"''' \n",
"\n",
"def graph_data(ex, cursor):\n",
" cursor.execute('SELECT score, jid, end_time FROM job WHERE typeof(score)==\\'real\\' and eid = '+ str(ex))\n",
" data = cursor.fetchall()\n",
"\n",
" jobs = []\n",
" score = []\n",
" times =[]\n",
" \n",
" for row in data:\n",
" times.append(row[2])\n",
" jobs.append(row[1])\n",
" score.append(row[0])\n",
"\n",
" times = [a-min(times) for a in times] \n",
" jobs = [a-min(jobs) for a in jobs]\n",
" \n",
" print(score)\n",
" print(jobs)\n",
" \n",
" return (score, times, jobs)\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[6148.106139221136, 10438.637919012073, 7415.6298730541, 6060.406530867955, 3664.920102436913, 8514.390146411502, 3909.7273413323915, 16227.975850616904, 18949.333977751823, 3002.7529813288706, 12791.65578998259, 5710.14811693708, 6586.3076663701, 17482.08784937461, 106.05097215471763, 157.98208630594334, 11.44000290631149, 14146.904978499952, 12357.080808454182, 15445.805059455917, 19541.970126709333, 13032.9272883446, 4347.975242825175, 12432.524670292085, 288.5003098979952, 8357.696833979042, 422.36446655402744, 18209.8318006542, 5559.088337974499, 3511.112188373727, 1431.0292860232905, 12232.83011505206, 4248.207908109023, 6595.336180527122, 10.303060762137134, 7785.924535077077, 7646.9372673592525, 7768.255367710197, 18174.35420915232, 9487.559731339637, 2639.9754755905624, 3899.819338147345, 9932.534803401542, 77.10024584741284, 9073.377353360214, 9179.004614490392, 906.1092905253714, 342.22184461100574, 2032.997899250835, 2701.989279003208, 6636.2871314115855, 3927.859009848557, 19933.46377839, 215.54895165905884, 893.2290434271752, 533.9755569510351, 8705.582990730807, 1312.0236859821218, 4439.427923863416, 1221.9944362568633, 518.6856445554187, 251.65086979919593, 8791.639549216163, 392.6665611417196, 791.5276376934777, 2776.9202466736824, 13754.657682272258, 195.46347563258027, 14328.398330600858, 191.5105114029914, 19455.857917184265, 4484.071823536638, 19467.877481863343, 7466.92897242087, 11153.010073705345, 34.43100582618657, 1634.8474324227357, 297.8520463070646, 1792.335336437659, 290.69212600328666, 2066.013595105786, 3504.3653315921824, 87.0522160740486, 9786.218811810271, 6552.901115759534, 1440.0451339462006, 5588.933496367393, 183.1439686948838, 6770.736489897746, 17622.0922085697, 2073.62099265998, 9090.897825009191, 357.4965426541574, 10471.869381877272, 1711.8875991265356, 687.7722504762877, 7021.3354179159505, 11.34880226594397, 14022.162352737285, 3.5498130271587858, 9376.498124625585, 1490.493642592927, 11030.590123862217, 18891.41025923904, 1265.537535038463, 6775.692304218736, 7154.279454332183, 6684.18871558561, 1018.4139931903604, 18522.62131186837, 4081.8903679691225, 14619.248688302143, 9985.2121771267, 1808.0392280530514, 13514.669117373822, 3210.637205107219, 15842.046702461934, 6896.490066536726, 15864.782780740976, 9787.899266421542, 10734.424903633586, 5130.655824921306, 18652.484890178817, 8464.286331511501, 3668.3825908448157, 7505.175120324333, 10.615665529087309, 1858.6082438067806, 8894.885290153567, 1719.826661417118, 7795.502523945654, 3753.8567029765054, 377.5424368139047, 1818.313802637847, 6630.89370838516, 7126.0629089348, 6732.689324891997, 8708.048088287565, 8678.819442186163, 3800.3583189517426, 16402.1418816525, 2759.445461044005, 3879.035911233086, 16233.447387479662, 13263.35435616637, 10111.45733426073, 208.04684124025985, 17251.935746920182, 10410.989214492674, 20358.080378288647, 458.77450320056, 15378.921455929238, 541.7934658303482, 7733.6948016532115, 315.8909810941599, 14674.544558677915, 13300.387110585678, 6610.811025411567, 3385.7156422877497, 100.70466265645877, 9926.773443429818, 4199.811088150739, 10639.974218597716, 15317.215264171993, 19418.50430156459, 14945.51176006914, 5.8995577127201795, 2646.8769892878286, 10875.044685497587, 604.076534276305, 5541.817516386234, 63.33929155979352, 819.1516406209234, 10.099046262581181, 12855.46321611145, 1026.102509359792, 2436.4013161142993, 17576.054370302154, 10126.56525189996, 23.781863082338973, 556.4872180953723, 7883.074702833368, 6800.900393126976, 1157.7102730040097, 17809.063707527817, 7693.719488126548, 5856.484798397378, 7102.869103694476, 10878.960987263978, 1988.4120955376934, 3238.4494536360403, 901.489808070223, 710.3931786214773, 18198.403582086852, 11161.675591300796, 4910.797895877393, 1058.2386603574305, 1323.052678739327, 71.8013571039346, 3853.320648659735]\n",
"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199]\n",
"[3.1, 3.1, 5103.6, 11479.225, 1278.225, 20402.609597487746, 255.96139678955078, 3.1000760033726693, 14.541851735115051, 118.45974311828613, 91.58624259978532, 130.55955605059862, 19.182313656806947, 91.25851605683565, 93.73128578662872, 81.09230492860078, 67.8494772747159, 78.19406831860542, 90.44185981750488, 90.60488705188035, 92.24351976662874, 93.89735315591096, 101.00944475680589, 96.91255895644426, 94.89694951325654, 85.93881199359893, 78.11853996701538, 79.8653064802289, 83.57548712044954, 90.27898458987474, 100.83699310421943, 91.42230332493781, 96.40660450458526, 84.35946190953254, 85.147236867249, 100.7508242804557, 103.52707547731697, 92.90322904139757, 94.64653740115463, 88.01469211131334, 83.41914818286895, 87.05340145379304, 73.72963020801544, 90.11626136898994, 99.46285212635993, 61.423164105415346, 79.10337266921997, 107.31894074231386, 97.08151445388793, 57.62550358325243]\n",
"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]\n",
"[18979.21737542377, 10497.40426337924, 9084.777253670089, 1626.7190672055335, 14096.376058669908, 268.50144260897343, 18979.21737542377, 9877.485035005397, 1556.5394610585156, 271.0992430993217, 9819.855781400875, 19716.036917848396, 9877.485035005397, 3207.843110329192, 19551.05251353619, 19689.349388015635, 19742.153301964954, 19677.51246064784, 19716.036917848396, 19633.343953874253, 19713.289439959462, 19712.24227687632, 19725.254604338556, 9295.909023220589, 19713.289439959462, 17317.693515832267, 19731.12717887784, 19733.216794325952, 19737.628596809598, 7446.474946617792, 19731.12717887784, 2476.1773148555417, 8345.886366596742, 19739.3868119695, 19740.456360036816, 19742.194761087318, 8345.886366596742, 19738.781705770016, 19741.95688885989, 19746.456647336945, 2713.676016023622, 1309.4209515403686, 19742.194761087318, 19744.750109136494, 19745.66982152892, 19744.34078633421, 19741.194753359396, 8832.95573481031, 19745.66982152892, 9127.999213721223, 7251.815216566596, 19741.060733514478, 1590.6745786114336, 19747.68389984932, 9127.999213721223, 19742.6247419077, 19744.476725744116, 246.40151623307847, 3669.688925237476, 19745.476732496467, 19747.68389984932, 13713.17457638431, 19744.68561151229, 19742.783368158573, 19745.921504493457, 19747.592473779277, 19745.476732496467, 19742.386375027792, 19747.328560705166, 3579.6167810146503, 19746.203188789626, 19747.82926204643, 19747.592473779277, 19746.297771278998, 19743.895074749948, 19744.35998019022, 19745.803931349496, 877.765253830673, 19747.82926204643, 19751.47664617664, 19746.031270812986, 19752.979148038638, 19746.946310211188, 19742.570869463092, 19751.47664617664, 19748.36504786568, 19748.431933352636, 17770.905960331205, 11995.819917720033, 19747.829082206645, 19748.431933352636, 19749.075901273456, 19466.617572533705, 19752.381577633278, 4969.7059505406505, 17986.83995564123, 19749.075901273456, 19746.896069134582, 19752.08201039945, 19738.11134102032, 498.84938563626804, 19749.19249969937, 19752.08201039945, 19747.896118769637, 13601.619408698383, 19743.260052504305, 5135.726070979244, 3133.919968644151, 19749.19249969937, 19753.853767755834, 19750.87022180548, 7298.115437640935, 19751.536408207703, 19752.884480408073, 19753.853767755834, 19751.542936109065, 19743.659117157807, 19760.302930084134, 19751.59945855852, 155.5773758426209, 19752.884480408073, 19755.991748653338, 19752.848989854927, 19755.563943464425, 19756.570167465317, 19755.165796254398, 19755.991748653338, 650.7690257847578, 19754.41195073914, 19754.28124965953, 19748.34824238597, 18766.323660913975, 19755.165796254398, 19755.60056741995, 19754.622690785753, 367.3642336334456, 19755.873657926197, 19756.508036857296, 19755.60056741995, 896.5164168797209, 19754.35601348591, 19752.144031455326, 19758.934314172016, 19757.074107499466, 19756.508036857296, 19754.32508982097, 832.1434461672089, 19755.20497131739, 19759.584197574448, 1538.0075030758635, 19757.074107499466, 19761.133662230972, 19753.245506417017, 13092.52239813087, 19755.795496009916, 19761.03190742165, 19761.133662230972, 2833.7605702102355, 17897.963529724206, 12634.212612740075, 2124.1020040114136, 19768.180504463475, 19761.03190742165, 7370.455989808017, 19.68243774657819, 19761.025772048826, 19756.31579370775, 19758.785769706912, 19768.180504463475, 19754.827785827918, 8326.354828157624, 15510.591799173293, 19762.62750619028, 3454.7163394186455, 19758.785769706912, 19758.84516555402, 19757.229152422216, 15161.613822921197, 19760.613475738526, 11572.67413015717, 19758.84516555402, 4596.1173031221815, 19758.406050973317, 19765.02777169259, 19766.450367628546, 19757.71886826186, 19758.406050973317, 947.87754489118, 19759.648783668217, 6099.361165615458, 19763.894327864342, 19765.454229586278, 19759.648783668217, 7173.26535664318, 6593.733650972089, 16764.745687285093, 19761.162153941772, 19761.21269354197, 19765.454229586278, 19760.80146521984, 19764.942853018514, 19769.653476055715, 19764.458245166665, 19763.86148253791, 19764.942853018514, 19780.862477780596, 17509.968569680586, 1188.7126078128838, 2948.7390915310903, 19767.273057036124, 19780.862477780596, 19770.83449841099, 19762.24796241168, 19763.014907286077, 19774.493144593405, 19767.817147789738, 19770.83449841099, 19763.606051752115, 19766.647667721933, 19769.9669115918, 19765.245253123256, 19772.13609427558, 19767.817147789738, 19766.82965303794, 2021.0541706174527, 19767.018690237168, 19768.163866661707, 343.13581503560255, 19772.13609427558, 15497.754482203469, 2259.588169542944, 19768.520159473534, 3842.342404806207, 19770.515286477763, 15497.754482203469, 19771.618744039686, 840.6255719899882, 19772.29882048401, 3934.568916780458, 19771.779838303763, 19771.618744039686, 19769.272677170917, 19769.576530850543, 19774.06668734663, 19767.990555865745, 19770.71086662648, 19771.779838303763, 19769.298004300243, 20289.809991203307, 19774.009803237353, 19768.58680843855, 19769.898960254384, 20289.809991203307, 332.94766286057086, 13180.818897143856, 19770.658513369137, 19770.759098203314, 19772.181931828116, 19769.898960254384, 4.609652930357215, 19768.017935004573, 19775.780817380066, 19769.57671490288, 4071.1011003611598, 19772.181931828116, 19773.04814379857, 19770.31847903407, 19771.740975815937, 98.7419864464947, 19768.273187442115, 19773.04814379857, 19783.255165380335, 301.34496071138784, 19770.150298601307, 19778.30606851176, 19769.072046836565, 19783.255165380335, 19769.9782000595, 19768.143609604238, 13592.892915751581, 19774.67576404146, 5043.185009066357, 19769.9782000595, 19772.504137017688, 19777.070430468226, 19782.794211123328, 19776.787553892347, 1566.1128553495048, 19777.070430468226, 3962.414046441464, 19774.87037405038, 19776.070188033074, 3759.439600325705, 19775.58958600117, 19774.87037405038, 19773.81023731156, 19777.767151405817, 377.6584150002128, 19778.81655239048]\n",
"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298]\n"
]
}
],
"source": [
"'''\n",
"Compile results for grid search, random, spearmint, hyperband, hyperopt Proposers\n",
"''' \n",
"\n",
"s_ran, t_ran, j_ran = graph_data(2, c)\n",
"s_spe, t_spe, j_spe = graph_data(3, c)\n",
"s_bohb, t_bohb, j_bohb = graph_data(1, c)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Visualization"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can easily visualize the performance of the hyperparameter optimization algorithms for our current experiment and how configurations can affect the final performance. In the plots below we compare job performance and time with accuracy achieved. "
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"'''\n",
"Plots for the experiment results\n",
"'''\n",
"\n",
"import matplotlib.pyplot as plt\n",
"\n",
"plt.figure(figsize=(20,15))\n",
"\n",
"\n",
"plt.plot(j_bohb, s_bohb)\n",
"plt.plot(j_ran, s_ran)\n",
"plt.plot(j_spe, s_spe)\n",
"\n",
"plt.legend(['y = bohb','y = random', 'y = spearmint'], loc='upper left')\n",
"\n",
"plt.title('Hyperparameter Optimization using various proposers on Quad min')\n",
"plt.xlabel('Jobs')\n",
"plt.ylabel('Accuracy')\n",
"\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"'''\n",
"Plots for the experiment results\n",
"'''\n",
"\n",
"\n",
"import matplotlib.pyplot as plt\n",
"\n",
"plt.figure(figsize=(20,15))\n",
"\n",
"\n",
"plt.plot(t_bohb, s_bohb)\n",
"plt.plot(t_ran, s_ran)\n",
"plt.plot(t_spe, s_spe)\n",
"\n",
"plt.legend(['y = bohb','y = random', 'y = spearmint'], loc='upper left')\n",
"\n",
"plt.title('Hyperparameter Optimization using various proposers on Quad min')\n",
"plt.xlabel('Time')\n",
"plt.ylabel('AMC score (less is better)')\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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H/nIR+a2/9rXvpZjjjkma9asySmnnLLRvl72spfl4IMPzrp163Ldddfd5bkBAAAA259VKz2BSfPq//OFXPy1G+/WfR50/91z2tMPXvL+k046Kccdd1xe+tKXZmZmJmeddVbOO++822135JFH5qabbrrd+Omnn54nPelJtxu/4oorct555+Wyyy7L0UcfnS9/+ct585vfnKrK5z73uVxyySV58pOfnC996UtJkosuuigXXnhhdtpppxxwwAE5+eSTs+++++a73/1u1q5dmz/+4z/Oa17zmrz61a/Om970prtwRgAAAIDtkSBqAuy3335ZvXp1Lrzwwlx77bU57LDDsnr16tttd84559yh/T7rWc/KaDTKmjVr8uAHPziXXHJJPv7xj+fkk09Okjz0oQ/Nj/zIj8wFUevWrcsee+yRJDnooINy5ZVXZt99981oNMrxxx+fJHnOc56T44477q4cLgAAALCdEkQtsKnKpS3p+c9/ftavX59rrrkmJ5100qLb3NGKqKra5O8L7bTTTnO3p6amcuutty663eb2AwAAALAYa0RNiGc+85n5p3/6p3zmM5/JU57ylEW3Oeecc3LRRRfd7mexECpJzj777MzMzOSyyy7L5ZdfngMOOCBHHnlk3vnOdyZJvvSlL+WrX/1qDjjggE3ObWZmZu4KfGeeeWae8IQn3IUjBQAAALZXKqImxI477pijjz46e+65Z6ampu6WfT7wgQ/M4YcfnhtvvDFvectbsvPOO+fFL35xXvSiF+WQQw7JqlWrsn79+o0qoRaz66675rzzzstrX/va3Oc+98m73vWuu2V+AAAAwPalWmsrPYetau3ate3888/faOyLX/xiDjzwwBWa0djMzEwe+chH5uyzz86aNWtWdC6TbBJeKwAAAGBjVXVBa23t5rbTmjcBLr744uy///5Zt26dEAoAAADYZmnNmwAHHXRQLr/88pWeBgAAAMAWpSIKAAAAgK1CEAUAAADAViGIAgAAAGCrEEQBAAAAsFUIoliW9evX5yUveclKTwMAAADomCBqO3Drrbeu9BQAAAAABFGT4NRTT80b3vCGud9f+cpX5owzzrhL+zzxxBPzwhe+MEcccUROOeWUnHfeeXnsYx+bww47LI973OPyH//xH0nGlU7HHXdcjjnmmKxZsyannHLK3D7+8i//Mg95yENy+OGH59xzz50bv+KKK/LjP/7jefjDH55169blq1/96txzvuhFL8pjHvOYPPjBD85HP/rRnHTSSTnwwANz4okn3qXjAQAAAPq3aqUnMHE++BvJNZ+7e/f5Xw5Jnvr7S9590kkn5bjjjstLX/rSzMzM5Kyzzsp55513u+2OPPLI3HTTTbcbP/300/OkJz3pduMbNmzIJz7xiUxNTeXGG2/MOeeck1WrVuXDH/5wfuu3fivvfe97kyQXXXRRLrzwwuy000454IADcvLJJ2fVqlU57bTTcsEFF2SPPfbI0UcfncMOOyxJcvLJJ+eEE07ICSeckLe97W35tV/7tbzvfe9Lknzzm9/MJz/5ybz//e/PM57xjJx77rn58z//8zz60Y/ORRddlEMPPfROnUIAAACgf4KoCbDffvtl9erVufDCC3PttdfmsMMOy+rVq2+33TnnnHOH9vtzP/dzmZqaSpJ8+9vfzgknnJBLL700VZVbbrllbrt169Zljz32SJIcdNBBufLKK/ONb3wjRx11VPbee+8kyfHHH58vfelLSZJPfvKT+du//dskyS/90i9tVEX19Kc/PVWVQw45JPe9731zyCGHJEkOPvjgXHHFFYIoAAAA2I4JohbaROXSlvT85z8/69evzzXXXJOTTjpp0W3uaEXUrrvuOnf7d37nd3L00Ufn7/7u73LFFVfkqKOOmrtvp512mrs9NTV1l9aUmt3XaDTaaL+j0chaVQAAALCdE0RNiGc+85k59dRTc8stt+TMM89cdJs7WhE137e//e084AEPSDJeF2pzjjjiiPz6r/96rr/++uy+++45++yz84hHPCJJ8rjHPS5nnXVWfumXfinvfOc7c+SRR97peQEAAADbD0HUhNhxxx1z9NFHZ88995xrp7s7nXLKKTnhhBPy2te+Nk972tM2u/397ne/vOpVr8pjH/vY7Lnnnhu11P3Jn/xJnve85+UP//APs/fee+cv//Iv7/b5AgAAANueaq2t9By2qrVr17bzzz9/o7EvfvGLOfDAA1doRmMzMzN55CMfmbPPPjtr1qxZ0blMskl4rQAAAICNVdUFrbW1m9tutDUmw6ZdfPHF2X///bNu3TohFAAAALDN0po3AQ466KBcfvnlKz0NAAAAgC1KRVSHpmdmcuP3b8kt0zMrPRUAAACAZRNEdegHt87kiuu/m+//cHqlpwIAAACwbIKoDtXw7/a1zDwAAADQO0FUl0RRAAAAQH8EUR2qIYdqHeZQj3vc4za7zRve8IZ873vf2wqzAQAAALYmQRR32fT08teq+sQnPrHZbQRRAAAAsG0SRE2AU089NW94wxvmfn/lK1+ZM8444y7t8+yzz87DHvawPOIRj8gTn/jEJMn69etz7LHH5qijjsqaNWvy6le/em77d7zjHTn88MNz6KGH5ld/9VfnwqUXvehFWbt2bQ4++OCcdtppc9vvt99+ecUrXpFHPvKROfvss3PUUUflZS97WdauXZsDDzwwn/nMZ3LcccdlzZo1+e3f/u25x93znvdMknz0ox/NUUcdlZ/92Z/NQx/60PziL/5iWmt54xvfmK997Ws5+uijc/TRR9+lcwAAAABMllUrPYFJ8wfn/UEuueGSu3WfD93roXnF4a9Y8v6TTjopxx13XF760pdmZmYmZ511Vs4777zbbXfkkUfmpptuSmvJD26dzg5To0yNKqeffnqe9KQnbbTta17zmvzzP/9zHvCAB+Rb3/rW3Ph5552Xz3/+87nHPe6RRz/60Xna056WXXfdNe9617ty7rnnZocddsiLX/zivPOd78xzn/vcvO51r8tee+2V6enprFu3Lp/97Gfz8Ic/PEmyevXq/Pu//3uS5C1veUt23HHHnH/++TnjjDNy7LHH5oILLshee+2VH/3RH83LXvayrF69eqM5XnjhhfnCF76Q+9///nn84x+fc889N7/2a7+WP/qjP8pHPvKR3Pve977T5xwAAACYPIKoCbDffvtl9erVufDCC3PttdfmsMMOu11okyTnnHNOkuSHt07nkmtuyj73ukf22nXHRff5+Mc/PieeeGKe9axn5bjjjpsb/4mf+Im5fR933HH5+Mc/nlWrVuWCCy7Iox/96CTJ97///dznPvdJkrz73e/OW9/61tx66625+uqrc/HFF88FUccff/xGz/mMZzwjSXLIIYfk4IMPzv3ud78kyYMf/OBcddVVtzumww8/PPvss0+S5NBDD80VV1yRJzzhCXfgzAEAAAA9EUQtsKnKpS3p+c9/ftavX59rrrkmJ5100qLb3JGKqLe85S359Kc/nQ984AN51KMelQsuuCBJUrMrnQ+qKq21nHDCCXn961+/0X1f+cpXcvrpp+czn/lM7nWve+XEE0/MzTffPHf/rrvuutH2O+20U5JkNBrN3Z79/dZbb73d8czfZmpqatFtAAAAgG2HIGpCPPOZz8ypp56aW265JWeeeeai29xWETWTS665MQ+41y5ZvetOi2572WWX5YgjjsgRRxyRD37wg7nqqquSJB/60Idyww03ZJdddsn73ve+vO1tb8s97nGPHHvssXnZy16W+9znPrnhhhty00035cYbb8yuu+6aPfbYI9dee20++MEP5qijjtoixz/fbrvtlptuuklrHgAAAGxjBFETYscdd8zRRx+dPffcM1NTU5vcdq6oqS29zctf/vJceumlaa1l3bp1ecQjHpGLLroohx9+eH7mZ34mGzZsyHOe85ysXbs2SfLa1742T37ykzMzM5Mddtghb37zm/OYxzwmhx12WB760Idm3333zeMf//i76Wg37QUveEGOOeaY3P/+989HPvKRrfKcAAAAwJZXrW0izdgGrV27tp1//vkbjX3xi1/MgQceuEIzGpuZmZm7At2aNWs2ue0t0zP54tU35gF77pLV91y8Imox69evz/nnn583velNd3W6K2YSXisAAABgY1V1QWtt7ea2G22NybBpF198cfbff/+sW7dusyFUkiyjIAoAAABg4mjNmwAHHXRQLr/88jv+wDuYRJ144ok58cQT7/jzAAAAANwNVEQNempRnF0jqp8Z3z16eo0AAACA2xNEJdl5551z/fXXdxR0bH/Nea21XH/99dl5551XeioAAADAnaQ1L8k+++yTDRs25LrrrlvpqSxLay3Xfuvm3LzLqnxj5x1Wejpbzc4775x99tlnpacBAAAA3EmCqCQ77LBDHvSgB630NJbtB7dO5yd/+5/y8qcckP969P4rPR0AAACAZdGa16GpYZGomZntpzUPAAAA6J8gqkOjIYia7mZNKwAAAABBVJdGIxVRAAAAQH8EUZ0aVSKHAgAAAHoiiOrU1Ki05gEAAABdEUR1alSVGUEUAAAA0BFBVKdGVdaIAgAAALoiiOrU1KgyPbPSswAAAABYPkFUp8aLlauIAgAAAPohiOrUaGSNKAAAAKAvgqhOTVVl2hpRAAAAQEcEUZ0aV0St9CwAAAAAlk8Q1alRxVXzAAAAgK4Iojo1VdaIAgAAAPoiiOpUVWVaEAUAAAB0RBDVqalRac0DAAAAuiKI6tSUxcoBAACAzgiiOlUVrXkAAABAVwRRnZoqrXkAAABA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\n",
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"'''\n",
"Plots for the experiment results\n",
"'''\n",
"\n",
"import sys\n",
"\n",
"def min_so_far(arr):\n",
" mmin=float('inf')\n",
" new_arr=[]\n",
" for _,x in enumerate(arr):\n",
" if x"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(20,15))\n",
"\n",
"plt.plot(t_bohb_median, min_so_far(s_bohb_median))\n",
"plt.plot(t_bohb_bandit, min_so_far(s_bohb_bandit))\n",
"plt.plot(t_bohb_trunc, min_so_far(s_bohb_trunc))\n",
"plt.plot(t_bohb, min_so_far(s_bohb))\n",
"\n",
"plt.legend(['y = bohb_median','y = bohb_bandit', 'y = bohb_trunc', 'y = bohb'], loc='lower right')\n",
"\n",
"plt.title('Hyperparameter Optimization using ES with various policies on Quad min')\n",
"plt.xlabel('Time')\n",
"plt.ylabel('Min res')\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[6060.406530867955, 3664.920102436913, 3909.7273413323915, 3002.7529813288706, 106.05097215471763, 157.98208630594334, 11.44000290631149, 288.5003098979952, 422.36446655402744, 10.303060762137134, 77.10024584741284, 215.54895165905884, 251.65086979919593, 195.46347563258027, 191.5105114029914, 34.43100582618657, 87.0522160740486, 183.1439686948838, 11.34880226594397, 3.5498130271587858, 10.615665529087309, 100.70466265645877, 5.8995577127201795, 63.33929155979352, 10.099046262581181, 23.781863082338973, 71.8013571039346]\n",
"[0, 1, 3, 6, 11, 12, 13, 21, 23, 31, 40, 50, 58, 64, 66, 72, 79, 84, 94, 96, 123, 156, 163, 168, 170, 176, 195]\n",
"[6060.406530867955, 3664.920102436913, 3002.7529813288706, 106.05097215471763, 157.98208630594334, 11.44000290631149, 10.303060762137134, 77.10024584741284, 34.43100582618657, 11.34880226594397, 3.5498130271587858, 10.615665529087309, 5.8995577127201795, 10.099046262581181]\n",
"[0, 1, 6, 11, 12, 13, 31, 40, 72, 94, 96, 123, 163, 170]\n",
"[6148.106139221136, 7415.6298730541, 6060.406530867955, 3664.920102436913, 3909.7273413323915, 3002.7529813288706, 106.05097215471763, 157.98208630594334, 11.44000290631149, 10.303060762137134, 11.34880226594397, 3.5498130271587858]\n",
"[0, 2, 3, 4, 6, 9, 14, 15, 16, 34, 97, 99]\n"
]
}
],
"source": [
"s_random_median, t_random_median, j_random_median = graph_data(2, c2)\n",
"s_random_trunc, t_random_trunc, j_random_trunc = graph_data(6, c2)\n",
"s_random_bandit, t_random_bandit, j_random_bandit = graph_data(1, c2)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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L7rookxMTOT666/PU5/61Pzcz/1cXvOa12TXrl2ZnJzM5Zdfni1btuSKK67IJz7xiTz44IP51re+lRe84AV5+9vfniS5/PLL8zu/8zs55phj8vjHPz6dTidJsn379lx88cX5wQ9+kI0bN+byyy/PySefnIsuuiiTk5O5/vrrc9ddd+UDH/hAPvShD+Waa67JueeemyuuuGLoedetW5dXvOIV+eQnP5njjz8+b33rW3PppZfmu9/9bn7v934vF1xwQaanp/P6178+n//857N79+688pWvzC//8i+nrutccskl+fSnP52TTjop7XZ77nPPO++8XHbZZTnnnHPyile8Itdee2127tyZF77whXnzm9+cJDnllFOydevW/MVf/EWmpqby0Y9+NKeffvqK/90PoqWOedrN3n+ku7tTgx+oOoaGAwAAsCoXX3xxPvShDyVJZmZmcuWVV+YXfuEXFjz3tKc9LWedddaCP5/5zGcGfu5tt92WL3/5y3nHO96R008/PVdffXWuv/76vOUtb8kb3vCGueduuOGGfPjDH85Xv/rVfPjDH873vve93HHHHXnTm96UL33pS/niF784ry3tkksuydatW3PjjTfm53/+5/PqV7967rX77rsv11xzTd75znfmggsuyOte97rcdNNN+epXv5obbrhh6L+DBx54IM94xjNy0003Zf369fmN3/iNfPrTn85VV12VN77xjUmS97///Tn66KNz7bXX5tprr80f/uEf5jvf+U6uuuqqfP3rX8+2bdvyoQ99KF/+8pcHfsdv//Zv57rrrsuNN96YL3zhC7nxxhvnXjvuuOPyD//wD3nFK16Ryy67bOg5V0qFE/N0+i11D+zZkxw14IHWpMAJAADgCLJYJdJaOeWUU7Jhw4Zcf/31ufPOO3P22Wdnw4YNC567+uqrl/W5L3rRi9JsNpMkO3bsyNatW3PLLbeklJKpqb2FFeeff36OPvroJMkZZ5yRW2+9NT/4wQ9y3nnnZePGjUmSF7/4xfnGN76RJLnmmmvy8Y9/PEny0pe+NJdeeuncZz33uc9NKSVnnnlmNm3alDPPPDNJ8tjHPjbbt2/PWWedNfCs7XY7z372s5MkZ555ZjqdTlqtVs4888xs3749SfJXf/VXufHGG+fmM+3YsSO33HJL/uZv/iYveclL0mw2c8IJJ+QZz3jGwO/4yEc+kve+973pdru54447sm3btjzucY9Lklx44YVJkp/6qZ+a+7uNksCJedr9wOnBqSFzmqqOLXUAAACs2ste9rJcccUV+f73v5+LL7544DNPe9rTcv/99y+4f9lll+WZz3zmgvtHHbW3cuK//Jf/kqc//em56qqrsn379px33nlzr822yiVJs9lMt9td8d9j9rMajca8z200Got+bqvVSillwXv3fV9d13nXu96VZz3rWfPe+8lPfvKA5/rOd76Tyy67LNdee22OPfbYXHTRRdm1a+//z89+32r//sNoqWOe2cBp59DAadKWOgAAAFbtBS94Qf7yL/8y11577YJAZdbVV1+dG264YcGfQWHT/nbs2JETTzwxSRadpTTr3HPPzRe+8IXcc889c3ONZj3lKU/JlVdemST54z/+4zztaU9bwt9w9Z71rGflPe95z1x11je+8Y088MAD+Zf/8l/mwx/+cKanp3PHHXfkc5/73IL3/uhHP8pRRx2Vo48+OnfeeWc+9alPHZQzz1LhxDyd/gynB6f2DH7AljoAAABGoN1u5+lPf3qOOeaYuTa4Ubr00kuzdevW/NZv/Vae85znHPD5448/Pr/5m7+ZJz/5yTnmmGPmtcK9613vyi/+4i/mv/23/zY3NPxgeNnLXpbt27fnCU94Quq6zsaNG/Nnf/ZnecELXpC//uu/zhlnnJGTTz45T37ykxe89/GPf3zOPvvsnH766TnppJPy1Kc+9aCceVap6/qgfuHBcM4559TXXXfdoT7GYeltX/hY/nj7m3PZUy7Ps049Z+EDn3tr8oXfTd70w6Rf+rcWbv/VX82um7+Wn/jUgcsEAQAAWJ6bb745j3nMYw7pGWZmZvKEJzwhH/3oR3Pqqace0rOw0KD/Rkopf1/X9YCwYCEtdcwzUfUqnIa31E30rqqcAAAAWKFt27bl0Y9+dM4//3xh0xFKSx3zzAVO3WEtdZO9a3dnr70OAAAAlumMM87It7/97UN9jIPi3HPPze7d84s2/uiP/mhum92RSuDEPLOB065hM5yq/sT9qV3J5EE6FAAAABym/u7v/u5QH+GQ0FLHPBOtfuDUnRr8QDVb4bRr8OsAAADA2BM4Mc9E1UqS7BraUjc7w0ngBAAAAAympY55HtLqtcx97rtX5/49D+Z1T/nZrO/s0zs3OzT8pquS2/++9/PEMcnpzxn91rojcIMiAAAAjAOBE/M88thNqetmvrPnM/nOdz+TJHnj039h7wMPPbF3/cLvzn/jy7+QnHDWyM5RbdqcPZ/8VL73qldl06WXpn3yySP7bAAAAGBtaaljnlOPOz6fev5n8r6nfzx13cy2H3x9/gPHPy75tW8lr/1q789Fn+zdv/OmkZ5j42tfk42ve10e+PI1+fZz/m3uesc7M/3jB0b6HQAAADDMFVdckVe96lVr9vnr1q1bk8+96KKL8rGPfSxJ8rKXvSzbtm1Lkrz1rW9dk+8bRoUTC5x0zHE56Zjj0prelNsf+M7CB446bu/PDz2x12Z317aRnqHR6eS4X355jn7+83L3O96Re9773uy46qps/E+/mqMvuCClISsFAAAYiU+9Pvn+V0f7mZvPTP7120b7mcvQ7XZTVSKP973vfXM/v/Wtb80b3vCGg/bd/q+doR7WPjk7Zr63+EONZrJxy8gDp1mtTZtywu/+bk658k9Sbd6cO17/n7P9JS/Jzn/8xzX5PgAAANbeG9/4xvze7/3e3D//+q//en7/939/VZ950UUX5Vd+5Vdy7rnn5tJLL81XvvKVPPnJT87ZZ5+dpzzlKfn613sdPFdccUUuvPDCPPvZz86pp56aSy+9dO4zLr/88px22mn56Z/+6XzpS1+au799+/Y84xnPyOMe97icf/75+e53vzv3na94xSvypCc9KY961KPy+c9/PhdffHEe85jH5KKLLjrgmV/3utflsY99bM4///zcfffdSZI//MM/zBOf+MQ8/vGPz8/+7M/mwQcfnPuuV7/61XnKU56SRz3qUXNVTHVd51WvelW2bNmSZz7zmbnrrrvmPv+8887Lddddl9e//vXZuXNnzjrrrPz8z//8qv49L5W4j6FOWf8TuWvH3+bOH/8wm9YdM/zBh5+RfPvza3qWybPOyikfvjI7/vwTuesd/2e2v/jncvTzn5+Nv/q6tB7+8DX9bgAAgCPaIahEuvjii3PhhRfmta99bWZmZnLllVfmK1/5yoLnnva0p+X+++9fcP+yyy7LM5/5zAX3b7vttnz5y19Os9nMj370o1x99dWpqiqf+cxn8oY3vCF/+qd/miS54YYbcv3116fT6WTLli255JJLUlVV3vSmN+Xv//7vc/TRR+fpT396zj777CTJJZdckq1bt2br1q35wAc+kFe/+tX5sz/7syTJfffdl2uuuSaf+MQncsEFF+RLX/pS3ve+9+WJT3xibrjhhpx11uB5xw888EDOOeecvPOd78xb3vKWvPnNb8673/3uXHjhhfkP/+E/JEl+4zd+I+9///tzySWXJEnuuOOOfPGLX8zXvva1XHDBBXnhC1+Yq666Kl//+tezbdu23HnnnTnjjDNy8cUXz/uut73tbXn3u9+dG264Yam/olUTODHUT27ckq/sSP7mO/+UF535vw1/8OGPSf7xT5IH700e8rA1O09pNHLMC56f9f/qX+We//f/yb1XfDD3/9VfZcOv/EoedtHWNNrtNftuAAAARueUU07Jhg0bcv311+fOO+/M2WefnQ0bNix47uqrr17W577oRS9Ks9lMkuzYsSNbt27NLbfcklJKpqam5p47//zzc/TRRydJzjjjjNx66635wQ9+kPPOOy8bN25Mkrz4xS/ON77xjSTJNddck49//ONJkpe+9KXzqqKe+9znppSSM888M5s2bcqZZ56ZJHnsYx+b7du3Dw2cGo1GXvziFydJ/v/27j446ure4/jnu5uQ8FBAKNAUVKRESDQh4UZDEtMhRK/0VihQH69iYsow7ZSnmd5JEctDn6y2TFsL3jvTQRJlsNGgKHaqt0Wtpkh5usktlKgIN1QsRB4DFKEJOfeP/WVJSBCQzf7y8H7N7OT3O7+zZ7+78zub3373nLMPPPCApk2bJknasWOHvve97+nYsWM6efKkbr/99vB9pkyZokAgoOTkZNXW1kqS3n77bd13330KBoP64he/qAkTJlzWa9ZemFKHC8q++kZJ0ta/V396xcHJob8H323niEKCfXpr8He+oxG/fUW9srJ08Oc/1547JunE66/LOReVGAAAAAAAV2bGjBkqLS1VSUlJqxE5TXJzc5WWltbqtn79+jbr9+7dO7y9cOFC5eXlaceOHXrllVd0+vTp8LG4uLjwdjAYVENDw2d+Hk1tBQKBFu0GAoHLatfMJIWmzi1fvlzbt2/X4sWLLxh3R//8S8IJF/QvQ0fINfbQ+0d3fXrFwUmhv+20jtOF9Lj2Wl395HJd/dQKWY9Y7fv2LH34jRk6s+si8QIAAAAAfDd16lS99tpr2rJlS4tRPM1VVFSoqqqq1a2t6XTnq6ur09ChQyWF1m26mMzMTL311ls6fPiw6uvrVV5eHj6WnZ2tsrIySdLq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\n",
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(20,15))\n",
"\n",
"plt.plot(t_random_median, min_so_far(s_random_median))\n",
"plt.plot(t_random_bandit, min_so_far(s_random_bandit))\n",
"plt.plot(t_random_trunc, min_so_far(s_random_trunc))\n",
"plt.plot(t_ran, min_so_far(s_ran))\n",
"\n",
"plt.legend(['y = random_median','y = random_bandit', 'y = random_trunc', 'y = random'], loc='lower right')\n",
"\n",
"plt.title('Hyperparameter Optimization using ES with various policies on Quad min')\n",
"plt.xlabel('Time')\n",
"plt.ylabel('Min res')\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[3.1, 3.1]\n",
"[0, 1]\n",
"[6148.106139221136, 7415.6298730541, 6060.406530867955, 3664.920102436913, 3909.7273413323915, 3002.7529813288706, 106.05097215471763, 157.98208630594334, 11.44000290631149, 10.303060762137134, 11.34880226594397, 3.5498130271587858]\n",
"[0, 2, 3, 4, 6, 9, 14, 15, 16, 34, 97, 99]\n",
"[11588.561807897513, 10264.661252507878, 13259.660327218779, 9756.147263836081, 192.30551743308826, 54.01004445189671, 10.657509615206326, 3.542402417237454]\n",
"[0, 1, 2, 3, 4, 13, 66, 81]\n"
]
}
],
"source": [
"s_spearmint_median, t_spearmint_median, j_spearmint_median = graph_data(9, c2)\n",
"s_spearmint_trunc, t_spearmint_trunc, j_spearmint_trunc = graph_data(1, c2)\n",
"s_spearmint_bandit, t_spearmint_bandit, j_spearmint_bandit = graph_data(8, c2)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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Z/d7zooVK3T06NETnlNRUZGtJXVTX1+vdevWSZKWLFmipqYmSdI999yTk8/rC3fNgzSkRlrysvTiP0ubHpA++j/SokelQdVJX4KteQAAAAAwwL1wi7Tv99m95hcmSX97X3avOYBEIhH5/f6kzn3zzTf7PWfFihVavHixysrKMl1aRn72s58lnt9zzz364Q9/mLfPZiIKMVap9PUV0mWPSi3vSKvOkf74UtJvT2zNa2ciCgAAAAAQc/vtt2vFihWJn2+99VY9+OCDGV1z7dq1OuOMMzRlyhSdd955kqTVq1drwYIFmjNnjsaOHas777wzcf4TTzyhmTNnaurUqbrhhhsUiUQkSTfeeKNqa2s1ceJE3XHHHYnza2pq9IMf/EDTp0/X2rVrNWfOHN18882qra3V+PHj1dDQoIULF2rs2LG67bbbEu+LTzK99tprmjNnjhYtWqRx48bp6quvluu6euihh/Txxx9r7ty5mjt37gm/480336yJEyeqrq5O+/fvlyT99Kc/1YwZMzRlyhRddtllicmq+vp6LV26VGeffbZOPfXUxNST67r6p3/6J51++um64IIL9MknnySuP2fOHDU2NuqWW25RW1ubpk6dqquvvjrt/yapYCIK3U1aJI2aKq2tl/7jcunspVLd7ZLfOuHbgnZQhgwddpiIAgAAAIABqQCTS9ddd50WLlyom266SdFoVE899ZQ2b97c47xzzz1Xhw/3/Pfk8uXLdcEFF3Q7tmzZMr300kuqqqrSwYMHE8c3b96sd955R2VlZZoxY4YuueQSlZeXa82aNXrjjTdkWZa+9a1v6cknn9Q111yju+++W0OHDlUkElFdXZ22b9+uyZMnS5IqKyv19ttvS5JWrVol27bV2NioBx98UAsWLNCWLVs0dOhQnXbaabr55ptVWVnZbY1bt27Vu+++q9GjR2v27Nl64403tHTpUv34xz/Whg0bNGzYsD5/Z0eOHFFtba0eeOABLVu2THfeeadWrlyphQsX6h/+4R8kSbfddpseffRRfec735Ek7d27V5s2bdJ7772n+fPna9GiRXrmmWf0hz/8QU1NTWppadGECRN03XXXdfus++67TytXrtS2bdv6XE+2EUShp2Ffjm3Ve+mfpTcfkv7839Kin0uDx/T5Fp/hU4VVoVf+/Ir2H42ltYZh6O/+r7/ThMoJWV/i0a3b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t9+WJK1atUq2bauxsVEPPvigFixYoC1btmjo0KE67bTTdPPNN6uysrLbGrdu3ap3331Xo0eP1uzZs/XGG29o6dKl+vGPf6wNGzb0ORF13333aeXKldq2bZskadeuXdq5c6d+8Ytf6Kyzzur39713715t2rRJ7733nubPn69FixbphRde0K9+9Su99dZbKisr04EDB/q9Ti4RRAEAAAAAgJyoqalRZWWltm7dqpaWFk2bNq1HaCNJGzduTPqas2fPVn19vS6//HItXLgwcfzCCy9MXHvhwoXatGmTTNPUli1bNGPGDElSW1ubRowYIUl6+umn9cgjjygcDmvv3r1qampKBFFXXHFFt8+cP3++JGnSpEmaOHGiRo0aJUk69dRTtXv37h7faebMmaqurpYkTZ06Vbt27dI555yT9Hfs6otf/GJSIZQkfeMb35DP59OECRPU0tIiSXr55Zf193//9yorK5MkDR06NK11ZAtBFAAAAAAAyJklS5Zo9erV2rdvn6677rpez0llImrVqlV666239Pzzz+vMM8/Uli1bJEmGYXQ7zzAMua6ra6+9Vvfee2+31z788EMtX75cDQ0NGjJkiOrr63Xs2LHE6+Xl5d3ODwQCkiSfz5d4Hv85HA73WHfXc/x+f6/nJOv4tXT9nl3XfPznDtROcDqiAAAAAABAzlx66aV68cUX1dDQoIsvvrjXczZu3Kht27b1+HN8CCVJ77//vmbNmqVly5Zp+PDh2r17tyRp/fr1OnDggNra2vTss89q9uzZqqur07p16/TJJ59Ikg4cOKCPPvpIhw4dUnl5uQYNGqSWlha98MILufsFdBEMBnsN3LqyLEuO4/T5+siRI7Vjxw5Fo1E988wz/X7mhRdeqH//939P3K2PrXkAAAAAAKBo2batuXPnavDgwVm5A933vvc97dy5U67rqq6uTlOmTNG2bds0c+ZMXXbZZWpubtbixYtVWxu7gdtdd92liy66SNFoVJZl6eGHH9ZZZ52ladOmady4cRozZoxmz56d8bqScf3112vevHkaPXq0NmzY0Oc5kydP1vTp03X33Xf3eP2+++7T1772NQ0fPly1tbW93mGvq3nz5mnbtm2qra2Vbdv66le/qnvuuScr3ycdxkAd1cqV2tpat7GxsdDLAAAAAAAg53bs2KHx48cXdA3RaDRxB7qxY8fm5DNWr16txsZGrVy5MifXR3e9/b0yDGOL67q1/b2XrXkAAAAAACAnmpqa9OUvf1l1dXU5C6FwcmFrHgAAAAAAyIkJEybogw8+yPnn1NfXq76+Puefk02zZs1Se3t7t2OPP/64Jk2aVKAV5QdBFPD/t3f3MVaV+QHHvz+GgRGw6Mw4u8LQ4gsNYBRGXutgAqKGrhupSGSbpTJlDak2FUir2WqCYmBpE+K7lpitYV20CrvF3dQQJS5WxBSYEQoI26joBpC3woIg+ALz9I854IzQFcV7LsP9fpKbe57nPPfe352cH5z7u+d5riRJkiRJOVu5cmWxQygKp+ZJkiRJkiQpFxaiJEmSJEmSlAsLUZIkSZIkScqFhShJkiRJkqTTdNVVV33lmIcffphDhw7lEM2Zy0KUJEmSJEnSSRw9evSUx7755ptfOcZClL+aJ0mSJElSSdjxk5/w6abffqvP2blfX757zz3/7/4ZM2ZQWVnJtGnTALj33nupqalh6tSp3/g1Fy1axMyZMykrK6N79+68/vrrzJ8/n8WLF7N//362bdvGxIkTue+++wBYsGABjz76KJ999hnDhg3jySefpKysjNtvv53Vq1dz+PBhxo8fz8yZMwHo3bs3EyZMYOnSpdx9993MmzePuro6li9fzscff8wzzzzDnDlzWL9+PRMmTGDWrFkAdOvWjYMHD/Laa69x//33U11dzYYNGxg0aBALFizgscce48MPP2TUqFFUV1ezbNmyb/w3aM8sREmSJEmSpIKYPHky48aNY9q0aTQ3N/P888+zatWqE8ZdffXVHDhw4IT+uXPncu2117bpe+CBB3j55Zfp2bMn+/btO96/atUqNmzYQJcuXRgyZAg33HADXbt25YUXXmDFihWUl5dzxx138Oyzz3Lrrbcye/ZsKisrOXr0KKNHj2bdunVcccUVAFRVVfHWW28BMG/ePDp16kRjYyOPPPIIY8eOpampicrKSi655BKmT59OVVVVmxjXrFnD22+/TY8ePaivr2fFihXceeedPPjggyxbtozq6urT/tu2VxaiJEmSJEkqAX/oyqVC6d27N1VVVaxZs4adO3dSV1d3QtEGYPny5af8nPX19TQ0NHDLLbcwbty44/3XXXfd8eceN24cb7zxBh07dqSpqYkhQ4YAcPjwYWpqagBYuHAhTz31FEeOHGH79u1s3LjxeCFq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\n",
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(20,15))\n",
"\n",
"plt.plot(t_spearmint_median, min_so_far(s_spearmint_median))\n",
"plt.plot(t_spearmint_bandit, min_so_far(s_spearmint_bandit))\n",
"plt.plot(t_spearmint_trunc, min_so_far(s_spearmint_trunc))\n",
"plt.plot(t_ran, min_so_far(s_ran))\n",
"\n",
"plt.legend(['y = spearmint_median','y = spearmint_bandit', 'y = spearmint_trunc', 'y = spearmint'], loc='lower right')\n",
"\n",
"plt.title('Hyperparameter Optimization using ES with various policies on Quad min')\n",
"plt.xlabel('Time')\n",
"plt.ylabel('Min res')\n",
"\n",
"plt.show()\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: Examples/quad_equation_min/cpu.ini
================================================
[Auptimizer]
# Auptimizer environment folder to be created, this file will be copied over
Auptimizer_PATH=./.aup
# Temp folder
TMP_FOLDER=./aup_tmp
# SQL engine
SQL_ENGINE=sqlite
================================================
FILE: Examples/quad_equation_min/quad_min.py
================================================
#!/usr/bin/env python
"""
Modified Rosenbrock function for HPO and aup
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import sys
""" # ver 1.0 - modify existing code
from aup import BasicConfig, print_result
def rosenbrock(conf, a=1, b=100):
x = conf.x
y = conf.y
return (a-x)*(a-x) + b*(y-x*x)*(y-x*x)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("config file required")
exit(1)
config = BasicConfig().load(sys.argv[1])
val = rosenbrock(config)
print_result(val)
"""
from aup import aup_args
from time import sleep
@aup_args
def rosenbrock(x, a=2, b=4, c=5):
global it
it = 1.0
res = None
for _ in range(0, 10):
sleep(1)
res = x*x*a + x*b + c
res += it
it -= 1.0 / 10
return res
if __name__ == "__main__":
if len(sys.argv) != 2:
print("config file required")
exit(1)
rosenbrock(sys.argv[1])
================================================
FILE: Examples/quad_equation_min/quad_min_BOHB.json
================================================
{
"name": "./quad_equation_min/quad_min_BOHB.json",
"proposer": "bohb",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_iterations": 100,
"num_samples": 64,
"random_fraction": 0.3333333333333333,
"bandwidth_factor": 3,
"min_bandwidth": 0.001,
"eta": 3,
"min_budget": 1,
"max_budget": 5,
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/quad_equation_min/quad_min_random.json
================================================
{
"name": "./quad_equation_min/quad_min_random.json",
"proposer": "random",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_samples": 200,
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/quad_equation_min/quad_min_spearmint.json
================================================
{
"name": "./quad_equation_min/quad_min_spearmint.json",
"proposer": "spearmint",
"script": "quad_min.py",
"resource": "cpu",
"n_parallel": 4,
"target":"min",
"n_samples": 50,
"engine":"GPEIChooser",
"parameter_config": [
{
"name": "x",
"range": [-1.0, 100.0],
"type": "float"
}
]
}
================================================
FILE: Examples/tf_flags/README.md
================================================
# Demo for tf.app.flags
Tensorflow flags is widely used for tensorflow to parse input arguments.
It is naturally supported by **Auptimizer** with minor modification on you existing code.
Similar to adopting **Auptimizer** for plain python training code, there are two parts of changes.
A working example is shown as `rosenbrock_hpo.py`, whereas the plain code is `rosenbrock_tf.py`.
## Modification on training code
Because `tf.flags` already takes care of the input argument, we just insert `aup.BasicConfig` properly:
```python
def main(unused_arguments):
config = BasicConfig().load(unused[1])
config.to_flags(FLAGS)
if __name__ == "__main__"
tf.app.run()
```
`config = BasicConfig().load(unused[1])` parses the input file for the parameter values.
And `config.to_flags(FLAGS)` assigns the value to `tensorflow.app.flags.FLAGS`, thus it can be used without changing how to access those values in the original code.
Similar to other examples, you need to call `aup.print_result()` at the end of the program to return your target to optimize.
## Modification on experiment configuration
The same configuration of experiment can be used. See other examples for reference.
## Run
Same to other example, you now can run **Auptimizer** as
```bash
python -m aup experiment.json
```
to initiate your continuous training experiment.
================================================
FILE: Examples/tf_flags/experiment.json
================================================
{
"name": "./tf_flags/experiment.json",
"proposer": "random",
"n_samples": 10,
"random_seed": 1,
"script": "rosenbrock_hpo.py",
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
},
{
"name": "y",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "cpu",
"n_parallel": 2,
"target":"min"
}
================================================
FILE: Examples/tf_flags/rosenbrock_hpo.py
================================================
#!/usr/bin/env python
"""
Modified Rosenbrock function with tf.app.flags
==============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import tensorflow as tf
"""
from aup import print_result, BasicConfig
flags = tf.app.flags
FLAGS = flags.FLAGS
flags.DEFINE_float("x", 0, "x value")
flags.DEFINE_float("y", 0, "y value")
def rosenbrock(x, y, a=1, b=100):
return (a-x)*(a-x) + b*(y-x*x)*(y-x*x)
def main(unused):
## change
config = BasicConfig().load(unused[1])
config.to_flags(FLAGS)
##
val = rosenbrock(FLAGS.x, FLAGS.y)
print_result(val)
if __name__ == "__main__":
tf.app.run()
"""
from aup import aup_flags
flags = tf.app.flags
FLAGS = flags.FLAGS
flags.DEFINE_float("x", 0, "x value")
flags.DEFINE_float("y", 0, "y value")
def rosenbrock(x, y, a=1, b=100):
return (a-x)*(a-x) + b*(y-x*x)*(y-x*x)
@aup_flags(FLAGS)
def main(unused):
return rosenbrock(FLAGS.x, FLAGS.y)
if __name__ == "__main__":
tf.app.run()
================================================
FILE: Examples/tf_flags/rosenbrock_tf.py
================================================
#!/usr/bin/env python
"""
Modified Rosenbrock function with tf.app.flags
==============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import sys
import tensorflow as tf
flags = tf.app.flags
FLAGS = flags.FLAGS
flags.DEFINE_float("x", 0, "x value")
flags.DEFINE_float("y", 0, "y value")
def rosenbrock(x, y, a=1, b=100):
return (a-x)*(a-x) + b*(y-x*x)*(y-x*x)
def main(unused):
val = rosenbrock(FLAGS.x, FLAGS.y)
print(val)
if __name__ == "__main__":
tf.app.run()
================================================
FILE: Examples/tf_iris_diff_opt/History.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import pandas as pd\n",
"import matplotlib.pylab as plt\n",
"from aup.ET.Connector.SQLiteConnector import SQLiteConnector\n",
"import json\n",
"%matplotlib inline"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# connect to the result (The database is copied from ~/.aup/sqlite3.db)\n",
"sql = SQLiteConnector(\"./sqlite3.db\")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1, 2, 3, 4]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Get all experiments (this run is for iris using hyperopt, sequence, spearmint, random)\n",
"eids = sql.get_all_experiment()\n",
"eids"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(2,\n",
" 1,\n",
" 1534397382,\n",
" 1534397474,\n",
" '{\"script\": \"premade_estimator_hpo.py\", \"n_parallel\": 2, \"parameter_config\": [{\"type\": \"int\", \"name\": \"layer1\", \"range\": [2, 6]}, {\"type\": \"int\", \"name\": \"layer2\", \"range\": [2, 6]}], \"resource\": \"gpu\", \"proposer\": \"sequence\", \"target\": \"max\", \"workingdir\": \"Examples/tf_iris_diff_opt\"}')"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Show details in one experiment\n",
"sql.cursor.execute(\"SELECT * FROM experiment where eid = ?\", (eids[1],))\n",
"sql.cursor.fetchone()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" jid \n",
" score \n",
" eid \n",
" rid \n",
" start_time \n",
" end_time \n",
" job_config \n",
" \n",
" \n",
" \n",
" \n",
" 0 \n",
" 1 \n",
" 0.966667 \n",
" 1 \n",
" 1 \n",
" 1534397309 \n",
" 1534397318 \n",
" {'layer2': 2, 'tid': 0, 'layer1': 2} \n",
" \n",
" \n",
" 1 \n",
" 2 \n",
" 0.933333 \n",
" 1 \n",
" 2 \n",
" 1534397309 \n",
" 1534397318 \n",
" {'layer2': 5, 'tid': 1, 'layer1': 2} \n",
" \n",
" \n",
" 2 \n",
" 3 \n",
" 0.966667 \n",
" 1 \n",
" 1 \n",
" 1534397318 \n",
" 1534397325 \n",
" {'layer2': 4, 'tid': 2, 'layer1': 3} \n",
" \n",
" \n",
" 3 \n",
" 4 \n",
" 0.966667 \n",
" 1 \n",
" 2 \n",
" 1534397318 \n",
" 1534397325 \n",
" {'layer2': 4, 'tid': 3, 'layer1': 3} \n",
" \n",
" \n",
" 4 \n",
" 5 \n",
" 0.966667 \n",
" 1 \n",
" 1 \n",
" 1534397325 \n",
" 1534397332 \n",
" {'layer2': 4, 'tid': 4, 'layer1': 4} \n",
" \n",
" \n",
" 5 \n",
" 6 \n",
" 0.966667 \n",
" 1 \n",
" 2 \n",
" 1534397325 \n",
" 1534397332 \n",
" {'layer2': 3, 'tid': 5, 'layer1': 5} \n",
" \n",
" \n",
" 6 \n",
" 7 \n",
" 1.000000 \n",
" 1 \n",
" 1 \n",
" 1534397332 \n",
" 1534397339 \n",
" {'layer2': 3, 'tid': 6, 'layer1': 5} \n",
" \n",
" \n",
" 7 \n",
" 8 \n",
" 0.266667 \n",
" 1 \n",
" 2 \n",
" 1534397332 \n",
" 1534397340 \n",
" {'layer2': 2, 'tid': 7, 'layer1': 2} \n",
" \n",
" \n",
" 8 \n",
" 9 \n",
" 0.966667 \n",
" 1 \n",
" 1 \n",
" 1534397339 \n",
" 1534397346 \n",
" {'layer2': 5, 'tid': 8, 'layer1': 5} \n",
" \n",
" \n",
" 9 \n",
" 10 \n",
" 0.966667 \n",
" 1 \n",
" 2 \n",
" 1534397340 \n",
" 1534397347 \n",
" {'layer2': 3, 'tid': 9, 'layer1': 2} \n",
" \n",
" \n",
" 10 \n",
" 11 \n",
" 0.966667 \n",
" 1 \n",
" 1 \n",
" 1534397347 \n",
" 1534397354 \n",
" {'layer2': 5, 'tid': 10, 'layer1': 5} \n",
" \n",
" \n",
" 11 \n",
" 12 \n",
" 0.966667 \n",
" 1 \n",
" 2 \n",
" 1534397347 \n",
" 1534397354 \n",
" {'layer2': 5, 'tid': 11, 'layer1': 4} \n",
" \n",
" \n",
" 12 \n",
" 13 \n",
" 0.266667 \n",
" 1 \n",
" 1 \n",
" 1534397354 \n",
" 1534397361 \n",
" {'layer2': 3, 'tid': 12, 'layer1': 5} \n",
" \n",
" \n",
" 13 \n",
" 14 \n",
" 0.966667 \n",
" 1 \n",
" 2 \n",
" 1534397354 \n",
" 1534397361 \n",
" {'layer2': 5, 'tid': 13, 'layer1': 3} \n",
" \n",
" \n",
" 14 \n",
" 15 \n",
" 0.966667 \n",
" 1 \n",
" 1 \n",
" 1534397361 \n",
" 1534397368 \n",
" {'layer2': 4, 'tid': 14, 'layer1': 5} \n",
" \n",
" \n",
" 15 \n",
" 16 \n",
" 0.966667 \n",
" 1 \n",
" 2 \n",
" 1534397361 \n",
" 1534397368 \n",
" {'layer2': 4, 'tid': 15, 'layer1': 4} \n",
" \n",
" \n",
" 16 \n",
" 17 \n",
" 0.966667 \n",
" 1 \n",
" 1 \n",
" 1534397368 \n",
" 1534397375 \n",
" {'layer2': 3, 'tid': 16, 'layer1': 4} \n",
" \n",
" \n",
" 17 \n",
" 18 \n",
" 1.000000 \n",
" 1 \n",
" 2 \n",
" 1534397368 \n",
" 1534397375 \n",
" {'layer2': 5, 'tid': 17, 'layer1': 2} \n",
" \n",
" \n",
" 18 \n",
" 19 \n",
" 1.000000 \n",
" 1 \n",
" 1 \n",
" 1534397375 \n",
" 1534397382 \n",
" {'layer2': 4, 'tid': 18, 'layer1': 3} \n",
" \n",
" \n",
" 19 \n",
" 20 \n",
" 1.000000 \n",
" 1 \n",
" 2 \n",
" 1534397375 \n",
" 1534397382 \n",
" {'layer2': 4, 'tid': 19, 'layer1': 2} \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" jid score eid rid start_time end_time \\\n",
"0 1 0.966667 1 1 1534397309 1534397318 \n",
"1 2 0.933333 1 2 1534397309 1534397318 \n",
"2 3 0.966667 1 1 1534397318 1534397325 \n",
"3 4 0.966667 1 2 1534397318 1534397325 \n",
"4 5 0.966667 1 1 1534397325 1534397332 \n",
"5 6 0.966667 1 2 1534397325 1534397332 \n",
"6 7 1.000000 1 1 1534397332 1534397339 \n",
"7 8 0.266667 1 2 1534397332 1534397340 \n",
"8 9 0.966667 1 1 1534397339 1534397346 \n",
"9 10 0.966667 1 2 1534397340 1534397347 \n",
"10 11 0.966667 1 1 1534397347 1534397354 \n",
"11 12 0.966667 1 2 1534397347 1534397354 \n",
"12 13 0.266667 1 1 1534397354 1534397361 \n",
"13 14 0.966667 1 2 1534397354 1534397361 \n",
"14 15 0.966667 1 1 1534397361 1534397368 \n",
"15 16 0.966667 1 2 1534397361 1534397368 \n",
"16 17 0.966667 1 1 1534397368 1534397375 \n",
"17 18 1.000000 1 2 1534397368 1534397375 \n",
"18 19 1.000000 1 1 1534397375 1534397382 \n",
"19 20 1.000000 1 2 1534397375 1534397382 \n",
"\n",
" job_config \n",
"0 {'layer2': 2, 'tid': 0, 'layer1': 2} \n",
"1 {'layer2': 5, 'tid': 1, 'layer1': 2} \n",
"2 {'layer2': 4, 'tid': 2, 'layer1': 3} \n",
"3 {'layer2': 4, 'tid': 3, 'layer1': 3} \n",
"4 {'layer2': 4, 'tid': 4, 'layer1': 4} \n",
"5 {'layer2': 3, 'tid': 5, 'layer1': 5} \n",
"6 {'layer2': 3, 'tid': 6, 'layer1': 5} \n",
"7 {'layer2': 2, 'tid': 7, 'layer1': 2} \n",
"8 {'layer2': 5, 'tid': 8, 'layer1': 5} \n",
"9 {'layer2': 3, 'tid': 9, 'layer1': 2} \n",
"10 {'layer2': 5, 'tid': 10, 'layer1': 5} \n",
"11 {'layer2': 5, 'tid': 11, 'layer1': 4} \n",
"12 {'layer2': 3, 'tid': 12, 'layer1': 5} \n",
"13 {'layer2': 5, 'tid': 13, 'layer1': 3} \n",
"14 {'layer2': 4, 'tid': 14, 'layer1': 5} \n",
"15 {'layer2': 4, 'tid': 15, 'layer1': 4} \n",
"16 {'layer2': 3, 'tid': 16, 'layer1': 4} \n",
"17 {'layer2': 5, 'tid': 17, 'layer1': 2} \n",
"18 {'layer2': 4, 'tid': 18, 'layer1': 3} \n",
"19 {'layer2': 4, 'tid': 19, 'layer1': 2} "
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# show details in job history for one experiment\n",
"history = sql.get_all_history(1)\n",
"history = pd.DataFrame(history)\n",
"history.columns = ['jid', 'score','eid','rid','start_time','end_time','job_config']\n",
"history"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Text(0,0.5,'Best Accuracy so far')"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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1/CBpg/U9HoZZOPnwc4nIH3A/2ujTXDe/4Cm/1b/don4FY7C6ZMgOWqoWyIiIgFhzrmhboMvkyZNcs3NzUPdDBEROUaY2Sbn3KShbkeQqW8WkWPdrl27OO+884a6Gcekno5tX/tmjZkVERERERE5zowePZp33nlnqJvRLwpmRUREREREAsQ5R6FQGOpmDDmNmRUREREREemjv3zjG3Tu2l3XfcbOG8tf3X33EbdpaWlh1qxZXHbZZWzatInJkyezfft2UqkUf/d3f8eyZcuAYsb1hhtu4Je//CXZbJaf//znjB07lnfffZf58+fz+uuvM2XKFKqHm95///388Ic/BGDhwoXcfvvttLS0MHv2bC6//HJefPFFLr30Um688UaWLl3K22+/zcMPP8zkyZPrehz8UmZWREREREQkAF555RU+97nP8fLLL/Otb32L5uZmtm3bxnPPPce2bdsq240aNYrNmzdz8803s3z5cgCWLVvGRz/6UV5++WWuu+46XnvtNQA2bdrEypUreemll1i/fj3f//73+f3vfw/Aq6++yp133snu3bvZvXs3q1evZt26dSxfvpxvfOMbg38ADqPMrIiIiIiISB8dLYM6kM466ywuv/xyAH72s5/xve99j1wux5tvvsnOnTsZP348AHPmFJfsmzhxIo899hgAzz//fOX+Jz7xCUaOHAnAunXruO6662hqaqq89oUXXuCaa65hzJgxjBs3DoALLriAj33sY5gZ48aNo6WlZdB+794omBUREREREQmAcsD5pz/9ieXLl7Nx40ZGjhzJggULSKfTle1isRgAoVCIXC5X8/uV9wPgeV7lsed5/dpvvajMWEREREREJEAOHjxIU1MTI0aM4K233mLt2rVHfc0VV1zB6tWrAVi7di2tra0ATJs2jTVr1tDR0UF7ezuPP/4406ZNG9D214sysyIiIiIiIgFy0UUXcfHFFzN27FjOPPNMpk6detTXLF26lPnz53PBBRfwkY98hA9+8IMAXHLJJSxYsKAymdPChQu5+OKLG6KM+GiseharINDC7CIiUk99XZhdeqe+WUSOdbt27eK8884b6mYck3o6tn3tm1VmLCIiIiIiIoGjYFZEREREREQCR8GsiIiIiIjIUQRteGYQ9PeYKpgVERERERE5gng8zrvvvquAto6cc7z77rvE4/Ga96HZjEVERERE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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(16,8))\n",
"plt.subplot(121)\n",
"for i in eids:\n",
" history = sql.get_all_history(i)\n",
" history = pd.DataFrame(history)\n",
" history.columns = ['jid', 'score','eid','rid','start_time','end_time','job_config']\n",
" sql.cursor.execute(\"SELECT * FROM experiment where eid = ?\", (i,))\n",
" label = json.loads(sql.cursor.fetchone()[4])['proposer']\n",
" plt.plot(history.score, label=label)\n",
"plt.legend()\n",
"plt.xlabel(\"Iteration\")\n",
"plt.ylabel(\"Accuracy\")\n",
"\n",
"plt.subplot(122)\n",
"for i in eids:\n",
" history = sql.get_all_history(i)\n",
" history = pd.DataFrame(history)\n",
" history.columns = ['jid', 'score','eid','rid','start_time','end_time','job_config']\n",
" sql.cursor.execute(\"SELECT * FROM experiment where eid = ?\", (i,))\n",
" label = json.loads(sql.cursor.fetchone()[4])['proposer']\n",
" plt.plot(history.score.cummax(), label=label)\n",
"plt.legend()\n",
"plt.xlabel(\"Iteration\")\n",
"plt.ylabel(\"Best Accuracy so far\")"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"sql.close()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: Examples/tf_iris_diff_opt/README.md
================================================
# Tensorflow Iris Example
Here we demonstrate how to adopt an existing [tensorflow code](https://raw.githubusercontent.com/tensorflow/models/master/samples/core/get_started/premade_estimator.py) for **Auptimizer** using the automatic code generation tool.
## Steps
1. The original `premade_estimator.py` use two hyperparameters, `batch_size` and `train_steps`. To make the code more relevant to our hyperparameter search, we drop them as input arguments and use fixed values instead.
2. We add two hyperparameters into the `main()` function to update the number of neurons in each layer, i.e. `layer1` and `layer2`. The model is constructed accordingly in line 51.
3. Add the return value `probability` in the `main()` function and also remove the "__main__" segments as we will create it automatically.
See `premade_estimator_hyper.py` for the changed script.
4. Write an experiment configution JSON file, such as:
```JSON
{
"proposer": "random",
"script": "premade_estimator_wrapper.py",
"n_samples": 20,
"random_seed": 1,
"parameter_config": [
{
"name": "layer1",
"range": [
2,
6
],
"type": "int"
},
{
"name": "layer2",
"range": [
2,
6
],
"type": "int"
}
],
"resource": "cpu",
"n_parallel": 2,
"target":"max"
}
```
4. Run code conversion as: `python -m aup.convert premade_estimator_hyper.py experiment_random.json main`. The output file is saved as `premade_estimator_wrapper.py`.
Now we have the code for **Auptimizer** to tune.
## Run
1. If you haven't setup **Auptimzier** yet, run `python -m aup.setup ../../FirstTime/env_local_template.ini` to setup a local environment for testing (with CPU only). Then following instruction on the screen to run `python -m aup.setupdb.sqlite ./.aup/env.ini`.
2. Run **Auptimizer** as: `python -m aup experiment_demo.json`.
================================================
FILE: Examples/tf_iris_diff_opt/experiment_demo.json
================================================
{
"name": "./tf_iris_diff_opt/experiment_demo.json",
"proposer": "random",
"script": "premade_estimator_wrapper.py",
"n_samples": 4,
"random_seed": 1,
"parameter_config": [
{
"name": "layer1",
"range": [
2,
6
],
"type": "int"
},
{
"name": "layer2",
"range": [
2,
6
],
"type": "int"
}
],
"resource": "cpu",
"n_parallel": 2,
"target":"max"
}
================================================
FILE: Examples/tf_iris_diff_opt/experiment_hpo.json
================================================
{
"name": "./tf_iris_diff_opt/experiment_hpo.json",
"proposer": "hyperopt",
"random_seed": 1,
"engine":"tpe",
"n_samples": 20,
"script": "premade_estimator_hpo.py",
"parameter_config": [
{
"name": "layer1",
"range": [
2,
6
],
"type": "int"
},
{
"name": "layer2",
"range": [
2,
6
],
"type": "int"
}
],
"resource": "gpu",
"n_parallel": 2,
"target":"max"
}
================================================
FILE: Examples/tf_iris_diff_opt/experiment_hyperband.json
================================================
{
"name": "./tf_iris_diff_opt/experiment_hyperband.json",
"proposer": "hyperopt",
"random_seed": 1,
"engine":"tpe",
"n_samples": 20,
"script": "premade_estimator_hpo.py",
"parameter_config": [
{
"name": "layer1",
"range": [
2,
6
],
"type": "int"
},
{
"name": "layer2",
"range": [
2,
6
],
"type": "int"
}
],
"resource": "gpu",
"n_parallel": 2,
"target":"max"
}
================================================
FILE: Examples/tf_iris_diff_opt/experiment_random.json
================================================
{
"name": "./tf_iris_diff_opt/experiment_random.json",
"proposer": "random",
"script": "premade_estimator_hpo.py",
"n_samples": 20,
"random_seed": 1,
"parameter_config": [
{
"name": "layer1",
"range": [
2,
6
],
"type": "int"
},
{
"name": "layer2",
"range": [
2,
6
],
"type": "int"
}
],
"resource": "gpu",
"n_parallel": 2,
"target":"max"
}
================================================
FILE: Examples/tf_iris_diff_opt/experiment_sequence.json
================================================
{
"name": "./tf_iris_diff_opt/experiment_sequence.json",
"proposer": "sequence",
"script": "premade_estimator_hpo.py",
"parameter_config": [
{
"name": "layer1",
"range": [
2,
6
],
"type": "int"
},
{
"name": "layer2",
"range": [
2,
6
],
"type": "int"
}
],
"resource": "gpu",
"n_parallel": 2,
"target":"max"
}
================================================
FILE: Examples/tf_iris_diff_opt/experiment_spearmint.json
================================================
{
"name": "./tf_iris_diff_opt/experiment_spearmint.json",
"proposer": "spearmint",
"n_samples": 20,
"random_seed": 1,
"script": "premade_estimator_hpo.py",
"engine":"GPEIChooser",
"parameter_config": [
{
"name": "layer1",
"range": [
2,
6
],
"type": "int",
"size": 1
},
{
"name": "layer2",
"range": [
2,
6
],
"type": "int",
"size": 1
}
],
"resource": "gpu",
"n_parallel": 2,
"target":"max"
}
================================================
FILE: Examples/tf_iris_diff_opt/iris_data.py
================================================
# Download from https://raw.githubusercontent.com/tensorflow/models/master/samples/core/get_started/iris_data.py
# Date: Jun 11, 2018
import pandas as pd
import tensorflow as tf
TRAIN_URL = "http://download.tensorflow.org/data/iris_training.csv"
TEST_URL = "http://download.tensorflow.org/data/iris_test.csv"
CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth',
'PetalLength', 'PetalWidth', 'Species']
SPECIES = ['Setosa', 'Versicolor', 'Virginica']
def maybe_download():
train_path = tf.keras.utils.get_file(TRAIN_URL.split('/')[-1], TRAIN_URL)
test_path = tf.keras.utils.get_file(TEST_URL.split('/')[-1], TEST_URL)
return train_path, test_path
def load_data(y_name='Species'):
"""Returns the iris dataset as (train_x, train_y), (test_x, test_y)."""
train_path, test_path = maybe_download()
train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0)
train_x, train_y = train, train.pop(y_name)
test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0)
test_x, test_y = test, test.pop(y_name)
return (train_x, train_y), (test_x, test_y)
def train_input_fn(features, labels, batch_size):
"""An input function for training"""
# Convert the inputs to a Dataset.
dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))
# Shuffle, repeat, and batch the examples.
dataset = dataset.shuffle(1000).repeat().batch(batch_size)
# Return the dataset.
return dataset
def eval_input_fn(features, labels, batch_size):
"""An input function for evaluation or prediction"""
features=dict(features)
if labels is None:
# No labels, use only features.
inputs = features
else:
inputs = (features, labels)
# Convert the inputs to a Dataset.
dataset = tf.data.Dataset.from_tensor_slices(inputs)
# Batch the examples
assert batch_size is not None, "batch_size must not be None"
dataset = dataset.batch(batch_size)
# Return the dataset.
return dataset
# The remainder of this file contains a simple example of a csv parser,
# implemented using the `Dataset` class.
# `tf.parse_csv` sets the types of the outputs to match the examples given in
# the `record_defaults` argument.
CSV_TYPES = [[0.0], [0.0], [0.0], [0.0], [0]]
def _parse_line(line):
# Decode the line into its fields
fields = tf.decode_csv(line, record_defaults=CSV_TYPES)
# Pack the result into a dictionary
features = dict(zip(CSV_COLUMN_NAMES, fields))
# Separate the label from the features
label = features.pop('Species')
return features, label
def csv_input_fn(csv_path, batch_size):
# Create a dataset containing the text lines.
dataset = tf.data.TextLineDataset(csv_path).skip(1)
# Parse each line.
dataset = dataset.map(_parse_line)
# Shuffle, repeat, and batch the examples.
dataset = dataset.shuffle(1000).repeat().batch(batch_size)
# Return the dataset.
return dataset
================================================
FILE: Examples/tf_iris_diff_opt/premade_estimator.py
================================================
# Download from https://raw.githubusercontent.com/tensorflow/models/master/samples/core/get_started/premade_estimator.py
# Date: Jun 11, 2018
# This the original code. The converted version for Auptimizer is `premade_estimator_hpo.py`
# #########################################################################################################
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""An Example of a DNNClassifier for the Iris dataset."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse
import tensorflow as tf
import iris_data
parser = argparse.ArgumentParser()
parser.add_argument('--batch_size', default=100, type=int, help='batch size')
parser.add_argument('--train_steps', default=1000, type=int,
help='number of training steps')
def main(argv):
args = parser.parse_args(argv[1:])
# Fetch the data
(train_x, train_y), (test_x, test_y) = iris_data.load_data()
# Feature columns describe how to use the input.
my_feature_columns = []
for key in train_x.keys():
my_feature_columns.append(tf.feature_column.numeric_column(key=key))
# Build 2 hidden layer DNN with 10, 10 units respectively.
classifier = tf.estimator.DNNClassifier(
feature_columns=my_feature_columns,
# Two hidden layers of 10 nodes each.
hidden_units=[10, 10],
# The model must choose between 3 classes.
n_classes=3)
# Train the Model.
classifier.train(
input_fn=lambda:iris_data.train_input_fn(train_x, train_y,
args.batch_size),
steps=args.train_steps)
# Evaluate the model.
eval_result = classifier.evaluate(
input_fn=lambda:iris_data.eval_input_fn(test_x, test_y,
args.batch_size))
print('\nTest set accuracy: {accuracy:0.3f}\n'.format(**eval_result))
# Generate predictions from the model
expected = ['Setosa', 'Versicolor', 'Virginica']
predict_x = {
'SepalLength': [5.1, 5.9, 6.9],
'SepalWidth': [3.3, 3.0, 3.1],
'PetalLength': [1.7, 4.2, 5.4],
'PetalWidth': [0.5, 1.5, 2.1],
}
predictions = classifier.predict(
input_fn=lambda:iris_data.eval_input_fn(predict_x,
labels=None,
batch_size=args.batch_size))
template = ('\nPrediction is "{}" ({:.1f}%), expected "{}"')
for pred_dict, expec in zip(predictions, expected):
class_id = pred_dict['class_ids'][0]
probability = pred_dict['probabilities'][class_id]
print(template.format(iris_data.SPECIES[class_id],
100 * probability, expec))
if __name__ == '__main__':
tf.logging.set_verbosity(tf.logging.INFO)
tf.app.run(main)
================================================
FILE: Examples/tf_iris_diff_opt/premade_estimator_hpo.py
================================================
#!/usr/bin/env python3
# Download from https://raw.githubusercontent.com/tensorflow/models/master/samples/core/get_started/premade_estimator.py
# Date: Jun 11, 2018
# Modified for auptimizer
########################################################################################################################
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""An Example of a DNNClassifier for the Iris dataset."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import tensorflow as tf
import sys
import iris_data
from aup import BasicConfig, print_result
#
# ChangeLog:
# + remove argparse and set default batch_size, and train_step
# + add aup config and change __main__
# + change main() to get conf, which contains the number of nodes per layer
# + change main() to return eval_result["accuracy"].
# + add shebang line and execute permission
# + change train_steps to be consistent with hyperband
batch_size = 100
config = tf.ConfigProto()
config.gpu_options.allow_growth=True
sess = tf.Session(config=config)
def main(conf):
# Fetch the data
(train_x, train_y), (test_x, test_y) = iris_data.load_data()
if "n_iterations" in config:
train_steps = 100 * config.n_iterations
else:
train_steps = 1000
# Feature columns describe how to use the input.
my_feature_columns = []
for key in train_x.keys():
my_feature_columns.append(tf.feature_column.numeric_column(key=key))
# Build 2 hidden layer DNN with 10, 10 units respectively.
classifier = tf.estimator.DNNClassifier(
feature_columns=my_feature_columns,
# Two hidden layers of 10 nodes each.
hidden_units=[conf["layer1"], conf["layer2"]],
# The model must choose between 3 classes.
n_classes=3)
# Train the Model.
classifier.train(input_fn=lambda:iris_data.train_input_fn(train_x, train_y,batch_size), steps=train_steps)
# Evaluate the model.
eval_result = classifier.evaluate(input_fn=lambda:iris_data.eval_input_fn(test_x, test_y,batch_size))
print('\nTest set accuracy: {accuracy:0.3f}\n'.format(**eval_result))
# Generate predictions from the model
expected = ['Setosa', 'Versicolor', 'Virginica']
predict_x = {
'SepalLength': [5.1, 5.9, 6.9],
'SepalWidth': [3.3, 3.0, 3.1],
'PetalLength': [1.7, 4.2, 5.4],
'PetalWidth': [0.5, 1.5, 2.1],
}
predictions = classifier.predict(input_fn=lambda:iris_data.eval_input_fn(predict_x, labels=None, batch_size=batch_size))
template = ('\nPrediction is "{}" ({:.1f}%), expected "{}"')
for pred_dict, expec in zip(predictions, expected):
class_id = pred_dict['class_ids'][0]
probability = pred_dict['probabilities'][class_id]
print(template.format(iris_data.SPECIES[class_id],
100 * probability, expec))
return eval_result["accuracy"]
if __name__ == '__main__':
if len(sys.argv) != 2:
print("config file required")
exit(1)
config = BasicConfig().load(sys.argv[1])
val = main(config)
print_result(val)
================================================
FILE: Examples/tf_iris_diff_opt/premade_estimator_hyper.py
================================================
# Download from https://raw.githubusercontent.com/tensorflow/models/master/samples/core/get_started/premade_estimator.py
# Date: Jun 11, 2018
# Modified to work with aup.convert
########################################################################################################################
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""An Example of a DNNClassifier for the Iris dataset."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse
import tensorflow as tf
import iris_data
#parser = argparse.ArgumentParser()
#parser.add_argument('--batch_size', default=100, type=int, help='batch size')
#parser.add_argument('--train_steps', default=1000, type=int,
# help='number of training steps')
batch_size=100
train_steps=1000
def main(layer1, layer2):
#args = parser.parse_args(argv[1:])
# Fetch the data
(train_x, train_y), (test_x, test_y) = iris_data.load_data()
# Feature columns describe how to use the input.
my_feature_columns = []
for key in train_x.keys():
my_feature_columns.append(tf.feature_column.numeric_column(key=key))
# Build 2 hidden layer DNN with 10, 10 units respectively.
classifier = tf.estimator.DNNClassifier(
feature_columns=my_feature_columns,
# Two hidden layers of 10 nodes each.
hidden_units=[layer1, layer2],
# The model must choose between 3 classes.
n_classes=3)
# Train the Model.
classifier.train(
input_fn=lambda:iris_data.train_input_fn(train_x, train_y,
batch_size),
steps=train_steps)
# Evaluate the model.
eval_result = classifier.evaluate(
input_fn=lambda:iris_data.eval_input_fn(test_x, test_y,
batch_size))
print('\nTest set accuracy: {accuracy:0.3f}\n'.format(**eval_result))
# Generate predictions from the model
expected = ['Setosa', 'Versicolor', 'Virginica']
predict_x = {
'SepalLength': [5.1, 5.9, 6.9],
'SepalWidth': [3.3, 3.0, 3.1],
'PetalLength': [1.7, 4.2, 5.4],
'PetalWidth': [0.5, 1.5, 2.1],
}
predictions = classifier.predict(
input_fn=lambda:iris_data.eval_input_fn(predict_x,
labels=None,
batch_size=batch_size))
template = ('\nPrediction is "{}" ({:.1f}%), expected "{}"')
for pred_dict, expec in zip(predictions, expected):
class_id = pred_dict['class_ids'][0]
probability = pred_dict['probabilities'][class_id]
print(template.format(iris_data.SPECIES[class_id],
100 * probability, expec))
return probability
#if __name__ == '__main__':
# tf.logging.set_verbosity(tf.logging.INFO)
# tf.app.run(main)
================================================
FILE: Examples/tf_iris_diff_opt/premade_estimator_wrapper.py
================================================
#!/usr/bin/env python
# Download from https://raw.githubusercontent.com/tensorflow/models/master/samples/core/get_started/premade_estimator.py
# Date: Jun 11, 2018
# This is created by aup.convert
########################################################################################################################
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""An Example of a DNNClassifier for the Iris dataset."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse
import tensorflow as tf
import iris_data
#parser = argparse.ArgumentParser()
#parser.add_argument('--batch_size', default=100, type=int, help='batch size')
#parser.add_argument('--train_steps', default=1000, type=int,
# help='number of training steps')
batch_size=100
train_steps=1000
def main(layer1, layer2):
#args = parser.parse_args(argv[1:])
# Fetch the data
(train_x, train_y), (test_x, test_y) = iris_data.load_data()
# Feature columns describe how to use the input.
my_feature_columns = []
for key in train_x.keys():
my_feature_columns.append(tf.feature_column.numeric_column(key=key))
# Build 2 hidden layer DNN with 10, 10 units respectively.
classifier = tf.estimator.DNNClassifier(
feature_columns=my_feature_columns,
# Two hidden layers of 10 nodes each.
hidden_units=[layer1, layer2],
# The model must choose between 3 classes.
n_classes=3)
# Train the Model.
classifier.train(
input_fn=lambda:iris_data.train_input_fn(train_x, train_y,
batch_size),
steps=train_steps)
# Evaluate the model.
eval_result = classifier.evaluate(
input_fn=lambda:iris_data.eval_input_fn(test_x, test_y,
batch_size))
print('\nTest set accuracy: {accuracy:0.3f}\n'.format(**eval_result))
# Generate predictions from the model
expected = ['Setosa', 'Versicolor', 'Virginica']
predict_x = {
'SepalLength': [5.1, 5.9, 6.9],
'SepalWidth': [3.3, 3.0, 3.1],
'PetalLength': [1.7, 4.2, 5.4],
'PetalWidth': [0.5, 1.5, 2.1],
}
predictions = classifier.predict(
input_fn=lambda:iris_data.eval_input_fn(predict_x,
labels=None,
batch_size=batch_size))
template = ('\nPrediction is "{}" ({:.1f}%), expected "{}"')
for pred_dict, expec in zip(predictions, expected):
class_id = pred_dict['class_ids'][0]
probability = pred_dict['probabilities'][class_id]
print(template.format(iris_data.SPECIES[class_id],
100 * probability, expec))
return probability
#if __name__ == '__main__':
# tf.logging.set_verbosity(tf.logging.INFO)
# tf.app.run(main)
def aup_wrapper(config):
res = main(layer1=config['layer1'],layer2=config['layer2'])
print_result(res)
if __name__ == "__main__":
import sys
from aup import BasicConfig, print_result
if len(sys.argv) != 2:
print("config file required")
exit(1)
config = BasicConfig().load(sys.argv[1])
aup_wrapper(config)
================================================
FILE: LICENSE
================================================
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
.
================================================
FILE: MANIFEST.in
================================================
include LICENSE
include README.md
recursive-include src/aup/dashboard/frontend/febuild/auptimizer-dashboard/ *
================================================
FILE: R-src/README.md
================================================
# Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
# R Package for Auptimizer
## Installation
*IMPORTANT:* Auptimizer is well tested on Unix or similar OS. Windows users may have to make some changes to run Aptimizer.
1. [Install Python Auptimizer](../README.md)
2. Install Auptimizer for R, (run R from `R-src` folder):
```R
install.packages("devtools")
devtools::install("Rpackage")
```
## Usage
The workflow for Auptimizer is the same as the Python version. The difference is in how to change existing R code to use Auptimizer.
1. Setup Python Auptimizer environment using `python -m aup.setup`
2. Change your R script:
a. Make all hyperparameters global variables.
b. Add `#!/usr/bin/env Rscript` as the first line.
c. Add `source("auptimizer")`.
d. Add `get_config()`, which will automatically update the hyperparameters (set globally in step 2a).
e. Add `print_result(score)` to return the target score you want to optimize for your script.
f. Change file permission using `chmod u+x `.
g. Add them into an Auptimizer experiment using `python -m aup.init`.
3. Run Auptimizer using `python -m aup experiment.json`.
## Examples
See examples in [example].
+ `exp_ridge.R` for synthetic Ridge regression. Run as `python -m aup ridge.json`.
+ `exp_rosenbrock.R` for analytic Rosenbrock function. Run as `python -m aup rosenbrock.json`.
================================================
FILE: R-src/Rpackage/DESCRIPTION
================================================
Package: auptimizer
Title: R Helper Functions for Auptimizer
Version: 0.0.2
Authors@R: c(
person(given = "Jiayi",
family = "Liu",
role = c("aut","cre"),
email = "Jason.Liu@lge.com",
comment = c(ORCID = "0000-0001-6007-5256")),
person(given = "Sauptik",
family = "Dhar",
role = c("aut"),
email = "Sauptik.Dhar@lge.com"),
person(given = "",
family = "LG Electronics Inc.",
role = c("cph", "asn"),
email = "auptimizer@lge.com")
)
Description: Enable R users to use Auptimizer
for hyperparameter
optimization (HPO). Auptimizer helps data scientists to easily apply HPO
in their work and to speed up model training jobs by executing jobs
asynchronously when computing resources are available.
License: GPL (>=3)
Encoding: UTF-8
LazyData: true
Imports:
rjson
URL: https://github.com/LGE-ARC-AdvancedAI/auptimizer
BugReports: https://github.com/LGE-ARC-AdvancedAI/auptimizer/issues
RoxygenNote: 6.1.1
Suggests:
testthat,
knitr,
rmarkdown
VignetteBuilder: knitr
================================================
FILE: R-src/Rpackage/NAMESPACE
================================================
# Generated by roxygen2: do not edit by hand
export(get_config)
export(print_result)
================================================
FILE: R-src/Rpackage/R/auptimizer.R
================================================
# Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
#' Get hyperparameter configuration
#'
#' This function loads a configuration file in JSON format. It assumes that the first argument of the running script is
#' the configuraiton file. It will parse and inject the hyperparameter variables into the global environment and return
#' them.
#'
#'
#' Assuming the R script is used as follows: `./test.R config.json`, the script should look like below:
#'
#' \preformatted{
#' #!Rscript
#'
#' # hyperparameter definitions
#' get_config()
#' # use hyperparamter to train model,
#' # after training, return optimization target (e.g. validation accuracy) as score
#' print_result(score)
#' }
#'
#'
#'
#' @param filename optional JSON filename containing hyperparameters as dict, default to first argument in command line.
#' @param env optional environment containing hyperparameters, default to global env.
#' @return a matrix of hyperparameters as name-value pairs
#' @export
get_config <- function(filename, env) {
if (missing(filename)){
args <- commandArgs(TRUE)
if (length(args) < 1) {
warning("R script requires an input configuration file for Auptimzier experiment.")
return()
} else if (length(args) > 1) {
warning("R script takes the first input as configuration file.")
}
filename = args[1]
}
data <- rjson::fromJSON(file=filename)
if (missing(env)){
env = globalenv()
}
for (name in names(data)) {
if (length(data[name]) > 1){
warning("Auptimizer only supports hyperparameter as a single value, not a list. Only first value is used")
}
# set to global variables
assign(name, data[name][[1]], envir = env)
}
return(data)
}
#' Report result to Auptimizer
#'
#' This function reports the results to Auptimizer. A numerical score will be converted to string automatically. If
#' you want to report multiple scores, use ',' to seperate them and only the first value will be used by Auptimzier for
#' Optimization.
#'
#'
#' Assume the R script is used as follows `./test.R config.json`, the script should look like below:
#'
#' \preformatted{
#' #!Rscript
#'
#' # hyperparameter definitions
#' get_config()
#' # use hyperparamter to train model,
#' # after training, return optimization target (e.g. validation accuracy) as score
#' print_result(score)
#' }
#'
#' @param score Score to be reported back to Auptimizer
#' @export
print_result <- function(score) {
message(paste("\n#Auptimizer:", score))
}
================================================
FILE: R-src/Rpackage/man/get_config.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/auptimizer.R
\name{get_config}
\alias{get_config}
\title{Get hyperparameter configuration}
\usage{
get_config(filename, env)
}
\arguments{
\item{filename}{optional JSON filename contains hyperparameters as dict, default to first argument in command line.}
\item{env}{optional environment contains hyperparameters, de fault to global env.}
}
\value{
A matrix of the infile
}
\description{
This function loads a configuration file in JSON format. It assumes that the first argument of the running script is
the configuraiton file. It will parse and inject the hyperparameter variables into the global environment and return
them.
}
\details{
Assume an R script is used as `./test.R config.json`, the script should look like below:
\preformatted{
#!Rscript
# hyperparameter definitions
get_config()
# use hyperparamter to train model,
# after training, return optimization target (e.g. validation accuracy) as score
print_result(score)
}
}
================================================
FILE: R-src/Rpackage/man/print_result.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/auptimizer.R
\name{print_result}
\alias{print_result}
\title{Report result to Auptimizer}
\usage{
print_result(score)
}
\arguments{
\item{score}{Score to be reported back to Auptimizer}
}
\description{
This function reports the results to Auptimizer. A numerical score will be converted to string automatically. If
you want to report multiple scores, use ',' to seperate them and only the first value will be used by Auptimzier for
Optimization.
}
\details{
Assume an R script is used as `./test.R config.json`, the script should look like below:
\preformatted{
#!Rscript
# hyperparameter definitions
get_config()
# use hyperparamter to train model,
# after training, return optimization target (e.g. validation accuracy) as score
print_result(score)
}
}
================================================
FILE: R-src/Rpackage/tests/testthat/test_IO.R
================================================
# Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
library(auptimizer)
test_that("auptimizer output format", {
expect_message(print_result(1), '\n#Auptimizer: 1')
})
test_that("auptimizer input format", {
a <- 2
b <- TRUE
expect_equal(a, 2)
get_config("test_io.json", environment())
expect_equal(b, FALSE)
expect_equal(a, 1)
})
================================================
FILE: R-src/Rpackage/tests/testthat/test_io.json
================================================
{"a":1,"b":false}
================================================
FILE: R-src/Rpackage/tests/testthat.R
================================================
library(testthat)
library(auptimizer)
test_check("auptimizer")
================================================
FILE: R-src/Rpackage/vignettes/.gitignore
================================================
*.html
*.R
================================================
FILE: R-src/Rpackage/vignettes/auptimizer.Rmd
================================================
---
title: "Auptimizer"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{auptimizer}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
# R Package for Auptimizer
## Installation
*IMPORTANT:* Auptimizer is well tested on Unix or similar OS. Windows users may have to make some changes to run Aptimizer.
1. [Install Python Auptimizer](https://github.com/LGE-ARC-AdvancedAI/auptimizer)
2. Install R Auptimizer
```R
install.packages("auptimizer")
```
## Usage
The workflow for Auptimizer is the same as the Python version. The difference is in how to change existing R code to use Auptimizer.
1. Setup Python Auptimizer environment using `python -m aup.setup`
2. Change your R script:
a. Make all hyperparameters global variables.
b. Add `#!/usr/bin/env Rscript` as the first line.
c. Add `library("auptimizer")`.
d. Add `get_config()`, which will automatically update the hyperparameters (set globally in step 2a).
e. Add `print_result(score)` to return the target score you want to optimize for your script.
f. Change file permission using `chmod u+x `.
g. Add them into an Auptimizer experiment using `python -m aup.init`.
3. Run Auptimizer using `python -m aup experiment.json`.
## Examples
There are more examples in the `R-src/example` folder on the [Auptimizer Github repo](https://github.com/LGE-ARC-AdvancedAI/auptimizer/).
+ `exp_ridge.R` for synthetic Ridge regression. Run as `python -m aup ridge.json`.
+ `exp_rosenbrock.R` for analytic Rosenbrock function. Run as `python -m aup rosenbrock.json`.
================================================
FILE: R-src/examples/exp_ridge.R
================================================
#!/usr/bin/env Rscript
# Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
#
# Example code to use Auptimzier.
library("auptimizer")
source("ridgeRegression.R")
# Synthetic Data
Nt = 90
D = 100
w<-rnorm(D)
trainX<-matrix(rnorm(Nt*D,0,1),nrow=Nt,ncol=D)
trainy<-trainX%*%w + rnorm(Nt,0,1) # Add Gaussian noise
Nv = 40
valX<-matrix(rnorm(Nv*D,0,1),nrow=Nv,ncol=D)
valy<-valX%*%w + rnorm(Nv,0,1) # Add Gaussian noise
# Hyperparameters
lambda = 1.0
hp <- get_config()
score <- ridgeRegression(trainX,trainy,valX,valy,lambda)
print_result(score)
================================================
FILE: R-src/examples/exp_rosenbrock.R
================================================
#!/usr/bin/env Rscript
# Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
#
# Example code to use Auptimzier.
library("auptimizer")
source("rosenbrock.R")
# Hyperparameters
x <- 10
y <- 20
get_config()
score <- rosen(c(x, y))
print_result(score)
================================================
FILE: R-src/examples/ridge.json
================================================
{
"script": "exp_ridge.R",
"resource": "cpu",
"n_parallel": 1,
"target": "min",
"workingdir": "./",
"proposer": "random",
"n_samples": 10,
"random_seed": 0,
"parameter_config": [
{
"name": "lambda",
"range": [
0.001,
10
],
"type": "float"
}
]
}
================================================
FILE: R-src/examples/ridgeRegression.R
================================================
# Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
ridgeRegression<-function(trainX,trainy,valX,valy,lambda){
#######################################################
# A Typical Ridge Regression Problem
#######################################################
# N = No. of Training samples, D = Dimension of Problem
N<-nrow(trainX)
D <-ncol(trainX)
I <- diag(D)
# Closed form solution for Ridge regression: min (1/2N)*sum_i (w'*x_i - y_i)^2 + (lambda/2)*||w||^2
w <- solve((t(trainX)%*%trainX)/N + lambda*I) %*% ((t(trainX)%*%trainy)/N)
# Predict on the validation samples and return the error.
valErr<-MSE(valX,valy,w)
return(valErr)
}
MSE<-function(X,y,w){
#####################################################
# This is the mean squared error
#####################################################
N <- nrow(X)
return((0.5*N)*norm(X%*%w-y))
}
================================================
FILE: R-src/examples/rosenbrock.R
================================================
rosen <- function(xx)
{
##########################################################################
#
# ROSENBROCK FUNCTION
#
# Authors: Sonja Surjanovic, Simon Fraser University
# Derek Bingham, Simon Fraser University
# Questions/Comments: Please email Derek Bingham at dbingham@stat.sfu.ca.
#
# Copyright 2013. Derek Bingham, Simon Fraser University.
#
# THERE IS NO WARRANTY, EXPRESS OR IMPLIED. WE DO NOT ASSUME ANY LIABILITY
# FOR THE USE OF THIS SOFTWARE. If software is modified to produce
# derivative works, such modified software should be clearly marked.
# Additionally, 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; version 2.0 of the License.
# Accordingly, 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.
#
# For function details and reference information, see:
# http://www.sfu.ca/~ssurjano/
#
##########################################################################
#
# INPUT:
#
# xx = c(x1, x2, ..., xd)
#
##########################################################################
d <- length(xx)
xi <- xx[1:(d-1)]
xnext <- xx[2:d]
sum <- sum(100*(xnext-xi^2)^2 + (xi-1)^2)
y <- sum
return(y)
}
================================================
FILE: R-src/examples/rosenbrock.json
================================================
{
"script": "exp_rosenbrock.R",
"resource": "cpu",
"n_parallel": 1,
"target": "min",
"workingdir": "./",
"proposer": "random",
"n_samples": 10,
"random_seed": 0,
"parameter_config": [
{
"name": "x",
"range": [
0,
10
],
"type": "float"
},
{
"name": "y",
"range": [
0,
10
],
"type": "float"
}
]
}
================================================
FILE: README.md
================================================
# 
[](https://LGE-ARC-AdvancedAI.github.io/auptimizer)
[](https://opensource.org/licenses/GPL-3.0)
[](https://travis-ci.com/LGE-ARC-AdvancedAI/auptimizer)
[](https://codecov.io/gh/LGE-ARC-AdvancedAI/auptimizer)
**Auptimizer** is an optimization tool for Machine Learning (ML) that automates many of the tedious parts of the model building and deployment process.
Currently, **Auptimizer** helps with:
+ **Getting the best models in minimum time** - Generate optimal models and achieve better performance by employing
state-of-the-art hyperparameter optimization (HPO) and model compression techniques. Auptimizer will run and record sophisticated HPO and model compression experiments on compute resources of your choice with effortless consistency and reproducibility.
+ **Making your models edge-ready** - Get model-device compatibility and enhanced on-device performance by converting models into the industry-standard ONNX and TensorFlow Lite formats. Auptimizer-Converter provides validated conversion techniques to ensure worry-free format transformations.
+ **Selecting the most suitable model for your edge deployment effortlessly** - Compare how different models will perform under specific compute and memory constraints on a CPU-based edge device. Auptimizer-Profiler will help you identify the most efficient models without the hustle of going through multiple physical deployment cycles.
Best of all, **Auptimizer** offers a consistent interface that allows users to switch between different HPO and compression algorithms, conversion frameworks, and computing resources with minimal changes to the existing code.
## What's New in Auptimizer v2.0
Auptimizer v2.0 introduces two core capabilities - Dashboard and Compressor!
### Dashboard
[**Auptimizer Dashboard**](https://lge-arc-advancedai.github.io/auptimizer/dashboard.html) is a powerful analytics tool that complements Auptimizer's core hyperparameter optimization (HPO) and model compression capabilities. It provides insightful visualizations to help you track experiment progress, analyze and contrast jobs, experiments, and optimization approaches. Additionally, it can be used to control an experiment or even set up a new Auptimizer environment.
### Compressor
[**Compressor**](https://lge-arc-advancedai.github.io/auptimizer/compression_main.html) is a model compression tool that helps reduce memory complexity and inference time of neural networks. It is particularly useful for adapting ML models for deployment on resource-constrained edge devices.
Similar to Auptimizer-Hyperparameter Optimization (HPO), Compressor aims to provide a unified interface to the existing state-of-the-art toolkits. Currently, Compressor leverages [NNI (version 2.0)](https://nni.readthedocs.io/en/latest/model_compression.html) model compression modules. NNI is an open-source toolkit that supports two types of compression, pruning and quantization, for TensorFlow, and PyTorch models. In the future, we will be integrating other off-the-shelf toolkits to expand the selection of model compression approaches.
## Capabilities
### Hyperparameter Optimization
Auptimizer automates tedious experimentation by performing and recording hyperparameter optimization experiments. Auptimizer provides a single seamless access point to top-notch HPO algorithms, including Bayesian optimization and multi-armed bandit. You can even integrate your own proprietary solution. Moreover, with Auptimizer, you can make the best use of your compute-resources. Whether you are using a couple of GPUs or AWS, Auptimizer will help you orchestrate compute resources for faster hyperparameter tuning.
The table below shows a full list of currently supported techniques and resources for hyperparameter optimization.
| Supported HPO Algorithms | Supported Infrastructure |
| ----------- | ----------- |
| Random Grid [Hyperband](https://github.com/zygmuntz/hyperband) [Hyperopt](https://github.com/hyperopt/hyperopt) [Spearmint](https://github.com/JasperSnoek/spearmint) [BOHB](https://github.com/automl/HpBandSter) [EAS (experimental)](https://github.com/han-cai/EAS) Passive | Multiple CPUs Multiple GPUs Multiple Machines (SSH) AWS EC2 instances |
### Profiler
[**Profiler**](https://lge-arc-advancedai.github.io/auptimizer/profiler.html) is a simulator for profiling performance of machine learning model scripts. Given compute- and memory resource constraints for a CPU-based Edge device, Profiler can provide estimates of compute and memory usage for model scripts on the device. These estimations can be used to choose the best performing models or, in certain cases, to predict how much compute and memory models will use on the target device.
Because Profiler mimics the target device environment on the user's development machine, the user can gain insights into the performance and resource needs of a model script without having to deploy it on the target device. Profiler helps accelerate the model selection cycle and simplifies finding model-device fit. Please see [Profiler](https://github.com/LGE-ARC-AdvancedAI/auptimizer/tree/master/src/aup/profiler) for usages.
### Converter
[**Converter**](https://lge-arc-advancedai.github.io/auptimizer/dlconvert.html) is a format conversion tool for machine learning models. It encapsulates best practices of individual machine learning model conversions under a single API. Converter makes ML models suitable for edge (on-device) deployments by transforming them into the industry-standard ONNX and TensorFlow Lite formats and reducing model size through quantization.
## Install
**Auptimizer** currently is well tested on Linux systems, it may require some tweaks for Windows users.
```
pip install auptimizer
```
**Note** Dependencies are not included. Using `pip install`
[requirements.txt](https://github.com/LGE-ARC-AdvancedAI/auptimizer/blob/master/requirements.txt) will install
necessary libraries for all functionalities.
Usage for the UI dashboard:
```
dashboard --path --port
```
## Documentation
See more in [documentation](https://lge-arc-advancedai.github.io/auptimizer/)
## Example
```
cd Examples/demo
# Setup environment (Interactively create the environment file based on user input)
python -m aup.setup
# Setup experiment
python -m aup.init
# Create training script - auto.py
python -m aup.convert origin.py experiment.json demo_func
# Run aup for this experiment
python -m aup experiment.json
```
Each job's hyperparameter configuration is saved separately under `jobs/*.json` and is also recorded in the SQLite file `.aup/sqlite3.db`.

More examples are under [Examples](https://github.com/LGE-ARC-AdvancedAI/auptimizer/tree/master/Examples).
## License
[GPL 3.0 License](./LICENSE)
## Cite
If you have used this software for research, please cite the following paper (accepted at IEEE Big Data 2019):
```
@misc{liu2019auptimizer,
title={Auptimizer -- an Extensible, Open-Source Framework for Hyperparameter Tuning},
author={Jiayi Liu and Samarth Tripathi and Unmesh Kurup and Mohak Shah},
year={2019},
eprint={1911.02522},
archivePrefix={arXiv},
primaryClass={cs.LG}
}
```
================================================
FILE: docs/Database/schema.dot
================================================
/*
* Graphviz of '<_io.TextIOWrapper name='' mode='r' encoding='UTF-8'>', created 2018-08-14 18:52:17.684968
* Generated from https://github.com/rm-hull/sql_graphviz
*/
digraph g { graph [ rankdir = "LR" ];
"user" [
shape=none
label=<
user
uid INTEGER PRIMARY KEY NOT NULL
name TEXT UNIQUE
permission BLOB
>];
"resource" [
shape=none
label=<
resource
rid INTEGER PRIMARY KEY NOT NULL
name TEXT
type TEXT
status TEXT
>];
"experiment" [
shape=none
label=<
experiment
eid INTEGER PRIMARY KEY NOT NULL
uid INTEGER
start_time INTEGER
end_time INTEGER
exp_config BLOB
>];
"job" [
shape=none
label=<
job
jid INTEGER PRIMARY KEY NOT NULL
score REAL
eid INTEGER
rid INTEGER
start_time INTEGER
end_time INTEGER
job_config BLOB
>];
"job_attempt" [
shape=none
label=<
job_attempt
jaid INTEGER PRIMARY KEY NOT NULL
jid INTEGER
num INTEGER
rid INTEGER
start_time INTEGER
end_time INTEGER
>];
"intermediate_result" [
shape=none
label=<
intermediate_result
irid INTEGER PRIMARY KEY NOT NULL
num INTEGER
score REAL
jid INTEGER
receive_time INTEGER
>];
"multiple_result" [
shape=none
label=<
multiple_result
mrid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
label_order INTEGER
value REAL
receive_time INTEGER
jid INTEGER
irid INTEGER
eid INTEGER
is_last_result INTEGER
>];
"experiment":uid -> "user":uid;
"job":eid -> "experiment":eid;
"job":rid -> "resource":rid;
"job":jid -> "job_attempt":jid;
"job":jid -> "intermediate_result":jid;
"job":jid -> "multiple_result":jid;
"intermediate_result":irid -> "multiple_result":irid
}
================================================
FILE: docs/Database/schema.sql
================================================
CREATE TABLE user
(uid INTEGER PRIMARY KEY NOT NULL, name TEXT UNIQUE, permission BLOB);
CREATE TABLE resource
(rid INTEGER PRIMARY KEY NOT NULL, name TEXT, type TEXT, status TEXT);
CREATE TABLE experiment
(eid INTEGER PRIMARY KEY NOT NULL, uid INTEGER, start_time INTEGER, end_time INTEGER, exp_config BLOB,
FOREIGN KEY(uid) REFERENCES user(uid));
CREATE TABLE job
(jid INTEGER PRIMARY KEY NOT NULL, score REAL, eid INTEGER, rid INTEGER, start_time INTEGER, end_time INTEGER,
job_config BLOB,
FOREIGN KEY(eid) REFERENCES experiment(eid),
FOREIGN KEY(rid) REFERENCES resource(rid));
CREATE TABLE job_attempt
(jaid INTEGER PRIMARY KEY NOT NULL, jid INTEGER, num INTEGER, rid INTEGER, start_time INTEGER, end_time INTEGER,
FOREIGN KEY(jid) REFERENCES job(jid),
FOREIGN KEY(rid) REFERENCES resource(rid));
CREATE TABLE intermediate_result
(irid INTEGER PRIMARY KEY NOT NULL, num INTEGER, score REAL, jid INTEGER, receive_time INTEGER,
FOREIGN KEY(jid) REFERENCES job(jid));
CREATE TABLE multiple_result
(mrid INTEGER PRIMARY KEY NOT NULL, label_order INTEGER, value REAL, receive_time INTEGER,
jid INTEGER, irid INTEGER, eid INTEGER, is_last_result INTERGER,
FOREIGN KEY(jid) REFERENCES job(jid),
FOREIGN KEY(irid) REFERENCES intermediate_result(irid),
FOREIGN KEY(eid) REFERENCES experiment(eid));
================================================
FILE: docs/Database/sql_graphviz.py
================================================
#!/usr/bin/env python
# https://github.com/rm-hull/sql_graphviz
# run dot -Tpdf schema.dot > schema.pdf
import sys
from datetime import datetime
from pyparsing import alphas, alphanums, Literal, Word, Forward, OneOrMore, ZeroOrMore, CharsNotIn, Suppress, QuotedString, Optional
def field_act(s, loc, tok):
return '{0} {1} '.format(tok[0].replace('"', ''), ' '.join(tok[1::]).replace('"', '\\"'))
def field_list_act(s, loc, tok):
return "\n ".join(tok)
def create_table_act(s, loc, tok):
return '''
"{tableName}" [
shape=none
label=<
>];'''.format(**tok)
def add_fkey_act(s, loc, tok):
return ' "{tableName}":{keyName} -> "{fkTable}":{fkCol}'.format(**tok)
def other_statement_act(s, loc, tok):
return ""
def grammar():
parenthesis = Forward()
parenthesis <<= "(" + ZeroOrMore(CharsNotIn("()") | parenthesis) + ")"
field_def = OneOrMore(Word(alphanums + "_\"'`:-") | parenthesis)
field_def.setParseAction(field_act)
tablename_def = ( Word(alphas + "`_") | QuotedString("\"") )
field_list_def = field_def + ZeroOrMore(Suppress(",") + field_def)
field_list_def.setParseAction(field_list_act)
create_table_def = Literal("CREATE") + "TABLE" + tablename_def.setResultsName("tableName") + "(" + field_list_def.setResultsName("fields") + ")" + ";"
create_table_def.setParseAction(create_table_act)
add_fkey_def = Literal("FOREIGN") + "KEY" + "(" + Word(alphanums).setResultsName("keyName") + ")" + "REFERENCES" + Word(alphanums).setResultsName("fkTable") + "(" + Word(alphanums + "_").setResultsName("fkCol") + ")" + Optional(Literal("DEFERRABLE")) + ";"
add_fkey_def.setParseAction(add_fkey_act)
other_statement_def = OneOrMore(CharsNotIn(";")) + ";"
other_statement_def.setParseAction(other_statement_act)
comment_def = "--" + ZeroOrMore(CharsNotIn("\n"))
comment_def.setParseAction(other_statement_act)
return OneOrMore(comment_def | create_table_def | add_fkey_def | other_statement_def)
def graphviz(filename):
print("/*")
print(" * Graphviz of '%s', created %s" % (filename, datetime.now()))
print(" * Generated from https://github.com/rm-hull/sql_graphviz")
print(" */")
print("digraph g { graph [ rankdir = \"LR\" ];")
for i in grammar().parseFile(filename):
if i != "":
print(i)
print("}")
if __name__ == '__main__':
filename = sys.stdin if len(sys.argv) == 1 else sys.argv[1]
graphviz(filename)
================================================
FILE: docs/Dockerfile
================================================
FROM nginx
COPY _build/html /usr/share/nginx/html
# fix version urls
RUN mkdir /usr/share/nginx/html/archive
RUN mv /usr/share/nginx/html/_static/*.whl /usr/share/nginx/html/archive
RUN mv /usr/share/nginx/html/_static/aup.py /usr/share/nginx/html/archive/
================================================
FILE: docs/LICENSE
================================================
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
.
================================================
FILE: docs/Makefile
================================================
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = ContinuousTrainingEngine
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
# pandoc ../README.md -o tmp.rst;sed 's/docs\///g' tmp.rst > README.rst;rm tmp.rst
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
================================================
FILE: docs/README.rst
================================================
Auptimizer Quickstart
=====================
|GPL 3.0 License| |pipeline status| |coverage report| |repo url|
**Auptimizer** is an optimization tool for Machine Learning (ML) that automates many of the tedious parts of the model building and deployment process.
Currently, **Auptimizer** helps with:
- **Getting the best models in minimum time** - Generate optimal models
and achieve better performance by employing state-of-the-art hyperparameter
optimization (HPO) and model compression techniques techniques. Auptimizer will run and record sophisticated
HPO and model compression experiments on compute resources of your choice with effortless consistency
and reproducibility.
- **Making your models edge-ready** - Get model-device compatibility and
enhanced on-device performance by converting models into the industry-standard
ONNX and TensorFlow Lite formats. Auptimizer-Converter provides validated
conversion techniques to ensure worry-free format transformations.
- **Selecting the most suitable model for your edge deployment effortlessly**
- Compare how different models will perform under specific compute and memory
constraints on a CPU-based edge device. Auptimizer-Profiler will help you identify
the most efficient models without the hustle of going through multiple physical
deployment cycles.
Best of all, **Auptimizer** offers a consistent interface that allows
users to switch between different HPO and compression algorithms, conversion frameworks,
and computing resources with minimal changes to the existing code.
What's New in Auptimizer v2.0
-----------------------------
Auptimizer v2.0 introduces two core capabilites - Dashboard and Compressor!
Dashboard
~~~~~~~~~
`Auptimizer Dashboard `__
is a powerful analytics tool that complements Auptimizer's core hyperparameter optimization
(HPO) and model compression capabilities. It provides insightful visualizations to help you
track experiment progress, analyze and contrast jobs, experiments, and optimization approaches.
Additionally, it can be used to control an experiment or even set up a new Auptimizer environment.
Compressor
~~~~~~~~~~
`Compressor `__
is a model compression tool that helps reduce memory complexity and inference time of
neural networks. It is particularly useful for adapting ML models for deployment on
resource-constrained edge devices.
Similar to Auptimizer-Hyperparameter Optimization (HPO), Compressor aims to provide
a unified interface to the existing state-of-the-art toolkits. Currently, Compressor
leverages `NNI (version 2.0) `__
model compression modules. NNI is an open-source toolkit that supports two types of compression,
pruning and quantization, for TensorFlow, and PyTorch models. In the future, we will be integrating
other off-the-shelf toolkits to expand the selection of model compression approaches.
Capabilities
------------
Hyperparameter Optimization
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Auptimizer automates tedious experimentation by performing and recording
hyperparameter experiments. Auptimizer provides a single seamless access
point to top-notch HPO algorithms, including Bayesian optimization and multi-armed
bandit. You can even integrate your own proprietary solution. Moreover, with
Auptimizer, you can make the best use of your compute-resources. Whether you are
using a couple of GPUs or AWS, Auptimizer will help you orchestrate compute resources
for faster hyperparameter tuning.
The table below shows a full list of currently supported techniques and resources
for hyperparameter optimization.
+----------------------------------------------------------------+-----------------------------------+
| Supported HPO Algorithms | Supported Infrastructure |
+================================================================+===================================+
| | Random | | Multiple CPUs |
| | Grid | | Multiple GPUs |
| | `Hyperband `__ | | Multiple Machines (SSH) |
| | `Hyperopt `__ | | EC2 instances |
| | `Spearmint `__ | |
| | `BOHB `__ | |
| | `EAS (experimental) `__ | |
| | Passive | |
+----------------------------------------------------------------+-----------------------------------+
Profiler
~~~~~~~~
`Profiler `__
is a simulator for profiling performance of machine learning model scripts. Given compute- and memory
resource constraints for a CPU-based Edge device, Profiler can provide estimates of compute and memory
usage for model scripts on the device. These estimations can be used to choose best performing models or,
in certain cases, to predict how much compute and memory models will use on the target device.
Because Profiler mimics the target device environment on the user's development machine, the user
can gain insights about the performance and resource needs of a model script without having to
deploy it on the target device. Profiler helps accelerate the model selection cycle and simplifies
finding model-device fit. Please see :doc:`prof_readme` for usages.
Converter
~~~~~~~~~
`Converter `__
is a format conversion tool for machine learning models. It encapsulates best practices of individual
machine learning model conversions under a single API. Converter makes ML models suitable for edge
(on-device) deployments by transforming them into the industry-standard ONNX and TensorFlow Lite formats
and reducing model size through quantization. Please see :doc:`dlconvert_readme` for usages.
Install
-------
**Auptimizer** currently is well tested on Linux systems, it may require
some tweaks for Windows users.
::
pip install auptimizer
**Note** Dependencies are not included. Using ``pip install -r``
`requirements.txt `_ will install
necessary libraries for all functionalities.
Example
-------
::
cd Examples/demo
# Setup environment (Interactively create the environment file based on user input)
python -m aup.setup
# Setup experiment
python -m aup.init
# Create training script - auto.py
python -m aup.convert origin.py experiment.json demo_func
# Run aup for this experiment
python -m aup experiment.json
Each job’s hyperparameter configuration is saved separately under
``jobs/*.json`` and is also recorded in the SQLite file
``.aup/sqlite3.db``.
.. figure:: ./images/demo.gif
:alt: demo
License
-------
`GPL 3.0 License <./LICENSE>`__
.. |GPL 3.0 License| image:: https://img.shields.io/badge/License-GPL%203.0-blue.svg
:target: https://opensource.org/licenses/GPL-3.0
.. |pipeline status| image:: https://travis-ci.org/LGE-ARC-AdvancedAI/auptimizer.svg?branch=master
:target: https://travis-ci.org/LGE-ARC-AdvancedAI/auptimizer
.. |coverage report| image:: https://codecov.io/gh/LGE-ARC-AdvancedAI/auptimizer/branch/master/graph/badge.svg
:target: https://codecov.io/gh/LGE-ARC-AdvancedAI/auptimizer
.. |repo url| image:: https://img.shields.io/badge/github-repo-information.svg
:target: https://github.com/LGE-ARC-AdvancedAI/auptimizer
Cite
----
If you have used this software for research, please cite the following paper (accepted at IEEE Big Data 2019):
.. code-block:: none
@misc{liu2019auptimizer,
title={Auptimizer -- an Extensible, Open-Source Framework for Hyperparameter Tuning},
author={Jiayi Liu and Samarth Tripathi and Unmesh Kurup and Mohak Shah},
year={2019},
eprint={1911.02522},
archivePrefix={arXiv},
primaryClass={cs.LG}
}
================================================
FILE: docs/_static/.gitkeep
================================================
================================================
FILE: docs/algorithm.rst
================================================
Configure HPO Algorithm
=======================
Supported algorithms
--------------------
**Auptimizer** supports a number of different HPO algorithms. The names and descriptions are listed below:
=========== ============================================================================================================================
Name Algorithm
=========== ============================================================================================================================
passive Manually run job (for debug purpose)
random Random search
sequence Grid Search
spearmint `Spearmint `_: Bayesian Optimization based on Gaussian Process
bohb `HpBandSter `_: Bayesian Optimization and HyperBand
hyperopt `Hyperopt `_: Bayesian Optimization with Tree of Parzen Estimators (TPE)
hyperband `Hyperband `_: Multi-armed bandit approach
eas `EAS `_: Efficient Architecture Search by Network Transformation (Illustration purpose)
=========== ============================================================================================================================
Use ``python -m aup.init`` to set up the experiment configuration interactively.
For finer control, advanced users can change the configuration manually by directly modifying the ``experiment.json``
file.
Configuration details
---------------------
Below we cover the most common pieces. For requirements related to specific algorithms, please refer to the respective
documentation.
The general structure of the configuration file is as follows::
{
"proposer": "random",
"n_samples": 10,
"random_seed": 1,
"script": "auto.py",
"parameter_config": [
{
"name": "x",
"range": [
-5,
5
],
"type": "float"
}
],
"resource": "cpu",
"resource_args": {
"save_model": true
},
"job_failure": {
"job_retries": 3,
"ignore_fail": true
},
"n_parallel": 3,
"target":"min",
"workingdir:"./"
}
================ ======== ==============================================================================
Name Default Explanation
================ ======== ==============================================================================
proposer random hpo method used to propose new hyperparameter values (see below for full list)
n_samples 10 number of jobs to run
script - script to run
n_parallel 1 number of parallel jobs
job_retries 0 number of retries for failed jobs.
ignore_fail False whether to continue the experiment if a job fails.
target max search for max or min
resource - type of resource to run the experiment, [cpu, gpu, aws, node, passive]
parameter_config {} hyperparameter specification (see below)
workingdir "./" path to run the script, important for running jobs remotely (SSH/AWS)
resource_args {} other parameters to enable features like tracking intermediate results, saving best model, etc (see below)
================ ======== ==============================================================================
for ``parameter_config``:
================= ======================================================================================
Name Content
================= ======================================================================================
name name of the hyperparameter variable. Must be the same as used in the training script
range [min, max] or a list of values
type `float`, `int`, `choice` types are supported
================= ======================================================================================
Minor modifications or changes may be required for each algorithm. These options can be found at the corresponding API
pages under :doc:`aup.Proposer` (see API links below).
for ``resource_args``:
========================== ======== ==============================================================================
Name Default Explanation
========================== ======== ==============================================================================
save_model False whether to save the best performing model (see below)
multi_res_labels None a list of additional results to be tracked, e.g. ["flops", "param"]
track_intermediate_results False if true, intermediate results during training epoches will be tracked
early_stop None parameters related to early stopping strategies
========================== ======== ==============================================================================
For details of the ``early_stop`` parameter and how to apply early stopping strategies to HPO experiments, please refer to
:doc:`Early Stopping `.
``resource_args`` can also include SSH/AWS specific parameters, please refer to :ref:`AWSRuntimeAnchor` for more details.
**Note**:
| If ``job_failure`` is not specified, the experiment will stop whenever a job fails.
| For ``job_retries``, preferance is given to a different resource, if multiple resources are available.
| For ``ignore_fail``, currently [BOHB, EAS, Hyperband] proposers do not support experiment continuation upon job failure.
Additional functionalities
--------------------------
Track intermediate results
~~~~~~~~~~~~~~~~~~~~~~~~~~
This feature allows the user to save and track multiple intermediate results at different points during the HPO experiment. Auptimizer still uses the final result as the main result for the HPO algorithm, but saves the intermediate records in the database under the table ``intermediate_results``.
Usage
@@@@@
The feature can be used by adding the following parameter to the experiment configuration file::
"resource_args": {
"track_intermediate_results": true
}
Then in the training script, ``aup.print_result(res)`` should be placed where the user wants the results to be tracked::
def main(*args, **kwargs):
# model and data preparation
for epoch in range(n_epochs):
# training for one epoch
aup.print_result(res)
In the above example, the intermediate results are returned every epoch. The result at last epoch is regarded as the main result for the user script and is then used by the HPO algorithm.
The intermediate results will be shown on the dashboard if tracked.
**Note**: It is possible to use multiple results feature in conjunction with intermediate results to track multiple intermediate results as well.
Save the best model
~~~~~~~~~~~~~~~~~~~
This feature allows the user to save the best performing model after running the HPO experiment. This is achieved by
running the training script again using the best hyperparamters obtained during HPO the experiment.
The model, by default, will be saved to path ``aup_models/models_/``.
Usage
@@@@@
In order to use this feature, please add the following parameter to the experiment configuration file::
"resource_args": {
"save_model": true
}
Depending on whether the ``@aup_args`` decorator is used, the training script needs the following additional modifications.
If ``@aup_args`` is used, the user needs to define a funtion to save the model, and register this function with ``aup_save_model``.
We suggest using this approach if running the experiment on remote machines (SSH/AWS) to be able to correctly locate and retrieve the model saved
on the remote machine.
Please see the example below::
# define a function "save_model(model)" to save the model to a user-defined path
def save_model(model):
os.makedirs('model_train')
model.save('./model_train/mnist.h5')
@aup_args
def main(*args, **kwargs):
# training code
...
# register the model saving function with model as argument
aup.aup_save_model(save_model, model)
...
If ``@aup_args`` is not used, the user needs to manually check whether the ``save_model`` parameter is True in the job's
configuration. The main function should also take ``save_model`` and ``folder_name`` as arguments. Please see the
example below::
def main(*args, **kwargs, save_model=False, folder_name=None):
# training code
...
if save_model is True:
# manually locate the path for saving the model
# this is important if running on remote machines
path = os.path.join('aup_models', folder_name)
if os.path.exists('aup_models') is False:
os.makedirs('aup_models')
if os.path.exists(path) is True:
shutil.rmtree(path)
os.makedirs(path)
os.chdir(path)
model.save('./model_train/mnist.h5')
...
Return multiple results
~~~~~~~~~~~~~~~~~~~~~~~
This feature allows the user to save and track multiple secondary results along with the primary result for the HPO experiment. Auptimizer still uses the main result for the HPO algorithm, but saves the secondary results in the database under the table ``multiple_results``. There is no upper limit
on how many secondary results the user can track.
Usage
@@@@@
The feature can be used by adding the following parameter to the experiment configuration file::
"resource_args": {
"multi_res_labels": ["x", "y"]
}
In the above configuration file, ``x`` and ``y`` are the secondary results the user wants to track and record. The user script would then return the results as a list including the primary result ``res`` along with the secondary parameters as follows::
@aup_args
def HPO():
res = calculate_results()
return [res, x, y]
In the above example, ``res`` is the primary result which is always placed at the first index of the returned list, which will be used by the HPO algorithm. The remaining results are matched directly with the list provided in ``multi_res_labels``. Hence, the length of the returned list from user script is 1 + length of ``multi_res_labels`` parameter.
**Note**: It is possible to use multiple results feature in conjunction with intermediate results to track multiple intermediate results as well.
Pause and resume jobs
~~~~~~~~~~~~~~~~~~~~~
+ Serial: optimize parameters by running jobs sequentially
+ Parallel: optimize parameters by running jobs in parallel
+ Pause: pause and save current HPO status
+ Resume: resume previously paused HPO process
+-----------+-------------------------------------------------+--------+----------+--------------+--------+
| Algorithm | Documentation | Serial | Parallel | Pause (save) | Resume |
+===========+=================================================+========+==========+==============+========+
| Random | :class:`aup.Proposer.RandomProposer` | |Y| | |Y| | |Y| | |Y| |
+-----------+-------------------------------------------------+--------+----------+--------------+--------+
| Sequence | :class:`aup.Proposer.SequenceProposer` | |Y| | |Y| | |Y| | |Y| |
+-----------+-------------------------------------------------+--------+----------+--------------+--------+
| Passive | :class:`aup.EE.Resource.PassiveResourceManager` | |Y| | |Y| | |Y| | |Y| |
+-----------+-------------------------------------------------+--------+----------+--------------+--------+
| Spearmint | :class:`aup.Proposer.SpearmintProposer` | |Y| | |Y| | |N| | |N| |
+-----------+-------------------------------------------------+--------+----------+--------------+--------+
| Hyperopt | :class:`aup.Proposer.HyperoptProposer` | |Y| | |Y| | |N| | |N| |
+-----------+-------------------------------------------------+--------+----------+--------------+--------+
| Hyperband | :class:`aup.Proposer.HyperbandProposer` | |Y| | |Y| | |N| | |N| |
+-----------+-------------------------------------------------+--------+----------+--------------+--------+
| BOHB | :class:`aup.Proposer.BOHBProposer` | |Y| | |Y| | |N| | |N| |
+-----------+-------------------------------------------------+--------+----------+--------------+--------+
| EAS | :class:`aup.Proposer.EASProposer` | |Y| | |N| | |N| | |N| |
+-----------+-------------------------------------------------+--------+----------+--------------+--------+
.. |Y| unicode:: U+2713 .. checked
.. |N| unicode:: U+274C .. no check
.. |?| unicode:: U+274C .. check pending
================================================
FILE: docs/archive/aup.py
================================================
"""
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
Auptimizer client side functions
================================
This file can be copied to a remote machine instead of installing the whole Auptimizer package for job execution.
APIs
----
"""
from __future__ import print_function
import logging
import json
import pickle
import sys
import inspect
import functools
import os
import shutil
logger = logging.getLogger("aup-minimal")
# supported data loading format
_SUPPORT_FORMAT = ("pkl", "json")
global user_callback_fn
global user_args
global user_kwargs
user_callback_fn = None
user_args = []
user_kwargs = {}
def print_result(result):
"""Function to print the result for :func:`parse_result`.
This function should be the last line of your training code
:param result: result from training code
:type result: str
"""
if result is list:
result = ','.join([str(r) for r in result])
else:
result = str(result).lstrip() # avoid line break
# force flush to get intermediate results in real time
print("\n#Auptimizer:%s" % result, file=sys.stderr, flush=True)
class BasicConfig(dict):
"""
User-friendly :class:`dict` supports:
* load and save for json/pickle format (.json/.pkl)
* easy key/value access as config.key or config["key"]
* compatible with :class:`dict`
:param kwargs: key-value pairs to initialize the configuration
:type kwargs: dict
"""
def load(self, filename):
"""Load config parameters from JSON/pickle file
:param filename: file name ends with [.json|.pkl]
:type filename: string
:return: configuration parsed from file
:rtype: aup.BasicConfig
"""
name = "_load_" + BasicConfig._get_format(filename)
func = getattr(self, name)
data = func(filename)
if type(data) is not dict:
raise TypeError("Config must be dict")
self.update(data)
logger.debug("Load config from %s: %s" % (filename, data.__str__()))
return self
def save(self, filename):
"""
Save configuration as dict in JSON/pickle
:param filename: file name ends with [.json|.pkl]
:type filename: string
"""
name = "_save_" + BasicConfig._get_format(filename)
func = getattr(self, name)
func(filename)
logger.debug("Config saved to %s" % filename)
@staticmethod
def _get_format(filename):
name = filename.split(".")[-1].lower()
if name not in _SUPPORT_FORMAT:
raise ValueError("Un-support file format, choose from %s." % ",".join(_SUPPORT_FORMAT))
return name
@staticmethod
def _load_json(filename):
with open(filename, 'r') as f:
return json.load(f)
@staticmethod
def _load_pkl(filename):
with open(filename, 'rb') as f:
return pickle.load(f)
def _save_json(self, filename):
with open(filename, 'w') as f:
json.dump(self, f)
def _save_pkl(self, filename):
with open(filename, 'wb') as f:
pickle.dump(dict(self), f)
@staticmethod
def save_flags(filename):
"""
Save tf flags for reuse - not used, not tested
:param filename: output file
"""
from absl import flags
logger.info("Write flags into %s")
with open(filename, 'w') as f:
f.write(flags.FLAGS.flags_into_string())
def to_flags(self, FLAGS):
"""
Update values in FLAGS from BasicConfig
:param FLAGS: tensorflow/absl FLAGS
"""
for i in FLAGS:
if i in self:
logger.debug("set %s in FLAGS", i)
setattr(FLAGS, i, self[i])
else:
logger.debug("Use default %s", i)
def __setattr__(self, key, value):
self.__setitem__(key, value)
def __getattr__(self, key):
return self.__getitem__(key)
def __delattr__(self, key):
self.__delitem__(key)
def __hash__(self):
return super(BasicConfig, self).__hash__()
def aup_args(func):
"""Decorator to wrap optimization target function `func`.
Arguments:
func {function} -- A function computes optimization target with specified hyperparameters
"""
@functools.wraps(func)
def wrapper(filename, **kwargs):
"""wrapper function
Arguments:
filename {str} -- configuration file
kwargs {dict} -- additional arguments will overwrite existing configuration value
Raises:
ValueError: if a parameter is not assigned in config
"""
# get current frame stack
frm = inspect.stack()[1]
# get module from stack
mod = inspect.getmodule(frm[0])
# get functions that contain "init" in name and call them
functions_list = inspect.getmembers(sys.modules[mod.__name__], inspect.isfunction)
functions_list = sorted(list(filter(lambda x: "init" in x[0], functions_list)))
config = BasicConfig().load(filename)
if kwargs:
logger.critical("Overwritting config values from script, be cautious!")
config.update(kwargs)
for f in functions_list:
f[1](**config)
parameters = inspect.signature(func).parameters
for p in parameters.items():
if p[0] not in config:
if p[1].default is inspect.Parameter.empty:
raise ValueError("`%s` is required in `%s()` but is not assigned in config file %s" %
(p[0], func.__name__, filename))
logger.info("Using default value for %s", p[0])
run_config = dict()
for p in config:
if p in parameters:
run_config[p] = config[p]
else:
logger.warning("%s is not used in optimization"%p)
val = func(**run_config)
print_result(val)
save_model = config.get('save_model', False)
if save_model is True:
# this means this is the "best job" found
# the user wants to save the model
try:
dir = config.get('folder_name', None)
previous_dir = os.getcwd()
if os.path.exists(dir) is True:
logger.warning('Deleting {}'.format(dir))
shutil.rmtree(dir)
os.makedirs(dir)
os.chdir(dir)
assert user_callback_fn is not None, \
"Please use 'aup_save_model' to register the save model callback fn"
user_callback_fn(*user_args, **user_kwargs)
except Exception as e:
raise e
finally:
os.chdir(previous_dir)
return wrapper
def aup_flags(flags):
"""wrapper function for absl flags (or tf.app).
It will assign values to flags parameters using the given configuration file as the first argument when executed
from the command line.
Arguments:
args {list} -- a list of unused arguments passed by app.run()
"""
def decorator_wrapper(func):
@functools.wraps(func)
def wrapper(args):
config = BasicConfig(**flags.__dict__).load(args[1])
flags.__dict__.update()
parameters = inspect.signature(func).parameters
if parameters:
logger.warning("TF FLAG main() should not accept arguments with Auptimizer, it has %s",
parameters.keys())
val = func({p:None for p in parameters})
else:
val = func()
print_result(val)
return wrapper
return decorator_wrapper
def aup_save_model(callback_fn, *args, **kwargs):
global user_callback_fn
global user_args
global user_kwargs
user_callback_fn = callback_fn
user_args = args
user_kwargs = kwargs
================================================
FILE: docs/aup.EE.Experiment.rst
================================================
.. automodule:: aup.EE.Experiment
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.EE.Job.rst
================================================
.. automodule:: aup.EE.Job
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.EE.Resource.rst
================================================
Resource Managers
=================
.. automodule:: aup.EE.Resource.AbstractResourceManager
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.EE.Resource.CPUResourceManager
:members:
:show-inheritance:
.. automodule:: aup.EE.Resource.GPUResourceManager
:members:
:show-inheritance:
.. automodule:: aup.EE.Resource.SSHResourceManager
:members:
:show-inheritance:
.. automodule:: aup.EE.Resource.AWSResourceManager
:members:
:show-inheritance:
.. automodule:: aup.EE.Resource.PassiveResourceManager
:members:
:show-inheritance:
================================================
FILE: docs/aup.EE.rst
================================================
aup.EE - Experiment execution
=============================
Overview
--------
:mod:`aup.EE` supports all experiment execution-related code.
+ :mod:`aup.EE.Experiment` controls the flow of an experiment.
+ :mod:`aup.EE.Job` takes care of job execution.
+ :mod:`aup.EE.Resource` provides the backbone where :mod:`aup.EE.Job` runs on.
APIs
----
.. toctree::
:maxdepth: 1
aup.EE.Experiment
aup.EE.Job
aup.EE.Resource
================================================
FILE: docs/aup.ET.Connector.rst
================================================
aup.ET.Connector package
========================
.. automodule:: aup.ET.Connector.AbstractConnector
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.ET.Connector.SQLiteConnector
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.ET.rst
================================================
aup.ET - Experiment tracking package
====================================
.. toctree::
:caption: Modules for experiment tracking
aup.ET.Connector
================================================
FILE: docs/aup.Proposer.BOHBProposer.rst
================================================
.. automodule:: aup.Proposer.BOHBProposer
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.Proposer.EASProposer.rst
================================================
.. automodule:: aup.Proposer.EASProposer
:members:
:show-inheritance:
================================================
FILE: docs/aup.Proposer.HyperbandProposer.rst
================================================
.. automodule:: aup.Proposer.HyperbandProposer
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.Proposer.HyperoptProposer.rst
================================================
.. automodule:: aup.Proposer.HyperoptProposer
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.Proposer.RandomProposer.rst
================================================
.. automodule:: aup.Proposer.RandomProposer
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.Proposer.SequenceProposer.rst
================================================
.. automodule:: aup.Proposer.SequenceProposer
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.Proposer.SpearmintProposer.rst
================================================
.. automodule:: aup.Proposer.SpearmintProposer
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.Proposer.rst
================================================
aup.Proposer package
====================
The proposer is the core component to optimize hyperparameters for model training.
Use :func:`aup.Proposer.AbstractProposer.get_proposer` to initialize proposer.
All of them adopt the same interface as described below.
Proposers
---------
.. toctree::
:maxdepth: 1
aup.Proposer.HyperbandProposer
aup.Proposer.HyperoptProposer
aup.Proposer.RandomProposer
aup.Proposer.SequenceProposer
aup.Proposer.SpearmintProposer
aup.Proposer.BOHBProposer
aup.Proposer.EASProposer
.. automodule:: aup.Proposer.AbstractProposer
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.__main__.rst
================================================
.. automodule:: aup.__main__
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.compression.rst
================================================
Compression main entry
======================
.. automodule:: aup.compression.__main__
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.convert.rst
================================================
.. automodule:: aup.convert
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.dlconvert.rst
================================================
DLconvert - Model conversion
============================
.. automodule:: aup.dlconvert.__main__
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.dlconvert_API.rst
================================================
aup.dlconvert package
========================
.. automodule:: aup.dlconvert.checkpoint_to_onnx
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.checkpoint_to_pb
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.checkpoint_to_tflite
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.keras_to_onnx
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.keras_to_pb
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.keras_to_tflite
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.pb_to_onnx
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.pb_to_tflite
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.pytorch_to_keras
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.pytorch_to_onnx
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.pytorch_to_tflite
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.savedmodel_to_onnx
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.savedmodel_to_tflite
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.to_frozen_pb
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.to_onnx
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.dlconvert.to_tflite
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.init.rst
================================================
.. automodule:: aup.init
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.profiler.rst
================================================
Profiler - Profiling model scripts
==================================
.. automodule:: aup.profiler.__main__
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.rst
================================================
**Auptimizer** APIs
===================
Main Entry
----------
.. toctree::
:maxdepth: 1
aup.__main__
aup.compression
aup.setup
aup.init
aup.convert
aup.setupdb
aup.profiler
aup.dlconvert
Internal modules and packages
-----------------------------
.. toctree::
:maxdepth: 2
aup.EE
aup.ET
aup.Proposer
aup.utils
aup.setupdb_API
aup.dlconvert_API
================================================
FILE: docs/aup.setup.rst
================================================
.. automodule:: aup.setup
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.setupdb.rst
================================================
Database setup and reset
========================
.. automodule:: aup.setupdb.__main__
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.setupdb.reset
================================================
FILE: docs/aup.setupdb.sqlite.rst
================================================
.. automodule:: aup.setupdb.sqlite
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/aup.setupdb_API.rst
================================================
aup.setupdb package
===================
.. toctree::
:maxdepth: 1
aup.setupdb.sqlite
================================================
FILE: docs/aup.utils.rst
================================================
.. automodule:: aup.utils
:members:
:undoc-members:
:show-inheritance:
.. automodule:: aup.aup
:members:
:undoc-members:
:show-inheritance:
:imported-members:
================================================
FILE: docs/aup.visualize.rst
================================================
.. automodule:: aup.visualize
:members:
:undoc-members:
:show-inheritance:
================================================
FILE: docs/compression.rst
================================================
Compressor
==========
**Compressor** is a model compression tool that helps reduce memory complexity and inference time of neural networks.
With Compressor, you can:
1. **Make your ML models suitable for deployment on resource-constrained devices.** Use Compressor to optimize models for Edge device's limited memory, compute, or power and enable uncompromised on-device intelligence.
2. **Slash latency and enhance the user experience of your AI-powered application.** Tap Compressor's speed-up functionality to accelerate your model's inference time.
3. **Maximize the cost-effectiveness of your neural nets.** Cut down on cloud or on-prem model storage and compute costs by reducing their footprint.
Similar to Auptimizer-Hyperparameter Optimization (HPO), Compressor aims to provide a unified interface to the existing state-of-the-art toolkits. Currently, Compressor leverages `NNI (version 2.0) `__ model compression modules. NNI is an open-source toolkit that supports two types of compression, pruning and quantization, for TensorFlow, and PyTorch models. You can find more detail on supported compression algorithms (compressors) in the `NNI Compression documentation `__.
In the future, we will be integrating other off-the-shelf toolkits to expand the selection of model compression approaches.
How to run compression experiments
----------------------------------
Running a compression experiment is similar to running an HPO experiment and requires just a few steps:
1. Install Auptimizer and set up Auptimizer environment
2. Prepare an experiment configuration file
3. Modify a few lines in the training script
4. Run the experiment
Auptimizer also includes the NNI `compression utility functions `__ that will help
you design compression experiments more efficiently. These utility functions enable layer sensitivity
and channel dependency analysis, which can guide the selection of layers to be pruned and the target
sparsity levels. We recommend running this analysis to have a better understanding of the model architecture.
For more details, please check :doc:`Utility functions `.
Step #1. Installation and environment setup
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compressor is automatically installed as a part of Auptimizer.
For PyTorch compression algorithms, Pytorch version >= 1.7.0 is required. For
Tensorflow compression algorithms, TensorFlow version >= 2.0 is required.
Compression experiments use the same Auptimizer environment as the HPO experiments. Please refer
to the :doc:`Install and setup Auptimizer ` and :doc:`Set up environment
` sections for detail on how to set up your Auptimizer environment.
Step #2. Prepare an experiment configuration file
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compressor supports two compression paradigms:
1. one-time compression
2. automatic compression.
A **one-time compression** experiment runs one compression job with a fixed set of parameters.
Whereas an **automatic compression** experiment leverages Auptimizer's HPO
module to find the best possible parameters of a compression algorithm that generates the
best compressed model.
One-time approach is a good option for performing a dry-run or an experiment with a specific set of parameters. Alternatively, use automatic compression if you are not certain about
the compression parameters or would like to explore the relationship between compressed model performance
and different parameter settings.
Below, we explain the differences between one-time and automatic compression configuration files.
One-time compression configuration
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Here is an example of the configuration file for one-time compression using the `L1Filter` pruning method::
{
"name": "MNIST L1 Filter Pruner",
"script": "mnist.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "l1_filter",
"config_list": [
{
"sparsity": 0.8,
"op_types": ["Conv2d"]
},
{
"sparsity": 0.6,
"op_names": ["conv1", "conv2"]
},
{
"exclude": True,
"op_names": ["conv3"]
}
]
}
}
One-time compression experiment configurations take the following parameters, where all parameters except for **compression**
have the same meaning as in :doc:`Configure HPO Algorithm `:
+ **name**: name of the experiment
+ **script**: script to run
+ **resource**: type of resource to run the experiment, [cpu, gpu, passive, node]
+ **workingdir**: path to run the script, important for running jobs remotely (SSH/AWS)
+ **compression**: compression-specific parameters
+ **framework**: either "torch" or "tensorflow"
+ **type**: either "pruning" or "quantization"
+ **compressor**: string, one from the list of supported compression algorithms for the given
framework and type (see below)
+ **config_list**: a list of parameters which define the specific requirements for the chosen NNI
compression algorithm
The ``config_list`` parameter is specific to individual NNI compression algorithms. However, there a few parameters
common among all compressors:
+ **op_types**: list of strings, the names of the specific type of layers to be compressed.
If not specified, will use ``default`` as the value which denotes the default layer types
supported by the chosen compression algorithm.
+ **op_names**: list of strings, the names of the layers to be compressed. Will overwrite
``op_types`` if both are provided. The layer names can be found using ``model.state_dict().keys()``.
+ **exclude**: "True" or "False" (default is "False"). When set to "True", the layers
defined in ``op_types`` or ``op_names`` will be excluded from compression.
In the above example, "config_list" means pruning all layers of the type "Conv2d" to 0.8 sparsity, except for
layers named "conv1" and "conv2" which should be pruned to 0.6 sparsity and layer "conv3" which should be
excluded from the pruning.
Please refer to the `NNI docs `__
for more description of the "config_list" parameter. Each compressor will have
an example of its supported "config_list".
Automatic compression configuration
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Here is an example of the configuration file for automatic compression using the
`L1Filter` pruning method::
{
"name": "MNIST L1 Filter Pruner (automatic)",
"script": "mnist.py",
"resource": "cpu",
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "l1_filter",
"config_list": [
{
"sparsity": {
"range": [0.6, 0.8],
"type": "float"
}
"op_types": ['Conv2d']
},
{
"sparsity": {
"range": [0.1, 0.9],
"type": "float"
},
"op_names": ["conv1", "conv2"]
},
{
"exclude": True,
"op_names": ["conv3"]
}
]
},
"proposer": "random",
"n_parallel": 4,
"target": "max",
"n_samples": 4
}
An automatic compression experiment uses HPO to find the best hyperparameters of a chosen compression algorithm.
The experiment is launched as an HPO experiment, therefore, its configuration recognizes all parameters
in an HPO experiment (see :doc:`Configure HPO Algorithm ` for parameter definitions).
Some important parameters include:
+ **proposer**: HPO method used to propose new hyperparameter values
+ **target**: "min" or "max", minimizing or maximizing user-defined HPO metric
+ **n_samples**: total number of jobs to run
+ **n_parallel**: number of parallel jobs
Another notable difference in automatic compression configuration is that for the values of the
parameters in ``config_list``, a search space is defined via the following parameters:
+ **range**: [min, max] or a list of values
+ **type**: `float`, `int`, `choice` types are supported
Additional parameters may be needed for specific Proposers (see
:doc:`Configure HPO Algorithm `).
There are two potential scenarios for identifying the best hyperparameters. We will
use hyperparameter "sparsity" as an example. In the first scenario, the user may set the same search range for the sparsity for
a group of layers defined in ``op_names`` or ``op_types``, however, the user allows the Proposer to choose a different value in the defined
range for each layer in the group. While in the second scenario, the user would like to use the same sparsity value for
all layers in the group due to the dependency among those layers.
To handle these two scenarios, we introduce an additional parameter ``expand_op_names`` ("true" or "false", default is "true").
If set to "true", Auptimizer will propose a different hyperparameter value for each layer defined in the group; whereas
when set to "false", the same hyperparameter value will apply to all layers defined in the group.
For example, if the configuration is written as follows, in one job, the hyperparameter Proposer may assign sparsity value 0.2 and 0.4
to "conv1" and "conv2" layers, respectively. However, if the ``expand_op_names`` is set to "false" in the following example, the Proposer
will always assign the same value (e.g., 0.2) to both "conv1" and "conv2" layers::
[
{
'sparsity': {
'range': [0.1, 0.9],
'type': 'float'
},
'op_names': ['conv1', 'conv2'],
'expand_op_names': true,
'op_types': ['default']
}
]
Step #3. Modify the training script
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Only a few modifications to the training script are needed to run a compression experiment. The
modifications are the same for both one-time and automatic compression experiments. We first present
an example training script and then explain the changes below::
#!/usr/bin/env python #Step 1: add shebang line to make script executable
import aup #Step 2: import auptimizer package
def main(config): #Step 3a: the main function should take "config" as argument
... # code to generate model and load dataset ...
#Step 4: create a compressor and call the compress method to compress the model
compressor = aup.compression.create_compressor(model, config, optimizer=optimizer)
model = compressor.compress()
#Step 5 (optional): speed-up the model by removing zero weights
model = compressor.apply_speedup(dummy_input=torch.randn(1, 1, 28, 28).to(device))
... code for model fine-tuning after compression ...
#Step 6 (optional): export the compressed model and the mask
compressor.export_model(
model_path="model_compressed.pth",
mask_path="model_mask.pth",
speedup=True,
folder_name=".")
#Step 7: return the metric for HPO or any metric for one-time compression
aup.print_result(validation_acc)
# Step 3b: parse the configuration file and call the main function as follows
if __name__ == '__main__':
config = aup.BasicConfig().load(sys.argv[1])
main(config)
1. Add Shebang line ``#!/usr/bin/env python`` on top of the script and make the script executable
(``chmod u+x script.py``).
2. Import Auptimizer package by ``import aup``
3. Parse the configuration file using ``aup.BasicConfig.load(sys.argv[1])``.
4. Create the compressor and apply compression
+ this can happen before the optimizer is created:
``compressor = aup.compression.create_compressor(model, config)``
+ or after the optimizer is defined:
``compressor = aup.compression.create_compressor(model, config, optimizer=optimizer)``
Note: any additional arguments specifically required by the compression algorithms must be
passed here.
Apply compression by ``compressor.compress()``.
5. (Optional) Speed-up can be applied for pruned models:
``model = compressor.apply_speedup(dummy_input=torch.randn(*input_shape).to(device))``
This will modify the actual architecture of the model by removing zero parameters.
``dummy_input`` should be a ``pytorch tensor`` that conforms
to the model input shape. We recommend fine-tuning the model after applying model speed-up
as pruning zero parameters may affect the accuracy of the model.
Note: Not all pruners support speed-up, please refer to the **Model Speed-Up** section below for more detail.
6. (Optional) Export the model::
compressor.export_model(
model_path="model_compressed.pth",
mask_path="model_mask.pth",
speedup=True,
folder_name=".")
This saves the model to disk. Note that the speed-up is only applied if it has not been applied
yet; otherwise, the model is saved as it is.
+ ``model_path``: the path where the compressed model will be saved
+ ``mask_path``: is a pruning-only argument, the path where the pruning mask will be saved.
+ ``speedup``: must be present and True if speed-up has been applied; can be True if speed-up has not
been applied yet, and will apply speed-up before saving the model.
+ ``folder_name``: (optional) the directory relative to the working directory to save the model,
the model and mask files will be saved to ``working_directory/folder_name/model(mask)_path``.
+ ``dummy_input``: (optional) a ``pytorch tensor`` that conforms to the model input
shape required only for applying speedup when speedup has not been applied yet.
7. Return the final result or any intermediate result by using ``aup.print_result``:
+ For one-time compression, this result can be any metric the user would like to visualize on the dashboard
+ For automatic compression, this result should be the metric to use in HPO
A few compression algorithms require additional changes in training procedures.
Please refer to :doc:`Supported Compression Algorithms ` section for specific requirements
of each compressor.
Step #4. Run experiment
~~~~~~~~~~~~~~~~~~~~~~~
A one-shot compression experiment is run by issuing the ``aup.compression``
command::
python -m aup.compression experiment.json
Automatic compression experiments require the ``--automatic`` flag::
python -m aup.compression experiment.json --automatic
Advanced usages
---------------
Use decorator to modify training script
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An alternative way to pass the configuration file to the training script is to use the decorator
``aup_args`` with the following changes::
@aup.aup_args
def main(compression_type, compression_framework, compressor, config_list, folder_name = None, save_model = False):
config = locals()
...
if __name__ == '__main__':
main(sys.argv[1])
Save the best model
~~~~~~~~~~~~~~~~~~~
Automatic compression experiments can use the "save best model" feature in HPO. If this
feature is enabled, only the compressed model and mask obtained using the best hyperparameter
combination will be exported, instead of all the models and masks for every hyperparameter
combinations explored.
To use this feature, please make sure to define the following in the configuration file::
"resource_args": {
"save_model": true
}
There are two ways to modify the code for exporting only the best model and its mask:
+ In case the ``@aup_args`` decorator is used, then the compressor's export_model method
can be registered as a model saving function::
aup_save_model(
compressor.export_model,
model_path="model_compressed.pth",
mask_path="model_mask.pth",
speedup=False)
+ Alternatively, if the decorator is not used, apply the following code::
if "save_model" in config and config["save_model"]:
compressor.export_model(
model_path="model_compressed.pth",
mask_path="model_mask.pth",
speedup=False,
folder_name=config["folder_name"])
Model Speed-up
--------------
NNI compression provides a `model speedup module `__
which aims to export models with their architecture modified to reflect
the effect of pruning methods. Normally, users would export the model with its
structure unchanged and, for pruning, a mask of the pruned weights. However, with
model speed-up, the mask is applied to the model before exporting.
**Important:** Note that without applying model speed-up, compression will not result in model size reduction or inference acceleration.
In order to use model speed-up, the script should call ``compressor.apply_speedup``
with the appropriate parameters. Model speed-up can also be used
during ``compressor.export_model``. Please see `Modify the Training Script` step above for detailed usages.
Not all compression algorithms support model speed-up. Compressors that support model speed-up include:
+ ActivationAPoZRankFilter Pruner
+ ActivationMeanRankFilter Pruner
+ ADMM Pruner
+ FPGM Pruner
+ L1Filter Pruner
+ L2Filter Pruner
+ NetAdapt Pruner
+ Sensitivity Pruner
+ TaylorFOWeightFilter Pruner
================================================
FILE: docs/compression_main.rst
================================================
Model Compression
-----------------
.. toctree::
:maxdepth: 1
compression
compressors
compression_utilities
================================================
FILE: docs/compression_utilities.rst
================================================
Utility Functions
=================
The utility functions of the NNI compression module are also integrated into Auptimizer. They are useful for analyzing
the network topology, making informed decisions on the target sparsity levels, and measuring model parameters and FLOPS.
The basic usages are presented below. Please refer to `NNI compression utilities `__
for more advanced applications.
Sensitivity analysis
--------------------
Sensitivity analysis prunes the layers one-by-one to measure the sentivity of each layer to different levels of target sparsity::
# Example of Sensitivity Analysis usage
# Define a test function to measure the model performance after pruning each layer
def test(model, device, test_loader):
...
return accuracy # return a metric for evaluation
s_analyzer = aup.compression.sensitivity_analysis.SensitivityAnalysis(model=model, val_func=lambda model: test(model, device, test_loader))
sensitivity = s_analyzer.analysis(val_args=[model])
s_analyzer.export(os.path.join(OUT_DIR, "sensitivity_analysis.log"))
The sensitivity analysis result will be saved to "sensitivity_analysis.log".
Channel dependency
------------------
We recommend that users run a channel dependency analysis if they want to manually assign target sparsity levels to selected layers, as the
layers with dependency on each other need to be assigned the same sparsity. Channel dependency can be
done as follows::
# Assume input has dimension (1, 1, 28, 28)
data = torch.ones(1, 1, 28, 28).to(device)
channel_depen = aup.compression.shape_dependency.ChannelDependency(model, data)
channel_depen.export(os.path.join(OUT_DIR, "channel_dependency.csv"))
The channel dependency result will be saved to "channel_dependency.csv".
Parameters / FLOPs counter
--------------------------
The FLOPs, number of parameters, and detailed information per layer ("flops", "params",
"weight_size", "input_size", "output_size", etc) can be measured using::
flops, params, results = compressor.count_flops_params(input_shape_tuple)
print(results)
The per layer information is saved in "results".
================================================
FILE: docs/compressors.rst
================================================
Supported Compression Algorithms
================================
For a high-level introduction of all NNI pruners and quantizers, and the full list of parameters required for each compression algorithm in ``config_list``,
please refer to `compressors `__.
We maintain the same parameters for each compression algorithm as in the original NNI compression module.
In this section, we provide examples for all of the supported compression algorithms that include:
+ An example configuration (for one-time compression) to present the required "framework",
"type" and "compressor" parameters.
+ An example of a``aup.create_compressor`` call. If the compressor supports `"dependency-aware" mode `__,
it will be included in the call.
Pruners
-------
Level Pruner
~~~~~~~~~~~~
Supports both TensorFlow and PyTorch.
**Configuration**::
"compression": {
"framework": "tensorflow" | "torch"
"type": "pruning",
"compressor": "level",
"config_list": [{
"sparsity": 0.8,
"op_types": ["default"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, optimizer=None)
Slim Pruner
~~~~~~~~~~~
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "slim",
"config_list": [{
"sparsity": 0.8,
"op_types": ["BatchNorm2d"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, optimizer=None)
FPGM Pruner
~~~~~~~~~~~
This pruner supports a dependency-aware mode to get better speed-up from the pruning.
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "fpgm",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, optimizer=None, dependency_aware=False, dummy_input=None)
L1Filter Pruner
~~~~~~~~~~~~~~~
This pruner supports the dependency-aware mode.
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "l1_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, optimizer=None, dependency_aware=False, dummy_input=None)
L2Filter Pruner
~~~~~~~~~~~~~~~
This pruner supports the dependency-aware mode.
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "l2_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, optimizer=None, dependency_aware=False, dummy_input=None)
ActivationAPoZRankFilter Pruner
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This pruner supports the dependency-aware mode.
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "activation_apoz_rank_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, optimizer=None, activation='relu', statistics_batch_num=1, dependency_aware=False, dummy_input=None)
ActivationMeanRankFilter Pruner
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This pruner supports the dependency-aware mode.
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "activation_mean_rank_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, optimizer=None, activation='relu', statistics_batch_num=1, dependency_aware=False, dummy_input=None)
TaylorFOWeightFilter Pruner
~~~~~~~~~~~~~~~~~~~~~~~~~~~
This pruner supports the dependency-aware mode.
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "taylor_fo_weight_filter",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, optimizer=None, statistics_batch_num=1, dependency_aware=False, dummy_input=None)
AGP Pruner
~~~~~~~~~~
**Special requirements for usage** (example)::
compressor = aup.compression.create_compressor(model, config, optimizer=optimizer)
model = compressor.compress()
for epoch in range(1, args.epochs + 1):
# ... train the model here for one epoch
compressor.update_epoch(epoch)
Use ``compressor.update_epoch(epoch)`` to update epoch number when you finish one epoch in
your training code.
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "agp",
"config_list": [{
"initial_sparsity": 0.0,
"final_sparsity": 0.8,
"start_epoch": 0,
"end_epoch": 10,
"frequency": 1,
"op_types": ["default"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, optimizer, pruning_algorithm='level')
NetAdapt Pruner
~~~~~~~~~~~~~~~
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "net_adapt",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, short_term_fine_tuner, evaluator, optimize_mode='maximize', base_algo='l1', sparsity_per_iteration=0.05, experiment_data_dir='./')
SimulatedAnnealing Pruner
~~~~~~~~~~~~~~~~~~~~~~~~~
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "simulated_annealing",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, evaluator, optimize_mode='maximize', base_algo='l1', start_temperature=100, stop_temperature=20, cool_down_rate=0.9, perturbation_magnitude=0.35, experiment_data_dir='./')
AutoCompress Pruner
~~~~~~~~~~~~~~~~~~~
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "auto_compress",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, trainer, evaluator, dummy_input, num_iterations=3, optimize_mode='maximize', base_algo='l1', start_temperature=100, stop_temperature=20, cool_down_rate=0.9, perturbation_magnitude=0.35, admm_num_iterations=30, admm_training_epochs=5, row=0.0001, experiment_data_dir='./')
AMC Pruner
~~~~~~~~~~
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "amc",
"config_list": [{
"op_types": ["Conv2d", "Linear"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, evaluator, val_loader, suffix=None, model_type='mobilenet', dataset='cifar10', flops_ratio=0.5, lbound=0.2, rbound=1.0, reward='acc_reward', n_calibration_batches=60, n_points_per_layer=10, channel_round=8, hidden1=300, hidden2=300, lr_c=0.001, lr_a=0.0001, warmup=100, discount=1.0, bsize=64, rmsize=100, window_length=1, tau=0.01, init_delta=0.5, delta_decay=0.99, max_episode_length=1000000000.0, output_dir='./logs', debug=False, train_episode=800, epsilon=50000, seed=None)
ADMM Pruner
~~~~~~~~~~~
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "admm",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"],
"op_names": ["conv1"]
}, {
"sparsity": 0.5,
"op_types": ["Conv2d"],
"op_names": ["conv2"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, trainer, num_iterations=30, training_epochs=5, row=0.0001, base_algo='l1')
Lottery Ticket Hypothesis Pruner
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Special requirements for usage** (example)::
compressor = aup.compression.create_compressor(model, config, optimizer=optimizer, lr_scheduler=scheduler)
model = compressor.compress()
for _ in compressor.get_prune_iterations():
compressor.prune_iteration_start()
for epoch in range(1, args.epochs + 1):
# ... train model here for one epoch
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "lottery_ticket",
"config_list": [{
"prune_iterations": 5,
"sparsity": 0.8,
"op_types": ["Conv2d"]
}
]
}
**Example creation**::
aup.create_compressor(model, config, optimizer=None, lr_scheduler=None, reset_weights=True)
Sensitivity Pruner
~~~~~~~~~~~~~~~~~~
**Special requirements for usage** (example)::
compressor = aup.compression.create_compressor(model, config, finetuner=short_term_fine_tuner, evaluator=evaluator)
model = compressor.compress(eval_args=[model], finetune_args=[model])
Notice the arguments passed to ``compressor.compress``.
**Configuration**::
"compression": {
"framework": "torch",
"type": "pruning",
"compressor": "sensitivity",
"config_list": [{
"sparsity": 0.5,
"op_types": ["Conv2d"]
}
]
}
**Example creation**::
aup.create_compressor(model, config_list, evaluator, finetuner=None, base_algo='l1', sparsity_proportion_calc=None, sparsity_per_iter=0.1, acc_drop_threshold=0.05, checkpoint_dir=None)
Quantizers
----------
Naive Quantizer
~~~~~~~~~~~~~~~
**Configuration**::
"compression": {
"framework": "torch",
"type": "quantization",
"compressor": "naive",
"config_list": []
}
**Example creation**::
aup.create_compressor(model, config)
QAT Quantizer
~~~~~~~~~~~~~
**Configuration**::
"compression": {
"framework": "torch",
"type": "quantization",
"compressor": "qat",
"config_list": [{
"quant_types": ["weight"],
"quant_bits": {
"weight": 8
},
"op_types": ["Conv2d", "Linear"]
}, {
"quant_types": ["output"],
"quant_bits": 8,
"quant_start_step": 7000,
"op_types":["ReLU6"]
}]
}
**Example creation**::
aup.create_compressor(model, config)
DoReFa Quantizer
~~~~~~~~~~~~~~~~
**Configuration**::
"compression": {
"framework": "torch",
"type": "quantization",
"compressor": "dorefa",
"config_list": [{
"quant_types": ["weight"],
"quant_bits": 8,
"op_types": ["default"]
}]
}
**Example creation**::
aup.create_compressor(model, config)
BNN Quantizer
~~~~~~~~~~~~~
**Configuration**::
"compression": {
"framework": "torch",
"type": "quantization",
"compressor": "bnn",
"config_list": [{
"quant_bits": 1,
"quant_types": ["weight"],
"op_types": ["Conv2d", "Linear"],
"op_names": ["conv1", "conv2", "fc1", "fc2"]
}, {
"quant_bits": 1,
"quant_types": ["output"],
"op_types": ["relu"],
"op_names": ["relu1", "relu2", "relu3"]
}]
}
**Example creation**::
aup.create_compressor(model, config)
================================================
FILE: docs/conf.py
================================================
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/stable/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
sys.path.insert(0, os.path.abspath('../src'))
import aup
# -- Project information -----------------------------------------------------
project = 'Auptimizer'
copyright = '2018, LG Electronics Inc.'
author = 'LG Electronics Inc.'
# The short X.Y version
version = aup.__version__
# The full version, including alpha/beta/rc tags
release = ''
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.coverage',
'sphinx.ext.mathjax',
'sphinx.ext.viewcode',
'sphinx.ext.intersphinx',
'sphinx.ext.autosummary',
'sphinxcontrib.programoutput',
'sphinx.ext.autosectionlabel'
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
#source_parsers = {
# '.md': 'recommonmark.parser.CommonMarkParser',
#}
#source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path .
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'wiki']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
html_logo = "images/AuptimizerWhiteLong.png"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static', 'archive']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = { '**': [ 'globaltoc.html','about.html','navigation.html','relations.html','searchbox.html',]}
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'Auptimizerdoc'
# -- Options for LaTeX output ------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'Auptimizer.tex', 'Auptimizer Documentation',
'LG Electronics Inc.', 'manual'),
]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'Auptimizer', 'Auptimizer Documentation',
[author], 1)
]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'Auptimizer', 'Auptimizer Documentation',
author, 'Auptimizer', 'One line description of project.',
'Miscellaneous'),
]
# Additional
add_module_names = False
modindex_common_prefix = [".",".aup.","aup."]
# -- Extension configuration -------------------------------------------------
autodoc_inherit_docstrings = True
html_theme_options = {
#'canonical_url': '',
#'analytics_id': 'UA-XXXXXXX-1', # Provided by Google in your dashboard
'logo_only': True,
'display_version': True,
'prev_next_buttons_location': 'bottom',
'style_external_links': False,
# 'vcs_pageview_mode': '',
#'style_nav_header_background': 'black',
# Toc options
'collapse_navigation': True,
'sticky_navigation': True,
'navigation_depth': 4,
'includehidden': True,
'titles_only': False
}
================================================
FILE: docs/dashboard.rst
================================================
Auptimizer Dashboard
====================
**Dashboard** is a powerful analytics tool that complements Auptimizer's core hyperparameter optimization (HPO) and model compression capabilities.
Use the Dashboard to:
- **Supercharge the analysis of your HPO or model compression experiments.** The dashboard provides insightful visualizations to help you analyze and contrast jobs, experiments, and optimization approaches. Get to the root of your experiment results by exploring the interplay of hyperparameters, the progression of intermediate results, and the efficacies of different HPO algorithms.
- **Get a snapshot of the information that matters.** Use the dashboard to track experiment progress and get clutter-free insights on the ongoing and completed experiments.
- **Run your experiments with ease.** Create, launch or stop an experiment or even set up a new Auptimizer environment using the dashboard.
Launch the dashboard
---------------------
There are two ways to launch the dashboard:
1. In terminal, use the ``dashboard`` command to visualize an exisiting Auptimizer experiment that is currently running or has been completed::
dashboard --port --path
The ``port`` is the port number to show the dashboard on the local machine. The ``path`` should point to the database (default is `sqlite3.db`) for the corresponding experiment.
2. Use the ``launch_dashboard`` flag and the ``dashboard_port`` flag (optional) when starting an HPO or compression experiment::
python3 -m aup exp_config.json --launch_dashboard --dashboard_port
python3 -m aup.compression exp_compression.json --launch_dashboard --dashboard_port
If ``dashboard_port`` is not provided, a random port will be assigned. The local host address will be shown in the console when the experiment starts.
**Important:** With this second approach, some dashboard functionalities, like starting a new experiment or restarting a past experiment, will be disabled. For full functionalities of the dashboard, please, use the first approach.
Track experiment status and visualize results
---------------------------------------------
Main page
~~~~~~~~~
When you open the dashboard in a web browser, you will first land on the main page with a list of completed and active experiments. This page presents the meta info of each experiment.
You can check the experiment configuration via the ``CONFIG`` button, and access detailed results via the ``RESULTS`` button. Here, you can also stop or start an experiment.
The page also offers a *tile view* layout that provides the same information as the *list view*.
.. figure:: images/dashboard/main_page_list.png
:alt: main_page_list
Main Page List View
.. figure:: images/dashboard/main_page_tile.png
:alt: main_page_tile
Main Page Tile View
Experiment overview
~~~~~~~~~~~~~~~~~~~
This page provides a summary of an experiment's status and the best result and corresponding best hyperparameters so far.
You can also select other experiments to view without going back to the main page.
.. figure:: images/dashboard/overview.png
:alt: overview
Experiment Overview
Job status
~~~~~~~~~~
The Job Status page shows the status, result, and hyperparameters of each job. Job details are presented in a scatter plot and a table.
By default, the user-defined score used for HPO will be plotted for each job, while the best result over finished jobs is shown with a line. You can zoom in/out,
check out details of each data point, and change the range of axes on the plot. Please refer to ``INTERACTION GUIDE`` for exploring the plot. If you return multiple
metrics in an experiment, the other metrics can be shown on this plot as well.
.. figure:: images/dashboard/job_status_plot.png
:alt: job_status_plot
Job Status Plot
The job status table presents more detail for each job, including status, score, start/end time, all hyperparameters, etc.
The table can be sorted by each column and exported as xls/xlsx/csv/txt file.
.. figure:: images/dashboard/job_status_table.png
:alt: job_status_table
Job Status Table
Hyperparameter interaction graph
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The hyperparameter interaction graph (HIG) is a parallel coordinate graph that is commonly used to show the interplay among different hyperparameters.
Each vertical axis represents the range of one hyperparameter. You can interact with this graph by selecting certain hyperparameters, moving the
vertical axes, highlighting certain ranges of one or multiple hyperparameters, etc. This HIG can provide more insights into the impact of each hyperparameter
on the evaluation metric.
.. figure:: images/dashboard/hig.png
:alt: hig
Hyperparameter Interaction Graph
Intermediate results
~~~~~~~~~~~~~~~~~~~~
If you enable tracking intermediate results or use early stopping strategies, this page will show the intermediate
results plotted over time for the selected jobs within one experiment.
.. figure:: images/dashboard/intermediate_results.png
:alt: intermediate_results
Intermediate Results
Multi-experiment comparison
~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can view the HPO progress for multiple experiments on this page. The best result for each experiment can be plotted
over jobs or time. This can be particularly useful if you want to compare different HPO strategies.
.. figure:: images/dashboard/multi_experiment.png
:alt: multi_experiment
Multi-Experiment Comparison
Create new experiment
---------------------
In addition to visualizing the results of existing experiments, you can also use the dashboard to create and run new experiments.
There are two ways to do so:
1. click on the ``CREATE NEW EXPERIMENT`` button on the main page,
2. click on the ``Create experiment from copy`` button located on each experiment entry on the main page.
Both approaches will open the following page, where on the top you will need to specify your working directory for the
new experiment, and in the left panel, you can input the experiment configuration in json format. If you choose
``Create experiment from copy``, the configuration json file of the selected experiment will be copied over to the left panel for
further modification. The right panel is for validation purposes. You can check the argument values parsed from
the json file in the right panel to make sure everything in the configuration json file is correct.
.. figure:: images/dashboard/create_experiment.png
:alt: create_experiment
Create New Experiment
Please review the ``INTERACTION GUIDE`` for a complete guide on setting up the experiment. After clicking on
the ``CREATE EXPERIMENT`` button, this experiment will be shown on top of the experiment list on the main page. You can
click on the ``START`` button to run the experiment.
Reset Auptimizer environment
----------------------------
You can also reset the Auptimizer environment via the dashboard. After you click on the ``RESET AUPTIMIZER ENVIRONMENT`` button on the
main page, a software wizard will lead you through the Auptimizer environment set-up process.
**Important:** the existing Auptimizer environment in the same working directory will be overwritten, which will erase existing databases. We
suggest the user back up their databases for finished experiments before resetting the Auptimizer environment in the same working directory.
.. figure:: images/dashboard/setup_environment.png
:alt: setup_environment
Set Up Auptimizer Environment
Compression experiments
-----------------------
For **one-shot compression experiments**, as each experiment contains only one job and there are no hyperparameters to be tuned or visualized, the dashboard will have a few modifications:
1. The `Experiment Overview` page will only show the experiment configuration instead of hyperparameters.
2. The `Job status` page will only show one job.
3. The `Hyperparameter Interaction Graph` page will not show a graph.
4. The `Multi-Experiment Comparison` page will show one data point for the experiment.
For **automatic compression experiments**, the dashboard will function in the same way as for the HPO experiments. Note that in compression experiments, the hyperparameter names may not be explicitly specified
in the configuration file. For example, the target sparsity for multiple layers might be a single hyperparameter. However, on the dashboard, the hyperparameter names will be rephrased for better clarity.
.. figure:: images/dashboard/overview_compression.png
:alt: overview_compression
Experiment Overview for Automatic Compression experiments
.. figure:: images/dashboard/hig_compression.png
:alt: hig_compression
Hyperparameter Interaction Graph for Automatic Compression experiments
Dark mode
---------
All pages are also available in the *dark mode*.
.. figure:: images/dashboard/dark_mode.png
:alt: dark_mode
Experiment Overview in Dark Mode
================================================
FILE: docs/demo.rst
================================================
Examples
========
An easy way to get started with **Auptimizer** is to modify the demo code in the ``Examples`` folder.
+-----------------------------------------+-------------------------+-------------------------------------------------------------------------------+
| Example | Folder | Purpose |
+=========================================+=========================+===============================================================================+
| Basic Demo | ``demo`` | Tutorial |
+-----------------------------------------+-------------------------+-------------------------------------------------------------------------------+
| `2D function with different HPOs` | ``2dfunc_diff_opt`` | Show how to switch between different optimizers |
+-----------------------------------------+-------------------------+-------------------------------------------------------------------------------+
| `2D function with different resources` | ``2dfunc_diff_res`` | Show how to switch between different resources |
+-----------------------------------------+-------------------------+-------------------------------------------------------------------------------+
| `MNIST DNN` | ``hpo_mnist`` | Show HPO usage for DNN |
+-----------------------------------------+-------------------------+-------------------------------------------------------------------------------+
| `Tensorflow flags` | ``tf_flags`` | Show how to integrate with Tensorflow Flags |
+-----------------------------------------+-------------------------+-------------------------------------------------------------------------------+
| `Tensorflow Iris` | ``tf_iris_diff_opt`` | Show example on Iris data |
+-----------------------------------------+-------------------------+-------------------------------------------------------------------------------+
| `NAS integration` | ``cai_nas`` | Show how to do NAS (uses a publicly available open-source NAS implementation) |
+-----------------------------------------+-------------------------+-------------------------------------------------------------------------------+
| `Failure Control` | ``job_failure_control`` | Show job failure control cases |
+-----------------------------------------+-------------------------+-------------------------------------------------------------------------------+
Auto convert
~~~~~~~~~~~~
In example ``2dfunc_diff_opt``, the ``experiment_auto.json`` shows how experiment configuration is managed.
The user can use::
python -m aup.convert rosenbrock_origin.py experiment_auto.json rosenbrock
to automatically convert the original file to the **Auptimizer** version.
The output file name is defined in ``experiment_auto.json`` as ``script``.
Manual modification
~~~~~~~~~~~~~~~~~~~
We also provide a modified training script in example ``2dfunc_diff_opt`` for users' reference. In ``rosenbrock_hpo.py``, we show how to manually convert the function for tuning with **Auptimizer**.
For the end-user’s experiment, simply replacing the ``rosenbrock()`` function with their code is enough to use **Auptimizer** (**Need to return the score in that function**).
Experiment configurations
~~~~~~~~~~~~~~~~~~~~~~~~~
Different ``experiment*.json`` files in multiple examples illustrate how to specify the configuration for different HPO algorithms. Most of
them are identical. To set up a new experiment, please define the corresponding ``parameter_config`` in the ``JSON``
file.
================================================
FILE: docs/developer.rst
================================================
For Developers
---------------------------
.. toctree::
:maxdepth: 1
developer_guide
version
errors
================================================
FILE: docs/developer_guide.rst
================================================
Developer Guide
===============
For Machine Learning development
--------------------------------
**Auptimizer** makes it a little easier to debug your experiment / job in the following ways.
Set level of logging
~~~~~~~~~~~~~~~~~~~~
Logging can be activated using the :code:`--log` flag. (E.g. :code:`python -m aup --log `).
The following logging levels are available:
1. **error** - everything stops the process
2. **warn** - using default values
3. **info** - progress update
4. **debug** - everything else
Test in passive mode
~~~~~~~~~~~~~~~~~~~~
Change :code:`resource` in :code:`experiment.json` to :code:`"passive"` and then run::
python -m aup
By doing so, **Auptimizer** will run in a passive mode where it interactively prints running script with its working
path and asks for the returned value. You should run your script in a second terminal to see whether it finishes
correctly. And then you can return that value back to **Auptimizer**\'s command line.
For Auptimizer Software Development
-----------------------------------
Environment
~~~~~~~~~~~
Either use :code:`virtualenv`::
virtualenv testenv
source testenv/bin/activate
pip install -r requirements.txt
export PYTHONPATH=`pwd`:$PYTHONPATH
or::
export PYTHONPATH=:$PYTHONPATH
Unit testing
~~~~~~~~~~~~
If you make changes to the **Auptimizer** code, you can run the included unit tests to make sure that you didn't break anything.
If it's the first time you are running these tests, do::
chmod u+x tests/EE/test_Job.py
to set the correct permissions. You can then run the tests using::
python -m unittest
================================================
FILE: docs/dlconvert.rst
================================================
Converter
---------
.. toctree::
:maxdepth: 1
dlconvert_readme
dlconvert_example
================================================
FILE: docs/dlconvert_example.rst
================================================
Examples
========
We provide extensive examples to demonstrate Converter coverage and efficacy. Please check out `converter_examples `__ for detailed usages. We provide specific instructions for each example in the respective folder.
Evaluating supported model architectures
----------------------------------------
`Tested_Models `__ - This example evaluates common model architecture coverage by the individual conversion functions. It also summarizes known issues. **We strongly recommend that users review this example first before running their conversion tasks.**
Benchmarking quantized TensorFlow Lite models on an Android phone
-----------------------------------------------------------------
`Convert_Benchmark `__ - This example demonstrates how to benchmark quantized TensorFlow Lite model performance (i.e. running time and memory usage) on an Android phone. Specifically, we converted models from a TensorFlow frozen protobuf file to a quantized .tflite file, and benchmarked their performance on an LG G6 mobile phone.
This example shows that models converted and quantized with Converter match the performance of the official quantized models provided in the `TensorFlow repo `__.
Profiling performances of converted models using Auptimizer Profiler
--------------------------------------------------------------------
`Convert_Profiler `__ - This example demonstrates how to use Auptimizer-Profiler to profile TensorFlow or ONNX model performance on CPU. Performance benchmarking scripts are provided for TensorFlow and ONNX.
================================================
FILE: docs/dlconvert_readme.rst
================================================
How to use Converter
====================
**Converter** is a format conversion tool for machine learning models. It encapsulates best practices of individual Machine Learning model conversions under a single API.
Converter allows you to:
1. **Make your models edge device-friendly.** Transform models in Checkpoint (.meta), Keras (.h5/.hdf5), SavedModel (directory name), Protobuf (.pb), and Pytorch (.pt) into edge-optimized ONNX (.onnx) and TensorFlow Lite (.tflite) formats.
2. **Enhance model interoperability through standardization.** Boost model compatibility with countless compilers, inference engines, and SoCs by converting it into the industry-standard ONNX format.
3. **Get a smaller and faster model.** Make your model more compact and efficient by leveraging Quantization_ built into the TensorFlow Lite converter.
The following source model formats (and file extensions) can be converted to **TensorFlow Lite (.tflite)** and **ONNX (.onnx)**:
- **Checkpoint (.meta)**
- **Keras (.h5/.hdf5)**
- **SavedModel (directory name)**
- **Protobuf (.pb)**
- **PyTorch (.pt)**
Additionally, Converter supports conversions:
- from Checkpoint to Protobuf
- from Keras to Protobuf
- from PyTorch to Keras
TensorFlow 1.15, 2.1 - 2.3 and PyTorch 1.6.0 are tested. The conversion from SavedModel to TensorFlow Lite/ONNX requires TensorFlow version 2.x. Other conversions can be run using both TensorFlow 1.15 or 2.x.
Install
-------
*Note:* Converter leverages conversion libraries that have different version requirements (mainly for TensorFlow).
It is highly recommended to use Docker or Python's virtualenv to isolate your environment.
1. Install Auptimizer
2. Install additional libraries for using Converter:
If you would like to convert from Checkpoint/Keras/Protobuf/SavedModel model formats, please run: ``pip install keras2onnx tf2onnx``. If you would like to convert from PyTorch format, please run:``pip install pytorch2keras keras2onnx tf2onnx``
Usage
-----
1. **Recommended:** Check whether your model architecture is supported for the target conversion `here `__.
2. **Important:** Ensure that you can load and run your model, otherwise you will not be able to convert it successfully.
3. Specify conversion parameters. There are certain parameters to specify for each type of conversion. These parameters need to be written in a configuration *.json* file. You can list configurations for multiple model conversion tasks in a single .json file to execute model conversions sequentially.
An example configuration for converting a VGG16 Keras model to ONNX is as follows::
{
"convert_from":"test_models/VGG16.h5",
"convert_to":"converted_models/VGG16_keras.onnx",
}
4. After preparing the configuration .json file, run the following command to start the conversions::
python -m aup.dlconvert -f
Alternatively, you can also write the configuration in a *json dictionary* format, and run ::
python -m aup.dlconvert -d
Parameters
----------
For **all** conversions, the two required parameters are **convert_from** and **convert_to**.
For each specific conversion, there can be additional parameters needed. These parameters are usually dependent on the source- and target model formats, and are summarized in the following chart:
+------------+----------------+--------------+-------------+--------------+-----------------+-------------+------------+------------+----------------------+
| | From || To || quantization|| input_nodes|| output_nodes| | network_script|| input_shape|| onnx_opset|| frozen || savedmodel_tag |
| | | | | | | network_name | | | || savedmodel_signature|
+============+================+==============+=============+==============+=================+=============+============+============+======================+
| Keras |TensorFlow Lite | Optional | | | | | | | |
+------------+----------------+--------------+-------------+--------------+-----------------+-------------+------------+------------+----------------------+
| SavedModel |TensorFlow Lite | Optional | | | | | | | |
+------------+----------------+--------------+-------------+--------------+-----------------+-------------+------------+------------+----------------------+
| Checkpoint |TensorFlow Lite | Optional | Required |Required | | Optional | | | |
+------------+----------------+--------------+-------------+--------------+-----------------+-------------+------------+------------+----------------------+
| Protobuf |TensorFlow Lite | Optional |Required | Required | | Optional | | | |
+------------+----------------+--------------+-------------+--------------+-----------------+-------------+------------+------------+----------------------+
| PyTorch |TensorFlow Lite | Optional | | |Required |Required | | | |
+------------+----------------+--------------+-------------+--------------+-----------------+-------------+------------+------------+----------------------+
| Keras |ONNX | | | | | |Optional | | |
+------------+----------------+--------------+-------------+--------------+-----------------+-------------+------------+------------+----------------------+
| SavedModel |ONNX | | | | | |Optional | | Optional |
+------------+----------------+--------------+-------------+--------------+-----------------+-------------+------------+------------+----------------------+
| Checkpoint |ONNX | |Required | Required | | Optional |Optional | | |
+------------+----------------+--------------+-------------+--------------+-----------------+-------------+------------+------------+----------------------+
| Protobuf |ONNX | |Required | Required | | Optional |Optional | | |
+------------+----------------+--------------+-------------+--------------+-----------------+-------------+------------+------------+----------------------+
| PyTorch |ONNX | | | | Required |Required | Optional | | |
+------------+----------------+--------------+-------------+--------------+-----------------+-------------+------------+------------+----------------------+
| Keras |Protobuf | | | Required | | | |Optional | |
+------------+----------------+--------------+-------------+--------------+-----------------+-------------+------------+------------+----------------------+
| Checkpoint |Protobuf | | | Required | | | |Optional | |
+------------+----------------+--------------+-------------+--------------+-----------------+-------------+------------+------------+----------------------+
| PyTorch |Keras | | | | Required | Required | | | |
+------------+----------------+--------------+-------------+--------------+-----------------+-------------+------------+------------+----------------------+
``convert_from``
Required for **all** conversions. Input model file with one of the supported extensions: `.meta`, `.h5/.hdf5`, `.pb`, `.pt`, or a directory path for SavedModel.
``convert_to``
Required for **all** conversions. Output model name with one of the supported extensions: `.tflite`, `.onnx`, `.pb`, or `.h5/.hdf5`.
.. _Quantization:
``quantization``
Parameter *quantization* includes a group of parameters used for enabling quantization while converting to TensorFlow Lite format. Post-training quantization is a built-in functionality of the TensorFlow Lite Converter. The Converter API supports calling this functionality.
To specify quantization parameters, write in the configuration .json file::
{
"convert_from":"test_models/VGG16.h5",
"convert_to":"converted_models/VGG16_keras.tflite",
"quantization": {
"optimization":"default",
"type":"float16",
"opsset":"tf",
"load":"repdata.py"
}
}
More detail on post-training quantization capabilities and parameter setting can be found in `Post-training quantization `__
``optimization``
Enable/disable quantization for conversion. Choose from `none` or `default`. Default is `none`. When using `none`, no quantization will be performed and the converted TensorFlow Lite model will be in float32 format. When using `default`, best practices will be applied for quantization with the other given information via `--type`, `--opsset`, and `--load`.
``type``
Target data type for constant values of the converted TensorFlow Lite model. Choose from `float32`, `float16`, `int8`,and `uint8`. Default is `float32`.
``opsset``
Set of OpsSet options supported by the target device (experimental). Choose from
1. `tflite`, which refers to `[tensorflow.lite.OpsSet.TFLITE_BUILTINS]`
2. `tf`, which refers to `[tensorflow.lite.OpsSet.SELECT_TF_OPS, tensorflow.lite.OpsSet.TFLITE_BUILTINS]`
3. `int8`, which refers to `[tensorflow.lite.OpsSet.TFLITE_BUILTINS_INT8]`
Default is `tflite`.
``load``
A python script that implements a data generation function that generates representative data for quantizing variable data, such as feature maps. The function should be named `get_dataset`, and it should be a generator function that yields large enough dataset to represent typical data values. Check `representative data `__ for example.
``input_nodes``
Model input names (separated by comma), which can be found with `summarize graph tool `__. Those names typically end with `:0`, for example `input:0`.
``output_nodes``
Model output names (separated by comma). which can be found with `summarize graph tool `__. Those names typically end with `:0`, for example `output/Softmax:0`.
``input_shape``
If the `input_nodes` in *protobuf* or *checkpoint* has unspecified shapes other than the 1st dimension, the `input_shape` needs to be specified by a comma separated string, for example `1,3,224,224`. For multiple `input_nodes`, use `;` to separate their corresponding `input_shape`.
The shape of `input_nodes` can also be checked using the `summarize graph tool `__, where unspecified shape is usually represented by **-1**.
``network_script``
Path to a Python script that contains the model definition of the PyTorch model to be converted.
``network_name``
Class name of the model to be converted, defined in the script specified in `network_script` .
``onnx_opset``
Opset version to use for ONNX. Default is `10`. The ONNX opset version updates can be found in `ONNX release notes `__.
``frozen``
Flag to control whether to create a frozen Protobuf. Default is `True`.
``savedmodel_tag``
Tag to use for SavedModel. Default is `serve`. The SavedModel to be converted *cannot have an emtpy tag*.
``savedmodel_signature``
Signature to use for SavedModel within the specified `--tag` value. Default is `serving_default`. The SavedModel to be converted *cannot have an emtpy signature*.
``skip``
This parameter is for converting selected models when there are multiple conversion configurations in the json file. When set to `True`, the model will be skipped and not be converted. Default is `False`.
Known Issues
------------
1. Limited support on certain model architectures
2. Quantization for TensorFlow Lite conversion can lead to `significant accuracy loss `__
================================================
FILE: docs/early_stop.rst
================================================
Early Stopping
==============
Early stopping (ES) strategies provide increased efficiency for HPO algorithms by reducing the computational cost. This is
done by detecting the poor performance of certain hyperparameter settings early in the training and stopping the corresponding jobs.
As a result, better hyperparameter configurations can be found sooner with reduced compute resources.
ES is especially useful in the context of deep learning where the search space grows exponentially over increasing hyperparameters.
ES strategies provide users with advanced tools to aggressively explore larger search spaces over limited resources,
with tradeoffs between HPO speed and final model performance.
Auptimizer provides 4 popular ES strategies namely – Bandit, Median, Truncation and Curve-Fitting, which can be applied to all Proposers.
The Bandit, Median and Curve-Fitting strategies are inspired by the following papers, while the Truncation strategy is provided to be used
as a benchmark for other ES strategies:
=============== ==============================================================================================================================================================================================================
Strategy Algorithm
=============== ==============================================================================================================================================================================================================
Bandit `HYPERBAND: Bandit-Based Configuration Evaluation For Hyperparameter Optimization `__
Median `Google Vizier: A Service for Black-Box Optimization `__
Curve-fitting `Speeding up Automatic Hyperparameter Optimization of Deep Neural Networks by Extrapolation of Learning Curves `__
=============== ==============================================================================================================================================================================================================
Usage
@@@@@
The feature can be used by adding the following parameter to the experiment configuration file::
"resource_args": {
"early_stop":
{
"aup_policy": "bandit",
"aup_policy_steps": 5
...
}
}
+ ``aup_policy``: the early stopping strategy in ["bandit", "median", "truncation" or "curve_fitting"].
+ ``aup_policy_steps``: integer, the interval of epochs, by which the intermediate results are compared among jobs and the ES policy is applied.
``aup_policy`` and ``aup_policy_steps`` are the two parameters required for all ES strategies. There are also strategy-specifc parameters, which will be introduced below.
In order to track the intermediate results to be used in ES, ``aup.print_result`` should be called from the user script in the training loop, e.g.::
def main(*args, **kwargs):
# model and data preparation code
for epoch in range(10):
# training code for one epoch
aup.print_result(res)
Examples can be found in Auptimizer Gibhub repository at ``Examples/early_stopping/quad_equation_min`` and ``Examples/early_stopping/mnist_keras``.
Additionally, when using ES, the ``track_intermediate_results`` feature will be triggerd automatically. This means that the resulting intermediate results will be stored in the database in the ``intermediate_results`` table and
can be visualized via dashboard. This also means that the presence of ``track_intermediate_results`` in the experiment configuration file with any value, even false, will be ignored.
An optional parameter of ``warmup`` can also be used for all ES strategies (default is 0). ``warmup`` defines the number of initial epochs that should finish before the ES strategy starts to apply.
Strategies
@@@@@@@@@@
Bandit
~~~~~~~
The bandit policy stops jobs that have a result lower than a specified percentage of the global best result of all jobs. This percentage is defined by the parameter ``bandit_factor``. The result to be compared among jobs is the best result obtained by
the job up to the same epoch.
Example::
"resource_args":
{
"early_stop":
{
"aup_policy": "bandit",
"aup_policy_steps": 10,
"bandit_factor": 0.5
}
}
In this example, we stop jobs with the best result which is worse than 0.5 of the global best result.
This comparison and job cut-off is carried out every 10 epochs.
Default value for ``bandit_factor`` is 0.5, with higher values indicating more aggressive ES strategy.
Truncation
~~~~~~~~~~
The truncation policy cuts a fraction of the worst performing jobs, based on the jobs' best result obtained up to the same epoch. The fraction is specified by the parameter ``truncation_percentage``.
Example::
"resource_args":
{
"early_stop":
{
"aup_policy": "truncation",
"aup_policy_steps": 10,
"truncation_percentage": 0.6
}
}
This example stops 60% of the jobs every 10 epochs. Default value for ``truncation_percentage`` is 0.3, with higher values indicating more aggressive ES strategy.
Median
~~~~~~~
The median policy stops the jobs that yield worse results than the median of the running average of results of all jobs up to the same epoch.
Example::
"resource_args":
{
"early_stop":
{
"aup_policy": "median",
"aup_policy_steps": 10
}
}
Curve Fitting
~~~~~~~~~~~~~~
The curve fitting policy attempts to fit each job's history to a weighted combination of multiple, pre-selected functions, in order to predict its final (best) value. It then stops jobs that fail to attain at least a threshold of the best overall result across all jobs. The implementation is adapted from `NNI `__.
**Caveats:** the biggest downside to curve fitting is that it is usually time-consuming. This makes curve fitting less applicable for small datasets, or tasks that train quickly. In order to address this issue, users should always provide a timeout for the maximum time allowed for each curve fitting process (default: 30s). After the specified time has run out, the curve-fitting process will be stopped and the last result obtained will be used. The longer the timeout, the better the results.
Example::
"resource_args":
{
"early_stop":
{
"aup_policy": "curve_fitting",
"aup_policy_steps": 10,
"curve_fitting_threshold": 0.95,
"curve_fitting_timeout": 60
}
}
Example for scripts that use ``aup.print_result`` for result reporting instead::
"resource_args":
{
"early_stop":
{
"aup_policy": "curve_fitting",
"aup_policy_steps": 10,
"curve_fitting_threshold": 0.95,
"curve_fitting_timeout": 60,
"curve_fitting_max_iters": 100
}
}
Default values for ``curve_fitting_threshold``, ``curve_fitting_timeout`` are 0.95 and 60. ``curve_fitting_max_iters`` defaults to None.
================================================
FILE: docs/edge.rst
================================================
Edge Deployment Tools
---------------------
.. toctree::
:maxdepth: 1
profiler.rst
dlconvert.rst
================================================
FILE: docs/environment.rst
================================================
Set up environment
==================
**Auptimizer** needs to be initialized properly before use, either through `python -m aup.setup` for interactive setup or `python -m aup.setup ` with the specified configuration file. \
**We recommend users use the interactive setup when using auptimizer for the first time**.
The configuration file is used to set up **all** environment-related information for **Auptimizer**, such as number of CPUs, GPUs, or remote servers.
All your experiments and jobs will use the configuration.
Configuration Options
---------------------
Two templates can be found at :code:`Examples/2dfunc_diff_res/*.ini`.
The detailed options are as follows.
Environment Template File (\*.ini)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For a normal workstation, an example template is at :code:`Examples/2dfunc_diff_res/env_user_template.ini` for direct use. The command below will create a user environment profile (at `~/.aup/env.ini`) for quick-start::
python -m aup.setup ./Examples/2dfunc_diff_res/env_user_template.ini
or to create a local environment folder for a particular experiment (in working folder `./.aup/env.ini`), use::
python -m aup.setup ./Examples/2dfunc_diff_res/env_local_template.ini
The content of the template is the following:
+------------------+-----------------------------+--------------------------------------+
| Name | Purpose | Default value |
+==================+=============================+======================================+
| Auptimizer_PATH | configuration folder for | ``.aup`` |
| | Auptimizer, containing this | |
| | ``env.ini`` file and | |
| | database file | |
+------------------+-----------------------------+--------------------------------------+
| SQL_ENGINE | database engine, currently | ``sqlite`` |
| | only supports sqlite | |
+------------------+-----------------------------+--------------------------------------+
| TMP_FOLDER | temp folder for logging / | ``/tmp/aup`` or ``./aup_tmp`` |
| | scratch | |
+------------------+-----------------------------+--------------------------------------+
Available resources for model training are specified by additional arguments, or via interactive questions.
========= ======== =====================================================================================================
Argument Default Details
========= ======== =====================================================================================================
--aws none a file containing ``user@instance-id``, or a comma-separated list
--cpu 4 Number of processors to use on a single machine.
--gpu none a file containing the GPU id number on a machine, or a comma-separated list.
--node none a file containing ``user@IP`` for training, or a comma-separated list
========= ======== =====================================================================================================
AWS Mapping Configuration (--aws)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Auptimizer** has the capability to start and stop AWS instances and assign jobs dynamically to the instances depending
on availability. To allow fragmented resource utilization within the AWS instance, edit the resources available to
**Auptimizer** when installing it on the remote instance.
1. To use the Auptimizer’s AWS option, install and configure `boto3 `_ and ensure your AWS
instances can be accessed with boto3. Then proceed to `install configure AWS
CLI `_.
2. To run jobs on multiple AWS instances, we need to specify an AWS mapping file with `--aws `.
Similar to the above-mentioned node case, `` is a text file containing the username, AWS instance id
and the SSH key file (.pem file).
The format of the file must be one of the following:
+ `@ `
+ `@`
+ `@:port`
+ `@:port `
with each instance on a separate line, or if you setup the configuration interactively, you can provide them as
comma-separated values. The ``username`` is the one used to log into the instance. The ``instance_id`` can be the EC2
instance ID, IP, or public DNS.
Refer to ``Examples/2dfunc_diff_res/aws.txt`` for an example.
3. Either you need to install **Auptimizer** on every node, or you need to copy ``/src/aup.py`` to your remote working
directory.
4. Finally, the environment of the instances might be *sourced* correspondingly for environment variables, e.g. ``PATH``. For
instance, if you want to activate virtualenv on the instance before job running, use `prescript` in experiment.json (See
:ref:`AWSRuntimeAnchor` for more detail).
CPU Configuration (``--cpu``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We can specify how many parallel jobs to be run on CPUs.
The current implementation does **NOT** provide real isolation of hardware.
GPU Mapping Configuration (``--gpu``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To run jobs on multiple GPUs, we need to specify a GPU mapping file with ``--gpu ``.
This file is a text file containing the IDs of GPU cards to use (in ``CUDA_VISIBLE_DEVICES``).
Specifically, each line contains a GPU id as an integer.
+ For single GPU without parallel execution, use ``Examples/2dfunc_diff_res/plainGPU.txt``.
+ Assign multiple jobs to run on the same device by assigning multiple
resource IDs to the same GPU id, i.e. ``0,0``). See ``Examples/2dfunc_diff_res/singleGPU.txt``.
+ Assign multiple jobs on different GPUs on a local machine, i.e.::
0
1
See ``Examples/2dfunc_diff_res/twoGPUs.txt``.
Node Mapping Configuration (``--node``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Similar to GPU mapping, users can specify the computing nodes to be used with ``--node `` during setup
A node configuration file contains each node per line (e.g., ``username@hostname``.)
An SSH connection without password authentication is required
(use ``ssh-keygen`` to create keyless access).
The format of the file must be one of the followings:
+ `@ `
+ `@`
+ `@:port`
+ `@:port `
with each instance on a separate line, or if you setup the configuration interactively, you can provide them as
comma-separated values.
For remote execution, **Auptimizer** will not copy all job-related files to the remote machine. User should make sure
the job script can run on the remote machine first.
Refer to ``Examples/2dfunc_diff_res/node.txt`` for an example.
Either you need to install **Auptimizer** on every node, or you need to copy ``/src/aup.py`` to your remote working
directory.
Optional arguments
~~~~~~~~~~~~~~~~~~
+ ``--overwrite`` - overwrite existing ``.aup`` folder. Otherwise, do nothing
+ ``--log`` - choose log level from ``[debug,info,warn,error]``
+ ``--user`` - not used. It specifies the user ownership for experiments.
Examples
~~~~~~~~
Please refer to ``Examples/2dfunc_diff_res/README.md`` for examples of
how to use different resources with **Auptimizer**.
Database Setup
--------------
During the setup, **Auptimizer** creates a SQL database to track the jobs and experiments (currently
only ``sqlite`` is supported).
Typically, users do not need to manually access it.
Here we provide a little more detail for users to retrieve additional records for their analyses.
The database schema is described below:
.. figure:: images/schema.png
:alt: SQL Schema
SQL Schema
Refresh tables
~~~~~~~~~~~~~~
To (re)create the database, users just need to follow the command printed
after ``python -m aup.setup``::
python -m aup.setupdb .aup/env.ini
This will parse the ``.aup/env.ini`` file to create the new database.
Refresh tables with additional modification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following argument can be customized by changing it in the
headings or from the command line to overwrite the ``aup.setup`` configuration:
+-----------------------+-----------------------+-----------------------+
| Name | Purpose | Default value |
+=======================+=======================+=======================+
| --cpu | Number of CPUs to be | 4 |
| | used. | |
+-----------------------+-----------------------+-----------------------+
| --name | Name for resources | “localhost” , |
| | | Currently not in use. |
+-----------------------+-----------------------+-----------------------+
| --user | username for | Currently not in use. |
| | experiment tracking | |
| | and control | |
+-----------------------+-----------------------+-----------------------+
For example, if user wants to set maximum 2 jobs to be run on CPU in
parallel, under name ``test``::
python -m aup.setupdb --cpus 2 --user test
Then you can see the allocated ``resource`` table as::
echo "select * from resource;" | sqlite3 .aup/sqlite3.db
1|test|gpu|free
2|test|cpu|free
3|test|cpu|free
4|test|passive|free
Reset
-----
To reset the history of **Auptimizer** experiments, there are two levels.
Full reset
~~~~~~~~~~
Removing :code:`.aup` folder will completely remove all history saved by **Auptimizer** as well as any configurations.
Using ``python -m aup.setup --overwrite `` will overwrite the existing folder and remove only the history.
Database reset
~~~~~~~~~~~~~~
Currently, **Auptimizer** experiments and jobs history is saved in ``.aup/sqlite.db`` database. There are two levels to reset the database itself.
Reset all
@@@@@@@@@
We can also just refresh the database for the history by resetting the database file.
Use (locally in working folder)::
python -m aup.setupdb .aup/env.ini
or (user account-wise)::
python -m aup.setupdb ~/.aup/env.ini
Reset job status
@@@@@@@@@@@@@@@@
Sometimes, when **Auptimizer** accidentally exits, the resources are not marked as free in the database and will prevent
you from using them within **Auptimizer**. In such scenarios, you might want to reset the status of resources by::
python -m aup.setupdb.reset env.ini
================================================
FILE: docs/errors.rst
================================================
Errors and Solutions
====================
Setup stage
-----------
+ ``Failed to load environment template %s using ConfigParser``
Check the input ``env.ini`` template for potential mistakes or typos.
+ ``SQL engine XXX is not implemented``
Currently only ``sqlite`` is supported.
+ ``Cant' find XXX file for resource XXX``
The resource file is not found for Auptimizer
+ ``Error while finding module specification for XXX``
XXX can be either ``aup.init`` or ``aup.setup``. Double check whether you put the `aup.py` file for remote execution in your PYTHONPATH instead of the `aup` package.
Run Experiment
--------------
+ Sometimes, when **Auptimizer** accidentally exits, the resources are not marked as free in the database and will prevent you from using them within **Auptimizer**. In such scenarios, you might want to reset the status of resources by::
python -m aup.setupdb.reset env.ini
================================================
FILE: docs/experiment.rst
================================================
Create and run a new experiment
===============================
**Auptimizer** only needs a modified training script and an experiment configuration file (.json) to run a new experiment.
1. Create the ``experiment.json`` file to specify the experiment configuration and hyperparameters. Using ``python -m aup.init`` will guide you interactively. This json file structure is generally the same for most algorithms with minor modifications. See :doc:`algorithm` for more details.
2. Modify your training script. We provide three approaches for modifying the training script:
+ `Manual conversion <#manual-modification-of-training-code>`_;
+ `Python decorator <#code-conversion-with-decorator>`_;
+ `Auto conversion for script (beta) <#auto-code-conversion>`_.
3. Your experiment is now ready to run via ``python -m aup experiment.json``. For more details, see `Run experiment`_.
Terminology
-----------
For data science applications, the **user** (AI scientist/engineer) solves a given data mining problem with a specified
machine learning model.
A script (**code**) is written and some hyperparameters are identified to be explored during model training.
Typically, the user carries out an **experiment** to examine a range of hyperparameter combinations and measures the
**performance** of the model on a hold-out dataset. For example, testing a deep learning model by exploring the learning
rate between 0 and 1, and dropout between 0.4 and 0.6. the performance is measured by accuracy.
Each individual training process for a given selection of hyperparameters (e.g., learning rate = 0.1, dropout = 0.5) is
called a **job**.
All jobs run on an assigned computational **resource**, e.g. CPU, GPU. And after all jobs are finished, the user
retrieves the best model from the training history for further analysis or application.
Manual modification of training code
------------------------------------
If you plan to change your training script manually, the general flow of the conversion process is as follows:
a. parse the configuration file (first argument from command line, i.e. ``sys.argv[1]``) using ``aup.BasicConfig.load(sys.argv[1])``. And use the hyperparameters parsed from the ``BasicConfig`` in your code, such as ``config.param_name`` or ``config['param_name']``, where ``param_name`` need to be consistent with the one used in ``experiment.json``.
b. to report the result by using ``aup.print_result``.
c. Add Shebang line ``#!/usr/bin/env python`` and make the script executable (``chmod u+x script.py``).
Code conversion with decorator
------------------------------
For better control, you can use `aup_args` or `aup_flags` to decorate your code. The examples are in below:
.. figure:: images/comparison.png
:alt: Code comparison
Example of using decorator for code conversion.
Auto code conversion
--------------------
If your training function takes all hyperparameters as input, then **Auptimizer** converts code to run if the training script is well defined as follows::
python -m aup.convert ')
return output
# Read in the chooser from file. Returns True only on success
def _read_only(self):
if os.path.exists(self.state_pkl):
fh = open(self.state_pkl, 'rb')
state = pickle.load(fh)
fh.close()
self.D = state['dims']
self.ls = state['ls']
self.amp2 = state['amp2']
self.noise = state['noise']
self.mean = state['mean']
self.hyper_samples = state['hyper_samples']
self.needs_burnin = False
return True
return False
def _real_init(self, dims, values):
self.locker.lock_wait(self.state_pkl)
self.randomstate = npr.get_state()
if os.path.exists(self.state_pkl):
fh = open(self.state_pkl, 'r')
state = pickle.load(fh)
fh.close()
self.D = state['dims']
self.ls = state['ls']
self.amp2 = state['amp2']
self.noise = state['noise']
self.mean = state['mean']
self.hyper_samples = state['hyper_samples']
self.needs_burnin = False
else:
# Input dimensionality.
self.D = dims
# Initial length scales.
self.ls = np.ones(self.D)
# Initial amplitude.
self.amp2 = np.std(values)+1e-4
# Initial observation noise.
self.noise = 1e-3
# Initial mean.
self.mean = np.mean(values)
# Save hyperparameter samples
self.hyper_samples.append((self.mean, self.noise, self.amp2,
self.ls))
self.locker.unlock(self.state_pkl)
def cov(self, x1, x2=None):
if x2 is None:
return self.amp2 * (self.cov_func(self.ls, x1, None)
+ 1e-6*np.eye(x1.shape[0]))
else:
return self.amp2 * self.cov_func(self.ls, x1, x2)
# Given a set of completed 'experiments' in the unit hypercube with
# corresponding objective 'values', pick from the next experiment to
# run according to the acquisition function.
def next(self, grid, values, durations,
candidates, pending, complete):
# Don't bother using fancy GP stuff at first.
if complete.shape[0] < 2:
return int(candidates[0])
# Perform the real initialization.
if self.D == -1:
self._real_init(grid.shape[1], values[complete])
# Grab out the relevant sets.
comp = grid[complete,:]
cand = grid[candidates,:]
pend = grid[pending,:]
vals = values[complete]
numcand = cand.shape[0]
# Spray a set of candidates around the min so far
best_comp = np.argmin(vals)
cand2 = np.vstack((np.random.randn(10,comp.shape[1])*0.001 +
comp[best_comp,:], cand))
if self.mcmc_iters > 0:
# Possibly burn in.
if self.needs_burnin:
for mcmc_iter in range(self.burnin):
self.sample_hypers(comp, vals)
log("BURN %d/%d] mean: %.2f amp: %.2f "
"noise: %.4f min_ls: %.4f max_ls: %.4f"
% (mcmc_iter+1, self.burnin, self.mean,
np.sqrt(self.amp2), self.noise,
np.min(self.ls), np.max(self.ls)))
self.needs_burnin = False
# Sample from hyperparameters.
# Adjust the candidates to hit ei peaks
self.hyper_samples = []
for mcmc_iter in range(self.mcmc_iters):
self.sample_hypers(comp, vals)
log("%d/%d] mean: %.2f amp: %.2f noise: %.4f "
"min_ls: %.4f max_ls: %.4f"
% (mcmc_iter+1, self.mcmc_iters, self.mean,
np.sqrt(self.amp2), self.noise,
np.min(self.ls), np.max(self.ls)))
self.dump_hypers()
b = []# optimization bounds
for i in range(0, cand.shape[1]):
b.append((0, 1))
overall_ei = self.ei_over_hypers(comp,pend,cand2,vals)
inds = np.argsort(np.mean(overall_ei,axis=1))[-self.grid_subset:]
cand2 = cand2[inds,:]
# Optimize each point in parallel
if self.use_multiprocessing:
pool = multiprocessing.Pool(self.grid_subset)
results = [pool.apply_async(optimize_pt,args=(
c,b,comp,pend,vals,copy.copy(self))) for c in cand2]
for res in results:
cand = np.vstack((cand, res.get(1e8)))
pool.close()
else:
# This is old code to optimize each point in parallel.
for i in range(0, cand2.shape[0]):
log("Optimizing candidate %d/%d" %
(i+1, cand2.shape[0]))
#self.check_grad_ei(cand2[i,:].flatten(), comp, pend, vals)
ret = spo.fmin_l_bfgs_b(self.grad_optimize_ei_over_hypers,
cand2[i,:].flatten(), args=(comp,pend,vals),
bounds=b, disp=0)
cand2[i,:] = ret[0]
cand = np.vstack((cand, cand2))
overall_ei = self.ei_over_hypers(comp,pend,cand,vals)
best_cand = np.argmax(np.mean(overall_ei, axis=1))
if (best_cand >= numcand):
return (int(numcand), cand[best_cand,:])
return int(candidates[best_cand])
else:
# Optimize hyperparameters
self.optimize_hypers(comp, vals)
log("mean: %.2f amp: %.2f noise: %.4f "
"min_ls: %.4f max_ls: %.4f"
% (self.mean, np.sqrt(self.amp2), self.noise,
np.min(self.ls), np.max(self.ls)))
# Optimize over EI
b = []# optimization bounds
for i in range(0, cand.shape[1]):
b.append((0, 1))
for i in range(0, cand2.shape[0]):
ret = spo.fmin_l_bfgs_b(self.grad_optimize_ei,
cand2[i,:].flatten(), args=(comp,vals,True),
bounds=b, disp=0)
cand2[i,:] = ret[0]
cand = np.vstack((cand, cand2))
ei = self.compute_ei(comp, pend, cand, vals)
best_cand = np.argmax(ei)
if (best_cand >= numcand):
return (int(numcand), cand[best_cand,:])
return int(candidates[best_cand])
# Compute EI over hyperparameter samples
def ei_over_hypers(self,comp,pend,cand,vals):
overall_ei = np.zeros((cand.shape[0], self.mcmc_iters))
for mcmc_iter in range(self.mcmc_iters):
hyper = self.hyper_samples[mcmc_iter]
self.mean = hyper[0]
self.noise = hyper[1]
self.amp2 = hyper[2]
self.ls = hyper[3]
overall_ei[:,mcmc_iter] = self.compute_ei(comp, pend, cand,
vals)
return overall_ei
def check_grad_ei(self, cand, comp, pend, vals):
(ei,dx1) = self.grad_optimize_ei_over_hypers(cand, comp, pend, vals)
dx2 = dx1*0
idx = np.zeros(cand.shape[0])
for i in range(0, cand.shape[0]):
idx[i] = 1e-6
(ei1,tmp) = self.grad_optimize_ei_over_hypers(cand + idx, comp, pend, vals)
(ei2,tmp) = self.grad_optimize_ei_over_hypers(cand - idx, comp, pend, vals)
dx2[i] = (ei - ei2)/(2*1e-6)
idx[i] = 0
print('computed grads', dx1)
print('finite diffs', dx2)
print(dx1/dx2)
print(np.sum((dx1 - dx2)**2))
time.sleep(2)
# Adjust points by optimizing EI over a set of hyperparameter samples
def grad_optimize_ei_over_hypers(self, cand, comp, pend, vals, compute_grad=True):
summed_ei = 0
summed_grad_ei = np.zeros(cand.shape).flatten()
ls = self.ls.copy()
amp2 = self.amp2
mean = self.mean
noise = self.noise
for hyper in self.hyper_samples:
self.mean = hyper[0]
self.noise = hyper[1]
self.amp2 = hyper[2]
self.ls = hyper[3]
if compute_grad:
(ei,g_ei) = self.grad_optimize_ei(cand,comp,pend,vals,compute_grad)
summed_grad_ei = summed_grad_ei + g_ei
else:
ei = self.grad_optimize_ei(cand,comp,pend,vals,compute_grad)
summed_ei += ei
self.mean = mean
self.amp2 = amp2
self.noise = noise
self.ls = ls.copy()
if compute_grad:
return (summed_ei, summed_grad_ei)
else:
return summed_ei
# Adjust points based on optimizing their ei
def grad_optimize_ei(self, cand, comp, pend, vals, compute_grad=True):
if pend.shape[0] == 0:
best = np.min(vals)
cand = np.reshape(cand, (-1, comp.shape[1]))
# The primary covariances for prediction.
comp_cov = self.cov(comp)
cand_cross = self.cov(comp, cand)
# Compute the required Cholesky.
obsv_cov = comp_cov + self.noise*np.eye(comp.shape[0])
obsv_chol = spla.cholesky(obsv_cov, lower=True)
cov_grad_func = getattr(gp, 'grad_' + self.cov_func.__name__)
cand_cross_grad = cov_grad_func(self.ls, comp, cand)
# Predictive things.
# Solve the linear systems.
alpha = spla.cho_solve((obsv_chol, True), vals - self.mean)
beta = spla.solve_triangular(obsv_chol, cand_cross, lower=True)
# Predict the marginal means and variances at candidates.
func_m = np.dot(cand_cross.T, alpha) + self.mean
func_v = self.amp2*(1+1e-6) - np.sum(beta**2, axis=0)
# Expected improvement
func_s = np.sqrt(func_v)
u = (best - func_m) / func_s
ncdf = sps.norm.cdf(u)
npdf = sps.norm.pdf(u)
ei = func_s*( u*ncdf + npdf)
if not compute_grad:
return ei
# Gradients of ei w.r.t. mean and variance
g_ei_m = -ncdf
g_ei_s2 = 0.5*npdf / func_s
# Apply covariance function
grad_cross = np.squeeze(cand_cross_grad)
grad_xp_m = np.dot(alpha.transpose(),grad_cross)
grad_xp_v = np.dot(-2*spla.cho_solve(
(obsv_chol, True),cand_cross).transpose(), grad_cross)
grad_xp = 0.5*self.amp2*(grad_xp_m*g_ei_m + grad_xp_v*g_ei_s2)
ei = -np.sum(ei)
return ei, grad_xp.flatten()
else:
# If there are pending experiments, fantasize their outcomes.
cand = np.reshape(cand, (-1, comp.shape[1]))
# Create a composite vector of complete and pending.
comp_pend = np.concatenate((comp, pend))
# Compute the covariance and Cholesky decomposition.
comp_pend_cov = (self.cov(comp_pend) +
self.noise*np.eye(comp_pend.shape[0]))
comp_pend_chol = spla.cholesky(comp_pend_cov, lower=True)
# Compute submatrices.
pend_cross = self.cov(comp, pend)
pend_kappa = self.cov(pend)
# Use the sub-Cholesky.
obsv_chol = comp_pend_chol[:comp.shape[0],:comp.shape[0]]
# Solve the linear systems.
alpha = spla.cho_solve((obsv_chol, True), vals - self.mean)
beta = spla.cho_solve((obsv_chol, True), pend_cross)
# Finding predictive means and variances.
pend_m = np.dot(pend_cross.T, alpha) + self.mean
pend_K = pend_kappa - np.dot(pend_cross.T, beta)
# Take the Cholesky of the predictive covariance.
pend_chol = spla.cholesky(pend_K, lower=True)
# Make predictions.
npr.set_state(self.randomstate)
pend_fant = np.dot(pend_chol, npr.randn(pend.shape[0],self.pending_samples)) + pend_m[:,None]
# Include the fantasies.
fant_vals = np.concatenate(
(np.tile(vals[:,np.newaxis],
(1,self.pending_samples)), pend_fant))
# Compute bests over the fantasies.
bests = np.min(fant_vals, axis=0)
# Now generalize from these fantasies.
cand_cross = self.cov(comp_pend, cand)
cov_grad_func = getattr(gp, 'grad_' + self.cov_func.__name__)
cand_cross_grad = cov_grad_func(self.ls, comp_pend, cand)
# Solve the linear systems.
alpha = spla.cho_solve((comp_pend_chol, True),
fant_vals - self.mean)
beta = spla.solve_triangular(comp_pend_chol, cand_cross,
lower=True)
# Predict the marginal means and variances at candidates.
func_m = np.dot(cand_cross.T, alpha) + self.mean
func_v = self.amp2*(1+1e-6) - np.sum(beta**2, axis=0)
# Expected improvement
func_s = np.sqrt(func_v[:,np.newaxis])
u = (bests[np.newaxis,:] - func_m) / func_s
ncdf = sps.norm.cdf(u)
npdf = sps.norm.pdf(u)
ei = func_s*( u*ncdf + npdf)
# Gradients of ei w.r.t. mean and variance
g_ei_m = -ncdf
g_ei_s2 = 0.5*npdf / func_s
# Apply covariance function
# Squeeze can break the 1D case be careful
if pend.shape[1] == 1:
grad_cross = np.squeeze(cand_cross_grad, axis=(2,))
else:
grad_cross = np.squeeze(cand_cross_grad)
grad_xp_m = np.dot(alpha.transpose(),grad_cross)
grad_xp_v = np.dot(-2*spla.cho_solve(
(comp_pend_chol, True),cand_cross).transpose(), grad_cross)
grad_xp = 0.5*self.amp2*(grad_xp_m*np.tile(g_ei_m,(comp.shape[1],1)).T + (grad_xp_v.T*g_ei_s2).T)
ei = -np.mean(ei, axis=1)
grad_xp = np.mean(grad_xp,axis=0)
return ei, grad_xp.flatten()
def compute_ei(self, comp, pend, cand, vals):
if pend.shape[0] == 0:
# If there are no pending, don't do anything fancy.
# Current best.
best = np.min(vals)
# The primary covariances for prediction.
comp_cov = self.cov(comp)
cand_cross = self.cov(comp, cand)
# Compute the required Cholesky.
obsv_cov = comp_cov + self.noise*np.eye(comp.shape[0])
obsv_chol = spla.cholesky( obsv_cov, lower=True )
# Solve the linear systems.
alpha = spla.cho_solve((obsv_chol, True), vals - self.mean)
beta = spla.solve_triangular(obsv_chol, cand_cross, lower=True)
# Predict the marginal means and variances at candidates.
func_m = np.dot(cand_cross.T, alpha) + self.mean
func_v = self.amp2*(1+1e-6) - np.sum(beta**2, axis=0)
# Expected improvement
func_s = np.sqrt(func_v)
u = (best - func_m) / func_s
ncdf = sps.norm.cdf(u)
npdf = sps.norm.pdf(u)
ei = func_s*( u*ncdf + npdf)
return ei
else:
# If there are pending experiments, fantasize their outcomes.
# Create a composite vector of complete and pending.
comp_pend = np.concatenate((comp, pend))
# Compute the covariance and Cholesky decomposition.
comp_pend_cov = (self.cov(comp_pend) +
self.noise*np.eye(comp_pend.shape[0]))
comp_pend_chol = spla.cholesky(comp_pend_cov, lower=True)
# Compute submatrices.
pend_cross = self.cov(comp, pend)
pend_kappa = self.cov(pend)
# Use the sub-Cholesky.
obsv_chol = comp_pend_chol[:comp.shape[0],:comp.shape[0]]
# Solve the linear systems.
alpha = spla.cho_solve((obsv_chol, True), vals - self.mean)
beta = spla.cho_solve((obsv_chol, True), pend_cross)
# Finding predictive means and variances.
pend_m = np.dot(pend_cross.T, alpha) + self.mean
pend_K = pend_kappa - np.dot(pend_cross.T, beta)
# Take the Cholesky of the predictive covariance.
pend_chol = spla.cholesky(pend_K, lower=True)
# Make predictions.
npr.set_state(self.randomstate)
pend_fant = np.dot(pend_chol, npr.randn(pend.shape[0],self.pending_samples)) + pend_m[:,None]
# Include the fantasies.
fant_vals = np.concatenate(
(np.tile(vals[:,np.newaxis],
(1,self.pending_samples)), pend_fant))
# Compute bests over the fantasies.
bests = np.min(fant_vals, axis=0)
# Now generalize from these fantasies.
cand_cross = self.cov(comp_pend, cand)
# Solve the linear systems.
alpha = spla.cho_solve((comp_pend_chol, True),
fant_vals - self.mean)
beta = spla.solve_triangular(comp_pend_chol, cand_cross,
lower=True)
# Predict the marginal means and variances at candidates.
func_m = np.dot(cand_cross.T, alpha) + self.mean
func_v = self.amp2*(1+1e-6) - np.sum(beta**2, axis=0)
# Expected improvement
func_s = np.sqrt(func_v[:,np.newaxis])
u = (bests[np.newaxis,:] - func_m) / func_s
ncdf = sps.norm.cdf(u)
npdf = sps.norm.pdf(u)
ei = func_s*( u*ncdf + npdf)
return np.mean(ei, axis=1)
def sample_hypers(self, comp, vals):
if self.noiseless:
self.noise = 1e-3
self._sample_noiseless(comp, vals)
else:
self._sample_noisy(comp, vals)
self._sample_ls(comp, vals)
self.hyper_samples.append((self.mean, self.noise, self.amp2, self.ls))
def _sample_ls(self, comp, vals):
def logprob(ls):
if np.any(ls < 0) or np.any(ls > self.max_ls):
return -np.inf
cov = (self.amp2 * (self.cov_func(ls, comp, None) +
1e-6*np.eye(comp.shape[0])) + self.noise*np.eye(comp.shape[0]))
chol = spla.cholesky(cov, lower=True)
solve = spla.cho_solve((chol, True), vals - self.mean)
lp = (-np.sum(np.log(np.diag(chol))) -
0.5*np.dot(vals-self.mean, solve))
return lp
self.ls = util.slice_sample(self.ls, logprob, compwise=True)
def _sample_noisy(self, comp, vals):
def logprob(hypers):
mean = hypers[0]
amp2 = hypers[1]
noise = hypers[2]
# This is pretty hacky, but keeps things sane.
if mean > np.max(vals) or mean < np.min(vals):
return -np.inf
if amp2 < 0 or noise < 0:
return -np.inf
cov = (amp2 * (self.cov_func(self.ls, comp, None) +
1e-6*np.eye(comp.shape[0])) + noise*np.eye(comp.shape[0]))
chol = spla.cholesky(cov, lower=True)
solve = spla.cho_solve((chol, True), vals - mean)
lp = -np.sum(np.log(np.diag(chol)))-0.5*np.dot(vals-mean, solve)
# Roll in noise horseshoe prior.
lp += np.log(np.log(1 + (self.noise_scale/noise)**2))
# Roll in amplitude lognormal prior
lp -= 0.5*(np.log(np.sqrt(amp2))/self.amp2_scale)**2
return lp
hypers = util.slice_sample(np.array(
[self.mean, self.amp2, self.noise]), logprob, compwise=False)
self.mean = hypers[0]
self.amp2 = hypers[1]
self.noise = hypers[2]
def _sample_noiseless(self, comp, vals):
def logprob(hypers):
mean = hypers[0]
amp2 = hypers[1]
noise = 1e-3
# This is pretty hacky, but keeps things sane.
if mean > np.max(vals) or mean < np.min(vals):
return -np.inf
if amp2 < 0:
return -np.inf
cov = (amp2 * (self.cov_func(self.ls, comp, None) +
1e-6*np.eye(comp.shape[0])) + noise*np.eye(comp.shape[0]))
chol = spla.cholesky(cov, lower=True)
solve = spla.cho_solve((chol, True), vals - mean)
lp = -np.sum(np.log(np.diag(chol)))-0.5*np.dot(vals-mean, solve)
# Roll in amplitude lognormal prior
lp -= 0.5*(np.log(np.sqrt(amp2))/self.amp2_scale)**2
return lp
hypers = util.slice_sample(np.array(
[self.mean, self.amp2, self.noise]), logprob, compwise=False)
self.mean = hypers[0]
self.amp2 = hypers[1]
self.noise = 1e-3
def optimize_hypers(self, comp, vals):
mygp = gp.GP(self.cov_func.__name__)
mygp.real_init(comp.shape[1], vals)
mygp.optimize_hypers(comp,vals)
self.mean = mygp.mean
self.ls = mygp.ls
self.amp2 = mygp.amp2
self.noise = mygp.noise
# Save hyperparameter samples
self.hyper_samples.append((self.mean, self.noise, self.amp2, self.ls))
self.dump_hypers()
return
================================================
FILE: src/aup/Proposer/spearmint/chooser/GPEIperSecChooser.py
================================================
##
# Copyright (C) 2012 Jasper Snoek, Hugo Larochelle and Ryan P. Adams
#
# This code is written for research and educational purposes only to
# supplement the paper entitled
# "Practical Bayesian Optimization of Machine Learning Algorithms"
# by Snoek, Larochelle and Adams
# Advances in Neural Information Processing Systems, 2012
#
# 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 .
import os
from .. import gp
import sys
from .. import util
import tempfile
import numpy as np
import numpy.random as npr
import scipy.linalg as spla
import scipy.stats as sps
import scipy.optimize as spo
import pickle
from ..helpers import *
from ..Locker import *
import logging
logger = logging.getLogger(__name__)
log = logger.debug
def init(expt_dir, arg_string):
args = util.unpack_args(arg_string)
return GPEIperSecChooser(expt_dir, **args)
"""
Chooser module for the Gaussian process expected improvement per
second (EI) acquisition function. Candidates are sampled densely in the unit
hypercube and then a subset of the most promising points are optimized to maximize
EI per second over hyperparameter samples. Slice sampling is used to sample
Gaussian process hyperparameters for two GPs, one over the objective function and
the other over the running time of the algorithm.
"""
class GPEIperSecChooser:
def __init__(self, expt_dir, covar="Matern52", mcmc_iters=10,
pending_samples=100, noiseless=False, burnin=100,
grid_subset=20):
self.cov_func = getattr(gp, covar)
self.locker = Locker()
self.state_pkl = os.path.join(expt_dir, self.__module__ + ".pkl")
self.stats_file = os.path.join(expt_dir,
self.__module__ + "_hyperparameters.txt")
self.mcmc_iters = int(mcmc_iters)
self.burnin = int(burnin)
self.needs_burnin = True
self.pending_samples = pending_samples
self.D = -1
self.hyper_iters = 1
# Number of points to optimize EI over
self.grid_subset = int(grid_subset)
self.noiseless = bool(int(noiseless))
self.hyper_samples = []
self.time_hyper_samples = []
self.noise_scale = 0.1 # horseshoe prior
self.amp2_scale = 1 # zero-mean log normal prior
self.max_ls = 10 # top-hat prior on length scales
self.time_noise_scale = 0.1 # horseshoe prior
self.time_amp2_scale = 1 # zero-mean log normal prior
self.time_max_ls = 10 # top-hat prior on length scales
self.ls = None
self.amp2 = None
self.noise = None
self.mean = None
self.time_ls = None
self.time_amp2 = None
self.time_mean = None
self.time_noise = None
# A simple function to dump out hyperparameters to allow for a hot start
# if the optimization is restarted.
def dump_hypers(self):
self.locker.lock_wait(self.state_pkl)
# Write the hyperparameters out to a Pickle.
fh = tempfile.NamedTemporaryFile(mode='wb', delete=False)
pickle.dump({ 'dims' : self.D,
'ls' : self.ls,
'amp2' : self.amp2,
'noise' : self.noise,
'mean' : self.mean,
'time_ls' : self.time_ls,
'time_amp2' : self.time_amp2,
'time_noise' : self.time_noise,
'time_mean' : self.time_mean },
fh)
fh.close()
# Use an atomic move for better NFS happiness.
cmd = 'mv "%s" "%s"' % (fh.name, self.state_pkl)
os.system(cmd) # TODO: Should check system-dependent return status.
self.locker.unlock(self.state_pkl)
def _real_init(self, dims, values, durations):
self.locker.lock_wait(self.state_pkl)
if os.path.exists(self.state_pkl):
fh = open(self.state_pkl, 'rb')
state = pickle.load(fh)
fh.close()
self.D = state['dims']
self.ls = state['ls']
self.amp2 = state['amp2']
self.noise = state['noise']
self.mean = state['mean']
self.time_ls = state['time_ls']
self.time_amp2 = state['time_amp2']
self.time_noise = state['time_noise']
self.time_mean = state['time_mean']
else:
# Input dimensionality.
self.D = dims
# Initial length scales.
self.ls = np.ones(self.D)
self.time_ls = np.ones(self.D)
# Initial amplitude.
self.amp2 = np.std(values)+1e-4
self.time_amp2 = np.std(durations)+1e-4
# Initial observation noise.
self.noise = 1e-3
self.time_noise = 1e-3
# Initial mean.
self.mean = np.mean(values)
self.time_mean = np.mean(np.log(durations))
self.locker.unlock(self.state_pkl)
def cov(self, amp2, ls, x1, x2=None):
if x2 is None:
return amp2 * (self.cov_func(ls, x1, None)
+ 1e-6*np.eye(x1.shape[0]))
else:
return amp2 * self.cov_func(ls, x1, x2)
# Given a set of completed 'experiments' in the unit hypercube with
# corresponding objective 'values', pick from the next experiment to
# run according to the acquisition function.
def next(self, grid, values, durations,
candidates, pending, complete):
# Don't bother using fancy GP stuff at first.
if complete.shape[0] < 2:
return int(candidates[0])
# Perform the real initialization.
if self.D == -1:
self._real_init(grid.shape[1], values[complete],
durations[complete])
# Grab out the relevant sets.
comp = grid[complete,:]
cand = grid[candidates,:]
pend = grid[pending,:]
vals = values[complete]
durs = durations[complete]
# Bring time into the log domain before we do anything
# to maintain strict positivity
durs = np.log(durs)
# Spray a set of candidates around the min so far
numcand = cand.shape[0]
best_comp = np.argmin(vals)
cand2 = np.vstack((np.random.randn(10,comp.shape[1])*0.001 +
comp[best_comp,:], cand))
if self.mcmc_iters > 0:
# Possibly burn in.
if self.needs_burnin:
for mcmc_iter in range(self.burnin):
self.sample_hypers(comp, vals, durs)
log("BURN %d/%d] mean: %.2f amp: %.2f "
"noise: %.4f min_ls: %.4f max_ls: %.4f"
% (mcmc_iter+1, self.burnin, self.mean,
np.sqrt(self.amp2), self.noise,
np.min(self.ls), np.max(self.ls)))
self.needs_burnin = False
# Sample from hyperparameters.
# Adjust the candidates to hit ei/sec peaks
self.hyper_samples = []
for mcmc_iter in range(self.mcmc_iters):
self.sample_hypers(comp, vals, durs)
log("%d/%d] mean: %.2f amp: %.2f noise: %.4f "
"min_ls: %.4f max_ls: %.4f"
% (mcmc_iter+1, self.mcmc_iters, self.mean,
np.sqrt(self.amp2), self.noise,
np.min(self.ls), np.max(self.ls)))
log("%d/%d] time_mean: %.2fs time_amp: %.2f time_noise: %.4f "
"time_min_ls: %.4f time_max_ls: %.4f"
% (mcmc_iter+1, self.mcmc_iters, np.exp(self.time_mean),
np.sqrt(self.time_amp2), np.exp(self.time_noise),
np.min(self.time_ls), np.max(self.time_ls)))
self.dump_hypers()
# Pick the top candidates to optimize over
overall_ei = self.ei_over_hypers(comp,pend,cand2,vals,durs)
inds = np.argsort(np.mean(overall_ei, axis=1))[-self.grid_subset:]
cand2 = cand2[inds,:]
# Adjust the candidates to hit ei peaks
b = []# optimization bounds
for i in range(0, cand.shape[1]):
b.append((0, 1))
for i in range(0, cand2.shape[0]):
log("Optimizing candidate %d/%d" %
(i+1, cand2.shape[0]))
ret = spo.fmin_l_bfgs_b(self.grad_optimize_ei_over_hypers,
cand2[i,:].flatten(),
args=(comp,vals,durs,True),
bounds=b, disp=0)
cand2[i,:] = ret[0]
cand = np.vstack((cand, cand2))
overall_ei = self.ei_over_hypers(comp,pend,cand,vals,durs)
best_cand = np.argmax(np.mean(overall_ei, axis=1))
self.dump_hypers()
if (best_cand >= numcand):
return (int(numcand), cand[best_cand,:])
return int(candidates[best_cand])
else:
# Optimize hyperparameters
self.optimize_hypers(comp, vals, durs)
log("mean: %f amp: %f noise: %f "
"min_ls: %f max_ls: %f"
% (self.mean, np.sqrt(self.amp2),
self.noise, np.min(self.ls), np.max(self.ls)))
# Pick the top candidates to optimize over
ei = self.compute_ei_per_s(comp, pend, cand2, vals, durs)
inds = np.argsort(np.mean(overall_ei, axis=1))[-self.grid_subset:]
cand2 = cand2[inds,:]
# Adjust the candidates to hit ei peaks
b = []# optimization bounds
for i in range(0, cand.shape[1]):
b.append((0, 1))
for i in range(0, cand2.shape[0]):
log("Optimizing candidate %d/%d" %
(i+1, cand2.shape[0]))
ret = spo.fmin_l_bfgs_b(self.grad_optimize_ei,
cand2[i,:].flatten(),
args=(comp,vals,durs,True),
bounds=b, disp=0)
cand2[i,:] = ret[0]
cand = np.vstack((cand, cand2))
ei = self.compute_ei_per_s(comp, pend, cand, vals, durs)
best_cand = np.argmax(ei)
self.dump_hypers()
if (best_cand >= numcand):
return (int(numcand), cand[best_cand,:])
return int(candidates[best_cand])
# Compute EI over hyperparameter samples
def ei_over_hypers(self,comp,pend,cand,vals,durs):
overall_ei = np.zeros((cand.shape[0], self.mcmc_iters))
for mcmc_iter in range(self.mcmc_iters):
hyper = self.hyper_samples[mcmc_iter]
time_hyper = self.time_hyper_samples[mcmc_iter]
self.mean = hyper[0]
self.noise = hyper[1]
self.amp2 = hyper[2]
self.ls = hyper[3]
self.time_mean = time_hyper[0]
self.time_noise = time_hyper[1]
self.time_amp2 = time_hyper[2]
self.time_ls = time_hyper[3]
overall_ei[:,mcmc_iter] = self.compute_ei_per_s(comp, pend, cand,
vals, durs.squeeze())
return overall_ei
def check_grad_ei_per(self, cand, comp, vals, durs):
(ei,dx1) = self.grad_optimize_ei_over_hypers(cand, comp, vals, durs)
dx2 = dx1*0
idx = np.zeros(cand.shape[0])
for i in range(0, cand.shape[0]):
idx[i] = 1e-6
(ei1,tmp) = self.grad_optimize_ei_over_hypers(cand + idx, comp, vals, durs)
(ei2,tmp) = self.grad_optimize_ei_over_hypers(cand - idx, comp, vals, durs)
dx2[i] = (ei - ei2)/(2*1e-6)
idx[i] = 0
log('computed grads %s'%dx1)
log('finite diffs %s'% dx2)
log(dx1/dx2)
log(np.sum((dx1 - dx2)**2))
time.sleep(2)
# Adjust points by optimizing EI over a set of hyperparameter samples
def grad_optimize_ei_over_hypers(self, cand, comp, vals, durs, compute_grad=True):
summed_ei = 0
summed_grad_ei = np.zeros(cand.shape).flatten()
for mcmc_iter in range(self.mcmc_iters):
hyper = self.hyper_samples[mcmc_iter]
time_hyper = self.time_hyper_samples[mcmc_iter]
self.mean = hyper[0]
self.noise = hyper[1]
self.amp2 = hyper[2]
self.ls = hyper[3]
self.time_mean = time_hyper[0]
self.time_noise = time_hyper[1]
self.time_amp2 = time_hyper[2]
self.time_ls = time_hyper[3]
if compute_grad:
(ei,g_ei) = self.grad_optimize_ei(cand,comp,vals,durs,compute_grad)
summed_grad_ei = summed_grad_ei + g_ei
else:
ei = self.grad_optimize_ei(cand,comp,vals,durs,compute_grad)
summed_ei += ei
if compute_grad:
return (summed_ei, summed_grad_ei)
else:
return summed_ei
def grad_optimize_ei(self, cand, comp, vals, durs, compute_grad=True):
# Here we have to compute the gradients for ei per second
# This means deriving through the two kernels, the one for predicting
# time and the one predicting ei
best = np.min(vals)
cand = np.reshape(cand, (-1, comp.shape[1]))
# First we make predictions for the durations
# Compute covariances
comp_time_cov = self.cov(self.time_amp2, self.time_ls, comp)
cand_time_cross = self.cov(self.time_amp2, self.time_ls,comp,cand)
# Cholesky decompositions
obsv_time_cov = comp_time_cov + self.time_noise*np.eye(comp.shape[0])
obsv_time_chol = spla.cholesky( obsv_time_cov, lower=True )
# Linear systems
t_alpha = spla.cho_solve((obsv_time_chol, True), durs - self.time_mean)
# Predict marginal mean times and (possibly) variances
func_time_m = np.dot(cand_time_cross.T, t_alpha) + self.time_mean
# We don't really need the time variances now
#func_time_v = self.time_amp2*(1+1e-6) - np.sum(t_beta**2, axis=0)
# Bring time out of the log domain
func_time_m = np.exp(func_time_m)
# Compute derivative of cross-distances.
grad_cross_r = gp.grad_dist2(self.time_ls, comp, cand)
# Apply covariance function
cov_grad_func = getattr(gp, 'grad_' + self.cov_func.__name__)
cand_cross_grad = cov_grad_func(self.time_ls, comp, cand)
grad_cross_t = np.squeeze(cand_cross_grad)
# Now compute the gradients w.r.t. ei
# The primary covariances for prediction.
comp_cov = self.cov(self.amp2, self.ls, comp)
cand_cross = self.cov(self.amp2, self.ls, comp, cand)
# Compute the required Cholesky.
obsv_cov = comp_cov + self.noise*np.eye(comp.shape[0])
obsv_chol = spla.cholesky( obsv_cov, lower=True )
cand_cross_grad = cov_grad_func(self.ls, comp, cand)
# Predictive things.
# Solve the linear systems.
alpha = spla.cho_solve((obsv_chol, True), vals - self.mean)
beta = spla.solve_triangular(obsv_chol, cand_cross, lower=True)
# Predict the marginal means and variances at candidates.
func_m = np.dot(cand_cross.T, alpha) + self.mean
func_v = self.amp2*(1+1e-6) - np.sum(beta**2, axis=0)
# Expected improvement
func_s = np.sqrt(func_v)
u = (best - func_m) / func_s
ncdf = sps.norm.cdf(u)
npdf = sps.norm.pdf(u)
ei = func_s*(u*ncdf + npdf)
ei_per_s = -np.sum(ei/func_time_m)
if not compute_grad:
return ei
grad_time_xp_m = np.dot(t_alpha.transpose(),grad_cross_t)
# Gradients of ei w.r.t. mean and variance
g_ei_m = -ncdf
g_ei_s2 = 0.5*npdf / func_s
# Apply covariance function
grad_cross = np.squeeze(cand_cross_grad)
grad_xp_m = np.dot(alpha.transpose(),grad_cross)
grad_xp_v = np.dot(-2*spla.cho_solve((obsv_chol, True),
cand_cross).transpose(),grad_cross)
grad_xp = 0.5*self.amp2*(grad_xp_m*g_ei_m + grad_xp_v*g_ei_s2)
grad_time_xp_m = 0.5*self.time_amp2*grad_time_xp_m*func_time_m
grad_xp = (func_time_m*grad_xp - ei*grad_time_xp_m)/(func_time_m**2)
return ei_per_s, grad_xp.flatten()
def compute_ei_per_s(self, comp, pend, cand, vals, durs):
# First we make predictions for the durations as that
# doesn't depend on pending experiments
# Compute covariances
comp_time_cov = self.cov(self.time_amp2, self.time_ls, comp)
cand_time_cross = self.cov(self.time_amp2, self.time_ls,comp,cand)
# Cholesky decompositions
obsv_time_cov = comp_time_cov + self.time_noise*np.eye(comp.shape[0])
obsv_time_chol = spla.cholesky( obsv_time_cov, lower=True )
# Linear systems
t_alpha = spla.cho_solve((obsv_time_chol, True), durs - self.time_mean)
#t_beta = spla.solve_triangular(obsv_time_chol, cand_time_cross, lower=True)
# Predict marginal mean times and (possibly) variances
func_time_m = np.dot(cand_time_cross.T, t_alpha) + self.time_mean
# We don't really need the time variances now
#func_time_v = self.time_amp2*(1+1e-6) - np.sum(t_beta**2, axis=0)
# Bring time out of the log domain
func_time_m = np.exp(func_time_m)
if pend.shape[0] == 0:
# If there are no pending, don't do anything fancy.
# Current best.
best = np.min(vals)
# The primary covariances for prediction.
comp_cov = self.cov(self.amp2, self.ls, comp)
cand_cross = self.cov(self.amp2, self.ls, comp, cand)
# Compute the required Cholesky.
obsv_cov = comp_cov + self.noise*np.eye(comp.shape[0])
obsv_chol = spla.cholesky( obsv_cov, lower=True )
# Solve the linear systems.
alpha = spla.cho_solve((obsv_chol, True), vals - self.mean)
beta = spla.solve_triangular(obsv_chol, cand_cross, lower=True)
# Predict the marginal means and variances at candidates.
func_m = np.dot(cand_cross.T, alpha) + self.mean
func_v = self.amp2*(1+1e-6) - np.sum(beta**2, axis=0)
# Expected improvement
func_s = np.sqrt(func_v)
u = (best - func_m) / func_s
ncdf = sps.norm.cdf(u)
npdf = sps.norm.pdf(u)
ei = func_s*( u*ncdf + npdf)
ei_per_s = ei/func_time_m
return ei_per_s
else:
# If there are pending experiments, fantasize their outcomes.
# Create a composite vector of complete and pending.
comp_pend = np.concatenate((comp, pend))
# Compute the covariance and Cholesky decomposition.
comp_pend_cov = self.cov(self.amp2, self.ls, comp_pend) + self.noise*np.eye(comp_pend.shape[0])
comp_pend_chol = spla.cholesky(comp_pend_cov, lower=True)
# Compute submatrices.
pend_cross = self.cov(self.amp2, self.ls, comp, pend)
pend_kappa = self.cov(self.amp2, self.ls, pend)
# Use the sub-Cholesky.
obsv_chol = comp_pend_chol[:comp.shape[0],:comp.shape[0]]
# Solve the linear systems.
alpha = spla.cho_solve((obsv_chol, True), vals - self.mean)
beta = spla.cho_solve((obsv_chol, True), pend_cross)
# Finding predictive means and variances.
pend_m = np.dot(pend_cross.T, alpha) + self.mean
pend_K = pend_kappa - np.dot(pend_cross.T, beta)
# Take the Cholesky of the predictive covariance.
pend_chol = spla.cholesky(pend_K, lower=True)
# Make predictions.
pend_fant = np.dot(pend_chol, npr.randn(pend.shape[0],self.pending_samples)) + pend_m[:,None]
# Include the fantasies.
fant_vals = np.concatenate((np.tile(vals[:,np.newaxis],
(1,self.pending_samples)), pend_fant))
# Compute bests over the fantasies.
bests = np.min(fant_vals, axis=0)
# Now generalize from these fantasies.
cand_cross = self.cov(self.amp2, self.ls, comp_pend, cand)
# Solve the linear systems.
alpha = spla.cho_solve((comp_pend_chol, True), fant_vals - self.mean)
beta = spla.solve_triangular(comp_pend_chol, cand_cross, lower=True)
# Predict the marginal means and variances at candidates.
func_m = np.dot(cand_cross.T, alpha) + self.mean
func_v = self.amp2*(1+1e-6) - np.sum(beta**2, axis=0)
# Expected improvement
func_s = np.sqrt(func_v[:,np.newaxis])
u = (bests[np.newaxis,:] - func_m) / func_s
ncdf = sps.norm.cdf(u)
npdf = sps.norm.pdf(u)
ei = func_s*( u*ncdf + npdf)
return np.divide(np.mean(ei, axis=1), func_time_m)
def sample_hypers(self, comp, vals, durs):
if self.noiseless:
self.noise = 1e-3
self._sample_noiseless(comp, vals)
else:
self._sample_noisy(comp, vals)
self._sample_ls(comp, vals)
self._sample_time_noisy(comp, durs.squeeze())
self._sample_time_ls(comp, durs.squeeze())
self.hyper_samples.append((self.mean, self.noise, self.amp2, self.ls))
self.time_hyper_samples.append((self.time_mean, self.time_noise, self.time_amp2,
self.time_ls))
def _sample_ls(self, comp, vals):
def logprob(ls):
if np.any(ls < 0) or np.any(ls > self.max_ls):
return -np.inf
cov = self.amp2 * (self.cov_func(ls, comp, None) + 1e-6*np.eye(comp.shape[0])) + self.noise*np.eye(comp.shape[0])
chol = spla.cholesky(cov, lower=True)
solve = spla.cho_solve((chol, True), vals - self.mean)
lp = -np.sum(np.log(np.diag(chol)))-0.5*np.dot(vals-self.mean, solve)
return lp
self.ls = util.slice_sample(self.ls, logprob, compwise=True)
def _sample_time_ls(self, comp, vals):
def logprob(ls):
if np.any(ls < 0) or np.any(ls > self.time_max_ls):
return -np.inf
cov = self.time_amp2 * (self.cov_func(ls, comp, None) + 1e-6*np.eye(comp.shape[0])) + self.time_noise*np.eye(comp.shape[0])
chol = spla.cholesky(cov, lower=True)
solve = spla.cho_solve((chol, True), vals - self.time_mean)
lp = -np.sum(np.log(np.diag(chol)))-0.5*np.dot(vals-self.time_mean, solve)
return lp
self.time_ls = util.slice_sample(self.time_ls, logprob, compwise=True)
def _sample_noisy(self, comp, vals):
def logprob(hypers):
mean = hypers[0]
amp2 = hypers[1]
noise = hypers[2]
# This is pretty hacky, but keeps things sane.
if mean > np.max(vals) or mean < np.min(vals):
return -np.inf
if amp2 < 0 or noise < 0:
return -np.inf
cov = amp2 * (self.cov_func(self.ls, comp, None) + 1e-6*np.eye(comp.shape[0])) + noise*np.eye(comp.shape[0])
chol = spla.cholesky(cov, lower=True)
solve = spla.cho_solve((chol, True), vals - mean)
lp = -np.sum(np.log(np.diag(chol)))-0.5*np.dot(vals-mean, solve)
# Roll in noise horseshoe prior.
lp += np.log(np.log(1 + (self.noise_scale/noise)**2))
#lp -= 0.5*(np.log(noise)/self.noise_scale)**2
# Roll in amplitude lognormal prior
lp -= 0.5*(np.log(amp2)/self.amp2_scale)**2
return lp
hypers = util.slice_sample(np.array([self.mean, self.amp2, self.noise]), logprob, compwise=False)
self.mean = hypers[0]
self.amp2 = hypers[1]
self.noise = hypers[2]
def _sample_time_noisy(self, comp, vals):
def logprob(hypers):
mean = hypers[0]
amp2 = hypers[1]
noise = hypers[2]
# This is pretty hacky, but keeps things sane.
if mean > np.max(vals) or mean < np.min(vals):
return -np.inf
if amp2 < 0 or noise < 0:
return -np.inf
cov = amp2 * (self.cov_func(self.time_ls, comp, None) + 1e-6*np.eye(comp.shape[0])) + noise*np.eye(comp.shape[0])
chol = spla.cholesky(cov, lower=True)
solve = spla.cho_solve((chol, True), vals - mean)
lp = -np.sum(np.log(np.diag(chol)))-0.5*np.dot(vals-mean, solve)
# Roll in noise horseshoe prior.
lp += np.log(np.log(1 + (self.time_noise_scale/noise)**2))
#lp -= 0.5*(np.log(noise)/self.time_noise_scale)**2
# Roll in amplitude lognormal prior
lp -= 0.5*(np.log(np.sqrt(amp2))/self.time_amp2_scale)**2
return lp
hypers = util.slice_sample(np.array([self.time_mean, self.time_amp2, self.time_noise]), logprob, compwise=False)
self.time_mean = hypers[0]
self.time_amp2 = hypers[1]
self.time_noise = hypers[2]
def _sample_noiseless(self, comp, vals):
def logprob(hypers):
mean = hypers[0]
amp2 = hypers[1]
noise = 1e-3
# This is pretty hacky, but keeps things sane.
if mean > np.max(vals) or mean < np.min(vals):
return -np.inf
if amp2 < 0:
return -np.inf
cov = amp2 * (self.cov_func(self.ls, comp, None) + 1e-6*np.eye(comp.shape[0])) + noise*np.eye(comp.shape[0])
chol = spla.cholesky(cov, lower=True)
solve = spla.cho_solve((chol, True), vals - mean)
lp = -np.sum(np.log(np.diag(chol)))-0.5*np.dot(vals-mean, solve)
# Roll in amplitude lognormal prior
lp -= 0.5*(np.log(amp2)/self.amp2_scale)**2
return lp
hypers = util.slice_sample(np.array([self.mean, self.amp2, self.noise]), logprob, compwise=False)
self.mean = hypers[0]
self.amp2 = hypers[1]
self.noise = 1e-3
def optimize_hypers(self, comp, vals, durs):
# First the GP to observations
mygp = gp.GP(self.cov_func.__name__)
mygp.real_init(comp.shape[1], vals)
mygp.optimize_hypers(comp,vals)
self.mean = mygp.mean
self.ls = mygp.ls
self.amp2 = mygp.amp2
self.noise = mygp.noise
# Now the GP to times
timegp = gp.GP(self.cov_func.__name__)
timegp.real_init(comp.shape[1], durs)
timegp.optimize_hypers(comp, durs)
self.time_mean = timegp.mean
self.time_amp2 = timegp.amp2
self.time_noise = timegp.noise
self.time_ls = timegp.ls
# Save hyperparameter samples
self.hyper_samples.append((self.mean, self.noise, self.amp2, self.ls))
self.time_hyper_samples.append((self.time_mean, self.time_noise, self.time_amp2,
self.time_ls))
self.dump_hypers()
================================================
FILE: src/aup/Proposer/spearmint/chooser/RandomChooser.py
================================================
##
# Copyright (C) 2012 Jasper Snoek, Hugo Larochelle and Ryan P. Adams
#
# This code is written for research and educational purposes only to
# supplement the paper entitled
# "Practical Bayesian Optimization of Machine Learning Algorithms"
# by Snoek, Larochelle and Adams
# Advances in Neural Information Processing Systems, 2012
#
# 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 .
import numpy as np
import numpy.random as npr
def init(expt_dir, arg_string):
return RandomChooser()
class RandomChooser:
def __init__(self):
pass
def next(self, grid, values, durations,
candidates, pending, complete):
return int(candidates[int(np.floor(candidates.shape[0]*npr.rand()))])
================================================
FILE: src/aup/Proposer/spearmint/chooser/RandomForestEIChooser.py
================================================
import numpy as np
import numpy.random as npr
import scipy.stats as sps
import sklearn.ensemble
import sklearn.ensemble.forest
from .. import util
import logging
logger = logging.getLogger(__name__)
log = logger.debug
from sklearn.externals.joblib import Parallel, delayed
def init(expt_dir, arg_string):
args = util.unpack_args(arg_string)
return RandomForestEIChooser(**args)
class RandomForestRegressorWithVariance(sklearn.ensemble.RandomForestRegressor):
def predict(self,X):
# Check data
X = np.atleast_2d(X)
all_y_hat = [ tree.predict(X) for tree in self.estimators_ ]
# Reduce
y_hat = sum(all_y_hat) / self.n_estimators
y_var = np.var(all_y_hat,axis=0,ddof=1)
return y_hat, y_var
class RandomForestEIChooser:
def __init__(self,n_trees=50,
max_depth=None,
min_samples_split=1,
max_monkeys=7,
max_features="auto",
n_jobs=1,
random_state=None):
self.n_trees = float(n_trees)
self.max_depth = max_depth
self.min_samples_split = min_samples_split
self.max_features = max_features
self.n_jobs = float(n_jobs)
self.random_state = random_state
self.rf = RandomForestRegressorWithVariance(n_estimators=n_trees,
max_depth=max_depth,
min_samples_split=min_samples_split,
max_features=max_features,
n_jobs=n_jobs,
random_state=random_state)
def next(self, grid, values, durations,
candidates, pending, complete):
# Grab out the relevant sets.
# Don't bother using fancy RF stuff at first.
if complete.shape[0] < 2:
return int(candidates[0])
# Grab out the relevant sets.
comp = grid[complete,:]
cand = grid[candidates,:]
pend = grid[pending,:]
vals = values[complete]
self.rf.fit(comp,vals)
if pend.shape[0] != 0:
# Generate fantasies for pending
func_m, func_v = self.rf.predict(pend)
vals_pend = func_m + np.sqrt(func_v) + npr.randn(func_m.shape[0])
# Re-fit using fantasies
self.rf.fit(np.vstack[comp,pend],np.hstack[vals,vals_pend])
# Predict the marginal means and variances at candidates.
func_m, func_v = self.rf.predict(cand)
# Current best.
best = np.min(vals)
# Expected improvement
func_s = np.sqrt(func_v) + 0.0001
u = (best - func_m) / func_s
ncdf = sps.norm.cdf(u)
npdf = sps.norm.pdf(u)
ei = func_s*( u*ncdf + npdf)
best_cand = np.argmax(ei)
ei.sort()
return int(candidates[best_cand])
================================================
FILE: src/aup/Proposer/spearmint/chooser/SequentialChooser.py
================================================
##
# Copyright (C) 2012 Jasper Snoek, Hugo Larochelle and Ryan P. Adams
#
# This code is written for research and educational purposes only to
# supplement the paper entitled
# "Practical Bayesian Optimization of Machine Learning Algorithms"
# by Snoek, Larochelle and Adams
# Advances in Neural Information Processing Systems, 2012
#
# 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 .
import numpy as np
def init(expt_dir, arg_string):
return SequentialChooser()
class SequentialChooser:
def __init__(self):
pass
def next(self, grid, values, durations,
candidates, pending, complete):
return int(candidates[0])
================================================
FILE: src/aup/Proposer/spearmint/chooser/__init__.py
================================================
================================================
FILE: src/aup/Proposer/spearmint/chooser/cma.py
================================================
#!/usr/bin/env python
"""Module cma implements the CMA-ES, Covariance Matrix Adaptation Evolution
Strategy, a stochastic optimizer for robust non-linear non-convex
derivative-free function minimization for Python versions 2.6, 2.7, 3.x
(for Python 2.5 class SolutionDict would need to be re-implemented, because
it depends on collections.MutableMapping, since version 0.91.01).
CMA-ES searches for a minimizer (a solution x in R**n) of an
objective function f (cost function), such that f(x) is
minimal. Regarding f, only function values for candidate solutions
need to be available, gradients are not necessary. Even less
restrictive, only a passably reliable ranking of the candidate
solutions in each iteration is necessary, the function values
itself do not matter. Some termination criteria however depend
on actual f-values.
Two interfaces are provided:
- function `fmin(func, x0, sigma0,...)`
runs a complete minimization
of the objective function func with CMA-ES.
- class `CMAEvolutionStrategy`
allows for minimization such that the
control of the iteration loop remains with the user.
Used packages:
- unavoidable: `numpy` (see `barecmaes2.py` if `numpy` is not
available),
- avoidable with small changes: `time`, `sys`
- optional: `matplotlib.pylab` (for `plot` etc., highly
recommended), `pprint` (pretty print), `pickle` (in class
`Sections`), `doctest`, `inspect`, `pygsl` (never by default)
Testing
-------
The code can be tested on a given system. Typing::
python cma.py --test
or in the Python shell ``ipython -pylab``::
run cma.py --test
runs ``doctest.testmod(cma)`` showing only exceptions (and not the
tests that fail due to small differences in the output) and should
run without complaints in about under two minutes. On some systems,
the pop up windows must be closed manually to continue and finish
the test.
Install
-------
The code can be installed by::
python cma.py --install
which solely calls the ``setup`` function from the ``distutils.core``
package for installation.
Example
-------
::
import cma
help(cma) # "this" help message, use cma? in ipython
help(cma.fmin)
help(cma.CMAEvolutionStrategy)
help(cma.Options)
cma.Options('tol') # display 'tolerance' termination options
cma.Options('verb') # display verbosity options
res = cma.fmin(cma.Fcts.tablet, 15 * [1], 1)
res[0] # best evaluated solution
res[5] # mean solution, presumably better with noise
:See: `fmin()`, `Options`, `CMAEvolutionStrategy`
:Author: Nikolaus Hansen, 2008-2012
:License: GPL 2 and 3
"""
from __future__ import division # future is >= 3.0, this code has mainly been used with 2.6 & 2.7
from __future__ import with_statement # only necessary for python 2.5 and not in heavy use
# from __future__ import collections.MutableMapping # does not exist in future, otherwise 2.5 would work
from __future__ import print_function # for cross-checking, available from python 2.6
import sys
if sys.version.startswith('3'): # in python 3.x
range = range
raw_input = input
__version__ = "0.92.04 $Revision: 3322 $ $Date: 2012-11-22 18:05:10 +0100 (Thu, 22 Nov 2012) $"
# bash: svn propset svn:keywords 'Date Revision' cma.py
#
# 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, version 2 or 3.
#
# 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 .
#
# for testing:
# pyflakes cma.py # finds bugs by static analysis
# pychecker --limit 60 cma.py # also executes, gives 60 warnings (all checked)
# python cma.py -t -quiet # executes implemented tests based on doctest
# to create a html documentation file:
# pydoc -w cma # edit the header (remove local pointers)
# epydoc cma.py # comes close to javadoc but does not find the
# # links of function references etc
# doxygen needs @package cma as first line in the module docstring
# some things like class attributes are not interpreted correctly
# sphinx: doc style of doc.python.org, could not make it work
# TODO: make those options that are only used in fmin an error in init of CMA, but still Options() should
# work as input to CMA.
# TODO: add a default logger in CMAEvolutionStrategy, see fmin() and optimize() first
# tell() should probably not add data, but optimize() should handle even an after_iteration_handler.
# TODO: CMAEvolutionStrategy(ones(10), 1).optimize(cma.fcts.elli) # should work like fmin
# one problem: the data logger is not default and seemingly cannot be attached in one line
# TODO: check combination of boundary handling and transformation: penalty must be computed
# on gp.pheno(x_geno, bounds=None), but without bounds, check/remove usage of .geno everywhere
# TODO: check whether all new solutions are put into self.sent_solutions
# TODO: separate initialize==reset_state from __init__
# TODO: introduce Zpos == diffC which makes the code more consistent and the active update "exact"
# TODO: split tell into a variable transformation part and the "pure" functionality
# usecase: es.tell_geno(X, [func(es.pheno(x)) for x in X])
# genotypic repair is not part of tell_geno
# TODO: read settable "options" from a (properties) file, see myproperties.py
#
# typical parameters in scipy.optimize: disp, xtol, ftol, maxiter, maxfun, callback=None
# maxfev, diag (A sequency of N positive entries that serve as
# scale factors for the variables.)
# full_output -- non-zero to return all optional outputs.
# If xtol < 0.0, xtol is set to sqrt(machine_precision)
# 'infot -- a dictionary of optional outputs with the keys:
# 'nfev': the number of function calls...
#
# see eg fmin_powell
# typical returns
# x, f, dictionary d
# (xopt, {fopt, gopt, Hopt, func_calls, grad_calls, warnflag}, )
#
# TODO: keep best ten solutions
# TODO: implement constraints handling
# TODO: option full_output -- non-zero to return all optional outputs.
# TODO: extend function unitdoctest, or use unittest?
# TODO: implement equal-fitness termination, covered by stagnation?
# TODO: apply style guide: no capitalizations!?
# TODO: check and test dispdata()
# TODO: eigh(): thorough testing would not hurt
#
# TODO (later): implement readSignals from a file like properties file (to be called after tell())
import time # not really essential
import collections, numpy as np # arange, cos, size, eye, inf, dot, floor, outer, zeros, linalg.eigh, sort, argsort, random, ones,...
from numpy import inf, array, dot, exp, log, sqrt, sum # to access the built-in sum fct: __builtins__.sum or del sum removes the imported sum and recovers the shadowed
try:
import matplotlib.pylab as pylab # also: use ipython -pylab
show = pylab.show
savefig = pylab.savefig # we would like to be able to use cma.savefig() etc
closefig = pylab.close
except:
pylab = None
print(' Could not import matplotlib.pylab, therefore ``cma.plot()`` etc. is not available')
def show():
pass
__docformat__ = "reStructuredText" # this hides some comments entirely?
sys.py3kwarning = True # TODO: out-comment from version 2.6
# why not package math?
# TODO: check scitools.easyviz and how big the adaptation would be
# changes:
# 12/10/25: removed useless check_points from fmin interface
# 12/10/17: bug fix printing number of infeasible samples, moved not-in-use methods
# timesCroot and divCroot to the right class
# 12/10/16 (0.92.00): various changes commit: bug bound[0] -> bounds[0], more_to_write fixed,
# sigma_vec introduced, restart from elitist, trace normalization, max(mu,popsize/2)
# is used for weight calculation.
# 12/07/23: (bug:) BoundPenalty.update respects now genotype-phenotype transformation
# 12/07/21: convert value True for noisehandling into 1 making the output compatible
# 12/01/30: class Solution and more old stuff removed r3101
# 12/01/29: class Solution is depreciated, GenoPheno and SolutionDict do the job (v0.91.00, r3100)
# 12/01/06: CMA_eigenmethod option now takes a function (integer still works)
# 11/09/30: flat fitness termination checks also history length
# 11/09/30: elitist option (using method clip_or_fit_solutions)
# 11/09/xx: method clip_or_fit_solutions for check_points option for all sorts of
# injected or modified solutions and even reliable adaptive encoding
# 11/08/19: fixed: scaling and typical_x type clashes 1 vs array(1) vs ones(dim) vs dim * [1]
# 11/07/25: fixed: fmin wrote first and last line even with verb_log==0
# fixed: method settableOptionsList, also renamed to versatileOptions
# default seed depends on time now
# 11/07/xx (0.9.92): added: active CMA, selective mirrored sampling, noise/uncertainty handling
# fixed: output argument ordering in fmin, print now only used as function
# removed: parallel option in fmin
# 11/07/01: another try to get rid of the memory leak by replacing self.unrepaired = self[:]
# 11/07/01: major clean-up and reworking of abstract base classes and of the documentation,
# also the return value of fmin changed and attribute stop is now a method.
# 11/04/22: bug-fix: option fixed_variables in combination with scaling
# 11/04/21: stopdict is not a copy anymore
# 11/04/15: option fixed_variables implemented
# 11/03/23: bug-fix boundary update was computed even without boundaries
# 11/03/12: bug-fix of variable annotation in plots
# 11/02/05: work around a memory leak in numpy
# 11/02/05: plotting routines improved
# 10/10/17: cleaning up, now version 0.9.30
# 10/10/17: bug-fix: return values of fmin now use phenotyp (relevant
# if input scaling_of_variables is given)
# 08/10/01: option evalparallel introduced,
# bug-fix for scaling being a vector
# 08/09/26: option CMAseparable becomes CMA_diagonal
# 08/10/18: some names change, test functions go into a class
# 08/10/24: more refactorizing
# 10/03/09: upper bound exp(min(1,...)) for step-size control
# TODO: this would define the visible interface
# __all__ = ['fmin', 'CMAEvolutionStrategy', 'plot', ...]
#
# emptysets = ('', (), [], {}) # array([]) does not work but also np.size(.) == 0
# "x in emptysets" cannot be well replaced by "not x"
# which is also True for array([]) and None, but also for 0 and False, and False for NaN
use_sent_solutions = True # 5-30% CPU slower, particularly for large lambda, will be mandatory soon
#____________________________________________________________
#____________________________________________________________
#
def unitdoctest():
"""is used to describe test cases and might in future become helpful
as an experimental tutorial as well. The main testing feature at the
moment is by doctest with ``cma._test()`` or conveniently by
``python cma.py --test``. With the ``--verbose`` option added, the
results will always slightly differ and many "failed" test cases
might be reported.
A simple first overall test:
>>> import cma
>>> res = cma.fmin(cma.fcts.elli, 3*[1], 1, CMA_diagonal=2, seed=1, verb_time=0)
(3_w,7)-CMA-ES (mu_w=2.3,w_1=58%) in dimension 3 (seed=1)
Covariance matrix is diagonal for 2 iterations (1/ccov=7.0)
Iterat #Fevals function value axis ratio sigma minstd maxstd min:sec
1 7 1.453161670768570e+04 1.2e+00 1.08e+00 1e+00 1e+00
2 14 3.281197961927601e+04 1.3e+00 1.22e+00 1e+00 2e+00
3 21 1.082851071704020e+04 1.3e+00 1.24e+00 1e+00 2e+00
100 700 8.544042012075362e+00 1.4e+02 3.18e-01 1e-03 2e-01
200 1400 5.691152415221861e-12 1.0e+03 3.82e-05 1e-09 1e-06
220 1540 3.890107746209078e-15 9.5e+02 4.56e-06 8e-11 7e-08
termination on tolfun : 1e-11
final/bestever f-value = 3.89010774621e-15 2.52273602735e-15
mean solution: [ -4.63614606e-08 -3.42761465e-10 1.59957987e-11]
std deviation: [ 6.96066282e-08 2.28704425e-09 7.63875911e-11]
Test on the Rosenbrock function with 3 restarts. The first trial only
finds the local optimum, which happens in about 20% of the cases.
>>> import cma
>>> res = cma.fmin(cma.fcts.rosen, 4*[-1],1, ftarget=1e-6, restarts=3, verb_time=0, verb_disp=500, seed=3)
(4_w,8)-CMA-ES (mu_w=2.6,w_1=52%) in dimension 4 (seed=3)
Iterat #Fevals function value axis ratio sigma minstd maxstd min:sec
1 8 4.875315645656848e+01 1.0e+00 8.43e-01 8e-01 8e-01
2 16 1.662319948123120e+02 1.1e+00 7.67e-01 7e-01 8e-01
3 24 6.747063604799602e+01 1.2e+00 7.08e-01 6e-01 7e-01
184 1472 3.701428610430019e+00 4.3e+01 9.41e-07 3e-08 5e-08
termination on tolfun : 1e-11
final/bestever f-value = 3.70142861043 3.70142861043
mean solution: [-0.77565922 0.61309336 0.38206284 0.14597202]
std deviation: [ 2.54211502e-08 3.88803698e-08 4.74481641e-08 3.64398108e-08]
(8_w,16)-CMA-ES (mu_w=4.8,w_1=32%) in dimension 4 (seed=4)
Iterat #Fevals function value axis ratio sigma minstd maxstd min:sec
1 1489 2.011376859371495e+02 1.0e+00 8.90e-01 8e-01 9e-01
2 1505 4.157106647905128e+01 1.1e+00 8.02e-01 7e-01 7e-01
3 1521 3.548184889359060e+01 1.1e+00 1.02e+00 8e-01 1e+00
111 3249 6.831867555502181e-07 5.1e+01 2.62e-02 2e-04 2e-03
termination on ftarget : 1e-06
final/bestever f-value = 6.8318675555e-07 1.18576673231e-07
mean solution: [ 0.99997004 0.99993938 0.99984868 0.99969505]
std deviation: [ 0.00018973 0.00038006 0.00076479 0.00151402]
>>> assert res[1] <= 1e-6
Notice the different termination conditions. Termination on the target
function value ftarget prevents further restarts.
Test of scaling_of_variables option
>>> import cma
>>> opts = cma.Options()
>>> opts['seed'] = 456
>>> opts['verb_disp'] = 0
>>> opts['CMA_active'] = 1
>>> # rescaling of third variable: for searching in roughly
>>> # x0 plus/minus 1e3*sigma0 (instead of plus/minus sigma0)
>>> opts.scaling_of_variables = [1, 1, 1e3, 1]
>>> res = cma.fmin(cma.fcts.rosen, 4 * [0.1], 0.1, **opts)
termination on tolfun : 1e-11
final/bestever f-value = 2.68096173031e-14 1.09714829146e-14
mean solution: [ 1.00000001 1.00000002 1.00000004 1.00000007]
std deviation: [ 3.00466854e-08 5.88400826e-08 1.18482371e-07 2.34837383e-07]
The printed std deviations reflect the actual true value (not the one
in the internal representation which would be different).
>>> import cma
>>> r = cma.fmin(cma.fcts.diffpow, 15 * [1], 1, CMA_dampsvec_fac=0.5, ftarget=1e-9)
>>> assert(r[1] < 1e-9)
>>> assert(r[2] < 13000) # only passed with CMA_dampsvec_fac
:See: cma.main(), cma._test()
"""
pass
#____________________________________________________________
#____________________________________________________________
#
class BlancClass(object):
"""blanc container class for having a collection of attributes"""
#_____________________________________________________________________
#_____________________________________________________________________
#
class DerivedDictBase(collections.MutableMapping):
"""for conveniently adding features to a dictionary. The actual
dictionary is in ``self.data``. Copy-paste
and modify setitem, getitem, and delitem, if necessary"""
def __init__(self, *args, **kwargs):
# collections.MutableMapping.__init__(self)
super(DerivedDictBase, self).__init__()
# super(SolutionDict, self).__init__() # the same
self.data = dict(*args, **kwargs)
def __len__(self):
return len(self.data)
def __contains__(self, value):
return value in self.data
def __iter__(self):
return iter(self.data)
def __setitem__(self, key, value):
"""defines self[key] = value"""
self.data[key] = value
def __getitem__(self, key):
"""defines self[key]"""
return self.data[key]
def __delitem__(self, key):
del self.data[key]
class SolutionDict(DerivedDictBase):
"""dictionary with computation of an hash key for the inserted solutions and
a stack of previously inserted same solutions.
Each entry is meant to store additional information related to the solution.
>>> import cma, numpy as np
>>> d = cma.SolutionDict()
>>> x = np.array([1,2,4])
>>> d[x] = {'x': x, 'iteration': 1}
>>> d.get(x) == (d[x] if d.key(x) in d.keys() else None)
The last line is always true.
TODO: data_with_same_key behaves like a stack (see setitem and delitem), but rather should behave like a queue?!
A queue is less consistent with the operation self[key] = ..., if self.data_with_same_key[key] is not empty.
"""
def __init__(self, *args, **kwargs):
DerivedDictBase.__init__(self, *args, **kwargs)
self.data_with_same_key = {}
def key(self, x):
try:
return tuple(x)
except TypeError:
return x
def __setitem__(self, key, value):
"""defines self[key] = value"""
key = self.key(key)
if key in self.data_with_same_key:
self.data_with_same_key[key] += [self.data[key]]
elif key in self.data:
self.data_with_same_key[key] = [self.data[key]]
self.data[key] = value
def __getitem__(self, key):
"""defines self[key]"""
return self.data[self.key(key)]
def __delitem__(self, key):
"""remove only most current key-entry"""
key = self.key(key)
if key in self.data_with_same_key:
if len(self.data_with_same_key[key]) == 1:
self.data[key] = self.data_with_same_key.pop(key)[0]
else:
self.data[key] = self.data_with_same_key[key].pop(-1)
else:
del self.data[key]
def truncate(self, max_len, min_iter):
if len(self) > max_len:
for k in list(self.keys()):
if self[k]['iteration'] < min_iter:
del self[k] # only deletes one item with k as key, should delete all?
class SolutionDictOld(dict):
"""depreciated, SolutionDict should do, to be removed after SolutionDict
has been successfully applied.
dictionary with computation of an hash key for the inserted solutions and
stack of previously inserted same solutions.
Each entry is meant to store additional information related to the solution.
Methods ``pop`` and ``get`` are modified accordingly.
d = SolutionDict()
x = array([1,2,4])
d.insert(x, {'x': x, 'iteration': 1})
d.get(x) == d[d.key(x)] if d.key(x) in d.keys() else d.get(x) is None
TODO: not yet tested
TODO: behaves like a stack (see _pop_derived), but rather should behave like a queue?!
A queue is less consistent with the operation self[key] = ..., if self.more[key] is not empty.
"""
def __init__(self):
self.more = {} # previously inserted same solutions
self._pop_base = self.pop
self.pop = self._pop_derived
self._get_base = self.get
self.get = self._get_derived
def key(self, x):
"""compute the hash key of ``x``"""
return tuple(x)
def insert(self, x, datadict):
key = self.key(x)
if key in self.more:
self.more[key] += [self[key]]
elif key in self:
self.more[key] = [self[key]]
self[key] = datadict
def _get_derived(self, x, default=None):
return self._get_base(self.key(x), default)
def _pop_derived(self, x):
key = self.key(x)
res = self[key]
if key in self.more:
if len(self.more[key]) == 1:
self[key] = self.more.pop(key)[0]
else:
self[key] = self.more[key].pop(-1)
return res
class BestSolution(object):
"""container to keep track of the best solution seen"""
def __init__(self, x=None, f=np.inf, evals=None):
"""initialize the best solution with `x`, `f`, and `evals`.
Better solutions have smaller `f`-values.
"""
self.x = x
self.x_geno = None
self.f = f if f is not None and f is not np.nan else np.inf
self.evals = evals
self.evalsall = evals
self.last = BlancClass()
self.last.x = x
self.last.f = f
def update(self, arx, xarchive=None, arf=None, evals=None):
"""checks for better solutions in list `arx`, based on the smallest
corresponding value in `arf`, alternatively, `update` may be called
with a `BestSolution` instance like ``update(another_best_solution)``
in which case the better solution becomes the current best.
`xarchive` is used to retrieve the genotype of a solution.
"""
if arf is not None: # find failsave minimum
minidx = np.nanargmin(arf)
if minidx is np.nan:
return
minarf = arf[minidx]
# minarf = reduce(lambda x, y: y if y and y is not np.nan and y < x else x, arf, np.inf)
if type(arx) == BestSolution:
if self.evalsall is None:
self.evalsall = arx.evalsall
elif arx.evalsall is not None:
self.evalsall = max((self.evalsall, arx.evalsall))
if arx.f is not None and arx.f < np.inf:
self.update([arx.x], xarchive, [arx.f], arx.evals)
return self
elif minarf < np.inf and (minarf < self.f or self.f is None):
self.x, self.f = arx[minidx], arf[minidx]
self.x_geno = xarchive[self.x]['geno'] if xarchive is not None else None
self.evals = None if not evals else evals - len(arf) + minidx+1
self.evalsall = evals
elif evals:
self.evalsall = evals
self.last.x = arx[minidx]
self.last.f = minarf
def get(self):
"""return ``(x, f, evals)`` """
return self.x, self.f, self.evals, self.x_geno
#____________________________________________________________
#____________________________________________________________
#
class BoundPenalty(object):
"""Computes the boundary penalty. Must be updated each iteration,
using the `update` method.
Details
-------
The penalty computes like ``sum(w[i] * (x[i]-xfeas[i])**2)``,
where `xfeas` is the closest feasible (in-bounds) solution from `x`.
The weight `w[i]` should be updated during each iteration using
the update method.
This class uses `GenoPheno.into_bounds` in method `update` to access
domain boundary values and repair. This inconsistency is going to be
removed in future.
"""
def __init__(self, bounds=None):
"""Argument bounds can be `None` or ``bounds[0]`` and ``bounds[1]``
are lower and upper domain boundaries, each is either `None` or
a scalar or a list or array of appropriate size.
"""
##
# bounds attribute reminds the domain boundary values
self.bounds = bounds
self.gamma = 1 # a very crude assumption
self.weights_initialized = False # gamma becomes a vector after initialization
self.hist = [] # delta-f history
def has_bounds(self):
"""return True, if any variable is bounded"""
bounds = self.bounds
if bounds in (None, [None, None]):
return False
for i in range(bounds[0]):
if bounds[0][i] is not None and bounds[0][i] > -np.inf:
return True
for i in range(bounds[1]):
if bounds[1][i] is not None and bounds[1][i] < np.inf:
return True
return False
def repair(self, x, bounds=None, copy=False, copy_always=False):
"""sets out-of-bounds components of ``x`` on the bounds.
Arguments
---------
`bounds`
can be `None`, in which case the "default" bounds are used,
or ``[lb, ub]``, where `lb` and `ub`
represent lower and upper domain bounds respectively that
can be `None` or a scalar or a list or array of length ``len(self)``
code is more or less copy-paste from Solution.repair, but never tested
"""
# TODO (old data): CPU(N,lam,iter=20,200,100): 3.3s of 8s for two bounds, 1.8s of 6.5s for one bound
# TODO: test whether np.max([bounds[0], x], axis=0) etc is speed relevant
if bounds is None:
bounds = self.bounds
if copy_always:
x_out = array(x, copy=True)
if bounds not in (None, [None, None], (None, None)): # solely for effiency
x_out = array(x, copy=True) if copy and not copy_always else x
if bounds[0] is not None:
if np.isscalar(bounds[0]):
for i in range(len(x)):
x_out[i] = max([bounds[0], x[i]])
else:
for i in range(len(x)):
if bounds[0][i] is not None:
x_out[i] = max([bounds[0][i], x[i]])
if bounds[1] is not None:
if np.isscalar(bounds[1]):
for i in range(len(x)):
x_out[i] = min([bounds[1], x[i]])
else:
for i in range(len(x)):
if bounds[1][i] is not None:
x_out[i] = min([bounds[1][i], x[i]])
return x_out # convenience return
#____________________________________________________________
#
def __call__(self, x, archive, gp):
"""returns the boundary violation penalty for `x` ,where `x` is a
single solution or a list or array of solutions.
If `bounds` is not `None`, the values in `bounds` are used, see `__init__`"""
if x in (None, (), []):
return x
if gp.bounds in (None, [None, None], (None, None)):
return 0.0 if np.isscalar(x[0]) else [0.0] * len(x) # no penalty
x_is_single_vector = np.isscalar(x[0])
x = [x] if x_is_single_vector else x
pen = []
for xi in x:
# CAVE: this does not work with already repaired values!!
# CPU(N,lam,iter=20,200,100)?: 3s of 10s, array(xi): 1s (check again)
# remark: one deep copy can be prevented by xold = xi first
xpheno = gp.pheno(archive[xi]['geno'])
xinbounds = gp.into_bounds(xpheno)
fac = 1 # exp(0.1 * (log(self.scal) - np.mean(self.scal)))
pen.append(sum(self.gamma * ((xinbounds - xpheno) / fac)**2) / len(xi))
return pen[0] if x_is_single_vector else pen
#____________________________________________________________
#
def feasible_ratio(self, solutions):
"""counts for each coordinate the number of feasible values in
``solutions`` and returns an array of length ``len(solutions[0])``
with the ratios.
`solutions` is a list or array of repaired `Solution` instances
"""
count = np.zeros(len(solutions[0]))
for x in solutions:
count += x.unrepaired == x
return count / float(len(solutions))
#____________________________________________________________
#
def update(self, function_values, es, bounds=None):
"""updates the weights for computing a boundary penalty.
Arguments
---------
`function_values`
all function values of recent population of solutions
`es`
`CMAEvolutionStrategy` object instance, in particular the
method `into_bounds` of the attribute `gp` of type `GenoPheno`
is used.
`bounds`
not (yet) in use other than for ``bounds == [None, None]`` nothing
is updated.
Reference: Hansen et al 2009, A Method for Handling Uncertainty...
IEEE TEC, with addendum at http://www.lri.fr/~hansen/TEC2009online.pdf
"""
if bounds is None:
bounds = self.bounds
if bounds is None or (bounds[0] is None and bounds[1] is None): # no bounds ==> no penalty
return self # len(function_values) * [0.0] # case without voilations
N = es.N
### prepare
# compute varis = sigma**2 * C_ii
varis = es.sigma**2 * array(N * [es.C] if np.isscalar(es.C) else ( # scalar case
es.C if np.isscalar(es.C[0]) else # diagonal matrix case
[es.C[i][i] for i in range(N)])) # full matrix case
# dmean = (es.mean - es.gp.into_bounds(es.mean)) / varis**0.5
dmean = (es.mean - es.gp.geno(es.gp.into_bounds(es.gp.pheno(es.mean)))) / varis**0.5
### Store/update a history of delta fitness value
fvals = sorted(function_values)
l = 1 + len(fvals)
val = fvals[3*l // 4] - fvals[l // 4] # exact interquartile range apart interpolation
val = val / np.mean(varis) # new: val is normalized with sigma of the same iteration
# insert val in history
if np.isfinite(val) and val > 0:
self.hist.insert(0, val)
elif val == inf and len(self.hist) > 1:
self.hist.insert(0, max(self.hist))
else:
pass # ignore 0 or nan values
if len(self.hist) > 20 + (3*N) / es.popsize:
self.hist.pop()
### prepare
dfit = np.median(self.hist) # median interquartile range
damp = min(1, es.sp.mueff/10./N)
### set/update weights
# Throw initialization error
if len(self.hist) == 0:
raise _Error('wrongful initialization, no feasible solution sampled. ' +
'Reasons can be mistakenly set bounds (lower bound not smaller than upper bound) or a too large initial sigma0 or... ' +
'See description of argument func in help(cma.fmin) or an example handling infeasible solutions in help(cma.CMAEvolutionStrategy). ')
# initialize weights
if (dmean.any() and (not self.weights_initialized or es.countiter == 2)): # TODO
self.gamma = array(N * [2*dfit])
self.weights_initialized = True
# update weights gamma
if self.weights_initialized:
edist = array(abs(dmean) - 3 * max(1, N**0.5/es.sp.mueff))
if 1 < 3: # this is better, around a factor of two
# increase single weights possibly with a faster rate than they can decrease
# value unit of edst is std dev, 3==random walk of 9 steps
self.gamma *= exp((edist>0) * np.tanh(edist/3) / 2.)**damp
# decrease all weights up to the same level to avoid single extremely small weights
# use a constant factor for pseudo-keeping invariance
self.gamma[self.gamma > 5 * dfit] *= exp(-1./3)**damp
# self.gamma[idx] *= exp(5*dfit/self.gamma[idx] - 1)**(damp/3)
elif 1 < 3 and (edist>0).any(): # previous method
# CAVE: min was max in TEC 2009
self.gamma[edist>0] *= 1.1**min(1, es.sp.mueff/10./N)
# max fails on cigtab(N=12,bounds=[0.1,None]):
# self.gamma[edist>0] *= 1.1**max(1, es.sp.mueff/10./N) # this was a bug!?
# self.gamma *= exp((edist>0) * np.tanh(edist))**min(1, es.sp.mueff/10./N)
else: # alternative version, but not better
solutions = es.pop # this has not been checked
r = self.feasible_ratio(solutions) # has to be the averaged over N iterations
self.gamma *= exp(np.max([N*[0], 0.3 - r], axis=0))**min(1, es.sp.mueff/10/N)
es.more_to_write += list(self.gamma) if self.weights_initialized else N * [1.0]
### return penalty
# es.more_to_write = self.gamma if not np.isscalar(self.gamma) else N*[1]
return self # bound penalty values
#____________________________________________________________
#____________________________________________________________
#
class GenoPhenoBase(object):
"""depreciated, abstract base class for genotyp-phenotype transformation,
to be implemented.
See (and rather use) option ``transformation`` of ``fmin`` or ``CMAEvolutionStrategy``.
Example
-------
::
import cma
class Mygpt(cma.GenoPhenoBase):
def pheno(self, x):
return x # identity for the time being
gpt = Mygpt()
optim = cma.CMAEvolutionStrategy(...)
while not optim.stop():
X = optim.ask()
f = [func(gpt.pheno(x)) for x in X]
optim.tell(X, f)
In case of a repair, we might pass the repaired solution into `tell()`
(with check_points being True).
TODO: check usecases in `CMAEvolutionStrategy` and implement option GenoPhenoBase
"""
def pheno(self, x):
raise NotImplementedError()
return x
#____________________________________________________________
#____________________________________________________________
#
class GenoPheno(object):
"""Genotype-phenotype transformation.
Method `pheno` provides the transformation from geno- to phenotype,
that is from the internal representation to the representation used
in the objective function. Method `geno` provides the "inverse" pheno-
to genotype transformation. The geno-phenotype transformation comprises,
in this order:
- insert fixed variables (with the phenotypic and therefore quite
possibly "wrong" values)
- affine linear transformation (scaling and shift)
- user-defined transformation
- projection into feasible domain (boundaries)
- assign fixed variables their original phenotypic value
By default all transformations are the identity. The boundary
transformation is only applied, if the boundaries are given as argument to
the method `pheno` or `geno` respectively.
``geno`` is not really necessary and might disappear in future.
"""
def __init__(self, dim, scaling=None, typical_x=None, bounds=None, fixed_values=None, tf=None):
"""return `GenoPheno` instance with fixed dimension `dim`.
Keyword Arguments
-----------------
`scaling`
the diagonal of a scaling transformation matrix, multipliers
in the genotyp-phenotyp transformation, see `typical_x`
`typical_x`
``pheno = scaling*geno + typical_x``
`bounds` (obsolete, might disappear)
list with two elements,
lower and upper bounds both can be a scalar or a "vector"
of length dim or `None`. Without effect, as `bounds` must
be given as argument to `pheno()`.
`fixed_values`
a dictionary of variable indices and values, like ``{0:2.0, 2:1.1}``,
that are not subject to change, negative indices are ignored
(they act like incommenting the index), values are phenotypic
values.
`tf`
list of two user-defined transformation functions, or `None`.
``tf[0]`` is a function that transforms the internal representation
as used by the optimizer into a solution as used by the
objective function. ``tf[1]`` does the back-transformation.
For example ::
tf_0 = lambda x: [xi**2 for xi in x]
tf_1 = lambda x: [abs(xi)**0.5 fox xi in x]
or "equivalently" without the `lambda` construct ::
def tf_0(x):
return [xi**2 for xi in x]
def tf_1(x):
return [abs(xi)**0.5 fox xi in x]
``tf=[tf_0, tf_1]`` is a reasonable way to guaranty that only positive
values are used in the objective function.
Details
-------
If ``tf_1`` is ommitted, the initial x-value must be given as genotype (as the
phenotype-genotype transformation is unknown) and injection of solutions
might lead to unexpected results.
"""
self.N = dim
self.bounds = bounds
self.fixed_values = fixed_values
if tf is not None:
self.tf_pheno = tf[0]
self.tf_geno = tf[1] # TODO: should not necessarily be needed
# r = np.random.randn(dim)
# assert all(tf[0](tf[1](r)) - r < 1e-7)
# r = np.random.randn(dim)
# assert all(tf[0](tf[1](r)) - r > -1e-7)
print("WARNING in class GenoPheno: user defined transformations have not been tested thoroughly")
else:
self.tf_geno = None
self.tf_pheno = None
if fixed_values:
if type(fixed_values) is not dict:
raise _Error("fixed_values must be a dictionary {index:value,...}")
if max(fixed_values.keys()) >= dim:
raise _Error("max(fixed_values.keys()) = " + str(max(fixed_values.keys())) +
" >= dim=N=" + str(dim) + " is not a feasible index")
# convenience commenting functionality: drop negative keys
for k in list(fixed_values.keys()):
if k < 0:
fixed_values.pop(k)
if bounds:
if len(bounds) != 2:
raise _Error('len(bounds) must be 2 for lower and upper bounds')
for i in (0,1):
if bounds[i] is not None:
bounds[i] = array(dim * [bounds[i]] if np.isscalar(bounds[i]) else
[b for b in bounds[i]])
def vec_is_default(vec, default_val=0):
"""return True if `vec` has the value `default_val`,
None or [None] are also recognized as default"""
try:
if len(vec) == 1:
vec = vec[0] # [None] becomes None and is always default
else:
return False
except TypeError:
pass # vec is a scalar
if vec is None or vec == array(None) or vec == default_val:
return True
return False
self.scales = array(scaling)
if vec_is_default(self.scales, 1):
self.scales = 1 # CAVE: 1 is not array(1)
elif self.scales.shape is not () and len(self.scales) != self.N:
raise _Error('len(scales) == ' + str(len(self.scales)) +
' does not match dimension N == ' + str(self.N))
self.typical_x = array(typical_x)
if vec_is_default(self.typical_x, 0):
self.typical_x = 0
elif self.typical_x.shape is not () and len(self.typical_x) != self.N:
raise _Error('len(typical_x) == ' + str(len(self.typical_x)) +
' does not match dimension N == ' + str(self.N))
if (self.scales is 1 and
self.typical_x is 0 and
self.bounds in (None, [None, None]) and
self.fixed_values is None and
self.tf_pheno is None):
self.isidentity = True
else:
self.isidentity = False
def into_bounds(self, y, bounds=None, copy_never=False, copy_always=False):
"""Argument `y` is a phenotypic vector,
return `y` put into boundaries, as a copy iff ``y != into_bounds(y)``.
Note: this code is duplicated in `Solution.repair` and might
disappear in future.
"""
bounds = bounds if bounds is not None else self.bounds
if bounds in (None, [None, None]):
return y if not copy_always else array(y, copy=True)
if bounds[0] is not None:
if len(bounds[0]) not in (1, len(y)):
raise ValueError('len(bounds[0]) = ' + str(len(bounds[0])) +
' and len of initial solution (' + str(len(y)) + ') disagree')
if copy_never: # is rather slower
for i in range(len(y)):
y[i] = max(bounds[0][i], y[i])
else:
y = np.max([bounds[0], y], axis=0)
if bounds[1] is not None:
if len(bounds[1]) not in (1, len(y)):
raise ValueError('len(bounds[1]) = ' + str(len(bounds[1])) +
' and initial solution (' + str(len(y)) + ') disagree')
if copy_never:
for i in range(len(y)):
y[i] = min(bounds[1][i], y[i])
else:
y = np.min([bounds[1], y], axis=0)
return y
def pheno(self, x, bounds=None, copy=True, copy_always=False):
"""maps the genotypic input argument into the phenotypic space,
boundaries are only applied if argument ``bounds is not None``, see
help for class `GenoPheno`
"""
if copy_always and not copy:
raise ValueError('arguments copy_always=' + str(copy_always) +
' and copy=' + str(copy) + ' have inconsistent values')
if self.isidentity and bounds in (None, [None, None], (None, None)):
return x if not copy_always else array(x, copy=copy_always)
if self.fixed_values is None:
y = array(x, copy=copy) # make a copy, in case
else: # expand with fixed values
y = list(x) # is a copy
for i in sorted(self.fixed_values.keys()):
y.insert(i, self.fixed_values[i])
y = array(y, copy=False)
if self.scales is not 1: # just for efficiency
y *= self.scales
if self.typical_x is not 0:
y += self.typical_x
if self.tf_pheno is not None:
y = array(self.tf_pheno(y), copy=False)
if bounds is not None:
y = self.into_bounds(y, bounds)
if self.fixed_values is not None:
for i, k in list(self.fixed_values.items()):
y[i] = k
return y
def geno(self, y, bounds=None, copy=True, copy_always=False, archive=None):
"""maps the phenotypic input argument into the genotypic space.
If `bounds` are given, first `y` is projected into the feasible
domain. In this case ``copy==False`` leads to a copy.
by default a copy is made only to prevent to modify ``y``
method geno is only needed if external solutions are injected
(geno(initial_solution) is depreciated and will disappear)
TODO: arg copy=True should become copy_never=False
"""
if archive is not None and bounds is not None:
try:
return archive[y]['geno']
except:
pass
x = array(y, copy=(copy and not self.isidentity) or copy_always)
# bounds = self.bounds if bounds is None else bounds
if bounds is not None: # map phenotyp into bounds first
x = self.into_bounds(x, bounds)
if self.isidentity:
return x
# user-defined transformation
if self.tf_geno is not None:
x = array(self.tf_geno(x), copy=False)
else:
_Error('t1 of options transformation was not defined but is needed as being the inverse of t0')
# affine-linear transformation: shift and scaling
if self.typical_x is not 0:
x -= self.typical_x
if self.scales is not 1: # just for efficiency
x /= self.scales
# kick out fixed_values
if self.fixed_values is not None:
# keeping the transformed values does not help much
# therefore it is omitted
if 1 < 3:
keys = sorted(self.fixed_values.keys())
x = array([x[i] for i in range(len(x)) if i not in keys], copy=False)
else: # TODO: is this more efficient?
x = list(x)
for key in sorted(list(self.fixed_values.keys()), reverse=True):
x.remove(key)
x = array(x, copy=False)
return x
#____________________________________________________________
#____________________________________________________________
# check out built-in package abc: class ABCMeta, abstractmethod, abstractproperty...
# see http://docs.python.org/whatsnew/2.6.html PEP 3119 abstract base classes
#
class OOOptimizer(object):
""""abstract" base class for an OO optimizer interface with methods
`__init__`, `ask`, `tell`, `stop`, `result`, and `optimize`. Only
`optimize` is fully implemented in this base class.
Examples
--------
All examples minimize the function `elli`, the output is not shown.
(A preferred environment to execute all examples is ``ipython -pylab``.)
First we need ::
from cma import CMAEvolutionStrategy, CMADataLogger # CMAEvolutionStrategy derives from the OOOptimizer class
elli = lambda x: sum(1e3**((i-1.)/(len(x)-1.)*x[i])**2 for i in range(len(x)))
The shortest example uses the inherited method `OOOptimizer.optimize()`::
res = CMAEvolutionStrategy(8 * [0.1], 0.5).optimize(elli)
The input parameters to `CMAEvolutionStrategy` are specific to this
inherited class. The remaining functionality is based on interface
defined by `OOOptimizer`. We might have a look at the result::
print(res[0]) # best solution and
print(res[1]) # its function value
`res` is the return value from method
`CMAEvolutionStrategy.result()` appended with `None` (no logger).
In order to display more exciting output we rather do ::
logger = CMADataLogger() # derives from the abstract BaseDataLogger class
res = CMAEvolutionStrategy(9 * [0.5], 0.3).optimize(elli, logger)
logger.plot() # if matplotlib is available, logger == res[-1]
or even shorter ::
res = CMAEvolutionStrategy(9 * [0.5], 0.3).optimize(elli, CMADataLogger())
res[-1].plot() # if matplotlib is available
Virtually the same example can be written with an explicit loop
instead of using `optimize()`. This gives the necessary insight into
the `OOOptimizer` class interface and gives entire control over the
iteration loop::
optim = CMAEvolutionStrategy(9 * [0.5], 0.3) # a new CMAEvolutionStrategy instance calling CMAEvolutionStrategy.__init__()
logger = CMADataLogger(optim) # get a logger instance
# this loop resembles optimize()
while not optim.stop(): # iterate
X = optim.ask() # get candidate solutions
f = [elli(x) for x in X] # evaluate solutions
# maybe do something else that needs to be done
optim.tell(X, f) # do all the real work: prepare for next iteration
optim.disp(20) # display info every 20th iteration
logger.add() # log another "data line"
# final output
print('termination by', optim.stop())
print('best f-value =', optim.result()[1])
print('best solution =', optim.result()[0])
logger.plot() # if matplotlib is available
raw_input('press enter to continue') # prevents exiting and closing figures
Details
-------
Most of the work is done in the method `tell(...)`. The method `result()` returns
more useful output.
"""
def __init__(self, xstart, **more_args):
"""``xstart`` is a mandatory argument"""
self.xstart = xstart
self.more_args = more_args
self.initialize()
def initialize(self):
"""(re-)set to the initial state"""
self.countiter = 0
self.xcurrent = self.xstart[:]
raise NotImplementedError('method initialize() must be implemented in derived class')
def ask(self):
"""abstract method, AKA "get" or "sample_distribution", deliver new candidate solution(s), a list of "vectors"
"""
raise NotImplementedError('method ask() must be implemented in derived class')
def tell(self, solutions, function_values):
"""abstract method, AKA "update", prepare for next iteration"""
self.countiter += 1
raise NotImplementedError('method tell() must be implemented in derived class')
def stop(self):
"""abstract method, return satisfied termination conditions in a dictionary like
``{'termination reason': value, ...}``, for example ``{'tolfun': 1e-12}``, or the empty
dictionary ``{}``. The implementation of `stop()` should prevent an infinite loop.
"""
raise NotImplementedError('method stop() is not implemented')
def disp(self, modulo=None):
"""abstract method, display some iteration infos if ``self.iteration_counter % modulo == 0``"""
raise NotImplementedError('method disp() is not implemented')
def result(self):
"""abstract method, return ``(x, f(x), ...)``, that is, the minimizer, its function value, ..."""
raise NotImplementedError('method result() is not implemented')
def optimize(self, objectivefct, logger=None, verb_disp=20, iterations=None):
"""find minimizer of `objectivefct` by iterating over `OOOptimizer` `self`
with verbosity `verb_disp`, using `BaseDataLogger` `logger` with at
most `iterations` iterations. ::
return self.result() + (self.stop(), self, logger)
Example
-------
>>> import cma
>>> res = cma.CMAEvolutionStrategy(7 * [0.1], 0.5).optimize(cma.fcts.rosen, cma.CMADataLogger(), 100)
(4_w,9)-CMA-ES (mu_w=2.8,w_1=49%) in dimension 7 (seed=630721393)
Iterat #Fevals function value axis ratio sigma minstd maxstd min:sec
1 9 3.163954777181882e+01 1.0e+00 4.12e-01 4e-01 4e-01 0:0.0
2 18 3.299006223906629e+01 1.0e+00 3.60e-01 3e-01 4e-01 0:0.0
3 27 1.389129389866704e+01 1.1e+00 3.18e-01 3e-01 3e-01 0:0.0
100 900 2.494847340045985e+00 8.6e+00 5.03e-02 2e-02 5e-02 0:0.3
200 1800 3.428234862999135e-01 1.7e+01 3.77e-02 6e-03 3e-02 0:0.5
300 2700 3.216640032470860e-04 5.6e+01 6.62e-03 4e-04 9e-03 0:0.8
400 3600 6.155215286199821e-12 6.6e+01 7.44e-06 1e-07 4e-06 0:1.1
438 3942 1.187372505161762e-14 6.0e+01 3.27e-07 4e-09 9e-08 0:1.2
438 3942 1.187372505161762e-14 6.0e+01 3.27e-07 4e-09 9e-08 0:1.2
('termination by', {'tolfun': 1e-11})
('best f-value =', 1.1189867885201275e-14)
('solution =', array([ 1. , 1. , 1. , 0.99999999, 0.99999998,
0.99999996, 0.99999992]))
>>> print(res[0])
[ 1. 1. 1. 0.99999999 0.99999998 0.99999996
0.99999992]
"""
if logger is None:
if hasattr(self, 'logger'):
logger = self.logger
citer = 0
while not self.stop():
if iterations is not None and citer >= iterations:
return self.result()
citer += 1
X = self.ask() # deliver candidate solutions
fitvals = [objectivefct(x) for x in X]
self.tell(X, fitvals) # all the work is done here
self.disp(verb_disp)
logger.add(self) if logger else None
logger.add(self, modulo=bool(logger.modulo)) if logger else None
if verb_disp:
self.disp(1)
if verb_disp in (1, True):
print('termination by', self.stop())
print('best f-value =', self.result()[1])
print('solution =', self.result()[0])
return self.result() + (self.stop(), self, logger)
#____________________________________________________________
#____________________________________________________________
#
class CMAEvolutionStrategy(OOOptimizer):
"""CMA-ES stochastic optimizer class with ask-and-tell interface.
See `fmin` for the one-line-call functional interface.
Calling sequence
================
``optim = CMAEvolutionStrategy(x0, sigma0, opts)``
returns a class instance.
Arguments
---------
`x0`
initial solution, starting point (phenotype).
`sigma0`
initial standard deviation. The problem
variables should have been scaled, such that a single
standard deviation on all variables is useful and the
optimum is expected to lie within about `x0` +- ``3*sigma0``.
See also options `scaling_of_variables`.
Often one wants to check for solutions close to the initial
point. This allows for an easier check for consistency of
the objective function and its interfacing with the optimizer.
In this case, a much smaller `sigma0` is advisable.
`opts`
options, a dictionary with optional settings,
see class `Options`.
Main interface / usage
======================
The ask-and-tell interface is inherited from the generic `OOOptimizer`
interface for iterative optimization algorithms (see there). With ::
optim = CMAEvolutionStrategy(8 * [0.5], 0.2)
an object instance is generated. In each iteration ::
solutions = optim.ask()
is used to ask for new candidate solutions (possibly several times) and ::
optim.tell(solutions, func_values)
passes the respective function values to `optim`. Instead of `ask()`,
the class `CMAEvolutionStrategy` also provides ::
(solutions, func_values) = optim.ask_and_eval(objective_func)
Therefore, after initialization, an entire optimization can be written
in two lines like ::
while not optim.stop():
optim.tell(*optim.ask_and_eval(objective_func))
Without the freedom of executing additional lines within the iteration,
the same reads in a single line as ::
optim.optimize(objective_func)
Besides for termination criteria, in CMA-ES only
the ranks of the `func_values` are relevant.
Attributes and Properties
=========================
- `inputargs` -- passed input arguments
- `inopts` -- passed options
- `opts` -- actually used options, some of them can be changed any
time, see class `Options`
- `popsize` -- population size lambda, number of candidate solutions
returned by `ask()`
Details
=======
The following two enhancements are turned off by default.
**Active CMA** is implemented with option ``CMA_active`` and conducts
an update of the covariance matrix with negative weights. The
exponential update is implemented, where from a mathematical
viewpoint positive definiteness is guarantied. The update is applied
after the default update and only before the covariance matrix is
decomposed, which limits the additional computational burden to be
at most a factor of three (typically smaller). A typical speed up
factor (number of f-evaluations) is between 1.1 and two.
References: Jastrebski and Arnold, CEC 2006, Glasmachers et al, GECCO 2010.
**Selective mirroring** is implemented with option ``CMA_mirrors`` in
the method ``get_mirror()``. Only the method `ask_and_eval()` will
then sample selectively mirrored vectors. In selective mirroring, only
the worst solutions are mirrored. With the default small number of mirrors,
*pairwise selection* (where at most one of the two mirrors contribute to the
update of the distribution mean) is implicitely guarantied under selective
mirroring and therefore not explicitly implemented.
References: Brockhoff et al, PPSN 2010, Auger et al, GECCO 2011.
Examples
========
Super-short example, with output shown:
>>> import cma
>>> # construct an object instance in 4-D, sigma0=1
>>> es = cma.CMAEvolutionStrategy(4 * [1], 1, {'seed':234})
(4_w,8)-CMA-ES (mu_w=2.6,w_1=52%) in dimension 4 (seed=234)
>>>
>>> # iterate until termination
>>> while not es.stop():
... X = es.ask()
... es.tell(X, [cma.fcts.elli(x) for x in X])
... es.disp() # by default sparse, see option verb_disp
Iterat #Fevals function value axis ratio sigma minstd maxstd min:sec
1 8 2.093015112685775e+04 1.0e+00 9.27e-01 9e-01 9e-01 0:0.0
2 16 4.964814235917688e+04 1.1e+00 9.54e-01 9e-01 1e+00 0:0.0
3 24 2.876682459926845e+05 1.2e+00 1.02e+00 9e-01 1e+00 0:0.0
100 800 6.809045875281943e-01 1.3e+02 1.41e-02 1e-04 1e-02 0:0.2
200 1600 2.473662150861846e-10 8.0e+02 3.08e-05 1e-08 8e-06 0:0.5
233 1864 2.766344961865341e-14 8.6e+02 7.99e-07 8e-11 7e-08 0:0.6
>>>
>>> cma.pprint(es.result())
(Solution([ -1.98546755e-09, -1.10214235e-09, 6.43822409e-11,
-1.68621326e-11]),
4.5119610261406537e-16,
1666,
1672,
209,
array([ -9.13545269e-09, -1.45520541e-09, -6.47755631e-11,
-1.00643523e-11]),
array([ 3.20258681e-08, 3.15614974e-09, 2.75282215e-10,
3.27482983e-11]))
>>>
>>> # help(es.result) shows
result(self) method of cma.CMAEvolutionStrategy instance
return ``(xbest, f(xbest), evaluations_xbest, evaluations, iterations, pheno(xmean), effective_stds)``
Using the multiprocessing module, we can evaluate the function in parallel with a simple
modification of the example ::
import multiprocessing
# prepare es = ...
pool = multiprocessing.Pool(es.popsize)
while not es.stop():
X = es.ask()
es.tell(X, pool.map_async(cma.felli, X).get()) # use chunksize parameter as popsize/len(pool)?
Example with a data logger, lower bounds (at zero) and handling infeasible solutions:
>>> import cma
>>> import numpy as np
>>> es = cma.CMAEvolutionStrategy(10 * [0.2], 0.5, {'bounds': [0, np.inf]})
>>> logger = cma.CMADataLogger().register(es)
>>> while not es.stop():
... fit, X = [], []
... while len(X) < es.popsize:
... curr_fit = np.NaN
... while curr_fit is np.NaN:
... x = es.ask(1)[0]
... curr_fit = cma.fcts.somenan(x, cma.fcts.elli) # might return np.NaN
... X.append(x)
... fit.append(curr_fit)
... es.tell(X, fit)
... logger.add()
... es.disp()
>>>
>>> assert es.result()[1] < 1e-9
>>> assert es.result()[2] < 9000 # by internal termination
>>> logger.plot() # plot data
>>> cma.show()
>>> print(' *** if execution stalls close the figure window to continue (and check out ipython --pylab) ***')
Example implementing restarts with increasing popsize (IPOP), output is not displayed:
>>> import cma, numpy as np
>>>
>>> # restart with increasing population size (IPOP)
>>> bestever = cma.BestSolution()
>>> for lam in 10 * 2**np.arange(7): # 10, 20, 40, 80, ..., 10 * 2**6
... es = cma.CMAEvolutionStrategy('6 - 8 * np.random.rand(9)', # 9-D
... 5, # initial std sigma0
... {'popsize': lam,
... 'verb_append': bestever.evalsall}) # pass options
... logger = cma.CMADataLogger().register(es, append=bestever.evalsall)
... while not es.stop():
... X = es.ask() # get list of new solutions
... fit = [cma.fcts.rastrigin(x) for x in X] # evaluate each solution
... es.tell(X, fit) # besides for termination only the ranking in fit is used
...
... # display some output
... logger.add() # add a "data point" to the log, writing in files
... es.disp() # uses option verb_disp with default 100
...
... print('termination:', es.stop())
... cma.pprint(es.best.__dict__)
...
... bestever.update(es.best)
...
... # show a plot
... logger.plot();
... if bestever.f < 1e-8: # global optimum was hit
... break
>>> assert es.result()[1] < 1e-8
On the Rastrigin function, usually after five restarts the global optimum
is located.
The final example shows how to resume:
>>> import cma, pickle
>>>
>>> es = cma.CMAEvolutionStrategy(12 * [0.1], # a new instance, 12-D
... 0.5) # initial std sigma0
>>> logger = cma.CMADataLogger().register(es)
>>> es.optimize(cma.fcts.rosen, logger, iterations=100)
>>> logger.plot()
>>> pickle.dump(es, open('saved-cma-object.pkl', 'wb'))
>>> print('saved')
>>> del es, logger # let's start fresh
>>>
>>> es = pickle.load(open('saved-cma-object.pkl', 'rb'))
>>> print('resumed')
>>> logger = cma.CMADataLogger(es.opts['verb_filenameprefix'] # use same name
... ).register(es, True) # True: append to old log data
>>> es.optimize(cma.fcts.rosen, logger, verb_disp=200)
>>> assert es.result()[2] < 15000
>>> cma.pprint(es.result())
>>> logger.plot()
Missing Features
================
Option ``randn`` to pass a random number generator.
:See: `fmin()`, `Options`, `plot()`, `ask()`, `tell()`, `ask_and_eval()`
"""
# __all__ = () # TODO this would be the interface
#____________________________________________________________
@property # read only attribute decorator for a method
def popsize(self):
"""number of samples by default returned by` ask()`
"""
return self.sp.popsize
# this is not compatible with python2.5:
# @popsize.setter
# def popsize(self, p):
# """popsize cannot be set (this might change in future)
# """
# raise _Error("popsize cannot be changed (this might change in future)")
#____________________________________________________________
#____________________________________________________________
def stop(self, check=True):
"""return a dictionary with the termination status.
With ``check==False``, the termination conditions are not checked and
the status might not reflect the current situation.
"""
if (check and self.countiter > 0 and self.opts['termination_callback'] and
self.opts['termination_callback'] != str(self.opts['termination_callback'])):
self.callbackstop = self.opts['termination_callback'](self)
return self.stopdict(self if check else None) # update the stopdict and return a Dict
#____________________________________________________________
#____________________________________________________________
def __init__(self, x0, sigma0, inopts = {}):
"""see class `CMAEvolutionStrategy`
"""
self.inputargs = dict(locals()) # for the record
del self.inputargs['self'] # otherwise the instance self has a cyclic reference
self.inopts = inopts
opts = Options(inopts).complement() # Options() == fmin([],[]) == defaultOptions()
if opts['noise_handling'] and eval(opts['noise_handling']):
raise ValueError('noise_handling not available with class CMAEvolutionStrategy, use function fmin')
if opts['restarts'] and eval(opts['restarts']):
raise ValueError('restarts not available with class CMAEvolutionStrategy, use function fmin')
if x0 == str(x0):
x0 = eval(x0)
self.mean = array(x0) # should not have column or row, is just 1-D
if self.mean.ndim == 2:
print('WARNING: input x0 should be a list or 1-D array, trying to flatten ' +
str(self.mean.shape) + '-array')
if self.mean.shape[0] == 1:
self.mean = self.mean[0]
elif self.mean.shape[1] == 1:
self.mean = array([x[0] for x in self.mean])
if self.mean.ndim != 1:
raise _Error('x0 must be 1-D array')
if len(self.mean) <= 1:
raise _Error('optimization in 1-D is not supported (code was never tested)')
self.N = self.mean.shape[0]
N = self.N
self.mean.resize(N) # 1-D array, not really necessary?!
self.x0 = self.mean
self.mean = self.x0.copy() # goes to initialize
self.sigma0 = sigma0
if isinstance(sigma0, str): # TODO: no real need here (do rather in fmin)
self.sigma0 = eval(sigma0) # like '1./N' or 'np.random.rand(1)[0]+1e-2'
if np.size(self.sigma0) != 1 or np.shape(self.sigma0):
raise _Error('input argument sigma0 must be (or evaluate to) a scalar')
self.sigma = self.sigma0 # goes to inialize
# extract/expand options
opts.evalall(locals()) # using only N
self.opts = opts
self.randn = opts['randn']
self.gp = GenoPheno(N, opts['scaling_of_variables'], opts['typical_x'],
opts['bounds'], opts['fixed_variables'], opts['transformation'])
self.boundPenalty = BoundPenalty(self.gp.bounds)
s = self.gp.geno(self.mean)
self.mean = self.gp.geno(self.mean, bounds=self.gp.bounds)
self.N = len(self.mean)
N = self.N
if (self.mean != s).any():
print('WARNING: initial solution is out of the domain boundaries:')
print(' x0 = ' + str(self.inputargs['x0']))
print(' ldom = ' + str(self.gp.bounds[0]))
print(' udom = ' + str(self.gp.bounds[1]))
self.fmean = np.NaN # TODO name should change? prints nan (OK with matlab&octave)
self.fmean_noise_free = 0. # for output only
self.sp = CMAParameters(N, opts)
self.sp0 = self.sp # looks useless, as it is not a copy
# initialization of state variables
self.countiter = 0
self.countevals = max((0, opts['verb_append'])) if type(opts['verb_append']) is not bool else 0
self.ps = np.zeros(N)
self.pc = np.zeros(N)
stds = np.ones(N)
self.sigma_vec = np.ones(N) if np.isfinite(self.sp.dampsvec) else 1
if np.all(self.opts['CMA_teststds']): # also 0 would not make sense
stds = self.opts['CMA_teststds']
if np.size(stds) != N:
raise _Error('CMA_teststds option must have dimension = ' + str(N))
if self.opts['CMA_diagonal']: # is True or > 0
# linear time and space complexity
self.B = array(1) # works fine with np.dot(self.B, anything) and self.B.T
self.C = stds**2 # TODO: remove this!?
self.dC = self.C
else:
self.B = np.eye(N) # identity(N), do not from matlib import *, as eye is a matrix there
# prevent equal eigenvals, a hack for np.linalg:
self.C = np.diag(stds**2 * exp(1e-6*(np.random.rand(N)-0.5)))
self.dC = np.diag(self.C)
self.Zneg = np.zeros((N, N))
self.D = stds
self.flgtelldone = True
self.itereigenupdated = self.countiter
self.noiseS = 0 # noise "signal"
self.hsiglist = []
if not opts['seed']:
np.random.seed()
six_decimals = (time.time() - 1e6 * (time.time() // 1e6))
opts['seed'] = 1e5 * np.random.rand() + six_decimals + 1e5 * (time.time() % 1)
opts['seed'] = int(opts['seed'])
np.random.seed(opts['seed'])
self.sent_solutions = SolutionDict()
self.best = BestSolution()
out = {} # TODO: obsolete, replaced by method results()?
out['best'] = self.best
# out['hsigcount'] = 0
out['termination'] = {}
self.out = out
self.const = BlancClass()
self.const.chiN = N**0.5*(1-1./(4.*N)+1./(21.*N**2)) # expectation of norm(randn(N,1))
# attribute for stopping criteria in function stop
self.stopdict = CMAStopDict()
self.callbackstop = 0
self.fit = BlancClass()
self.fit.fit = [] # not really necessary
self.fit.hist = [] # short history of best
self.fit.histbest = [] # long history of best
self.fit.histmedian = [] # long history of median
self.more_to_write = [] #[1, 1, 1, 1] # N*[1] # needed when writing takes place before setting
# say hello
if opts['verb_disp'] > 0:
sweighted = '_w' if self.sp.mu > 1 else ''
smirr = 'mirr%d' % (self.sp.lam_mirr) if self.sp.lam_mirr else ''
print('(%d' % (self.sp.mu) + sweighted + ',%d' % (self.sp.popsize) + smirr + ')-CMA-ES' +
' (mu_w=%2.1f,w_1=%d%%)' % (self.sp.mueff, int(100*self.sp.weights[0])) +
' in dimension %d (seed=%d, %s)' % (N, opts['seed'], time.asctime())) # + func.__name__
if opts['CMA_diagonal'] and self.sp.CMA_on:
s = ''
if opts['CMA_diagonal'] is not True:
s = ' for '
if opts['CMA_diagonal'] < np.inf:
s += str(int(opts['CMA_diagonal']))
else:
s += str(np.floor(opts['CMA_diagonal']))
s += ' iterations'
s += ' (1/ccov=' + str(round(1./(self.sp.c1+self.sp.cmu))) + ')'
print(' Covariance matrix is diagonal' + s)
#____________________________________________________________
#____________________________________________________________
def ask(self, number=None, xmean=None, sigma_fac=1):
"""get new candidate solutions, sampled from a multi-variate
normal distribution and transformed to f-representation
(phenotype) to be evaluated.
Arguments
---------
`number`
number of returned solutions, by default the
population size ``popsize`` (AKA ``lambda``).
`xmean`
distribution mean
`sigma`
multiplier for internal sample width (standard
deviation)
Return
------
A list of N-dimensional candidate solutions to be evaluated
Example
-------
>>> import cma
>>> es = cma.CMAEvolutionStrategy([0,0,0,0], 0.3)
>>> while not es.stop() and es.best.f > 1e-6: # my_desired_target_f_value
... X = es.ask() # get list of new solutions
... fit = [cma.fcts.rosen(x) for x in X] # call function rosen with each solution
... es.tell(X, fit) # feed values
:See: `ask_and_eval`, `ask_geno`, `tell`
"""
pop_geno = self.ask_geno(number, xmean, sigma_fac)
# N,lambda=20,200: overall CPU 7s vs 5s == 40% overhead, even without bounds!
# new data: 11.5s vs 9.5s == 20%
# TODO: check here, whether this is necessary?
# return [self.gp.pheno(x, copy=False, bounds=self.gp.bounds) for x in pop] # probably fine
# return [Solution(self.gp.pheno(x, copy=False), copy=False) for x in pop] # here comes the memory leak, now solved
# pop_pheno = [Solution(self.gp.pheno(x, copy=False), copy=False).repair(self.gp.bounds) for x in pop_geno]
pop_pheno = [self.gp.pheno(x, copy=True, bounds=self.gp.bounds) for x in pop_geno]
if not self.gp.isidentity or use_sent_solutions: # costs 25% in CPU performance with N,lambda=20,200
# archive returned solutions, first clean up archive
if self.countiter % 30/self.popsize**0.5 < 1:
self.sent_solutions.truncate(0, self.countiter - 1 - 3 * self.N/self.popsize**0.5)
# insert solutions
for i in range(len(pop_geno)):
self.sent_solutions[pop_pheno[i]] = {'geno': pop_geno[i],
'pheno': pop_pheno[i],
'iteration': self.countiter}
return pop_pheno
#____________________________________________________________
#____________________________________________________________
def ask_geno(self, number=None, xmean=None, sigma_fac=1):
"""get new candidate solutions in genotyp, sampled from a
multi-variate normal distribution.
Arguments are
`number`
number of returned solutions, by default the
population size `popsize` (AKA lambda).
`xmean`
distribution mean
`sigma_fac`
multiplier for internal sample width (standard
deviation)
`ask_geno` returns a list of N-dimensional candidate solutions
in genotyp representation and is called by `ask`.
:See: `ask`, `ask_and_eval`
"""
if number is None or number < 1:
number = self.sp.popsize
if xmean is None:
xmean = self.mean
if self.countiter == 0:
self.tic = time.clock() # backward compatible
self.elapsed_time = ElapsedTime()
if self.opts['CMA_AII']:
if self.countiter == 0:
self.aii = AII(self.x0, self.sigma0)
self.flgtelldone = False
pop = self.aii.ask(number)
return pop
sigma = sigma_fac * self.sigma
# update parameters for sampling the distribution
# fac 0 1 10
# 150-D cigar:
# 50749 50464 50787
# 200-D elli: == 6.9
# 99900 101160
# 100995 103275 == 2% loss
# 100-D elli: == 6.9
# 363052 369325 < 2% loss
# 365075 365755
# update distribution
if self.sp.CMA_on and (
(self.opts['updatecovwait'] is None and
self.countiter >=
self.itereigenupdated + 1./(self.sp.c1+self.sp.cmu)/self.N/10
) or
(self.opts['updatecovwait'] is not None and
self.countiter > self.itereigenupdated + self.opts['updatecovwait']
)):
self.updateBD()
# sample distribution
if self.flgtelldone: # could be done in tell()!?
self.flgtelldone = False
self.ary = []
# each row is a solution
arz = self.randn((number, self.N))
if 11 < 3: # mutate along the principal axes only
perm = np.random.permutation(self.N) # indices for mutated principal component
for i in range(min((len(arz), self.N))):
# perm = np.random.permutation(self.N) # random principal component, should be much worse
l = sum(arz[i]**2)**0.5
arz[i] *= 0
if 11 < 3: # mirrored sampling
arz[i][perm[int(i/2)]] = l * (2 * (i % 2) - 1)
else:
arz[i][perm[i % self.N]] = l * np.sign(np.random.rand(1) - 0.5)
if number == self.sp.popsize:
self.arz = arz # is never used
else:
pass
if 11 < 3: # normalize the length to chiN
for i in range(len(arz)):
# arz[i] *= exp(self.randn(1)[0] / 8)
ss = sum(arz[i]**2)**0.5
arz[i] *= self.const.chiN / ss
# or to average
# arz *= 1 * self.const.chiN / np.mean([sum(z**2)**0.5 for z in arz])
# fac = np.mean(sum(arz**2, 1)**0.5)
# print fac
# arz *= self.const.chiN / fac
self.ary = self.sigma_vec * np.dot(self.B, (self.D * arz).T).T
pop = xmean + sigma * self.ary
self.evaluations_per_f_value = 1
return pop
def get_mirror(self, x):
"""return ``pheno(self.mean - (geno(x) - self.mean))``.
TODO: this implementation is yet experimental.
Selectively mirrored sampling improves to a moderate extend but
overadditively with active CMA for quite understandable reasons.
Optimal number of mirrors are suprisingly small: 1,2,3 for maxlam=7,13,20
however note that 3,6,10 are the respective maximal possible mirrors that
must be clearly suboptimal.
"""
try:
# dx = x.geno - self.mean, repair or boundary handling is not taken into account
dx = self.sent_solutions[x]['geno'] - self.mean
except:
print('WARNING: use of geno is depreciated')
dx = self.gp.geno(x, copy=True) - self.mean
dx *= sum(self.randn(self.N)**2)**0.5 / self.mahalanobisNorm(dx)
x = self.mean - dx
y = self.gp.pheno(x, bounds=self.gp.bounds)
if not self.gp.isidentity or use_sent_solutions: # costs 25% in CPU performance with N,lambda=20,200
self.sent_solutions[y] = {'geno': x,
'pheno': y,
'iteration': self.countiter}
return y
def mirror_penalized(self, f_values, idx):
"""obsolete and subject to removal (TODO),
return modified f-values such that for each mirror one becomes worst.
This function is useless when selective mirroring is applied with no
more than (lambda-mu)/2 solutions.
Mirrors are leading and trailing values in ``f_values``.
"""
assert len(f_values) >= 2 * len(idx)
m = np.max(np.abs(f_values))
for i in len(idx):
if f_values[idx[i]] > f_values[-1-i]:
f_values[idx[i]] += m
else:
f_values[-1-i] += m
return f_values
def mirror_idx_cov(self, f_values, idx1): # will most likely be removed
"""obsolete and subject to removal (TODO),
return indices for negative ("active") update of the covariance matrix
assuming that ``f_values[idx1[i]]`` and ``f_values[-1-i]`` are
the corresponding mirrored values
computes the index of the worse solution sorted by the f-value of the
better solution.
TODO: when the actual mirror was rejected, it is better
to return idx1 instead of idx2.
Remark: this function might not be necessary at all: if the worst solution
is the best mirrored, the covariance matrix updates cancel (cave: weights
and learning rates), which seems what is desirable. If the mirror is bad,
as strong negative update is made, again what is desirable.
And the fitness--step-length correlation is in part addressed by
using flat weights.
"""
idx2 = np.arange(len(f_values) - 1, len(f_values) - 1 - len(idx1), -1)
f = []
for i in range(len(idx1)):
f.append(min((f_values[idx1[i]], f_values[idx2[i]])))
# idx.append(idx1[i] if f_values[idx1[i]] > f_values[idx2[i]] else idx2[i])
return idx2[np.argsort(f)][-1::-1]
#____________________________________________________________
#____________________________________________________________
#
def ask_and_eval(self, func, args=(), number=None, xmean=None, sigma_fac=1,
evaluations=1, aggregation=np.median):
"""samples `number` solutions and evaluates them on `func`, where
each solution `s` is resampled until ``func(s) not in (numpy.NaN, None)``.
Arguments
---------
`func`
objective function
`args`
additional parameters for `func`
`number`
number of solutions to be sampled, by default
population size ``popsize`` (AKA lambda)
`xmean`
mean for sampling the solutions, by default ``self.mean``.
`sigma_fac`
multiplier for sampling width, standard deviation, for example
to get a small perturbation of solution `xmean`
`evaluations`
number of evaluations for each sampled solution
`aggregation`
function that aggregates `evaluations` values to
as single value.
Return
------
``(X, fit)``, where
X -- list of solutions
fit -- list of respective function values
Details
-------
When ``func(x)`` returns `NaN` or `None` a new solution is sampled until
``func(x) not in (numpy.NaN, None)``. The argument to `func` can be
freely modified within `func`.
Depending on the ``CMA_mirrors`` option, some solutions are not sampled
independently but as mirrors of other bad solutions. This is a simple
derandomization that can save 10-30% of the evaluations in particular
with small populations, for example on the cigar function.
Example
-------
>>> import cma
>>> x0, sigma0 = 8*[10], 1 # 8-D
>>> es = cma.CMAEvolutionStrategy(x0, sigma0)
>>> while not es.stop():
... X, fit = es.ask_and_eval(cma.fcts.elli) # handles NaN with resampling
... es.tell(X, fit) # pass on fitness values
... es.disp(20) # print every 20-th iteration
>>> print('terminated on ' + str(es.stop()))
A single iteration step can be expressed in one line, such that
an entire optimization after initialization becomes
::
while not es.stop():
es.tell(*es.ask_and_eval(cma.fcts.elli))
"""
# initialize
popsize = self.sp.popsize
if number is not None:
popsize = number
selective_mirroring = True
nmirrors = self.sp.lam_mirr
if popsize != self.sp.popsize:
nmirrors = Mh.sround(popsize * self.sp.lam_mirr / self.sp.popsize)
# TODO: now selective mirroring might be impaired
assert nmirrors <= popsize // 2
self.mirrors_idx = np.arange(nmirrors) # might never be used
self.mirrors_rejected_idx = [] # might never be used
if xmean is None:
xmean = self.mean
# do the work
fit = [] # or np.NaN * np.empty(number)
X_first = self.ask(popsize)
X = []
for k in range(int(popsize)):
nreject = -1
f = np.NaN
while f in (np.NaN, None): # rejection sampling
nreject += 1
if k < popsize - nmirrors or nreject:
if nreject:
x = self.ask(1, xmean, sigma_fac)[0]
else:
x = X_first.pop(0)
else: # mirrored sample
if k == popsize - nmirrors and selective_mirroring:
self.mirrors_idx = np.argsort(fit)[-1:-1-nmirrors:-1]
x = self.get_mirror(X[self.mirrors_idx[popsize - 1 - k]])
if nreject == 1 and k >= popsize - nmirrors:
self.mirrors_rejected_idx.append(k)
# contraints handling test hardwired ccccccccccc
if 11 < 3 and self.opts['vv'] and nreject < 2: # trying out negative C-update as constraints handling
if not hasattr(self, 'constraints_paths'):
k = 1
self.constraints_paths = [np.zeros(self.N) for _i in range(k)]
Izero = np.zeros([self.N, self.N])
for i in range(self.N):
if x[i] < 0:
Izero[i][i] = 1
self.C -= self.opts['vv'] * Izero
Izero[i][i] = 0
if 1 < 3 and sum([ (9 + i + 1) * x[i] for i in range(self.N)]) > 50e3:
self.constraints_paths[0] = 0.9 * self.constraints_paths[0] + 0.1 * (x - self.mean) / self.sigma
self.C -= (self.opts['vv'] / self.N) * np.outer(self.constraints_paths[0], self.constraints_paths[0])
f = func(x, *args)
if f not in (np.NaN, None) and evaluations > 1:
f = aggregation([f] + [func(x, *args) for _i in range(int(evaluations-1))])
if nreject + 1 % 1000 == 0:
print(' %d solutions rejected (f-value NaN or None) at iteration %d' %
(nreject, self.countiter))
fit.append(f)
X.append(x)
self.evaluations_per_f_value = int(evaluations)
return X, fit
#____________________________________________________________
def tell(self, solutions, function_values, check_points=None, copy=False):
"""pass objective function values to prepare for next
iteration. This core procedure of the CMA-ES algorithm updates
all state variables, in particular the two evolution paths, the
distribution mean, the covariance matrix and a step-size.
Arguments
---------
`solutions`
list or array of candidate solution points (of
type `numpy.ndarray`), most presumably before
delivered by method `ask()` or `ask_and_eval()`.
`function_values`
list or array of objective function values
corresponding to the respective points. Beside for termination
decisions, only the ranking of values in `function_values`
is used.
`check_points`
If ``check_points is None``, only solutions that are not generated
by `ask()` are possibly clipped (recommended). ``False`` does not clip
any solution (not recommended).
If ``True``, clips solutions that realize long steps (i.e. also
those that are unlikely to be generated with `ask()`). `check_points`
can be a list of indices to be checked in solutions.
`copy`
``solutions`` can be modified in this routine, if ``copy is False``
Details
-------
`tell()` updates the parameters of the multivariate
normal search distribution, namely covariance matrix and
step-size and updates also the attributes `countiter` and
`countevals`. To check the points for consistency is quadratic
in the dimension (like sampling points).
Bugs
----
The effect of changing the solutions delivered by `ask()` depends on whether
boundary handling is applied. With boundary handling, modifications are
disregarded. This is necessary to apply the default boundary handling that
uses unrepaired solutions but might change in future.
Example
-------
::
import cma
func = cma.fcts.elli # choose objective function
es = cma.CMAEvolutionStrategy(cma.np.random.rand(10), 1)
while not es.stop():
X = es.ask()
es.tell(X, [func(x) for x in X])
es.result() # where the result can be found
:See: class `CMAEvolutionStrategy`, `ask()`, `ask_and_eval()`, `fmin()`
"""
#____________________________________________________________
# TODO: consider an input argument that flags injected trust-worthy solutions (which means
# that they can be treated "absolut" rather than "relative")
if self.flgtelldone:
raise _Error('tell should only be called once per iteration')
lam = len(solutions)
if lam != array(function_values).shape[0]:
raise _Error('for each candidate solution '
+ 'a function value must be provided')
if lam + self.sp.lam_mirr < 3:
raise _Error('population size ' + str(lam) + ' is too small when option CMA_mirrors * popsize < 0.5')
if not np.isscalar(function_values[0]):
if np.isscalar(function_values[0][0]):
if self.countiter <= 1:
print('WARNING: function values are not a list of scalars (further warnings are suppressed)')
function_values = [val[0] for val in function_values]
else:
raise _Error('objective function values must be a list of scalars')
### prepare
N = self.N
sp = self.sp
if 11 < 3 and lam != sp.popsize: # turned off, because mu should stay constant, still not desastrous
print('WARNING: population size has changed, recomputing parameters')
self.sp.set(self.opts, lam) # not really tested
if lam < sp.mu: # rather decrease cmean instead of having mu > lambda//2
raise _Error('not enough solutions passed to function tell (mu>lambda)')
self.countiter += 1 # >= 1 now
self.countevals += sp.popsize * self.evaluations_per_f_value
self.best.update(solutions, self.sent_solutions, function_values, self.countevals)
flgseparable = self.opts['CMA_diagonal'] is True \
or self.countiter <= self.opts['CMA_diagonal']
if not flgseparable and len(self.C.shape) == 1: # C was diagonal ie 1-D
# enter non-separable phase (no easy return from here)
self.B = np.eye(N) # identity(N)
self.C = np.diag(self.C)
idx = np.argsort(self.D)
self.D = self.D[idx]
self.B = self.B[:,idx]
self.Zneg = np.zeros((N, N))
### manage fitness
fit = self.fit # make short cut
# CPU for N,lam=20,200: this takes 10s vs 7s
fit.bndpen = self.boundPenalty.update(function_values, self)(solutions, self.sent_solutions, self.gp)
# for testing:
# fit.bndpen = self.boundPenalty.update(function_values, self)([s.unrepaired for s in solutions])
fit.idx = np.argsort(array(fit.bndpen) + array(function_values))
fit.fit = array(function_values, copy=False)[fit.idx]
# update output data TODO: this is obsolete!? However: need communicate current best x-value?
# old: out['recent_x'] = self.gp.pheno(pop[0])
self.out['recent_x'] = array(solutions[fit.idx[0]]) # TODO: change in a data structure(?) and use current as identify
self.out['recent_f'] = fit.fit[0]
# fitness histories
fit.hist.insert(0, fit.fit[0])
# if len(self.fit.histbest) < 120+30*N/sp.popsize or # does not help, as tablet in the beginning is the critical counter-case
if ((self.countiter % 5) == 0): # 20 percent of 1e5 gen.
fit.histbest.insert(0, fit.fit[0])
fit.histmedian.insert(0, np.median(fit.fit) if len(fit.fit) < 21
else fit.fit[self.popsize // 2])
if len(fit.histbest) > 2e4: # 10 + 30*N/sp.popsize:
fit.histbest.pop()
fit.histmedian.pop()
if len(fit.hist) > 10 + 30*N/sp.popsize:
fit.hist.pop()
if self.opts['CMA_AII']:
self.aii.tell(solutions, function_values)
self.flgtelldone = True
# for output:
self.mean = self.aii.mean
self.dC = self.aii.sigmai**2
self.sigma = self.aii.sigma
self.D = 1e-11 + (self.aii.r**2)**0.5
self.more_to_write += [self.aii.sigma_r]
return
# TODO: clean up inconsistency when an unrepaired solution is available and used
pop = [] # create pop from input argument solutions
for s in solutions: # use phenotype before Solution.repair()
if use_sent_solutions:
x = self.sent_solutions.pop(s, None) # 12.7s vs 11.3s with N,lambda=20,200
if x is not None:
pop.append(x['geno'])
# TODO: keep additional infos or don't pop s from sent_solutions in the first place
else:
# print 'WARNING: solution not found in ``self.sent_solutions`` (is expected for injected solutions)'
pop.append(self.gp.geno(s, copy=copy)) # cannot recover the original genotype with boundary handling
if check_points in (None, True, 1):
self.repair_genotype(pop[-1]) # necessary if pop[-1] was changed or injected by the user.
else: # TODO: to be removed?
# print 'WARNING: ``geno`` mapping depreciated'
pop.append(self.gp.geno(s, copy=copy))
if check_points in (None, True, 1):
self.repair_genotype(pop[-1]) # necessary or not?
# print 'repaired'
mold = self.mean
sigma_fac = 1
# check and normalize each x - m
# check_points is a flag (None is default: check non-known solutions) or an index list
# should also a number possible (first check_points points)?
if check_points not in (None, False, 0, [], ()): # useful in case of injected solutions and/or adaptive encoding, however is automatic with use_sent_solutions
try:
if len(check_points):
idx = check_points
except:
idx = range(sp.popsize)
for k in idx:
self.repair_genotype(pop[k])
# sort pop
if type(pop) is not array: # only arrays can be multiple indexed
pop = array(pop, copy=False)
pop = pop[fit.idx]
if self.opts['CMA_elitist'] and self.best.f < fit.fit[0]:
if self.best.x_geno is not None:
xp = [self.best.x_geno]
# xp = [self.best.xdict['geno']]
# xp = [self.gp.geno(self.best.x[:])] # TODO: remove
# print self.mahalanobisNorm(xp[0]-self.mean)
self.clip_or_fit_solutions(xp, [0])
pop = array([xp[0]] + list(pop))
else:
print('genotype for elitist not found')
# compute new mean
self.mean = mold + self.sp.cmean * \
(sum(sp.weights * pop[0:sp.mu].T, 1) - mold)
# check Delta m (this is not default, but could become at some point)
# CAVE: upper_length=sqrt(2)+2 is too restrictive, test upper_length = sqrt(2*N) thoroughly.
# simple test case injecting self.mean:
# self.mean = 1e-4 * self.sigma * np.random.randn(N)
if 11 < 3 and self.opts['vv'] and check_points: # TODO: check_points might be an index-list
cmean = self.sp.cmean / min(1, (sqrt(self.opts['vv']*N)+2) / ( # abuse of cmean
(sqrt(self.sp.mueff) / self.sp.cmean) *
self.mahalanobisNorm(self.mean - mold)))
else:
cmean = self.sp.cmean
if 11 < 3: # plot length of mean - mold
self.more_to_write += [sqrt(sp.mueff) *
sum(((1./self.D) * dot(self.B.T, self.mean - mold))**2)**0.5 /
self.sigma / sqrt(N) / cmean]
# get learning rate constants
cc, c1, cmu = sp.cc, sp.c1, sp.cmu
if flgseparable:
cc, c1, cmu = sp.cc_sep, sp.c1_sep, sp.cmu_sep
# now the real work can start
# evolution paths
self.ps = (1-sp.cs) * self.ps + \
(sqrt(sp.cs*(2-sp.cs)*sp.mueff) / self.sigma / cmean) * \
dot(self.B, (1./self.D) * dot(self.B.T, (self.mean - mold) / self.sigma_vec))
# "hsig", correction with self.countiter seems not necessary, also pc starts with zero
hsig = sum(self.ps**2) / (1-(1-sp.cs)**(2*self.countiter)) / self.N < 2 + 4./(N+1)
if 11 < 3:
# hsig = 1
# sp.cc = 4 / (N + 4)
# sp.cs = 4 / (N + 4)
# sp.cc = 1
# sp.damps = 2 #
# sp.CMA_on = False
# c1 = 0 # 2 / ((N + 1.3)**2 + 0 * sp.mu) # 1 / N**2
# cmu = min([1 - c1, cmu])
if self.countiter == 1:
print('parameters modified')
# hsig = sum(self.ps**2) / self.N < 2 + 4./(N+1)
# adjust missing variance due to hsig, in 4-D with damps=1e99 and sig0 small
# hsig leads to premature convergence of C otherwise
#hsiga = (1-hsig**2) * c1 * cc * (2-cc) # to be removed in future
c1a = c1 - (1-hsig**2) * c1 * cc * (2-cc) # adjust for variance loss
if 11 < 3: # diagnostic data
self.out['hsigcount'] += 1 - hsig
if not hsig:
self.hsiglist.append(self.countiter)
if 11 < 3: # diagnostic message
if not hsig:
print(str(self.countiter) + ': hsig-stall')
if 11 < 3: # for testing purpose
hsig = 1 # TODO:
# put correction term, but how?
if self.countiter == 1:
print('hsig=1')
self.pc = (1-cc) * self.pc + \
hsig * (sqrt(cc*(2-cc)*sp.mueff) / self.sigma / cmean) * \
(self.mean - mold) / self.sigma_vec
# covariance matrix adaptation/udpate
if sp.CMA_on:
# assert sp.c1 + sp.cmu < sp.mueff / N # ??
assert c1 + cmu <= 1
# default full matrix case
if not flgseparable:
Z = (pop[0:sp.mu] - mold) / (self.sigma * self.sigma_vec)
Z = dot((cmu * sp.weights) * Z.T, Z) # learning rate integrated
if self.sp.neg.cmuexp:
tmp = (pop[-sp.neg.mu:] - mold) / (self.sigma * self.sigma_vec)
self.Zneg *= 1 - self.sp.neg.cmuexp # for some reason necessary?
self.Zneg += dot(sp.neg.weights * tmp.T, tmp) - self.C
# self.update_exponential(dot(sp.neg.weights * tmp.T, tmp) - 1 * self.C, -1*self.sp.neg.cmuexp)
if 11 < 3: # ?3 to 5 times slower??
Z = np.zeros((N,N))
for k in range(sp.mu):
z = (pop[k]-mold)
Z += np.outer((cmu * sp.weights[k] / (self.sigma * self.sigma_vec)**2) * z, z)
self.C *= 1 - c1a - cmu
self.C += np.outer(c1 * self.pc, self.pc) + Z
self.dC = np.diag(self.C) # for output and termination checking
else: # separable/diagonal linear case
assert(c1+cmu <= 1)
Z = np.zeros(N)
for k in range(sp.mu):
z = (pop[k]-mold) / (self.sigma * self.sigma_vec) # TODO see above
Z += sp.weights[k] * z * z # is 1-D
self.C = (1-c1a-cmu) * self.C + c1 * self.pc * self.pc + cmu * Z
# TODO: self.C *= exp(cmuneg * (N - dot(sp.neg.weights, **2)
self.dC = self.C
self.D = sqrt(self.C) # C is a 1-D array
self.itereigenupdated = self.countiter
# idx = self.mirror_idx_cov() # take half of mirrored vectors for negative update
# qqqqqqqqqqq
if 1 < 3 and np.isfinite(sp.dampsvec):
if self.countiter == 1:
print("WARNING: CMA_dampsvec option is experimental")
sp.dampsvec *= np.exp(sp.dampsvec_fading/self.N)
# TODO: rank-lambda update: *= (1 + sum(z[z>1]**2-1) * exp(sum(z[z<1]**2-1))
self.sigma_vec *= np.exp((sp.cs/sp.dampsvec/2) * (self.ps**2 - 1))
# self.sigma_vec *= np.exp((sp.cs/sp.dampsvec) * (abs(self.ps) - (2/np.pi)**0.5))
self.more_to_write += [exp(np.mean((self.ps**2 - 1)**2))]
# TODO: rank-mu update
# step-size adaptation, adapt sigma
if 1 < 3: #
self.sigma *= sigma_fac * \
np.exp((min((1, (sp.cs/sp.damps) *
(sqrt(sum(self.ps**2))/self.const.chiN - 1)))))
else:
self.sigma *= sigma_fac * \
np.exp((min((1000, (sp.cs/sp.damps/2) *
(sum(self.ps**2)/N - 1)))))
if 11 < 3:
# derandomized MSR = natural gradient descent using mean(z**2) instead of mu*mean(z)**2
lengths = array([sum(z**2)**0.5 for z in self.arz[fit.idx[:self.sp.mu]]])
# print lengths[0::int(self.sp.mu/5)]
self.sigma *= np.exp(self.sp.mueff**0.5 * dot(self.sp.weights, lengths / self.const.chiN - 1))**(2/(N+1))
if 11 < 3 and self.opts['vv']:
if self.countiter < 2:
print('constant sigma applied')
print(self.opts['vv']) # N=10,lam=10: 0.8 is optimal
self.sigma = self.opts['vv'] * self.sp.mueff * sum(self.mean**2)**0.5 / N
if self.sigma * min(self.dC)**0.5 < self.opts['minstd']:
self.sigma = self.opts['minstd'] / min(self.dC)**0.5
# g = self.countiter
# N = self.N
mindx = eval(self.opts['mindx']) if type(self.opts['mindx']) == type('') else self.opts['mindx']
if self.sigma * min(self.D) < mindx: # TODO: sigma_vec is missing here
self.sigma = mindx / min(self.D)
if self.sigma > 1e9 * self.sigma0:
alpha = self.sigma / max(self.D)
self.multiplyC(alpha)
self.sigma /= alpha**0.5
self.opts['tolupsigma'] /= alpha**0.5 # to be compared with sigma
# TODO increase sigma in case of a plateau?
# Uncertainty noise measurement is done on an upper level
# output, has moved up, e.g. as part of fmin, TODO to be removed
if 11 < 3 and self.opts['verb_log'] > 0 and (self.countiter < 4 or
self.countiter % self.opts['verb_log'] == 0):
# this assumes that two logger with the same name access the same data!
CMADataLogger(self.opts['verb_filenameprefix']).register(self, append=True).add()
# self.writeOutput(solutions[fit.idx[0]])
self.flgtelldone = True
# end tell()
def result(self):
"""return ``(xbest, f(xbest), evaluations_xbest, evaluations, iterations, pheno(xmean), effective_stds)``"""
# TODO: how about xcurrent?
return self.best.get() + (
self.countevals, self.countiter, self.gp.pheno(self.mean), self.gp.scales * self.sigma * self.sigma_vec * self.dC**0.5)
def clip_or_fit_solutions(self, pop, idx):
"""make sure that solutions fit to sample distribution, this interface will probably change.
In particular the frequency of long vectors appearing in pop[idx] - self.mean is limited.
"""
for k in idx:
self.repair_genotype(pop[k])
def repair_genotype(self, x):
"""make sure that solutions fit to sample distribution, this interface will probably change.
In particular the frequency of x - self.mean being long is limited.
"""
mold = self.mean
if 1 < 3: # hard clip at upper_length
upper_length = self.N**0.5 + 2 * self.N / (self.N+2) # should become an Option, but how? e.g. [0, 2, 2]
fac = self.mahalanobisNorm(x - mold) / upper_length
if fac > 1:
x = (x - mold) / fac + mold
# print self.countiter, k, fac, self.mahalanobisNorm(pop[k] - mold)
# adapt also sigma: which are the trust-worthy/injected solutions?
elif 11 < 3:
return exp(np.tanh(((upper_length*fac)**2/self.N-1)/2) / 2)
else:
if 'checktail' not in self.__dict__: # hasattr(self, 'checktail')
raise NotImplementedError
# from check_tail_smooth import CheckTail # for the time being
# self.checktail = CheckTail()
# print('untested feature checktail is on')
fac = self.checktail.addchin(self.mahalanobisNorm(x - mold))
if fac < 1:
x = fac * (x - mold) + mold
return 1.0 # sigma_fac, not in use
#____________________________________________________________
#____________________________________________________________
#
def updateBD(self):
"""update internal variables for sampling the distribution with the
current covariance matrix C. This method is O(N^3), if C is not diagonal.
"""
# itereigenupdated is always up-to-date in the diagonal case
# just double check here
if self.itereigenupdated == self.countiter:
return
if self.sp.neg.cmuexp: # cave:
self.update_exponential(self.Zneg, -self.sp.neg.cmuexp)
# self.C += self.Zpos # pos update after Zneg would be the correct update, overall:
# self.C = self.Zpos + Cs * Mh.expms(-self.sp.neg.cmuexp*Csi*self.Zneg*Csi) * Cs
self.Zneg = np.zeros((self.N, self.N))
if self.sigma_vec is not 1 and not np.all(self.sigma_vec == 1):
self.C = dot(dot(np.diag(self.sigma_vec), self.C), np.diag(self.sigma_vec))
self.sigma_vec[:] = 1
if self.opts['CMA_const_trace'] in (True, 1, 2): # normalize trace of C
if self.opts['CMA_const_trace'] == 2:
s = np.exp(np.mean(np.log(self.dC)))
else:
s = np.mean(self.dC)
self.C /= s
self.dC /= s
self.C = (self.C + self.C.T) / 2
# self.C = np.triu(self.C) + np.triu(self.C,1).T # should work as well
# self.D, self.B = eigh(self.C) # hermitian, ie symmetric C is assumed
if type(self.opts['CMA_eigenmethod']) == type(1):
print('WARNING: option CMA_eigenmethod should be a function, not an integer')
if self.opts['CMA_eigenmethod'] == -1:
# pygsl
# easy to install (well, in Windows install gsl binaries first,
# set system path to respective libgsl-0.dll (or cp the dll to
# python\DLLS ?), in unzipped pygsl edit
# gsl_dist/gsl_site_example.py into gsl_dist/gsl_site.py
# and run "python setup.py build" and "python setup.py install"
# in MINGW32)
if 1 < 3: # import pygsl on the fly
try:
import pygsl.eigen.eigenvectors # TODO efficient enough?
except ImportError:
print('WARNING: could not find pygsl.eigen module, either install pygsl \n' +
' or set option CMA_eigenmethod=1 (is much slower), option set to 1')
self.opts['CMA_eigenmethod'] = 0 # use 0 if 1 is too slow
self.D, self.B = pygsl.eigen.eigenvectors(self.C)
elif self.opts['CMA_eigenmethod'] == 0:
# TODO: thoroughly test np.linalg.eigh
# numpy.linalg.eig crashes in 200-D
# and EVecs with same EVals are not orthogonal
self.D, self.B = np.linalg.eigh(self.C) # self.B[i] is a row and not an eigenvector
else: # is overall two;ten times slower in 10;20-D
self.D, self.B = Misc.eig(self.C) # def eig, see below
else:
self.D, self.B = self.opts['CMA_eigenmethod'](self.C)
# assert(sum(self.D-DD) < 1e-6)
# assert(sum(sum(np.dot(BB, BB.T)-np.eye(self.N))) < 1e-6)
# assert(sum(sum(np.dot(BB * DD, BB.T) - self.C)) < 1e-6)
idx = np.argsort(self.D)
self.D = self.D[idx]
self.B = self.B[:,idx] # self.B[i] is a row, columns self.B[:,i] are eigenvectors
# assert(all(self.B[self.countiter % self.N] == self.B[self.countiter % self.N,:]))
# qqqqqqqqqq
if 11 < 3: # limit condition number to 1e13
climit = 1e13 # cave: conditioncov termination is 1e14
if self.D[-1] / self.D[0] > climit:
self.D += self.D[-1] / climit
for i in range(self.N):
self.C[i][i] += self.D[-1] / climit
if 11 < 3 and any(abs(sum(self.B[:,0:self.N-1] * self.B[:,1:], 0)) > 1e-6):
print('B is not orthogonal')
print(self.D)
print(sum(self.B[:,0:self.N-1] * self.B[:,1:], 0))
else:
# is O(N^3)
# assert(sum(abs(self.C - np.dot(self.D * self.B, self.B.T))) < N**2*1e-11)
pass
self.D **= 0.5
self.itereigenupdated = self.countiter
def multiplyC(self, alpha):
"""multiply C with a scalar and update all related internal variables (dC, D,...)"""
self.C *= alpha
if self.dC is not self.C:
self.dC *= alpha
self.D *= alpha**0.5
def update_exponential(self, Z, eta, BDpair=None):
"""exponential update of C that guarantees positive definiteness, that is,
instead of the assignment ``C = C + eta * Z``,
C gets C**.5 * exp(eta * C**-.5 * Z * C**-.5) * C**.5.
Parameter Z should have expectation zero, e.g. sum(w[i] * z[i] * z[i].T) - C
if E z z.T = C.
This function conducts two eigendecompositions, assuming that
B and D are not up to date, unless `BDpair` is given. Given BDpair,
B is the eigensystem and D is the vector of sqrt(eigenvalues), one
eigendecomposition is omitted.
Reference: Glasmachers et al 2010, Exponential Natural Evolution Strategies
"""
if eta == 0:
return
if BDpair:
B, D = BDpair
else:
D, B = self.opts['CMA_eigenmethod'](self.C)
D **= 0.5
Csi = dot(B, (B / D).T)
Cs = dot(B, (B * D).T)
self.C = dot(Cs, dot(Mh.expms(eta * dot(Csi, dot(Z, Csi)), self.opts['CMA_eigenmethod']), Cs))
#____________________________________________________________
#____________________________________________________________
#
def _updateCholesky(self, A, Ainv, p, alpha, beta):
"""not yet implemented"""
# BD is A, p is A*Normal(0,I) distributed
# input is assumed to be numpy arrays
# Ainv is needed to compute the evolution path
# this is a stump and is not tested
raise _Error("not yet implemented")
# prepare
alpha = float(alpha)
beta = float(beta)
y = np.dot(Ainv, p)
y_sum = sum(y**2)
# compute scalars
tmp = sqrt(1 + beta * y_sum / alpha)
fac = (sqrt(alpha) / sum(y**2)) * (tmp - 1)
facinv = (1. / (sqrt(alpha) * sum(y**2))) * (1 - 1. / tmp)
# update matrices
A *= sqrt(alpha)
A += np.outer(fac * p, y)
Ainv /= sqrt(alpha)
Ainv -= np.outer(facinv * y, np.dot(y.T, Ainv))
#____________________________________________________________
#____________________________________________________________
def feedForResume(self, X, function_values):
"""Given all "previous" candidate solutions and their respective
function values, the state of a `CMAEvolutionStrategy` object
can be reconstructed from this history. This is the purpose of
function `feedForResume`.
Arguments
---------
`X`
(all) solution points in chronological order, phenotypic
representation. The number of points must be a multiple
of popsize.
`function_values`
respective objective function values
Details
-------
`feedForResume` can be called repeatedly with only parts of
the history. The part must have the length of a multiple
of the population size.
`feedForResume` feeds the history in popsize-chunks into `tell`.
The state of the random number generator might not be
reconstructed, but this would be only relevant for the future.
Example
-------
::
import cma
# prepare
(x0, sigma0) = ... # initial values from previous trial
X = ... # list of generated solutions from a previous trial
f = ... # respective list of f-values
# resume
es = cma.CMAEvolutionStrategy(x0, sigma0)
es.feedForResume(X, f)
# continue with func as objective function
while not es.stop():
X = es.ask()
es.tell(X, [func(x) for x in X])
Credits to Dirk Bueche and Fabrice Marchal for the feeding idea.
:See: class `CMAEvolutionStrategy` for a simple dump/load to resume
"""
if self.countiter > 0:
print('WARNING: feed should generally be used with a new object instance')
if len(X) != len(function_values):
raise _Error('number of solutions ' + str(len(X)) +
' and number function values ' +
str(len(function_values))+' must not differ')
popsize = self.sp.popsize
if (len(X) % popsize) != 0:
raise _Error('number of solutions ' + str(len(X)) +
' must be a multiple of popsize (lambda) ' +
str(popsize))
for i in range(len(X) / popsize):
# feed in chunks of size popsize
self.ask() # a fake ask, mainly for a conditioned calling of updateBD
# and secondary to get possibly the same random state
self.tell(X[i*popsize:(i+1)*popsize], function_values[i*popsize:(i+1)*popsize])
#____________________________________________________________
#____________________________________________________________
def readProperties(self):
"""reads dynamic parameters from property file (not implemented)
"""
print('not yet implemented')
#____________________________________________________________
#____________________________________________________________
def mahalanobisNorm(self, dx):
"""
compute the Mahalanobis norm that is induced by the adapted covariance
matrix C times sigma**2.
Argument
--------
A *genotype* difference `dx`.
Example
-------
>>> import cma, numpy
>>> es = cma.CMAEvolutionStrategy(numpy.ones(10), 1)
>>> xx = numpy.random.randn(2, 10)
>>> d = es.mahalanobisNorm(es.gp.geno(xx[0]-xx[1]))
`d` is the distance "in" the true sample distribution,
sampled points have a typical distance of ``sqrt(2*es.N)``,
where `N` is the dimension. In the example, `d` is the
Euclidean distance, because C = I and sigma = 1.
"""
return sqrt(sum((self.D**-1 * np.dot(self.B.T, dx))**2)) / self.sigma
#____________________________________________________________
#____________________________________________________________
#
def timesCroot(self, mat):
"""return C**0.5 times mat, where mat can be a vector or matrix.
Not functional, because _Croot=C**0.5 is never computed (should be in updateBD)
"""
print("WARNING: timesCroot is not yet tested")
if self.opts['CMA_diagonal'] is True \
or self.countiter <= self.opts['CMA_diagonal']:
res = (self._Croot * mat.T).T
else:
res = np.dot(self._Croot, mat)
return res
def divCroot(self, mat):
"""return C**-1/2 times mat, where mat can be a vector or matrix"""
print("WARNING: divCroot is not yet tested")
if self.opts['CMA_diagonal'] is True \
or self.countiter <= self.opts['CMA_diagonal']:
res = (self._Crootinv * mat.T).T
else:
res = np.dot(self._Crootinv, mat)
return res
#____________________________________________________________
#____________________________________________________________
def disp_annotation(self):
"""print annotation for `disp()`"""
print('Iterat #Fevals function value axis ratio sigma minstd maxstd min:sec')
sys.stdout.flush()
#____________________________________________________________
#____________________________________________________________
def disp(self, modulo=None): # TODO: rather assign opt['verb_disp'] as default?
"""prints some infos according to `disp_annotation()`, if
``iteration_counter % modulo == 0``
"""
if modulo is None:
modulo = self.opts['verb_disp']
# console display
if modulo:
if (self.countiter-1) % (10 * modulo) < 1:
self.disp_annotation()
if self.countiter > 0 and (self.stop() or self.countiter < 4
or self.countiter % modulo < 1):
if self.opts['verb_time']:
toc = self.elapsed_time()
stime = str(int(toc//60))+':'+str(round(toc%60,1))
else:
stime = ''
print(' '.join((repr(self.countiter).rjust(5),
repr(self.countevals).rjust(7),
'%.15e' % (min(self.fit.fit)),
'%4.1e' % (self.D.max()/self.D.min()),
'%6.2e' % self.sigma,
'%6.0e' % (self.sigma * sqrt(min(self.dC))),
'%6.0e' % (self.sigma * sqrt(max(self.dC))),
stime)))
# if self.countiter < 4:
sys.stdout.flush()
class Options(dict):
"""``Options()`` returns a dictionary with the available options and their
default values for function fmin and for class CMAEvolutionStrategy.
``Options(opts)`` returns the subset of recognized options in dict(opts).
``Options('pop')`` returns a subset of recognized options that contain
'pop' in there keyword name, value or description.
Option values can be "written" in a string and, when passed to fmin
or CMAEvolutionStrategy, are evaluated using "N" and "popsize" as
known values for dimension and population size (sample size, number
of new solutions per iteration). All default option values are such
a string.
Details
-------
All Options are originally defined via the input arguments of
`fmin()`.
Options starting with ``tol`` are termination "tolerances".
For `tolstagnation`, the median over the first and the second half
of at least `tolstagnation` iterations are compared for both, the
per-iteration best and per-iteration median function value.
Some options are, as mentioned (`restarts`,...), only used with `fmin`.
Example
-------
::
import cma
cma.Options('tol')
is a shortcut for cma.Options().match('tol') that returns all options
that contain 'tol' in their name or description.
:See: `fmin`(), `CMAEvolutionStrategy`, `CMAParameters`
"""
# @classmethod # self is the class, not the instance
# @property
# def default(self):
# """returns all options with defaults"""
# return fmin([],[])
@staticmethod
def defaults():
"""return a dictionary with default option values and description,
calls `fmin([], [])`"""
return fmin([], [])
@staticmethod
def versatileOptions():
"""return list of options that can be changed at any time (not only be
initialized), however the list might not be entirely up to date. The
string ' #v ' in the default value indicates a 'versatile' option
that can be changed any time.
"""
return tuple(sorted(i[0] for i in list(Options.defaults().items()) if i[1].find(' #v ') > 0))
def __init__(self, s=None, unchecked=False):
"""return an `Options` instance, either with the default options,
if ``s is None``, or with all options whose name or description
contains `s`, if `s` is a string (case is disregarded),
or with entries from dictionary `s` as options, not complemented
with default options or settings
Returns: see above.
"""
# if not Options.defaults: # this is different from self.defaults!!!
# Options.defaults = fmin([],[])
if s is None:
super(Options, self).__init__(Options.defaults())
# self = Options.defaults()
elif type(s) is str:
super(Options, self).__init__(Options().match(s))
# we could return here
else:
super(Options, self).__init__(s)
if not unchecked:
for key in list(self.keys()):
if key not in Options.defaults():
print('Warning in cma.Options.__init__(): invalid key ``' + str(key) + '`` popped')
self.pop(key)
# self.evaluated = False # would become an option entry
def init(self, dict_or_str, val=None, warn=True):
"""initialize one or several options.
Arguments
---------
`dict_or_str`
a dictionary if ``val is None``, otherwise a key.
If `val` is provided `dict_or_str` must be a valid key.
`val`
value for key
Details
-------
Only known keys are accepted. Known keys are in `Options.defaults()`
"""
#dic = dict_or_key if val is None else {dict_or_key:val}
dic = dict_or_str
if val is not None:
dic = {dict_or_str:val}
for key, val in list(dic.items()):
if key not in Options.defaults():
# TODO: find a better solution?
if warn:
print('Warning in cma.Options.init(): key ' +
str(key) + ' ignored')
else:
self[key] = val
return self
def set(self, dic, val=None, warn=True):
"""set can assign versatile options from `Options.versatileOptions()`
with a new value, use `init()` for the others.
Arguments
---------
`dic`
either a dictionary or a key. In the latter
case, val must be provided
`val`
value for key
`warn`
bool, print a warning if the option cannot be changed
and is therefore omitted
This method will be most probably used with the ``opts`` attribute of
a `CMAEvolutionStrategy` instance.
"""
if val is not None: # dic is a key in this case
dic = {dic:val} # compose a dictionary
for key, val in list(dic.items()):
if key in Options.versatileOptions():
self[key] = val
elif warn:
print('Warning in cma.Options.set(): key ' + str(key) + ' ignored')
return self # to allow o = Options(o).set(new)
def complement(self):
"""add all missing options with their default values"""
for key in Options.defaults():
if key not in self:
self[key] = Options.defaults()[key]
return self
def settable(self):
"""return the subset of those options that are settable at any
time.
Settable options are in `versatileOptions()`, but the
list might be incomlete.
"""
return Options([i for i in list(self.items())
if i[0] in Options.versatileOptions()])
def __call__(self, key, default=None, loc=None):
"""evaluate and return the value of option `key` on the fly, or
returns those options whose name or description contains `key`,
case disregarded.
Details
-------
Keys that contain `filename` are not evaluated.
For ``loc==None``, `self` is used as environment
but this does not define `N`.
:See: `eval()`, `evalall()`
"""
try:
val = self[key]
except:
return self.match(key)
if loc is None:
loc = self # TODO: this hack is not so useful: popsize could be there, but N is missing
try:
if type(val) is str:
val = val.split('#')[0].strip() # remove comments
if type(val) == type('') and key.find('filename') < 0 and key.find('mindx') < 0:
val = eval(val, globals(), loc)
# invoke default
# TODO: val in ... fails with array type, because it is applied element wise!
# elif val in (None,(),[],{}) and default is not None:
elif val is None and default is not None:
val = eval(str(default), globals(), loc)
except:
pass # slighly optimistic: the previous is bug-free
return val
def eval(self, key, default=None, loc=None):
"""Evaluates and sets the specified option value in
environment `loc`. Many options need `N` to be defined in
`loc`, some need `popsize`.
Details
-------
Keys that contain 'filename' are not evaluated.
For `loc` is None, the self-dict is used as environment
:See: `evalall()`, `__call__`
"""
self[key] = self(key, default, loc)
return self[key]
def evalall(self, loc=None):
"""Evaluates all option values in environment `loc`.
:See: `eval()`
"""
# TODO: this needs rather the parameter N instead of loc
if 'N' in list(loc.keys()): # TODO: __init__ of CMA can be simplified
popsize = self('popsize', Options.defaults()['popsize'], loc)
for k in list(self.keys()):
self.eval(k, Options.defaults()[k],
{'N':loc['N'], 'popsize':popsize})
return self
def match(self, s=''):
"""return all options that match, in the name or the description,
with string `s`, case is disregarded.
Example: ``cma.Options().match('verb')`` returns the verbosity options.
"""
match = s.lower()
res = {}
for k in sorted(self):
s = str(k) + '=\'' + str(self[k]) + '\''
if match in s.lower():
res[k] = self[k]
return Options(res)
def pp(self):
pprint(self)
def printme(self, linebreak=80):
for i in sorted(Options.defaults().items()):
s = str(i[0]) + "='" + str(i[1]) + "'"
a = s.split(' ')
# print s in chunks
l = '' # start entire to the left
while a:
while a and len(l) + len(a[0]) < linebreak:
l += ' ' + a.pop(0)
print(l)
l = ' ' # tab for subsequent lines
#____________________________________________________________
#____________________________________________________________
class CMAParameters(object):
"""strategy parameters like population size and learning rates.
Note:
contrary to `Options`, `CMAParameters` is not (yet) part of the
"user-interface" and subject to future changes (it might become
a `collections.namedtuple`)
Example
-------
>>> import cma
>>> es = cma.CMAEvolutionStrategy(20 * [0.1], 1)
(6_w,12)-CMA-ES (mu_w=3.7,w_1=40%) in dimension 20 (seed=504519190) # the seed is "random" by default
>>>
>>> type(es.sp) # sp contains the strategy parameters
>>>
>>> es.sp.disp()
{'CMA_on': True,
'N': 20,
'c1': 0.004181139918745593,
'c1_sep': 0.034327992810300939,
'cc': 0.17176721127681213,
'cc_sep': 0.25259494835857677,
'cmean': 1.0,
'cmu': 0.0085149624979034746,
'cmu_sep': 0.057796356229390715,
'cs': 0.21434997799189287,
'damps': 1.2143499779918929,
'mu': 6,
'mu_f': 6.0,
'mueff': 3.7294589343030671,
'popsize': 12,
'rankmualpha': 0.3,
'weights': array([ 0.40240294, 0.25338908, 0.16622156, 0.10437523, 0.05640348,
0.01720771])}
>>>
>> es.sp == cma.CMAParameters(20, 12, cma.Options().evalall({'N': 20}))
True
:See: `Options`, `CMAEvolutionStrategy`
"""
def __init__(self, N, opts, ccovfac=1, verbose=True):
"""Compute strategy parameters, mainly depending on
dimension and population size, by calling `set`
"""
self.N = N
if ccovfac == 1:
ccovfac = opts['CMA_on'] # that's a hack
self.set(opts, ccovfac=ccovfac, verbose=verbose)
def set(self, opts, popsize=None, ccovfac=1, verbose=True):
"""Compute strategy parameters as a function
of dimension and population size """
alpha_cc = 1.0 # cc-correction for mueff, was zero before
def cone(df, mu, N, alphacov=2.0):
"""rank one update learning rate, ``df`` is disregarded and obsolete, reduce alphacov on noisy problems, say to 0.5"""
return alphacov / ((N + 1.3)**2 + mu)
def cmu(df, mu, alphamu=0.0, alphacov=2.0):
"""rank mu learning rate, disregarding the constrant cmu <= 1 - cone"""
c = alphacov * (alphamu + mu - 2 + 1/mu) / ((N + 2)**2 + alphacov * mu / 2)
# c = alphacov * (alphamu + mu - 2 + 1/mu) / (2 * (N + 2)**1.5 + alphacov * mu / 2)
# print 'cmu =', c
return c
def conedf(df, mu, N):
"""used for computing separable learning rate"""
return 1. / (df + 2.*sqrt(df) + float(mu)/N)
def cmudf(df, mu, alphamu):
"""used for computing separable learning rate"""
return (alphamu + mu - 2. + 1./mu) / (df + 4.*sqrt(df) + mu/2.)
sp = self
N = sp.N
if popsize:
opts.evalall({'N':N, 'popsize':popsize})
else:
popsize = opts.evalall({'N':N})['popsize'] # the default popsize is computed in Options()
sp.popsize = popsize
if opts['CMA_mirrors'] < 0.5:
sp.lam_mirr = int(0.5 + opts['CMA_mirrors'] * popsize)
elif opts['CMA_mirrors'] > 1:
sp.lam_mirr = int(0.5 + opts['CMA_mirrors'])
else:
sp.lam_mirr = int(0.5 + 0.16 * min((popsize, 2 * N + 2)) + 0.29) # 0.158650... * popsize is optimal
# lam = arange(2,22)
# mirr = 0.16 + 0.29/lam
# print(lam); print([int(0.5 + l) for l in mirr*lam])
# [ 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21]
# [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4]
sp.mu_f = sp.popsize / 2.0 # float value of mu
if opts['CMA_mu'] is not None:
sp.mu_f = opts['CMA_mu']
sp.mu = int(sp.mu_f + 0.499999) # round down for x.5
# in principle we have mu_opt = popsize/2 + lam_mirr/2,
# which means in particular weights should only be negative for q > 0.5+mirr_frac/2
if sp.mu > sp.popsize - 2 * sp.lam_mirr + 1:
print("WARNING: pairwise selection is not implemented, therefore " +
" mu = %d > %d = %d - 2*%d + 1 = popsize - 2*mirr + 1 can produce a bias" % (
sp.mu, sp.popsize - 2 * sp.lam_mirr + 1, sp.popsize, sp.lam_mirr))
if sp.lam_mirr > sp.popsize // 2:
raise _Error("fraction of mirrors in the population as read from option CMA_mirrors cannot be larger 0.5, " +
"theoretically optimal is 0.159")
sp.weights = log(max([sp.mu, sp.popsize / 2.0]) + 0.5) - log(1 + np.arange(sp.mu))
if 11 < 3: # equal recombination weights
sp.mu = sp.popsize // 4
sp.weights = np.ones(sp.mu)
print(sp.weights[:10])
sp.weights /= sum(sp.weights)
sp.mueff = 1 / sum(sp.weights**2)
sp.cs = (sp.mueff + 2) / (N + sp.mueff + 3)
# TODO: clean up (here the cumulation constant is shorter if sigma_vec is used)
sp.dampsvec = opts['CMA_dampsvec_fac'] * (N + 2) if opts['CMA_dampsvec_fac'] else np.Inf
sp.dampsvec_fading = opts['CMA_dampsvec_fade']
if np.isfinite(sp.dampsvec):
sp.cs = ((sp.mueff + 2) / (N + sp.mueff + 3))**0.5
# sp.cs = (sp.mueff + 2) / (N + 1.5*sp.mueff + 1)
sp.cc = (4 + alpha_cc * sp.mueff / N) / (N + 4 + alpha_cc * 2 * sp.mueff / N)
sp.cc_sep = (1 + 1/N + alpha_cc * sp.mueff / N) / (N**0.5 + 1/N + alpha_cc * 2 * sp.mueff / N) # \not\gg\cc
sp.rankmualpha = opts['CMA_rankmualpha']
# sp.rankmualpha = _evalOption(opts['CMA_rankmualpha'], 0.3)
sp.c1 = ccovfac * min(1, sp.popsize/6) * cone((N**2 + N) / 2, sp.mueff, N) # 2. / ((N+1.3)**2 + sp.mucov)
sp.c1_sep = ccovfac * conedf(N, sp.mueff, N)
if 11 < 3:
sp.c1 = 0.
print('c1 is zero')
if opts['CMA_rankmu'] != 0: # also empty
sp.cmu = min(1 - sp.c1, ccovfac * cmu((N**2+N)/2, sp.mueff, sp.rankmualpha))
sp.cmu_sep = min(1 - sp.c1_sep, ccovfac * cmudf(N, sp.mueff, sp.rankmualpha))
else:
sp.cmu = sp.cmu_sep = 0
sp.neg = BlancClass()
if opts['CMA_active']:
# in principle we have mu_opt = popsize/2 + lam_mirr/2,
# which means in particular weights should only be negative for q > 0.5+mirr_frac/2
sp.neg.mu_f = popsize - (popsize + sp.lam_mirr) / 2 if popsize > 2 else 1
sp.neg.weights = log(sp.mu_f + 0.5) - log(1 + np.arange(sp.popsize - int(sp.neg.mu_f), sp.popsize))
sp.neg.mu = len(sp.neg.weights) # maybe never useful?
sp.neg.weights /= sum(sp.neg.weights)
sp.neg.mueff = 1 / sum(sp.neg.weights**2)
sp.neg.cmuexp = opts['CMA_activefac'] * 0.25 * sp.neg.mueff / ((N+2)**1.5 + 2 * sp.neg.mueff)
assert sp.neg.mu >= sp.lam_mirr # not really necessary
# sp.neg.minresidualvariance = 0.66 # not it use, keep at least 0.66 in all directions, small popsize is most critical
else:
sp.neg.cmuexp = 0
sp.CMA_on = sp.c1 + sp.cmu > 0
# print(sp.c1_sep / sp.cc_sep)
if not opts['CMA_on'] and opts['CMA_on'] not in (None,[],(),''):
sp.CMA_on = False
# sp.c1 = sp.cmu = sp.c1_sep = sp.cmu_sep = 0
sp.damps = opts['CMA_dampfac'] * (0.5 +
0.5 * min([1, (sp.lam_mirr/(0.159*sp.popsize) - 1)**2])**1 +
2 * max([0, ((sp.mueff-1) / (N+1))**0.5 - 1]) + sp.cs
)
if 11 < 3:
# this is worse than damps = 1 + sp.cs for the (1,10000)-ES on 40D parabolic ridge
sp.damps = 0.3 + 2 * max([sp.mueff/sp.popsize, ((sp.mueff-1)/(N+1))**0.5 - 1]) + sp.cs
if 11 < 3:
# this does not work for lambda = 4*N^2 on the parabolic ridge
sp.damps = opts['CMA_dampfac'] * (2 - 0*sp.lam_mirr/sp.popsize) * sp.mueff/sp.popsize + 0.3 + sp.cs # nicer future setting
print('damps =', sp.damps)
if 11 < 3:
sp.damps = 10 * sp.damps # 1e99 # (1 + 2*max(0,sqrt((sp.mueff-1)/(N+1))-1)) + sp.cs;
# sp.damps = 20 # 1. + 20 * sp.cs**-1 # 1e99 # (1 + 2*max(0,sqrt((sp.mueff-1)/(N+1))-1)) + sp.cs;
print('damps is %f' % (sp.damps))
sp.cmean = float(opts['CMA_cmean'])
# sp.kappa = 1 # 4-D, lam=16, rank1, kappa < 4 does not influence convergence rate
# in larger dim it does, 15-D with defaults, kappa=8 factor 2
if sp.cmean != 1:
print(' cmean = %f' % (sp.cmean))
if verbose:
if not sp.CMA_on:
print('covariance matrix adaptation turned off')
if opts['CMA_mu'] != None:
print('mu = %f' % (sp.mu_f))
# return self # the constructor returns itself
def disp(self):
pprint(self.__dict__)
#____________________________________________________________
#____________________________________________________________
class CMAStopDict(dict):
"""keep and update a termination condition dictionary, which is
"usually" empty and returned by `CMAEvolutionStrategy.stop()`.
Details
-------
This could be a nested class, but nested classes cannot be serialized.
:See: `stop()`
"""
def __init__(self, d={}):
update = (type(d) == CMAEvolutionStrategy)
inherit = (type(d) == CMAStopDict)
super(CMAStopDict, self).__init__({} if update else d)
self._stoplist = d._stoplist if inherit else [] # multiple entries
self.lastiter = d.lastiter if inherit else 0 # probably not necessary
if update:
self._update(d)
def __call__(self, es):
"""update the dictionary"""
return self._update(es)
def _addstop(self, key, cond, val=None):
if cond:
self.stoplist.append(key) # can have the same key twice
if key in list(self.opts.keys()):
val = self.opts[key]
self[key] = val
def _update(self, es):
"""Test termination criteria and update dictionary.
"""
if es.countiter == self.lastiter:
if es.countiter == 0:
self.__init__()
return self
try:
if es == self.es:
return self
except: # self.es not yet assigned
pass
self.lastiter = es.countiter
self.es = es
self.stoplist = []
N = es.N
opts = es.opts
self.opts = opts # a hack to get _addstop going
# fitness: generic criterion, user defined w/o default
self._addstop('ftarget',
es.best.f < opts['ftarget'])
# maxiter, maxfevals: generic criteria
self._addstop('maxfevals',
es.countevals - 1 >= opts['maxfevals'])
self._addstop('maxiter',
es.countiter >= opts['maxiter'])
# tolx, tolfacupx: generic criteria
# tolfun, tolfunhist (CEC:tolfun includes hist)
self._addstop('tolx',
all([es.sigma*xi < opts['tolx'] for xi in es.pc]) and \
all([es.sigma*xi < opts['tolx'] for xi in sqrt(es.dC)]))
self._addstop('tolfacupx',
any([es.sigma * sig > es.sigma0 * opts['tolfacupx']
for sig in sqrt(es.dC)]))
self._addstop('tolfun',
es.fit.fit[-1] - es.fit.fit[0] < opts['tolfun'] and \
max(es.fit.hist) - min(es.fit.hist) < opts['tolfun'])
self._addstop('tolfunhist',
len(es.fit.hist) > 9 and \
max(es.fit.hist) - min(es.fit.hist) < opts['tolfunhist'])
# worst seen false positive: table N=80,lam=80, getting worse for fevals=35e3 \approx 50 * N**1.5
# but the median is not so much getting worse
# / 5 reflects the sparsity of histbest/median
# / 2 reflects the left and right part to be compared
l = int(max(opts['tolstagnation'] / 5. / 2, len(es.fit.histbest) / 10));
# TODO: why max(..., len(histbest)/10) ???
# TODO: the problem in the beginning is only with best ==> ???
if 11 < 3: #
print(es.countiter, (opts['tolstagnation'], es.countiter > N * (5 + 100 / es.popsize),
len(es.fit.histbest) > 100,
np.median(es.fit.histmedian[:l]) >= np.median(es.fit.histmedian[l:2*l]),
np.median(es.fit.histbest[:l]) >= np.median(es.fit.histbest[l:2*l])))
# equality should handle flat fitness
self._addstop('tolstagnation', # leads sometimes early stop on ftablet, fcigtab, N>=50?
1 < 3 and opts['tolstagnation'] and es.countiter > N * (5 + 100 / es.popsize) and
len(es.fit.histbest) > 100 and 2*l < len(es.fit.histbest) and
np.median(es.fit.histmedian[:l]) >= np.median(es.fit.histmedian[l:2*l]) and
np.median(es.fit.histbest[:l]) >= np.median(es.fit.histbest[l:2*l]))
# iiinteger: stagnation termination can prevent to find the optimum
self._addstop('tolupsigma', opts['tolupsigma'] and
es.sigma / es.sigma0 / np.max(es.D) > opts['tolupsigma'])
if 11 < 3 and 2*l < len(es.fit.histbest): # TODO: this might go wrong, because the nb of written columns changes
tmp = np.array((-np.median(es.fit.histmedian[:l]) + np.median(es.fit.histmedian[l:2*l]),
-np.median(es.fit.histbest[:l]) + np.median(es.fit.histbest[l:2*l])))
es.more_to_write += [(10**t if t < 0 else t + 1) for t in tmp] # the latter to get monotonicy
if 1 < 3:
# non-user defined, method specific
# noeffectaxis (CEC: 0.1sigma), noeffectcoord (CEC:0.2sigma), conditioncov
self._addstop('noeffectcoord',
any([es.mean[i] == es.mean[i] + 0.2*es.sigma*sqrt(es.dC[i])
for i in range(N)]))
if opts['CMA_diagonal'] is not True and es.countiter > opts['CMA_diagonal']:
i = es.countiter % N
self._addstop('noeffectaxis',
sum(es.mean == es.mean + 0.1 * es.sigma * es.D[i] * es.B[:, i]) == N)
self._addstop('conditioncov',
es.D[-1] > 1e7 * es.D[0], 1e14) # TODO
self._addstop('callback', es.callbackstop) # termination_callback
if len(self):
self._addstop('flat fitness: please (re)consider how to compute the fitness more elaborate',
len(es.fit.hist) > 9 and \
max(es.fit.hist) == min(es.fit.hist))
if 11 < 3 and opts['vv'] == 321:
self._addstop('||xmean||^2 1:
f.write(' '.join(map(str, es.gp.scales)))
else:
f.write(str(es.gp.scales))
f.write(', typical_x: ')
if np.size(es.gp.typical_x) > 1:
f.write(' '.join(map(str, es.gp.typical_x)))
else:
f.write(str(es.gp.typical_x))
f.write('\n')
f.close()
except (IOError, OSError):
print('could not open/write file ' + fn)
fn = self.name_prefix + 'xrecentbest.dat'
try:
with open(fn, 'w') as f:
f.write('% # iter+eval+sigma+0+fitness+xbest, ' +
strseedtime +
'\n')
except (IOError, OSError):
print('could not open/write file ' + fn)
return self
# end def __init__
def load(self, filenameprefix=None):
"""loads data from files written and return a data dictionary, *not*
a prerequisite for using `plot()` or `disp()`.
Argument `filenameprefix` is the filename prefix of data to be loaded (five files),
by default ``'outcmaes'``.
Return data dictionary with keys `xrecent`, `xmean`, `f`, `D`, `std`
"""
if not filenameprefix:
filenameprefix = self.name_prefix
for i in range(len(self.file_names)):
fn = filenameprefix + self.file_names[i] + '.dat'
try:
self.__dict__[self.key_names[i]] = _fileToMatrix(fn)
except:
print('WARNING: reading from file "' + fn + '" failed')
if self.key_names[i] in self.key_names_with_annotation:
self.__dict__[self.key_names[i]].append(self.__dict__[self.key_names[i]][-1]) # copy last row to later fill in annotation position for display
self.__dict__[self.key_names[i]] = array(self.__dict__[self.key_names[i]], copy=False)
return self
def add(self, es=None, more_data=[], modulo=None): # TODO: find a different way to communicate current x and f
"""append some logging data from `CMAEvolutionStrategy` class instance `es`,
if ``number_of_times_called % modulo`` equals to zero, never if ``modulo==0``.
The sequence ``more_data`` must always have the same length.
When used for a different optimizer class, this function can be
(easily?) adapted by changing the assignments under INTERFACE
in the implemention.
"""
self.counter += 1
mod = modulo if modulo is not None else self.modulo
if mod == 0 or (self.counter > 3 and self.counter % mod):
return
if es is None:
try:
es = self.es # must have been registered
except AttributeError :
raise _Error('call `add` with argument `es` or ``register(es)`` before ``add()``')
elif not self.registered:
self.register(es) # calls initialize
# --- INTERFACE, can be changed if necessary ---
if type(es) is not CMAEvolutionStrategy: # not necessary
print('WARNING: expected, found '
+ str(type(es)) + ' in method CMADataLogger.add')
evals = es.countevals
iteration = es.countiter
sigma = es.sigma
axratio = es.D.max()/es.D.min()
xmean = es.mean # TODO: should be optionally phenotype?
fmean_noise_free = es.fmean_noise_free
fmean = es.fmean
try:
besteverf = es.best.f
bestf = es.fit.fit[0]
medianf = es.fit.fit[es.sp.popsize//2]
worstf = es.fit.fit[-1]
except:
if self.counter > 1: # first call without f-values is OK
raise
try:
xrecent = es.best.last.x
except:
xrecent = None
maxD = es.D.max()
minD = es.D.min()
diagD = es.D
diagC = es.sigma*es.sigma_vec*sqrt(es.dC)
more_to_write = es.more_to_write
es.more_to_write = []
# --- end interface ---
try:
# fit
if self.counter > 1:
fn = self.name_prefix + 'fit.dat'
with open(fn, 'a') as f:
f.write(str(iteration) + ' '
+ str(evals) + ' '
+ str(sigma) + ' '
+ str(axratio) + ' '
+ str(besteverf) + ' '
+ '%.16e' % bestf + ' '
+ str(medianf) + ' '
+ str(worstf) + ' '
# + str(es.sp.popsize) + ' '
# + str(10**es.noiseS) + ' '
# + str(es.sp.cmean) + ' '
+ ' '.join(str(i) for i in more_to_write)
+ ' '.join(str(i) for i in more_data)
+ '\n')
# axlen
fn = self.name_prefix + 'axlen.dat'
with open(fn, 'a') as f: # does not rely on reference counting
f.write(str(iteration) + ' '
+ str(evals) + ' '
+ str(sigma) + ' '
+ str(maxD) + ' '
+ str(minD) + ' '
+ ' '.join(map(str, diagD))
+ '\n')
# stddev
fn = self.name_prefix + 'stddev.dat'
with open(fn, 'a') as f:
f.write(str(iteration) + ' '
+ str(evals) + ' '
+ str(sigma) + ' '
+ '0 0 '
+ ' '.join(map(str, diagC))
+ '\n')
# xmean
fn = self.name_prefix + 'xmean.dat'
with open(fn, 'a') as f:
if iteration < 1: # before first iteration
f.write('0 0 0 0 0 '
+ ' '.join(map(str, xmean))
+ '\n')
else:
f.write(str(iteration) + ' '
+ str(evals) + ' '
# + str(sigma) + ' '
+ '0 '
+ str(fmean_noise_free) + ' '
+ str(fmean) + ' ' # TODO: this does not make sense
# TODO should be optional the phenotyp?
+ ' '.join(map(str, xmean))
+ '\n')
# xrecent
fn = self.name_prefix + 'xrecentbest.dat'
if iteration > 0 and xrecent is not None:
with open(fn, 'a') as f:
f.write(str(iteration) + ' '
+ str(evals) + ' '
+ str(sigma) + ' '
+ '0 '
+ str(bestf) + ' '
+ ' '.join(map(str, xrecent))
+ '\n')
except (IOError, OSError):
if iteration <= 1:
print('could not open/write file')
def closefig(self):
pylab.close(self.fighandle)
def save(self, nameprefix, switch=False):
"""saves logger data to a different set of files, for
``switch=True`` also the loggers name prefix is switched to
the new value
"""
if not nameprefix or type(nameprefix) is not str:
_Error('filename prefix must be a nonempty string')
if nameprefix == self.default_prefix:
_Error('cannot save to default name "' + nameprefix + '...", chose another name')
if nameprefix == self.name_prefix:
return
for name in CMADataLogger.names:
open(nameprefix+name+'.dat', 'w').write(open(self.name_prefix+name+'.dat').read())
if switch:
self.name_prefix = nameprefix
def plot(self, fig=None, iabscissa=1, iteridx=None, plot_mean=True, # TODO: plot_mean default should be False
foffset=1e-19, x_opt = None, fontsize=10):
"""
plot data from a `CMADataLogger` (using the files written by the logger).
Arguments
---------
`fig`
figure number, by default 325
`iabscissa`
``0==plot`` versus iteration count,
``1==plot`` versus function evaluation number
`iteridx`
iteration indices to plot
Return `CMADataLogger` itself.
Examples
--------
::
import cma
logger = cma.CMADataLogger() # with default name
# try to plot the "default logging" data (e.g. from previous fmin calls)
logger.plot() # to continue you might need to close the pop-up window
# once and call plot() again.
# This behavior seems to disappear in subsequent
# calls of plot(). Also using ipython with -pylab
# option might help.
cma.savefig('fig325.png') # save current figure
logger.closefig()
Dependencies: matlabplotlib/pylab.
"""
dat = self.load(self.name_prefix)
try:
# pylab: prodedural interface for matplotlib
from matplotlib.pylab import figure, ioff, ion, subplot, semilogy, hold, plot, grid, \
axis, title, text, xlabel, isinteractive, draw, gcf
except ImportError:
ImportError('could not find matplotlib.pylab module, function plot() is not available')
return
if fontsize and pylab.rcParams['font.size'] != fontsize:
print('global variable pylab.rcParams[\'font.size\'] set (from ' +
str(pylab.rcParams['font.size']) + ') to ' + str(fontsize))
pylab.rcParams['font.size'] = fontsize # subtracted in the end, but return can happen inbetween
if fig:
figure(fig)
else:
figure(325)
# show() # should not be necessary
self.fighandle = gcf() # fighandle.number
if iabscissa not in (0,1):
iabscissa = 1
interactive_status = isinteractive()
ioff() # prevents immediate drawing
dat.x = dat.xmean # this is the genotyp
if not plot_mean:
try:
dat.x = dat.xrecent
except:
pass
if len(dat.x) < 2:
print('not enough data to plot')
return {}
if iteridx is not None:
dat.f = dat.f[np.where([x in iteridx for x in dat.f[:,0]])[0],:]
dat.D = dat.D[np.where([x in iteridx for x in dat.D[:,0]])[0],:]
iteridx.append(dat.x[-1,1]) # last entry is artificial
dat.x = dat.x[np.where([x in iteridx for x in dat.x[:,0]])[0],:]
dat.std = dat.std[np.where([x in iteridx for x in dat.std[:,0]])[0],:]
if iabscissa == 0:
xlab = 'iterations'
elif iabscissa == 1:
xlab = 'function evaluations'
# use fake last entry in x and std for line extension-annotation
if dat.x.shape[1] < 100:
minxend = int(1.06*dat.x[-2, iabscissa])
# write y-values for individual annotation into dat.x
dat.x[-1, iabscissa] = minxend # TODO: should be ax[1]
idx = np.argsort(dat.x[-2,5:])
idx2 = np.argsort(idx)
if x_opt is None:
dat.x[-1,5+idx] = np.linspace(np.min(dat.x[:,5:]),
np.max(dat.x[:,5:]), dat.x.shape[1]-5)
else:
dat.x[-1,5+idx] = np.logspace(np.log10(np.min(abs(dat.x[:,5:]))),
np.log10(np.max(abs(dat.x[:,5:]))), dat.x.shape[1]-5)
else:
minxend = 0
if len(dat.f) == 0:
print('nothing to plot')
return
# not in use anymore, see formatter above
# xticklocs = np.arange(5) * np.round(minxend/4., -int(np.log10(minxend/4.)))
# dfit(dfit<1e-98) = NaN;
ioff() # turns update off
# TODO: if abscissa==0 plot in chunks, ie loop over subsets where dat.f[:,0]==countiter is monotonous
subplot(2,2,1)
self.plotdivers(dat, iabscissa, foffset)
# TODO: modularize also the remaining subplots
subplot(2,2,2)
hold(False)
if x_opt is not None: # TODO: differentate neg and pos?
semilogy(dat.x[:, iabscissa], abs(dat.x[:,5:]) - x_opt, '-')
else:
plot(dat.x[:, iabscissa], dat.x[:,5:],'-')
hold(True)
grid(True)
ax = array(axis())
# ax[1] = max(minxend, ax[1])
axis(ax)
ax[1] -= 1e-6
if dat.x.shape[1] < 100:
yy = np.linspace(ax[2]+1e-6, ax[3]-1e-6, dat.x.shape[1]-5)
#yyl = np.sort(dat.x[-1,5:])
idx = np.argsort(dat.x[-1,5:])
idx2 = np.argsort(idx)
if x_opt is not None:
semilogy([dat.x[-1, iabscissa], ax[1]], [abs(dat.x[-1,5:]), yy[idx2]], 'k-') # line from last data point
semilogy(np.dot(dat.x[-2, iabscissa],[1,1]), array([ax[2]+1e-6, ax[3]-1e-6]), 'k-')
else:
# plot([dat.x[-1, iabscissa], ax[1]], [dat.x[-1,5:], yy[idx2]], 'k-') # line from last data point
plot(np.dot(dat.x[-2, iabscissa],[1,1]), array([ax[2]+1e-6, ax[3]-1e-6]), 'k-')
# plot(array([dat.x[-1, iabscissa], ax[1]]),
# reshape(array([dat.x[-1,5:], yy[idx2]]).flatten(), (2,4)), '-k')
for i in range(len(idx)):
# TODOqqq: annotate phenotypic value!?
# text(ax[1], yy[i], 'x(' + str(idx[i]) + ')=' + str(dat.x[-2,5+idx[i]]))
text(dat.x[-1,iabscissa], dat.x[-1,5+i], 'x(' + str(i) + ')=' + str(dat.x[-2,5+i]))
i = 2 # find smallest i where iteration count differs (in case the same row appears twice)
while i < len(dat.f) and dat.f[-i][0] == dat.f[-1][0]:
i += 1
title('Object Variables (' + ('mean' if plot_mean else 'curr best') +
', ' + str(dat.x.shape[1]-5) + '-D, popsize~' +
(str(int((dat.f[-1][1] - dat.f[-i][1]) / (dat.f[-1][0] - dat.f[-i][0])))
if len(dat.f.T[0]) > 1 and dat.f[-1][0] > dat.f[-i][0] else 'NA')
+ ')')
# pylab.xticks(xticklocs)
# Scaling
subplot(2,2,3)
hold(False)
semilogy(dat.D[:, iabscissa], dat.D[:,5:], '-b')
hold(True)
grid(True)
ax = array(axis())
# ax[1] = max(minxend, ax[1])
axis(ax)
title('Scaling (All Main Axes)')
# pylab.xticks(xticklocs)
xlabel(xlab)
# standard deviations
subplot(2,2,4)
hold(False)
# remove sigma from stds (graphs become much better readible)
dat.std[:,5:] = np.transpose(dat.std[:,5:].T / dat.std[:,2].T)
# ax = array(axis())
# ax[1] = max(minxend, ax[1])
# axis(ax)
if 1 < 2 and dat.std.shape[1] < 100:
# use fake last entry in x and std for line extension-annotation
minxend = int(1.06*dat.x[-2, iabscissa])
dat.std[-1, iabscissa] = minxend # TODO: should be ax[1]
idx = np.argsort(dat.std[-2,5:])
idx2 = np.argsort(idx)
dat.std[-1,5+idx] = np.logspace(np.log10(np.min(dat.std[:,5:])),
np.log10(np.max(dat.std[:,5:])), dat.std.shape[1]-5)
dat.std[-1, iabscissa] = minxend # TODO: should be ax[1]
yy = np.logspace(np.log10(ax[2]), np.log10(ax[3]), dat.std.shape[1]-5)
#yyl = np.sort(dat.std[-1,5:])
idx = np.argsort(dat.std[-1,5:])
idx2 = np.argsort(idx)
# plot(np.dot(dat.std[-2, iabscissa],[1,1]), array([ax[2]+1e-6, ax[3]-1e-6]), 'k-') # vertical separator
# vertical separator
plot(np.dot(dat.std[-2, iabscissa],[1,1]), array([np.min(dat.std[-2,5:]), np.max(dat.std[-2,5:])]), 'k-')
hold(True)
# plot([dat.std[-1, iabscissa], ax[1]], [dat.std[-1,5:], yy[idx2]], 'k-') # line from last data point
for i in range(len(idx)):
# text(ax[1], yy[i], ' '+str(idx[i]))
text(dat.std[-1, iabscissa], dat.std[-1, 5+i], ' '+str(i))
semilogy(dat.std[:, iabscissa], dat.std[:,5:], '-')
grid(True)
title('Standard Deviations in All Coordinates')
# pylab.xticks(xticklocs)
xlabel(xlab)
draw() # does not suffice
if interactive_status:
ion() # turns interactive mode on (again)
draw()
show()
return self
#____________________________________________________________
#____________________________________________________________
#
@staticmethod
def plotdivers(dat, iabscissa, foffset):
"""helper function for `plot()` that plots all what is
in the upper left subplot like fitness, sigma, etc.
Arguments
---------
`iabscissa` in ``(0,1)``
0==versus fevals, 1==versus iteration
`foffset`
offset to fitness for log-plot
:See: `plot()`
"""
from matplotlib.pylab import semilogy, hold, grid, \
axis, title, text
fontsize = pylab.rcParams['font.size']
hold(False)
dfit = dat.f[:,5]-min(dat.f[:,5])
dfit[dfit<1e-98] = np.NaN
if dat.f.shape[1] > 7:
# semilogy(dat.f[:, iabscissa], abs(dat.f[:,[6, 7, 10, 12]])+foffset,'-k')
semilogy(dat.f[:, iabscissa], abs(dat.f[:,[6, 7]])+foffset,'-k')
hold(True)
# (larger indices): additional fitness data, for example constraints values
if dat.f.shape[1] > 8:
# dd = abs(dat.f[:,7:]) + 10*foffset
# dd = np.where(dat.f[:,7:]==0, np.NaN, dd) # cannot be
semilogy(dat.f[:, iabscissa], np.abs(dat.f[:,8:]) + 10*foffset, 'm')
hold(True)
idx = np.where(dat.f[:,5]>1e-98)[0] # positive values
semilogy(dat.f[idx, iabscissa], dat.f[idx,5]+foffset, '.b')
hold(True)
grid(True)
idx = np.where(dat.f[:,5] < -1e-98) # negative values
semilogy(dat.f[idx, iabscissa], abs(dat.f[idx,5])+foffset,'.r')
semilogy(dat.f[:, iabscissa],abs(dat.f[:,5])+foffset,'-b')
semilogy(dat.f[:, iabscissa], dfit, '-c')
if 11 < 3: # delta-fitness as points
dfit = dat.f[1:, 5] - dat.f[:-1,5] # should be negative usually
semilogy(dat.f[1:,iabscissa], # abs(fit(g) - fit(g-1))
np.abs(dfit)+foffset, '.c')
i = dfit > 0
# print(np.sum(i) / float(len(dat.f[1:,iabscissa])))
semilogy(dat.f[1:,iabscissa][i], # abs(fit(g) - fit(g-1))
np.abs(dfit[i])+foffset, '.r')
# overall minimum
i = np.argmin(dat.f[:,5])
semilogy(dat.f[i, iabscissa]*np.ones(2), dat.f[i,5]*np.ones(2), 'rd')
# semilogy(dat.f[-1, iabscissa]*np.ones(2), dat.f[-1,4]*np.ones(2), 'rd')
# AR and sigma
semilogy(dat.f[:, iabscissa], dat.f[:,3], '-r') # AR
semilogy(dat.f[:, iabscissa], dat.f[:,2],'-g') # sigma
semilogy(dat.std[:-1, iabscissa], np.vstack([list(map(max, dat.std[:-1,5:])), list(map(min, dat.std[:-1,5:]))]).T,
'-m', linewidth=2)
text(dat.std[-2, iabscissa], max(dat.std[-2, 5:]), 'max std', fontsize=fontsize)
text(dat.std[-2, iabscissa], min(dat.std[-2, 5:]), 'min std', fontsize=fontsize)
ax = array(axis())
# ax[1] = max(minxend, ax[1])
axis(ax)
text(ax[0]+0.01, ax[2], # 10**(log10(ax[2])+0.05*(log10(ax[3])-log10(ax[2]))),
'.f_recent=' + repr(dat.f[-1,5]) )
# title('abs(f) (blue), f-min(f) (cyan), Sigma (green), Axis Ratio (red)')
title('blue:abs(f), cyan:f-min(f), green:sigma, red:axis ratio', fontsize=fontsize-1)
# pylab.xticks(xticklocs)
def downsampling(self, factor=10, first=3, switch=True):
"""
rude downsampling of a `CMADataLogger` data file by `factor`, keeping
also the first `first` entries. This function is a stump and subject
to future changes.
Arguments
---------
- `factor` -- downsampling factor
- `first` -- keep first `first` entries
- `switch` -- switch the new logger name to oldname+'down'
Details
-------
``self.name_prefix+'down'`` files are written
Example
-------
::
import cma
cma.downsampling() # takes outcmaes* files
cma.plot('outcmaesdown')
"""
newprefix = self.name_prefix + 'down'
for name in CMADataLogger.names:
f = open(newprefix+name+'.dat','w')
iline = 0
cwritten = 0
for line in open(self.name_prefix+name+'.dat'):
if iline < first or iline % factor == 0:
f.write(line)
cwritten += 1
iline += 1
f.close()
print('%d' % (cwritten) + ' lines written in ' + newprefix+name+'.dat')
if switch:
self.name_prefix += 'down'
return self
#____________________________________________________________
#____________________________________________________________
#
def disp(self, idx=100): # r_[0:5,1e2:1e9:1e2,-10:0]):
"""displays selected data from (files written by) the class `CMADataLogger`.
Arguments
---------
`idx`
indices corresponding to rows in the data file;
if idx is a scalar (int), the first two, then every idx-th,
and the last three rows are displayed. Too large index values are removed.
Example
-------
>>> import cma, numpy as np
>>> res = cma.fmin(cma.fcts.elli, 7 * [0.1], 1, verb_disp=1e9) # generate data
>>> assert res[1] < 1e-9
>>> assert res[2] < 4400
>>> l = cma.CMADataLogger() # == res[-1], logger with default name, "points to" above data
>>> l.disp([0,-1]) # first and last
>>> l.disp(20) # some first/last and every 20-th line
>>> l.disp(np.r_[0:999999:100, -1]) # every 100-th and last
>>> l.disp(np.r_[0, -10:0]) # first and ten last
>>> cma.disp(l.name_prefix, np.r_[0::100, -10:]) # the same as l.disp(...)
Details
-------
The data line with the best f-value is displayed as last line.
:See: `disp()`
"""
filenameprefix=self.name_prefix
def printdatarow(dat, iteration):
"""print data of iteration i"""
i = np.where(dat.f[:, 0] == iteration)[0][0]
j = np.where(dat.std[:, 0] == iteration)[0][0]
print('%5d' % (int(dat.f[i,0])) + ' %6d' % (int(dat.f[i,1])) + ' %.14e' % (dat.f[i,5]) +
' %5.1e' % (dat.f[i,3]) +
' %6.2e' % (max(dat.std[j,5:])) + ' %6.2e' % min(dat.std[j,5:]))
dat = CMADataLogger(filenameprefix).load()
ndata = dat.f.shape[0]
# map index to iteration number, is difficult if not all iteration numbers exist
# idx = idx[np.where(map(lambda x: x in dat.f[:,0], idx))[0]] # TODO: takes pretty long
# otherwise:
if idx is None:
idx = 100
if np.isscalar(idx):
# idx = np.arange(0, ndata, idx)
if idx:
idx = np.r_[0, 1, idx:ndata-3:idx, -3:0]
else:
idx = np.r_[0, 1, -3:0]
idx = array(idx)
idx = idx[idx 1:
f.write(' '.join(map(str, es.gp.scales)))
else:
f.write(str(es.gp.scales))
f.write(', typical_x: ')
if np.size(es.gp.typical_x) > 1:
f.write(' '.join(map(str, es.gp.typical_x)))
else:
f.write(str(es.gp.typical_x))
f.write('\n')
f.close()
except (IOError, OSError):
print('could not open/write file ' + fn)
if 11 < 3:
fn = self.name_prefix + 'xrecentbest.dat'
try:
with open(fn, 'w') as f:
f.write('% # iter+eval+sigma+0+fitness+xbest, ' +
strseedtime +
'\n')
except (IOError, OSError):
print('could not open/write file ' + fn)
return self
# end def __init__
def load(self, filenameprefix=None):
"""loads data from files written and return a data dictionary, *not*
a prerequisite for using `plot()` or `disp()`.
Argument `filenameprefix` is the filename prefix of data to be loaded (five files),
by default ``'outcmaes'``.
Return data dictionary with keys `xrecent`, `xmean`, `f`, `D`, `std`
"""
if not filenameprefix:
filenameprefix = self.name_prefix
dat = self # historical
# dat.xrecent = _fileToMatrix(filenameprefix + 'xrecentbest.dat')
dat.xmean = _fileToMatrix(filenameprefix + 'xmean.dat')
dat.std = _fileToMatrix(filenameprefix + 'stddev' + '.dat')
# a hack to later write something into the last entry
for key in ['xmean', 'std']: # 'xrecent',
dat.__dict__[key].append(dat.__dict__[key][-1]) # copy last row to later fill in annotation position for display
dat.__dict__[key] = array(dat.__dict__[key], copy=False)
dat.f = array(_fileToMatrix(filenameprefix + 'fit.dat'))
dat.D = array(_fileToMatrix(filenameprefix + 'axlen' + '.dat'))
return dat
def add(self, fitness_values, es=None, more_data=[], modulo=None): # TODO: find a different way to communicate current x and f
"""append some logging data from `CMAEvolutionStrategy` class instance `es`,
if ``number_of_times_called % modulo`` equals to zero, never if ``modulo==0``.
The sequence ``more_data`` must always have the same length.
"""
self.counter += 1
fitness_values = np.sort(fitness_values)
if fitness_values[0] < self.best_fitness:
self.best_fitness = fitness_values[0]
mod = modulo if modulo is not None else self.modulo
if mod == 0 or (self.counter > 3 and self.counter % mod):
return
if es is None:
try:
es = self.es # must have been registered
except AttributeError :
raise _Error('call register() before add() or add(es)')
elif not self.registered:
self.register(es)
if 11 < 3:
try: # TODO: find a more decent interface to store and pass recent_x
xrecent = es.best.last.x
except:
if self.counter == 2: # by now a recent_x should be available
print('WARNING: es.out[\'recent_x\'] not found in CMADataLogger.add, count='
+ str(self.counter))
try:
# fit
if es.update_count > 0:
# fit = es.fit.fit[0] # TODO: where do we get the fitness from?
fn = self.name_prefix + 'fit.dat'
with open(fn, 'a') as f:
f.write(str(es.update_count) + ' '
+ str(es.update_count * es.lambda_) + ' '
+ str(es.sigma) + ' '
+ str(es.diagD[-1]/es.diagD[0]) + ' '
+ str(self.best_fitness) + ' '
+ '%.16e' % fitness_values[0] + ' '
+ str(fitness_values[es.lambda_//2]) + ' '
+ str(fitness_values[-1]) + ' '
# + str(es.sp.popsize) + ' '
# + str(10**es.noiseS) + ' '
# + str(es.sp.cmean) + ' '
# + ' '.join(str(i) for i in es.more_to_write)
+ ' '.join(str(i) for i in more_data)
+ '\n')
# es.more_to_write = []
# axlen
fn = self.name_prefix + 'axlen.dat'
with open(fn, 'a') as f: # does not rely on reference counting
f.write(str(es.update_count) + ' '
+ str(es.update_count * es.lambda_) + ' '
+ str(es.sigma) + ' '
+ str(es.diagD[-1]) + ' '
+ str(es.diagD[0]) + ' '
+ ' '.join(map(str, es.diagD))
+ '\n')
# stddev
fn = self.name_prefix + 'stddev.dat'
with open(fn, 'a') as f:
f.write(str(es.update_count) + ' '
+ str(es.update_count * es.lambda_) + ' '
+ str(es.sigma) + ' '
+ '0 0 '
+ ' '.join(map(str, es.sigma*np.sqrt([es.C[i][i] for i in range(es.dim)])))
+ '\n')
# xmean
fn = self.name_prefix + 'xmean.dat'
with open(fn, 'a') as f:
if es.update_count < 1:
f.write('0 0 0 0 0 '
+ ' '.join(map(str,
# TODO should be optional the phenotyp?
# es.gp.geno(es.x0)
es.mean))
+ '\n')
else:
f.write(str(es.update_count) + ' '
+ str(es.update_count * es.lambda_) + ' '
# + str(es.sigma) + ' '
+ '0 0 0 '
# + str(es.fmean_noise_free) + ' '
# + str(es.fmean) + ' ' # TODO: this does not make sense
# TODO should be optional the phenotyp?
+ ' '.join(map(str, es.centroid))
+ '\n')
# xrecent
if 11 < 3:
fn = self.name_prefix + 'xrecentbest.dat'
if es.countiter > 0 and xrecent is not None:
with open(fn, 'a') as f:
f.write(str(es.countiter) + ' '
+ str(es.countevals) + ' '
+ str(es.sigma) + ' '
+ '0 '
+ str(es.fit.fit[0]) + ' '
+ ' '.join(map(str, xrecent))
+ '\n')
except (IOError, OSError):
if es.countiter == 1:
print('could not open/write file')
def closefig(self):
pylab.close(self.fighandle)
def save(self, nameprefix, switch=False):
"""saves logger data to a different set of files, for
``switch=True`` also the loggers name prefix is switched to
the new value
"""
if not nameprefix or type(nameprefix) is not str:
_Error('filename prefix must be a nonempty string')
if nameprefix == self.default_prefix:
_Error('cannot save to default name "' + nameprefix + '...", chose another name')
if nameprefix == self.name_prefix:
return
for name in CMADataLogger.names:
open(nameprefix+name+'.dat', 'w').write(open(self.name_prefix+name+'.dat').read())
if switch:
self.name_prefix = nameprefix
def plot(self, fig=None, iabscissa=1, iteridx=None, plot_mean=True, # TODO: plot_mean default should be False
foffset=1e-19, x_opt = None, fontsize=10):
"""
plot data from a `CMADataLogger` (using the files written by the logger).
Arguments
---------
`fig`
figure number, by default 325
`iabscissa`
``0==plot`` versus iteration count,
``1==plot`` versus function evaluation number
`iteridx`
iteration indices to plot
Return `CMADataLogger` itself.
Examples
--------
::
import cma
logger = cma.CMADataLogger() # with default name
# try to plot the "default logging" data (e.g. from previous fmin calls)
logger.plot() # to continue you might need to close the pop-up window
# once and call plot() again.
# This behavior seems to disappear in subsequent
# calls of plot(). Also using ipython with -pylab
# option might help.
cma.savefig('fig325.png') # save current figure
logger.closefig()
Dependencies: matlabplotlib/pylab.
"""
dat = self.load(self.name_prefix)
try:
# pylab: prodedural interface for matplotlib
from matplotlib.pylab import figure, ioff, ion, subplot, semilogy, hold, plot, grid, \
axis, title, text, xlabel, isinteractive, draw, gcf
except ImportError:
ImportError('could not find matplotlib.pylab module, function plot() is not available')
return
if fontsize and pylab.rcParams['font.size'] != fontsize:
print('global variable pylab.rcParams[\'font.size\'] set (from ' +
str(pylab.rcParams['font.size']) + ') to ' + str(fontsize))
pylab.rcParams['font.size'] = fontsize # subtracted in the end, but return can happen inbetween
if fig:
figure(fig)
else:
figure(325)
# show() # should not be necessary
self.fighandle = gcf() # fighandle.number
if iabscissa not in (0,1):
iabscissa = 1
interactive_status = isinteractive()
ioff() # prevents immediate drawing
if 11 < 3:
dat.x = dat.xrecent
if len(dat.x) < 2:
print('not enough data to plot')
return {}
# if plot_mean:
dat.x = dat.xmean # this is the genotyp
if iteridx is not None:
dat.f = dat.f[np.where([x in iteridx for x in dat.f[:,0]])[0],:]
dat.D = dat.D[np.where([x in iteridx for x in dat.D[:,0]])[0],:]
iteridx.append(dat.x[-1,1]) # last entry is artificial
dat.x = dat.x[np.where([x in iteridx for x in dat.x[:,0]])[0],:]
dat.std = dat.std[np.where([x in iteridx for x in dat.std[:,0]])[0],:]
if iabscissa == 0:
xlab = 'iterations'
elif iabscissa == 1:
xlab = 'function evaluations'
# use fake last entry in x and std for line extension-annotation
if dat.x.shape[1] < 100:
minxend = int(1.06*dat.x[-2, iabscissa])
# write y-values for individual annotation into dat.x
dat.x[-1, iabscissa] = minxend # TODO: should be ax[1]
idx = np.argsort(dat.x[-2,5:])
idx2 = np.argsort(idx)
if x_opt is None:
dat.x[-1,5+idx] = np.linspace(np.min(dat.x[:,5:]),
np.max(dat.x[:,5:]), dat.x.shape[1]-5)
else:
dat.x[-1,5+idx] = np.logspace(np.log10(np.min(abs(dat.x[:,5:]))),
np.log10(np.max(abs(dat.x[:,5:]))), dat.x.shape[1]-5)
else:
minxend = 0
if len(dat.f) == 0:
print('nothing to plot')
return
# not in use anymore, see formatter above
# xticklocs = np.arange(5) * np.round(minxend/4., -int(np.log10(minxend/4.)))
# dfit(dfit<1e-98) = NaN;
ioff() # turns update off
# TODO: if abscissa==0 plot in chunks, ie loop over subsets where dat.f[:,0]==countiter is monotonous
subplot(2,2,1)
self.plotdivers(dat, iabscissa, foffset)
# TODO: modularize also the remaining subplots
subplot(2,2,2)
hold(False)
if x_opt is not None: # TODO: differentate neg and pos?
semilogy(dat.x[:, iabscissa], abs(dat.x[:,5:]) - x_opt, '-')
else:
plot(dat.x[:, iabscissa], dat.x[:,5:],'-')
hold(True)
grid(True)
ax = array(axis())
# ax[1] = max(minxend, ax[1])
axis(ax)
ax[1] -= 1e-6
if dat.x.shape[1] < 100:
yy = np.linspace(ax[2]+1e-6, ax[3]-1e-6, dat.x.shape[1]-5)
#yyl = np.sort(dat.x[-1,5:])
idx = np.argsort(dat.x[-1,5:])
idx2 = np.argsort(idx)
if x_opt is not None:
semilogy([dat.x[-1, iabscissa], ax[1]], [abs(dat.x[-1,5:]), yy[idx2]], 'k-') # line from last data point
semilogy(np.dot(dat.x[-2, iabscissa],[1,1]), array([ax[2]+1e-6, ax[3]-1e-6]), 'k-')
else:
# plot([dat.x[-1, iabscissa], ax[1]], [dat.x[-1,5:], yy[idx2]], 'k-') # line from last data point
plot(np.dot(dat.x[-2, iabscissa],[1,1]), array([ax[2]+1e-6, ax[3]-1e-6]), 'k-')
# plot(array([dat.x[-1, iabscissa], ax[1]]),
# reshape(array([dat.x[-1,5:], yy[idx2]]).flatten(), (2,4)), '-k')
for i in range(len(idx)):
# TODOqqq: annotate phenotypic value!?
# text(ax[1], yy[i], 'x(' + str(idx[i]) + ')=' + str(dat.x[-2,5+idx[i]]))
text(dat.x[-1,iabscissa], dat.x[-1,5+i], 'x(' + str(i) + ')=' + str(dat.x[-2,5+i]))
i = 2 # find smallest i where iteration count differs (in case the same row appears twice)
while i < len(dat.f) and dat.f[-i][0] == dat.f[-1][0]:
i += 1
title('Object Variables (' + ('mean' if plot_mean else 'curr best') +
', ' + str(dat.x.shape[1]-5) + '-D, popsize~' +
(str(int((dat.f[-1][1] - dat.f[-i][1]) / (dat.f[-1][0] - dat.f[-i][0])))
if len(dat.f.T[0]) > 1 and dat.f[-1][0] > dat.f[-i][0] else 'NA')
+ ')')
# pylab.xticks(xticklocs)
# Scaling
subplot(2,2,3)
hold(False)
semilogy(dat.D[:, iabscissa], dat.D[:,5:], '-b')
hold(True)
grid(True)
ax = array(axis())
# ax[1] = max(minxend, ax[1])
axis(ax)
title('Scaling (All Main Axes)')
# pylab.xticks(xticklocs)
xlabel(xlab)
# standard deviations
subplot(2,2,4)
hold(False)
# remove sigma from stds (graphs become much better readible)
dat.std[:,5:] = np.transpose(dat.std[:,5:].T / dat.std[:,2].T)
# ax = array(axis())
# ax[1] = max(minxend, ax[1])
# axis(ax)
if 1 < 2 and dat.std.shape[1] < 100:
# use fake last entry in x and std for line extension-annotation
minxend = int(1.06*dat.x[-2, iabscissa])
dat.std[-1, iabscissa] = minxend # TODO: should be ax[1]
idx = np.argsort(dat.std[-2,5:])
idx2 = np.argsort(idx)
dat.std[-1,5+idx] = np.logspace(np.log10(np.min(dat.std[:,5:])),
np.log10(np.max(dat.std[:,5:])), dat.std.shape[1]-5)
dat.std[-1, iabscissa] = minxend # TODO: should be ax[1]
yy = np.logspace(np.log10(ax[2]), np.log10(ax[3]), dat.std.shape[1]-5)
#yyl = np.sort(dat.std[-1,5:])
idx = np.argsort(dat.std[-1,5:])
idx2 = np.argsort(idx)
# plot(np.dot(dat.std[-2, iabscissa],[1,1]), array([ax[2]+1e-6, ax[3]-1e-6]), 'k-') # vertical separator
# vertical separator
plot(np.dot(dat.std[-2, iabscissa],[1,1]), array([np.min(dat.std[-2,5:]), np.max(dat.std[-2,5:])]), 'k-')
hold(True)
# plot([dat.std[-1, iabscissa], ax[1]], [dat.std[-1,5:], yy[idx2]], 'k-') # line from last data point
for i in range(len(idx)):
# text(ax[1], yy[i], ' '+str(idx[i]))
text(dat.std[-1, iabscissa], dat.std[-1, 5+i], ' '+str(i))
semilogy(dat.std[:, iabscissa], dat.std[:,5:], '-')
grid(True)
title('Standard Deviations in All Coordinates')
# pylab.xticks(xticklocs)
xlabel(xlab)
draw() # does not suffice
if interactive_status:
ion() # turns interactive mode on (again)
draw()
show()
return self
#____________________________________________________________
#____________________________________________________________
#
@staticmethod
def plotdivers(dat, iabscissa, foffset):
"""helper function for `plot()` that plots all what is
in the upper left subplot like fitness, sigma, etc.
Arguments
---------
`iabscissa` in ``(0,1)``
0==versus fevals, 1==versus iteration
`foffset`
offset to fitness for log-plot
:See: `plot()`
"""
from matplotlib.pylab import semilogy, hold, grid, \
axis, title, text
fontsize = pylab.rcParams['font.size']
hold(False)
dfit = dat.f[:,5]-min(dat.f[:,5])
dfit[dfit<1e-98] = np.NaN
if dat.f.shape[1] > 7:
# semilogy(dat.f[:, iabscissa], abs(dat.f[:,[6, 7, 10, 12]])+foffset,'-k')
semilogy(dat.f[:, iabscissa], abs(dat.f[:,[6, 7]])+foffset,'-k')
hold(True)
# (larger indices): additional fitness data, for example constraints values
if dat.f.shape[1] > 8:
# dd = abs(dat.f[:,7:]) + 10*foffset
# dd = np.where(dat.f[:,7:]==0, np.NaN, dd) # cannot be
semilogy(dat.f[:, iabscissa], np.abs(dat.f[:,8:]) + 10*foffset, 'm')
hold(True)
idx = np.where(dat.f[:,5]>1e-98)[0] # positive values
semilogy(dat.f[idx, iabscissa], dat.f[idx,5]+foffset, '.b')
hold(True)
grid(True)
idx = np.where(dat.f[:,5] < -1e-98) # negative values
semilogy(dat.f[idx, iabscissa], abs(dat.f[idx,5])+foffset,'.r')
semilogy(dat.f[:, iabscissa],abs(dat.f[:,5])+foffset,'-b')
semilogy(dat.f[:, iabscissa], dfit, '-c')
if 11 < 3: # delta-fitness as points
dfit = dat.f[1:, 5] - dat.f[:-1,5] # should be negative usually
semilogy(dat.f[1:,iabscissa], # abs(fit(g) - fit(g-1))
np.abs(dfit)+foffset, '.c')
i = dfit > 0
# print(np.sum(i) / float(len(dat.f[1:,iabscissa])))
semilogy(dat.f[1:,iabscissa][i], # abs(fit(g) - fit(g-1))
np.abs(dfit[i])+foffset, '.r')
# overall minimum
i = np.argmin(dat.f[:,5])
semilogy(dat.f[i, iabscissa]*np.ones(2), dat.f[i,5]*np.ones(2), 'rd')
# semilogy(dat.f[-1, iabscissa]*np.ones(2), dat.f[-1,4]*np.ones(2), 'rd')
# AR and sigma
semilogy(dat.f[:, iabscissa], dat.f[:,3], '-r') # AR
semilogy(dat.f[:, iabscissa], dat.f[:,2],'-g') # sigma
semilogy(dat.std[:-1, iabscissa], np.vstack([list(map(max, dat.std[:-1,5:])), list(map(min, dat.std[:-1,5:]))]).T,
'-m', linewidth=2)
text(dat.std[-2, iabscissa], max(dat.std[-2, 5:]), 'max std', fontsize=fontsize)
text(dat.std[-2, iabscissa], min(dat.std[-2, 5:]), 'min std', fontsize=fontsize)
ax = array(axis())
# ax[1] = max(minxend, ax[1])
axis(ax)
text(ax[0]+0.01, ax[2], # 10**(log10(ax[2])+0.05*(log10(ax[3])-log10(ax[2]))),
'.f_recent=' + repr(dat.f[-1,5]) )
# title('abs(f) (blue), f-min(f) (cyan), Sigma (green), Axis Ratio (red)')
title('blue:abs(f), cyan:f-min(f), green:sigma, red:axis ratio', fontsize=fontsize-1)
# pylab.xticks(xticklocs)
def downsampling(self, factor=10, first=3, switch=True):
"""
rude downsampling of a `CMADataLogger` data file by `factor`, keeping
also the first `first` entries. This function is a stump and subject
to future changes.
Arguments
---------
- `factor` -- downsampling factor
- `first` -- keep first `first` entries
- `switch` -- switch the new logger name to oldname+'down'
Details
-------
``self.name_prefix+'down'`` files are written
Example
-------
::
import cma
cma.downsampling() # takes outcmaes* files
cma.plot('outcmaesdown')
"""
newprefix = self.name_prefix + 'down'
for name in CMADataLogger.names:
f = open(newprefix+name+'.dat','w')
iline = 0
cwritten = 0
for line in open(self.name_prefix+name+'.dat'):
if iline < first or iline % factor == 0:
f.write(line)
cwritten += 1
iline += 1
f.close()
print('%d' % (cwritten) + ' lines written in ' + newprefix+name+'.dat')
if switch:
self.name_prefix += 'down'
return self
#____________________________________________________________
#____________________________________________________________
#
def disp_header(self):
heading = 'Iterat Nfevals function value axis ratio maxstd minstd'
print(heading)
def disp(self, idx=100): # r_[0:5,1e2:1e9:1e2,-10:0]):
"""displays selected data from (files written by) the class `CMADataLogger`.
Arguments
---------
`idx`
indices corresponding to rows in the data file;
if idx is a scalar (int), the first two, then every idx-th,
and the last three rows are displayed. Too large index values are removed.
If ``len(idx) == 1``, only a single row is displayed, e.g. the last
entry when ``idx == [-1]``.
Example
-------
>>> import cma, numpy as np
>>> res = cma.fmin(cma.fcts.elli, 7 * [0.1], 1, verb_disp=1e9) # generate data
>>> assert res[1] < 1e-9
>>> assert res[2] < 4400
>>> l = cma.CMADataLogger() # == res[-1], logger with default name, "points to" above data
>>> l.disp([0,-1]) # first and last
>>> l.disp(20) # some first/last and every 20-th line
>>> l.disp(np.r_[0:999999:100, -1]) # every 100-th and last
>>> l.disp(np.r_[0, -10:0]) # first and ten last
>>> cma.disp(l.name_prefix, np.r_[0::100, -10:]) # the same as l.disp(...)
Details
-------
The data line with the best f-value is displayed as last line.
:See: `disp()`
"""
filenameprefix=self.name_prefix
def printdatarow(dat, iteration):
"""print data of iteration i"""
i = np.where(dat.f[:, 0] == iteration)[0][0]
j = np.where(dat.std[:, 0] == iteration)[0][0]
print('%5d' % (int(dat.f[i,0])) + ' %6d' % (int(dat.f[i,1])) + ' %.14e' % (dat.f[i,5]) +
' %5.1e' % (dat.f[i,3]) +
' %6.2e' % (max(dat.std[j,5:])) + ' %6.2e' % min(dat.std[j,5:]))
dat = CMADataLogger(filenameprefix).load()
ndata = dat.f.shape[0]
# map index to iteration number, is difficult if not all iteration numbers exist
# idx = idx[np.where(map(lambda x: x in dat.f[:,0], idx))[0]] # TODO: takes pretty long
# otherwise:
if idx is None:
idx = 100
if np.isscalar(idx):
# idx = np.arange(0, ndata, idx)
if idx:
idx = np.r_[0, 1, idx:ndata-3:idx, -3:0]
else:
idx = np.r_[0, 1, -3:0]
idx = array(idx)
idx = idx[idx<=ndata] # TODO: shouldn't this be "<"?
idx = idx[-idx<=ndata]
iters = dat.f[idx, 0]
idxbest = np.argmin(dat.f[:,5])
iterbest = dat.f[idxbest, 0]
if len(iters) == 1:
printdatarow(dat, iters[0])
else:
self.disp_header()
for i in iters:
printdatarow(dat, i)
self.disp_header()
printdatarow(dat, iterbest)
sys.stdout.flush()
def irg(ar):
return range(len(ar))
class AII(object):
"""unstable experimental code, updates ps, sigma, sigmai, pr, r, sigma_r, mean,
all from self.
Depends on that the ordering of solutions has not change upon calling update
should become a OOOptimizer in far future?
"""
# Try: ps**2 - 1 instead of (ps**2)**0.5 / chi1 - 1: compare learning rate etc
# and dito for psr
def __init__(self, x0, sigma0, randn=np.random.randn):
"""TODO: check scaling of r-learing: seems worse than linear: 9e3 25e3 65e3 (10,20,40-D)"""
self.N = len(x0)
N = self.N
# parameters to play with:
# PROBLEM: smaller eta_r even fails on *axparallel* cigar!! Also dampi needs to be smaller then!
self.dampi = 4 * N # two times smaller is
self.eta_r = 0 / N / 3 # c_r learning rate for direction, cigar: 4/N/3 is optimal in 10-D, 10/N/3 still works (15 in 20-D) but not on the axparallel cigar with recombination
self.mu = 1
self.use_abs_sigma = 1 # without it is a problem on 20=D axpar-cigar!!, but why?? Because dampi is just boarderline
self.use_abs_sigma_r = 1 #
self.randn = randn
self.x0 = array(x0, copy=True)
self.sigma0 = sigma0
self.cs = 1 / N**0.5 # evolution path for step-size(s)
self.damps = 1
self.use_sign = 0
self.use_scalar_product = 0 # sometimes makes it somewhat worse on Rosenbrock, don't know why
self.csr = 1 / N**0.5 # cumulation for sigma_r
self.dampsr = (4 * N)**0.5
self.chi1 = (2/np.pi)**0.5
self.chiN = N**0.5*(1-1./(4.*N)+1./(21.*N**2)) # expectation of norm(randn(N,1))
self.initialize()
def initialize(self):
"""alias ``reset``, set all state variables to initial values"""
N = self.N
self.mean = array(self.x0, copy=True)
self.sigma = self.sigma0
self.sigmai = np.ones(N)
self.ps = np.zeros(N) # path for individual and globalstep-size(s)
self.r = np.zeros(N)
self.pr = 0 # cumulation for zr = N(0,1)
self.sigma_r = 0
def ask(self, popsize):
if popsize == 1:
raise NotImplementedError()
self.Z = [self.randn(self.N) for _i in range(popsize)]
self.zr = list(self.randn(popsize))
pop = [self.mean + self.sigma * (self.sigmai * self.Z[k])
+ self.zr[k] * self.sigma_r * self.r
for k in range(popsize)]
if not np.isfinite(pop[0][0]):
raise ValueError()
return pop
def tell(self, X, f):
"""update """
mu = 1 if self.mu else int(len(f) / 4)
idx = np.argsort(f)[:mu]
zr = [self.zr[i] for i in idx]
Z = [self.Z[i] for i in idx]
X = [X[i] for i in idx]
xmean = np.mean(X, axis=0)
self.ps *= 1 - self.cs
self.ps += (self.cs*(2-self.cs))**0.5 * mu**0.5 * np.mean(Z, axis=0)
self.sigma *= np.exp((self.cs/self.damps) * (sum(self.ps**2)**0.5 / self.chiN - 1))
if self.use_abs_sigma:
self.sigmai *= np.exp((1/self.dampi) * (np.abs(self.ps) / self.chi1 - 1))
else:
self.sigmai *= np.exp((1.3/self.dampi/2) * (self.ps**2 - 1))
self.pr *= 1 - self.csr
self.pr += (self.csr*(2-self.csr))**0.5 * mu**0.5 * np.mean(zr)
fac = 1
if self.use_sign:
fac = np.sign(self.pr) # produces readaptations on the cigar
else:
self.pr = max([0, self.pr])
if self.use_scalar_product:
if np.sign(sum(self.r * (xmean - self.mean))) < 0: # and self.pr > 1:
# if np.sign(sum(self.r * self.ps)) < 0:
self.r *= -1
if self.eta_r:
self.r *= (1 - self.eta_r) * self.sigma_r
self.r += fac * self.eta_r * mu**0.5 * (xmean - self.mean)
self.r /= sum(self.r**2)**0.5
if self.use_abs_sigma_r:
self.sigma_r *= np.exp((1/self.dampsr) * ((self.pr**2)**0.5 / self.chi1 - 1))
else:
# this is worse on the cigar, where the direction vector(!) behaves strangely
self.sigma_r *= np.exp((1/self.dampsr) * (self.pr**2 - 1) / 2)
self.sigma_r = max([self.sigma * sum(self.sigmai**2)**0.5 / 3, self.sigma_r])
# self.sigma_r = 0
self.mean = xmean
def fmin(func, x0, sigma0=None, args=()
# the follow string arguments are evaluated, besides the verb_filenameprefix
, CMA_active='False # exponential negative update, conducted after the original update'
, CMA_activefac='1 # learning rate multiplier for active update'
, CMA_cmean='1 # learning rate for the mean value'
, CMA_const_trace='False # normalize trace, value CMA_const_trace=2 normalizes sum log eigenvalues to zero'
, CMA_diagonal='0*100*N/sqrt(popsize) # nb of iterations with diagonal covariance matrix, True for always' # TODO 4/ccov_separable?
, CMA_eigenmethod='np.linalg.eigh # 0=numpy-s eigh, -1=pygsl, otherwise cma.Misc.eig (slower)'
, CMA_elitist='False # elitism likely impairs global search performance'
, CMA_mirrors='popsize < 6 # values <0.5 are interpreted as fraction, values >1 as numbers (rounded), otherwise about 0.16 is used'
, CMA_mu='None # parents selection parameter, default is popsize // 2'
, CMA_on='True # False or 0 for no adaptation of the covariance matrix'
, CMA_rankmu='True # False or 0 for omitting rank-mu update of covariance matrix'
, CMA_rankmualpha='0.3 # factor of rank-mu update if mu=1, subject to removal, default might change to 0.0'
, CMA_dampfac='1 #v positive multiplier for step-size damping, 0.3 is close to optimal on the sphere'
, CMA_dampsvec_fac='np.Inf # tentative and subject to changes, 0.5 would be a "default" damping for sigma vector update'
, CMA_dampsvec_fade='0.1 # tentative fading out parameter for sigma vector update'
, CMA_teststds='None # factors for non-isotropic initial distr. mainly for test purpose, see scaling_...'
, CMA_AII='False # not yet tested'
, bounds='[None, None] # lower (=bounds[0]) and upper domain boundaries, each a scalar or a list/vector'
, eval_parallel='False # when True, func might be called with more than one solution as first argument'
, eval_initial_x='False # '
, fixed_variables='None # dictionary with index-value pairs like {0:1.1, 2:0.1} that are not optimized'
, ftarget='-inf #v target function value, minimization'
, incpopsize='2 # in fmin(): multiplier for increasing popsize before each restart'
, maxfevals='inf #v maximum number of function evaluations'
, maxiter='100 + 50 * (N+3)**2 // popsize**0.5 #v maximum number of iterations'
, mindx='0 #v minimal std in any direction, cave interference with tol*'
, minstd='0 #v minimal std in any coordinate direction, cave interference with tol*'
, noise_handling='False # maximal number of evaluations for noise treatment, only fmin'
, noise_reevals=' 1.5 + popsize/20 # number of solution to be reevaluated for noise measurement, only fmin'
, noise_eps='1e-7 # perturbation factor for noise handling reevaluations, only fmin'
, noise_change_sigma='True # exponent to default sigma increment'
, popsize='4+int(3*log(N)) # population size, AKA lambda, number of new solution per iteration'
, randn='np.random.standard_normal #v randn((lam, N)) must return an np.array of shape (lam, N)'
, restarts='0 # in fmin(): number of restarts'
, restart_from_best='False'
, scaling_of_variables='None # scale for each variable, sigma0 is interpreted w.r.t. this scale, in that effective_sigma0 = sigma0*scaling. Internally the variables are divided by scaling_of_variables and sigma is unchanged, default is ones(N)'
, seed='None # random number seed'
, termination_callback='None #v a function returning True for termination, called after each iteration step and could be abused for side effects'
, tolfacupx='1e3 #v termination when step-size increases by tolfacupx (diverges). That is, the initial step-size was chosen far too small and better solutions were found far away from the initial solution x0'
, tolupsigma='1e20 #v sigma/sigma0 > tolupsigma * max(sqrt(eivenvals(C))) indicates "creeping behavior" with usually minor improvements'
, tolfun='1e-11 #v termination criterion: tolerance in function value, quite useful'
, tolfunhist='1e-12 #v termination criterion: tolerance in function value history'
, tolstagnation='int(100 + 100 * N**1.5 / popsize) #v termination if no improvement over tolstagnation iterations'
, tolx='1e-11 #v termination criterion: tolerance in x-changes'
, transformation='None # [t0, t1] are two mappings, t0 transforms solutions from CMA-representation to f-representation (tf_pheno), t1 is the (optional) back transformation, see class GenoPheno'
, typical_x='None # used with scaling_of_variables'
, updatecovwait='None #v number of iterations without distribution update, name is subject to future changes' # TODO: rename: iterwaitupdatedistribution?
, verb_append='0 # initial evaluation counter, if append, do not overwrite output files'
, verb_disp='100 #v verbosity: display console output every verb_disp iteration'
, verb_filenameprefix='outcmaes # output filenames prefix'
, verb_log='1 #v verbosity: write data to files every verb_log iteration, writing can be time critical on fast to evaluate functions'
, verb_plot='0 #v in fmin(): plot() is called every verb_plot iteration'
, verb_time='True #v output timings on console'
, vv='0 #? versatile variable for hacking purposes, value found in self.opts[\'vv\']'
):
"""functional interface to the stochastic optimizer CMA-ES
for non-convex function minimization.
Calling Sequences
=================
``fmin([],[])``
returns all optional arguments, that is,
all keyword arguments to fmin with their default values
in a dictionary.
``fmin(func, x0, sigma0)``
minimizes `func` starting at `x0` and with standard deviation
`sigma0` (step-size)
``fmin(func, x0, sigma0, ftarget=1e-5)``
minimizes `func` up to target function value 1e-5
``fmin(func, x0, sigma0, args=('f',), **options)``
minimizes `func` called with an additional argument ``'f'``.
`options` is a dictionary with additional keyword arguments, e.g.
delivered by `Options()`.
``fmin(func, x0, sigma0, **{'ftarget':1e-5, 'popsize':40})``
the same as ``fmin(func, x0, sigma0, ftarget=1e-5, popsize=40)``
``fmin(func, esobj, **{'maxfevals': 1e5})``
uses the `CMAEvolutionStrategy` object instance `esobj` to optimize
`func`, similar to `CMAEvolutionStrategy.optimize()`.
Arguments
=========
`func`
function to be minimized. Called as
``func(x,*args)``. `x` is a one-dimensional `numpy.ndarray`. `func`
can return `numpy.NaN`,
which is interpreted as outright rejection of solution `x`
and invokes an immediate resampling and (re-)evaluation
of a new solution not counting as function evaluation.
`x0`
list or `numpy.ndarray`, initial guess of minimum solution
or `cma.CMAEvolutionStrategy` object instance. In this case
`sigma0` can be omitted.
`sigma0`
scalar, initial standard deviation in each coordinate.
`sigma0` should be about 1/4 of the search domain width where the
optimum is to be expected. The variables in `func` should be
scaled such that they presumably have similar sensitivity.
See also option `scaling_of_variables`.
Keyword Arguments
=================
All arguments besides `args` and `verb_filenameprefix` are evaluated
if they are of type `str`, see class `Options` for details. The following
list might not be fully up-to-date, use ``cma.Options()`` or
``cma.fmin([],[])`` to get the actual list.
::
args=() -- additional arguments for func, not in `cma.Options()`
CMA_active='False # exponential negative update, conducted after the original
update'
CMA_activefac='1 # learning rate multiplier for active update'
CMA_cmean='1 # learning rate for the mean value'
CMA_dampfac='1 #v positive multiplier for step-size damping, 0.3 is close to
optimal on the sphere'
CMA_diagonal='0*100*N/sqrt(popsize) # nb of iterations with diagonal
covariance matrix, True for always'
CMA_eigenmethod='np.linalg.eigh # 0=numpy-s eigh, -1=pygsl, alternative: Misc.eig (slower)'
CMA_elitist='False # elitism likely impairs global search performance'
CMA_mirrors='0 # values <0.5 are interpreted as fraction, values >1 as numbers
(rounded), otherwise about 0.16 is used'
CMA_mu='None # parents selection parameter, default is popsize // 2'
CMA_on='True # False or 0 for no adaptation of the covariance matrix'
CMA_rankmu='True # False or 0 for omitting rank-mu update of covariance
matrix'
CMA_rankmualpha='0.3 # factor of rank-mu update if mu=1, subject to removal,
default might change to 0.0'
CMA_teststds='None # factors for non-isotropic initial distr. mainly for test
purpose, see scaling_...'
bounds='[None, None] # lower (=bounds[0]) and upper domain boundaries, each a
scalar or a list/vector'
eval_initial_x='False # '
fixed_variables='None # dictionary with index-value pairs like {0:1.1, 2:0.1}
that are not optimized'
ftarget='-inf #v target function value, minimization'
incpopsize='2 # in fmin(): multiplier for increasing popsize before each
restart'
maxfevals='inf #v maximum number of function evaluations'
maxiter='long(1e3*N**2/sqrt(popsize)) #v maximum number of iterations'
mindx='0 #v minimal std in any direction, cave interference with tol*'
minstd='0 #v minimal std in any coordinate direction, cave interference with
tol*'
noise_eps='1e-7 # perturbation factor for noise handling reevaluations, only
fmin'
noise_handling='False # maximal number of evaluations for noise treatment,
only fmin'
noise_reevals=' 1.5 + popsize/20 # number of solution to be reevaluated for
noise measurement, only fmin'
popsize='4+int(3*log(N)) # population size, AKA lambda, number of new solution
per iteration'
randn='np.random.standard_normal #v randn((lam, N)) must return an np.array of
shape (lam, N)'
restarts='0 # in fmin(): number of restarts'
scaling_of_variables='None # scale for each variable, sigma0 is interpreted
w.r.t. this scale, in that effective_sigma0 = sigma0*scaling.
Internally the variables are divided by scaling_of_variables and sigma
is unchanged, default is ones(N)'
seed='None # random number seed'
termination_callback='None #v in fmin(): a function returning True for
termination, called after each iteration step and could be abused for
side effects'
tolfacupx='1e3 #v termination when step-size increases by tolfacupx
(diverges). That is, the initial step-size was chosen far too small and
better solutions were found far away from the initial solution x0'
tolupsigma='1e20 #v sigma/sigma0 > tolupsigma * max(sqrt(eivenvals(C)))
indicates "creeping behavior" with usually minor improvements'
tolfun='1e-11 #v termination criterion: tolerance in function value, quite
useful'
tolfunhist='1e-12 #v termination criterion: tolerance in function value
history'
tolstagnation='int(100 * N**1.5 / popsize) #v termination if no improvement
over tolstagnation iterations'
tolx='1e-11 #v termination criterion: tolerance in x-changes'
transformation='None # [t0, t1] are two mappings, t0 transforms solutions from
CMA-representation to f-representation, t1 is the back transformation,
see class GenoPheno'
typical_x='None # used with scaling_of_variables'
updatecovwait='None #v number of iterations without distribution update, name
is subject to future changes'
verb_append='0 # initial evaluation counter, if append, do not overwrite
output files'
verb_disp='100 #v verbosity: display console output every verb_disp iteration'
verb_filenameprefix='outcmaes # output filenames prefix'
verb_log='1 #v verbosity: write data to files every verb_log iteration,
writing can be time critical on fast to evaluate functions'
verb_plot='0 #v in fmin(): plot() is called every verb_plot iteration'
verb_time='True #v output timings on console'
vv='0 #? versatile variable for hacking purposes, value found in
self.opts['vv']'
Subsets of options can be displayed, for example like ``cma.Options('tol')``,
see also class `Options`.
Return
======
Similar to `OOOptimizer.optimize()` and/or `CMAEvolutionStrategy.optimize()`, return the
list provided by `CMAEvolutionStrategy.result()` appended with an `OOOptimizer` and an
`BaseDataLogger`::
res = optim.result() + (optim.stop(), optim, logger)
where
- ``res[0]`` (``xopt``) -- best evaluated solution
- ``res[1]`` (``fopt``) -- respective function value
- ``res[2]`` (``evalsopt``) -- respective number of function evaluations
- ``res[3]`` (``evals``) -- number of overall conducted objective function evaluations
- ``res[4]`` (``iterations``) -- number of overall conducted iterations
- ``res[5]`` (``xmean``) -- mean of the final sample distribution
- ``res[6]`` (``stds``) -- effective stds of the final sample distribution
- ``res[-3]`` (``stop``) -- termination condition(s) in a dictionary
- ``res[-2]`` (``cmaes``) -- class `CMAEvolutionStrategy` instance
- ``res[-1]`` (``logger``) -- class `CMADataLogger` instance
Details
=======
This function is an interface to the class `CMAEvolutionStrategy`. The
class can be used when full control over the iteration loop of the
optimizer is desired.
The noise handling follows closely [Hansen et al 2009, A Method for Handling
Uncertainty in Evolutionary Optimization...] in the measurement part, but the
implemented treatment is slightly different: for ``noiseS > 0``, ``evaluations``
(time) and sigma are increased by ``alpha``. For ``noiseS < 0``, ``evaluations``
(time) is decreased by ``alpha**(1/4)``. The option ``noise_handling`` switches
the uncertainty handling on/off, the given value defines the maximal number
of evaluations for a single fitness computation. If ``noise_handling`` is a list,
the smallest element defines the minimal number and if the list has three elements,
the median value is the start value for ``evaluations``. See also class
`NoiseHandler`.
Examples
========
The following example calls `fmin` optimizing the Rosenbrock function
in 10-D with initial solution 0.1 and initial step-size 0.5. The
options are specified for the usage with the `doctest` module.
>>> import cma
>>> # cma.Options() # returns all possible options
>>> options = {'CMA_diagonal':10, 'seed':1234, 'verb_time':0}
>>>
>>> res = cma.fmin(cma.fcts.rosen, [0.1] * 10, 0.5, **options)
(5_w,10)-CMA-ES (mu_w=3.2,w_1=45%) in dimension 10 (seed=1234)
Covariance matrix is diagonal for 10 iterations (1/ccov=29.0)
Iterat #Fevals function value axis ratio sigma minstd maxstd min:sec
1 10 1.264232686260072e+02 1.1e+00 4.40e-01 4e-01 4e-01
2 20 1.023929748193649e+02 1.1e+00 4.00e-01 4e-01 4e-01
3 30 1.214724267489674e+02 1.2e+00 3.70e-01 3e-01 4e-01
100 1000 6.366683525319511e+00 6.2e+00 2.49e-02 9e-03 3e-02
200 2000 3.347312410388666e+00 1.2e+01 4.52e-02 8e-03 4e-02
300 3000 1.027509686232270e+00 1.3e+01 2.85e-02 5e-03 2e-02
400 4000 1.279649321170636e-01 2.3e+01 3.53e-02 3e-03 3e-02
500 5000 4.302636076186532e-04 4.6e+01 4.78e-03 3e-04 5e-03
600 6000 6.943669235595049e-11 5.1e+01 5.41e-06 1e-07 4e-06
650 6500 5.557961334063003e-14 5.4e+01 1.88e-07 4e-09 1e-07
termination on tolfun : 1e-11
final/bestever f-value = 5.55796133406e-14 2.62435631419e-14
mean solution: [ 1. 1.00000001 1. 1.
1. 1.00000001 1.00000002 1.00000003 ...]
std deviation: [ 3.9193387e-09 3.7792732e-09 4.0062285e-09 4.6605925e-09
5.4966188e-09 7.4377745e-09 1.3797207e-08 2.6020765e-08 ...]
>>>
>>> print('best solutions fitness = %f' % (res[1]))
best solutions fitness = 2.62435631419e-14
>>> assert res[1] < 1e-12
The method ::
cma.plot();
(based on `matplotlib.pylab`) produces a plot of the run and, if necessary::
cma.show()
shows the plot in a window. To continue you might need to
close the pop-up window. This behavior seems to disappear in
subsequent calls of `cma.plot()` and is avoided by using
`ipython` with `-pylab` option. Finally ::
cma.savefig('myfirstrun') # savefig from matplotlib.pylab
will save the figure in a png.
:See: `CMAEvolutionStrategy`, `OOOptimizer.optimize(), `plot()`, `Options`, `scipy.optimize.fmin()`
""" # style guides say there should be the above empty line
try: # pass on KeyboardInterrupt
opts = locals() # collect all local variables (i.e. arguments) in a dictionary
del opts['func'] # remove those without a default value
del opts['args']
del opts['x0'] # is not optional, no default available
del opts['sigma0'] # is not optional for the constructor CMAEvolutionStrategy
if not func: # return available options in a dictionary
return Options(opts, True) # these opts are by definition valid
# TODO: this is very ugly:
incpopsize = Options({'incpopsize':incpopsize}).eval('incpopsize')
restarts = Options({'restarts':restarts}).eval('restarts')
del opts['restarts']
noise_handling = Options({'noise_handling': noise_handling}).eval('noise_handling')
del opts['noise_handling']# otherwise CMA throws an error
irun = 0
best = BestSolution()
while 1:
# recover from a CMA object
if irun == 0 and isinstance(x0, CMAEvolutionStrategy):
es = x0
x0 = es.inputargs['x0'] # for the next restarts
if sigma0 is None or not np.isscalar(array(sigma0)):
sigma0 = es.inputargs['sigma0'] # for the next restarts
# ignore further input args and keep original options
else: # default case
if irun and opts['restart_from_best']:
print('CAVE: restart_from_best is typically not useful')
es = CMAEvolutionStrategy(best.x, sigma0, opts)
else:
es = CMAEvolutionStrategy(x0, sigma0, opts)
if opts['eval_initial_x']:
x = es.gp.pheno(es.mean, bounds=es.gp.bounds)
es.best.update([x], None, [func(x, *args)], 1)
es.countevals += 1
opts = es.opts # processed options, unambiguous
append = opts['verb_append'] or es.countiter > 0 or irun > 0
logger = CMADataLogger(opts['verb_filenameprefix'], opts['verb_log'])
logger.register(es, append).add() # initial values, not fitness values
# if es.countiter == 0 and es.opts['verb_log'] > 0 and not es.opts['verb_append']:
# logger = CMADataLogger(es.opts['verb_filenameprefix']).register(es)
# logger.add()
# es.writeOutput() # initial values for sigma etc
noisehandler = NoiseHandler(es.N, noise_handling, np.median, opts['noise_reevals'], opts['noise_eps'], opts['eval_parallel'])
while not es.stop():
X, fit = es.ask_and_eval(func, args, evaluations=noisehandler.evaluations,
aggregation=np.median) # treats NaN with resampling
# TODO: check args and in case use args=(noisehandler.evaluations, )
if 11 < 3 and opts['vv']: # inject a solution
# use option check_point = [0]
if 0 * np.random.randn() >= 0:
X[0] = 0 + opts['vv'] * es.sigma**0 * np.random.randn(es.N)
fit[0] = func(X[0], *args)
# print fit[0]
es.tell(X, fit) # prepare for next iteration
if noise_handling:
es.sigma *= noisehandler(X, fit, func, es.ask, args)**opts['noise_change_sigma']
es.countevals += noisehandler.evaluations_just_done # TODO: this is a hack, not important though
es.disp()
logger.add(more_data=[noisehandler.evaluations, 10**noisehandler.noiseS] if noise_handling else [],
modulo=1 if es.stop() and logger.modulo else None)
if opts['verb_log'] and opts['verb_plot'] and \
(es.countiter % max(opts['verb_plot'], opts['verb_log']) == 0 or es.stop()):
logger.plot(324, fontsize=10)
# end while not es.stop
mean_pheno = es.gp.pheno(es.mean, bounds=es.gp.bounds)
fmean = func(mean_pheno, *args)
es.countevals += 1
es.best.update([mean_pheno], None, [fmean], es.countevals)
best.update(es.best) # in restarted case
# final message
if opts['verb_disp']:
srestarts = (' after %i restart' + ('s' if irun > 1 else '')) % irun if irun else ''
for k, v in list(es.stop().items()):
print('termination on %s=%s%s (%s)' % (k, str(v), srestarts, time.asctime()))
print('final/bestever f-value = %e %e' % (es.best.last.f, best.f))
if es.N < 9:
print('mean solution: ' + str(es.gp.pheno(es.mean)))
print('std deviation: ' + str(es.sigma * sqrt(es.dC) * es.gp.scales))
else:
print('mean solution: %s ...]' % (str(es.gp.pheno(es.mean)[:8])[:-1]))
print('std deviations: %s ...]' % (str((es.sigma * sqrt(es.dC) * es.gp.scales)[:8])[:-1]))
irun += 1
if irun > restarts or 'ftarget' in es.stopdict or 'maxfunevals' in es.stopdict:
break
opts['verb_append'] = es.countevals
opts['popsize'] = incpopsize * es.sp.popsize # TODO: use rather options?
opts['seed'] += 1
# while irun
es.out['best'] = best # TODO: this is a rather suboptimal type for inspection in the shell
if 1 < 3:
return es.result() + (es.stop(), es, logger)
else: # previously: to be removed
return (best.x.copy(), best.f, es.countevals,
dict((('stopdict', CMAStopDict(es.stopdict))
,('mean', es.gp.pheno(es.mean))
,('std', es.sigma * sqrt(es.dC) * es.gp.scales)
,('out', es.out)
,('opts', es.opts) # last state of options
,('cma', es)
,('inputargs', es.inputargs)
))
)
# TODO refine output, can #args be flexible?
# is this well usable as it is now?
except KeyboardInterrupt: # Exception, e:
if opts['verb_disp'] > 0:
print(' in/outcomment ``raise`` in last line of cma.fmin to prevent/restore KeyboardInterrupt exception')
raise # cave: swallowing this exception can silently mess up experiments, if ctrl-C is hit
def plot(name=None, fig=None, abscissa=1, iteridx=None, plot_mean=True, # TODO: plot_mean default should be False
foffset=1e-19, x_opt=None, fontsize=10):
"""
plot data from files written by a `CMADataLogger`,
the call ``cma.plot(name, **argsdict)`` is a shortcut for
``cma.CMADataLogger(name).plot(**argsdict)``
Arguments
---------
`name`
name of the logger, filename prefix, None evaluates to
the default 'outcmaes'
`fig`
filename or figure number, or both as a tuple (any order)
`abscissa`
0==plot versus iteration count,
1==plot versus function evaluation number
`iteridx`
iteration indices to plot
Return `None`
Examples
--------
::
cma.plot(); # the optimization might be still
# running in a different shell
cma.show() # to continue you might need to close the pop-up window
# once and call cma.plot() again.
# This behavior seems to disappear in subsequent
# calls of cma.plot(). Also using ipython with -pylab
# option might help.
cma.savefig('fig325.png')
cma.close()
cdl = cma.CMADataLogger().downsampling().plot()
Details
-------
Data from codes in other languages (C, Java, Matlab, Scilab) have the same
format and can be plotted just the same.
:See: `CMADataLogger`, `CMADataLogger.plot()`
"""
CMADataLogger(name).plot(fig, abscissa, iteridx, plot_mean, foffset, x_opt, fontsize)
def disp(name=None, idx=None):
"""displays selected data from (files written by) the class `CMADataLogger`.
The call ``cma.disp(name, idx)`` is a shortcut for ``cma.CMADataLogger(name).disp(idx)``.
Arguments
---------
`name`
name of the logger, filename prefix, `None` evaluates to
the default ``'outcmaes'``
`idx`
indices corresponding to rows in the data file; by
default the first five, then every 100-th, and the last
10 rows. Too large index values are removed.
Examples
--------
::
import cma, numpy
# assume some data are available from previous runs
cma.disp(None,numpy.r_[0,-1]) # first and last
cma.disp(None,numpy.r_[0:1e9:100,-1]) # every 100-th and last
cma.disp(idx=numpy.r_[0,-10:0]) # first and ten last
cma.disp(idx=numpy.r_[0:1e9:1e3,-10:0])
:See: `CMADataLogger.disp()`
"""
return CMADataLogger(name if name else 'outcmaes'
).disp(idx)
#____________________________________________________________
def _fileToMatrix(file_name):
"""rudimentary method to read in data from a file"""
# TODO: np.loadtxt() might be an alternative
# try:
if 1 < 3:
lres = []
for line in open(file_name, 'r').readlines():
if len(line) > 0 and line[0] not in ('%', '#'):
lres.append(list(map(float, line.split())))
res = lres
else:
fil = open(file_name, 'r')
fil.readline() # rudimentary, assume one comment line
lineToRow = lambda line: list(map(float, line.split()))
res = list(map(lineToRow, fil.readlines()))
fil.close() # close file could be omitted, reference counting should do during garbage collection, but...
while res != [] and res[0] == []: # remove further leading empty lines
del res[0]
return res
# except:
print('could not read file ' + file_name)
#____________________________________________________________
#____________________________________________________________
class NoiseHandler(object):
"""Noise handling according to [Hansen et al 2009, A Method for Handling
Uncertainty in Evolutionary Optimization...]
The interface of this class is yet versatile and subject to changes.
The attribute ``evaluations`` serves to control the noise via number of
evaluations, for example with `ask_and_eval()`, see also parameter
``maxevals`` and compare the example.
Example
-------
>>> import cma, numpy as np
>>> func = cma.Fcts.noisysphere
>>> es = cma.CMAEvolutionStrategy(np.ones(10), 1)
>>> logger = cma.CMADataLogger().register(es)
>>> nh = cma.NoiseHandler(es.N, maxevals=[1, 30])
>>> while not es.stop():
... X, fit = es.ask_and_eval(func, evaluations=nh.evaluations)
... es.tell(X, fit) # prepare for next iteration
... es.sigma *= nh(X, fit, func, es.ask) # see method __call__
... es.countevals += nh.evaluations_just_done # this is a hack, not important though
... logger.add(more_data = [nh.evaluations, nh.noiseS]) # add a data point
... es.disp()
... # nh.maxevals = ... it might be useful to start with smaller values and then increase
>>> print(es.stop())
>>> print(es.result()[-2]) # take mean value, the best solution is totally off
>>> assert sum(es.result()[-2]**2) < 1e-9
>>> print(X[np.argmin(fit)]) # not bad, but probably worse than the mean
>>> logger.plot()
The noise options of `fmin()` control a `NoiseHandler` instance similar to this
example. The command ``cma.Options('noise')`` lists in effect the parameters of
`__init__` apart from ``aggregate``.
Details
-------
The parameters reevals, theta, c_s, and alpha_t are set differently
than in the original publication, see method `__init__()`. For a
very small population size, say popsize <= 5, the measurement
technique based on rank changes is likely to fail.
Missing Features
----------------
In case no noise is found, ``self.lam_reeval`` should be adaptive
and get at least as low as 1 (however the possible savings from this
are rather limited). Another option might be to decide during the
first call by a quantitative analysis of fitness values whether
``lam_reeval`` is set to zero. More generally, an automatic noise
mode detection might also set the covariance matrix learning rates
to smaller values.
:See: `fmin()`, `ask_and_eval()`
"""
def __init__(self, N, maxevals=10, aggregate=np.median, reevals=None, epsilon=1e-7, parallel=False):
"""parameters are
`N`
dimension
`maxevals`
maximal value for ``self.evaluations``, where
``self.evaluations`` function calls are aggregated for
noise treatment. With ``maxevals == 0`` the noise
handler is (temporarily) "switched off". If `maxevals`
is a list, min value and (for >2 elements) median are
used to define minimal and initial value of
``self.evaluations``. Choosing ``maxevals > 1`` is only
reasonable, if also the original ``fit`` values (that
are passed to `__call__`) are computed by aggregation of
``self.evaluations`` values (otherwise the values are
not comparable), as it is done within `fmin()`.
`aggregate`
function to aggregate single f-values to a 'fitness', e.g.
``np.median``.
`reevals`
number of solutions to be reevaluated for noise measurement,
can be a float, by default set to ``1.5 + popsize/20``,
zero switches noise handling off.
`epsilon`
multiplier for perturbation of the reevaluated solutions
`parallel`
a single f-call with all resampled solutions
:See: `fmin()`, `Options`, `CMAEvolutionStrategy.ask_and_eval()`
"""
self.lam_reeval = reevals # 2 + popsize/20, see method indices(), originally 2 + popsize/10
self.epsilon = epsilon
self.parallel = parallel
self.theta = 0.5 # originally 0.2
self.cum = 0.3 # originally 1, 0.3 allows one disagreement of current point with resulting noiseS
self.alphasigma = 1 + 2 / (N+10)
self.alphaevals = 1 + 2 / (N+10) # originally 1.5
self.alphaevalsdown = self.alphaevals**-0.25 # originally 1/1.5
self.evaluations = 1 # to aggregate for a single f-evaluation
self.minevals = 1
self.maxevals = int(np.max(maxevals))
if hasattr(maxevals, '__contains__'): # i.e. can deal with ``in``
if len(maxevals) > 1:
self.minevals = min(maxevals)
self.evaluations = self.minevals
if len(maxevals) > 2:
self.evaluations = np.median(maxevals)
self.f_aggregate = aggregate
self.evaluations_just_done = 0 # actually conducted evals, only for documentation
self.noiseS = 0
def __call__(self, X, fit, func, ask=None, args=()):
"""proceed with noise measurement, set anew attributes ``evaluations``
(proposed number of evaluations to "treat" noise) and ``evaluations_just_done``
and return a factor for increasing sigma.
Parameters
----------
`X`
a list/sequence/vector of solutions
`fit`
the respective list of function values
`func`
the objective function, ``fit[i]`` corresponds to ``func(X[i], *args)``
`ask`
a method to generate a new, slightly disturbed solution. The argument
is mandatory if ``epsilon`` is not zero, see `__init__()`.
`args`
optional additional arguments to `func`
Details
-------
Calls the methods ``reeval()``, ``update_measure()`` and ``treat()`` in this order.
``self.evaluations`` is adapted within the method `treat()`.
"""
self.evaluations_just_done = 0
if not self.maxevals or self.lam_reeval == 0:
return 1.0
res = self.reeval(X, fit, func, ask, args)
if not len(res):
return 1.0
self.update_measure()
return self.treat()
def get_evaluations(self):
"""return ``self.evaluations``, the number of evalutions to get a single fitness measurement"""
return self.evaluations
def treat(self):
"""adapt self.evaluations depending on the current measurement value
and return ``sigma_fac in (1.0, self.alphasigma)``
"""
if self.noiseS > 0:
self.evaluations = min((self.evaluations * self.alphaevals, self.maxevals))
return self.alphasigma
else:
self.evaluations = max((self.evaluations * self.alphaevalsdown, self.minevals))
return 1.0
def reeval(self, X, fit, func, ask, args=()):
"""store two fitness lists, `fit` and ``fitre`` reevaluating some
solutions in `X`.
``self.evaluations`` evaluations are done for each reevaluated
fitness value.
See `__call__()`, where `reeval()` is called.
"""
self.fit = list(fit)
self.fitre = list(fit)
self.idx = self.indices(fit)
if not len(self.idx):
return self.idx
evals = int(self.evaluations) if self.f_aggregate else 1
fagg = np.median if self.f_aggregate is None else self.f_aggregate
for i in self.idx:
if self.epsilon:
if self.parallel:
self.fitre[i] = fagg(func(ask(evals, X[i], self.epsilon), *args))
else:
self.fitre[i] = fagg([func(ask(1, X[i], self.epsilon)[0], *args)
for _k in range(evals)])
else:
self.fitre[i] = fagg([func(X[i], *args) for _k in range(evals)])
self.evaluations_just_done = evals * len(self.idx)
return self.fit, self.fitre, self.idx
def update_measure(self):
"""updated noise level measure using two fitness lists ``self.fit`` and
``self.fitre``, return ``self.noiseS, all_individual_measures``.
Assumes that `self.idx` contains the indices where the fitness
lists differ
"""
lam = len(self.fit)
idx = np.argsort(self.fit + self.fitre)
ranks = np.argsort(idx).reshape((2, lam))
rankDelta = ranks[0] - ranks[1] - np.sign(ranks[0] - ranks[1])
# compute rank change limits using both ranks[0] and ranks[1]
r = np.arange(1, 2 * lam) # 2 * lam - 2 elements
limits = [0.5 * (Mh.prctile(np.abs(r - (ranks[0,i] + 1 - (ranks[0,i] > ranks[1,i]))),
self.theta*50) +
Mh.prctile(np.abs(r - (ranks[1,i] + 1 - (ranks[1,i] > ranks[0,i]))),
self.theta*50))
for i in self.idx]
# compute measurement
# max: 1 rankchange in 2*lambda is always fine
s = np.abs(rankDelta[self.idx]) - Mh.amax(limits, 1) # lives roughly in 0..2*lambda
self.noiseS += self.cum * (np.mean(s) - self.noiseS)
return self.noiseS, s
def indices(self, fit):
"""return the set of indices to be reevaluted for noise measurement,
taking the ``lam_reeval`` best from the first ``2 * lam_reeval + 2``
values.
Given the first values are the earliest, this is a useful policy also
with a time changing objective.
"""
lam = self.lam_reeval if self.lam_reeval else 2 + len(fit) / 20
reev = int(lam) + ((lam % 1) > np.random.rand())
return np.argsort(array(fit, copy=False)[:2 * (reev + 1)])[:reev]
#____________________________________________________________
#____________________________________________________________
class Sections(object):
"""plot sections through an objective function. A first
rational thing to do, when facing an (expensive) application.
By default 6 points in each coordinate are evaluated.
This class is still experimental.
Examples
--------
>>> import cma, numpy as np
>>> s = cma.Sections(cma.Fcts.rosen, np.zeros(3)).do(plot=False)
>>> s.do(plot=False) # evaluate the same points again, i.e. check for noise
>>> try:
... s.plot()
... except:
... print('plotting failed: pylab package is missing?')
Details
-------
Data are saved after each function call during `do()`. The filename is attribute
``name`` and by default ``str(func)``, see `__init__()`.
A random (orthogonal) basis can be generated with ``cma.Rotation()(np.eye(3))``.
The default name is unique in the function name, but it should be unique in all
parameters of `__init__()` but `plot_cmd` and `load`.
``self.res`` is a dictionary with an entry for each "coordinate" ``i`` and with an
entry ``'x'``, the middle point. Each entry ``i`` is again a dictionary with keys
being different dx values and the value being a sequence of f-values.
For example ``self.res[2][0.1] == [0.01, 0.01]``, which is generated using the
difference vector ``self.basis[2]`` like
``self.res[2][dx] += func(self.res['x'] + dx * self.basis[2])``.
:See: `__init__()`
"""
def __init__(self, func, x, args=(), basis=None, name=None,
plot_cmd=pylab.plot if pylab else None, load=True):
"""
Parameters
----------
`func`
objective function
`x`
point in search space, middle point of the sections
`args`
arguments passed to `func`
`basis`
evaluated points are ``func(x + locations[j] * basis[i]) for i in len(basis) for j in len(locations)``,
see `do()`
`name`
filename where to save the result
`plot_cmd`
command used to plot the data, typically matplotlib pylabs `plot` or `semilogy`
`load`
load previous data from file ``str(func) + '.pkl'``
"""
self.func = func
self.args = args
self.x = x
self.name = name if name else str(func).replace(' ', '_').replace('>', '').replace('<', '')
self.plot_cmd = plot_cmd # or semilogy
self.basis = np.eye(len(x)) if basis is None else basis
try:
self.load()
if any(self.res['x'] != x):
self.res = {}
self.res['x'] = x # TODO: res['x'] does not look perfect
else:
print(self.name + ' loaded')
except:
self.res = {}
self.res['x'] = x
def do(self, repetitions=1, locations=np.arange(-0.5, 0.6, 0.2), plot=True):
"""generates, plots and saves function values ``func(y)``,
where ``y`` is 'close' to `x` (see `__init__()`). The data are stored in
the ``res`` attribute and the class instance is saved in a file
with (the weired) name ``str(func)``.
Parameters
----------
`repetitions`
for each point, only for noisy functions is >1 useful. For
``repetitions==0`` only already generated data are plotted.
`locations`
coordinated wise deviations from the middle point given in `__init__`
"""
if not repetitions:
self.plot()
return
res = self.res
for i in range(len(self.basis)): # i-th coordinate
if i not in res:
res[i] = {}
# xx = np.array(self.x)
# TODO: store res[i]['dx'] = self.basis[i] here?
for dx in locations:
xx = self.x + dx * self.basis[i]
xkey = dx # xx[i] if (self.basis == np.eye(len(self.basis))).all() else dx
if xkey not in res[i]:
res[i][xkey] = []
n = repetitions
while n > 0:
n -= 1
res[i][xkey].append(self.func(xx, *self.args))
if plot:
self.plot()
self.save()
return self
def plot(self, plot_cmd=None, tf=lambda y: y):
"""plot the data we have, return ``self``"""
if not plot_cmd:
plot_cmd = self.plot_cmd
colors = 'bgrcmyk'
pylab.hold(False)
res = self.res
flatx, flatf = self.flattened()
minf = np.inf
for i in flatf:
minf = min((minf, min(flatf[i])))
addf = 1e-9 - minf if minf <= 0 else 0
for i in sorted(res.keys()): # we plot not all values here
if type(i) is int:
color = colors[i % len(colors)]
arx = sorted(res[i].keys())
plot_cmd(arx, [tf(np.median(res[i][x]) + addf) for x in arx], color + '-')
pylab.text(arx[-1], tf(np.median(res[i][arx[-1]])), i)
pylab.hold(True)
plot_cmd(flatx[i], tf(np.array(flatf[i]) + addf), color + 'o')
pylab.ylabel('f + ' + str(addf))
pylab.draw()
show()
# raw_input('press return')
return self
def flattened(self):
"""return flattened data ``(x, f)`` such that for the sweep through
coordinate ``i`` we have for data point ``j`` that ``f[i][j] == func(x[i][j])``
"""
flatx = {}
flatf = {}
for i in self.res:
if type(i) is int:
flatx[i] = []
flatf[i] = []
for x in sorted(self.res[i]):
for d in sorted(self.res[i][x]):
flatx[i].append(x)
flatf[i].append(d)
return flatx, flatf
def save(self, name=None):
"""save to file"""
import pickle
name = name if name else self.name
fun = self.func
del self.func # instance method produces error
pickle.dump(self, open(name + '.pkl', "wb" ))
self.func = fun
return self
def load(self, name=None):
"""load from file"""
import pickle
name = name if name else self.name
s = pickle.load(open(name + '.pkl', 'rb'))
self.res = s.res # disregard the class
return self
#____________________________________________________________
#____________________________________________________________
class _Error(Exception):
"""generic exception of cma module"""
pass
#____________________________________________________________
#____________________________________________________________
#
class ElapsedTime(object):
"""32-bit C overflows after int(2**32/1e6) == 4294s about 72 min"""
def __init__(self):
self.tic0 = time.clock()
self.tic = self.tic0
self.lasttoc = time.clock()
self.lastdiff = time.clock() - self.lasttoc
self.time_to_add = 0
self.messages = 0
def __call__(self):
toc = time.clock()
if toc - self.tic >= self.lasttoc - self.tic:
self.lastdiff = toc - self.lasttoc
self.lasttoc = toc
else: # overflow, reset self.tic
if self.messages < 3:
self.messages += 1
print(' in cma.ElapsedTime: time measure overflow, last difference estimated from',
self.tic0, self.tic, self.lasttoc, toc, toc - self.lasttoc, self.lastdiff)
self.time_to_add += self.lastdiff + self.lasttoc - self.tic
self.tic = toc # reset
self.lasttoc = toc
self.elapsedtime = toc - self.tic + self.time_to_add
return self.elapsedtime
#____________________________________________________________
#____________________________________________________________
#
class TimeIt(object):
def __init__(self, fct, args=(), seconds=1):
pass
class Misc(object):
#____________________________________________________________
#____________________________________________________________
#
class MathHelperFunctions(object):
"""static convenience math helper functions, if the function name
is preceded with an "a", a numpy array is returned
"""
@staticmethod
def aclamp(x, upper):
return -Misc.MathHelperFunctions.apos(-x, -upper)
@staticmethod
def expms(A, eig=np.linalg.eigh):
"""matrix exponential for a symmetric matrix"""
# TODO: check that this works reliably for low rank matrices
# first: symmetrize A
D, B = eig(A)
return np.dot(B, (np.exp(D) * B).T)
@staticmethod
def amax(vec, vec_or_scalar):
return array(Misc.MathHelperFunctions.max(vec, vec_or_scalar))
@staticmethod
def max(vec, vec_or_scalar):
b = vec_or_scalar
if np.isscalar(b):
m = [max(x, b) for x in vec]
else:
m = [max(vec[i], b[i]) for i in range(len(vec))]
return m
@staticmethod
def amin(vec_or_scalar, vec_or_scalar2):
return array(Misc.MathHelperFunctions.min(vec_or_scalar, vec_or_scalar2))
@staticmethod
def min(a, b):
iss = np.isscalar
if iss(a) and iss(b):
return min(a, b)
if iss(a):
a, b = b, a
# now only b can be still a scalar
if iss(b):
return [min(x, b) for x in a]
else: # two non-scalars must have the same length
return [min(a[i], b[i]) for i in range(len(a))]
@staticmethod
def norm(vec, expo=2):
return sum(vec**expo)**(1/expo)
@staticmethod
def apos(x, lower=0):
"""clips argument (scalar or array) from below at lower"""
if lower == 0:
return (x > 0) * x
else:
return lower + (x > lower) * (x - lower)
@staticmethod
def prctile(data, p_vals=[0, 25, 50, 75, 100], sorted_=False):
"""``prctile(data, 50)`` returns the median, but p_vals can
also be a sequence.
Provides for small samples better values than matplotlib.mlab.prctile,
however also slower.
"""
ps = [p_vals] if np.isscalar(p_vals) else p_vals
if not sorted_:
data = sorted(data)
n = len(data)
d = []
for p in ps:
fi = p * n / 100 - 0.5
if fi <= 0: # maybe extrapolate?
d.append(data[0])
elif fi >= n - 1:
d.append(data[-1])
else:
i = int(fi)
d.append((i+1 - fi) * data[i] + (fi - i) * data[i+1])
return d[0] if np.isscalar(p_vals) else d
@staticmethod
def sround(nb): # TODO: to be vectorized
"""return stochastic round: floor(nb) + (rand() 1000:
n = np.random.randn() / np.random.randn()
return n / 25
@staticmethod
def standard_finite_cauchy(size=1):
try:
l = len(size)
except TypeError:
l = 0
if l == 0:
return array([Mh.cauchy_with_variance_one() for _i in range(size)])
elif l == 1:
return array([Mh.cauchy_with_variance_one() for _i in range(size[0])])
elif l == 2:
return array([[Mh.cauchy_with_variance_one() for _i in range(size[1])]
for _j in range(size[0])])
else:
raise _Error('len(size) cannot be large than two')
@staticmethod
def likelihood(x, m=None, Cinv=None, sigma=1, detC=None):
"""return likelihood of x for the normal density N(m, sigma**2 * Cinv**-1)"""
# testing: MC integrate must be one: mean(p(x_i)) * volume(where x_i are uniformely sampled)
# for i in range(3): print mean([cma.likelihood(20*r-10, dim * [0], None, 3) for r in rand(10000,dim)]) * 20**dim
if m is None:
dx = x
else:
dx = x - m # array(x) - array(m)
n = len(x)
s2pi = (2*np.pi)**(n/2.)
if Cinv is None:
return exp(-sum(dx**2) / sigma**2 / 2) / s2pi / sigma**n
if detC is None:
detC = 1. / np.linalg.linalg.det(Cinv)
return exp(-np.dot(dx, np.dot(Cinv, dx)) / sigma**2 / 2) / s2pi / abs(detC)**0.5 / sigma**n
@staticmethod
def loglikelihood(self, x, previous=False):
"""return log-likelihood of `x` regarding the current sample distribution"""
# testing of original fct: MC integrate must be one: mean(p(x_i)) * volume(where x_i are uniformely sampled)
# for i in range(3): print mean([cma.likelihood(20*r-10, dim * [0], None, 3) for r in rand(10000,dim)]) * 20**dim
# TODO: test this!!
# c=cma.fmin...
# c[3]['cma'].loglikelihood(...)
if previous and hasattr(self, 'lastiter'):
sigma = self.lastiter.sigma
Crootinv = self.lastiter._Crootinv
xmean = self.lastiter.mean
D = self.lastiter.D
elif previous and self.countiter > 1:
raise _Error('no previous distribution parameters stored, check options importance_mixing')
else:
sigma = self.sigma
Crootinv = self._Crootinv
xmean = self.mean
D = self.D
dx = array(x) - xmean # array(x) - array(m)
n = self.N
logs2pi = n * log(2*np.pi) / 2.
logdetC = 2 * sum(log(D))
dx = np.dot(Crootinv, dx)
res = -sum(dx**2) / sigma**2 / 2 - logs2pi - logdetC/2 - n*log(sigma)
if 1 < 3: # testing
s2pi = (2*np.pi)**(n/2.)
detC = np.prod(D)**2
res2 = -sum(dx**2) / sigma**2 / 2 - log(s2pi * abs(detC)**0.5 * sigma**n)
assert res2 < res + 1e-8 or res2 > res - 1e-8
return res
#____________________________________________________________
#____________________________________________________________
#
# C and B are arrays rather than matrices, because they are
# addressed via B[i][j], matrices can only be addressed via B[i,j]
# tred2(N, B, diagD, offdiag);
# tql2(N, diagD, offdiag, B);
# Symmetric Householder reduction to tridiagonal form, translated from JAMA package.
@staticmethod
def eig(C):
"""eigendecomposition of a symmetric matrix, much slower than
`numpy.linalg.eigh`, return ``(EVals, Basis)``, the eigenvalues
and an orthonormal basis of the corresponding eigenvectors, where
``Basis[i]``
the i-th row of ``Basis``
columns of ``Basis``, ``[Basis[j][i] for j in range(len(Basis))]``
the i-th eigenvector with eigenvalue ``EVals[i]``
"""
# class eig(object):
# def __call__(self, C):
# Householder transformation of a symmetric matrix V into tridiagonal form.
# -> n : dimension
# -> V : symmetric nxn-matrix
# <- V : orthogonal transformation matrix:
# tridiag matrix == V * V_in * V^t
# <- d : diagonal
# <- e[0..n-1] : off diagonal (elements 1..n-1)
# Symmetric tridiagonal QL algorithm, iterative
# Computes the eigensystem from a tridiagonal matrix in roughtly 3N^3 operations
# -> n : Dimension.
# -> d : Diagonale of tridiagonal matrix.
# -> e[1..n-1] : off-diagonal, output from Householder
# -> V : matrix output von Householder
# <- d : eigenvalues
# <- e : garbage?
# <- V : basis of eigenvectors, according to d
# tred2(N, B, diagD, offdiag); B=C on input
# tql2(N, diagD, offdiag, B);
# private void tred2 (int n, double V[][], double d[], double e[]) {
def tred2 (n, V, d, e):
# This is derived from the Algol procedures tred2 by
# Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
# Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
# Fortran subroutine in EISPACK.
num_opt = False # factor 1.5 in 30-D
for j in range(n):
d[j] = V[n-1][j] # d is output argument
# Householder reduction to tridiagonal form.
for i in range(n-1,0,-1):
# Scale to avoid under/overflow.
h = 0.0
if not num_opt:
scale = 0.0
for k in range(i):
scale = scale + abs(d[k])
else:
scale = sum(abs(d[0:i]))
if scale == 0.0:
e[i] = d[i-1]
for j in range(i):
d[j] = V[i-1][j]
V[i][j] = 0.0
V[j][i] = 0.0
else:
# Generate Householder vector.
if not num_opt:
for k in range(i):
d[k] /= scale
h += d[k] * d[k]
else:
d[:i] /= scale
h = np.dot(d[:i],d[:i])
f = d[i-1]
g = h**0.5
if f > 0:
g = -g
e[i] = scale * g
h = h - f * g
d[i-1] = f - g
if not num_opt:
for j in range(i):
e[j] = 0.0
else:
e[:i] = 0.0
# Apply similarity transformation to remaining columns.
for j in range(i):
f = d[j]
V[j][i] = f
g = e[j] + V[j][j] * f
if not num_opt:
for k in range(j+1, i):
g += V[k][j] * d[k]
e[k] += V[k][j] * f
e[j] = g
else:
e[j+1:i] += V.T[j][j+1:i] * f
e[j] = g + np.dot(V.T[j][j+1:i],d[j+1:i])
f = 0.0
if not num_opt:
for j in range(i):
e[j] /= h
f += e[j] * d[j]
else:
e[:i] /= h
f += np.dot(e[:i],d[:i])
hh = f / (h + h)
if not num_opt:
for j in range(i):
e[j] -= hh * d[j]
else:
e[:i] -= hh * d[:i]
for j in range(i):
f = d[j]
g = e[j]
if not num_opt:
for k in range(j, i):
V[k][j] -= (f * e[k] + g * d[k])
else:
V.T[j][j:i] -= (f * e[j:i] + g * d[j:i])
d[j] = V[i-1][j]
V[i][j] = 0.0
d[i] = h
# end for i--
# Accumulate transformations.
for i in range(n-1):
V[n-1][i] = V[i][i]
V[i][i] = 1.0
h = d[i+1]
if h != 0.0:
if not num_opt:
for k in range(i+1):
d[k] = V[k][i+1] / h
else:
d[:i+1] = V.T[i+1][:i+1] / h
for j in range(i+1):
if not num_opt:
g = 0.0
for k in range(i+1):
g += V[k][i+1] * V[k][j]
for k in range(i+1):
V[k][j] -= g * d[k]
else:
g = np.dot(V.T[i+1][0:i+1], V.T[j][0:i+1])
V.T[j][:i+1] -= g * d[:i+1]
if not num_opt:
for k in range(i+1):
V[k][i+1] = 0.0
else:
V.T[i+1][:i+1] = 0.0
if not num_opt:
for j in range(n):
d[j] = V[n-1][j]
V[n-1][j] = 0.0
else:
d[:n] = V[n-1][:n]
V[n-1][:n] = 0.0
V[n-1][n-1] = 1.0
e[0] = 0.0
# Symmetric tridiagonal QL algorithm, taken from JAMA package.
# private void tql2 (int n, double d[], double e[], double V[][]) {
# needs roughly 3N^3 operations
def tql2 (n, d, e, V):
# This is derived from the Algol procedures tql2, by
# Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
# Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
# Fortran subroutine in EISPACK.
num_opt = False # using vectors from numpy makes it faster
if not num_opt:
for i in range(1,n): # (int i = 1; i < n; i++):
e[i-1] = e[i]
else:
e[0:n-1] = e[1:n]
e[n-1] = 0.0
f = 0.0
tst1 = 0.0
eps = 2.0**-52.0
for l in range(n): # (int l = 0; l < n; l++) {
# Find small subdiagonal element
tst1 = max(tst1, abs(d[l]) + abs(e[l]))
m = l
while m < n:
if abs(e[m]) <= eps*tst1:
break
m += 1
# If m == l, d[l] is an eigenvalue,
# otherwise, iterate.
if m > l:
iiter = 0
while 1: # do {
iiter += 1 # (Could check iteration count here.)
# Compute implicit shift
g = d[l]
p = (d[l+1] - g) / (2.0 * e[l])
r = (p**2 + 1)**0.5 # hypot(p,1.0)
if p < 0:
r = -r
d[l] = e[l] / (p + r)
d[l+1] = e[l] * (p + r)
dl1 = d[l+1]
h = g - d[l]
if not num_opt:
for i in range(l+2, n):
d[i] -= h
else:
d[l+2:n] -= h
f = f + h
# Implicit QL transformation.
p = d[m]
c = 1.0
c2 = c
c3 = c
el1 = e[l+1]
s = 0.0
s2 = 0.0
# hh = V.T[0].copy() # only with num_opt
for i in range(m-1, l-1, -1): # (int i = m-1; i >= l; i--) {
c3 = c2
c2 = c
s2 = s
g = c * e[i]
h = c * p
r = (p**2 + e[i]**2)**0.5 # hypot(p,e[i])
e[i+1] = s * r
s = e[i] / r
c = p / r
p = c * d[i] - s * g
d[i+1] = h + s * (c * g + s * d[i])
# Accumulate transformation.
if not num_opt: # overall factor 3 in 30-D
for k in range(n): # (int k = 0; k < n; k++) {
h = V[k][i+1]
V[k][i+1] = s * V[k][i] + c * h
V[k][i] = c * V[k][i] - s * h
else: # about 20% faster in 10-D
hh = V.T[i+1].copy()
# hh[:] = V.T[i+1][:]
V.T[i+1] = s * V.T[i] + c * hh
V.T[i] = c * V.T[i] - s * hh
# V.T[i] *= c
# V.T[i] -= s * hh
p = -s * s2 * c3 * el1 * e[l] / dl1
e[l] = s * p
d[l] = c * p
# Check for convergence.
if abs(e[l]) <= eps*tst1:
break
# } while (Math.abs(e[l]) > eps*tst1);
d[l] = d[l] + f
e[l] = 0.0
# Sort eigenvalues and corresponding vectors.
if 11 < 3:
for i in range(n-1): # (int i = 0; i < n-1; i++) {
k = i
p = d[i]
for j in range(i+1, n): # (int j = i+1; j < n; j++) {
if d[j] < p: # NH find smallest k>i
k = j
p = d[j]
if k != i:
d[k] = d[i] # swap k and i
d[i] = p
for j in range(n): # (int j = 0; j < n; j++) {
p = V[j][i]
V[j][i] = V[j][k]
V[j][k] = p
# tql2
N = len(C[0])
if 11 < 3:
V = np.array([x[:] for x in C]) # copy each "row"
N = V[0].size
d = np.zeros(N)
e = np.zeros(N)
else:
V = [[x[i] for i in range(N)] for x in C] # copy each "row"
d = N * [0.]
e = N * [0.]
tred2(N, V, d, e)
tql2(N, d, e, V)
return (array(d), array(V))
Mh = Misc.MathHelperFunctions
def pprint(to_be_printed):
"""nicely formated print"""
try:
import pprint as pp
# generate an instance PrettyPrinter
# pp.PrettyPrinter().pprint(to_be_printed)
pp.pprint(to_be_printed)
except ImportError:
print('could not use pprint module, will apply regular print')
print(to_be_printed)
class Rotation(object):
"""Rotation class that implements an orthogonal linear transformation,
one for each dimension. Used to implement non-separable test functions.
Example:
>>> import cma, numpy as np
>>> R = cma.Rotation()
>>> R2 = cma.Rotation() # another rotation
>>> x = np.array((1,2,3))
>>> print(R(R(x), inverse=1))
[ 1. 2. 3.]
"""
dicMatrices = {} # store matrix if necessary, for each dimension
def __init__(self):
self.dicMatrices = {} # otherwise there might be shared bases which is probably not what we want
def __call__(self, x, inverse=False): # function when calling an object
"""Rotates the input array `x` with a fixed rotation matrix
(``self.dicMatrices['str(len(x))']``)
"""
N = x.shape[0] # can be an array or matrix, TODO: accept also a list of arrays?
if str(N) not in self.dicMatrices: # create new N-basis for once and all
B = np.random.randn(N, N)
for i in range(N):
for j in range(0, i):
B[i] -= np.dot(B[i], B[j]) * B[j]
B[i] /= sum(B[i]**2)**0.5
self.dicMatrices[str(N)] = B
if inverse:
return np.dot(self.dicMatrices[str(N)].T, x) # compute rotation
else:
return np.dot(self.dicMatrices[str(N)], x) # compute rotation
# Use rotate(x) to rotate x
rotate = Rotation()
#____________________________________________________________
#____________________________________________________________
#
class FitnessFunctions(object):
""" versatile container for test objective functions """
def __init__(self):
self.counter = 0 # number of calls or any other practical use
def rot(self, x, fun, rot=1, args=()):
"""returns ``fun(rotation(x), *args)``, ie. `fun` applied to a rotated argument"""
if len(np.shape(array(x))) > 1: # parallelized
res = []
for x in x:
res.append(self.rot(x, fun, rot, args))
return res
if rot:
return fun(rotate(x, *args))
else:
return fun(x)
def somenan(self, x, fun, p=0.1):
"""returns sometimes np.NaN, otherwise fun(x)"""
if np.random.rand(1) < p:
return np.NaN
else:
return fun(x)
def rand(self, x):
"""Random test objective function"""
return np.random.random(1)[0]
def linear(self, x):
return -x[0]
def lineard(self, x):
if 1 < 3 and any(array(x) < 0):
return np.nan
if 1 < 3 and sum([ (10 + i) * x[i] for i in range(len(x))]) > 50e3:
return np.nan
return -sum(x)
def sphere(self, x):
"""Sphere (squared norm) test objective function"""
# return np.random.rand(1)[0]**0 * sum(x**2) + 1 * np.random.rand(1)[0]
return sum((x+0)**2)
def spherewithoneconstraint(self, x):
return sum((x+0)**2) if x[0] > 1 else np.nan
def elliwithoneconstraint(self, x, idx=[-1]):
return self.ellirot(x) if all(array(x)[idx] > 1) else np.nan
def spherewithnconstraints(self, x):
return sum((x+0)**2) if all(array(x) > 1) else np.nan
def noisysphere(self, x, noise=4.0, cond=1.0):
"""noise=10 does not work with default popsize, noise handling does not help """
return self.elli(x, cond=cond) * (1 + noise * np.random.randn() / len(x))
def spherew(self, x):
"""Sphere (squared norm) with sum x_i = 1 test objective function"""
# return np.random.rand(1)[0]**0 * sum(x**2) + 1 * np.random.rand(1)[0]
# s = sum(abs(x))
# return sum((x/s+0)**2) - 1/len(x)
# return sum((x/s)**2) - 1/len(x)
return -0.01*x[0] + abs(x[0])**-2 * sum(x[1:]**2)
def partsphere(self, x):
"""Sphere (squared norm) test objective function"""
self.counter += 1
# return np.random.rand(1)[0]**0 * sum(x**2) + 1 * np.random.rand(1)[0]
dim = len(x)
x = array([x[i % dim] for i in range(2*dim)])
N = 8
i = self.counter % dim
#f = sum(x[i:i + N]**2)
f = sum(x[np.random.randint(dim, size=N)]**2)
return f
def sectorsphere(self, x):
"""asymmetric Sphere (squared norm) test objective function"""
return sum(x**2) + (1e6-1) * sum(x[x<0]**2)
def cornersphere(self, x):
"""Sphere (squared norm) test objective function constraint to the corner"""
nconstr = len(x) - 0
if any(x[:nconstr] < 1):
return np.NaN
return sum(x**2) - nconstr
def cornerelli(self, x):
""" """
if any(x < 1):
return np.NaN
return self.elli(x) - self.elli(np.ones(len(x)))
def cornerellirot(self, x):
""" """
if any(x < 1):
return np.NaN
return self.ellirot(x)
def normalSkew(self, f):
N = np.random.randn(1)[0]**2
if N < 1:
N = f * N # diminish blow up lower part
return N
def noiseC(self, x, func=sphere, fac=10, expon=0.8):
f = func(self, x)
N = np.random.randn(1)[0]/np.random.randn(1)[0]
return max(1e-19, f + (float(fac)/len(x)) * f**expon * N)
def noise(self, x, func=sphere, fac=10, expon=1):
f = func(self, x)
#R = np.random.randn(1)[0]
R = np.log10(f) + expon * abs(10-np.log10(f)) * np.random.rand(1)[0]
# sig = float(fac)/float(len(x))
# R = log(f) + 0.5*log(f) * random.randn(1)[0]
# return max(1e-19, f + sig * (f**np.log10(f)) * np.exp(R))
# return max(1e-19, f * np.exp(sig * N / f**expon))
# return max(1e-19, f * normalSkew(f**expon)**sig)
return f + 10**R # == f + f**(1+0.5*RN)
def cigar(self, x, rot=0, cond=1e6):
"""Cigar test objective function"""
if rot:
x = rotate(x)
x = [x] if np.isscalar(x[0]) else x # scalar into list
f = [x[0]**2 + cond * sum(x[1:]**2) for x in x]
return f if len(f) > 1 else f[0] # 1-element-list into scalar
def tablet(self, x, rot=0):
"""Tablet test objective function"""
if rot:
x = rotate(x)
x = [x] if np.isscalar(x[0]) else x # scalar into list
f = [1e6*x[0]**2 + sum(x[1:]**2) for x in x]
return f if len(f) > 1 else f[0] # 1-element-list into scalar
def cigtab(self, y):
"""Cigtab test objective function"""
X = [y] if np.isscalar(y[0]) else y
f = [1e-4 * x[0]**2 + 1e4 * x[1]**2 + sum(x[2:]**2) for x in X]
return f if len(f) > 1 else f[0]
def twoaxes(self, y):
"""Cigtab test objective function"""
X = [y] if np.isscalar(y[0]) else y
N2 = len(X[0]) // 2
f = [1e6 * sum(x[0:N2]**2) + sum(x[N2:]**2) for x in X]
return f if len(f) > 1 else f[0]
def ellirot(self, x):
return fcts.elli(array(x), 1)
def hyperelli(self, x):
N = len(x)
return sum((np.arange(1, N+1) * x)**2)
def elli(self, x, rot=0, xoffset=0, cond=1e6, actuator_noise=0.0, both=False):
"""Ellipsoid test objective function"""
if not np.isscalar(x[0]): # parallel evaluation
return [self.elli(xi, rot) for xi in x] # could save 20% overall
if rot:
x = rotate(x)
N = len(x)
if actuator_noise:
x = x + actuator_noise * np.random.randn(N)
ftrue = sum(cond**(np.arange(N)/(N-1.))*(x+xoffset)**2)
alpha = 0.49 + 1./N
beta = 1
felli = np.random.rand(1)[0]**beta * ftrue * \
max(1, (10.**9 / (ftrue+1e-99))**(alpha*np.random.rand(1)[0]))
# felli = ftrue + 1*np.random.randn(1)[0] / (1e-30 +
# np.abs(np.random.randn(1)[0]))**0
if both:
return (felli, ftrue)
else:
# return felli # possibly noisy value
return ftrue # + np.random.randn()
def elliconstraint(self, x, cfac = 1e8, tough=True, cond=1e6):
"""ellipsoid test objective function with "constraints" """
N = len(x)
f = sum(cond**(np.arange(N)[-1::-1]/(N-1)) * x**2)
cvals = (x[0] + 1,
x[0] + 1 + 100*x[1],
x[0] + 1 - 100*x[1])
if tough:
f += cfac * sum(max(0,c) for c in cvals)
else:
f += cfac * sum(max(0,c+1e-3)**2 for c in cvals)
return f
def rosen(self, x, alpha=1e2):
"""Rosenbrock test objective function"""
x = [x] if np.isscalar(x[0]) else x # scalar into list
f = [sum(alpha*(x[:-1]**2-x[1:])**2 + (1.-x[:-1])**2) for x in x]
return f if len(f) > 1 else f[0] # 1-element-list into scalar
def diffpow(self, x, rot=0):
"""Diffpow test objective function"""
N = len(x)
if rot:
x = rotate(x)
return sum(np.abs(x)**(2.+4.*np.arange(N)/(N-1.)))**0.5
def rosenelli(self, x):
N = len(x)
return self.rosen(x[:N/2]) + self.elli(x[N/2:], cond=1)
def ridge(self, x, expo=2):
x = [x] if np.isscalar(x[0]) else x # scalar into list
f = [x[0] + 100*np.sum(x[1:]**2)**(expo/2.) for x in x]
return f if len(f) > 1 else f[0] # 1-element-list into scalar
def ridgecircle(self, x, expo=0.5):
"""happy cat by HG Beyer"""
a = len(x)
s = sum(x**2)
return ((s - a)**2)**(expo/2) + s/a + sum(x)/a
def happycat(self, x, alpha=1./8):
s = sum(x**2)
return ((s - len(x))**2)**alpha + (s/2 + sum(x)) / len(x) + 0.5
def flat(self,x):
return 1
return 1 if np.random.rand(1) < 0.9 else 1.1
return np.random.randint(1,30)
def branin(self, x):
# in [0,15]**2
y = x[1]
x = x[0] + 5
return (y - 5.1*x**2 / 4 / np.pi**2 + 5 * x / np.pi - 6)**2 + 10 * (1 - 1/8/np.pi) * np.cos(x) + 10 - 0.397887357729738160000
def goldsteinprice(self, x):
x1 = x[0]
x2 = x[1]
return (1 + (x1 +x2 + 1)**2 * (19 - 14 * x1 + 3 * x1**2 - 14 * x2 + 6 * x1 * x2 + 3 * x2**2)) * (
30 + (2 * x1 - 3 * x2)**2 * (18 - 32 * x1 + 12 * x1**2 + 48 * x2 - 36 * x1 * x2 + 27 * x2**2)) - 3
def griewank(self, x):
# was in [-600 600]
x = (600./5) * x
return 1 - np.prod(np.cos(x/sqrt(1.+np.arange(len(x))))) + sum(x**2)/4e3
def rastrigin(self, x):
"""Rastrigin test objective function"""
if not np.isscalar(x[0]):
N = len(x[0])
return [10*N + sum(xi**2 - 10*np.cos(2*np.pi*xi)) for xi in x]
# return 10*N + sum(x**2 - 10*np.cos(2*np.pi*x), axis=1)
N = len(x)
return 10*N + sum(x**2 - 10*np.cos(2*np.pi*x))
def schaffer(self, x):
""" Schaffer function x0 in [-100..100]"""
N = len(x);
s = x[0:N-1]**2 + x[1:N]**2;
return sum(s**0.25 * (np.sin(50*s**0.1)**2 + 1))
def schwefelelli(self, x):
s = 0
f = 0
for i in range(len(x)):
s += x[i]
f += s**2
return f
def schwefelmult(self, x, pen_fac = 1e4):
"""multimodal Schwefel function with domain -500..500"""
y = [x] if np.isscalar(x[0]) else x
N = len(y[0])
f = array([418.9829*N - 1.27275661e-5*N - sum(x * np.sin(np.abs(x)**0.5))
+ pen_fac * sum((abs(x) > 500) * (abs(x) - 500)**2) for x in y])
return f if len(f) > 1 else f[0]
def optprob(self, x):
n = np.arange(len(x)) + 1
f = n * x * (1-x)**(n-1)
return sum(1-f)
def lincon(self, x, theta=0.01):
"""ridge like linear function with one linear constraint"""
if x[0] < 0:
return np.NaN
return theta * x[1] + x[0]
def rosen_nesterov(self, x, rho=100):
"""needs exponential number of steps in a non-increasing f-sequence.
x_0 = (-1,1,...,1)
See Jarre (2011) "On Nesterov's Smooth Chebyshev-Rosenbrock Function"
"""
f = 0.25 * (x[0] - 1)**2
f += rho * sum((x[1:] - 2 * x[:-1]**2 + 1)**2)
return f
fcts = FitnessFunctions()
Fcts = fcts # for cross compatibility, as if the functions were static members of class Fcts
def felli(x): # unbound function, needed to test multiprocessor
return sum(1e6**(np.arange(len(x))/(len(x)-1))*(x)**2)
#____________________________________________
#____________________________________________________________
def _test(module=None): # None is fine when called from inside the module
import doctest
print(doctest.testmod(module)) # this is pretty coool!
def process_test(stream=None):
""" """
import fileinput
s1 = ""
s2 = ""
s3 = ""
state = 0
for line in fileinput.input(stream): # takes argv as file or stdin
if 1 < 3:
s3 += line
if state < -1 and line.startswith('***'):
print(s3)
if line.startswith('***'):
s3 = ""
if state == -1: # found a failed example line
s1 += '\n\n*** Failed Example:' + line
s2 += '\n\n\n' # line
# state = 0 # wait for 'Expected:' line
if line.startswith('Expected:'):
state = 1
continue
elif line.startswith('Got:'):
state = 2
continue
elif line.startswith('***'): # marks end of failed example
state = 0
elif line.startswith('Failed example:'):
state = -1
elif line.startswith('Exception raised'):
state = -2
# in effect more else:
if state == 1:
s1 += line + ''
if state == 2:
s2 += line + ''
#____________________________________________________________
#____________________________________________________________
#
def main(argv=None):
"""to install and/or test from the command line use::
python cma.py [options | func dim sig0 [optkey optval][optkey optval]...]
--test (or -t) to run the doctest, ``--test -v`` to get (much) verbosity
and ``--test -q`` to run it quietly with output only in case of errors.
install to install cma.py (uses setup from distutils.core).
--fcts and --doc for more infos or start ipython --pylab.
Examples
--------
First, testing with the local python distribution::
python cma.py --test
If succeeded install (uses setup from distutils.core)::
python cma.py install
A single run on the ellipsoid function::
python cma.py elli 10 1
"""
if argv is None:
argv = sys.argv # should have better been sys.argv[1:]
# uncomment for unit test
# _test()
# handle input arguments, getopt might be helpful ;-)
if len(argv) >= 1: # function and help
if len(argv) == 1 or argv[1].startswith('-h') or argv[1].startswith('--help'):
print(main.__doc__)
fun = None
elif argv[1].startswith('-t') or argv[1].startswith('--test'):
import doctest
if len(argv) > 2 and (argv[2].startswith('--v') or argv[2].startswith('-v')): # verbose
print('doctest for cma.py: due to different platforms and python versions')
print('and in some cases due to a missing unique random seed')
print('many examples will "fail". This is OK, if they give a similar')
print('to the expected result and if no exception occurs. ')
# if argv[1][2] == 'v':
doctest.testmod(report=True) # this is quite cool!
else: # was: if len(argv) > 2 and (argv[2].startswith('--qu') or argv[2].startswith('-q')):
print('doctest for cma.py: launching (it might be necessary to close a few pop up windows to finish)')
fn = '__cma_doctest__.txt'
stdout = sys.stdout
try:
with open(fn, 'w') as f:
sys.stdout = f
doctest.testmod(report=True) # this is quite cool!
finally:
sys.stdout = stdout
process_test(fn)
print('doctest for cma.py: finished (no other output should be seen after launching)')
return
elif argv[1] == '--doc':
print(__doc__)
print(CMAEvolutionStrategy.__doc__)
print(fmin.__doc__)
fun = None
elif argv[1] == '--fcts':
print('List of valid function names:')
print([d for d in dir(fcts) if not d.startswith('_')])
fun = None
elif argv[1] in ('install', '--install'):
from distutils.core import setup
setup(name = "cma",
version = __version__,
author = "Nikolaus Hansen",
# packages = ["cma"],
py_modules = ["cma"],
)
fun = None
elif argv[1] in ('plot',):
plot()
raw_input('press return')
fun = None
elif len(argv) > 3:
fun = eval('fcts.' + argv[1])
else:
print('try -h option')
fun = None
if fun is not None:
if len(argv) > 2: # dimension
x0 = np.ones(eval(argv[2]))
if len(argv) > 3: # sigma
sig0 = eval(argv[3])
opts = {}
for i in range(5, len(argv), 2):
opts[argv[i-1]] = eval(argv[i])
# run fmin
if fun is not None:
tic = time.time()
fmin(fun, x0, sig0, **opts) # ftarget=1e-9, tolfacupx=1e9, verb_log=10)
# plot()
# print ' best function value ', res[2]['es'].best[1]
print('elapsed time [s]: + %.2f', round(time.time() - tic, 2))
elif not len(argv):
fmin(fcts.elli, np.ones(6)*0.1, 0.1, ftarget=1e-9)
#____________________________________________________________
#____________________________________________________________
#
# mainly for testing purpose
# executed when called from an OS shell
if __name__ == "__main__":
# for i in range(1000): # how to find the memory leak
# main(["cma.py", "rastrigin", "10", "5", "popsize", "200", "maxfevals", "24999", "verb_log", "0"])
main()
================================================
FILE: src/aup/Proposer/spearmint/gp.py
================================================
##
# Copyright (C) 2012 Jasper Snoek, Hugo Larochelle and Ryan P. Adams
#
# This code is written for research and educational purposes only to
# supplement the paper entitled
# "Practical Bayesian Optimization of Machine Learning Algorithms"
# by Snoek, Larochelle and Adams
# Advances in Neural Information Processing Systems, 2012
#
# 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 .
"""
gp.py contains utility functions related to computation in Gaussian processes.
"""
import numpy as np
import scipy.linalg as spla
import scipy.optimize as spo
import scipy.io as sio
SQRT_3 = np.sqrt(3.0)
SQRT_5 = np.sqrt(5.0)
def dist2(ls, x1, x2=None):
# Assumes NxD and MxD matrices.
# Compute the squared distance matrix, given length scales.
if x2 is None:
# Find distance with self for x1.
# Rescale.
xx1 = x1 / ls
xx2 = xx1
else:
# Rescale.
xx1 = x1 / ls
xx2 = x2 / ls
r2 = np.maximum(-(np.dot(xx1, 2*xx2.T)
- np.sum(xx1*xx1, axis=1)[:,np.newaxis]
- np.sum(xx2*xx2, axis=1)[:,np.newaxis].T), 0.0)
return r2
def grad_dist2(ls, x1, x2=None):
if x2 is None:
x2 = x1
# Rescale.
x1 = x1 / ls
x2 = x2 / ls
N = x1.shape[0]
M = x2.shape[0]
D = x1.shape[1]
gX = np.zeros((x1.shape[0],x2.shape[0],x1.shape[1]))
code = \
"""
for (int i=0; i 100000):
val = spla.cholesky(np.eye(covmat.shape[0]))
break
try:
val = spla.cholesky(covmat +
jitter*np.eye(covmat.shape[0]), lower=True)
passed = True
except ValueError:
jitter = jitter*1.1
print("Covariance matrix not PSD, adding jitter:", jitter)
passed = False
return val
def memoize(amp2, noise, ls):
if ( 'corr' not in state
or state['amp2'] != amp2
or state['noise'] != noise
or np.any(state['ls'] != ls)):
# Get the correlation matrix
(corr, grad_corr) = self.cov_func(ls, comp, None, grad=True)
# Scale and add noise & jitter.
covmat = (amp2 * (corr + 1e-6*np.eye(comp.shape[0]))
+ noise * np.eye(comp.shape[0]))
# Memoize
state['corr'] = corr
state['grad_corr'] = grad_corr
state['chol'] = jitter_chol(covmat)
state['amp2'] = amp2
state['noise'] = noise
state['ls'] = ls
return (state['chol'], state['corr'], state['grad_corr'])
def nlogprob(hypers):
amp2 = np.exp(hypers[0])
noise = np.exp(hypers[1])
ls = np.exp(hypers[2:])
chol = memoize(amp2, noise, ls)[0]
solve = spla.cho_solve((chol, True), diffs)
lp = -np.sum(np.log(np.diag(chol)))-0.5*np.dot(diffs, solve)
return -lp
def grad_nlogprob(hypers):
amp2 = np.exp(hypers[0])
noise = np.exp(hypers[1])
ls = np.exp(hypers[2:])
chol, corr, grad_corr = memoize(amp2, noise, ls)
solve = spla.cho_solve((chol, True), diffs)
inv_cov = spla.cho_solve((chol, True), np.eye(chol.shape[0]))
jacobian = np.outer(solve, solve) - inv_cov
grad = np.zeros(self.D + 2)
# Log amplitude gradient.
grad[0] = 0.5 * np.trace(np.dot( jacobian, corr + 1e-6*np.eye(chol.shape[0]))) * amp2
# Log noise gradient.
grad[1] = 0.5 * np.trace(np.dot( jacobian, np.eye(chol.shape[0]))) * noise
# Log length scale gradients.
for dd in range(self.D):
grad[dd+2] = 1 * np.trace(np.dot( jacobian, -amp2*grad_corr[:,:,dd]*comp[:,dd][:,np.newaxis]/(np.exp(ls[dd]))))*np.exp(ls[dd])
# Roll in the prior variance.
#grad -= 2*hypers/self.hyper_prior
return -grad
# Initial length scales.
self.ls = np.ones(self.D)
# Initial amplitude.
self.amp2 = np.std(vals)
# Initial observation noise.
self.noise = 1e-3
hypers = np.zeros(self.ls.shape[0]+2)
hypers[0] = np.log(self.amp2)
hypers[1] = np.log(self.noise)
hypers[2:] = np.log(self.ls)
# Use a bounded bfgs just to prevent the length-scales and noise from
# getting into regions that are numerically unstable
b = [(-10,10),(-10,10)]
for i in range(comp.shape[1]):
b.append((-10,5))
hypers = spo.fmin_l_bfgs_b(nlogprob, hypers, grad_nlogprob, args=(), bounds=b, disp=0)
#hypers = spo.fmin_bfgs(nlogprob, hypers, grad_nlogprob, maxiter=100)
hypers = hypers[0]
#hypers = spo.fmin_bfgs(nlogprob, hypers, grad_nlogprob, maxiter=100)
self.amp2 = np.exp(hypers[0])
self.noise = np.exp(hypers[1])
self.ls = np.exp(hypers[2:])
def main():
try:
import matplotlib.pyplot as plt
except:
pass
# Let's start with some random values
x = np.linspace(0,1,10)[:,np.newaxis]*10#np.random.rand(100)[:,np.newaxis]
y = np.random.randn(10)
mygp = GP(covar='ARDSE')
mygp.real_init(x.shape[1], y)
# Sample some functions given these hyperparameters and plot them
for i in range(0,5):
x = np.linspace(0,1,100)[:,np.newaxis]*10
K = mygp.cov(x)
y = np.random.randn(100)
fsamp = mygp.mean + np.dot(spla.cholesky(K).transpose(), y)
try:
plt.plot(x, fsamp)
except:
pass
print('Loglikelihood before optimizing: ', mygp.logprob(x,y))
mygp.optimize_hypers(x,y)
print('Loglikelihood after optimizing: ', mygp.logprob(x,y))
try:
plt.show()
except:
print('Install matplotlib to get figures')
if __name__ == '__main__':
main()
================================================
FILE: src/aup/Proposer/spearmint/helpers.py
================================================
import os
import sys
import subprocess
import tempfile
from google.protobuf import text_format
from .spearmint_pb2 import *
def log(*args):
'''Write a msg to stderr.'''
for v in args:
sys.stderr.write(str(v))
sys.stderr.write("\n")
def sh(cmd):
'''Run a shell command (blocking until completion).'''
subprocess.check_call(cmd, shell=True)
def redirect_output(path):
'''Redirect stdout and stderr to a file.'''
outfile = open(path, 'a')
sys.stdout = outfile
sys.stderr = outfile
def check_dir(path):
'''Create a directory if it doesn't exist.'''
if not os.path.exists(path):
os.mkdir(path)
def grid_for(job):
return os.path.join(job.expt_dir, 'expt-grid.pkl')
def file_write_safe(path, data):
'''Write data to a temporary file, then move to the destination path.'''
fh = tempfile.NamedTemporaryFile(mode='w', delete=False)
fh.write(data)
fh.close()
cmd = 'mv "%s" "%s"' % (fh.name, path)
sh(cmd)
def save_experiment(filename, expt):
file_write_safe(filename, text_format.MessageToString(expt))
def load_experiment(filename):
fh = open(filename, 'rb')
expt = Experiment()
text_format.Merge(fh.read(), expt)
fh.close()
return expt
def job_output_file(job):
return os.path.join(job.expt_dir, 'output', '%08d.out' % (job.id))
def job_file_for(job):
'''Get the path to the job file corresponding to a job object.'''
return os.path.join(job.expt_dir, 'jobs', '%08d.pb' % (job.id))
def save_job(job):
filename = job_file_for(job)
file_write_safe(filename, job.SerializeToString())
def load_job(filename):
fh = open(filename, 'rb')
job = Job()
job.ParseFromString(fh.read())
fh.close()
return job
================================================
FILE: src/aup/Proposer/spearmint/runner.py
================================================
import sys
import os
import traceback
from spearmint_pb2 import *
from ExperimentGrid import *
from helpers import *
# System dependent modules
DEFAULT_MODULES = [ 'packages/epd/7.1-2',
'packages/matlab/r2011b',
'mpi/openmpi/1.2.8/intel',
'libraries/mkl/10.0',
'packages/cuda/4.0',
]
MCR_LOCATION = "/home/matlab/v715" # hack
def job_runner(job):
'''This fn runs in a new process. Now we are going to do a little
bookkeeping and then spin off the actual job that does whatever it is we're
trying to achieve.'''
redirect_output(job_output_file(job))
log("Running in wrapper mode for '%s'\n" % (job.id))
ExperimentGrid.job_running(job.expt_dir, job.id)
# Update metadata and save the job file, which will be read by the job wrappers.
job.start_t = int(time.time())
job.status = 'running'
save_job(job)
success = False
start_time = time.time()
try:
if job.language == MATLAB: run_matlab_job(job)
elif job.language == PYTHON: run_python_job(job)
elif job.language == SHELL: run_torch_job(job)
elif job.language == MCR: run_mcr_job(job)
else:
raise Exception("That function type has not been implemented.")
success = True
except:
log("-" * 40)
log("Problem running the job:")
log(sys.exc_info())
log(traceback.print_exc(limit=1000))
log("-" * 40)
end_time = time.time()
duration = end_time - start_time
# The job output is written back to the job file, so we read it back in to
# get the results.
job_file = job_file_for(job)
job = load_job(job_file)
log("Job file reloaded.")
if not job.HasField("value"):
log("Could not find value in output file.")
success = False
if success:
log("Completed successfully in %0.2f seconds. [%f]"
% (duration, job.value))
# Update the status for this job.
ExperimentGrid.job_complete(job.expt_dir, job.id,
job.value, duration)
job.status = 'complete'
else:
log("Job failed in %0.2f seconds." % (duration))
# Update the experiment status for this job.
ExperimentGrid.job_broken(job.expt_dir, job.id)
job.status = 'broken'
job.end_t = int(time.time())
job.duration = duration
save_job(job)
def run_matlab_job(job):
'''Run it as a Matlab function.'''
log("Running matlab job.")
job_file = job_file_for(job)
function_call = "matlab_wrapper('%s'),quit;" % (job_file)
matlab_cmd = ('matlab -nosplash -nodesktop -r "%s"' %
(function_call))
log(matlab_cmd)
sh(matlab_cmd)
# TODO: change this function to be more flexible when running python jobs
# regarding the python path, experiment directory, etc...
def run_python_job(job):
'''Run a Python function.'''
log("Running python job.\n")
# Add experiment directory to the system path.
sys.path.append(os.path.realpath(job.expt_dir))
# Convert the PB object into useful parameters.
params = {}
for param in job.param:
dbl_vals = param.dbl_val._values
int_vals = param.int_val._values
str_vals = param.str_val._values
if len(dbl_vals) > 0:
params[param.name] = np.array(dbl_vals)
elif len(int_vals) > 0:
params[param.name] = np.array(int_vals, dtype=int)
elif len(str_vals) > 0:
params[param.name] = str_vals
else:
raise Exception("Unknown parameter type.")
# Load up this module and run
module = __import__(job.name)
result = module.main(job.id, params)
log("Got result %f\n" % (result))
# Store the result.
job.value = result
save_job(job)
def run_torch_job(job):
'''Run a torch based job.'''
params = {}
for param in job.param:
dbl_vals = param.dbl_val._values
int_vals = param.int_val._values
str_vals = param.str_val._values
if len(dbl_vals) > 0:
params[param.name] = dbl_vals
elif len(int_vals) > 0:
params[param.name] = int_vals
elif len(str_vals) > 0:
params[param.name] = str_vals
else:
raise Exception("Unknown parameter type.")
#TODO: this passes args correctly for experiment utils, but we need to
# figure out how to get the result back out when the experiment completes.
param_str = ""
for pname, pval in params.iteritems():
if len(pval) == 1:
pval = str(pval[0])
else:
pval = ','.join([str(v) for v in pval])
param_str += "-" + pname + " " + pval + " "
cmd = "./%s %s" % (job.name, param_str)
log("Executing command: %s\n" % (cmd))
sh(cmd)
def run_shell_job(job):
'''Run a shell based job.'''
log("Running shell job.\n")
# Change into the directory.
os.chdir(job.expt_dir)
cmd = './%s %s' % (job.name, job_file_for(job))
log("Executing command '%s'\n" % (cmd))
sh(cmd)
def run_mcr_job(job):
'''Run a compiled Matlab job.'''
log("Running a compiled Matlab job.\n")
# Change into the directory.
os.chdir(job.expt_dir)
if os.environ.has_key('MATLAB'):
mcr_loc = os.environ['MATLAB']
else:
mcr_loc = MCR_LOCATION
cmd = './run_%s.sh %s %s' % (job.name, mcr_loc, job_file_for(job))
log("Executing command '%s'\n" % (cmd))
sh(cmd)
================================================
FILE: src/aup/Proposer/spearmint/sobol_lib.py
================================================
from __future__ import print_function
import math
from numpy import *
def i4_bit_hi1 ( n ):
#*****************************************************************************80
#
## I4_BIT_HI1 returns the position of the high 1 bit base 2 in an integer.
#
# Example:
#
# N Binary BIT
# ---- -------- ----
# 0 0 0
# 1 1 1
# 2 10 2
# 3 11 2
# 4 100 3
# 5 101 3
# 6 110 3
# 7 111 3
# 8 1000 4
# 9 1001 4
# 10 1010 4
# 11 1011 4
# 12 1100 4
# 13 1101 4
# 14 1110 4
# 15 1111 4
# 16 10000 5
# 17 10001 5
# 1023 1111111111 10
# 1024 10000000000 11
# 1025 10000000001 11
#
# Licensing:
#
# This code is distributed under the GNU LGPL license.
#
# Modified:
#
# 26 Nov 2011
#
# Author:
#
# Original MATLAB version by John Burkardt.
# PYTHON version by Corrado Chisari
# Modified by Jasper Snoek to scale to 1111 dimensions
#
# Parameters:
#
# Input, integer N, the integer to be measured.
# N should be nonnegative. If N is nonpositive, the value will always be 0.
#
# Output, integer BIT, the number of bits base 2.
#
i = math.floor ( n )
bit = 0
while ( 1 ):
if ( i <= 0 ):
break
bit += 1
i = math.floor ( i / 2. )
return bit
def i4_bit_lo0 ( n ):
#*****************************************************************************80
#
## I4_BIT_LO0 returns the position of the low 0 bit base 2 in an integer.
#
# Example:
#
# N Binary BIT
# ---- -------- ----
# 0 0 1
# 1 1 2
# 2 10 1
# 3 11 3
# 4 100 1
# 5 101 2
# 6 110 1
# 7 111 4
# 8 1000 1
# 9 1001 2
# 10 1010 1
# 11 1011 3
# 12 1100 1
# 13 1101 2
# 14 1110 1
# 15 1111 5
# 16 10000 1
# 17 10001 2
# 1023 1111111111 1
# 1024 10000000000 1
# 1025 10000000001 1
#
# Licensing:
#
# This code is distributed under the GNU LGPL license.
#
# Modified:
#
# 22 February 2011
#
# Author:
#
# Original MATLAB version by John Burkardt.
# PYTHON version by Corrado Chisari
#
# Parameters:
#
# Input, integer N, the integer to be measured.
# N should be nonnegative.
#
# Output, integer BIT, the position of the low 1 bit.
#
bit = 0
i = math.floor ( n )
while ( 1 ):
bit = bit + 1
i2 = math.floor ( i / 2. )
if ( i == 2 * i2 ):
break
i = i2
return bit
def i4_sobol_generate ( m, n, skip ):
#*****************************************************************************80
#
## I4_SOBOL_GENERATE generates a Sobol dataset.
#
# Licensing:
#
# This code is distributed under the GNU LGPL license.
#
# Modified:
#
# 22 February 2011
#
# Author:
#
# Original MATLAB version by John Burkardt.
# PYTHON version by Corrado Chisari
#
# Parameters:
#
# Input, integer M, the spatial dimension.
#
# Input, integer N, the number of points to generate.
#
# Input, integer SKIP, the number of initial points to skip.
#
# Output, real R(M,N), the points.
#
r=zeros((m,n))
for j in range (1, n+1):
seed = skip + j - 2
[ r[0:m,j-1], seed ] = i4_sobol ( m, seed )
return r
def i4_sobol ( dim_num, seed ):
#*****************************************************************************80
#
## I4_SOBOL generates a new quasirandom Sobol vector with each call.
#
# Discussion:
#
# The routine adapts the ideas of Antonov and Saleev.
#
# Licensing:
#
# This code is distributed under the GNU LGPL license.
#
# Modified:
#
# 26 February 2013
#
# Author:
#
# Original FORTRAN77 version by Bennett Fox.
# MATLAB version by John Burkardt.
# PYTHON version by Corrado Chisari
# PYTHON version modified by Jasper Snoek to scale (Joe & Kuo)
#
# Reference:
#
# Antonov, Saleev,
# USSR Computational Mathematics and Mathematical Physics,
# Volume 19, 1980, pages 252 - 256.
#
# Paul Bratley, Bennett Fox,
# Algorithm 659:
# Implementing Sobol's Quasirandom Sequence Generator,
# ACM Transactions on Mathematical Software,
# Volume 14, Number 1, pages 88-100, 1988.
#
# Bennett Fox,
# Algorithm 647:
# Implementation and Relative Efficiency of Quasirandom
# Sequence Generators,
# ACM Transactions on Mathematical Software,
# Volume 12, Number 4, pages 362-376, 1986.
#
# Ilya Sobol,
# USSR Computational Mathematics and Mathematical Physics,
# Volume 16, pages 236-242, 1977.
#
# Ilya Sobol, Levitan,
# The Production of Points Uniformly Distributed in a Multidimensional
# Cube (in Russian),
# Preprint IPM Akad. Nauk SSSR,
# Number 40, Moscow 1976.
#
# Stephen Joe, Frances Kuo,
# Remark on Algorithm 659: Implementing Sobol's Quasirandom Sequence Generator,
# ACM Transactions on Mathematical Software,
# Volume 29, Number 1, March 2003, pages 49-57.
#
# Parameters:
#
# Input, integer DIM_NUM, the number of spatial dimensions.
# DIM_NUM must satisfy 1 <= DIM_NUM <= 1111.
#
# Input/output, integer SEED, the "seed" for the sequence.
# This is essentially the index in the sequence of the quasirandom
# value to be generated. On output, SEED has been set to the
# appropriate next value, usually simply SEED+1.
# If SEED is less than 0 on input, it is treated as though it were 0.
# An input value of 0 requests the first (0-th) element of the sequence.
#
# Output, real QUASI(DIM_NUM), the next quasirandom vector.
#
global atmost
global dim_max
global dim_num_save
global initialized
global lastq
global log_max
global maxcol
global poly
global recipd
global seed_save
global v
if ( not 'initialized' in globals().keys() ):
initialized = 0
dim_num_save = -1
if ( not initialized or dim_num != dim_num_save ):
initialized = 1
dim_max = 1111
dim_num_save = -1
log_max = 30
seed_save = -1
#
# Initialize (part of) V.
#
v = zeros((dim_max,log_max))
v[0,0] = 1
v[1,0] = 1
v[2,0] = 1
v[3,0] = 1
v[4,0] = 1
v[5,0] = 1
v[6,0] = 1
v[7,0] = 1
v[8,0] = 1
v[9,0] = 1
v[10,0] = 1
v[11,0] = 1
v[12,0] = 1
v[13,0] = 1
v[14,0] = 1
v[15,0] = 1
v[16,0] = 1
v[17,0] = 1
v[18,0] = 1
v[19,0] = 1
v[20,0] = 1
v[21,0] = 1
v[22,0] = 1
v[23,0] = 1
v[24,0] = 1
v[25,0] = 1
v[26,0] = 1
v[27,0] = 1
v[28,0] = 1
v[29,0] = 1
v[30,0] = 1
v[31,0] = 1
v[32,0] = 1
v[33,0] = 1
v[34,0] = 1
v[35,0] = 1
v[36,0] = 1
v[37,0] = 1
v[38,0] = 1
v[39,0] = 1
v[40,0] = 1
v[41,0] = 1
v[42,0] = 1
v[43,0] = 1
v[44,0] = 1
v[45,0] = 1
v[46,0] = 1
v[47,0] = 1
v[48,0] = 1
v[49,0] = 1
v[50,0] = 1
v[51,0] = 1
v[52,0] = 1
v[53,0] = 1
v[54,0] = 1
v[55,0] = 1
v[56,0] = 1
v[57,0] = 1
v[58,0] = 1
v[59,0] = 1
v[60,0] = 1
v[61,0] = 1
v[62,0] = 1
v[63,0] = 1
v[64,0] = 1
v[65,0] = 1
v[66,0] = 1
v[67,0] = 1
v[68,0] = 1
v[69,0] = 1
v[70,0] = 1
v[71,0] = 1
v[72,0] = 1
v[73,0] = 1
v[74,0] = 1
v[75,0] = 1
v[76,0] = 1
v[77,0] = 1
v[78,0] = 1
v[79,0] = 1
v[80,0] = 1
v[81,0] = 1
v[82,0] = 1
v[83,0] = 1
v[84,0] = 1
v[85,0] = 1
v[86,0] = 1
v[87,0] = 1
v[88,0] = 1
v[89,0] = 1
v[90,0] = 1
v[91,0] = 1
v[92,0] = 1
v[93,0] = 1
v[94,0] = 1
v[95,0] = 1
v[96,0] = 1
v[97,0] = 1
v[98,0] = 1
v[99,0] = 1
v[100,0] = 1
v[101,0] = 1
v[102,0] = 1
v[103,0] = 1
v[104,0] = 1
v[105,0] = 1
v[106,0] = 1
v[107,0] = 1
v[108,0] = 1
v[109,0] = 1
v[110,0] = 1
v[111,0] = 1
v[112,0] = 1
v[113,0] = 1
v[114,0] = 1
v[115,0] = 1
v[116,0] = 1
v[117,0] = 1
v[118,0] = 1
v[119,0] = 1
v[120,0] = 1
v[121,0] = 1
v[122,0] = 1
v[123,0] = 1
v[124,0] = 1
v[125,0] = 1
v[126,0] = 1
v[127,0] = 1
v[128,0] = 1
v[129,0] = 1
v[130,0] = 1
v[131,0] = 1
v[132,0] = 1
v[133,0] = 1
v[134,0] = 1
v[135,0] = 1
v[136,0] = 1
v[137,0] = 1
v[138,0] = 1
v[139,0] = 1
v[140,0] = 1
v[141,0] = 1
v[142,0] = 1
v[143,0] = 1
v[144,0] = 1
v[145,0] = 1
v[146,0] = 1
v[147,0] = 1
v[148,0] = 1
v[149,0] = 1
v[150,0] = 1
v[151,0] = 1
v[152,0] = 1
v[153,0] = 1
v[154,0] = 1
v[155,0] = 1
v[156,0] = 1
v[157,0] = 1
v[158,0] = 1
v[159,0] = 1
v[160,0] = 1
v[161,0] = 1
v[162,0] = 1
v[163,0] = 1
v[164,0] = 1
v[165,0] = 1
v[166,0] = 1
v[167,0] = 1
v[168,0] = 1
v[169,0] = 1
v[170,0] = 1
v[171,0] = 1
v[172,0] = 1
v[173,0] = 1
v[174,0] = 1
v[175,0] = 1
v[176,0] = 1
v[177,0] = 1
v[178,0] = 1
v[179,0] = 1
v[180,0] = 1
v[181,0] = 1
v[182,0] = 1
v[183,0] = 1
v[184,0] = 1
v[185,0] = 1
v[186,0] = 1
v[187,0] = 1
v[188,0] = 1
v[189,0] = 1
v[190,0] = 1
v[191,0] = 1
v[192,0] = 1
v[193,0] = 1
v[194,0] = 1
v[195,0] = 1
v[196,0] = 1
v[197,0] = 1
v[198,0] = 1
v[199,0] = 1
v[200,0] = 1
v[201,0] = 1
v[202,0] = 1
v[203,0] = 1
v[204,0] = 1
v[205,0] = 1
v[206,0] = 1
v[207,0] = 1
v[208,0] = 1
v[209,0] = 1
v[210,0] = 1
v[211,0] = 1
v[212,0] = 1
v[213,0] = 1
v[214,0] = 1
v[215,0] = 1
v[216,0] = 1
v[217,0] = 1
v[218,0] = 1
v[219,0] = 1
v[220,0] = 1
v[221,0] = 1
v[222,0] = 1
v[223,0] = 1
v[224,0] = 1
v[225,0] = 1
v[226,0] = 1
v[227,0] = 1
v[228,0] = 1
v[229,0] = 1
v[230,0] = 1
v[231,0] = 1
v[232,0] = 1
v[233,0] = 1
v[234,0] = 1
v[235,0] = 1
v[236,0] = 1
v[237,0] = 1
v[238,0] = 1
v[239,0] = 1
v[240,0] = 1
v[241,0] = 1
v[242,0] = 1
v[243,0] = 1
v[244,0] = 1
v[245,0] = 1
v[246,0] = 1
v[247,0] = 1
v[248,0] = 1
v[249,0] = 1
v[250,0] = 1
v[251,0] = 1
v[252,0] = 1
v[253,0] = 1
v[254,0] = 1
v[255,0] = 1
v[256,0] = 1
v[257,0] = 1
v[258,0] = 1
v[259,0] = 1
v[260,0] = 1
v[261,0] = 1
v[262,0] = 1
v[263,0] = 1
v[264,0] = 1
v[265,0] = 1
v[266,0] = 1
v[267,0] = 1
v[268,0] = 1
v[269,0] = 1
v[270,0] = 1
v[271,0] = 1
v[272,0] = 1
v[273,0] = 1
v[274,0] = 1
v[275,0] = 1
v[276,0] = 1
v[277,0] = 1
v[278,0] = 1
v[279,0] = 1
v[280,0] = 1
v[281,0] = 1
v[282,0] = 1
v[283,0] = 1
v[284,0] = 1
v[285,0] = 1
v[286,0] = 1
v[287,0] = 1
v[288,0] = 1
v[289,0] = 1
v[290,0] = 1
v[291,0] = 1
v[292,0] = 1
v[293,0] = 1
v[294,0] = 1
v[295,0] = 1
v[296,0] = 1
v[297,0] = 1
v[298,0] = 1
v[299,0] = 1
v[300,0] = 1
v[301,0] = 1
v[302,0] = 1
v[303,0] = 1
v[304,0] = 1
v[305,0] = 1
v[306,0] = 1
v[307,0] = 1
v[308,0] = 1
v[309,0] = 1
v[310,0] = 1
v[311,0] = 1
v[312,0] = 1
v[313,0] = 1
v[314,0] = 1
v[315,0] = 1
v[316,0] = 1
v[317,0] = 1
v[318,0] = 1
v[319,0] = 1
v[320,0] = 1
v[321,0] = 1
v[322,0] = 1
v[323,0] = 1
v[324,0] = 1
v[325,0] = 1
v[326,0] = 1
v[327,0] = 1
v[328,0] = 1
v[329,0] = 1
v[330,0] = 1
v[331,0] = 1
v[332,0] = 1
v[333,0] = 1
v[334,0] = 1
v[335,0] = 1
v[336,0] = 1
v[337,0] = 1
v[338,0] = 1
v[339,0] = 1
v[340,0] = 1
v[341,0] = 1
v[342,0] = 1
v[343,0] = 1
v[344,0] = 1
v[345,0] = 1
v[346,0] = 1
v[347,0] = 1
v[348,0] = 1
v[349,0] = 1
v[350,0] = 1
v[351,0] = 1
v[352,0] = 1
v[353,0] = 1
v[354,0] = 1
v[355,0] = 1
v[356,0] = 1
v[357,0] = 1
v[358,0] = 1
v[359,0] = 1
v[360,0] = 1
v[361,0] = 1
v[362,0] = 1
v[363,0] = 1
v[364,0] = 1
v[365,0] = 1
v[366,0] = 1
v[367,0] = 1
v[368,0] = 1
v[369,0] = 1
v[370,0] = 1
v[371,0] = 1
v[372,0] = 1
v[373,0] = 1
v[374,0] = 1
v[375,0] = 1
v[376,0] = 1
v[377,0] = 1
v[378,0] = 1
v[379,0] = 1
v[380,0] = 1
v[381,0] = 1
v[382,0] = 1
v[383,0] = 1
v[384,0] = 1
v[385,0] = 1
v[386,0] = 1
v[387,0] = 1
v[388,0] = 1
v[389,0] = 1
v[390,0] = 1
v[391,0] = 1
v[392,0] = 1
v[393,0] = 1
v[394,0] = 1
v[395,0] = 1
v[396,0] = 1
v[397,0] = 1
v[398,0] = 1
v[399,0] = 1
v[400,0] = 1
v[401,0] = 1
v[402,0] = 1
v[403,0] = 1
v[404,0] = 1
v[405,0] = 1
v[406,0] = 1
v[407,0] = 1
v[408,0] = 1
v[409,0] = 1
v[410,0] = 1
v[411,0] = 1
v[412,0] = 1
v[413,0] = 1
v[414,0] = 1
v[415,0] = 1
v[416,0] = 1
v[417,0] = 1
v[418,0] = 1
v[419,0] = 1
v[420,0] = 1
v[421,0] = 1
v[422,0] = 1
v[423,0] = 1
v[424,0] = 1
v[425,0] = 1
v[426,0] = 1
v[427,0] = 1
v[428,0] = 1
v[429,0] = 1
v[430,0] = 1
v[431,0] = 1
v[432,0] = 1
v[433,0] = 1
v[434,0] = 1
v[435,0] = 1
v[436,0] = 1
v[437,0] = 1
v[438,0] = 1
v[439,0] = 1
v[440,0] = 1
v[441,0] = 1
v[442,0] = 1
v[443,0] = 1
v[444,0] = 1
v[445,0] = 1
v[446,0] = 1
v[447,0] = 1
v[448,0] = 1
v[449,0] = 1
v[450,0] = 1
v[451,0] = 1
v[452,0] = 1
v[453,0] = 1
v[454,0] = 1
v[455,0] = 1
v[456,0] = 1
v[457,0] = 1
v[458,0] = 1
v[459,0] = 1
v[460,0] = 1
v[461,0] = 1
v[462,0] = 1
v[463,0] = 1
v[464,0] = 1
v[465,0] = 1
v[466,0] = 1
v[467,0] = 1
v[468,0] = 1
v[469,0] = 1
v[470,0] = 1
v[471,0] = 1
v[472,0] = 1
v[473,0] = 1
v[474,0] = 1
v[475,0] = 1
v[476,0] = 1
v[477,0] = 1
v[478,0] = 1
v[479,0] = 1
v[480,0] = 1
v[481,0] = 1
v[482,0] = 1
v[483,0] = 1
v[484,0] = 1
v[485,0] = 1
v[486,0] = 1
v[487,0] = 1
v[488,0] = 1
v[489,0] = 1
v[490,0] = 1
v[491,0] = 1
v[492,0] = 1
v[493,0] = 1
v[494,0] = 1
v[495,0] = 1
v[496,0] = 1
v[497,0] = 1
v[498,0] = 1
v[499,0] = 1
v[500,0] = 1
v[501,0] = 1
v[502,0] = 1
v[503,0] = 1
v[504,0] = 1
v[505,0] = 1
v[506,0] = 1
v[507,0] = 1
v[508,0] = 1
v[509,0] = 1
v[510,0] = 1
v[511,0] = 1
v[512,0] = 1
v[513,0] = 1
v[514,0] = 1
v[515,0] = 1
v[516,0] = 1
v[517,0] = 1
v[518,0] = 1
v[519,0] = 1
v[520,0] = 1
v[521,0] = 1
v[522,0] = 1
v[523,0] = 1
v[524,0] = 1
v[525,0] = 1
v[526,0] = 1
v[527,0] = 1
v[528,0] = 1
v[529,0] = 1
v[530,0] = 1
v[531,0] = 1
v[532,0] = 1
v[533,0] = 1
v[534,0] = 1
v[535,0] = 1
v[536,0] = 1
v[537,0] = 1
v[538,0] = 1
v[539,0] = 1
v[540,0] = 1
v[541,0] = 1
v[542,0] = 1
v[543,0] = 1
v[544,0] = 1
v[545,0] = 1
v[546,0] = 1
v[547,0] = 1
v[548,0] = 1
v[549,0] = 1
v[550,0] = 1
v[551,0] = 1
v[552,0] = 1
v[553,0] = 1
v[554,0] = 1
v[555,0] = 1
v[556,0] = 1
v[557,0] = 1
v[558,0] = 1
v[559,0] = 1
v[560,0] = 1
v[561,0] = 1
v[562,0] = 1
v[563,0] = 1
v[564,0] = 1
v[565,0] = 1
v[566,0] = 1
v[567,0] = 1
v[568,0] = 1
v[569,0] = 1
v[570,0] = 1
v[571,0] = 1
v[572,0] = 1
v[573,0] = 1
v[574,0] = 1
v[575,0] = 1
v[576,0] = 1
v[577,0] = 1
v[578,0] = 1
v[579,0] = 1
v[580,0] = 1
v[581,0] = 1
v[582,0] = 1
v[583,0] = 1
v[584,0] = 1
v[585,0] = 1
v[586,0] = 1
v[587,0] = 1
v[588,0] = 1
v[589,0] = 1
v[590,0] = 1
v[591,0] = 1
v[592,0] = 1
v[593,0] = 1
v[594,0] = 1
v[595,0] = 1
v[596,0] = 1
v[597,0] = 1
v[598,0] = 1
v[599,0] = 1
v[600,0] = 1
v[601,0] = 1
v[602,0] = 1
v[603,0] = 1
v[604,0] = 1
v[605,0] = 1
v[606,0] = 1
v[607,0] = 1
v[608,0] = 1
v[609,0] = 1
v[610,0] = 1
v[611,0] = 1
v[612,0] = 1
v[613,0] = 1
v[614,0] = 1
v[615,0] = 1
v[616,0] = 1
v[617,0] = 1
v[618,0] = 1
v[619,0] = 1
v[620,0] = 1
v[621,0] = 1
v[622,0] = 1
v[623,0] = 1
v[624,0] = 1
v[625,0] = 1
v[626,0] = 1
v[627,0] = 1
v[628,0] = 1
v[629,0] = 1
v[630,0] = 1
v[631,0] = 1
v[632,0] = 1
v[633,0] = 1
v[634,0] = 1
v[635,0] = 1
v[636,0] = 1
v[637,0] = 1
v[638,0] = 1
v[639,0] = 1
v[640,0] = 1
v[641,0] = 1
v[642,0] = 1
v[643,0] = 1
v[644,0] = 1
v[645,0] = 1
v[646,0] = 1
v[647,0] = 1
v[648,0] = 1
v[649,0] = 1
v[650,0] = 1
v[651,0] = 1
v[652,0] = 1
v[653,0] = 1
v[654,0] = 1
v[655,0] = 1
v[656,0] = 1
v[657,0] = 1
v[658,0] = 1
v[659,0] = 1
v[660,0] = 1
v[661,0] = 1
v[662,0] = 1
v[663,0] = 1
v[664,0] = 1
v[665,0] = 1
v[666,0] = 1
v[667,0] = 1
v[668,0] = 1
v[669,0] = 1
v[670,0] = 1
v[671,0] = 1
v[672,0] = 1
v[673,0] = 1
v[674,0] = 1
v[675,0] = 1
v[676,0] = 1
v[677,0] = 1
v[678,0] = 1
v[679,0] = 1
v[680,0] = 1
v[681,0] = 1
v[682,0] = 1
v[683,0] = 1
v[684,0] = 1
v[685,0] = 1
v[686,0] = 1
v[687,0] = 1
v[688,0] = 1
v[689,0] = 1
v[690,0] = 1
v[691,0] = 1
v[692,0] = 1
v[693,0] = 1
v[694,0] = 1
v[695,0] = 1
v[696,0] = 1
v[697,0] = 1
v[698,0] = 1
v[699,0] = 1
v[700,0] = 1
v[701,0] = 1
v[702,0] = 1
v[703,0] = 1
v[704,0] = 1
v[705,0] = 1
v[706,0] = 1
v[707,0] = 1
v[708,0] = 1
v[709,0] = 1
v[710,0] = 1
v[711,0] = 1
v[712,0] = 1
v[713,0] = 1
v[714,0] = 1
v[715,0] = 1
v[716,0] = 1
v[717,0] = 1
v[718,0] = 1
v[719,0] = 1
v[720,0] = 1
v[721,0] = 1
v[722,0] = 1
v[723,0] = 1
v[724,0] = 1
v[725,0] = 1
v[726,0] = 1
v[727,0] = 1
v[728,0] = 1
v[729,0] = 1
v[730,0] = 1
v[731,0] = 1
v[732,0] = 1
v[733,0] = 1
v[734,0] = 1
v[735,0] = 1
v[736,0] = 1
v[737,0] = 1
v[738,0] = 1
v[739,0] = 1
v[740,0] = 1
v[741,0] = 1
v[742,0] = 1
v[743,0] = 1
v[744,0] = 1
v[745,0] = 1
v[746,0] = 1
v[747,0] = 1
v[748,0] = 1
v[749,0] = 1
v[750,0] = 1
v[751,0] = 1
v[752,0] = 1
v[753,0] = 1
v[754,0] = 1
v[755,0] = 1
v[756,0] = 1
v[757,0] = 1
v[758,0] = 1
v[759,0] = 1
v[760,0] = 1
v[761,0] = 1
v[762,0] = 1
v[763,0] = 1
v[764,0] = 1
v[765,0] = 1
v[766,0] = 1
v[767,0] = 1
v[768,0] = 1
v[769,0] = 1
v[770,0] = 1
v[771,0] = 1
v[772,0] = 1
v[773,0] = 1
v[774,0] = 1
v[775,0] = 1
v[776,0] = 1
v[777,0] = 1
v[778,0] = 1
v[779,0] = 1
v[780,0] = 1
v[781,0] = 1
v[782,0] = 1
v[783,0] = 1
v[784,0] = 1
v[785,0] = 1
v[786,0] = 1
v[787,0] = 1
v[788,0] = 1
v[789,0] = 1
v[790,0] = 1
v[791,0] = 1
v[792,0] = 1
v[793,0] = 1
v[794,0] = 1
v[795,0] = 1
v[796,0] = 1
v[797,0] = 1
v[798,0] = 1
v[799,0] = 1
v[800,0] = 1
v[801,0] = 1
v[802,0] = 1
v[803,0] = 1
v[804,0] = 1
v[805,0] = 1
v[806,0] = 1
v[807,0] = 1
v[808,0] = 1
v[809,0] = 1
v[810,0] = 1
v[811,0] = 1
v[812,0] = 1
v[813,0] = 1
v[814,0] = 1
v[815,0] = 1
v[816,0] = 1
v[817,0] = 1
v[818,0] = 1
v[819,0] = 1
v[820,0] = 1
v[821,0] = 1
v[822,0] = 1
v[823,0] = 1
v[824,0] = 1
v[825,0] = 1
v[826,0] = 1
v[827,0] = 1
v[828,0] = 1
v[829,0] = 1
v[830,0] = 1
v[831,0] = 1
v[832,0] = 1
v[833,0] = 1
v[834,0] = 1
v[835,0] = 1
v[836,0] = 1
v[837,0] = 1
v[838,0] = 1
v[839,0] = 1
v[840,0] = 1
v[841,0] = 1
v[842,0] = 1
v[843,0] = 1
v[844,0] = 1
v[845,0] = 1
v[846,0] = 1
v[847,0] = 1
v[848,0] = 1
v[849,0] = 1
v[850,0] = 1
v[851,0] = 1
v[852,0] = 1
v[853,0] = 1
v[854,0] = 1
v[855,0] = 1
v[856,0] = 1
v[857,0] = 1
v[858,0] = 1
v[859,0] = 1
v[860,0] = 1
v[861,0] = 1
v[862,0] = 1
v[863,0] = 1
v[864,0] = 1
v[865,0] = 1
v[866,0] = 1
v[867,0] = 1
v[868,0] = 1
v[869,0] = 1
v[870,0] = 1
v[871,0] = 1
v[872,0] = 1
v[873,0] = 1
v[874,0] = 1
v[875,0] = 1
v[876,0] = 1
v[877,0] = 1
v[878,0] = 1
v[879,0] = 1
v[880,0] = 1
v[881,0] = 1
v[882,0] = 1
v[883,0] = 1
v[884,0] = 1
v[885,0] = 1
v[886,0] = 1
v[887,0] = 1
v[888,0] = 1
v[889,0] = 1
v[890,0] = 1
v[891,0] = 1
v[892,0] = 1
v[893,0] = 1
v[894,0] = 1
v[895,0] = 1
v[896,0] = 1
v[897,0] = 1
v[898,0] = 1
v[899,0] = 1
v[900,0] = 1
v[901,0] = 1
v[902,0] = 1
v[903,0] = 1
v[904,0] = 1
v[905,0] = 1
v[906,0] = 1
v[907,0] = 1
v[908,0] = 1
v[909,0] = 1
v[910,0] = 1
v[911,0] = 1
v[912,0] = 1
v[913,0] = 1
v[914,0] = 1
v[915,0] = 1
v[916,0] = 1
v[917,0] = 1
v[918,0] = 1
v[919,0] = 1
v[920,0] = 1
v[921,0] = 1
v[922,0] = 1
v[923,0] = 1
v[924,0] = 1
v[925,0] = 1
v[926,0] = 1
v[927,0] = 1
v[928,0] = 1
v[929,0] = 1
v[930,0] = 1
v[931,0] = 1
v[932,0] = 1
v[933,0] = 1
v[934,0] = 1
v[935,0] = 1
v[936,0] = 1
v[937,0] = 1
v[938,0] = 1
v[939,0] = 1
v[940,0] = 1
v[941,0] = 1
v[942,0] = 1
v[943,0] = 1
v[944,0] = 1
v[945,0] = 1
v[946,0] = 1
v[947,0] = 1
v[948,0] = 1
v[949,0] = 1
v[950,0] = 1
v[951,0] = 1
v[952,0] = 1
v[953,0] = 1
v[954,0] = 1
v[955,0] = 1
v[956,0] = 1
v[957,0] = 1
v[958,0] = 1
v[959,0] = 1
v[960,0] = 1
v[961,0] = 1
v[962,0] = 1
v[963,0] = 1
v[964,0] = 1
v[965,0] = 1
v[966,0] = 1
v[967,0] = 1
v[968,0] = 1
v[969,0] = 1
v[970,0] = 1
v[971,0] = 1
v[972,0] = 1
v[973,0] = 1
v[974,0] = 1
v[975,0] = 1
v[976,0] = 1
v[977,0] = 1
v[978,0] = 1
v[979,0] = 1
v[980,0] = 1
v[981,0] = 1
v[982,0] = 1
v[983,0] = 1
v[984,0] = 1
v[985,0] = 1
v[986,0] = 1
v[987,0] = 1
v[988,0] = 1
v[989,0] = 1
v[990,0] = 1
v[991,0] = 1
v[992,0] = 1
v[993,0] = 1
v[994,0] = 1
v[995,0] = 1
v[996,0] = 1
v[997,0] = 1
v[998,0] = 1
v[999,0] = 1
v[1000,0] = 1
v[1001,0] = 1
v[1002,0] = 1
v[1003,0] = 1
v[1004,0] = 1
v[1005,0] = 1
v[1006,0] = 1
v[1007,0] = 1
v[1008,0] = 1
v[1009,0] = 1
v[1010,0] = 1
v[1011,0] = 1
v[1012,0] = 1
v[1013,0] = 1
v[1014,0] = 1
v[1015,0] = 1
v[1016,0] = 1
v[1017,0] = 1
v[1018,0] = 1
v[1019,0] = 1
v[1020,0] = 1
v[1021,0] = 1
v[1022,0] = 1
v[1023,0] = 1
v[1024,0] = 1
v[1025,0] = 1
v[1026,0] = 1
v[1027,0] = 1
v[1028,0] = 1
v[1029,0] = 1
v[1030,0] = 1
v[1031,0] = 1
v[1032,0] = 1
v[1033,0] = 1
v[1034,0] = 1
v[1035,0] = 1
v[1036,0] = 1
v[1037,0] = 1
v[1038,0] = 1
v[1039,0] = 1
v[1040,0] = 1
v[1041,0] = 1
v[1042,0] = 1
v[1043,0] = 1
v[1044,0] = 1
v[1045,0] = 1
v[1046,0] = 1
v[1047,0] = 1
v[1048,0] = 1
v[1049,0] = 1
v[1050,0] = 1
v[1051,0] = 1
v[1052,0] = 1
v[1053,0] = 1
v[1054,0] = 1
v[1055,0] = 1
v[1056,0] = 1
v[1057,0] = 1
v[1058,0] = 1
v[1059,0] = 1
v[1060,0] = 1
v[1061,0] = 1
v[1062,0] = 1
v[1063,0] = 1
v[1064,0] = 1
v[1065,0] = 1
v[1066,0] = 1
v[1067,0] = 1
v[1068,0] = 1
v[1069,0] = 1
v[1070,0] = 1
v[1071,0] = 1
v[1072,0] = 1
v[1073,0] = 1
v[1074,0] = 1
v[1075,0] = 1
v[1076,0] = 1
v[1077,0] = 1
v[1078,0] = 1
v[1079,0] = 1
v[1080,0] = 1
v[1081,0] = 1
v[1082,0] = 1
v[1083,0] = 1
v[1084,0] = 1
v[1085,0] = 1
v[1086,0] = 1
v[1087,0] = 1
v[1088,0] = 1
v[1089,0] = 1
v[1090,0] = 1
v[1091,0] = 1
v[1092,0] = 1
v[1093,0] = 1
v[1094,0] = 1
v[1095,0] = 1
v[1096,0] = 1
v[1097,0] = 1
v[1098,0] = 1
v[1099,0] = 1
v[1100,0] = 1
v[1101,0] = 1
v[1102,0] = 1
v[1103,0] = 1
v[1104,0] = 1
v[1105,0] = 1
v[1106,0] = 1
v[1107,0] = 1
v[1108,0] = 1
v[1109,0] = 1
v[1110,0] = 1
v[2,1] = 1
v[3,1] = 3
v[4,1] = 1
v[5,1] = 3
v[6,1] = 1
v[7,1] = 3
v[8,1] = 3
v[9,1] = 1
v[10,1] = 3
v[11,1] = 1
v[12,1] = 3
v[13,1] = 1
v[14,1] = 3
v[15,1] = 1
v[16,1] = 1
v[17,1] = 3
v[18,1] = 1
v[19,1] = 3
v[20,1] = 1
v[21,1] = 3
v[22,1] = 1
v[23,1] = 3
v[24,1] = 3
v[25,1] = 1
v[26,1] = 1
v[27,1] = 1
v[28,1] = 3
v[29,1] = 1
v[30,1] = 3
v[31,1] = 1
v[32,1] = 3
v[33,1] = 3
v[34,1] = 1
v[35,1] = 3
v[36,1] = 1
v[37,1] = 1
v[38,1] = 1
v[39,1] = 3
v[40,1] = 1
v[41,1] = 3
v[42,1] = 1
v[43,1] = 1
v[44,1] = 1
v[45,1] = 3
v[46,1] = 3
v[47,1] = 1
v[48,1] = 3
v[49,1] = 3
v[50,1] = 1
v[51,1] = 1
v[52,1] = 3
v[53,1] = 3
v[54,1] = 1
v[55,1] = 3
v[56,1] = 3
v[57,1] = 3
v[58,1] = 1
v[59,1] = 3
v[60,1] = 1
v[61,1] = 3
v[62,1] = 1
v[63,1] = 1
v[64,1] = 3
v[65,1] = 3
v[66,1] = 1
v[67,1] = 1
v[68,1] = 1
v[69,1] = 1
v[70,1] = 3
v[71,1] = 1
v[72,1] = 1
v[73,1] = 3
v[74,1] = 1
v[75,1] = 1
v[76,1] = 1
v[77,1] = 3
v[78,1] = 3
v[79,1] = 1
v[80,1] = 3
v[81,1] = 3
v[82,1] = 1
v[83,1] = 3
v[84,1] = 3
v[85,1] = 3
v[86,1] = 1
v[87,1] = 3
v[88,1] = 3
v[89,1] = 3
v[90,1] = 1
v[91,1] = 3
v[92,1] = 3
v[93,1] = 1
v[94,1] = 3
v[95,1] = 3
v[96,1] = 3
v[97,1] = 1
v[98,1] = 3
v[99,1] = 1
v[100,1] = 3
v[101,1] = 1
v[102,1] = 1
v[103,1] = 3
v[104,1] = 3
v[105,1] = 1
v[106,1] = 3
v[107,1] = 3
v[108,1] = 1
v[109,1] = 1
v[110,1] = 1
v[111,1] = 3
v[112,1] = 3
v[113,1] = 1
v[114,1] = 3
v[115,1] = 3
v[116,1] = 1
v[117,1] = 3
v[118,1] = 1
v[119,1] = 1
v[120,1] = 3
v[121,1] = 3
v[122,1] = 3
v[123,1] = 1
v[124,1] = 1
v[125,1] = 1
v[126,1] = 3
v[127,1] = 1
v[128,1] = 1
v[129,1] = 3
v[130,1] = 1
v[131,1] = 1
v[132,1] = 3
v[133,1] = 3
v[134,1] = 1
v[135,1] = 3
v[136,1] = 1
v[137,1] = 3
v[138,1] = 3
v[139,1] = 3
v[140,1] = 3
v[141,1] = 1
v[142,1] = 1
v[143,1] = 1
v[144,1] = 3
v[145,1] = 3
v[146,1] = 1
v[147,1] = 1
v[148,1] = 3
v[149,1] = 1
v[150,1] = 1
v[151,1] = 1
v[152,1] = 1
v[153,1] = 1
v[154,1] = 1
v[155,1] = 3
v[156,1] = 1
v[157,1] = 3
v[158,1] = 1
v[159,1] = 1
v[160,1] = 1
v[161,1] = 3
v[162,1] = 1
v[163,1] = 3
v[164,1] = 1
v[165,1] = 3
v[166,1] = 3
v[167,1] = 3
v[168,1] = 1
v[169,1] = 1
v[170,1] = 3
v[171,1] = 3
v[172,1] = 1
v[173,1] = 3
v[174,1] = 1
v[175,1] = 3
v[176,1] = 1
v[177,1] = 1
v[178,1] = 3
v[179,1] = 1
v[180,1] = 3
v[181,1] = 1
v[182,1] = 3
v[183,1] = 1
v[184,1] = 3
v[185,1] = 1
v[186,1] = 1
v[187,1] = 1
v[188,1] = 3
v[189,1] = 3
v[190,1] = 1
v[191,1] = 3
v[192,1] = 3
v[193,1] = 1
v[194,1] = 3
v[195,1] = 1
v[196,1] = 1
v[197,1] = 1
v[198,1] = 3
v[199,1] = 1
v[200,1] = 3
v[201,1] = 1
v[202,1] = 1
v[203,1] = 3
v[204,1] = 1
v[205,1] = 1
v[206,1] = 3
v[207,1] = 3
v[208,1] = 1
v[209,1] = 1
v[210,1] = 3
v[211,1] = 3
v[212,1] = 3
v[213,1] = 1
v[214,1] = 3
v[215,1] = 3
v[216,1] = 3
v[217,1] = 1
v[218,1] = 3
v[219,1] = 1
v[220,1] = 3
v[221,1] = 1
v[222,1] = 1
v[223,1] = 1
v[224,1] = 3
v[225,1] = 1
v[226,1] = 1
v[227,1] = 1
v[228,1] = 3
v[229,1] = 1
v[230,1] = 1
v[231,1] = 1
v[232,1] = 1
v[233,1] = 1
v[234,1] = 3
v[235,1] = 3
v[236,1] = 3
v[237,1] = 1
v[238,1] = 1
v[239,1] = 1
v[240,1] = 1
v[241,1] = 3
v[242,1] = 3
v[243,1] = 3
v[244,1] = 1
v[245,1] = 3
v[246,1] = 3
v[247,1] = 1
v[248,1] = 1
v[249,1] = 1
v[250,1] = 1
v[251,1] = 3
v[252,1] = 1
v[253,1] = 1
v[254,1] = 3
v[255,1] = 1
v[256,1] = 3
v[257,1] = 3
v[258,1] = 1
v[259,1] = 1
v[260,1] = 3
v[261,1] = 3
v[262,1] = 1
v[263,1] = 1
v[264,1] = 1
v[265,1] = 1
v[266,1] = 3
v[267,1] = 1
v[268,1] = 3
v[269,1] = 3
v[270,1] = 1
v[271,1] = 3
v[272,1] = 3
v[273,1] = 1
v[274,1] = 1
v[275,1] = 1
v[276,1] = 3
v[277,1] = 3
v[278,1] = 3
v[279,1] = 1
v[280,1] = 3
v[281,1] = 3
v[282,1] = 1
v[283,1] = 3
v[284,1] = 3
v[285,1] = 1
v[286,1] = 3
v[287,1] = 1
v[288,1] = 3
v[289,1] = 3
v[290,1] = 3
v[291,1] = 1
v[292,1] = 3
v[293,1] = 1
v[294,1] = 1
v[295,1] = 3
v[296,1] = 1
v[297,1] = 3
v[298,1] = 1
v[299,1] = 1
v[300,1] = 1
v[301,1] = 3
v[302,1] = 3
v[303,1] = 3
v[304,1] = 1
v[305,1] = 1
v[306,1] = 3
v[307,1] = 1
v[308,1] = 3
v[309,1] = 1
v[310,1] = 1
v[311,1] = 1
v[312,1] = 1
v[313,1] = 1
v[314,1] = 1
v[315,1] = 3
v[316,1] = 1
v[317,1] = 1
v[318,1] = 3
v[319,1] = 1
v[320,1] = 3
v[321,1] = 3
v[322,1] = 1
v[323,1] = 1
v[324,1] = 1
v[325,1] = 1
v[326,1] = 3
v[327,1] = 1
v[328,1] = 3
v[329,1] = 1
v[330,1] = 3
v[331,1] = 1
v[332,1] = 1
v[333,1] = 1
v[334,1] = 1
v[335,1] = 3
v[336,1] = 3
v[337,1] = 1
v[338,1] = 1
v[339,1] = 1
v[340,1] = 1
v[341,1] = 1
v[342,1] = 3
v[343,1] = 3
v[344,1] = 3
v[345,1] = 1
v[346,1] = 1
v[347,1] = 3
v[348,1] = 3
v[349,1] = 3
v[350,1] = 3
v[351,1] = 3
v[352,1] = 1
v[353,1] = 3
v[354,1] = 3
v[355,1] = 1
v[356,1] = 3
v[357,1] = 3
v[358,1] = 3
v[359,1] = 3
v[360,1] = 1
v[361,1] = 1
v[362,1] = 1
v[363,1] = 1
v[364,1] = 1
v[365,1] = 1
v[366,1] = 3
v[367,1] = 1
v[368,1] = 1
v[369,1] = 3
v[370,1] = 1
v[371,1] = 1
v[372,1] = 1
v[373,1] = 3
v[374,1] = 1
v[375,1] = 1
v[376,1] = 1
v[377,1] = 3
v[378,1] = 3
v[379,1] = 3
v[380,1] = 1
v[381,1] = 3
v[382,1] = 1
v[383,1] = 1
v[384,1] = 3
v[385,1] = 3
v[386,1] = 3
v[387,1] = 1
v[388,1] = 3
v[389,1] = 3
v[390,1] = 1
v[391,1] = 3
v[392,1] = 1
v[393,1] = 3
v[394,1] = 3
v[395,1] = 1
v[396,1] = 3
v[397,1] = 3
v[398,1] = 3
v[399,1] = 1
v[400,1] = 1
v[401,1] = 3
v[402,1] = 3
v[403,1] = 1
v[404,1] = 3
v[405,1] = 1
v[406,1] = 3
v[407,1] = 1
v[408,1] = 1
v[409,1] = 1
v[410,1] = 3
v[411,1] = 3
v[412,1] = 3
v[413,1] = 3
v[414,1] = 1
v[415,1] = 3
v[416,1] = 1
v[417,1] = 1
v[418,1] = 3
v[419,1] = 1
v[420,1] = 3
v[421,1] = 1
v[422,1] = 1
v[423,1] = 1
v[424,1] = 3
v[425,1] = 1
v[426,1] = 3
v[427,1] = 1
v[428,1] = 3
v[429,1] = 1
v[430,1] = 3
v[431,1] = 3
v[432,1] = 3
v[433,1] = 3
v[434,1] = 3
v[435,1] = 3
v[436,1] = 3
v[437,1] = 3
v[438,1] = 1
v[439,1] = 3
v[440,1] = 3
v[441,1] = 3
v[442,1] = 3
v[443,1] = 3
v[444,1] = 1
v[445,1] = 3
v[446,1] = 1
v[447,1] = 3
v[448,1] = 3
v[449,1] = 3
v[450,1] = 1
v[451,1] = 3
v[452,1] = 1
v[453,1] = 3
v[454,1] = 1
v[455,1] = 3
v[456,1] = 3
v[457,1] = 1
v[458,1] = 3
v[459,1] = 3
v[460,1] = 3
v[461,1] = 3
v[462,1] = 3
v[463,1] = 3
v[464,1] = 3
v[465,1] = 3
v[466,1] = 3
v[467,1] = 1
v[468,1] = 1
v[469,1] = 1
v[470,1] = 1
v[471,1] = 1
v[472,1] = 1
v[473,1] = 3
v[474,1] = 3
v[475,1] = 1
v[476,1] = 1
v[477,1] = 3
v[478,1] = 3
v[479,1] = 1
v[480,1] = 1
v[481,1] = 1
v[482,1] = 3
v[483,1] = 3
v[484,1] = 1
v[485,1] = 1
v[486,1] = 3
v[487,1] = 3
v[488,1] = 3
v[489,1] = 3
v[490,1] = 1
v[491,1] = 1
v[492,1] = 3
v[493,1] = 1
v[494,1] = 3
v[495,1] = 3
v[496,1] = 1
v[497,1] = 3
v[498,1] = 3
v[499,1] = 1
v[500,1] = 1
v[501,1] = 1
v[502,1] = 3
v[503,1] = 3
v[504,1] = 3
v[505,1] = 1
v[506,1] = 1
v[507,1] = 3
v[508,1] = 3
v[509,1] = 3
v[510,1] = 3
v[511,1] = 3
v[512,1] = 1
v[513,1] = 1
v[514,1] = 1
v[515,1] = 3
v[516,1] = 1
v[517,1] = 3
v[518,1] = 3
v[519,1] = 1
v[520,1] = 3
v[521,1] = 3
v[522,1] = 3
v[523,1] = 3
v[524,1] = 1
v[525,1] = 1
v[526,1] = 3
v[527,1] = 1
v[528,1] = 1
v[529,1] = 3
v[530,1] = 1
v[531,1] = 3
v[532,1] = 1
v[533,1] = 3
v[534,1] = 1
v[535,1] = 3
v[536,1] = 3
v[537,1] = 1
v[538,1] = 1
v[539,1] = 3
v[540,1] = 3
v[541,1] = 1
v[542,1] = 3
v[543,1] = 3
v[544,1] = 1
v[545,1] = 3
v[546,1] = 3
v[547,1] = 1
v[548,1] = 1
v[549,1] = 3
v[550,1] = 1
v[551,1] = 3
v[552,1] = 3
v[553,1] = 1
v[554,1] = 1
v[555,1] = 3
v[556,1] = 1
v[557,1] = 3
v[558,1] = 1
v[559,1] = 3
v[560,1] = 1
v[561,1] = 1
v[562,1] = 3
v[563,1] = 3
v[564,1] = 1
v[565,1] = 1
v[566,1] = 1
v[567,1] = 3
v[568,1] = 3
v[569,1] = 1
v[570,1] = 3
v[571,1] = 1
v[572,1] = 1
v[573,1] = 3
v[574,1] = 3
v[575,1] = 1
v[576,1] = 1
v[577,1] = 3
v[578,1] = 1
v[579,1] = 3
v[580,1] = 1
v[581,1] = 1
v[582,1] = 1
v[583,1] = 1
v[584,1] = 1
v[585,1] = 3
v[586,1] = 1
v[587,1] = 1
v[588,1] = 1
v[589,1] = 1
v[590,1] = 3
v[591,1] = 1
v[592,1] = 3
v[593,1] = 1
v[594,1] = 1
v[595,1] = 3
v[596,1] = 3
v[597,1] = 1
v[598,1] = 1
v[599,1] = 3
v[600,1] = 1
v[601,1] = 3
v[602,1] = 1
v[603,1] = 3
v[604,1] = 3
v[605,1] = 3
v[606,1] = 1
v[607,1] = 3
v[608,1] = 3
v[609,1] = 3
v[610,1] = 1
v[611,1] = 1
v[612,1] = 3
v[613,1] = 3
v[614,1] = 3
v[615,1] = 1
v[616,1] = 1
v[617,1] = 1
v[618,1] = 1
v[619,1] = 3
v[620,1] = 1
v[621,1] = 3
v[622,1] = 1
v[623,1] = 3
v[624,1] = 1
v[625,1] = 1
v[626,1] = 3
v[627,1] = 3
v[628,1] = 1
v[629,1] = 1
v[630,1] = 1
v[631,1] = 3
v[632,1] = 3
v[633,1] = 1
v[634,1] = 3
v[635,1] = 1
v[636,1] = 3
v[637,1] = 1
v[638,1] = 1
v[639,1] = 1
v[640,1] = 1
v[641,1] = 1
v[642,1] = 1
v[643,1] = 3
v[644,1] = 1
v[645,1] = 3
v[646,1] = 3
v[647,1] = 1
v[648,1] = 3
v[649,1] = 3
v[650,1] = 3
v[651,1] = 1
v[652,1] = 3
v[653,1] = 1
v[654,1] = 1
v[655,1] = 3
v[656,1] = 3
v[657,1] = 1
v[658,1] = 1
v[659,1] = 3
v[660,1] = 3
v[661,1] = 1
v[662,1] = 1
v[663,1] = 1
v[664,1] = 3
v[665,1] = 1
v[666,1] = 3
v[667,1] = 3
v[668,1] = 1
v[669,1] = 1
v[670,1] = 3
v[671,1] = 1
v[672,1] = 1
v[673,1] = 3
v[674,1] = 1
v[675,1] = 3
v[676,1] = 1
v[677,1] = 1
v[678,1] = 1
v[679,1] = 3
v[680,1] = 3
v[681,1] = 3
v[682,1] = 3
v[683,1] = 1
v[684,1] = 1
v[685,1] = 3
v[686,1] = 3
v[687,1] = 1
v[688,1] = 1
v[689,1] = 1
v[690,1] = 1
v[691,1] = 3
v[692,1] = 1
v[693,1] = 1
v[694,1] = 3
v[695,1] = 3
v[696,1] = 3
v[697,1] = 1
v[698,1] = 1
v[699,1] = 3
v[700,1] = 3
v[701,1] = 1
v[702,1] = 3
v[703,1] = 3
v[704,1] = 1
v[705,1] = 1
v[706,1] = 3
v[707,1] = 3
v[708,1] = 3
v[709,1] = 3
v[710,1] = 3
v[711,1] = 3
v[712,1] = 3
v[713,1] = 1
v[714,1] = 3
v[715,1] = 3
v[716,1] = 1
v[717,1] = 3
v[718,1] = 1
v[719,1] = 3
v[720,1] = 1
v[721,1] = 1
v[722,1] = 3
v[723,1] = 3
v[724,1] = 1
v[725,1] = 1
v[726,1] = 1
v[727,1] = 3
v[728,1] = 1
v[729,1] = 3
v[730,1] = 3
v[731,1] = 1
v[732,1] = 3
v[733,1] = 3
v[734,1] = 1
v[735,1] = 3
v[736,1] = 1
v[737,1] = 1
v[738,1] = 3
v[739,1] = 3
v[740,1] = 3
v[741,1] = 1
v[742,1] = 1
v[743,1] = 1
v[744,1] = 3
v[745,1] = 1
v[746,1] = 1
v[747,1] = 1
v[748,1] = 3
v[749,1] = 3
v[750,1] = 3
v[751,1] = 1
v[752,1] = 3
v[753,1] = 3
v[754,1] = 1
v[755,1] = 3
v[756,1] = 1
v[757,1] = 1
v[758,1] = 3
v[759,1] = 3
v[760,1] = 3
v[761,1] = 1
v[762,1] = 3
v[763,1] = 3
v[764,1] = 1
v[765,1] = 1
v[766,1] = 1
v[767,1] = 3
v[768,1] = 1
v[769,1] = 3
v[770,1] = 3
v[771,1] = 3
v[772,1] = 3
v[773,1] = 3
v[774,1] = 3
v[775,1] = 3
v[776,1] = 3
v[777,1] = 1
v[778,1] = 3
v[779,1] = 3
v[780,1] = 1
v[781,1] = 3
v[782,1] = 1
v[783,1] = 1
v[784,1] = 3
v[785,1] = 3
v[786,1] = 3
v[787,1] = 1
v[788,1] = 3
v[789,1] = 3
v[790,1] = 3
v[791,1] = 3
v[792,1] = 3
v[793,1] = 1
v[794,1] = 3
v[795,1] = 3
v[796,1] = 3
v[797,1] = 1
v[798,1] = 1
v[799,1] = 1
v[800,1] = 3
v[801,1] = 3
v[802,1] = 1
v[803,1] = 3
v[804,1] = 3
v[805,1] = 1
v[806,1] = 3
v[807,1] = 1
v[808,1] = 3
v[809,1] = 1
v[810,1] = 3
v[811,1] = 1
v[812,1] = 3
v[813,1] = 3
v[814,1] = 3
v[815,1] = 3
v[816,1] = 3
v[817,1] = 3
v[818,1] = 1
v[819,1] = 1
v[820,1] = 3
v[821,1] = 1
v[822,1] = 3
v[823,1] = 1
v[824,1] = 1
v[825,1] = 1
v[826,1] = 1
v[827,1] = 1
v[828,1] = 3
v[829,1] = 1
v[830,1] = 1
v[831,1] = 1
v[832,1] = 3
v[833,1] = 1
v[834,1] = 3
v[835,1] = 1
v[836,1] = 1
v[837,1] = 3
v[838,1] = 3
v[839,1] = 3
v[840,1] = 1
v[841,1] = 3
v[842,1] = 1
v[843,1] = 3
v[844,1] = 1
v[845,1] = 1
v[846,1] = 3
v[847,1] = 1
v[848,1] = 3
v[849,1] = 3
v[850,1] = 1
v[851,1] = 3
v[852,1] = 1
v[853,1] = 3
v[854,1] = 3
v[855,1] = 1
v[856,1] = 3
v[857,1] = 3
v[858,1] = 1
v[859,1] = 3
v[860,1] = 3
v[861,1] = 3
v[862,1] = 3
v[863,1] = 3
v[864,1] = 3
v[865,1] = 1
v[866,1] = 3
v[867,1] = 1
v[868,1] = 1
v[869,1] = 3
v[870,1] = 3
v[871,1] = 3
v[872,1] = 1
v[873,1] = 1
v[874,1] = 3
v[875,1] = 3
v[876,1] = 3
v[877,1] = 3
v[878,1] = 3
v[879,1] = 3
v[880,1] = 3
v[881,1] = 1
v[882,1] = 3
v[883,1] = 3
v[884,1] = 3
v[885,1] = 3
v[886,1] = 1
v[887,1] = 3
v[888,1] = 1
v[889,1] = 3
v[890,1] = 3
v[891,1] = 3
v[892,1] = 1
v[893,1] = 3
v[894,1] = 1
v[895,1] = 3
v[896,1] = 1
v[897,1] = 1
v[898,1] = 1
v[899,1] = 3
v[900,1] = 3
v[901,1] = 1
v[902,1] = 3
v[903,1] = 1
v[904,1] = 1
v[905,1] = 3
v[906,1] = 3
v[907,1] = 1
v[908,1] = 3
v[909,1] = 1
v[910,1] = 1
v[911,1] = 1
v[912,1] = 1
v[913,1] = 3
v[914,1] = 1
v[915,1] = 3
v[916,1] = 1
v[917,1] = 1
v[918,1] = 3
v[919,1] = 1
v[920,1] = 3
v[921,1] = 1
v[922,1] = 3
v[923,1] = 3
v[924,1] = 3
v[925,1] = 3
v[926,1] = 3
v[927,1] = 3
v[928,1] = 1
v[929,1] = 3
v[930,1] = 3
v[931,1] = 3
v[932,1] = 3
v[933,1] = 1
v[934,1] = 3
v[935,1] = 3
v[936,1] = 1
v[937,1] = 3
v[938,1] = 3
v[939,1] = 3
v[940,1] = 3
v[941,1] = 3
v[942,1] = 1
v[943,1] = 1
v[944,1] = 1
v[945,1] = 1
v[946,1] = 3
v[947,1] = 3
v[948,1] = 3
v[949,1] = 1
v[950,1] = 3
v[951,1] = 3
v[952,1] = 1
v[953,1] = 1
v[954,1] = 3
v[955,1] = 3
v[956,1] = 1
v[957,1] = 1
v[958,1] = 3
v[959,1] = 3
v[960,1] = 1
v[961,1] = 3
v[962,1] = 1
v[963,1] = 1
v[964,1] = 3
v[965,1] = 1
v[966,1] = 3
v[967,1] = 3
v[968,1] = 3
v[969,1] = 3
v[970,1] = 3
v[971,1] = 1
v[972,1] = 3
v[973,1] = 1
v[974,1] = 1
v[975,1] = 3
v[976,1] = 3
v[977,1] = 3
v[978,1] = 3
v[979,1] = 1
v[980,1] = 3
v[981,1] = 1
v[982,1] = 1
v[983,1] = 3
v[984,1] = 3
v[985,1] = 3
v[986,1] = 3
v[987,1] = 3
v[988,1] = 3
v[989,1] = 1
v[990,1] = 1
v[991,1] = 3
v[992,1] = 1
v[993,1] = 3
v[994,1] = 1
v[995,1] = 1
v[996,1] = 3
v[997,1] = 1
v[998,1] = 1
v[999,1] = 1
v[1000,1] = 1
v[1001,1] = 3
v[1002,1] = 3
v[1003,1] = 1
v[1004,1] = 1
v[1005,1] = 3
v[1006,1] = 1
v[1007,1] = 1
v[1008,1] = 1
v[1009,1] = 3
v[1010,1] = 1
v[1011,1] = 3
v[1012,1] = 1
v[1013,1] = 1
v[1014,1] = 3
v[1015,1] = 3
v[1016,1] = 1
v[1017,1] = 3
v[1018,1] = 1
v[1019,1] = 1
v[1020,1] = 3
v[1021,1] = 3
v[1022,1] = 3
v[1023,1] = 3
v[1024,1] = 3
v[1025,1] = 1
v[1026,1] = 3
v[1027,1] = 1
v[1028,1] = 1
v[1029,1] = 1
v[1030,1] = 3
v[1031,1] = 1
v[1032,1] = 1
v[1033,1] = 1
v[1034,1] = 3
v[1035,1] = 1
v[1036,1] = 1
v[1037,1] = 3
v[1038,1] = 1
v[1039,1] = 3
v[1040,1] = 3
v[1041,1] = 3
v[1042,1] = 3
v[1043,1] = 3
v[1044,1] = 1
v[1045,1] = 1
v[1046,1] = 1
v[1047,1] = 3
v[1048,1] = 3
v[1049,1] = 3
v[1050,1] = 3
v[1051,1] = 1
v[1052,1] = 3
v[1053,1] = 3
v[1054,1] = 3
v[1055,1] = 3
v[1056,1] = 1
v[1057,1] = 1
v[1058,1] = 3
v[1059,1] = 3
v[1060,1] = 3
v[1061,1] = 1
v[1062,1] = 3
v[1063,1] = 1
v[1064,1] = 1
v[1065,1] = 3
v[1066,1] = 3
v[1067,1] = 1
v[1068,1] = 3
v[1069,1] = 3
v[1070,1] = 1
v[1071,1] = 1
v[1072,1] = 1
v[1073,1] = 1
v[1074,1] = 1
v[1075,1] = 3
v[1076,1] = 1
v[1077,1] = 1
v[1078,1] = 3
v[1079,1] = 3
v[1080,1] = 1
v[1081,1] = 1
v[1082,1] = 1
v[1083,1] = 3
v[1084,1] = 1
v[1085,1] = 1
v[1086,1] = 3
v[1087,1] = 3
v[1088,1] = 1
v[1089,1] = 3
v[1090,1] = 3
v[1091,1] = 3
v[1092,1] = 3
v[1093,1] = 3
v[1094,1] = 3
v[1095,1] = 3
v[1096,1] = 3
v[1097,1] = 1
v[1098,1] = 1
v[1099,1] = 3
v[1100,1] = 3
v[1101,1] = 1
v[1102,1] = 1
v[1103,1] = 3
v[1104,1] = 1
v[1105,1] = 3
v[1106,1] = 3
v[1107,1] = 3
v[1108,1] = 3
v[1109,1] = 3
v[1110,1] = 1
v[3,2] = 7
v[4,2] = 5
v[5,2] = 1
v[6,2] = 3
v[7,2] = 3
v[8,2] = 7
v[9,2] = 5
v[10,2] = 5
v[11,2] = 7
v[12,2] = 7
v[13,2] = 1
v[14,2] = 3
v[15,2] = 3
v[16,2] = 7
v[17,2] = 5
v[18,2] = 1
v[19,2] = 1
v[20,2] = 5
v[21,2] = 3
v[22,2] = 7
v[23,2] = 1
v[24,2] = 7
v[25,2] = 5
v[26,2] = 1
v[27,2] = 3
v[28,2] = 7
v[29,2] = 7
v[30,2] = 1
v[31,2] = 1
v[32,2] = 1
v[33,2] = 5
v[34,2] = 7
v[35,2] = 7
v[36,2] = 5
v[37,2] = 1
v[38,2] = 3
v[39,2] = 3
v[40,2] = 7
v[41,2] = 5
v[42,2] = 5
v[43,2] = 5
v[44,2] = 3
v[45,2] = 3
v[46,2] = 3
v[47,2] = 1
v[48,2] = 1
v[49,2] = 5
v[50,2] = 1
v[51,2] = 1
v[52,2] = 5
v[53,2] = 3
v[54,2] = 3
v[55,2] = 3
v[56,2] = 3
v[57,2] = 1
v[58,2] = 3
v[59,2] = 7
v[60,2] = 5
v[61,2] = 7
v[62,2] = 3
v[63,2] = 7
v[64,2] = 1
v[65,2] = 3
v[66,2] = 3
v[67,2] = 5
v[68,2] = 1
v[69,2] = 3
v[70,2] = 5
v[71,2] = 5
v[72,2] = 7
v[73,2] = 7
v[74,2] = 7
v[75,2] = 1
v[76,2] = 1
v[77,2] = 3
v[78,2] = 3
v[79,2] = 1
v[80,2] = 1
v[81,2] = 5
v[82,2] = 1
v[83,2] = 5
v[84,2] = 7
v[85,2] = 5
v[86,2] = 1
v[87,2] = 7
v[88,2] = 5
v[89,2] = 3
v[90,2] = 3
v[91,2] = 1
v[92,2] = 5
v[93,2] = 7
v[94,2] = 1
v[95,2] = 7
v[96,2] = 5
v[97,2] = 1
v[98,2] = 7
v[99,2] = 3
v[100,2] = 1
v[101,2] = 7
v[102,2] = 1
v[103,2] = 7
v[104,2] = 3
v[105,2] = 3
v[106,2] = 5
v[107,2] = 7
v[108,2] = 3
v[109,2] = 3
v[110,2] = 5
v[111,2] = 1
v[112,2] = 3
v[113,2] = 3
v[114,2] = 1
v[115,2] = 3
v[116,2] = 5
v[117,2] = 1
v[118,2] = 3
v[119,2] = 3
v[120,2] = 3
v[121,2] = 7
v[122,2] = 1
v[123,2] = 1
v[124,2] = 7
v[125,2] = 3
v[126,2] = 1
v[127,2] = 3
v[128,2] = 7
v[129,2] = 5
v[130,2] = 5
v[131,2] = 7
v[132,2] = 5
v[133,2] = 5
v[134,2] = 3
v[135,2] = 1
v[136,2] = 3
v[137,2] = 3
v[138,2] = 3
v[139,2] = 1
v[140,2] = 3
v[141,2] = 3
v[142,2] = 7
v[143,2] = 3
v[144,2] = 3
v[145,2] = 1
v[146,2] = 7
v[147,2] = 5
v[148,2] = 1
v[149,2] = 7
v[150,2] = 7
v[151,2] = 5
v[152,2] = 7
v[153,2] = 5
v[154,2] = 1
v[155,2] = 3
v[156,2] = 1
v[157,2] = 7
v[158,2] = 3
v[159,2] = 7
v[160,2] = 3
v[161,2] = 5
v[162,2] = 7
v[163,2] = 3
v[164,2] = 1
v[165,2] = 3
v[166,2] = 3
v[167,2] = 3
v[168,2] = 1
v[169,2] = 5
v[170,2] = 7
v[171,2] = 3
v[172,2] = 3
v[173,2] = 7
v[174,2] = 7
v[175,2] = 7
v[176,2] = 5
v[177,2] = 3
v[178,2] = 1
v[179,2] = 7
v[180,2] = 1
v[181,2] = 3
v[182,2] = 7
v[183,2] = 5
v[184,2] = 3
v[185,2] = 3
v[186,2] = 3
v[187,2] = 7
v[188,2] = 1
v[189,2] = 1
v[190,2] = 3
v[191,2] = 1
v[192,2] = 5
v[193,2] = 7
v[194,2] = 1
v[195,2] = 3
v[196,2] = 5
v[197,2] = 3
v[198,2] = 5
v[199,2] = 3
v[200,2] = 3
v[201,2] = 7
v[202,2] = 5
v[203,2] = 5
v[204,2] = 3
v[205,2] = 3
v[206,2] = 1
v[207,2] = 3
v[208,2] = 7
v[209,2] = 7
v[210,2] = 7
v[211,2] = 1
v[212,2] = 5
v[213,2] = 7
v[214,2] = 1
v[215,2] = 3
v[216,2] = 1
v[217,2] = 1
v[218,2] = 7
v[219,2] = 1
v[220,2] = 3
v[221,2] = 1
v[222,2] = 7
v[223,2] = 1
v[224,2] = 5
v[225,2] = 3
v[226,2] = 5
v[227,2] = 3
v[228,2] = 1
v[229,2] = 1
v[230,2] = 5
v[231,2] = 5
v[232,2] = 3
v[233,2] = 3
v[234,2] = 5
v[235,2] = 7
v[236,2] = 1
v[237,2] = 5
v[238,2] = 3
v[239,2] = 7
v[240,2] = 7
v[241,2] = 3
v[242,2] = 5
v[243,2] = 3
v[244,2] = 3
v[245,2] = 1
v[246,2] = 7
v[247,2] = 3
v[248,2] = 1
v[249,2] = 3
v[250,2] = 5
v[251,2] = 7
v[252,2] = 1
v[253,2] = 3
v[254,2] = 7
v[255,2] = 1
v[256,2] = 5
v[257,2] = 1
v[258,2] = 3
v[259,2] = 1
v[260,2] = 5
v[261,2] = 3
v[262,2] = 1
v[263,2] = 7
v[264,2] = 1
v[265,2] = 5
v[266,2] = 5
v[267,2] = 5
v[268,2] = 3
v[269,2] = 7
v[270,2] = 1
v[271,2] = 1
v[272,2] = 7
v[273,2] = 3
v[274,2] = 1
v[275,2] = 1
v[276,2] = 7
v[277,2] = 5
v[278,2] = 7
v[279,2] = 5
v[280,2] = 7
v[281,2] = 7
v[282,2] = 3
v[283,2] = 7
v[284,2] = 1
v[285,2] = 3
v[286,2] = 7
v[287,2] = 7
v[288,2] = 3
v[289,2] = 5
v[290,2] = 1
v[291,2] = 1
v[292,2] = 7
v[293,2] = 1
v[294,2] = 5
v[295,2] = 5
v[296,2] = 5
v[297,2] = 1
v[298,2] = 5
v[299,2] = 1
v[300,2] = 7
v[301,2] = 5
v[302,2] = 5
v[303,2] = 7
v[304,2] = 1
v[305,2] = 1
v[306,2] = 7
v[307,2] = 1
v[308,2] = 7
v[309,2] = 7
v[310,2] = 1
v[311,2] = 1
v[312,2] = 3
v[313,2] = 3
v[314,2] = 3
v[315,2] = 7
v[316,2] = 7
v[317,2] = 5
v[318,2] = 3
v[319,2] = 7
v[320,2] = 3
v[321,2] = 1
v[322,2] = 3
v[323,2] = 7
v[324,2] = 5
v[325,2] = 3
v[326,2] = 3
v[327,2] = 5
v[328,2] = 7
v[329,2] = 1
v[330,2] = 1
v[331,2] = 5
v[332,2] = 5
v[333,2] = 7
v[334,2] = 7
v[335,2] = 1
v[336,2] = 1
v[337,2] = 1
v[338,2] = 1
v[339,2] = 5
v[340,2] = 5
v[341,2] = 5
v[342,2] = 7
v[343,2] = 5
v[344,2] = 7
v[345,2] = 1
v[346,2] = 1
v[347,2] = 3
v[348,2] = 5
v[349,2] = 1
v[350,2] = 3
v[351,2] = 3
v[352,2] = 7
v[353,2] = 3
v[354,2] = 7
v[355,2] = 5
v[356,2] = 3
v[357,2] = 5
v[358,2] = 3
v[359,2] = 1
v[360,2] = 7
v[361,2] = 1
v[362,2] = 7
v[363,2] = 7
v[364,2] = 1
v[365,2] = 1
v[366,2] = 7
v[367,2] = 7
v[368,2] = 7
v[369,2] = 5
v[370,2] = 5
v[371,2] = 1
v[372,2] = 1
v[373,2] = 7
v[374,2] = 5
v[375,2] = 5
v[376,2] = 7
v[377,2] = 5
v[378,2] = 1
v[379,2] = 1
v[380,2] = 5
v[381,2] = 5
v[382,2] = 5
v[383,2] = 5
v[384,2] = 5
v[385,2] = 5
v[386,2] = 1
v[387,2] = 3
v[388,2] = 1
v[389,2] = 5
v[390,2] = 7
v[391,2] = 3
v[392,2] = 3
v[393,2] = 5
v[394,2] = 7
v[395,2] = 3
v[396,2] = 7
v[397,2] = 1
v[398,2] = 7
v[399,2] = 7
v[400,2] = 1
v[401,2] = 3
v[402,2] = 5
v[403,2] = 1
v[404,2] = 5
v[405,2] = 5
v[406,2] = 3
v[407,2] = 7
v[408,2] = 3
v[409,2] = 7
v[410,2] = 7
v[411,2] = 5
v[412,2] = 7
v[413,2] = 5
v[414,2] = 7
v[415,2] = 1
v[416,2] = 1
v[417,2] = 5
v[418,2] = 3
v[419,2] = 5
v[420,2] = 1
v[421,2] = 5
v[422,2] = 3
v[423,2] = 7
v[424,2] = 1
v[425,2] = 5
v[426,2] = 7
v[427,2] = 7
v[428,2] = 3
v[429,2] = 5
v[430,2] = 1
v[431,2] = 3
v[432,2] = 5
v[433,2] = 1
v[434,2] = 5
v[435,2] = 3
v[436,2] = 3
v[437,2] = 3
v[438,2] = 7
v[439,2] = 3
v[440,2] = 5
v[441,2] = 1
v[442,2] = 3
v[443,2] = 7
v[444,2] = 7
v[445,2] = 3
v[446,2] = 7
v[447,2] = 5
v[448,2] = 3
v[449,2] = 3
v[450,2] = 1
v[451,2] = 7
v[452,2] = 5
v[453,2] = 1
v[454,2] = 1
v[455,2] = 3
v[456,2] = 7
v[457,2] = 1
v[458,2] = 7
v[459,2] = 1
v[460,2] = 7
v[461,2] = 3
v[462,2] = 7
v[463,2] = 3
v[464,2] = 5
v[465,2] = 7
v[466,2] = 3
v[467,2] = 5
v[468,2] = 3
v[469,2] = 1
v[470,2] = 1
v[471,2] = 1
v[472,2] = 5
v[473,2] = 7
v[474,2] = 7
v[475,2] = 3
v[476,2] = 3
v[477,2] = 1
v[478,2] = 1
v[479,2] = 1
v[480,2] = 5
v[481,2] = 5
v[482,2] = 7
v[483,2] = 3
v[484,2] = 1
v[485,2] = 1
v[486,2] = 3
v[487,2] = 3
v[488,2] = 7
v[489,2] = 3
v[490,2] = 3
v[491,2] = 5
v[492,2] = 1
v[493,2] = 3
v[494,2] = 7
v[495,2] = 3
v[496,2] = 3
v[497,2] = 7
v[498,2] = 3
v[499,2] = 5
v[500,2] = 7
v[501,2] = 5
v[502,2] = 7
v[503,2] = 7
v[504,2] = 3
v[505,2] = 3
v[506,2] = 5
v[507,2] = 1
v[508,2] = 3
v[509,2] = 5
v[510,2] = 3
v[511,2] = 1
v[512,2] = 3
v[513,2] = 5
v[514,2] = 1
v[515,2] = 1
v[516,2] = 3
v[517,2] = 7
v[518,2] = 7
v[519,2] = 1
v[520,2] = 5
v[521,2] = 1
v[522,2] = 3
v[523,2] = 7
v[524,2] = 3
v[525,2] = 7
v[526,2] = 3
v[527,2] = 5
v[528,2] = 1
v[529,2] = 7
v[530,2] = 1
v[531,2] = 1
v[532,2] = 3
v[533,2] = 5
v[534,2] = 3
v[535,2] = 7
v[536,2] = 1
v[537,2] = 5
v[538,2] = 5
v[539,2] = 1
v[540,2] = 1
v[541,2] = 3
v[542,2] = 1
v[543,2] = 3
v[544,2] = 3
v[545,2] = 7
v[546,2] = 1
v[547,2] = 7
v[548,2] = 3
v[549,2] = 1
v[550,2] = 7
v[551,2] = 3
v[552,2] = 1
v[553,2] = 7
v[554,2] = 3
v[555,2] = 5
v[556,2] = 3
v[557,2] = 5
v[558,2] = 7
v[559,2] = 3
v[560,2] = 3
v[561,2] = 3
v[562,2] = 5
v[563,2] = 1
v[564,2] = 7
v[565,2] = 7
v[566,2] = 1
v[567,2] = 3
v[568,2] = 1
v[569,2] = 3
v[570,2] = 7
v[571,2] = 7
v[572,2] = 1
v[573,2] = 3
v[574,2] = 7
v[575,2] = 3
v[576,2] = 1
v[577,2] = 5
v[578,2] = 3
v[579,2] = 1
v[580,2] = 1
v[581,2] = 1
v[582,2] = 5
v[583,2] = 3
v[584,2] = 3
v[585,2] = 7
v[586,2] = 1
v[587,2] = 5
v[588,2] = 3
v[589,2] = 5
v[590,2] = 1
v[591,2] = 3
v[592,2] = 1
v[593,2] = 3
v[594,2] = 1
v[595,2] = 5
v[596,2] = 7
v[597,2] = 7
v[598,2] = 1
v[599,2] = 1
v[600,2] = 5
v[601,2] = 3
v[602,2] = 1
v[603,2] = 5
v[604,2] = 1
v[605,2] = 1
v[606,2] = 7
v[607,2] = 7
v[608,2] = 3
v[609,2] = 5
v[610,2] = 5
v[611,2] = 1
v[612,2] = 7
v[613,2] = 1
v[614,2] = 5
v[615,2] = 1
v[616,2] = 1
v[617,2] = 3
v[618,2] = 1
v[619,2] = 5
v[620,2] = 7
v[621,2] = 5
v[622,2] = 7
v[623,2] = 7
v[624,2] = 1
v[625,2] = 5
v[626,2] = 1
v[627,2] = 1
v[628,2] = 3
v[629,2] = 5
v[630,2] = 1
v[631,2] = 5
v[632,2] = 5
v[633,2] = 3
v[634,2] = 1
v[635,2] = 3
v[636,2] = 1
v[637,2] = 5
v[638,2] = 5
v[639,2] = 3
v[640,2] = 3
v[641,2] = 3
v[642,2] = 3
v[643,2] = 1
v[644,2] = 1
v[645,2] = 3
v[646,2] = 1
v[647,2] = 3
v[648,2] = 5
v[649,2] = 5
v[650,2] = 7
v[651,2] = 5
v[652,2] = 5
v[653,2] = 7
v[654,2] = 5
v[655,2] = 7
v[656,2] = 1
v[657,2] = 3
v[658,2] = 7
v[659,2] = 7
v[660,2] = 3
v[661,2] = 5
v[662,2] = 5
v[663,2] = 7
v[664,2] = 5
v[665,2] = 5
v[666,2] = 3
v[667,2] = 3
v[668,2] = 3
v[669,2] = 1
v[670,2] = 7
v[671,2] = 1
v[672,2] = 5
v[673,2] = 5
v[674,2] = 5
v[675,2] = 3
v[676,2] = 3
v[677,2] = 5
v[678,2] = 1
v[679,2] = 3
v[680,2] = 1
v[681,2] = 3
v[682,2] = 3
v[683,2] = 3
v[684,2] = 7
v[685,2] = 1
v[686,2] = 7
v[687,2] = 7
v[688,2] = 3
v[689,2] = 7
v[690,2] = 1
v[691,2] = 1
v[692,2] = 5
v[693,2] = 7
v[694,2] = 1
v[695,2] = 7
v[696,2] = 1
v[697,2] = 7
v[698,2] = 7
v[699,2] = 1
v[700,2] = 3
v[701,2] = 7
v[702,2] = 5
v[703,2] = 1
v[704,2] = 3
v[705,2] = 5
v[706,2] = 5
v[707,2] = 5
v[708,2] = 1
v[709,2] = 1
v[710,2] = 7
v[711,2] = 1
v[712,2] = 7
v[713,2] = 1
v[714,2] = 7
v[715,2] = 7
v[716,2] = 3
v[717,2] = 1
v[718,2] = 1
v[719,2] = 5
v[720,2] = 1
v[721,2] = 5
v[722,2] = 1
v[723,2] = 5
v[724,2] = 3
v[725,2] = 5
v[726,2] = 5
v[727,2] = 5
v[728,2] = 5
v[729,2] = 5
v[730,2] = 3
v[731,2] = 3
v[732,2] = 7
v[733,2] = 3
v[734,2] = 3
v[735,2] = 5
v[736,2] = 5
v[737,2] = 3
v[738,2] = 7
v[739,2] = 1
v[740,2] = 5
v[741,2] = 7
v[742,2] = 5
v[743,2] = 1
v[744,2] = 5
v[745,2] = 5
v[746,2] = 3
v[747,2] = 5
v[748,2] = 5
v[749,2] = 7
v[750,2] = 5
v[751,2] = 3
v[752,2] = 5
v[753,2] = 5
v[754,2] = 5
v[755,2] = 1
v[756,2] = 5
v[757,2] = 5
v[758,2] = 5
v[759,2] = 5
v[760,2] = 1
v[761,2] = 3
v[762,2] = 5
v[763,2] = 3
v[764,2] = 1
v[765,2] = 7
v[766,2] = 5
v[767,2] = 5
v[768,2] = 7
v[769,2] = 1
v[770,2] = 5
v[771,2] = 3
v[772,2] = 3
v[773,2] = 1
v[774,2] = 5
v[775,2] = 3
v[776,2] = 7
v[777,2] = 1
v[778,2] = 7
v[779,2] = 5
v[780,2] = 1
v[781,2] = 1
v[782,2] = 3
v[783,2] = 1
v[784,2] = 1
v[785,2] = 7
v[786,2] = 1
v[787,2] = 5
v[788,2] = 5
v[789,2] = 3
v[790,2] = 7
v[791,2] = 3
v[792,2] = 7
v[793,2] = 5
v[794,2] = 3
v[795,2] = 1
v[796,2] = 1
v[797,2] = 3
v[798,2] = 1
v[799,2] = 3
v[800,2] = 5
v[801,2] = 5
v[802,2] = 7
v[803,2] = 5
v[804,2] = 3
v[805,2] = 7
v[806,2] = 7
v[807,2] = 7
v[808,2] = 3
v[809,2] = 7
v[810,2] = 3
v[811,2] = 7
v[812,2] = 1
v[813,2] = 3
v[814,2] = 1
v[815,2] = 7
v[816,2] = 7
v[817,2] = 1
v[818,2] = 7
v[819,2] = 3
v[820,2] = 7
v[821,2] = 3
v[822,2] = 7
v[823,2] = 3
v[824,2] = 7
v[825,2] = 3
v[826,2] = 5
v[827,2] = 1
v[828,2] = 1
v[829,2] = 7
v[830,2] = 3
v[831,2] = 1
v[832,2] = 5
v[833,2] = 5
v[834,2] = 7
v[835,2] = 1
v[836,2] = 5
v[837,2] = 5
v[838,2] = 5
v[839,2] = 7
v[840,2] = 1
v[841,2] = 5
v[842,2] = 5
v[843,2] = 1
v[844,2] = 5
v[845,2] = 5
v[846,2] = 3
v[847,2] = 1
v[848,2] = 3
v[849,2] = 1
v[850,2] = 7
v[851,2] = 3
v[852,2] = 1
v[853,2] = 3
v[854,2] = 5
v[855,2] = 7
v[856,2] = 7
v[857,2] = 7
v[858,2] = 1
v[859,2] = 1
v[860,2] = 7
v[861,2] = 3
v[862,2] = 1
v[863,2] = 5
v[864,2] = 5
v[865,2] = 5
v[866,2] = 1
v[867,2] = 1
v[868,2] = 1
v[869,2] = 1
v[870,2] = 1
v[871,2] = 5
v[872,2] = 3
v[873,2] = 5
v[874,2] = 1
v[875,2] = 3
v[876,2] = 5
v[877,2] = 3
v[878,2] = 1
v[879,2] = 1
v[880,2] = 1
v[881,2] = 1
v[882,2] = 3
v[883,2] = 7
v[884,2] = 3
v[885,2] = 7
v[886,2] = 5
v[887,2] = 7
v[888,2] = 1
v[889,2] = 5
v[890,2] = 5
v[891,2] = 7
v[892,2] = 5
v[893,2] = 3
v[894,2] = 3
v[895,2] = 7
v[896,2] = 5
v[897,2] = 3
v[898,2] = 1
v[899,2] = 1
v[900,2] = 3
v[901,2] = 1
v[902,2] = 3
v[903,2] = 1
v[904,2] = 1
v[905,2] = 3
v[906,2] = 7
v[907,2] = 1
v[908,2] = 7
v[909,2] = 1
v[910,2] = 1
v[911,2] = 5
v[912,2] = 1
v[913,2] = 7
v[914,2] = 5
v[915,2] = 3
v[916,2] = 7
v[917,2] = 3
v[918,2] = 5
v[919,2] = 3
v[920,2] = 1
v[921,2] = 1
v[922,2] = 5
v[923,2] = 5
v[924,2] = 1
v[925,2] = 7
v[926,2] = 7
v[927,2] = 3
v[928,2] = 7
v[929,2] = 3
v[930,2] = 7
v[931,2] = 1
v[932,2] = 5
v[933,2] = 1
v[934,2] = 5
v[935,2] = 3
v[936,2] = 7
v[937,2] = 3
v[938,2] = 5
v[939,2] = 7
v[940,2] = 7
v[941,2] = 7
v[942,2] = 3
v[943,2] = 3
v[944,2] = 1
v[945,2] = 1
v[946,2] = 5
v[947,2] = 5
v[948,2] = 3
v[949,2] = 7
v[950,2] = 1
v[951,2] = 1
v[952,2] = 1
v[953,2] = 3
v[954,2] = 5
v[955,2] = 3
v[956,2] = 1
v[957,2] = 1
v[958,2] = 3
v[959,2] = 3
v[960,2] = 7
v[961,2] = 5
v[962,2] = 1
v[963,2] = 1
v[964,2] = 3
v[965,2] = 7
v[966,2] = 1
v[967,2] = 5
v[968,2] = 7
v[969,2] = 3
v[970,2] = 7
v[971,2] = 5
v[972,2] = 5
v[973,2] = 7
v[974,2] = 3
v[975,2] = 5
v[976,2] = 3
v[977,2] = 1
v[978,2] = 5
v[979,2] = 3
v[980,2] = 1
v[981,2] = 1
v[982,2] = 7
v[983,2] = 5
v[984,2] = 1
v[985,2] = 7
v[986,2] = 3
v[987,2] = 7
v[988,2] = 5
v[989,2] = 1
v[990,2] = 7
v[991,2] = 1
v[992,2] = 7
v[993,2] = 7
v[994,2] = 1
v[995,2] = 1
v[996,2] = 7
v[997,2] = 1
v[998,2] = 5
v[999,2] = 5
v[1000,2] = 1
v[1001,2] = 1
v[1002,2] = 7
v[1003,2] = 5
v[1004,2] = 7
v[1005,2] = 1
v[1006,2] = 5
v[1007,2] = 3
v[1008,2] = 5
v[1009,2] = 3
v[1010,2] = 3
v[1011,2] = 7
v[1012,2] = 1
v[1013,2] = 5
v[1014,2] = 1
v[1015,2] = 1
v[1016,2] = 5
v[1017,2] = 5
v[1018,2] = 3
v[1019,2] = 3
v[1020,2] = 7
v[1021,2] = 5
v[1022,2] = 5
v[1023,2] = 1
v[1024,2] = 1
v[1025,2] = 1
v[1026,2] = 3
v[1027,2] = 1
v[1028,2] = 5
v[1029,2] = 7
v[1030,2] = 7
v[1031,2] = 1
v[1032,2] = 7
v[1033,2] = 5
v[1034,2] = 7
v[1035,2] = 3
v[1036,2] = 7
v[1037,2] = 3
v[1038,2] = 1
v[1039,2] = 3
v[1040,2] = 7
v[1041,2] = 3
v[1042,2] = 1
v[1043,2] = 5
v[1044,2] = 5
v[1045,2] = 3
v[1046,2] = 5
v[1047,2] = 1
v[1048,2] = 3
v[1049,2] = 5
v[1050,2] = 5
v[1051,2] = 5
v[1052,2] = 1
v[1053,2] = 1
v[1054,2] = 7
v[1055,2] = 7
v[1056,2] = 1
v[1057,2] = 5
v[1058,2] = 5
v[1059,2] = 1
v[1060,2] = 3
v[1061,2] = 5
v[1062,2] = 1
v[1063,2] = 5
v[1064,2] = 3
v[1065,2] = 5
v[1066,2] = 3
v[1067,2] = 3
v[1068,2] = 7
v[1069,2] = 5
v[1070,2] = 7
v[1071,2] = 3
v[1072,2] = 7
v[1073,2] = 3
v[1074,2] = 1
v[1075,2] = 3
v[1076,2] = 7
v[1077,2] = 7
v[1078,2] = 3
v[1079,2] = 3
v[1080,2] = 1
v[1081,2] = 1
v[1082,2] = 3
v[1083,2] = 3
v[1084,2] = 3
v[1085,2] = 3
v[1086,2] = 3
v[1087,2] = 5
v[1088,2] = 5
v[1089,2] = 3
v[1090,2] = 3
v[1091,2] = 3
v[1092,2] = 1
v[1093,2] = 3
v[1094,2] = 5
v[1095,2] = 7
v[1096,2] = 7
v[1097,2] = 1
v[1098,2] = 5
v[1099,2] = 7
v[1100,2] = 3
v[1101,2] = 7
v[1102,2] = 1
v[1103,2] = 1
v[1104,2] = 3
v[1105,2] = 5
v[1106,2] = 7
v[1107,2] = 5
v[1108,2] = 3
v[1109,2] = 3
v[1110,2] = 3
v[5,3] = 1
v[6,3] = 7
v[7,3] = 9
v[8,3] = 13
v[9,3] = 11
v[10,3] = 1
v[11,3] = 3
v[12,3] = 7
v[13,3] = 9
v[14,3] = 5
v[15,3] = 13
v[16,3] = 13
v[17,3] = 11
v[18,3] = 3
v[19,3] = 15
v[20,3] = 5
v[21,3] = 3
v[22,3] = 15
v[23,3] = 7
v[24,3] = 9
v[25,3] = 13
v[26,3] = 9
v[27,3] = 1
v[28,3] = 11
v[29,3] = 7
v[30,3] = 5
v[31,3] = 15
v[32,3] = 1
v[33,3] = 15
v[34,3] = 11
v[35,3] = 5
v[36,3] = 11
v[37,3] = 1
v[38,3] = 7
v[39,3] = 9
v[40,3] = 7
v[41,3] = 7
v[42,3] = 1
v[43,3] = 15
v[44,3] = 15
v[45,3] = 15
v[46,3] = 13
v[47,3] = 3
v[48,3] = 3
v[49,3] = 15
v[50,3] = 5
v[51,3] = 9
v[52,3] = 7
v[53,3] = 13
v[54,3] = 3
v[55,3] = 7
v[56,3] = 5
v[57,3] = 11
v[58,3] = 9
v[59,3] = 1
v[60,3] = 9
v[61,3] = 1
v[62,3] = 5
v[63,3] = 7
v[64,3] = 13
v[65,3] = 9
v[66,3] = 9
v[67,3] = 1
v[68,3] = 7
v[69,3] = 3
v[70,3] = 5
v[71,3] = 1
v[72,3] = 11
v[73,3] = 11
v[74,3] = 13
v[75,3] = 7
v[76,3] = 7
v[77,3] = 9
v[78,3] = 9
v[79,3] = 1
v[80,3] = 1
v[81,3] = 3
v[82,3] = 9
v[83,3] = 15
v[84,3] = 1
v[85,3] = 5
v[86,3] = 13
v[87,3] = 1
v[88,3] = 9
v[89,3] = 9
v[90,3] = 9
v[91,3] = 9
v[92,3] = 9
v[93,3] = 13
v[94,3] = 11
v[95,3] = 3
v[96,3] = 5
v[97,3] = 11
v[98,3] = 11
v[99,3] = 13
v[100,3] = 5
v[101,3] = 3
v[102,3] = 15
v[103,3] = 1
v[104,3] = 11
v[105,3] = 11
v[106,3] = 7
v[107,3] = 13
v[108,3] = 15
v[109,3] = 11
v[110,3] = 13
v[111,3] = 9
v[112,3] = 11
v[113,3] = 15
v[114,3] = 15
v[115,3] = 13
v[116,3] = 3
v[117,3] = 15
v[118,3] = 7
v[119,3] = 9
v[120,3] = 11
v[121,3] = 13
v[122,3] = 11
v[123,3] = 9
v[124,3] = 9
v[125,3] = 5
v[126,3] = 13
v[127,3] = 9
v[128,3] = 1
v[129,3] = 13
v[130,3] = 7
v[131,3] = 7
v[132,3] = 7
v[133,3] = 7
v[134,3] = 7
v[135,3] = 5
v[136,3] = 9
v[137,3] = 7
v[138,3] = 13
v[139,3] = 11
v[140,3] = 9
v[141,3] = 11
v[142,3] = 15
v[143,3] = 3
v[144,3] = 13
v[145,3] = 11
v[146,3] = 1
v[147,3] = 11
v[148,3] = 3
v[149,3] = 3
v[150,3] = 9
v[151,3] = 11
v[152,3] = 1
v[153,3] = 7
v[154,3] = 1
v[155,3] = 15
v[156,3] = 15
v[157,3] = 3
v[158,3] = 1
v[159,3] = 9
v[160,3] = 1
v[161,3] = 7
v[162,3] = 13
v[163,3] = 11
v[164,3] = 3
v[165,3] = 13
v[166,3] = 11
v[167,3] = 7
v[168,3] = 3
v[169,3] = 3
v[170,3] = 5
v[171,3] = 13
v[172,3] = 11
v[173,3] = 5
v[174,3] = 11
v[175,3] = 1
v[176,3] = 3
v[177,3] = 9
v[178,3] = 7
v[179,3] = 15
v[180,3] = 7
v[181,3] = 5
v[182,3] = 13
v[183,3] = 7
v[184,3] = 9
v[185,3] = 13
v[186,3] = 15
v[187,3] = 13
v[188,3] = 9
v[189,3] = 7
v[190,3] = 15
v[191,3] = 7
v[192,3] = 9
v[193,3] = 5
v[194,3] = 11
v[195,3] = 11
v[196,3] = 13
v[197,3] = 13
v[198,3] = 9
v[199,3] = 3
v[200,3] = 5
v[201,3] = 13
v[202,3] = 9
v[203,3] = 11
v[204,3] = 15
v[205,3] = 11
v[206,3] = 7
v[207,3] = 1
v[208,3] = 7
v[209,3] = 13
v[210,3] = 3
v[211,3] = 13
v[212,3] = 3
v[213,3] = 13
v[214,3] = 9
v[215,3] = 15
v[216,3] = 7
v[217,3] = 13
v[218,3] = 13
v[219,3] = 3
v[220,3] = 13
v[221,3] = 15
v[222,3] = 15
v[223,3] = 11
v[224,3] = 9
v[225,3] = 13
v[226,3] = 9
v[227,3] = 15
v[228,3] = 1
v[229,3] = 1
v[230,3] = 15
v[231,3] = 11
v[232,3] = 11
v[233,3] = 7
v[234,3] = 1
v[235,3] = 11
v[236,3] = 13
v[237,3] = 9
v[238,3] = 13
v[239,3] = 3
v[240,3] = 5
v[241,3] = 11
v[242,3] = 13
v[243,3] = 9
v[244,3] = 9
v[245,3] = 13
v[246,3] = 1
v[247,3] = 11
v[248,3] = 15
v[249,3] = 13
v[250,3] = 3
v[251,3] = 13
v[252,3] = 7
v[253,3] = 15
v[254,3] = 1
v[255,3] = 15
v[256,3] = 3
v[257,3] = 3
v[258,3] = 11
v[259,3] = 7
v[260,3] = 13
v[261,3] = 7
v[262,3] = 7
v[263,3] = 9
v[264,3] = 7
v[265,3] = 5
v[266,3] = 15
v[267,3] = 9
v[268,3] = 5
v[269,3] = 5
v[270,3] = 7
v[271,3] = 15
v[272,3] = 13
v[273,3] = 15
v[274,3] = 5
v[275,3] = 15
v[276,3] = 5
v[277,3] = 3
v[278,3] = 1
v[279,3] = 11
v[280,3] = 7
v[281,3] = 1
v[282,3] = 5
v[283,3] = 7
v[284,3] = 9
v[285,3] = 3
v[286,3] = 11
v[287,3] = 1
v[288,3] = 15
v[289,3] = 1
v[290,3] = 3
v[291,3] = 15
v[292,3] = 11
v[293,3] = 13
v[294,3] = 5
v[295,3] = 13
v[296,3] = 1
v[297,3] = 7
v[298,3] = 1
v[299,3] = 15
v[300,3] = 7
v[301,3] = 5
v[302,3] = 1
v[303,3] = 1
v[304,3] = 15
v[305,3] = 13
v[306,3] = 11
v[307,3] = 11
v[308,3] = 13
v[309,3] = 5
v[310,3] = 11
v[311,3] = 7
v[312,3] = 9
v[313,3] = 7
v[314,3] = 1
v[315,3] = 5
v[316,3] = 3
v[317,3] = 9
v[318,3] = 5
v[319,3] = 5
v[320,3] = 11
v[321,3] = 5
v[322,3] = 1
v[323,3] = 7
v[324,3] = 1
v[325,3] = 11
v[326,3] = 7
v[327,3] = 9
v[328,3] = 13
v[329,3] = 15
v[330,3] = 13
v[331,3] = 3
v[332,3] = 1
v[333,3] = 11
v[334,3] = 13
v[335,3] = 15
v[336,3] = 1
v[337,3] = 1
v[338,3] = 11
v[339,3] = 9
v[340,3] = 13
v[341,3] = 3
v[342,3] = 13
v[343,3] = 11
v[344,3] = 15
v[345,3] = 13
v[346,3] = 9
v[347,3] = 9
v[348,3] = 9
v[349,3] = 5
v[350,3] = 5
v[351,3] = 5
v[352,3] = 5
v[353,3] = 1
v[354,3] = 15
v[355,3] = 5
v[356,3] = 9
v[357,3] = 11
v[358,3] = 7
v[359,3] = 15
v[360,3] = 5
v[361,3] = 3
v[362,3] = 13
v[363,3] = 5
v[364,3] = 3
v[365,3] = 11
v[366,3] = 5
v[367,3] = 1
v[368,3] = 11
v[369,3] = 13
v[370,3] = 9
v[371,3] = 11
v[372,3] = 3
v[373,3] = 7
v[374,3] = 13
v[375,3] = 15
v[376,3] = 1
v[377,3] = 7
v[378,3] = 11
v[379,3] = 1
v[380,3] = 13
v[381,3] = 1
v[382,3] = 15
v[383,3] = 1
v[384,3] = 9
v[385,3] = 7
v[386,3] = 3
v[387,3] = 9
v[388,3] = 11
v[389,3] = 1
v[390,3] = 9
v[391,3] = 13
v[392,3] = 13
v[393,3] = 3
v[394,3] = 11
v[395,3] = 7
v[396,3] = 9
v[397,3] = 1
v[398,3] = 7
v[399,3] = 15
v[400,3] = 9
v[401,3] = 1
v[402,3] = 5
v[403,3] = 13
v[404,3] = 5
v[405,3] = 11
v[406,3] = 3
v[407,3] = 9
v[408,3] = 15
v[409,3] = 11
v[410,3] = 13
v[411,3] = 5
v[412,3] = 1
v[413,3] = 7
v[414,3] = 7
v[415,3] = 5
v[416,3] = 13
v[417,3] = 7
v[418,3] = 7
v[419,3] = 9
v[420,3] = 5
v[421,3] = 11
v[422,3] = 11
v[423,3] = 1
v[424,3] = 1
v[425,3] = 15
v[426,3] = 3
v[427,3] = 13
v[428,3] = 9
v[429,3] = 13
v[430,3] = 9
v[431,3] = 9
v[432,3] = 11
v[433,3] = 5
v[434,3] = 5
v[435,3] = 13
v[436,3] = 15
v[437,3] = 3
v[438,3] = 9
v[439,3] = 15
v[440,3] = 3
v[441,3] = 11
v[442,3] = 11
v[443,3] = 15
v[444,3] = 15
v[445,3] = 3
v[446,3] = 11
v[447,3] = 15
v[448,3] = 15
v[449,3] = 3
v[450,3] = 1
v[451,3] = 3
v[452,3] = 1
v[453,3] = 3
v[454,3] = 3
v[455,3] = 1
v[456,3] = 3
v[457,3] = 13
v[458,3] = 1
v[459,3] = 11
v[460,3] = 5
v[461,3] = 15
v[462,3] = 7
v[463,3] = 15
v[464,3] = 9
v[465,3] = 1
v[466,3] = 7
v[467,3] = 1
v[468,3] = 9
v[469,3] = 11
v[470,3] = 15
v[471,3] = 1
v[472,3] = 13
v[473,3] = 9
v[474,3] = 13
v[475,3] = 11
v[476,3] = 7
v[477,3] = 3
v[478,3] = 7
v[479,3] = 3
v[480,3] = 13
v[481,3] = 7
v[482,3] = 9
v[483,3] = 7
v[484,3] = 7
v[485,3] = 3
v[486,3] = 3
v[487,3] = 9
v[488,3] = 9
v[489,3] = 7
v[490,3] = 5
v[491,3] = 11
v[492,3] = 13
v[493,3] = 13
v[494,3] = 7
v[495,3] = 7
v[496,3] = 15
v[497,3] = 9
v[498,3] = 5
v[499,3] = 5
v[500,3] = 3
v[501,3] = 3
v[502,3] = 13
v[503,3] = 3
v[504,3] = 9
v[505,3] = 3
v[506,3] = 1
v[507,3] = 11
v[508,3] = 1
v[509,3] = 3
v[510,3] = 11
v[511,3] = 15
v[512,3] = 11
v[513,3] = 11
v[514,3] = 11
v[515,3] = 9
v[516,3] = 13
v[517,3] = 7
v[518,3] = 9
v[519,3] = 15
v[520,3] = 9
v[521,3] = 11
v[522,3] = 1
v[523,3] = 3
v[524,3] = 3
v[525,3] = 9
v[526,3] = 7
v[527,3] = 15
v[528,3] = 13
v[529,3] = 13
v[530,3] = 7
v[531,3] = 15
v[532,3] = 9
v[533,3] = 13
v[534,3] = 9
v[535,3] = 15
v[536,3] = 13
v[537,3] = 15
v[538,3] = 9
v[539,3] = 13
v[540,3] = 1
v[541,3] = 11
v[542,3] = 7
v[543,3] = 11
v[544,3] = 3
v[545,3] = 13
v[546,3] = 5
v[547,3] = 1
v[548,3] = 7
v[549,3] = 15
v[550,3] = 3
v[551,3] = 13
v[552,3] = 7
v[553,3] = 13
v[554,3] = 13
v[555,3] = 11
v[556,3] = 3
v[557,3] = 5
v[558,3] = 3
v[559,3] = 13
v[560,3] = 11
v[561,3] = 9
v[562,3] = 9
v[563,3] = 3
v[564,3] = 11
v[565,3] = 11
v[566,3] = 7
v[567,3] = 9
v[568,3] = 13
v[569,3] = 11
v[570,3] = 7
v[571,3] = 15
v[572,3] = 13
v[573,3] = 7
v[574,3] = 5
v[575,3] = 3
v[576,3] = 1
v[577,3] = 5
v[578,3] = 15
v[579,3] = 15
v[580,3] = 3
v[581,3] = 11
v[582,3] = 1
v[583,3] = 7
v[584,3] = 3
v[585,3] = 15
v[586,3] = 11
v[587,3] = 5
v[588,3] = 5
v[589,3] = 3
v[590,3] = 5
v[591,3] = 5
v[592,3] = 1
v[593,3] = 15
v[594,3] = 5
v[595,3] = 1
v[596,3] = 5
v[597,3] = 3
v[598,3] = 7
v[599,3] = 5
v[600,3] = 11
v[601,3] = 3
v[602,3] = 13
v[603,3] = 9
v[604,3] = 13
v[605,3] = 15
v[606,3] = 5
v[607,3] = 3
v[608,3] = 5
v[609,3] = 9
v[610,3] = 5
v[611,3] = 3
v[612,3] = 11
v[613,3] = 1
v[614,3] = 13
v[615,3] = 9
v[616,3] = 15
v[617,3] = 3
v[618,3] = 5
v[619,3] = 11
v[620,3] = 9
v[621,3] = 1
v[622,3] = 3
v[623,3] = 15
v[624,3] = 9
v[625,3] = 9
v[626,3] = 9
v[627,3] = 11
v[628,3] = 7
v[629,3] = 5
v[630,3] = 13
v[631,3] = 1
v[632,3] = 15
v[633,3] = 3
v[634,3] = 13
v[635,3] = 9
v[636,3] = 13
v[637,3] = 5
v[638,3] = 1
v[639,3] = 5
v[640,3] = 1
v[641,3] = 13
v[642,3] = 13
v[643,3] = 7
v[644,3] = 7
v[645,3] = 1
v[646,3] = 9
v[647,3] = 5
v[648,3] = 11
v[649,3] = 9
v[650,3] = 11
v[651,3] = 13
v[652,3] = 3
v[653,3] = 15
v[654,3] = 15
v[655,3] = 13
v[656,3] = 15
v[657,3] = 7
v[658,3] = 5
v[659,3] = 7
v[660,3] = 9
v[661,3] = 7
v[662,3] = 9
v[663,3] = 9
v[664,3] = 9
v[665,3] = 11
v[666,3] = 9
v[667,3] = 3
v[668,3] = 11
v[669,3] = 15
v[670,3] = 13
v[671,3] = 13
v[672,3] = 5
v[673,3] = 9
v[674,3] = 15
v[675,3] = 1
v[676,3] = 1
v[677,3] = 9
v[678,3] = 5
v[679,3] = 13
v[680,3] = 3
v[681,3] = 13
v[682,3] = 15
v[683,3] = 3
v[684,3] = 1
v[685,3] = 3
v[686,3] = 11
v[687,3] = 13
v[688,3] = 1
v[689,3] = 15
v[690,3] = 9
v[691,3] = 9
v[692,3] = 3
v[693,3] = 1
v[694,3] = 9
v[695,3] = 1
v[696,3] = 9
v[697,3] = 1
v[698,3] = 13
v[699,3] = 11
v[700,3] = 15
v[701,3] = 7
v[702,3] = 11
v[703,3] = 15
v[704,3] = 13
v[705,3] = 15
v[706,3] = 1
v[707,3] = 9
v[708,3] = 9
v[709,3] = 7
v[710,3] = 3
v[711,3] = 5
v[712,3] = 11
v[713,3] = 7
v[714,3] = 3
v[715,3] = 9
v[716,3] = 5
v[717,3] = 15
v[718,3] = 7
v[719,3] = 5
v[720,3] = 3
v[721,3] = 13
v[722,3] = 7
v[723,3] = 1
v[724,3] = 1
v[725,3] = 9
v[726,3] = 15
v[727,3] = 15
v[728,3] = 15
v[729,3] = 11
v[730,3] = 3
v[731,3] = 5
v[732,3] = 15
v[733,3] = 13
v[734,3] = 7
v[735,3] = 15
v[736,3] = 15
v[737,3] = 11
v[738,3] = 11
v[739,3] = 9
v[740,3] = 5
v[741,3] = 15
v[742,3] = 9
v[743,3] = 7
v[744,3] = 3
v[745,3] = 13
v[746,3] = 1
v[747,3] = 1
v[748,3] = 5
v[749,3] = 1
v[750,3] = 3
v[751,3] = 1
v[752,3] = 7
v[753,3] = 1
v[754,3] = 1
v[755,3] = 5
v[756,3] = 1
v[757,3] = 11
v[758,3] = 11
v[759,3] = 9
v[760,3] = 9
v[761,3] = 5
v[762,3] = 13
v[763,3] = 7
v[764,3] = 7
v[765,3] = 7
v[766,3] = 1
v[767,3] = 1
v[768,3] = 9
v[769,3] = 9
v[770,3] = 11
v[771,3] = 11
v[772,3] = 15
v[773,3] = 7
v[774,3] = 5
v[775,3] = 5
v[776,3] = 3
v[777,3] = 11
v[778,3] = 1
v[779,3] = 3
v[780,3] = 7
v[781,3] = 13
v[782,3] = 7
v[783,3] = 7
v[784,3] = 7
v[785,3] = 3
v[786,3] = 15
v[787,3] = 15
v[788,3] = 11
v[789,3] = 9
v[790,3] = 3
v[791,3] = 9
v[792,3] = 3
v[793,3] = 15
v[794,3] = 13
v[795,3] = 5
v[796,3] = 3
v[797,3] = 3
v[798,3] = 3
v[799,3] = 5
v[800,3] = 9
v[801,3] = 15
v[802,3] = 9
v[803,3] = 9
v[804,3] = 1
v[805,3] = 5
v[806,3] = 9
v[807,3] = 9
v[808,3] = 15
v[809,3] = 5
v[810,3] = 15
v[811,3] = 7
v[812,3] = 9
v[813,3] = 1
v[814,3] = 9
v[815,3] = 9
v[816,3] = 5
v[817,3] = 11
v[818,3] = 5
v[819,3] = 15
v[820,3] = 15
v[821,3] = 11
v[822,3] = 7
v[823,3] = 7
v[824,3] = 7
v[825,3] = 1
v[826,3] = 1
v[827,3] = 11
v[828,3] = 11
v[829,3] = 13
v[830,3] = 15
v[831,3] = 3
v[832,3] = 13
v[833,3] = 5
v[834,3] = 1
v[835,3] = 7
v[836,3] = 1
v[837,3] = 11
v[838,3] = 3
v[839,3] = 13
v[840,3] = 15
v[841,3] = 3
v[842,3] = 5
v[843,3] = 3
v[844,3] = 5
v[845,3] = 7
v[846,3] = 3
v[847,3] = 9
v[848,3] = 9
v[849,3] = 5
v[850,3] = 1
v[851,3] = 7
v[852,3] = 11
v[853,3] = 9
v[854,3] = 3
v[855,3] = 5
v[856,3] = 11
v[857,3] = 13
v[858,3] = 13
v[859,3] = 13
v[860,3] = 9
v[861,3] = 15
v[862,3] = 5
v[863,3] = 7
v[864,3] = 1
v[865,3] = 15
v[866,3] = 11
v[867,3] = 9
v[868,3] = 15
v[869,3] = 15
v[870,3] = 13
v[871,3] = 13
v[872,3] = 13
v[873,3] = 1
v[874,3] = 11
v[875,3] = 9
v[876,3] = 15
v[877,3] = 9
v[878,3] = 5
v[879,3] = 15
v[880,3] = 5
v[881,3] = 7
v[882,3] = 3
v[883,3] = 11
v[884,3] = 3
v[885,3] = 15
v[886,3] = 7
v[887,3] = 13
v[888,3] = 11
v[889,3] = 7
v[890,3] = 3
v[891,3] = 7
v[892,3] = 13
v[893,3] = 5
v[894,3] = 13
v[895,3] = 15
v[896,3] = 5
v[897,3] = 13
v[898,3] = 9
v[899,3] = 1
v[900,3] = 15
v[901,3] = 11
v[902,3] = 5
v[903,3] = 5
v[904,3] = 1
v[905,3] = 11
v[906,3] = 3
v[907,3] = 3
v[908,3] = 7
v[909,3] = 1
v[910,3] = 9
v[911,3] = 7
v[912,3] = 15
v[913,3] = 9
v[914,3] = 9
v[915,3] = 3
v[916,3] = 11
v[917,3] = 15
v[918,3] = 7
v[919,3] = 1
v[920,3] = 3
v[921,3] = 1
v[922,3] = 1
v[923,3] = 1
v[924,3] = 9
v[925,3] = 1
v[926,3] = 5
v[927,3] = 15
v[928,3] = 15
v[929,3] = 7
v[930,3] = 5
v[931,3] = 5
v[932,3] = 7
v[933,3] = 9
v[934,3] = 7
v[935,3] = 15
v[936,3] = 13
v[937,3] = 13
v[938,3] = 11
v[939,3] = 1
v[940,3] = 9
v[941,3] = 11
v[942,3] = 1
v[943,3] = 13
v[944,3] = 1
v[945,3] = 7
v[946,3] = 15
v[947,3] = 15
v[948,3] = 5
v[949,3] = 5
v[950,3] = 1
v[951,3] = 11
v[952,3] = 3
v[953,3] = 9
v[954,3] = 11
v[955,3] = 9
v[956,3] = 9
v[957,3] = 9
v[958,3] = 1
v[959,3] = 9
v[960,3] = 3
v[961,3] = 5
v[962,3] = 15
v[963,3] = 1
v[964,3] = 1
v[965,3] = 9
v[966,3] = 7
v[967,3] = 3
v[968,3] = 3
v[969,3] = 1
v[970,3] = 9
v[971,3] = 9
v[972,3] = 11
v[973,3] = 9
v[974,3] = 9
v[975,3] = 13
v[976,3] = 13
v[977,3] = 3
v[978,3] = 13
v[979,3] = 11
v[980,3] = 13
v[981,3] = 5
v[982,3] = 1
v[983,3] = 5
v[984,3] = 5
v[985,3] = 9
v[986,3] = 9
v[987,3] = 3
v[988,3] = 13
v[989,3] = 13
v[990,3] = 9
v[991,3] = 15
v[992,3] = 9
v[993,3] = 11
v[994,3] = 7
v[995,3] = 11
v[996,3] = 9
v[997,3] = 13
v[998,3] = 9
v[999,3] = 1
v[1000,3] = 15
v[1001,3] = 9
v[1002,3] = 7
v[1003,3] = 7
v[1004,3] = 1
v[1005,3] = 7
v[1006,3] = 9
v[1007,3] = 9
v[1008,3] = 15
v[1009,3] = 1
v[1010,3] = 11
v[1011,3] = 1
v[1012,3] = 13
v[1013,3] = 13
v[1014,3] = 15
v[1015,3] = 9
v[1016,3] = 13
v[1017,3] = 7
v[1018,3] = 15
v[1019,3] = 3
v[1020,3] = 9
v[1021,3] = 3
v[1022,3] = 1
v[1023,3] = 13
v[1024,3] = 7
v[1025,3] = 5
v[1026,3] = 9
v[1027,3] = 3
v[1028,3] = 1
v[1029,3] = 7
v[1030,3] = 1
v[1031,3] = 1
v[1032,3] = 13
v[1033,3] = 3
v[1034,3] = 3
v[1035,3] = 11
v[1036,3] = 1
v[1037,3] = 7
v[1038,3] = 13
v[1039,3] = 15
v[1040,3] = 15
v[1041,3] = 5
v[1042,3] = 7
v[1043,3] = 13
v[1044,3] = 13
v[1045,3] = 15
v[1046,3] = 11
v[1047,3] = 13
v[1048,3] = 1
v[1049,3] = 13
v[1050,3] = 13
v[1051,3] = 3
v[1052,3] = 9
v[1053,3] = 15
v[1054,3] = 15
v[1055,3] = 11
v[1056,3] = 15
v[1057,3] = 9
v[1058,3] = 15
v[1059,3] = 1
v[1060,3] = 13
v[1061,3] = 15
v[1062,3] = 1
v[1063,3] = 1
v[1064,3] = 5
v[1065,3] = 11
v[1066,3] = 5
v[1067,3] = 1
v[1068,3] = 11
v[1069,3] = 11
v[1070,3] = 5
v[1071,3] = 3
v[1072,3] = 9
v[1073,3] = 1
v[1074,3] = 3
v[1075,3] = 5
v[1076,3] = 13
v[1077,3] = 9
v[1078,3] = 7
v[1079,3] = 7
v[1080,3] = 1
v[1081,3] = 9
v[1082,3] = 9
v[1083,3] = 15
v[1084,3] = 7
v[1085,3] = 5
v[1086,3] = 5
v[1087,3] = 15
v[1088,3] = 13
v[1089,3] = 9
v[1090,3] = 7
v[1091,3] = 13
v[1092,3] = 3
v[1093,3] = 13
v[1094,3] = 11
v[1095,3] = 13
v[1096,3] = 7
v[1097,3] = 9
v[1098,3] = 13
v[1099,3] = 13
v[1100,3] = 13
v[1101,3] = 15
v[1102,3] = 9
v[1103,3] = 5
v[1104,3] = 5
v[1105,3] = 3
v[1106,3] = 3
v[1107,3] = 3
v[1108,3] = 1
v[1109,3] = 3
v[1110,3] = 15
v[7,4] = 9
v[8,4] = 3
v[9,4] = 27
v[10,4] = 15
v[11,4] = 29
v[12,4] = 21
v[13,4] = 23
v[14,4] = 19
v[15,4] = 11
v[16,4] = 25
v[17,4] = 7
v[18,4] = 13
v[19,4] = 17
v[20,4] = 1
v[21,4] = 25
v[22,4] = 29
v[23,4] = 3
v[24,4] = 31
v[25,4] = 11
v[26,4] = 5
v[27,4] = 23
v[28,4] = 27
v[29,4] = 19
v[30,4] = 21
v[31,4] = 5
v[32,4] = 1
v[33,4] = 17
v[34,4] = 13
v[35,4] = 7
v[36,4] = 15
v[37,4] = 9
v[38,4] = 31
v[39,4] = 25
v[40,4] = 3
v[41,4] = 5
v[42,4] = 23
v[43,4] = 7
v[44,4] = 3
v[45,4] = 17
v[46,4] = 23
v[47,4] = 3
v[48,4] = 3
v[49,4] = 21
v[50,4] = 25
v[51,4] = 25
v[52,4] = 23
v[53,4] = 11
v[54,4] = 19
v[55,4] = 3
v[56,4] = 11
v[57,4] = 31
v[58,4] = 7
v[59,4] = 9
v[60,4] = 5
v[61,4] = 17
v[62,4] = 23
v[63,4] = 17
v[64,4] = 17
v[65,4] = 25
v[66,4] = 13
v[67,4] = 11
v[68,4] = 31
v[69,4] = 27
v[70,4] = 19
v[71,4] = 17
v[72,4] = 23
v[73,4] = 7
v[74,4] = 5
v[75,4] = 11
v[76,4] = 19
v[77,4] = 19
v[78,4] = 7
v[79,4] = 13
v[80,4] = 21
v[81,4] = 21
v[82,4] = 7
v[83,4] = 9
v[84,4] = 11
v[85,4] = 1
v[86,4] = 5
v[87,4] = 21
v[88,4] = 11
v[89,4] = 13
v[90,4] = 25
v[91,4] = 9
v[92,4] = 7
v[93,4] = 7
v[94,4] = 27
v[95,4] = 15
v[96,4] = 25
v[97,4] = 15
v[98,4] = 21
v[99,4] = 17
v[100,4] = 19
v[101,4] = 19
v[102,4] = 21
v[103,4] = 5
v[104,4] = 11
v[105,4] = 3
v[106,4] = 5
v[107,4] = 29
v[108,4] = 31
v[109,4] = 29
v[110,4] = 5
v[111,4] = 5
v[112,4] = 1
v[113,4] = 31
v[114,4] = 27
v[115,4] = 11
v[116,4] = 13
v[117,4] = 1
v[118,4] = 3
v[119,4] = 7
v[120,4] = 11
v[121,4] = 7
v[122,4] = 3
v[123,4] = 23
v[124,4] = 13
v[125,4] = 31
v[126,4] = 17
v[127,4] = 1
v[128,4] = 27
v[129,4] = 11
v[130,4] = 25
v[131,4] = 1
v[132,4] = 23
v[133,4] = 29
v[134,4] = 17
v[135,4] = 25
v[136,4] = 7
v[137,4] = 25
v[138,4] = 27
v[139,4] = 17
v[140,4] = 13
v[141,4] = 17
v[142,4] = 23
v[143,4] = 5
v[144,4] = 17
v[145,4] = 5
v[146,4] = 13
v[147,4] = 11
v[148,4] = 21
v[149,4] = 5
v[150,4] = 11
v[151,4] = 5
v[152,4] = 9
v[153,4] = 31
v[154,4] = 19
v[155,4] = 17
v[156,4] = 9
v[157,4] = 9
v[158,4] = 27
v[159,4] = 21
v[160,4] = 15
v[161,4] = 15
v[162,4] = 1
v[163,4] = 1
v[164,4] = 29
v[165,4] = 5
v[166,4] = 31
v[167,4] = 11
v[168,4] = 17
v[169,4] = 23
v[170,4] = 19
v[171,4] = 21
v[172,4] = 25
v[173,4] = 15
v[174,4] = 11
v[175,4] = 5
v[176,4] = 5
v[177,4] = 1
v[178,4] = 19
v[179,4] = 19
v[180,4] = 19
v[181,4] = 7
v[182,4] = 13
v[183,4] = 21
v[184,4] = 17
v[185,4] = 17
v[186,4] = 25
v[187,4] = 23
v[188,4] = 19
v[189,4] = 23
v[190,4] = 15
v[191,4] = 13
v[192,4] = 5
v[193,4] = 19
v[194,4] = 25
v[195,4] = 9
v[196,4] = 7
v[197,4] = 3
v[198,4] = 21
v[199,4] = 17
v[200,4] = 25
v[201,4] = 1
v[202,4] = 27
v[203,4] = 25
v[204,4] = 27
v[205,4] = 25
v[206,4] = 9
v[207,4] = 13
v[208,4] = 3
v[209,4] = 17
v[210,4] = 25
v[211,4] = 23
v[212,4] = 9
v[213,4] = 25
v[214,4] = 9
v[215,4] = 13
v[216,4] = 17
v[217,4] = 17
v[218,4] = 3
v[219,4] = 15
v[220,4] = 7
v[221,4] = 7
v[222,4] = 29
v[223,4] = 3
v[224,4] = 19
v[225,4] = 29
v[226,4] = 29
v[227,4] = 19
v[228,4] = 29
v[229,4] = 13
v[230,4] = 15
v[231,4] = 25
v[232,4] = 27
v[233,4] = 1
v[234,4] = 3
v[235,4] = 9
v[236,4] = 9
v[237,4] = 13
v[238,4] = 31
v[239,4] = 29
v[240,4] = 31
v[241,4] = 5
v[242,4] = 15
v[243,4] = 29
v[244,4] = 1
v[245,4] = 19
v[246,4] = 5
v[247,4] = 9
v[248,4] = 19
v[249,4] = 5
v[250,4] = 15
v[251,4] = 3
v[252,4] = 5
v[253,4] = 7
v[254,4] = 15
v[255,4] = 17
v[256,4] = 17
v[257,4] = 23
v[258,4] = 11
v[259,4] = 9
v[260,4] = 23
v[261,4] = 19
v[262,4] = 3
v[263,4] = 17
v[264,4] = 1
v[265,4] = 27
v[266,4] = 9
v[267,4] = 9
v[268,4] = 17
v[269,4] = 13
v[270,4] = 25
v[271,4] = 29
v[272,4] = 23
v[273,4] = 29
v[274,4] = 11
v[275,4] = 31
v[276,4] = 25
v[277,4] = 21
v[278,4] = 29
v[279,4] = 19
v[280,4] = 27
v[281,4] = 31
v[282,4] = 3
v[283,4] = 5
v[284,4] = 3
v[285,4] = 3
v[286,4] = 13
v[287,4] = 21
v[288,4] = 9
v[289,4] = 29
v[290,4] = 3
v[291,4] = 17
v[292,4] = 11
v[293,4] = 11
v[294,4] = 9
v[295,4] = 21
v[296,4] = 19
v[297,4] = 7
v[298,4] = 17
v[299,4] = 31
v[300,4] = 25
v[301,4] = 1
v[302,4] = 27
v[303,4] = 5
v[304,4] = 15
v[305,4] = 27
v[306,4] = 29
v[307,4] = 29
v[308,4] = 29
v[309,4] = 25
v[310,4] = 27
v[311,4] = 25
v[312,4] = 3
v[313,4] = 21
v[314,4] = 17
v[315,4] = 25
v[316,4] = 13
v[317,4] = 15
v[318,4] = 17
v[319,4] = 13
v[320,4] = 23
v[321,4] = 9
v[322,4] = 3
v[323,4] = 11
v[324,4] = 7
v[325,4] = 9
v[326,4] = 9
v[327,4] = 7
v[328,4] = 17
v[329,4] = 7
v[330,4] = 1
v[331,4] = 27
v[332,4] = 1
v[333,4] = 9
v[334,4] = 5
v[335,4] = 31
v[336,4] = 21
v[337,4] = 25
v[338,4] = 25
v[339,4] = 21
v[340,4] = 11
v[341,4] = 1
v[342,4] = 23
v[343,4] = 19
v[344,4] = 27
v[345,4] = 15
v[346,4] = 3
v[347,4] = 5
v[348,4] = 23
v[349,4] = 9
v[350,4] = 25
v[351,4] = 7
v[352,4] = 29
v[353,4] = 11
v[354,4] = 9
v[355,4] = 13
v[356,4] = 5
v[357,4] = 11
v[358,4] = 1
v[359,4] = 3
v[360,4] = 31
v[361,4] = 27
v[362,4] = 3
v[363,4] = 17
v[364,4] = 27
v[365,4] = 11
v[366,4] = 13
v[367,4] = 15
v[368,4] = 29
v[369,4] = 15
v[370,4] = 1
v[371,4] = 15
v[372,4] = 23
v[373,4] = 25
v[374,4] = 13
v[375,4] = 21
v[376,4] = 15
v[377,4] = 3
v[378,4] = 29
v[379,4] = 29
v[380,4] = 5
v[381,4] = 25
v[382,4] = 17
v[383,4] = 11
v[384,4] = 7
v[385,4] = 15
v[386,4] = 5
v[387,4] = 21
v[388,4] = 7
v[389,4] = 31
v[390,4] = 13
v[391,4] = 11
v[392,4] = 23
v[393,4] = 5
v[394,4] = 7
v[395,4] = 23
v[396,4] = 27
v[397,4] = 21
v[398,4] = 29
v[399,4] = 15
v[400,4] = 7
v[401,4] = 27
v[402,4] = 27
v[403,4] = 19
v[404,4] = 7
v[405,4] = 15
v[406,4] = 27
v[407,4] = 27
v[408,4] = 19
v[409,4] = 19
v[410,4] = 9
v[411,4] = 15
v[412,4] = 1
v[413,4] = 3
v[414,4] = 29
v[415,4] = 29
v[416,4] = 5
v[417,4] = 27
v[418,4] = 31
v[419,4] = 9
v[420,4] = 1
v[421,4] = 7
v[422,4] = 3
v[423,4] = 19
v[424,4] = 19
v[425,4] = 29
v[426,4] = 9
v[427,4] = 3
v[428,4] = 21
v[429,4] = 31
v[430,4] = 29
v[431,4] = 25
v[432,4] = 1
v[433,4] = 3
v[434,4] = 9
v[435,4] = 27
v[436,4] = 5
v[437,4] = 27
v[438,4] = 25
v[439,4] = 21
v[440,4] = 11
v[441,4] = 29
v[442,4] = 31
v[443,4] = 27
v[444,4] = 21
v[445,4] = 29
v[446,4] = 17
v[447,4] = 9
v[448,4] = 17
v[449,4] = 13
v[450,4] = 11
v[451,4] = 25
v[452,4] = 15
v[453,4] = 21
v[454,4] = 11
v[455,4] = 19
v[456,4] = 31
v[457,4] = 3
v[458,4] = 19
v[459,4] = 5
v[460,4] = 3
v[461,4] = 3
v[462,4] = 9
v[463,4] = 13
v[464,4] = 13
v[465,4] = 3
v[466,4] = 29
v[467,4] = 7
v[468,4] = 5
v[469,4] = 9
v[470,4] = 23
v[471,4] = 13
v[472,4] = 21
v[473,4] = 23
v[474,4] = 21
v[475,4] = 31
v[476,4] = 11
v[477,4] = 7
v[478,4] = 7
v[479,4] = 3
v[480,4] = 23
v[481,4] = 1
v[482,4] = 23
v[483,4] = 5
v[484,4] = 9
v[485,4] = 17
v[486,4] = 21
v[487,4] = 1
v[488,4] = 17
v[489,4] = 29
v[490,4] = 7
v[491,4] = 5
v[492,4] = 17
v[493,4] = 13
v[494,4] = 25
v[495,4] = 17
v[496,4] = 9
v[497,4] = 19
v[498,4] = 9
v[499,4] = 5
v[500,4] = 7
v[501,4] = 21
v[502,4] = 19
v[503,4] = 13
v[504,4] = 9
v[505,4] = 7
v[506,4] = 3
v[507,4] = 9
v[508,4] = 3
v[509,4] = 15
v[510,4] = 31
v[511,4] = 29
v[512,4] = 29
v[513,4] = 25
v[514,4] = 13
v[515,4] = 9
v[516,4] = 21
v[517,4] = 9
v[518,4] = 31
v[519,4] = 7
v[520,4] = 15
v[521,4] = 5
v[522,4] = 31
v[523,4] = 7
v[524,4] = 15
v[525,4] = 27
v[526,4] = 25
v[527,4] = 19
v[528,4] = 9
v[529,4] = 9
v[530,4] = 25
v[531,4] = 25
v[532,4] = 23
v[533,4] = 1
v[534,4] = 9
v[535,4] = 7
v[536,4] = 11
v[537,4] = 15
v[538,4] = 19
v[539,4] = 15
v[540,4] = 27
v[541,4] = 17
v[542,4] = 11
v[543,4] = 11
v[544,4] = 31
v[545,4] = 13
v[546,4] = 25
v[547,4] = 25
v[548,4] = 9
v[549,4] = 7
v[550,4] = 13
v[551,4] = 29
v[552,4] = 19
v[553,4] = 5
v[554,4] = 19
v[555,4] = 31
v[556,4] = 25
v[557,4] = 13
v[558,4] = 25
v[559,4] = 15
v[560,4] = 5
v[561,4] = 9
v[562,4] = 29
v[563,4] = 31
v[564,4] = 9
v[565,4] = 29
v[566,4] = 27
v[567,4] = 25
v[568,4] = 27
v[569,4] = 11
v[570,4] = 17
v[571,4] = 5
v[572,4] = 17
v[573,4] = 3
v[574,4] = 23
v[575,4] = 15
v[576,4] = 9
v[577,4] = 9
v[578,4] = 17
v[579,4] = 17
v[580,4] = 31
v[581,4] = 11
v[582,4] = 19
v[583,4] = 25
v[584,4] = 13
v[585,4] = 23
v[586,4] = 15
v[587,4] = 25
v[588,4] = 21
v[589,4] = 31
v[590,4] = 19
v[591,4] = 3
v[592,4] = 11
v[593,4] = 25
v[594,4] = 7
v[595,4] = 15
v[596,4] = 19
v[597,4] = 7
v[598,4] = 5
v[599,4] = 3
v[600,4] = 13
v[601,4] = 13
v[602,4] = 1
v[603,4] = 23
v[604,4] = 5
v[605,4] = 25
v[606,4] = 11
v[607,4] = 25
v[608,4] = 15
v[609,4] = 13
v[610,4] = 21
v[611,4] = 11
v[612,4] = 23
v[613,4] = 29
v[614,4] = 5
v[615,4] = 17
v[616,4] = 27
v[617,4] = 9
v[618,4] = 19
v[619,4] = 15
v[620,4] = 5
v[621,4] = 29
v[622,4] = 23
v[623,4] = 19
v[624,4] = 1
v[625,4] = 27
v[626,4] = 3
v[627,4] = 23
v[628,4] = 21
v[629,4] = 19
v[630,4] = 27
v[631,4] = 11
v[632,4] = 17
v[633,4] = 13
v[634,4] = 27
v[635,4] = 11
v[636,4] = 31
v[637,4] = 23
v[638,4] = 5
v[639,4] = 9
v[640,4] = 21
v[641,4] = 31
v[642,4] = 29
v[643,4] = 11
v[644,4] = 21
v[645,4] = 17
v[646,4] = 15
v[647,4] = 7
v[648,4] = 15
v[649,4] = 7
v[650,4] = 9
v[651,4] = 21
v[652,4] = 27
v[653,4] = 25
v[654,4] = 29
v[655,4] = 11
v[656,4] = 3
v[657,4] = 21
v[658,4] = 13
v[659,4] = 23
v[660,4] = 19
v[661,4] = 27
v[662,4] = 17
v[663,4] = 29
v[664,4] = 25
v[665,4] = 17
v[666,4] = 9
v[667,4] = 1
v[668,4] = 19
v[669,4] = 23
v[670,4] = 5
v[671,4] = 23
v[672,4] = 1
v[673,4] = 17
v[674,4] = 17
v[675,4] = 13
v[676,4] = 27
v[677,4] = 23
v[678,4] = 7
v[679,4] = 7
v[680,4] = 11
v[681,4] = 13
v[682,4] = 17
v[683,4] = 13
v[684,4] = 11
v[685,4] = 21
v[686,4] = 13
v[687,4] = 23
v[688,4] = 1
v[689,4] = 27
v[690,4] = 13
v[691,4] = 9
v[692,4] = 7
v[693,4] = 1
v[694,4] = 27
v[695,4] = 29
v[696,4] = 5
v[697,4] = 13
v[698,4] = 25
v[699,4] = 21
v[700,4] = 3
v[701,4] = 31
v[702,4] = 15
v[703,4] = 13
v[704,4] = 3
v[705,4] = 19
v[706,4] = 13
v[707,4] = 1
v[708,4] = 27
v[709,4] = 15
v[710,4] = 17
v[711,4] = 1
v[712,4] = 3
v[713,4] = 13
v[714,4] = 13
v[715,4] = 13
v[716,4] = 31
v[717,4] = 29
v[718,4] = 27
v[719,4] = 7
v[720,4] = 7
v[721,4] = 21
v[722,4] = 29
v[723,4] = 15
v[724,4] = 17
v[725,4] = 17
v[726,4] = 21
v[727,4] = 19
v[728,4] = 17
v[729,4] = 3
v[730,4] = 15
v[731,4] = 5
v[732,4] = 27
v[733,4] = 27
v[734,4] = 3
v[735,4] = 31
v[736,4] = 31
v[737,4] = 7
v[738,4] = 21
v[739,4] = 3
v[740,4] = 13
v[741,4] = 11
v[742,4] = 17
v[743,4] = 27
v[744,4] = 25
v[745,4] = 1
v[746,4] = 9
v[747,4] = 7
v[748,4] = 29
v[749,4] = 27
v[750,4] = 21
v[751,4] = 23
v[752,4] = 13
v[753,4] = 25
v[754,4] = 29
v[755,4] = 15
v[756,4] = 17
v[757,4] = 29
v[758,4] = 9
v[759,4] = 15
v[760,4] = 3
v[761,4] = 21
v[762,4] = 15
v[763,4] = 17
v[764,4] = 17
v[765,4] = 31
v[766,4] = 9
v[767,4] = 9
v[768,4] = 23
v[769,4] = 19
v[770,4] = 25
v[771,4] = 3
v[772,4] = 1
v[773,4] = 11
v[774,4] = 27
v[775,4] = 29
v[776,4] = 1
v[777,4] = 31
v[778,4] = 29
v[779,4] = 25
v[780,4] = 29
v[781,4] = 1
v[782,4] = 23
v[783,4] = 29
v[784,4] = 25
v[785,4] = 13
v[786,4] = 3
v[787,4] = 31
v[788,4] = 25
v[789,4] = 5
v[790,4] = 5
v[791,4] = 11
v[792,4] = 3
v[793,4] = 21
v[794,4] = 9
v[795,4] = 23
v[796,4] = 7
v[797,4] = 11
v[798,4] = 23
v[799,4] = 11
v[800,4] = 1
v[801,4] = 1
v[802,4] = 3
v[803,4] = 23
v[804,4] = 25
v[805,4] = 23
v[806,4] = 1
v[807,4] = 23
v[808,4] = 3
v[809,4] = 27
v[810,4] = 9
v[811,4] = 27
v[812,4] = 3
v[813,4] = 23
v[814,4] = 25
v[815,4] = 19
v[816,4] = 29
v[817,4] = 29
v[818,4] = 13
v[819,4] = 27
v[820,4] = 5
v[821,4] = 9
v[822,4] = 29
v[823,4] = 29
v[824,4] = 13
v[825,4] = 17
v[826,4] = 3
v[827,4] = 23
v[828,4] = 19
v[829,4] = 7
v[830,4] = 13
v[831,4] = 3
v[832,4] = 19
v[833,4] = 23
v[834,4] = 5
v[835,4] = 29
v[836,4] = 29
v[837,4] = 13
v[838,4] = 13
v[839,4] = 5
v[840,4] = 19
v[841,4] = 5
v[842,4] = 17
v[843,4] = 9
v[844,4] = 11
v[845,4] = 11
v[846,4] = 29
v[847,4] = 27
v[848,4] = 23
v[849,4] = 19
v[850,4] = 17
v[851,4] = 25
v[852,4] = 13
v[853,4] = 1
v[854,4] = 13
v[855,4] = 3
v[856,4] = 11
v[857,4] = 1
v[858,4] = 17
v[859,4] = 29
v[860,4] = 1
v[861,4] = 13
v[862,4] = 17
v[863,4] = 9
v[864,4] = 17
v[865,4] = 21
v[866,4] = 1
v[867,4] = 11
v[868,4] = 1
v[869,4] = 1
v[870,4] = 25
v[871,4] = 5
v[872,4] = 7
v[873,4] = 29
v[874,4] = 29
v[875,4] = 19
v[876,4] = 19
v[877,4] = 1
v[878,4] = 29
v[879,4] = 13
v[880,4] = 3
v[881,4] = 1
v[882,4] = 31
v[883,4] = 15
v[884,4] = 13
v[885,4] = 3
v[886,4] = 1
v[887,4] = 11
v[888,4] = 19
v[889,4] = 5
v[890,4] = 29
v[891,4] = 13
v[892,4] = 29
v[893,4] = 23
v[894,4] = 3
v[895,4] = 1
v[896,4] = 31
v[897,4] = 13
v[898,4] = 19
v[899,4] = 17
v[900,4] = 5
v[901,4] = 5
v[902,4] = 1
v[903,4] = 29
v[904,4] = 23
v[905,4] = 3
v[906,4] = 19
v[907,4] = 25
v[908,4] = 19
v[909,4] = 27
v[910,4] = 9
v[911,4] = 27
v[912,4] = 13
v[913,4] = 15
v[914,4] = 29
v[915,4] = 23
v[916,4] = 13
v[917,4] = 25
v[918,4] = 25
v[919,4] = 17
v[920,4] = 19
v[921,4] = 17
v[922,4] = 15
v[923,4] = 27
v[924,4] = 3
v[925,4] = 25
v[926,4] = 17
v[927,4] = 27
v[928,4] = 3
v[929,4] = 27
v[930,4] = 31
v[931,4] = 23
v[932,4] = 13
v[933,4] = 31
v[934,4] = 11
v[935,4] = 15
v[936,4] = 7
v[937,4] = 21
v[938,4] = 19
v[939,4] = 27
v[940,4] = 19
v[941,4] = 21
v[942,4] = 29
v[943,4] = 7
v[944,4] = 31
v[945,4] = 13
v[946,4] = 9
v[947,4] = 9
v[948,4] = 7
v[949,4] = 21
v[950,4] = 13
v[951,4] = 11
v[952,4] = 9
v[953,4] = 11
v[954,4] = 29
v[955,4] = 19
v[956,4] = 11
v[957,4] = 19
v[958,4] = 21
v[959,4] = 5
v[960,4] = 29
v[961,4] = 13
v[962,4] = 7
v[963,4] = 19
v[964,4] = 19
v[965,4] = 27
v[966,4] = 23
v[967,4] = 31
v[968,4] = 1
v[969,4] = 27
v[970,4] = 21
v[971,4] = 7
v[972,4] = 3
v[973,4] = 7
v[974,4] = 11
v[975,4] = 23
v[976,4] = 13
v[977,4] = 29
v[978,4] = 11
v[979,4] = 31
v[980,4] = 19
v[981,4] = 1
v[982,4] = 5
v[983,4] = 5
v[984,4] = 11
v[985,4] = 5
v[986,4] = 3
v[987,4] = 27
v[988,4] = 5
v[989,4] = 7
v[990,4] = 11
v[991,4] = 31
v[992,4] = 1
v[993,4] = 27
v[994,4] = 31
v[995,4] = 31
v[996,4] = 23
v[997,4] = 5
v[998,4] = 21
v[999,4] = 27
v[1000,4] = 9
v[1001,4] = 25
v[1002,4] = 3
v[1003,4] = 15
v[1004,4] = 19
v[1005,4] = 1
v[1006,4] = 19
v[1007,4] = 9
v[1008,4] = 5
v[1009,4] = 25
v[1010,4] = 21
v[1011,4] = 15
v[1012,4] = 25
v[1013,4] = 29
v[1014,4] = 15
v[1015,4] = 21
v[1016,4] = 11
v[1017,4] = 19
v[1018,4] = 15
v[1019,4] = 3
v[1020,4] = 7
v[1021,4] = 13
v[1022,4] = 11
v[1023,4] = 25
v[1024,4] = 17
v[1025,4] = 1
v[1026,4] = 5
v[1027,4] = 31
v[1028,4] = 13
v[1029,4] = 29
v[1030,4] = 23
v[1031,4] = 9
v[1032,4] = 5
v[1033,4] = 29
v[1034,4] = 7
v[1035,4] = 17
v[1036,4] = 27
v[1037,4] = 7
v[1038,4] = 17
v[1039,4] = 31
v[1040,4] = 9
v[1041,4] = 31
v[1042,4] = 9
v[1043,4] = 9
v[1044,4] = 7
v[1045,4] = 21
v[1046,4] = 3
v[1047,4] = 3
v[1048,4] = 3
v[1049,4] = 9
v[1050,4] = 11
v[1051,4] = 21
v[1052,4] = 11
v[1053,4] = 31
v[1054,4] = 9
v[1055,4] = 25
v[1056,4] = 5
v[1057,4] = 1
v[1058,4] = 31
v[1059,4] = 13
v[1060,4] = 29
v[1061,4] = 9
v[1062,4] = 29
v[1063,4] = 1
v[1064,4] = 11
v[1065,4] = 19
v[1066,4] = 7
v[1067,4] = 27
v[1068,4] = 13
v[1069,4] = 31
v[1070,4] = 7
v[1071,4] = 31
v[1072,4] = 7
v[1073,4] = 25
v[1074,4] = 23
v[1075,4] = 21
v[1076,4] = 29
v[1077,4] = 11
v[1078,4] = 11
v[1079,4] = 13
v[1080,4] = 11
v[1081,4] = 27
v[1082,4] = 1
v[1083,4] = 23
v[1084,4] = 31
v[1085,4] = 21
v[1086,4] = 23
v[1087,4] = 21
v[1088,4] = 19
v[1089,4] = 31
v[1090,4] = 5
v[1091,4] = 31
v[1092,4] = 25
v[1093,4] = 25
v[1094,4] = 19
v[1095,4] = 17
v[1096,4] = 11
v[1097,4] = 25
v[1098,4] = 7
v[1099,4] = 13
v[1100,4] = 1
v[1101,4] = 29
v[1102,4] = 17
v[1103,4] = 23
v[1104,4] = 15
v[1105,4] = 7
v[1106,4] = 29
v[1107,4] = 17
v[1108,4] = 13
v[1109,4] = 3
v[1110,4] = 17
v[13,5] = 37
v[14,5] = 33
v[15,5] = 7
v[16,5] = 5
v[17,5] = 11
v[18,5] = 39
v[19,5] = 63
v[20,5] = 59
v[21,5] = 17
v[22,5] = 15
v[23,5] = 23
v[24,5] = 29
v[25,5] = 3
v[26,5] = 21
v[27,5] = 13
v[28,5] = 31
v[29,5] = 25
v[30,5] = 9
v[31,5] = 49
v[32,5] = 33
v[33,5] = 19
v[34,5] = 29
v[35,5] = 11
v[36,5] = 19
v[37,5] = 27
v[38,5] = 15
v[39,5] = 25
v[40,5] = 63
v[41,5] = 55
v[42,5] = 17
v[43,5] = 63
v[44,5] = 49
v[45,5] = 19
v[46,5] = 41
v[47,5] = 59
v[48,5] = 3
v[49,5] = 57
v[50,5] = 33
v[51,5] = 49
v[52,5] = 53
v[53,5] = 57
v[54,5] = 57
v[55,5] = 39
v[56,5] = 21
v[57,5] = 7
v[58,5] = 53
v[59,5] = 9
v[60,5] = 55
v[61,5] = 15
v[62,5] = 59
v[63,5] = 19
v[64,5] = 49
v[65,5] = 31
v[66,5] = 3
v[67,5] = 39
v[68,5] = 5
v[69,5] = 5
v[70,5] = 41
v[71,5] = 9
v[72,5] = 19
v[73,5] = 9
v[74,5] = 57
v[75,5] = 25
v[76,5] = 1
v[77,5] = 15
v[78,5] = 51
v[79,5] = 11
v[80,5] = 19
v[81,5] = 61
v[82,5] = 53
v[83,5] = 29
v[84,5] = 19
v[85,5] = 11
v[86,5] = 9
v[87,5] = 21
v[88,5] = 19
v[89,5] = 43
v[90,5] = 13
v[91,5] = 13
v[92,5] = 41
v[93,5] = 25
v[94,5] = 31
v[95,5] = 9
v[96,5] = 11
v[97,5] = 19
v[98,5] = 5
v[99,5] = 53
v[100,5] = 37
v[101,5] = 7
v[102,5] = 51
v[103,5] = 45
v[104,5] = 7
v[105,5] = 7
v[106,5] = 61
v[107,5] = 23
v[108,5] = 45
v[109,5] = 7
v[110,5] = 59
v[111,5] = 41
v[112,5] = 1
v[113,5] = 29
v[114,5] = 61
v[115,5] = 37
v[116,5] = 27
v[117,5] = 47
v[118,5] = 15
v[119,5] = 31
v[120,5] = 35
v[121,5] = 31
v[122,5] = 17
v[123,5] = 51
v[124,5] = 13
v[125,5] = 25
v[126,5] = 45
v[127,5] = 5
v[128,5] = 5
v[129,5] = 33
v[130,5] = 39
v[131,5] = 5
v[132,5] = 47
v[133,5] = 29
v[134,5] = 35
v[135,5] = 47
v[136,5] = 63
v[137,5] = 45
v[138,5] = 37
v[139,5] = 47
v[140,5] = 59
v[141,5] = 21
v[142,5] = 59
v[143,5] = 33
v[144,5] = 51
v[145,5] = 9
v[146,5] = 27
v[147,5] = 13
v[148,5] = 25
v[149,5] = 43
v[150,5] = 3
v[151,5] = 17
v[152,5] = 21
v[153,5] = 59
v[154,5] = 61
v[155,5] = 27
v[156,5] = 47
v[157,5] = 57
v[158,5] = 11
v[159,5] = 17
v[160,5] = 39
v[161,5] = 1
v[162,5] = 63
v[163,5] = 21
v[164,5] = 59
v[165,5] = 17
v[166,5] = 13
v[167,5] = 31
v[168,5] = 3
v[169,5] = 31
v[170,5] = 7
v[171,5] = 9
v[172,5] = 27
v[173,5] = 37
v[174,5] = 23
v[175,5] = 31
v[176,5] = 9
v[177,5] = 45
v[178,5] = 43
v[179,5] = 31
v[180,5] = 63
v[181,5] = 21
v[182,5] = 39
v[183,5] = 51
v[184,5] = 27
v[185,5] = 7
v[186,5] = 53
v[187,5] = 11
v[188,5] = 1
v[189,5] = 59
v[190,5] = 39
v[191,5] = 23
v[192,5] = 49
v[193,5] = 23
v[194,5] = 7
v[195,5] = 55
v[196,5] = 59
v[197,5] = 3
v[198,5] = 19
v[199,5] = 35
v[200,5] = 13
v[201,5] = 9
v[202,5] = 13
v[203,5] = 15
v[204,5] = 23
v[205,5] = 9
v[206,5] = 7
v[207,5] = 43
v[208,5] = 55
v[209,5] = 3
v[210,5] = 19
v[211,5] = 9
v[212,5] = 27
v[213,5] = 33
v[214,5] = 27
v[215,5] = 49
v[216,5] = 23
v[217,5] = 47
v[218,5] = 19
v[219,5] = 7
v[220,5] = 11
v[221,5] = 55
v[222,5] = 27
v[223,5] = 35
v[224,5] = 5
v[225,5] = 5
v[226,5] = 55
v[227,5] = 35
v[228,5] = 37
v[229,5] = 9
v[230,5] = 33
v[231,5] = 29
v[232,5] = 47
v[233,5] = 25
v[234,5] = 11
v[235,5] = 47
v[236,5] = 53
v[237,5] = 61
v[238,5] = 59
v[239,5] = 3
v[240,5] = 53
v[241,5] = 47
v[242,5] = 5
v[243,5] = 19
v[244,5] = 59
v[245,5] = 5
v[246,5] = 47
v[247,5] = 23
v[248,5] = 45
v[249,5] = 53
v[250,5] = 3
v[251,5] = 49
v[252,5] = 61
v[253,5] = 47
v[254,5] = 39
v[255,5] = 29
v[256,5] = 17
v[257,5] = 57
v[258,5] = 5
v[259,5] = 17
v[260,5] = 31
v[261,5] = 23
v[262,5] = 41
v[263,5] = 39
v[264,5] = 5
v[265,5] = 27
v[266,5] = 7
v[267,5] = 29
v[268,5] = 29
v[269,5] = 33
v[270,5] = 31
v[271,5] = 41
v[272,5] = 31
v[273,5] = 29
v[274,5] = 17
v[275,5] = 29
v[276,5] = 29
v[277,5] = 9
v[278,5] = 9
v[279,5] = 31
v[280,5] = 27
v[281,5] = 53
v[282,5] = 35
v[283,5] = 5
v[284,5] = 61
v[285,5] = 1
v[286,5] = 49
v[287,5] = 13
v[288,5] = 57
v[289,5] = 29
v[290,5] = 5
v[291,5] = 21
v[292,5] = 43
v[293,5] = 25
v[294,5] = 57
v[295,5] = 49
v[296,5] = 37
v[297,5] = 27
v[298,5] = 11
v[299,5] = 61
v[300,5] = 37
v[301,5] = 49
v[302,5] = 5
v[303,5] = 63
v[304,5] = 63
v[305,5] = 3
v[306,5] = 45
v[307,5] = 37
v[308,5] = 63
v[309,5] = 21
v[310,5] = 21
v[311,5] = 19
v[312,5] = 27
v[313,5] = 59
v[314,5] = 21
v[315,5] = 45
v[316,5] = 23
v[317,5] = 13
v[318,5] = 15
v[319,5] = 3
v[320,5] = 43
v[321,5] = 63
v[322,5] = 39
v[323,5] = 19
v[324,5] = 63
v[325,5] = 31
v[326,5] = 41
v[327,5] = 41
v[328,5] = 15
v[329,5] = 43
v[330,5] = 63
v[331,5] = 53
v[332,5] = 1
v[333,5] = 63
v[334,5] = 31
v[335,5] = 7
v[336,5] = 17
v[337,5] = 11
v[338,5] = 61
v[339,5] = 31
v[340,5] = 51
v[341,5] = 37
v[342,5] = 29
v[343,5] = 59
v[344,5] = 25
v[345,5] = 63
v[346,5] = 59
v[347,5] = 47
v[348,5] = 15
v[349,5] = 27
v[350,5] = 19
v[351,5] = 29
v[352,5] = 45
v[353,5] = 35
v[354,5] = 55
v[355,5] = 39
v[356,5] = 19
v[357,5] = 43
v[358,5] = 21
v[359,5] = 19
v[360,5] = 13
v[361,5] = 17
v[362,5] = 51
v[363,5] = 37
v[364,5] = 5
v[365,5] = 33
v[366,5] = 35
v[367,5] = 49
v[368,5] = 25
v[369,5] = 45
v[370,5] = 1
v[371,5] = 63
v[372,5] = 47
v[373,5] = 9
v[374,5] = 63
v[375,5] = 15
v[376,5] = 25
v[377,5] = 25
v[378,5] = 15
v[379,5] = 41
v[380,5] = 13
v[381,5] = 3
v[382,5] = 19
v[383,5] = 51
v[384,5] = 49
v[385,5] = 37
v[386,5] = 25
v[387,5] = 49
v[388,5] = 13
v[389,5] = 53
v[390,5] = 47
v[391,5] = 23
v[392,5] = 35
v[393,5] = 29
v[394,5] = 33
v[395,5] = 21
v[396,5] = 35
v[397,5] = 23
v[398,5] = 3
v[399,5] = 43
v[400,5] = 31
v[401,5] = 63
v[402,5] = 9
v[403,5] = 1
v[404,5] = 61
v[405,5] = 43
v[406,5] = 3
v[407,5] = 11
v[408,5] = 55
v[409,5] = 11
v[410,5] = 35
v[411,5] = 1
v[412,5] = 63
v[413,5] = 35
v[414,5] = 49
v[415,5] = 19
v[416,5] = 45
v[417,5] = 9
v[418,5] = 57
v[419,5] = 51
v[420,5] = 1
v[421,5] = 47
v[422,5] = 41
v[423,5] = 9
v[424,5] = 11
v[425,5] = 37
v[426,5] = 19
v[427,5] = 55
v[428,5] = 23
v[429,5] = 55
v[430,5] = 55
v[431,5] = 13
v[432,5] = 7
v[433,5] = 47
v[434,5] = 37
v[435,5] = 11
v[436,5] = 43
v[437,5] = 17
v[438,5] = 3
v[439,5] = 25
v[440,5] = 19
v[441,5] = 55
v[442,5] = 59
v[443,5] = 37
v[444,5] = 33
v[445,5] = 43
v[446,5] = 1
v[447,5] = 5
v[448,5] = 21
v[449,5] = 5
v[450,5] = 63
v[451,5] = 49
v[452,5] = 61
v[453,5] = 21
v[454,5] = 51
v[455,5] = 15
v[456,5] = 19
v[457,5] = 43
v[458,5] = 47
v[459,5] = 17
v[460,5] = 9
v[461,5] = 53
v[462,5] = 45
v[463,5] = 11
v[464,5] = 51
v[465,5] = 25
v[466,5] = 11
v[467,5] = 25
v[468,5] = 47
v[469,5] = 47
v[470,5] = 1
v[471,5] = 43
v[472,5] = 29
v[473,5] = 17
v[474,5] = 31
v[475,5] = 15
v[476,5] = 59
v[477,5] = 27
v[478,5] = 63
v[479,5] = 11
v[480,5] = 41
v[481,5] = 51
v[482,5] = 29
v[483,5] = 7
v[484,5] = 27
v[485,5] = 63
v[486,5] = 31
v[487,5] = 43
v[488,5] = 3
v[489,5] = 29
v[490,5] = 39
v[491,5] = 3
v[492,5] = 59
v[493,5] = 59
v[494,5] = 1
v[495,5] = 53
v[496,5] = 63
v[497,5] = 23
v[498,5] = 63
v[499,5] = 47
v[500,5] = 51
v[501,5] = 23
v[502,5] = 61
v[503,5] = 39
v[504,5] = 47
v[505,5] = 21
v[506,5] = 39
v[507,5] = 15
v[508,5] = 3
v[509,5] = 9
v[510,5] = 57
v[511,5] = 61
v[512,5] = 39
v[513,5] = 37
v[514,5] = 21
v[515,5] = 51
v[516,5] = 1
v[517,5] = 23
v[518,5] = 43
v[519,5] = 27
v[520,5] = 25
v[521,5] = 11
v[522,5] = 13
v[523,5] = 21
v[524,5] = 43
v[525,5] = 7
v[526,5] = 11
v[527,5] = 33
v[528,5] = 55
v[529,5] = 1
v[530,5] = 37
v[531,5] = 35
v[532,5] = 27
v[533,5] = 61
v[534,5] = 39
v[535,5] = 5
v[536,5] = 19
v[537,5] = 61
v[538,5] = 61
v[539,5] = 57
v[540,5] = 59
v[541,5] = 21
v[542,5] = 59
v[543,5] = 61
v[544,5] = 57
v[545,5] = 25
v[546,5] = 55
v[547,5] = 27
v[548,5] = 31
v[549,5] = 41
v[550,5] = 33
v[551,5] = 63
v[552,5] = 19
v[553,5] = 57
v[554,5] = 35
v[555,5] = 13
v[556,5] = 63
v[557,5] = 35
v[558,5] = 17
v[559,5] = 11
v[560,5] = 11
v[561,5] = 49
v[562,5] = 41
v[563,5] = 55
v[564,5] = 5
v[565,5] = 45
v[566,5] = 17
v[567,5] = 35
v[568,5] = 5
v[569,5] = 31
v[570,5] = 31
v[571,5] = 37
v[572,5] = 17
v[573,5] = 45
v[574,5] = 51
v[575,5] = 1
v[576,5] = 39
v[577,5] = 49
v[578,5] = 55
v[579,5] = 19
v[580,5] = 41
v[581,5] = 13
v[582,5] = 5
v[583,5] = 51
v[584,5] = 5
v[585,5] = 49
v[586,5] = 1
v[587,5] = 21
v[588,5] = 13
v[589,5] = 17
v[590,5] = 59
v[591,5] = 51
v[592,5] = 11
v[593,5] = 3
v[594,5] = 61
v[595,5] = 1
v[596,5] = 33
v[597,5] = 37
v[598,5] = 33
v[599,5] = 61
v[600,5] = 25
v[601,5] = 27
v[602,5] = 59
v[603,5] = 7
v[604,5] = 49
v[605,5] = 13
v[606,5] = 63
v[607,5] = 3
v[608,5] = 33
v[609,5] = 3
v[610,5] = 15
v[611,5] = 9
v[612,5] = 13
v[613,5] = 35
v[614,5] = 39
v[615,5] = 11
v[616,5] = 59
v[617,5] = 59
v[618,5] = 1
v[619,5] = 57
v[620,5] = 11
v[621,5] = 5
v[622,5] = 57
v[623,5] = 13
v[624,5] = 31
v[625,5] = 13
v[626,5] = 11
v[627,5] = 55
v[628,5] = 45
v[629,5] = 9
v[630,5] = 55
v[631,5] = 55
v[632,5] = 19
v[633,5] = 25
v[634,5] = 41
v[635,5] = 23
v[636,5] = 45
v[637,5] = 29
v[638,5] = 63
v[639,5] = 59
v[640,5] = 27
v[641,5] = 39
v[642,5] = 21
v[643,5] = 37
v[644,5] = 7
v[645,5] = 61
v[646,5] = 49
v[647,5] = 35
v[648,5] = 39
v[649,5] = 9
v[650,5] = 29
v[651,5] = 7
v[652,5] = 25
v[653,5] = 23
v[654,5] = 57
v[655,5] = 5
v[656,5] = 19
v[657,5] = 15
v[658,5] = 33
v[659,5] = 49
v[660,5] = 37
v[661,5] = 25
v[662,5] = 17
v[663,5] = 45
v[664,5] = 29
v[665,5] = 15
v[666,5] = 25
v[667,5] = 3
v[668,5] = 3
v[669,5] = 49
v[670,5] = 11
v[671,5] = 39
v[672,5] = 15
v[673,5] = 19
v[674,5] = 57
v[675,5] = 39
v[676,5] = 15
v[677,5] = 11
v[678,5] = 3
v[679,5] = 57
v[680,5] = 31
v[681,5] = 55
v[682,5] = 61
v[683,5] = 19
v[684,5] = 5
v[685,5] = 41
v[686,5] = 35
v[687,5] = 59
v[688,5] = 61
v[689,5] = 39
v[690,5] = 41
v[691,5] = 53
v[692,5] = 53
v[693,5] = 63
v[694,5] = 31
v[695,5] = 9
v[696,5] = 59
v[697,5] = 13
v[698,5] = 35
v[699,5] = 55
v[700,5] = 41
v[701,5] = 49
v[702,5] = 5
v[703,5] = 41
v[704,5] = 25
v[705,5] = 27
v[706,5] = 43
v[707,5] = 5
v[708,5] = 5
v[709,5] = 43
v[710,5] = 5
v[711,5] = 5
v[712,5] = 17
v[713,5] = 5
v[714,5] = 15
v[715,5] = 27
v[716,5] = 29
v[717,5] = 17
v[718,5] = 9
v[719,5] = 3
v[720,5] = 55
v[721,5] = 31
v[722,5] = 1
v[723,5] = 45
v[724,5] = 45
v[725,5] = 13
v[726,5] = 57
v[727,5] = 17
v[728,5] = 3
v[729,5] = 61
v[730,5] = 15
v[731,5] = 49
v[732,5] = 15
v[733,5] = 47
v[734,5] = 9
v[735,5] = 37
v[736,5] = 45
v[737,5] = 9
v[738,5] = 51
v[739,5] = 61
v[740,5] = 21
v[741,5] = 33
v[742,5] = 11
v[743,5] = 21
v[744,5] = 63
v[745,5] = 63
v[746,5] = 47
v[747,5] = 57
v[748,5] = 61
v[749,5] = 49
v[750,5] = 9
v[751,5] = 59
v[752,5] = 19
v[753,5] = 29
v[754,5] = 21
v[755,5] = 23
v[756,5] = 55
v[757,5] = 23
v[758,5] = 43
v[759,5] = 41
v[760,5] = 57
v[761,5] = 9
v[762,5] = 39
v[763,5] = 27
v[764,5] = 41
v[765,5] = 35
v[766,5] = 61
v[767,5] = 29
v[768,5] = 57
v[769,5] = 63
v[770,5] = 21
v[771,5] = 31
v[772,5] = 59
v[773,5] = 35
v[774,5] = 49
v[775,5] = 3
v[776,5] = 49
v[777,5] = 47
v[778,5] = 49
v[779,5] = 33
v[780,5] = 21
v[781,5] = 19
v[782,5] = 21
v[783,5] = 35
v[784,5] = 11
v[785,5] = 17
v[786,5] = 37
v[787,5] = 23
v[788,5] = 59
v[789,5] = 13
v[790,5] = 37
v[791,5] = 35
v[792,5] = 55
v[793,5] = 57
v[794,5] = 1
v[795,5] = 29
v[796,5] = 45
v[797,5] = 11
v[798,5] = 1
v[799,5] = 15
v[800,5] = 9
v[801,5] = 33
v[802,5] = 19
v[803,5] = 53
v[804,5] = 43
v[805,5] = 39
v[806,5] = 23
v[807,5] = 7
v[808,5] = 13
v[809,5] = 13
v[810,5] = 1
v[811,5] = 19
v[812,5] = 41
v[813,5] = 55
v[814,5] = 1
v[815,5] = 13
v[816,5] = 15
v[817,5] = 59
v[818,5] = 55
v[819,5] = 15
v[820,5] = 3
v[821,5] = 57
v[822,5] = 37
v[823,5] = 31
v[824,5] = 17
v[825,5] = 1
v[826,5] = 3
v[827,5] = 21
v[828,5] = 29
v[829,5] = 25
v[830,5] = 55
v[831,5] = 9
v[832,5] = 37
v[833,5] = 33
v[834,5] = 53
v[835,5] = 41
v[836,5] = 51
v[837,5] = 19
v[838,5] = 57
v[839,5] = 13
v[840,5] = 63
v[841,5] = 43
v[842,5] = 19
v[843,5] = 7
v[844,5] = 13
v[845,5] = 37
v[846,5] = 33
v[847,5] = 19
v[848,5] = 15
v[849,5] = 63
v[850,5] = 51
v[851,5] = 11
v[852,5] = 49
v[853,5] = 23
v[854,5] = 57
v[855,5] = 47
v[856,5] = 51
v[857,5] = 15
v[858,5] = 53
v[859,5] = 41
v[860,5] = 1
v[861,5] = 15
v[862,5] = 37
v[863,5] = 61
v[864,5] = 11
v[865,5] = 35
v[866,5] = 29
v[867,5] = 33
v[868,5] = 23
v[869,5] = 55
v[870,5] = 11
v[871,5] = 59
v[872,5] = 19
v[873,5] = 61
v[874,5] = 61
v[875,5] = 45
v[876,5] = 13
v[877,5] = 49
v[878,5] = 13
v[879,5] = 63
v[880,5] = 5
v[881,5] = 61
v[882,5] = 5
v[883,5] = 31
v[884,5] = 17
v[885,5] = 61
v[886,5] = 63
v[887,5] = 13
v[888,5] = 27
v[889,5] = 57
v[890,5] = 1
v[891,5] = 21
v[892,5] = 5
v[893,5] = 11
v[894,5] = 39
v[895,5] = 57
v[896,5] = 51
v[897,5] = 53
v[898,5] = 39
v[899,5] = 25
v[900,5] = 41
v[901,5] = 39
v[902,5] = 37
v[903,5] = 23
v[904,5] = 31
v[905,5] = 25
v[906,5] = 33
v[907,5] = 17
v[908,5] = 57
v[909,5] = 29
v[910,5] = 27
v[911,5] = 23
v[912,5] = 47
v[913,5] = 41
v[914,5] = 29
v[915,5] = 19
v[916,5] = 47
v[917,5] = 41
v[918,5] = 25
v[919,5] = 5
v[920,5] = 51
v[921,5] = 43
v[922,5] = 39
v[923,5] = 29
v[924,5] = 7
v[925,5] = 31
v[926,5] = 45
v[927,5] = 51
v[928,5] = 49
v[929,5] = 55
v[930,5] = 17
v[931,5] = 43
v[932,5] = 49
v[933,5] = 45
v[934,5] = 9
v[935,5] = 29
v[936,5] = 3
v[937,5] = 5
v[938,5] = 47
v[939,5] = 9
v[940,5] = 15
v[941,5] = 19
v[942,5] = 51
v[943,5] = 45
v[944,5] = 57
v[945,5] = 63
v[946,5] = 9
v[947,5] = 21
v[948,5] = 59
v[949,5] = 3
v[950,5] = 9
v[951,5] = 13
v[952,5] = 45
v[953,5] = 23
v[954,5] = 15
v[955,5] = 31
v[956,5] = 21
v[957,5] = 15
v[958,5] = 51
v[959,5] = 35
v[960,5] = 9
v[961,5] = 11
v[962,5] = 61
v[963,5] = 23
v[964,5] = 53
v[965,5] = 29
v[966,5] = 51
v[967,5] = 45
v[968,5] = 31
v[969,5] = 29
v[970,5] = 5
v[971,5] = 35
v[972,5] = 29
v[973,5] = 53
v[974,5] = 35
v[975,5] = 17
v[976,5] = 59
v[977,5] = 55
v[978,5] = 27
v[979,5] = 51
v[980,5] = 59
v[981,5] = 27
v[982,5] = 47
v[983,5] = 15
v[984,5] = 29
v[985,5] = 37
v[986,5] = 7
v[987,5] = 49
v[988,5] = 55
v[989,5] = 5
v[990,5] = 19
v[991,5] = 45
v[992,5] = 29
v[993,5] = 19
v[994,5] = 57
v[995,5] = 33
v[996,5] = 53
v[997,5] = 45
v[998,5] = 21
v[999,5] = 9
v[1000,5] = 3
v[1001,5] = 35
v[1002,5] = 29
v[1003,5] = 43
v[1004,5] = 31
v[1005,5] = 39
v[1006,5] = 3
v[1007,5] = 45
v[1008,5] = 1
v[1009,5] = 41
v[1010,5] = 29
v[1011,5] = 5
v[1012,5] = 59
v[1013,5] = 41
v[1014,5] = 33
v[1015,5] = 35
v[1016,5] = 27
v[1017,5] = 19
v[1018,5] = 13
v[1019,5] = 25
v[1020,5] = 27
v[1021,5] = 43
v[1022,5] = 33
v[1023,5] = 35
v[1024,5] = 17
v[1025,5] = 17
v[1026,5] = 23
v[1027,5] = 7
v[1028,5] = 35
v[1029,5] = 15
v[1030,5] = 61
v[1031,5] = 61
v[1032,5] = 53
v[1033,5] = 5
v[1034,5] = 15
v[1035,5] = 23
v[1036,5] = 11
v[1037,5] = 13
v[1038,5] = 43
v[1039,5] = 55
v[1040,5] = 47
v[1041,5] = 25
v[1042,5] = 43
v[1043,5] = 15
v[1044,5] = 57
v[1045,5] = 45
v[1046,5] = 1
v[1047,5] = 49
v[1048,5] = 63
v[1049,5] = 57
v[1050,5] = 15
v[1051,5] = 31
v[1052,5] = 31
v[1053,5] = 7
v[1054,5] = 53
v[1055,5] = 27
v[1056,5] = 15
v[1057,5] = 47
v[1058,5] = 23
v[1059,5] = 7
v[1060,5] = 29
v[1061,5] = 53
v[1062,5] = 47
v[1063,5] = 9
v[1064,5] = 53
v[1065,5] = 3
v[1066,5] = 25
v[1067,5] = 55
v[1068,5] = 45
v[1069,5] = 63
v[1070,5] = 21
v[1071,5] = 17
v[1072,5] = 23
v[1073,5] = 31
v[1074,5] = 27
v[1075,5] = 27
v[1076,5] = 43
v[1077,5] = 63
v[1078,5] = 55
v[1079,5] = 63
v[1080,5] = 45
v[1081,5] = 51
v[1082,5] = 15
v[1083,5] = 27
v[1084,5] = 5
v[1085,5] = 37
v[1086,5] = 43
v[1087,5] = 11
v[1088,5] = 27
v[1089,5] = 5
v[1090,5] = 27
v[1091,5] = 59
v[1092,5] = 21
v[1093,5] = 7
v[1094,5] = 39
v[1095,5] = 27
v[1096,5] = 63
v[1097,5] = 35
v[1098,5] = 47
v[1099,5] = 55
v[1100,5] = 17
v[1101,5] = 17
v[1102,5] = 17
v[1103,5] = 3
v[1104,5] = 19
v[1105,5] = 21
v[1106,5] = 13
v[1107,5] = 49
v[1108,5] = 61
v[1109,5] = 39
v[1110,5] = 15
v[19,6] = 13
v[20,6] = 33
v[21,6] = 115
v[22,6] = 41
v[23,6] = 79
v[24,6] = 17
v[25,6] = 29
v[26,6] = 119
v[27,6] = 75
v[28,6] = 73
v[29,6] = 105
v[30,6] = 7
v[31,6] = 59
v[32,6] = 65
v[33,6] = 21
v[34,6] = 3
v[35,6] = 113
v[36,6] = 61
v[37,6] = 89
v[38,6] = 45
v[39,6] = 107
v[40,6] = 21
v[41,6] = 71
v[42,6] = 79
v[43,6] = 19
v[44,6] = 71
v[45,6] = 61
v[46,6] = 41
v[47,6] = 57
v[48,6] = 121
v[49,6] = 87
v[50,6] = 119
v[51,6] = 55
v[52,6] = 85
v[53,6] = 121
v[54,6] = 119
v[55,6] = 11
v[56,6] = 23
v[57,6] = 61
v[58,6] = 11
v[59,6] = 35
v[60,6] = 33
v[61,6] = 43
v[62,6] = 107
v[63,6] = 113
v[64,6] = 101
v[65,6] = 29
v[66,6] = 87
v[67,6] = 119
v[68,6] = 97
v[69,6] = 29
v[70,6] = 17
v[71,6] = 89
v[72,6] = 5
v[73,6] = 127
v[74,6] = 89
v[75,6] = 119
v[76,6] = 117
v[77,6] = 103
v[78,6] = 105
v[79,6] = 41
v[80,6] = 83
v[81,6] = 25
v[82,6] = 41
v[83,6] = 55
v[84,6] = 69
v[85,6] = 117
v[86,6] = 49
v[87,6] = 127
v[88,6] = 29
v[89,6] = 1
v[90,6] = 99
v[91,6] = 53
v[92,6] = 83
v[93,6] = 15
v[94,6] = 31
v[95,6] = 73
v[96,6] = 115
v[97,6] = 35
v[98,6] = 21
v[99,6] = 89
v[100,6] = 5
v[101,6] = 1
v[102,6] = 91
v[103,6] = 53
v[104,6] = 35
v[105,6] = 95
v[106,6] = 83
v[107,6] = 19
v[108,6] = 85
v[109,6] = 55
v[110,6] = 51
v[111,6] = 101
v[112,6] = 33
v[113,6] = 41
v[114,6] = 55
v[115,6] = 45
v[116,6] = 95
v[117,6] = 61
v[118,6] = 27
v[119,6] = 37
v[120,6] = 89
v[121,6] = 75
v[122,6] = 57
v[123,6] = 61
v[124,6] = 15
v[125,6] = 117
v[126,6] = 15
v[127,6] = 21
v[128,6] = 27
v[129,6] = 25
v[130,6] = 27
v[131,6] = 123
v[132,6] = 39
v[133,6] = 109
v[134,6] = 93
v[135,6] = 51
v[136,6] = 21
v[137,6] = 91
v[138,6] = 109
v[139,6] = 107
v[140,6] = 45
v[141,6] = 15
v[142,6] = 93
v[143,6] = 127
v[144,6] = 3
v[145,6] = 53
v[146,6] = 81
v[147,6] = 79
v[148,6] = 107
v[149,6] = 79
v[150,6] = 87
v[151,6] = 35
v[152,6] = 109
v[153,6] = 73
v[154,6] = 35
v[155,6] = 83
v[156,6] = 107
v[157,6] = 1
v[158,6] = 51
v[159,6] = 7
v[160,6] = 59
v[161,6] = 33
v[162,6] = 115
v[163,6] = 43
v[164,6] = 111
v[165,6] = 45
v[166,6] = 121
v[167,6] = 105
v[168,6] = 125
v[169,6] = 87
v[170,6] = 101
v[171,6] = 41
v[172,6] = 95
v[173,6] = 75
v[174,6] = 1
v[175,6] = 57
v[176,6] = 117
v[177,6] = 21
v[178,6] = 27
v[179,6] = 67
v[180,6] = 29
v[181,6] = 53
v[182,6] = 117
v[183,6] = 63
v[184,6] = 1
v[185,6] = 77
v[186,6] = 89
v[187,6] = 115
v[188,6] = 49
v[189,6] = 127
v[190,6] = 15
v[191,6] = 79
v[192,6] = 81
v[193,6] = 29
v[194,6] = 65
v[195,6] = 103
v[196,6] = 33
v[197,6] = 73
v[198,6] = 79
v[199,6] = 29
v[200,6] = 21
v[201,6] = 113
v[202,6] = 31
v[203,6] = 33
v[204,6] = 107
v[205,6] = 95
v[206,6] = 111
v[207,6] = 59
v[208,6] = 99
v[209,6] = 117
v[210,6] = 63
v[211,6] = 63
v[212,6] = 99
v[213,6] = 39
v[214,6] = 9
v[215,6] = 35
v[216,6] = 63
v[217,6] = 125
v[218,6] = 99
v[219,6] = 45
v[220,6] = 93
v[221,6] = 33
v[222,6] = 93
v[223,6] = 9
v[224,6] = 105
v[225,6] = 75
v[226,6] = 51
v[227,6] = 115
v[228,6] = 11
v[229,6] = 37
v[230,6] = 17
v[231,6] = 41
v[232,6] = 21
v[233,6] = 43
v[234,6] = 73
v[235,6] = 19
v[236,6] = 93
v[237,6] = 7
v[238,6] = 95
v[239,6] = 81
v[240,6] = 93
v[241,6] = 79
v[242,6] = 81
v[243,6] = 55
v[244,6] = 9
v[245,6] = 51
v[246,6] = 63
v[247,6] = 45
v[248,6] = 89
v[249,6] = 73
v[250,6] = 19
v[251,6] = 115
v[252,6] = 39
v[253,6] = 47
v[254,6] = 81
v[255,6] = 39
v[256,6] = 5
v[257,6] = 5
v[258,6] = 45
v[259,6] = 53
v[260,6] = 65
v[261,6] = 49
v[262,6] = 17
v[263,6] = 105
v[264,6] = 13
v[265,6] = 107
v[266,6] = 5
v[267,6] = 5
v[268,6] = 19
v[269,6] = 73
v[270,6] = 59
v[271,6] = 43
v[272,6] = 83
v[273,6] = 97
v[274,6] = 115
v[275,6] = 27
v[276,6] = 1
v[277,6] = 69
v[278,6] = 103
v[279,6] = 3
v[280,6] = 99
v[281,6] = 103
v[282,6] = 63
v[283,6] = 67
v[284,6] = 25
v[285,6] = 121
v[286,6] = 97
v[287,6] = 77
v[288,6] = 13
v[289,6] = 83
v[290,6] = 103
v[291,6] = 41
v[292,6] = 11
v[293,6] = 27
v[294,6] = 81
v[295,6] = 37
v[296,6] = 33
v[297,6] = 125
v[298,6] = 71
v[299,6] = 41
v[300,6] = 41
v[301,6] = 59
v[302,6] = 41
v[303,6] = 87
v[304,6] = 123
v[305,6] = 43
v[306,6] = 101
v[307,6] = 63
v[308,6] = 45
v[309,6] = 39
v[310,6] = 21
v[311,6] = 97
v[312,6] = 15
v[313,6] = 97
v[314,6] = 111
v[315,6] = 21
v[316,6] = 49
v[317,6] = 13
v[318,6] = 17
v[319,6] = 79
v[320,6] = 91
v[321,6] = 65
v[322,6] = 105
v[323,6] = 75
v[324,6] = 1
v[325,6] = 45
v[326,6] = 67
v[327,6] = 83
v[328,6] = 107
v[329,6] = 125
v[330,6] = 87
v[331,6] = 15
v[332,6] = 81
v[333,6] = 95
v[334,6] = 105
v[335,6] = 65
v[336,6] = 45
v[337,6] = 59
v[338,6] = 103
v[339,6] = 23
v[340,6] = 103
v[341,6] = 99
v[342,6] = 67
v[343,6] = 99
v[344,6] = 47
v[345,6] = 117
v[346,6] = 71
v[347,6] = 89
v[348,6] = 35
v[349,6] = 53
v[350,6] = 73
v[351,6] = 9
v[352,6] = 115
v[353,6] = 49
v[354,6] = 37
v[355,6] = 1
v[356,6] = 35
v[357,6] = 9
v[358,6] = 45
v[359,6] = 81
v[360,6] = 19
v[361,6] = 127
v[362,6] = 17
v[363,6] = 17
v[364,6] = 105
v[365,6] = 89
v[366,6] = 49
v[367,6] = 101
v[368,6] = 7
v[369,6] = 37
v[370,6] = 33
v[371,6] = 11
v[372,6] = 95
v[373,6] = 95
v[374,6] = 17
v[375,6] = 111
v[376,6] = 105
v[377,6] = 41
v[378,6] = 115
v[379,6] = 5
v[380,6] = 69
v[381,6] = 101
v[382,6] = 27
v[383,6] = 27
v[384,6] = 101
v[385,6] = 103
v[386,6] = 53
v[387,6] = 9
v[388,6] = 21
v[389,6] = 43
v[390,6] = 79
v[391,6] = 91
v[392,6] = 65
v[393,6] = 117
v[394,6] = 87
v[395,6] = 125
v[396,6] = 55
v[397,6] = 45
v[398,6] = 63
v[399,6] = 85
v[400,6] = 83
v[401,6] = 97
v[402,6] = 45
v[403,6] = 83
v[404,6] = 87
v[405,6] = 113
v[406,6] = 93
v[407,6] = 95
v[408,6] = 5
v[409,6] = 17
v[410,6] = 77
v[411,6] = 77
v[412,6] = 127
v[413,6] = 123
v[414,6] = 45
v[415,6] = 81
v[416,6] = 85
v[417,6] = 121
v[418,6] = 119
v[419,6] = 27
v[420,6] = 85
v[421,6] = 41
v[422,6] = 49
v[423,6] = 15
v[424,6] = 107
v[425,6] = 21
v[426,6] = 51
v[427,6] = 119
v[428,6] = 11
v[429,6] = 87
v[430,6] = 101
v[431,6] = 115
v[432,6] = 63
v[433,6] = 63
v[434,6] = 37
v[435,6] = 121
v[436,6] = 109
v[437,6] = 7
v[438,6] = 43
v[439,6] = 69
v[440,6] = 19
v[441,6] = 77
v[442,6] = 49
v[443,6] = 71
v[444,6] = 59
v[445,6] = 35
v[446,6] = 7
v[447,6] = 13
v[448,6] = 55
v[449,6] = 101
v[450,6] = 127
v[451,6] = 103
v[452,6] = 85
v[453,6] = 109
v[454,6] = 29
v[455,6] = 61
v[456,6] = 67
v[457,6] = 21
v[458,6] = 111
v[459,6] = 67
v[460,6] = 23
v[461,6] = 57
v[462,6] = 75
v[463,6] = 71
v[464,6] = 101
v[465,6] = 123
v[466,6] = 41
v[467,6] = 107
v[468,6] = 101
v[469,6] = 107
v[470,6] = 125
v[471,6] = 27
v[472,6] = 47
v[473,6] = 119
v[474,6] = 41
v[475,6] = 19
v[476,6] = 127
v[477,6] = 33
v[478,6] = 31
v[479,6] = 109
v[480,6] = 7
v[481,6] = 91
v[482,6] = 91
v[483,6] = 39
v[484,6] = 125
v[485,6] = 105
v[486,6] = 47
v[487,6] = 125
v[488,6] = 123
v[489,6] = 91
v[490,6] = 9
v[491,6] = 103
v[492,6] = 45
v[493,6] = 23
v[494,6] = 117
v[495,6] = 9
v[496,6] = 125
v[497,6] = 73
v[498,6] = 11
v[499,6] = 37
v[500,6] = 61
v[501,6] = 79
v[502,6] = 21
v[503,6] = 5
v[504,6] = 47
v[505,6] = 117
v[506,6] = 67
v[507,6] = 53
v[508,6] = 85
v[509,6] = 33
v[510,6] = 81
v[511,6] = 121
v[512,6] = 47
v[513,6] = 61
v[514,6] = 51
v[515,6] = 127
v[516,6] = 29
v[517,6] = 65
v[518,6] = 45
v[519,6] = 41
v[520,6] = 95
v[521,6] = 57
v[522,6] = 73
v[523,6] = 33
v[524,6] = 117
v[525,6] = 61
v[526,6] = 111
v[527,6] = 59
v[528,6] = 123
v[529,6] = 65
v[530,6] = 47
v[531,6] = 105
v[532,6] = 23
v[533,6] = 29
v[534,6] = 107
v[535,6] = 37
v[536,6] = 81
v[537,6] = 67
v[538,6] = 29
v[539,6] = 115
v[540,6] = 119
v[541,6] = 75
v[542,6] = 73
v[543,6] = 99
v[544,6] = 103
v[545,6] = 7
v[546,6] = 57
v[547,6] = 45
v[548,6] = 61
v[549,6] = 95
v[550,6] = 49
v[551,6] = 101
v[552,6] = 101
v[553,6] = 35
v[554,6] = 47
v[555,6] = 119
v[556,6] = 39
v[557,6] = 67
v[558,6] = 31
v[559,6] = 103
v[560,6] = 7
v[561,6] = 61
v[562,6] = 127
v[563,6] = 87
v[564,6] = 3
v[565,6] = 35
v[566,6] = 29
v[567,6] = 73
v[568,6] = 95
v[569,6] = 103
v[570,6] = 71
v[571,6] = 75
v[572,6] = 51
v[573,6] = 87
v[574,6] = 57
v[575,6] = 97
v[576,6] = 11
v[577,6] = 105
v[578,6] = 87
v[579,6] = 41
v[580,6] = 73
v[581,6] = 109
v[582,6] = 69
v[583,6] = 35
v[584,6] = 121
v[585,6] = 39
v[586,6] = 111
v[587,6] = 1
v[588,6] = 77
v[589,6] = 39
v[590,6] = 47
v[591,6] = 53
v[592,6] = 91
v[593,6] = 3
v[594,6] = 17
v[595,6] = 51
v[596,6] = 83
v[597,6] = 39
v[598,6] = 125
v[599,6] = 85
v[600,6] = 111
v[601,6] = 21
v[602,6] = 69
v[603,6] = 85
v[604,6] = 29
v[605,6] = 55
v[606,6] = 11
v[607,6] = 117
v[608,6] = 1
v[609,6] = 47
v[610,6] = 17
v[611,6] = 65
v[612,6] = 63
v[613,6] = 47
v[614,6] = 117
v[615,6] = 17
v[616,6] = 115
v[617,6] = 51
v[618,6] = 25
v[619,6] = 33
v[620,6] = 123
v[621,6] = 123
v[622,6] = 83
v[623,6] = 51
v[624,6] = 113
v[625,6] = 95
v[626,6] = 121
v[627,6] = 51
v[628,6] = 91
v[629,6] = 109
v[630,6] = 43
v[631,6] = 55
v[632,6] = 35
v[633,6] = 55
v[634,6] = 87
v[635,6] = 33
v[636,6] = 37
v[637,6] = 5
v[638,6] = 3
v[639,6] = 45
v[640,6] = 21
v[641,6] = 105
v[642,6] = 127
v[643,6] = 35
v[644,6] = 17
v[645,6] = 35
v[646,6] = 37
v[647,6] = 97
v[648,6] = 97
v[649,6] = 21
v[650,6] = 77
v[651,6] = 123
v[652,6] = 17
v[653,6] = 89
v[654,6] = 53
v[655,6] = 105
v[656,6] = 75
v[657,6] = 25
v[658,6] = 125
v[659,6] = 13
v[660,6] = 47
v[661,6] = 21
v[662,6] = 125
v[663,6] = 23
v[664,6] = 55
v[665,6] = 63
v[666,6] = 61
v[667,6] = 5
v[668,6] = 17
v[669,6] = 93
v[670,6] = 57
v[671,6] = 121
v[672,6] = 69
v[673,6] = 73
v[674,6] = 93
v[675,6] = 121
v[676,6] = 105
v[677,6] = 75
v[678,6] = 91
v[679,6] = 67
v[680,6] = 95
v[681,6] = 75
v[682,6] = 9
v[683,6] = 69
v[684,6] = 97
v[685,6] = 99
v[686,6] = 93
v[687,6] = 11
v[688,6] = 53
v[689,6] = 19
v[690,6] = 73
v[691,6] = 5
v[692,6] = 33
v[693,6] = 79
v[694,6] = 107
v[695,6] = 65
v[696,6] = 69
v[697,6] = 79
v[698,6] = 125
v[699,6] = 25
v[700,6] = 93
v[701,6] = 55
v[702,6] = 61
v[703,6] = 17
v[704,6] = 117
v[705,6] = 69
v[706,6] = 97
v[707,6] = 87
v[708,6] = 111
v[709,6] = 37
v[710,6] = 93
v[711,6] = 59
v[712,6] = 79
v[713,6] = 95
v[714,6] = 53
v[715,6] = 115
v[716,6] = 53
v[717,6] = 85
v[718,6] = 85
v[719,6] = 65
v[720,6] = 59
v[721,6] = 23
v[722,6] = 75
v[723,6] = 21
v[724,6] = 67
v[725,6] = 27
v[726,6] = 99
v[727,6] = 79
v[728,6] = 27
v[729,6] = 3
v[730,6] = 95
v[731,6] = 27
v[732,6] = 69
v[733,6] = 19
v[734,6] = 75
v[735,6] = 47
v[736,6] = 59
v[737,6] = 41
v[738,6] = 85
v[739,6] = 77
v[740,6] = 99
v[741,6] = 55
v[742,6] = 49
v[743,6] = 93
v[744,6] = 93
v[745,6] = 119
v[746,6] = 51
v[747,6] = 125
v[748,6] = 63
v[749,6] = 13
v[750,6] = 15
v[751,6] = 45
v[752,6] = 61
v[753,6] = 19
v[754,6] = 105
v[755,6] = 115
v[756,6] = 17
v[757,6] = 83
v[758,6] = 7
v[759,6] = 7
v[760,6] = 11
v[761,6] = 61
v[762,6] = 37
v[763,6] = 63
v[764,6] = 89
v[765,6] = 95
v[766,6] = 119
v[767,6] = 113
v[768,6] = 67
v[769,6] = 123
v[770,6] = 91
v[771,6] = 33
v[772,6] = 37
v[773,6] = 99
v[774,6] = 43
v[775,6] = 11
v[776,6] = 33
v[777,6] = 65
v[778,6] = 81
v[779,6] = 79
v[780,6] = 81
v[781,6] = 107
v[782,6] = 63
v[783,6] = 63
v[784,6] = 55
v[785,6] = 89
v[786,6] = 91
v[787,6] = 25
v[788,6] = 93
v[789,6] = 101
v[790,6] = 27
v[791,6] = 55
v[792,6] = 75
v[793,6] = 121
v[794,6] = 79
v[795,6] = 43
v[796,6] = 125
v[797,6] = 73
v[798,6] = 27
v[799,6] = 109
v[800,6] = 35
v[801,6] = 21
v[802,6] = 71
v[803,6] = 113
v[804,6] = 89
v[805,6] = 59
v[806,6] = 95
v[807,6] = 41
v[808,6] = 45
v[809,6] = 113
v[810,6] = 119
v[811,6] = 113
v[812,6] = 39
v[813,6] = 59
v[814,6] = 73
v[815,6] = 15
v[816,6] = 13
v[817,6] = 59
v[818,6] = 67
v[819,6] = 121
v[820,6] = 27
v[821,6] = 7
v[822,6] = 105
v[823,6] = 15
v[824,6] = 59
v[825,6] = 59
v[826,6] = 35
v[827,6] = 91
v[828,6] = 89
v[829,6] = 23
v[830,6] = 125
v[831,6] = 97
v[832,6] = 53
v[833,6] = 41
v[834,6] = 91
v[835,6] = 111
v[836,6] = 29
v[837,6] = 31
v[838,6] = 3
v[839,6] = 103
v[840,6] = 61
v[841,6] = 71
v[842,6] = 35
v[843,6] = 7
v[844,6] = 119
v[845,6] = 29
v[846,6] = 45
v[847,6] = 49
v[848,6] = 111
v[849,6] = 41
v[850,6] = 109
v[851,6] = 59
v[852,6] = 125
v[853,6] = 13
v[854,6] = 27
v[855,6] = 19
v[856,6] = 79
v[857,6] = 9
v[858,6] = 75
v[859,6] = 83
v[860,6] = 81
v[861,6] = 33
v[862,6] = 91
v[863,6] = 109
v[864,6] = 33
v[865,6] = 29
v[866,6] = 107
v[867,6] = 111
v[868,6] = 101
v[869,6] = 107
v[870,6] = 109
v[871,6] = 65
v[872,6] = 59
v[873,6] = 43
v[874,6] = 37
v[875,6] = 1
v[876,6] = 9
v[877,6] = 15
v[878,6] = 109
v[879,6] = 37
v[880,6] = 111
v[881,6] = 113
v[882,6] = 119
v[883,6] = 79
v[884,6] = 73
v[885,6] = 65
v[886,6] = 71
v[887,6] = 93
v[888,6] = 17
v[889,6] = 101
v[890,6] = 87
v[891,6] = 97
v[892,6] = 43
v[893,6] = 23
v[894,6] = 75
v[895,6] = 109
v[896,6] = 41
v[897,6] = 49
v[898,6] = 53
v[899,6] = 31
v[900,6] = 97
v[901,6] = 105
v[902,6] = 109
v[903,6] = 119
v[904,6] = 51
v[905,6] = 9
v[906,6] = 53
v[907,6] = 113
v[908,6] = 97
v[909,6] = 73
v[910,6] = 89
v[911,6] = 79
v[912,6] = 49
v[913,6] = 61
v[914,6] = 105
v[915,6] = 13
v[916,6] = 99
v[917,6] = 53
v[918,6] = 71
v[919,6] = 7
v[920,6] = 87
v[921,6] = 21
v[922,6] = 101
v[923,6] = 5
v[924,6] = 71
v[925,6] = 31
v[926,6] = 123
v[927,6] = 121
v[928,6] = 121
v[929,6] = 73
v[930,6] = 79
v[931,6] = 115
v[932,6] = 13
v[933,6] = 39
v[934,6] = 101
v[935,6] = 19
v[936,6] = 37
v[937,6] = 51
v[938,6] = 83
v[939,6] = 97
v[940,6] = 55
v[941,6] = 81
v[942,6] = 91
v[943,6] = 127
v[944,6] = 105
v[945,6] = 89
v[946,6] = 63
v[947,6] = 47
v[948,6] = 49
v[949,6] = 75
v[950,6] = 37
v[951,6] = 77
v[952,6] = 15
v[953,6] = 49
v[954,6] = 107
v[955,6] = 23
v[956,6] = 23
v[957,6] = 35
v[958,6] = 19
v[959,6] = 69
v[960,6] = 17
v[961,6] = 59
v[962,6] = 63
v[963,6] = 73
v[964,6] = 29
v[965,6] = 125
v[966,6] = 61
v[967,6] = 65
v[968,6] = 95
v[969,6] = 101
v[970,6] = 81
v[971,6] = 57
v[972,6] = 69
v[973,6] = 83
v[974,6] = 37
v[975,6] = 11
v[976,6] = 37
v[977,6] = 95
v[978,6] = 1
v[979,6] = 73
v[980,6] = 27
v[981,6] = 29
v[982,6] = 57
v[983,6] = 7
v[984,6] = 65
v[985,6] = 83
v[986,6] = 99
v[987,6] = 69
v[988,6] = 19
v[989,6] = 103
v[990,6] = 43
v[991,6] = 95
v[992,6] = 25
v[993,6] = 19
v[994,6] = 103
v[995,6] = 41
v[996,6] = 125
v[997,6] = 97
v[998,6] = 71
v[999,6] = 105
v[1000,6] = 83
v[1001,6] = 83
v[1002,6] = 61
v[1003,6] = 39
v[1004,6] = 9
v[1005,6] = 45
v[1006,6] = 117
v[1007,6] = 63
v[1008,6] = 31
v[1009,6] = 5
v[1010,6] = 117
v[1011,6] = 67
v[1012,6] = 125
v[1013,6] = 41
v[1014,6] = 117
v[1015,6] = 43
v[1016,6] = 77
v[1017,6] = 97
v[1018,6] = 15
v[1019,6] = 29
v[1020,6] = 5
v[1021,6] = 59
v[1022,6] = 25
v[1023,6] = 63
v[1024,6] = 87
v[1025,6] = 39
v[1026,6] = 39
v[1027,6] = 77
v[1028,6] = 85
v[1029,6] = 37
v[1030,6] = 81
v[1031,6] = 73
v[1032,6] = 89
v[1033,6] = 29
v[1034,6] = 125
v[1035,6] = 109
v[1036,6] = 21
v[1037,6] = 23
v[1038,6] = 119
v[1039,6] = 105
v[1040,6] = 43
v[1041,6] = 93
v[1042,6] = 97
v[1043,6] = 15
v[1044,6] = 125
v[1045,6] = 29
v[1046,6] = 51
v[1047,6] = 69
v[1048,6] = 37
v[1049,6] = 45
v[1050,6] = 31
v[1051,6] = 75
v[1052,6] = 109
v[1053,6] = 119
v[1054,6] = 53
v[1055,6] = 5
v[1056,6] = 101
v[1057,6] = 125
v[1058,6] = 121
v[1059,6] = 35
v[1060,6] = 29
v[1061,6] = 7
v[1062,6] = 63
v[1063,6] = 17
v[1064,6] = 63
v[1065,6] = 13
v[1066,6] = 69
v[1067,6] = 15
v[1068,6] = 105
v[1069,6] = 51
v[1070,6] = 127
v[1071,6] = 105
v[1072,6] = 9
v[1073,6] = 57
v[1074,6] = 95
v[1075,6] = 59
v[1076,6] = 109
v[1077,6] = 35
v[1078,6] = 49
v[1079,6] = 23
v[1080,6] = 33
v[1081,6] = 107
v[1082,6] = 55
v[1083,6] = 33
v[1084,6] = 57
v[1085,6] = 79
v[1086,6] = 73
v[1087,6] = 69
v[1088,6] = 59
v[1089,6] = 107
v[1090,6] = 55
v[1091,6] = 11
v[1092,6] = 63
v[1093,6] = 95
v[1094,6] = 103
v[1095,6] = 23
v[1096,6] = 125
v[1097,6] = 91
v[1098,6] = 31
v[1099,6] = 91
v[1100,6] = 51
v[1101,6] = 65
v[1102,6] = 61
v[1103,6] = 75
v[1104,6] = 69
v[1105,6] = 107
v[1106,6] = 65
v[1107,6] = 101
v[1108,6] = 59
v[1109,6] = 35
v[1110,6] = 15
v[37,7] = 7
v[38,7] = 23
v[39,7] = 39
v[40,7] = 217
v[41,7] = 141
v[42,7] = 27
v[43,7] = 53
v[44,7] = 181
v[45,7] = 169
v[46,7] = 35
v[47,7] = 15
v[48,7] = 207
v[49,7] = 45
v[50,7] = 247
v[51,7] = 185
v[52,7] = 117
v[53,7] = 41
v[54,7] = 81
v[55,7] = 223
v[56,7] = 151
v[57,7] = 81
v[58,7] = 189
v[59,7] = 61
v[60,7] = 95
v[61,7] = 185
v[62,7] = 23
v[63,7] = 73
v[64,7] = 113
v[65,7] = 239
v[66,7] = 85
v[67,7] = 9
v[68,7] = 201
v[69,7] = 83
v[70,7] = 53
v[71,7] = 183
v[72,7] = 203
v[73,7] = 91
v[74,7] = 149
v[75,7] = 101
v[76,7] = 13
v[77,7] = 111
v[78,7] = 239
v[79,7] = 3
v[80,7] = 205
v[81,7] = 253
v[82,7] = 247
v[83,7] = 121
v[84,7] = 189
v[85,7] = 169
v[86,7] = 179
v[87,7] = 197
v[88,7] = 175
v[89,7] = 217
v[90,7] = 249
v[91,7] = 195
v[92,7] = 95
v[93,7] = 63
v[94,7] = 19
v[95,7] = 7
v[96,7] = 5
v[97,7] = 75
v[98,7] = 217
v[99,7] = 245
v[100,7] = 111
v[101,7] = 189
v[102,7] = 165
v[103,7] = 169
v[104,7] = 141
v[105,7] = 221
v[106,7] = 249
v[107,7] = 159
v[108,7] = 253
v[109,7] = 207
v[110,7] = 249
v[111,7] = 219
v[112,7] = 23
v[113,7] = 49
v[114,7] = 127
v[115,7] = 237
v[116,7] = 5
v[117,7] = 25
v[118,7] = 177
v[119,7] = 37
v[120,7] = 103
v[121,7] = 65
v[122,7] = 167
v[123,7] = 81
v[124,7] = 87
v[125,7] = 119
v[126,7] = 45
v[127,7] = 79
v[128,7] = 143
v[129,7] = 57
v[130,7] = 79
v[131,7] = 187
v[132,7] = 143
v[133,7] = 183
v[134,7] = 75
v[135,7] = 97
v[136,7] = 211
v[137,7] = 149
v[138,7] = 175
v[139,7] = 37
v[140,7] = 135
v[141,7] = 189
v[142,7] = 225
v[143,7] = 241
v[144,7] = 63
v[145,7] = 33
v[146,7] = 43
v[147,7] = 13
v[148,7] = 73
v[149,7] = 213
v[150,7] = 57
v[151,7] = 239
v[152,7] = 183
v[153,7] = 117
v[154,7] = 21
v[155,7] = 29
v[156,7] = 115
v[157,7] = 43
v[158,7] = 205
v[159,7] = 223
v[160,7] = 15
v[161,7] = 3
v[162,7] = 159
v[163,7] = 51
v[164,7] = 101
v[165,7] = 127
v[166,7] = 99
v[167,7] = 239
v[168,7] = 171
v[169,7] = 113
v[170,7] = 171
v[171,7] = 119
v[172,7] = 189
v[173,7] = 245
v[174,7] = 201
v[175,7] = 27
v[176,7] = 185
v[177,7] = 229
v[178,7] = 105
v[179,7] = 153
v[180,7] = 189
v[181,7] = 33
v[182,7] = 35
v[183,7] = 137
v[184,7] = 77
v[185,7] = 97
v[186,7] = 17
v[187,7] = 181
v[188,7] = 55
v[189,7] = 197
v[190,7] = 201
v[191,7] = 155
v[192,7] = 37
v[193,7] = 197
v[194,7] = 137
v[195,7] = 223
v[196,7] = 25
v[197,7] = 179
v[198,7] = 91
v[199,7] = 23
v[200,7] = 235
v[201,7] = 53
v[202,7] = 253
v[203,7] = 49
v[204,7] = 181
v[205,7] = 249
v[206,7] = 53
v[207,7] = 173
v[208,7] = 97
v[209,7] = 247
v[210,7] = 67
v[211,7] = 115
v[212,7] = 103
v[213,7] = 159
v[214,7] = 239
v[215,7] = 69
v[216,7] = 173
v[217,7] = 217
v[218,7] = 95
v[219,7] = 221
v[220,7] = 247
v[221,7] = 97
v[222,7] = 91
v[223,7] = 123
v[224,7] = 223
v[225,7] = 213
v[226,7] = 129
v[227,7] = 181
v[228,7] = 87
v[229,7] = 239
v[230,7] = 85
v[231,7] = 89
v[232,7] = 249
v[233,7] = 141
v[234,7] = 39
v[235,7] = 57
v[236,7] = 249
v[237,7] = 71
v[238,7] = 101
v[239,7] = 159
v[240,7] = 33
v[241,7] = 137
v[242,7] = 189
v[243,7] = 71
v[244,7] = 253
v[245,7] = 205
v[246,7] = 171
v[247,7] = 13
v[248,7] = 249
v[249,7] = 109
v[250,7] = 131
v[251,7] = 199
v[252,7] = 189
v[253,7] = 179
v[254,7] = 31
v[255,7] = 99
v[256,7] = 113
v[257,7] = 41
v[258,7] = 173
v[259,7] = 23
v[260,7] = 189
v[261,7] = 197
v[262,7] = 3
v[263,7] = 135
v[264,7] = 9
v[265,7] = 95
v[266,7] = 195
v[267,7] = 27
v[268,7] = 183
v[269,7] = 1
v[270,7] = 123
v[271,7] = 73
v[272,7] = 53
v[273,7] = 99
v[274,7] = 197
v[275,7] = 59
v[276,7] = 27
v[277,7] = 101
v[278,7] = 55
v[279,7] = 193
v[280,7] = 31
v[281,7] = 61
v[282,7] = 119
v[283,7] = 11
v[284,7] = 7
v[285,7] = 255
v[286,7] = 233
v[287,7] = 53
v[288,7] = 157
v[289,7] = 193
v[290,7] = 97
v[291,7] = 83
v[292,7] = 65
v[293,7] = 81
v[294,7] = 239
v[295,7] = 167
v[296,7] = 69
v[297,7] = 71
v[298,7] = 109
v[299,7] = 97
v[300,7] = 137
v[301,7] = 71
v[302,7] = 193
v[303,7] = 189
v[304,7] = 115
v[305,7] = 79
v[306,7] = 205
v[307,7] = 37
v[308,7] = 227
v[309,7] = 53
v[310,7] = 33
v[311,7] = 91
v[312,7] = 229
v[313,7] = 245
v[314,7] = 105
v[315,7] = 77
v[316,7] = 229
v[317,7] = 161
v[318,7] = 103
v[319,7] = 93
v[320,7] = 13
v[321,7] = 161
v[322,7] = 229
v[323,7] = 223
v[324,7] = 69
v[325,7] = 15
v[326,7] = 25
v[327,7] = 23
v[328,7] = 233
v[329,7] = 93
v[330,7] = 25
v[331,7] = 217
v[332,7] = 247
v[333,7] = 61
v[334,7] = 75
v[335,7] = 27
v[336,7] = 9
v[337,7] = 223
v[338,7] = 213
v[339,7] = 55
v[340,7] = 197
v[341,7] = 145
v[342,7] = 89
v[343,7] = 199
v[344,7] = 41
v[345,7] = 201
v[346,7] = 5
v[347,7] = 149
v[348,7] = 35
v[349,7] = 119
v[350,7] = 183
v[351,7] = 53
v[352,7] = 11
v[353,7] = 13
v[354,7] = 3
v[355,7] = 179
v[356,7] = 229
v[357,7] = 43
v[358,7] = 55
v[359,7] = 187
v[360,7] = 233
v[361,7] = 47
v[362,7] = 133
v[363,7] = 91
v[364,7] = 47
v[365,7] = 71
v[366,7] = 93
v[367,7] = 105
v[368,7] = 145
v[369,7] = 45
v[370,7] = 255
v[371,7] = 221
v[372,7] = 115
v[373,7] = 175
v[374,7] = 19
v[375,7] = 129
v[376,7] = 5
v[377,7] = 209
v[378,7] = 197
v[379,7] = 57
v[380,7] = 177
v[381,7] = 115
v[382,7] = 187
v[383,7] = 119
v[384,7] = 77
v[385,7] = 211
v[386,7] = 111
v[387,7] = 33
v[388,7] = 113
v[389,7] = 23
v[390,7] = 87
v[391,7] = 137
v[392,7] = 41
v[393,7] = 7
v[394,7] = 83
v[395,7] = 43
v[396,7] = 121
v[397,7] = 145
v[398,7] = 5
v[399,7] = 219
v[400,7] = 27
v[401,7] = 11
v[402,7] = 111
v[403,7] = 207
v[404,7] = 55
v[405,7] = 97
v[406,7] = 63
v[407,7] = 229
v[408,7] = 53
v[409,7] = 33
v[410,7] = 149
v[411,7] = 23
v[412,7] = 187
v[413,7] = 153
v[414,7] = 91
v[415,7] = 193
v[416,7] = 183
v[417,7] = 59
v[418,7] = 211
v[419,7] = 93
v[420,7] = 139
v[421,7] = 59
v[422,7] = 179
v[423,7] = 163
v[424,7] = 209
v[425,7] = 77
v[426,7] = 39
v[427,7] = 111
v[428,7] = 79
v[429,7] = 229
v[430,7] = 85
v[431,7] = 237
v[432,7] = 199
v[433,7] = 137
v[434,7] = 147
v[435,7] = 25
v[436,7] = 73
v[437,7] = 121
v[438,7] = 129
v[439,7] = 83
v[440,7] = 87
v[441,7] = 93
v[442,7] = 205
v[443,7] = 167
v[444,7] = 53
v[445,7] = 107
v[446,7] = 229
v[447,7] = 213
v[448,7] = 95
v[449,7] = 219
v[450,7] = 109
v[451,7] = 175
v[452,7] = 13
v[453,7] = 209
v[454,7] = 97
v[455,7] = 61
v[456,7] = 147
v[457,7] = 19
v[458,7] = 13
v[459,7] = 123
v[460,7] = 73
v[461,7] = 35
v[462,7] = 141
v[463,7] = 81
v[464,7] = 19
v[465,7] = 171
v[466,7] = 255
v[467,7] = 111
v[468,7] = 107
v[469,7] = 233
v[470,7] = 113
v[471,7] = 133
v[472,7] = 89
v[473,7] = 9
v[474,7] = 231
v[475,7] = 95
v[476,7] = 69
v[477,7] = 33
v[478,7] = 1
v[479,7] = 253
v[480,7] = 219
v[481,7] = 253
v[482,7] = 247
v[483,7] = 129
v[484,7] = 11
v[485,7] = 251
v[486,7] = 221
v[487,7] = 153
v[488,7] = 35
v[489,7] = 103
v[490,7] = 239
v[491,7] = 7
v[492,7] = 27
v[493,7] = 235
v[494,7] = 181
v[495,7] = 5
v[496,7] = 207
v[497,7] = 53
v[498,7] = 149
v[499,7] = 155
v[500,7] = 225
v[501,7] = 165
v[502,7] = 137
v[503,7] = 155
v[504,7] = 201
v[505,7] = 97
v[506,7] = 245
v[507,7] = 203
v[508,7] = 47
v[509,7] = 39
v[510,7] = 35
v[511,7] = 105
v[512,7] = 239
v[513,7] = 49
v[514,7] = 15
v[515,7] = 253
v[516,7] = 7
v[517,7] = 237
v[518,7] = 213
v[519,7] = 55
v[520,7] = 87
v[521,7] = 199
v[522,7] = 27
v[523,7] = 175
v[524,7] = 49
v[525,7] = 41
v[526,7] = 229
v[527,7] = 85
v[528,7] = 3
v[529,7] = 149
v[530,7] = 179
v[531,7] = 129
v[532,7] = 185
v[533,7] = 249
v[534,7] = 197
v[535,7] = 15
v[536,7] = 97
v[537,7] = 197
v[538,7] = 139
v[539,7] = 203
v[540,7] = 63
v[541,7] = 33
v[542,7] = 251
v[543,7] = 217
v[544,7] = 199
v[545,7] = 199
v[546,7] = 99
v[547,7] = 249
v[548,7] = 33
v[549,7] = 229
v[550,7] = 177
v[551,7] = 13
v[552,7] = 209
v[553,7] = 147
v[554,7] = 97
v[555,7] = 31
v[556,7] = 125
v[557,7] = 177
v[558,7] = 137
v[559,7] = 187
v[560,7] = 11
v[561,7] = 91
v[562,7] = 223
v[563,7] = 29
v[564,7] = 169
v[565,7] = 231
v[566,7] = 59
v[567,7] = 31
v[568,7] = 163
v[569,7] = 41
v[570,7] = 57
v[571,7] = 87
v[572,7] = 247
v[573,7] = 25
v[574,7] = 127
v[575,7] = 101
v[576,7] = 207
v[577,7] = 187
v[578,7] = 73
v[579,7] = 61
v[580,7] = 105
v[581,7] = 27
v[582,7] = 91
v[583,7] = 171
v[584,7] = 243
v[585,7] = 33
v[586,7] = 3
v[587,7] = 1
v[588,7] = 21
v[589,7] = 229
v[590,7] = 93
v[591,7] = 71
v[592,7] = 61
v[593,7] = 37
v[594,7] = 183
v[595,7] = 65
v[596,7] = 211
v[597,7] = 53
v[598,7] = 11
v[599,7] = 151
v[600,7] = 165
v[601,7] = 47
v[602,7] = 5
v[603,7] = 129
v[604,7] = 79
v[605,7] = 101
v[606,7] = 147
v[607,7] = 169
v[608,7] = 181
v[609,7] = 19
v[610,7] = 95
v[611,7] = 77
v[612,7] = 139
v[613,7] = 197
v[614,7] = 219
v[615,7] = 97
v[616,7] = 239
v[617,7] = 183
v[618,7] = 143
v[619,7] = 9
v[620,7] = 13
v[621,7] = 209
v[622,7] = 23
v[623,7] = 215
v[624,7] = 53
v[625,7] = 137
v[626,7] = 203
v[627,7] = 19
v[628,7] = 151
v[629,7] = 171
v[630,7] = 133
v[631,7] = 219
v[632,7] = 231
v[633,7] = 3
v[634,7] = 15
v[635,7] = 253
v[636,7] = 225
v[637,7] = 33
v[638,7] = 111
v[639,7] = 183
v[640,7] = 213
v[641,7] = 169
v[642,7] = 119
v[643,7] = 111
v[644,7] = 15
v[645,7] = 201
v[646,7] = 123
v[647,7] = 121
v[648,7] = 225
v[649,7] = 113
v[650,7] = 113
v[651,7] = 225
v[652,7] = 161
v[653,7] = 165
v[654,7] = 1
v[655,7] = 139
v[656,7] = 55
v[657,7] = 3
v[658,7] = 93
v[659,7] = 217
v[660,7] = 193
v[661,7] = 97
v[662,7] = 29
v[663,7] = 69
v[664,7] = 231
v[665,7] = 161
v[666,7] = 93
v[667,7] = 69
v[668,7] = 143
v[669,7] = 137
v[670,7] = 9
v[671,7] = 87
v[672,7] = 183
v[673,7] = 113
v[674,7] = 183
v[675,7] = 73
v[676,7] = 215
v[677,7] = 137
v[678,7] = 89
v[679,7] = 251
v[680,7] = 163
v[681,7] = 41
v[682,7] = 227
v[683,7] = 145
v[684,7] = 57
v[685,7] = 81
v[686,7] = 57
v[687,7] = 11
v[688,7] = 135
v[689,7] = 145
v[690,7] = 161
v[691,7] = 175
v[692,7] = 159
v[693,7] = 25
v[694,7] = 55
v[695,7] = 167
v[696,7] = 157
v[697,7] = 211
v[698,7] = 97
v[699,7] = 247
v[700,7] = 249
v[701,7] = 23
v[702,7] = 129
v[703,7] = 159
v[704,7] = 71
v[705,7] = 197
v[706,7] = 127
v[707,7] = 141
v[708,7] = 219
v[709,7] = 5
v[710,7] = 233
v[711,7] = 131
v[712,7] = 217
v[713,7] = 101
v[714,7] = 131
v[715,7] = 33
v[716,7] = 157
v[717,7] = 173
v[718,7] = 69
v[719,7] = 207
v[720,7] = 239
v[721,7] = 81
v[722,7] = 205
v[723,7] = 11
v[724,7] = 41
v[725,7] = 169
v[726,7] = 65
v[727,7] = 193
v[728,7] = 77
v[729,7] = 201
v[730,7] = 173
v[731,7] = 1
v[732,7] = 221
v[733,7] = 157
v[734,7] = 1
v[735,7] = 15
v[736,7] = 113
v[737,7] = 147
v[738,7] = 137
v[739,7] = 205
v[740,7] = 225
v[741,7] = 73
v[742,7] = 45
v[743,7] = 49
v[744,7] = 149
v[745,7] = 113
v[746,7] = 253
v[747,7] = 99
v[748,7] = 17
v[749,7] = 119
v[750,7] = 105
v[751,7] = 117
v[752,7] = 129
v[753,7] = 243
v[754,7] = 75
v[755,7] = 203
v[756,7] = 53
v[757,7] = 29
v[758,7] = 247
v[759,7] = 35
v[760,7] = 247
v[761,7] = 171
v[762,7] = 31
v[763,7] = 199
v[764,7] = 213
v[765,7] = 29
v[766,7] = 251
v[767,7] = 7
v[768,7] = 251
v[769,7] = 187
v[770,7] = 91
v[771,7] = 11
v[772,7] = 149
v[773,7] = 13
v[774,7] = 205
v[775,7] = 37
v[776,7] = 249
v[777,7] = 137
v[778,7] = 139
v[779,7] = 9
v[780,7] = 7
v[781,7] = 113
v[782,7] = 183
v[783,7] = 205
v[784,7] = 187
v[785,7] = 39
v[786,7] = 3
v[787,7] = 79
v[788,7] = 155
v[789,7] = 227
v[790,7] = 89
v[791,7] = 185
v[792,7] = 51
v[793,7] = 127
v[794,7] = 63
v[795,7] = 83
v[796,7] = 41
v[797,7] = 133
v[798,7] = 183
v[799,7] = 181
v[800,7] = 127
v[801,7] = 19
v[802,7] = 255
v[803,7] = 219
v[804,7] = 59
v[805,7] = 251
v[806,7] = 3
v[807,7] = 187
v[808,7] = 57
v[809,7] = 217
v[810,7] = 115
v[811,7] = 217
v[812,7] = 229
v[813,7] = 181
v[814,7] = 185
v[815,7] = 149
v[816,7] = 83
v[817,7] = 115
v[818,7] = 11
v[819,7] = 123
v[820,7] = 19
v[821,7] = 109
v[822,7] = 165
v[823,7] = 103
v[824,7] = 123
v[825,7] = 219
v[826,7] = 129
v[827,7] = 155
v[828,7] = 207
v[829,7] = 177
v[830,7] = 9
v[831,7] = 49
v[832,7] = 181
v[833,7] = 231
v[834,7] = 33
v[835,7] = 233
v[836,7] = 67
v[837,7] = 155
v[838,7] = 41
v[839,7] = 9
v[840,7] = 95
v[841,7] = 123
v[842,7] = 65
v[843,7] = 117
v[844,7] = 249
v[845,7] = 85
v[846,7] = 169
v[847,7] = 129
v[848,7] = 241
v[849,7] = 173
v[850,7] = 251
v[851,7] = 225
v[852,7] = 147
v[853,7] = 165
v[854,7] = 69
v[855,7] = 81
v[856,7] = 239
v[857,7] = 95
v[858,7] = 23
v[859,7] = 83
v[860,7] = 227
v[861,7] = 249
v[862,7] = 143
v[863,7] = 171
v[864,7] = 193
v[865,7] = 9
v[866,7] = 21
v[867,7] = 57
v[868,7] = 73
v[869,7] = 97
v[870,7] = 57
v[871,7] = 29
v[872,7] = 239
v[873,7] = 151
v[874,7] = 159
v[875,7] = 191
v[876,7] = 47
v[877,7] = 51
v[878,7] = 1
v[879,7] = 223
v[880,7] = 251
v[881,7] = 251
v[882,7] = 151
v[883,7] = 41
v[884,7] = 119
v[885,7] = 127
v[886,7] = 131
v[887,7] = 33
v[888,7] = 209
v[889,7] = 123
v[890,7] = 53
v[891,7] = 241
v[892,7] = 25
v[893,7] = 31
v[894,7] = 183
v[895,7] = 107
v[896,7] = 25
v[897,7] = 115
v[898,7] = 39
v[899,7] = 11
v[900,7] = 213
v[901,7] = 239
v[902,7] = 219
v[903,7] = 109
v[904,7] = 185
v[905,7] = 35
v[906,7] = 133
v[907,7] = 123
v[908,7] = 185
v[909,7] = 27
v[910,7] = 55
v[911,7] = 245
v[912,7] = 61
v[913,7] = 75
v[914,7] = 205
v[915,7] = 213
v[916,7] = 169
v[917,7] = 163
v[918,7] = 63
v[919,7] = 55
v[920,7] = 49
v[921,7] = 83
v[922,7] = 195
v[923,7] = 51
v[924,7] = 31
v[925,7] = 41
v[926,7] = 15
v[927,7] = 203
v[928,7] = 41
v[929,7] = 63
v[930,7] = 127
v[931,7] = 161
v[932,7] = 5
v[933,7] = 143
v[934,7] = 7
v[935,7] = 199
v[936,7] = 251
v[937,7] = 95
v[938,7] = 75
v[939,7] = 101
v[940,7] = 15
v[941,7] = 43
v[942,7] = 237
v[943,7] = 197
v[944,7] = 117
v[945,7] = 167
v[946,7] = 155
v[947,7] = 21
v[948,7] = 83
v[949,7] = 205
v[950,7] = 255
v[951,7] = 49
v[952,7] = 101
v[953,7] = 213
v[954,7] = 237
v[955,7] = 135
v[956,7] = 135
v[957,7] = 21
v[958,7] = 73
v[959,7] = 93
v[960,7] = 115
v[961,7] = 7
v[962,7] = 85
v[963,7] = 223
v[964,7] = 237
v[965,7] = 79
v[966,7] = 89
v[967,7] = 5
v[968,7] = 57
v[969,7] = 239
v[970,7] = 67
v[971,7] = 65
v[972,7] = 201
v[973,7] = 155
v[974,7] = 71
v[975,7] = 85
v[976,7] = 195
v[977,7] = 89
v[978,7] = 181
v[979,7] = 119
v[980,7] = 135
v[981,7] = 147
v[982,7] = 237
v[983,7] = 173
v[984,7] = 41
v[985,7] = 155
v[986,7] = 67
v[987,7] = 113
v[988,7] = 111
v[989,7] = 21
v[990,7] = 183
v[991,7] = 23
v[992,7] = 103
v[993,7] = 207
v[994,7] = 253
v[995,7] = 69
v[996,7] = 219
v[997,7] = 205
v[998,7] = 195
v[999,7] = 43
v[1000,7] = 197
v[1001,7] = 229
v[1002,7] = 139
v[1003,7] = 177
v[1004,7] = 129
v[1005,7] = 69
v[1006,7] = 97
v[1007,7] = 201
v[1008,7] = 163
v[1009,7] = 189
v[1010,7] = 11
v[1011,7] = 99
v[1012,7] = 91
v[1013,7] = 253
v[1014,7] = 239
v[1015,7] = 91
v[1016,7] = 145
v[1017,7] = 19
v[1018,7] = 179
v[1019,7] = 231
v[1020,7] = 121
v[1021,7] = 7
v[1022,7] = 225
v[1023,7] = 237
v[1024,7] = 125
v[1025,7] = 191
v[1026,7] = 119
v[1027,7] = 59
v[1028,7] = 175
v[1029,7] = 237
v[1030,7] = 131
v[1031,7] = 79
v[1032,7] = 43
v[1033,7] = 45
v[1034,7] = 205
v[1035,7] = 199
v[1036,7] = 251
v[1037,7] = 153
v[1038,7] = 207
v[1039,7] = 37
v[1040,7] = 179
v[1041,7] = 113
v[1042,7] = 255
v[1043,7] = 107
v[1044,7] = 217
v[1045,7] = 61
v[1046,7] = 7
v[1047,7] = 181
v[1048,7] = 247
v[1049,7] = 31
v[1050,7] = 13
v[1051,7] = 113
v[1052,7] = 145
v[1053,7] = 107
v[1054,7] = 233
v[1055,7] = 233
v[1056,7] = 43
v[1057,7] = 79
v[1058,7] = 23
v[1059,7] = 169
v[1060,7] = 137
v[1061,7] = 129
v[1062,7] = 183
v[1063,7] = 53
v[1064,7] = 91
v[1065,7] = 55
v[1066,7] = 103
v[1067,7] = 223
v[1068,7] = 87
v[1069,7] = 177
v[1070,7] = 157
v[1071,7] = 79
v[1072,7] = 213
v[1073,7] = 139
v[1074,7] = 183
v[1075,7] = 231
v[1076,7] = 205
v[1077,7] = 143
v[1078,7] = 129
v[1079,7] = 243
v[1080,7] = 205
v[1081,7] = 93
v[1082,7] = 59
v[1083,7] = 15
v[1084,7] = 89
v[1085,7] = 9
v[1086,7] = 11
v[1087,7] = 47
v[1088,7] = 133
v[1089,7] = 227
v[1090,7] = 75
v[1091,7] = 9
v[1092,7] = 91
v[1093,7] = 19
v[1094,7] = 171
v[1095,7] = 163
v[1096,7] = 79
v[1097,7] = 7
v[1098,7] = 103
v[1099,7] = 5
v[1100,7] = 119
v[1101,7] = 155
v[1102,7] = 75
v[1103,7] = 11
v[1104,7] = 71
v[1105,7] = 95
v[1106,7] = 17
v[1107,7] = 13
v[1108,7] = 243
v[1109,7] = 207
v[1110,7] = 187
v[53,8] = 235
v[54,8] = 307
v[55,8] = 495
v[56,8] = 417
v[57,8] = 57
v[58,8] = 151
v[59,8] = 19
v[60,8] = 119
v[61,8] = 375
v[62,8] = 451
v[63,8] = 55
v[64,8] = 449
v[65,8] = 501
v[66,8] = 53
v[67,8] = 185
v[68,8] = 317
v[69,8] = 17
v[70,8] = 21
v[71,8] = 487
v[72,8] = 13
v[73,8] = 347
v[74,8] = 393
v[75,8] = 15
v[76,8] = 391
v[77,8] = 307
v[78,8] = 189
v[79,8] = 381
v[80,8] = 71
v[81,8] = 163
v[82,8] = 99
v[83,8] = 467
v[84,8] = 167
v[85,8] = 433
v[86,8] = 337
v[87,8] = 257
v[88,8] = 179
v[89,8] = 47
v[90,8] = 385
v[91,8] = 23
v[92,8] = 117
v[93,8] = 369
v[94,8] = 425
v[95,8] = 207
v[96,8] = 433
v[97,8] = 301
v[98,8] = 147
v[99,8] = 333
v[100,8] = 85
v[101,8] = 221
v[102,8] = 423
v[103,8] = 49
v[104,8] = 3
v[105,8] = 43
v[106,8] = 229
v[107,8] = 227
v[108,8] = 201
v[109,8] = 383
v[110,8] = 281
v[111,8] = 229
v[112,8] = 207
v[113,8] = 21
v[114,8] = 343
v[115,8] = 251
v[116,8] = 397
v[117,8] = 173
v[118,8] = 507
v[119,8] = 421
v[120,8] = 443
v[121,8] = 399
v[122,8] = 53
v[123,8] = 345
v[124,8] = 77
v[125,8] = 385
v[126,8] = 317
v[127,8] = 155
v[128,8] = 187
v[129,8] = 269
v[130,8] = 501
v[131,8] = 19
v[132,8] = 169
v[133,8] = 235
v[134,8] = 415
v[135,8] = 61
v[136,8] = 247
v[137,8] = 183
v[138,8] = 5
v[139,8] = 257
v[140,8] = 401
v[141,8] = 451
v[142,8] = 95
v[143,8] = 455
v[144,8] = 49
v[145,8] = 489
v[146,8] = 75
v[147,8] = 459
v[148,8] = 377
v[149,8] = 87
v[150,8] = 463
v[151,8] = 155
v[152,8] = 233
v[153,8] = 115
v[154,8] = 429
v[155,8] = 211
v[156,8] = 419
v[157,8] = 143
v[158,8] = 487
v[159,8] = 195
v[160,8] = 209
v[161,8] = 461
v[162,8] = 193
v[163,8] = 157
v[164,8] = 193
v[165,8] = 363
v[166,8] = 181
v[167,8] = 271
v[168,8] = 445
v[169,8] = 381
v[170,8] = 231
v[171,8] = 135
v[172,8] = 327
v[173,8] = 403
v[174,8] = 171
v[175,8] = 197
v[176,8] = 181
v[177,8] = 343
v[178,8] = 113
v[179,8] = 313
v[180,8] = 393
v[181,8] = 311
v[182,8] = 415
v[183,8] = 267
v[184,8] = 247
v[185,8] = 425
v[186,8] = 233
v[187,8] = 289
v[188,8] = 55
v[189,8] = 39
v[190,8] = 247
v[191,8] = 327
v[192,8] = 141
v[193,8] = 5
v[194,8] = 189
v[195,8] = 183
v[196,8] = 27
v[197,8] = 337
v[198,8] = 341
v[199,8] = 327
v[200,8] = 87
v[201,8] = 429
v[202,8] = 357
v[203,8] = 265
v[204,8] = 251
v[205,8] = 437
v[206,8] = 201
v[207,8] = 29
v[208,8] = 339
v[209,8] = 257
v[210,8] = 377
v[211,8] = 17
v[212,8] = 53
v[213,8] = 327
v[214,8] = 47
v[215,8] = 375
v[216,8] = 393
v[217,8] = 369
v[218,8] = 403
v[219,8] = 125
v[220,8] = 429
v[221,8] = 257
v[222,8] = 157
v[223,8] = 217
v[224,8] = 85
v[225,8] = 267
v[226,8] = 117
v[227,8] = 337
v[228,8] = 447
v[229,8] = 219
v[230,8] = 501
v[231,8] = 41
v[232,8] = 41
v[233,8] = 193
v[234,8] = 509
v[235,8] = 131
v[236,8] = 207
v[237,8] = 505
v[238,8] = 421
v[239,8] = 149
v[240,8] = 111
v[241,8] = 177
v[242,8] = 167
v[243,8] = 223
v[244,8] = 291
v[245,8] = 91
v[246,8] = 29
v[247,8] = 305
v[248,8] = 151
v[249,8] = 177
v[250,8] = 337
v[251,8] = 183
v[252,8] = 361
v[253,8] = 435
v[254,8] = 307
v[255,8] = 507
v[256,8] = 77
v[257,8] = 181
v[258,8] = 507
v[259,8] = 315
v[260,8] = 145
v[261,8] = 423
v[262,8] = 71
v[263,8] = 103
v[264,8] = 493
v[265,8] = 271
v[266,8] = 469
v[267,8] = 339
v[268,8] = 237
v[269,8] = 437
v[270,8] = 483
v[271,8] = 31
v[272,8] = 219
v[273,8] = 61
v[274,8] = 131
v[275,8] = 391
v[276,8] = 233
v[277,8] = 219
v[278,8] = 69
v[279,8] = 57
v[280,8] = 459
v[281,8] = 225
v[282,8] = 421
v[283,8] = 7
v[284,8] = 461
v[285,8] = 111
v[286,8] = 451
v[287,8] = 277
v[288,8] = 185
v[289,8] = 193
v[290,8] = 125
v[291,8] = 251
v[292,8] = 199
v[293,8] = 73
v[294,8] = 71
v[295,8] = 7
v[296,8] = 409
v[297,8] = 417
v[298,8] = 149
v[299,8] = 193
v[300,8] = 53
v[301,8] = 437
v[302,8] = 29
v[303,8] = 467
v[304,8] = 229
v[305,8] = 31
v[306,8] = 35
v[307,8] = 75
v[308,8] = 105
v[309,8] = 503
v[310,8] = 75
v[311,8] = 317
v[312,8] = 401
v[313,8] = 367
v[314,8] = 131
v[315,8] = 365
v[316,8] = 441
v[317,8] = 433
v[318,8] = 93
v[319,8] = 377
v[320,8] = 405
v[321,8] = 465
v[322,8] = 259
v[323,8] = 283
v[324,8] = 443
v[325,8] = 143
v[326,8] = 445
v[327,8] = 3
v[328,8] = 461
v[329,8] = 329
v[330,8] = 309
v[331,8] = 77
v[332,8] = 323
v[333,8] = 155
v[334,8] = 347
v[335,8] = 45
v[336,8] = 381
v[337,8] = 315
v[338,8] = 463
v[339,8] = 207
v[340,8] = 321
v[341,8] = 157
v[342,8] = 109
v[343,8] = 479
v[344,8] = 313
v[345,8] = 345
v[346,8] = 167
v[347,8] = 439
v[348,8] = 307
v[349,8] = 235
v[350,8] = 473
v[351,8] = 79
v[352,8] = 101
v[353,8] = 245
v[354,8] = 19
v[355,8] = 381
v[356,8] = 251
v[357,8] = 35
v[358,8] = 25
v[359,8] = 107
v[360,8] = 187
v[361,8] = 115
v[362,8] = 113
v[363,8] = 321
v[364,8] = 115
v[365,8] = 445
v[366,8] = 61
v[367,8] = 77
v[368,8] = 293
v[369,8] = 405
v[370,8] = 13
v[371,8] = 53
v[372,8] = 17
v[373,8] = 171
v[374,8] = 299
v[375,8] = 41
v[376,8] = 79
v[377,8] = 3
v[378,8] = 485
v[379,8] = 331
v[380,8] = 13
v[381,8] = 257
v[382,8] = 59
v[383,8] = 201
v[384,8] = 497
v[385,8] = 81
v[386,8] = 451
v[387,8] = 199
v[388,8] = 171
v[389,8] = 81
v[390,8] = 253
v[391,8] = 365
v[392,8] = 75
v[393,8] = 451
v[394,8] = 149
v[395,8] = 483
v[396,8] = 81
v[397,8] = 453
v[398,8] = 469
v[399,8] = 485
v[400,8] = 305
v[401,8] = 163
v[402,8] = 401
v[403,8] = 15
v[404,8] = 91
v[405,8] = 3
v[406,8] = 129
v[407,8] = 35
v[408,8] = 239
v[409,8] = 355
v[410,8] = 211
v[411,8] = 387
v[412,8] = 101
v[413,8] = 299
v[414,8] = 67
v[415,8] = 375
v[416,8] = 405
v[417,8] = 357
v[418,8] = 267
v[419,8] = 363
v[420,8] = 79
v[421,8] = 83
v[422,8] = 437
v[423,8] = 457
v[424,8] = 39
v[425,8] = 97
v[426,8] = 473
v[427,8] = 289
v[428,8] = 179
v[429,8] = 57
v[430,8] = 23
v[431,8] = 49
v[432,8] = 79
v[433,8] = 71
v[434,8] = 341
v[435,8] = 287
v[436,8] = 95
v[437,8] = 229
v[438,8] = 271
v[439,8] = 475
v[440,8] = 49
v[441,8] = 241
v[442,8] = 261
v[443,8] = 495
v[444,8] = 353
v[445,8] = 381
v[446,8] = 13
v[447,8] = 291
v[448,8] = 37
v[449,8] = 251
v[450,8] = 105
v[451,8] = 399
v[452,8] = 81
v[453,8] = 89
v[454,8] = 265
v[455,8] = 507
v[456,8] = 205
v[457,8] = 145
v[458,8] = 331
v[459,8] = 129
v[460,8] = 119
v[461,8] = 503
v[462,8] = 249
v[463,8] = 1
v[464,8] = 289
v[465,8] = 463
v[466,8] = 163
v[467,8] = 443
v[468,8] = 63
v[469,8] = 123
v[470,8] = 361
v[471,8] = 261
v[472,8] = 49
v[473,8] = 429
v[474,8] = 137
v[475,8] = 355
v[476,8] = 175
v[477,8] = 507
v[478,8] = 59
v[479,8] = 277
v[480,8] = 391
v[481,8] = 25
v[482,8] = 185
v[483,8] = 381
v[484,8] = 197
v[485,8] = 39
v[486,8] = 5
v[487,8] = 429
v[488,8] = 119
v[489,8] = 247
v[490,8] = 177
v[491,8] = 329
v[492,8] = 465
v[493,8] = 421
v[494,8] = 271
v[495,8] = 467
v[496,8] = 151
v[497,8] = 45
v[498,8] = 429
v[499,8] = 137
v[500,8] = 471
v[501,8] = 11
v[502,8] = 17
v[503,8] = 409
v[504,8] = 347
v[505,8] = 199
v[506,8] = 463
v[507,8] = 177
v[508,8] = 11
v[509,8] = 51
v[510,8] = 361
v[511,8] = 95
v[512,8] = 497
v[513,8] = 163
v[514,8] = 351
v[515,8] = 127
v[516,8] = 395
v[517,8] = 511
v[518,8] = 327
v[519,8] = 353
v[520,8] = 49
v[521,8] = 105
v[522,8] = 151
v[523,8] = 321
v[524,8] = 331
v[525,8] = 329
v[526,8] = 509
v[527,8] = 107
v[528,8] = 109
v[529,8] = 303
v[530,8] = 467
v[531,8] = 287
v[532,8] = 161
v[533,8] = 45
v[534,8] = 385
v[535,8] = 289
v[536,8] = 363
v[537,8] = 331
v[538,8] = 265
v[539,8] = 407
v[540,8] = 37
v[541,8] = 433
v[542,8] = 315
v[543,8] = 343
v[544,8] = 63
v[545,8] = 51
v[546,8] = 185
v[547,8] = 71
v[548,8] = 27
v[549,8] = 267
v[550,8] = 503
v[551,8] = 239
v[552,8] = 293
v[553,8] = 245
v[554,8] = 281
v[555,8] = 297
v[556,8] = 75
v[557,8] = 461
v[558,8] = 371
v[559,8] = 129
v[560,8] = 189
v[561,8] = 189
v[562,8] = 339
v[563,8] = 287
v[564,8] = 111
v[565,8] = 111
v[566,8] = 379
v[567,8] = 93
v[568,8] = 27
v[569,8] = 185
v[570,8] = 347
v[571,8] = 337
v[572,8] = 247
v[573,8] = 507
v[574,8] = 161
v[575,8] = 231
v[576,8] = 43
v[577,8] = 499
v[578,8] = 73
v[579,8] = 327
v[580,8] = 263
v[581,8] = 331
v[582,8] = 249
v[583,8] = 493
v[584,8] = 37
v[585,8] = 25
v[586,8] = 115
v[587,8] = 3
v[588,8] = 167
v[589,8] = 197
v[590,8] = 127
v[591,8] = 357
v[592,8] = 497
v[593,8] = 103
v[594,8] = 125
v[595,8] = 191
v[596,8] = 165
v[597,8] = 55
v[598,8] = 101
v[599,8] = 95
v[600,8] = 79
v[601,8] = 351
v[602,8] = 341
v[603,8] = 43
v[604,8] = 125
v[605,8] = 135
v[606,8] = 173
v[607,8] = 289
v[608,8] = 373
v[609,8] = 133
v[610,8] = 421
v[611,8] = 241
v[612,8] = 281
v[613,8] = 213
v[614,8] = 177
v[615,8] = 363
v[616,8] = 151
v[617,8] = 227
v[618,8] = 145
v[619,8] = 363
v[620,8] = 239
v[621,8] = 431
v[622,8] = 81
v[623,8] = 397
v[624,8] = 241
v[625,8] = 67
v[626,8] = 291
v[627,8] = 255
v[628,8] = 405
v[629,8] = 421
v[630,8] = 399
v[631,8] = 75
v[632,8] = 399
v[633,8] = 105
v[634,8] = 329
v[635,8] = 41
v[636,8] = 425
v[637,8] = 7
v[638,8] = 283
v[639,8] = 375
v[640,8] = 475
v[641,8] = 427
v[642,8] = 277
v[643,8] = 209
v[644,8] = 411
v[645,8] = 3
v[646,8] = 137
v[647,8] = 195
v[648,8] = 289
v[649,8] = 509
v[650,8] = 121
v[651,8] = 55
v[652,8] = 147
v[653,8] = 275
v[654,8] = 251
v[655,8] = 19
v[656,8] = 129
v[657,8] = 285
v[658,8] = 415
v[659,8] = 487
v[660,8] = 491
v[661,8] = 193
v[662,8] = 219
v[663,8] = 403
v[664,8] = 23
v[665,8] = 97
v[666,8] = 65
v[667,8] = 285
v[668,8] = 75
v[669,8] = 21
v[670,8] = 373
v[671,8] = 261
v[672,8] = 339
v[673,8] = 239
v[674,8] = 495
v[675,8] = 415
v[676,8] = 333
v[677,8] = 107
v[678,8] = 435
v[679,8] = 297
v[680,8] = 213
v[681,8] = 149
v[682,8] = 463
v[683,8] = 199
v[684,8] = 323
v[685,8] = 45
v[686,8] = 19
v[687,8] = 301
v[688,8] = 121
v[689,8] = 499
v[690,8] = 187
v[691,8] = 229
v[692,8] = 63
v[693,8] = 425
v[694,8] = 99
v[695,8] = 281
v[696,8] = 35
v[697,8] = 125
v[698,8] = 349
v[699,8] = 87
v[700,8] = 101
v[701,8] = 59
v[702,8] = 195
v[703,8] = 511
v[704,8] = 355
v[705,8] = 73
v[706,8] = 263
v[707,8] = 243
v[708,8] = 101
v[709,8] = 165
v[710,8] = 141
v[711,8] = 11
v[712,8] = 389
v[713,8] = 219
v[714,8] = 187
v[715,8] = 449
v[716,8] = 447
v[717,8] = 393
v[718,8] = 477
v[719,8] = 305
v[720,8] = 221
v[721,8] = 51
v[722,8] = 355
v[723,8] = 209
v[724,8] = 499
v[725,8] = 479
v[726,8] = 265
v[727,8] = 377
v[728,8] = 145
v[729,8] = 411
v[730,8] = 173
v[731,8] = 11
v[732,8] = 433
v[733,8] = 483
v[734,8] = 135
v[735,8] = 385
v[736,8] = 341
v[737,8] = 89
v[738,8] = 209
v[739,8] = 391
v[740,8] = 33
v[741,8] = 395
v[742,8] = 319
v[743,8] = 451
v[744,8] = 119
v[745,8] = 341
v[746,8] = 227
v[747,8] = 375
v[748,8] = 61
v[749,8] = 331
v[750,8] = 493
v[751,8] = 411
v[752,8] = 293
v[753,8] = 47
v[754,8] = 203
v[755,8] = 375
v[756,8] = 167
v[757,8] = 395
v[758,8] = 155
v[759,8] = 5
v[760,8] = 237
v[761,8] = 361
v[762,8] = 489
v[763,8] = 127
v[764,8] = 21
v[765,8] = 345
v[766,8] = 101
v[767,8] = 371
v[768,8] = 233
v[769,8] = 431
v[770,8] = 109
v[771,8] = 119
v[772,8] = 277
v[773,8] = 125
v[774,8] = 263
v[775,8] = 73
v[776,8] = 135
v[777,8] = 123
v[778,8] = 83
v[779,8] = 123
v[780,8] = 405
v[781,8] = 69
v[782,8] = 75
v[783,8] = 287
v[784,8] = 401
v[785,8] = 23
v[786,8] = 283
v[787,8] = 393
v[788,8] = 41
v[789,8] = 379
v[790,8] = 431
v[791,8] = 11
v[792,8] = 475
v[793,8] = 505
v[794,8] = 19
v[795,8] = 365
v[796,8] = 265
v[797,8] = 271
v[798,8] = 499
v[799,8] = 489
v[800,8] = 443
v[801,8] = 165
v[802,8] = 91
v[803,8] = 83
v[804,8] = 291
v[805,8] = 319
v[806,8] = 199
v[807,8] = 107
v[808,8] = 245
v[809,8] = 389
v[810,8] = 143
v[811,8] = 137
v[812,8] = 89
v[813,8] = 125
v[814,8] = 281
v[815,8] = 381
v[816,8] = 215
v[817,8] = 131
v[818,8] = 299
v[819,8] = 249
v[820,8] = 375
v[821,8] = 455
v[822,8] = 43
v[823,8] = 73
v[824,8] = 281
v[825,8] = 217
v[826,8] = 297
v[827,8] = 229
v[828,8] = 431
v[829,8] = 357
v[830,8] = 81
v[831,8] = 357
v[832,8] = 171
v[833,8] = 451
v[834,8] = 481
v[835,8] = 13
v[836,8] = 387
v[837,8] = 491
v[838,8] = 489
v[839,8] = 439
v[840,8] = 385
v[841,8] = 487
v[842,8] = 177
v[843,8] = 393
v[844,8] = 33
v[845,8] = 71
v[846,8] = 375
v[847,8] = 443
v[848,8] = 129
v[849,8] = 407
v[850,8] = 395
v[851,8] = 127
v[852,8] = 65
v[853,8] = 333
v[854,8] = 309
v[855,8] = 119
v[856,8] = 197
v[857,8] = 435
v[858,8] = 497
v[859,8] = 373
v[860,8] = 71
v[861,8] = 379
v[862,8] = 509
v[863,8] = 387
v[864,8] = 159
v[865,8] = 265
v[866,8] = 477
v[867,8] = 463
v[868,8] = 449
v[869,8] = 47
v[870,8] = 353
v[871,8] = 249
v[872,8] = 335
v[873,8] = 505
v[874,8] = 89
v[875,8] = 141
v[876,8] = 55
v[877,8] = 235
v[878,8] = 187
v[879,8] = 87
v[880,8] = 363
v[881,8] = 93
v[882,8] = 363
v[883,8] = 101
v[884,8] = 67
v[885,8] = 215
v[886,8] = 321
v[887,8] = 331
v[888,8] = 305
v[889,8] = 261
v[890,8] = 411
v[891,8] = 491
v[892,8] = 479
v[893,8] = 65
v[894,8] = 307
v[895,8] = 469
v[896,8] = 415
v[897,8] = 131
v[898,8] = 315
v[899,8] = 487
v[900,8] = 83
v[901,8] = 455
v[902,8] = 19
v[903,8] = 113
v[904,8] = 163
v[905,8] = 503
v[906,8] = 99
v[907,8] = 499
v[908,8] = 251
v[909,8] = 239
v[910,8] = 81
v[911,8] = 167
v[912,8] = 391
v[913,8] = 255
v[914,8] = 317
v[915,8] = 363
v[916,8] = 359
v[917,8] = 395
v[918,8] = 419
v[919,8] = 307
v[920,8] = 251
v[921,8] = 267
v[922,8] = 171
v[923,8] = 461
v[924,8] = 183
v[925,8] = 465
v[926,8] = 165
v[927,8] = 163
v[928,8] = 293
v[929,8] = 477
v[930,8] = 223
v[931,8] = 403
v[932,8] = 389
v[933,8] = 97
v[934,8] = 335
v[935,8] = 357
v[936,8] = 297
v[937,8] = 19
v[938,8] = 469
v[939,8] = 501
v[940,8] = 249
v[941,8] = 85
v[942,8] = 213
v[943,8] = 311
v[944,8] = 265
v[945,8] = 379
v[946,8] = 297
v[947,8] = 283
v[948,8] = 393
v[949,8] = 449
v[950,8] = 463
v[951,8] = 289
v[952,8] = 159
v[953,8] = 289
v[954,8] = 499
v[955,8] = 407
v[956,8] = 129
v[957,8] = 137
v[958,8] = 221
v[959,8] = 43
v[960,8] = 89
v[961,8] = 403
v[962,8] = 271
v[963,8] = 75
v[964,8] = 83
v[965,8] = 445
v[966,8] = 453
v[967,8] = 389
v[968,8] = 149
v[969,8] = 143
v[970,8] = 423
v[971,8] = 499
v[972,8] = 317
v[973,8] = 445
v[974,8] = 157
v[975,8] = 137
v[976,8] = 453
v[977,8] = 163
v[978,8] = 87
v[979,8] = 23
v[980,8] = 391
v[981,8] = 119
v[982,8] = 427
v[983,8] = 323
v[984,8] = 173
v[985,8] = 89
v[986,8] = 259
v[987,8] = 377
v[988,8] = 511
v[989,8] = 249
v[990,8] = 31
v[991,8] = 363
v[992,8] = 229
v[993,8] = 353
v[994,8] = 329
v[995,8] = 493
v[996,8] = 427
v[997,8] = 57
v[998,8] = 205
v[999,8] = 389
v[1000,8] = 91
v[1001,8] = 83
v[1002,8] = 13
v[1003,8] = 219
v[1004,8] = 439
v[1005,8] = 45
v[1006,8] = 35
v[1007,8] = 371
v[1008,8] = 441
v[1009,8] = 17
v[1010,8] = 267
v[1011,8] = 501
v[1012,8] = 53
v[1013,8] = 25
v[1014,8] = 333
v[1015,8] = 17
v[1016,8] = 201
v[1017,8] = 475
v[1018,8] = 257
v[1019,8] = 417
v[1020,8] = 345
v[1021,8] = 381
v[1022,8] = 377
v[1023,8] = 55
v[1024,8] = 403
v[1025,8] = 77
v[1026,8] = 389
v[1027,8] = 347
v[1028,8] = 363
v[1029,8] = 211
v[1030,8] = 413
v[1031,8] = 419
v[1032,8] = 5
v[1033,8] = 167
v[1034,8] = 219
v[1035,8] = 201
v[1036,8] = 285
v[1037,8] = 425
v[1038,8] = 11
v[1039,8] = 77
v[1040,8] = 269
v[1041,8] = 489
v[1042,8] = 281
v[1043,8] = 403
v[1044,8] = 79
v[1045,8] = 425
v[1046,8] = 125
v[1047,8] = 81
v[1048,8] = 331
v[1049,8] = 437
v[1050,8] = 271
v[1051,8] = 397
v[1052,8] = 299
v[1053,8] = 475
v[1054,8] = 271
v[1055,8] = 249
v[1056,8] = 413
v[1057,8] = 233
v[1058,8] = 261
v[1059,8] = 495
v[1060,8] = 171
v[1061,8] = 69
v[1062,8] = 27
v[1063,8] = 409
v[1064,8] = 21
v[1065,8] = 421
v[1066,8] = 367
v[1067,8] = 81
v[1068,8] = 483
v[1069,8] = 255
v[1070,8] = 15
v[1071,8] = 219
v[1072,8] = 365
v[1073,8] = 497
v[1074,8] = 181
v[1075,8] = 75
v[1076,8] = 431
v[1077,8] = 99
v[1078,8] = 325
v[1079,8] = 407
v[1080,8] = 229
v[1081,8] = 281
v[1082,8] = 63
v[1083,8] = 83
v[1084,8] = 493
v[1085,8] = 5
v[1086,8] = 113
v[1087,8] = 15
v[1088,8] = 271
v[1089,8] = 37
v[1090,8] = 87
v[1091,8] = 451
v[1092,8] = 299
v[1093,8] = 83
v[1094,8] = 451
v[1095,8] = 311
v[1096,8] = 441
v[1097,8] = 47
v[1098,8] = 455
v[1099,8] = 47
v[1100,8] = 253
v[1101,8] = 13
v[1102,8] = 109
v[1103,8] = 369
v[1104,8] = 347
v[1105,8] = 11
v[1106,8] = 409
v[1107,8] = 275
v[1108,8] = 63
v[1109,8] = 441
v[1110,8] = 15
v[101,9] = 519
v[102,9] = 307
v[103,9] = 931
v[104,9] = 1023
v[105,9] = 517
v[106,9] = 771
v[107,9] = 151
v[108,9] = 1023
v[109,9] = 539
v[110,9] = 725
v[111,9] = 45
v[112,9] = 927
v[113,9] = 707
v[114,9] = 29
v[115,9] = 125
v[116,9] = 371
v[117,9] = 275
v[118,9] = 279
v[119,9] = 817
v[120,9] = 389
v[121,9] = 453
v[122,9] = 989
v[123,9] = 1015
v[124,9] = 29
v[125,9] = 169
v[126,9] = 743
v[127,9] = 99
v[128,9] = 923
v[129,9] = 981
v[130,9] = 181
v[131,9] = 693
v[132,9] = 309
v[133,9] = 227
v[134,9] = 111
v[135,9] = 219
v[136,9] = 897
v[137,9] = 377
v[138,9] = 425
v[139,9] = 609
v[140,9] = 227
v[141,9] = 19
v[142,9] = 221
v[143,9] = 143
v[144,9] = 581
v[145,9] = 147
v[146,9] = 919
v[147,9] = 127
v[148,9] = 725
v[149,9] = 793
v[150,9] = 289
v[151,9] = 411
v[152,9] = 835
v[153,9] = 921
v[154,9] = 957
v[155,9] = 443
v[156,9] = 349
v[157,9] = 813
v[158,9] = 5
v[159,9] = 105
v[160,9] = 457
v[161,9] = 393
v[162,9] = 539
v[163,9] = 101
v[164,9] = 197
v[165,9] = 697
v[166,9] = 27
v[167,9] = 343
v[168,9] = 515
v[169,9] = 69
v[170,9] = 485
v[171,9] = 383
v[172,9] = 855
v[173,9] = 693
v[174,9] = 133
v[175,9] = 87
v[176,9] = 743
v[177,9] = 747
v[178,9] = 475
v[179,9] = 87
v[180,9] = 469
v[181,9] = 763
v[182,9] = 721
v[183,9] = 345
v[184,9] = 479
v[185,9] = 965
v[186,9] = 527
v[187,9] = 121
v[188,9] = 271
v[189,9] = 353
v[190,9] = 467
v[191,9] = 177
v[192,9] = 245
v[193,9] = 627
v[194,9] = 113
v[195,9] = 357
v[196,9] = 7
v[197,9] = 691
v[198,9] = 725
v[199,9] = 355
v[200,9] = 889
v[201,9] = 635
v[202,9] = 737
v[203,9] = 429
v[204,9] = 545
v[205,9] = 925
v[206,9] = 357
v[207,9] = 873
v[208,9] = 187
v[209,9] = 351
v[210,9] = 677
v[211,9] = 999
v[212,9] = 921
v[213,9] = 477
v[214,9] = 233
v[215,9] = 765
v[216,9] = 495
v[217,9] = 81
v[218,9] = 953
v[219,9] = 479
v[220,9] = 89
v[221,9] = 173
v[222,9] = 473
v[223,9] = 131
v[224,9] = 961
v[225,9] = 411
v[226,9] = 291
v[227,9] = 967
v[228,9] = 65
v[229,9] = 511
v[230,9] = 13
v[231,9] = 805
v[232,9] = 945
v[233,9] = 369
v[234,9] = 827
v[235,9] = 295
v[236,9] = 163
v[237,9] = 835
v[238,9] = 259
v[239,9] = 207
v[240,9] = 331
v[241,9] = 29
v[242,9] = 315
v[243,9] = 999
v[244,9] = 133
v[245,9] = 967
v[246,9] = 41
v[247,9] = 117
v[248,9] = 677
v[249,9] = 471
v[250,9] = 717
v[251,9] = 881
v[252,9] = 755
v[253,9] = 351
v[254,9] = 723
v[255,9] = 259
v[256,9] = 879
v[257,9] = 455
v[258,9] = 721
v[259,9] = 289
v[260,9] = 149
v[261,9] = 199
v[262,9] = 805
v[263,9] = 987
v[264,9] = 851
v[265,9] = 423
v[266,9] = 597
v[267,9] = 129
v[268,9] = 11
v[269,9] = 733
v[270,9] = 549
v[271,9] = 153
v[272,9] = 285
v[273,9] = 451
v[274,9] = 559
v[275,9] = 377
v[276,9] = 109
v[277,9] = 357
v[278,9] = 143
v[279,9] = 693
v[280,9] = 615
v[281,9] = 677
v[282,9] = 701
v[283,9] = 475
v[284,9] = 767
v[285,9] = 85
v[286,9] = 229
v[287,9] = 509
v[288,9] = 547
v[289,9] = 151
v[290,9] = 389
v[291,9] = 711
v[292,9] = 785
v[293,9] = 657
v[294,9] = 319
v[295,9] = 509
v[296,9] = 99
v[297,9] = 1007
v[298,9] = 775
v[299,9] = 359
v[300,9] = 697
v[301,9] = 677
v[302,9] = 85
v[303,9] = 497
v[304,9] = 105
v[305,9] = 615
v[306,9] = 891
v[307,9] = 71
v[308,9] = 449
v[309,9] = 835
v[310,9] = 609
v[311,9] = 377
v[312,9] = 693
v[313,9] = 665
v[314,9] = 627
v[315,9] = 215
v[316,9] = 911
v[317,9] = 503
v[318,9] = 729
v[319,9] = 131
v[320,9] = 19
v[321,9] = 895
v[322,9] = 199
v[323,9] = 161
v[324,9] = 239
v[325,9] = 633
v[326,9] = 1013
v[327,9] = 537
v[328,9] = 255
v[329,9] = 23
v[330,9] = 149
v[331,9] = 679
v[332,9] = 1021
v[333,9] = 595
v[334,9] = 199
v[335,9] = 557
v[336,9] = 659
v[337,9] = 251
v[338,9] = 829
v[339,9] = 727
v[340,9] = 439
v[341,9] = 495
v[342,9] = 647
v[343,9] = 223
v[344,9] = 949
v[345,9] = 625
v[346,9] = 87
v[347,9] = 481
v[348,9] = 85
v[349,9] = 799
v[350,9] = 917
v[351,9] = 769
v[352,9] = 949
v[353,9] = 739
v[354,9] = 115
v[355,9] = 499
v[356,9] = 945
v[357,9] = 547
v[358,9] = 225
v[359,9] = 1015
v[360,9] = 469
v[361,9] = 737
v[362,9] = 495
v[363,9] = 353
v[364,9] = 103
v[365,9] = 17
v[366,9] = 665
v[367,9] = 639
v[368,9] = 525
v[369,9] = 75
v[370,9] = 447
v[371,9] = 185
v[372,9] = 43
v[373,9] = 729
v[374,9] = 577
v[375,9] = 863
v[376,9] = 735
v[377,9] = 317
v[378,9] = 99
v[379,9] = 17
v[380,9] = 477
v[381,9] = 893
v[382,9] = 537
v[383,9] = 519
v[384,9] = 1017
v[385,9] = 375
v[386,9] = 297
v[387,9] = 325
v[388,9] = 999
v[389,9] = 353
v[390,9] = 343
v[391,9] = 729
v[392,9] = 135
v[393,9] = 489
v[394,9] = 859
v[395,9] = 267
v[396,9] = 141
v[397,9] = 831
v[398,9] = 141
v[399,9] = 893
v[400,9] = 249
v[401,9] = 807
v[402,9] = 53
v[403,9] = 613
v[404,9] = 131
v[405,9] = 547
v[406,9] = 977
v[407,9] = 131
v[408,9] = 999
v[409,9] = 175
v[410,9] = 31
v[411,9] = 341
v[412,9] = 739
v[413,9] = 467
v[414,9] = 675
v[415,9] = 241
v[416,9] = 645
v[417,9] = 247
v[418,9] = 391
v[419,9] = 583
v[420,9] = 183
v[421,9] = 973
v[422,9] = 433
v[423,9] = 367
v[424,9] = 131
v[425,9] = 467
v[426,9] = 571
v[427,9] = 309
v[428,9] = 385
v[429,9] = 977
v[430,9] = 111
v[431,9] = 917
v[432,9] = 935
v[433,9] = 473
v[434,9] = 345
v[435,9] = 411
v[436,9] = 313
v[437,9] = 97
v[438,9] = 149
v[439,9] = 959
v[440,9] = 841
v[441,9] = 839
v[442,9] = 669
v[443,9] = 431
v[444,9] = 51
v[445,9] = 41
v[446,9] = 301
v[447,9] = 247
v[448,9] = 1015
v[449,9] = 377
v[450,9] = 329
v[451,9] = 945
v[452,9] = 269
v[453,9] = 67
v[454,9] = 979
v[455,9] = 581
v[456,9] = 643
v[457,9] = 823
v[458,9] = 557
v[459,9] = 91
v[460,9] = 405
v[461,9] = 117
v[462,9] = 801
v[463,9] = 509
v[464,9] = 347
v[465,9] = 893
v[466,9] = 303
v[467,9] = 227
v[468,9] = 783
v[469,9] = 555
v[470,9] = 867
v[471,9] = 99
v[472,9] = 703
v[473,9] = 111
v[474,9] = 797
v[475,9] = 873
v[476,9] = 541
v[477,9] = 919
v[478,9] = 513
v[479,9] = 343
v[480,9] = 319
v[481,9] = 517
v[482,9] = 135
v[483,9] = 871
v[484,9] = 917
v[485,9] = 285
v[486,9] = 663
v[487,9] = 301
v[488,9] = 15
v[489,9] = 763
v[490,9] = 89
v[491,9] = 323
v[492,9] = 757
v[493,9] = 317
v[494,9] = 807
v[495,9] = 309
v[496,9] = 1013
v[497,9] = 345
v[498,9] = 499
v[499,9] = 279
v[500,9] = 711
v[501,9] = 915
v[502,9] = 411
v[503,9] = 281
v[504,9] = 193
v[505,9] = 739
v[506,9] = 365
v[507,9] = 315
v[508,9] = 375
v[509,9] = 809
v[510,9] = 469
v[511,9] = 487
v[512,9] = 621
v[513,9] = 857
v[514,9] = 975
v[515,9] = 537
v[516,9] = 939
v[517,9] = 585
v[518,9] = 129
v[519,9] = 625
v[520,9] = 447
v[521,9] = 129
v[522,9] = 1017
v[523,9] = 133
v[524,9] = 83
v[525,9] = 3
v[526,9] = 415
v[527,9] = 661
v[528,9] = 53
v[529,9] = 115
v[530,9] = 903
v[531,9] = 49
v[532,9] = 79
v[533,9] = 55
v[534,9] = 385
v[535,9] = 261
v[536,9] = 345
v[537,9] = 297
v[538,9] = 199
v[539,9] = 385
v[540,9] = 617
v[541,9] = 25
v[542,9] = 515
v[543,9] = 275
v[544,9] = 849
v[545,9] = 401
v[546,9] = 471
v[547,9] = 377
v[548,9] = 661
v[549,9] = 535
v[550,9] = 505
v[551,9] = 939
v[552,9] = 465
v[553,9] = 225
v[554,9] = 929
v[555,9] = 219
v[556,9] = 955
v[557,9] = 659
v[558,9] = 441
v[559,9] = 117
v[560,9] = 527
v[561,9] = 427
v[562,9] = 515
v[563,9] = 287
v[564,9] = 191
v[565,9] = 33
v[566,9] = 389
v[567,9] = 197
v[568,9] = 825
v[569,9] = 63
v[570,9] = 417
v[571,9] = 949
v[572,9] = 35
v[573,9] = 571
v[574,9] = 9
v[575,9] = 131
v[576,9] = 609
v[577,9] = 439
v[578,9] = 95
v[579,9] = 19
v[580,9] = 569
v[581,9] = 893
v[582,9] = 451
v[583,9] = 397
v[584,9] = 971
v[585,9] = 801
v[586,9] = 125
v[587,9] = 471
v[588,9] = 187
v[589,9] = 257
v[590,9] = 67
v[591,9] = 949
v[592,9] = 621
v[593,9] = 453
v[594,9] = 411
v[595,9] = 621
v[596,9] = 955
v[597,9] = 309
v[598,9] = 783
v[599,9] = 893
v[600,9] = 597
v[601,9] = 377
v[602,9] = 753
v[603,9] = 145
v[604,9] = 637
v[605,9] = 941
v[606,9] = 593
v[607,9] = 317
v[608,9] = 555
v[609,9] = 375
v[610,9] = 575
v[611,9] = 175
v[612,9] = 403
v[613,9] = 571
v[614,9] = 555
v[615,9] = 109
v[616,9] = 377
v[617,9] = 931
v[618,9] = 499
v[619,9] = 649
v[620,9] = 653
v[621,9] = 329
v[622,9] = 279
v[623,9] = 271
v[624,9] = 647
v[625,9] = 721
v[626,9] = 665
v[627,9] = 429
v[628,9] = 957
v[629,9] = 803
v[630,9] = 767
v[631,9] = 425
v[632,9] = 477
v[633,9] = 995
v[634,9] = 105
v[635,9] = 495
v[636,9] = 575
v[637,9] = 687
v[638,9] = 385
v[639,9] = 227
v[640,9] = 923
v[641,9] = 563
v[642,9] = 723
v[643,9] = 481
v[644,9] = 717
v[645,9] = 111
v[646,9] = 633
v[647,9] = 113
v[648,9] = 369
v[649,9] = 955
v[650,9] = 253
v[651,9] = 321
v[652,9] = 409
v[653,9] = 909
v[654,9] = 367
v[655,9] = 33
v[656,9] = 967
v[657,9] = 453
v[658,9] = 863
v[659,9] = 449
v[660,9] = 539
v[661,9] = 781
v[662,9] = 911
v[663,9] = 113
v[664,9] = 7
v[665,9] = 219
v[666,9] = 725
v[667,9] = 1015
v[668,9] = 971
v[669,9] = 1021
v[670,9] = 525
v[671,9] = 785
v[672,9] = 873
v[673,9] = 191
v[674,9] = 893
v[675,9] = 297
v[676,9] = 507
v[677,9] = 215
v[678,9] = 21
v[679,9] = 153
v[680,9] = 645
v[681,9] = 913
v[682,9] = 755
v[683,9] = 371
v[684,9] = 881
v[685,9] = 113
v[686,9] = 903
v[687,9] = 225
v[688,9] = 49
v[689,9] = 587
v[690,9] = 201
v[691,9] = 927
v[692,9] = 429
v[693,9] = 599
v[694,9] = 513
v[695,9] = 97
v[696,9] = 319
v[697,9] = 331
v[698,9] = 833
v[699,9] = 325
v[700,9] = 887
v[701,9] = 139
v[702,9] = 927
v[703,9] = 399
v[704,9] = 163
v[705,9] = 307
v[706,9] = 803
v[707,9] = 169
v[708,9] = 1019
v[709,9] = 869
v[710,9] = 537
v[711,9] = 907
v[712,9] = 479
v[713,9] = 335
v[714,9] = 697
v[715,9] = 479
v[716,9] = 353
v[717,9] = 769
v[718,9] = 787
v[719,9] = 1023
v[720,9] = 855
v[721,9] = 493
v[722,9] = 883
v[723,9] = 521
v[724,9] = 735
v[725,9] = 297
v[726,9] = 1011
v[727,9] = 991
v[728,9] = 879
v[729,9] = 855
v[730,9] = 591
v[731,9] = 415
v[732,9] = 917
v[733,9] = 375
v[734,9] = 453
v[735,9] = 553
v[736,9] = 189
v[737,9] = 841
v[738,9] = 339
v[739,9] = 211
v[740,9] = 601
v[741,9] = 57
v[742,9] = 765
v[743,9] = 745
v[744,9] = 621
v[745,9] = 209
v[746,9] = 875
v[747,9] = 639
v[748,9] = 7
v[749,9] = 595
v[750,9] = 971
v[751,9] = 263
v[752,9] = 1009
v[753,9] = 201
v[754,9] = 23
v[755,9] = 77
v[756,9] = 621
v[757,9] = 33
v[758,9] = 535
v[759,9] = 963
v[760,9] = 661
v[761,9] = 523
v[762,9] = 263
v[763,9] = 917
v[764,9] = 103
v[765,9] = 623
v[766,9] = 231
v[767,9] = 47
v[768,9] = 301
v[769,9] = 549
v[770,9] = 337
v[771,9] = 675
v[772,9] = 189
v[773,9] = 357
v[774,9] = 1005
v[775,9] = 789
v[776,9] = 189
v[777,9] = 319
v[778,9] = 721
v[779,9] = 1005
v[780,9] = 525
v[781,9] = 675
v[782,9] = 539
v[783,9] = 191
v[784,9] = 813
v[785,9] = 917
v[786,9] = 51
v[787,9] = 167
v[788,9] = 415
v[789,9] = 579
v[790,9] = 755
v[791,9] = 605
v[792,9] = 721
v[793,9] = 837
v[794,9] = 529
v[795,9] = 31
v[796,9] = 327
v[797,9] = 799
v[798,9] = 961
v[799,9] = 279
v[800,9] = 409
v[801,9] = 847
v[802,9] = 649
v[803,9] = 241
v[804,9] = 285
v[805,9] = 545
v[806,9] = 407
v[807,9] = 161
v[808,9] = 591
v[809,9] = 73
v[810,9] = 313
v[811,9] = 811
v[812,9] = 17
v[813,9] = 663
v[814,9] = 269
v[815,9] = 261
v[816,9] = 37
v[817,9] = 783
v[818,9] = 127
v[819,9] = 917
v[820,9] = 231
v[821,9] = 577
v[822,9] = 975
v[823,9] = 793
v[824,9] = 921
v[825,9] = 343
v[826,9] = 751
v[827,9] = 139
v[828,9] = 221
v[829,9] = 79
v[830,9] = 817
v[831,9] = 393
v[832,9] = 545
v[833,9] = 11
v[834,9] = 781
v[835,9] = 71
v[836,9] = 1
v[837,9] = 699
v[838,9] = 767
v[839,9] = 917
v[840,9] = 9
v[841,9] = 107
v[842,9] = 341
v[843,9] = 587
v[844,9] = 903
v[845,9] = 965
v[846,9] = 599
v[847,9] = 507
v[848,9] = 843
v[849,9] = 739
v[850,9] = 579
v[851,9] = 397
v[852,9] = 397
v[853,9] = 325
v[854,9] = 775
v[855,9] = 565
v[856,9] = 925
v[857,9] = 75
v[858,9] = 55
v[859,9] = 979
v[860,9] = 931
v[861,9] = 93
v[862,9] = 957
v[863,9] = 857
v[864,9] = 753
v[865,9] = 965
v[866,9] = 795
v[867,9] = 67
v[868,9] = 5
v[869,9] = 87
v[870,9] = 909
v[871,9] = 97
v[872,9] = 995
v[873,9] = 271
v[874,9] = 875
v[875,9] = 671
v[876,9] = 613
v[877,9] = 33
v[878,9] = 351
v[879,9] = 69
v[880,9] = 811
v[881,9] = 669
v[882,9] = 729
v[883,9] = 401
v[884,9] = 647
v[885,9] = 241
v[886,9] = 435
v[887,9] = 447
v[888,9] = 721
v[889,9] = 271
v[890,9] = 745
v[891,9] = 53
v[892,9] = 775
v[893,9] = 99
v[894,9] = 343
v[895,9] = 451
v[896,9] = 427
v[897,9] = 593
v[898,9] = 339
v[899,9] = 845
v[900,9] = 243
v[901,9] = 345
v[902,9] = 17
v[903,9] = 573
v[904,9] = 421
v[905,9] = 517
v[906,9] = 971
v[907,9] = 499
v[908,9] = 435
v[909,9] = 769
v[910,9] = 75
v[911,9] = 203
v[912,9] = 793
v[913,9] = 985
v[914,9] = 343
v[915,9] = 955
v[916,9] = 735
v[917,9] = 523
v[918,9] = 659
v[919,9] = 703
v[920,9] = 303
v[921,9] = 421
v[922,9] = 951
v[923,9] = 405
v[924,9] = 631
v[925,9] = 825
v[926,9] = 735
v[927,9] = 433
v[928,9] = 841
v[929,9] = 485
v[930,9] = 49
v[931,9] = 749
v[932,9] = 107
v[933,9] = 669
v[934,9] = 211
v[935,9] = 497
v[936,9] = 143
v[937,9] = 99
v[938,9] = 57
v[939,9] = 277
v[940,9] = 969
v[941,9] = 107
v[942,9] = 397
v[943,9] = 563
v[944,9] = 551
v[945,9] = 447
v[946,9] = 381
v[947,9] = 187
v[948,9] = 57
v[949,9] = 405
v[950,9] = 731
v[951,9] = 769
v[952,9] = 923
v[953,9] = 955
v[954,9] = 915
v[955,9] = 737
v[956,9] = 595
v[957,9] = 341
v[958,9] = 253
v[959,9] = 823
v[960,9] = 197
v[961,9] = 321
v[962,9] = 315
v[963,9] = 181
v[964,9] = 885
v[965,9] = 497
v[966,9] = 159
v[967,9] = 571
v[968,9] = 981
v[969,9] = 899
v[970,9] = 785
v[971,9] = 947
v[972,9] = 217
v[973,9] = 217
v[974,9] = 135
v[975,9] = 753
v[976,9] = 623
v[977,9] = 565
v[978,9] = 717
v[979,9] = 903
v[980,9] = 581
v[981,9] = 955
v[982,9] = 621
v[983,9] = 361
v[984,9] = 869
v[985,9] = 87
v[986,9] = 943
v[987,9] = 907
v[988,9] = 853
v[989,9] = 353
v[990,9] = 335
v[991,9] = 197
v[992,9] = 771
v[993,9] = 433
v[994,9] = 743
v[995,9] = 195
v[996,9] = 91
v[997,9] = 1023
v[998,9] = 63
v[999,9] = 301
v[1000,9] = 647
v[1001,9] = 205
v[1002,9] = 485
v[1003,9] = 927
v[1004,9] = 1003
v[1005,9] = 987
v[1006,9] = 359
v[1007,9] = 577
v[1008,9] = 147
v[1009,9] = 141
v[1010,9] = 1017
v[1011,9] = 701
v[1012,9] = 273
v[1013,9] = 89
v[1014,9] = 589
v[1015,9] = 487
v[1016,9] = 859
v[1017,9] = 343
v[1018,9] = 91
v[1019,9] = 847
v[1020,9] = 341
v[1021,9] = 173
v[1022,9] = 287
v[1023,9] = 1003
v[1024,9] = 289
v[1025,9] = 639
v[1026,9] = 983
v[1027,9] = 685
v[1028,9] = 697
v[1029,9] = 35
v[1030,9] = 701
v[1031,9] = 645
v[1032,9] = 911
v[1033,9] = 501
v[1034,9] = 705
v[1035,9] = 873
v[1036,9] = 763
v[1037,9] = 745
v[1038,9] = 657
v[1039,9] = 559
v[1040,9] = 699
v[1041,9] = 315
v[1042,9] = 347
v[1043,9] = 429
v[1044,9] = 197
v[1045,9] = 165
v[1046,9] = 955
v[1047,9] = 859
v[1048,9] = 167
v[1049,9] = 303
v[1050,9] = 833
v[1051,9] = 531
v[1052,9] = 473
v[1053,9] = 635
v[1054,9] = 641
v[1055,9] = 195
v[1056,9] = 589
v[1057,9] = 821
v[1058,9] = 205
v[1059,9] = 3
v[1060,9] = 635
v[1061,9] = 371
v[1062,9] = 891
v[1063,9] = 249
v[1064,9] = 123
v[1065,9] = 77
v[1066,9] = 623
v[1067,9] = 993
v[1068,9] = 401
v[1069,9] = 525
v[1070,9] = 427
v[1071,9] = 71
v[1072,9] = 655
v[1073,9] = 951
v[1074,9] = 357
v[1075,9] = 851
v[1076,9] = 899
v[1077,9] = 535
v[1078,9] = 493
v[1079,9] = 323
v[1080,9] = 1003
v[1081,9] = 343
v[1082,9] = 515
v[1083,9] = 859
v[1084,9] = 1017
v[1085,9] = 5
v[1086,9] = 423
v[1087,9] = 315
v[1088,9] = 1011
v[1089,9] = 703
v[1090,9] = 41
v[1091,9] = 777
v[1092,9] = 163
v[1093,9] = 95
v[1094,9] = 831
v[1095,9] = 79
v[1096,9] = 975
v[1097,9] = 235
v[1098,9] = 633
v[1099,9] = 723
v[1100,9] = 297
v[1101,9] = 589
v[1102,9] = 317
v[1103,9] = 679
v[1104,9] = 981
v[1105,9] = 195
v[1106,9] = 399
v[1107,9] = 1003
v[1108,9] = 121
v[1109,9] = 501
v[1110,9] = 155
v[161,10] = 7
v[162,10] = 2011
v[163,10] = 1001
v[164,10] = 49
v[165,10] = 825
v[166,10] = 415
v[167,10] = 1441
v[168,10] = 383
v[169,10] = 1581
v[170,10] = 623
v[171,10] = 1621
v[172,10] = 1319
v[173,10] = 1387
v[174,10] = 619
v[175,10] = 839
v[176,10] = 217
v[177,10] = 75
v[178,10] = 1955
v[179,10] = 505
v[180,10] = 281
v[181,10] = 1629
v[182,10] = 1379
v[183,10] = 53
v[184,10] = 1111
v[185,10] = 1399
v[186,10] = 301
v[187,10] = 209
v[188,10] = 49
v[189,10] = 155
v[190,10] = 1647
v[191,10] = 631
v[192,10] = 129
v[193,10] = 1569
v[194,10] = 335
v[195,10] = 67
v[196,10] = 1955
v[197,10] = 1611
v[198,10] = 2021
v[199,10] = 1305
v[200,10] = 121
v[201,10] = 37
v[202,10] = 877
v[203,10] = 835
v[204,10] = 1457
v[205,10] = 669
v[206,10] = 1405
v[207,10] = 935
v[208,10] = 1735
v[209,10] = 665
v[210,10] = 551
v[211,10] = 789
v[212,10] = 1543
v[213,10] = 1267
v[214,10] = 1027
v[215,10] = 1
v[216,10] = 1911
v[217,10] = 163
v[218,10] = 1929
v[219,10] = 67
v[220,10] = 1975
v[221,10] = 1681
v[222,10] = 1413
v[223,10] = 191
v[224,10] = 1711
v[225,10] = 1307
v[226,10] = 401
v[227,10] = 725
v[228,10] = 1229
v[229,10] = 1403
v[230,10] = 1609
v[231,10] = 2035
v[232,10] = 917
v[233,10] = 921
v[234,10] = 1789
v[235,10] = 41
v[236,10] = 2003
v[237,10] = 187
v[238,10] = 67
v[239,10] = 1635
v[240,10] = 717
v[241,10] = 1449
v[242,10] = 277
v[243,10] = 1903
v[244,10] = 1179
v[245,10] = 363
v[246,10] = 1211
v[247,10] = 1231
v[248,10] = 647
v[249,10] = 1261
v[250,10] = 1029
v[251,10] = 1485
v[252,10] = 1309
v[253,10] = 1149
v[254,10] = 317
v[255,10] = 1335
v[256,10] = 171
v[257,10] = 243
v[258,10] = 271
v[259,10] = 1055
v[260,10] = 1601
v[261,10] = 1129
v[262,10] = 1653
v[263,10] = 205
v[264,10] = 1463
v[265,10] = 1681
v[266,10] = 1621
v[267,10] = 197
v[268,10] = 951
v[269,10] = 573
v[270,10] = 1697
v[271,10] = 1265
v[272,10] = 1321
v[273,10] = 1805
v[274,10] = 1235
v[275,10] = 1853
v[276,10] = 1307
v[277,10] = 945
v[278,10] = 1197
v[279,10] = 1411
v[280,10] = 833
v[281,10] = 273
v[282,10] = 1517
v[283,10] = 1747
v[284,10] = 1095
v[285,10] = 1345
v[286,10] = 869
v[287,10] = 57
v[288,10] = 1383
v[289,10] = 221
v[290,10] = 1713
v[291,10] = 335
v[292,10] = 1751
v[293,10] = 1141
v[294,10] = 839
v[295,10] = 523
v[296,10] = 1861
v[297,10] = 1105
v[298,10] = 389
v[299,10] = 1177
v[300,10] = 1877
v[301,10] = 805
v[302,10] = 93
v[303,10] = 1591
v[304,10] = 423
v[305,10] = 1835
v[306,10] = 99
v[307,10] = 1781
v[308,10] = 1515
v[309,10] = 1909
v[310,10] = 1011
v[311,10] = 303
v[312,10] = 385
v[313,10] = 1635
v[314,10] = 357
v[315,10] = 973
v[316,10] = 1781
v[317,10] = 1707
v[318,10] = 1363
v[319,10] = 1053
v[320,10] = 649
v[321,10] = 1469
v[322,10] = 623
v[323,10] = 1429
v[324,10] = 1241
v[325,10] = 1151
v[326,10] = 1055
v[327,10] = 503
v[328,10] = 921
v[329,10] = 3
v[330,10] = 349
v[331,10] = 1149
v[332,10] = 293
v[333,10] = 45
v[334,10] = 303
v[335,10] = 877
v[336,10] = 1565
v[337,10] = 1583
v[338,10] = 1001
v[339,10] = 663
v[340,10] = 1535
v[341,10] = 395
v[342,10] = 1141
v[343,10] = 1481
v[344,10] = 1797
v[345,10] = 643
v[346,10] = 1507
v[347,10] = 465
v[348,10] = 2027
v[349,10] = 1695
v[350,10] = 367
v[351,10] = 937
v[352,10] = 719
v[353,10] = 545
v[354,10] = 1991
v[355,10] = 83
v[356,10] = 819
v[357,10] = 239
v[358,10] = 1791
v[359,10] = 1461
v[360,10] = 1647
v[361,10] = 1501
v[362,10] = 1161
v[363,10] = 1629
v[364,10] = 139
v[365,10] = 1595
v[366,10] = 1921
v[367,10] = 1267
v[368,10] = 1415
v[369,10] = 509
v[370,10] = 347
v[371,10] = 777
v[372,10] = 1083
v[373,10] = 363
v[374,10] = 269
v[375,10] = 1015
v[376,10] = 1809
v[377,10] = 1105
v[378,10] = 1429
v[379,10] = 1471
v[380,10] = 2019
v[381,10] = 381
v[382,10] = 2025
v[383,10] = 1223
v[384,10] = 827
v[385,10] = 1733
v[386,10] = 887
v[387,10] = 1321
v[388,10] = 803
v[389,10] = 1951
v[390,10] = 1297
v[391,10] = 1995
v[392,10] = 833
v[393,10] = 1107
v[394,10] = 1135
v[395,10] = 1181
v[396,10] = 1251
v[397,10] = 983
v[398,10] = 1389
v[399,10] = 1565
v[400,10] = 273
v[401,10] = 137
v[402,10] = 71
v[403,10] = 735
v[404,10] = 1005
v[405,10] = 933
v[406,10] = 67
v[407,10] = 1471
v[408,10] = 551
v[409,10] = 457
v[410,10] = 1667
v[411,10] = 1729
v[412,10] = 919
v[413,10] = 285
v[414,10] = 1629
v[415,10] = 1815
v[416,10] = 653
v[417,10] = 1919
v[418,10] = 1039
v[419,10] = 531
v[420,10] = 393
v[421,10] = 1411
v[422,10] = 359
v[423,10] = 221
v[424,10] = 699
v[425,10] = 1485
v[426,10] = 471
v[427,10] = 1357
v[428,10] = 1715
v[429,10] = 595
v[430,10] = 1677
v[431,10] = 153
v[432,10] = 1903
v[433,10] = 1281
v[434,10] = 215
v[435,10] = 781
v[436,10] = 543
v[437,10] = 293
v[438,10] = 1807
v[439,10] = 965
v[440,10] = 1695
v[441,10] = 443
v[442,10] = 1985
v[443,10] = 321
v[444,10] = 879
v[445,10] = 1227
v[446,10] = 1915
v[447,10] = 839
v[448,10] = 1945
v[449,10] = 1993
v[450,10] = 1165
v[451,10] = 51
v[452,10] = 557
v[453,10] = 723
v[454,10] = 1491
v[455,10] = 817
v[456,10] = 1237
v[457,10] = 947
v[458,10] = 1215
v[459,10] = 1911
v[460,10] = 1225
v[461,10] = 1965
v[462,10] = 1889
v[463,10] = 1503
v[464,10] = 1177
v[465,10] = 73
v[466,10] = 1767
v[467,10] = 303
v[468,10] = 177
v[469,10] = 1897
v[470,10] = 1401
v[471,10] = 321
v[472,10] = 921
v[473,10] = 217
v[474,10] = 1779
v[475,10] = 327
v[476,10] = 1889
v[477,10] = 333
v[478,10] = 615
v[479,10] = 1665
v[480,10] = 1825
v[481,10] = 1639
v[482,10] = 237
v[483,10] = 1205
v[484,10] = 361
v[485,10] = 129
v[486,10] = 1655
v[487,10] = 983
v[488,10] = 1089
v[489,10] = 1171
v[490,10] = 401
v[491,10] = 677
v[492,10] = 643
v[493,10] = 749
v[494,10] = 303
v[495,10] = 1407
v[496,10] = 1873
v[497,10] = 1579
v[498,10] = 1491
v[499,10] = 1393
v[500,10] = 1247
v[501,10] = 789
v[502,10] = 763
v[503,10] = 49
v[504,10] = 5
v[505,10] = 1607
v[506,10] = 1891
v[507,10] = 735
v[508,10] = 1557
v[509,10] = 1909
v[510,10] = 1765
v[511,10] = 1777
v[512,10] = 1127
v[513,10] = 813
v[514,10] = 695
v[515,10] = 97
v[516,10] = 731
v[517,10] = 1503
v[518,10] = 1751
v[519,10] = 333
v[520,10] = 769
v[521,10] = 865
v[522,10] = 693
v[523,10] = 377
v[524,10] = 1919
v[525,10] = 957
v[526,10] = 1359
v[527,10] = 1627
v[528,10] = 1039
v[529,10] = 1783
v[530,10] = 1065
v[531,10] = 1665
v[532,10] = 1917
v[533,10] = 1947
v[534,10] = 991
v[535,10] = 1997
v[536,10] = 841
v[537,10] = 459
v[538,10] = 221
v[539,10] = 327
v[540,10] = 1595
v[541,10] = 1881
v[542,10] = 1269
v[543,10] = 1007
v[544,10] = 129
v[545,10] = 1413
v[546,10] = 475
v[547,10] = 1105
v[548,10] = 791
v[549,10] = 1983
v[550,10] = 1359
v[551,10] = 503
v[552,10] = 691
v[553,10] = 659
v[554,10] = 691
v[555,10] = 343
v[556,10] = 1375
v[557,10] = 1919
v[558,10] = 263
v[559,10] = 1373
v[560,10] = 603
v[561,10] = 1383
v[562,10] = 297
v[563,10] = 781
v[564,10] = 145
v[565,10] = 285
v[566,10] = 767
v[567,10] = 1739
v[568,10] = 1715
v[569,10] = 715
v[570,10] = 317
v[571,10] = 1333
v[572,10] = 85
v[573,10] = 831
v[574,10] = 1615
v[575,10] = 81
v[576,10] = 1667
v[577,10] = 1467
v[578,10] = 1457
v[579,10] = 1453
v[580,10] = 1825
v[581,10] = 109
v[582,10] = 387
v[583,10] = 1207
v[584,10] = 2039
v[585,10] = 213
v[586,10] = 1351
v[587,10] = 1329
v[588,10] = 1173
v[589,10] = 57
v[590,10] = 1769
v[591,10] = 951
v[592,10] = 183
v[593,10] = 23
v[594,10] = 451
v[595,10] = 1155
v[596,10] = 1551
v[597,10] = 2037
v[598,10] = 811
v[599,10] = 635
v[600,10] = 1671
v[601,10] = 1451
v[602,10] = 863
v[603,10] = 1499
v[604,10] = 1673
v[605,10] = 363
v[606,10] = 1029
v[607,10] = 1077
v[608,10] = 1525
v[609,10] = 277
v[610,10] = 1023
v[611,10] = 655
v[612,10] = 665
v[613,10] = 1869
v[614,10] = 1255
v[615,10] = 965
v[616,10] = 277
v[617,10] = 1601
v[618,10] = 329
v[619,10] = 1603
v[620,10] = 1901
v[621,10] = 395
v[622,10] = 65
v[623,10] = 1307
v[624,10] = 2029
v[625,10] = 21
v[626,10] = 1321
v[627,10] = 543
v[628,10] = 1569
v[629,10] = 1185
v[630,10] = 1905
v[631,10] = 1701
v[632,10] = 413
v[633,10] = 2041
v[634,10] = 1697
v[635,10] = 725
v[636,10] = 1417
v[637,10] = 1847
v[638,10] = 411
v[639,10] = 211
v[640,10] = 915
v[641,10] = 1891
v[642,10] = 17
v[643,10] = 1877
v[644,10] = 1699
v[645,10] = 687
v[646,10] = 1089
v[647,10] = 1973
v[648,10] = 1809
v[649,10] = 851
v[650,10] = 1495
v[651,10] = 1257
v[652,10] = 63
v[653,10] = 1323
v[654,10] = 1307
v[655,10] = 609
v[656,10] = 881
v[657,10] = 1543
v[658,10] = 177
v[659,10] = 617
v[660,10] = 1505
v[661,10] = 1747
v[662,10] = 1537
v[663,10] = 925
v[664,10] = 183
v[665,10] = 77
v[666,10] = 1723
v[667,10] = 1877
v[668,10] = 1703
v[669,10] = 397
v[670,10] = 459
v[671,10] = 521
v[672,10] = 257
v[673,10] = 1177
v[674,10] = 389
v[675,10] = 1947
v[676,10] = 1553
v[677,10] = 1583
v[678,10] = 1831
v[679,10] = 261
v[680,10] = 485
v[681,10] = 289
v[682,10] = 1281
v[683,10] = 1543
v[684,10] = 1591
v[685,10] = 1123
v[686,10] = 573
v[687,10] = 821
v[688,10] = 1065
v[689,10] = 1933
v[690,10] = 1373
v[691,10] = 2005
v[692,10] = 905
v[693,10] = 207
v[694,10] = 173
v[695,10] = 1573
v[696,10] = 1597
v[697,10] = 573
v[698,10] = 1883
v[699,10] = 1795
v[700,10] = 1499
v[701,10] = 1743
v[702,10] = 553
v[703,10] = 335
v[704,10] = 333
v[705,10] = 1645
v[706,10] = 791
v[707,10] = 871
v[708,10] = 1157
v[709,10] = 969
v[710,10] = 557
v[711,10] = 141
v[712,10] = 223
v[713,10] = 1129
v[714,10] = 1685
v[715,10] = 423
v[716,10] = 1069
v[717,10] = 391
v[718,10] = 99
v[719,10] = 95
v[720,10] = 1847
v[721,10] = 531
v[722,10] = 1859
v[723,10] = 1833
v[724,10] = 1833
v[725,10] = 341
v[726,10] = 237
v[727,10] = 1997
v[728,10] = 1799
v[729,10] = 409
v[730,10] = 431
v[731,10] = 1917
v[732,10] = 363
v[733,10] = 335
v[734,10] = 1039
v[735,10] = 1085
v[736,10] = 1657
v[737,10] = 1975
v[738,10] = 1527
v[739,10] = 1111
v[740,10] = 659
v[741,10] = 389
v[742,10] = 899
v[743,10] = 595
v[744,10] = 1439
v[745,10] = 1861
v[746,10] = 1979
v[747,10] = 1569
v[748,10] = 1087
v[749,10] = 1009
v[750,10] = 165
v[751,10] = 1895
v[752,10] = 1481
v[753,10] = 1583
v[754,10] = 29
v[755,10] = 1193
v[756,10] = 1673
v[757,10] = 1075
v[758,10] = 301
v[759,10] = 1081
v[760,10] = 1377
v[761,10] = 1747
v[762,10] = 1497
v[763,10] = 1103
v[764,10] = 1789
v[765,10] = 887
v[766,10] = 739
v[767,10] = 1577
v[768,10] = 313
v[769,10] = 1367
v[770,10] = 1299
v[771,10] = 1801
v[772,10] = 1131
v[773,10] = 1837
v[774,10] = 73
v[775,10] = 1865
v[776,10] = 1065
v[777,10] = 843
v[778,10] = 635
v[779,10] = 55
v[780,10] = 1655
v[781,10] = 913
v[782,10] = 1037
v[783,10] = 223
v[784,10] = 1871
v[785,10] = 1161
v[786,10] = 461
v[787,10] = 479
v[788,10] = 511
v[789,10] = 1721
v[790,10] = 1107
v[791,10] = 389
v[792,10] = 151
v[793,10] = 35
v[794,10] = 375
v[795,10] = 1099
v[796,10] = 937
v[797,10] = 1185
v[798,10] = 1701
v[799,10] = 769
v[800,10] = 639
v[801,10] = 1633
v[802,10] = 1609
v[803,10] = 379
v[804,10] = 1613
v[805,10] = 2031
v[806,10] = 685
v[807,10] = 289
v[808,10] = 975
v[809,10] = 671
v[810,10] = 1599
v[811,10] = 1447
v[812,10] = 871
v[813,10] = 647
v[814,10] = 99
v[815,10] = 139
v[816,10] = 1427
v[817,10] = 959
v[818,10] = 89
v[819,10] = 117
v[820,10] = 841
v[821,10] = 891
v[822,10] = 1959
v[823,10] = 223
v[824,10] = 1697
v[825,10] = 1145
v[826,10] = 499
v[827,10] = 1435
v[828,10] = 1809
v[829,10] = 1413
v[830,10] = 1445
v[831,10] = 1675
v[832,10] = 171
v[833,10] = 1073
v[834,10] = 1349
v[835,10] = 1545
v[836,10] = 2039
v[837,10] = 1027
v[838,10] = 1563
v[839,10] = 859
v[840,10] = 215
v[841,10] = 1673
v[842,10] = 1919
v[843,10] = 1633
v[844,10] = 779
v[845,10] = 411
v[846,10] = 1845
v[847,10] = 1477
v[848,10] = 1489
v[849,10] = 447
v[850,10] = 1545
v[851,10] = 351
v[852,10] = 1989
v[853,10] = 495
v[854,10] = 183
v[855,10] = 1639
v[856,10] = 1385
v[857,10] = 1805
v[858,10] = 1097
v[859,10] = 1249
v[860,10] = 1431
v[861,10] = 1571
v[862,10] = 591
v[863,10] = 697
v[864,10] = 1509
v[865,10] = 709
v[866,10] = 31
v[867,10] = 1563
v[868,10] = 165
v[869,10] = 513
v[870,10] = 1425
v[871,10] = 1299
v[872,10] = 1081
v[873,10] = 145
v[874,10] = 1841
v[875,10] = 1211
v[876,10] = 941
v[877,10] = 609
v[878,10] = 845
v[879,10] = 1169
v[880,10] = 1865
v[881,10] = 1593
v[882,10] = 347
v[883,10] = 293
v[884,10] = 1277
v[885,10] = 157
v[886,10] = 211
v[887,10] = 93
v[888,10] = 1679
v[889,10] = 1799
v[890,10] = 527
v[891,10] = 41
v[892,10] = 473
v[893,10] = 563
v[894,10] = 187
v[895,10] = 1525
v[896,10] = 575
v[897,10] = 1579
v[898,10] = 857
v[899,10] = 703
v[900,10] = 1211
v[901,10] = 647
v[902,10] = 709
v[903,10] = 981
v[904,10] = 285
v[905,10] = 697
v[906,10] = 163
v[907,10] = 981
v[908,10] = 153
v[909,10] = 1515
v[910,10] = 47
v[911,10] = 1553
v[912,10] = 599
v[913,10] = 225
v[914,10] = 1147
v[915,10] = 381
v[916,10] = 135
v[917,10] = 821
v[918,10] = 1965
v[919,10] = 609
v[920,10] = 1033
v[921,10] = 983
v[922,10] = 503
v[923,10] = 1117
v[924,10] = 327
v[925,10] = 453
v[926,10] = 2005
v[927,10] = 1257
v[928,10] = 343
v[929,10] = 1649
v[930,10] = 1199
v[931,10] = 599
v[932,10] = 1877
v[933,10] = 569
v[934,10] = 695
v[935,10] = 1587
v[936,10] = 1475
v[937,10] = 187
v[938,10] = 973
v[939,10] = 233
v[940,10] = 511
v[941,10] = 51
v[942,10] = 1083
v[943,10] = 665
v[944,10] = 1321
v[945,10] = 531
v[946,10] = 1875
v[947,10] = 1939
v[948,10] = 859
v[949,10] = 1507
v[950,10] = 1979
v[951,10] = 1203
v[952,10] = 1965
v[953,10] = 737
v[954,10] = 921
v[955,10] = 1565
v[956,10] = 1943
v[957,10] = 819
v[958,10] = 223
v[959,10] = 365
v[960,10] = 167
v[961,10] = 1705
v[962,10] = 413
v[963,10] = 1577
v[964,10] = 745
v[965,10] = 1573
v[966,10] = 655
v[967,10] = 1633
v[968,10] = 1003
v[969,10] = 91
v[970,10] = 1123
v[971,10] = 477
v[972,10] = 1741
v[973,10] = 1663
v[974,10] = 35
v[975,10] = 715
v[976,10] = 37
v[977,10] = 1513
v[978,10] = 815
v[979,10] = 941
v[980,10] = 1379
v[981,10] = 263
v[982,10] = 1831
v[983,10] = 1735
v[984,10] = 1111
v[985,10] = 1449
v[986,10] = 353
v[987,10] = 1941
v[988,10] = 1655
v[989,10] = 1349
v[990,10] = 877
v[991,10] = 285
v[992,10] = 1723
v[993,10] = 125
v[994,10] = 1753
v[995,10] = 985
v[996,10] = 723
v[997,10] = 175
v[998,10] = 439
v[999,10] = 791
v[1000,10] = 1051
v[1001,10] = 1261
v[1002,10] = 717
v[1003,10] = 1555
v[1004,10] = 1757
v[1005,10] = 1777
v[1006,10] = 577
v[1007,10] = 1583
v[1008,10] = 1957
v[1009,10] = 873
v[1010,10] = 331
v[1011,10] = 1163
v[1012,10] = 313
v[1013,10] = 1
v[1014,10] = 1963
v[1015,10] = 963
v[1016,10] = 1905
v[1017,10] = 821
v[1018,10] = 1677
v[1019,10] = 185
v[1020,10] = 709
v[1021,10] = 545
v[1022,10] = 1723
v[1023,10] = 215
v[1024,10] = 1885
v[1025,10] = 1249
v[1026,10] = 583
v[1027,10] = 1803
v[1028,10] = 839
v[1029,10] = 885
v[1030,10] = 485
v[1031,10] = 413
v[1032,10] = 1767
v[1033,10] = 425
v[1034,10] = 129
v[1035,10] = 1035
v[1036,10] = 329
v[1037,10] = 1263
v[1038,10] = 1881
v[1039,10] = 1779
v[1040,10] = 1565
v[1041,10] = 359
v[1042,10] = 367
v[1043,10] = 453
v[1044,10] = 707
v[1045,10] = 1419
v[1046,10] = 831
v[1047,10] = 1889
v[1048,10] = 887
v[1049,10] = 1871
v[1050,10] = 1869
v[1051,10] = 747
v[1052,10] = 223
v[1053,10] = 1547
v[1054,10] = 1799
v[1055,10] = 433
v[1056,10] = 1441
v[1057,10] = 553
v[1058,10] = 2021
v[1059,10] = 1303
v[1060,10] = 1505
v[1061,10] = 1735
v[1062,10] = 1619
v[1063,10] = 1065
v[1064,10] = 1161
v[1065,10] = 2047
v[1066,10] = 347
v[1067,10] = 867
v[1068,10] = 881
v[1069,10] = 1447
v[1070,10] = 329
v[1071,10] = 781
v[1072,10] = 1065
v[1073,10] = 219
v[1074,10] = 589
v[1075,10] = 645
v[1076,10] = 1257
v[1077,10] = 1833
v[1078,10] = 749
v[1079,10] = 1841
v[1080,10] = 1733
v[1081,10] = 1179
v[1082,10] = 1191
v[1083,10] = 1025
v[1084,10] = 1639
v[1085,10] = 1955
v[1086,10] = 1423
v[1087,10] = 1685
v[1088,10] = 1711
v[1089,10] = 493
v[1090,10] = 549
v[1091,10] = 783
v[1092,10] = 1653
v[1093,10] = 397
v[1094,10] = 895
v[1095,10] = 233
v[1096,10] = 759
v[1097,10] = 1505
v[1098,10] = 677
v[1099,10] = 1449
v[1100,10] = 1573
v[1101,10] = 1297
v[1102,10] = 1821
v[1103,10] = 1691
v[1104,10] = 791
v[1105,10] = 289
v[1106,10] = 1187
v[1107,10] = 867
v[1108,10] = 1535
v[1109,10] = 575
v[1110,10] = 183
v[337,11] = 3915
v[338,11] = 97
v[339,11] = 3047
v[340,11] = 937
v[341,11] = 2897
v[342,11] = 953
v[343,11] = 127
v[344,11] = 1201
v[345,11] = 3819
v[346,11] = 193
v[347,11] = 2053
v[348,11] = 3061
v[349,11] = 3759
v[350,11] = 1553
v[351,11] = 2007
v[352,11] = 2493
v[353,11] = 603
v[354,11] = 3343
v[355,11] = 3751
v[356,11] = 1059
v[357,11] = 783
v[358,11] = 1789
v[359,11] = 1589
v[360,11] = 283
v[361,11] = 1093
v[362,11] = 3919
v[363,11] = 2747
v[364,11] = 277
v[365,11] = 2605
v[366,11] = 2169
v[367,11] = 2905
v[368,11] = 721
v[369,11] = 4069
v[370,11] = 233
v[371,11] = 261
v[372,11] = 1137
v[373,11] = 3993
v[374,11] = 3619
v[375,11] = 2881
v[376,11] = 1275
v[377,11] = 3865
v[378,11] = 1299
v[379,11] = 3757
v[380,11] = 1193
v[381,11] = 733
v[382,11] = 993
v[383,11] = 1153
v[384,11] = 2945
v[385,11] = 3163
v[386,11] = 3179
v[387,11] = 437
v[388,11] = 271
v[389,11] = 3493
v[390,11] = 3971
v[391,11] = 1005
v[392,11] = 2615
v[393,11] = 2253
v[394,11] = 1131
v[395,11] = 585
v[396,11] = 2775
v[397,11] = 2171
v[398,11] = 2383
v[399,11] = 2937
v[400,11] = 2447
v[401,11] = 1745
v[402,11] = 663
v[403,11] = 1515
v[404,11] = 3767
v[405,11] = 2709
v[406,11] = 1767
v[407,11] = 3185
v[408,11] = 3017
v[409,11] = 2815
v[410,11] = 1829
v[411,11] = 87
v[412,11] = 3341
v[413,11] = 793
v[414,11] = 2627
v[415,11] = 2169
v[416,11] = 1875
v[417,11] = 3745
v[418,11] = 367
v[419,11] = 3783
v[420,11] = 783
v[421,11] = 827
v[422,11] = 3253
v[423,11] = 2639
v[424,11] = 2955
v[425,11] = 3539
v[426,11] = 1579
v[427,11] = 2109
v[428,11] = 379
v[429,11] = 2939
v[430,11] = 3019
v[431,11] = 1999
v[432,11] = 2253
v[433,11] = 2911
v[434,11] = 3733
v[435,11] = 481
v[436,11] = 1767
v[437,11] = 1055
v[438,11] = 4019
v[439,11] = 4085
v[440,11] = 105
v[441,11] = 1829
v[442,11] = 2097
v[443,11] = 2379
v[444,11] = 1567
v[445,11] = 2713
v[446,11] = 737
v[447,11] = 3423
v[448,11] = 3941
v[449,11] = 2659
v[450,11] = 3961
v[451,11] = 1755
v[452,11] = 3613
v[453,11] = 1937
v[454,11] = 1559
v[455,11] = 2287
v[456,11] = 2743
v[457,11] = 67
v[458,11] = 2859
v[459,11] = 325
v[460,11] = 2601
v[461,11] = 1149
v[462,11] = 3259
v[463,11] = 2403
v[464,11] = 3947
v[465,11] = 2011
v[466,11] = 175
v[467,11] = 3389
v[468,11] = 3915
v[469,11] = 1315
v[470,11] = 2447
v[471,11] = 141
v[472,11] = 359
v[473,11] = 3609
v[474,11] = 3933
v[475,11] = 729
v[476,11] = 2051
v[477,11] = 1755
v[478,11] = 2149
v[479,11] = 2107
v[480,11] = 1741
v[481,11] = 1051
v[482,11] = 3681
v[483,11] = 471
v[484,11] = 1055
v[485,11] = 845
v[486,11] = 257
v[487,11] = 1559
v[488,11] = 1061
v[489,11] = 2803
v[490,11] = 2219
v[491,11] = 1315
v[492,11] = 1369
v[493,11] = 3211
v[494,11] = 4027
v[495,11] = 105
v[496,11] = 11
v[497,11] = 1077
v[498,11] = 2857
v[499,11] = 337
v[500,11] = 3553
v[501,11] = 3503
v[502,11] = 3917
v[503,11] = 2665
v[504,11] = 3823
v[505,11] = 3403
v[506,11] = 3711
v[507,11] = 2085
v[508,11] = 1103
v[509,11] = 1641
v[510,11] = 701
v[511,11] = 4095
v[512,11] = 2883
v[513,11] = 1435
v[514,11] = 653
v[515,11] = 2363
v[516,11] = 1597
v[517,11] = 767
v[518,11] = 869
v[519,11] = 1825
v[520,11] = 1117
v[521,11] = 1297
v[522,11] = 501
v[523,11] = 505
v[524,11] = 149
v[525,11] = 873
v[526,11] = 2673
v[527,11] = 551
v[528,11] = 1499
v[529,11] = 2793
v[530,11] = 3277
v[531,11] = 2143
v[532,11] = 3663
v[533,11] = 533
v[534,11] = 3991
v[535,11] = 575
v[536,11] = 1877
v[537,11] = 1009
v[538,11] = 3929
v[539,11] = 473
v[540,11] = 3009
v[541,11] = 2595
v[542,11] = 3249
v[543,11] = 675
v[544,11] = 3593
v[545,11] = 2453
v[546,11] = 1567
v[547,11] = 973
v[548,11] = 595
v[549,11] = 1335
v[550,11] = 1715
v[551,11] = 589
v[552,11] = 85
v[553,11] = 2265
v[554,11] = 3069
v[555,11] = 461
v[556,11] = 1659
v[557,11] = 2627
v[558,11] = 1307
v[559,11] = 1731
v[560,11] = 1501
v[561,11] = 1699
v[562,11] = 3545
v[563,11] = 3803
v[564,11] = 2157
v[565,11] = 453
v[566,11] = 2813
v[567,11] = 2047
v[568,11] = 2999
v[569,11] = 3841
v[570,11] = 2361
v[571,11] = 1079
v[572,11] = 573
v[573,11] = 69
v[574,11] = 1363
v[575,11] = 1597
v[576,11] = 3427
v[577,11] = 2899
v[578,11] = 2771
v[579,11] = 1327
v[580,11] = 1117
v[581,11] = 1523
v[582,11] = 3521
v[583,11] = 2393
v[584,11] = 2537
v[585,11] = 1979
v[586,11] = 3179
v[587,11] = 683
v[588,11] = 2453
v[589,11] = 453
v[590,11] = 1227
v[591,11] = 779
v[592,11] = 671
v[593,11] = 3483
v[594,11] = 2135
v[595,11] = 3139
v[596,11] = 3381
v[597,11] = 3945
v[598,11] = 57
v[599,11] = 1541
v[600,11] = 3405
v[601,11] = 3381
v[602,11] = 2371
v[603,11] = 2879
v[604,11] = 1985
v[605,11] = 987
v[606,11] = 3017
v[607,11] = 3031
v[608,11] = 3839
v[609,11] = 1401
v[610,11] = 3749
v[611,11] = 2977
v[612,11] = 681
v[613,11] = 1175
v[614,11] = 1519
v[615,11] = 3355
v[616,11] = 907
v[617,11] = 117
v[618,11] = 771
v[619,11] = 3741
v[620,11] = 3337
v[621,11] = 1743
v[622,11] = 1227
v[623,11] = 3335
v[624,11] = 2755
v[625,11] = 1909
v[626,11] = 3603
v[627,11] = 2397
v[628,11] = 653
v[629,11] = 87
v[630,11] = 2025
v[631,11] = 2617
v[632,11] = 3257
v[633,11] = 287
v[634,11] = 3051
v[635,11] = 3809
v[636,11] = 897
v[637,11] = 2215
v[638,11] = 63
v[639,11] = 2043
v[640,11] = 1757
v[641,11] = 3671
v[642,11] = 297
v[643,11] = 3131
v[644,11] = 1305
v[645,11] = 293
v[646,11] = 3865
v[647,11] = 3173
v[648,11] = 3397
v[649,11] = 2269
v[650,11] = 3673
v[651,11] = 717
v[652,11] = 3041
v[653,11] = 3341
v[654,11] = 3595
v[655,11] = 3819
v[656,11] = 2871
v[657,11] = 3973
v[658,11] = 1129
v[659,11] = 513
v[660,11] = 871
v[661,11] = 1485
v[662,11] = 3977
v[663,11] = 2473
v[664,11] = 1171
v[665,11] = 1143
v[666,11] = 3063
v[667,11] = 3547
v[668,11] = 2183
v[669,11] = 3993
v[670,11] = 133
v[671,11] = 2529
v[672,11] = 2699
v[673,11] = 233
v[674,11] = 2355
v[675,11] = 231
v[676,11] = 3241
v[677,11] = 611
v[678,11] = 1309
v[679,11] = 3829
v[680,11] = 1839
v[681,11] = 1495
v[682,11] = 301
v[683,11] = 1169
v[684,11] = 1613
v[685,11] = 2673
v[686,11] = 243
v[687,11] = 3601
v[688,11] = 3669
v[689,11] = 2813
v[690,11] = 2671
v[691,11] = 2679
v[692,11] = 3463
v[693,11] = 2477
v[694,11] = 1795
v[695,11] = 617
v[696,11] = 2317
v[697,11] = 1855
v[698,11] = 1057
v[699,11] = 1703
v[700,11] = 1761
v[701,11] = 2515
v[702,11] = 801
v[703,11] = 1205
v[704,11] = 1311
v[705,11] = 473
v[706,11] = 3963
v[707,11] = 697
v[708,11] = 1221
v[709,11] = 251
v[710,11] = 381
v[711,11] = 3887
v[712,11] = 1761
v[713,11] = 3093
v[714,11] = 3721
v[715,11] = 2079
v[716,11] = 4085
v[717,11] = 379
v[718,11] = 3601
v[719,11] = 3845
v[720,11] = 433
v[721,11] = 1781
v[722,11] = 29
v[723,11] = 1897
v[724,11] = 1599
v[725,11] = 2163
v[726,11] = 75
v[727,11] = 3475
v[728,11] = 3957
v[729,11] = 1641
v[730,11] = 3911
v[731,11] = 2959
v[732,11] = 2833
v[733,11] = 1279
v[734,11] = 1099
v[735,11] = 403
v[736,11] = 799
v[737,11] = 2183
v[738,11] = 2699
v[739,11] = 1711
v[740,11] = 2037
v[741,11] = 727
v[742,11] = 289
v[743,11] = 1785
v[744,11] = 1575
v[745,11] = 3633
v[746,11] = 2367
v[747,11] = 1261
v[748,11] = 3953
v[749,11] = 1735
v[750,11] = 171
v[751,11] = 1959
v[752,11] = 2867
v[753,11] = 859
v[754,11] = 2951
v[755,11] = 3211
v[756,11] = 15
v[757,11] = 1279
v[758,11] = 1323
v[759,11] = 599
v[760,11] = 1651
v[761,11] = 3951
v[762,11] = 1011
v[763,11] = 315
v[764,11] = 3513
v[765,11] = 3351
v[766,11] = 1725
v[767,11] = 3793
v[768,11] = 2399
v[769,11] = 287
v[770,11] = 4017
v[771,11] = 3571
v[772,11] = 1007
v[773,11] = 541
v[774,11] = 3115
v[775,11] = 429
v[776,11] = 1585
v[777,11] = 1285
v[778,11] = 755
v[779,11] = 1211
v[780,11] = 3047
v[781,11] = 915
v[782,11] = 3611
v[783,11] = 2697
v[784,11] = 2129
v[785,11] = 3669
v[786,11] = 81
v[787,11] = 3939
v[788,11] = 2437
v[789,11] = 915
v[790,11] = 779
v[791,11] = 3567
v[792,11] = 3701
v[793,11] = 2479
v[794,11] = 3807
v[795,11] = 1893
v[796,11] = 3927
v[797,11] = 2619
v[798,11] = 2543
v[799,11] = 3633
v[800,11] = 2007
v[801,11] = 3857
v[802,11] = 3837
v[803,11] = 487
v[804,11] = 1769
v[805,11] = 3759
v[806,11] = 3105
v[807,11] = 2727
v[808,11] = 3155
v[809,11] = 2479
v[810,11] = 1341
v[811,11] = 1657
v[812,11] = 2767
v[813,11] = 2541
v[814,11] = 577
v[815,11] = 2105
v[816,11] = 799
v[817,11] = 17
v[818,11] = 2871
v[819,11] = 3637
v[820,11] = 953
v[821,11] = 65
v[822,11] = 69
v[823,11] = 2897
v[824,11] = 3841
v[825,11] = 3559
v[826,11] = 4067
v[827,11] = 2335
v[828,11] = 3409
v[829,11] = 1087
v[830,11] = 425
v[831,11] = 2813
v[832,11] = 1705
v[833,11] = 1701
v[834,11] = 1237
v[835,11] = 821
v[836,11] = 1375
v[837,11] = 3673
v[838,11] = 2693
v[839,11] = 3925
v[840,11] = 1541
v[841,11] = 1871
v[842,11] = 2285
v[843,11] = 847
v[844,11] = 4035
v[845,11] = 1101
v[846,11] = 2029
v[847,11] = 855
v[848,11] = 2733
v[849,11] = 2503
v[850,11] = 121
v[851,11] = 2855
v[852,11] = 1069
v[853,11] = 3463
v[854,11] = 3505
v[855,11] = 1539
v[856,11] = 607
v[857,11] = 1349
v[858,11] = 575
v[859,11] = 2301
v[860,11] = 2321
v[861,11] = 1101
v[862,11] = 333
v[863,11] = 291
v[864,11] = 2171
v[865,11] = 4085
v[866,11] = 2173
v[867,11] = 2541
v[868,11] = 1195
v[869,11] = 925
v[870,11] = 4039
v[871,11] = 1379
v[872,11] = 699
v[873,11] = 1979
v[874,11] = 275
v[875,11] = 953
v[876,11] = 1755
v[877,11] = 1643
v[878,11] = 325
v[879,11] = 101
v[880,11] = 2263
v[881,11] = 3329
v[882,11] = 3673
v[883,11] = 3413
v[884,11] = 1977
v[885,11] = 2727
v[886,11] = 2313
v[887,11] = 1419
v[888,11] = 887
v[889,11] = 609
v[890,11] = 2475
v[891,11] = 591
v[892,11] = 2613
v[893,11] = 2081
v[894,11] = 3805
v[895,11] = 3435
v[896,11] = 2409
v[897,11] = 111
v[898,11] = 3557
v[899,11] = 3607
v[900,11] = 903
v[901,11] = 231
v[902,11] = 3059
v[903,11] = 473
v[904,11] = 2959
v[905,11] = 2925
v[906,11] = 3861
v[907,11] = 2043
v[908,11] = 3887
v[909,11] = 351
v[910,11] = 2865
v[911,11] = 369
v[912,11] = 1377
v[913,11] = 2639
v[914,11] = 1261
v[915,11] = 3625
v[916,11] = 3279
v[917,11] = 2201
v[918,11] = 2949
v[919,11] = 3049
v[920,11] = 449
v[921,11] = 1297
v[922,11] = 897
v[923,11] = 1891
v[924,11] = 411
v[925,11] = 2773
v[926,11] = 749
v[927,11] = 2753
v[928,11] = 1825
v[929,11] = 853
v[930,11] = 2775
v[931,11] = 3547
v[932,11] = 3923
v[933,11] = 3923
v[934,11] = 987
v[935,11] = 3723
v[936,11] = 2189
v[937,11] = 3877
v[938,11] = 3577
v[939,11] = 297
v[940,11] = 2763
v[941,11] = 1845
v[942,11] = 3083
v[943,11] = 2951
v[944,11] = 483
v[945,11] = 2169
v[946,11] = 3985
v[947,11] = 245
v[948,11] = 3655
v[949,11] = 3441
v[950,11] = 1023
v[951,11] = 235
v[952,11] = 835
v[953,11] = 3693
v[954,11] = 3585
v[955,11] = 327
v[956,11] = 1003
v[957,11] = 543
v[958,11] = 3059
v[959,11] = 2637
v[960,11] = 2923
v[961,11] = 87
v[962,11] = 3617
v[963,11] = 1031
v[964,11] = 1043
v[965,11] = 903
v[966,11] = 2913
v[967,11] = 2177
v[968,11] = 2641
v[969,11] = 3279
v[970,11] = 389
v[971,11] = 2009
v[972,11] = 525
v[973,11] = 4085
v[974,11] = 3299
v[975,11] = 987
v[976,11] = 2409
v[977,11] = 813
v[978,11] = 2683
v[979,11] = 373
v[980,11] = 2695
v[981,11] = 3775
v[982,11] = 2375
v[983,11] = 1119
v[984,11] = 2791
v[985,11] = 223
v[986,11] = 325
v[987,11] = 587
v[988,11] = 1379
v[989,11] = 2877
v[990,11] = 2867
v[991,11] = 3793
v[992,11] = 655
v[993,11] = 831
v[994,11] = 3425
v[995,11] = 1663
v[996,11] = 1681
v[997,11] = 2657
v[998,11] = 1865
v[999,11] = 3943
v[1000,11] = 2977
v[1001,11] = 1979
v[1002,11] = 2271
v[1003,11] = 3247
v[1004,11] = 1267
v[1005,11] = 1747
v[1006,11] = 811
v[1007,11] = 159
v[1008,11] = 429
v[1009,11] = 2001
v[1010,11] = 1195
v[1011,11] = 3065
v[1012,11] = 553
v[1013,11] = 1499
v[1014,11] = 3529
v[1015,11] = 1081
v[1016,11] = 2877
v[1017,11] = 3077
v[1018,11] = 845
v[1019,11] = 1793
v[1020,11] = 2409
v[1021,11] = 3995
v[1022,11] = 2559
v[1023,11] = 4081
v[1024,11] = 1195
v[1025,11] = 2955
v[1026,11] = 1117
v[1027,11] = 1409
v[1028,11] = 785
v[1029,11] = 287
v[1030,11] = 1521
v[1031,11] = 1607
v[1032,11] = 85
v[1033,11] = 3055
v[1034,11] = 3123
v[1035,11] = 2533
v[1036,11] = 2329
v[1037,11] = 3477
v[1038,11] = 799
v[1039,11] = 3683
v[1040,11] = 3715
v[1041,11] = 337
v[1042,11] = 3139
v[1043,11] = 3311
v[1044,11] = 431
v[1045,11] = 3511
v[1046,11] = 2299
v[1047,11] = 365
v[1048,11] = 2941
v[1049,11] = 3067
v[1050,11] = 1331
v[1051,11] = 1081
v[1052,11] = 1097
v[1053,11] = 2853
v[1054,11] = 2299
v[1055,11] = 495
v[1056,11] = 1745
v[1057,11] = 749
v[1058,11] = 3819
v[1059,11] = 619
v[1060,11] = 1059
v[1061,11] = 3559
v[1062,11] = 183
v[1063,11] = 3743
v[1064,11] = 723
v[1065,11] = 949
v[1066,11] = 3501
v[1067,11] = 733
v[1068,11] = 2599
v[1069,11] = 3983
v[1070,11] = 3961
v[1071,11] = 911
v[1072,11] = 1899
v[1073,11] = 985
v[1074,11] = 2493
v[1075,11] = 1795
v[1076,11] = 653
v[1077,11] = 157
v[1078,11] = 433
v[1079,11] = 2361
v[1080,11] = 3093
v[1081,11] = 3119
v[1082,11] = 3679
v[1083,11] = 2367
v[1084,11] = 1701
v[1085,11] = 1445
v[1086,11] = 1321
v[1087,11] = 2397
v[1088,11] = 1241
v[1089,11] = 3305
v[1090,11] = 3985
v[1091,11] = 2349
v[1092,11] = 4067
v[1093,11] = 3805
v[1094,11] = 3073
v[1095,11] = 2837
v[1096,11] = 1567
v[1097,11] = 3783
v[1098,11] = 451
v[1099,11] = 2441
v[1100,11] = 1181
v[1101,11] = 487
v[1102,11] = 543
v[1103,11] = 1201
v[1104,11] = 3735
v[1105,11] = 2517
v[1106,11] = 733
v[1107,11] = 1535
v[1108,11] = 2175
v[1109,11] = 3613
v[1110,11] = 3019
v[481,12] = 2319
v[482,12] = 653
v[483,12] = 1379
v[484,12] = 1675
v[485,12] = 1951
v[486,12] = 7075
v[487,12] = 2087
v[488,12] = 7147
v[489,12] = 1427
v[490,12] = 893
v[491,12] = 171
v[492,12] = 2019
v[493,12] = 7235
v[494,12] = 5697
v[495,12] = 3615
v[496,12] = 1961
v[497,12] = 7517
v[498,12] = 6849
v[499,12] = 2893
v[500,12] = 1883
v[501,12] = 2863
v[502,12] = 2173
v[503,12] = 4543
v[504,12] = 73
v[505,12] = 381
v[506,12] = 3893
v[507,12] = 6045
v[508,12] = 1643
v[509,12] = 7669
v[510,12] = 1027
v[511,12] = 1549
v[512,12] = 3983
v[513,12] = 1985
v[514,12] = 6589
v[515,12] = 7497
v[516,12] = 2745
v[517,12] = 2375
v[518,12] = 7047
v[519,12] = 1117
v[520,12] = 1171
v[521,12] = 1975
v[522,12] = 5199
v[523,12] = 3915
v[524,12] = 3695
v[525,12] = 8113
v[526,12] = 4303
v[527,12] = 3773
v[528,12] = 7705
v[529,12] = 6855
v[530,12] = 1675
v[531,12] = 2245
v[532,12] = 2817
v[533,12] = 1719
v[534,12] = 569
v[535,12] = 1021
v[536,12] = 2077
v[537,12] = 5945
v[538,12] = 1833
v[539,12] = 2631
v[540,12] = 4851
v[541,12] = 6371
v[542,12] = 833
v[543,12] = 7987
v[544,12] = 331
v[545,12] = 1899
v[546,12] = 8093
v[547,12] = 6719
v[548,12] = 6903
v[549,12] = 5903
v[550,12] = 5657
v[551,12] = 5007
v[552,12] = 2689
v[553,12] = 6637
v[554,12] = 2675
v[555,12] = 1645
v[556,12] = 1819
v[557,12] = 689
v[558,12] = 6709
v[559,12] = 7717
v[560,12] = 6295
v[561,12] = 7013
v[562,12] = 7695
v[563,12] = 3705
v[564,12] = 7069
v[565,12] = 2621
v[566,12] = 3631
v[567,12] = 6571
v[568,12] = 6259
v[569,12] = 7261
v[570,12] = 3397
v[571,12] = 7645
v[572,12] = 1115
v[573,12] = 4753
v[574,12] = 2047
v[575,12] = 7579
v[576,12] = 2271
v[577,12] = 5403
v[578,12] = 4911
v[579,12] = 7629
v[580,12] = 4225
v[581,12] = 1209
v[582,12] = 6955
v[583,12] = 6951
v[584,12] = 1829
v[585,12] = 5579
v[586,12] = 5231
v[587,12] = 1783
v[588,12] = 4285
v[589,12] = 7425
v[590,12] = 599
v[591,12] = 5785
v[592,12] = 3275
v[593,12] = 5643
v[594,12] = 2263
v[595,12] = 657
v[596,12] = 6769
v[597,12] = 6261
v[598,12] = 1251
v[599,12] = 3249
v[600,12] = 4447
v[601,12] = 4111
v[602,12] = 3991
v[603,12] = 1215
v[604,12] = 131
v[605,12] = 4397
v[606,12] = 3487
v[607,12] = 7585
v[608,12] = 5565
v[609,12] = 7199
v[610,12] = 3573
v[611,12] = 7105
v[612,12] = 7409
v[613,12] = 1671
v[614,12] = 949
v[615,12] = 3889
v[616,12] = 5971
v[617,12] = 3333
v[618,12] = 225
v[619,12] = 3647
v[620,12] = 5403
v[621,12] = 3409
v[622,12] = 7459
v[623,12] = 6879
v[624,12] = 5789
v[625,12] = 6567
v[626,12] = 5581
v[627,12] = 4919
v[628,12] = 1927
v[629,12] = 4407
v[630,12] = 8085
v[631,12] = 4691
v[632,12] = 611
v[633,12] = 3005
v[634,12] = 591
v[635,12] = 753
v[636,12] = 589
v[637,12] = 171
v[638,12] = 5729
v[639,12] = 5891
v[640,12] = 1033
v[641,12] = 3049
v[642,12] = 6567
v[643,12] = 5257
v[644,12] = 8003
v[645,12] = 1757
v[646,12] = 4489
v[647,12] = 4923
v[648,12] = 6379
v[649,12] = 5171
v[650,12] = 1757
v[651,12] = 689
v[652,12] = 3081
v[653,12] = 1389
v[654,12] = 4113
v[655,12] = 455
v[656,12] = 2761
v[657,12] = 847
v[658,12] = 7575
v[659,12] = 5829
v[660,12] = 633
v[661,12] = 6629
v[662,12] = 1103
v[663,12] = 7635
v[664,12] = 803
v[665,12] = 6175
v[666,12] = 6587
v[667,12] = 2711
v[668,12] = 3879
v[669,12] = 67
v[670,12] = 1179
v[671,12] = 4761
v[672,12] = 7281
v[673,12] = 1557
v[674,12] = 3379
v[675,12] = 2459
v[676,12] = 4273
v[677,12] = 4127
v[678,12] = 7147
v[679,12] = 35
v[680,12] = 3549
v[681,12] = 395
v[682,12] = 3735
v[683,12] = 5787
v[684,12] = 4179
v[685,12] = 5889
v[686,12] = 5057
v[687,12] = 7473
v[688,12] = 4713
v[689,12] = 2133
v[690,12] = 2897
v[691,12] = 1841
v[692,12] = 2125
v[693,12] = 1029
v[694,12] = 1695
v[695,12] = 6523
v[696,12] = 1143
v[697,12] = 5105
v[698,12] = 7133
v[699,12] = 3351
v[700,12] = 2775
v[701,12] = 3971
v[702,12] = 4503
v[703,12] = 7589
v[704,12] = 5155
v[705,12] = 4305
v[706,12] = 1641
v[707,12] = 4717
v[708,12] = 2427
v[709,12] = 5617
v[710,12] = 1267
v[711,12] = 399
v[712,12] = 5831
v[713,12] = 4305
v[714,12] = 4241
v[715,12] = 3395
v[716,12] = 3045
v[717,12] = 4899
v[718,12] = 1713
v[719,12] = 171
v[720,12] = 411
v[721,12] = 7099
v[722,12] = 5473
v[723,12] = 5209
v[724,12] = 1195
v[725,12] = 1077
v[726,12] = 1309
v[727,12] = 2953
v[728,12] = 7343
v[729,12] = 4887
v[730,12] = 3229
v[731,12] = 6759
v[732,12] = 6721
v[733,12] = 6775
v[734,12] = 675
v[735,12] = 4039
v[736,12] = 2493
v[737,12] = 7511
v[738,12] = 3269
v[739,12] = 4199
v[740,12] = 6625
v[741,12] = 7943
v[742,12] = 2013
v[743,12] = 4145
v[744,12] = 667
v[745,12] = 513
v[746,12] = 2303
v[747,12] = 4591
v[748,12] = 7941
v[749,12] = 2741
v[750,12] = 987
v[751,12] = 8061
v[752,12] = 3161
v[753,12] = 5951
v[754,12] = 1431
v[755,12] = 831
v[756,12] = 5559
v[757,12] = 7405
v[758,12] = 1357
v[759,12] = 4319
v[760,12] = 4235
v[761,12] = 5421
v[762,12] = 2559
v[763,12] = 4415
v[764,12] = 2439
v[765,12] = 823
v[766,12] = 1725
v[767,12] = 6219
v[768,12] = 4903
v[769,12] = 6699
v[770,12] = 5451
v[771,12] = 349
v[772,12] = 7703
v[773,12] = 2927
v[774,12] = 7809
v[775,12] = 6179
v[776,12] = 1417
v[777,12] = 5987
v[778,12] = 3017
v[779,12] = 4983
v[780,12] = 3479
v[781,12] = 4525
v[782,12] = 4643
v[783,12] = 4911
v[784,12] = 227
v[785,12] = 5475
v[786,12] = 2287
v[787,12] = 5581
v[788,12] = 6817
v[789,12] = 1937
v[790,12] = 1421
v[791,12] = 4415
v[792,12] = 7977
v[793,12] = 1789
v[794,12] = 3907
v[795,12] = 6815
v[796,12] = 6789
v[797,12] = 6003
v[798,12] = 5609
v[799,12] = 4507
v[800,12] = 337
v[801,12] = 7427
v[802,12] = 7943
v[803,12] = 3075
v[804,12] = 6427
v[805,12] = 1019
v[806,12] = 7121
v[807,12] = 4763
v[808,12] = 81
v[809,12] = 3587
v[810,12] = 2929
v[811,12] = 1795
v[812,12] = 8067
v[813,12] = 2415
v[814,12] = 1265
v[815,12] = 4025
v[816,12] = 5599
v[817,12] = 4771
v[818,12] = 3025
v[819,12] = 2313
v[820,12] = 6129
v[821,12] = 7611
v[822,12] = 6881
v[823,12] = 5253
v[824,12] = 4413
v[825,12] = 7869
v[826,12] = 105
v[827,12] = 3173
v[828,12] = 1629
v[829,12] = 2537
v[830,12] = 1023
v[831,12] = 4409
v[832,12] = 7209
v[833,12] = 4413
v[834,12] = 7107
v[835,12] = 7469
v[836,12] = 33
v[837,12] = 1955
v[838,12] = 2881
v[839,12] = 5167
v[840,12] = 6451
v[841,12] = 4211
v[842,12] = 179
v[843,12] = 5573
v[844,12] = 7879
v[845,12] = 3387
v[846,12] = 7759
v[847,12] = 5455
v[848,12] = 7157
v[849,12] = 1891
v[850,12] = 5683
v[851,12] = 5689
v[852,12] = 6535
v[853,12] = 3109
v[854,12] = 6555
v[855,12] = 6873
v[856,12] = 1249
v[857,12] = 4251
v[858,12] = 6437
v[859,12] = 49
v[860,12] = 2745
v[861,12] = 1201
v[862,12] = 7327
v[863,12] = 4179
v[864,12] = 6783
v[865,12] = 623
v[866,12] = 2779
v[867,12] = 5963
v[868,12] = 2585
v[869,12] = 6927
v[870,12] = 5333
v[871,12] = 4033
v[872,12] = 285
v[873,12] = 7467
v[874,12] = 4443
v[875,12] = 4917
v[876,12] = 3
v[877,12] = 4319
v[878,12] = 5517
v[879,12] = 3449
v[880,12] = 813
v[881,12] = 5499
v[882,12] = 2515
v[883,12] = 5771
v[884,12] = 3357
v[885,12] = 2073
v[886,12] = 4395
v[887,12] = 4925
v[888,12] = 2643
v[889,12] = 7215
v[890,12] = 5817
v[891,12] = 1199
v[892,12] = 1597
v[893,12] = 1619
v[894,12] = 7535
v[895,12] = 4833
v[896,12] = 609
v[897,12] = 4797
v[898,12] = 8171
v[899,12] = 6847
v[900,12] = 793
v[901,12] = 6757
v[902,12] = 8165
v[903,12] = 3371
v[904,12] = 2431
v[905,12] = 5235
v[906,12] = 4739
v[907,12] = 7703
v[908,12] = 7223
v[909,12] = 6525
v[910,12] = 5891
v[911,12] = 5605
v[912,12] = 4433
v[913,12] = 3533
v[914,12] = 5267
v[915,12] = 5125
v[916,12] = 5037
v[917,12] = 225
v[918,12] = 6717
v[919,12] = 1121
v[920,12] = 5741
v[921,12] = 2013
v[922,12] = 4327
v[923,12] = 4839
v[924,12] = 569
v[925,12] = 5227
v[926,12] = 7677
v[927,12] = 4315
v[928,12] = 2391
v[929,12] = 5551
v[930,12] = 859
v[931,12] = 3627
v[932,12] = 6377
v[933,12] = 3903
v[934,12] = 4311
v[935,12] = 6527
v[936,12] = 7573
v[937,12] = 4905
v[938,12] = 7731
v[939,12] = 1909
v[940,12] = 1555
v[941,12] = 3279
v[942,12] = 1949
v[943,12] = 1887
v[944,12] = 6675
v[945,12] = 5509
v[946,12] = 2033
v[947,12] = 5473
v[948,12] = 3539
v[949,12] = 5033
v[950,12] = 5935
v[951,12] = 6095
v[952,12] = 4761
v[953,12] = 1771
v[954,12] = 1271
v[955,12] = 1717
v[956,12] = 4415
v[957,12] = 5083
v[958,12] = 6277
v[959,12] = 3147
v[960,12] = 7695
v[961,12] = 2461
v[962,12] = 4783
v[963,12] = 4539
v[964,12] = 5833
v[965,12] = 5583
v[966,12] = 651
v[967,12] = 1419
v[968,12] = 2605
v[969,12] = 5511
v[970,12] = 3913
v[971,12] = 5795
v[972,12] = 2333
v[973,12] = 2329
v[974,12] = 4431
v[975,12] = 3725
v[976,12] = 6069
v[977,12] = 2699
v[978,12] = 7055
v[979,12] = 6879
v[980,12] = 1017
v[981,12] = 3121
v[982,12] = 2547
v[983,12] = 4603
v[984,12] = 2385
v[985,12] = 6915
v[986,12] = 6103
v[987,12] = 5669
v[988,12] = 7833
v[989,12] = 2001
v[990,12] = 4287
v[991,12] = 6619
v[992,12] = 955
v[993,12] = 2761
v[994,12] = 5711
v[995,12] = 6291
v[996,12] = 3415
v[997,12] = 3909
v[998,12] = 2841
v[999,12] = 5627
v[1000,12] = 4939
v[1001,12] = 7671
v[1002,12] = 6059
v[1003,12] = 6275
v[1004,12] = 6517
v[1005,12] = 1931
v[1006,12] = 4583
v[1007,12] = 7301
v[1008,12] = 1267
v[1009,12] = 7509
v[1010,12] = 1435
v[1011,12] = 2169
v[1012,12] = 6939
v[1013,12] = 3515
v[1014,12] = 2985
v[1015,12] = 2787
v[1016,12] = 2123
v[1017,12] = 1969
v[1018,12] = 3307
v[1019,12] = 353
v[1020,12] = 4359
v[1021,12] = 7059
v[1022,12] = 5273
v[1023,12] = 5873
v[1024,12] = 6657
v[1025,12] = 6765
v[1026,12] = 6229
v[1027,12] = 3179
v[1028,12] = 1583
v[1029,12] = 6237
v[1030,12] = 2155
v[1031,12] = 371
v[1032,12] = 273
v[1033,12] = 7491
v[1034,12] = 3309
v[1035,12] = 6805
v[1036,12] = 3015
v[1037,12] = 6831
v[1038,12] = 7819
v[1039,12] = 713
v[1040,12] = 4747
v[1041,12] = 3935
v[1042,12] = 4109
v[1043,12] = 1311
v[1044,12] = 709
v[1045,12] = 3089
v[1046,12] = 7059
v[1047,12] = 4247
v[1048,12] = 2989
v[1049,12] = 1509
v[1050,12] = 4919
v[1051,12] = 1841
v[1052,12] = 3045
v[1053,12] = 3821
v[1054,12] = 6929
v[1055,12] = 4655
v[1056,12] = 1333
v[1057,12] = 6429
v[1058,12] = 6649
v[1059,12] = 2131
v[1060,12] = 5265
v[1061,12] = 1051
v[1062,12] = 261
v[1063,12] = 8057
v[1064,12] = 3379
v[1065,12] = 2179
v[1066,12] = 1993
v[1067,12] = 5655
v[1068,12] = 3063
v[1069,12] = 6381
v[1070,12] = 3587
v[1071,12] = 7417
v[1072,12] = 1579
v[1073,12] = 1541
v[1074,12] = 2107
v[1075,12] = 5085
v[1076,12] = 2873
v[1077,12] = 6141
v[1078,12] = 955
v[1079,12] = 3537
v[1080,12] = 2157
v[1081,12] = 841
v[1082,12] = 1999
v[1083,12] = 1465
v[1084,12] = 5171
v[1085,12] = 5651
v[1086,12] = 1535
v[1087,12] = 7235
v[1088,12] = 4349
v[1089,12] = 1263
v[1090,12] = 1453
v[1091,12] = 1005
v[1092,12] = 6893
v[1093,12] = 2919
v[1094,12] = 1947
v[1095,12] = 1635
v[1096,12] = 3963
v[1097,12] = 397
v[1098,12] = 969
v[1099,12] = 4569
v[1100,12] = 655
v[1101,12] = 6737
v[1102,12] = 2995
v[1103,12] = 7235
v[1104,12] = 7713
v[1105,12] = 973
v[1106,12] = 4821
v[1107,12] = 2377
v[1108,12] = 1673
v[1109,12] = 1
v[1110,12] = 6541
# v[0:40,0] = transpose([ \
# 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
# 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
# 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
# 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ])
# v[2:40,1] = transpose([ \
# 1, 3, 1, 3, 1, 3, 3, 1, \
# 3, 1, 3, 1, 3, 1, 1, 3, 1, 3, \
# 1, 3, 1, 3, 3, 1, 3, 1, 3, 1, \
# 3, 1, 1, 3, 1, 3, 1, 3, 1, 3 ])
# v[3:40,2] = transpose([ \
# 7, 5, 1, 3, 3, 7, 5, \
# 5, 7, 7, 1, 3, 3, 7, 5, 1, 1, \
# 5, 3, 3, 1, 7, 5, 1, 3, 3, 7, \
# 5, 1, 1, 5, 7, 7, 5, 1, 3, 3 ])
# v[5:40,3] = transpose([ \
# 1, 7, 9,13,11, \
# 1, 3, 7, 9, 5,13,13,11, 3,15, \
# 5, 3,15, 7, 9,13, 9, 1,11, 7, \
# 5,15, 1,15,11, 5, 3, 1, 7, 9 ])
# v[7:40,4] = transpose([ \
# 9, 3,27, \
# 15,29,21,23,19,11,25, 7,13,17, \
# 1,25,29, 3,31,11, 5,23,27,19, \
# 21, 5, 1,17,13, 7,15, 9,31, 9 ])
# v[13:40,5] = transpose([ \
# 37,33, 7, 5,11,39,63, \
# 27,17,15,23,29, 3,21,13,31,25, \
# 9,49,33,19,29,11,19,27,15,25 ])
# v[19:40,6] = transpose([ \
# 13, \
# 33,115, 41, 79, 17, 29,119, 75, 73,105, \
# 7, 59, 65, 21, 3,113, 61, 89, 45,107 ])
# v[37:40,7] = transpose([ \
# 7, 23, 39 ])
#
# Set POLY.
#
poly= [ \
1, 3, 7, 11, 13, 19, 25, 37, 59, 47, \
61, 55, 41, 67, 97, 91, 109, 103, 115, 131, \
193, 137, 145, 143, 241, 157, 185, 167, 229, 171, \
213, 191, 253, 203, 211, 239, 247, 285, 369, 299 ]
poly = [\
1, 3, 7, 11, 13, 19, 25, 37, 59, 47,
61, 55, 41, 67, 97, 91, 109, 103, 115, 131,
193, 137, 145, 143, 241, 157, 185, 167, 229, 171,
213, 191, 253, 203, 211, 239, 247, 285, 369, 299,
301, 333, 351, 355, 357, 361, 391, 397, 425, 451,
463, 487, 501, 529, 539, 545, 557, 563, 601, 607,
617, 623, 631, 637, 647, 661, 675, 677, 687, 695,
701, 719, 721, 731, 757, 761, 787, 789, 799, 803,
817, 827, 847, 859, 865, 875, 877, 883, 895, 901,
911, 949, 953, 967, 971, 973, 981, 985, 995, 1001,
1019, 1033, 1051, 1063, 1069, 1125, 1135, 1153, 1163, 1221,
1239, 1255, 1267, 1279, 1293, 1305, 1315, 1329, 1341, 1347,
1367, 1387, 1413, 1423, 1431, 1441, 1479, 1509, 1527, 1531,
1555, 1557, 1573, 1591, 1603, 1615, 1627, 1657, 1663, 1673,
1717, 1729, 1747, 1759, 1789, 1815, 1821, 1825, 1849, 1863,
1869, 1877, 1881, 1891, 1917, 1933, 1939, 1969, 2011, 2035,
2041, 2053, 2071, 2091, 2093, 2119, 2147, 2149, 2161, 2171,
2189, 2197, 2207, 2217, 2225, 2255, 2257, 2273, 2279, 2283,
2293, 2317, 2323, 2341, 2345, 2363, 2365, 2373, 2377, 2385,
2395, 2419, 2421, 2431, 2435, 2447, 2475, 2477, 2489, 2503,
2521, 2533, 2551, 2561, 2567, 2579, 2581, 2601, 2633, 2657,
2669, 2681, 2687, 2693, 2705, 2717, 2727, 2731, 2739, 2741,
2773, 2783, 2793, 2799, 2801, 2811, 2819, 2825, 2833, 2867,
2879, 2881, 2891, 2905, 2911, 2917, 2927, 2941, 2951, 2955,
2963, 2965, 2991, 2999, 3005, 3017, 3035, 3037, 3047, 3053,
3083, 3085, 3097, 3103, 3159, 3169, 3179, 3187, 3205, 3209,
3223, 3227, 3229, 3251, 3263, 3271, 3277, 3283, 3285, 3299,
3305, 3319, 3331, 3343, 3357, 3367, 3373, 3393, 3399, 3413,
3417, 3427, 3439, 3441, 3475, 3487, 3497, 3515, 3517, 3529,
3543, 3547, 3553, 3559, 3573, 3589, 3613, 3617, 3623, 3627,
3635, 3641, 3655, 3659, 3669, 3679, 3697, 3707, 3709, 3713,
3731, 3743, 3747, 3771, 3791, 3805, 3827, 3833, 3851, 3865,
3889, 3895, 3933, 3947, 3949, 3957, 3971, 3985, 3991, 3995,
4007, 4013, 4021, 4045, 4051, 4069, 4073, 4179, 4201, 4219,
4221, 4249, 4305, 4331, 4359, 4383, 4387, 4411, 4431, 4439,
4449, 4459, 4485, 4531, 4569, 4575, 4621, 4663, 4669, 4711,
4723, 4735, 4793, 4801, 4811, 4879, 4893, 4897, 4921, 4927,
4941, 4977, 5017, 5027, 5033, 5127, 5169, 5175, 5199, 5213,
5223, 5237, 5287, 5293, 5331, 5391, 5405, 5453, 5523, 5573,
5591, 5597, 5611, 5641, 5703, 5717, 5721, 5797, 5821, 5909,
5913, 5955, 5957, 6005, 6025, 6061, 6067, 6079, 6081, 6231,
6237, 6289, 6295, 6329, 6383, 6427, 6453, 6465, 6501, 6523,
6539, 6577, 6589, 6601, 6607, 6631, 6683, 6699, 6707, 6761,
6795, 6865, 6881, 6901, 6923, 6931, 6943, 6999, 7057, 7079,
7103, 7105, 7123, 7173, 7185, 7191, 7207, 7245, 7303, 7327,
7333, 7355, 7365, 7369, 7375, 7411, 7431, 7459, 7491, 7505,
7515, 7541, 7557, 7561, 7701, 7705, 7727, 7749, 7761, 7783,
7795, 7823, 7907, 7953, 7963, 7975, 8049, 8089, 8123, 8125,
8137, 8219, 8231, 8245, 8275, 8293, 8303, 8331, 8333, 8351,
8357, 8367, 8379, 8381, 8387, 8393, 8417, 8435, 8461, 8469,
8489, 8495, 8507, 8515, 8551, 8555, 8569, 8585, 8599, 8605,
8639, 8641, 8647, 8653, 8671, 8675, 8689, 8699, 8729, 8741,
8759, 8765, 8771, 8795, 8797, 8825, 8831, 8841, 8855, 8859,
8883, 8895, 8909, 8943, 8951, 8955, 8965, 8999, 9003, 9031,
9045, 9049, 9071, 9073, 9085, 9095, 9101, 9109, 9123, 9129,
9137, 9143, 9147, 9185, 9197, 9209, 9227, 9235, 9247, 9253,
9257, 9277, 9297, 9303, 9313, 9325, 9343, 9347, 9371, 9373,
9397, 9407, 9409, 9415, 9419, 9443, 9481, 9495, 9501, 9505,
9517, 9529, 9555, 9557, 9571, 9585, 9591, 9607, 9611, 9621,
9625, 9631, 9647, 9661, 9669, 9679, 9687, 9707, 9731, 9733,
9745, 9773, 9791, 9803, 9811, 9817, 9833, 9847, 9851, 9863,
9875, 9881, 9905, 9911, 9917, 9923, 9963, 9973,10003,10025,
10043,10063,10071,10077,10091,10099,10105,10115,10129,10145,
10169,10183,10187,10207,10223,10225,10247,10265,10271,10275,
10289,10299,10301,10309,10343,10357,10373,10411,10413,10431,
10445,10453,10463,10467,10473,10491,10505,10511,10513,10523,
10539,10549,10559,10561,10571,10581,10615,10621,10625,10643,
10655,10671,10679,10685,10691,10711,10739,10741,10755,10767,
10781,10785,10803,10805,10829,10857,10863,10865,10875,10877,
10917,10921,10929,10949,10967,10971,10987,10995,11009,11029,
11043,11045,11055,11063,11075,11081,11117,11135,11141,11159,
11163,11181,11187,11225,11237,11261,11279,11297,11307,11309,
11327,11329,11341,11377,11403,11405,11413,11427,11439,11453,
11461,11473,11479,11489,11495,11499,11533,11545,11561,11567,
11575,11579,11589,11611,11623,11637,11657,11663,11687,11691,
11701,11747,11761,11773,11783,11795,11797,11817,11849,11855,
11867,11869,11873,11883,11919,11921,11927,11933,11947,11955,
11961,11999,12027,12029,12037,12041,12049,12055,12095,12097,
12107,12109,12121,12127,12133,12137,12181,12197,12207,12209,
12239,12253,12263,12269,12277,12287,12295,12309,12313,12335,
12361,12367,12391,12409,12415,12433,12449,12469,12479,12481,
12499,12505,12517,12527,12549,12559,12597,12615,12621,12639,
12643,12657,12667,12707,12713,12727,12741,12745,12763,12769,
12779,12781,12787,12799,12809,12815,12829,12839,12857,12875,
12883,12889,12901,12929,12947,12953,12959,12969,12983,12987,
12995,13015,13019,13031,13063,13077,13103,13137,13149,13173,
13207,13211,13227,13241,13249,13255,13269,13283,13285,13303,
13307,13321,13339,13351,13377,13389,13407,13417,13431,13435,
13447,13459,13465,13477,13501,13513,13531,13543,13561,13581,
13599,13605,13617,13623,13637,13647,13661,13677,13683,13695,
13725,13729,13753,13773,13781,13785,13795,13801,13807,13825,
13835,13855,13861,13871,13883,13897,13905,13915,13939,13941,
13969,13979,13981,13997,14027,14035,14037,14051,14063,14085,
14095,14107,14113,14125,14137,14145,14151,14163,14193,14199,
14219,14229,14233,14243,14277,14287,14289,14295,14301,14305,
14323,14339,14341,14359,14365,14375,14387,14411,14425,14441,
14449,14499,14513,14523,14537,14543,14561,14579,14585,14593,
14599,14603,14611,14641,14671,14695,14701,14723,14725,14743,
14753,14759,14765,14795,14797,14803,14831,14839,14845,14855,
14889,14895,14909,14929,14941,14945,14951,14963,14965,14985,
15033,15039,15053,15059,15061,15071,15077,15081,15099,15121,
15147,15149,15157,15167,15187,15193,15203,15205,15215,15217,
15223,15243,15257,15269,15273,15287,15291,15313,15335,15347,
15359,15373,15379,15381,15391,15395,15397,15419,15439,15453,
15469,15491,15503,15517,15527,15531,15545,15559,15593,15611,
15613,15619,15639,15643,15649,15661,15667,15669,15681,15693,
15717,15721,15741,15745,15765,15793,15799,15811,15825,15835,
15847,15851,15865,15877,15881,15887,15899,15915,15935,15937,
15955,15973,15977,16011,16035,16061,16069,16087,16093,16097,
16121,16141,16153,16159,16165,16183,16189,16195,16197,16201,
16209,16215,16225,16259,16265,16273,16299,16309,16355,16375,
16381]
atmost = 2**log_max - 1
#
# Find the number of bits in ATMOST.
#
maxcol = i4_bit_hi1 ( atmost )
#
# Initialize row 1 of V.
#
v[0,0:maxcol] = 1
#
# Things to do only if the dimension changed.
#
if ( dim_num != dim_num_save ):
#
# Check parameters.
#
if ( dim_num < 1 or dim_max < dim_num ):
print('I4_SOBOL - Fatal error!' )
print(' The spatial dimension DIM_NUM should satisfy:')
print(' 1 <= DIM_NUM <= %d'%dim_max)
print(' But this input value is DIM_NUM = %d'%dim_num)
return
dim_num_save = dim_num
#
# Initialize the remaining rows of V.
#
for i in range(2 , dim_num+1):
#
# The bits of the integer POLY(I) gives the form of polynomial I.
#
# Find the degree of polynomial I from binary encoding.
#
j = poly[i-1]
m = 0
while ( 1 ):
j = math.floor ( j / 2. )
if ( j <= 0 ):
break
m = m + 1
#
# Expand this bit pattern to separate components of the logical array INCLUD.
#
j = poly[i-1]
includ=zeros(m)
for k in range(m, 0, -1):
j2 = math.floor ( j / 2. )
includ[k-1] = (j != 2 * j2 )
j = j2
#
# Calculate the remaining elements of row I as explained
# in Bratley and Fox, section 2.
#
for j in range( m+1, maxcol+1 ):
newv = v[i-1,j-m-1]
l = 1
for k in range(1, m+1):
l = 2 * l
if ( includ[k-1] ):
newv = bitwise_xor ( int(newv), int(l * v[i-1,j-k-1]) )
v[i-1,j-1] = newv
#
# Multiply columns of V by appropriate power of 2.
#
l = 1
for j in range( maxcol-1, 0, -1):
l = 2 * l
v[0:dim_num,j-1] = v[0:dim_num,j-1] * l
#
# RECIPD is 1/(common denominator of the elements in V).
#
recipd = 1.0 / ( 2 * l )
lastq=zeros(dim_num)
seed = int(math.floor ( seed ))
if ( seed < 0 ):
seed = 0
if ( seed == 0 ):
l = 1
lastq=zeros(dim_num)
elif ( seed == seed_save + 1 ):
#
# Find the position of the right-hand zero in SEED.
#
l = i4_bit_lo0 ( seed )
elif ( seed <= seed_save ):
seed_save = 0
l = 1
lastq=zeros(dim_num)
for seed_temp in range( int(seed_save), int(seed)):
l = i4_bit_lo0 ( seed_temp )
for i in range(1 , dim_num+1):
lastq[i-1] = bitwise_xor ( int(lastq[i-1]), int(v[i-1,l-1]) )
l = i4_bit_lo0 ( seed )
elif ( seed_save + 1 < seed ):
for seed_temp in range( int(seed_save + 1), int(seed) ):
l = i4_bit_lo0 ( seed_temp )
for i in range(1, dim_num+1):
lastq[i-1] = bitwise_xor ( int(lastq[i-1]), int(v[i-1,l-1]) )
l = i4_bit_lo0 ( seed )
#
# Check that the user is not calling too many times!
#
if ( maxcol < l ):
print('I4_SOBOL - Fatal error!')
print(' Too many calls!')
print(' MAXCOL = %d\n'%maxcol)
print(' L = %d\n'%l)
return
#
# Calculate the new components of QUASI.
#
quasi=zeros(dim_num)
for i in range( 1, dim_num+1):
quasi[i-1] = lastq[i-1] * recipd
lastq[i-1] = bitwise_xor ( int(lastq[i-1]), int(v[i-1,l-1]) )
seed_save = seed
seed = seed + 1
return [ quasi, seed ]
def i4_uniform ( a, b, seed ):
#*****************************************************************************80
#
## I4_UNIFORM returns a scaled pseudorandom I4.
#
# Discussion:
#
# The pseudorandom number will be scaled to be uniformly distributed
# between A and B.
#
# Licensing:
#
# This code is distributed under the GNU LGPL license.
#
# Modified:
#
# 22 February 2011
#
# Author:
#
# Original MATLAB version by John Burkardt.
# PYTHON version by Corrado Chisari
#
# Reference:
#
# Paul Bratley, Bennett Fox, Linus Schrage,
# A Guide to Simulation,
# Springer Verlag, pages 201-202, 1983.
#
# Pierre L'Ecuyer,
# Random Number Generation,
# in Handbook of Simulation,
# edited by Jerry Banks,
# Wiley Interscience, page 95, 1998.
#
# Bennett Fox,
# Algorithm 647:
# Implementation and Relative Efficiency of Quasirandom
# Sequence Generators,
# ACM Transactions on Mathematical Software,
# Volume 12, Number 4, pages 362-376, 1986.
#
# Peter Lewis, Allen Goodman, James Miller
# A Pseudo-Random Number Generator for the System/360,
# IBM Systems Journal,
# Volume 8, pages 136-143, 1969.
#
# Parameters:
#
# Input, integer A, B, the minimum and maximum acceptable values.
#
# Input, integer SEED, a seed for the random number generator.
#
# Output, integer C, the randomly chosen integer.
#
# Output, integer SEED, the updated seed.
#
if ( seed == 0 ):
print('I4_UNIFORM - Fatal error!')
print(' Input SEED = 0!')
seed = math.floor ( seed )
a = round ( a )
b = round ( b )
seed = mod ( seed, 2147483647 )
if ( seed < 0 ) :
seed = seed + 2147483647
k = math.floor ( seed / 127773 )
seed = 16807 * ( seed - k * 127773 ) - k * 2836
if ( seed < 0 ):
seed = seed + 2147483647
r = seed * 4.656612875E-10
#
# Scale R to lie between A-0.5 and B+0.5.
#
r = ( 1.0 - r ) * ( min ( a, b ) - 0.5 ) + r * ( max ( a, b ) + 0.5 )
#
# Use rounding to convert R to an integer between A and B.
#
value = round ( r )
value = max ( value, min ( a, b ) )
value = min ( value, max ( a, b ) )
c = value
return [ int(c), int(seed) ]
def prime_ge ( n ):
#*****************************************************************************80
#
## PRIME_GE returns the smallest prime greater than or equal to N.
#
#
# Example:
#
# N PRIME_GE
#
# -10 2
# 1 2
# 2 2
# 3 3
# 4 5
# 5 5
# 6 7
# 7 7
# 8 11
# 9 11
# 10 11
#
# Licensing:
#
# This code is distributed under the GNU LGPL license.
#
# Modified:
#
# 22 February 2011
#
# Author:
#
# Original MATLAB version by John Burkardt.
# PYTHON version by Corrado Chisari
#
# Parameters:
#
# Input, integer N, the number to be bounded.
#
# Output, integer P, the smallest prime number that is greater
# than or equal to N.
#
p = max ( math.ceil ( n ), 2 )
while ( not isprime ( p ) ):
p = p + 1
return p
def isprime(n):
#*****************************************************************************80
#
## IS_PRIME returns True if N is a prime number, False otherwise
#
#
# Licensing:
#
# This code is distributed under the GNU LGPL license.
#
# Modified:
#
# 22 February 2011
#
# Author:
#
# Corrado Chisari
#
# Parameters:
#
# Input, integer N, the number to be checked.
#
# Output, boolean value, True or False
#
if n!=int(n) or n<1:
return False
p=2
while p .
import re
import numpy as np
import numpy.random as npr
import logging
logger = logging.getLogger(__name__)
log = logger.debug
def unpack_args(str):
if len(str) > 1:
eq_re = re.compile("\s*=\s*")
return dict(map(lambda x: eq_re.split(x),
re.compile("\s*,\s*").split(str)))
else:
return {}
def slice_sample(init_x, logprob, sigma=1.0, step_out=True, max_steps_out=1000,
compwise=False, verbose=False):
def direction_slice(direction, init_x):
def dir_logprob(z):
return logprob(direction*z + init_x)
upper = sigma*npr.rand()
lower = upper - sigma
llh_s = np.log(npr.rand()) + dir_logprob(0.0)
l_steps_out = 0
u_steps_out = 0
if step_out:
while dir_logprob(lower) > llh_s and l_steps_out < max_steps_out:
l_steps_out += 1
lower -= sigma
while dir_logprob(upper) > llh_s and u_steps_out < max_steps_out:
u_steps_out += 1
upper += sigma
steps_in = 0
while True:
steps_in += 1
new_z = (upper - lower)*npr.rand() + lower
new_llh = dir_logprob(new_z)
if np.isnan(new_llh):
log(new_z, direction*new_z + init_x, new_llh, llh_s, init_x, logprob(init_x))
raise Exception("Slice sampler got a NaN")
if new_llh > llh_s:
break
elif new_z < 0:
lower = new_z
elif new_z > 0:
upper = new_z
else:
raise Exception("Slice sampler shrank to zero!")
if verbose:
log("Steps Out:", l_steps_out, u_steps_out, " Steps In:", steps_in)
return new_z*direction + init_x
if not init_x.shape:
init_x = np.array([init_x])
dims = init_x.shape[0]
if compwise:
ordering = list(range(dims))
npr.shuffle(ordering)
cur_x = init_x.copy()
for d in ordering:
direction = np.zeros((dims))
direction[d] = 1.0
cur_x = direction_slice(direction, cur_x)
return cur_x
else:
direction = npr.randn(dims)
direction = direction / np.sqrt(np.sum(direction**2))
return direction_slice(direction, init_x)
================================================
FILE: src/aup/RestAPI/__init__.py
================================================
"""
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
================================================
FILE: src/aup/RestAPI/server.py
================================================
"""
RestAPI server using Flask
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
from flask import render_template, jsonify, request
from flask_cors import CORS
import connexion
import click
import logging
import sys
import sqlite3 as sql
import os
import os.path
from six.moves.configparser import ConfigParser
import json
import signal
import multiprocessing
from aup.compression.utils import SERIALIZATION_SEPARATOR, run_non_automatic_experiment, verify_compression_config, adjust_compression_config
from aup.setup import setup
from aup.utils import get_default_username, set_default_keyvalue
from ..ET.Connector.SQLiteConnector import SQLiteConnector
from ..EE.Experiment import Experiment
from ..aup import BasicConfig
from aup.Proposer import get_proposer
from threading import Lock
logger = logging.getLogger("RestAPI")
# Create the application instance
app = connexion.App(__name__, specification_dir='./')
CORS(app.app)
EXPS = dict()
EXPS_lock = Lock()
EXPERIMENT_STATUS_TRANSLATE_DICT = {
"CREATED": "CREATED",
"RUNNING": "RUNNING",
"STOPPED": "STOPPED",
"FINISHED": "FINISHED",
"FAILED": "FAILED",
"STOPPING": "STOPPING",
"REQUEST_STOP": "STOPPING",
}
def fix_none_res(arg, value):
return arg if arg is not None and arg != 'None' else value
def get_display_names(params, exp_config=None, eid=None, cur=None):
if exp_config is None and eid is not None and cur is not None:
cur.execute("SELECT json_extract(exp_config, '$') as exp_config \
FROM experiment WHERE eid={eid} LIMIT 1;".format(eid=eid))
exp_config = json.loads(str(cur.fetchone()[0]))
display_names = {}
for param in params:
param = param['name']
full_param = param.split(SERIALIZATION_SEPARATOR)
index = int(full_param[0])
original_key = full_param[-1]
cdict = exp_config['compression']['config_list'][index]
for part in full_param[1:-1]:
cdict = cdict[part]
if 'op_names' in exp_config['compression']['config_list'][index]:
new_key = '{} ({})'.format(
SERIALIZATION_SEPARATOR.join(full_param[1:]),
", ".join([op_name for op_name in exp_config['compression']['config_list'][index]['op_names']]))
else:
new_key = '{} ({})'.format(
SERIALIZATION_SEPARATOR.join(full_param[1:]),
", ".join([op_type for op_type in exp_config['compression']['config_list'][index]['op_types']]))
display_names[param] = new_key
return display_names
def fix_compression_job_config(jobs, params, display_names):
new_jobs = []
for job in jobs:
if job['job_config'] is None:
new_job = {
**{display_names[param['name']]: None for param in params},
**{key: val for key, val in job.items() if key != 'job_config'},
}
new_jobs += [new_job]
continue
new_job = {key: val for key, val in job.items() if key != 'job_config'}
job_config = json.loads(job['job_config'])
for param in params:
param = param['name']
full_param = param.split(SERIALIZATION_SEPARATOR)
index = int(full_param[0])
original_key = full_param[-1]
cdict = job_config['config_list'][index]
for part in full_param[1:-1]:
cdict = cdict[part]
new_job[display_names[param]] = cdict[original_key]
new_jobs += [new_job]
return new_jobs
def is_compression_experiment(exp_config=None, eid=None, cur=None):
if exp_config is not None:
return "compression" in exp_config
elif eid is not None:
ret = query_db(cur, "SELECT (json_extract(exp_config, '$.compression') IS NOT NULL) as is_compression_exp \
FROM experiment WHERE eid={eid} LIMIT 1;".format(eid=eid))
return bool(ret[0]["is_compression_exp"])
else:
raise ValueError("Missing parameter for is_compression_experiment")
def get_params_for_experiment(cur, eid):
exp_config = query_db(cur, "SELECT json_extract(exp_config, '$.parameter_config') as parameters from experiment where eid={eid};".format(eid=eid))
params_json = json.loads(exp_config[0]['parameters'])
params = []
for v in params_json:
name = v['name']
new_param = {}
new_param['name'] = name
new_param['type'] = v['type']
new_param['range'] = v.get('range', None)
new_param['interval'] = v.get('interval', None)
new_param['n'] = v.get('n', None)
params.append(new_param)
return params
def query_db(cur, query, args=(), one=False):
cur.execute(query, args)
r = [dict((cur.description[i][0], value) \
for i, value in enumerate(row)) for row in cur.fetchall()]
return (r[0] if r else None) if one else r
def start_experiment_daemon(json_conf, eid, cwd, event):
daemon_logger = logging.getLogger("RestAPIDaemon")
is_compression_exp = "compression" in json_conf
is_one_shot_compression_exp = is_compression_exp and "proposer" not in json_conf
rc = 0
exp = None
previous_dir = os.getcwd()
os.chdir(cwd)
try:
os.setsid()
os.umask(0)
if is_compression_exp:
json_conf = verify_compression_config(json_conf)
json_conf["compression"] = adjust_compression_config(json_conf["compression"])
set_default_keyvalue("workingdir", cwd, json_conf, log=logger)
if not is_one_shot_compression_exp:
exp = Experiment(json_conf, eid=eid)
exp.add_suspend_signal()
exp.add_refresh_signal()
else:
_, finish = run_non_automatic_experiment(json_conf, os.path.join(".aup"), eid=eid)
event.set()
event.clear()
event.wait()
if not is_one_shot_compression_exp:
exp.start()
except Exception as e:
daemon_logger.exception('Exception caught:' + str(e))
rc = 1
finally:
if not is_one_shot_compression_exp:
exp.finish()
else:
finish()
os.chdir(previous_dir)
os._exit(rc)
def get_valid_jobs_interval(cursor, eid):
fin_cond = "(score is not NULL and (score == 'EARLY STOPPED' or typeof(score)=='real')) \
and start_time is not NULL and end_time is not NULL"
cursor.execute("SELECT jid, ({fin_cond}) AS stat FROM job WHERE \
eid={eid} ORDER BY jid;".format(fin_cond=fin_cond, eid=eid))
sql_res = cursor.fetchall()
first = None
last = None
idx = None
num_jobs = 0
for i in range(0, len(sql_res)):
if first == None and sql_res[i][1] == 1:
first = sql_res[i][0]
idx = i
if first != None and sql_res[i][1] == 0:
last = sql_res[i-1][0]
num_jobs = (i - idx)
break
start_job = first
if first == None:
num_jobs = 0
elif last == None:
num_jobs = len(sql_res)
return (start_job, num_jobs)
# Create a URL route in our application for "/"
@app.route('/')
def home():
"""
This function just responds to the browser ULR
localhost:5000/
:return: the rendered template 'home.html'
"""
return render_template('home.html')
@app.route('/api/resource_types', methods=['GET'])
def get_resource_types():
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
cur.execute("SELECT DISTINCT type from resource;")
res = [i[0] for i in cur.fetchall()]
return jsonify({'resources': res})
return None
@app.route('/api/experiments/', methods=['GET'])
def get_experiment(eid):
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
experiment = experiment = query_db(cur, "SELECT *, name as experiment_name, \
json_extract(exp_config, '$.script') as script_name \
from experiment where eid={eid};".format(eid=eid))
experiment[0]['status'] = EXPERIMENT_STATUS_TRANSLATE_DICT[experiment[0]['status']]
cur = con.cursor()
cur.execute("SELECT count(*) from job where (end_time is NULL or \
(typeof(score) is not 'real' and score is not 'EARLY STOPPED')) and eid={eid}".format(eid=eid))
unfinished = int(cur.fetchone()[0])
cur.execute("SELECT count(*) from job where (end_time is not NULL and \
(typeof(score) is 'real' or score is 'EARLY STOPPED')) and eid={eid}".format(eid=eid))
finished = int(cur.fetchone()[0])
cur.execute("SELECT json_extract(exp_config, '$.target') from experiment where eid={eid}".format(eid=eid))
order = str(cur.fetchone()[0])
order = fix_none_res(order, 'max')
cur.execute("SELECT json_extract(exp_config, '$.proposer') from experiment where eid={eid}".format(eid=eid))
proposer = str(cur.fetchone()[0])
cur = con.cursor()
cur.execute("SELECT exp_config \
from experiment where eid={eid};".format(eid=eid))
exp_config = json.loads(str(cur.fetchone()[0]))
num_params = len(exp_config['parameter_config'])
proposer_obj = None
n_samples = 1
if proposer is not None and proposer != 'None':
proposer_obj = get_proposer(proposer)
n_samples = proposer_obj(exp_config).nSamples
if proposer == 'bohb' and experiment[0]['status'] == 'FINISHED':
n_samples = finished
param_names = []
for p in range(0, num_params):
cur.execute("SELECT json_extract(exp_config, '$.parameter_config[{idx}].name') \
from experiment where eid={eid};".format(idx=p, eid=eid))
param_names.append(str(cur.fetchone()[0]))
is_compression_exp = is_compression_experiment(exp_config)
best_metrics_vs_hparams = query_db(cur, "SELECT {order}(score) as score, job_config \
from job where eid={eid} and typeof(score) = 'real';".format(order=order, eid=eid))
params = get_params_for_experiment(cur, eid)
new_best_score = {}
best_job_config = best_metrics_vs_hparams[0]['job_config']
if is_compression_exp:
display_names = get_display_names(params, exp_config)
best_metrics_vs_hparams = fix_compression_job_config(best_metrics_vs_hparams, params, display_names)
for param in params:
if param['name'] in display_names:
param['name'] = display_names[param['name']]
param['value'] = best_metrics_vs_hparams[0][param['name']]
else:
job_configs = [
json.loads(mvh["job_config"]) if "job_config" in mvh and mvh["job_config"] is not None else {}
for mvh in best_metrics_vs_hparams
]
best_metrics_vs_hparams = [{
"score": mvh["score"],
**{param['name']: job_config[param['name']] if param['name'] in job_config else None for param in params},
} for mvh, job_config in zip(best_metrics_vs_hparams, job_configs)]
for param in params:
param['value'] = best_metrics_vs_hparams[0][param['name']]
new_best_score['params'] = params
new_best_score['proposer'] = proposer
new_best_score['score'] = best_metrics_vs_hparams[0]['score']
res = {
'experiment': experiment[0],
'best_score': new_best_score,
'job_stats': {
'finished': finished,
'unfinished': unfinished,
'total': n_samples
},
}
if is_compression_exp and len(params) == 0:
best_config_list = query_db(cur, "SELECT json_extract(exp_config, '$.compression.config_list') as 'config_list' \
FROM experiment WHERE eid={eid} LIMIT 1;".format(eid=eid))
best_config_list = best_config_list[0]['config_list']
res['best_score'] = {
'score': res['best_score']['score'],
'config_list': best_config_list,
}
return res
return None
@app.route('/api/experiments', methods=['GET'])
def get_experiments():
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
experiment = query_db(cur, "SELECT *, json_extract(exp_config, '$.script') as script_name, \
name as experiment_name, \
(SELECT case when (SELECT json_extract(exp_config, '$.target') from experiment where eid=experiment.eid) is \"min\" \
then min(score) else max(score) end from job where job.eid=experiment.eid and typeof(score) is 'real') as best_score, \
(SELECT json_group_array(job.score) from job where job.eid=experiment.eid) as scores, \
(SELECT count(*) from job where (end_time is NULL or typeof(score) is not 'real') and job.eid=experiment.eid) as jobs_unfinished, \
(SELECT count(*) from job where end_time is not NULL and typeof(score) = 'real' and job.eid=experiment.eid) as jobs_finished, \
(SELECT json_group_array(json_object('jid', jid, 'score', score, \
'start_time', start_time, 'end_time', end_time, 'job_config', job_config)) from \
(SELECT * from job where eid=experiment.eid order by end_time)) as jobs \
from experiment order by start_time DESC;")
for e in experiment:
e['status'] = EXPERIMENT_STATUS_TRANSLATE_DICT[e['status']]
mult_res_labels = query_db(cur, "SELECT JSON_EXTRACT(exp_config, '$.resource_args.multi_res_labels') \
AS labels FROM experiment ORDER BY start_time DESC")
list_mult_res_labels = None
if mult_res_labels is not None:
for i in range(len(mult_res_labels)):
labels_str = mult_res_labels[i]['labels']
if labels_str is None:
continue
list_mult_res_labels = json.loads(labels_str)
experiment[i]['labels'] = list_mult_res_labels
resource = query_db(cur, "SELECT * FROM resource WHERE type IS NOT 'passive';")
return jsonify({'experiment': experiment, 'resource': resource})
return None
@app.route('/api/job_status', methods=['GET'])
def get_job_status():
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
eid = request.args.get('eid')
job = query_db(cur, "SELECT *, case when end_time is NULL then \"running\" \
else \"finished\" end as status, (SELECT rid from job_attempt where jid=job.jid) as rid \
from job where eid={eid} order by {sortby} {ASC};".format(eid=eid, \
sortby=request.args.get('sortby'), \
ASC="ASC" if int(request.args.get('asc')) else "DESC"))
mult_res_labels = query_db(cur, "SELECT JSON_EXTRACT(exp_config, '$.resource_args.multi_res_labels') AS labels \
FROM experiment WHERE eid=?", (eid,))
mult_res_labels = mult_res_labels[0]
list_mult_res_labels = None
if mult_res_labels is not None and mult_res_labels['labels'] is not None:
list_mult_res_labels = json.loads(mult_res_labels['labels'])
for j in job:
m_res = query_db(cur, "SELECT * FROM multiple_result WHERE jid=? AND is_last_result=1 ORDER BY mrid", (j['jid'],))
# take the last results
if len(m_res) < len(list_mult_res_labels):
continue
for res in m_res:
j[list_mult_res_labels[res['label_order']-1]] = res['value']
is_compression_exp = is_compression_experiment(cur=cur, eid=eid)
if len(job) > 0 and is_compression_exp:
params = get_params_for_experiment(cur, eid)
display_names = get_display_names(params, eid=eid, cur=cur)
job_configs = fix_compression_job_config([{'job_config': j['job_config']} for j in job], params, display_names)
job = [{
'job_config': json.dumps(job_configs[idx]),
**{key: val for key, val in j.items() if key != 'job_config'}
} for idx, j in enumerate(job)]
return jsonify({'job': job, 'mult_res_labels': list_mult_res_labels})
return None
@app.route('/api/hps_space', methods=['GET'])
def get_hps_space():
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
eid = request.args.get('eid')
exp_config = query_db(cur, "SELECT json_extract(exp_config, '$.parameter_config') as parameters, \
json_extract(exp_config, '$.proposer') as proposer, \
json_extract(exp_config, '$.n_samples') as num_samples from experiment where eid={eid};".format(eid=eid))
return jsonify({'exp_config': exp_config[0]})
return None
@app.route('/api/experiment_history', methods=['GET'])
def get_experiment_history():
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
experiment_history = query_db(cur, "SELECT *, (SELECT rid from job_attempt where jid=job.jid) as rid from job \
where eid={eid} order by end_time;".format(eid=request.args.get('eid')))
return jsonify({'experiment_history': experiment_history})
return None
@app.route('/api/experiment_history_best/', methods=['GET'], defaults={'label': None})
@app.route('/api/experiment_history_best//', methods=['GET'])
def get_experiment_history_best(eid, label):
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
sortby = request.args.get('sortby', 'jid')
cur = con.cursor()
cur.execute("SELECT json_extract(exp_config, '$.target') from experiment where eid={eid};".format(eid=eid))
order = str(cur.fetchone()[0])
order = fix_none_res(order, 'max')
list_mult_res_labels = None
label_order = None
if label is not None:
mult_res_labels = query_db(cur, "SELECT JSON_EXTRACT(exp_config, '$.resource_args.multi_res_labels') AS labels \
FROM experiment WHERE eid=?", (eid,))
mult_res_labels = mult_res_labels[0]
if mult_res_labels is not None and mult_res_labels['labels'] is not None:
list_mult_res_labels = json.loads(mult_res_labels['labels'])
try:
label_order = list_mult_res_labels.index(label)+1
except ValueError:
return jsonify({'experiment_history_best': []})
if list_mult_res_labels is None:
return jsonify({'experiment_history_best': []})
(start_job, num_jobs) = get_valid_jobs_interval(cur, eid)
result = []
for i in range(1, num_jobs+1):
best_job = None
if label is None:
best_job = query_db(cur, "SELECT jid, {order}(score) as score, job_config from \
(SELECT * from job where eid={eid} and jid >= {start_job} order by {sortby} limit 0,{start}) \
WHERE typeof(score) == 'real';". \
format(order=order, eid=eid, start=i, sortby=sortby, start_job=start_job))
elif list_mult_res_labels is not None and label_order is not None:
if sortby == 'end_time':
sortby = 'receive_time'
best_job = query_db(cur, "SELECT jid, {order}(value) as score, (SELECT job_config FROM job WHERE jid=jidm) as job_config FROM \
(SELECT *, jid AS jidm FROM multiple_result WHERE eid={eid} AND \
jid >= {start_job} AND label_order={label_order} AND is_last_result=1 ORDER BY {sortby} LIMIT 0,{start})". \
format(order=order, eid=eid, start=i, start_job=start_job, label_order=label_order, sortby=sortby))
if best_job is not None and best_job[0]['jid'] is None:
continue
is_compression_exp = is_compression_experiment(cur=cur, eid=eid)
if is_compression_exp:
params = get_params_for_experiment(cur, eid)
display_names = get_display_names(params, eid=eid, cur=cur)
job_configs = fix_compression_job_config([{"job_config": job["job_config"]} for job in best_job], params, display_names)
best_job = [{
"job_config": json.dumps(job_configs[idx]),
**{key: val for key, val in job.items() if key != "job_config"}
} for idx, job in enumerate(best_job)]
result.append(best_job[0])
return jsonify({'experiment_history_best': result})
return None
@app.route('/api/experiment_history_best', methods=['GET'], defaults={'label': None})
@app.route('/api/experiment_history_best/', methods=['GET'])
def get_experiments_history_best(label):
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
sortby = request.args.get('sortby', 'jid')
cur = con.cursor()
eids = query_db(cur, "SELECT eid from experiment;")
result = dict()
for eid in eids:
eid = eid['eid']
cur.execute("SELECT json_extract(exp_config, '$.target') from experiment where eid={eid};".format(eid=eid))
order = str(cur.fetchone()[0])
order = fix_none_res(order, 'max')
list_mult_res_labels = None
label_order = None
if label is not None:
mult_res_labels = query_db(cur, "SELECT JSON_EXTRACT(exp_config, '$.resource_args.multi_res_labels') AS labels \
FROM experiment WHERE eid=?", (eid,))
mult_res_labels = mult_res_labels[0]
if mult_res_labels is not None and mult_res_labels['labels'] is not None:
list_mult_res_labels = json.loads(mult_res_labels['labels'])
try:
label_order = list_mult_res_labels.index(label)+1
except ValueError:
continue
if list_mult_res_labels is None:
continue
(start_job, num_jobs) = get_valid_jobs_interval(cur, eid)
result[eid] = list()
for i in range(1, num_jobs+1):
best_job = None
if label is None:
best_job = query_db(cur, "SELECT jid, {order}(score) as score, job_config, start_time, end_time from \
(SELECT * from job where eid={eid} and jid >= {start_job} order by {sortby} limit 0,{start}) \
WHERE typeof(score) == 'real';". \
format(order=order, eid=eid, start=i, sortby=sortby, start_job=start_job))
elif list_mult_res_labels is not None and label_order is not None:
if sortby == 'end_time':
sortby = 'receive_time'
best_job = query_db(cur, "SELECT jid, {order}(value) as score, (SELECT job_config FROM job WHERE jid=jidm) as job_config FROM \
(SELECT *, jid AS jidm FROM multiple_result WHERE eid={eid} AND \
jid >= {start_job} AND label_order={label_order} AND is_last_result=1 ORDER BY {sortby} LIMIT 0,{start})". \
format(order=order, eid=eid, start=i, start_job=start_job, label_order=label_order, sortby=sortby))
if best_job is not None and best_job[0]['jid'] is None:
continue
is_compression_exp = is_compression_experiment(cur=cur, eid=eid)
if is_compression_exp:
params = get_params_for_experiment(cur, eid)
display_names = get_display_names(params, eid=eid, cur=cur)
job_configs = fix_compression_job_config([{"job_config": job["job_config"]} for job in best_job], params, display_names)
best_job = [{
"job_config": json.dumps(job_configs[idx]),
**{key: val for key, val in job.items() if key != "job_config"}
} for idx, job in enumerate(best_job)]
result[eid].append(best_job[0])
return jsonify({'experiment_history_best': result})
return None
@app.route('/api/experiment_comparison_best', methods=['GET'])
def get_experiment_comparison_best():
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
eid = request.args.get('eid')
cur = con.cursor()
cur.execute("SELECT json_extract(exp_config, '$.target') from experiment where eid={eid}".format(eid=eid))
order = str(cur.fetchone()[0])
order = fix_none_res(order, 'max')
result = query_db(cur, "SELECT jid, {order}(score), job_config from job where eid={eid};" \
.format(order=order, eid=eid))
return jsonify({'experiment_comparison_best': result})
return None
@app.route('/api/metrics_vs_hparams', methods=['GET'])
def get_metrics_vs_hparams():
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
eid = int(request.args.get('eid'))
cur = con.cursor()
params = get_params_for_experiment(cur, eid)
metrics_vs_hparams = query_db(cur, "SELECT score, job_config from job \
where eid={eid} and typeof(score) == 'real';".format(eid=eid))
is_compression_exp = is_compression_experiment(cur=cur, eid=eid)
if is_compression_exp:
display_names = get_display_names(params, eid=eid, cur=cur)
metrics_vs_hparams = fix_compression_job_config(metrics_vs_hparams, params, display_names)
else:
metrics_vs_hparams = [{
"score": mvh["score"],
**{key: val for key, val in json.loads(mvh["job_config"]).items()},
} for mvh in metrics_vs_hparams]
return jsonify({'metrics_vs_hparams': metrics_vs_hparams})
return None
@app.route('/api/experiments_status')
def get_experiment_status():
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
result = query_db(cur, "SELECT eid, start_time, end_time, status \
FROM experiment;")
for r in result:
r['status'] = EXPERIMENT_STATUS_TRANSLATE_DICT[r['status']]
return jsonify({'experiments_status': result})
return None
@app.route('/api/job_stats/')
def get_job_stats(eid):
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
cur.execute("SELECT count(*) from job where end_time is NULL and eid={eid}".format(eid=eid))
unfinished = int(cur.fetchone()[0])
cur.execute("SELECT count(*) from job where end_time is not NULL and eid={eid}".format(eid=eid))
finished = int(cur.fetchone()[0])
return jsonify({'finished': finished, 'unfinished': unfinished})
return None
@app.route('/api/current_db', methods=['GET'])
def get_current_db():
db_path = app.app.config['db_file']
return jsonify({'db_path': os.path.abspath(db_path) if db_path is not None else None})
@app.route('/api/setup', methods=['POST'])
def perform_setup():
if request.method == 'POST':
data = request.json
work_dir = data.get('work_dir')
previous_dir = os.getcwd()
try:
os.chdir(work_dir)
ini_path = data.get('ini_path')
cpu = data.get('cpu', '4')
cpu = int(cpu)
aws_file = data.get('aws_file', 'none')
gpu_file = data.get('gpu_file', 'none')
node_file = data.get('node_file', 'none')
if not os.path.exists(ini_path):
raise Exception("{} does not exist".format(ini_path))
if aws_file is not 'none' and (not os.path.exists(aws_file)):
raise Exception("{} does not exist".format(aws_file))
if gpu_file is not 'none' and (not os.path.exists(gpu_file)):
raise Exception("{} does not exist".format(gpu_file))
if node_file is not 'none' and (not os.path.exists(node_file)):
raise Exception("{} does not exist".format(node_file))
overwrite = data.get('overwrite', 'False')
overwrite = bool(overwrite)
user = data.get('user')
config = ConfigParser()
config.optionxform = str
config.read(ini_path)
user = get_default_username(user)
setup(config, cpu, gpu_file, node_file, aws_file, user, overwrite, 'info')
app.app.config['db_file'] = os.path.join(work_dir, config.get("Auptimizer", "Auptimizer_PATH"), 'sqlite3.db')
except Exception as e:
logger.fatal('Exception caught:' + str(e))
return jsonify(isError=True,
message=str(e),
statusCode=500), 500
finally:
os.chdir(previous_dir)
return jsonify(isError= False,
message= "Success",
statusCode=200), 200
@app.route('/api/create_experiment', methods=['POST'])
def create_experiment():
if request.method == 'POST':
data = request.json
cwd = data.get('cwd', None)
json_config_body = data.get('json_config_body', None)
is_compression_exp = "compression" in json_config_body
is_automatic_compression_exp = is_compression_exp and "proposer" in json_config_body
try:
previous_dir = os.getcwd()
os.chdir(cwd)
config = BasicConfig()
config.update(json_config_body)
config['cwd'] = cwd
if is_compression_exp:
config = verify_compression_config(config)
config["compression"] = adjust_compression_config(config["compression"])
set_default_keyvalue("workingdir", cwd, config, log=logger)
if is_compression_exp and not is_automatic_compression_exp:
eid, _ = run_non_automatic_experiment(config, os.path.join(".aup"), start=False)
else:
e = Experiment(config, start=False)
eid = e.eid
os.chdir(previous_dir)
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
experiment = query_db(cur, "SELECT *, name as experiment_name, \
json_extract(exp_config, '$.script') as script_name \
from experiment where eid={eid};".format(eid=eid))
experiment = experiment[0]
experiment['status'] = EXPERIMENT_STATUS_TRANSLATE_DICT[experiment['status']]
return jsonify(experiment)
except Exception as e:
os.chdir(previous_dir)
logger.exception('Exception caught:' + str(e))
return jsonify(isError=True,
message=str(e),
statusCode=500), 500
@app.route('/api/start_experiment', methods=['POST'])
def start_experiment():
if request.method == 'POST':
data = request.json
eid = int(data.get('eid', -1))
cwd = None
json_conf = None
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
cur.execute("SELECT exp_config from experiment where eid={eid};".format(eid=eid))
json_conf = BasicConfig()
json_conf.update(json.loads(cur.fetchone()[0]))
cur.execute("SELECT json_extract(exp_config, '$.cwd') from experiment where eid={eid};".format(eid=eid))
cwd = str(cur.fetchone()[0])
init_exp_event = None
try:
init_exp_event = multiprocessing.Event()
proc = multiprocessing.Process(target=start_experiment_daemon,
args=(json_conf, eid, cwd, init_exp_event))
proc.daemon = True
proc.start()
with EXPS_lock:
EXPS[eid] = (proc, cwd)
init_exp_event.wait()
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
experiment = query_db(cur, "SELECT *, name as experiment_name, \
json_extract(exp_config, '$.script') as script_name \
from experiment where eid={eid};".format(eid=eid))
experiment = experiment[0]
experiment['status'] = EXPERIMENT_STATUS_TRANSLATE_DICT[experiment['status']]
# let daemon start the experiment after we finished with the db
init_exp_event.set()
return jsonify(experiment)
except Exception as e:
logger.exception('Exception caught:' + str(e))
return jsonify(isError=True,
message= "Exception occurred:" + str(e),
statusCode=500), 500
@app.route('/api/stop_experiment', methods=['POST'])
def stop_experiment():
if request.method == 'POST':
data = request.json
eid = int(data.get('eid', -1))
try:
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
experiment = query_db(cur, "UPDATE experiment SET status = 'REQUEST_STOP' \
WHERE eid={eid}".format(eid=eid))
with EXPS_lock:
if eid in EXPS:
# force a last refresh
os.kill(EXPS[eid][0].pid, signal.SIGUSR1)
del EXPS[eid]
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
experiment = query_db(cur, "SELECT *, name as experiment_name, \
json_extract(exp_config, '$.script') as script_name \
from experiment where eid={eid};".format(eid=eid))
experiment = experiment[0]
experiment['status'] = EXPERIMENT_STATUS_TRANSLATE_DICT[experiment['status']]
return jsonify(experiment)
except Exception as e:
logger.exception('Exception caught:' + str(e))
return jsonify(isError=True,
message= "Exception occurred:" + str(e),
statusCode=500), 500
@app.route('/api/refresh_all', methods=['POST'])
def refresh_all():
try:
to_delete = list()
with EXPS_lock:
for k, v in EXPS.items():
if v[0].exitcode == None:
os.kill(v[0].pid, signal.SIGUSR1)
else:
to_delete.append(k)
for eid in to_delete:
del EXPS[eid]
return jsonify(isError= False,
message= "Success",
statusCode=200), 200
except Exception as e:
logger.exception('Exception caught:' + str(e))
return jsonify(isError=True,
message= "Exception occurred:" + str(e),
statusCode=500), 500
@app.route('/api/interm_res', methods=['GET'])
def get_interm_res():
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
experiments = query_db(cur, "SELECT * from experiment WHERE \
JSON_EXTRACT(exp_config, '$.resource_args.track_intermediate_results') = 1")
res = list()
for exp in experiments:
obj = dict()
eid = int(exp['eid'])
name = str(exp['name'])
script_name = str(json.loads(exp['exp_config'])['script'])
obj['eid'] = eid
obj['name'] = name
obj['scriptName'] = script_name
res.append(obj)
return jsonify(res)
@app.route('/api/interm_res/', methods=['GET'], defaults={'label': None})
@app.route('/api/interm_res//', methods=['GET'])
def get_interm_res_by_eid(eid, label):
with sql.connect(app.app.config['db_file'], check_same_thread=False) as con:
cur = con.cursor()
res = query_db(cur, "SELECT * from experiment WHERE \
JSON_EXTRACT(exp_config, '$.resource_args.track_intermediate_results') = 1 AND eid={eid}".format(eid=eid))
if len(res) == 0:
return jsonify(dict())
exp = res[0]
name = str(exp['name'])
script_name = str(json.loads(exp['exp_config'])['script'])
obj = dict()
list_mult_res_labels = None
label_order = None
mult_res_labels = query_db(cur, "SELECT JSON_EXTRACT(exp_config, '$.resource_args.multi_res_labels') AS labels \
FROM experiment WHERE eid=?", (eid,))
mult_res_labels = mult_res_labels[0]
if mult_res_labels is not None and mult_res_labels['labels'] is not None:
list_mult_res_labels = json.loads(mult_res_labels['labels'])
if label is not None:
label_order = list_mult_res_labels.index(label)+1
obj['multResLabels'] = list_mult_res_labels
obj['eid'] = eid
obj['name'] = name
obj['scriptName'] = script_name
obj['jobs'] = list()
jids = query_db(cur, "SELECT jid from job WHERE \
eid={eid}".format(eid=eid))
for jid in jids:
field = dict()
jid_int = int(jid['jid'])
field['jid'] = jid_int
field['interimResults'] = list()
interm_res = None
if label is None:
interm_res = query_db(cur, "SELECT * from intermediate_result WHERE \
jid={jid} ORDER BY receive_time".format(jid=jid_int))
elif list_mult_res_labels is not None and label_order is not None:
interm_res = query_db(cur, "SELECT *, value as score FROM multiple_result WHERE\
jid={jid} and label_order={label_order} ORDER BY receive_time".\
format(jid=jid_int, label_order=label_order))
for in_res in interm_res:
field['interimResults'].append({"irid": in_res['irid'], "receiveTime": in_res['receive_time'], "score": in_res['score']})
obj['jobs'].append(field)
return jsonify(obj)
@app.route('/api/experiment/', methods=['DELETE'])
def delete_experiment(eid):
if eid in EXPS:
del EXPS[eid]
conn = SQLiteConnector(app.app.config['db_file'])
rc = conn.delete_experiment(eid)
conn.close()
if not rc:
return jsonify(isError=True,
message= "Experiment with eid={eid} not found".format(eid=eid),
statusCode=500), 500
else:
return jsonify(isError=False,
message="Success",
statusCode=200), 200
def disable_http_logs(disable):
if disable:
import click
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
def secho(text, file=None, nl=None, err=None, color=None, **styles):
pass
def echo(text, file=None, nl=None, err=None, color=None, **styles):
pass
click.echo = echo
click.secho = secho
def main(path, port):
if path is not None and not os.path.exists(path):
return 1
app.app.config['db_file'] = os.path.abspath(path) if path is not None else None
app.app.config['CORS_HEADERS'] = 'Content-Type'
disable_http_logs(True)
app.run(host='0.0.0.0', port=int(port), debug=False)
return 0
# If we're running in stand alone mode, run the application
if __name__ == '__main__':
db_file = None
port = None
try:
if len(sys.argv) == 3:
db_file = str(sys.argv[1])
if not os.path.exists(db_file):
logger.fatal('Db file does not exist')
exit(1)
port = int(sys.argv[2])
elif len(sys.argv) == 2:
logger.warning("Running without db!")
port = int(sys.argv[1])
else:
logger.fatal('Specify at least a port!')
exit(1)
except ValueError as ve:
logger.fatal('Cannot parse port!:' + str(ve))
exit(1)
except Exception as e:
logger.fatal('Caught exception:' + str(e))
exit(1)
app.app.config['db_file'] = os.path.abspath(db_file) if db_file is not None else None
app.app.config['CORS_HEADERS'] = 'Content-Type'
app.run(host='0.0.0.0', port=port, debug=False)
================================================
FILE: src/aup/RestAPI/templates/home.html
================================================
Application Home Page
Auptimizer REST API
================================================
FILE: src/aup/__init__.py
================================================
"""
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
from .EE.Experiment import Experiment
from .aup import BasicConfig, print_result, aup_args, aup_flags, aup_save_model
import aup.compression
__version__ = "2.0"
================================================
FILE: src/aup/__main__.py
================================================
#!/usr/bin/env python3
"""
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
Auptimizer HPO main entry
=========================
:mod:`aup.__main__` is the Auptimizer main entry point for HPO experiments.
Use it as::
python -m aup
The usage is detailed in :doc:`experiment`.
Additional arguments
--------------------
.. program-output:: python3 -m aup -h
"""
import logging
import click
import coloredlogs
from . import Experiment, BasicConfig
from .utils import get_default_username
from .utils import get_available_port
from .dashboard import dashboard
_log_level = {"debug": logging.DEBUG, "info": logging.INFO, "warn": logging.WARN, "error": logging.ERROR}
logger = logging.getLogger("aup")
@click.command(name="Auptimizer training", context_settings=dict(help_option_names=['-h', '--help']))
@click.argument("experiment_file", type=click.Path(exists=True))
@click.option("--test", is_flag=True, help="Test one case to verify the code is working")
@click.option("--user", default=None, help="User name for job scheduling")
@click.option("--aup_folder", default=None, help="Specify customized aup folder")
@click.option("--resume", default="none", help="Resume from previous task")
@click.option("--log", default="info", type=click.Choice(["debug", "info", "warn", "error"]), help="Log level")
@click.option("--sleep", default=1, type=click.FLOAT, help="Sleep interval to sync updates")
@click.option("--launch_dashboard", is_flag=True, help="Launch the dashboard together with the experiment.")
@click.option("--dashboard_port", default=None, type=click.INT, help="Port for the dashboard frontend.")
def main(experiment_file, test, user, aup_folder, resume, log, sleep, launch_dashboard, dashboard_port):
"""Auptimizer main function for HPO experiment
\b\n
Copyright (C) 2018 LG Electronics Inc.
\b\n
GPL-3.0 License. This program comes with ABSOLUTELY NO WARRANTY;
\b\n
Arguments:
experiment_file {str} -- Experiment configuration (can be created by `python -m aup.init`).
"""
coloredlogs.install(level=_log_level[log],
fmt="%(asctime)-15s - %(name)s - %(levelname)s - %(message)s")
config = {
"username": get_default_username(user),
"sleep_time": sleep,
}
if not launch_dashboard and dashboard_port is not None:
logger.fatal("dashbord_port value given without launch_dashboard flag given.")
exit(0)
if launch_dashboard:
port = dashboard_port
if port is None:
port = get_available_port()
logger.info('Dashboard started on 0.0.0.0:{}'.format(port))
e = None
if aup_folder:
#TODO-the "connector" param in Experiment class is never customized
e = Experiment(BasicConfig().load(experiment_file), auppath=aup_folder, **config)
else:
e = Experiment(BasicConfig().load(experiment_file), **config)
if test:
logger.info("# Testing")
exit(0)
if launch_dashboard:
from .utils import load_default_env
if aup_folder:
db_path = load_default_env(aup_folder, log=None)["SQLITE_FILE"]
else:
from os.path import join
db_path = load_default_env(join(".aup"), log=None)["SQLITE_FILE"]
from multiprocessing import Process
frontend = True
proc = Process(target=dashboard._start_dashboard, args=(db_path, port, frontend))
proc.start()
logger.info("# Running Experiment")
try:
import signal
original_sigint_handler = signal.getsignal(signal.SIGINT)
e.add_suspend_signal()
if resume == "none":
e.start()
else:
e.resume(resume)
except Exception as exp:
if _log_level[log] > logging.DEBUG:
logging.critical("use --log debug to track error details")
else:
raise exp
finally:
e.finish()
if launch_dashboard:
signal.signal(signal.SIGINT, original_sigint_handler)
logger.info("Dashboard is still running on 0.0.0.0:{}".format(port))
logger.info("To exit press CTRL+C...")
proc.join()
if __name__ == "__main__":
main()
================================================
FILE: src/aup/aup.py
================================================
"""
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
Auptimizer client side functions
================================
This file can be copied to a remote machine instead of installing the whole Auptimizer package for job execution.
APIs
----
"""
from __future__ import print_function
import logging
import json
import pickle
import sys
import inspect
import functools
import os
import shutil
logger = logging.getLogger("aup-minimal")
# supported data loading format
_SUPPORT_FORMAT = ("pkl", "json")
global user_callback_fn
global user_args
global user_kwargs
user_callback_fn = None
user_args = []
user_kwargs = {}
def print_result(result):
"""Function to print the result for :func:`parse_result`.
This function should be the last line of your training code
:param result: result from training code
:type result: str
"""
if type(result) is list:
result = ','.join([str(r) for r in result])
else:
result = str(result).lstrip() # avoid line break
# force flush to get intermediate results in real time
print("\n#Auptimizer:%s" % result, file=sys.stderr, flush=True)
class BasicConfig(dict):
"""
User-friendly :class:`dict` supports:
* load and save for json/pickle format (.json/.pkl)
* easy key/value access as config.key or config["key"]
* compatible with :class:`dict`
:param kwargs: key-value pairs to initialize the configuration
:type kwargs: dict
"""
def load(self, filename):
"""Load config parameters from JSON/pickle file
:param filename: file name ends with [.json|.pkl]
:type filename: string
:return: configuration parsed from file
:rtype: aup.BasicConfig
"""
name = "_load_" + BasicConfig._get_format(filename)
func = getattr(self, name)
data = func(filename)
if type(data) is not dict:
raise TypeError("Config must be dict")
self.update(data)
logger.debug("Load config from %s: %s" % (filename, data.__str__()))
return self
def save(self, filename):
"""
Save configuration as dict in JSON/pickle
:param filename: file name ends with [.json|.pkl]
:type filename: string
"""
name = "_save_" + BasicConfig._get_format(filename)
func = getattr(self, name)
func(filename)
logger.debug("Config saved to %s" % filename)
@staticmethod
def _get_format(filename):
name = filename.split(".")[-1].lower()
if name not in _SUPPORT_FORMAT:
raise ValueError("Un-support file format, choose from %s." % ",".join(_SUPPORT_FORMAT))
return name
@staticmethod
def _load_json(filename):
with open(filename, 'r') as f:
return json.load(f)
@staticmethod
def _load_pkl(filename):
with open(filename, 'rb') as f:
return pickle.load(f)
def _save_json(self, filename):
with open(filename, 'w') as f:
json.dump(self, f)
def _save_pkl(self, filename):
with open(filename, 'wb') as f:
pickle.dump(dict(self), f)
@staticmethod
def save_flags(filename):
"""
Save tf flags for reuse - not used, not tested
:param filename: output file
"""
from absl import flags
logger.info("Write flags into %s")
with open(filename, 'w') as f:
f.write(flags.FLAGS.flags_into_string())
def to_flags(self, FLAGS):
"""
Update values in FLAGS from BasicConfig
:param FLAGS: tensorflow/absl FLAGS
"""
for i in FLAGS:
if i in self:
logger.debug("set %s in FLAGS", i)
setattr(FLAGS, i, self[i])
else:
logger.debug("Use default %s", i)
def __setattr__(self, key, value):
self.__setitem__(key, value)
def __getattr__(self, key):
return self.__getitem__(key)
def __delattr__(self, key):
self.__delitem__(key)
def __hash__(self):
return super(BasicConfig, self).__hash__()
def aup_args(func):
"""Decorator to wrap optimization target function `func`.
Arguments:
func {function} -- A function computes optimization target with specified hyperparameters
"""
@functools.wraps(func)
def wrapper(filename, **kwargs):
"""wrapper function
Arguments:
filename {str} -- configuration file
kwargs {dict} -- additional arguments will overwrite existing configuration value
Raises:
ValueError: if a parameter is not assigned in config
"""
# get current frame stack
frm = inspect.stack()[1]
# get module from stack
mod = inspect.getmodule(frm[0])
# get functions that contain "init" in name and call them
functions_list = inspect.getmembers(sys.modules[mod.__name__], inspect.isfunction)
functions_list = sorted(list(filter(lambda x: "init" in x[0], functions_list)))
config = BasicConfig().load(filename)
if kwargs:
logger.critical("Overwritting config values from script, be cautious!")
config.update(kwargs)
for f in functions_list:
f[1](**config)
parameters = inspect.signature(func).parameters
for p in parameters.items():
if p[0] not in config:
if p[1].default is inspect.Parameter.empty:
raise ValueError("`%s` is required in `%s()` but is not assigned in config file %s" %
(p[0], func.__name__, filename))
logger.info("Using default value for %s", p[0])
run_config = dict()
for p in config:
if p in parameters:
run_config[p] = config[p]
else:
logger.warning("%s is not used in optimization"%p)
val = func(**run_config)
print_result(val)
save_model = config.get('save_model', False)
if save_model is True and user_callback_fn is not None:
# this means this is the "best job" found
# the user wants to save the model
try:
dir = os.path.join('aup_models', config.get('folder_name', None))
previous_dir = os.getcwd()
if os.path.exists('aup_models') is False:
os.makedirs('aup_models')
if os.path.exists(dir) is True:
logger.warning('Deleting {}'.format(dir))
shutil.rmtree(dir)
os.makedirs(dir)
os.chdir(dir)
user_callback_fn(*user_args, **user_kwargs)
except Exception as e:
raise e
finally:
os.chdir(previous_dir)
return wrapper
def aup_flags(flags):
"""wrapper function for absl flags (or tf.app).
It will assign values to flags parameters using the given configuration file as the first argument when executed
from the command line.
Arguments:
args {list} -- a list of unused arguments passed by app.run()
"""
def decorator_wrapper(func):
@functools.wraps(func)
def wrapper(args):
config = BasicConfig(**flags.__dict__).load(args[1])
flags.__dict__.update()
parameters = inspect.signature(func).parameters
if parameters:
logger.warning("TF FLAG main() should not accept arguments with Auptimizer, it has %s",
parameters.keys())
val = func({p:None for p in parameters})
else:
val = func()
print_result(val)
return wrapper
return decorator_wrapper
def aup_save_model(callback_fn, *args, **kwargs):
global user_callback_fn
global user_args
global user_kwargs
user_callback_fn = callback_fn
user_args = args
user_kwargs = kwargs
================================================
FILE: src/aup/compression/Compressor.py
================================================
"""
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import copy
import logging
import os
logger = logging.getLogger(__name__)
COMPRESSORS = {}
try:
from .tensorflow import pruning as tf_pruning
COMPRESSORS.update({
"tensorflow": {
"pruning": {
"level": tf_pruning.LevelPruner,
}
},
})
except (ImportError, AssertionError) as ex:
logger.warning("Error when importing Tensorflow 2.X for compression: {}".format(ex))
try:
from .torch import pruning as torch_pruning
from .torch import quantization as torch_quantization
from .torch import ModelSpeedup, apply_compression_results
from .torch.utils.counter import count_flops_params
import torch
COMPRESSORS.update({
"torch": {
"pruning": {
"agp": torch_pruning.AGPPruner,
"admm": torch_pruning.ADMMPruner,
"auto_compress": torch_pruning.AutoCompressPruner,
"lottery_ticket": torch_pruning.LotteryTicketPruner,
"level": torch_pruning.LevelPruner,
"slim": torch_pruning.SlimPruner,
"l1_filter": torch_pruning.L1FilterPruner,
"l2_filter": torch_pruning.L2FilterPruner,
"fpgm": torch_pruning.FPGMPruner,
"net_adapt": torch_pruning.NetAdaptPruner,
"sensitivity": torch_pruning.SensitivityPruner,
"simulated_annealing": torch_pruning.SimulatedAnnealingPruner,
"amc": torch_pruning.AMCPruner,
"taylor_fo_weight_filter": torch_pruning.TaylorFOWeightFilterPruner,
"activation_apoz_rank_filter": torch_pruning.ActivationAPoZRankFilterPruner,
"activation_mean_rank_filter": torch_pruning.ActivationMeanRankFilterPruner,
},
"quantization": {
"naive": torch_quantization.NaiveQuantizer,
"qat": torch_quantization.QAT_Quantizer,
"dorefa": torch_quantization.DoReFaQuantizer,
"bnn": torch_quantization.BNNQuantizer,
}
}
})
except ImportError as ex:
logger.warning("Error when importing PyTorch for compression: {}".format(ex))
from ..utils import check_missing_key
def create_compressor(model, config, *args, **kwargs):
"""
Helper function for user scripts to generate a compression object
:param model: Model to compress
:param config: Compressor configuration
"""
logging.basicConfig(format="%(asctime)-15s - %(name)s - %(levelname)s - %(message)s")
c_framework = config["compression_framework"]
c_type = config["compression_type"]
c_str = config["compressor"]
logger.info("Creating compressor: framework={} type={} compressor={}".format(
c_framework, c_type, c_str))
if c_framework not in COMPRESSORS:
raise ValueError(("Compression framework \"{}\" not recognized. Supported frameworks for installed packages: {}\n" +
"Please check messages above, it is possible that an import error lead to this situation.").format(
c_framework, ", ".join(COMPRESSORS.keys())))
if c_type not in COMPRESSORS[c_framework]:
raise ValueError("Compression type \"{}\" not recognized for framework \"{}\". Supported types: {}".format(
c_type, c_framework, ", ".join(COMPRESSORS[c_framework].keys())))
if c_str not in COMPRESSORS[c_framework][c_type]:
raise ValueError("Compressor \"{}\" not recognized for framework \"{}\" and type \"{}\". Supported compressors: {}".format(
c_str, c_framework, c_type, ", ".join(COMPRESSORS[c_framework][c_type].keys())))
# Check if all op_names can be found in model
if c_framework == "torch":
layer_names = {".".join(key.split(".")[:-1]) for key in model.state_dict().keys()}
elif c_framework == "tensorflow":
layer_names = {layer.name for layer in model.layers}
else:
raise NotImplementedError
for config_item in config["config_list"]:
if "op_names" in config_item:
for op_name in config_item["op_names"]:
if op_name not in layer_names:
raise ValueError("op_name \"{}\" not found in model. Ops found: {}".format(op_name, layer_names))
nni_compressor = COMPRESSORS[c_framework][c_type][c_str](model=model, config_list=config["config_list"], *args, **kwargs)
compressor = Compressor(model, nni_compressor, c_framework, c_type, c_str)
return compressor
class Compressor:
def __init__(self, model, nni_compressor, c_framework, c_type, c_str):
self._nni_compressor = nni_compressor
self._compression_framework = c_framework
self._compression_type = c_type
self._compressor_str = c_str
self._applied_speedup = False
self.model = model
def compress(self, *args, **kwargs):
return self._nni_compressor.compress(*args, **kwargs)
def update_epoch(self, epoch):
return self._nni_compressor.update_epoch(epoch)
def step(self):
return self._nni_compressor.step()
def get_prune_iterations(self):
return self._nni_compressor.get_prune_iterations()
def prune_iteration_start(self):
return self._nni_compressor.prune_iteration_start()
def apply_speedup(self, dummy_input, mask_path=None, *args, **kwargs):
if self._compression_framework != "torch" or \
self._compression_type != "pruning":
raise ValueError("Can only apply_speedup for PyTorch pruning compressions.")
# Ideally, inference model would be re-created here, but _unwrap_model() seems to work as well
self._nni_compressor._unwrap_model()
try:
# Normally, the model would be exported first and its masks file used for speedup ("else" case)
# But here ("if"), the mask_dictionary object is constructed on-demand in-memory without saving to disk
if mask_path is None:
mask_dict = self._nni_compressor.get_mask_dict()
m_speedup = ModelSpeedup(self.model, dummy_input, masks=mask_dict)
else:
m_speedup = ModelSpeedup(self.model, dummy_input, masks_file=mask_path)
m_speedup.speedup_model()
except Exception as ex:
self._nni_compressor._wrap_model()
raise ValueError("Error encountered in apply_speedup.")
self._applied_speedup = True
return self.model
def count_flops_params(self, *args, **kwargs):
if not self._applied_speedup:
self._nni_compressor._unwrap_model()
ret = count_flops_params(self.model, *args, **kwargs)
if not self._applied_speedup:
self._nni_compressor._wrap_model()
return ret
def export_model(self, model_path, mask_path=None, folder_name=".", speedup=False, dummy_input=None, *args, **kwargs):
model = self.model
os.makedirs(folder_name, exist_ok=True)
model_path = os.path.join(folder_name, model_path)
if mask_path is not None:
mask_path = os.path.join(folder_name, mask_path)
if self._compression_framework == "torch":
if self._compression_type == "pruning":
self._nni_compressor.export_model(model_path, mask_path)
if speedup and not self._applied_speedup:
if dummy_input is None:
logger.warning("Missing required parameter \"dummy_input\" for speed-up, " +
"saving compressed model without speed-up.")
else:
try:
model = self.apply_speedup(dummy_input, mask_path)
except ValueError:
logger.warning("Error encountered when applying speed-up, " +
"saving compressed model without speed-up.")
torch.save(model.state_dict(), model_path)
elif self._compression_framework == "tensorflow":
self.model.save(model_path)
================================================
FILE: src/aup/compression/NNI-LICENSE
================================================
License file for package: https://github.com/microsoft/nni
Copyright (c) Microsoft Corporation.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: src/aup/compression/__init__.py
================================================
"""
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
# The actual Compressor class used
from .Compressor import *
from .utils import run_non_automatic_experiment
# Utilities
try:
from .torch.utils import sensitivity_analysis
from .torch.utils import shape_dependency
from .torch.utils import mask_conflict
from .torch.utils import counter
except (ImportError):
logger.debug("Could not import pytorch for compression. " +
"Make sure pytorch is installed if you intend to use pytorch during compression.")
================================================
FILE: src/aup/compression/__main__.py
================================================
#!/usr/bin/env python
"""
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
:mod:`aup.compression.__main__` is the Auptimizer main entry point for compression experiments.
Use it as::
python -m aup.compression
The usage is detailed in :doc:`compression`.
Additional arguments
--------------------
.. program-output:: python3 -m aup.compression -h
"""
import os
import json
import logging
import re
import signal
import sys
import time
import signal
import click
import coloredlogs
from ..EE.Resource import get_resource_manager
from ..EE.Experiment import Experiment
from ..EE.Job import Job
from .. import BasicConfig
from ..utils import get_default_username, get_default_connector, check_missing_key, set_default_keyvalue, get_available_port, load_default_env
from .utils import adjust_compression_config, run_non_automatic_experiment, verify_compression_config
from ..dashboard import dashboard
_log_level = {"debug": logging.DEBUG, "info": logging.INFO, "warn": logging.WARN, "error": logging.ERROR}
logger = logging.getLogger("aup.compression")
@click.command(name="Model Compression", context_settings=dict(help_option_names=['-h', '--help']))
@click.argument("experiment_file", type=click.Path(exists=True))
@click.option("--automatic", is_flag=True, help="Whether or not running an automatic compression experiment with hyperparameter optimization.")
@click.option("--user", default=None, help="User name for job scheduling")
@click.option("--aup_folder", default=os.path.join(".aup"), help="Specify customized aup folder")
@click.option("--resume", default="none", help="Resume from previous task")
@click.option("--log", default="info", type=click.Choice(["debug", "info", "warn", "error"]), help="Log level")
@click.option("--sleep", default=1, type=click.FLOAT, help="Sleep interval to sync updates")
@click.option("--launch_dashboard", is_flag=True, help="Launch the dashboard together with the experiment.")
@click.option("--dashboard_port", default=None, type=click.INT, help="Port for the dashboard frontend.")
def main(experiment_file, automatic, user, aup_folder, resume, log, sleep, launch_dashboard, dashboard_port):
"""Compress a given model.
\b
Arguments:
experiment_file {str} -- Compression configuration
"""
coloredlogs.install(level=_log_level[log],
fmt="%(asctime)-15s - %(name)s - %(levelname)s - %(message)s")
user = get_default_username(user)
exp_config = BasicConfig().load(experiment_file)
exp_config = verify_compression_config(exp_config)
exp_config["compression"] = adjust_compression_config(exp_config["compression"])
set_default_keyvalue("cwd", os.getcwd(), exp_config, log=logger)
set_default_keyvalue("workingdir", exp_config.get("cwd", os.getcwd()), exp_config, log=logger)
if not launch_dashboard and dashboard_port is not None:
logger.fatal("dashbord_port value given without launch_dashboard flag given.")
exit(0)
if launch_dashboard:
port = dashboard_port
if port is None:
port = get_available_port()
logger.info('Dashboard started on 0.0.0.0:{}'.format(port))
if aup_folder:
db_path = load_default_env(aup_folder, log=None)["SQLITE_FILE"]
else:
from os.path import join
db_path = load_default_env(join(".aup"), log=None)["SQLITE_FILE"]
from multiprocessing import Process
frontend = True
proc = Process(target=dashboard._start_dashboard, args=(db_path, port, frontend))
proc.daemon = True
proc.start()
original_sigint_handler = signal.getsignal(signal.SIGINT)
if automatic:
config = {
"username": get_default_username(user),
"sleep_time": sleep,
}
if aup_folder:
e = Experiment(exp_config, auppath=aup_folder, **config)
else:
e = Experiment(exp_config, **config)
logger.info("# Running automatic compression experiment")
try:
e.add_suspend_signal()
if resume == "none":
e.start()
else:
e.resume(resume)
e.finish()
except Exception as e:
if _log_level[log] > logging.DEBUG:
logging.critical("use --log debug to track error details")
else:
raise e
else:
_, finish = run_non_automatic_experiment(exp_config, aup_folder, user)
finish()
if launch_dashboard:
signal.signal(signal.SIGINT, original_sigint_handler)
logger.info("Dashboard is still running on 0.0.0.0:{}'".format(port))
logger.info("To exit press CTRL+C...")
proc.join()
if __name__ == "__main__":
main()
================================================
FILE: src/aup/compression/tensorflow/__init__.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
from .compressor import Compressor, Pruner
from .pruning import *
================================================
FILE: src/aup/compression/tensorflow/compressor.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
"""
Abstract base classes for TensorFlow model compression.
"""
import logging
_logger = logging.getLogger(__name__)
try:
import tensorflow as tf
except (ImportError):
_logger.debug("Could not import Tensorflow")
try:
assert tf.__version__.startswith('2'), 'NNI model compression only supports TensorFlow v2.x'
except (AssertionError):
_logger.debug("Could not import Tensorflow 2.X. Install Tensorflow 2.X for NNI model compression.")
from . import default_layers
class Compressor:
"""
Common base class for all compressors.
This class is designed for other base classes.
Algorithms should inherit ``Pruner`` or ``Quantizer`` instead.
Attributes
----------
compressed_model : tf.keras.Model
Compressed user model.
wrappers : list of tf.keras.Model
A wrapper is an instrumented TF ``Layer``, in ``Model`` format.
Parameters
----------
model : tf.keras.Model
The user model to be compressed.
config_list : list of JSON object
User configuration. The format is detailed in tutorial.
LayerWrapperClass : a class derive from Model
The class used to instrument layers.
"""
def __init__(self, model, config_list, LayerWrapperClass):
assert isinstance(model, tf.keras.Model)
self.validate_config(model, config_list)
self._original_model = model
self._config_list = config_list
self._wrapper_class = LayerWrapperClass
self._wrappers = {} # key: id(layer) , value: Wrapper(layer)
self.compressed_model = self._instrument(model)
self.wrappers = list(self._wrappers.values())
if not self.wrappers:
_logger.warning('Nothing is configured to compress, please check your model and config list')
def set_wrappers_attribute(self, name, value):
"""
Call ``setattr`` on all wrappers.
"""
for wrapper in self.wrappers:
setattr(wrapper, name, value)
def validate_config(self, model, config_list):
"""
Compression algorithm should overload this function to validate configuration.
"""
pass
def _instrument(self, layer):
if isinstance(layer, tf.keras.Sequential):
return self._instrument_sequential(layer)
if isinstance(layer, tf.keras.Model):
return self._instrument_model(layer)
# a layer can be referenced in multiple attributes of a model,
# but should only be instrumented once
if id(layer) in self._wrappers:
return self._wrappers[id(layer)]
config = self._select_config(layer)
if config is not None:
wrapper = self._wrapper_class(layer, config, self)
self._wrappers[id(layer)] = wrapper
return wrapper
return layer
def _instrument_sequential(self, seq):
layers = list(seq.layers) # seq.layers is read-only property
need_rebuild = False
for i, layer in enumerate(layers):
new_layer = self._instrument(layer)
if new_layer is not layer:
layers[i] = new_layer
need_rebuild = True
return tf.keras.Sequential(layers) if need_rebuild else seq
def _instrument_model(self, model):
for key, value in list(model.__dict__.items()): # avoid "dictionary keys changed during iteration"
if isinstance(value, tf.keras.layers.Layer):
new_layer = self._instrument(value)
if new_layer is not value:
setattr(model, key, new_layer)
elif isinstance(value, list):
for i, item in enumerate(value):
if isinstance(item, tf.keras.layers.Layer):
value[i] = self._instrument(item)
return model
def _select_config(self, layer):
# Find the last matching config block for given layer.
# Returns None if the layer should not be compressed.
layer_type = type(layer).__name__
last_match = None
for config in self._config_list:
if 'op_types' in config:
match = layer_type in config['op_types']
match_default = 'default' in config['op_types'] and layer_type in default_layers.weighted_modules
if not match and not match_default:
continue
if 'op_names' in config and layer.name not in config['op_names']:
continue
last_match = config
if last_match is None or 'exclude' in last_match:
return None
return last_match
class Pruner(Compressor):
"""
Base class for pruning algorithms.
End users should use ``compress`` and callback APIs (WIP) to prune their models.
The underlying model is instrumented upon initialization of pruner object.
So if you want to pre-train the model, train it before creating pruner object.
The compressed model can only execute in eager mode.
Algorithm developers should override ``calc_masks`` method to specify pruning strategy.
Parameters
----------
model : tf.keras.Model
The user model to prune.
config_list : list of JSON object
User configuration. The format is detailed in tutorial.
"""
def __init__(self, model, config_list):
super().__init__(model, config_list, PrunerLayerWrapper)
#self.callback = PrunerCallback(self)
def compress(self):
"""
Apply compression on a pre-trained model.
If you want to prune the model during training, use callback API (WIP) instead.
Returns
-------
tf.keras.Model
The compressed model.
"""
self._update_mask()
return self.compressed_model
def calc_masks(self, wrapper, **kwargs):
"""
Abstract method to be overridden by algorithm. End users should ignore it.
If the callback is set up, this method will be invoked at end of each training minibatch.
If not, it will only be called when end user invokes ``compress``.
Parameters
----------
wrapper : PrunerLayerWrapper
The instrumented layer.
**kwargs
Reserved for forward compatibility.
Returns
-------
dict of (str, tf.Tensor), or None
The key is weight ``Variable``'s name. The value is a mask ``Tensor`` of weight's shape and dtype.
If a weight's key does not appear in the return value, that weight will not be pruned.
Returning ``None`` means the mask is not changed since last time.
Weight names are globally unique, e.g. `model/conv_1/kernel:0`.
"""
# TODO: maybe it should be able to calc on weight-granularity, beside from layer-granularity
raise NotImplementedError("Pruners must overload calc_masks()")
def _update_mask(self):
for wrapper_idx, wrapper in enumerate(self.wrappers):
masks = self.calc_masks(wrapper, wrapper_idx=wrapper_idx)
if masks is not None:
wrapper.masks = masks
class PrunerLayerWrapper(tf.keras.Model):
"""
Instrumented TF layer.
Wrappers will be passed to pruner's ``calc_masks`` API,
and the pruning algorithm should use wrapper's attributes to calculate masks.
Once instrumented, underlying layer's weights will get **modified** by masks before forward pass.
Attributes
----------
layer_info : LayerInfo
All static information of the original layer.
layer : tf.keras.layers.Layer
The original layer.
config : JSON object
Selected configuration. The format is detailed in tutorial.
pruner : Pruner
Bound pruner object.
masks : dict of (str, tf.Tensor)
Current masks. The key is weight's name and the value is mask tensor.
On initialization, `masks` is an empty dict, which means no weight is pruned.
Afterwards, `masks` is the last return value of ``Pruner.calc_masks``.
See ``Pruner.calc_masks`` for details.
"""
def __init__(self, layer, config, pruner):
super().__init__()
self.layer = layer
self.config = config
self.pruner = pruner
self.masks = {}
_logger.info('Layer detected to compress: %s', self.layer.name)
def call(self, *inputs):
new_weights = []
for weight in self.layer.weights:
mask = self.masks.get(weight.name)
if mask is not None:
new_weights.append(tf.math.multiply(weight, mask))
else:
new_weights.append(weight)
if new_weights and not hasattr(new_weights[0], 'numpy'):
raise RuntimeError('NNI: Compressed model can only run in eager mode')
self.layer.set_weights([weight.numpy() for weight in new_weights])
return self.layer(*inputs)
# TODO: designed to replace `patch_optimizer`
#class PrunerCallback(tf.keras.callbacks.Callback):
# def __init__(self, pruner):
# super().__init__()
# self._pruner = pruner
#
# def on_train_batch_end(self, batch, logs=None):
# self._pruner.update_mask()
================================================
FILE: src/aup/compression/tensorflow/default_layers.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
weighted_modules = [
'Conv1D', 'Conv2D', 'Conv3D', 'Conv1DTranspose', 'Conv2DTranspose', 'Conv3DTranspose',
'Dense',
'PReLU',
'Embedding',
]
================================================
FILE: src/aup/compression/tensorflow/pruning/__init__.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
from .one_shot import *
================================================
FILE: src/aup/compression/tensorflow/pruning/one_shot.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import tensorflow as tf
from ..compressor import Pruner
__all__ = [
'OneshotPruner',
'LevelPruner',
]
class OneshotPruner(Pruner):
def __init__(self, model, config_list, pruning_algorithm='level', **algo_kwargs):
super().__init__(model, config_list)
self.set_wrappers_attribute('calculated', False)
self.masker = MASKER_DICT[pruning_algorithm](model, self, **algo_kwargs)
def validate_config(self, model, config_list):
pass # TODO
def calc_masks(self, wrapper, wrapper_idx=None):
if wrapper.calculated:
return None
sparsity = wrapper.config['sparsity']
masks = self.masker.calc_masks(sparsity, wrapper, wrapper_idx)
if masks is not None:
wrapper.calculated = True
return masks
class LevelPruner(OneshotPruner):
def __init__(self, model, config_list):
super().__init__(model, config_list, pruning_algorithm='level')
class WeightMasker:
def __init__(self, model, pruner, **kwargs):
self.model = model
self.pruner = pruner
def calc_masks(self, sparsity, wrapper, wrapper_idx=None):
raise NotImplementedError()
class LevelPrunerMasker(WeightMasker):
def calc_masks(self, sparsity, wrapper, wrapper_idx=None):
masks = {}
for weight_variable in wrapper.layer.weights:
if 'bias' in weight_variable.name:
continue
num_prune = int(tf.size(weight_variable).numpy() * sparsity)
if num_prune == 0:
continue
weight = weight_variable.read_value()
if wrapper.masks.get(weight_variable.name) is not None:
weight = tf.math.multiply(weight, wrapper.masks[weight_variable.name])
w_abs = tf.math.abs(weight)
k = tf.size(weight) - num_prune
topk = tf.math.top_k(tf.reshape(w_abs, [-1]), k)[0]
if tf.size(topk) == 0:
mask = tf.zeros_like(weight)
else:
mask = tf.math.greater_equal(w_abs, topk[-1])
masks[weight_variable.name] = tf.cast(mask, weight.dtype)
return masks
MASKER_DICT = {
'level': LevelPrunerMasker,
}
================================================
FILE: src/aup/compression/torch/__init__.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
from .speedup import ModelSpeedup
from .pruning import *
from .quantization import *
from .compressor import Compressor, Pruner, Quantizer
================================================
FILE: src/aup/compression/torch/_graph_utils.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import logging
import queue
import re
from collections import defaultdict
import torch
from torch.utils.tensorboard._pytorch_graph import NodePy, NodePyIO, NodePyOP, GraphPy
CLASSTYPE_KIND = 'ClassType'
GETATTR_KIND = 'prim::GetAttr'
CAT_KIND = 'aten::cat'
LIST_CONSTRUCT_KIND = 'prim::ListConstruct'
LIST_UNPACK_KIND = 'prim::ListUnpack'
TUPLE_CONSTRUCT_KIND = 'prim::TupleConstruct'
TUPLE_UNPACK_KIND = 'prim::TupleUnpack'
CONSTANT_KIND = 'prim::Constant'
_logger = logging.getLogger(__name__)
def build_module_graph(model, dummy_input):
return TorchModuleGraph(model, dummy_input)
def build_graph(model, dummy_input, verbose=False):
g = TorchProtoGraph(model, dummy_input, verbose)
return g.graph_def, g.stepstats
def parse_traced_name(module_name):
prefix = 'TracedModule['
suffix = ']'
if module_name.startswith(prefix) and module_name.endswith(suffix):
module_name = module_name[len(prefix):-len(suffix)]
return module_name
class TorchGraph:
"""
This class is to extract pytorch model topology graph by tracing
"""
def __init__(self, model=None, dummy_input=None, traced_model=None):
"""
Parameters
----------
model : pytorch model
The model user wants to speed up
dummy_input : pytorch tensor
The dummy input for ```jit.trace```, users should put it on right device before pass in
traced_model : torch._C.torch.jit.TopLevelTracedModule
An alredy traced model, if traced_model is not None, then TorchGraph will build the graph
based on this traced model and won't trace the model again.
"""
assert torch.__version__ >= '1.3.1'
# check if the input is legal
if traced_model is not None:
assert isinstance(traced_model, torch.jit.TopLevelTracedModule)
self.trace = traced_model
# it's ok if the graph is already unpacked
torch._C._jit_pass_inline(self.trace.graph)
elif model is not None and dummy_input is not None:
self.bound_model = model
self._trace(model, dummy_input)
else:
raise Exception(
'Please provide model & dummy_input or the traced_model as inputs')
def _trace(self, model, dummy_input):
training = model.training
model.eval()
self.trace = torch.jit.trace(model, dummy_input)
torch._C._jit_pass_inline(self.trace.graph)
model.train(training)
class TorchProtoGraph(TorchGraph):
"""
Generates model graph for pytorch models in protobuf, this implementation
is borrowed from pytorch v1.4.0, and fixed following issues:
https://github.com/pytorch/pytorch/issues/33691
https://github.com/pytorch/pytorch/issues/33670
"""
def __init__(self, model, dummy_input, verbose=False):
super().__init__(model, dummy_input)
from tensorboard.compat.proto.config_pb2 import RunMetadata
from tensorboard.compat.proto.graph_pb2 import GraphDef
from tensorboard.compat.proto.step_stats_pb2 import StepStats, DeviceStepStats
from tensorboard.compat.proto.versions_pb2 import VersionDef
list_of_nodes = self.parse(self.trace.graph, self.trace, dummy_input)
if verbose:
print(self.trace.graph)
self.stepstats = RunMetadata(step_stats=StepStats(
dev_stats=[DeviceStepStats(device="/device:CPU:0")]))
self.graph_def = GraphDef(
node=list_of_nodes, versions=VersionDef(producer=22))
def parse(self, graph, trace, args=None, omit_useless_nodes=True):
"""This method parses an optimized PyTorch model graph and produces
a list of nodes and node stats for eventual conversion to TensorBoard
protobuf format.
Args:
graph (PyTorch module): The model graph to be parsed.
trace (PyTorch JIT TracedModule): The model trace to be parsed.
args (tuple): input tensor[s] for the model.
omit_useless_nodes (boolean): Whether to remove nodes from the graph.
"""
nodes_py = GraphPy()
for node in graph.inputs():
if omit_useless_nodes:
if not node.uses(): # number of user of the node (= number of outputs/ fanout)
continue
if node.type().kind() != CLASSTYPE_KIND:
nodes_py.append(NodePyIO(node, 'input'))
attr_to_scope = dict()
def node_to_name(d):
return str(d).split(":")[0].strip()
for node in graph.nodes():
if node.kind() == GETATTR_KIND:
attr_name = node.s('name')
node_name = node_to_name(node)
parent = node.input().node()
# If the parent node is not the top-level "self" node
if parent.kind() == GETATTR_KIND:
parent_scope = attr_to_scope[node_to_name(parent)]
attr_scope = parent_scope.split('/')[-1]
attr_to_scope[node_name] = '{}/{}.{}'.format(
parent_scope, attr_scope, attr_name)
else:
attr_to_scope[node_name] = '__module.{}'.format(attr_name)
# We don't need classtype nodes; scope will provide this information
if node.output().type().kind() != CLASSTYPE_KIND:
node_py = NodePyOP(node)
node_py.scopeName = attr_to_scope[node_name]
nodes_py.append(node_py)
else:
nodes_py.append(NodePyOP(node))
# Create sink nodes for output ops
for i, node in enumerate(graph.outputs()):
node_py = NodePyIO(node, 'output')
node_py.debugName = "output.{}".format(i + 1)
node_py.inputs = [node.debugName()]
nodes_py.append(node_py)
alias_to_name = dict()
base_name = parse_traced_name(trace._name)
for name, module in trace.named_modules(prefix='__module'):
mod_name = parse_traced_name(module._name)
attr_name = name.split('.')[-1]
alias_to_name[name] = '{}[{}]'.format(mod_name, attr_name)
for node in nodes_py.nodes_op:
module_aliases = node.scopeName.split('/')[-1].split('.')
module_name = ''
for i, alias in enumerate(module_aliases):
if i == 0:
module_name = alias
node.scopeName = base_name
else:
module_name += '.' + alias
node.scopeName += '/' + \
(alias_to_name[module_name]
if module_name in alias_to_name else alias)
nodes_py.populate_namespace_from_OP_to_IO()
return nodes_py.to_proto()
class NodePyGroup(NodePy):
"""
This class is used to represent a graph node which consists of multiple jit traced nodes. In a pytorch trace graph,
there are multiple nodes are traced for one torch.nn.Module object, we group them together to form a single node to
represent the torch.nn.Module object. We also group some functional call trace nodes together to form a new node.
"""
def __init__(self, name, unique_name, node_type, op_type, node_cpps, inputs=None, outputs=None, key_node=None):
"""
Parameters:
-----------
name: str
node name, such as `conv1`, `backbone.classifier`
unique_name: str
A global unique name for current node. Due to some modules,
such as relu, may be reused several times, so the scopename
is not suitable as the global unique identifier, so we add a
unique_name for each node as the global unique identifier.
We should use the unique_name to traverset the module graph.
node_type: str
`module` or `func`
op_type: str
operation type, such as `Conv2d`, `aten::view`
node_cpps: list of torch._C.Node
jit trace nodes which are included in this new node
inputs: list of str
All the inputs of this node, each element is debugName of one input
outputs: list of str
All the outputs of this node, each element is debugName of one output
key_node: torch._C.Node
The key node of this NodePyGroup.
"""
super(NodePyGroup, self).__init__(name, [])
self.node_cpps = node_cpps
self.name = name
self.unique_name = unique_name
self.op_type = op_type
self.type = node_type
self.nodes = []
self.auxiliary = None
self.add_nodes(node_cpps)
self.inputs = inputs
self.outputs = outputs
# The core node in this NodePyGroup
self.key_node = key_node
def add_nodes(self, node_cpps):
for node_cpp in node_cpps:
nodepy = NodePyOP(node_cpp)
nodepy.name = node_cpp.scopeName() + '_' + node_cpp.kind()
self.nodes.append(nodepy)
def sub_node_names(self):
return [x.name for x in self.nodes]
def __repr__(self):
return 'name: {}, type: {}, op_type: {}, sub_nodes: {}, inputs: {}, outputs: {}, aux: {}'.format(
self.name, self.type, self.op_type, self.sub_node_names(),
self.inputs, self.outputs, self.auxiliary
)
class TorchModuleGraph(TorchGraph):
"""
Generates model graph, each node is created from single or multiple jit trace nodes.
"""
def __init__(self, model=None, dummy_input=None, traced_model=None):
super().__init__(model, dummy_input, traced_model)
self.global_count = 0
self.name_to_node, self.input_to_node, self.output_to_node = self._build_graph()
self._extract_auxiliary_info()
def _expand_key_func_node(self, node, nodes, input_to_node, output_to_node,
module_type):
"""
For trace graph nodes, some nodes are not in modules, these nodes are usually generated by
the functions directly called in module ```forward```. For such nodes, some of them are
trivial op which are label by ```prim::```, some of them are not such ops which is call
non-prim ops. This function is to merge neighbor prim ops to a non-prim op, to construct
a node.
Parameters
----------
node : trace graph node
The non-prim node to expand
nodes : list of trace graph node
All the trace graph nodes within the same scope as the non-prim node
input_to_node : dict
key: input name, value: a node that uses this input
output_to_node : dict
key: output name, value: a node that generates this output
module_type : str
can be 'module' or 'func'
Returns
-------
node
the expanded non-prim node
"""
# TODO: scope name could be empty
node_name = '.'.join([self._get_module_name(
node.scopeName()), node.kind(), str(self.global_count)])
unique_name = node_name
_logger.debug("expand non-prim node, node name: %s", node_name)
self.global_count += 1
op_type = node.kind()
node_group = [node]
inputs = set()
outputs = set()
node_queue = queue.Queue()
node_queue.put(node)
while not node_queue.empty():
curr_node = node_queue.get()
for _input in curr_node.inputs():
if _input.node().kind() == CONSTANT_KIND:
continue
input_name = _input.debugName()
if input_name in output_to_node:
for predecessor_node in output_to_node[input_name]:
if predecessor_node in nodes:
if not self._is_key_func(predecessor_node):
if predecessor_node not in node_group:
node_group.append(predecessor_node)
node_queue.put(predecessor_node)
else:
inputs.add(input_name)
else:
inputs.add(input_name)
else:
inputs.add(input_name)
for output in node.outputs():
if output.node().kind() == CONSTANT_KIND:
continue
outputs.add(output.debugName())
nodepy = NodePyGroup(node_name, unique_name, module_type, op_type,
node_group, inputs=list(inputs), outputs=list(outputs), key_node=node)
return nodepy
def _expand_module_node(self, node, node_name, unique_name, op_type, nodes,
input_to_node, output_to_node, module_type):
"""
merge the adjacent nodes of the module. The difference between the
_expand_module_node and _expand_non_prim_node is that, the _expand_non_prim_node
only merge the prim:: nodes into the aten:: node, in contrast,the _expand_module_node
will merge all adjacent nodes into a same nodepy group.
Parameters
----------
node : trace graph node
The non-prim node to expand
node_name : str
specify the node_name for NodePyGroup
unique_name : str
unique_name for the NodePyGroup
op_type : str
specify the op_type for the NodePyGroup
nodes : list of trace graph node
All the trace graph nodes within the same scope as the non-prim node
input_to_node : dict
key: input name, value: a node that uses this input
output_to_node : dict
key: output name, value: a node that generates this output
module_type : str
can be 'module' or 'func'
Returns
-------
node
the expanded non-prim node
"""
_logger.debug("expand module node, node name: %s", node_name)
self.global_count += 1
if not op_type:
op_type = node.kind()
node_group = [node]
inputs = set()
outputs = set()
node_queue = queue.Queue()
node_queue.put(node)
visited = {node}
while not node_queue.empty():
curr_node = node_queue.get()
for _input in curr_node.inputs():
if _input.node().kind() == CONSTANT_KIND:
continue
input_name = _input.debugName()
if input_name in output_to_node:
for predecessor_node in output_to_node[input_name]:
if predecessor_node in nodes:
if predecessor_node not in visited:
node_group.append(predecessor_node)
node_queue.put(predecessor_node)
visited.add(predecessor_node)
else:
inputs.add(input_name)
else:
inputs.add(input_name)
for _output in curr_node.outputs():
if _output.node().kind() == CONSTANT_KIND:
continue
output_name = _output.debugName()
if output_name in input_to_node:
for successor_node in input_to_node[output_name]:
if successor_node in nodes:
if successor_node not in visited:
node_group.append(successor_node)
node_queue.put(successor_node)
visited.add(successor_node)
else:
outputs.add(output_name)
else:
outputs.add(output_name)
nodepy = NodePyGroup(node_name, unique_name, module_type, op_type,
node_group, inputs=list(inputs), outputs=list(outputs))
return nodepy
def _extract_cat_info(self, node_group, cpp_node):
"""
Extract the detail information of the cat operation,
such the order of the input tensor, the shape of each
input tensor, the output shape, and the cat dimension.
Parameters
----------
node_group : NodePyGroup
cpp_node: torch._C.Node
It should be ```aten::cat``` node
Returns
-------
dict
Include auxiliary information for the cat operation.
This dict objec has four keys: 'cat_dim', 'out_shape',
'in_order' and 'in_shape'. cat_dim is the dimension of
the cat operation to concat the input tensors. out_shape
is the shape of the output tensor of the cat operation.
in_order is an ordered list which contains the corresponding
parent operaion nodes of the input tensors. in_shape is also
an ordered list that contains the input shapes of the input
tensor.
"""
# only suport the cat operation
assert cpp_node.kind() == CAT_KIND
cat_info = {}
# get the shape of the output tensor
t_output = cpp_node.output()
out_shape = t_output.type().sizes()
cat_info['out_shape'] = out_shape
# get the cat dimension
inputs = cpp_node.inputs()
cat_dim = list(inputs)[1].toIValue()
cat_info['cat_dim'] = cat_dim
# get the order of the input tensors
# To get the order of the input tensors, we need
# to be aware of the topology of the model, which
# means we should extract the auxiliary information
# after the build_index function.
input_order = []
list_construct_cpp = list(cpp_node.inputs())[0].node()
input_tensors = list(list_construct_cpp.inputs())
for _tensor in input_tensors:
debug_name = _tensor.debugName()
input_order.append(self.output_to_node[debug_name].unique_name)
cat_info['in_order'] = input_order
input_shapes = [t.type().sizes() for t in input_tensors]
cat_info['in_shape'] = input_shapes
return cat_info
def _extract_linear_shape_info(self, node_group):
"""
Extract linear shape input/output tensor shape info from its aten::addmm op.
Parameters
----------
node_group : NodePyGroup
NodePyGroup object associated with the linear module.
Returns
-------
dict
Include shape of input tensor and shape of output tensor
"""
for cpp_node in node_group.node_cpps:
if cpp_node.kind() == 'aten::addmm':
# https://github.com/pytorch/pytorch/blob/1.6/torch/nn/functional.py#L1682
# inputs of aten::addmm:
# inputs[0] is bias
# inputs[1] is input data
# inputs[2] is weight
t_input = list(cpp_node.inputs())[1]
t_output = cpp_node.output()
assert isinstance(t_input.type(), torch._C.TensorType)
assert isinstance(t_output.type(), torch._C.TensorType)
in_shape = t_input.type().sizes()
out_shape = t_output.type().sizes()
return {'in_shape': in_shape, 'out_shape': out_shape}
return None
def _extract_shape_info(self, node):
"""
Extract the shape information of ```aten::view``` node
Parameters
----------
node : trace graph node
It should be ```aten::view``` node
Returns
-------
dict
Include shape of input tensor and shape of output tensor
"""
t_input = None
for _input in node.inputs():
t_input = _input
break
t_output = node.output()
assert isinstance(t_input.type(), torch._C.TensorType)
assert isinstance(t_output.type(), torch._C.TensorType)
in_shape = t_input.type().sizes()
out_shape = t_output.type().sizes()
return {'in_shape': in_shape, 'out_shape': out_shape}
def _extract_leaf_modules(self):
"""
Extract leaf modules from the given graph. Leaf module means it does not have submodules.
To extract leaf modules because only leaf module can be replaced. And shape inference can
be done in leaf module level. Other shape inference is done in lower level i.e.,
operation level.
Returns
-------
list
a list of scope name of all the leaf modules
"""
def is_parent(name1, name2):
"""
check if name1 is parent node of name2, for example:
name1: aa.bb, name2: aa.bb.cc, return True
name1: aa.b, name2: aa.bb, return False
"""
parts1, parts2 = name1.split('.'), name2.split('.')
if len(parts1) >= len(parts2):
return False
for i, _ in enumerate(parts1):
if parts2[i] != parts1[i]:
return False
return True
module_names = sorted([x[0]
for x in self.trace.named_modules() if x[0]])
leaf_nodes = []
for i, name in enumerate(module_names):
if i + 1 >= len(module_names) or not is_parent(name, module_names[i + 1]):
leaf_nodes.append(name)
return leaf_nodes
def _get_module_name(self, scope_name):
"""
Retrieve module name from scope name.
Parameters:
-----------
scope_name: str
scope_name of a graph node, for example:
for pytorch 1.3.1: MyModel/BackboneModel[backbone]/Conv2d[conv2]
for pytorch 1.4.0: __module.backbone/__module.backbone.conv2
Returns:
-------
str
module name, such as backbone.conv2
"""
if torch.__version__ >= '1.4.0':
return scope_name.split('/')[-1].replace('__module.', '')
else:
return '.'.join(re.findall(r'\[(.*?)\]', scope_name))
def _build_index(self, nodes_op):
name_to_node = dict()
input_to_node = defaultdict(list)
output_to_node = dict()
for node in nodes_op:
name_to_node[node.unique_name] = node
for _input in node.inputs:
input_to_node[_input].append(node)
for output in node.outputs:
assert not output in output_to_node, \
"One output cannot be generated by multiple nodes %s" % output
output_to_node[output] = node
return name_to_node, input_to_node, output_to_node
def _is_key_func(self, node_cpp):
"""
Judge if a cpp node is a key function node.
If so, we should not merge this node into the
adjacent node.
"""
if node_cpp.kind().startswith('aten::'):
# the nodes that start with 'aten' are key function
# nodes
return True
if node_cpp.kind() in [LIST_UNPACK_KIND, TUPLE_UNPACK_KIND]:
# We cannot merge the List/Tuple
# Unpack func into other nodes, else it
# may lead to a graph construction error.
# The reason why we donnot take the construct node
# also as a key node is that `cat` operation node need
# the last(previous) visited node to infer the mask. If
# we take the Construct node as the important node, the
# predecessor of the `cat` node will always be a construct
# node, which means we cannot infer the mask for the cat
# operation.
return True
return False
def unpack_manually(self):
"""
Unpack the tensor tuple or tensor list manually,
and remove the ListUnpack/TupleUnpack node from
the graph. Note: this function will change the
graph structure.
"""
if hasattr(self, 'unpacked'):
# if already unpacked the tuple/list manually
return
for node in self.nodes_py.nodes_op:
if node.op_type in [TUPLE_UNPACK_KIND, LIST_UNPACK_KIND]:
unpack_cpp = node.key_node
last_cpp = list(unpack_cpp.inputs())[0].node()
if last_cpp.kind() in [TUPLE_CONSTRUCT_KIND, LIST_CONSTRUCT_KIND]:
# we need check if the tensor tuple or tensor list is produced
# by a list/tuple construct node. If so, we can unpack the tuple
# or list manunally.
_logger.debug('List/Tuple Construct Node(cpp) %s', str(last_cpp))
_logger.debug('List/Tuple Unpack Node(cpp) %s', str(unpack_cpp))
assert len(list(unpack_cpp.outputs())) == len(list(last_cpp.inputs()))
errmsg = '%s Input number: %d if inconsistent with the output number %d' % (unpack_cpp, \
len(node.inputs), len(list(last_cpp.inputs())))
assert len(node.inputs) == len(list(last_cpp.inputs())), errmsg
for _debug_input, _debug_output in zip(node.inputs, node.outputs):
# _debug_input = _input.debugName()
# _debug_output = _output.debugName()
if _debug_input in self.input_to_node and _debug_output in self.input_to_node:
# input_to_node[_debug_input] is a list of NodePyGroup, because
# one tensor can be used as input for multiple nodes at the same time.
# note that, in this case, the construct cpp node and unpack cpp node
# will be merged into the same NodePyGroup, so we remove the `node` from
# input_to_node[_debug_input] and directly connect this tensor to the
# input_to_node[_debug_output]
self.input_to_node[_debug_input].remove(node)
# add the following nodes of _output into the input_to_node[_debug_input]
self.input_to_node[_debug_input].extend(self.input_to_node[_debug_output])
# just remove the _debug_output from the grapgh index. So that we can also skip
# the construct and tuple
if _debug_output in self.input_to_node:
for following_node in self.input_to_node[_debug_output]:
_tmp_index = following_node.inputs.index(_debug_output)
following_node.inputs[_tmp_index] = _debug_input
self.unpacked = True
def _build_graph(self):
"""
Build graph using our defined format from jit trace.
There are basically three steps: first, construct necessary information (data structures),
second, extract all the modules to convert to node, Third, extract all functions to convert
to node.
Returns
-------
dict
use name to index nodes, key: node name, value: node
dict
use input (its name) to index nodes,
key: input, value: list of nodes that take this input
dict
use output (its name) to index nodes,
key: output, value: node that generates this output
"""
omit_useless_nodes = True
graph = self.trace.graph
_logger.debug(graph)
# build input/output mapping, from input/output debugName to its node
input_to_node = defaultdict(list)
output_to_node = defaultdict(list)
for node in graph.nodes():
if node.kind() == CONSTANT_KIND:
continue
for x in node.outputs():
if x.node().kind() == CONSTANT_KIND:
continue
output_to_node[x.debugName()].append(node)
assert len(output_to_node[x.debugName()]) <= 1, "One output cannot be generated by multiple nodes %s" % x.debugName()
for x in node.inputs():
if x.node().kind() == CONSTANT_KIND:
continue
input_to_node[x.debugName()].append(node)
# build module mapping, from module name to all nodes (as list) under this module scope
module_to_nodes = defaultdict(list)
# the mapping of function (non-module in forward) to nodes, key is scope name
func_to_nodes = defaultdict(list)
nodes_py = GraphPy()
for node in graph.inputs():
if omit_useless_nodes:
if not node.uses(): # number of user of the node (= number of outputs/ fanout)
continue
if node.type().kind() != 'ClassType':
nodes_py.append(NodePyIO(node, 'input'))
self.leaf_modules = self._extract_leaf_modules()
module_to_type = {name: parse_traced_name(
module._name) for name, module in self.trace.named_modules()}
# associate module name with their trace graph nodes
for node in graph.nodes():
if node.kind() == CONSTANT_KIND:
continue
module_name = self._get_module_name(node.scopeName())
if module_name in self.leaf_modules:
module_to_nodes[module_name].append(node)
else:
func_to_nodes[node.scopeName()].append(node)
# build node group for module
for module_name, node_cpps in module_to_nodes.items():
use_count = 0
merged = set()
for node in node_cpps:
if node not in merged:
# modules that have same scope name may have different locations in the
# graph. Futhermore, there are also lots of prim:: nodes that in node_cpps,
# so we also need to call the expand_module_node.
unique_name = module_name
if use_count > 0:
unique_name = module_name + '.%d' % use_count
node_group = self._expand_module_node(
node, module_name, unique_name, module_to_type[module_name],
node_cpps, input_to_node, output_to_node, 'module')
nodes_py.nodes_op.append(node_group)
use_count += 1
merged.update(node_group.node_cpps)
# each scope_name may have multiple funcs, we split them and create node for each of them
# build node group for torch.nn.functional
for _, nodes in func_to_nodes.items():
# extract non prim:: nodes
key_func_nodes = list()
for node in nodes:
if self._is_key_func(node):
# find the key function nodes
key_func_nodes.append(node)
# for each non prim node, expand it
for node in key_func_nodes:
node_group = self._expand_key_func_node(
node, nodes, input_to_node, output_to_node, 'func')
nodes_py.nodes_op.append(node_group)
# get shape infor for view (aten::view) func
# if node_group.op_type in ['aten::view', 'aten::flatten']:
# node_group.auxiliary = self._extract_shape_info(node)
for node in graph.outputs(): # Create sink nodes for output ops
node_py = NodePyIO(node, 'output')
nodes_py.append(node_py)
self.nodes_py = nodes_py
# build index
return self._build_index(self.nodes_py.nodes_op)
def _extract_auxiliary_info(self):
"""
Extract the auxiliary information for the nodegroups
if necessary. For example, view/flatten operations may
need the shape of the input tensor and output tensor.
"""
# extract the input & output shape for the view and flatten
for node_group in self.nodes_py.nodes_op:
if node_group.op_type in ['aten::view', 'aten::flatten', 'aten::mean', 'aten::reshape']:
# get shape infor for view (aten::view) func
cpp_node = list(filter(lambda x: x.kind() == node_group.op_type,
node_group.node_cpps))[0]
node_group.auxiliary = self._extract_shape_info(cpp_node)
elif node_group.op_type == 'Linear':
node_group.auxiliary = self._extract_linear_shape_info(node_group)
elif node_group.op_type == CAT_KIND:
# get the detail information for cat func
cpp_node = list(filter(lambda x: x.kind() == node_group.op_type,
node_group.node_cpps))[0]
node_group.auxiliary = self._extract_cat_info(
node_group, cpp_node)
def find_predecessors(self, unique_name):
"""
Find predecessor node of the given node
Parameters
----------
unique_name : str
The unique name of the node
Returns
-------
list
a list of nodes who are the given node's predecessor
"""
predecessors = []
for _input in self.name_to_node[unique_name].inputs:
if not _input in self.output_to_node:
_logger.debug("cannot find node with %s as its output", _input)
else:
node_py = self.output_to_node[_input]
predecessors.append(node_py.unique_name)
return predecessors
def find_successors(self, unique_name):
"""
Find successor nodes of the given node
Parameters
----------
unique_name : str
The unique name of the node
Returns
-------
list
a list of nodes who are the given node's successor
"""
successors = []
for output in self.name_to_node[unique_name].outputs:
if output not in self.input_to_node:
# may reach the output of the whole graph
continue
nodes_py = self.input_to_node[output]
for node_py in nodes_py:
successors.append(node_py.unique_name)
return successors
================================================
FILE: src/aup/compression/torch/compressor.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import types
import logging
import torch
from . import default_layers
_logger = logging.getLogger(__name__)
class LayerInfo:
def __init__(self, name, module):
self.module = module
self.name = name
self.type = type(module).__name__
def _setattr(model, name, module):
name_list = name.split(".")
for name in name_list[:-1]:
model = getattr(model, name)
setattr(model, name_list[-1], module)
class Compressor:
"""
Abstract base PyTorch compressor
"""
def __init__(self, model, config_list, optimizer=None):
"""
Record necessary info in class members
Parameters
----------
model : pytorch model
the model user wants to compress
config_list : list
the configurations that users specify for compression
optimizer: pytorch optimizer
optimizer used to train the model
"""
assert isinstance(model, torch.nn.Module)
self.validate_config(model, config_list)
self.bound_model = model
self.config_list = config_list
self.optimizer = optimizer
self.modules_to_compress = None
self.modules_wrapper = []
self.is_wrapped = False
self._fwd_hook_handles = {}
self._fwd_hook_id = 0
self.reset()
if not self.modules_wrapper:
_logger.warning('Nothing is configured to compress, please check your model and config_list')
def validate_config(self, model, config_list):
"""
subclass can optionally implement this method to check if config_list if valid
"""
pass
def reset(self, checkpoint=None):
"""
reset model state dict and model wrapper
"""
self._unwrap_model()
if checkpoint is not None:
self.bound_model.load_state_dict(checkpoint)
self.modules_to_compress = None
self.modules_wrapper = []
for layer, config in self._detect_modules_to_compress():
wrapper = self._wrap_modules(layer, config)
self.modules_wrapper.append(wrapper)
self._wrap_model()
def _detect_modules_to_compress(self):
"""
detect all modules should be compressed, and save the result in `self.modules_to_compress`.
The model will be instrumented and user should never edit it after calling this method.
"""
if self.modules_to_compress is None:
self.modules_to_compress = []
for name, module in self.bound_model.named_modules():
if module == self.bound_model:
continue
layer = LayerInfo(name, module)
config = self.select_config(layer)
if config is not None:
self.modules_to_compress.append((layer, config))
return self.modules_to_compress
def _wrap_model(self):
"""
wrap all modules that needed to be compressed
"""
for wrapper in reversed(self.get_modules_wrapper()):
_setattr(self.bound_model, wrapper.name, wrapper)
self.is_wrapped = True
def _unwrap_model(self):
"""
unwrap all modules that needed to be compressed
"""
for wrapper in self.get_modules_wrapper():
_setattr(self.bound_model, wrapper.name, wrapper.module)
self.is_wrapped = False
def compress(self):
"""
Compress the model with algorithm implemented by subclass.
The model will be instrumented and user should never edit it after calling this method.
`self.modules_to_compress` records all the to-be-compressed layers
Returns
-------
torch.nn.Module
model with specified modules compressed.
"""
return self.bound_model
def set_wrappers_attribute(self, name, value):
"""
To register attributes used in wrapped module's forward method.
If the type of the value is Torch.tensor, then this value is registered as a buffer in wrapper,
which will be saved by model.state_dict. Otherwise, this value is just a regular variable in wrapper.
Parameters
----------
name : str
name of the variable
value: any
value of the variable
"""
for wrapper in self.get_modules_wrapper():
if isinstance(value, torch.Tensor):
wrapper.register_buffer(name, value.clone())
else:
setattr(wrapper, name, value)
def get_modules_to_compress(self):
"""
To obtain all the to-be-compressed modules.
Returns
-------
list
a list of the layers, each of which is a tuple (`layer`, `config`),
`layer` is `LayerInfo`, `config` is a `dict`
"""
return self.modules_to_compress
def get_modules_wrapper(self):
"""
To obtain all the wrapped modules.
Returns
-------
list
a list of the wrapped modules
"""
return self.modules_wrapper
def select_config(self, layer):
"""
Find the configuration for `layer` by parsing `self.config_list`
Parameters
----------
layer : LayerInfo
one layer
Returns
-------
config or None
the retrieved configuration for this layer, if None, this layer should
not be compressed
"""
ret = None
for config in self.config_list:
config = config.copy()
# expand config if key `default` is in config['op_types']
if 'op_types' in config and 'default' in config['op_types']:
expanded_op_types = []
for op_type in config['op_types']:
if op_type == 'default':
expanded_op_types.extend(default_layers.weighted_modules)
else:
expanded_op_types.append(op_type)
config['op_types'] = expanded_op_types
# check if condition is satisified
if 'op_types' in config and layer.type not in config['op_types']:
continue
if 'op_names' in config and layer.name not in config['op_names']:
continue
ret = config
if ret is None or 'exclude' in ret:
return None
return ret
def update_epoch(self, epoch):
"""
If user want to update model every epoch, user can override this method.
This method should be called at the beginning of each epoch
Parameters
----------
epoch : num
the current epoch number
"""
pass
def _wrap_modules(self, layer, config):
"""
This method is implemented in the subclasses, i.e., `Pruner` and `Quantizer`
Parameters
----------
layer : LayerInfo
the layer to instrument the compression operation
config : dict
the configuration for compressing this layer
"""
raise NotImplementedError()
def add_activation_collector(self, collector):
self._fwd_hook_id += 1
self._fwd_hook_handles[self._fwd_hook_id] = []
for wrapper in self.get_modules_wrapper():
handle = wrapper.register_forward_hook(collector)
self._fwd_hook_handles[self._fwd_hook_id].append(handle)
return self._fwd_hook_id
def remove_activation_collector(self, fwd_hook_id):
if fwd_hook_id not in self._fwd_hook_handles:
raise ValueError("%s is not a valid collector id" % str(fwd_hook_id))
for handle in self._fwd_hook_handles[fwd_hook_id]:
handle.remove()
del self._fwd_hook_handles[fwd_hook_id]
def patch_optimizer(self, *tasks):
def patch_step(old_step):
def new_step(_, *args, **kwargs):
# call origin optimizer step method
output = old_step(*args, **kwargs)
# calculate mask
for task in tasks:
task()
return output
return new_step
if self.optimizer is not None:
self.optimizer.step = types.MethodType(patch_step(self.optimizer.step), self.optimizer)
class PrunerModuleWrapper(torch.nn.Module):
def __init__(self, module, module_name, module_type, config, pruner):
"""
Wrap an module to enable data parallel, forward method customization and buffer registeration.
Parameters
----------
module : pytorch module
the module user wants to compress
config : dict
the configurations that users specify for compression
module_name : str
the name of the module to compress, wrapper module shares same name
module_type : str
the type of the module to compress
pruner : Pruner
the pruner used to calculate mask
"""
super().__init__()
# origin layer information
self.module = module
self.name = module_name
self.type = module_type
# config and pruner
self.config = config
self.pruner = pruner
# register buffer for mask
self.register_buffer("weight_mask", torch.ones(self.module.weight.shape))
if hasattr(self.module, 'bias') and self.module.bias is not None:
self.register_buffer("bias_mask", torch.ones(self.module.bias.shape))
else:
self.register_buffer("bias_mask", None)
def forward(self, *inputs):
# apply mask to weight, bias
self.module.weight.data = self.module.weight.data.mul_(self.weight_mask)
if hasattr(self.module, 'bias') and self.module.bias is not None:
self.module.bias.data = self.module.bias.data.mul_(self.bias_mask)
return self.module(*inputs)
class Pruner(Compressor):
"""
Prune to an exact pruning level specification
Attributes
----------
mask_dict : dict
Dictionary for saving masks, `key` should be layer name and
`value` should be a tensor which has the same shape with layer's weight
"""
def __init__(self, model, config_list, optimizer=None):
super().__init__(model, config_list, optimizer)
if optimizer is not None:
self.patch_optimizer(self.update_mask)
def compress(self):
self.update_mask()
return self.bound_model
def update_mask(self):
for wrapper_idx, wrapper in enumerate(self.get_modules_wrapper()):
masks = self.calc_mask(wrapper, wrapper_idx=wrapper_idx)
if masks is not None:
for k in masks:
assert hasattr(wrapper, k), "there is no attribute '%s' in wrapper" % k
setattr(wrapper, k, masks[k])
def calc_mask(self, wrapper, **kwargs):
"""
Pruners should overload this method to provide mask for weight tensors.
The mask must have the same shape and type comparing to the weight.
It will be applied with `mul()` operation on the weight.
This method is effectively hooked to `forward()` method of the model.
Parameters
----------
wrapper : Module
calculate mask for `wrapper.module`'s weight
"""
raise NotImplementedError("Pruners must overload calc_mask()")
def _wrap_modules(self, layer, config):
"""
Create a wrapper module to replace the original one.
Parameters
----------
layer : LayerInfo
the layer to instrument the mask
config : dict
the configuration for generating the mask
"""
_logger.debug("Module detected to compress : %s.", layer.name)
wrapper = PrunerModuleWrapper(layer.module, layer.name, layer.type, config, self)
assert hasattr(layer.module, 'weight'), "module %s does not have 'weight' attribute" % layer.name
# move newly registered buffers to the same device of weight
wrapper.to(layer.module.weight.device)
return wrapper
def get_mask_dict(self):
"""
Builds mask dictionary for saving to disk or otherwise
Returns
-------
mask_dict
dictionary containing weight and bias pruning masks
"""
mask_dict = {}
for wrapper in self.get_modules_wrapper():
weight_mask = wrapper.weight_mask
bias_mask = wrapper.bias_mask
if weight_mask is not None:
mask_sum = weight_mask.sum().item()
mask_num = weight_mask.numel()
_logger.debug('Layer: %s Sparsity: %.4f', wrapper.name, 1 - mask_sum / mask_num)
wrapper.module.weight.data = wrapper.module.weight.data.mul(weight_mask)
if bias_mask is not None:
wrapper.module.bias.data = wrapper.module.bias.data.mul(bias_mask)
# save mask to dict
mask_dict[wrapper.name] = {"weight": weight_mask, "bias": bias_mask}
return mask_dict
def export_model(self, model_path, mask_path=None, onnx_path=None, input_shape=None, device=None):
"""
Export pruned model weights, masks and onnx model(optional)
Parameters
----------
model_path : str
path to save pruned model state_dict
mask_path : str
(optional) path to save mask dict
onnx_path : str
(optional) path to save onnx model
input_shape : list or tuple
input shape to onnx model
device : torch.device
device of the model, used to place the dummy input tensor for exporting onnx file.
the tensor is placed on cpu if ```device``` is None
"""
assert model_path is not None, 'model_path must be specified'
self._unwrap_model() # used for generating correct state_dict name without wrapper state
mask_dict = self.get_mask_dict()
torch.save(self.bound_model.state_dict(), model_path)
_logger.info('Model state_dict saved to %s', model_path)
if mask_path is not None:
torch.save(mask_dict, mask_path)
_logger.info('Mask dict saved to %s', mask_path)
if onnx_path is not None:
assert input_shape is not None, 'input_shape must be specified to export onnx model'
# input info needed
if device is None:
device = torch.device('cpu')
input_data = torch.Tensor(*input_shape)
torch.onnx.export(self.bound_model, input_data.to(device), onnx_path)
_logger.info('Model in onnx with input shape %s saved to %s', input_data.shape, onnx_path)
self._wrap_model()
def load_model_state_dict(self, model_state):
"""
Load the state dict saved from unwrapped model.
Parameters:
-----------
model_state : dict
state dict saved from unwrapped model
"""
if self.is_wrapped:
self._unwrap_model()
self.bound_model.load_state_dict(model_state)
self._wrap_model()
else:
self.bound_model.load_state_dict(model_state)
class QuantizerModuleWrapper(torch.nn.Module):
def __init__(self, module, module_name, module_type, config, quantizer):
"""
Wrap an module to enable data parallel, forward method customization and buffer registeration.
Parameters
----------
module : pytorch module
the module user wants to compress
config : dict
the configurations that users specify for compression
module_name : str
the name of the module to compress, wrapper module shares same name
module_type : str
the type of the module to compress
quantizer :quantizer
the quantizer used to calculate mask
"""
super().__init__()
# origin layer information
self.module = module
self.name = module_name
self.type = module_type
# config and pruner
self.config = config
self.quantizer = quantizer
# register buffer and parameter
# old_weight is used to store origin weight and weight is used to store quantized weight
# the reason why weight is buffer instead of parameter is because in pytorch parameter is used as leaf
# if weight is leaf , then old_weight can not be updated.
if 'weight' in config['quant_types']:
if not _check_weight(self.module):
_logger.warning('Module %s does not have parameter "weight"', self.name)
else:
self.module.register_parameter('old_weight', torch.nn.Parameter(self.module.weight))
delattr(self.module, 'weight')
self.module.register_buffer('weight', self.module.old_weight)
def forward(self, *inputs):
if 'input' in self.config['quant_types']:
inputs = self.quantizer.quant_grad.apply(
inputs,
QuantType.QUANT_INPUT,
self)
if 'weight' in self.config['quant_types'] and _check_weight(self.module):
self.quantizer.quant_grad.apply(
self.module.old_weight,
QuantType.QUANT_WEIGHT,
self)
result = self.module(*inputs)
else:
result = self.module(*inputs)
if 'output' in self.config['quant_types']:
result = self.quantizer.quant_grad.apply(
result,
QuantType.QUANT_OUTPUT,
self)
return result
class Quantizer(Compressor):
"""
Base quantizer for pytorch quantizer
"""
def __init__(self, model, config_list, optimizer=None):
super().__init__(model, config_list, optimizer)
self.quant_grad = QuantGrad
if self.optimizer is not None:
self.patch_optimizer(self.step_with_optimizer)
for wrapper in self.get_modules_wrapper():
if 'weight' in wrapper.config['quant_types']:
# old_weight is registered to keep track of weight before quantization
# and it is trainable, therefore, it should be added to optimizer.
self.optimizer.add_param_group({"params": wrapper.module.old_weight})
def quantize_weight(self, weight, wrapper, **kwargs):
"""
quantize should overload this method to quantize weight.
This method is effectively hooked to :meth:`forward` of the model.
Parameters
----------
weight : Tensor
weight that needs to be quantized
wrapper : QuantizerModuleWrapper
the wrapper for origin module
"""
raise NotImplementedError('Quantizer must overload quantize_weight()')
def quantize_output(self, output, wrapper, **kwargs):
"""
quantize should overload this method to quantize output.
This method is effectively hooked to :meth:`forward` of the model.
Parameters
----------
output : Tensor
output that needs to be quantized
wrapper : QuantizerModuleWrapper
the wrapper for origin module
"""
raise NotImplementedError('Quantizer must overload quantize_output()')
def quantize_input(self, *inputs, wrapper, **kwargs):
"""
quantize should overload this method to quantize input.
This method is effectively hooked to :meth:`forward` of the model.
Parameters
----------
inputs : Tensor
inputs that needs to be quantized
wrapper : QuantizerModuleWrapper
the wrapper for origin module
"""
raise NotImplementedError('Quantizer must overload quantize_input()')
def _wrap_modules(self, layer, config):
"""
Create a wrapper forward function to replace the original one.
Parameters
----------
layer : LayerInfo
the layer to instrument the mask
config : dict
the configuration for quantization
"""
assert 'quant_types' in config, 'must provide quant_types in config'
assert isinstance(config['quant_types'], list), 'quant_types must be list type'
assert 'quant_bits' in config, 'must provide quant_bits in config'
assert isinstance(config['quant_bits'], int) or isinstance(config['quant_bits'], dict), 'quant_bits must be dict type or int type'
if isinstance(config['quant_bits'], dict):
for quant_type in config['quant_types']:
assert quant_type in config['quant_bits'], 'bits length for %s must be specified in quant_bits dict' % quant_type
return QuantizerModuleWrapper(layer.module, layer.name, layer.type, config, self)
def step_with_optimizer(self):
pass
class QuantType:
"""
Enum class for quantization type.
"""
QUANT_INPUT = 0
QUANT_WEIGHT = 1
QUANT_OUTPUT = 2
QType_Dict = {
0: "input",
1: "weight",
2: "output"
}
class QuantGrad(torch.autograd.Function):
"""
Base class for overriding backward function of quantization operation.
"""
@classmethod
def _quantize(cls, x, scale, zero_point):
"""
Reference function for quantizing x -- non-clamped.
Parameters
----------
x : Tensor
tensor to be quantized
scale : Tensor
scale for quantizing x
zero_point : Tensor
zero_point for quantizing x
Returns
-------
tensor
quantized x without clamped
"""
return ((x / scale) + zero_point).round()
@classmethod
def get_bits_length(cls, config, quant_type):
"""
Get bit for quantize config
Parameters
----------
config : Dict
the configuration for quantization
quant_type : str
quant type
Returns
-------
int
n-bits for quantization configuration
"""
if isinstance(config["quant_bits"], int):
return config["quant_bits"]
else:
return config["quant_bits"].get(quant_type)
@staticmethod
def quant_backward(tensor, grad_output, quant_type, scale, zero_point, qmin, qmax):
"""
This method should be overrided by subclass to provide customized backward function,
default implementation is Straight-Through Estimator
Parameters
----------
tensor : Tensor
input of quantization operation
grad_output : Tensor
gradient of the output of quantization operation
scale : Tensor
the type of quantization, it can be `QuantType.QUANT_INPUT`, `QuantType.QUANT_WEIGHT`, `QuantType.QUANT_OUTPUT`,
you can define different behavior for different types.
zero_point : Tensor
zero_point for quantizing tensor
qmin : Tensor
quant_min for quantizing tensor
qmax : Tensor
quant_max for quantizng tensor
Returns
-------
tensor
gradient of the input of quantization operation
"""
return grad_output
@staticmethod
def forward(ctx, tensor, quant_type, wrapper, **kwargs):
if quant_type == QuantType.QUANT_INPUT:
output = wrapper.quantizer.quantize_input(tensor, wrapper, **kwargs)
elif quant_type == QuantType.QUANT_WEIGHT:
output = wrapper.quantizer.quantize_weight(wrapper, **kwargs)
elif quant_type == QuantType.QUANT_OUTPUT:
output = wrapper.quantizer.quantize_output(tensor, wrapper, **kwargs)
else:
raise ValueError("unrecognized QuantType.")
bits = QuantGrad.get_bits_length(wrapper.config, QType_Dict[quant_type])
qmin, qmax = torch.Tensor([0]).to(tensor.device), torch.Tensor([(1 << bits) - 1]).to(tensor.device)
if hasattr(wrapper.module, 'scale') and hasattr(wrapper.module, 'zero_point'):
scale = wrapper.module.scale
zero_point = wrapper.module.zero_point
else:
scale, zero_point = None, None
ctx.save_for_backward(tensor, torch.Tensor([quant_type]), scale, zero_point, qmin, qmax)
return output
@classmethod
def backward(cls, ctx, grad_output):
tensor, quant_type, scale, zero_point, qmin, qmax = ctx.saved_variables
output = cls.quant_backward(tensor, grad_output, quant_type, scale, zero_point, qmin, qmax)
return output, None, None, None
def _check_weight(module):
try:
return isinstance(module.weight.data, torch.Tensor)
except AttributeError:
return False
================================================
FILE: src/aup/compression/torch/default_layers.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
weighted_modules = [
'Conv1d', 'Conv2d', 'Conv3d', 'ConvTranspose1d', 'ConvTranspose2d', 'ConvTranspose3d',
'Linear', 'Bilinear',
'PReLU',
'Embedding', 'EmbeddingBag',
]
================================================
FILE: src/aup/compression/torch/parameter_expressions.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
'''
parameter_expression.py
'''
import numpy as np
def choice(options, random_state):
'''
options: 1-D array-like or int
random_state: an object of numpy.random.RandomState
'''
return random_state.choice(options)
def randint(lower, upper, random_state):
'''
Generate a random integer from `lower` (inclusive) to `upper` (exclusive).
lower: an int that represent an lower bound
upper: an int that represent an upper bound
random_state: an object of numpy.random.RandomState
'''
return random_state.randint(lower, upper)
def uniform(low, high, random_state):
'''
low: an float that represent an lower bound
high: an float that represent an upper bound
random_state: an object of numpy.random.RandomState
'''
assert high >= low, 'Upper bound must be larger than lower bound'
return random_state.uniform(low, high)
def quniform(low, high, q, random_state):
'''
low: an float that represent an lower bound
high: an float that represent an upper bound
q: sample step
random_state: an object of numpy.random.RandomState
'''
return np.clip(np.round(uniform(low, high, random_state) / q) * q, low, high)
def loguniform(low, high, random_state):
'''
low: an float that represent an lower bound
high: an float that represent an upper bound
random_state: an object of numpy.random.RandomState
'''
assert low > 0, 'Lower bound must be positive'
return np.exp(uniform(np.log(low), np.log(high), random_state))
def qloguniform(low, high, q, random_state):
'''
low: an float that represent an lower bound
high: an float that represent an upper bound
q: sample step
random_state: an object of numpy.random.RandomState
'''
return np.clip(np.round(loguniform(low, high, random_state) / q) * q, low, high)
def normal(mu, sigma, random_state):
'''
The probability density function of the normal distribution,
first derived by De Moivre and 200 years later by both Gauss and Laplace independently.
mu: float or array_like of floats
Mean (“centre”) of the distribution.
sigma: float or array_like of floats
Standard deviation (spread or “width”) of the distribution.
random_state: an object of numpy.random.RandomState
'''
return random_state.normal(mu, sigma)
def qnormal(mu, sigma, q, random_state):
'''
mu: float or array_like of floats
sigma: float or array_like of floats
q: sample step
random_state: an object of numpy.random.RandomState
'''
return np.round(normal(mu, sigma, random_state) / q) * q
def lognormal(mu, sigma, random_state):
'''
mu: float or array_like of floats
sigma: float or array_like of floats
random_state: an object of numpy.random.RandomState
'''
return np.exp(normal(mu, sigma, random_state))
def qlognormal(mu, sigma, q, random_state):
'''
mu: float or array_like of floats
sigma: float or array_like of floats
q: sample step
random_state: an object of numpy.random.RandomState
'''
return np.round(lognormal(mu, sigma, random_state) / q) * q
================================================
FILE: src/aup/compression/torch/pruning/__init__.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
from .finegrained_pruning import *
from .structured_pruning import *
from .apply_compression import apply_compression_results
from .one_shot import *
from .agp import *
from .lottery_ticket import LotteryTicketPruner
from .simulated_annealing_pruner import SimulatedAnnealingPruner
from .net_adapt_pruner import NetAdaptPruner
from .admm_pruner import ADMMPruner
from .auto_compress_pruner import AutoCompressPruner
from .sensitivity_pruner import SensitivityPruner
from .amc import AMCPruner
================================================
FILE: src/aup/compression/torch/pruning/admm_pruner.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import logging
import torch
from schema import And, Optional
import copy
from ..utils.config_validation import CompressorSchema
from .constants import MASKER_DICT
from .one_shot import OneshotPruner
_logger = logging.getLogger(__name__)
class ADMMPruner(OneshotPruner):
"""
A Pytorch implementation of ADMM Pruner algorithm.
Parameters
----------
model : torch.nn.Module
Model to be pruned.
config_list : list
List on pruning configs.
trainer : function
Function used for the first subproblem.
Users should write this function as a normal function to train the Pytorch model
and include `model, optimizer, criterion, epoch, callback` as function arguments.
Here `callback` acts as an L2 regulizer as presented in the formula (7) of the original paper.
The logic of `callback` is implemented inside the Pruner,
users are just required to insert `callback()` between `loss.backward()` and `optimizer.step()`.
Example::
def trainer(model, criterion, optimizer, epoch, callback):
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
train_loader = ...
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target)
loss.backward()
# callback should be inserted between loss.backward() and optimizer.step()
if callback:
callback()
optimizer.step()
num_iterations : int
Total number of iterations.
training_epochs : int
Training epochs of the first subproblem.
row : float
Penalty parameters for ADMM training.
base_algo : str
Base pruning algorithm. `level`, `l1`, `l2` or `fpgm`, by default `l1`. Given the sparsity distribution among the ops,
the assigned `base_algo` is used to decide which filters/channels/weights to prune.
"""
def __init__(self, model, config_list, trainer, num_iterations=30, training_epochs=5, row=1e-4, base_algo='l1'):
self._base_algo = base_algo
super().__init__(model, config_list)
self._trainer = trainer
self._num_iterations = num_iterations
self._training_epochs = training_epochs
self._row = row
self.set_wrappers_attribute("if_calculated", False)
self.masker = MASKER_DICT[self._base_algo](self.bound_model, self)
def validate_config(self, model, config_list):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
List on pruning configs
"""
if self._base_algo == 'level':
schema = CompressorSchema([{
'sparsity': And(float, lambda n: 0 < n < 1),
Optional('op_types'): [str],
Optional('op_names'): [str],
}], model, _logger)
elif self._base_algo in ['l1', 'l2', 'fpgm']:
schema = CompressorSchema([{
'sparsity': And(float, lambda n: 0 < n < 1),
'op_types': ['Conv2d'],
Optional('op_names'): [str]
}], model, _logger)
schema.validate(config_list)
def _projection(self, weight, sparsity, wrapper):
'''
Return the Euclidean projection of the weight matrix according to the pruning mode.
Parameters
----------
weight : tensor
original matrix
sparsity : float
the ratio of parameters which need to be set to zero
wrapper: PrunerModuleWrapper
layer wrapper of this layer
Returns
-------
tensor
the projected matrix
'''
wrapper_copy = copy.deepcopy(wrapper)
wrapper_copy.module.weight.data = weight
return weight.data.mul(self.masker.calc_mask(sparsity, wrapper_copy)['weight_mask'])
def compress(self):
"""
Compress the model with ADMM.
Returns
-------
torch.nn.Module
model with specified modules compressed.
"""
_logger.info('Starting ADMM Compression...')
# initiaze Z, U
# Z_i^0 = W_i^0
# U_i^0 = 0
Z = []
U = []
for wrapper in self.get_modules_wrapper():
z = wrapper.module.weight.data
Z.append(z)
U.append(torch.zeros_like(z))
optimizer = torch.optim.Adam(
self.bound_model.parameters(), lr=1e-3, weight_decay=5e-5)
# Loss = cross_entropy + l2 regulization + \Sum_{i=1}^N \row_i ||W_i - Z_i^k + U_i^k||^2
criterion = torch.nn.CrossEntropyLoss()
# callback function to do additonal optimization, refer to the deriatives of Formula (7)
def callback():
for i, wrapper in enumerate(self.get_modules_wrapper()):
wrapper.module.weight.data -= self._row * \
(wrapper.module.weight.data - Z[i] + U[i])
# optimization iteration
for k in range(self._num_iterations):
_logger.info('ADMM iteration : %d', k)
# step 1: optimize W with AdamOptimizer
for epoch in range(self._training_epochs):
self._trainer(self.bound_model, optimizer=optimizer,
criterion=criterion, epoch=epoch, callback=callback)
# step 2: update Z, U
# Z_i^{k+1} = projection(W_i^{k+1} + U_i^k)
# U_i^{k+1} = U^k + W_i^{k+1} - Z_i^{k+1}
for i, wrapper in enumerate(self.get_modules_wrapper()):
z = wrapper.module.weight.data + U[i]
Z[i] = self._projection(z, wrapper.config['sparsity'], wrapper)
U[i] = U[i] + wrapper.module.weight.data - Z[i]
# apply prune
self.update_mask()
_logger.info('Compression finished.')
return self.bound_model
================================================
FILE: src/aup/compression/torch/pruning/agp.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
"""
An automated gradual pruning algorithm that prunes the smallest magnitude
weights to achieve a preset level of network sparsity.
Michael Zhu and Suyog Gupta, "To prune, or not to prune: exploring the
efficacy of pruning for model compression", 2017 NIPS Workshop on Machine
Learning of Phones and other Consumer Devices.
"""
import logging
import torch
from schema import And, Optional
from .constants import MASKER_DICT
from ..utils.config_validation import CompressorSchema
from ..compressor import Pruner
__all__ = ['AGPPruner']
logger = logging.getLogger('torch pruner')
class AGPPruner(Pruner):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned.
config_list : listlist
Supported keys:
- initial_sparsity: This is to specify the sparsity when compressor starts to compress.
- final_sparsity: This is to specify the sparsity when compressor finishes to compress.
- start_epoch: This is to specify the epoch number when compressor starts to compress, default start from epoch 0.
- end_epoch: This is to specify the epoch number when compressor finishes to compress.
- frequency: This is to specify every *frequency* number epochs compressor compress once, default frequency=1.
optimizer: torch.optim.Optimizer
Optimizer used to train model.
pruning_algorithm: str
Algorithms being used to prune model,
choose from `['level', 'slim', 'l1', 'l2', 'fpgm', 'taylorfo', 'apoz', 'mean_activation']`, by default `level`
"""
def __init__(self, model, config_list, optimizer, pruning_algorithm='level'):
super().__init__(model, config_list, optimizer)
assert isinstance(optimizer, torch.optim.Optimizer), "AGP pruner is an iterative pruner, please pass optimizer of the model to it"
self.masker = MASKER_DICT[pruning_algorithm](model, self)
self.now_epoch = 0
self.set_wrappers_attribute("if_calculated", False)
def validate_config(self, model, config_list):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
List on pruning configs
"""
schema = CompressorSchema([{
'initial_sparsity': And(float, lambda n: 0 <= n <= 1),
'final_sparsity': And(float, lambda n: 0 <= n <= 1),
'start_epoch': And(int, lambda n: n >= 0),
'end_epoch': And(int, lambda n: n >= 0),
'frequency': And(int, lambda n: n > 0),
Optional('op_types'): [str],
Optional('op_names'): [str]
}], model, logger)
schema.validate(config_list)
def calc_mask(self, wrapper, wrapper_idx=None):
"""
Calculate the mask of given layer.
Scale factors with the smallest absolute value in the BN layer are masked.
Parameters
----------
wrapper : Module
the layer to instrument the compression operation
wrapper_idx: int
index of this wrapper in pruner's all wrappers
Returns
-------
dict | None
Dictionary for storing masks, keys of the dict:
'weight_mask': weight mask tensor
'bias_mask': bias mask tensor (optional)
"""
config = wrapper.config
start_epoch = config.get('start_epoch', 0)
freq = config.get('frequency', 1)
if wrapper.if_calculated:
return None
if not (self.now_epoch >= start_epoch and (self.now_epoch - start_epoch) % freq == 0):
return None
target_sparsity = self.compute_target_sparsity(config)
new_mask = self.masker.calc_mask(sparsity=target_sparsity, wrapper=wrapper, wrapper_idx=wrapper_idx)
if new_mask is not None:
wrapper.if_calculated = True
return new_mask
def compute_target_sparsity(self, config):
"""
Calculate the sparsity for pruning
Parameters
----------
config : dict
Layer's pruning config
Returns
-------
float
Target sparsity to be pruned
"""
end_epoch = config.get('end_epoch', 1)
start_epoch = config.get('start_epoch', 0)
freq = config.get('frequency', 1)
final_sparsity = config.get('final_sparsity', 0)
initial_sparsity = config.get('initial_sparsity', 0)
if end_epoch <= start_epoch or initial_sparsity >= final_sparsity:
logger.warning('your end epoch <= start epoch or initial_sparsity >= final_sparsity')
return final_sparsity
if end_epoch <= self.now_epoch:
return final_sparsity
span = ((end_epoch - start_epoch - 1) // freq) * freq
assert span > 0
target_sparsity = (final_sparsity +
(initial_sparsity - final_sparsity) *
(1.0 - ((self.now_epoch - start_epoch) / span)) ** 3)
return target_sparsity
def update_epoch(self, epoch):
"""
Update epoch
Parameters
----------
epoch : int
current training epoch
"""
if epoch > 0:
self.now_epoch = epoch
for wrapper in self.get_modules_wrapper():
wrapper.if_calculated = False
================================================
FILE: src/aup/compression/torch/pruning/amc/__init__.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
from .amc_pruner import AMCPruner
================================================
FILE: src/aup/compression/torch/pruning/amc/amc_pruner.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import os
import logging
from copy import deepcopy
from argparse import Namespace
import numpy as np
import torch
from torch.utils.tensorboard import SummaryWriter
from ...compressor import Pruner
from .channel_pruning_env import ChannelPruningEnv
from .lib.agent import DDPG
from .lib.utils import get_output_folder
torch.backends.cudnn.deterministic = True
_logger = logging.getLogger(__name__)
class AMCPruner(Pruner):
"""
A pytorch implementation of AMC: AutoML for Model Compression and Acceleration on Mobile Devices.
(https://arxiv.org/pdf/1802.03494.pdf)
Parameters:
model: nn.Module
The model to be pruned.
config_list: list
Configuration list to configure layer pruning.
Supported keys:
- op_types: operation type to be pruned
- op_names: operation name to be pruned
evaluator: function
function to evaluate the pruned model.
The prototype of the function:
>>> def evaluator(val_loader, model):
>>> ...
>>> return acc
val_loader: torch.utils.data.DataLoader
Data loader of validation dataset.
suffix: str
suffix to help you remember what experiment you ran. Default: None.
# parameters for pruning environment
model_type: str
model type to prune, currently 'mobilenet' and 'mobilenetv2' are supported. Default: mobilenet
flops_ratio: float
preserve flops ratio. Default: 0.5
lbound: float
minimum weight preserve ratio for each layer. Default: 0.2
rbound: float
maximum weight preserve ratio for each layer. Default: 1.0
reward: function
reward function type:
- acc_reward: accuracy * 0.01
- acc_flops_reward: - (100 - accuracy) * 0.01 * np.log(flops)
Default: acc_reward
# parameters for channel pruning
n_calibration_batches: int
number of batches to extract layer information. Default: 60
n_points_per_layer: int
number of feature points per layer. Default: 10
channel_round: int
round channel to multiple of channel_round. Default: 8
# parameters for ddpg agent
hidden1: int
hidden num of first fully connect layer. Default: 300
hidden2: int
hidden num of second fully connect layer. Default: 300
lr_c: float
learning rate for critic. Default: 1e-3
lr_a: float
learning rate for actor. Default: 1e-4
warmup: int
number of episodes without training but only filling the replay memory. During warmup episodes,
random actions ares used for pruning. Default: 100
discount: float
next Q value discount for deep Q value target. Default: 0.99
bsize: int
minibatch size for training DDPG agent. Default: 64
rmsize: int
memory size for each layer. Default: 100
window_length: int
replay buffer window length. Default: 1
tau: float
moving average for target network being used by soft_update. Default: 0.99
# noise
init_delta: float
initial variance of truncated normal distribution
delta_decay: float
delta decay during exploration
# parameters for training ddpg agent
max_episode_length: int
maximum episode length
output_dir: str
output directory to save log files and model files. Default: ./logs
debug: boolean
debug mode
train_episode: int
train iters each timestep. Default: 800
epsilon: int
linear decay of exploration policy. Default: 50000
seed: int
random seed to set for reproduce experiment. Default: None
"""
def __init__(
self,
model,
config_list,
evaluator,
val_loader,
suffix=None,
model_type='mobilenet',
dataset='cifar10',
flops_ratio=0.5,
lbound=0.2,
rbound=1.,
reward='acc_reward',
n_calibration_batches=60,
n_points_per_layer=10,
channel_round=8,
hidden1=300,
hidden2=300,
lr_c=1e-3,
lr_a=1e-4,
warmup=100,
discount=1.,
bsize=64,
rmsize=100,
window_length=1,
tau=0.01,
init_delta=0.5,
delta_decay=0.99,
max_episode_length=1e9,
output_dir='./logs',
debug=False,
train_episode=800,
epsilon=50000,
seed=None):
self.val_loader = val_loader
self.evaluator = evaluator
if seed is not None:
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
checkpoint = deepcopy(model.state_dict())
super().__init__(model, config_list, optimizer=None)
# build folder and logs
base_folder_name = '{}_{}_r{}_search'.format(model_type, dataset, flops_ratio)
if suffix is not None:
self.output_dir = os.path.join(output_dir, base_folder_name + '-' + suffix)
else:
self.output_dir = get_output_folder(output_dir, base_folder_name)
self.env_args = Namespace(
model_type=model_type,
preserve_ratio=flops_ratio,
lbound=lbound,
rbound=rbound,
reward=reward,
n_calibration_batches=n_calibration_batches,
n_points_per_layer=n_points_per_layer,
channel_round=channel_round,
output=self.output_dir
)
self.env = ChannelPruningEnv(
self, evaluator, val_loader, checkpoint, args=self.env_args)
_logger.info('=> Saving logs to %s', self.output_dir)
self.tfwriter = SummaryWriter(log_dir=self.output_dir)
self.text_writer = open(os.path.join(self.output_dir, 'log.txt'), 'w')
_logger.info('=> Output path: %s...', self.output_dir)
nb_states = self.env.layer_embedding.shape[1]
nb_actions = 1 # just 1 action here
rmsize = rmsize * len(self.env.prunable_idx) # for each layer
_logger.info('** Actual replay buffer size: %d', rmsize)
self.ddpg_args = Namespace(
hidden1=hidden1,
hidden2=hidden2,
lr_c=lr_c,
lr_a=lr_a,
warmup=warmup,
discount=discount,
bsize=bsize,
rmsize=rmsize,
window_length=window_length,
tau=tau,
init_delta=init_delta,
delta_decay=delta_decay,
max_episode_length=max_episode_length,
debug=debug,
train_episode=train_episode,
epsilon=epsilon
)
self.agent = DDPG(nb_states, nb_actions, self.ddpg_args)
def compress(self):
self.train(self.ddpg_args.train_episode, self.agent, self.env, self.output_dir)
def train(self, num_episode, agent, env, output_dir):
agent.is_training = True
step = episode = episode_steps = 0
episode_reward = 0.
observation = None
T = [] # trajectory
while episode < num_episode: # counting based on episode
# reset if it is the start of episode
if observation is None:
observation = deepcopy(env.reset())
agent.reset(observation)
# agent pick action ...
if episode <= self.ddpg_args.warmup:
action = agent.random_action()
# action = sample_from_truncated_normal_distribution(lower=0., upper=1., mu=env.preserve_ratio, sigma=0.5)
else:
action = agent.select_action(observation, episode=episode)
# env response with next_observation, reward, terminate_info
observation2, reward, done, info = env.step(action)
T.append([reward, deepcopy(observation), deepcopy(observation2), action, done])
# fix-length, never reach here
# if max_episode_length and episode_steps >= max_episode_length - 1:
# done = True
# [optional] save intermideate model
if num_episode / 3 <= 1 or episode % int(num_episode / 3) == 0:
agent.save_model(output_dir)
# update
step += 1
episode_steps += 1
episode_reward += reward
observation = deepcopy(observation2)
if done: # end of episode
_logger.info(
'#%d: episode_reward: %.4f acc: %.4f, ratio: %.4f',
episode, episode_reward,
info['accuracy'],
info['compress_ratio']
)
self.text_writer.write(
'#{}: episode_reward:{:.4f} acc: {:.4f}, ratio: {:.4f}\n'.format(
episode, episode_reward,
info['accuracy'],
info['compress_ratio']
)
)
final_reward = T[-1][0]
# print('final_reward: {}'.format(final_reward))
# agent observe and update policy
for _, s_t, s_t1, a_t, done in T:
agent.observe(final_reward, s_t, s_t1, a_t, done)
if episode > self.ddpg_args.warmup:
agent.update_policy()
#agent.memory.append(
# observation,
# agent.select_action(observation, episode=episode),
# 0., False
#)
# reset
observation = None
episode_steps = 0
episode_reward = 0.
episode += 1
T = []
self.tfwriter.add_scalar('reward/last', final_reward, episode)
self.tfwriter.add_scalar('reward/best', env.best_reward, episode)
self.tfwriter.add_scalar('info/accuracy', info['accuracy'], episode)
self.tfwriter.add_scalar('info/compress_ratio', info['compress_ratio'], episode)
self.tfwriter.add_text('info/best_policy', str(env.best_strategy), episode)
# record the preserve rate for each layer
for i, preserve_rate in enumerate(env.strategy):
self.tfwriter.add_scalar('preserve_rate/{}'.format(i), preserve_rate, episode)
self.text_writer.write('best reward: {}\n'.format(env.best_reward))
self.text_writer.write('best policy: {}\n'.format(env.best_strategy))
self.text_writer.close()
================================================
FILE: src/aup/compression/torch/pruning/amc/channel_pruning_env.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import os
import logging
import time
import math
import copy
import numpy as np
import torch
import torch.nn as nn
from ...compressor import PrunerModuleWrapper
from .. import AMCWeightMasker
_logger = logging.getLogger(__name__)
# for pruning
def acc_reward(net, acc, flops):
return acc * 0.01
def acc_flops_reward(net, acc, flops):
error = (100 - acc) * 0.01
return -error * np.log(flops)
class ChannelPruningEnv:
"""
Env for channel pruning search.
This class is used to prune model using specified pruner. It prunes one layer when
step() is called. When the last layer is pruned, it evaluate the pruned model using
evaluator, and use the returned value of evaluator as reward of the episode.
Usage:
env = ChannelPruningEnv(pruner, evaluator, val_loader, checkpoint, env_args)
episode = 0
T = []
while episode < num_episode:
action = agent.select_action(observation)
observation2, reward, done, info = env.step(action)
T.append([reward, deepcopy(observation), deepcopy(observation2), action, done])
if done: # end of episode, last layer pruned
episode += 1
# train agent with episode data
for _, s_t, s_t1, a_t, done in T:
agent.observe(final_reward, s_t, s_t1, a_t, done)
agent.update_policy()
T = []
Attributes:
prunable_idx: layer indices for pruable layers, the index values are the index
of list(self.model.modules()). Pruable layers are pointwise Conv2d layers and Linear
layers.
buffer_idx: layer indices for buffer layers which refers the depthwise layers.
Each depthwise layer is always followd by a pointwise layer for both mobilenet and
mobilenetv2. The depthwise layer's filters are pruned when its next pointwise layer's
corresponding input channels are pruned.
shared_idx: layer indices for layers which share input.
For example: [[1,4], [8, 10, 15]] means layer 1 and 4 share same input, and layer
8, 10 and 15 share another input.
layer_embedding: embeddings for each prunable layers, the embedding is used as
observation for DDPG agent.
layer_info_dict: flops and number of parameters of each layer.
min_strategy_dict: key is layer index, value is a tuple, the first value is the minimum
action of input channel, the second value is the minimum action value of output channel.
strategy_dict: key is layer index, value is a tuple, the first value is the action of input
channel, the second value is the action of output channel.
Parameters:
pruner: Pruner
NNI Pruner instance used to prune model.
evaluator: function
function to evaluate the pruned model.
The prototype of the function:
>>> def evaluator(val_loader, model):
>>> ...
>>> return acc
val_loader: torch.utils.data.DataLoader
Data loader of validation dataset.
checkpoint: dict
checkpoint of the model to be pruned. It is used to reset model at beginning of each
episode.
args:
A Namespace object containing following arguments:
model_type: str
model type to prune, currently 'mobilenet' and 'mobilenetv2' are supported.
flops_ratio: float
preserve flops ratio.
lbound: float
minimum weight preserve ratio for each layer.
rbound: float
maximum weight preserve ratio for each layer.
reward: function
reward function type
# parameters for channel pruning
n_calibration_batches: int
number of batches to extract layer information.
n_points_per_layer: int
number of feature points per layer.
channel_round: int
round channel to multiple of channel_round.
"""
def __init__(self, pruner, evaluator, val_loader, checkpoint, args):
self.pruner = pruner
self.model = pruner.bound_model
self.checkpoint = checkpoint
self.batch_size = val_loader.batch_size
self.preserve_ratio = args.preserve_ratio
self.channel_prune_masker = AMCWeightMasker(self.model, self.pruner, args.channel_round)
# options from args
self.args = args
self.lbound = args.lbound
self.rbound = args.rbound
self.n_calibration_batches = args.n_calibration_batches
self.n_points_per_layer = args.n_points_per_layer
self.channel_round = args.channel_round
# sanity check
assert self.preserve_ratio > self.lbound, 'Error! You can not achieve preserve_ratio smaller than lbound!'
# prepare data
self._val_loader = val_loader
self._validate = evaluator
# build indexs
self._build_index()
self.n_prunable_layer = len(self.prunable_idx)
# extract information for preparing
self._extract_layer_information()
# build embedding (static part)
self._build_state_embedding()
# build reward
self.reset() # restore weight
self.org_acc = self._validate(self._val_loader, self.model)
_logger.info('=> original acc: %.3f', self.org_acc)
self.org_model_size = sum(self.wsize_list)
_logger.info('=> original weight size: %.4f M param', self.org_model_size * 1. / 1e6)
self.org_flops = sum(self.flops_list)
_logger.info('=> FLOPs:')
_logger.info([self.layer_info_dict[idx]['flops']/1e6 for idx in sorted(self.layer_info_dict.keys())])
_logger.info('=> original FLOPs: %.4f M', self.org_flops * 1. / 1e6)
self.expected_preserve_computation = self.preserve_ratio * self.org_flops
self.reward = eval(args.reward)
self.best_reward = -math.inf
self.best_strategy = None
self.best_d_prime_list = None
self.best_masks = None
self.org_w_size = sum(self.wsize_list)
def step(self, action):
# Pseudo prune and get the corresponding statistics. The real pruning happens till the end of all pseudo pruning
if self.visited[self.cur_ind]:
action = self.strategy_dict[self.prunable_idx[self.cur_ind]][0]
preserve_idx = self.index_buffer[self.cur_ind]
else:
action = self._action_wall(action) # percentage to preserve
preserve_idx = None
# prune and update action
action, d_prime, preserve_idx = self.prune_kernel(self.prunable_idx[self.cur_ind], action, preserve_idx)
if not self.visited[self.cur_ind]:
for group in self.shared_idx:
if self.cur_ind in group: # set the shared ones
for g_idx in group:
self.strategy_dict[self.prunable_idx[g_idx]][0] = action
self.strategy_dict[self.prunable_idx[g_idx - 1]][1] = action
self.visited[g_idx] = True
self.index_buffer[g_idx] = preserve_idx.copy()
self.strategy.append(action) # save action to strategy
self.d_prime_list.append(d_prime)
self.strategy_dict[self.prunable_idx[self.cur_ind]][0] = action
if self.cur_ind > 0:
self.strategy_dict[self.prunable_idx[self.cur_ind - 1]][1] = action
# all the actions are made
if self._is_final_layer():
assert len(self.strategy) == len(self.prunable_idx)
current_flops = self._cur_flops()
acc_t1 = time.time()
acc = self._validate(self._val_loader, self.model)
acc_t2 = time.time()
self.val_time = acc_t2 - acc_t1
compress_ratio = current_flops * 1. / self.org_flops
info_set = {'compress_ratio': compress_ratio, 'accuracy': acc, 'strategy': self.strategy.copy()}
reward = self.reward(self, acc, current_flops)
if reward > self.best_reward:
self.best_reward = reward
self.best_strategy = self.strategy.copy()
self.best_d_prime_list = self.d_prime_list.copy()
best_model = os.path.join(self.args.output, 'best_model.pth')
best_mask = os.path.join(self.args.output, 'best_mask.pth')
self.pruner.export_model(model_path=best_model, mask_path=best_mask)
_logger.info('New best reward: %.4f, acc: %.4f, compress: %.4f', self.best_reward, acc, compress_ratio)
_logger.info('New best policy: %s', self.best_strategy)
_logger.info('New best d primes: %s', self.best_d_prime_list)
obs = self.layer_embedding[self.cur_ind, :].copy() # actually the same as the last state
done = True
return obs, reward, done, info_set
info_set = None
reward = 0
done = False
self.visited[self.cur_ind] = True # set to visited
self.cur_ind += 1 # the index of next layer
# build next state (in-place modify)
self.layer_embedding[self.cur_ind][-3] = self._cur_reduced() * 1. / self.org_flops # reduced
self.layer_embedding[self.cur_ind][-2] = sum(self.flops_list[self.cur_ind + 1:]) * 1. / self.org_flops # rest
self.layer_embedding[self.cur_ind][-1] = self.strategy[-1] # last action
obs = self.layer_embedding[self.cur_ind, :].copy()
return obs, reward, done, info_set
def reset(self):
# restore env by loading the checkpoint
self.pruner.reset(self.checkpoint)
self.cur_ind = 0
self.strategy = [] # pruning strategy
self.d_prime_list = []
self.strategy_dict = copy.deepcopy(self.min_strategy_dict)
# reset layer embeddings
self.layer_embedding[:, -1] = 1.
self.layer_embedding[:, -2] = 0.
self.layer_embedding[:, -3] = 0.
obs = self.layer_embedding[0].copy()
obs[-2] = sum(self.wsize_list[1:]) * 1. / sum(self.wsize_list)
self.extract_time = 0
self.fit_time = 0
self.val_time = 0
# for share index
self.visited = [False] * len(self.prunable_idx)
self.index_buffer = {}
return obs
def prune_kernel(self, op_idx, preserve_ratio, preserve_idx=None):
m_list = list(self.model.modules())
op = m_list[op_idx]
assert (0. < preserve_ratio <= 1.)
assert type(op) == PrunerModuleWrapper
if preserve_ratio == 1: # do not prune
if (preserve_idx is None) or (len(preserve_idx) == op.module.weight.size(1)):
return 1., op.module.weight.size(1), None # should be a full index
op.input_feat = self.layer_info_dict[op_idx]['input_feat']
op.output_feat = self.layer_info_dict[op_idx]['output_feat']
masks = self.channel_prune_masker.calc_mask(sparsity=1-preserve_ratio, wrapper=op, preserve_idx=preserve_idx)
m = masks['weight_mask'].cpu().data
if type(op.module) == nn.Conv2d:
d_prime = (m.sum((0, 2, 3)) > 0).sum().item()
preserve_idx = np.nonzero((m.sum((0, 2, 3)) > 0).numpy())[0]
else:
assert type(op.module) == nn.Linear
d_prime = (m.sum(1) > 0).sum().item()
preserve_idx = np.nonzero((m.sum(1) > 0).numpy())[0]
op.weight_mask = masks['weight_mask']
if hasattr(op.module, 'bias') and op.module.bias is not None and 'bias_mask' in masks:
op.bias_mask = masks['bias_mask']
action = (m == 1).sum().item() / m.numel()
return action, d_prime, preserve_idx
def _is_final_layer(self):
return self.cur_ind == len(self.prunable_idx) - 1
def _action_wall(self, action):
"""
Limit the action generated by DDPG for this layer by two constraints:
1. The total flops must meet the flops reduce target.
For example: the original flops of entire model is 1000, target flops ratio is 0.5, target flops
is 1000*0.5 = 500. The reduced flops of other layers is 400, so the remaining flops quota is 500-400=100,
if the total original flops of this layer is 250, then the maximum ratio is 100/250 = 0.4. So the
action of this layer can not be greater than 0.4.
2. The action must be greater than lbound which is stored in self.strategy_dict.
"""
assert len(self.strategy) == self.cur_ind
action = float(action)
action = np.clip(action, 0, 1)
other_comp = 0
this_comp = 0
for i, idx in enumerate(self.prunable_idx):
flop = self.layer_info_dict[idx]['flops']
buffer_flop = self._get_buffer_flops(idx)
if i == self.cur_ind - 1: # TODO: add other member in the set
this_comp += flop * self.strategy_dict[idx][0]
# add buffer (but not influenced by ratio)
other_comp += buffer_flop * self.strategy_dict[idx][0]
elif i == self.cur_ind:
this_comp += flop * self.strategy_dict[idx][1]
# also add buffer here (influenced by ratio)
this_comp += buffer_flop
else:
other_comp += flop * self.strategy_dict[idx][0] * self.strategy_dict[idx][1]
# add buffer
other_comp += buffer_flop * self.strategy_dict[idx][0] # only consider input reduction
self.expected_min_preserve = other_comp + this_comp * action
max_preserve_ratio = (self.expected_preserve_computation - other_comp) * 1. / this_comp
action = np.minimum(action, max_preserve_ratio)
action = np.maximum(action, self.strategy_dict[self.prunable_idx[self.cur_ind]][0]) # impossible (should be)
return action
def _get_buffer_flops(self, idx):
buffer_idx = self.buffer_dict[idx]
buffer_flop = sum([self.layer_info_dict[_]['flops'] for _ in buffer_idx])
return buffer_flop
def _cur_flops(self):
flops = 0
for idx in self.prunable_idx:
c, n = self.strategy_dict[idx] # input, output pruning ratio
flops += self.layer_info_dict[idx]['flops'] * c * n
# add buffer computation
flops += self._get_buffer_flops(idx) * c # only related to input channel reduction
return flops
def _cur_reduced(self):
# return the reduced weight
reduced = self.org_flops - self._cur_flops()
return reduced
def _build_index(self):
"""
Build following information/data for later pruning:
self.prunable_idx: layer indices for pruable layers, the index values are the index
of list(self.model.modules()). Pruable layers are pointwise Conv2d layers and Linear
layers.
self.prunable_ops: prunable modules
self.buffer_idx: layer indices for buffer layers which refers the depthwise layers.
Each depthwise layer is always followd by a pointwise layer for both mobilenet and
mobilenetv2. The depthwise layer's filters are pruned when its next pointwise layer's
corresponding input channels are pruned.
self.shared_idx: layer indices for layers which share input.
For example: [[1,4], [8, 10, 15]] means layer 1 and 4 share same input, and layer
8, 10 and 15 share another input.
self.org_channels: number of input channels for each layer
self.min_strategy_dict: key is layer index, value is a tuple, the first value is the minimum
action of input channel, the second value is the minimum action value of output channel.
self.strategy_dict: same as self.min_strategy_dict, but it will be updated later.
"""
self.prunable_idx = []
self.prunable_ops = []
self.layer_type_dict = {}
self.strategy_dict = {}
self.buffer_dict = {}
this_buffer_list = []
self.org_channels = []
# build index and the min strategy dict
for i, m in enumerate(self.model.modules()):
if isinstance(m, PrunerModuleWrapper):
m = m.module
if type(m) == nn.Conv2d and m.groups == m.in_channels: # depth-wise conv, buffer
this_buffer_list.append(i)
else: # really prunable
self.prunable_idx.append(i)
self.prunable_ops.append(m)
self.layer_type_dict[i] = type(m)
self.buffer_dict[i] = this_buffer_list
this_buffer_list = [] # empty
self.org_channels.append(m.in_channels if type(m) == nn.Conv2d else m.in_features)
self.strategy_dict[i] = [self.lbound, self.lbound]
self.strategy_dict[self.prunable_idx[0]][0] = 1 # modify the input
self.strategy_dict[self.prunable_idx[-1]][1] = 1 # modify the output
self.shared_idx = []
if self.args.model_type == 'mobilenetv2': # TODO: to be tested! Share index for residual connection
connected_idx = [4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32] # to be partitioned
last_ch = -1
share_group = None
for c_idx in connected_idx:
if self.prunable_ops[c_idx].in_channels != last_ch: # new group
last_ch = self.prunable_ops[c_idx].in_channels
if share_group is not None:
self.shared_idx.append(share_group)
share_group = [c_idx]
else: # same group
share_group.append(c_idx)
self.shared_idx.append(share_group)
_logger.info('=> Conv layers to share channels: %s', self.shared_idx)
self.min_strategy_dict = copy.deepcopy(self.strategy_dict)
self.buffer_idx = []
for _, v in self.buffer_dict.items():
self.buffer_idx += v
_logger.info('=> Prunable layer idx: %s', self.prunable_idx)
_logger.info('=> Buffer layer idx: %s', self.buffer_idx)
_logger.info('=> Shared idx: %s', self.shared_idx)
_logger.info('=> Initial min strategy dict: %s', self.min_strategy_dict)
# added for supporting residual connections during pruning
self.visited = [False] * len(self.prunable_idx)
self.index_buffer = {}
def _extract_layer_information(self):
m_list = list(self.model.modules())
self.data_saver = []
self.layer_info_dict = dict()
self.wsize_list = []
self.flops_list = []
from .lib.utils import measure_layer_for_pruning
# extend the forward fn to record layer info
def new_forward(m):
def lambda_forward(x):
m.input_feat = x.clone()
#TODO replace this flops counter with nni.compression.torch.utils.counter.count_flops_params
measure_layer_for_pruning(m, x)
y = m.old_forward(x)
m.output_feat = y.clone()
return y
return lambda_forward
device = None
for idx in self.prunable_idx + self.buffer_idx: # get all
m = m_list[idx]
m.old_forward = m.forward
m.forward = new_forward(m)
if device is None and type(m) == PrunerModuleWrapper:
device = m.module.weight.device
# now let the image flow
_logger.info('=> Extracting information...')
with torch.no_grad():
for i_b, (inputs, target) in enumerate(self._val_loader): # use image from train set
if i_b == self.n_calibration_batches:
break
self.data_saver.append((inputs.clone(), target.clone()))
input_var = torch.autograd.Variable(inputs).to(device)
# inference and collect stats
_ = self.model(input_var)
if i_b == 0: # first batch
for idx in self.prunable_idx + self.buffer_idx:
self.layer_info_dict[idx] = dict()
self.layer_info_dict[idx]['params'] = m_list[idx].params
self.layer_info_dict[idx]['flops'] = m_list[idx].flops
self.wsize_list.append(m_list[idx].params)
self.flops_list.append(m_list[idx].flops)
_logger.info('flops: %s', self.flops_list)
for idx in self.prunable_idx:
f_in_np = m_list[idx].input_feat.data.cpu().numpy()
f_out_np = m_list[idx].output_feat.data.cpu().numpy()
if len(f_in_np.shape) == 4: # conv
if self.prunable_idx.index(idx) == 0: # first conv
f_in2save, f_out2save = None, None
elif m_list[idx].module.weight.size(3) > 1: # normal conv
f_in2save, f_out2save = f_in_np, f_out_np
else: # 1x1 conv
# assert f_out_np.shape[2] == f_in_np.shape[2] # now support k=3
randx = np.random.randint(0, f_out_np.shape[2] - 0, self.n_points_per_layer)
randy = np.random.randint(0, f_out_np.shape[3] - 0, self.n_points_per_layer)
# input: [N, C, H, W]
self.layer_info_dict[idx][(i_b, 'randx')] = randx.copy()
self.layer_info_dict[idx][(i_b, 'randy')] = randy.copy()
f_in2save = f_in_np[:, :, randx, randy].copy().transpose(0, 2, 1)\
.reshape(self.batch_size * self.n_points_per_layer, -1)
f_out2save = f_out_np[:, :, randx, randy].copy().transpose(0, 2, 1) \
.reshape(self.batch_size * self.n_points_per_layer, -1)
else:
assert len(f_in_np.shape) == 2
f_in2save = f_in_np.copy()
f_out2save = f_out_np.copy()
if 'input_feat' not in self.layer_info_dict[idx]:
self.layer_info_dict[idx]['input_feat'] = f_in2save
self.layer_info_dict[idx]['output_feat'] = f_out2save
else:
self.layer_info_dict[idx]['input_feat'] = np.vstack(
(self.layer_info_dict[idx]['input_feat'], f_in2save))
self.layer_info_dict[idx]['output_feat'] = np.vstack(
(self.layer_info_dict[idx]['output_feat'], f_out2save))
def _build_state_embedding(self):
# build the static part of the state embedding
_logger.info('Building state embedding...')
layer_embedding = []
module_list = list(self.model.modules())
for i, ind in enumerate(self.prunable_idx):
m = module_list[ind].module
this_state = []
if type(m) == nn.Conv2d:
this_state.append(i) # index
this_state.append(0) # layer type, 0 for conv
this_state.append(m.in_channels) # in channels
this_state.append(m.out_channels) # out channels
this_state.append(m.stride[0]) # stride
this_state.append(m.kernel_size[0]) # kernel size
this_state.append(np.prod(m.weight.size())) # weight size
elif type(m) == nn.Linear:
this_state.append(i) # index
this_state.append(1) # layer type, 1 for fc
this_state.append(m.in_features) # in channels
this_state.append(m.out_features) # out channels
this_state.append(0) # stride
this_state.append(1) # kernel size
this_state.append(np.prod(m.weight.size())) # weight size
# this 3 features need to be changed later
this_state.append(0.) # reduced
this_state.append(0.) # rest
this_state.append(1.) # a_{t-1}
layer_embedding.append(np.array(this_state))
# normalize the state
layer_embedding = np.array(layer_embedding, 'float')
_logger.info('=> shape of embedding (n_layer * n_dim): %s', layer_embedding.shape)
assert len(layer_embedding.shape) == 2, layer_embedding.shape
for i in range(layer_embedding.shape[1]):
fmin = min(layer_embedding[:, i])
fmax = max(layer_embedding[:, i])
if fmax - fmin > 0:
layer_embedding[:, i] = (layer_embedding[:, i] - fmin) / (fmax - fmin)
self.layer_embedding = layer_embedding
================================================
FILE: src/aup/compression/torch/pruning/amc/lib/__init__.py
================================================
================================================
FILE: src/aup/compression/torch/pruning/amc/lib/agent.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import numpy as np
import torch
import torch.nn as nn
from torch.optim import Adam
from .memory import SequentialMemory
from .utils import to_numpy, to_tensor
criterion = nn.MSELoss()
USE_CUDA = torch.cuda.is_available()
class Actor(nn.Module):
def __init__(self, nb_states, nb_actions, hidden1=400, hidden2=300):
super(Actor, self).__init__()
self.fc1 = nn.Linear(nb_states, hidden1)
self.fc2 = nn.Linear(hidden1, hidden2)
self.fc3 = nn.Linear(hidden2, nb_actions)
self.relu = nn.ReLU()
self.sigmoid = nn.Sigmoid()
def forward(self, x):
out = self.fc1(x)
out = self.relu(out)
out = self.fc2(out)
out = self.relu(out)
out = self.fc3(out)
out = self.sigmoid(out)
return out
class Critic(nn.Module):
def __init__(self, nb_states, nb_actions, hidden1=400, hidden2=300):
super(Critic, self).__init__()
self.fc11 = nn.Linear(nb_states, hidden1)
self.fc12 = nn.Linear(nb_actions, hidden1)
self.fc2 = nn.Linear(hidden1, hidden2)
self.fc3 = nn.Linear(hidden2, 1)
self.relu = nn.ReLU()
def forward(self, xs):
x, a = xs
out = self.fc11(x) + self.fc12(a)
out = self.relu(out)
out = self.fc2(out)
out = self.relu(out)
out = self.fc3(out)
return out
class DDPG(object):
def __init__(self, nb_states, nb_actions, args):
self.nb_states = nb_states
self.nb_actions = nb_actions
# Create Actor and Critic Network
net_cfg = {
'hidden1': args.hidden1,
'hidden2': args.hidden2,
# 'init_w': args.init_w
}
self.actor = Actor(self.nb_states, self.nb_actions, **net_cfg)
self.actor_target = Actor(self.nb_states, self.nb_actions, **net_cfg)
self.actor_optim = Adam(self.actor.parameters(), lr=args.lr_a)
self.critic = Critic(self.nb_states, self.nb_actions, **net_cfg)
self.critic_target = Critic(self.nb_states, self.nb_actions, **net_cfg)
self.critic_optim = Adam(self.critic.parameters(), lr=args.lr_c)
self.hard_update(self.actor_target, self.actor) # Make sure target is with the same weight
self.hard_update(self.critic_target, self.critic)
# Create replay buffer
self.memory = SequentialMemory(limit=args.rmsize, window_length=args.window_length)
# self.random_process = OrnsteinUhlenbeckProcess(size=nb_actions, theta=args.ou_theta, mu=args.ou_mu,
# sigma=args.ou_sigma)
# Hyper-parameters
self.batch_size = args.bsize
self.tau = args.tau
self.discount = args.discount
self.depsilon = 1.0 / args.epsilon
self.lbound = 0. # args.lbound
self.rbound = 1. # args.rbound
# noise
self.init_delta = args.init_delta
self.delta_decay = args.delta_decay
self.warmup = args.warmup
#
self.epsilon = 1.0
# self.s_t = None # Most recent state
# self.a_t = None # Most recent action
self.is_training = True
#
if USE_CUDA: self.cuda()
# moving average baseline
self.moving_average = None
self.moving_alpha = 0.5 # based on batch, so small
def update_policy(self):
# Sample batch
state_batch, action_batch, reward_batch, \
next_state_batch, terminal_batch = self.memory.sample_and_split(self.batch_size)
# normalize the reward
batch_mean_reward = np.mean(reward_batch)
if self.moving_average is None:
self.moving_average = batch_mean_reward
else:
self.moving_average += self.moving_alpha * (batch_mean_reward - self.moving_average)
reward_batch -= self.moving_average
# if reward_batch.std() > 0:
# reward_batch /= reward_batch.std()
# Prepare for the target q batch
with torch.no_grad():
next_q_values = self.critic_target([
to_tensor(next_state_batch),
self.actor_target(to_tensor(next_state_batch)),
])
target_q_batch = to_tensor(reward_batch) + \
self.discount * to_tensor(terminal_batch.astype(np.float)) * next_q_values
# Critic update
self.critic.zero_grad()
q_batch = self.critic([to_tensor(state_batch), to_tensor(action_batch)])
value_loss = criterion(q_batch, target_q_batch)
value_loss.backward()
self.critic_optim.step()
# Actor update
self.actor.zero_grad()
policy_loss = -self.critic([ # pylint: disable=all
to_tensor(state_batch),
self.actor(to_tensor(state_batch))
])
policy_loss = policy_loss.mean()
policy_loss.backward()
self.actor_optim.step()
# Target update
self.soft_update(self.actor_target, self.actor)
self.soft_update(self.critic_target, self.critic)
def eval(self):
self.actor.eval()
self.actor_target.eval()
self.critic.eval()
self.critic_target.eval()
def cuda(self):
self.actor.cuda()
self.actor_target.cuda()
self.critic.cuda()
self.critic_target.cuda()
def observe(self, r_t, s_t, s_t1, a_t, done):
if self.is_training:
self.memory.append(s_t, a_t, r_t, done) # save to memory
# self.s_t = s_t1
def random_action(self):
action = np.random.uniform(self.lbound, self.rbound, self.nb_actions)
# self.a_t = action
return action
def select_action(self, s_t, episode):
# assert episode >= self.warmup, 'Episode: {} warmup: {}'.format(episode, self.warmup)
action = to_numpy(self.actor(to_tensor(np.array(s_t).reshape(1, -1)))).squeeze(0)
delta = self.init_delta * (self.delta_decay ** (episode - self.warmup))
# action += self.is_training * max(self.epsilon, 0) * self.random_process.sample()
action = self.sample_from_truncated_normal_distribution(lower=self.lbound, upper=self.rbound, mu=action, sigma=delta)
action = np.clip(action, self.lbound, self.rbound)
# self.a_t = action
return action
def reset(self, obs):
pass
# self.s_t = obs
# self.random_process.reset_states()
def load_weights(self, output):
if output is None: return
self.actor.load_state_dict(
torch.load('{}/actor.pkl'.format(output))
)
self.critic.load_state_dict(
torch.load('{}/critic.pkl'.format(output))
)
def save_model(self, output):
torch.save(
self.actor.state_dict(),
'{}/actor.pkl'.format(output)
)
torch.save(
self.critic.state_dict(),
'{}/critic.pkl'.format(output)
)
def soft_update(self, target, source):
for target_param, param in zip(target.parameters(), source.parameters()):
target_param.data.copy_(
target_param.data * (1.0 - self.tau) + param.data * self.tau
)
def hard_update(self, target, source):
for target_param, param in zip(target.parameters(), source.parameters()):
target_param.data.copy_(param.data)
def sample_from_truncated_normal_distribution(self, lower, upper, mu, sigma, size=1):
from scipy import stats
return stats.truncnorm.rvs((lower-mu)/sigma, (upper-mu)/sigma, loc=mu, scale=sigma, size=size)
================================================
FILE: src/aup/compression/torch/pruning/amc/lib/memory.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
from __future__ import absolute_import
from collections import deque, namedtuple
import warnings
import random
import numpy as np
# [reference] https://github.com/matthiasplappert/keras-rl/blob/master/rl/memory.py
# This is to be understood as a transition: Given `state0`, performing `action`
# yields `reward` and results in `state1`, which might be `terminal`.
Experience = namedtuple('Experience', 'state0, action, reward, state1, terminal1')
def sample_batch_indexes(low, high, size):
if high - low >= size:
# We have enough data. Draw without replacement, that is each index is unique in the
# batch. We cannot use `np.random.choice` here because it is horribly inefficient as
# the memory grows. See https://github.com/numpy/numpy/issues/2764 for a discussion.
# `random.sample` does the same thing (drawing without replacement) and is way faster.
r = range(low, high)
batch_idxs = random.sample(r, size)
else:
# Not enough data. Help ourselves with sampling from the range, but the same index
# can occur multiple times. This is not good and should be avoided by picking a
# large enough warm-up phase.
warnings.warn(
'Not enough entries to sample without replacement. '
'Consider increasing your warm-up phase to avoid oversampling!')
batch_idxs = np.random.random_integers(low, high - 1, size=size)
assert len(batch_idxs) == size
return batch_idxs
class RingBuffer(object):
def __init__(self, maxlen):
self.maxlen = maxlen
self.start = 0
self.length = 0
self.data = [None for _ in range(maxlen)]
def __len__(self):
return self.length
def __getitem__(self, idx):
if idx < 0 or idx >= self.length:
raise KeyError()
return self.data[(self.start + idx) % self.maxlen]
def append(self, v):
if self.length < self.maxlen:
# We have space, simply increase the length.
self.length += 1
elif self.length == self.maxlen:
# No space, "remove" the first item.
self.start = (self.start + 1) % self.maxlen
else:
# This should never happen.
raise RuntimeError()
self.data[(self.start + self.length - 1) % self.maxlen] = v
def zeroed_observation(observation):
if hasattr(observation, 'shape'):
return np.zeros(observation.shape)
elif hasattr(observation, '__iter__'):
out = []
for x in observation:
out.append(zeroed_observation(x))
return out
else:
return 0.
class Memory(object):
def __init__(self, window_length, ignore_episode_boundaries=False):
self.window_length = window_length
self.ignore_episode_boundaries = ignore_episode_boundaries
self.recent_observations = deque(maxlen=window_length)
self.recent_terminals = deque(maxlen=window_length)
def sample(self, batch_size, batch_idxs=None):
raise NotImplementedError()
def append(self, observation, action, reward, terminal, training=True):
self.recent_observations.append(observation)
self.recent_terminals.append(terminal)
def get_recent_state(self, current_observation):
# This code is slightly complicated by the fact that subsequent observations might be
# from different episodes. We ensure that an experience never spans multiple episodes.
# This is probably not that important in practice but it seems cleaner.
state = [current_observation]
idx = len(self.recent_observations) - 1
for offset in range(0, self.window_length - 1):
current_idx = idx - offset
current_terminal = self.recent_terminals[current_idx - 1] if current_idx - 1 >= 0 else False
if current_idx < 0 or (not self.ignore_episode_boundaries and current_terminal):
# The previously handled observation was terminal, don't add the current one.
# Otherwise we would leak into a different episode.
break
state.insert(0, self.recent_observations[current_idx])
while len(state) < self.window_length:
state.insert(0, zeroed_observation(state[0]))
return state
def get_config(self):
config = {
'window_length': self.window_length,
'ignore_episode_boundaries': self.ignore_episode_boundaries,
}
return config
class SequentialMemory(Memory):
def __init__(self, limit, **kwargs):
super(SequentialMemory, self).__init__(**kwargs)
self.limit = limit
# Do not use deque to implement the memory. This data structure may seem convenient but
# it is way too slow on random access. Instead, we use our own ring buffer implementation.
self.actions = RingBuffer(limit)
self.rewards = RingBuffer(limit)
self.terminals = RingBuffer(limit)
self.observations = RingBuffer(limit)
def sample(self, batch_size, batch_idxs=None):
if batch_idxs is None:
# Draw random indexes such that we have at least a single entry before each
# index.
batch_idxs = sample_batch_indexes(0, self.nb_entries - 1, size=batch_size)
batch_idxs = np.array(batch_idxs) + 1
assert np.min(batch_idxs) >= 1
assert np.max(batch_idxs) < self.nb_entries
assert len(batch_idxs) == batch_size
# Create experiences
experiences = []
for idx in batch_idxs:
terminal0 = self.terminals[idx - 2] if idx >= 2 else False
while terminal0:
# Skip this transition because the environment was reset here. Select a new, random
# transition and use this instead. This may cause the batch to contain the same
# transition twice.
idx = sample_batch_indexes(1, self.nb_entries, size=1)[0]
terminal0 = self.terminals[idx - 2] if idx >= 2 else False
assert 1 <= idx < self.nb_entries
# This code is slightly complicated by the fact that subsequent observations might be
# from different episodes. We ensure that an experience never spans multiple episodes.
# This is probably not that important in practice but it seems cleaner.
state0 = [self.observations[idx - 1]]
for offset in range(0, self.window_length - 1):
current_idx = idx - 2 - offset
current_terminal = self.terminals[current_idx - 1] if current_idx - 1 > 0 else False
if current_idx < 0 or (not self.ignore_episode_boundaries and current_terminal):
# The previously handled observation was terminal, don't add the current one.
# Otherwise we would leak into a different episode.
break
state0.insert(0, self.observations[current_idx])
while len(state0) < self.window_length:
state0.insert(0, zeroed_observation(state0[0]))
action = self.actions[idx - 1]
reward = self.rewards[idx - 1]
terminal1 = self.terminals[idx - 1]
# Okay, now we need to create the follow-up state. This is state0 shifted on timestep
# to the right. Again, we need to be careful to not include an observation from the next
# episode if the last state is terminal.
state1 = [np.copy(x) for x in state0[1:]]
state1.append(self.observations[idx])
assert len(state0) == self.window_length
assert len(state1) == len(state0)
experiences.append(Experience(state0=state0, action=action, reward=reward,
state1=state1, terminal1=terminal1))
assert len(experiences) == batch_size
return experiences
def sample_and_split(self, batch_size, batch_idxs=None):
experiences = self.sample(batch_size, batch_idxs)
state0_batch = []
reward_batch = []
action_batch = []
terminal1_batch = []
state1_batch = []
for e in experiences:
state0_batch.append(e.state0)
state1_batch.append(e.state1)
reward_batch.append(e.reward)
action_batch.append(e.action)
terminal1_batch.append(0. if e.terminal1 else 1.)
# Prepare and validate parameters.
state0_batch = np.array(state0_batch, 'double').reshape(batch_size, -1)
state1_batch = np.array(state1_batch, 'double').reshape(batch_size, -1)
terminal1_batch = np.array(terminal1_batch, 'double').reshape(batch_size, -1)
reward_batch = np.array(reward_batch, 'double').reshape(batch_size, -1)
action_batch = np.array(action_batch, 'double').reshape(batch_size, -1)
return state0_batch, action_batch, reward_batch, state1_batch, terminal1_batch
def append(self, observation, action, reward, terminal, training=True):
super(SequentialMemory, self).append(observation, action, reward, terminal, training=training)
# This needs to be understood as follows: in `observation`, take `action`, obtain `reward`
# and weather the next state is `terminal` or not.
if training:
self.observations.append(observation)
self.actions.append(action)
self.rewards.append(reward)
self.terminals.append(terminal)
@property
def nb_entries(self):
return len(self.observations)
def get_config(self):
config = super(SequentialMemory, self).get_config()
config['limit'] = self.limit
return config
================================================
FILE: src/aup/compression/torch/pruning/amc/lib/net_measure.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import torch
# [reference] https://github.com/ShichenLiu/CondenseNet/blob/master/utils.py
def get_num_gen(gen):
return sum(1 for _ in gen)
def is_leaf(model):
return get_num_gen(model.children()) == 0
def get_layer_info(layer):
layer_str = str(layer)
type_name = layer_str[:layer_str.find('(')].strip()
return type_name
def get_layer_param(model):
import operator
import functools
return sum([functools.reduce(operator.mul, i.size(), 1) for i in model.parameters()])
count_ops = 0
count_params = 0
def measure_layer(layer, x):
global count_ops, count_params
delta_ops = 0
delta_params = 0
multi_add = 1
type_name = get_layer_info(layer)
# ops_conv
if type_name in ['Conv2d']:
out_h = int((x.size()[2] + 2 * layer.padding[0] - layer.kernel_size[0]) /
layer.stride[0] + 1)
out_w = int((x.size()[3] + 2 * layer.padding[1] - layer.kernel_size[1]) /
layer.stride[1] + 1)
delta_ops = layer.in_channels * layer.out_channels * layer.kernel_size[0] * \
layer.kernel_size[1] * out_h * out_w / layer.groups * multi_add
delta_params = get_layer_param(layer)
# ops_nonlinearity
elif type_name in ['ReLU']:
delta_ops = x.numel() / x.size(0)
delta_params = get_layer_param(layer)
# ops_pooling
elif type_name in ['AvgPool2d']:
in_w = x.size()[2]
kernel_ops = layer.kernel_size * layer.kernel_size
out_w = int((in_w + 2 * layer.padding - layer.kernel_size) / layer.stride + 1)
out_h = int((in_w + 2 * layer.padding - layer.kernel_size) / layer.stride + 1)
delta_ops = x.size()[1] * out_w * out_h * kernel_ops
delta_params = get_layer_param(layer)
elif type_name in ['AdaptiveAvgPool2d']:
delta_ops = x.size()[1] * x.size()[2] * x.size()[3]
delta_params = get_layer_param(layer)
# ops_linear
elif type_name in ['Linear']:
weight_ops = layer.weight.numel() * multi_add
bias_ops = layer.bias.numel()
delta_ops = weight_ops + bias_ops
delta_params = get_layer_param(layer)
# ops_nothing
elif type_name in ['BatchNorm2d', 'Dropout2d', 'DropChannel', 'Dropout']:
delta_params = get_layer_param(layer)
# unknown layer type
else:
delta_params = get_layer_param(layer)
count_ops += delta_ops
count_params += delta_params
return
def measure_model(model, H, W, device):
global count_ops, count_params
count_ops = 0
count_params = 0
data = torch.zeros(2, 3, H, W).to(device)
def should_measure(x):
return is_leaf(x)
def modify_forward(model):
for child in model.children():
if should_measure(child):
def new_forward(m):
def lambda_forward(x):
measure_layer(m, x)
return m.old_forward(x)
return lambda_forward
child.old_forward = child.forward
child.forward = new_forward(child)
else:
modify_forward(child)
def restore_forward(model):
for child in model.children():
# leaf node
if is_leaf(child) and hasattr(child, 'old_forward'):
child.forward = child.old_forward
child.old_forward = None
else:
restore_forward(child)
modify_forward(model)
model.forward(data)
restore_forward(model)
return count_ops, count_params
================================================
FILE: src/aup/compression/torch/pruning/amc/lib/utils.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import os
import torch
class TextLogger(object):
"""Write log immediately to the disk"""
def __init__(self, filepath):
self.f = open(filepath, 'w')
self.fid = self.f.fileno()
self.filepath = filepath
def close(self):
self.f.close()
def write(self, content):
self.f.write(content)
self.f.flush()
os.fsync(self.fid)
def write_buf(self, content):
self.f.write(content)
def print_and_write(self, content):
print(content)
self.write(content+'\n')
def to_numpy(var):
use_cuda = torch.cuda.is_available()
return var.cpu().data.numpy() if use_cuda else var.data.numpy()
def to_tensor(ndarray, requires_grad=False): # return a float tensor by default
tensor = torch.from_numpy(ndarray).float() # by default does not require grad
if requires_grad:
tensor.requires_grad_()
return tensor.cuda() if torch.cuda.is_available() else tensor
def measure_layer_for_pruning(wrapper, x):
def get_layer_type(layer):
layer_str = str(layer)
return layer_str[:layer_str.find('(')].strip()
def get_layer_param(model):
import operator
import functools
return sum([functools.reduce(operator.mul, i.size(), 1) for i in model.parameters()])
multi_add = 1
layer = wrapper.module
type_name = get_layer_type(layer)
# ops_conv
if type_name in ['Conv2d']:
out_h = int((x.size()[2] + 2 * layer.padding[0] - layer.kernel_size[0]) /
layer.stride[0] + 1)
out_w = int((x.size()[3] + 2 * layer.padding[1] - layer.kernel_size[1]) /
layer.stride[1] + 1)
wrapper.flops = layer.in_channels * layer.out_channels * layer.kernel_size[0] * \
layer.kernel_size[1] * out_h * out_w / layer.groups * multi_add
wrapper.params = get_layer_param(layer)
# ops_linear
elif type_name in ['Linear']:
weight_ops = layer.weight.numel() * multi_add
bias_ops = layer.bias.numel()
wrapper.flops = weight_ops + bias_ops
wrapper.params = get_layer_param(layer)
return
def least_square_sklearn(X, Y):
from sklearn.linear_model import LinearRegression
reg = LinearRegression(fit_intercept=False)
reg.fit(X, Y)
return reg.coef_
def get_output_folder(parent_dir, env_name):
"""Return save folder.
Assumes folders in the parent_dir have suffix -run{run
number}. Finds the highest run number and sets the output folder
to that number + 1. This is just convenient so that if you run the
same script multiple times tensorboard can plot all of the results
on the same plots with different names.
Parameters
----------
parent_dir: str
Path of the directory containing all experiment runs.
Returns
-------
parent_dir/run_dir
Path to this run's save directory.
"""
os.makedirs(parent_dir, exist_ok=True)
experiment_id = 0
for folder_name in os.listdir(parent_dir):
if not os.path.isdir(os.path.join(parent_dir, folder_name)):
continue
try:
folder_name = int(folder_name.split('-run')[-1])
if folder_name > experiment_id:
experiment_id = folder_name
except:
pass
experiment_id += 1
parent_dir = os.path.join(parent_dir, env_name)
parent_dir = parent_dir + '-run{}'.format(experiment_id)
os.makedirs(parent_dir, exist_ok=True)
return parent_dir
================================================
FILE: src/aup/compression/torch/pruning/apply_compression.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import logging
import torch
logger = logging.getLogger('torch apply compression')
def apply_compression_results(model, masks=None, masks_file=None, map_location=None):
"""
Apply the masks from ```masks_file``` to the model
Note: this API is for inference, because it simply multiplies weights with
corresponding masks when this API is called.
Parameters
----------
model : torch.nn.Module
The model to be compressed
masks : dict
The pre-loaded dictionary of weight pruning masks
masks_file : str
The path of user provided mask file
map_location : str
the device on which masks are placed, same to map_location in ```torch.load```
"""
if masks is not None:
masks = masks
elif masks_file is not None:
masks = torch.load(masks_file, map_location)
else:
raise ValueError("Either masks or masks_file must be passed to apply_compression_results")
for name, module in model.named_modules():
if name in masks:
module.weight.data = module.weight.data.mul_(masks[name]['weight'])
if hasattr(module, 'bias') and module.bias is not None and 'bias' in masks[name]:
module.bias.data = module.bias.data.mul_(masks[name]['bias'])
================================================
FILE: src/aup/compression/torch/pruning/auto_compress_pruner.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import logging
import os
import copy
import torch
from schema import And, Optional
from ..torch_utils import OptimizeMode
from .. import ModelSpeedup
from ..compressor import Pruner
from ..utils.config_validation import CompressorSchema
from .simulated_annealing_pruner import SimulatedAnnealingPruner
from .admm_pruner import ADMMPruner
_logger = logging.getLogger(__name__)
class AutoCompressPruner(Pruner):
"""
A Pytorch implementation of AutoCompress pruning algorithm.
Parameters
----------
model : pytorch model
The model to be pruned.
config_list : list
Supported keys:
- sparsity : The target overall sparsity.
- op_types : The operation type to prune.
trainer : function
Function used for the first subproblem of ADMM Pruner.
Users should write this function as a normal function to train the Pytorch model
and include `model, optimizer, criterion, epoch, callback` as function arguments.
Here `callback` acts as an L2 regulizer as presented in the formula (7) of the original paper.
The logic of `callback` is implemented inside the Pruner,
users are just required to insert `callback()` between `loss.backward()` and `optimizer.step()`.
Example::
def trainer(model, criterion, optimizer, epoch, callback):
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
train_loader = ...
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target)
loss.backward()
# callback should be inserted between loss.backward() and optimizer.step()
if callback:
callback()
optimizer.step()
evaluator : function
function to evaluate the pruned model.
This function should include `model` as the only parameter, and returns a scalar value.
Example::
def evaluator(model):
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
val_loader = ...
model.eval()
correct = 0
with torch.no_grad():
for data, target in val_loader:
data, target = data.to(device), target.to(device)
output = model(data)
# get the index of the max log-probability
pred = output.argmax(dim=1, keepdim=True)
correct += pred.eq(target.view_as(pred)).sum().item()
accuracy = correct / len(val_loader.dataset)
return accuracy
dummy_input : pytorch tensor
The dummy input for ```jit.trace```, users should put it on right device before pass in.
num_iterations : int
Number of overall iterations.
optimize_mode : str
optimize mode, `maximize` or `minimize`, by default `maximize`.
base_algo : str
Base pruning algorithm. `level`, `l1`, `l2` or `fpgm`, by default `l1`. Given the sparsity distribution among the ops,
the assigned `base_algo` is used to decide which filters/channels/weights to prune.
start_temperature : float
Start temperature of the simulated annealing process.
stop_temperature : float
Stop temperature of the simulated annealing process.
cool_down_rate : float
Cool down rate of the temperature.
perturbation_magnitude : float
Initial perturbation magnitude to the sparsities. The magnitude decreases with current temperature.
admm_num_iterations : int
Number of iterations of ADMM Pruner.
admm_training_epochs : int
Training epochs of the first optimization subproblem of ADMMPruner.
row : float
Penalty parameters for ADMM training.
experiment_data_dir : string
PATH to store temporary experiment data.
"""
def __init__(self, model, config_list, trainer, evaluator, dummy_input,
num_iterations=3, optimize_mode='maximize', base_algo='l1',
# SimulatedAnnealing related
start_temperature=100, stop_temperature=20, cool_down_rate=0.9, perturbation_magnitude=0.35,
# ADMM related
admm_num_iterations=30, admm_training_epochs=5, row=1e-4,
experiment_data_dir='./'):
# original model
self._model_to_prune = model
self._base_algo = base_algo
self._trainer = trainer
self._evaluator = evaluator
self._dummy_input = dummy_input
self._num_iterations = num_iterations
self._optimize_mode = OptimizeMode(optimize_mode)
# hyper parameters for SA algorithm
self._start_temperature = start_temperature
self._stop_temperature = stop_temperature
self._cool_down_rate = cool_down_rate
self._perturbation_magnitude = perturbation_magnitude
# hyper parameters for ADMM algorithm
self._admm_num_iterations = admm_num_iterations
self._admm_training_epochs = admm_training_epochs
self._row = row
# overall pruning rate
self._sparsity = config_list[0]['sparsity']
self._experiment_data_dir = experiment_data_dir
if not os.path.exists(self._experiment_data_dir):
os.makedirs(self._experiment_data_dir)
def validate_config(self, model, config_list):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
List on pruning configs
"""
if self._base_algo == 'level':
schema = CompressorSchema([{
'sparsity': And(float, lambda n: 0 < n < 1),
Optional('op_types'): [str],
Optional('op_names'): [str],
}], model, _logger)
elif self._base_algo in ['l1', 'l2', 'fpgm']:
schema = CompressorSchema([{
'sparsity': And(float, lambda n: 0 < n < 1),
'op_types': ['Conv2d'],
Optional('op_names'): [str]
}], model, _logger)
schema.validate(config_list)
def calc_mask(self, wrapper, **kwargs):
return None
def compress(self):
"""
Compress the model with AutoCompress.
Returns
-------
torch.nn.Module
model with specified modules compressed.
"""
_logger.info('Starting AutoCompress pruning...')
sparsity_each_round = 1 - pow(1-self._sparsity, 1/self._num_iterations)
for i in range(self._num_iterations):
_logger.info('Pruning iteration: %d', i)
_logger.info('Target sparsity this round: %s',
1-pow(1-sparsity_each_round, i+1))
# SimulatedAnnealingPruner
_logger.info(
'Generating sparsities with SimulatedAnnealingPruner...')
SApruner = SimulatedAnnealingPruner(
model=copy.deepcopy(self._model_to_prune),
config_list=[
{"sparsity": sparsity_each_round, "op_types": ['Conv2d']}],
evaluator=self._evaluator,
optimize_mode=self._optimize_mode,
base_algo=self._base_algo,
start_temperature=self._start_temperature,
stop_temperature=self._stop_temperature,
cool_down_rate=self._cool_down_rate,
perturbation_magnitude=self._perturbation_magnitude,
experiment_data_dir=self._experiment_data_dir)
config_list = SApruner.compress(return_config_list=True)
_logger.info("Generated config_list : %s", config_list)
# ADMMPruner
_logger.info('Performing structured pruning with ADMMPruner...')
ADMMpruner = ADMMPruner(
model=copy.deepcopy(self._model_to_prune),
config_list=config_list,
trainer=self._trainer,
num_iterations=self._admm_num_iterations,
training_epochs=self._admm_training_epochs,
row=self._row,
base_algo=self._base_algo)
ADMMpruner.compress()
ADMMpruner.export_model(os.path.join(self._experiment_data_dir, 'model_admm_masked.pth'), os.path.join(
self._experiment_data_dir, 'mask.pth'))
# use speed up to prune the model before next iteration, because SimulatedAnnealingPruner & ADMMPruner don't take masked models
self._model_to_prune.load_state_dict(torch.load(os.path.join(
self._experiment_data_dir, 'model_admm_masked.pth')))
masks_file = os.path.join(self._experiment_data_dir, 'mask.pth')
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
_logger.info('Speeding up models...')
m_speedup = ModelSpeedup(self._model_to_prune, self._dummy_input, masks_file=masks_file, map_location=device)
m_speedup.speedup_model()
evaluation_result = self._evaluator(self._model_to_prune)
_logger.info('Evaluation result of the pruned model in iteration %d: %s', i, evaluation_result)
_logger.info('----------Compression finished--------------')
os.remove(os.path.join(self._experiment_data_dir, 'model_admm_masked.pth'))
os.remove(os.path.join(self._experiment_data_dir, 'mask.pth'))
return self._model_to_prune
def export_model(self, model_path, mask_path=None, onnx_path=None, input_shape=None, device=None):
_logger.info("AutoCompressPruner export directly the pruned model without mask")
torch.save(self._model_to_prune.state_dict(), model_path)
_logger.info('Model state_dict saved to %s', model_path)
if onnx_path is not None:
assert input_shape is not None, 'input_shape must be specified to export onnx model'
# input info needed
if device is None:
device = torch.device('cpu')
input_data = torch.Tensor(*input_shape)
torch.onnx.export(self._model_to_prune, input_data.to(device), onnx_path)
_logger.info('Model in onnx with input shape %s saved to %s', input_data.shape, onnx_path)
================================================
FILE: src/aup/compression/torch/pruning/constants.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
from . import LevelPrunerMasker, SlimPrunerMasker, L1FilterPrunerMasker, \
L2FilterPrunerMasker, FPGMPrunerMasker, TaylorFOWeightFilterPrunerMasker, \
ActivationAPoZRankFilterPrunerMasker, ActivationMeanRankFilterPrunerMasker
MASKER_DICT = {
'level': LevelPrunerMasker,
'slim': SlimPrunerMasker,
'l1': L1FilterPrunerMasker,
'l2': L2FilterPrunerMasker,
'fpgm': FPGMPrunerMasker,
'taylorfo': TaylorFOWeightFilterPrunerMasker,
'apoz': ActivationAPoZRankFilterPrunerMasker,
'mean_activation': ActivationMeanRankFilterPrunerMasker
}
================================================
FILE: src/aup/compression/torch/pruning/constants_pruner.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
from .one_shot import LevelPruner, L1FilterPruner, L2FilterPruner, FPGMPruner
PRUNER_DICT = {
'level': LevelPruner,
'l1': L1FilterPruner,
'l2': L2FilterPruner,
'fpgm': FPGMPruner
}
================================================
FILE: src/aup/compression/torch/pruning/finegrained_pruning.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import logging
import torch
from .weight_masker import WeightMasker
__all__ = ['LevelPrunerMasker']
logger = logging.getLogger('torch pruner')
class LevelPrunerMasker(WeightMasker):
"""
Prune to an exact pruning level specification
"""
def calc_mask(self, sparsity, wrapper, wrapper_idx=None):
weight = wrapper.module.weight.data.clone()
if wrapper.weight_mask is not None:
# apply base mask for iterative pruning
weight = weight * wrapper.weight_mask
w_abs = weight.abs()
k = int(weight.numel() * sparsity)
if k == 0:
return {'weight_mask': torch.ones(weight.shape).type_as(weight)}
threshold = torch.topk(w_abs.view(-1), k, largest=False)[0].max()
mask_weight = torch.gt(w_abs, threshold).type_as(weight)
mask = {'weight_mask': mask_weight}
return mask
================================================
FILE: src/aup/compression/torch/pruning/lottery_ticket.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import copy
import logging
import torch
from schema import And, Optional
from ..utils.config_validation import CompressorSchema
from ..compressor import Pruner
from .finegrained_pruning import LevelPrunerMasker
logger = logging.getLogger('torch pruner')
class LotteryTicketPruner(Pruner):
"""
Parameters
----------
model : pytorch model
The model to be pruned
config_list : list
Supported keys:
- prune_iterations : The number of rounds for the iterative pruning.
- sparsity : The final sparsity when the compression is done.
optimizer : pytorch optimizer
The optimizer for the model
lr_scheduler : pytorch lr scheduler
The lr scheduler for the model if used
reset_weights : bool
Whether reset weights and optimizer at the beginning of each round.
"""
def __init__(self, model, config_list, optimizer=None, lr_scheduler=None, reset_weights=True):
# save init weights and optimizer
self.reset_weights = reset_weights
if self.reset_weights:
self._model = model
self._optimizer = optimizer
self._model_state = copy.deepcopy(model.state_dict())
self._optimizer_state = copy.deepcopy(optimizer.state_dict())
self._lr_scheduler = lr_scheduler
if lr_scheduler is not None:
self._scheduler_state = copy.deepcopy(lr_scheduler.state_dict())
super().__init__(model, config_list, optimizer)
self.curr_prune_iteration = None
self.prune_iterations = config_list[0]['prune_iterations']
self.masker = LevelPrunerMasker(model, self)
def validate_config(self, model, config_list):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
Supported keys:
- prune_iterations : The number of rounds for the iterative pruning.
- sparsity : The final sparsity when the compression is done.
"""
schema = CompressorSchema([{
'sparsity': And(float, lambda n: 0 < n < 1),
'prune_iterations': And(int, lambda n: n > 0),
Optional('op_types'): [str],
Optional('op_names'): [str]
}], model, logger)
schema.validate(config_list)
assert len(set([x['prune_iterations'] for x in config_list])) == 1, 'The values of prune_iterations must be equal in your config'
def _calc_sparsity(self, sparsity):
keep_ratio_once = (1 - sparsity) ** (1 / self.prune_iterations)
curr_keep_ratio = keep_ratio_once ** self.curr_prune_iteration
return max(1 - curr_keep_ratio, 0)
def _calc_mask(self, wrapper, sparsity):
weight = wrapper.module.weight.data
if self.curr_prune_iteration == 0:
mask = {'weight_mask': torch.ones(weight.shape).type_as(weight)}
else:
curr_sparsity = self._calc_sparsity(sparsity)
mask = self.masker.calc_mask(sparsity=curr_sparsity, wrapper=wrapper)
return mask
def calc_mask(self, wrapper, **kwargs):
"""
Generate mask for the given ``weight``.
Parameters
----------
wrapper : Module
The layer to be pruned
Returns
-------
tensor
The mask for this weight, it is ```None``` because this pruner
calculates and assigns masks in ```prune_iteration_start```,
no need to do anything in this function.
"""
return None
def get_prune_iterations(self):
"""
Return the range for iterations.
In the first prune iteration, masks are all one, thus, add one more iteration
Returns
-------
list
A list for pruning iterations
"""
return range(self.prune_iterations + 1)
def prune_iteration_start(self):
"""
Control the pruning procedure on updated epoch number.
Should be called at the beginning of the epoch.
"""
if self.curr_prune_iteration is None:
self.curr_prune_iteration = 0
else:
self.curr_prune_iteration += 1
assert self.curr_prune_iteration < self.prune_iterations + 1, 'Exceed the configured prune_iterations'
modules_wrapper = self.get_modules_wrapper()
modules_to_compress = self.get_modules_to_compress()
for layer, config in modules_to_compress:
module_wrapper = None
for wrapper in modules_wrapper:
if wrapper.name == layer.name:
module_wrapper = wrapper
break
assert module_wrapper is not None
sparsity = config.get('sparsity')
mask = self._calc_mask(module_wrapper, sparsity)
# TODO: directly use weight_mask is not good
module_wrapper.weight_mask = mask['weight_mask']
# there is no mask for bias
# reinit weights back to original after new masks are generated
if self.reset_weights:
# should use this member function to reset model weights
self.load_model_state_dict(self._model_state)
self._optimizer.load_state_dict(self._optimizer_state)
if self._lr_scheduler is not None:
self._lr_scheduler.load_state_dict(self._scheduler_state)
================================================
FILE: src/aup/compression/torch/pruning/net_adapt_pruner.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import logging
import os
import copy
import json
import torch
from schema import And, Optional
from ..torch_utils import OptimizeMode
from ..compressor import Pruner
from ..utils.config_validation import CompressorSchema
from ..utils.num_param_counter import get_total_num_weights
from .constants_pruner import PRUNER_DICT
_logger = logging.getLogger(__name__)
class NetAdaptPruner(Pruner):
"""
A Pytorch implementation of NetAdapt compression algorithm.
Parameters
----------
model : pytorch model
The model to be pruned.
config_list : list
Supported keys:
- sparsity : The target overall sparsity.
- op_types : The operation type to prune.
short_term_fine_tuner : function
function to short-term fine tune the masked model.
This function should include `model` as the only parameter,
and fine tune the model for a short term after each pruning iteration.
Example::
def short_term_fine_tuner(model, epoch=3):
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
train_loader = ...
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
model.train()
for _ in range(epoch):
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target)
loss.backward()
optimizer.step()
evaluator : function
function to evaluate the masked model.
This function should include `model` as the only parameter, and returns a scalar value.
Example::
def evaluator(model):
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
val_loader = ...
model.eval()
correct = 0
with torch.no_grad():
for data, target in val_loader:
data, target = data.to(device), target.to(device)
output = model(data)
# get the index of the max log-probability
pred = output.argmax(dim=1, keepdim=True)
correct += pred.eq(target.view_as(pred)).sum().item()
accuracy = correct / len(val_loader.dataset)
return accuracy
optimize_mode : str
optimize mode, `maximize` or `minimize`, by default `maximize`.
base_algo : str
Base pruning algorithm. `level`, `l1`, `l2` or `fpgm`, by default `l1`. Given the sparsity distribution among the ops,
the assigned `base_algo` is used to decide which filters/channels/weights to prune.
sparsity_per_iteration : float
sparsity to prune in each iteration.
experiment_data_dir : str
PATH to save experiment data,
including the config_list generated for the base pruning algorithm and the performance of the pruned model.
"""
def __init__(self, model, config_list, short_term_fine_tuner, evaluator,
optimize_mode='maximize', base_algo='l1', sparsity_per_iteration=0.05, experiment_data_dir='./'):
# models used for iterative pruning and evaluation
self._model_to_prune = copy.deepcopy(model)
self._base_algo = base_algo
super().__init__(model, config_list)
self._short_term_fine_tuner = short_term_fine_tuner
self._evaluator = evaluator
self._optimize_mode = OptimizeMode(optimize_mode)
# hyper parameters for NetAdapt algorithm
self._sparsity_per_iteration = sparsity_per_iteration
# overall pruning rate
self._sparsity = config_list[0]['sparsity']
# config_list
self._config_list_generated = []
self._experiment_data_dir = experiment_data_dir
if not os.path.exists(self._experiment_data_dir):
os.makedirs(self._experiment_data_dir)
self._tmp_model_path = os.path.join(self._experiment_data_dir, 'tmp_model.pth')
def validate_config(self, model, config_list):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
List on pruning configs
"""
if self._base_algo == 'level':
schema = CompressorSchema([{
'sparsity': And(float, lambda n: 0 < n < 1),
Optional('op_types'): [str],
Optional('op_names'): [str],
}], model, _logger)
elif self._base_algo in ['l1', 'l2', 'fpgm']:
schema = CompressorSchema([{
'sparsity': And(float, lambda n: 0 < n < 1),
'op_types': ['Conv2d'],
Optional('op_names'): [str]
}], model, _logger)
schema.validate(config_list)
def calc_mask(self, wrapper, **kwargs):
return None
def _update_config_list(self, config_list, op_name, sparsity):
'''
update sparsity of op_name in config_list
'''
config_list_updated = copy.deepcopy(config_list)
for idx, item in enumerate(config_list):
if op_name in item['op_names']:
config_list_updated[idx]['sparsity'] = sparsity
return config_list_updated
# if op_name is not in self._config_list_generated, create a new json item
if self._base_algo in ['l1', 'l2', 'fpgm']:
config_list_updated.append(
{'sparsity': sparsity, 'op_types': ['Conv2d'], 'op_names': [op_name]})
elif self._base_algo == 'level':
config_list_updated.append(
{'sparsity': sparsity, 'op_names': [op_name]})
return config_list_updated
def _get_op_num_weights_remained(self, op_name, module):
'''
Get the number of weights remained after channel pruning with current sparsity
Returns
-------
int
remained number of weights of the op
'''
# if op is wrapped by the pruner
for wrapper in self.get_modules_wrapper():
if wrapper.name == op_name:
return wrapper.weight_mask.sum().item()
# if op is not wrapped by the pruner
return module.weight.data.numel()
def _get_op_sparsity(self, op_name):
for config in self._config_list_generated:
if 'op_names' in config and op_name in config['op_names']:
return config['sparsity']
return 0
def _calc_num_related_weights(self, op_name):
'''
Calculate total number weights of the op and the next op, applicable only for models without dependencies among ops
Parameters
----------
op_name : str
Returns
-------
int
total number of all the realted (current and the next) op weights
'''
num_weights = 0
flag_found = False
previous_name = None
previous_module = None
for name, module in self._model_to_prune.named_modules():
if not flag_found and name != op_name and type(module).__name__ in ['Conv2d', 'Linear']:
previous_name = name
previous_module = module
if not flag_found and name == op_name:
_logger.debug("original module found: %s", name)
num_weights = module.weight.data.numel()
# consider related pruning in this op caused by previous op's pruning
if previous_module:
sparsity_previous_op = self._get_op_sparsity(previous_name)
if sparsity_previous_op:
_logger.debug(
"decrease op's weights by %s due to previous op %s's pruning...", sparsity_previous_op, previous_name)
num_weights *= (1-sparsity_previous_op)
flag_found = True
continue
if flag_found and type(module).__name__ in ['Conv2d', 'Linear']:
_logger.debug("related module found: %s", name)
# channel/filter pruning crossing is considered here, so only the num_weights after channel pruning is valuable
num_weights += self._get_op_num_weights_remained(name, module)
break
_logger.debug("num related weights of op %s : %d", op_name, num_weights)
return num_weights
def compress(self):
"""
Compress the model.
Returns
-------
torch.nn.Module
model with specified modules compressed.
"""
_logger.info('Starting NetAdapt Compression...')
pruning_iteration = 0
current_sparsity = 0
delta_num_weights_per_iteration = \
int(get_total_num_weights(self._model_to_prune, ['Conv2d', 'Linear']) * self._sparsity_per_iteration)
# stop condition
while current_sparsity < self._sparsity:
_logger.info('Pruning iteration: %d', pruning_iteration)
# calculate target sparsity of this iteration
target_sparsity = current_sparsity + self._sparsity_per_iteration
# variable to store the info of the best layer found in this iteration
best_op = {}
for wrapper in self.get_modules_wrapper():
_logger.debug("op name : %s", wrapper.name)
_logger.debug("op weights : %d", wrapper.weight_mask.numel())
_logger.debug("op left weights : %d", wrapper.weight_mask.sum().item())
current_op_sparsity = 1 - wrapper.weight_mask.sum().item() / wrapper.weight_mask.numel()
_logger.debug("current op sparsity : %s", current_op_sparsity)
# sparsity that this layer needs to prune to satisfy the requirement
target_op_sparsity = current_op_sparsity + delta_num_weights_per_iteration / self._calc_num_related_weights(wrapper.name)
if target_op_sparsity >= 1:
_logger.info('Layer %s has no enough weights (remained) to prune', wrapper.name)
continue
config_list = self._update_config_list(self._config_list_generated, wrapper.name, target_op_sparsity)
_logger.debug("config_list used : %s", config_list)
pruner = PRUNER_DICT[self._base_algo](copy.deepcopy(self._model_to_prune), config_list)
model_masked = pruner.compress()
# Short-term fine tune the pruned model
self._short_term_fine_tuner(model_masked)
performance = self._evaluator(model_masked)
_logger.info("Layer : %s, evaluation result after short-term fine tuning : %s", wrapper.name, performance)
if not best_op \
or (self._optimize_mode is OptimizeMode.Maximize and performance > best_op['performance']) \
or (self._optimize_mode is OptimizeMode.Minimize and performance < best_op['performance']):
_logger.debug("updating best layer to %s...", wrapper.name)
# find weight mask of this layer
for w in pruner.get_modules_wrapper():
if w.name == wrapper.name:
masks = {'weight_mask': w.weight_mask,
'bias_mask': w.bias_mask}
break
best_op = {
'op_name': wrapper.name,
'sparsity': target_op_sparsity,
'performance': performance,
'masks': masks
}
# save model weights
pruner.export_model(self._tmp_model_path)
if not best_op:
# decrease pruning step
self._sparsity_per_iteration *= 0.5
_logger.info("No more layers to prune, decrease pruning step to %s", self._sparsity_per_iteration)
continue
# Pick the best layer to prune, update iterative information
# update config_list
self._config_list_generated = self._update_config_list(
self._config_list_generated, best_op['op_name'], best_op['sparsity'])
# update weights parameters
self._model_to_prune.load_state_dict(torch.load(self._tmp_model_path))
# update mask of the chosen op
for wrapper in self.get_modules_wrapper():
if wrapper.name == best_op['op_name']:
for k in best_op['masks']:
setattr(wrapper, k, best_op['masks'][k])
break
current_sparsity = target_sparsity
_logger.info('Pruning iteration %d finished, current sparsity: %s', pruning_iteration, current_sparsity)
_logger.info('Layer %s seleted with sparsity %s, performance after pruning & short term fine-tuning : %s',
best_op['op_name'], best_op['sparsity'], best_op['performance'])
pruning_iteration += 1
self._final_performance = best_op['performance']
# load weights parameters
self.load_model_state_dict(torch.load(self._tmp_model_path))
os.remove(self._tmp_model_path)
_logger.info('----------Compression finished--------------')
_logger.info('config_list generated: %s', self._config_list_generated)
_logger.info("Performance after pruning: %s", self._final_performance)
_logger.info("Masked sparsity: %.6f", current_sparsity)
# save best config found and best performance
with open(os.path.join(self._experiment_data_dir, 'search_result.json'), 'w') as jsonfile:
json.dump({
'performance': self._final_performance,
'config_list': json.dumps(self._config_list_generated)
}, jsonfile)
_logger.info('search history and result saved to foler : %s', self._experiment_data_dir)
return self.bound_model
================================================
FILE: src/aup/compression/torch/pruning/one_shot.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import logging
from schema import And, Optional, SchemaError
from .._graph_utils import TorchModuleGraph
from ..utils.shape_dependency import ChannelDependency, GroupDependency
from .constants import MASKER_DICT
from ..utils.config_validation import CompressorSchema
from ..compressor import Pruner
__all__ = ['LevelPruner', 'SlimPruner', 'L1FilterPruner', 'L2FilterPruner', 'FPGMPruner',
'TaylorFOWeightFilterPruner', 'ActivationAPoZRankFilterPruner', 'ActivationMeanRankFilterPruner']
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
class OneshotPruner(Pruner):
"""
Prune model to an exact pruning level for one time.
"""
def __init__(self, model, config_list, pruning_algorithm='level', optimizer=None, **algo_kwargs):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
List on pruning configs
pruning_algorithm: str
algorithms being used to prune model
optimizer: torch.optim.Optimizer
Optimizer used to train model
algo_kwargs: dict
Additional parameters passed to pruning algorithm masker class
"""
super().__init__(model, config_list, optimizer)
self.set_wrappers_attribute("if_calculated", False)
self.masker = MASKER_DICT[pruning_algorithm](
model, self, **algo_kwargs)
def validate_config(self, model, config_list):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
List on pruning configs
"""
schema = CompressorSchema([{
'sparsity': And(float, lambda n: 0 < n < 1),
Optional('op_types'): [str],
Optional('op_names'): [str]
}], model, logger)
schema.validate(config_list)
def calc_mask(self, wrapper, wrapper_idx=None):
"""
Calculate the mask of given layer
Parameters
----------
wrapper : Module
the module to instrument the compression operation
wrapper_idx: int
index of this wrapper in pruner's all wrappers
Returns
-------
dict
dictionary for storing masks, keys of the dict:
'weight_mask': weight mask tensor
'bias_mask': bias mask tensor (optional)
"""
if wrapper.if_calculated:
return None
sparsity = wrapper.config['sparsity']
if not wrapper.if_calculated:
masks = self.masker.calc_mask(
sparsity=sparsity, wrapper=wrapper, wrapper_idx=wrapper_idx)
# masker.calc_mask returns None means calc_mask is not calculated sucessfully, can try later
if masks is not None:
wrapper.if_calculated = True
return masks
else:
return None
class LevelPruner(OneshotPruner):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
Supported keys:
- sparsity : This is to specify the sparsity operations to be compressed to.
- op_types : Operation types to prune.
optimizer: torch.optim.Optimizer
Optimizer used to train model
"""
def __init__(self, model, config_list, optimizer=None):
super().__init__(model, config_list, pruning_algorithm='level', optimizer=optimizer)
class SlimPruner(OneshotPruner):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
Supported keys:
- sparsity : This is to specify the sparsity operations to be compressed to.
- op_types : Only BatchNorm2d is supported in Slim Pruner.
optimizer: torch.optim.Optimizer
Optimizer used to train model
"""
def __init__(self, model, config_list, optimizer=None):
super().__init__(model, config_list, pruning_algorithm='slim', optimizer=optimizer)
def validate_config(self, model, config_list):
schema = CompressorSchema([{
'sparsity': And(float, lambda n: 0 < n < 1),
'op_types': ['BatchNorm2d'],
Optional('op_names'): [str]
}], model, logger)
schema.validate(config_list)
if len(config_list) > 1:
logger.warning('Slim pruner only supports 1 configuration')
class _StructuredFilterPruner(OneshotPruner):
"""
_StructuredFilterPruner has two ways to calculate the masks
for conv layers. In the normal way, the _StructuredFilterPruner
will calculate the mask of each layer separately. For example, each
conv layer determine which filters should be pruned according to its L1
norm. In constrast, in the dependency-aware way, the layers that in a
dependency group will be pruned jointly and these layers will be forced
to prune the same channels.
"""
def __init__(self, model, config_list, pruning_algorithm, optimizer=None, dependency_aware=False, dummy_input=None, **algo_kwargs):
super().__init__(model, config_list, pruning_algorithm=pruning_algorithm,
optimizer=optimizer, **algo_kwargs)
self.dependency_aware = dependency_aware
# set the dependency-aware switch for the masker
self.masker.dependency_aware = dependency_aware
self.dummy_input = dummy_input
if self.dependency_aware:
errmsg = "When dependency_aware is set, the dummy_input should not be None"
assert self.dummy_input is not None, errmsg
# Get the TorchModuleGraph of the target model
# to trace the model, we need to unwrap the wrappers
self._unwrap_model()
self.graph = TorchModuleGraph(model, dummy_input)
self._wrap_model()
self.channel_depen = ChannelDependency(
traced_model=self.graph.trace)
self.group_depen = GroupDependency(traced_model=self.graph.trace)
self.channel_depen = self.channel_depen.dependency_sets
self.channel_depen = {
name: sets for sets in self.channel_depen for name in sets}
self.group_depen = self.group_depen.dependency_sets
def update_mask(self):
if not self.dependency_aware:
# if we use the normal way to update the mask,
# then call the update_mask of the father class
super(_StructuredFilterPruner, self).update_mask()
else:
# if we update the mask in a dependency-aware way
# then we call _dependency_update_mask
self._dependency_update_mask()
def validate_config(self, model, config_list):
schema = CompressorSchema([{
Optional('sparsity'): And(float, lambda n: 0 < n < 1),
Optional('op_types'): ['Conv2d'],
Optional('op_names'): [str],
Optional('exclude'): bool
}], model, logger)
schema.validate(config_list)
for config in config_list:
if 'exclude' not in config and 'sparsity' not in config:
raise SchemaError('Either sparisty or exclude must be specified!')
def _dependency_calc_mask(self, wrappers, channel_dsets, wrappers_idx=None):
"""
calculate the masks for the conv layers in the same
channel dependecy set. All the layers passed in have
the same number of channels.
Parameters
----------
wrappers: list
The list of the wrappers that in the same channel dependency
set.
wrappers_idx: list
The list of the indexes of wrapppers.
Returns
-------
masks: dict
A dict object that contains the masks of the layers in this
dependency group, the key is the name of the convolutional layers.
"""
# The number of the groups for each conv layers
# Note that, this number may be different from its
# original number of groups of filters.
groups = [self.group_depen[_w.name] for _w in wrappers]
sparsities = [_w.config['sparsity'] for _w in wrappers]
masks = self.masker.calc_mask(
sparsities, wrappers, wrappers_idx, channel_dsets=channel_dsets, groups=groups)
if masks is not None:
# if masks is None, then the mask calculation fails.
# for example, in activation related maskers, we should
# pass enough batches of data to the model, so that the
# masks can be calculated successfully.
for _w in wrappers:
_w.if_calculated = True
return masks
def _dependency_update_mask(self):
"""
In the original update_mask, the wraper of each layer will update its
own mask according to the sparsity specified in the config_list. However, in
the _dependency_update_mask, we may prune several layers at the same
time according the sparsities and the channel/group dependencies.
"""
name2wrapper = {x.name: x for x in self.get_modules_wrapper()}
wrapper2index = {x: i for i, x in enumerate(self.get_modules_wrapper())}
for wrapper in self.get_modules_wrapper():
if wrapper.if_calculated:
continue
# find all the conv layers that have channel dependecy with this layer
# and prune all these layers at the same time.
_names = [x for x in self.channel_depen[wrapper.name]]
logger.info('Pruning the dependent layers: %s', ','.join(_names))
_wrappers = [name2wrapper[name]
for name in _names if name in name2wrapper]
_wrapper_idxes = [wrapper2index[_w] for _w in _wrappers]
masks = self._dependency_calc_mask(
_wrappers, _names, wrappers_idx=_wrapper_idxes)
if masks is not None:
for layer in masks:
for mask_type in masks[layer]:
assert hasattr(
name2wrapper[layer], mask_type), "there is no attribute '%s' in wrapper on %s" % (mask_type, layer)
setattr(name2wrapper[layer], mask_type, masks[layer][mask_type])
class L1FilterPruner(_StructuredFilterPruner):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
Supported keys:
- sparsity : This is to specify the sparsity operations to be compressed to.
- op_types : Only Conv2d is supported in L1FilterPruner.
optimizer: torch.optim.Optimizer
Optimizer used to train model
dependency_aware: bool
If prune the model in a dependency-aware way. If it is `True`, this pruner will
prune the model according to the l2-norm of weights and the channel-dependency or
group-dependency of the model. In this way, the pruner will force the conv layers
that have dependencies to prune the same channels, so the speedup module can better
harvest the speed benefit from the pruned model. Note that, if this flag is set True
, the dummy_input cannot be None, because the pruner needs a dummy input to trace the
dependency between the conv layers.
dummy_input : torch.Tensor
The dummy input to analyze the topology constraints. Note that, the dummy_input
should on the same device with the model.
"""
def __init__(self, model, config_list, optimizer=None, dependency_aware=False, dummy_input=None):
super().__init__(model, config_list, pruning_algorithm='l1', optimizer=optimizer,
dependency_aware=dependency_aware, dummy_input=dummy_input)
class L2FilterPruner(_StructuredFilterPruner):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
Supported keys:
- sparsity : This is to specify the sparsity operations to be compressed to.
- op_types : Only Conv2d is supported in L2FilterPruner.
optimizer: torch.optim.Optimizer
Optimizer used to train model
dependency_aware: bool
If prune the model in a dependency-aware way. If it is `True`, this pruner will
prune the model according to the l2-norm of weights and the channel-dependency or
group-dependency of the model. In this way, the pruner will force the conv layers
that have dependencies to prune the same channels, so the speedup module can better
harvest the speed benefit from the pruned model. Note that, if this flag is set True
, the dummy_input cannot be None, because the pruner needs a dummy input to trace the
dependency between the conv layers.
dummy_input : torch.Tensor
The dummy input to analyze the topology constraints. Note that, the dummy_input
should on the same device with the model.
"""
def __init__(self, model, config_list, optimizer=None, dependency_aware=False, dummy_input=None):
super().__init__(model, config_list, pruning_algorithm='l2', optimizer=optimizer,
dependency_aware=dependency_aware, dummy_input=dummy_input)
class FPGMPruner(_StructuredFilterPruner):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
Supported keys:
- sparsity : This is to specify the sparsity operations to be compressed to.
- op_types : Only Conv2d is supported in FPGM Pruner.
optimizer: torch.optim.Optimizer
Optimizer used to train model
dependency_aware: bool
If prune the model in a dependency-aware way. If it is `True`, this pruner will
prune the model according to the l2-norm of weights and the channel-dependency or
group-dependency of the model. In this way, the pruner will force the conv layers
that have dependencies to prune the same channels, so the speedup module can better
harvest the speed benefit from the pruned model. Note that, if this flag is set True
, the dummy_input cannot be None, because the pruner needs a dummy input to trace the
dependency between the conv layers.
dummy_input : torch.Tensor
The dummy input to analyze the topology constraints. Note that, the dummy_input
should on the same device with the model.
"""
def __init__(self, model, config_list, optimizer=None, dependency_aware=False, dummy_input=None):
super().__init__(model, config_list, pruning_algorithm='fpgm',
dependency_aware=dependency_aware, dummy_input=dummy_input, optimizer=optimizer)
class TaylorFOWeightFilterPruner(_StructuredFilterPruner):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
Supported keys:
- sparsity : How much percentage of convolutional filters are to be pruned.
- op_types : Currently only Conv2d is supported in TaylorFOWeightFilterPruner.
optimizer: torch.optim.Optimizer
Optimizer used to train model
statistics_batch_num: int
The number of batches to statistic the activation.
dependency_aware: bool
If prune the model in a dependency-aware way. If it is `True`, this pruner will
prune the model according to the l2-norm of weights and the channel-dependency or
group-dependency of the model. In this way, the pruner will force the conv layers
that have dependencies to prune the same channels, so the speedup module can better
harvest the speed benefit from the pruned model. Note that, if this flag is set True
, the dummy_input cannot be None, because the pruner needs a dummy input to trace the
dependency between the conv layers.
dummy_input : torch.Tensor
The dummy input to analyze the topology constraints. Note that, the dummy_input
should on the same device with the model.
"""
def __init__(self, model, config_list, optimizer=None, statistics_batch_num=1,
dependency_aware=False, dummy_input=None):
super().__init__(model, config_list, pruning_algorithm='taylorfo',
dependency_aware=dependency_aware, dummy_input=dummy_input,
optimizer=optimizer, statistics_batch_num=statistics_batch_num)
class ActivationAPoZRankFilterPruner(_StructuredFilterPruner):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
Supported keys:
- sparsity : How much percentage of convolutional filters are to be pruned.
- op_types : Only Conv2d is supported in ActivationAPoZRankFilterPruner.
optimizer: torch.optim.Optimizer
Optimizer used to train model
activation: str
The activation type.
statistics_batch_num: int
The number of batches to statistic the activation.
dependency_aware: bool
If prune the model in a dependency-aware way. If it is `True`, this pruner will
prune the model according to the l2-norm of weights and the channel-dependency or
group-dependency of the model. In this way, the pruner will force the conv layers
that have dependencies to prune the same channels, so the speedup module can better
harvest the speed benefit from the pruned model. Note that, if this flag is set True
, the dummy_input cannot be None, because the pruner needs a dummy input to trace the
dependency between the conv layers.
dummy_input : torch.Tensor
The dummy input to analyze the topology constraints. Note that, the dummy_input
should on the same device with the model.
"""
def __init__(self, model, config_list, optimizer=None, activation='relu',
statistics_batch_num=1, dependency_aware=False, dummy_input=None):
super().__init__(model, config_list, pruning_algorithm='apoz', optimizer=optimizer,
dependency_aware=dependency_aware, dummy_input=dummy_input,
activation=activation, statistics_batch_num=statistics_batch_num)
class ActivationMeanRankFilterPruner(_StructuredFilterPruner):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
Supported keys:
- sparsity : How much percentage of convolutional filters are to be pruned.
- op_types : Only Conv2d is supported in ActivationMeanRankFilterPruner.
optimizer: torch.optim.Optimizer
Optimizer used to train model.
activation: str
The activation type.
statistics_batch_num: int
The number of batches to statistic the activation.
dependency_aware: bool
If prune the model in a dependency-aware way. If it is `True`, this pruner will
prune the model according to the l2-norm of weights and the channel-dependency or
group-dependency of the model. In this way, the pruner will force the conv layers
that have dependencies to prune the same channels, so the speedup module can better
harvest the speed benefit from the pruned model. Note that, if this flag is set True
, the dummy_input cannot be None, because the pruner needs a dummy input to trace the
dependency between the conv layers.
dummy_input : torch.Tensor
The dummy input to analyze the topology constraints. Note that, the dummy_input
should on the same device with the model.
"""
def __init__(self, model, config_list, optimizer=None, activation='relu',
statistics_batch_num=1, dependency_aware=False, dummy_input=None):
super().__init__(model, config_list, pruning_algorithm='mean_activation', optimizer=optimizer,
dependency_aware=dependency_aware, dummy_input=dummy_input,
activation=activation, statistics_batch_num=statistics_batch_num)
================================================
FILE: src/aup/compression/torch/pruning/sensitivity_pruner.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import os
import csv
import copy
import json
import logging
import torch
from schema import And, Optional
from ..compressor import Pruner
from ..utils.config_validation import CompressorSchema
from .constants_pruner import PRUNER_DICT
from ..utils.sensitivity_analysis import SensitivityAnalysis
MAX_PRUNE_RATIO_PER_ITER = 0.95
_logger = logging.getLogger('Sensitivity_Pruner')
_logger.setLevel(logging.INFO)
class SensitivityPruner(Pruner):
"""
This function prune the model based on the sensitivity
for each layer.
Parameters
----------
model: torch.nn.Module
model to be compressed
evaluator: function
validation function for the model. This function should return the accuracy
of the validation dataset. The input parameters of evaluator can be specified
in the parameter `eval_args` and 'eval_kwargs' of the compress function if needed.
Example:
>>> def evaluator(model):
>>> device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
>>> val_loader = ...
>>> model.eval()
>>> correct = 0
>>> with torch.no_grad():
>>> for data, target in val_loader:
>>> data, target = data.to(device), target.to(device)
>>> output = model(data)
>>> # get the index of the max log-probability
>>> pred = output.argmax(dim=1, keepdim=True)
>>> correct += pred.eq(target.view_as(pred)).sum().item()
>>> accuracy = correct / len(val_loader.dataset)
>>> return accuracy
finetuner: function
finetune function for the model. This parameter is not essential, if is not None,
the sensitivity pruner will finetune the model after pruning in each iteration.
The input parameters of finetuner can be specified in the parameter of compress
called `finetune_args` and `finetune_kwargs` if needed.
Example:
>>> def finetuner(model, epoch=3):
>>> device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
>>> train_loader = ...
>>> criterion = torch.nn.CrossEntropyLoss()
>>> optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
>>> model.train()
>>> for _ in range(epoch):
>>> for _, (data, target) in enumerate(train_loader):
>>> data, target = data.to(device), target.to(device)
>>> optimizer.zero_grad()
>>> output = model(data)
>>> loss = criterion(output, target)
>>> loss.backward()
>>> optimizer.step()
base_algo: str
base pruning algorithm. `level`, `l1`, `l2` or `fpgm`, by default `l1`.
sparsity_proportion_calc: function
This function generate the sparsity proportion between the conv layers according to the
sensitivity analysis results. We provide a default function to quantify the sparsity
proportion according to the sensitivity analysis results. Users can also customize
this function according to their needs. The input of this function is a dict,
for example : {'conv1' : {0.1: 0.9, 0.2 : 0.8}, 'conv2' : {0.1: 0.9, 0.2 : 0.8}},
in which, 'conv1' and is the name of the conv layer, and 0.1:0.9 means when the
sparsity of conv1 is 0.1 (10%), the model's val accuracy equals to 0.9.
sparsity_per_iter: float
The sparsity of the model that the pruner try to prune in each iteration.
acc_drop_threshold : float
The hyperparameter used to quantifiy the sensitivity for each layer.
checkpoint_dir: str
The dir path to save the checkpoints during the pruning.
"""
def __init__(self, model, config_list, evaluator,
finetuner=None, base_algo='l1', sparsity_proportion_calc=None,
sparsity_per_iter=0.1, acc_drop_threshold=0.05, checkpoint_dir=None):
self.base_algo = base_algo
self.model = model
super(SensitivityPruner, self).__init__(model, config_list)
# unwrap the model
self._unwrap_model()
_logger.debug(str(self.model))
self.evaluator = evaluator
self.finetuner = finetuner
self.analyzer = SensitivityAnalysis(
self.model, self.evaluator, prune_type=base_algo, \
early_stop_mode='dropped', early_stop_value=acc_drop_threshold)
# Get the original accuracy of the pretrained model
self.ori_acc = None
# Copy the original weights before pruning
self.ori_state_dict = copy.deepcopy(self.model.state_dict())
self.sensitivities = {}
# Save the weight count for each layer
self.weight_count = {}
self.weight_sum = 0
# Map the layer name to the layer module
self.named_module = {}
self.Pruner = PRUNER_DICT[self.base_algo]
# Count the total weight count of the model
for name, submodule in self.model.named_modules():
self.named_module[name] = submodule
if name in self.analyzer.target_layer:
# Currently, only count the weights in the conv layers
# else the fully connected layer (which contains
# the most weights) may make the pruner prune the
# model too hard
# if hasattr(submodule, 'weight'): # Count all the weights of the model
self.weight_count[name] = submodule.weight.data.numel()
self.weight_sum += self.weight_count[name]
# function to generate the sparsity proportion between the conv layers
if sparsity_proportion_calc is None:
self.sparsity_proportion_calc = self._max_prune_ratio
else:
self.sparsity_proportion_calc = sparsity_proportion_calc
# The ratio of remained weights is 1.0 at the begining
self.remained_ratio = 1.0
self.sparsity_per_iter = sparsity_per_iter
self.acc_drop_threshold = acc_drop_threshold
self.checkpoint_dir = checkpoint_dir
def validate_config(self, model, config_list):
"""
Parameters
----------
model : torch.nn.module
Model to be pruned
config_list : list
List on pruning configs
"""
if self.base_algo == 'level':
schema = CompressorSchema([{
'sparsity': And(float, lambda n: 0 < n < 1),
Optional('op_types'): [str],
Optional('op_names'): [str],
}], model, _logger)
elif self.base_algo in ['l1', 'l2', 'fpgm']:
schema = CompressorSchema([{
'sparsity': And(float, lambda n: 0 < n < 1),
'op_types': ['Conv2d'],
Optional('op_names'): [str]
}], model, _logger)
schema.validate(config_list)
def load_sensitivity(self, filepath):
"""
load the sensitivity results exported by the sensitivity analyzer
"""
assert os.path.exists(filepath)
with open(filepath, 'r') as csvf:
csv_r = csv.reader(csvf)
header = next(csv_r)
sparsities = [float(x) for x in header[1:]]
sensitivities = {}
for row in csv_r:
layername = row[0]
accuracies = [float(x) for x in row[1:]]
sensitivities[layername] = {}
for i, accuracy in enumerate(accuracies):
sensitivities[layername][sparsities[i]] = accuracy
return sensitivities
def _max_prune_ratio(self, ori_acc, threshold, sensitivities):
"""
Find the maximum prune ratio for a single layer whose accuracy
drop is lower than the threshold.
Parameters
----------
ori_acc: float
Original accuracy
threshold: float
Accuracy drop threshold
sensitivities: dict
The dict object that stores the sensitivity results for each layer.
For example: {'conv1' : {0.1: 0.9, 0.2 : 0.8}}
Returns
-------
max_ratios: dict
return the maximum prune ratio for each layer. For example:
{'conv1':0.1, 'conv2':0.2}
"""
max_ratio = {}
for layer in sensitivities:
prune_ratios = sorted(sensitivities[layer].keys())
last_ratio = 0
for ratio in prune_ratios:
last_ratio = ratio
cur_acc = sensitivities[layer][ratio]
if cur_acc + threshold < ori_acc:
break
max_ratio[layer] = last_ratio
return max_ratio
def normalize(self, ratios, target_pruned):
"""
Normalize the prune ratio of each layer according to the
total already pruned ratio and the final target total pruning
ratio
Parameters
----------
ratios:
Dict object that save the prune ratio for each layer
target_pruned:
The amount of the weights expected to be pruned in this
iteration
Returns
-------
new_ratios:
return the normalized prune ratios for each layer.
"""
w_sum = 0
_Max = 0
for layername, ratio in ratios.items():
wcount = self.weight_count[layername]
w_sum += ratio * wcount * \
(1-self.analyzer.already_pruned[layername])
target_count = self.weight_sum * target_pruned
for layername in ratios:
ratios[layername] = ratios[layername] * target_count / w_sum
_Max = max(_Max, ratios[layername])
# Cannot Prune too much in a single iteration
# If a layer's prune ratio is larger than the
# MAX_PRUNE_RATIO_PER_ITER we rescal all prune
# ratios under this threshold
if _Max > MAX_PRUNE_RATIO_PER_ITER:
for layername in ratios:
ratios[layername] = ratios[layername] * \
MAX_PRUNE_RATIO_PER_ITER / _Max
return ratios
def create_cfg(self, ratios):
"""
Generate the cfg_list for the pruner according to the prune ratios.
Parameters
---------
ratios:
For example: {'conv1' : 0.2}
Returns
-------
cfg_list:
For example: [{'sparsity':0.2, 'op_names':['conv1'], 'op_types':['Conv2d']}]
"""
cfg_list = []
for layername in ratios:
prune_ratio = ratios[layername]
remain = 1 - self.analyzer.already_pruned[layername]
sparsity = remain * prune_ratio + \
self.analyzer.already_pruned[layername]
if sparsity > 0:
# Pruner does not allow the prune ratio to be zero
cfg = {'sparsity': sparsity, 'op_names': [
layername], 'op_types': ['Conv2d']}
cfg_list.append(cfg)
return cfg_list
def current_sparsity(self):
"""
The sparsity of the weight.
"""
pruned_weight = 0
for layer_name in self.analyzer.already_pruned:
w_count = self.weight_count[layer_name]
prune_ratio = self.analyzer.already_pruned[layer_name]
pruned_weight += w_count * prune_ratio
return pruned_weight / self.weight_sum
def compress(self, eval_args=None, eval_kwargs=None,
finetune_args=None, finetune_kwargs=None, resume_sensitivity=None):
"""
This function iteratively prune the model according to the results of
the sensitivity analysis.
Parameters
----------
eval_args: list
eval_kwargs: list& dict
Parameters for the val_funtion, the val_function will be called like
evaluator(*eval_args, **eval_kwargs)
finetune_args: list
finetune_kwargs: dict
Parameters for the finetuner function if needed.
resume_sensitivity:
resume the sensitivity results from this file.
"""
# pylint suggest not use the empty list and dict
# as the default input parameter
if not eval_args:
eval_args = []
if not eval_kwargs:
eval_kwargs = {}
if not finetune_args:
finetune_args = []
if not finetune_kwargs:
finetune_kwargs = {}
if self.ori_acc is None:
self.ori_acc = self.evaluator(*eval_args, **eval_kwargs)
assert isinstance(self.ori_acc, float) or isinstance(self.ori_acc, int)
if not resume_sensitivity:
self.sensitivities = self.analyzer.analysis(
val_args=eval_args, val_kwargs=eval_kwargs)
else:
self.sensitivities = self.load_sensitivity(resume_sensitivity)
self.analyzer.sensitivities = self.sensitivities
# the final target sparsity of the model
target_ratio = 1 - self.config_list[0]['sparsity']
cur_ratio = self.remained_ratio
ori_acc = self.ori_acc
iteration_count = 0
if self.checkpoint_dir is not None:
os.makedirs(self.checkpoint_dir, exist_ok=True)
modules_wrapper_final = None
while cur_ratio > target_ratio:
iteration_count += 1
# Each round have three steps:
# 1) Get the current sensitivity for each layer(the sensitivity
# of each layer may change during the pruning)
# 2) Prune each layer according the sensitivies
# 3) finetune the model
_logger.info('Current base accuracy %f', ori_acc)
_logger.info('Remained %f weights', cur_ratio)
# determine the sparsity proportion between different
# layers according to the sensitivity result
proportion = self.sparsity_proportion_calc(
ori_acc, self.acc_drop_threshold, self.sensitivities)
new_pruneratio = self.normalize(proportion, self.sparsity_per_iter)
cfg_list = self.create_cfg(new_pruneratio)
if not cfg_list:
_logger.error('The threshold is too small, please set a larger threshold')
return self.model
_logger.debug('Pruner Config: %s', str(cfg_list))
cfg_str = ['%s:%.3f'%(cfg['op_names'][0], cfg['sparsity']) for cfg in cfg_list]
_logger.info('Current Sparsities: %s', ','.join(cfg_str))
pruner = self.Pruner(self.model, cfg_list)
pruner.compress()
pruned_acc = self.evaluator(*eval_args, **eval_kwargs)
_logger.info('Accuracy after pruning: %f', pruned_acc)
finetune_acc = pruned_acc
if self.finetuner is not None:
# if the finetune function is None, then skip the finetune
self.finetuner(*finetune_args, **finetune_kwargs)
finetune_acc = self.evaluator(*eval_args, **eval_kwargs)
_logger.info('Accuracy after finetune: %f', finetune_acc)
ori_acc = finetune_acc
# unwrap the pruner
pruner._unwrap_model()
# update the already prune ratio of each layer befor the new
# sensitivity analysis
for layer_cfg in cfg_list:
name = layer_cfg['op_names'][0]
sparsity = layer_cfg['sparsity']
self.analyzer.already_pruned[name] = sparsity
# update the cur_ratio
cur_ratio = 1 - self.current_sparsity()
modules_wrapper_final = pruner.get_modules_wrapper()
del pruner
_logger.info('Currently remained weights: %f', cur_ratio)
if self.checkpoint_dir is not None:
checkpoint_name = 'Iter_%d_finetune_acc_%.5f_sparsity_%.4f' % (
iteration_count, finetune_acc, cur_ratio)
checkpoint_path = os.path.join(
self.checkpoint_dir, '%s.pth' % checkpoint_name)
cfg_path = os.path.join(
self.checkpoint_dir, '%s_pruner.json' % checkpoint_name)
sensitivity_path = os.path.join(
self.checkpoint_dir, '%s_sensitivity.csv' % checkpoint_name)
torch.save(self.model.state_dict(), checkpoint_path)
with open(cfg_path, 'w') as jf:
json.dump(cfg_list, jf)
self.analyzer.export(sensitivity_path)
if cur_ratio > target_ratio:
# If this is the last prune iteration, skip the time-consuming
# sensitivity analysis
self.analyzer.load_state_dict(self.model.state_dict())
self.sensitivities = self.analyzer.analysis(
val_args=eval_args, val_kwargs=eval_kwargs)
_logger.info('After Pruning: %.2f weights remains', cur_ratio)
self.modules_wrapper = modules_wrapper_final
self._wrap_model()
return self.model
def calc_mask(self, wrapper, **kwargs):
return None
================================================
FILE: src/aup/compression/torch/pruning/simulated_annealing_pruner.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import logging
import os
import math
import copy
import csv
import json
import numpy as np
from schema import And, Optional
from ..torch_utils import OptimizeMode
from ..compressor import Pruner
from ..utils.config_validation import CompressorSchema
from .constants_pruner import PRUNER_DICT
_logger = logging.getLogger(__name__)
class SimulatedAnnealingPruner(Pruner):
"""
A Pytorch implementation of Simulated Annealing compression algorithm.
Parameters
----------
model : pytorch model
The model to be pruned.
config_list : list
Supported keys:
- sparsity : The target overall sparsity.
- op_types : The operation type to prune.
evaluator : function
Function to evaluate the pruned model.
This function should include `model` as the only parameter, and returns a scalar value.
Example::
def evaluator(model):
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
val_loader = ...
model.eval()
correct = 0
with torch.no_grad():
for data, target in val_loader:
data, target = data.to(device), target.to(device)
output = model(data)
# get the index of the max log-probability
pred = output.argmax(dim=1, keepdim=True)
correct += pred.eq(target.view_as(pred)).sum().item()
accuracy = correct / len(val_loader.dataset)
return accuracy
optimize_mode : str
Optimize mode, `maximize` or `minimize`, by default `maximize`.
base_algo : str
Base pruning algorithm. `level`, `l1`, `l2` or `fpgm`, by default `l1`. Given the sparsity distribution among the ops,
the assigned `base_algo` is used to decide which filters/channels/weights to prune.
start_temperature : float
Start temperature of the simulated annealing process.
stop_temperature : float
Stop temperature of the simulated annealing process.
cool_down_rate : float
Cool down rate of the temperature.
perturbation_magnitude : float
Initial perturbation magnitude to the sparsities. The magnitude decreases with current temperature.
experiment_data_dir : string
PATH to save experiment data,
including the config_list generated for the base pruning algorithm, the performance of the pruned model and the pruning history.
"""
def __init__(self, model, config_list, evaluator, optimize_mode='maximize', base_algo='l1',
start_temperature=100, stop_temperature=20, cool_down_rate=0.9, perturbation_magnitude=0.35, experiment_data_dir='./'):
# original model
self._model_to_prune = copy.deepcopy(model)
self._base_algo = base_algo
super().__init__(model, config_list)
self._evaluator = evaluator
self._optimize_mode = OptimizeMode(optimize_mode)
# hyper parameters for SA algorithm
self._start_temperature = start_temperature
self._current_temperature = start_temperature
self._stop_temperature = stop_temperature
self._cool_down_rate = cool_down_rate
self._perturbation_magnitude = perturbation_magnitude
# overall pruning rate
self._sparsity = config_list[0]['sparsity']
# pruning rates of the layers
self._sparsities = None
# init current performance & best performance
self._current_performance = -np.inf
self._best_performance = -np.inf
self._best_config_list = []
self._search_history = []
self._experiment_data_dir = experiment_data_dir
if not os.path.exists(self._experiment_data_dir):
os.makedirs(self._experiment_data_dir)
def validate_config(self, model, config_list):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list
List on pruning configs
"""
if self._base_algo == 'level':
schema = CompressorSchema([{
'sparsity': And(float, lambda n: 0 < n < 1),
Optional('op_types'): [str],
Optional('op_names'): [str],
}], model, _logger)
elif self._base_algo in ['l1', 'l2', 'fpgm']:
schema = CompressorSchema([{
'sparsity': And(float, lambda n: 0 < n < 1),
'op_types': ['Conv2d'],
Optional('op_names'): [str]
}], model, _logger)
schema.validate(config_list)
def _sparsities_2_config_list(self, sparsities):
'''
convert sparsities vector into config_list for LevelPruner or L1FilterPruner
Parameters
----------
sparsities : list
list of sparsities
Returns
-------
list of dict
config_list for LevelPruner or L1FilterPruner
'''
config_list = []
sparsities = sorted(sparsities)
self.modules_wrapper = sorted(
self.modules_wrapper, key=lambda wrapper: wrapper.module.weight.data.numel())
# a layer with more weights will have no less pruning rate
for idx, wrapper in enumerate(self.get_modules_wrapper()):
# L1Filter Pruner requires to specify op_types
if self._base_algo in ['l1', 'l2', 'fpgm']:
config_list.append(
{'sparsity': sparsities[idx], 'op_types': ['Conv2d'], 'op_names': [wrapper.name]})
elif self._base_algo == 'level':
config_list.append(
{'sparsity': sparsities[idx], 'op_names': [wrapper.name]})
config_list = [val for val in config_list if not math.isclose(val['sparsity'], 0, abs_tol=1e-6)]
return config_list
def _rescale_sparsities(self, sparsities, target_sparsity):
'''
Rescale the sparsities list to satisfy the target overall sparsity
Parameters
----------
sparsities : list
target_sparsity : float
the target overall sparsity
Returns
-------
list
the rescaled sparsities
'''
num_weights = []
for wrapper in self.get_modules_wrapper():
num_weights.append(wrapper.module.weight.data.numel())
num_weights = sorted(num_weights)
sparsities = sorted(sparsities)
total_weights = 0
total_weights_pruned = 0
# calculate the scale
for idx, num_weight in enumerate(num_weights):
total_weights += num_weight
total_weights_pruned += int(num_weight*sparsities[idx])
if total_weights_pruned == 0:
return None
scale = target_sparsity / (total_weights_pruned/total_weights)
# rescale the sparsities
sparsities = np.asarray(sparsities)*scale
return sparsities
def _init_sparsities(self):
'''
Generate a sorted sparsities vector
'''
# repeatedly generate a distribution until satisfies the overall sparsity requirement
_logger.info('Gererating sparsities...')
while True:
sparsities = sorted(np.random.uniform(
0, 1, len(self.get_modules_wrapper())))
sparsities = self._rescale_sparsities(
sparsities, target_sparsity=self._sparsity)
if sparsities is not None and sparsities[0] >= 0 and sparsities[-1] < 1:
_logger.info('Initial sparsities generated : %s', sparsities)
self._sparsities = sparsities
break
def _generate_perturbations(self):
'''
Generate perturbation to the current sparsities distribution.
Returns:
--------
list
perturbated sparsities
'''
_logger.info("Gererating perturbations to the current sparsities...")
# decrease magnitude with current temperature
magnitude = self._current_temperature / \
self._start_temperature * self._perturbation_magnitude
_logger.info('current perturation magnitude:%s', magnitude)
while True:
perturbation = np.random.uniform(-magnitude, magnitude, len(self.get_modules_wrapper()))
sparsities = np.clip(0, self._sparsities + perturbation, None)
_logger.debug("sparsities before rescalling:%s", sparsities)
sparsities = self._rescale_sparsities(sparsities, target_sparsity=self._sparsity)
_logger.debug("sparsities after rescalling:%s", sparsities)
if sparsities is not None and sparsities[0] >= 0 and sparsities[-1] < 1:
_logger.info("Sparsities perturbated:%s", sparsities)
return sparsities
def calc_mask(self, wrapper, **kwargs):
return None
def compress(self, return_config_list=False):
"""
Compress the model with Simulated Annealing.
Returns
-------
torch.nn.Module
model with specified modules compressed.
"""
_logger.info('Starting Simulated Annealing Compression...')
# initiaze a randomized action
pruning_iteration = 0
self._init_sparsities()
# stop condition
self._current_temperature = self._start_temperature
while self._current_temperature > self._stop_temperature:
_logger.info('Pruning iteration: %d', pruning_iteration)
_logger.info('Current temperature: %d, Stop temperature: %d',
self._current_temperature, self._stop_temperature)
while True:
# generate perturbation
sparsities_perturbated = self._generate_perturbations()
config_list = self._sparsities_2_config_list(
sparsities_perturbated)
_logger.info(
"config_list for Pruner generated: %s", config_list)
# fast evaluation
pruner = PRUNER_DICT[self._base_algo](copy.deepcopy(self._model_to_prune), config_list)
model_masked = pruner.compress()
evaluation_result = self._evaluator(model_masked)
self._search_history.append(
{'sparsity': self._sparsity, 'performance': evaluation_result, 'config_list': config_list})
if self._optimize_mode is OptimizeMode.Minimize:
evaluation_result *= -1
# if better evaluation result, then accept the perturbation
if evaluation_result > self._current_performance:
self._current_performance = evaluation_result
self._sparsities = sparsities_perturbated
# save best performance and best params
if evaluation_result > self._best_performance:
_logger.info('updating best model...')
self._best_performance = evaluation_result
self._best_config_list = config_list
# save the overall best masked model
self.bound_model = model_masked
# the ops with sparsity 0 are not included in this modules_wrapper
modules_wrapper_final = pruner.get_modules_wrapper()
break
# if not, accept with probability e^(-deltaE/current_temperature)
else:
delta_E = np.abs(evaluation_result -
self._current_performance)
probability = math.exp(-1 * delta_E /
self._current_temperature)
if np.random.uniform(0, 1) < probability:
self._current_performance = evaluation_result
self._sparsities = sparsities_perturbated
break
# cool down
self._current_temperature *= self._cool_down_rate
pruning_iteration += 1
_logger.info('----------Compression finished--------------')
_logger.info('Best performance: %s', self._best_performance)
_logger.info('config_list found : %s',
self._best_config_list)
# save search history
with open(os.path.join(self._experiment_data_dir, 'search_history.csv'), 'w') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=['sparsity', 'performance', 'config_list'])
writer.writeheader()
for item in self._search_history:
writer.writerow({'sparsity': item['sparsity'], 'performance': item['performance'], 'config_list': json.dumps(
item['config_list'])})
# save best config found and best performance
if self._optimize_mode is OptimizeMode.Minimize:
self._best_performance *= -1
with open(os.path.join(self._experiment_data_dir, 'search_result.json'), 'w+') as jsonfile:
json.dump({
'performance': self._best_performance,
'config_list': json.dumps(self._best_config_list)
}, jsonfile)
_logger.info('search history and result saved to foler : %s',
self._experiment_data_dir)
if return_config_list:
return self._best_config_list
# This should be done only at the final stage,
# because the modules_wrapper with all the ops are used during the annealing process
self.modules_wrapper = modules_wrapper_final
return self.bound_model
================================================
FILE: src/aup/compression/torch/pruning/structured_pruning.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import logging
import math
import numpy as np
import torch
from .weight_masker import WeightMasker
__all__ = ['L1FilterPrunerMasker', 'L2FilterPrunerMasker', 'FPGMPrunerMasker',
'TaylorFOWeightFilterPrunerMasker', 'ActivationAPoZRankFilterPrunerMasker',
'ActivationMeanRankFilterPrunerMasker', 'SlimPrunerMasker', 'AMCWeightMasker']
logger = logging.getLogger('torch filter pruners')
class StructuredWeightMasker(WeightMasker):
"""
A structured pruning masker base class that prunes convolutional layer filters.
Parameters
----------
model: nn.Module
model to be pruned
pruner: Pruner
A Pruner instance used to prune the model
preserve_round: int
after pruning, preserve filters/channels round to `preserve_round`, for example:
for a Conv2d layer, output channel is 32, sparsity is 0.2, if preserve_round is
1 (no preserve round), then there will be int(32 * 0.2) = 6 filters pruned, and
32 - 6 = 26 filters are preserved. If preserve_round is 4, preserved filters will
be round up to 28 (which can be divided by 4) and only 4 filters are pruned.
"""
def __init__(self, model, pruner, preserve_round=1, dependency_aware=False):
self.model = model
self.pruner = pruner
self.preserve_round = preserve_round
self.dependency_aware = dependency_aware
def calc_mask(self, sparsity, wrapper, wrapper_idx=None, **depen_kwargs):
"""
calculate the mask for `wrapper`.
Parameters
----------
sparsity: float/list of float
The target sparsity of the wrapper. If we calculate the mask in
the normal way, then sparsity is a float number. In contrast, if
we calculate the mask in the dependency-aware way, sparsity is a
list of float numbers, each float number corressponds to a sparsity
of a layer.
wrapper: PrunerModuleWrapper/list of PrunerModuleWrappers
The wrapper of the target layer. If we calculate the mask in the normal
way, then `wrapper` is an instance of PrunerModuleWrapper, else `wrapper`
is a list of PrunerModuleWrapper.
wrapper_idx: int/list of int
The index of the wrapper.
depen_kwargs: dict
The kw_args for the dependency-aware mode.
"""
if not self.dependency_aware:
# calculate the mask in the normal way, each layer calculate its
# own mask separately
return self._normal_calc_mask(sparsity, wrapper, wrapper_idx)
else:
# if the dependency_aware switch is on, then calculate the mask
# in the dependency-aware way
return self._dependency_calc_mask(sparsity, wrapper, wrapper_idx, **depen_kwargs)
def _get_current_state(self, sparsity, wrapper, wrapper_idx=None):
"""
Some pruner may prune the layers in a iterative way. In each pruning iteration,
we may get the current state of this wrapper/layer, and continue to prune this layer
based on the current state. This function is to get the current pruning state of the
target wrapper/layer.
Parameters
----------
sparsity: float
pruning ratio, preserved weight ratio is `1 - sparsity`
wrapper: PrunerModuleWrapper
layer wrapper of this layer
wrapper_idx: int
index of this wrapper in pruner's all wrappers
Returns
-------
base_mask: dict
dict object that stores the mask of this wrapper in this iteration, if it is the
first iteration, then we create a new mask with all ones. If there is already a
mask in this wrapper, then we return the existing mask.
weight: tensor
the current weight of this layer
num_prune: int
how many filters we should prune
"""
msg = 'module type {} is not supported!'.format(wrapper.type)
assert wrapper.type == 'Conv2d', msg
weight = wrapper.module.weight.data
bias = None
if hasattr(wrapper.module, 'bias') and wrapper.module.bias is not None:
bias = wrapper.module.bias.data
if wrapper.weight_mask is None:
mask_weight = torch.ones(weight.size()).type_as(weight).detach()
else:
mask_weight = wrapper.weight_mask.clone()
if bias is not None:
if wrapper.bias_mask is None:
mask_bias = torch.ones(bias.size()).type_as(bias).detach()
else:
mask_bias = wrapper.bias_mask.clone()
else:
mask_bias = None
mask = {'weight_mask': mask_weight, 'bias_mask': mask_bias}
num_total = weight.size(0)
num_prune = int(num_total * sparsity)
if self.preserve_round > 1:
num_preserve = num_total - num_prune
num_preserve = int(
math.ceil(num_preserve * 1. / self.preserve_round) * self.preserve_round)
if num_preserve > num_total:
num_preserve = int(math.floor(
num_total * 1. / self.preserve_round) * self.preserve_round)
num_prune = num_total - num_preserve
# weight*mask_weight: apply base mask for iterative pruning
return mask, weight * mask_weight, num_prune
def _normal_calc_mask(self, sparsity, wrapper, wrapper_idx=None):
"""
Calculate the mask of given layer.
Parameters
----------
sparsity: float
pruning ratio, preserved weight ratio is `1 - sparsity`
wrapper: PrunerModuleWrapper
layer wrapper of this layer
wrapper_idx: int
index of this wrapper in pruner's all wrappers
Returns
-------
dict
dictionary for storing masks, keys of the dict:
'weight_mask': weight mask tensor
'bias_mask': bias mask tensor (optional)
"""
mask, weight, num_prune = self._get_current_state(
sparsity, wrapper, wrapper_idx)
num_total = weight.size(0)
if num_total < 2 or num_prune < 1:
return mask
return self.get_mask(mask, weight, num_prune, wrapper, wrapper_idx)
def _common_channel_to_prune(self, sparsities, wrappers, wrappers_idx, channel_dsets, groups):
"""
Calculate the common channels should be pruned by all the layers in this group.
This function is for filter pruning of Conv layers. if want to support the dependency-aware
mode for others ops, you need to inherit this class and overwrite `_common_channel_to_prune`.
Parameters
----------
sparsities : list
List of float that specify the sparsity for each conv layer.
wrappers : list
List of wrappers
groups : list
The number of the filter groups of each layer.
wrappers_idx : list
The indexes of the wrappers
"""
# sparsity configs for each wrapper
# sparsities = [_w.config['sparsity'] for _w in wrappers]
# check the type of the input wrappers
for _w in wrappers:
msg = 'module type {} is not supported!'.format(_w.type)
assert _w.type == 'Conv2d', msg
# Among the dependent layers, the layer with smallest
# sparsity determines the final benefit of the speedup
# module. To better harvest the speed benefit, we need
# to ensure that these dependent layers have at least
# `min_sparsity` pruned channel are the same.
if len(channel_dsets) == len(wrappers):
# all the layers in the dependency sets are pruned
min_sparsity = min(sparsities)
else:
# not all the layers in the dependency set
# are pruned
min_sparsity = 0
# donnot prune the channels that we cannot harvest the speed from
sparsities = [min_sparsity] * len(sparsities)
# find the max number of the filter groups of the dependent
# layers. The group constraint of this dependency set is decided
# by the layer with the max groups.
# should use the least common multiple for all the groups
# the max_group is lower than the channel_count, because
# the number of the filter is always divisible by the number of the group
max_group = np.lcm.reduce(groups)
channel_count = wrappers[0].module.weight.data.size(0)
device = wrappers[0].module.weight.device
channel_sum = torch.zeros(channel_count).to(device)
for _w, _w_idx in zip(wrappers, wrappers_idx):
# calculate the L1/L2 sum for all channels
c_sum = self.get_channel_sum(_w, _w_idx)
if c_sum is None:
# if the channel sum cannot be calculated
# now, return None
return None
channel_sum += c_sum
# prune the same `min_sparsity` channels based on channel_sum
# for all the layers in the channel sparsity
target_pruned = int(channel_count * min_sparsity)
# pruned_per_group may be zero, for example dw conv
pruned_per_group = int(target_pruned / max_group)
group_step = int(channel_count / max_group)
channel_masks = []
for gid in range(max_group):
_start = gid * group_step
_end = (gid + 1) * group_step
if pruned_per_group > 0:
threshold = torch.topk(
channel_sum[_start: _end], pruned_per_group, largest=False)[0].max()
group_mask = torch.gt(channel_sum[_start:_end], threshold)
else:
group_mask = torch.ones(group_step).to(device)
channel_masks.append(group_mask)
channel_masks = torch.cat(channel_masks, dim=0)
pruned_channel_index = (
channel_masks == False).nonzero().squeeze(1).tolist()
logger.info('Prune the %s channels for all dependent',
','.join([str(x) for x in pruned_channel_index]))
return channel_masks
def _dependency_calc_mask(self, sparsities, wrappers, wrappers_idx, channel_dsets, groups):
"""
Calculate the masks for the layers in the same dependency sets.
Similar to the traditional original calc_mask, _dependency_calc_mask
will prune the target layers based on the L1/L2 norm of the weights.
However, StructuredWeightMasker prunes the filter completely based on the
L1/L2 norm of each filter. In contrast, _dependency_calc_mask
will try to satisfy the channel/group dependency(see nni.compression.torch.
utils.shape_dependency for details). Specifically, _dependency_calc_mask
will try to prune the same channels for the layers that have channel dependency.
In addition, this mask calculator will also ensure that the number of filters
pruned in each group is the same(meet the group dependency).
Parameters
----------
sparsities : list
List of float that specify the sparsity for each conv layer.
wrappers : list
List of wrappers
groups : list
The number of the filter groups of each layer.
wrappers_idx : list
The indexes of the wrappers
"""
channel_masks = self._common_channel_to_prune(
sparsities, wrappers, wrappers_idx, channel_dsets, groups)
# calculate the mask for each layer based on channel_masks, first
# every layer will prune the same channels masked in channel_masks.
# If the sparsity of a layers is larger than min_sparsity, then it
# will continue prune sparsity - min_sparsity channels to meet the sparsity
# config.
masks = {}
for _pos, _w in enumerate(wrappers):
_w_idx = wrappers_idx[_pos]
sparsity = sparsities[_pos]
name = _w.name
# _tmp_mask = self._normal_calc_mask(
# sparsity, _w, _w_idx, channel_masks)
base_mask, current_weight, num_prune = self._get_current_state(
sparsity, _w, _w_idx)
num_total = current_weight.size(0)
if num_total < 2 or num_prune < 1:
masks[name] = base_mask
continue
_tmp_mask = self.get_mask(
base_mask, current_weight, num_prune, _w, _w_idx, channel_masks)
if _tmp_mask is None:
# if the mask calculation fails
return None
masks[name] = _tmp_mask
return masks
def get_mask(self, base_mask, weight, num_prune, wrapper, wrapper_idx, channel_masks=None):
"""
Calculate the mask of given layer.
Parameters
----------
base_mask: dict
The basic mask with the same shape of weight, all item in the basic mask is 1.
weight: tensor
the module weight to be pruned
num_prune: int
Num of filters to prune
wrapper: PrunerModuleWrapper
layer wrapper of this layer
wrapper_idx: int
index of this wrapper in pruner's all wrappers
channel_masks: Tensor
If mask some channels for this layer in advance. In the dependency-aware
mode, before calculating the masks for each layer, we will calculate a common
mask for all the layers in the dependency set. For the pruners that doesnot
support dependency-aware mode, they can just ignore this parameter.
Returns
-------
dict
dictionary for storing masks
"""
raise NotImplementedError(
'{} get_mask is not implemented'.format(self.__class__.__name__))
def get_channel_sum(self, wrapper, wrapper_idx):
"""
Calculate the importance weight for each channel. If want to support the
dependency-aware mode for this one-shot pruner, this function must be
implemented.
Parameters
----------
wrapper: PrunerModuleWrapper
layer wrapper of this layer
wrapper_idx: int
index of this wrapper in pruner's all wrappers
Returns
-------
tensor
Tensor that indicates the importance of each channel
"""
raise NotImplementedError(
'{} get_channel_sum is not implemented'.format(self.__class__.__name__))
class L1FilterPrunerMasker(StructuredWeightMasker):
"""
A structured pruning algorithm that prunes the filters of smallest magnitude
weights sum in the convolution layers to achieve a preset level of network sparsity.
Hao Li, Asim Kadav, Igor Durdanovic, Hanan Samet and Hans Peter Graf,
"PRUNING FILTERS FOR EFFICIENT CONVNETS", 2017 ICLR
https://arxiv.org/abs/1608.08710
"""
def get_mask(self, base_mask, weight, num_prune, wrapper, wrapper_idx, channel_masks=None):
# get the l1-norm sum for each filter
w_abs_structured = self.get_channel_sum(wrapper, wrapper_idx)
if channel_masks is not None:
# if we need to mask some channels in advance
w_abs_structured = w_abs_structured * channel_masks
threshold = torch.topk(w_abs_structured.view(-1),
num_prune, largest=False)[0].max()
mask_weight = torch.gt(w_abs_structured, threshold)[
:, None, None, None].expand_as(weight).type_as(weight)
mask_bias = torch.gt(w_abs_structured, threshold).type_as(
weight).detach() if base_mask['bias_mask'] is not None else None
return {'weight_mask': mask_weight.detach(), 'bias_mask': mask_bias}
def get_channel_sum(self, wrapper, wrapper_idx):
weight = wrapper.module.weight.data
filters = weight.shape[0]
w_abs = weight.abs()
w_abs_structured = w_abs.view(filters, -1).sum(dim=1)
return w_abs_structured
class L2FilterPrunerMasker(StructuredWeightMasker):
"""
A structured pruning algorithm that prunes the filters with the
smallest L2 norm of the weights.
"""
def get_mask(self, base_mask, weight, num_prune, wrapper, wrapper_idx, channel_masks=None):
# get the l2-norm sum for each filter
w_l2_norm = self.get_channel_sum(wrapper, wrapper_idx)
if channel_masks is not None:
# if we need to mask some channels in advance
w_l2_norm = w_l2_norm * channel_masks
threshold = torch.topk(
w_l2_norm.view(-1), num_prune, largest=False)[0].max()
mask_weight = torch.gt(w_l2_norm, threshold)[
:, None, None, None].expand_as(weight).type_as(weight)
mask_bias = torch.gt(w_l2_norm, threshold).type_as(
weight).detach() if base_mask['bias_mask'] is not None else None
return {'weight_mask': mask_weight.detach(), 'bias_mask': mask_bias}
def get_channel_sum(self, wrapper, wrapper_idx):
weight = wrapper.module.weight.data
filters = weight.shape[0]
w = weight.view(filters, -1)
w_l2_norm = torch.sqrt((w ** 2).sum(dim=1))
return w_l2_norm
class FPGMPrunerMasker(StructuredWeightMasker):
"""
A filter pruner via geometric median.
"Filter Pruning via Geometric Median for Deep Convolutional Neural Networks Acceleration",
https://arxiv.org/pdf/1811.00250.pdf
"""
def get_mask(self, base_mask, weight, num_prune, wrapper, wrapper_idx, channel_masks=None):
min_gm_idx = self._get_min_gm_kernel_idx(
num_prune, wrapper, wrapper_idx, channel_masks)
for idx in min_gm_idx:
base_mask['weight_mask'][idx] = 0.
if base_mask['bias_mask'] is not None:
base_mask['bias_mask'][idx] = 0.
return base_mask
def _get_min_gm_kernel_idx(self, num_prune, wrapper, wrapper_idx, channel_masks):
channel_dist = self.get_channel_sum(wrapper, wrapper_idx)
if channel_masks is not None:
channel_dist = channel_dist * channel_masks
dist_list = [(channel_dist[i], i)
for i in range(channel_dist.size(0))]
min_gm_kernels = sorted(dist_list, key=lambda x: x[0])[:num_prune]
return [x[1] for x in min_gm_kernels]
def _get_distance_sum(self, weight, out_idx):
"""
Calculate the total distance between a specified filter (by out_idex and in_idx) and
all other filters.
Parameters
----------
weight: Tensor
convolutional filter weight
out_idx: int
output channel index of specified filter, this method calculates the total distance
between this specified filter and all other filters.
Returns
-------
float32
The total distance
"""
logger.debug('weight size: %s', weight.size())
assert len(weight.size()) in [3, 4], 'unsupported weight shape'
w = weight.view(weight.size(0), -1)
anchor_w = w[out_idx].unsqueeze(0).expand(w.size(0), w.size(1))
x = w - anchor_w
x = (x * x).sum(-1)
x = torch.sqrt(x)
return x.sum()
def get_channel_sum(self, wrapper, wrapper_idx):
weight = wrapper.module.weight.data
assert len(weight.size()) in [3, 4]
dist_list = []
for out_i in range(weight.size(0)):
dist_sum = self._get_distance_sum(weight, out_i)
dist_list.append(dist_sum)
return torch.Tensor(dist_list).to(weight.device)
class TaylorFOWeightFilterPrunerMasker(StructuredWeightMasker):
"""
A structured pruning algorithm that prunes the filters with the smallest
importance approximations based on the first order taylor expansion on the weight.
Molchanov, Pavlo and Mallya, Arun and Tyree, Stephen and Frosio, Iuri and Kautz, Jan,
"Importance Estimation for Neural Network Pruning", CVPR 2019.
http://jankautz.com/publications/Importance4NNPruning_CVPR19.pdf
"""
def __init__(self, model, pruner, statistics_batch_num=1):
super().__init__(model, pruner)
self.pruner.statistics_batch_num = statistics_batch_num
self.pruner.set_wrappers_attribute("contribution", None)
self.pruner.iterations = 0
self.pruner.patch_optimizer(self.calc_contributions)
def get_mask(self, base_mask, weight, num_prune, wrapper, wrapper_idx, channel_masks=None):
channel_contribution = self.get_channel_sum(wrapper, wrapper_idx)
if channel_contribution is None:
# iteration is not enough
return None
if channel_masks is not None:
channel_contribution = channel_contribution * channel_masks
prune_indices = torch.argsort(channel_contribution)[:num_prune]
for idx in prune_indices:
base_mask['weight_mask'][idx] = 0.
if base_mask['bias_mask'] is not None:
base_mask['bias_mask'][idx] = 0.
return base_mask
def calc_contributions(self):
"""
Calculate the estimated importance of filters as a sum of individual contribution
based on the first order taylor expansion.
"""
if self.pruner.iterations >= self.pruner.statistics_batch_num:
return
for wrapper in self.pruner.get_modules_wrapper():
filters = wrapper.module.weight.size(0)
contribution = (
wrapper.module.weight*wrapper.module.weight.grad).data.pow(2).view(filters, -1).sum(dim=1)
if wrapper.contribution is None:
wrapper.contribution = contribution
else:
wrapper.contribution += contribution
self.pruner.iterations += 1
def get_channel_sum(self, wrapper, wrapper_idx):
if self.pruner.iterations < self.pruner.statistics_batch_num:
return None
if wrapper.contribution is None:
return None
return wrapper.contribution
class ActivationFilterPrunerMasker(StructuredWeightMasker):
def __init__(self, model, pruner, statistics_batch_num=1, activation='relu'):
super().__init__(model, pruner)
self.statistics_batch_num = statistics_batch_num
self.pruner.hook_id = self._add_activation_collector(self.pruner)
assert activation in ['relu', 'relu6']
if activation == 'relu':
self.pruner.activation = torch.nn.functional.relu
elif activation == 'relu6':
self.pruner.activation = torch.nn.functional.relu6
else:
self.pruner.activation = None
def _add_activation_collector(self, pruner):
def collector(collected_activation):
def hook(module_, input_, output):
collected_activation.append(
pruner.activation(output.detach().cpu()))
return hook
pruner.collected_activation = {}
pruner._fwd_hook_id += 1
pruner._fwd_hook_handles[pruner._fwd_hook_id] = []
for wrapper_idx, wrapper in enumerate(pruner.get_modules_wrapper()):
pruner.collected_activation[wrapper_idx] = []
handle = wrapper.register_forward_hook(
collector(pruner.collected_activation[wrapper_idx]))
pruner._fwd_hook_handles[pruner._fwd_hook_id].append(handle)
return pruner._fwd_hook_id
class ActivationAPoZRankFilterPrunerMasker(ActivationFilterPrunerMasker):
"""
A structured pruning algorithm that prunes the filters with the
smallest APoZ(average percentage of zeros) of output activations.
Hengyuan Hu, Rui Peng, Yu-Wing Tai and Chi-Keung Tang,
"Network Trimming: A Data-Driven Neuron Pruning Approach towards Efficient Deep Architectures", ICLR 2016.
https://arxiv.org/abs/1607.03250
"""
def get_mask(self, base_mask, weight, num_prune, wrapper, wrapper_idx, channel_masks=None):
apoz = self.get_channel_sum(wrapper, wrapper_idx)
if apoz is None:
# the collected activations are not enough
return None
if channel_masks is not None:
apoz = apoz * channel_masks
prune_indices = torch.argsort(apoz)[:num_prune]
for idx in prune_indices:
base_mask['weight_mask'][idx] = 0.
if base_mask['bias_mask'] is not None:
base_mask['bias_mask'][idx] = 0.
if self.pruner.hook_id in self.pruner._fwd_hook_handles:
self.pruner.remove_activation_collector(self.pruner.hook_id)
return base_mask
def _calc_apoz(self, activations):
"""
Calculate APoZ(average percentage of zeros) of activations.
Parameters
----------
activations : list
Layer's output activations
Returns
-------
torch.Tensor
Filter's APoZ(average percentage of zeros) of the activations
"""
activations = torch.cat(activations, 0)
_eq_zero = torch.eq(activations, torch.zeros_like(activations))
_apoz = torch.sum(_eq_zero, dim=(0, 2, 3), dtype=torch.float64) / \
torch.numel(_eq_zero[:, 0, :, :])
return torch.ones_like(_apoz) - _apoz
def get_channel_sum(self, wrapper, wrapper_idx):
assert wrapper_idx is not None
activations = self.pruner.collected_activation[wrapper_idx]
if len(activations) < self.statistics_batch_num:
# collected activations is not enough
return None
return self._calc_apoz(activations).to(wrapper.module.weight.device)
class ActivationMeanRankFilterPrunerMasker(ActivationFilterPrunerMasker):
"""
A structured pruning algorithm that prunes the filters with the
smallest mean value of output activations.
Pavlo Molchanov, Stephen Tyree, Tero Karras, Timo Aila and Jan Kautz,
"Pruning Convolutional Neural Networks for Resource Efficient Inference", ICLR 2017.
https://arxiv.org/abs/1611.06440
"""
def get_mask(self, base_mask, weight, num_prune, wrapper, wrapper_idx, channel_masks=None):
mean_activation = self.get_channel_sum(wrapper, wrapper_idx)
if mean_activation is None:
# the collected activation is not enough
return None
if channel_masks is not None:
mean_activation = mean_activation * channel_masks
prune_indices = torch.argsort(mean_activation)[:num_prune]
for idx in prune_indices:
base_mask['weight_mask'][idx] = 0.
if base_mask['bias_mask'] is not None:
base_mask['bias_mask'][idx] = 0.
# if len(activations) < self.statistics_batch_num, the code
# cannot reach here
if self.pruner.hook_id in self.pruner._fwd_hook_handles:
self.pruner.remove_activation_collector(self.pruner.hook_id)
return base_mask
def _cal_mean_activation(self, activations):
"""
Calculate mean value of activations.
Parameters
----------
activations : list
Layer's output activations
Returns
-------
torch.Tensor
Filter's mean value of the output activations
"""
activations = torch.cat(activations, 0)
mean_activation = torch.mean(activations, dim=(0, 2, 3))
return mean_activation
def get_channel_sum(self, wrapper, wrapper_idx):
assert wrapper_idx is not None
activations = self.pruner.collected_activation[wrapper_idx]
if len(activations) < self.statistics_batch_num:
return None
# the memory overhead here is acceptable, because only
# the mean_activation tensor returned by _cal_mean_activation
# is transfer to gpu.
return self._cal_mean_activation(activations).to(wrapper.module.weight.device)
class SlimPrunerMasker(WeightMasker):
"""
A structured pruning algorithm that prunes channels by pruning the weights of BN layers.
Zhuang Liu, Jianguo Li, Zhiqiang Shen, Gao Huang, Shoumeng Yan and Changshui Zhang
"Learning Efficient Convolutional Networks through Network Slimming", 2017 ICCV
https://arxiv.org/pdf/1708.06519.pdf
"""
def __init__(self, model, pruner, **kwargs):
super().__init__(model, pruner)
weight_list = []
for (layer, _) in pruner.get_modules_to_compress():
weight_list.append(layer.module.weight.data.abs().clone())
all_bn_weights = torch.cat(weight_list)
k = int(all_bn_weights.shape[0] * pruner.config_list[0]['sparsity'])
self.global_threshold = torch.topk(
all_bn_weights.view(-1), k, largest=False)[0].max()
def calc_mask(self, sparsity, wrapper, wrapper_idx=None):
assert wrapper.type == 'BatchNorm2d', 'SlimPruner only supports 2d batch normalization layer pruning'
weight = wrapper.module.weight.data.clone()
if wrapper.weight_mask is not None:
# apply base mask for iterative pruning
weight = weight * wrapper.weight_mask
base_mask = torch.ones(weight.size()).type_as(weight).detach()
mask = {'weight_mask': base_mask.detach(
), 'bias_mask': base_mask.clone().detach()}
filters = weight.size(0)
num_prune = int(filters * sparsity)
if filters >= 2 and num_prune >= 1:
w_abs = weight.abs()
mask_weight = torch.gt(
w_abs, self.global_threshold).type_as(weight)
mask_bias = mask_weight.clone()
mask = {'weight_mask': mask_weight.detach(
), 'bias_mask': mask_bias.detach()}
return mask
def least_square_sklearn(X, Y):
from sklearn.linear_model import LinearRegression
reg = LinearRegression(fit_intercept=False)
reg.fit(X, Y)
return reg.coef_
class AMCWeightMasker(WeightMasker):
"""
Weight maskser class for AMC pruner. Currently, AMCPruner only supports pruning kernel
size 1x1 pointwise Conv2d layer. Before using this class to prune kernels, AMCPruner
collected input and output feature maps for each layer, the features maps are flattened
and save into wrapper.input_feat and wrapper.output_feat.
Parameters
----------
model: nn.Module
model to be pruned
pruner: Pruner
A Pruner instance used to prune the model
preserve_round: int
after pruning, preserve filters/channels round to `preserve_round`, for example:
for a Conv2d layer, output channel is 32, sparsity is 0.2, if preserve_round is
1 (no preserve round), then there will be int(32 * 0.2) = 6 filters pruned, and
32 - 6 = 26 filters are preserved. If preserve_round is 4, preserved filters will
be round up to 28 (which can be divided by 4) and only 4 filters are pruned.
"""
def __init__(self, model, pruner, preserve_round=1):
self.model = model
self.pruner = pruner
self.preserve_round = preserve_round
def calc_mask(self, sparsity, wrapper, wrapper_idx=None, preserve_idx=None):
"""
Calculate the mask of given layer.
Parameters
----------
sparsity: float
pruning ratio, preserved weight ratio is `1 - sparsity`
wrapper: PrunerModuleWrapper
layer wrapper of this layer
wrapper_idx: int
index of this wrapper in pruner's all wrappers
Returns
-------
dict
dictionary for storing masks, keys of the dict:
'weight_mask': weight mask tensor
'bias_mask': bias mask tensor (optional)
"""
msg = 'module type {} is not supported!'.format(wrapper.type)
assert wrapper.type in ['Conv2d', 'Linear'], msg
weight = wrapper.module.weight.data
bias = None
if hasattr(wrapper.module, 'bias') and wrapper.module.bias is not None:
bias = wrapper.module.bias.data
if wrapper.weight_mask is None:
mask_weight = torch.ones(weight.size()).type_as(weight).detach()
else:
mask_weight = wrapper.weight_mask.clone()
if bias is not None:
if wrapper.bias_mask is None:
mask_bias = torch.ones(bias.size()).type_as(bias).detach()
else:
mask_bias = wrapper.bias_mask.clone()
else:
mask_bias = None
mask = {'weight_mask': mask_weight, 'bias_mask': mask_bias}
num_total = weight.size(1)
num_prune = int(num_total * sparsity)
if self.preserve_round > 1:
num_preserve = num_total - num_prune
num_preserve = int(
math.ceil(num_preserve * 1. / self.preserve_round) * self.preserve_round)
if num_preserve > num_total:
num_preserve = num_total
num_prune = num_total - num_preserve
if (num_total < 2 or num_prune < 1) and preserve_idx is None:
return mask
return self.get_mask(mask, weight, num_preserve, wrapper, wrapper_idx, preserve_idx)
def get_mask(self, base_mask, weight, num_preserve, wrapper, wrapper_idx, preserve_idx):
w = weight.data.cpu().numpy()
if wrapper.type == 'Linear':
w = w[:, :, None, None]
if preserve_idx is None:
importance = np.abs(w).sum((0, 2, 3))
# sum magnitude along C_in, sort descend
sorted_idx = np.argsort(-importance)
d_prime = num_preserve
preserve_idx = sorted_idx[:d_prime] # to preserve index
else:
d_prime = len(preserve_idx)
assert len(preserve_idx) == d_prime
mask = np.zeros(w.shape[1], bool)
mask[preserve_idx] = True
# reconstruct, X, Y <= [N, C]
X, Y = wrapper.input_feat, wrapper.output_feat
masked_X = X[:, mask]
if w.shape[2] == 1: # 1x1 conv or fc
rec_weight = least_square_sklearn(X=masked_X, Y=Y)
rec_weight = rec_weight.reshape(-1, 1, 1, d_prime) # (C_out, K_h, K_w, C_in')
rec_weight = np.transpose(rec_weight, (0, 3, 1, 2)) # (C_out, C_in', K_h, K_w)
rec_weight_pad = np.zeros_like(w)
# pylint: disable=all
rec_weight_pad[:, mask, :, :] = rec_weight
rec_weight = rec_weight_pad
if wrapper.type == 'Linear':
rec_weight = rec_weight.squeeze()
assert len(rec_weight.shape) == 2
# now assign
wrapper.module.weight.data = torch.from_numpy(rec_weight).to(weight.device)
mask_weight = torch.zeros_like(weight)
if wrapper.type == 'Linear':
mask_weight[:, preserve_idx] = 1.
if base_mask['bias_mask'] is not None and wrapper.module.bias is not None:
mask_bias = torch.ones_like(wrapper.module.bias)
else:
mask_weight[:, preserve_idx, :, :] = 1.
mask_bias = None
return {'weight_mask': mask_weight.detach(), 'bias_mask': mask_bias}
================================================
FILE: src/aup/compression/torch/pruning/weight_masker.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
class WeightMasker(object):
def __init__(self, model, pruner, **kwargs):
self.model = model
self.pruner = pruner
def calc_mask(self, sparsity, wrapper, wrapper_idx=None):
"""
Calculate the mask of given layer.
Parameters
----------
sparsity: float
pruning ratio, preserved weight ratio is `1 - sparsity`
wrapper: PrunerModuleWrapper
layer wrapper of this layer
wrapper_idx: int
index of this wrapper in pruner's all wrappers
Returns
-------
dict
dictionary for storing masks, keys of the dict:
'weight_mask': weight mask tensor
'bias_mask': bias mask tensor (optional)
"""
raise NotImplementedError('{} calc_mask is not implemented'.format(self.__class__.__name__))
================================================
FILE: src/aup/compression/torch/quantization/__init__.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
from .quantizers import *
================================================
FILE: src/aup/compression/torch/quantization/quantizers.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import logging
import copy
import torch
from schema import Schema, And, Or, Optional
from ..utils.config_validation import CompressorSchema
from ..compressor import Quantizer, QuantGrad, QuantType
__all__ = ['NaiveQuantizer', 'QAT_Quantizer', 'DoReFaQuantizer', 'BNNQuantizer']
logger = logging.getLogger(__name__)
class NaiveQuantizer(Quantizer):
"""quantize weight to 8 bits
"""
def __init__(self, model, config_list, optimizer=None):
super().__init__(model, config_list, optimizer)
self.layer_scale = {}
def validate_config(self, model, config_list):
schema = CompressorSchema([{
Optional('quant_types'): ['weight'],
Optional('quant_bits'): Or(8, {'weight': 8}),
Optional('op_types'): [str],
Optional('op_names'): [str]
}], model, logger)
schema.validate(config_list)
def quantize_weight(self, wrapper, **kwargs):
weight = copy.deepcopy(wrapper.module.old_weight.data)
new_scale = weight.abs().max() / 127
scale = max(self.layer_scale.get(wrapper.name, 0), new_scale)
self.layer_scale[wrapper.name] = scale
orig_type = weight.type() # TODO: user layer
weight = weight.div(scale).type(torch.int8).type(orig_type).mul(scale)
wrapper.module.weight = weight
return weight
def update_ema(biased_ema, value, decay):
"""
calculate biased stat and unbiased stat in each step using exponential moving average method
Parameters
----------
biased_ema : float
previous stat value
value : float
current stat value
decay : float
the weight of previous stat value, larger means smoother curve
Returns
-------
float, float
"""
biased_ema = biased_ema * decay + (1 - decay) * value
return biased_ema
def update_quantization_param(bits, rmin, rmax):
"""
calculate the `zero_point` and `scale`.
Parameters
----------
bits : int
quantization bits length
rmin : Tensor
min value of real value
rmax : Tensor
max value of real value
Returns
-------
float, float
"""
# extend the [min, max] interval to ensure that it contains 0.
# Otherwise, we would not meet the requirement that 0 be an exactly
# representable value.
rmin = torch.min(rmin, torch.Tensor([0]).to(rmin.device))
rmax = torch.max(rmax, torch.Tensor([0]).to(rmin.device))
qmin = torch.Tensor([0]).to(rmin.device)
qmax = torch.Tensor([(1 << bits) - 1]).to(rmin.device)
# First determine the scale.
scale = (rmax - rmin) / (qmax - qmin)
# Zero-point computation.
initial_zero_point = qmin - rmin / scale
# Now we need to nudge the zero point to be an integer
if initial_zero_point < qmin:
nudged_zero_point = qmin
elif initial_zero_point > qmax:
nudged_zero_point = qmax
else:
nudged_zero_point = torch.round(initial_zero_point)
return scale, nudged_zero_point
def get_bits_length(config, quant_type):
if isinstance(config["quant_bits"], int):
return config["quant_bits"]
else:
return config["quant_bits"].get(quant_type)
class QATGrad(QuantGrad):
@staticmethod
def quant_backward(tensor, grad_output, quant_type, scale, zero_point, qmin, qmax):
tensor_q = QuantGrad._quantize(tensor, scale, zero_point)
mask = (tensor_q < qmin) | (tensor_q > qmax)
grad_output[mask] = 0
return grad_output
class QAT_Quantizer(Quantizer):
"""Quantizer defined in:
Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference
http://openaccess.thecvf.com/content_cvpr_2018/papers/Jacob_Quantization_and_Training_CVPR_2018_paper.pdf
"""
def __init__(self, model, config_list, optimizer=None):
"""
Parameters
----------
layer : LayerInfo
the layer to quantize
config_list : list of dict
list of configurations for quantization
supported keys for dict:
- quant_types : list of string
type of quantization you want to apply, currently support 'weight', 'input', 'output'
- quant_bits : int or dict of {str : int}
bits length of quantization, key is the quantization type, value is the length, eg. {'weight', 8},
when the type is int, all quantization types share same bits length
- quant_start_step : int
disable quantization until model are run by certain number of steps, this allows the network to enter a more stable
state where activation quantization ranges do not exclude a significant fraction of values, default value is 0
- op_types : list of string
types of nn.module you want to apply quantization, eg. 'Conv2d'
"""
super().__init__(model, config_list, optimizer)
self.quant_grad = QATGrad
modules_to_compress = self.get_modules_to_compress()
self.bound_model.register_buffer("steps", torch.Tensor([1]))
for layer, config in modules_to_compress:
layer.module.register_buffer("zero_point", torch.Tensor([0.0]))
layer.module.register_buffer("scale", torch.Tensor([1.0]))
if "output" in config.get("quant_types", []):
layer.module.register_buffer('ema_decay', torch.Tensor([0.99]))
layer.module.register_buffer('tracked_min_biased', torch.zeros(1))
layer.module.register_buffer('tracked_min', torch.zeros(1))
layer.module.register_buffer('tracked_max_biased', torch.zeros(1))
layer.module.register_buffer('tracked_max', torch.zeros(1))
def validate_config(self, model, config_list):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list of dict
List of configurations
"""
schema = CompressorSchema([{
Optional('quant_types'): Schema([lambda x: x in ['weight', 'output']]),
Optional('quant_bits'): Or(And(int, lambda n: 0 < n < 32), Schema({
Optional('weight'): And(int, lambda n: 0 < n < 32),
Optional('output'): And(int, lambda n: 0 < n < 32),
})),
Optional('quant_start_step'): And(int, lambda n: n >= 0),
Optional('op_types'): [str],
Optional('op_names'): [str]
}], model, logger)
schema.validate(config_list)
def _quantize(self, bits, op, real_val):
"""
quantize real value.
Parameters
----------
bits : int
quantization bits length
op : torch.nn.Module
target module
real_val : Tensor
real value to be quantized
Returns
-------
Tensor
"""
op.zero_point = op.zero_point.to(real_val.device)
op.scale = op.scale.to(real_val.device)
transformed_val = op.zero_point + real_val / op.scale
qmin = 0
qmax = (1 << bits) - 1
clamped_val = torch.clamp(transformed_val, qmin, qmax)
quantized_val = torch.round(clamped_val)
return quantized_val
def _dequantize(self, op, quantized_val):
"""
dequantize quantized value.
Because we simulate quantization in training process, all the computations still happen as float point computations, which means we
first quantize tensors then dequantize them. For more details, please refer to the paper.
Parameters
----------
op : torch.nn.Module
target module
quantized_val : float
quantized_val value to be dequantized
Returns
-------
float
"""
real_val = op.scale * (quantized_val - op.zero_point)
return real_val
def quantize_weight(self, wrapper, **kwargs):
config = wrapper.config
module = wrapper.module
weight = copy.deepcopy(wrapper.module.old_weight.data)
weight_bits = get_bits_length(config, 'weight')
quant_start_step = config.get('quant_start_step', 0)
assert weight_bits >= 1, "quant bits length should be at least 1"
# we dont update weight in evaluation stage
if quant_start_step > self.bound_model.steps or not wrapper.training:
return weight
# if bias exists, quantize bias to uint32
if hasattr(wrapper.module, 'bias') and wrapper.module.bias is not None:
bias = wrapper.module.bias.data
bias_bits = 32
rmin, rmax = torch.min(bias), torch.max(bias)
module.scale, module.zero_point = update_quantization_param(bias_bits, rmin, rmax)
bias = self._quantize(bias_bits, module, bias)
bias = self._dequantize(module, bias)
wrapper.module.bias.data = bias
# quantize weight
rmin, rmax = torch.min(weight), torch.max(weight)
module.scale, module.zero_point = update_quantization_param(weight_bits, rmin, rmax)
weight = self._quantize(weight_bits, module, weight)
weight = self._dequantize(module, weight)
wrapper.module.weight = weight
return weight
def quantize_output(self, output, wrapper, **kwargs):
config = wrapper.config
module = wrapper.module
output_bits = get_bits_length(config, 'output')
quant_start_step = config.get('quant_start_step', 0)
assert output_bits >= 1, "quant bits length should be at least 1"
if quant_start_step > self.bound_model.steps:
module.tracked_min_biased, module.tracked_max_biased = torch.min(output), torch.max(output)
return output
# we dont update output quantization parameters in evaluation stage
if wrapper.training:
current_min, current_max = torch.min(output), torch.max(output)
module.tracked_min_biased = update_ema(module.tracked_min_biased, current_min,
module.ema_decay)
module.tracked_max_biased = update_ema(module.tracked_max_biased, current_max,
module.ema_decay)
module.scale, module.zero_point = update_quantization_param(output_bits, module.tracked_min_biased, module.tracked_max_biased)
out = self._quantize(output_bits, module, output)
out = self._dequantize(module, out)
return out
def fold_bn(self, config, **kwargs):
# TODO simulate folded weight
pass
def step_with_optimizer(self):
"""
override `compressor` `step` method, quantization only happens after certain number of steps
"""
self.bound_model.steps +=1
class DoReFaQuantizer(Quantizer):
"""Quantizer using the DoReFa scheme, as defined in:
Zhou et al., DoReFa-Net: Training Low Bitwidth Convolutional Neural Networks with Low Bitwidth Gradients
(https://arxiv.org/abs/1606.06160)
"""
def __init__(self, model, config_list, optimizer=None):
super().__init__(model, config_list, optimizer)
def validate_config(self, model, config_list):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list of dict
List of configurations
"""
schema = CompressorSchema([{
Optional('quant_types'): Schema([lambda x: x in ['weight']]),
Optional('quant_bits'): Or(And(int, lambda n: 0 < n < 32), Schema({
Optional('weight'): And(int, lambda n: 0 < n < 32)
})),
Optional('op_types'): [str],
Optional('op_names'): [str]
}], model, logger)
schema.validate(config_list)
def quantize_weight(self, wrapper, **kwargs):
weight = copy.deepcopy(wrapper.module.old_weight.data)
weight_bits = get_bits_length(wrapper.config, 'weight')
weight = weight.tanh()
weight = weight / (2 * weight.abs().max()) + 0.5
weight = self.quantize(weight, weight_bits)
weight = 2 * weight - 1
wrapper.module.weight = weight
# wrapper.module.weight.data = weight
return weight
def quantize(self, input_ri, q_bits):
scale = pow(2, q_bits) - 1
output = torch.round(input_ri * scale) / scale
return output
class ClipGrad(QuantGrad):
@staticmethod
def quant_backward(tensor, grad_output, quant_type, scale, zero_point, qmin, qmax):
if quant_type == QuantType.QUANT_OUTPUT:
grad_output[torch.abs(tensor) > 1] = 0
return grad_output
class BNNQuantizer(Quantizer):
"""Binarized Neural Networks, as defined in:
Binarized Neural Networks: Training Deep Neural Networks with Weights and Activations Constrained to +1 or -1
(https://arxiv.org/abs/1602.02830)
"""
def __init__(self, model, config_list, optimizer=None):
super().__init__(model, config_list, optimizer)
self.quant_grad = ClipGrad
def validate_config(self, model, config_list):
"""
Parameters
----------
model : torch.nn.Module
Model to be pruned
config_list : list of dict
List of configurations
"""
schema = CompressorSchema([{
Optional('quant_types'): Schema([lambda x: x in ['weight', 'output']]),
Optional('quant_bits'): Or(And(int, lambda n: 0 < n < 32), Schema({
Optional('weight'): And(int, lambda n: 0 < n < 32),
Optional('output'): And(int, lambda n: 0 < n < 32),
})),
Optional('op_types'): [str],
Optional('op_names'): [str]
}], model, logger)
schema.validate(config_list)
def quantize_weight(self, wrapper, **kwargs):
weight = copy.deepcopy(wrapper.module.old_weight.data)
weight = torch.sign(weight)
# remove zeros
weight[weight == 0] = 1
wrapper.module.weight = weight
return weight
def quantize_output(self, output, wrapper, **kwargs):
out = torch.sign(output)
# remove zeros
out[out == 0] = 1
return out
================================================
FILE: src/aup/compression/torch/speedup/__init__.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
from .compressor import ModelSpeedup
================================================
FILE: src/aup/compression/torch/speedup/compress_modules.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import logging
import torch
from .infer_shape import ModuleMasks
_logger = logging.getLogger(__name__)
replace_module = {
'BatchNorm2d': lambda module, mask: replace_batchnorm2d(module, mask),
'Conv2d': lambda module, mask: replace_conv2d(module, mask),
'ConvTranspose2d': lambda module, mask: replace_convtranspose2d(module, mask),
'MaxPool2d': lambda module, mask: no_replace(module, mask),
'AvgPool2d': lambda module, mask: no_replace(module, mask),
'AdaptiveAvgPool2d': lambda module, mask: no_replace(module, mask),
'ReLU': lambda module, mask: no_replace(module, mask),
'ReLU6': lambda module, mask: no_replace(module, mask),
'Sigmoid': lambda module, mask: no_replace(module, mask),
'Linear': lambda module, mask: replace_linear(module, mask),
'Dropout': lambda module, mask: no_replace(module, mask),
'Dropout2d': lambda module, mask: no_replace(module, mask),
'Dropout3d': lambda module, mask: no_replace(module, mask)
}
def no_replace(module, mask):
"""
No need to replace
"""
_logger.debug("no need to replace")
return module
def replace_linear(linear, mask):
"""
Parameters
----------
linear : torch.nn.Linear
The linear module to be replace
mask : ModuleMasks
The masks of this module
Returns
-------
torch.nn.Linear
The new linear module
"""
assert isinstance(mask, ModuleMasks)
assert mask.input_mask is not None
assert mask.output_mask is None
assert not mask.param_masks
index = mask.input_mask.mask_index[-1]
in_features = index.size()[0]
_logger.debug("replace linear with new in_features: %d", in_features)
new_linear = torch.nn.Linear(in_features=in_features,
out_features=linear.out_features,
bias=linear.bias is not None)
new_linear.to(linear.weight.device)
new_linear.weight.data = torch.index_select(
linear.weight.data, -1, index.to(linear.weight.device))
if linear.bias is not None:
new_linear.bias.data.copy_(linear.bias.data)
return new_linear
def replace_batchnorm2d(norm, mask):
"""
Parameters
----------
norm : torch.nn.BatchNorm2d
The batchnorm module to be replace
mask : ModuleMasks
The masks of this module
Returns
-------
torch.nn.BatchNorm2d
The new batchnorm module
"""
assert isinstance(mask, ModuleMasks)
assert 'weight' in mask.param_masks and 'bias' in mask.param_masks
index = mask.param_masks['weight'].mask_index[0]
num_features = index.size()[0]
_logger.debug("replace batchnorm2d with num_features: %d", num_features)
new_norm = torch.nn.BatchNorm2d(num_features=num_features,
eps=norm.eps,
momentum=norm.momentum,
affine=norm.affine,
track_running_stats=norm.track_running_stats)
# assign weights
new_norm.weight.data = torch.index_select(norm.weight.data, 0, index)
new_norm.bias.data = torch.index_select(norm.bias.data, 0, index)
if norm.track_running_stats:
new_norm.running_mean.data = torch.index_select(
norm.running_mean.data, 0, index)
new_norm.running_var.data = torch.index_select(
norm.running_var.data, 0, index)
return new_norm
def replace_conv2d(conv, mask):
"""
Parameters
----------
conv : torch.nn.Conv2d
The conv2d module to be replaced
mask : ModuleMasks
The masks of this module
Returns
-------
torch.nn.Conv2d
The new conv2d module
"""
assert isinstance(mask, ModuleMasks)
if mask.input_mask is None:
in_channels = conv.in_channels
else:
in_channels_index = mask.input_mask.mask_index[1]
in_channels = in_channels_index.size()[0]
if mask.output_mask is None:
out_channels = conv.out_channels
else:
out_channels_index = mask.output_mask.mask_index[1]
out_channels = out_channels_index.size()[0]
groups = conv.groups
if conv.in_channels == conv.out_channels == conv.groups:
# remove groups for depthwise layers
assert in_channels == out_channels
groups = in_channels
_logger.debug("replace conv2d %s with in_channels: %d, out_channels: %d",
mask.module_name, in_channels, out_channels)
new_conv = torch.nn.Conv2d(in_channels=in_channels,
out_channels=out_channels,
kernel_size=conv.kernel_size,
stride=conv.stride,
padding=conv.padding,
dilation=conv.dilation,
groups=groups,
bias=conv.bias is not None,
padding_mode=conv.padding_mode)
new_conv.to(conv.weight.device)
tmp_weight_data = tmp_bias_data = None
if mask.output_mask is not None:
tmp_weight_data = torch.index_select(
conv.weight.data, 0, out_channels_index)
if conv.bias is not None:
tmp_bias_data = torch.index_select(
conv.bias.data, 0, out_channels_index)
else:
tmp_weight_data = conv.weight.data
# For the convolutional layers that have more than one group
# we need to copy the weight group by group, because the input
# channal is also divided into serveral groups and each group
# filter may have different input channel indexes.
input_step = int(conv.in_channels / conv.groups)
in_channels_group = int(in_channels / groups)
filter_step = int(out_channels / groups)
if mask.input_mask is not None and not (in_channels == out_channels == groups):
for groupid in range(conv.groups):
start = groupid * input_step
end = (groupid + 1) * input_step
current_input_index = list(
filter(lambda x: start <= x and x < end, in_channels_index.tolist()))
if not current_input_index:
# there is no kept channel in current group
# TODO bug here, the groups is directly get from conv.groups, if the whole group is removed,
# then the number of groups in the new_conv also need to change
raise Exception(
" Donnot support removing the whole group filter except in the depth-wise conv temporarily")
# shift the global index into the group index
current_input_index = [x-start for x in current_input_index]
# if the groups is larger than 1, the input channels of each
# group should be pruned evenly.
assert len(current_input_index) == in_channels_group, \
'Input channels of each group are not pruned evenly'
current_input_index = torch.tensor(current_input_index).to(tmp_weight_data.device) # pylint: disable=not-callable
f_start = groupid * filter_step
f_end = (groupid + 1) * filter_step
new_conv.weight.data[f_start:f_end] = torch.index_select(
tmp_weight_data[f_start:f_end], 1, current_input_index)
else:
new_conv.weight.data.copy_(tmp_weight_data)
if conv.bias is not None:
new_conv.bias.data.copy_(
conv.bias.data if tmp_bias_data is None else tmp_bias_data)
return new_conv
def replace_convtranspose2d(convtrans, mask):
"""
We need anothor replace function for
convtranspose2d, because the layout of
the weight is different from traditional
conv layers. The layout of the weight is [N_in, N_out, ksize_1, ksize_2]
Parameters
----------
convtrans : torch.nn.ConvTranspose2d
The conv2d module to be replaced
mask : ModuleMasks
The masks of this module
Returns
-------
torch.nn.ConvTranspose2d
The new conv2d module
"""
assert isinstance(mask, ModuleMasks)
assert isinstance(convtrans, torch.nn.ConvTranspose2d)
if mask.input_mask is None:
in_channels = convtrans.in_channels
else:
in_channels_index = mask.input_mask.mask_index[1]
in_channels = in_channels_index.size(0)
if mask.output_mask is None:
out_channels = convtrans.out_channels
else:
out_channels_index = mask.output_mask.mask_index[1]
out_channels = out_channels_index.size(0)
groups = convtrans.groups
# check if can remove the whole group of filters
if convtrans.in_channels == convtrans.out_channels == convtrans.groups:
# remove groups for depthwise layers
# this needs the group dependency to be fixed before the speedup
assert in_channels == out_channels
groups = in_channels
_logger.debug('Replace convtranspose2d %s with in_channels:%d out_channels:%d',
mask.module_name, in_channels, out_channels)
new_convtrans = torch.nn.ConvTranspose2d(in_channels=in_channels,
out_channels=out_channels,
kernel_size=convtrans.kernel_size,
stride=convtrans.stride,
padding=convtrans.padding,
dilation=convtrans.dilation,
groups=groups,
bias=convtrans.bias is not None,
padding_mode=convtrans.padding_mode)
new_convtrans.to(convtrans.weight.device)
tmp_weight_data = None
if mask.input_mask is not None:
# in convtranspose2d we need to select the input channel first
tmp_weight_data = torch.index_select(
convtrans.weight.data, 0, in_channels_index)
else:
tmp_weight_data = convtrans.weight.data
# we need to handle the output channel group by group like the conv layer
out_step = int(convtrans.out_channels / convtrans.groups)
out_channel_group = int(out_channels/groups)
new_in_per_group = int(in_channels/groups)
if mask.output_mask is not None and not(in_channels == out_channels == groups):
for groupid in range(convtrans.groups):
start = groupid * out_step
end = (groupid + 1) * out_step
current_output_index = list(
filter(lambda x: start <= x and x < end, out_channels_index.tolist()))
# we need to shift the index into the group-wise
current_output_index = [x-start for x in current_output_index]
if not current_output_index:
# No kept channel in the current group
raise Exception(
" Donnot support removing the whole group filter except in the depth-wise conv temporarily")
assert len(current_output_index) == out_channel_group, \
'Output channel of each group should be the same after pruning'
current_output_index = torch.tensor(current_output_index).to(tmp_weight_data.device) # pylint: disable=not-callable
new_start = groupid * new_in_per_group
new_end = (groupid + 1) * new_in_per_group
new_convtrans.weight.data[new_start:new_end] = torch.index_select(
tmp_weight_data[new_start:new_end], 1, current_output_index)
else:
new_convtrans.weight.data.copy_(tmp_weight_data)
if convtrans.bias is not None:
if mask.output_mask is not None:
new_convtrans.bias.data[:] = torch.index_select(
convtrans.bias.data, 0, out_channels_index)
else:
new_convtrans.bias.data.copy_(convtrans.bias.data)
return new_convtrans
================================================
FILE: src/aup/compression/torch/speedup/compressor.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import logging
import torch
from ..utils.mask_conflict import fix_mask_conflict
from ..utils.utils import get_module_by_name
from .compress_modules import replace_module
from .infer_shape import ModuleMasks, infer_from_mask, infer_from_inshape, infer_from_outshape, set_conv_prune_dim
_logger = logging.getLogger(__name__)
class ModelSpeedup:
"""
This class is to speedup the model with provided weight mask
"""
def __init__(self, model, dummy_input, masks=None, masks_file=None, map_location=None):
"""
Parameters
----------
model : pytorch model
The model user wants to speed up
dummy_input : pytorch tensor
The dummy input for ```jit.trace```, users should put it on right device before pass in
masks : dict
The pre-loaded dictionary of weight pruning masks
masks_file : str
The path of user provided mask file
map_location : str
the device on which masks are placed, same to map_location in ```torch.load```
"""
from .._graph_utils import build_module_graph
self.bound_model = model
if masks is not None:
self.masks = masks
elif masks_file is not None:
self.masks = torch.load(masks_file, map_location)
else:
raise ValueError("Either masks or masks_file must be passed to ModelSpeedup constructor")
self.inferred_masks = dict() # key: module_name, value: ModuleMasks
self.dummy_input = dummy_input
self.torch_graph = build_module_graph(model, dummy_input)
def infer_module_mask(self, module_name, last_module, mask=None, in_shape=None, out_shape=None):
"""
Infer input shape / output shape based on the module's weight mask / input shape / output shape.
For a module:
Infer its input and output shape from its weight mask
Infer its output shape from its input shape
Infer its input shape from its output shape
If its input shape is changed, continue infering its predecessors
If its output shape is changed, continue infering its successors
Parameters
----------
module_name : str
The name of the node
last_module : str
The name of last visited node
mask : tensor of mask or ModuleMasks
Mask of the weights in this node (i.e., module)
in_shape : ModuleMasks
Input shape of this node
out_shape : ModuleMasks
Output shape of this node
"""
input_cmask = output_cmask = None
if module_name in self.inferred_masks:
module_masks = self.inferred_masks[module_name]
else:
_, m = get_module_by_name(self.bound_model, module_name)
module_masks = ModuleMasks(module_name, m)
self.inferred_masks[module_name] = module_masks
m_type = self.torch_graph.name_to_node[module_name].op_type
_logger.debug("infer mask of module %s with op_type %s", module_name, m_type)
if mask is not None:
_logger.debug("mask is not None")
if not m_type in infer_from_mask:
raise RuntimeError(
"Has not supported infering input/output shape from mask for module/function: `{}`, {}"
.format(m_type, module_name))
if m_type in ['Linear']:
input_cmask, output_cmask = infer_from_mask[m_type](
module_masks, mask, self.torch_graph.name_to_node[module_name].auxiliary
)
else:
input_cmask, output_cmask = infer_from_mask[m_type](module_masks, mask)
if in_shape is not None:
_logger.debug("in_shape is not None")
if not m_type in infer_from_inshape:
raise RuntimeError(
"Has not supported infering output shape from input shape for module/function: `{}`, {}"
.format(m_type, module_name))
if m_type in ['aten::view', 'aten::flatten', 'aten::mean', 'aten::reshape']:
output_cmask = infer_from_inshape[m_type](module_masks,
in_shape,
self.torch_graph.name_to_node[module_name].auxiliary)
elif m_type in ['aten::cat']:
# To calculate the mask for concat operation, the output shape
# , cat dimension, and the order of the input parameters.
output_cmask = infer_from_inshape[m_type](module_masks,
in_shape,
self.torch_graph.name_to_node[module_name].auxiliary,
last_module)
else:
output_cmask = infer_from_inshape[m_type](module_masks, in_shape)
if out_shape is not None:
_logger.debug("out_shape is not None")
if not m_type in infer_from_outshape:
raise RuntimeError(
"Has not supported infering input shape from output shape for module/function: `{}`, {}"
.format(m_type, module_name))
if m_type in ['aten::view', 'aten::flatten', 'aten::mean', 'aten::reshape']:
input_cmask = infer_from_outshape[m_type](module_masks, out_shape, self.torch_graph.name_to_node[module_name].auxiliary)
else:
input_cmask = infer_from_outshape[m_type](module_masks, out_shape)
if input_cmask:
predecessors = self.torch_graph.find_predecessors(module_name)
for _module_name in predecessors:
self.infer_module_mask(_module_name, module_name, out_shape=input_cmask)
if output_cmask:
successors = self.torch_graph.find_successors(module_name)
for _module_name in successors:
self.infer_module_mask(_module_name, module_name, in_shape=output_cmask)
def infer_modules_masks(self):
"""
Do shape inference of involved modules, including the shape of weights, inputs, output
"""
for module_name, mask in self.masks.items():
_logger.debug('Start mask inference from %s', module_name)
if module_name not in self.torch_graph.name_to_node:
# this module is not traced in the torch_graph,
# jit.trace only correctly records functions and
# modules which are not data dependent (e.g., do
# not have conditionals on data in tensors)
# so, if a node is not traced, we just skip it.
_logger.warning('%s has mask, but not found in the traced graph, just skip it.', module_name)
continue
self.infer_module_mask(module_name, None, mask=mask)
def replace_compressed_modules(self):
"""
Replace all the modules that have changed (weights/inputs/output) shape.
The new module is created using the same arguments of the to-be-replaced module,
and correctly inherits its weights.
NOTE: ```func``` type cannot be replaced as it is not a module, thus, one limitation
is that ```func``` should be not required to be replaced.
"""
for module_name in self.inferred_masks:
g_node = self.torch_graph.name_to_node[module_name]
_logger.debug("replace %s, in %s type, with op_type %s",
module_name, g_node.type, g_node.op_type)
if g_node.type == 'module':
super_module, leaf_module = get_module_by_name(self.bound_model, g_node.name)
m_type = g_node.op_type
if not m_type in replace_module:
raise RuntimeError("Has not supported replacing the module: `{}`".format(m_type))
_logger.info("replace module (name: %s, op_type: %s)", g_node.name, m_type)
compressed_module = replace_module[m_type](leaf_module, self.inferred_masks[module_name])
setattr(super_module, g_node.name.split('.')[-1], compressed_module)
elif g_node.type == 'func':
_logger.info("Warning: cannot replace (name: %s, op_type: %s) which is func type",
module_name, g_node.op_type)
else:
raise RuntimeError("Unsupported node type: {}".format(g_node.type))
def speedup_model(self):
"""
There are basically two steps:
first, do mask/shape inference,
second, replace modules
"""
training = self.bound_model.training
_logger.info("start to speed up the model")
_logger.info("fix the mask conflict of the interdependent layers")
_, conv_prune_dim = fix_mask_conflict(self.masks, self.bound_model, self.dummy_input)
set_conv_prune_dim(conv_prune_dim)
_logger.info("infer module masks...")
self.infer_modules_masks()
_logger.info("replace compressed modules...")
self.replace_compressed_modules()
self.bound_model.train(training)
_logger.info("speedup done")
================================================
FILE: src/aup/compression/torch/speedup/infer_shape.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
"""
For each operation or module, there are two functions.
One is given output shape, infer its input shape and initialization parameters (e.g., weight's shape)
The other is given input shape, infer its output shape and initialization parameters (e.g., weight's shape)
"""
import logging
import torch
_logger = logging.getLogger(__name__)
conv_prune_dim = -1
def set_conv_prune_dim(dim):
"""
Parameters:
dim: int
0: filter pruning
1: channel pruning
"""
global conv_prune_dim
conv_prune_dim = dim
class CoarseMask:
"""
Coarse grained mask for a given tensor, here tensor could be weights,
input tensor, or output tensor
"""
def __init__(self, num_dim):
"""
Parameters
----------
num_dim : int
The number of dimensions of the tensor that will be masked
"""
self.mask_index = [None for _ in range(num_dim)]
def add_index_mask(self, dim, index):
"""
Add mask for the specified dimension
Parameters
----------
dim : int
The dimension to add mask
index : tensor
The mask for this dimension, its a 1 dimension tensor which specifies
the index of the elements that are not pruned
"""
self.mask_index[dim] = index
@staticmethod
def merge_index(index_a, index_b):
"""
Parameters
----------
index_a : tensor
One index (1-dimension) tensor
index_b : tensor
The other index (1-dimension) tensor
Returns
-------
tensor
The merged index (1-dimension) tensor
Note that: the output tensor will be moved
to the same device as index_a.
"""
device = index_a.device
s = set()
for num in index_a.tolist():
# we need to transfer the tensor to list here
# first, directly traversing the tensor by for
# loop will return the list of tensor(x) object,
# even the value are the same, but they are different
# tensor objects, so the set will contains multiple
# tensor objects that has the same value. For example
# for num in torch.ones(2):
# s.add(num)
# s will be {tensor(1), tensor(1)}
s.add(num)
for num in index_b.tolist():
s.add(num)
# move the output tensor to the same device with index_a
return torch.tensor(sorted(s)).to(device) # pylint: disable=not-callable
def merge(self, cmask):
"""
Merge another CoarseMask
Parameters
----------
cmask : CoarseMask
Another CoarseMask to merge
Returns
-------
list
The member variable ```mask_index```
"""
assert isinstance(cmask, CoarseMask)
assert len(self.mask_index) == len(cmask.mask_index), \
"Only masks with the same number of dimensions can be merged"
for i, index in enumerate(self.mask_index):
if index is None:
self.mask_index[i] = cmask.mask_index[i]
elif cmask.mask_index[i] is not None:
self.mask_index[i] = CoarseMask.merge_index(self.mask_index[i],
cmask.mask_index[i])
return self.mask_index
def __repr__(self):
return 'mask_index: {}'.format(self.mask_index)
def eq_on_dim(self, other, dim):
assert isinstance(other, CoarseMask)
if self.mask_index[dim] is None and other.mask_index[dim] is None:
return True
elif isinstance(self.mask_index[dim], torch.Tensor) \
and isinstance(other.mask_index[dim], torch.Tensor):
return torch.equal(self.mask_index[dim], other.mask_index[dim])
else:
return False
def __eq__(self, other):
assert isinstance(other, CoarseMask)
if len(self.mask_index) != len(other.mask_index):
return False
for i in range(len(self.mask_index)):
if not self.eq_on_dim(other, i):
return False
return True
def __lt__(self, other):
"""
Judge if the mask is a subset of another CoarseMask.
"""
assert isinstance(other, CoarseMask)
for dim, _ in enumerate(self.mask_index):
# if self has more dimensions
if dim >= len(other.mask_index):
return False
if self.mask_index[dim] is None:
# if no mask on this dimension, then we have less
# masks then the other CoraseMask.
continue
elif other.mask_index[dim] is None:
return False
else:
s1 = set(self.mask_index[dim].tolist())
s2 = set(other.mask_index[dim].tolist())
if not s1 < s2:
return False
return True
def __le__(self, other):
"""
Return if self's mask is less or equal to other's mask.
"""
assert isinstance(other, CoarseMask)
if self.__lt__(other) or self.__eq__(other):
return True
return False
def __ne__(self, other):
return not self.__eq__(other)
class ModuleMasks:
"""
The masks of a module, including the masks for weights, inputs, output
"""
def __init__(self, module_name, module=None):
"""
Parameters
----------
module_name : str
The name of the module or function
"""
self.module_name = module_name
self.module = module
self.param_masks = dict()
self.input_mask = None
self.output_mask = None
def set_param_masks(self, name, mask):
"""
Parameters
----------
name : str
The name of the weight
mask : CoarseMask
The mask for this weight
"""
self.param_masks[name] = mask
def set_input_mask(self, mask):
"""
Parameters
----------
mask : CoarseMask
The mask for input
"""
self.input_mask = mask
def set_output_mask(self, mask):
"""
Parameters
----------
mask : CoarseMask
The mask for output
"""
self.output_mask = mask
def __repr__(self):
return 'module_name: {}, input_mask: {}, output_mask: {}, param_masks: {}'.format(
self.module_name, self.input_mask, self.output_mask, self.param_masks
)
"""
Infer input and output shape of a module/function from its weight mask
"""
infer_from_mask = {
'BatchNorm2d': lambda module_masks, mask: batchnorm2d_mask(module_masks, mask),
'Conv2d': lambda module_masks, mask: conv2d_mask(module_masks, mask),
'ConvTranspose2d': lambda module_masks, mask: convtranspose2d_mask(module_masks, mask),
'Linear': lambda module_masks, mask, shape: linear_mask(module_masks, mask, shape)
}
"""
Infer output and weight shape of a module/function from its input shape
"""
infer_from_inshape = {
'ReLU': lambda module_masks, mask: relu_inshape(module_masks, mask),
'ReLU6': lambda module_masks, mask: relu_inshape(module_masks, mask),
'Sigmoid': lambda module_masks, mask: relu_inshape(module_masks, mask),
'aten::relu': lambda module_masks, mask: relu_inshape(module_masks, mask),
'aten::tanh': lambda module_masks, mask: relu_inshape(module_masks, mask),
'aten::tanh_': lambda module_masks, mask: relu_inshape(module_masks, mask),
'aten::hardtanh': lambda module_masks, mask: relu_inshape(module_masks, mask),
'aten::hardtanh_': lambda module_masks, mask: relu_inshape(module_masks, mask),
'aten::relu_': lambda module_masks, mask: relu_inshape(module_masks, mask),
'aten::sigmoid': lambda module_masks, mask: relu_inshape(module_masks, mask),
'Conv2d': lambda module_masks, mask: conv2d_inshape(module_masks, mask),
'ConvTranspose2d': lambda module_masks, mask: convtranspose2d_inshape(module_masks, mask),
'MaxPool2d': lambda module_masks, mask: maxpool2d_inshape(module_masks, mask),
'aten::max_pool2d': lambda module_masks, mask: maxpool2d_inshape(module_masks, mask),
'aten::avg_pool2d': lambda module_masks, mask: maxpool2d_inshape(module_masks, mask),
'aten::adaptive_avg_pool2d': lambda module_masks, mask: maxpool2d_inshape(module_masks, mask),
'AvgPool2d': lambda module_masks, mask: maxpool2d_inshape(module_masks, mask),
'AdaptiveAvgPool2d': lambda module_masks, mask: maxpool2d_inshape(module_masks, mask),
'aten::size': lambda module_masks, mask: size_inshape(module_masks, mask),
'aten::view': lambda module_masks, mask, shape: view_inshape(module_masks, mask, shape),
'aten::reshape': lambda module_masks, mask, shape: view_inshape(module_masks, mask, shape),
# support only start_dim=1
'aten::flatten': lambda module_masks, mask, shape: view_inshape(module_masks, mask, shape),
'Linear': lambda module_masks, mask: linear_inshape(module_masks, mask),
'BatchNorm2d': lambda module_masks, mask: batchnorm2d_inshape(module_masks, mask),
'aten::add_': lambda module_masks, mask: add_inshape(module_masks, mask),
'aten::add': lambda module_mask, mask: add_inshape(module_mask, mask),
# mul has the similar behaviour with add, they both request
# the input tesors to have the same shape
'aten::mul': lambda module_mask, mask: add_inshape(module_mask, mask),
'aten::mul_': lambda module_mask, mask: add_inshape(module_mask, mask),
'aten::cat': lambda module_mask, mask, cat_info, last_visited: cat_inshape(module_mask, mask, cat_info, last_visited),
'aten::mean': lambda module_masks, mask, shape: mean_inshape(module_masks, mask, shape),
'Dropout': lambda module_masks, mask: dropout_inshape(module_masks, mask),
'Dropout2d': lambda module_masks, mask: dropout_inshape(module_masks, mask),
'aten::dropout': lambda module_masks, mask: dropout_inshape(module_masks, mask),
'aten::detach': lambda module_masks, mask: dropout_inshape(module_masks, mask)
}
"""
Infer input and weight shape of a module/function from its output shape
"""
infer_from_outshape = {
'Conv2d': lambda module_masks, mask: conv2d_outshape(module_masks, mask),
'ConvTranspose2d': lambda module_masks, mask: convtranspose2d_outshape(module_masks, mask),
'BatchNorm2d': lambda module_masks, mask: batchnorm2d_outshape(module_masks, mask),
'MaxPool2d': lambda module_masks, mask: maxpool2d_outshape(module_masks, mask),
'aten::max_pool2d': lambda module_masks, mask: maxpool2d_outshape(module_masks, mask),
'aten::avg_pool2d': lambda module_masks, mask: maxpool2d_outshape(module_masks, mask),
'aten::adaptive_avg_pool2d': lambda module_masks, mask: maxpool2d_outshape(module_masks, mask),
'AvgPool2d': lambda module_masks, mask: maxpool2d_outshape(module_masks, mask),
'AdaptiveAvgPool2d': lambda module_masks, mask: maxpool2d_outshape(module_masks, mask),
'ReLU': lambda module_masks, mask: relu_outshape(module_masks, mask),
'ReLU6': lambda module_masks, mask: relu_outshape(module_masks, mask),
'aten::relu': lambda module_masks, mask: relu_outshape(module_masks, mask),
'aten::tanh': lambda module_masks, mask: relu_outshape(module_masks, mask),
'aten::tanh_': lambda module_masks, mask: relu_outshape(module_masks, mask),
'aten::hardtanh': lambda module_masks, mask: relu_outshape(module_masks, mask),
'aten::hardtanh_': lambda module_masks, mask: relu_outshape(module_masks, mask),
'aten::relu_': lambda module_masks, mask: relu_outshape(module_masks, mask),
'aten::add_': lambda module_masks, mask: add_outshape(module_masks, mask),
'aten::add': lambda module_mask, mask: add_outshape(module_mask, mask),
'aten::flatten': lambda module_mask, mask, shape: view_outshape(module_mask, mask, shape),
'aten::view': lambda module_masks, mask, shape: view_outshape(module_masks, mask, shape),
'aten::reshape': lambda module_masks, mask, shape: view_outshape(module_masks, mask, shape),
'aten::mean': lambda module_masks, mask, shape: mean_outshape(module_masks, mask, shape),
'Dropout': lambda module_masks, mask: dropout_outshape(module_masks, mask),
'Dropout2d': lambda module_masks, mask: dropout_outshape(module_masks, mask),
'aten::dropout': lambda module_masks, mask: dropout_outshape(module_masks, mask),
'aten::detach': lambda module_masks, mask: dropout_outshape(module_masks, mask)
}
def dropout_inshape(module_masks, mask):
if module_masks.input_mask is None:
module_masks.set_input_mask(mask)
module_masks.set_output_mask(mask)
return module_masks.output_mask
# if alreay visited
assert module_masks.input_mask <= mask
# It should be the same, we pass the masks by the reference(not the value),
# so they acutually are two references of the same object(mask,
# module_masks.input_mask). So we should continue pass the mask
# to the following nodes even module_masks.input_mask == mask.
# if pass the mask by copy.deepcopy(), then we can stop when
# module_masks.input_mask == mask.
# if module_masks.input_mask == mask:
# return None
module_masks.set_input_mask(mask)
module_masks.set_output_mask(mask)
return module_masks.output_mask
def dropout_outshape(module_masks, mask):
if module_masks.output_mask is None:
module_masks.set_output_mask(mask)
module_masks.set_input_mask(mask)
return module_masks.input_mask
# if alreay visited
assert all(module_masks.output_mask.mask_index[1] == mask.mask_index[1])
return module_masks.output_mask
def cat_inshape(module_masks, mask, cat_info, last_visited):
"""
Inference the output mask of the cat operation from the
input mask.
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the Conv2d
mask : CoarseMask
The mask of its input tensor
cat_info: dict
Dict object that records the necessary information
of cat operation, such as the order of the input
tensors.
last_visited: str
The unique_name of the last visited node group.
Returns
-------
CoarseMask
The mask of its output tensor
"""
assert isinstance(mask, CoarseMask)
out_shape = cat_info['out_shape']
cat_dim = cat_info['cat_dim']
in_order = cat_info['in_order']
in_shape = cat_info['in_shape']
if module_masks.output_mask is None:
# First visit to this cat node
# initialize the mask based on
# the number of the output channel.
output_mask = CoarseMask(num_dim=len(out_shape))
for dim, _ in enumerate(out_shape):
if dim == cat_dim:
if mask.mask_index[dim] is None:
continue
device = mask.mask_index[dim].device
# calculate the offset of the mask
pos = in_order.index(last_visited)
offsets = [in_shape[i][cat_dim]
for i, _ in enumerate(in_shape)]
offset = 0
for i in range(pos):
offset += offsets[i]
_tmp_mask = (mask.mask_index[dim] + offset).to(device)
output_mask.mask_index[dim] = _tmp_mask
else:
# directly copy the mask
if mask.mask_index[dim] is not None:
output_mask.mask_index[dim] = mask.mask_index[dim].data.clone(
)
module_masks.set_output_mask(output_mask)
return module_masks.output_mask
# If this cat node is already visited, we need
# validating if the mask is legel, for cat operation,
# the mask on the 'cat_dim' dimension should be stitched
# together. In the other dimensions, the mask should be
# the same, else the mask is not legal.
for dim, _ in enumerate(out_shape):
if dim == cat_dim:
if mask.mask_index[dim] is None:
continue
pos = in_order.index(last_visited)
offsets = [in_shape[i][cat_dim] for i, _ in enumerate(in_shape)]
offset = 0
for i in range(pos):
offset += offsets[i]
device = mask.mask_index[dim].device
new_mask = mask.mask_index[dim] + offset
module_masks.output_mask.mask_index[dim] = CoarseMask.merge_index(
module_masks.output_mask.mask_index[dim], new_mask).to(device)
else:
assert module_masks.output_mask.eq_on_dim(mask, dim)
return module_masks.output_mask
def add_inshape(module_masks, mask):
"""
Inference the output mask of the add operation from the
input mask.
"""
assert isinstance(mask, CoarseMask)
if module_masks.input_mask is None:
module_masks.set_input_mask(mask)
module_masks.set_output_mask(mask)
# module_masks.input_mask = mask
return mask
# If alreay visited, validate if have the conflict
# if the mask is different with previous input_mask
# then there is a mask confilct.
if mask != module_masks.input_mask:
raise Exception('Mask conflict happenes!')
return None
def add_outshape(module_masks, mask):
"""
Inference the input mask of the add operation from the
output mask.
"""
assert isinstance(mask, CoarseMask)
if module_masks.output_mask is None:
module_masks.set_output_mask(mask)
module_masks.set_input_mask(mask)
return mask
else:
assert all(
module_masks.output_mask.mask_index[1] == mask.mask_index[1])
return mask
def batchnorm2d_inshape(module_masks, mask):
"""
We assume only the second dimension has coarse grained mask
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the batchnorm2d
mask : CoarseMask
The mask of its input tensor
Returns
-------
CoarseMask
The mask of its output tensor
"""
assert isinstance(mask, CoarseMask)
assert mask.mask_index[1] is not None
assert mask.mask_index[0] is None
assert mask.mask_index[2] is None
assert mask.mask_index[3] is None
module_masks.set_input_mask(mask)
module_masks.set_output_mask(mask)
weight_cmask = CoarseMask(num_dim=1)
weight_cmask.add_index_mask(dim=0, index=mask.mask_index[1])
module_masks.set_param_masks('weight', weight_cmask)
module_masks.set_param_masks('bias', weight_cmask)
return mask
def batchnorm2d_outshape(module_masks, mask):
"""
We assume only the second dimension has coarse grained mask
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the batchnorm2d
mask : CoarseMask
The mask of its input tensor
Returns
-------
CoarseMask
The mask of its output tensor
"""
assert isinstance(mask, CoarseMask)
assert len(mask.mask_index) in [2, 4]
assert mask.mask_index[1] is not None
assert mask.mask_index[0] is None
module_masks.set_input_mask(mask)
module_masks.set_output_mask(mask)
weight_cmask = CoarseMask(num_dim=1)
weight_cmask.add_index_mask(dim=0, index=mask.mask_index[1])
module_masks.set_param_masks('weight', weight_cmask)
module_masks.set_param_masks('bias', weight_cmask)
return mask
def linear_inshape(module_masks, mask):
"""
Coarse grained input mask does not change the shape of weights and output tensor
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the linear
mask : CoarseMask
The mask of its input tensor
Returns
-------
CoarseMask
The mask of its output tensor, ```None``` means shape of output tensor is not changed
"""
assert isinstance(mask, CoarseMask)
assert mask.mask_index[0] is None
if module_masks.input_mask is not None:
assert module_masks.input_mask <= mask
module_masks.set_input_mask(mask)
return None
def view_inshape(module_masks, mask, shape):
"""
This is a limited support
TODO: consider replace tensor.view with nn.Flatten, because tensor.view is not
included in module, thus, cannot be replaced by our framework.
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the ```view``` op
mask : CoarseMask
The mask of its input tensor
shape : dict
Original shape of its input and output tensors
Returns
-------
CoarseMask
The mask of its output tensor
"""
# NOTE: the case constrained by the following four asserts
assert shape['in_shape'][0] == shape['out_shape'][0]
assert len(shape['in_shape']) == 4
assert len(shape['out_shape']) == 2
assert shape['out_shape'][1] == shape['in_shape'][1] * \
shape['in_shape'][2]*shape['in_shape'][3]
assert isinstance(mask, CoarseMask)
assert mask.mask_index[1] is not None
assert mask.mask_index[0] is None
assert mask.mask_index[2] is None
assert mask.mask_index[3] is None
# due to the cat operation, the same node may be
# accessed more than once
if module_masks.input_mask is not None:
assert module_masks.input_mask <= mask
module_masks.set_input_mask(mask)
output_cmask = CoarseMask(num_dim=2)
index = []
step_size = shape['in_shape'][2] * shape['in_shape'][3]
for loc in mask.mask_index[1]:
index.extend([loc * step_size + i for i in range(step_size)])
output_cmask.add_index_mask(dim=1, index=torch.tensor(index).to(mask.mask_index[1].device)) # pylint: disable=not-callable
module_masks.set_output_mask(output_cmask)
return output_cmask
def view_outshape(module_masks, mask, shape):
"""
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the ```flatten``` op
mask : CoarseMask
The mask of its input tensor
shape : dict
Original shape of its input and output tensors
Returns
-------
CoarseMask
The mask of its output tensor
"""
# NOTE: the case constrained by the following four asserts
assert shape['in_shape'][0] == shape['out_shape'][0]
assert len(shape['in_shape']) == 4
assert len(shape['out_shape']) == 2
assert shape['out_shape'][1] == shape['in_shape'][1] * \
shape['in_shape'][2]*shape['in_shape'][3]
assert isinstance(mask, CoarseMask)
assert mask.mask_index[1] is not None
assert mask.mask_index[0] is None
module_masks.set_output_mask(mask)
input_cmask = CoarseMask(num_dim=4)
index = []
step_size = shape['in_shape'][2] * shape['in_shape'][3]
for loc in mask.mask_index[1]:
index.extend([loc * step_size + i for i in range(step_size)])
input_cmask.add_index_mask(dim=1, index=torch.tensor(index).to(mask.mask_index[1].device)) # pylint: disable=not-callable
module_masks.set_input_mask(input_cmask)
return input_cmask
def size_inshape(module_masks, mask):
"""
No need to do anything for this ```size``` op
"""
return None
def mean_inshape(module_masks, mask, shape):
"""
Similar to view operation, currently mask inference only supports
the mean operation on the 3rd and 4th dimensions.
"""
assert shape['in_shape'][0] == shape['out_shape'][0]
assert shape['out_shape'][1] == shape['in_shape'][1]
assert len(shape['in_shape']) == 4
assert len(shape['out_shape']) == 2
assert isinstance(mask, CoarseMask)
assert mask.mask_index[1] is not None
assert mask.mask_index[0] is None
assert mask.mask_index[2] is None
assert mask.mask_index[3] is None
module_masks.set_input_mask(mask)
output_cmask = CoarseMask(num_dim=2)
output_cmask.add_index_mask(dim=1, index=mask.mask_index[1])
module_masks.set_output_mask(output_cmask)
return output_cmask
def mean_outshape(module_masks, mask, shape):
"""
Similar to view operation, currently mask inference only supports
the mean operation on the 3rd and 4th dimensions.
"""
assert shape['in_shape'][0] == shape['out_shape'][0]
assert shape['out_shape'][1] == shape['in_shape'][1]
assert len(shape['in_shape']) == 4
assert len(shape['out_shape']) == 2
assert isinstance(mask, CoarseMask)
assert mask.mask_index[1] is not None
assert mask.mask_index[0] is None
module_masks.set_output_mask(mask)
input_cmask = CoarseMask(num_dim=4)
input_cmask.add_index_mask(dim=1, index=mask.mask_index[1])
module_masks.set_input_mask(input_cmask)
return input_cmask
def maxpool2d_inshape(module_masks, mask):
"""
Assume only the second dimension is masked
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the maxpool2d
mask : CoarseMask
The mask of its input tensor
Returns
-------
CoarseMask
The mask of its output tensor
"""
assert isinstance(mask, CoarseMask)
assert mask.mask_index[1] is not None
assert mask.mask_index[0] is None
assert mask.mask_index[2] is None
assert mask.mask_index[3] is None
if module_masks.input_mask is not None:
assert module_masks.input_mask <= mask
# assert module_masks.input_mask is None
module_masks.set_input_mask(mask)
module_masks.set_output_mask(mask)
return mask
def maxpool2d_outshape(module_masks, mask):
"""
Assume only the second dimension is masked
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the maxpool2d
mask : CoarseMask
The mask of its input tensor
Returns
-------
CoarseMask
The mask of its output tensor
"""
assert isinstance(mask, CoarseMask)
assert mask.mask_index[1] is not None
assert mask.mask_index[0] is None
module_masks.set_input_mask(mask)
module_masks.set_output_mask(mask)
return mask
def relu_inshape(module_masks, mask):
"""
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the relu
mask : CoarseMask
The mask of its input tensor
Returns
-------
CoarseMask
The mask of its output tensor
"""
assert isinstance(mask, CoarseMask)
if module_masks.input_mask is not None:
# mask conflict should be solved before speedup
assert module_masks.input_mask <= mask
# assert module_masks.input_mask is None, "A relu op can only be processed once"
module_masks.set_input_mask(mask)
module_masks.set_output_mask(mask)
return mask
def relu_outshape(module_masks, mask):
"""
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the relu
mask : CoarseMask
The mask of its input tensor
Returns
-------
CoarseMask
The mask of its output tensor
"""
assert isinstance(mask, CoarseMask)
if module_masks.output_mask is not None:
# mask conflict should be solved before speedup
assert all(
module_masks.output_mask.mask_index[1] == mask.mask_index[1])
module_masks.set_input_mask(mask)
module_masks.set_output_mask(mask)
return mask
def batchnorm2d_mask(module_masks, mask):
"""
Infer input and output shape from weight mask
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the batchnorm2d
mask : dict
The mask of its weights, from the user provided mask file
Returns
-------
CoarseMask, CoarseMask
The mask of its input tensor, the mask of its output tensor
"""
assert 'weight' in mask and 'bias' in mask
sum_mask = mask['weight'] + mask['bias']
nonzero_index = torch.nonzero(sum_mask, as_tuple=True)[0]
# infer shape of parameters
param_cmask = CoarseMask(num_dim=1)
param_cmask.add_index_mask(dim=0, index=nonzero_index)
module_masks.set_param_masks('weight', param_cmask)
module_masks.set_param_masks('bias', param_cmask)
# infer shape of input tensor
input_cmask = CoarseMask(num_dim=4)
input_cmask.add_index_mask(dim=1,
index=torch.nonzero(mask['weight'], as_tuple=True)[0])
module_masks.set_input_mask(input_cmask)
# infer shape of output tensor
output_cmask = CoarseMask(num_dim=4)
output_cmask.add_index_mask(dim=1, index=nonzero_index)
module_masks.set_output_mask(output_cmask)
return input_cmask, output_cmask
def linear_mask(module_masks, mask, shape):
"""
Infer input and output shape from weight mask with limitations:
Only support infer input mask
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the Linear
mask : dict
The mask of its weights, from the user provided mask file
shape: dict
Shape of its input and output tensors
Returns
-------
CoarseMask, CoarseMask
The mask of its input tensor, the mask of its output tensor
"""
assert 'weight' in mask
num_input_dim = len(shape['in_shape'])
# Input data of Linear module can have multiple dimensions.
# here we only support infer coarse mask on the first dimension (dimension 0)
nonzero_index = torch.nonzero(mask['weight'].sum(0), as_tuple=True)[0]
# infer shape of input tensor
input_cmask = CoarseMask(num_dim=num_input_dim)
input_cmask.add_index_mask(dim=num_input_dim-1, index=nonzero_index)
module_masks.set_input_mask(input_cmask)
return input_cmask, None
def conv2d_mask(module_masks, mask):
"""
Infer input and output shape from weight mask
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the conv2d
mask : dict
The mask of its weights, from the user provided mask file
Returns
-------
CoarseMask, CoarseMask
The mask of its input tensor, the mask of its output tensor
"""
def convert_to_coarse_mask(mask, dim=0):
"""
Parameters
----------
mask : dict
Weight mask from user provided mask file
dim: int
0: filter pruning
1: channel pruning
Returns
-------
LongTensor, CoarseMask, CoarseMask
Index of the masked dimension, weight mask, bias mask
"""
assert 'weight' in mask
assert isinstance(mask['weight'], torch.Tensor)
assert dim in [0, 1]
weight_mask = mask['weight']
sum_idx = (1, 2, 3) if dim == 0 else (0, 2, 3)
index = torch.nonzero(weight_mask.abs().sum(
sum_idx) != 0, as_tuple=True)[0]
index = index.long().to(weight_mask.device)
weight_cmask = CoarseMask(num_dim=4)
weight_cmask.add_index_mask(dim=dim, index=index)
bias_cmask = None
if dim == 0 and 'bias' in mask and mask['bias'] is not None:
bias_index = torch.nonzero(mask['bias'], as_tuple=True)[0]
assert torch.all(torch.eq(index, bias_index)), \
"bias mask should be consistent with weight mask"
bias_cmask = CoarseMask(num_dim=1)
bias_cmask.add_index_mask(dim=0, index=bias_index)
return index, weight_cmask, bias_cmask
index, weight_cmask, bias_cmask = convert_to_coarse_mask(
mask, dim=conv_prune_dim)
if index is None:
# TODO: fine grained mask speedup
return None, None
# deal with coarse grain mask
# mask conflict should be solved by fix_mask_conflict before speedup
if 'weight' in module_masks.param_masks:
assert module_masks.param_masks['weight'] == weight_cmask
else:
module_masks.set_param_masks('weight', weight_cmask)
if conv_prune_dim == 0:
module_masks.set_param_masks('bias', bias_cmask)
io_cmask = CoarseMask(num_dim=4)
io_cmask.add_index_mask(dim=1, index=index)
if conv_prune_dim == 0:
if module_masks.output_mask is None:
module_masks.set_output_mask(io_cmask)
else:
assert module_masks.output_mask == io_cmask
return None, module_masks.output_mask
else:
if module_masks.input_mask is None:
module_masks.set_input_mask(io_cmask)
else:
assert module_masks.input_mask == io_cmask
return module_masks.input_mask, None
def conv2d_inshape(module_masks, mask):
"""
Shape change of input tensor does not affect the shape of its output tensor
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the conv2d
mask : CoarseMask
The mask of its input tensor
Returns
-------
CoarseMask
The mask of its output tensor
"""
assert isinstance(mask, CoarseMask)
if module_masks.input_mask is None:
module_masks.set_input_mask(mask)
else:
# the same conv layer may be accessed more
# than once, such as a concat operation.
# mask conflict should be solved by fix_mask_conflict before speedup
assert module_masks.input_mask == mask
# shape changes pass through depths wise conv layers
m = module_masks.module
if m.in_channels == m.out_channels == m.groups:
module_masks.output_mask = mask
module_masks.input_mask = mask
return mask
return None
def conv2d_outshape(module_masks, mask):
"""
Assume only the second dimension is masked
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the conv2d
mask : CoarseMask
The mask of its output tensor
Returns
-------
CoarseMask
The mask of its input tensor
"""
assert isinstance(mask, CoarseMask)
assert mask.mask_index[1] is not None
assert mask.mask_index[0] is None
assert mask.mask_index[2] is None
assert mask.mask_index[3] is None
if module_masks.output_mask is None:
module_masks.output_mask = mask
else:
# mask conflict should be solved by fix_mask_conflict before speedup
# mask and module_masks.output_mask may have different number of dimensions
# since they could be passed by linear or conv2d
assert all(
module_masks.output_mask.mask_index[1] == mask.mask_index[1])
weight_cmask = CoarseMask(num_dim=4)
weight_cmask.add_index_mask(dim=0, index=mask.mask_index[1])
bias_cmask = CoarseMask(num_dim=1)
bias_cmask.add_index_mask(dim=0, index=mask.mask_index[1])
module_masks.set_param_masks('weight', weight_cmask)
module_masks.set_param_masks('bias', bias_cmask)
# shape changes pass through depths wise conv layers
m = module_masks.module
if m.in_channels == m.out_channels == m.groups:
module_masks.output_mask = mask
module_masks.input_mask = mask
return mask
return None
def convtranspose2d_mask(module_masks, mask):
# TODO support the Convtranspose2d Pruning for the L1FilterPruner
raise Exception(
"Current Filter pruner cannot prune the ConvTranspose2d, will support pruning ConvTranspose2d later")
def convtranspose2d_inshape(module_masks, mask):
"""
Shape change of input tensor does not affect the shape of its output tensor
Parameters
----------
module_masks : ModuleMasks
The ModuleMasks instance of the conv2d
mask : CoarseMask
The mask of its input tensor
Returns
-------
CoarseMask
The mask of its output tensor
"""
assert isinstance(mask, CoarseMask)
if module_masks.input_mask is None:
module_masks.set_input_mask(mask)
else:
# the same conv layer may be accessed more
# than once, such as a concat operation.
# mask conflict should be solved by fix_mask_conflict before speedup
assert module_masks.input_mask == mask
# shape changes pass through depths wise conv layers
m = module_masks.module
if m.in_channels == m.out_channels == m.groups:
module_masks.output_mask = mask
module_masks.input_mask = mask
return mask
return None
def convtranspose2d_outshape(module_masks, mask):
assert isinstance(mask, CoarseMask)
assert mask.mask_index[1] is not None
assert mask.mask_index[0] is None
assert mask.mask_index[2] is None
assert mask.mask_index[3] is None
if module_masks.output_mask is None:
module_masks.output_mask = mask
else:
# mask conflict should be solved by fix_mask_conflict before speedup
# mask and module_masks.output_mask may have different number of dimensions
# since they could be passed by linear or conv2d
assert all(
module_masks.output_mask.mask_index[1] == mask.mask_index[1])
weight_cmask = CoarseMask(num_dim=4)
# Note the memory layout of Convtranspose2d is C_in, C_out, k1, k2
weight_cmask.add_index_mask(dim=1, index=mask.mask_index[1])
bias_cmask = CoarseMask(num_dim=1)
bias_cmask.add_index_mask(dim=0, index=mask.mask_index[1])
module_masks.set_param_masks('weight', weight_cmask)
module_masks.set_param_masks('bias', bias_cmask)
# shape changes pass through depths wise conv layers
m = module_masks.module
if m.in_channels == m.out_channels == m.groups:
module_masks.output_mask = mask
module_masks.input_mask = mask
return mask
return None
================================================
FILE: src/aup/compression/torch/torch_utils.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import os
import copy
import functools
from enum import Enum, unique
import json_tricks
from schema import And
from . import parameter_expressions
to_json = functools.partial(json_tricks.dumps, allow_nan=True)
@unique
class OptimizeMode(Enum):
"""Optimize Mode class
if OptimizeMode is 'minimize', it means the tuner need to minimize the reward
that received from Trial.
if OptimizeMode is 'maximize', it means the tuner need to maximize the reward
that received from Trial.
"""
Minimize = 'minimize'
Maximize = 'maximize'
class NodeType:
"""Node Type class
"""
ROOT = 'root'
TYPE = '_type'
VALUE = '_value'
INDEX = '_index'
NAME = '_name'
class MetricType:
"""The types of metric data
"""
FINAL = 'FINAL'
PERIODICAL = 'PERIODICAL'
REQUEST_PARAMETER = 'REQUEST_PARAMETER'
def split_index(params):
"""
Delete index infromation from params
"""
if isinstance(params, dict):
if NodeType.INDEX in params.keys():
return split_index(params[NodeType.VALUE])
result = {}
for key in params:
result[key] = split_index(params[key])
return result
else:
return params
def extract_scalar_reward(value, scalar_key='default'):
"""
Extract scalar reward from trial result.
Parameters
----------
value : int, float, dict
the reported final metric data
scalar_key : str
the key name that indicates the numeric number
Raises
------
RuntimeError
Incorrect final result: the final result should be float/int,
or a dict which has a key named "default" whose value is float/int.
"""
if isinstance(value, (float, int)):
reward = value
elif isinstance(value, dict) and scalar_key in value and isinstance(value[scalar_key], (float, int)):
reward = value[scalar_key]
else:
raise RuntimeError('Incorrect final result: the final result should be float/int, ' \
'or a dict which has a key named "default" whose value is float/int.')
return reward
def extract_scalar_history(trial_history, scalar_key='default'):
"""
Extract scalar value from a list of intermediate results.
Parameters
----------
trial_history : list
accumulated intermediate results of a trial
scalar_key : str
the key name that indicates the numeric number
Raises
------
RuntimeError
Incorrect final result: the final result should be float/int,
or a dict which has a key named "default" whose value is float/int.
"""
return [extract_scalar_reward(ele, scalar_key) for ele in trial_history]
def convert_dict2tuple(value):
"""
convert dict type to tuple to solve unhashable problem.
"""
if isinstance(value, dict):
for _keys in value:
value[_keys] = convert_dict2tuple(value[_keys])
return tuple(sorted(value.items()))
return value
def json2space(x, oldy=None, name=NodeType.ROOT):
"""
Change search space from json format to hyperopt format
"""
y = list()
if isinstance(x, dict):
if NodeType.TYPE in x.keys():
_type = x[NodeType.TYPE]
name = name + '-' + _type
if _type == 'choice':
if oldy is not None:
_index = oldy[NodeType.INDEX]
y += json2space(x[NodeType.VALUE][_index],
oldy[NodeType.VALUE], name=name+'[%d]' % _index)
else:
y += json2space(x[NodeType.VALUE], None, name=name)
y.append(name)
else:
for key in x.keys():
y += json2space(x[key], oldy[key] if oldy else None, name+"[%s]" % str(key))
elif isinstance(x, list):
for i, x_i in enumerate(x):
if isinstance(x_i, dict):
if NodeType.NAME not in x_i.keys():
raise RuntimeError('\'_name\' key is not found in this nested search space.')
y += json2space(x_i, oldy[i] if oldy else None, name + "[%d]" % i)
return y
def json2parameter(x, is_rand, random_state, oldy=None, Rand=False, name=NodeType.ROOT):
"""
Json to pramaters.
"""
if isinstance(x, dict):
if NodeType.TYPE in x.keys():
_type = x[NodeType.TYPE]
_value = x[NodeType.VALUE]
name = name + '-' + _type
Rand |= is_rand[name]
if Rand is True:
if _type == 'choice':
_index = random_state.randint(len(_value))
y = {
NodeType.INDEX: _index,
NodeType.VALUE: json2parameter(
x[NodeType.VALUE][_index],
is_rand,
random_state,
None,
Rand,
name=name+"[%d]" % _index
)
}
else:
y = getattr(parameter_expressions, _type)(*(_value + [random_state]))
else:
y = copy.deepcopy(oldy)
else:
y = dict()
for key in x.keys():
y[key] = json2parameter(
x[key],
is_rand,
random_state,
oldy[key] if oldy else None,
Rand,
name + "[%s]" % str(key)
)
elif isinstance(x, list):
y = list()
for i, x_i in enumerate(x):
if isinstance(x_i, dict):
if NodeType.NAME not in x_i.keys():
raise RuntimeError('\'_name\' key is not found in this nested search space.')
y.append(json2parameter(
x_i,
is_rand,
random_state,
oldy[i] if oldy else None,
Rand,
name + "[%d]" % i
))
else:
y = copy.deepcopy(x)
return y
def merge_parameter(base_params, override_params):
"""
Update the parameters in ``base_params`` with ``override_params``.
Can be useful to override parsed command line arguments.
Parameters
----------
base_params : namespace or dict
Base parameters. A key-value mapping.
override_params : dict or None
Parameters to override. Usually the parameters got from ``get_next_parameters()``.
When it is none, nothing will happen.
Returns
-------
namespace or dict
The updated ``base_params``. Note that ``base_params`` will be updated inplace. The return value is
only for convenience.
"""
if override_params is None:
return base_params
is_dict = isinstance(base_params, dict)
for k, v in override_params.items():
if is_dict:
if k not in base_params:
raise ValueError('Key \'%s\' not found in base parameters.' % k)
if type(base_params[k]) != type(v) and base_params[k] is not None:
raise TypeError('Expected \'%s\' in override parameters to have type \'%s\', but found \'%s\'.' %
(k, type(base_params[k]), type(v)))
base_params[k] = v
else:
if not hasattr(base_params, k):
raise ValueError('Key \'%s\' not found in base parameters.' % k)
if type(getattr(base_params, k)) != type(v) and getattr(base_params, k) is not None:
raise TypeError('Expected \'%s\' in override parameters to have type \'%s\', but found \'%s\'.' %
(k, type(getattr(base_params, k)), type(v)))
setattr(base_params, k, v)
return base_params
class ClassArgsValidator(object):
"""
NNI tuners/assessors/adivisors accept a `classArgs` parameter in experiment configuration file.
This ClassArgsValidator interface is used to validate the classArgs section in exeperiment
configuration file.
"""
def validate_class_args(self, **kwargs):
"""
Validate the classArgs configuration in experiment configuration file.
Parameters
----------
kwargs: dict
kwargs passed to tuner/assessor/advisor constructor
Raises:
Raise an execption if the kwargs is invalid.
"""
pass
def choices(self, key, *args):
"""
Utility method to create a scheme to check whether the `key` is one of the `args`.
Parameters:
----------
key: str
key name of the data to be validated
args: list of str
list of the choices
Returns: Schema
--------
A scheme to check whether the `key` is one of the `args`.
"""
return And(lambda n: n in args, error='%s should be in [%s]!' % (key, str(args)))
def range(self, key, keyType, start, end):
"""
Utility method to create a schema to check whether the `key` is in the range of [start, end].
Parameters:
----------
key: str
key name of the data to be validated
keyType: type
python data type, such as int, float
start: type is specified by keyType
start of the range
end: type is specified by keyType
end of the range
Returns: Schema
--------
A scheme to check whether the `key` is in the range of [start, end].
"""
return And(
And(keyType, error='%s should be %s type!' % (key, keyType.__name__)),
And(lambda n: start <= n <= end, error='%s should be in range of (%s, %s)!' % (key, start, end))
)
================================================
FILE: src/aup/compression/torch/utils/__init__.py
================================================
================================================
FILE: src/aup/compression/torch/utils/config_validation.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
from schema import Schema, And, SchemaError
def validate_op_names(model, op_names, logger):
found_names = set(map(lambda x: x[0], model.named_modules()))
not_found_op_names = list(set(op_names) - found_names)
if not_found_op_names:
logger.warning('op_names %s not found in model', not_found_op_names)
return True
def validate_op_types(model, op_types, logger):
found_types = set(['default']) | set(map(lambda x: type(x[1]).__name__, model.named_modules()))
not_found_op_types = list(set(op_types) - found_types)
if not_found_op_types:
logger.warning('op_types %s not found in model', not_found_op_types)
return True
def validate_op_types_op_names(data):
if not ('op_types' in data or 'op_names' in data):
raise SchemaError('Either op_types or op_names must be specified.')
return True
class CompressorSchema:
def __init__(self, data_schema, model, logger):
assert isinstance(data_schema, list) and len(data_schema) <= 1
self.data_schema = data_schema
self.compressor_schema = Schema(self._modify_schema(data_schema, model, logger))
def _modify_schema(self, data_schema, model, logger):
if not data_schema:
return data_schema
for k in data_schema[0]:
old_schema = data_schema[0][k]
if k == 'op_types' or (isinstance(k, Schema) and k._schema == 'op_types'):
new_schema = And(old_schema, lambda n: validate_op_types(model, n, logger))
data_schema[0][k] = new_schema
if k == 'op_names' or (isinstance(k, Schema) and k._schema == 'op_names'):
new_schema = And(old_schema, lambda n: validate_op_names(model, n, logger))
data_schema[0][k] = new_schema
data_schema[0] = And(data_schema[0], lambda d: validate_op_types_op_names(d))
return data_schema
def validate(self, data):
self.compressor_schema.validate(data)
================================================
FILE: src/aup/compression/torch/utils/counter.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import functools
from collections import Counter
from prettytable import PrettyTable
import torch
import torch.nn as nn
from ..compressor import PrunerModuleWrapper
__all__ = ['count_flops_params']
def _get_params(m):
return sum([p.numel() for p in m.parameters()])
class ModelProfiler:
def __init__(self, custom_ops=None, mode='default'):
"""
ModelProfiler is used to share state to hooks.
Parameters
----------
custom_ops: dict
a mapping of (module -> torch.nn.Module : custom operation)
the custom operation is a callback funtion to calculate
the module flops, parameters and the weight shape, it will overwrite the default operation.
for reference, please see ``self.ops``.
mode:
the mode of how to collect information. If the mode is set to `default`,
only the information of convolution and linear will be collected.
If the mode is set to `full`, other operations will also be collected.
"""
self.ops = {
nn.Conv1d: self._count_convNd,
nn.Conv2d: self._count_convNd,
nn.Conv3d: self._count_convNd,
nn.Linear: self._count_linear
}
self._count_bias = False
if mode == 'full':
self.ops.update({
nn.ConvTranspose1d: self._count_convNd,
nn.ConvTranspose2d: self._count_convNd,
nn.ConvTranspose3d: self._count_convNd,
nn.BatchNorm1d: self._count_bn,
nn.BatchNorm2d: self._count_bn,
nn.BatchNorm3d: self._count_bn,
nn.LeakyReLU: self._count_relu,
nn.AvgPool1d: self._count_avgpool,
nn.AvgPool2d: self._count_avgpool,
nn.AvgPool3d: self._count_avgpool,
nn.AdaptiveAvgPool1d: self._count_adap_avgpool,
nn.AdaptiveAvgPool2d: self._count_adap_avgpool,
nn.AdaptiveAvgPool3d: self._count_adap_avgpool,
nn.Upsample: self._count_upsample,
nn.UpsamplingBilinear2d: self._count_upsample,
nn.UpsamplingNearest2d: self._count_upsample
})
self._count_bias = True
if custom_ops is not None:
self.ops.update(custom_ops)
self.mode = mode
self.results = []
def _push_result(self, result):
self.results.append(result)
def _get_result(self, m, flops):
# assume weight is called `weight`, otherwise it's not applicable
# if user customize the operation, the callback function should
# return the dict result, inluding calculated flops, params and weight_shape.
result = {
'flops': flops,
'params': _get_params(m),
'weight_shape': tuple(m.weight.size()) if hasattr(m, 'weight') else 0,
}
return result
def _count_convNd(self, m, x, y):
cin = m.in_channels
kernel_ops = m.weight.size()[2] * m.weight.size()[3]
output_size = torch.zeros(y.size()[2:]).numel()
cout = y.size()[1]
if hasattr(m, 'weight_mask'):
cout = m.weight_mask.sum() // (cin * kernel_ops)
total_ops = cout * output_size * kernel_ops * cin // m.groups # cout x oW x oH
if self._count_bias:
bias_flops = 1 if m.bias is not None else 0
total_ops += cout * output_size * bias_flops
return self._get_result(m, total_ops)
def _count_linear(self, m, x, y):
out_features = m.out_features
if hasattr(m, 'weight_mask'):
out_features = m.weight_mask.sum() // m.in_features
total_ops = out_features * m.in_features
if self._count_bias:
bias_flops = 1 if m.bias is not None else 0
total_ops += out_features * bias_flops
return self._get_result(m, total_ops)
def _count_bn(self, m, x, y):
total_ops = 2 * x[0].numel()
return self._get_result(m, total_ops)
def _count_relu(self, m, x, y):
total_ops = x[0].numel()
return self._get_result(m, total_ops)
def _count_avgpool(self, m, x, y):
total_ops = y.numel()
return self._get_result(m, total_ops)
def _count_adap_avgpool(self, m, x, y):
kernel = torch.Tensor([*(x[0].shape[2:])]) // torch.Tensor(list((m.output_size,))).squeeze()
total_add = int(torch.prod(kernel))
total_div = 1
kernel_ops = total_add + total_div
num_elements = y.numel()
total_ops = kernel_ops * num_elements
return self._get_result(m, total_ops)
def _count_upsample(self, m, x, y):
if m.mode == 'linear':
total_ops = y.nelement() * 5 # 2 muls + 3 add
elif m.mode == 'bilinear':
# https://en.wikipedia.org/wiki/Bilinear_interpolation
total_ops = y.nelement() * 11 # 6 muls + 5 adds
elif m.mode == 'bicubic':
# https://en.wikipedia.org/wiki/Bicubic_interpolation
# Product matrix [4x4] x [4x4] x [4x4]
ops_solve_A = 224 # 128 muls + 96 adds
ops_solve_p = 35 # 16 muls + 12 adds + 4 muls + 3 adds
total_ops = y.nelement() * (ops_solve_A + ops_solve_p)
elif m.mode == 'trilinear':
# https://en.wikipedia.org/wiki/Trilinear_interpolation
# can viewed as 2 bilinear + 1 linear
total_ops = y.nelement() * (13 * 2 + 5)
else:
total_ops = 0
return self._get_result(m, total_ops)
def count_module(self, m, x, y, name):
# assume x is tuple of single tensor
result = self.ops[type(m)](m, x, y)
total_result = {
'name': name,
'input_size': tuple(x[0].size()),
'output_size': tuple(y.size()),
'module_type': type(m).__name__,
**result
}
self._push_result(total_result)
def sum_flops(self):
return sum([s['flops'] for s in self.results])
def sum_params(self):
return sum({s['name']: s['params'] for s in self.results}.values())
def format_results(self):
table = PrettyTable()
name_counter = Counter([s['name'] for s in self.results])
has_multi_use = any(map(lambda v: v > 1, name_counter.values()))
name_counter = Counter() # clear the counter to count from 0
headers = [
'Index',
'Name',
'Type',
'Weight Shape',
'FLOPs',
'#Params',
]
if has_multi_use:
headers.append('#Call')
table.field_names = headers
for i, result in enumerate(self.results):
row_values = [
i,
result['name'],
result['module_type'],
str(result['weight_shape']),
result['flops'],
result['params'],
]
name_counter[result['name']] += 1
if has_multi_use:
row_values.append(name_counter[result['name']])
table.add_row(row_values)
return table
def count_flops_params(model, x, custom_ops=None, verbose=True, mode='default'):
"""
Count FLOPs and Params of the given model. This function would
identify the mask on the module and take the pruned shape into consideration.
Note that, for sturctured pruning, we only identify the remained filters
according to its mask, and do not take the pruned input channels into consideration,
so the calculated FLOPs will be larger than real number.
Parameters
---------
model : nn.Module
Target model.
x : tuple or tensor
The input shape of data (a tuple), a tensor or a tuple of tensor as input data.
custom_ops : dict
A mapping of (module -> torch.nn.Module : custom operation)
the custom operation is a callback funtion to calculate
the module flops and parameters, it will overwrite the default operation.
for reference, please see ``ops`` in ``ModelProfiler``.
verbose : bool
If False, mute detail information about modules. Default is True.
mode : str
the mode of how to collect information. If the mode is set to ``default``,
only the information of convolution and linear will be collected.
If the mode is set to ``full``, other operations will also be collected.
Returns
-------
tuple of int, int and dict
Representing total FLOPs, total parameters, and a detailed list of results respectively.
The list of results are a list of dict, each of which contains (name, module_type, weight_shape,
flops, params, input_size, output_size) as its keys.
"""
assert isinstance(x, tuple) or isinstance(x, torch.Tensor)
assert mode in ['default', 'full']
original_device = next(model.parameters()).device
training = model.training
if isinstance(x, tuple) and all(isinstance(t, int) for t in x):
x = (torch.zeros(x).to(original_device), )
elif torch.is_tensor(x):
x = (x.to(original_device), )
else:
x = (t.to(original_device) for t in x)
handler_collection = []
profiler = ModelProfiler(custom_ops, mode)
prev_m = None
for name, m in model.named_modules():
# dealing with weight mask here
if isinstance(prev_m, PrunerModuleWrapper):
# weight mask is set to weight mask of its parent (wrapper)
weight_mask = prev_m.weight_mask
m.weight_mask = weight_mask
prev_m = m
if type(m) in profiler.ops:
# if a leaf node
_handler = m.register_forward_hook(functools.partial(profiler.count_module, name=name))
handler_collection.append(_handler)
model.eval()
with torch.no_grad():
model(*x)
# restore origin status
for name, m in model.named_modules():
if hasattr(m, 'weight_mask'):
delattr(m, 'weight_mask')
model.train(training).to(original_device)
for handler in handler_collection:
handler.remove()
if verbose:
# get detail information
print(profiler.format_results())
print('FLOPs total: {}'.format(profiler.sum_flops()))
print('#Params total: {}'.format(profiler.sum_params()))
return profiler.sum_flops(), profiler.sum_params(), profiler.results
================================================
FILE: src/aup/compression/torch/utils/mask_conflict.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import os
import logging
import torch
import numpy as np
from .shape_dependency import ChannelDependency, GroupDependency, CatPaddingDependency, InputChannelDependency
from .utils import get_module_by_name
# logging.basicConfig(level = logging.DEBUG)
_logger = logging.getLogger(__name__)
def fix_mask_conflict(masks, model=None, dummy_input=None, traced=None):
"""
MaskConflict fix the mask conflict for the channel dependencies
and group dependency.
Parameters
----------
masks : dict/str
A dict object that stores the masks or the path of the mask file
model : torch.nn.Module
model to fix the mask conflict
dummy_input : torch.Tensor
input example to trace the model
traced : torch._C.torch.jit.TopLevelTracedModule
the traced model of the target model, is this parameter is not None,
we donnot use the model and dummpy_input to get the trace graph.
"""
if isinstance(masks, str):
# if the input is the path of the mask_file
assert os.path.exists(masks)
masks = torch.load(masks)
assert len(masks) > 0, 'Mask tensor cannot be empty'
# if the user uses the model and dummy_input to trace the model, we
# should get the traced model handly, so that, we only trace the
# model once, GroupMaskConflict and ChannelMaskConflict will reuse
# this traced model.
if traced is None:
assert model is not None and dummy_input is not None
training = model.training
model.eval()
# We need to trace the model in eval mode
traced = torch.jit.trace(model, dummy_input)
model.train(training)
fix_group_mask = GroupMaskConflict(masks, model, dummy_input, traced)
masks = fix_group_mask.fix_mask()
fix_channel_mask = ChannelMaskConflict(masks, model, dummy_input, traced)
masks = fix_channel_mask.fix_mask()
padding_cat_mask = CatMaskPadding(masks, model, dummy_input, traced)
masks = padding_cat_mask.fix_mask()
return masks, fix_channel_mask.conv_prune_dim
class MaskFix:
def __init__(self, masks, model=None, dummy_input=None, traced=None):
# check if the parameters are valid
parameter_valid = False
if traced is not None:
parameter_valid = True
elif (model is not None) and (dummy_input is not None):
parameter_valid = True
if not parameter_valid:
raise Exception('The input parameters is invalid!')
self.model = model
self.dummy_input = dummy_input
self.traced = traced
self.masks = masks
def fix_mask(self):
raise NotImplementedError
def export(self, path):
"""
Export the masks after fixing the conflict to file.
"""
torch.save(self.masks, path)
class CatMaskPadding(MaskFix):
def __init__(self, masks, model, dummy_input=None, traced=None):
"""
CatMaskPadding find the layers whose output tensor is passed
to the same cat operation. The cat operation concatnates the
masks of the input tensors as the output mask, so when some
of the input layers of the cat operation are not pruned, we still
need to pass the masks of these non-pruned layers(the mask are
all ones) to the cat operation to ensure the shape of the output
mask is right.
Parameters
----------
masks : dict
a dict object that stores the masks
model : torch.nn.Module
model to fix the mask conflict
dummy_input : torch.Tensor
input example to trace the model
traced : torch._C.torch.jit.TopLevelTracedModule
the traced model of the target model, is this parameter is not None,
we donnot use the model and dummpy_input to get the trace graph.
"""
super(CatMaskPadding, self).__init__(masks, model, dummy_input, traced)
def fix_mask(self):
cat_padding_depen = CatPaddingDependency(
self.model, self.dummy_input, self.traced)
name_to_module = {}
for name, module in self.model.named_modules():
name_to_module[name] = module
depen = cat_padding_depen.dependency_sets
for layers in depen:
device = None
count = 0
for layer in layers:
if layer in self.masks:
count += 1
if device is None:
device = self.masks[layer]['weight'].device
if count == 0:
# no layer is pruned
continue
elif count == len(layers):
# all the layers have been pruned
continue
# pad the mask for the non-pruned layers
for layer in layers:
if layer in self.masks:
continue
module = name_to_module[layer]
w_shape = module.weight.data.size()
w_mask = torch.ones(w_shape).to(device)
b_mask = None
if hasattr(module, 'bias') and module.bias is not None:
# module.bias may be None
b_shape = module.bias.data.size()
b_mask = torch.ones(b_shape).to(device)
self.masks[layer] = {'weight': w_mask, 'bias': b_mask}
return self.masks
class GroupMaskConflict(MaskFix):
def __init__(self, masks, model=None, dummy_input=None, traced=None):
"""
GroupMaskConflict fix the mask conflict between the layers that
has group dependecy with each other.
Parameters
----------
masks : dict
a dict object that stores the masks
model : torch.nn.Module
model to fix the mask conflict
dummy_input : torch.Tensor
input example to trace the model
traced : torch._C.torch.jit.TopLevelTracedModule
the traced model of the target model, is this parameter is not None,
we donnot use the model and dummpy_input to get the trace graph.
"""
super(GroupMaskConflict, self).__init__(
masks, model, dummy_input, traced)
def fix_mask(self):
"""
Fix the mask conflict before the mask inference for the layers that
has group dependencies. This function should be called before the
mask inference of the 'speedup' module.
"""
group_depen = GroupDependency(
self.model, self.dummy_input, self.traced)
depens = group_depen.dependency
_logger.info(depens)
for layername in depens:
group = depens[layername]
if layername not in self.masks:
# this layer not pruned
continue
w_mask = self.masks[layername]['weight']
shape = w_mask.size()
count = np.prod(shape[1:])
all_ones = (w_mask.flatten(1).sum(-1) ==
count).nonzero().squeeze(1).tolist()
all_zeros = (w_mask.flatten(1).sum(-1) ==
0).nonzero().squeeze(1).tolist()
if len(all_ones) + len(all_zeros) < w_mask.size(0):
# In fine-grained pruning, skip this layer
_logger.info('Layers %s using fine-grained pruning', layername)
continue
assert shape[0] % group == 0
# Find the number of masked filter for each group (mini_masked).
# Because we have to keep the pruned filter can still
# be divided into the same number of groups, so we only can
# prune mini_masked filters for each group.
step = shape[0] / group
group_masked = []
for i in range(group):
_start = step * i
_end = step * (i+1)
_tmp_list = list(
filter(lambda x: _start <= x and x < _end, all_zeros))
group_masked.append(_tmp_list)
mini_masked = min([len(x) for x in group_masked])
for gm in group_masked:
for i in range(mini_masked, len(gm)):
# To keep the output channel number still being divisible to
# groups, we set the masks of following filters to be zero.
pos = gm[i]
self.masks[layername]['weight'][pos] = torch.ones(
shape[1:])
if 'bias' in self.masks[layername] and self.masks[layername]['bias'] is not None:
self.masks[layername]['bias'][pos] = 1
return self.masks
class ChannelMaskConflict(MaskFix):
def __init__(self, masks, model=None, dummy_input=None, traced=None):
"""
ChannelMaskConflict fix the mask conflict between the layers that
has channel dependecy with each other.
Parameters
----------
masks : dict
a dict object that stores the masks
model : torch.nn.Module
model to fix the mask conflict
dummy_input : torch.Tensor
input example to trace the model
graph : torch._C.torch.jit.TopLevelTracedModule
the traced graph of the target model, is this parameter is not None,
we donnot use the model and dummpy_input to get the trace graph.
"""
super(ChannelMaskConflict, self).__init__(
masks, model, dummy_input, traced)
self.conv_prune_dim = detect_mask_prune_dim(masks, model)
_logger.info('detected conv prune dim: %s', self.conv_prune_dim)
def fix_mask(self):
"""
Fix the mask conflict before the mask inference for the layers that
has shape dependencies. This function should be called before the
mask inference of the 'speedup' module. Only structured pruning masks
are supported.
"""
if self.conv_prune_dim == 0:
channel_depen = ChannelDependency(
self.model, self.dummy_input, self.traced)
else:
channel_depen = InputChannelDependency(
self.model, self.dummy_input, self.traced)
depen_sets = channel_depen.dependency_sets
sum_idx = (1, 2, 3) if self.conv_prune_dim == 0 else (0, 2, 3)
(_tmp_name, _tmp_tensor) = list(self.masks.items())[0]
device = _tmp_tensor['weight'].device
for dset in depen_sets:
if len(dset) <= 1:
continue
# channel_masks is a list, each element is None or a vector, for example:
# [[0, 1, 1, 0, 0], [0, 0, 1, 1, 0], None], None means no channel
# is pruned.
channel_masks = []
fine_grained = False
for name in dset:
if name in self.masks:
_, m = get_module_by_name(self.model, name)
assert m is not None
mask = self.masks[name]['weight']
if type(m).__name__ == 'Conv2d':
channel_mask = (mask.abs().sum(sum_idx) != 0).int()
channel_masks.append(channel_mask)
if (channel_mask.sum() * (mask.numel() / mask.shape[self.conv_prune_dim])).item() != (mask > 0).sum().item():
fine_grained = True
elif type(m).__name__ == 'Linear':
channel_masks.append((mask.abs().sum(0) != 0).int())
elif type(m).__name__ == 'BatchNorm2d':
channel_masks.append(mask.int())
elif type(m).__name__ == 'ConvTranspose2d':
# convtranspose have difference memory layout, so that we need create
# a tmp_sum_idx for conv_transpose
tmp_sum_idx = (
0, 2, 3) if self.conv_prune_dim == 0 else (1, 2, 3)
channel_mask = (mask.abs().sum(tmp_sum_idx) != 0).int()
channel_masks.append(channel_mask)
if (channel_mask.sum() * (mask.numel() / mask.shape[1-self.conv_prune_dim])).item() != (mask > 0).sum().item():
fine_grained = True
else:
raise RuntimeError('unsupported module type: {}'.format(type(m).__name__))
else:
# no mask means not pruned, equivlent to full masks
channel_masks.append(None)
if fine_grained:
_logger.info(
'fine-grained mask detected, skip solving conflict for this set: %s', dset)
continue
if all(x is None for x in channel_masks):
continue
num_channels_list = [len(x)
for x in channel_masks if x is not None]
# number of channels in same set should be identical
assert len(set(num_channels_list)) == 1
num_channels = num_channels_list[0]
for i, dim_mask in enumerate(channel_masks):
if dim_mask is None:
channel_masks[i] = torch.ones(num_channels).int().to(device)
# merge masks with 'or'
merged_channel_mask = channel_masks[0].clone()
for i in range(1, len(channel_masks)):
merged_channel_mask = (
(merged_channel_mask + channel_masks[i]) != 0).int()
merged_index = torch.nonzero(merged_channel_mask, as_tuple=True)[0]
for name in dset:
if name not in self.masks:
assert all(merged_channel_mask)
continue
orig_mask = self.masks[name]['weight']
_, m = get_module_by_name(self.model, name)
new_mask = torch.zeros_like(orig_mask)
if type(m).__name__ == 'Conv2d':
if self.conv_prune_dim == 0:
new_mask[merged_index, :, :, :] = 1.
else:
new_mask[:, merged_index, :, :] = 1.
elif type(m).__name__ == 'Linear':
new_mask[:, merged_index] = 1.
elif type(m).__name__ == 'BatchNorm2d':
new_mask = merged_index.type_as(orig_mask)
else:
raise RuntimeError('unsupported module type: {}'.format(type(m).__name__))
self.masks[name]['weight'] = new_mask
if 'bias' in self.masks[name] and self.masks[name]['bias'] is not None:
if type(m).__name__ == 'Conv2d':
assert self.conv_prune_dim == 0
self.masks[name]['bias'] = merged_channel_mask.type_as(
self.masks[name]['bias'])
return self.masks
def detect_mask_prune_dim(masks, model):
"""
Detect how the masks of convolutional layers are pruned.
Parameters
----------
masks: dict
A dict object that stores the masks.
model: nn.Module
Model object which the mask can be applied on.
Returns:
-------
How the masks of convolutional layers are pruned, this depends on pruning algorithms, it should
return 1 for masks generated by AMCPruner, and returns 0 for masks generated by the rest
NNI builtin pruners.
0: filter pruning, prune filters of weights which causes channels of output feature maps are pruned.
1: channel pruning, prune kernels corresponding to each input channels which causes channels of
input feature maps are pruned.
"""
dim0_preserved, dim1_preserved = 0., 0.
dim0_num, dim1_num = 0., 0.
for module_name in masks:
_, m = get_module_by_name(model, module_name)
if m is None or type(m).__name__ != 'Conv2d':
continue
mask = masks[module_name]['weight'].clone()
assert (mask >= 0).sum() == mask.numel(), \
"mask values should be greater than or equal to 0."
mask = (mask > 0).int()
mask = mask.view(mask.shape[0], mask.shape[1], -1)
dim0_mask = (mask.sum((1, 2)) > 0).int()
dim1_mask = (mask.sum((0, 2)) > 0).int()
dim0_preserved += dim0_mask.sum().item()
dim1_preserved += dim1_mask.sum().item()
dim0_num += len(dim0_mask)
dim1_num += len(dim1_mask)
if dim0_num == 0 or dim1_num == 0:
_logger.warning('no multi-dimension masks found.')
return 0
dim0_sparsity, dim1_sparsity = 1. - dim0_preserved / \
dim0_num, 1. - dim1_preserved / dim1_num
_logger.info('dim0 sparsity: %f', dim0_sparsity)
_logger.info('dim1 sparsity: %f', dim1_sparsity)
if dim0_sparsity == dim1_sparsity == 0.:
_logger.warning('nothing masked.')
if dim0_sparsity > 0 and dim1_sparsity > 0:
_logger.warning('both dim0 and dim1 masks found.')
return 0 if dim0_sparsity >= dim1_sparsity else 1
================================================
FILE: src/aup/compression/torch/utils/num_param_counter.py
================================================
def get_total_num_weights(model, op_types=['default']):
'''
calculate the total number of weights
Returns
-------
int
total weights of all the op considered
'''
num_weights = 0
for _, module in model.named_modules():
if module == model:
continue
if 'default' in op_types or type(module).__name__ in op_types:
num_weights += module.weight.data.numel()
return num_weights
================================================
FILE: src/aup/compression/torch/utils/sensitivity_analysis.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
# Modified work Copyright (c) 2018 LG Electronics Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
import copy
import csv
import logging
from collections import OrderedDict
import numpy as np
import torch.nn as nn
SUPPORTED_OP_NAME = ['Conv2d', 'Conv1d']
SUPPORTED_OP_TYPE = [getattr(nn, name) for name in SUPPORTED_OP_NAME]
logger = logging.getLogger('Sensitivity_Analysis')
logger.setLevel(logging.INFO)
class SensitivityAnalysis:
def __init__(self, model, val_func, sparsities=None, prune_type='l1', early_stop_mode=None, early_stop_value=None):
"""
Perform sensitivity analysis for this model.
Parameters
----------
model : torch.nn.Module
the model to perform sensitivity analysis
val_func : function
validation function for the model. Due to
different models may need different dataset/criterion
, therefore the user need to cover this part by themselves.
In the val_func, the model should be tested on the validation dateset,
and the validation accuracy/loss should be returned as the output of val_func.
There are no restrictions on the input parameters of the val_function.
User can use the val_args, val_kwargs parameters in analysis
to pass all the parameters that val_func needed.
sparsities : list
The sparsity list provided by users. This parameter is set when the user
only wants to test some specific sparsities. In the sparsity list, each element
is a sparsity value which means how much weight the pruner should prune. Take
[0.25, 0.5, 0.75] for an example, the SensitivityAnalysis will prune 25% 50% 75%
weights gradually for each layer.
prune_type : str
The pruner type used to prune the conv layers, default is 'l1',
and 'l2', 'fine-grained' is also supported.
early_stop_mode : str
If this flag is set, the sensitivity analysis
for a conv layer will early stop when the validation metric(
for example, accurracy/loss) has alreay meet the threshold. We
support four different early stop modes: minimize, maximize, dropped,
raised. The default value is None, which means the analysis won't stop
until all given sparsities are tested. This option should be used with
early_stop_value together.
minimize: The analysis stops when the validation metric return by the val_func
lower than early_stop_value.
maximize: The analysis stops when the validation metric return by the val_func
larger than early_stop_value.
dropped: The analysis stops when the validation metric has dropped by early_stop_value.
raised: The analysis stops when the validation metric has raised by early_stop_value.
early_stop_value : float
This value is used as the threshold for different earlystop modes.
This value is effective only when the early_stop_mode is set.
"""
from ..pruning.constants_pruner import PRUNER_DICT
self.model = model
self.val_func = val_func
self.target_layer = OrderedDict()
self.ori_state_dict = copy.deepcopy(self.model.state_dict())
self.target_layer = {}
self.sensitivities = {}
if sparsities is not None:
self.sparsities = sorted(sparsities)
else:
self.sparsities = np.arange(0.1, 1.0, 0.1)
self.sparsities = [np.round(x, 2) for x in self.sparsities]
self.Pruner = PRUNER_DICT[prune_type]
self.early_stop_mode = early_stop_mode
self.early_stop_value = early_stop_value
self.ori_metric = None # original validation metric for the model
# already_pruned is for the iterative sensitivity analysis
# For example, sensitivity_pruner iteratively prune the target
# model according to the sensitivity. After each round of
# pruning, the sensitivity_pruner will test the new sensitivity
# for each layer
self.already_pruned = {}
self.model_parse()
@property
def layers_count(self):
return len(self.target_layer)
def model_parse(self):
for name, submodel in self.model.named_modules():
for op_type in SUPPORTED_OP_TYPE:
if isinstance(submodel, op_type):
self.target_layer[name] = submodel
self.already_pruned[name] = 0
def _need_to_stop(self, ori_metric, cur_metric):
"""
Judge if meet the stop conditon(early_stop, min_threshold,
max_threshold).
Parameters
----------
ori_metric : float
original validation metric
cur_metric : float
current validation metric
Returns
-------
stop : bool
if stop the sensitivity analysis
"""
if self.early_stop_mode is None:
# early stop mode is not enable
return False
assert self.early_stop_value is not None
if self.early_stop_mode == 'minimize':
if cur_metric < self.early_stop_value:
return True
elif self.early_stop_mode == 'maximize':
if cur_metric > self.early_stop_value:
return True
elif self.early_stop_mode == 'dropped':
if cur_metric < ori_metric - self.early_stop_value:
return True
elif self.early_stop_mode == 'raised':
if cur_metric > ori_metric + self.early_stop_value:
return True
return False
def analysis(self, val_args=None, val_kwargs=None, specified_layers=None):
"""
This function analyze the sensitivity to pruning for
each conv layer in the target model.
If start and end are not set, we analyze all the conv
layers by default. Users can specify several layers to
analyze or parallelize the analysis process easily through
the start and end parameter.
Parameters
----------
val_args : list
args for the val_function
val_kwargs : dict
kwargs for the val_funtion
specified_layers : list
list of layer names to analyze sensitivity.
If this variable is set, then only analyze
the conv layers that specified in the list.
User can also use this option to parallelize
the sensitivity analysis easily.
Returns
-------
sensitivities : dict
dict object that stores the trajectory of the
accuracy/loss when the prune ratio changes
"""
if val_args is None:
val_args = []
if val_kwargs is None:
val_kwargs = {}
# Get the original validation metric(accuracy/loss) before pruning
# Get the accuracy baseline before starting the analysis.
self.ori_metric = self.val_func(*val_args, **val_kwargs)
namelist = list(self.target_layer.keys())
if specified_layers is not None:
# only analyze several specified conv layers
namelist = list(filter(lambda x: x in specified_layers, namelist))
for name in namelist:
self.sensitivities[name] = {}
for sparsity in self.sparsities:
# here the sparsity is the relative sparsity of the
# the remained weights
# Calculate the actual prune ratio based on the already pruned ratio
real_sparsity = (
1.0 - self.already_pruned[name]) * sparsity + self.already_pruned[name]
# TODO In current L1/L2 Filter Pruner, the 'op_types' is still necessary
# I think the L1/L2 Pruner should specify the op_types automaticlly
# according to the op_names
cfg = [{'sparsity': real_sparsity, 'op_names': [
name], 'op_types': ['Conv2d']}]
pruner = self.Pruner(self.model, cfg)
pruner.compress()
val_metric = self.val_func(*val_args, **val_kwargs)
logger.info('Layer: %s Sparsity: %.2f Validation Metric: %.4f',
name, real_sparsity, val_metric)
self.sensitivities[name][sparsity] = val_metric
pruner._unwrap_model()
del pruner
# check if the current metric meet the stop condition
if self._need_to_stop(self.ori_metric, val_metric):
break
# reset the weights pruned by the pruner, because the
# input sparsities is sorted, so we donnot need to reset
# weight of the layer when the sparsity changes, instead,
# we only need reset the weight when the pruning layer changes.
self.model.load_state_dict(self.ori_state_dict)
return self.sensitivities
def export(self, filepath):
"""
Export the results of the sensitivity analysis
to a csv file. The firstline of the csv file describe the content
structure. The first line is constructed by 'layername' and sparsity
list. Each line below records the validation metric returned by val_func
when this layer is under different sparsities. Note that, due to the early_stop
option, some layers may not have the metrics under all sparsities.
layername, 0.25, 0.5, 0.75
conv1, 0.6, 0.55
conv2, 0.61, 0.57, 0.56
Parameters
----------
filepath : str
Path of the output file
"""
str_sparsities = [str(x) for x in self.sparsities]
header = ['layername'] + str_sparsities
with open(filepath, 'w') as csvf:
csv_w = csv.writer(csvf)
csv_w.writerow(header)
for layername in self.sensitivities:
row = []
row.append(layername)
for sparsity in sorted(self.sensitivities[layername].keys()):
row.append(self.sensitivities[layername][sparsity])
csv_w.writerow(row)
def update_already_pruned(self, layername, ratio):
"""
Set the already pruned ratio for the target layer.
"""
self.already_pruned[layername] = ratio
def load_state_dict(self, state_dict):
"""
Update the weight of the model
"""
self.ori_state_dict = copy.deepcopy(state_dict)
self.model.load_state_dict(self.ori_state_dict)
================================================
FILE: src/aup/compression/torch/utils/shape_dependency.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import csv
import logging
__all__ = ['ChannelDependency', 'GroupDependency',
'CatPaddingDependency', 'InputChannelDependency']
CONV_TYPE = 'aten::_convolution'
ADD_TYPES = ['aten::add', 'aten::add_']
CAT_TYPE = 'aten::cat'
logger = logging.getLogger('Shape_Dependency')
RESHAPE_OPS = [CAT_TYPE, 'aten::view',
'aten::reshape', 'aten::flatten', 'aten::mean']
class Dependency:
def __init__(self, model=None, dummy_input=None, traced_model=None):
"""
Build the graph for the model.
"""
from aup.compression.torch._graph_utils import TorchModuleGraph
# check if the input is legal
if traced_model is None:
# user should provide model & dummy_input to trace
# the model or a already traced model
assert model is not None and dummy_input is not None
self.graph = TorchModuleGraph(model, dummy_input, traced_model)
self.dependency = dict()
self.build_dependency()
def build_dependency(self):
raise NotImplementedError
def export(self, filepath):
raise NotImplementedError
class ChannelDependency(Dependency):
def __init__(self, model=None, dummy_input=None, traced_model=None):
"""
This model analyze the channel dependencies between the conv
layers in a model.
Parameters
----------
model : torch.nn.Module
The model to be analyzed.
data : torch.Tensor
The example input data to trace the network architecture.
traced_model : torch._C.Graph
if we alreay has the traced graph of the target model, we donnot
need to trace the model again.
"""
super(ChannelDependency, self).__init__(
model, dummy_input, traced_model)
def _get_parent_layers(self, node):
"""
Find the nearest father conv layers for the target node.
Parameters
---------
node : torch._C.Node
target node.
Returns
-------
parent_layers: list
nearest father conv/linear layers for the target worknode.
"""
parent_layers = []
queue = []
queue.append(node)
while queue:
curnode = queue.pop(0)
if curnode.op_type == 'Conv2d' or curnode.op_type == 'Linear' or curnode.op_type == 'ConvTranspose2d':
# find the first met conv
parent_layers.append(curnode.name)
continue
parents = self.graph.find_predecessors(curnode.unique_name)
parents = [self.graph.name_to_node[name] for name in parents]
for parent in parents:
queue.append(parent)
return parent_layers
def build_dependency(self):
"""
Build the channel dependency for the conv layers
in the model.
"""
# unpack the tuple/list manually before analyze the
# channel dependency
self.graph.unpack_manually()
for node in self.graph.nodes_py.nodes_op:
parent_layers = []
# find the node that contains aten::add
# or aten::cat operations
if node.op_type in ADD_TYPES:
parent_layers = self._get_parent_layers(node)
elif node.op_type == CAT_TYPE:
# To determine if this cat operation will introduce channel
# dependency, we need the specific input parameters of the cat
# opertion. To get the input parameters of the cat opertion, we
# need to traverse all the cpp_nodes included by this NodePyGroup,
# because, TorchModuleGraph merges the important nodes and the adjacent
# unimportant nodes (nodes started with prim::attr, for example) into a
# NodepyGroup.
cat_dim = None
for cnode in node.node_cpps:
if cnode.kind() == CAT_TYPE:
cat_dim = list(cnode.inputs())[1].toIValue()
break
if cat_dim != 1:
parent_layers = self._get_parent_layers(node)
dependency_set = set(parent_layers)
# merge the dependencies
for parent in parent_layers:
if parent in self.dependency:
dependency_set.update(self.dependency[parent])
# save the dependencies
for _node in dependency_set:
self.dependency[_node] = dependency_set
def export(self, filepath):
"""
export the channel dependencies as a csv file.
The layers at the same line have output channel
dependencies with each other. For example,
layer1.1.conv2, conv1, and layer1.0.conv2 have
output channel dependencies with each other, which
means the output channel(filters) numbers of these
three layers should be same with each other, otherwise
the model may has shape conflict.
Output example:
Dependency Set,Convolutional Layers
Set 1,layer1.1.conv2,layer1.0.conv2,conv1
Set 2,layer1.0.conv1
Set 3,layer1.1.conv1
"""
header = ['Dependency Set', 'Layers']
setid = 0
visited = set()
with open(filepath, 'w') as csvf:
csv_w = csv.writer(csvf, delimiter=',')
csv_w.writerow(header)
for node in self.graph.nodes_py.nodes_op:
if node.op_type != 'Conv2d' or node in visited:
continue
setid += 1
row = ['Set %d' % setid]
if node.name not in self.dependency:
visited.add(node)
row.append(node.name)
else:
for other in self.dependency[node.name]:
visited.add(self.graph.name_to_node[other])
row.append(other)
csv_w.writerow(row)
@property
def dependency_sets(self):
"""
Get the list of the dependency set.
Returns
-------
dependency_sets : list
list of the dependency sets. For example,
[set(['conv1', 'conv2']), set(['conv3', 'conv4'])]
"""
d_sets = []
visited = set()
for node in self.graph.nodes_py.nodes_op:
if node.op_type != 'Conv2d' or node in visited:
continue
tmp_set = set()
if node.name not in self.dependency:
visited.add(node)
tmp_set.add(node.name)
else:
for other in self.dependency[node.name]:
visited.add(self.graph.name_to_node[other])
tmp_set.add(other)
d_sets.append(tmp_set)
return d_sets
def reshape_break_channel_dependency(op_node):
"""
The reshape operations such as (reshape, view, flatten) may break
the channel dependency. We need to check the input parameters of
these reshape operations to check if this reshape node will break
the channel dependency. However, it's complicated to analyze the the input
parameters for each reshape function and infer if it will break the channel
dependency. So currently, we just check if the input channel and the output
channel is the same, if so, then we can say the original reshape function
doesn't want to change the number of the channels, which means the channel
dependency is not broken. In contrast, the original reshap operation wants
to change the number of channels, so it breaks the channel dependency.
Parameters
----------
opnode: NodePyOP
A Op node of the graph.
Returns
-------
bool
If this operation will break the channel dependency.
"""
in_shape = op_node.auxiliary['in_shape']
out_shape = op_node.auxiliary['out_shape']
in_channel = in_shape[1]
out_channel = out_shape[1]
return in_channel != out_channel
class InputChannelDependency(ChannelDependency):
"""
Some pruners may prune the input channel of the convolutional
layers. While pruning the input channel of the convolutional layers,
the layers that share the same input tensor should prune the same
channels, and we say these layers that share the same input tensor/channel
has the input channel dependency. If we only prune the input channel of one
layer in the dependency set, there will be a shape conflict for the other
layers in the same dependency set, which may trigger a runtime error.
Here we judge whether the application will truncate the dependency by analyzing
whether the number of channels before and after the operation has changed.
If not, the input channel dependency will be passed to the following nodes.
"""
def __init__(self, model, dummy_input=None, traced_model=None):
"""
This model analyze the input channel dependencies between the conv
layers in a model.
Parameters
----------
model : torch.nn.Module
The model to be analyzed.
data : torch.Tensor
The example input data to trace the network architecture.
traced_model : torch._C.Graph
if we alreay has the traced graph of the target model, we donnot
need to trace the model again.
"""
super(InputChannelDependency, self).__init__(
model, dummy_input, traced_model)
def _get_following_convs(self, tensor):
queue = []
key_layers = []
queue.extend(self.graph.input_to_node[tensor])
while queue:
curnode = queue.pop(0)
if curnode.op_type == 'Conv2d' or curnode.op_type == 'Linear' or curnode.op_type == 'ConvTranspose2d':
# find the first met conv
key_layers.append(curnode.name)
continue
elif curnode.op_type in RESHAPE_OPS:
# check if the reshape operation will break the channel dependency
if reshape_break_channel_dependency(curnode):
# reshape operations also breaks the dependency relationship
continue
successors = self.graph.find_successors(curnode.unique_name)
successors = [self.graph.name_to_node[name] for name in successors]
for layer in successors:
queue.append(layer)
return key_layers
def build_dependency(self):
"""
Build the input channel dependencies.
The `InputChannelDependency` indicates the layers that have
dependencies when pruning the input channel of the conv layers.
In contrast, `ChannelDependency` indicates the dependent layers
when pruning the output channles of conv layers (for example, L1FilterPruner).
"""
# unpack the tuple or list manually
self.graph.unpack_manually()
for tensor in self.graph.input_to_node:
# start from this tensor, find all the conv layers that
# take this tensor as input. Similar to the `ChannelDependency`
# the conv layer will truncate the dependencies
layers = self._get_following_convs(tensor)
dependency_set = set(layers)
for layer in layers:
if layer in self.dependency:
dependency_set.update(self.dependency[layer])
for layer in dependency_set:
self.dependency[layer] = dependency_set
class CatPaddingDependency(ChannelDependency):
def __init__(self, model=None, dummy_input=None, traced_model=None):
super(CatPaddingDependency, self).__init__(
model, dummy_input, traced_model)
def build_dependency(self):
"""
Build the cat padding dependencies.
If the output features of several layers are stitched together
by cat operation, then these layers have cat padding dependencies.
This is because when inferring the cat mask, we need all the input
masks for the cat operation. At this time we need to know the source
of all input vectors of a cat operation.
"""
for node in self.graph.nodes_py.nodes_op:
parent_layers = []
if node.op_type == CAT_TYPE:
parent_layers = self._get_parent_layers(node)
dependency_set = set(parent_layers)
# merge the dependencies
for parent in parent_layers:
if parent in self.dependency:
dependency_set.update(self.dependency[parent])
# save the dependencies
for _node in dependency_set:
self.dependency[_node] = dependency_set
@property
def dependency_sets(self):
d_sets = []
visited = set()
for nodename in self.dependency:
if nodename in visited:
continue
d_sets.append(self.dependency[nodename])
return d_sets
def export(self, filepath):
"""
Export the dependencies into a file.
In the output file, each line contains a set of layers
whose output features are stitched together by the cat
operation.
output example:
Dependency Set, Layers
set1, Conv1, Conv2
set2, Conv3, Conv4
"""
header = ['Dependency Set', 'Layers']
setid = 0
with open(filepath, 'w') as csvf:
csv_w = csv.writer(csvf, delimiter=',')
csv_w.writerow(header)
for layers in self.dependency_sets:
setid += 1
row = ['Set %d' % setid]
row.extend(list(layers))
csv_w.writerow(row)
class GroupDependency(Dependency):
def __init__(self, model=None, dummy_input=None, traced_model=None):
"""
This model analyze the group dependencis between the conv
layers in a model.
Parameters
----------
model : torch.nn.Module
The model to be analyzed.
data : torch.Tensor
The example input data to trace the network architecture.
traced_model : torch._C.Graph
if we alreay has the traced graph of the target model, we donnot
need to trace the model again.
"""
super(GroupDependency, self).__init__(model, dummy_input, traced_model)
def _get_parent_convs(self, node):
"""
Find the nearest father conv layers for the target node.
Parameters
---------
node : torch._C.Node
target node.
Returns
-------
parent_layers : list
nearest father conv layers for the target node. Due to the group
dependency only exists between the conv layers, so we only find
the parent conv layers.
"""
parent_layers = []
# the input node is a Conv node
predeessors = self.graph.find_predecessors(node.unique_name)
predeessors = [self.graph.name_to_node[x] for x in predeessors]
queue = predeessors
while queue:
curnode = queue.pop(0)
if curnode.op_type == 'Conv2d' or curnode.op_type == 'ConvTranspose2d':
# find the first met conv
parent_layers.append(curnode.name)
continue
parents = self.graph.find_predecessors(curnode.unique_name)
parents = [self.graph.name_to_node[name] for name in parents]
for parent in parents:
queue.append(parent)
return parent_layers
def _get_conv_groups(self, node_group):
"""
Get the number of groups for a convolutional layer.
Parameters
----------
node_group : NodePyGroup
target node.
Returns
-------
group : int
the number of the groups of the target conv layer.
"""
cpp_conv = list(filter(lambda x: x.kind() ==
CONV_TYPE, node_group.node_cpps))
assert len(cpp_conv) == 1
cpp_conv = cpp_conv[0]
inputs = list(cpp_conv.inputs())
# get the number of the group from the input parameters
group = inputs[8].toIValue()
return group
def build_dependency(self):
"""
Build the channel dependency for the conv layers
in the model. This function return the group number
of each conv layers. Note that, here, the group count
of conv layers may be larger than their originl groups.
This is because that the input channel will also be grouped
for the group conv layers. To make this clear, assume we
have two group conv layers: conv1(group=2), conv2(group=4).
conv2 takes the output features of conv1 as input.
Then we have to the filters of conv1 can still be
divided into 4 groups after filter pruning, because
the input channels of conv2 shoule be divided into
4 groups.
Returns
-------
self.dependency : dict
key: the name of conv layers, value: the minimum value that the number of
filters should be divisible to.
"""
for node in self.graph.nodes_py.nodes_op:
if node.op_type == 'Conv2d' or node.op_type == 'ConvTranspose2d':
group = self._get_conv_groups(node)
if node.name in self.dependency:
# the conv layer whose group is larger than 1 will require that
# it's number of output channel to be divisible by the number of group.
self.dependency[node.name] = max(
self.dependency[node.name], group)
else:
self.dependency[node.name] = group
if group > 1:
# for the conv layer whose group is larger than 1, it will require the number
# of output channels of their parent conv layer to be divisible by group.
parent_convs = self._get_parent_convs(node)
for parent in parent_convs:
if parent in self.dependency:
self.dependency[parent] = max(
self.dependency[parent], group)
else:
self.dependency[parent] = group
return self.dependency
def export(self, filepath):
"""
export the group dependency to a csv file.
Each line describes a convolution layer, the
first part of each line is the Pytorch module
name of the conv layer. The second part of each
line is the group count of the filters in this layer.
Note that, the group count may be larger than this
layers original group number.
output example:
Conv layer, Groups
Conv1, 1
Conv2, 2
Conv3, 4
"""
header = ['Conv Layer Name', 'Group']
with open(filepath, 'w') as csvf:
csv_w = csv.writer(csvf, delimiter=',')
csv_w.writerow(header)
for name in self.dependency:
group = self.dependency[name]
csv_w.writerow([name, group])
@property
def dependency_sets(self):
return self.dependency
================================================
FILE: src/aup/compression/torch/utils/utils.py
================================================
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
def get_module_by_name(model, module_name):
"""
Get a module specified by its module name
Parameters
----------
model : pytorch model
the pytorch model from which to get its module
module_name : str
the name of the required module
Returns
-------
module, module
the parent module of the required module, the required module
"""
name_list = module_name.split(".")
for name in name_list[:-1]:
if hasattr(model, name):
model = getattr(model, name)
else:
return None, None
if hasattr(model, name_list[-1]):
leaf_module = getattr(model, name_list[-1])
return model, leaf_module
else:
return None, None
================================================
FILE: src/aup/compression/utils.py
================================================
"""
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import json
import logging
import signal
import sys
import _thread
import threading
import time
from ..EE.Resource import get_resource_manager
from ..aup import BasicConfig
from ..EE.Job import Job
from ..utils import get_default_username, get_default_connector, check_missing_key
SERIALIZATION_SEPARATOR = "."
logger = logging.getLogger(__name__)
hyperparams_params = ["range", "type", "interval", "n"]
def verify_compression_config(config):
"""
verify the experiment configuration is fulfilled for experiment
:param config: experiment configuration
:type config: dict
:return: config if verified
:rtype: dict
"""
check_missing_key(config, "name", "Missing required value for 'script'.", log=logger)
check_missing_key(config, "script", "Missing required value for 'script'.", log=logger)
check_missing_key(config, "resource", "Missing required value for 'resource'", log=logger)
check_missing_key(config, "compression", "Missing required map 'compression'", log=logger)
if "compression_framework" not in config["compression"]:
check_missing_key(config["compression"], "framework", "Missing required value for 'compression.framework'", log=logger)
if "compression_type" not in config["compression"]:
check_missing_key(config["compression"], "type", "Missing required value for 'compression.type'", log=logger)
check_missing_key(config["compression"], "compressor", "Missing required value for 'compression.compressor'", log=logger)
check_missing_key(config["compression"], "config_list", "Missing required value for 'compression.config_list'", log=logger)
return config
def run_non_automatic_experiment(exp_config, aup_folder, user=None, eid=None, start=True):
"""
Non-automatic experiment pipeline, separated into a different function for unit test purposes
:param exp_config: experiment configuration
:type exp_config: dict
:param aup_folder: the folder containing the .aup files
:type aup_folder: str
:param user: the username
:type user: str
:param eid: eid of the experiment, if re-running
:type eid: int
:param start: whether or not to start the experiment immediately
:type start: bool
"""
user = get_default_username(user)
create_only = False
exp_config["parameter_config"] = []
connector = get_default_connector(auppath=aup_folder, log=logger)
if "resource_args" in exp_config:
resource_manager = get_resource_manager(exp_config["resource"], connector,
n_parallel=1, auppath=aup_folder,
maximize=True,
**exp_config["resource_args"],
workingdir=exp_config['workingdir'],
script=exp_config['script'], one_shot=True)
else:
resource_manager = get_resource_manager(exp_config["resource"], connector,
n_parallel=1, auppath=aup_folder,
maximize=True,
workingdir=exp_config['workingdir'],
script=exp_config['script'], one_shot=True)
if eid is None:
if start is True:
eid = resource_manager.connector.start_experiment(user, exp_config["name"], json.dumps(exp_config))
else:
eid = resource_manager.connector.create_experiment(user, exp_config["name"], json.dumps(exp_config))
create_only = True
else:
resource_manager.connector.start_experiment_by_eid(eid)
resource_manager.eid = eid
resource_manager.save_model = False
if "runtime_args" in exp_config:
runtime_args = exp_config['runtime_args']
else:
runtime_args = {}
logger.info("Experiment %d is created" % eid)
logger.debug("Experiment config is %s" % json.dumps(exp_config))
if create_only:
connector.close()
return eid, None
def _check_status():
if connector is None or eid is None:
logger.warning("Could not start thread for checking external experiment stopping requests.")
return
while True:
try:
if connector.is_closed():
logger.debug("Closing down clean-up thread.")
return
status = connector.maybe_get_experiment_status(eid)
if status == "REQUEST_STOP":
return _thread.interrupt_main()
except Exception as ex:
logger.critical("Error in clean-up thread: {}".format(ex))
finally:
time.sleep(5)
request_stop_thr = threading.Thread(target=_check_status)
request_stop_thr.start()
rid = resource_manager.get_available(user, exp_config["resource"])
if rid is None:
logger.warning("Not enough resources to run compression")
return eid
finished = False
def update(score, jid):
nonlocal finished
if score == "ERROR":
logger.fatal("Compression job {} failed".format(job.jid))
resource_manager.finish_job(job.jid, None, "FAILED")
else:
logger.critical("Compression job {} finished successfully with result {}".format(job.jid, score))
resource_manager.finish_job(job.jid, score, "FINISHED")
finished = True
logger.info("# Running one-time compression experiment {}".format(eid))
job_config = BasicConfig(**exp_config["compression"])
job_config["save_model"] = True
job_config["folder_name"] = "models_{}".format(eid)
job = Job(exp_config["script"], job_config, exp_config["workingdir"], retries=0)
def _suspend(sig, frame):
logger.fatal("Compression ended at user's request")
resource_manager.suspend()
resource_manager.finish_job(job.jid, None)
resource_manager.finish(status="STOPPED")
connector.close()
if request_stop_thr is not None:
request_stop_thr.join()
sys.exit(1)
signal.signal(signal.SIGINT, lambda x, y: _suspend(x, y))
def _force_refresh(sig, frame):
# currently useful for async resource manager timers
resource_manager.refresh()
signal.signal(signal.SIGUSR1, lambda x, y: _force_refresh(x, y))
job.jid = resource_manager.connector.job_started(eid, rid, job_config)
resource_manager.run_job(job, rid, exp_config, update, **runtime_args)
def _finish_callback():
nonlocal finished
while not finished:
time.sleep(1)
resource_manager.finish(status="FINISHED")
connector.close()
if request_stop_thr is not None:
request_stop_thr.join()
return eid, _finish_callback
def _extract_compression_hyperparameters(params):
"""
Helper function used to extract a list of compression hyperparameters from
config_list mappings.
This function searches recursively in compression config_list entries for keywords
mapped to dictionaries containing a "range" and "type", and resolves for them a
serialized name based on the depth at which they are found.
:param params: config_list element
:type params: dict
:return: list of hyperparameters as dictionaries containing "name", "range" and
"type"
:rtype: list
"""
args = []
for key, val in params.items():
if isinstance(val, dict):
if "type" in val:
args += [{
"name": key,
**{key: val[key] for key in hyperparams_params if key in val},
}]
else:
ret = _extract_compression_hyperparameters(val)
ret = [{
**val,
"name": "{}{}{}".format(key, SERIALIZATION_SEPARATOR, val["name"]),
} for val in ret
]
args += ret
return args
def translate_compression_config(config):
"""
Helper function used to parse the experiment config for automatic compression experiments
into the HPO experiment config format.
:param config: experiment config
:type config: dict
:return: modified experiment config
:rtype: dict
"""
config = config.copy()
# Expand op names (resolve "expand_op_names" keyword in entries)
new_config_list = []
for param in config["compression"]["config_list"]:
if ("expand_op_names" not in param or param["expand_op_names"]) and \
"op_names" in param:
for op_name in param["op_names"]:
# For each op_name in "op_names", create a separate config_list entry
# with only that op_name and otherwise all other key-value pairs (e.g.
# "op_types", "sparsity", "quantize_type" etc.)
new_config_list += [{
**{"op_names": [op_name]},
**{key: val for key, val in param.items()
if key not in ("expand_op_names", "op_names")}
}]
else:
if ("expand_op_names" not in param or param["expand_op_names"]) and \
"op_names" not in param:
logger.debug("No op_names param supplied in config_list for expand_op_names, " +
"can not supply individual parameters to each layer.")
new_config_list += [{
key: val for key, val in param.items()
if key not in ("expand_op_names",)
}]
config["compression"]["config_list"] = new_config_list
# Resolving compression hyperparams to previous parameter_config format
# The following code block adds a serialization name to all hyperparameters found in config_list
compression_params = [] # auxiliary list of serialized compression hyperparams names (strings)
config["parameter_config"] = []
for idx, param in enumerate(config["compression"]["config_list"]):
config["parameter_config"] += [{
**param,
"name": "{}{}{}".format(idx, SERIALIZATION_SEPARATOR, param["name"]),
} for param in _extract_compression_hyperparameters(param)
]
compression_params += [param["name"] for param in config["parameter_config"]]
config = BasicConfig(**config)
return config, compression_params
def deserialize_compression_proposal(config, compression_params, proposal):
"""
Helper function used to deserialize a proposal meant for compression from its usual
HPO format into a NNI-compatible config_list format.
The output of this function is normally passed to the user script, where it is loaded
using BasicConfig.
:param config: full experiment config
:type config: dict
:param compression_params: list of names of compression params to filter proposal by
:type compression_params: list
:param proposal: proposal generated by an auptimizer proposer
:type proposal: dict
:return: deserialized job configuration
:rtype: dict
"""
new_proposal = config["compression"].copy()
for param in compression_params:
full_param = param.split(SERIALIZATION_SEPARATOR)
index = int(full_param[0]) # the first string until . is always the config_list index
original_key = full_param[-1] # the last string is always the original key of the config_list entry
cdict = new_proposal["config_list"][index]
# Parse the config_list entry recursively until arriving at max depth
for part in full_param[1:-1]:
cdict = cdict[part]
cdict[original_key] = proposal[param] # assign the actual proposed value for the param, instead of the hyperparameter "range" and "type" format
new_proposal.update({key: val for key, val in proposal.items() if key not in compression_params})
return new_proposal
def adjust_compression_config(config):
"""
Adjust certain config parameters
:param config: experiment configuration
:type config: dict
:return: config adjusted
:rtype: dict
"""
# In order to avoid possible conflicts ("framework", "type" etc. are too ubiquitous parameter names)
for old_name, new_name in [
("type", "compression_type"),
("framework", "compression_framework")
]:
if old_name in config:
config[new_name] = config[old_name]
del config[old_name]
return config
================================================
FILE: src/aup/convert.py
================================================
"""
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
Convert python code for Auptimizer automatically
================================================
See :doc:`experiment` for how to convert a job to a **Auptimizer** Experiment.
Basic Usage
-----------
::
python convert.py origin.py experiment.json demo_func
Additional arguments
--------------------
.. program-output:: python -m aup.convert -h
Example
-------
See `Examples/demo`:
APIs
----
"""
import json
import logging
import os
import stat
import click
def get_param(experiment_file):
"""Parse experiment file to retrieve hyperparameter names
:param experiment_file: JSON file of the experiment
:return: list of variable names
:rtype: [String]
"""
with open(experiment_file) as f:
j = json.load(f)
try:
return [i["name"] for i in j["parameter_config"]]
except KeyError as e:
if "parameter_config" not in j:
logging.fatal("parameter_config not in the experiment config")
else:
logging.fatal("name not in parameter_config")
raise e
def get_output_name(experiment_file):
"""Retrieves the Python script to be executed from the experiment json file"""
with open(experiment_file) as f:
j = json.load(f)
try:
return j["script"]
except KeyError as e:
logging.fatal("script need to be defined in experiment json")
raise e
def add_shenbang(script):
"""
Makes the Python script executable.
"""
if script.splitlines()[0][:2] != "#!":
#
if os.name == "posix":
return "#!/usr/bin/env python\n" + script
else:
logging.critical('Be cautious, add #!"C:\\Python33\\python.exe", make sure it executable on Windows')
return '#!"C:\\Python33\\python.exe\n' + script
else:
return script
def add_main(script):
"""
Adds a main function to the executable Python file.
"""
if "__main__" in script:
logging.critical("__main__ is already defined in the script. Make sure no duplicated __main__ blocks in output.")
return script + """\nif __name__ == "__main__":
import sys
from aup import BasicConfig, print_result
if len(sys.argv) != 2:
print("config file required")
exit(1)
config = BasicConfig().load(sys.argv[1])
aup_wrapper(config)\n"""
def add_func(script, func_name, variables):
"""
Adds wrapper function to the python script.
"""
arguments = ",".join(["{0}=config['{0}']".format(i) for i in variables])
wrapper_script = """\ndef aup_wrapper(config):
res = {0}({1})
print_result(res)\n""".format(func_name, arguments)
return script + wrapper_script
# TODO-handle exception: func_name does not have any return value (need some ast parsing)
# TODO-"config" is not used/referenced in "aup_wrapper" function
@click.command(name="auto convert script for Auptimizer",
context_settings=dict(help_option_names=['-h', '--help']))
@click.argument("script", type=click.Path(exists=True))
@click.argument("exp_json", type=click.Path(exists=True))
@click.argument("func_name", type=click.STRING)
@click.option("-o", "--output", type=click.STRING, default=None,
help="output file name")
def main(script, exp_json, func_name, output):
"""Convert script for Auptimizer
\b\n
Copyright (C) 2018 LG Electronics Inc.
\b\n
GPL-3.0 License. This program comes with ABSOLUTELY NO WARRANTY;
\b\n
Arguments:
script {str} -- Script name to train an ML model and return result
exp_json {str} -- JSON file name contrains experiment configuration (e.g. hyperparameter)
func_name {str} -- Name of the main function in the script for the training
\b\n
Raises:
Exception: If the script is not self-executable.
"""
variable_names = get_param(exp_json)
script = open(script).read()
script = add_shenbang(script)
script = add_func(script, func_name, variable_names)
script = add_main(script)
if output is None:
output = get_output_name(exp_json)
with open(output, 'w') as f:
f.write(script)
if os.name == "posix":
os.chmod(output, stat.S_IRWXU)
else:
logging.critical("Non-*nix OS is not fully supported, change permission by yourself.")
if not os.access(output, os.X_OK):
raise Exception("Failed at the last step - script %s is not executable" % output)
if __name__ == "__main__":
main()
================================================
FILE: src/aup/dashboard/README.md
================================================
# Auptimizer Dashboard
## Launching the dashboard together with an experiment
The dashboard can be launched with ``aup`` using `--launch-dashboard`
`python -m aup Examples/2dfunc_diff_res/exp_cpu.json --launch_dashboard`
You can specify the port for the dashboard with `--dashboard-port`, otherwise,
the first available port will be used and printed to the console when running
the experiment.
## Seeing HTTP requests from the frontend
The HTTP requests are all saved in `./src/aup/dashboard/frontend/febuild/auptimizer-dashboard/dashboard_logs`
================================================
FILE: src/aup/dashboard/__init__.py
================================================
"""
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
================================================
FILE: src/aup/dashboard/dashboard.py
================================================
"""
Dashboard entry point
============================================
..
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
"""
import os
import sys
import shutil
import logging
import argparse
import subprocess
import threading
import requests
import re
import cgi
import json
import aup.RestAPI.server as be_server
from http.server import SimpleHTTPRequestHandler, HTTPServer
from ..utils import get_available_port
SUCCESS = 0
ERROR = 1
global BACKEND_PORT
FE_BUILD_DIR = 'frontend/febuild/auptimizer-dashboard'
LOGGER = logging.getLogger("dashboard")
class ProxyHTTPRequestHandler(SimpleHTTPRequestHandler):
global BACKEND_PORT
def log_message(self, format, *args):
dir_path = os.environ['PWD']
log_file_name = "dashboard_logs"
path = os.path.join(dir_path, log_file_name)
append_write = 'a'
if not os.path.exists(path):
append_write = 'w'
with open (path, append_write) as f:
f.write("%s - - [%s] %s\n" %
(self.address_string(),
self.log_date_time_string(),
format%args))
def do_DELETE(self):
rv = re.search('/api/(.*)$', self.path)
if rv != None:
url = 'http://127.0.0.1:{}{}'.format(str(BACKEND_PORT), self.path)
resp = requests.delete(url)
self.send_response(resp.status_code)
self.send_resp_headers(resp)
self.wfile.write(resp.content)
return
super().do_DELETE()
return
def do_GET(self):
rv = re.search('/api/(.*)$', self.path)
if rv != None:
url = 'http://127.0.0.1:{}{}'.format(str(BACKEND_PORT), self.path)
resp = requests.get(url)
self.send_response(resp.status_code)
self.send_resp_headers(resp)
self.wfile.write(resp.content)
return
super().do_GET()
return
def do_POST(self):
rv = re.search('/api/(.*)$', self.path)
if rv != None:
url = 'http://127.0.0.1:{}{}'.format(str(BACKEND_PORT), self.path)
content_type = self.headers.get('content-type')
body = None
if content_type is not None:
ctype, pdict = cgi.parse_header(content_type)
# refuse to receive non-json content
if ctype != 'application/json':
self.send_response(400)
self.end_headers()
return
# read the message and convert it into a python dictionary
length = int(self.headers.get('content-length'))
encoded_msg = self.rfile.read(length)
body = json.loads(encoded_msg.decode(sys.stdin.encoding if sys.stdin.encoding is not None else 'UTF-8'))
resp = requests.post(url, json=body)
self.send_response(resp.status_code)
self.send_resp_headers(resp)
self.wfile.write(resp.content)
return
return
def send_resp_headers(self, resp):
respheaders = resp.headers
for key in respheaders:
if key not in ['Content-Encoding', 'Transfer-Encoding', 'content-encoding', 'transfer-encoding', 'content-length', 'Content-Length']:
self.send_header(key, respheaders[key])
self.send_header('Content-Length', len(resp.content))
self.end_headers()
def start_fe_server(PORT):
server_address = ('', PORT)
httpd = HTTPServer(server_address, ProxyHTTPRequestHandler)
httpd.serve_forever()
def start_servers(path, port, frontend):
global BACKEND_PORT
import pathlib
pa = pathlib.Path(__file__).resolve().parent
if (path is not None) and (not os.path.exists(path)):
LOGGER.error('{} does not exist!'.format(path))
return ERROR
LOGGER.debug('Backend started on 0.0.0.0:{}'.format(BACKEND_PORT))
# separate thread
path = os.path.abspath(path) if path is not None else None
be_server_thr = threading.Thread(target=be_server.main, args=(path, BACKEND_PORT), daemon=True)
if frontend:
fe_server_thr = threading.Thread(target=start_fe_server, args=(port,), daemon=True)
# for frontend
os.chdir(os.path.join(str(pa), FE_BUILD_DIR))
try:
be_server_thr.start()
if frontend:
fe_server_thr.start()
if frontend:
fe_server_thr.join()
be_server_thr.join()
except Exception as e:
LOGGER.error('Exception occurred:' + str(e))
return ERROR
return SUCCESS
def _start_dashboard(path, port, frontend):
global BACKEND_PORT
BACKEND_PORT = get_available_port()
return start_servers(path, port, frontend)
def main():
global BACKEND_PORT
""" Main function that parses arguments and opens fe+be."""
parser = argparse.ArgumentParser(
description='Open frontend and backend of the auptimizer dashboard')
parser.add_argument(
'--path',
type=str,
dest='path',
default=None,
help='Path of the sqlite db file.'
)
parser.add_argument(
'--port',
type=int,
dest='port',
help='Port for frontend. Leave blank for backend only.',
)
parser.add_argument(
'--backend_port',
type=int,
dest='backend_port',
help='Port for the backend. Optional, for easier debugging.'
)
args = parser.parse_args()
BACKEND_PORT = get_available_port()
if args.backend_port is not None:
BACKEND_PORT = args.backend_port
if args.port is None:
args.frontend = False
else:
args.frontend = True
print('Dashboard started on 0.0.0.0:{}'.format(args.port))
print('To exit press CTRL+C...')
return start_servers(args.path, args.port, args.frontend)
if __name__ == "__main__":
sys.exit(main())
================================================
FILE: src/aup/dashboard/frontend/.browserslistrc
================================================
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line.
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
================================================
FILE: src/aup/dashboard/frontend/.editorconfig
================================================
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
================================================
FILE: src/aup/dashboard/frontend/.eslintrc.json
================================================
{
"root": true,
"ignorePatterns": ["projects/**/*", "febuild/**/*"],
"overrides": [
{
"files": ["*.ts"],
"parserOptions": {
"project": ["tsconfig.json", "e2e/tsconfig.json"],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/ng-cli-compat",
"plugin:@angular-eslint/ng-cli-compat--formatting-add-on",
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates",
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
"plugins": ["eslint-plugin-react"],
"rules": {
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "app",
"style": "kebab-case"
}
],
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "app",
"style": "camelCase"
}
],
"@angular-eslint/use-component-view-encapsulation": "error",
"@angular-eslint/use-pipe-decorator": "error",
"@typescript-eslint/consistent-type-definitions": "error",
"@typescript-eslint/dot-notation": "off",
"@typescript-eslint/explicit-member-accessibility": [
"off",
{
"accessibility": "explicit"
}
],
"arrow-parens": ["off", "always"],
"brace-style": ["off", "off"],
"eol-last": "off",
"id-blacklist": "off",
"id-match": "off",
"import/order": "off",
"linebreak-style": "off",
"new-parens": "off",
"newline-per-chained-call": "off",
"no-extra-semi": "off",
"no-irregular-whitespace": "off",
"no-trailing-spaces": "off",
"no-underscore-dangle": "off",
"react/jsx-curly-spacing": "off",
"react/jsx-equals-spacing": "off",
"react/jsx-wrap-multilines": "off",
"space-before-function-paren": "off",
"space-in-parens": ["off", "never"]
}
},
{
"files": ["*.html"],
"extends": ["plugin:@angular-eslint/template/recommended"],
"rules": {}
}
]
}
================================================
FILE: src/aup/dashboard/frontend/.gitignore
================================================
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
#/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
================================================
FILE: src/aup/dashboard/frontend/.prettierignore
================================================
package.json
package-lock.json
yarn.lock
dist
.angulardoc.json
.vscode/*
**/*.html
================================================
FILE: src/aup/dashboard/frontend/.prettierrc
================================================
{
"printWidth": 120,
"singleQuote": true,
"useTabs": false,
"tabWidth": 2,
"jsxSingleQuote": true,
"htmlWhitespaceSensitivity": "ignore",
"semi": true,
"bracketSpacing": true,
"endOfLine": "auto"
}
================================================
FILE: src/aup/dashboard/frontend/Angular-LICENSE
================================================
License file for package: https://github.com/angular/angular-cli
The MIT License
Copyright (c) 2017 Google, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: src/aup/dashboard/frontend/README.md
================================================
# Auptimizer Frontend
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.0.2.
## Getting started
Clone the repo. Navigate to `Auptimizer-Dashboard` folder. Run `npm install` to install all dependencies.
## Development server
Run `npm run start` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
## Copyright
Copyright (c) 2018 LG Electronics Inc.
SPDX-License-Identifier: GPL-3.0-or-later
================================================
FILE: src/aup/dashboard/frontend/angular.json
================================================
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"auptimizer-dashboard": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./webpack.config.js"
},
"outputPath": "febuild/auptimizer-dashboard",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/favicon.ico",
"src/assets",
"src/_redirects"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "10mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "30kb",
"maximumError": "70kb"
}
]
}
}
},
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"customWebpackConfig": {
"path": "./webpack.config.js"
},
"browserTarget": "auptimizer-dashboard:build"
},
"configurations": {
"production": {
"browserTarget": "auptimizer-dashboard:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "auptimizer-dashboard:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "auptimizer-dashboard:serve"
},
"configurations": {
"production": {
"devServerTarget": "auptimizer-dashboard:serve:production"
}
}
}
}
}
},
"defaultProject": "auptimizer-dashboard"
}
================================================
FILE: src/aup/dashboard/frontend/e2e/protractor.conf.js
================================================
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY
}
}));
}
};
================================================
FILE: src/aup/dashboard/frontend/e2e/src/app.e2e-spec.ts
================================================
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('auptimizer-dashboard app is running!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});
================================================
FILE: src/aup/dashboard/frontend/e2e/src/app.po.ts
================================================
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo(): Promise {
return browser.get(browser.baseUrl) as Promise;
}
getTitleText(): Promise {
return element(by.css('app-root .content span')).getText() as Promise;
}
}
================================================
FILE: src/aup/dashboard/frontend/e2e/tsconfig.json
================================================
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
================================================
FILE: src/aup/dashboard/frontend/febuild/auptimizer-dashboard/3rdpartylicenses.txt
================================================
@angular/animations
MIT
@angular/cdk
MIT
The MIT License
Copyright (c) 2021 Google LLC.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@angular/common
MIT
@angular/core
MIT
@angular/flex-layout
MIT
The MIT License
Copyright (c) 2020 Google LLC.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@angular/forms
MIT
@angular/material
MIT
The MIT License
Copyright (c) 2021 Google LLC.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@angular/platform-browser
MIT
@angular/router
MIT
@ngxs/devtools-plugin
MIT
@ngxs/logger-plugin
MIT
@ngxs/router-plugin
MIT
@ngxs/storage-plugin
MIT
@ngxs/store
MIT
angular-plotly.js
cdk-table-exporter
Apache-2.0
css-loader
MIT
Copyright JS Foundation and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
file-saver
MIT
The MIT License
Copyright © 2016 [Eli Grey][1].
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
[1]: http://eligrey.com
jsoneditor
Apache-2.0
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
mat-table-exporter
Apache-2.0
moment
MIT
Copyright (c) JS Foundation and other contributors
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
ngx-pagination
MIT
The MIT License (MIT)
Copyright (c) 2016 Michael Bromley
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
plotly.js
MIT
The MIT License (MIT)
Copyright (c) 2020 Plotly, Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
rxjs
Apache-2.0
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2015-2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
tslib
0BSD
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
webpack
MIT
Copyright JS Foundation and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
xlsx
Apache-2.0
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (C) 2012-present SheetJS LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
zone.js
MIT
The MIT License
Copyright (c) 2010-2020 Google LLC. http://angular.io/license
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: src/aup/dashboard/frontend/febuild/auptimizer-dashboard/4.3bf463bd37e16d085b6c.js
================================================
(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{"+c+f":function(e,t,n){"use strict";n.r(t),n.d(t,"ExperimentModule",(function(){return ls}));var r=n("ofXK"),i=n("tyNb"),s=n("mrSG"),o=n("AcyG"),a=n("quSY"),c=n("HDdC"),l=n("D0XW"),h=n("Y7HM");function d(e=0,t=l.a){return(!Object(h.a)(e)||e<0)&&(e=0),t&&"function"==typeof t.schedule||(t=l.a),new c.a(n=>(n.add(t.schedule(u,e,{subscriber:n,counter:0,period:e})),n))}function u(e){const{subscriber:t,counter:n,period:r}=e;t.next(n),this.schedule({subscriber:t,counter:n+1,period:r},r)}let f=(()=>{class e{}return e.type="[Experiment] Getting list of experiments",e})(),g=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Experiment] Get experiment by id",e})(),p=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Experiment] Get hyperparameters by experiment id",e})(),m=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Experiment] Get job status by experiment id",e})(),C=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Experiment] Get experiment history best by experiment id",e})(),b=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Experiment] Get metrics vs hyperparameters by experiment id",e})(),v=(()=>{class e{}return e.type="[Experiment] Get intermediate results",e})(),I=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Experiment] Set intermediate experiment",e})(),A=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Experiment] Get experiment intermediate results",e})(),y=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Config] Set refresh interval value",e})(),w=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Config] Refresh interval",e})(),S=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Experiment] Start experiment by id",e})(),x=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Experiment] Stop experiment by id",e})(),E=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Database] Initialize setup database",e})(),k=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Experiment] Create experiment",e})(),_=(()=>{class e{}return e.type="[Sidenav] Toggle sidenav",e})(),T=(()=>{class e{}return e.type="[Experiment] Refresh all",e})(),R=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Experiment] Delete experiment",e})(),B=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Experiment] Set experiment display view",e})(),O=(()=>{class e{constructor(e){this.payload=e}}return e.type="[Experiment] Change job status graph for multiple labels",e})();var L=n("z6cu"),P=n("JIr8"),F=n("lJxs"),N=n("vkgz"),M=n("TkeJ"),D=(n("EHOR"),n("93C2"),n("BHZs")),$=n("KkKq"),W=n("ENCR"),H=(n("Bzzc"),n("h93q")),G=n("efK2"),V=n("nHCO");const j=[{displayName:"Overview",route:"overview",iconName:"table_chart",tooltip:"Overview"},{displayName:"Job Status",route:"job-status",iconName:"science",tooltip:"Job Status"},{displayName:"Hyperparameter Interaction Graph",route:"hig",iconName:"bar_chart",tooltip:"Hyperparameter Interaction Graph"},{displayName:"Intermediate Results",route:"interm",iconName:"graphic_eq",tooltip:"Intermediate Results"},{displayName:"Multi-Experiment Comparison",route:"multi",iconName:"stacked_line_chart",tooltip:"Multi-Experiment Comparison"}];var Z=n("OCth"),z=function(e){return e.LIST="list",e.CARD="card",e}({}),K=n("fXoL"),X=n("nm5K"),U=n("sIil");let Y=(()=>{class e{constructor(e,t){this.apiService=e,this.helperService=t}getExperiments(){return this.apiService.get("experiments")}getExperiment(e){if(e)return this.apiService.get("experiments/"+e)}getHyperparameters(e){if(e)return this.apiService.get("hps_space?eid="+e)}getInterimResults(){return this.apiService.get("interm_res")}getExperimentInterimResults(e,t){if(e)return this.apiService.get(`interm_res/${e}${t&&"score"!==t?"/"+t:""}`)}getJobsStatus(e,t){if(e&&null!=t)return this.apiService.get(`job_status?sortby=${t.sortby}&asc=${t.asc}&eid=${e}`)}getMetricsVsHparams(e){if(e)return this.apiService.get("metrics_vs_hparams?eid="+e)}getExperimentHistoryBest(e,t,n,r){if(e&&t&&n)return this.apiService.get(`experiment_history_best/${e}${r&&"score"!==r?"/"+r:""}?n=${t}&sortby=${n}`)}getAllExperimentHistoryBest(e,t,n){if(e)return this.apiService.get(`experiment_history_best${n&&"score"!==n?"/"+n:""}?n=${e}${t?"&sortby="+t:""}`)}startExperiment(e){if(e)return this.apiService.post("start_experiment",Object.assign({},e))}stopExperiment(e){if(e)return this.apiService.post("stop_experiment",{eid:e})}setupDB(e){if(e)return this.apiService.post("setup",Object.assign({},e))}createExperiment(e){if(e)return this.apiService.post("create_experiment",e)}refreshAll(){return this.apiService.post("refresh_all")}deleteExperiment(e){if(e)return this.apiService.delete("experiment/"+e)}}return e.\u0275fac=function(t){return new(t||e)(K.hc(X.a),K.hc(U.a))},e.\u0275prov=K.Tb({token:e,factory:e.\u0275fac,providedIn:"root"}),e})();var J=n("DHDI"),Q=n("ZF+8");let q=(()=>{let e=class{constructor(e,t,n,r,i){this.service=e,this.helperService=t,this.snackbarService=n,this.utilsService=r,this.store=i}static loadingExperiment(e){return e.loadingExperiment}static loadingAllExperiments(e){return e.loadingAllExperiments}static loadingIntermResults(e){return e.loadingIntermResults}static experimentsMultiplier(e){return e.experimentsMultiplier}static refreshInterval(e){return e.refreshInterval}static refreshIntervalOptions(e){return e.refreshIntervalOptions}static refreshingInterval(e){return e.refreshingInterval}static experiments(e){return e.experiments}static selectedExperiment(e){return e.selectedExperiment}static resources(e){return e.resources}static hyperparameters(e){return e.hyperparameters}static metricsVsHparams(e){return e.metricsVsHparams}static parallelCoordinatesTrace(e){return e.parallelCoordinatesTrace}static jobs(e){return e.jobs}static jobMultipleResulsLabels(e){return e.jobsMultipleResults.labels}static jobMultipleResulsSelectedLabel(e){return e.jobsMultipleResults.selectedLabel}static jobsGraphData(e){return e.jobsGraphData}static jobsOptimizationGraphData(e){return e.jobsOptimizationGraphData}static sidenavOpen(e){return e.sidenavOpen}static intermResults(e){return e.intermResults}static interimExperiment(e){return e.interimExperiment}static interimExperimentJobs(e){return e.interimExperiment.jobs}static interimExperimentMultResLabels(e){return e.interimExperiment.multResLabels}static interimExperimentSelectedLabel(e){return e.interimExperiment.selectedLabel}static navItems(e){return e.navItems}static experimentViewType(e){return e.experimentViewType}toggleSideNav(e){const t=e.getState();localStorage.setItem("sidenavOpen",JSON.stringify(!t.sidenavOpen)),e.patchState({sidenavOpen:!t.sidenavOpen})}setRefreshInterval(e,{payload:t}){t&&e.patchState({refreshInterval:t})}setExperimentDisplayView(e,{payload:t}){t&&(localStorage.setItem("experimentViewType",t),e.patchState({experimentViewType:t}))}refreshInterval(e,{payload:t}){e.patchState({refreshingInterval:t})}getExperiments(e){e.patchState({loadingAllExperiments:!0});const t=e.getState().experiments;return this.service.getExperiments().pipe(Object(P.a)(t=>{e.patchState({loadingAllExperiments:!1});const n={type:Z.a.ERROR,receivedAt:(new Date).getTime(),message:this.utilsService.formatErrorMessage(t)};return this.store.dispatch(new V.a(n)),this.snackbarService.error(this.utilsService.formatErrorMessage(t)),Object(L.a)(t)}),Object(F.a)(n=>{if(n){const r=[];n.experiment.map(e=>{const n=Object.assign(Object.assign({},this.utilsService.keysToCamel(e)),{scores:JSON.parse(e.scores),jobs:JSON.parse(e.jobs),expConfigDetails:JSON.parse(e.exp_config)});return t.length&&t.map(e=>{if(e.eid===n.eid&&e.status!==n.status){const t=`Experiment ${n.experimentName} changed status from ${e.status} to ${n.status}`,r={type:Z.a.INFO,receivedAt:(new Date).getTime(),message:t};this.store.dispatch(new V.a(r)),this.snackbarService.info(t)}}),r.push(n)});const i=r.slice().sort((e,t)=>this.utilsService.compare(e.eid,t.eid,!1));e.patchState({experiments:i,resources:n.resource||[],loadingAllExperiments:!1})}}))}getExperiment(e,{payload:t}){if(t)return e.patchState({loadingExperiment:!0,selectedExperiment:null}),this.service.getExperiment(t).pipe(Object(P.a)(t=>{e.patchState({loadingExperiment:!1});const n={type:Z.a.ERROR,receivedAt:(new Date).getTime(),message:this.utilsService.formatErrorMessage(t)};return this.store.dispatch(new V.a(n)),this.snackbarService.error(this.utilsService.formatErrorMessage(t)),Object(L.a)(t)}),Object(F.a)(t=>{if(t){const n=this.utilsService.keysToCamel(t),r={bestScore:n.bestScore,experiment:n.experiment,jobStats:n.jobStats};if(r.bestScore&&r.bestScore.configList){const e=JSON.parse(r.bestScore.configList);r.bestScore.configList=e}const i=JSON.parse(r.experiment.expConfig);r.experiment.expConfig=i,e.patchState({selectedExperiment:r,loadingExperiment:!1})}}))}getHyperparameters(e,{payload:t}){if(t)return this.service.getHyperparameters(t).pipe(Object(P.a)(e=>{const t={type:Z.a.ERROR,receivedAt:(new Date).getTime(),message:this.utilsService.formatErrorMessage(e)};return this.store.dispatch(new V.a(t)),this.snackbarService.error(this.utilsService.formatErrorMessage(e)),Object(L.a)(e)}),Object(F.a)(t=>{if(t){const n=this.utilsService.keysToCamel(t.exp_config),r=JSON.parse(n.parameters);n.parameters=r.sort((e,t)=>this.utilsService.compare(e.name,t.name,!0)),e.patchState({hyperparameters:n})}}))}getJobStatus(e,{payload:t}){const n=e.getState(),r=n.jobsMultipleResults.selectedLabel||"score";if(t.eid)return this.service.getJobsStatus(t.eid,t.sortCriteria||n.jobStatusSortCriteria).pipe(Object(P.a)(e=>{const t={type:Z.a.ERROR,receivedAt:(new Date).getTime(),message:this.utilsService.formatErrorMessage(e)};return this.store.dispatch(new V.a(t)),this.snackbarService.error(this.utilsService.formatErrorMessage(e)),Object(L.a)(e)}),Object(F.a)(t=>{if(t&&t.job){let i=[];const s=new W.a;t.mult_res_labels&&(i=t.mult_res_labels,i.push("score"));const o=[],a={x:[],y:[],hovertext:[],hoverinfo:"text",type:"scatter",mode:"markers",name:s.transform(r),line:{color:"#3DDF7E"}},c=new G.a;let l,h=1,d=null;return t.job.map(e=>{const t=this.utilsService.keysToCamel(e);t.tableData={},t.tableHyperParams={},t.tableFullData={},t.tableData["job ID"]=t.jid,t.tableFullData["job ID"]=t.jid,t.tableData["resource ID"]=t.rid,t.tableFullData["resource ID"]=t.rid,t.tableData.status=s.transform(t.status),t.tableFullData.status=s.transform(t.status),t.tableData.score=t.score,t.tableFullData.score=t.score,t.tableData["start time"]=t.startTime,t.tableFullData["start time"]=t.startTime,t.tableData["end time"]=t.endTime,t.tableFullData["end time"]=t.endTime,r?null!=e[r]&&(l=`${s.transform(r)} ${e[r]} `,l+=`Job ID: ${e.jid} `,a.x.push(h),a.y.push(e[r]),null===d&&this.utilsService.isNumber(e[r])&&(d=h),h++):null!=t.score&&(l=`${r}: ${t.score} `,l+=`Job ID: ${t.jid} `,a.x.push(h),a.y.push(t.score),null===d&&this.utilsService.isNumber(t.score)&&(d=h),h++);const n=JSON.parse(e.job_config);return t.jobConfig=n,Object.entries(t.jobConfig).map(e=>{t.tableFullData[""+e[0]]=e[1],t.tableHyperParams[""+e[0]]=e[1],null!=t.score&&(l+=`${s.transform(c.transform(e[0],[100,"..."]))}: ${e[1]} `)}),i.length&&i.map(e=>{"score"===e||Object.keys(t.tableFullData).includes(e)||Object.keys(t.tableHyperParams).includes(e)||(t.tableFullData[e]=t[e],t.tableHyperParams[e]=t[e])}),null!=t.score&&a.hovertext.push(l),o.push(t)}),void e.patchState({jobs:o,jobsGraphData:a,firstValidJobNumber:d,jobsMultipleResults:Object.assign(Object.assign({},n.jobsMultipleResults),{labels:i,selectedLabel:r||(i&&i.length?i[i.length-1]:null)})})}}))}changeJobsGraphForLabel(e,{payload:t}){const n=e.getState(),r=new W.a;if(!t)return;const i=n.jobs,s={x:[],y:[],hovertext:[],hoverinfo:"text",type:"scatter",mode:"markers",name:r.transform(t),line:{color:"#3DDF7E"}},o=new G.a;let a,c=1,l=null;i.map(i=>{null!=i[t]&&(a=`${r.transform(t)} ${i[t]} `,a+=`Job ID: ${i.jid} `,s.x.push(c),s.y.push(i[t]),null===l&&this.utilsService.isNumber(i[t])&&(l=c),c++),Object.entries(i.jobConfig).map(e=>{null!=i[t]&&(a+=`${r.transform(o.transform(e[0],[100,"..."]))}: ${e[1]} `)}),null!=i[t]&&s.hovertext.push(a),e.patchState({jobsGraphData:s,firstValidJobNumber:l,jobsMultipleResults:Object.assign(Object.assign({},n.jobsMultipleResults),{selectedLabel:t})})})}getExperimentHistory(e,{payload:t}){const n=e.getState();if(!t)return;const r=t.n||n.jobsMultiplier;return this.service.getExperimentHistoryBest(t.eid,r,t.sortby||"jid",n.jobsMultipleResults.selectedLabel).pipe(Object(P.a)(e=>{const t={type:Z.a.ERROR,receivedAt:(new Date).getTime(),message:this.utilsService.formatErrorMessage(e)};return this.store.dispatch(new V.a(t)),this.snackbarService.error(this.utilsService.formatErrorMessage(e)),Object(L.a)(e)}),Object(F.a)(t=>{if(t&&t.experiment_history_best&&t.experiment_history_best.length){const i={x:[],y:[],hovertext:[],hoverinfo:"text",type:"scatter",mode:"lines+markers",name:"Best Results",line:{color:"#f44336"}},s=new W.a,o=new G.a;let a;const c=[],l=n.jobsMultipleResults.selectedLabel||"score";let h=n.firstValidJobNumber;if(t.experiment_history_best.map(e=>{a=`${s.transform(l)}: ${e.score} `,a+=`Job ID: ${e.jid} `,null!=e.score&&(c.push(h),i.y.push(e.score),h++);const t=JSON.parse(e.job_config);Object.entries(t).map(e=>{a+=`${s.transform(o.transform(e[0],[100,"..."]))}: ${e[1]} `}),i.hovertext.push(a)}),n.jobsGraphData&&n.jobsGraphData.x.length){const e=c[c.length-1],t=c.slice(),n=t.findIndex(t=>t===e);if(-1!==n){const r=t.slice(n),s=t.indexOf(e);t.splice(s);const o=t.concat(r);i.x=o}else i.x=c}else i.x=c;e.patchState({jobsOptimizationGraphData:i,jobsMultiplier:r})}}))}getMetricVsHparams(e,{payload:t}){if(t)return this.service.getMetricsVsHparams(t).pipe(Object(P.a)(e=>{const t={type:Z.a.ERROR,receivedAt:(new Date).getTime(),message:this.utilsService.formatErrorMessage(e)};return this.store.dispatch(new V.a(t)),this.snackbarService.error(this.utilsService.formatErrorMessage(e)),Object(L.a)(e)}),Object(F.a)(t=>{if(t&&t.metrics_vs_hparams){const n=this.utilsService.keysToCamel(t.metrics_vs_hparams);if(n&&n.length>0){n.sort((e,t)=>e.score>t.score?1:-1);const t=[];Object.keys(n[0]).reverse().map(e=>t.push({label:e,values:[]}));const r=n.map(e=>isNaN(e.score)?0:e.score);n.map(e=>{t.map(t=>"score"===t.label&&isNaN(e[t.label])?t.values.push(0):t.values.push(e[t.label]))}),e.patchState({metricsVsHparams:n,parallelCoordinatesTrace:{type:"parcoords",line:{showscale:!0,reversescale:!0,colorscale:"Jet",cmin:n[0].score,cmax:n[n.length-1].score,color:r},dimensions:t}})}else e.patchState({metricsVsHparams:n,parallelCoordinatesTrace:null})}}))}startExperiment(e,{payload:t}){const n=e.getState();if(t)return this.service.startExperiment(t).pipe(Object(N.a)(t=>{if(t){const r=n.experiments.slice();let i=this.utilsService.keysToCamel(t);i=Object.assign(Object.assign({},i),{expConfigDetails:JSON.parse(i.expConfig)});const s=r.findIndex(e=>e.eid===i.eid);-1!==s&&(r[s]=i),e.patchState({experiments:r});const o="Experiment started!",a={type:Z.a.SUCCESS,receivedAt:(new Date).getTime(),message:o};this.store.dispatch(new V.a(a)),this.snackbarService.success(o)}}),Object(P.a)(e=>{const t={type:Z.a.ERROR,receivedAt:(new Date).getTime(),message:this.utilsService.formatErrorMessage(e)};return this.store.dispatch(new V.a(t)),this.snackbarService.error(this.utilsService.formatErrorMessage(e)),Object(L.a)(e)}))}stopExperiment(e,{payload:t}){const n=e.getState();if(t)return this.service.stopExperiment(t).pipe(Object(N.a)(t=>{if(t){const r=n.experiments.slice();let i=this.utilsService.keysToCamel(t);i=Object.assign(Object.assign({},i),{expConfigDetails:JSON.parse(i.expConfig)});const s=r.findIndex(e=>e.eid===i.eid);-1!==s&&(r[s]=i),e.patchState({experiments:r});const o="Experiment stopped!",a={type:Z.a.SUCCESS,receivedAt:(new Date).getTime(),message:o};return this.store.dispatch(new V.a(a)),void this.snackbarService.success(o)}}),Object(P.a)(e=>{const t={type:Z.a.ERROR,receivedAt:(new Date).getTime(),message:this.utilsService.formatErrorMessage(e)};return this.store.dispatch(new V.a(t)),this.snackbarService.error(this.utilsService.formatErrorMessage(e)),Object(L.a)(e)}))}setupDatabase(e,{payload:t}){if(t)return this.service.setupDB(t).pipe(Object(N.a)(e=>{e&&(this.snackbarService.success("Setup completed"),this.store.dispatch(new V.c).subscribe(()=>{this.store.dispatch(new M.a(["/list"]))}))}),Object(P.a)(e=>{const t={type:Z.a.ERROR,receivedAt:(new Date).getTime(),message:this.utilsService.formatErrorMessage(e)};return this.store.dispatch(new V.a(t)),this.snackbarService.error(this.utilsService.formatErrorMessage(e)),Object(L.a)(e)}))}getInterimResults(e){const t=e.getState();return e.patchState({loadingIntermResults:!0}),this.service.getInterimResults().pipe(Object(N.a)(n=>{if(n&&n.length)e.patchState({intermResults:n,loadingIntermResults:!1});else{const n=t.navItems.slice(),r=n.findIndex(e=>"interm"===e.route);-1!==r&&(n[r].disabled=!0),e.patchState({navItems:n,loadingIntermResults:!1})}}),Object(P.a)(t=>{e.patchState({loadingIntermResults:!1});const n={type:Z.a.ERROR,receivedAt:(new Date).getTime(),message:this.utilsService.formatErrorMessage(t)};return this.store.dispatch(new V.a(n)),this.snackbarService.error(this.utilsService.formatErrorMessage(t)),Object(L.a)(t)}))}getExperimentInterimResults(e,{payload:t}){if(!t)return;const n=e.getState(),r=t.label||n.interimExperiment.selectedLabel;return n.intermResults&&n.intermResults.length?this.service.getExperimentInterimResults(t.eid,r).pipe(Object(N.a)(t=>{if(t){const n=t;n.multResLabels&&n.multResLabels.length&&n.multResLabels.push("score"),e.patchState({interimExperiment:Object.assign(Object.assign({},n),{selectedLabel:r||(n.multResLabels&&n.multResLabels.length?n.multResLabels[n.multResLabels.length-1]:null)})})}}),Object(P.a)(e=>{const t={type:Z.a.ERROR,receivedAt:(new Date).getTime(),message:this.utilsService.formatErrorMessage(e)};return this.store.dispatch(new V.a(t)),this.snackbarService.error(this.utilsService.formatErrorMessage(e)),Object(L.a)(e)})):void 0}setInterimExperiment(e,{payload:t}){t&&e.patchState({interimExperiment:t})}createExperiment(e,{payload:t}){if(t)return this.service.createExperiment(t).pipe(Object(N.a)(e=>{if(e){const e="Experiment created!",t={type:Z.a.SUCCESS,receivedAt:(new Date).getTime(),message:e};return this.store.dispatch(new V.a(t)),this.snackbarService.success(e),void this.store.dispatch(new M.a(["/list"]))}}),Object(P.a)(e=>{const t={type:Z.a.ERROR,receivedAt:(new Date).getTime(),message:this.utilsService.formatErrorMessage(e)};return this.store.dispatch(new V.a(t)),this.snackbarService.error(this.utilsService.formatErrorMessage(e)),Object(L.a)(e)}))}deleteExperiment(e,{payload:t}){const n=e.getState();if(t)return this.service.deleteExperiment(t).pipe(Object(N.a)(()=>{const r=n.experiments.slice(),i=r.findIndex(e=>e.eid===t);if(-1!==i){r.splice(i,1);const t="Experiment deleted!",n={type:Z.a.SUCCESS,receivedAt:(new Date).getTime(),message:t};this.store.dispatch(new V.a(n)),this.snackbarService.success(t),e.patchState({experiments:r})}}),Object(P.a)(e=>{const t={type:Z.a.ERROR,receivedAt:(new Date).getTime(),message:this.utilsService.formatErrorMessage(e)};return this.store.dispatch(new V.a(t)),this.snackbarService.error(this.utilsService.formatErrorMessage(e)),Object(L.a)(e)}))}refreshAll(e){return this.service.refreshAll().pipe(Object(P.a)(e=>{const t={type:Z.a.ERROR,receivedAt:(new Date).getTime(),message:this.utilsService.formatErrorMessage(e)};return this.store.dispatch(new V.a(t)),this.snackbarService.error(this.utilsService.formatErrorMessage(e)),Object(L.a)(e)}))}};return e.\u0275fac=function(t){return new(t||e)(K.hc(Y),K.hc(U.a),K.hc(J.a),K.hc(Q.c),K.hc(o.i))},e.\u0275prov=K.Tb({token:e,factory:e.\u0275fac}),Object(s.b)([Object(o.a)(_)],e.prototype,"toggleSideNav",null),Object(s.b)([Object(o.a)(y)],e.prototype,"setRefreshInterval",null),Object(s.b)([Object(o.a)(B)],e.prototype,"setExperimentDisplayView",null),Object(s.b)([Object(o.a)(w)],e.prototype,"refreshInterval",null),Object(s.b)([Object(o.a)(f)],e.prototype,"getExperiments",null),Object(s.b)([Object(o.a)(g)],e.prototype,"getExperiment",null),Object(s.b)([Object(o.a)(p)],e.prototype,"getHyperparameters",null),Object(s.b)([Object(o.a)(m)],e.prototype,"getJobStatus",null),Object(s.b)([Object(o.a)(O)],e.prototype,"changeJobsGraphForLabel",null),Object(s.b)([Object(o.a)(C)],e.prototype,"getExperimentHistory",null),Object(s.b)([Object(o.a)(b)],e.prototype,"getMetricVsHparams",null),Object(s.b)([Object(o.a)(S)],e.prototype,"startExperiment",null),Object(s.b)([Object(o.a)(x)],e.prototype,"stopExperiment",null),Object(s.b)([Object(o.a)(E)],e.prototype,"setupDatabase",null),Object(s.b)([Object(o.a)(v)],e.prototype,"getInterimResults",null),Object(s.b)([Object(o.a)(A)],e.prototype,"getExperimentInterimResults",null),Object(s.b)([Object(o.a)(I)],e.prototype,"setInterimExperiment",null),Object(s.b)([Object(o.a)(k)],e.prototype,"createExperiment",null),Object(s.b)([Object(o.a)(R)],e.prototype,"deleteExperiment",null),Object(s.b)([Object(o.a)(T)],e.prototype,"refreshAll",null),Object(s.b)([Object(o.f)()],e,"loadingExperiment",null),Object(s.b)([Object(o.f)()],e,"loadingAllExperiments",null),Object(s.b)([Object(o.f)()],e,"loadingIntermResults",null),Object(s.b)([Object(o.f)()],e,"experimentsMultiplier",null),Object(s.b)([Object(o.f)()],e,"refreshInterval",null),Object(s.b)([Object(o.f)()],e,"refreshIntervalOptions",null),Object(s.b)([Object(o.f)()],e,"refreshingInterval",null),Object(s.b)([Object(o.f)()],e,"experiments",null),Object(s.b)([Object(o.f)()],e,"selectedExperiment",null),Object(s.b)([Object(o.f)()],e,"resources",null),Object(s.b)([Object(o.f)()],e,"hyperparameters",null),Object(s.b)([Object(o.f)()],e,"metricsVsHparams",null),Object(s.b)([Object(o.f)()],e,"parallelCoordinatesTrace",null),Object(s.b)([Object(o.f)()],e,"jobs",null),Object(s.b)([Object(o.f)()],e,"jobMultipleResulsLabels",null),Object(s.b)([Object(o.f)()],e,"jobMultipleResulsSelectedLabel",null),Object(s.b)([Object(o.f)()],e,"jobsGraphData",null),Object(s.b)([Object(o.f)()],e,"jobsOptimizationGraphData",null),Object(s.b)([Object(o.f)()],e,"sidenavOpen",null),Object(s.b)([Object(o.f)()],e,"intermResults",null),Object(s.b)([Object(o.f)()],e,"interimExperiment",null),Object(s.b)([Object(o.f)()],e,"interimExperimentJobs",null),Object(s.b)([Object(o.f)()],e,"interimExperimentMultResLabels",null),Object(s.b)([Object(o.f)()],e,"interimExperimentSelectedLabel",null),Object(s.b)([Object(o.f)()],e,"navItems",null),Object(s.b)([Object(o.f)()],e,"experimentViewType",null),e=Object(s.b)([Object(o.g)({name:"experiment",defaults:{experiments:[],firstValidJobNumber:null,intermResults:null,interimExperiment:null,experimentsMultiplier:1,resources:[],selectedExperiment:null,hyperparameters:null,jobStatusSortCriteria:{sortby:"jid",asc:1},jobs:[],jobsGraphData:null,jobsOptimizationGraphData:null,jobsMultipleResults:{labels:[],selectedLabel:null},jobsMultiplier:1,metricsVsHparams:null,parallelCoordinatesTrace:null,refreshIntervalOptions:[5,10,30,60],refreshInterval:60,refreshingInterval:!1,sidenavOpen:null===localStorage.getItem("sidenavOpen")||void 0===localStorage.getItem("sidenavOpen")||JSON.parse(localStorage.getItem("sidenavOpen")),navItems:j,loadingExperiment:!1,loadingAllExperiments:!1,loadingIntermResults:!1,experimentViewType:localStorage.getItem("experimentViewType")||z.LIST}})],e),e})();var ee=n("JX91"),te=n("AytR"),ne=n("dhIe"),re=n("d4xE"),ie=n("SUpr"),se=n("/t3+"),oe=n("XiUz"),ae=n("bTqV"),ce=n("NFeN"),le=n("STbY"),he=n("TU8p"),de=n("3Pt+"),ue=n("XhcP"),fe=n("kmnG"),ge=n("d3UM"),pe=n("jaxi"),me=n("FKr1"),Ce=n("Qu3c");function be(e,t){if(1&e&&(K.dc(0,"mat-option",9),K.Oc(1),K.qc(2,"flu"),K.cc()),2&e){const e=t.$implicit;K.vc("value",e),K.Lb(1),K.Qc(" ",K.rc(2,2,e)," ")}}function ve(e,t){if(1&e&&(K.dc(0,"div",21),K.dc(1,"mat-icon"),K.Oc(2),K.qc(3,"notifyIcon"),K.cc(),K.dc(4,"span"),K.Oc(5),K.cc(),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Nb("color-"+e.type.toLowerCase()),K.Lb(1),K.Qc("",K.rc(3,4,e.type)," "),K.Lb(3),K.Pc(e.type)}}function Ie(e,t){if(1&e&&(K.dc(0,"div",22),K.Oc(1),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Pc(e.message)}}function Ae(e,t){if(1&e&&(K.dc(0,"div"),K.Oc(1),K.qc(2,"date"),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Pc(K.sc(2,1,e.receivedAt,"medium"))}}function ye(e,t){if(1&e){const e=K.ec();K.bc(0),K.dc(1,"div",16),K.Mc(2,ve,6,6,"div",17),K.Mc(3,Ie,2,1,"div",18),K.Mc(4,Ae,3,4,"div",19),K.dc(5,"div"),K.dc(6,"mat-icon",20),K.lc("click",(function(){K.Ec(e);const n=t.$implicit;return K.pc(2).removeNotification(n)})),K.Oc(7,"delete"),K.cc(),K.cc(),K.cc(),K.Yb(8,"hr"),K.ac()}if(2&e){const e=t.$implicit;K.Lb(2),K.vc("ngIf",e.type),K.Lb(1),K.vc("ngIf",e.message),K.Lb(1),K.vc("ngIf",e.receivedAt)}}function we(e,t){if(1&e&&(K.dc(0,"div",14),K.Yb(1,"hr"),K.Mc(2,ye,9,3,"ng-container",15),K.cc()),2&e){const e=K.pc();K.Lb(2),K.vc("ngForOf",e.notifications)}}let Se=(()=>{class e{constructor(e,t,n,r){this.store=e,this.cdRef=t,this.helperService=n,this.utilsService=r,this.COLUMNS={TYPE:"Type",MESSAGE:"Message",RECEIVED:"Received at"},this.pageSize=5,this.page=1,this.NOTIFICATION_TYPE=Z.a,this.sortOptions=[this.COLUMNS.TYPE,this.COLUMNS.MESSAGE,this.COLUMNS.RECEIVED],this.sortOption=new de.e(this.COLUMNS.RECEIVED),this.sortDirectionType="desc",this.sortDirections=["asc","desc"]}ngOnInit(){this.subscriptions=new a.a,this.subscriptions.add(this.notifications$.subscribe(e=>{this.notifications=e&&e.length?e.slice().sort((e,t)=>this.utilsService.compare(e.receivedAt,t.receivedAt,!1)):null,this.cdRef.markForCheck()}))}ngOnDestroy(){this.subscriptions.unsubscribe()}removeNotification(e){e&&this.store.dispatch(new V.f(e))}removeAllNotifications(){this.store.dispatch(new V.e),this.helperService.redirectTo("list")}onSortOption(e){e.value&&this.sortData({active:e.value,direction:this.sortDirectionType})}onSortDirection(e){e.value&&(this.sortDirectionType=e.value,this.sortData({active:this.sortOption.value,direction:e.value}))}sortData(e){const t=this.notifications.slice();this.notifications=e.active&&""!==e.direction?t.sort((t,n)=>{const r="asc"===e.direction;switch(e.active){case this.COLUMNS.TYPE:return this.utilsService.compare(t.type,n.type,r);case this.COLUMNS.MESSAGE:return this.utilsService.compare(t.message,n.message,r);case this.COLUMNS.RECEIVED:return this.utilsService.compare(t.receivedAt,n.receivedAt,r);default:return 0}}):t}}return e.\u0275fac=function(t){return new(t||e)(K.Xb(o.i),K.Xb(K.j),K.Xb(Q.a),K.Xb(Q.c))},e.\u0275cmp=K.Rb({type:e,selectors:[["app-notification"]],decls:22,vars:8,consts:[[1,"h-vh","w-full"],["fxLayout","column","fxLayoutAlign","start center",1,"w-full","h-full","p-5"],["fxLayout","column",1,"notifications-container"],["fxLayout","row","fxLayoutAlign","space-between center","fxLayoutGap","20px",1,"w-full","mb-4"],["fxLayoutAlign","center","fxLayoutGap","10px"],["appearance","outline"],["name","sortOption",3,"formControl","selectionChange"],[3,"value",4,"ngFor","ngForOf"],["name","fontStyle",1,"sort-toggle","mt-1",3,"value","change"],[3,"value"],[3,"color"],["type","button","mat-button","",1,"mb-5",3,"click"],[1,"mr-3"],["class","mb-3",4,"ngIf"],[1,"mb-3"],[4,"ngFor","ngForOf"],["fxLayout","row","fxLayoutAlign","space-between center",1,"notification","py-4"],["class","type","fxLayoutAlign","start center","fxLayoutGap","5px",4,"ngIf"],["class","message mx-3",4,"ngIf"],[4,"ngIf"],["matTooltip","Remove notification",1,"actionable-warn",3,"click"],["fxLayoutAlign","start center","fxLayoutGap","5px",1,"type"],[1,"message","mx-3"]],template:function(e,t){1&e&&(K.dc(0,"mat-drawer-container",0),K.dc(1,"div",1),K.dc(2,"div",2),K.dc(3,"div",3),K.dc(4,"div",4),K.dc(5,"mat-form-field",5),K.dc(6,"mat-label"),K.Oc(7,"Sort by"),K.cc(),K.dc(8,"mat-select",6),K.lc("selectionChange",(function(e){return t.onSortOption(e)})),K.Mc(9,be,3,4,"mat-option",7),K.cc(),K.cc(),K.dc(10,"mat-button-toggle-group",8),K.lc("change",(function(e){return t.onSortDirection(e)})),K.dc(11,"mat-button-toggle",9),K.dc(12,"mat-icon",10),K.Oc(13,"north"),K.cc(),K.cc(),K.dc(14,"mat-button-toggle",9),K.dc(15,"mat-icon",10),K.Oc(16,"south"),K.cc(),K.cc(),K.cc(),K.cc(),K.dc(17,"button",11),K.lc("click",(function(){return t.removeAllNotifications()})),K.dc(18,"mat-icon",12),K.Oc(19,"delete_sweep"),K.cc(),K.Oc(20," Clear all "),K.cc(),K.cc(),K.Mc(21,we,3,1,"div",13),K.cc(),K.cc(),K.cc()),2&e&&(K.Lb(8),K.vc("formControl",t.sortOption),K.Lb(1),K.vc("ngForOf",t.sortOptions),K.Lb(1),K.vc("value",t.sortDirectionType),K.Lb(1),K.vc("value",t.sortDirections[0]),K.Lb(1),K.vc("color",t.sortDirectionType===t.sortDirections[0]?"primary":""),K.Lb(2),K.vc("value",t.sortDirections[1]),K.Lb(1),K.vc("color",t.sortDirectionType===t.sortDirections[1]?"primary":""),K.Lb(6),K.vc("ngIf",t.notifications&&t.notifications.length))},directives:[ue.b,oe.d,oe.c,oe.e,fe.c,fe.g,ge.a,de.n,de.f,r.n,pe.b,pe.a,ce.a,ae.b,r.o,me.m,Ce.a],pipes:[W.a,H.a,r.e],styles:[".notifications-container[_ngcontent-%COMP%]{min-width:700px;max-height:500px}.mat-column-Message[_ngcontent-%COMP%]{max-width:500px!important}[class^=mat-column-][_ngcontent-%COMP%]{padding-right:10px!important}.notification[_ngcontent-%COMP%] .type[_ngcontent-%COMP%]{width:100px}.notification[_ngcontent-%COMP%] .message[_ngcontent-%COMP%]{width:300px;word-wrap:break-word}"],changeDetection:0}),Object(s.b)([Object(o.e)(ne.a.notifications)],e.prototype,"notifications$",void 0),e})();var xe=n("znSr");function Ee(e,t){1&e&&(K.dc(0,"a",18),K.Yb(1,"img",19),K.cc()),2&e&&(K.Lb(1),K.vc("src","assets/images/Auptimizer-dark.png",K.Fc))}function ke(e,t){if(1&e&&(K.dc(0,"mat-option",23),K.Oc(1),K.cc()),2&e){const e=t.$implicit;K.vc("value",e),K.Lb(1),K.Qc("",e," seconds ")}}function _e(e,t){if(1&e){const e=K.ec();K.dc(0,"mat-form-field",20),K.dc(1,"mat-label"),K.Oc(2,"Refresh every"),K.cc(),K.dc(3,"mat-select",21),K.lc("selectionChange",(function(t){return K.Ec(e),K.pc().changeRefreshInverval(t)})),K.qc(4,"async"),K.Mc(5,ke,2,2,"mat-option",22),K.cc(),K.cc()}if(2&e){const e=t.ngIf,n=K.pc();K.Lb(3),K.vc("value",K.rc(4,2,n.refreshInterval$)),K.Lb(2),K.vc("ngForOf",e)}}function Te(e,t){if(1&e){const e=K.ec();K.dc(0,"button",5),K.lc("click",(function(){return K.Ec(e),K.pc().setTheme()})),K.dc(1,"mat-icon",6),K.Oc(2),K.cc(),K.Oc(3),K.qc(4,"flu"),K.cc()}if(2&e){const e=K.pc();K.Lb(2),K.Pc(e.currentTheme.icon),K.Lb(1),K.Qc("",K.rc(4,2,e.currentTheme.name)," mode ")}}function Re(e,t){if(1&e&&(K.dc(0,"span"),K.Oc(1),K.cc()),2&e){const e=K.pc();K.Lb(1),K.Qc("v",e.version,"")}}let Be=(()=>{class e{constructor(e,t,n,r){this.store=e,this.colorSchemeService=t,this.helperService=n,this.cdRef=r,this.THEME_OPTION=re.a,this.currentTheme={name:null,icon:null}}ngOnInit(){this.subscriptions=new a.a,this.version=te.a.version,this.subscriptions.add(this.themes$.subscribe(e=>{this.themes=e})),this.subscriptions.add(this.theme$.subscribe(e=>{this.currentTheme=e})),this.subscriptions.add(this.notifications$.subscribe(e=>{e&&(this.notifications=e,this.cdRef.markForCheck())})),this.subscriptions.add(this.theme$.subscribe(e=>{this.currentTheme=e}))}ngOnDestroy(){this.subscriptions.unsubscribe()}changeRefreshInverval(e){e.value&&this.store.dispatch(new y(e.value))}onRefresh(){this.store.dispatch(new w(!0))}setTheme(){this.themes&&this.themes.length&&(this.currentTheme=this.currentTheme===this.themes[0]?this.themes[1]:this.themes[0],this.store.dispatch(new V.b(this.currentTheme)))}onToggleSideNav(){this.store.dispatch(new _)}}return e.\u0275fac=function(t){return new(t||e)(K.Xb(o.i),K.Xb(ie.a),K.Xb(Q.a),K.Xb(K.j))},e.\u0275cmp=K.Rb({type:e,selectors:[["app-header"]],decls:27,vars:10,consts:[["color","primary","fxLayout","row","fxLayoutAlign","space-between center",1,"header-font"],["fxLayout","row","fxLayoutAlign","center center"],["fxHide.lt-md","","routerLink","/","class","logo-container",4,"ngIf"],["fxLayout","row","fxLayoutGap","40px","fxLayoutGap.md","15px","fxLayoutGap.lt-md","15px","fxLayoutAlign","center center"],["class","mt-5 refresh-interval-select","appearance","outline",4,"ngIf"],["mat-button","",1,"header-font",3,"click"],[1,"mr-3"],["mat-icon-button","",3,"disabled","matMenuTriggerFor"],["matBadgeColor","warn",3,"matBadgeHidden","matBadge"],["mat-button","","class","header-font",3,"click",4,"ngIf"],["fxLayoutAlign","center center"],[4,"ngIf"],["xPosition","before"],["notificationMenu","matMenu"],[1,"notification-menu"],["fxLayout","row","fxLayoutAlign","end center",1,"w-full"],["mat-icon-button",""],[3,"click"],["fxHide.lt-md","","routerLink","/",1,"logo-container"],[3,"src"],["appearance","outline",1,"mt-5","refresh-interval-select"],[3,"value","selectionChange"],[3,"value",4,"ngFor","ngForOf"],[3,"value"]],template:function(e,t){if(1&e&&(K.dc(0,"mat-toolbar",0),K.dc(1,"div",1),K.Mc(2,Ee,2,1,"a",2),K.cc(),K.dc(3,"div",3),K.dc(4,"div",1),K.Mc(5,_e,6,4,"mat-form-field",4),K.qc(6,"async"),K.cc(),K.dc(7,"button",5),K.lc("click",(function(){return t.onRefresh()})),K.dc(8,"mat-icon",6),K.Oc(9,"cached"),K.cc(),K.Oc(10,"Refresh now "),K.cc(),K.dc(11,"button",7),K.dc(12,"mat-icon",8),K.Oc(13," notifications"),K.cc(),K.cc(),K.Mc(14,Te,5,4,"button",9),K.dc(15,"div",10),K.dc(16,"span"),K.Oc(17,"Version:\xa0"),K.cc(),K.Mc(18,Re,2,1,"span",11),K.cc(),K.cc(),K.cc(),K.dc(19,"mat-menu",12,13),K.dc(21,"mat-toolbar",14),K.dc(22,"div",15),K.dc(23,"button",16),K.dc(24,"mat-icon"),K.Oc(25,"close"),K.cc(),K.cc(),K.cc(),K.cc(),K.dc(26,"app-notification",17),K.lc("click",(function(e){return e.stopPropagation()})),K.cc(),K.cc()),2&e){const e=K.Bc(20);K.Lb(2),K.vc("ngIf",null==t.currentTheme?null:t.currentTheme.name),K.Lb(3),K.vc("ngIf",K.rc(6,8,t.refreshIntervalOptions$)),K.Lb(6),K.vc("disabled",!(null!=t.notifications&&t.notifications.length))("matMenuTriggerFor",e),K.Lb(1),K.vc("matBadgeHidden",!(null!=t.notifications&&t.notifications.length))("matBadge",null==t.notifications?null:t.notifications.length),K.Lb(2),K.vc("ngIf",t.currentTheme),K.Lb(4),K.vc("ngIf",t.version)}},directives:[se.a,oe.d,oe.c,r.o,oe.e,ae.b,ce.a,le.d,he.a,le.a,Se,i.i,xe.b,fe.c,fe.g,ge.a,r.n,me.m],pipes:[r.b,W.a],styles:["[_nghost-%COMP%] .logo-container[_ngcontent-%COMP%] > img[_ngcontent-%COMP%]{height:35px} .mat-menu-panel{max-width:none!important} .mat-menu-content:not(:empty){padding:0!important} .mat-form-field-infix{padding:10px!important}.notification-menu[_ngcontent-%COMP%]{height:50px}.header-font[_ngcontent-%COMP%]{font-size:15px}"],changeDetection:0}),Object(s.b)([Object(o.e)(ne.a.themes)],e.prototype,"themes$",void 0),Object(s.b)([Object(o.e)(ne.a.theme)],e.prototype,"theme$",void 0),Object(s.b)([Object(o.e)(ne.a.notifications)],e.prototype,"notifications$",void 0),Object(s.b)([Object(o.e)(q.refreshInterval)],e.prototype,"refreshInterval$",void 0),Object(s.b)([Object(o.e)(q.refreshIntervalOptions)],e.prototype,"refreshIntervalOptions$",void 0),Object(s.b)([Object(o.e)(q.sidenavOpen)],e.prototype,"sidenavOpen$",void 0),e})(),Oe=(()=>{class e{constructor(e,t,n,r){this.store=e,this.route=t,this.router=n,this.snackbarService=r}ngOnInit(){this.subscriptions=new a.a,this.subscriptions.add(this.refreshInterval$.subscribe(e=>{e&&(this.refreshIntervalSubscription&&e!==this.refreshInterval&&this.refreshIntervalSubscription.unsubscribe(),this.refreshInterval=e,this.refreshIntervalSubscription=this.refreshIntervalData().subscribe())})),this.subscriptions.add(this.refreshingInterval$.subscribe(e=>{e&&(this.refreshData(),this.snackbarService.success("Data refreshed!"),this.store.dispatch(new w(!1)))})),this.subscriptions.add(this.interimExperiment$.subscribe(e=>{e&&(this.interimExperiment=e)}))}ngOnDestroy(){this.subscriptions.unsubscribe(),this.refreshIntervalSubscription&&this.refreshIntervalSubscription.unsubscribe()}refreshIntervalData(){if(this.refreshInterval)return d(1e3*this.refreshInterval).pipe(Object(ee.a)(0),Object(F.a)(()=>{console.log("Fetching data..."),this.refreshData()}))}refreshData(){this.store.dispatch(new f),this.store.dispatch(new T),this.interimExperiment&&this.store.dispatch(new A({eid:this.interimExperiment.eid}))}}return e.\u0275fac=function(t){return new(t||e)(K.Xb(o.i),K.Xb(i.a),K.Xb(i.g),K.Xb(Q.b))},e.\u0275cmp=K.Rb({type:e,selectors:[["app-main"]],decls:6,vars:0,consts:[[1,"main-container","text-primary"],[1,"main"],[1,"h-vh","w-full"]],template:function(e,t){1&e&&(K.dc(0,"div",0),K.Yb(1,"app-header"),K.dc(2,"div",1),K.dc(3,"mat-drawer-container",2),K.dc(4,"mat-drawer-content"),K.Yb(5,"router-outlet"),K.cc(),K.cc(),K.cc(),K.cc())},directives:[Be,ue.b,ue.c,i.k],styles:["[_nghost-%COMP%] .main-container[_ngcontent-%COMP%]{height:calc(100vh - 64px)}[_nghost-%COMP%] .logo-container[_ngcontent-%COMP%] > img[_ngcontent-%COMP%]{height:35px}[_nghost-%COMP%] mat-drawer[_ngcontent-%COMP%]{width:300px}[_nghost-%COMP%] mat-drawer[_ngcontent-%COMP%] mat-toolbar[_ngcontent-%COMP%] mat-icon[_ngcontent-%COMP%]{font-size:40px;width:40px;height:40px}[_nghost-%COMP%] mat-drawer[_ngcontent-%COMP%] mat-toolbar[_ngcontent-%COMP%] span[_ngcontent-%COMP%]{font-size:35px;font-weight:200}[_nghost-%COMP%] mat-drawer[_ngcontent-%COMP%] .side-nav-content[_ngcontent-%COMP%] > a[_ngcontent-%COMP%]{padding:10px;font-size:20px;font-weight:400;text-align:center;line-height:1.5;width:100%}[_nghost-%COMP%] .refresh-interval[_ngcontent-%COMP%]{width:150px;font-size:18px;margin-top:10px}[_nghost-%COMP%] .version[_ngcontent-%COMP%]{font-size:15px;font-weight:400}[_nghost-%COMP%] .h-vh[_ngcontent-%COMP%]{height:calc(100vh - 64px)}"]}),Object(s.b)([Object(o.e)(q.refreshInterval)],e.prototype,"refreshInterval$",void 0),Object(s.b)([Object(o.e)(q.refreshingInterval)],e.prototype,"refreshingInterval$",void 0),Object(s.b)([Object(o.e)(q.interimExperiment)],e.prototype,"interimExperiment$",void 0),e})();var Le=n("R0Ic"),Pe=n("MutI");function Fe(e,t){if(1&e&&(K.dc(0,"mat-icon",6),K.Oc(1),K.cc()),2&e){const e=K.pc(2);K.Lb(1),K.Qc("",null==e.navItem?null:e.navItem.iconName," ")}}function Ne(e,t){if(1&e&&(K.dc(0,"span",7),K.qc(1,"async"),K.Oc(2),K.cc()),2&e){const e=K.pc(2);K.vc("ngClass",!1===K.rc(1,2,e.sidenavOpen$)?"hidden":""),K.Lb(2),K.Pc(null==e.navItem?null:e.navItem.displayName)}}function Me(e,t){if(1&e&&(K.dc(0,"span",8),K.Yb(1,"span",8),K.dc(2,"mat-icon"),K.Oc(3," expand_more "),K.cc(),K.cc()),2&e){const e=K.pc(2);K.Lb(2),K.vc("@indicatorRotate",e.expanded?"expanded":"collapsed")}}const De=function(e){return{"padding-left":e}},$e=function(e){return{expanded:e}},We=function(){return{exact:!0}};function He(e,t){if(1&e){const e=K.ec();K.dc(0,"a",2),K.lc("click",(function(){K.Ec(e);const t=K.pc();return t.onItemSelected(t.navItem)})),K.qc(1,"async"),K.Mc(2,Fe,2,1,"mat-icon",3),K.Mc(3,Ne,3,4,"span",4),K.Mc(4,Me,4,1,"span",5),K.cc()}if(2&e){const e=K.pc();K.vc("ngStyle",K.yc(12,De,12*e.depth+"px"))("ngClass",K.yc(14,$e,e.expanded))("routerLink",""!==(null==e.navItem?null:e.navItem.route)?null==e.navItem?null:e.navItem.route:null)("routerLinkActive",""!==(null==e.navItem?null:e.navItem.route)?"menu-list-item--active":"")("routerLinkActiveOptions",K.xc(16,We))("disabled",e.navItem.disabled)("matTooltip",!1===K.rc(1,10,e.sidenavOpen$)?e.navItem.tooltip:""),K.Lb(2),K.vc("ngIf",e.navItem.iconName),K.Lb(1),K.vc("ngIf",e.navItem.displayName),K.Lb(1),K.vc("ngIf",(null==e.navItem?null:e.navItem.children)&&(null==e.navItem?null:e.navItem.children.length))}}function Ge(e,t){if(1&e&&K.Yb(0,"app-sidenav-element",10),2&e){const e=t.$implicit,n=K.pc(2);K.vc("navItem",e)("depth",n.depth+1)}}function Ve(e,t){if(1&e&&(K.dc(0,"div"),K.Mc(1,Ge,1,2,"app-sidenav-element",9),K.cc()),2&e){const e=K.pc();K.Lb(1),K.vc("ngForOf",e.navItem.children)}}let je=(()=>{class e{constructor(){this.depth=0,this.expanded=!1}onItemSelected(e){e.children&&e.children.length&&(this.expanded=!this.expanded)}}return e.\u0275fac=function(t){return new(t||e)},e.\u0275cmp=K.Rb({type:e,selectors:[["app-sidenav-element"]],inputs:{navItem:"navItem",depth:"depth"},decls:2,vars:2,consts:[["mat-list-item","","fxLayout","","fxLayoutAlign","left center","class","menu-list-item my-2 h-15",3,"ngStyle","ngClass","routerLink","routerLinkActive","routerLinkActiveOptions","disabled","matTooltip","click",4,"ngIf"],[4,"ngIf"],["mat-list-item","","fxLayout","","fxLayoutAlign","left center",1,"menu-list-item","my-2","h-15",3,"ngStyle","ngClass","routerLink","routerLinkActive","routerLinkActiveOptions","disabled","matTooltip","click"],["class","mr-10","fxLayout","","fxLayoutAlign","right center",4,"ngIf"],[3,"ngClass",4,"ngIf"],["fxFlex","",4,"ngIf"],["fxLayout","","fxLayoutAlign","right center",1,"mr-10"],[3,"ngClass"],["fxFlex",""],[3,"navItem","depth",4,"ngFor","ngForOf"],[3,"navItem","depth"]],template:function(e,t){1&e&&(K.Mc(0,He,5,17,"a",0),K.Mc(1,Ve,2,1,"div",1)),2&e&&(K.vc("ngIf",t.navItem),K.Lb(1),K.vc("ngIf",t.expanded))},directives:[r.o,Pe.a,i.i,oe.d,oe.c,r.p,xe.c,r.m,xe.a,i.h,Ce.a,ce.a,oe.b,r.n,e],pipes:[r.b],styles:["[_nghost-%COMP%]{display:flex;flex-direction:column;outline:none;width:100%}[_nghost-%COMP%] .menu-list-item[_ngcontent-%COMP%]{margin-right:10px;margin-left:10px;width:auto}[_nghost-%COMP%] .mat-list-item-content{width:100%}[_nghost-%COMP%] .hidden[_ngcontent-%COMP%]{visibility:hidden}"],data:{animation:[Object(Le.m)("indicatorRotate",[Object(Le.j)("collapsed",Object(Le.k)({transform:"rotate(0deg)"})),Object(Le.j)("expanded",Object(Le.k)({transform:"rotate(180deg)"})),Object(Le.l)("expanded <=> collapsed",Object(Le.e)("225ms cubic-bezier(0.4,0.0,0.2,1)"))])]}}),Object(s.b)([Object(o.e)(q.sidenavOpen)],e.prototype,"sidenavOpen$",void 0),e})();function Ze(e,t){1&e&&K.Yb(0,"app-sidenav-element",2),2&e&&K.vc("navItem",t.$implicit)}function ze(e,t){1&e&&(K.dc(0,"div",8),K.dc(1,"mat-icon",9),K.Oc(2,"menu_open"),K.cc(),K.dc(3,"span"),K.Oc(4,"Close menu"),K.cc(),K.cc())}function Ke(e,t){1&e&&(K.dc(0,"div",8),K.dc(1,"mat-icon"),K.Oc(2,"menu"),K.cc(),K.cc())}let Xe=(()=>{class e{constructor(e){this.store=e,this.listElement={displayName:"Experiments",route:"/list",iconName:"west",tooltip:"Experiment list"}}onToggleSideNav(){this.store.dispatch(new _)}}return e.\u0275fac=function(t){return new(t||e)(K.Xb(o.i))},e.\u0275cmp=K.Rb({type:e,selectors:[["app-sidenav"]],inputs:{sideNavElements:"sideNavElements"},decls:11,vars:8,consts:[["fxLayout","column","fxLayoutAlign","space-between center",1,"h-full","w-full"],[1,"w-full"],[3,"navItem"],[1,"mx-5"],[3,"navItem",4,"ngFor","ngForOf"],["fxLayout","row","fxLayoutAlign","center center",1,"w-full","close-menu-container"],["mat-raised-button","",1,"w-full",3,"click"],["class","close-menu-item w-full py-2","fxLayoutAlign","start center",4,"ngIf"],["fxLayoutAlign","start center",1,"close-menu-item","w-full","py-2"],["fxLayout","","fxLayoutAlign","right center",1,"mr-10"]],template:function(e,t){1&e&&(K.dc(0,"mat-nav-list",0),K.dc(1,"div",1),K.Yb(2,"app-sidenav-element",2),K.Yb(3,"hr",3),K.Mc(4,Ze,1,1,"app-sidenav-element",4),K.cc(),K.dc(5,"div",5),K.dc(6,"button",6),K.lc("click",(function(){return t.onToggleSideNav()})),K.Mc(7,ze,5,0,"div",7),K.qc(8,"async"),K.Mc(9,Ke,3,0,"div",7),K.qc(10,"async"),K.cc(),K.cc(),K.cc()),2&e&&(K.Lb(2),K.vc("navItem",t.listElement),K.Lb(2),K.vc("ngForOf",t.sideNavElements),K.Lb(3),K.vc("ngIf",!0===K.rc(8,4,t.sidenavOpen$)),K.Lb(2),K.vc("ngIf",!1===K.rc(10,6,t.sidenavOpen$)))},directives:[Pe.c,oe.d,oe.c,je,r.n,ae.b,r.o,ce.a],pipes:[r.b],styles:[".close-menu-item[_ngcontent-%COMP%]{margin-right:10px;margin-left:10px;width:auto}.close-menu-container[_ngcontent-%COMP%] > button[_ngcontent-%COMP%]{height:52px}.hidden[_ngcontent-%COMP%]{visibility:hidden;width:0;transition:.5s}.mat-list-base[_ngcontent-%COMP%]{padding-top:0!important}"]}),Object(s.b)([Object(o.e)(q.sidenavOpen)],e.prototype,"sidenavOpen$",void 0),e})(),Ue=(()=>{class e{constructor(e,t,n){this.store=e,this.route=t,this.router=n,this.isSidenavOpen=!0}ngOnInit(){this.subscription=new a.a,this.subscription.add(this.navItems$.subscribe(e=>{e&&e.length&&(this.sideNavElements=e)})),this.store.dispatch(new v)}ngOnDestroy(){this.subscription.unsubscribe()}}return e.\u0275fac=function(t){return new(t||e)(K.Xb(o.i),K.Xb(i.a),K.Xb(i.g))},e.\u0275cmp=K.Rb({type:e,selectors:[["app-experiment"]],decls:9,vars:7,consts:[[1,"mat-typography","main-container"],["mode","side","opened",""],["sidenav",""],[3,"sideNavElements"],[1,"p-5"]],template:function(e,t){1&e&&(K.dc(0,"mat-drawer-container",0),K.dc(1,"mat-drawer",1,2),K.qc(3,"async"),K.Yb(4,"app-sidenav",3),K.cc(),K.dc(5,"mat-drawer-content"),K.qc(6,"async"),K.dc(7,"main",4),K.Yb(8,"router-outlet"),K.cc(),K.cc(),K.cc()),2&e&&(K.Lb(1),K.vc("@sidenavVisibility",K.rc(3,3,t.sidenavOpen$)),K.Lb(3),K.vc("sideNavElements",t.sideNavElements),K.Lb(1),K.vc("@contentVisibility",K.rc(6,5,t.sidenavOpen$)))},directives:[ue.b,ue.a,Xe,ue.c,i.k],pipes:[r.b],styles:["[_nghost-%COMP%] .main-container[_ngcontent-%COMP%]{height:calc(100vh - 64px)}[_nghost-%COMP%] .logo-container[_ngcontent-%COMP%] > img[_ngcontent-%COMP%]{height:35px}[_nghost-%COMP%] #sidenav[_ngcontent-%COMP%], [_nghost-%COMP%] mat-drawer[_ngcontent-%COMP%]{width:300px}[_nghost-%COMP%] mat-drawer[_ngcontent-%COMP%] mat-toolbar[_ngcontent-%COMP%] mat-icon[_ngcontent-%COMP%]{font-size:40px;width:40px;height:40px}[_nghost-%COMP%] mat-drawer[_ngcontent-%COMP%] mat-toolbar[_ngcontent-%COMP%] span[_ngcontent-%COMP%]{font-size:35px;font-weight:200}[_nghost-%COMP%] mat-drawer[_ngcontent-%COMP%] .side-nav-content[_ngcontent-%COMP%] > a[_ngcontent-%COMP%]{padding:10px;font-size:20px;font-weight:400;text-align:center;line-height:1.5;width:100%}[_nghost-%COMP%] .refresh-interval[_ngcontent-%COMP%]{width:150px;font-size:18px;margin-top:10px}[_nghost-%COMP%] .version[_ngcontent-%COMP%]{font-size:15px;font-weight:400}"],data:{animation:[Object(Le.m)("sidenavVisibility",[Object(Le.j)("false",Object(Le.k)({width:"75px"})),Object(Le.j)("true",Object(Le.k)({width:"250px"})),Object(Le.l)("false => true",Object(Le.e)("200ms ease-in")),Object(Le.l)("true => false",Object(Le.e)("200ms ease-in"))]),Object(Le.m)("contentVisibility",[Object(Le.j)("false",Object(Le.k)({marginLeft:"75px"})),Object(Le.j)("true",Object(Le.k)({marginLeft:"250px"})),Object(Le.l)("false => true",Object(Le.e)("200ms ease-in")),Object(Le.l)("true => false",Object(Le.e)("200ms ease-in"))])]}}),Object(s.b)([Object(o.e)(q.refreshInterval)],e.prototype,"refreshInterval$",void 0),Object(s.b)([Object(o.e)(q.refreshingInterval)],e.prototype,"refreshingInterval$",void 0),Object(s.b)([Object(o.e)(q.sidenavOpen)],e.prototype,"sidenavOpen$",void 0),Object(s.b)([Object(o.e)(q.navItems)],e.prototype,"navItems$",void 0),e})();var Ye=n("Dh3D"),Je=n("+0xr"),Qe=function(e){return e.CREATED="CREATED",e.RUNNING="RUNNING",e.STOPPED="STOPPED",e.FINISHED="FINISHED",e.FAILED="FAILED",e.STOPPING="STOPPING",e.REQUEST_STOP="REQUEST_STOP",e}({}),qe=n("NXqw"),et=n("Kj3r"),tt=n("/uUt"),nt=n("0IaG"),rt=n("qFsG"),it=n("oOf3"),st=n("Xa2L"),ot=n("Wp6s"),at=n("M9IT");const ct=["paginatorExperiment"],lt=["paginatorResource"],ht=["showConfigDialog"],dt=["showErrorDialog"],ut=["startExperimentDialog"];function ft(e,t){if(1&e){const e=K.ec();K.dc(0,"mat-icon",36),K.lc("click",(function(){return K.Ec(e),K.pc().clearSearch()})),K.Oc(1,"clear "),K.cc()}}function gt(e,t){1&e&&(K.dc(0,"mat-icon",37),K.Oc(1,"search"),K.cc())}function pt(e,t){if(1&e&&(K.dc(0,"mat-option",11),K.Oc(1),K.qc(2,"flu"),K.cc()),2&e){const e=t.$implicit;K.vc("value",e),K.Lb(1),K.Qc(" ",K.rc(2,2,e)," ")}}function mt(e,t){if(1&e&&(K.dc(0,"button",38),K.dc(1,"mat-icon",39),K.Oc(2,"settings"),K.cc(),K.Oc(3),K.qc(4,"uppercase"),K.cc()),2&e){K.pc();const e=K.Bc(59);K.vc("matMenuTriggerFor",e),K.Lb(3),K.Qc(" ",K.rc(4,2,"Resources")," ")}}function Ct(e,t){1&e&&(K.dc(0,"div",40),K.Yb(1,"mat-spinner",41),K.cc()),2&e&&(K.Lb(1),K.vc("diameter",100))}function bt(e,t){1&e&&(K.dc(0,"p",44),K.Oc(1,"No experiments created! "),K.cc())}function vt(e,t){if(1&e&&(K.dc(0,"div",42),K.Mc(1,bt,2,0,"p",43),K.cc()),2&e){const e=K.pc();K.Lb(1),K.vc("ngIf",!e.experiments||e.experiments&&0===e.experiments.length)}}function It(e,t){if(1&e&&(K.dc(0,"span"),K.Oc(1),K.qc(2,"flu"),K.qc(3,"lowercase"),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Pc(K.rc(2,1,K.rc(3,3,e.status)))}}function At(e,t){if(1&e&&(K.dc(0,"span"),K.Oc(1),K.qc(2,"flu"),K.qc(3,"lowercase"),K.cc()),2&e){const e=K.pc(4);K.Lb(1),K.Pc(K.rc(2,1,K.rc(3,3,e.EXPERIMENT_STATUS.FINISHED)))}}function yt(e,t){if(1&e){const e=K.ec();K.dc(0,"button",68),K.lc("click",(function(){K.Ec(e);const t=K.pc().$implicit;return K.pc(3).showErrorDetails(t)})),K.dc(1,"mat-icon"),K.Oc(2,"error"),K.cc(),K.cc()}}function wt(e,t){if(1&e&&(K.dc(0,"div",62),K.Oc(1),K.qc(2,"date"),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Qc(" ",K.sc(2,1,1e3*e.startTime,"short")," ")}}function St(e,t){1&e&&(K.dc(0,"div",62),K.Oc(1,"NA"),K.cc())}function xt(e,t){if(1&e&&(K.dc(0,"div",62),K.Oc(1),K.qc(2,"date"),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Qc(" ",K.sc(2,1,1e3*e.endTime,"short")," ")}}function Et(e,t){1&e&&(K.dc(0,"div",62),K.Oc(1,"NA"),K.cc())}function kt(e,t){if(1&e){const e=K.ec();K.dc(0,"span"),K.dc(1,"button",69),K.lc("click",(function(){K.Ec(e);const t=K.pc().$implicit;return K.pc(3).startExperiment(t)})),K.dc(2,"mat-icon"),K.Oc(3,"play_arrow"),K.cc(),K.Oc(4),K.qc(5,"uppercase"),K.cc(),K.cc()}2&e&&(K.Lb(4),K.Qc(" ",K.rc(5,1,"Start")," "))}function _t(e,t){if(1&e){const e=K.ec();K.dc(0,"span"),K.dc(1,"button",69),K.lc("click",(function(){K.Ec(e);const t=K.pc().$implicit;return K.pc(3).stopExperiment(t.eid)})),K.dc(2,"mat-icon"),K.Oc(3,"stop"),K.cc(),K.Oc(4),K.qc(5,"uppercase"),K.cc(),K.cc()}2&e&&(K.Lb(4),K.Qc(" ",K.rc(5,1,"Stop")," "))}function Tt(e,t){if(1&e){const e=K.ec();K.dc(0,"span"),K.dc(1,"button",69),K.lc("click",(function(){K.Ec(e);const t=K.pc().$implicit;return K.pc(3).startExperiment(t)})),K.dc(2,"mat-icon"),K.Oc(3,"replay"),K.cc(),K.Oc(4),K.qc(5,"uppercase"),K.cc(),K.cc()}2&e&&(K.Lb(4),K.Qc(" ",K.rc(5,1,"Restart")," "))}function Rt(e,t){if(1&e){const e=K.ec();K.dc(0,"span"),K.dc(1,"button",69),K.lc("click",(function(){K.Ec(e);const t=K.pc().$implicit;return K.pc(3).startExperiment(t)})),K.dc(2,"mat-icon"),K.Oc(3,"replay"),K.cc(),K.Oc(4),K.qc(5,"uppercase"),K.cc(),K.cc()}2&e&&(K.Lb(4),K.Qc(" ",K.rc(5,1,"Restart")," "))}function Bt(e,t){if(1&e){const e=K.ec();K.dc(0,"span"),K.dc(1,"button",69),K.lc("click",(function(){K.Ec(e);const t=K.pc().$implicit;return K.pc(3).startExperiment(t)})),K.dc(2,"mat-icon"),K.Oc(3,"replay"),K.cc(),K.Oc(4),K.qc(5,"uppercase"),K.cc(),K.cc()}2&e&&(K.Lb(4),K.Qc(" ",K.rc(5,1,"Restart")," "))}function Ot(e,t){1&e&&(K.dc(0,"span"),K.Oc(1),K.qc(2,"uppercase"),K.cc()),2&e&&(K.Lb(1),K.Qc(" ",K.rc(2,1,"Stopping")," "))}function Lt(e,t){1&e&&(K.dc(0,"span"),K.Oc(1),K.qc(2,"uppercase"),K.cc()),2&e&&(K.Lb(1),K.Qc(" ",K.rc(2,1,"Stopping")," "))}function Pt(e,t){if(1&e){const e=K.ec();K.dc(0,"button",70),K.lc("click",(function(){K.Ec(e);const t=K.pc().$implicit;return K.pc(3).showConfig(t)})),K.Oc(1),K.qc(2,"uppercase"),K.cc()}2&e&&(K.Lb(1),K.Qc(" ",K.rc(2,1,"Config")," "))}const Ft=function(){return[25,"..."]};function Nt(e,t){if(1&e){const e=K.ec();K.dc(0,"mat-card",49),K.dc(1,"div",50),K.dc(2,"div",51),K.dc(3,"div"),K.Oc(4),K.cc(),K.dc(5,"div",52),K.Oc(6),K.cc(),K.cc(),K.dc(7,"div"),K.dc(8,"button",53),K.lc("click",(function(){K.Ec(e);const n=t.$implicit;return K.pc(3).helperService.redirectTo("create",n.eid)})),K.Yb(9,"mat-icon",54),K.cc(),K.dc(10,"button",55),K.lc("click",(function(){K.Ec(e);const n=t.$implicit;return K.pc(3).deleteExperiment(n.eid)})),K.dc(11,"mat-icon"),K.Oc(12,"delete"),K.cc(),K.cc(),K.cc(),K.cc(),K.Yb(13,"hr"),K.dc(14,"mat-card-content",56),K.dc(15,"div",2),K.dc(16,"div",57),K.Oc(17,"Script Name:"),K.cc(),K.dc(18,"div",58),K.Oc(19),K.qc(20,"truncate"),K.cc(),K.cc(),K.dc(21,"div",2),K.dc(22,"div",57),K.Oc(23,"Status:"),K.cc(),K.dc(24,"div",59),K.Mc(25,It,4,5,"span",23),K.Mc(26,At,4,5,"span",23),K.Mc(27,yt,3,0,"button",60),K.cc(),K.cc(),K.dc(28,"div",2),K.dc(29,"div",57),K.Oc(30,"Start Time:"),K.cc(),K.Mc(31,wt,3,4,"div",61),K.Mc(32,St,2,0,"div",61),K.cc(),K.dc(33,"div",2),K.dc(34,"div",57),K.Oc(35,"End Time:"),K.cc(),K.Mc(36,xt,3,4,"div",61),K.Mc(37,Et,2,0,"div",61),K.cc(),K.dc(38,"div",2),K.dc(39,"div",57),K.Oc(40,"Best Result:"),K.cc(),K.dc(41,"div",62),K.Oc(42),K.qc(43,"roundNumber"),K.cc(),K.cc(),K.cc(),K.Yb(44,"hr"),K.dc(45,"mat-card-actions",63),K.dc(46,"div",64),K.Mc(47,kt,6,3,"span",65),K.Mc(48,_t,6,3,"span",65),K.Mc(49,Tt,6,3,"span",65),K.Mc(50,Rt,6,3,"span",65),K.Mc(51,Bt,6,3,"span",65),K.Mc(52,Ot,3,3,"span",65),K.Mc(53,Lt,3,3,"span",65),K.cc(),K.dc(54,"div"),K.Mc(55,Pt,3,3,"button",66),K.dc(56,"button",67),K.lc("click",(function(){K.Ec(e);const n=t.$implicit;return K.pc(3).onDetails(n.eid)})),K.Oc(57),K.qc(58,"uppercase"),K.cc(),K.cc(),K.cc(),K.cc()}if(2&e){const e=t.$implicit,n=K.pc(3);K.Lb(4),K.Pc(e.eid),K.Lb(1),K.vc("matTooltip",e.experimentName||e.scriptName),K.Lb(1),K.Qc("",e.experimentName||e.scriptName," "),K.Lb(4),K.vc("disabled",e.status===n.EXPERIMENT_STATUS.RUNNING||e.status===n.EXPERIMENT_STATUS.STOPPING||e.status===n.EXPERIMENT_STATUS.REQUEST_STOP),K.Lb(8),K.vc("matTooltip",e.scriptName),K.Lb(1),K.Qc(" ",K.sc(20,24,e.scriptName,K.xc(31,Ft))," "),K.Lb(6),K.vc("ngIf",e.status),K.Lb(1),K.vc("ngIf",!e.status),K.Lb(1),K.vc("ngIf",e.status===n.EXPERIMENT_STATUS.FAILED),K.Lb(4),K.vc("ngIf",e.startTime&&-1!==e.startTime),K.Lb(1),K.vc("ngIf",null===e.startTime),K.Lb(4),K.vc("ngIf",e.endTime&&-1!==e.endTime),K.Lb(1),K.vc("ngIf",null===e.endTime),K.Lb(5),K.Qc(" ",null!==e.bestScore?K.rc(43,27,e.bestScore):"NA"," "),K.Lb(4),K.vc("ngSwitch",e.status),K.Lb(1),K.vc("ngSwitchCase",n.EXPERIMENT_STATUS.CREATED),K.Lb(1),K.vc("ngSwitchCase",n.EXPERIMENT_STATUS.RUNNING),K.Lb(1),K.vc("ngSwitchCase",n.EXPERIMENT_STATUS.STOPPED),K.Lb(1),K.vc("ngSwitchCase",n.EXPERIMENT_STATUS.FINISHED),K.Lb(1),K.vc("ngSwitchCase",n.EXPERIMENT_STATUS.FAILED),K.Lb(1),K.vc("ngSwitchCase",n.EXPERIMENT_STATUS.STOPPING),K.Lb(1),K.vc("ngSwitchCase",n.EXPERIMENT_STATUS.REQUEST_STOP),K.Lb(2),K.vc("ngIf",e.expConfig),K.Lb(2),K.Qc(" ",K.rc(58,29,"Results")," ")}}const Mt=function(e,t){return{itemsPerPage:e,currentPage:t}};function Dt(e,t){if(1&e&&(K.dc(0,"div",47),K.Mc(1,Nt,59,32,"mat-card",48),K.qc(2,"paginate"),K.cc()),2&e){const e=K.pc(2);K.Lb(1),K.vc("ngForOf",K.sc(2,1,e.experiments,K.zc(4,Mt,e.pageSize,e.page)))}}function $t(e,t){if(1&e){const e=K.ec();K.dc(0,"button",89),K.lc("click",(function(){K.Ec(e);const t=K.pc().$implicit;return K.pc(3).showConfig(t)})),K.Oc(1),K.qc(2,"uppercase"),K.cc()}2&e&&(K.Lb(1),K.Qc(" ",K.rc(2,1,"Config")," "))}function Wt(e,t){if(1&e&&(K.dc(0,"span"),K.Oc(1),K.qc(2,"flu"),K.qc(3,"lowercase"),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Pc(K.rc(2,1,K.rc(3,3,e.status)))}}function Ht(e,t){if(1&e&&(K.dc(0,"span"),K.Oc(1),K.qc(2,"flu"),K.qc(3,"lowercase"),K.cc()),2&e){const e=K.pc(4);K.Lb(1),K.Pc(K.rc(2,1,K.rc(3,3,e.EXPERIMENT_STATUS.FINISHED)))}}function Gt(e,t){if(1&e){const e=K.ec();K.dc(0,"button",68),K.lc("click",(function(){K.Ec(e);const t=K.pc().$implicit;return K.pc(3).showErrorDetails(t)})),K.dc(1,"mat-icon"),K.Oc(2,"error"),K.cc(),K.cc()}}function Vt(e,t){if(1&e&&(K.dc(0,"div",62),K.Oc(1),K.qc(2,"date"),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Qc(" ",K.sc(2,1,1e3*e.startTime,"short")," ")}}function jt(e,t){1&e&&(K.dc(0,"div",62),K.Oc(1,"NA"),K.cc())}function Zt(e,t){if(1&e&&(K.dc(0,"div",62),K.Oc(1),K.qc(2,"date"),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Qc(" ",K.sc(2,1,1e3*e.endTime,"short")," ")}}function zt(e,t){1&e&&(K.dc(0,"div",62),K.Oc(1,"NA"),K.cc())}function Kt(e,t){if(1&e){const e=K.ec();K.dc(0,"span"),K.dc(1,"button",17),K.lc("click",(function(){K.Ec(e);const t=K.pc().$implicit;return K.pc(3).startExperiment(t)})),K.dc(2,"mat-icon"),K.Oc(3,"play_arrow"),K.cc(),K.Oc(4),K.qc(5,"uppercase"),K.cc(),K.cc()}2&e&&(K.Lb(4),K.Qc(" ",K.rc(5,1,"Start")," "))}function Xt(e,t){if(1&e){const e=K.ec();K.dc(0,"span"),K.dc(1,"button",17),K.lc("click",(function(){K.Ec(e);const t=K.pc().$implicit;return K.pc(3).stopExperiment(t.eid)})),K.dc(2,"mat-icon"),K.Oc(3,"stop"),K.cc(),K.Oc(4),K.qc(5,"uppercase"),K.cc(),K.cc()}2&e&&(K.Lb(4),K.Qc(" ",K.rc(5,1,"Stop")," "))}function Ut(e,t){if(1&e){const e=K.ec();K.dc(0,"span"),K.dc(1,"button",17),K.lc("click",(function(){K.Ec(e);const t=K.pc().$implicit;return K.pc(3).startExperiment(t)})),K.dc(2,"mat-icon"),K.Oc(3,"replay"),K.cc(),K.Oc(4),K.qc(5,"uppercase"),K.cc(),K.cc()}2&e&&(K.Lb(4),K.Qc(" ",K.rc(5,1,"Restart")," "))}function Yt(e,t){if(1&e){const e=K.ec();K.dc(0,"span"),K.dc(1,"button",17),K.lc("click",(function(){K.Ec(e);const t=K.pc().$implicit;return K.pc(3).startExperiment(t)})),K.dc(2,"mat-icon"),K.Oc(3,"replay"),K.cc(),K.Oc(4),K.qc(5,"uppercase"),K.cc(),K.cc()}2&e&&(K.Lb(4),K.Qc(" ",K.rc(5,1,"Restart")," "))}function Jt(e,t){if(1&e){const e=K.ec();K.dc(0,"span"),K.dc(1,"button",17),K.lc("click",(function(){K.Ec(e);const t=K.pc().$implicit;return K.pc(3).startExperiment(t)})),K.dc(2,"mat-icon"),K.Oc(3,"replay"),K.cc(),K.Oc(4),K.qc(5,"uppercase"),K.cc(),K.cc()}2&e&&(K.Lb(4),K.Qc(" ",K.rc(5,1,"Restart")," "))}function Qt(e,t){1&e&&(K.dc(0,"span"),K.Oc(1),K.qc(2,"uppercase"),K.cc()),2&e&&(K.Lb(1),K.Qc(" ",K.rc(2,1,"Stopping")," "))}function qt(e,t){1&e&&(K.dc(0,"span"),K.Oc(1),K.qc(2,"uppercase"),K.cc()),2&e&&(K.Lb(1),K.Qc(" ",K.rc(2,1,"Stopping")," "))}function en(e,t){if(1&e){const e=K.ec();K.dc(0,"mat-card",73),K.dc(1,"div",74),K.dc(2,"div",75),K.dc(3,"div"),K.Oc(4),K.cc(),K.dc(5,"div",76),K.Oc(6),K.cc(),K.cc(),K.dc(7,"div",77),K.Mc(8,$t,3,3,"button",78),K.dc(9,"button",79),K.lc("click",(function(){K.Ec(e);const n=t.$implicit;return K.pc(3).onDetails(n.eid)})),K.Oc(10),K.qc(11,"uppercase"),K.cc(),K.cc(),K.cc(),K.dc(12,"div",80),K.dc(13,"div",81),K.dc(14,"div",82),K.Oc(15,"Best Result:"),K.cc(),K.dc(16,"div",62),K.Oc(17),K.qc(18,"roundNumber"),K.cc(),K.cc(),K.dc(19,"div",83),K.dc(20,"div",82),K.Oc(21,"Script Name:"),K.cc(),K.dc(22,"div",84),K.Oc(23),K.cc(),K.cc(),K.dc(24,"div",81),K.dc(25,"div",82),K.Oc(26,"Status:"),K.cc(),K.dc(27,"div",59),K.Mc(28,Wt,4,5,"span",23),K.Mc(29,Ht,4,5,"span",23),K.Mc(30,Gt,3,0,"button",60),K.cc(),K.cc(),K.dc(31,"div",85),K.dc(32,"div",82),K.Oc(33,"Start Time:"),K.cc(),K.Mc(34,Vt,3,4,"div",61),K.Mc(35,jt,2,0,"div",61),K.cc(),K.dc(36,"div",85),K.dc(37,"div",82),K.Oc(38,"End Time:"),K.cc(),K.Mc(39,Zt,3,4,"div",61),K.Mc(40,zt,2,0,"div",61),K.cc(),K.cc(),K.dc(41,"div",86),K.dc(42,"div",87),K.Mc(43,Kt,6,3,"span",65),K.Mc(44,Xt,6,3,"span",65),K.Mc(45,Ut,6,3,"span",65),K.Mc(46,Yt,6,3,"span",65),K.Mc(47,Jt,6,3,"span",65),K.Mc(48,Qt,3,3,"span",65),K.Mc(49,qt,3,3,"span",65),K.cc(),K.dc(50,"div"),K.dc(51,"button",53),K.lc("click",(function(){K.Ec(e);const n=t.$implicit;return K.pc(3).helperService.redirectTo("create",n.eid)})),K.Yb(52,"mat-icon",54),K.cc(),K.dc(53,"button",88),K.lc("click",(function(){K.Ec(e);const n=t.$implicit;return K.pc(3).deleteExperiment(n.eid)})),K.dc(54,"mat-icon"),K.Oc(55,"delete"),K.cc(),K.cc(),K.cc(),K.cc(),K.cc()}if(2&e){const e=t.$implicit,n=K.pc(3);K.Lb(4),K.Pc(e.eid),K.Lb(1),K.vc("matTooltip",e.experimentName||e.scriptName),K.Lb(1),K.Pc(e.experimentName||e.scriptName),K.Lb(2),K.vc("ngIf",e.expConfig),K.Lb(2),K.Qc(" ",K.rc(11,26,"Results")," "),K.Lb(7),K.Qc(" ",null!==e.bestScore?K.rc(18,28,e.bestScore):"NA"," "),K.Lb(5),K.vc("matTooltip",e.scriptName),K.Lb(1),K.Qc(" ",e.scriptName," "),K.Lb(5),K.vc("ngIf",e.status),K.Lb(1),K.vc("ngIf",!e.status),K.Lb(1),K.vc("ngIf",e.status===n.EXPERIMENT_STATUS.FAILED),K.Lb(4),K.vc("ngIf",e.startTime&&-1!==e.startTime),K.Lb(1),K.vc("ngIf",null===e.startTime),K.Lb(4),K.vc("ngIf",e.endTime&&-1!==e.endTime),K.Lb(1),K.vc("ngIf",null===e.endTime),K.Lb(2),K.vc("ngSwitch",e.status),K.Lb(1),K.vc("ngSwitchCase",n.EXPERIMENT_STATUS.CREATED),K.Lb(1),K.vc("ngSwitchCase",n.EXPERIMENT_STATUS.RUNNING),K.Lb(1),K.vc("ngSwitchCase",n.EXPERIMENT_STATUS.STOPPED),K.Lb(1),K.vc("ngSwitchCase",n.EXPERIMENT_STATUS.FINISHED),K.Lb(1),K.vc("ngSwitchCase",n.EXPERIMENT_STATUS.FAILED),K.Lb(1),K.vc("ngSwitchCase",n.EXPERIMENT_STATUS.STOPPING),K.Lb(1),K.vc("ngSwitchCase",n.EXPERIMENT_STATUS.REQUEST_STOP),K.Lb(4),K.Nb(e.status===n.EXPERIMENT_STATUS.RUNNING||e.status===n.EXPERIMENT_STATUS.STOPPING||e.status===n.EXPERIMENT_STATUS.REQUEST_STOP?"":"actionable-warn"),K.vc("disabled",e.status===n.EXPERIMENT_STATUS.RUNNING||e.status===n.EXPERIMENT_STATUS.STOPPING||e.status===n.EXPERIMENT_STATUS.REQUEST_STOP)}}function tn(e,t){if(1&e&&(K.dc(0,"div",71),K.Mc(1,en,56,30,"mat-card",72),K.qc(2,"paginate"),K.cc()),2&e){const e=K.pc(2);K.Lb(1),K.vc("ngForOf",K.sc(2,1,e.experiments,K.zc(4,Mt,e.pageSize,e.page)))}}function nn(e,t){if(1&e&&(K.bc(0),K.Mc(1,Dt,3,7,"div",45),K.qc(2,"async"),K.Mc(3,tn,3,7,"div",46),K.qc(4,"async"),K.ac()),2&e){const e=K.pc();K.Lb(1),K.vc("ngIf",K.rc(2,2,e.experimentViewType$)===e.VIEW_TYPE.CARD),K.Lb(2),K.vc("ngIf",K.rc(4,4,e.experimentViewType$)===e.VIEW_TYPE.LIST)}}function rn(e,t){if(1&e&&(K.dc(0,"div",90),K.dc(1,"div",91),K.Oc(2),K.cc(),K.dc(3,"button",92),K.dc(4,"mat-icon"),K.Oc(5,"close"),K.cc(),K.cc(),K.cc(),K.dc(6,"div",93),K.dc(7,"pre"),K.Oc(8),K.qc(9,"json"),K.cc(),K.cc()),2&e){const e=t.$implicit;K.Lb(2),K.Pc(e.name),K.Lb(6),K.Pc(K.rc(9,2,e.config))}}function sn(e,t){if(1&e&&(K.dc(0,"div",90),K.dc(1,"div",91),K.Oc(2),K.cc(),K.dc(3,"button",92),K.dc(4,"mat-icon"),K.Oc(5,"close"),K.cc(),K.cc(),K.cc(),K.dc(6,"div",94),K.dc(7,"pre"),K.Oc(8),K.cc(),K.cc()),2&e){const e=t.$implicit;K.Lb(2),K.Pc(e.name),K.Lb(6),K.Pc(e.message)}}function on(e,t){if(1&e){const e=K.ec();K.dc(0,"div",95),K.dc(1,"form",96,97),K.lc("ngSubmit",(function(){K.Ec(e);const t=K.Bc(2);return K.pc().onConfirmStartExperiment(t)})),K.dc(4,"mat-dialog-content"),K.dc(5,"mat-form-field",98),K.dc(6,"mat-label"),K.Oc(7," Current working directory "),K.cc(),K.Yb(8,"input",99),K.dc(9,"mat-error"),K.Oc(10,"Missing field"),K.cc(),K.cc(),K.dc(11,"mat-dialog-actions",100),K.dc(12,"button",101),K.lc("click",(function(){return K.Ec(e),K.pc().onExpCancel()})),K.Oc(13,"Cancel"),K.cc(),K.dc(14,"button",102),K.Oc(15," Start "),K.cc(),K.cc(),K.cc(),K.cc(),K.cc()}if(2&e){const e=K.pc();K.Lb(1),K.vc("formGroup",e.startExperimentForm),K.Lb(13),K.vc("disabled",!e.startExperimentForm.valid)}}function an(e,t){if(1&e&&(K.dc(0,"th",113),K.Oc(1),K.cc()),2&e){const e=K.pc(2);K.Lb(1),K.Qc(" ",e.RESOURCE_COLUMNS.ID," ")}}function cn(e,t){if(1&e&&(K.dc(0,"span"),K.Oc(1),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Pc(e.rid)}}function ln(e,t){if(1&e&&(K.dc(0,"td",114),K.Mc(1,cn,2,1,"span",23),K.cc()),2&e){const e=t.$implicit;K.Lb(1),K.vc("ngIf",e.rid)}}function hn(e,t){if(1&e&&(K.dc(0,"th",113),K.Oc(1),K.cc()),2&e){const e=K.pc(2);K.Lb(1),K.Qc(" ",e.RESOURCE_COLUMNS.NAME," ")}}function dn(e,t){if(1&e&&(K.dc(0,"span"),K.Oc(1),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Pc(e.name)}}function un(e,t){if(1&e&&(K.dc(0,"td",114),K.Mc(1,dn,2,1,"span",23),K.cc()),2&e){const e=t.$implicit;K.Lb(1),K.vc("ngIf",e.name)}}function fn(e,t){if(1&e&&(K.dc(0,"th",113),K.Oc(1),K.cc()),2&e){const e=K.pc(2);K.Lb(1),K.Qc(" ",e.RESOURCE_COLUMNS.STATUS," ")}}function gn(e,t){if(1&e&&(K.dc(0,"span"),K.Oc(1),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Pc(e.status)}}function pn(e,t){if(1&e&&(K.dc(0,"td",114),K.Mc(1,gn,2,1,"span",23),K.cc()),2&e){const e=t.$implicit;K.Lb(1),K.vc("ngIf",e.status)}}function mn(e,t){if(1&e&&(K.dc(0,"th",113),K.Oc(1),K.cc()),2&e){const e=K.pc(2);K.Lb(1),K.Qc(" ",e.RESOURCE_COLUMNS.TYPE," ")}}function Cn(e,t){if(1&e&&(K.dc(0,"span"),K.Oc(1),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Pc(e.type)}}function bn(e,t){if(1&e&&(K.dc(0,"td",114),K.Mc(1,Cn,2,1,"span",23),K.cc()),2&e){const e=t.$implicit;K.Lb(1),K.vc("ngIf",e.type)}}function vn(e,t){1&e&&K.Yb(0,"tr",115)}function In(e,t){1&e&&K.Yb(0,"tr",116)}function An(e,t){if(1&e){const e=K.ec();K.dc(0,"div",103),K.lc("click",(function(e){return e.stopPropagation()})),K.dc(1,"div",104),K.dc(2,"table",105),K.lc("matSortChange",(function(t){return K.Ec(e),K.pc().sortResourceData(t)})),K.bc(3,106),K.Mc(4,an,2,1,"th",107),K.Mc(5,ln,2,1,"td",108),K.ac(),K.bc(6,106),K.Mc(7,hn,2,1,"th",107),K.Mc(8,un,2,1,"td",108),K.ac(),K.bc(9,106),K.Mc(10,fn,2,1,"th",107),K.Mc(11,pn,2,1,"td",108),K.ac(),K.bc(12,106),K.Mc(13,mn,2,1,"th",107),K.Mc(14,bn,2,1,"td",108),K.ac(),K.Mc(15,vn,1,0,"tr",109),K.Mc(16,In,1,0,"tr",110),K.cc(),K.cc(),K.Yb(17,"mat-paginator",111,112),K.qc(19,"async"),K.cc()}if(2&e){const e=K.pc();K.Lb(2),K.vc("dataSource",e.dataSourceResource),K.Lb(1),K.wc("matColumnDef",e.RESOURCE_COLUMNS.ID),K.Lb(3),K.wc("matColumnDef",e.RESOURCE_COLUMNS.NAME),K.Lb(3),K.wc("matColumnDef",e.RESOURCE_COLUMNS.STATUS),K.Lb(3),K.wc("matColumnDef",e.RESOURCE_COLUMNS.TYPE),K.Lb(3),K.vc("matHeaderRowDef",e.displayedResourceColumns)("matHeaderRowDefSticky",!0),K.Lb(1),K.vc("matRowDefColumns",e.displayedResourceColumns),K.Lb(1),K.Jc("display",(null==e.dataSourceResource.data?null:e.dataSourceResource.data.length)>0&&K.rc(19,11,e.resources$)?"block":"none"),K.vc("pageSize",e.pageSize)}}function yn(e,t){1&e&&(K.dc(0,"h1",117),K.Oc(1,"No resource data available!"),K.cc())}let wn=(()=>{class e{constructor(e,t,n,r,i,s){this.store=e,this.cdRef=t,this.dialog=n,this.fb=r,this.utilsService=i,this.helperService=s,this.EXPERIMENT_COLUMNS={ID:"ID",EXPERIMENT_NAME:"Experiment Name",SCRIPT_NAME:"Script Name",END_TIME:"End Time",STATUS:"Status",CONFIG:"Config",START_TIME:"Start Time",DETAILS:"Details",ACTION:"Action",CREATE:"Create",DELETE:"Delete"},this.displayedExperimentColumns=[this.EXPERIMENT_COLUMNS.ID,this.EXPERIMENT_COLUMNS.EXPERIMENT_NAME,this.EXPERIMENT_COLUMNS.SCRIPT_NAME,this.EXPERIMENT_COLUMNS.CONFIG,this.EXPERIMENT_COLUMNS.START_TIME,this.EXPERIMENT_COLUMNS.END_TIME,this.EXPERIMENT_COLUMNS.STATUS,this.EXPERIMENT_COLUMNS.DETAILS,this.EXPERIMENT_COLUMNS.ACTION,this.EXPERIMENT_COLUMNS.CREATE,this.EXPERIMENT_COLUMNS.DELETE],this.RESOURCE_COLUMNS={ID:"ID",NAME:"Name",STATUS:"Status",TYPE:"Type"},this.displayedResourceColumns=[this.RESOURCE_COLUMNS.ID,this.RESOURCE_COLUMNS.NAME,this.RESOURCE_COLUMNS.STATUS,this.RESOURCE_COLUMNS.TYPE],this.dataSourceResource=new Je.k,this.pageSize=8,this.page=1,this.VIEW_TYPE=z,this.sortOptions=[this.EXPERIMENT_COLUMNS.ID,this.EXPERIMENT_COLUMNS.EXPERIMENT_NAME,this.EXPERIMENT_COLUMNS.SCRIPT_NAME,this.EXPERIMENT_COLUMNS.START_TIME,this.EXPERIMENT_COLUMNS.END_TIME,this.EXPERIMENT_COLUMNS.STATUS],this.sortOption=new de.e(this.EXPERIMENT_COLUMNS.ID),this.searchStr=new de.e,this.sortDirectionType="desc",this.sortDirections=["asc","desc"],this.EXPERIMENT_STATUS=Qe,this.showResources=!1}ngOnInit(){this.store.dispatch(new f),this.subscriptions=new a.a,this.startExperimentForm=this.fb.group({cwd:["",[de.t.required]]}),this.subscriptions.add(this.experiments$.subscribe(e=>{this.searchStr.value||(this.experiments=e&&e.length?e.slice().sort((e,t)=>this.utilsService.compare(e.eid,t.eid,!1)):null),this.allExperiments=e&&e.length?e.slice().sort((e,t)=>this.utilsService.compare(e.eid,t.eid,!1)):null,this.sort&&this.sortExperimentData(),this.cdRef.markForCheck()})),this.subscriptions.add(this.resources$.subscribe(e=>{this.dataSourceResource.data=e&&e.length?e.slice():null,this.resources=e&&e.length?e.slice():null,this.dataSourceResource.paginator=this.paginatorResource,this.dataSourceResource.sort=this.sortResource,this.cdRef.markForCheck()})),this.subscriptions.add(this.searchStr.valueChanges.pipe(Object(et.a)(300),Object(tt.a)()).subscribe(e=>{const t=this.utilsService.trimString(e);this.searchStringInExperiments(t)}))}ngOnDestroy(){this.subscriptions.unsubscribe()}clearSearch(){this.searchStr.setValue("")}changeDisplayView(e){e&&this.store.dispatch(new B(e))}showConfig(e){if(!e)return;const t=JSON.parse(e.expConfig);this.showConfigDialogRef=this.dialog.open(this.showConfigDialog,{width:"650px",data:{name:e.experimentName,config:t},panelClass:"info-modal"}),this.subscriptions.add(this.showConfigDialogRef.afterClosed().subscribe())}showErrorDetails(e){e&&e.errorMsg&&(this.showErrorDialogRef=this.dialog.open(this.showErrorDialog,{width:"650px",data:{name:e.experimentName,message:e.errorMsg},panelClass:"info-modal"}),this.subscriptions.add(this.showErrorDialogRef.afterClosed().subscribe()))}startExperiment(e){e&&e.expConfigDetails.workingdir&&this.store.dispatch(new S({eid:e.eid}))}onConfirmStartExperiment(e){this.startExperimentForm.valid&&(this.store.dispatch(new S({eid:this.selectedStartExperiment})),e.resetForm(),this.startExperimentForm.reset())}stopExperiment(e){e&&this.store.dispatch(new x(e))}onExpCancel(){this.startExperimentForm.reset()}onDetails(e){e&&this.helperService.redirectTo("/experiment",e)}onSortOption(e){e.value&&(this.sort={active:e.value,direction:this.sortDirectionType},this.sortExperimentData())}onSortDirection(e){e.value&&(this.sortDirectionType=e.value,this.sort={active:this.sortOption.value,direction:e.value},this.sortExperimentData())}sortExperimentData(){const e=this.sort,t=this.allExperiments.slice();this.experiments=e.active&&""!==e.direction?t.sort((t,n)=>{const r="asc"===e.direction;switch(e.active){case this.EXPERIMENT_COLUMNS.ID:return this.utilsService.compare(t.eid,n.eid,r);case this.EXPERIMENT_COLUMNS.EXPERIMENT_NAME:return this.utilsService.compare(t.experimentName,n.experimentName,r);case this.EXPERIMENT_COLUMNS.SCRIPT_NAME:return this.utilsService.compare(t.scriptName,n.scriptName,r);case this.EXPERIMENT_COLUMNS.START_TIME:return this.utilsService.compare(t.startTime,n.startTime,r);case this.EXPERIMENT_COLUMNS.END_TIME:return this.utilsService.compare(t.endTime,n.endTime,r);case this.EXPERIMENT_COLUMNS.STATUS:return this.utilsService.compare(t.status,n.status,r);default:return 0}}):t}sortResourceData(e){const t=this.resources.slice();this.dataSourceResource.data=e.active&&""!==e.direction?t.sort((t,n)=>{const r="asc"===e.direction;switch(e.active){case this.RESOURCE_COLUMNS.ID:return this.utilsService.compare(t.rid,n.rid,r);case this.RESOURCE_COLUMNS.NAME:return this.utilsService.compare(t.name,n.name,r);case this.RESOURCE_COLUMNS.STATUS:return this.utilsService.compare(t.status,n.status,r);case this.RESOURCE_COLUMNS.TYPE:return this.utilsService.compare(t.type,n.type,r);default:return 0}}):t}searchStringInExperiments(e){this.experiments=this.allExperiments.filter(t=>this.utilsService.trimString(t.experimentName).indexOf(e)>=0),this.cdRef.markForCheck()}deleteExperiment(e){e&&(this.confirmDialogRef=this.dialog.open(qe.a,{width:"450px",data:{title:"Delete experiment",content:"Are you sure you want to permanently delete this experiment?",confirmButtonText:"Delete"},panelClass:"header-modal"}),this.confirmDialogRef.afterClosed().subscribe(t=>{t&&this.store.dispatch(new R(e))}))}}return e.\u0275fac=function(t){return new(t||e)(K.Xb(o.i),K.Xb(K.j),K.Xb(nt.b),K.Xb(de.d),K.Xb(Q.c),K.Xb(Q.a))},e.\u0275cmp=K.Rb({type:e,selectors:[["app-list"]],viewQuery:function(e,t){if(1&e&&(K.Ic(ct,!0),K.Ic(Ye.a,!0),K.Ic(lt,!0),K.Ic(Ye.a,!0),K.Ic(ht,!0),K.Ic(dt,!0),K.Ic(ut,!0)),2&e){let e;K.Ac(e=K.mc())&&(t.paginatorExperiment=e.first),K.Ac(e=K.mc())&&(t.sortExperiment=e.first),K.Ac(e=K.mc())&&(t.paginatorResource=e.first),K.Ac(e=K.mc())&&(t.sortResource=e.first),K.Ac(e=K.mc())&&(t.showConfigDialog=e.first),K.Ac(e=K.mc())&&(t.showErrorDialog=e.first),K.Ac(e=K.mc())&&(t.startExperimentDialog=e.first)}},decls:68,vars:38,consts:[[1,"px-5","mb-5"],["fxLayout","row wrap","fxLayoutAlign","space-between center"],["fxLayout","row","fxLayoutAlign","start center"],[1,"mat-display-1","mt-0","mb-0","mr-2"],["appearance","outline",1,"mr-2","mt-4"],["type","text","matInput","","placeholder","Experiment name",3,"formControl"],["matSuffix","","class","cursor-pointer",3,"click",4,"ngIf"],["matSuffix","",4,"ngIf"],["name","sortOption",3,"formControl","selectionChange"],[3,"value",4,"ngFor","ngForOf"],["name","fontStyle",1,"sort-toggle","mr-2",3,"value","change"],[3,"value"],[3,"color"],["mat-icon-button","",3,"color","click"],[1,"view-toggle"],["fxLayout","row wrap","fxLayoutAlign","start center"],["mat-button","",3,"matMenuTriggerFor",4,"ngIf","ngIfElse"],["mat-button","",3,"click"],["svgIcon","database",1,"mr-3"],["svgIcon","bulb",1,"mr-3"],["fxLayout","row","fxLayoutAlign","center center","class","p-10",4,"ngIf"],["fxLayout","column",1,"w-full"],["fxLayout","row","class","w-full",4,"ngIf"],[4,"ngIf"],["fxLayoutAlign","end center",1,"w-full","pt-5"],["previousLabel","","directionLinks","true","nextLabel","",3,"pageChange"],["showConfigDialog",""],["showErrorDialog",""],["startExperimentDialog",""],["xPosition","before"],["resourcesMenu","matMenu"],[1,"resources-menu","p-3","bg-primary","text-white"],["fxLayout","row","fxLayoutAlign","end center",1,"w-full"],["mat-icon-button",""],["class","resources-menu-content",3,"click",4,"ngIf"],["noData",""],["matSuffix","",1,"cursor-pointer",3,"click"],["matSuffix",""],["mat-button","",3,"matMenuTriggerFor"],[1,"mr-3"],["fxLayout","row","fxLayoutAlign","center center",1,"p-10"],["color","accent",1,"mt-10",3,"diameter"],["fxLayout","row",1,"w-full"],["class","font-medium",4,"ngIf"],[1,"font-medium"],["fxLayout","row wrap","fxLayoutAlign","",4,"ngIf"],["fxLayout","column","fxLayoutAlign","start","class","w-full",4,"ngIf"],["fxLayout","row wrap","fxLayoutAlign",""],["class","py-3 px-4 experiment-card mt-3 mr-3","fxFlex","calc(25% - 20px)",4,"ngFor","ngForOf"],["fxFlex","calc(25% - 20px)",1,"py-3","px-4","experiment-card","mt-3","mr-3"],["fxLayoutAlign","start center",1,"w-full","experiment-card-header","mat-title","mb-2"],["fxLayout","row","fxLayoutAlign","start center","fxFlex","1 1 auto","fxLayoutGap","15px"],[1,"elipsis-card-title",3,"matTooltip"],["mat-icon-button","","matTooltip","Create experiment from copy",1,"actionable",3,"click"],["svgIcon","copy"],["mat-icon-button","","matTooltip","Delete experiment",1,"actionable-warn",3,"disabled","click"],["fxLayout","column","fxLayoutGap","15px",1,"mt-3"],[1,"w-130","color-gray"],[1,"color-text-primary","font-medium",3,"matTooltip"],["fxLayout","row","fxLayoutAlign","start center",1,"color-text-primary","font-medium"],["mat-icon-button","","color","warn","matTooltip","Click to show error details","class","error-icon",3,"click",4,"ngIf"],["class","color-text-primary font-medium",4,"ngIf"],[1,"color-text-primary","font-medium"],["fxLayoutAlign","space-between center",1,"w-full","p-0","m-0","mt-3"],["fxFlex","1 0 auto",3,"ngSwitch"],[4,"ngSwitchCase"],["class","m-0 ml-2","mat-raised-button","","color","primary",3,"click",4,"ngIf"],["mat-raised-button","","color","accent",1,"m-0","ml-2",3,"click"],["mat-icon-button","","color","warn","matTooltip","Click to show error details",1,"error-icon",3,"click"],["mat-button","",1,"mr-3",3,"click"],["mat-raised-button","","color","primary",1,"m-0","ml-2",3,"click"],["fxLayout","column","fxLayoutAlign","start",1,"w-full"],["class","py-3 px-4 experiment-list-card mt-3 w-full","fxLayout","row","fxLayout.md","column","fxLayout.lt-md","column","fxLayoutAlign","start","fxLayoutAlign.lt-md","start","fxLayoutAlign.md","start","fxLayoutGap.md","20px","fxLayoutGap.lt-md","20px",4,"ngFor","ngForOf"],["fxLayout","row","fxLayout.md","column","fxLayout.lt-md","column","fxLayoutAlign","start","fxLayoutAlign.lt-md","start","fxLayoutAlign.md","start","fxLayoutGap.md","20px","fxLayoutGap.lt-md","20px",1,"py-3","px-4","experiment-list-card","mt-3","w-full"],["fxFlex","25","fxFlex.lt-md","100","fxLayout","row","fxLayoutAlign","start center","fxFlex","1 1 auto","fxLayoutGap","25px"],["fxFlex.gt-md","1 1 auto","fxLayout","row","fxLayoutAlign","start start","fxLayoutGap","15px",1,"mat-title","mb-0"],[1,"elipsis-title",3,"matTooltip"],["fxLayout","row","fxLayoutAlign","end center"],["class","m-2","mat-raised-button","","color","primary",3,"click",4,"ngIf"],["mat-raised-button","","color","accent",1,"m-2",3,"click"],["fxFlex","55","fxFlex.md","100","fxFlex.lt-md","100","fxLayout","row","fxLayoutAlign","space-around center","fxLayoutAlign.md","start","fxLayoutAlign.lt-md","start","ngClass.md","w-full","ngClass.lt-md","w-full","fxLayoutGap","15px"],["fxFlex","12","fxFlex.md","20","fxFlex.lt-md","20","fxLayout","column","fxLayoutAlign","start start"],[1,"color-gray"],["fxFlex","15","fxFlex.md","20","fxFlex.lt-md","20","fxLayout","column","fxLayoutAlign","start start"],[1,"color-text-primary","font-medium","elipsis-title",3,"matTooltip"],["fxFlex","17","fxFlex.md","20","fxFlex.lt-md","20","fxLayout","column","fxLayoutAlign","start start"],["fxFlex","15","fxFlex.md","100","fxFlex.lt-md","100","fxLayout","row","fxLayoutAlign","end center","fxLayoutAlign.md","start center","fxLayoutAlign.lt-md","start center"],["fxFlex.gt-md","1 1 auto",3,"ngSwitch"],["mat-icon-button","","matTooltip","Delete experiment",3,"disabled","click"],["mat-raised-button","","color","primary",1,"m-2",3,"click"],["fxLayout","row","fxLayoutAlign","space-between center",1,"info-dialog-title","w-full","p-3","bg-primary","text-white"],[1,"w-full","exp-config-title"],["mat-icon-button","","matDialogClose",""],[1,"info-dialog-content","p-3"],[1,"info-dialog-content","m-3"],["fxLayout","column","fxLayoutGap","30px"],[1,"w-full",3,"formGroup","ngSubmit"],["f","ngForm","formDirective","ngForm"],["appearance","outline",1,"w-full","mb-4"],["type","text","matInput","","placeholder","CWD","formControlName","cwd"],["fxLayout","row","fxLayoutAlign","center start","fxLayoutGap","10px",1,"mb-3"],["matDialogClose","",1,"mat-raised-button",3,"click"],["type","submit","matDialogClose","","mat-raised-button","","color","accent",3,"disabled"],[1,"resources-menu-content",3,"click"],[1,"table-container","w-full"],["fxFill","","mat-table","","multiTemplateDataRows","","matSort","","matSortDisableClear","",1,"mat-elevation-z3","w-full",3,"dataSource","matSortChange"],[3,"matColumnDef"],["mat-header-cell","","mat-sort-header","",4,"matHeaderCellDef"],["mat-cell","",4,"matCellDef"],["mat-header-row","",4,"matHeaderRowDef","matHeaderRowDefSticky"],["mat-row","",4,"matRowDef","matRowDefColumns"],["showFirstLastButtons","",1,"mat-elevation-z3",3,"pageSize"],["paginatorResource",""],["mat-header-cell","","mat-sort-header",""],["mat-cell",""],["mat-header-row",""],["mat-row",""],[1,"mat-title","text-primary"]],template:function(e,t){if(1&e&&(K.dc(0,"div",0),K.dc(1,"div",1),K.dc(2,"div",2),K.dc(3,"h1",3),K.Oc(4,"Experiments"),K.cc(),K.dc(5,"mat-form-field",4),K.dc(6,"mat-label"),K.Oc(7,"Search"),K.cc(),K.Yb(8,"input",5),K.Mc(9,ft,2,0,"mat-icon",6),K.Mc(10,gt,2,0,"mat-icon",7),K.cc(),K.dc(11,"mat-form-field",4),K.dc(12,"mat-label"),K.Oc(13,"Sort by"),K.cc(),K.dc(14,"mat-select",8),K.lc("selectionChange",(function(e){return t.onSortOption(e)})),K.Mc(15,pt,3,4,"mat-option",9),K.cc(),K.cc(),K.dc(16,"mat-button-toggle-group",10),K.lc("change",(function(e){return t.onSortDirection(e)})),K.dc(17,"mat-button-toggle",11),K.dc(18,"mat-icon",12),K.Oc(19,"north"),K.cc(),K.cc(),K.dc(20,"mat-button-toggle",11),K.dc(21,"mat-icon",12),K.Oc(22,"south"),K.cc(),K.cc(),K.cc(),K.dc(23,"button",13),K.lc("click",(function(){return t.changeDisplayView(t.VIEW_TYPE.LIST)})),K.qc(24,"async"),K.dc(25,"mat-icon",14),K.Oc(26,"menu"),K.cc(),K.cc(),K.dc(27,"button",13),K.lc("click",(function(){return t.changeDisplayView(t.VIEW_TYPE.CARD)})),K.qc(28,"async"),K.dc(29,"mat-icon",14),K.Oc(30,"view_module"),K.cc(),K.cc(),K.cc(),K.dc(31,"div",15),K.Mc(32,mt,5,4,"button",16),K.qc(33,"async"),K.dc(34,"button",17),K.lc("click",(function(){return t.helperService.redirectTo("initialize")})),K.Yb(35,"mat-icon",18),K.Oc(36),K.qc(37,"uppercase"),K.cc(),K.dc(38,"button",17),K.lc("click",(function(){return t.helperService.redirectTo("create")})),K.Yb(39,"mat-icon",19),K.Oc(40),K.qc(41,"uppercase"),K.cc(),K.cc(),K.cc(),K.Mc(42,Ct,2,1,"div",20),K.qc(43,"async"),K.qc(44,"async"),K.dc(45,"div",21),K.Mc(46,vt,2,1,"div",22),K.qc(47,"async"),K.qc(48,"async"),K.Mc(49,nn,5,6,"ng-container",23),K.dc(50,"div",24),K.dc(51,"pagination-controls",25),K.lc("pageChange",(function(e){return t.page=e})),K.cc(),K.cc(),K.cc(),K.cc(),K.Mc(52,rn,10,4,"ng-template",null,26,K.Nc),K.Mc(54,sn,9,2,"ng-template",null,27,K.Nc),K.Mc(56,on,16,2,"ng-template",null,28,K.Nc),K.dc(58,"mat-menu",29,30),K.dc(60,"mat-toolbar",31),K.dc(61,"div",32),K.dc(62,"button",33),K.dc(63,"mat-icon"),K.Oc(64,"close"),K.cc(),K.cc(),K.cc(),K.cc(),K.Mc(65,An,20,13,"div",34),K.cc(),K.Mc(66,yn,2,0,"ng-template",null,35,K.Nc)),2&e){const e=K.Bc(67);let n=null,r=null;K.Lb(8),K.vc("formControl",t.searchStr),K.Lb(1),K.vc("ngIf",null!==t.searchStr.value&&""!==t.searchStr.value),K.Lb(1),K.vc("ngIf",null===t.searchStr.value||""===t.searchStr.value),K.Lb(4),K.vc("formControl",t.sortOption),K.Lb(1),K.vc("ngForOf",t.sortOptions),K.Lb(1),K.vc("value",t.sortDirectionType),K.Lb(1),K.vc("value",t.sortDirections[0]),K.Lb(1),K.vc("color",t.sortDirectionType===t.sortDirections[0]?"primary":""),K.Lb(2),K.vc("value",t.sortDirections[1]),K.Lb(1),K.vc("color",t.sortDirectionType===t.sortDirections[1]?"primary":""),K.Lb(2),K.vc("color",K.rc(24,20,t.experimentViewType$)===t.VIEW_TYPE.LIST?"primary":""),K.Lb(4),K.vc("color",K.rc(28,22,t.experimentViewType$)===t.VIEW_TYPE.CARD?"primary":""),K.Lb(5),K.vc("ngIf",t.dataSourceResource.data&&(null==t.dataSourceResource.data?null:t.dataSourceResource.data.length)>0&&K.rc(33,24,t.resources$))("ngIfElse",e),K.Lb(4),K.Qc(" ",K.rc(37,26,"Reset Auptimizer Environment")," "),K.Lb(4),K.Qc(" ",K.rc(41,28,"Create new experiment")," "),K.Lb(2),K.vc("ngIf",K.rc(43,30,t.loadingAllExperiments$)&&0===(null==(n=K.rc(44,32,t.experiments$))?null:n.length)),K.Lb(4),K.vc("ngIf",!1===K.rc(47,34,t.loadingAllExperiments$)&&0===(null==(r=K.rc(48,36,t.experiments$))?null:r.length)),K.Lb(3),K.vc("ngIf",t.experiments&&t.experiments.length),K.Lb(16),K.vc("ngIf",t.dataSourceResource.data&&(null==t.dataSourceResource.data?null:t.dataSourceResource.data.length)>0)}},directives:[oe.d,oe.c,fe.c,fe.g,rt.b,de.c,de.n,de.f,r.o,ge.a,r.n,pe.b,pe.a,ce.a,ae.b,it.c,le.a,se.a,fe.h,me.m,le.d,st.b,ot.a,oe.b,oe.e,Ce.a,ot.c,ot.b,r.q,r.r,xe.a,nt.d,de.u,de.o,de.i,nt.e,de.g,fe.b,nt.c,Je.j,oe.f,Ye.a,Je.c,Je.e,Je.b,Je.g,Je.i,at.a,Je.d,Ye.b,Je.a,Je.f,Je.h],pipes:[r.b,r.w,W.a,it.b,G.a,D.a,r.l,r.e,r.h],styles:[".experiment-card[_ngcontent-%COMP%]{min-width:430px!important;max-width:430px!important}.experiment-card[_ngcontent-%COMP%] mat-card-content[_ngcontent-%COMP%] > *[_ngcontent-%COMP%]{font-size:16px}.elipsis-title[_ngcontent-%COMP%]{width:170px!important}.elipsis-card-title[_ngcontent-%COMP%], .elipsis-title[_ngcontent-%COMP%]{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.elipsis-card-title[_ngcontent-%COMP%]{max-width:200px}.view-toggle[_ngcontent-%COMP%]{height:30px;width:30px;font-size:30px;line-height:30px}.experiment-list-card[_ngcontent-%COMP%] mat-card-content[_ngcontent-%COMP%] > *[_ngcontent-%COMP%]{font-size:16px}.error-icon[_ngcontent-%COMP%]{font-size:18px;width:18px;height:18px;line-height:18px;margin-left:10px;margin-top:-7px}.mat-header-cell[_ngcontent-%COMP%]{text-transform:uppercase} .mat-dialog-container{padding:0!important;overflow:unset!important}.minw-200[_ngcontent-%COMP%]{min-width:200px}.minw-300[_ngcontent-%COMP%]{min-width:300px}.w-130[_ngcontent-%COMP%]{width:130px;max-width:130px}.resources-menu[_ngcontent-%COMP%]{height:50px}.resources-menu-content[_ngcontent-%COMP%]{width:600px} .mat-tooltip{word-break:break-all!important;white-space:normal!important}.exp-config-title[_ngcontent-%COMP%]{white-space:normal;word-break:break-all}"],changeDetection:0}),Object(s.b)([Object(o.e)(q.experiments)],e.prototype,"experiments$",void 0),Object(s.b)([Object(o.e)(q.resources)],e.prototype,"resources$",void 0),Object(s.b)([Object(o.e)(q.loadingAllExperiments)],e.prototype,"loadingAllExperiments$",void 0),Object(s.b)([Object(o.e)(q.experimentViewType)],e.prototype,"experimentViewType$",void 0),e})();function Sn(e,t){if(1&e&&(K.dc(0,"mat-option",6),K.Oc(1),K.cc()),2&e){const e=t.$implicit;K.vc("value",e.eid),K.Lb(1),K.Qc(" ",e.experimentName||e.scriptName," ")}}function xn(e,t){if(1&e){const e=K.ec();K.dc(0,"mat-form-field",3),K.dc(1,"mat-label"),K.Oc(2," Select experiment "),K.cc(),K.dc(3,"mat-select",4),K.lc("selectionChange",(function(t){return K.Ec(e),K.pc(2).selectedNewExperiment(t)})),K.Mc(4,Sn,2,2,"mat-option",5),K.cc(),K.cc()}if(2&e){const e=K.pc().ngIf,t=K.pc();K.Lb(3),K.vc("value",e[t.experimentIndex].eid),K.Lb(1),K.vc("ngForOf",e)}}function En(e,t){if(1&e&&(K.dc(0,"div",1),K.Mc(1,xn,5,2,"mat-form-field",2),K.cc()),2&e){const e=t.ngIf,n=K.pc();K.Lb(1),K.vc("ngIf",e&&e.length>0&&null!==n.experimentIndex)}}let kn=(()=>{class e{constructor(e){this.store=e,this.toggleExperiment=new K.s}ngOnInit(){this.subscription=new a.a,this.subscription.add(this.experiments$.subscribe(e=>{if(e&&e.length>0&&this.experimentId){const t=e.findIndex(e=>+e.eid==+this.experimentId);-1!==t&&(this.experimentIndex=t)}}))}ngOnDestroy(){this.subscription.unsubscribe()}selectedNewExperiment(e){e.value&&this.toggleExperiment.emit(e.value)}}return e.\u0275fac=function(t){return new(t||e)(K.Xb(o.i))},e.\u0275cmp=K.Rb({type:e,selectors:[["app-experiment-dropdown"]],inputs:{experimentId:"experimentId"},outputs:{toggleExperiment:"toggleExperiment"},decls:2,vars:3,consts:[["class","w-full",4,"ngIf"],[1,"w-full"],["appearance","outline","class","w-full",4,"ngIf"],["appearance","outline",1,"w-full"],[3,"value","selectionChange"],[3,"value",4,"ngFor","ngForOf"],[3,"value"]],template:function(e,t){1&e&&(K.Mc(0,En,2,1,"div",0),K.qc(1,"async")),2&e&&K.vc("ngIf",K.rc(1,1,t.experiments$))},directives:[r.o,fe.c,fe.g,ge.a,r.n,me.m],pipes:[r.b],styles:["mat-select[_ngcontent-%COMP%]{font-size:18px;padding-top:10px}"],changeDetection:0}),Object(s.b)([Object(o.e)(q.experiments)],e.prototype,"experiments$",void 0),e})();const _n=["paginatorParams"],Tn=["showDetailsDialog"],Rn=function(e){return{width:e}};function Bn(e,t){if(1&e&&(K.dc(0,"div",6),K.dc(1,"h2",15),K.Oc(2),K.qc(3,"roundNumber"),K.cc(),K.dc(4,"div",16),K.dc(5,"div",17),K.Yb(6,"span",18),K.cc(),K.dc(7,"div",19),K.dc(8,"h2",20),K.Oc(9),K.cc(),K.cc(),K.cc(),K.cc()),2&e){const e=K.pc();K.Lb(2),K.Qc(" Best Result: ",K.rc(3,4,e.experimentData.bestScore.score)," "),K.Lb(4),K.vc("ngStyle",K.yc(6,Rn,e.progressBarWidth+"%")),K.Lb(3),K.Rc("",e.experimentData.jobStats.finished," / ",e.experimentData.jobStats.total," jobs")}}function On(e,t){1&e&&(K.dc(0,"h2",15),K.Oc(1,"Best Hyperparameter Combination"),K.cc())}function Ln(e,t){1&e&&(K.dc(0,"h2",21),K.Oc(1," Compression config_list "),K.cc())}function Pn(e,t){if(1&e&&(K.dc(0,"th",29),K.Oc(1),K.cc()),2&e){const e=K.pc(2);K.Lb(1),K.Qc(" ",e.PARAM_COLUMNS.NAME," ")}}function Fn(e,t){if(1&e&&(K.dc(0,"div",32),K.Oc(1),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Pc(e.name)}}function Nn(e,t){if(1&e&&(K.dc(0,"td",30),K.Mc(1,Fn,2,1,"div",31),K.cc()),2&e){const e=t.$implicit;K.Lb(1),K.vc("ngIf",e.name)}}function Mn(e,t){if(1&e&&(K.dc(0,"th",29),K.dc(1,"strong"),K.Oc(2),K.cc(),K.cc()),2&e){const e=K.pc(2);K.Lb(2),K.Pc(e.PARAM_COLUMNS.VALUE)}}function Dn(e,t){if(1&e&&(K.dc(0,"td",30),K.dc(1,"div",32),K.dc(2,"strong"),K.dc(3,"span"),K.Oc(4),K.qc(5,"roundNumber"),K.cc(),K.cc(),K.cc(),K.cc()),2&e){const e=t.$implicit;K.Lb(4),K.Pc(K.rc(5,1,e.value))}}function $n(e,t){if(1&e&&(K.dc(0,"th",29),K.Oc(1),K.cc()),2&e){const e=K.pc(2);K.Lb(1),K.Qc(" ",e.PARAM_COLUMNS.RANGE," ")}}function Wn(e,t){if(1&e&&(K.dc(0,"div"),K.Oc(1),K.cc()),2&e){const e=K.pc(2).$implicit;K.Lb(1),K.Qc(" \xa0 by ",e.interval," ")}}function Hn(e,t){if(1&e&&(K.dc(0,"div"),K.Oc(1),K.qc(2,"roundNumber"),K.cc()),2&e){const e=K.pc(2).$implicit;K.Lb(1),K.Qc(" \xa0 by ",K.rc(2,1,(e.range[1]-e.range[0])/(e.n-1))," ")}}function Gn(e,t){1&e&&(K.dc(0,"div"),K.Oc(1," \xa0 by 1 "),K.cc())}function Vn(e,t){if(1&e&&(K.dc(0,"div"),K.Mc(1,Wn,2,1,"div",34),K.Mc(2,Hn,3,3,"div",34),K.Mc(3,Gn,2,0,"div",34),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.vc("ngIf",e.interval),K.Lb(1),K.vc("ngIf",e.n),K.Lb(1),K.vc("ngIf",!e.n&&!e.interval)}}function jn(e,t){if(1&e&&(K.dc(0,"td",30),K.dc(1,"div",33),K.dc(2,"div"),K.Oc(3),K.cc(),K.Mc(4,Vn,4,3,"div",34),K.cc(),K.cc()),2&e){const e=t.$implicit,n=K.pc(2);K.Lb(3),K.Rc("",e.range[0]," to ",e.range[1],""),K.Lb(1),K.vc("ngIf","sequence"===n.experimentData.bestScore.proposer)}}function Zn(e,t){if(1&e&&(K.dc(0,"th",29),K.Oc(1),K.cc()),2&e){const e=K.pc(2);K.Lb(1),K.Qc(" ",e.PARAM_COLUMNS.TYPE," ")}}function zn(e,t){if(1&e&&(K.dc(0,"div",32),K.Oc(1),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Pc(e.type)}}function Kn(e,t){if(1&e&&(K.dc(0,"td",30),K.Mc(1,zn,2,1,"div",31),K.cc()),2&e){const e=t.$implicit;K.Lb(1),K.vc("ngIf",e.type)}}function Xn(e,t){1&e&&K.Yb(0,"tr",35)}function Un(e,t){1&e&&K.Yb(0,"tr",36)}function Yn(e,t){if(1&e){const e=K.ec();K.dc(0,"div",22),K.dc(1,"table",23),K.lc("matSortChange",(function(t){return K.Ec(e),K.pc().sortParamData(t)})),K.bc(2,24),K.Mc(3,Pn,2,1,"th",25),K.Mc(4,Nn,2,1,"td",26),K.ac(),K.bc(5,24),K.Mc(6,Mn,3,1,"th",25),K.Mc(7,Dn,6,3,"td",26),K.ac(),K.bc(8,24),K.Mc(9,$n,2,1,"th",25),K.Mc(10,jn,5,3,"td",26),K.ac(),K.bc(11,24),K.Mc(12,Zn,2,1,"th",25),K.Mc(13,Kn,2,1,"td",26),K.ac(),K.Mc(14,Xn,1,0,"tr",27),K.Mc(15,Un,1,0,"tr",28),K.cc(),K.cc()}if(2&e){const e=K.pc();K.Lb(1),K.vc("dataSource",e.dataSourceParams),K.Lb(1),K.wc("matColumnDef",e.PARAM_COLUMNS.NAME),K.Lb(3),K.wc("matColumnDef",e.PARAM_COLUMNS.VALUE),K.Lb(3),K.wc("matColumnDef",e.PARAM_COLUMNS.RANGE),K.Lb(3),K.wc("matColumnDef",e.PARAM_COLUMNS.TYPE),K.Lb(3),K.vc("matHeaderRowDef",e.displayedResourceColumns)("matHeaderRowDefSticky",!0),K.Lb(1),K.vc("matRowDefColumns",e.displayedResourceColumns)}}function Jn(e,t){if(1&e&&(K.dc(0,"div",37),K.dc(1,"pre"),K.Oc(2),K.qc(3,"json"),K.cc(),K.cc()),2&e){const e=K.pc();K.Lb(2),K.Pc(K.rc(3,1,null==e.experimentData||null==e.experimentData.bestScore?null:e.experimentData.bestScore.configList))}}function Qn(e,t){if(1&e&&(K.dc(0,"div",42),K.dc(1,"div",46),K.Oc(2,"Name"),K.cc(),K.dc(3,"div",47),K.Oc(4),K.cc(),K.cc()),2&e){const e=K.pc(2);K.Lb(4),K.Pc(null==e.experimentData.experiment?null:e.experimentData.experiment.experimentName)}}function qn(e,t){if(1&e&&(K.dc(0,"div",42),K.dc(1,"div",46),K.Oc(2,"Script"),K.cc(),K.dc(3,"div",47),K.Oc(4),K.cc(),K.cc()),2&e){const e=K.pc(2);K.Lb(4),K.Pc(null==e.experimentData.experiment?null:e.experimentData.experiment.scriptName)}}function er(e,t){if(1&e&&(K.dc(0,"div",42),K.dc(1,"div",46),K.Oc(2,"Experiment ID:"),K.cc(),K.dc(3,"div",47),K.Oc(4),K.cc(),K.cc()),2&e){const e=K.pc(2);K.Lb(4),K.Pc(null==e.experimentData.experiment?null:e.experimentData.experiment.eid)}}function tr(e,t){if(1&e&&(K.dc(0,"div",42),K.dc(1,"div",46),K.Oc(2,"Started at:"),K.cc(),K.dc(3,"div",47),K.Oc(4),K.qc(5,"date"),K.cc(),K.cc()),2&e){const e=K.pc(2);K.Lb(4),K.Qc("",K.sc(5,1,1e3*(null==e.experimentData.experiment?null:e.experimentData.experiment.startTime),"medium")," ")}}function nr(e,t){if(1&e&&(K.dc(0,"div",42),K.dc(1,"div",46),K.Oc(2,"Ended at:"),K.cc(),K.dc(3,"div",47),K.Oc(4),K.qc(5,"date"),K.cc(),K.cc()),2&e){const e=K.pc(2);K.Lb(4),K.Pc(K.sc(5,1,1e3*(null==e.experimentData.experiment?null:e.experimentData.experiment.endTime),"medium"))}}function rr(e,t){if(1&e&&(K.dc(0,"div",42),K.dc(1,"div",46),K.Oc(2,"Technique:"),K.cc(),K.dc(3,"div",47),K.Oc(4),K.cc(),K.cc()),2&e){const e=K.pc(2);K.Lb(4),K.Pc(null==e.experimentData.experiment||null==e.experimentData.experiment.expConfig?null:e.experimentData.experiment.expConfig.proposer)}}function ir(e,t){if(1&e&&(K.dc(0,"div",42),K.dc(1,"div",46),K.Oc(2,"Working Directory:"),K.cc(),K.dc(3,"div",47),K.Oc(4),K.cc(),K.cc()),2&e){const e=K.pc(2);K.Lb(4),K.Pc(null==e.experimentData.experiment||null==e.experimentData.experiment.expConfig?null:e.experimentData.experiment.expConfig.workingdir)}}function sr(e,t){if(1&e&&(K.dc(0,"div",38),K.dc(1,"div",39),K.Oc(2,"Details"),K.cc(),K.dc(3,"button",40),K.dc(4,"mat-icon"),K.Oc(5,"close"),K.cc(),K.cc(),K.cc(),K.dc(6,"div",41),K.dc(7,"div",42),K.dc(8,"div",43),K.Oc(9,"SETTING"),K.cc(),K.dc(10,"div",44),K.Oc(11,"VALUE"),K.cc(),K.cc(),K.Yb(12,"hr"),K.Mc(13,Qn,5,1,"div",45),K.Mc(14,qn,5,1,"div",45),K.Mc(15,er,5,1,"div",45),K.Mc(16,tr,6,4,"div",45),K.Mc(17,nr,6,4,"div",45),K.Mc(18,rr,5,1,"div",45),K.Mc(19,ir,5,1,"div",45),K.cc()),2&e){const e=K.pc();K.Lb(13),K.vc("ngIf",null==e.experimentData.experiment?null:e.experimentData.experiment.experimentName),K.Lb(1),K.vc("ngIf",null==e.experimentData.experiment?null:e.experimentData.experiment.scriptName),K.Lb(1),K.vc("ngIf",null==e.experimentData.experiment?null:e.experimentData.experiment.eid),K.Lb(1),K.vc("ngIf",null==e.experimentData.experiment?null:e.experimentData.experiment.startTime),K.Lb(1),K.vc("ngIf",null==e.experimentData.experiment?null:e.experimentData.experiment.endTime),K.Lb(1),K.vc("ngIf",null==e.experimentData.experiment||null==e.experimentData.experiment.expConfig?null:e.experimentData.experiment.expConfig.proposer),K.Lb(1),K.vc("ngIf",null==e.experimentData.experiment||null==e.experimentData.experiment.expConfig?null:e.experimentData.experiment.expConfig.workingdir)}}let or=(()=>{class e{constructor(e,t,n,r,i,s,o){this.dialog=e,this.store=t,this.router=n,this.route=r,this.cdRef=i,this.snackbarService=s,this.utilsService=o,this.pageSize=8,this.showDetails=!1,this.PARAM_COLUMNS={NAME:"NAME",VALUE:"BEST VALUE",RANGE:"RANGE",TYPE:"TYPE"},this.displayedResourceColumns=[this.PARAM_COLUMNS.NAME,this.PARAM_COLUMNS.VALUE,this.PARAM_COLUMNS.RANGE,this.PARAM_COLUMNS.TYPE],this.dataSourceParams=new Je.k}ngOnInit(){var e,t;this.subscriptions=new a.a,this.subscriptions.add(null===(t=null===(e=this.route)||void 0===e?void 0:e.parent)||void 0===t?void 0:t.params.subscribe(e=>{this.experimentId=e.id})),this.subscriptions.add(this.experiment$.subscribe(e=>{if(this.dataSourceParams.data=[],this.dataSourceParams.paginator=null,this.dataSourceParams.sort=null,e){this.experimentData=e,e&&e.bestScore&&e.bestScore.params&&e.bestScore.params.length&&(this.dataSourceParams.data=e.bestScore.params.slice(),this.dataSourceParams.paginator=this.paginatorParams,this.dataSourceParams.sort=this.sortParams,this.cdRef.markForCheck());const t=new $.a;this.progressBarWidth=t.transform({value:this.experimentData.jobStats.finished,maxValue:this.experimentData.jobStats.total}),this.cdRef.markForCheck()}})),this.subscriptions.add(this.refreshInterval$.subscribe(e=>{e&&(this.refreshIntervalSubscription&&e!==this.refreshInterval&&this.refreshIntervalSubscription.unsubscribe(),this.refreshInterval=e,this.refreshIntervalSubscription=this.refreshIntervalData().subscribe())})),this.subscriptions.add(this.refreshingInterval$.subscribe(e=>{e&&(this.refreshData(),this.snackbarService.success("Data refreshed!"),this.store.dispatch(new w(!1)))}))}ngOnDestroy(){this.subscriptions.unsubscribe(),this.refreshIntervalSubscription.unsubscribe()}openDetails(){this.showDetailsDialogRef=this.dialog.open(this.showDetailsDialog,{width:"650px",data:null,panelClass:"info-modal"}),this.subscriptions.add(this.showDetailsDialogRef.afterClosed().subscribe())}toggleExperiment(e){return Object(s.a)(this,void 0,void 0,(function*(){e&&(this.experimentId=e,this.store.dispatch(new g(this.experimentId)),this.store.dispatch(new p(this.experimentId)),yield this.router.navigate([`experiment/${e}/overview`]))}))}refreshIntervalData(){if(this.refreshInterval)return d(1e3*this.refreshInterval).pipe(Object(ee.a)(0),Object(F.a)(()=>{console.log("Fetching overview data..."),this.refreshData()}))}refreshData(){this.experimentId&&(this.store.dispatch(new g(this.experimentId)),this.store.dispatch(new p(this.experimentId)))}sortParamData(e){var t,n;const r=null===(n=null===(t=this.experimentData)||void 0===t?void 0:t.bestScore)||void 0===n?void 0:n.params.slice();this.dataSourceParams.data=e.active&&""!==e.direction?r.sort((t,n)=>{const r="asc"===e.direction;switch(e.active){case this.PARAM_COLUMNS.NAME:return this.utilsService.compare(t.name,n.name,r);case this.PARAM_COLUMNS.RANGE:return this.utilsService.compare(t.range,n.range,r);case this.PARAM_COLUMNS.VALUE:return this.utilsService.compare(t.value,n.value,r);case this.PARAM_COLUMNS.TYPE:return this.utilsService.compare(t.type,n.type,r);default:return 0}}):r}}return e.\u0275fac=function(t){return new(t||e)(K.Xb(nt.b),K.Xb(o.i),K.Xb(i.g),K.Xb(i.a),K.Xb(K.j),K.Xb(Q.b),K.Xb(Q.c))},e.\u0275cmp=K.Rb({type:e,selectors:[["app-overview"]],viewQuery:function(e,t){if(1&e&&(K.Ic(_n,!0),K.Ic(Ye.a,!0),K.Ic(Tn,!0)),2&e){let e;K.Ac(e=K.mc())&&(t.paginatorParams=e.first),K.Ac(e=K.mc())&&(t.sortParams=e.first),K.Ac(e=K.mc())&&(t.showDetailsDialog=e.first)}},decls:20,vars:12,consts:[["fxLayout","column"],["fxLayoutAlign","space-between center"],[1,"mat-headline"],["mat-button","",3,"click"],["svgIcon","information",1,"mr-3"],["fxFlex.gt-md","50","fxFlex","100",3,"experimentId","toggleExperiment"],["fxLayout","column","fxLayoutGap","15px",1,"w-full"],["fxLayout","column","class","w-full","fxLayoutGap","15px",4,"ngIf"],["class","mat-title",4,"ngIf"],["class","mat-title mb-0",4,"ngIf"],["class","table-container w-full",4,"ngIf"],["showFirstLastButtons","",1,"mat-elevation-z3",3,"pageSize"],["paginatorParams",""],["class","config-list p-3",4,"ngIf"],["showDetailsDialog",""],[1,"mat-title"],["fxLayout","row","fxLayoutAlign","center center",1,"w-full"],[1,"progress-bar"],[1,"progress-bar-fill",3,"ngStyle"],["fxLayout","row","fxLayoutAlign","start",1,"w-full","progress-legend"],[1,"mb-0","mat-title"],[1,"mat-title","mb-0"],[1,"table-container","w-full"],["fxFill","","mat-table","","multiTemplateDataRows","","matSort","","matSortDisableClear","",1,"mat-elevation-z3","w-full",3,"dataSource","matSortChange"],[3,"matColumnDef"],["mat-header-cell","","mat-sort-header","",4,"matHeaderCellDef"],["mat-cell","",4,"matCellDef"],["mat-header-row","",4,"matHeaderRowDef","matHeaderRowDefSticky"],["mat-row","",4,"matRowDef","matRowDefColumns"],["mat-header-cell","","mat-sort-header",""],["mat-cell",""],["class","maxw-500",4,"ngIf"],[1,"maxw-500"],["fxLayout","row",1,"maxw-500","minw-200"],[4,"ngIf"],["mat-header-row",""],["mat-row",""],[1,"config-list","p-3"],["fxLayout","row","fxLayoutAlign","space-between center",1,"info-dialog-title","w-full","p-3","bg-primary","text-white"],[1,"w-full"],["mat-icon-button","","matDialogClose",""],[1,"info-dialog-content","m-3"],["fxLayout","row"],[1,"p-2","minw-200","details-header"],[1,"p-2","details-header"],["fxLayout","row",4,"ngIf"],[1,"p-2","minw-200"],[1,"p-2"]],template:function(e,t){1&e&&(K.dc(0,"div",0),K.dc(1,"div",1),K.dc(2,"h1",2),K.Oc(3,"Experiment Overview"),K.cc(),K.dc(4,"button",3),K.lc("click",(function(){return t.openDetails()})),K.Yb(5,"mat-icon",4),K.Oc(6),K.qc(7,"uppercase"),K.cc(),K.cc(),K.dc(8,"app-experiment-dropdown",5),K.lc("toggleExperiment",(function(e){return t.toggleExperiment(e)})),K.cc(),K.dc(9,"div",6),K.Mc(10,Bn,10,8,"div",7),K.Mc(11,On,2,0,"h2",8),K.Mc(12,Ln,2,0,"h2",9),K.dc(13,"div"),K.Mc(14,Yn,16,8,"div",10),K.Yb(15,"mat-paginator",11,12),K.cc(),K.Mc(17,Jn,4,3,"div",13),K.cc(),K.cc(),K.Mc(18,sr,20,7,"ng-template",null,14,K.Nc)),2&e&&(K.Lb(6),K.Qc(" ",K.rc(7,10,"Experiment details")," "),K.Lb(2),K.vc("experimentId",t.experimentId),K.Lb(2),K.vc("ngIf",t.experimentData),K.Lb(1),K.vc("ngIf",t.dataSourceParams.data.length>0),K.Lb(1),K.vc("ngIf",!(t.dataSourceParams.data&&t.dataSourceParams.data.length||null===(null==t.experimentData||null==t.experimentData.bestScore?null:t.experimentData.bestScore.configList))),K.Lb(2),K.vc("ngIf",t.dataSourceParams.data.length>0),K.Lb(1),K.Jc("display",t.dataSourceParams.data.length>0?"block":"none"),K.vc("pageSize",t.pageSize),K.Lb(2),K.vc("ngIf",0===t.dataSourceParams.data.length&&null!==(null==t.experimentData||null==t.experimentData.bestScore?null:t.experimentData.bestScore.configList)))},directives:[oe.d,oe.c,ae.b,ce.a,kn,oe.b,oe.e,r.o,at.a,r.p,xe.c,Je.j,oe.f,Ye.a,Je.c,Je.e,Je.b,Je.g,Je.i,Je.d,Ye.b,Je.a,Je.f,Je.h,nt.d],pipes:[r.w,D.a,r.h,r.e],styles:[".table-col[_ngcontent-%COMP%]{text-align:center}.progress-legend[_ngcontent-%COMP%]{position:absolute;width:90%!important;color:#000!important}.hyper-table[_ngcontent-%COMP%]{overflow-y:auto;max-height:300px}.details-header[_ngcontent-%COMP%]{font-size:18px;font-stretch:normal;font-style:normal;line-height:1.22;letter-spacing:normal;text-align:left}.minw-200[_ngcontent-%COMP%]{min-width:200px}.maxw-500[_ngcontent-%COMP%]{max-width:500px}.config-list[_ngcontent-%COMP%]{overflow:auto!important;max-height:500px!important}"],changeDetection:0}),Object(s.b)([Object(o.e)(q.selectedExperiment)],e.prototype,"experiment$",void 0),Object(s.b)([Object(o.e)(q.experiments)],e.prototype,"experiments$",void 0),Object(s.b)([Object(o.e)(q.hyperparameters)],e.prototype,"hyperparameters$",void 0),Object(s.b)([Object(o.e)(q.refreshInterval)],e.prototype,"refreshInterval$",void 0),Object(s.b)([Object(o.e)(q.refreshingInterval)],e.prototype,"refreshingInterval$",void 0),Object(s.b)([Object(o.e)(q.loadingExperiment)],e.prototype,"loadingExperiment$",void 0),e})();const ar=["#3DDF7E","#4B6FFF","#ff9800","#4caf50","#00bcd4","#673ab7","#ff5722","#607d8b","#ffeb3b","#ba68c8","#7cb342","#e64a19","#03a9f4","#3f51b5","#cddc39","#ad1457","#2e7d32","#1e88e5","#e53935","#9c27b0","#009688","#795548","#f57c00","#311b92","#ec407a","#aa00ff","#00695c","#304ffe","#f44336"];var cr=n("u3+B"),lr=n("1jcm"),hr=n("f6nW"),dr=n("EUZL"),ur=n("Iab2");let fr=(()=>{let e=class{};return e.\u0275mod=K.Vb({type:e}),e.\u0275inj=K.Ub({factory:function(t){return new(t||e)},imports:[[hr.r]]}),e})();var gr=function(e){return e.XLS="xls",e.XLSX="xlsx",e.CSV="csv",e.TXT="txt",e.JSON="json",e.OTHER="other",e}({});let pr=(()=>{let e=class{constructor(){}extractRows(e,t,n){return this.getRowsAsJsonArray(e,t,null!=n?n:e._rowOutlet)}getRowsAsJsonArray(e,t,n){const r=this.getRenderedRows(e,n);return this.convertToJsonArray(t,r)}getRenderedRows(e,t){return e._getRenderedRows(t)}convertToJsonArray(e,t){const n=new Array;for(let r=0;r{let e=class extends Lr{constructor(){super()}workSheetToContent(e,t){var n,r;return dr.utils.sheet_to_csv(e,{FS:(r=null===(n=t)||void 0===n?void 0:n.delimiter,null!=r?r:",")})}getMimeType(){return Tr}};return e.\u0275fac=function(t){return new(t||e)},e.\u0275prov=Object(K.Tb)({factory:function(){return new e},token:e,providedIn:"root"}),e})(),Fr=(()=>{let e=class extends Or{constructor(){super()}createContent(e,t){let n="";return e.forEach(e=>{n+=Object.values(e).join(this.getDelimiter(t))+"\n"}),n}getMimeType(){return _r}getDelimiter(e){return e&&e.delimiter?e.delimiter:"\t"}};return e.\u0275fac=function(t){return new(t||e)},e.\u0275prov=Object(K.Tb)({factory:function(){return new e},token:e,providedIn:"root"}),e})(),Nr=(()=>{let e=class extends Lr{constructor(){super()}workSheetToContent(e,t={}){const n=dr.utils.book_new();return t.columnWidths&&(e["!cols"]=this.convertToWch(t.columnWidths)),this.correctTypes(t),dr.utils.book_append_sheet(n,e,t.sheet),Object(dr.write)(n,t)}getMimeType(){return xr}correctTypes(e){e.type||(e.type="array"),e.bookType=this.getMimeType().extension.replace(".","")}convertToWch(e){return e.map(e=>({wch:e}))}};return e.\u0275fac=function(t){return new(t||e)},e.\u0275prov=Object(K.Tb)({factory:function(){return new e},token:e,providedIn:"root"}),e})(),Mr=(()=>{let e=class extends Or{constructor(){super()}createContent(e,t){return JSON.stringify(e)}getMimeType(){return kr}};return e.\u0275fac=function(t){return new(t||e)},e.\u0275prov=Object(K.Tb)({factory:function(){return new e},token:e,providedIn:"root"}),e})(),Dr=(()=>{let e=class extends Nr{constructor(){super()}getMimeType(){return Er}};return e.\u0275fac=function(t){return new(t||e)},e.\u0275prov=Object(K.Tb)({factory:function(){return new e},token:e,providedIn:"root"}),e})(),$r=(()=>{let e=class{constructor(e){this.injector=e}getService(e){switch(e){case gr.XLS.valueOf():return this.injector.get(Nr);case gr.XLSX.valueOf():return this.injector.get(Dr);case gr.JSON.valueOf():return this.injector.get(Mr);case gr.TXT.valueOf():return this.injector.get(Fr);case gr.CSV.valueOf():return this.injector.get(Pr);case gr.OTHER.valueOf():return null;default:return this.injector.get(Dr)}}};return e.\u0275fac=function(t){return new(t||e)(K.hc(K.x))},e.\u0275prov=Object(K.Tb)({factory:function(){return new e(Object(K.hc)(K.t))},token:e,providedIn:"root"}),e})(),Wr=(()=>{let e=class{constructor(e,t,n,r){this.renderer=e,this.serviceLocator=t,this.dataExtractor=n,this._cdkTable=r,this.exportCompleted=new K.s,this.exportStarted=new K.s}exportTable(e,t){this.loadExporter(e),this._options=t,this.exportStarted.emit(),this._isIterating=!0,this._isExporting=!0,this._data=new Array,this.extractTableHeader();try{this.exportWithPagination()}catch(n){this.exportSinglePage()}}toggleRow(e){const t=this.getPaginatedRowIndex(e);this.isToggleOn(t)?this.toggleOff(t):this.toggleOn(t)}resetToggleRows(){this._selectedRows=[]}toggleOn(e){this._selectedRows=[...this._selectedRows||[],e]}toggleOff(e){this._selectedRows=this._selectedRows.filter(t=>t!==e)}isToggleOn(e){var t;return null===(t=this._selectedRows)||void 0===t?void 0:t.includes(e)}loadExporter(e){this._exporterService=e===gr.OTHER.valueOf()?this.exporter:this.serviceLocator.getService(e)}exportWithPagination(){this._initialPageIndex=this.getCurrentPageIndex(),this.initPageHandler(),this.goToPage(0)}exportSinglePage(){this.extractDataOnCurrentPage(),this.extractTableFooter(),this.exportExtractedData()}extractDataOnCurrentPage(){const e=this.dataExtractor.extractRows(this._cdkTable,this.hiddenColumns);this._data=this._data.concat(this.getSelectedRows(e))}getSelectedRows(e){return this.isSelectiveExport()?e.filter((e,t)=>this._selectedRows.includes(this.getPaginatedRowIndex(t))):e}isSelectiveExport(){return this._selectedRows&&!this.isMasterToggleOff()&&!this.isMasterToggleOn()}isMasterToggleOn(){return this.compareSelectedRowCount(this.getTotalItemsCount())}isMasterToggleOff(){return this.compareSelectedRowCount(0)}compareSelectedRowCount(e){var t;return!((null===(t=this._selectedRows)||void 0===t?void 0:t.length)!==e)}initPageHandler(){this._subscription||(this._subscription=this.getPageChangeObservable().subscribe(()=>{setTimeout(()=>{this._isIterating?(this.extractDataOnCurrentPage(),this.hasNextPage()?this.nextPage():(this._isIterating=!1,this.goToPage(this._initialPageIndex))):this._isExporting&&(this._isExporting=!1,this.extractTableFooter(),this.exportExtractedData())})}))}exportExtractedData(){this._exporterService.export(this._data,this._options),this._data=new Array,this.exportCompleted.emit()}extractSpecialRows(e){this._data.push(...this.dataExtractor.extractRows(this._cdkTable,this.hiddenColumns,e))}extractTableHeader(){this.extractSpecialRows(this._cdkTable._headerRowOutlet)}extractTableFooter(){this.extractSpecialRows(this._cdkTable._footerRowOutlet)}hasNextPage(){return this.getCurrentPageIndex(){let e=class extends Wr{constructor(e,t,n,r){super(e,t,n,r)}ngAfterViewInit(){this.exportStarted.subscribe(e=>{this.enablePaginator(!1)}),this.exportCompleted.subscribe(e=>{this.enablePaginator(!0)})}getPageCount(){return this.getPaginator().getNumberOfPages()}getPageSize(){var e,t;return null!=(t=null===(e=this.getPaginator())||void 0===e?void 0:e.pageSize)?t:0}getCurrentPageIndex(){var e,t;return null!=(t=null===(e=this.getPaginator())||void 0===e?void 0:e.pageIndex)?t:0}getTotalItemsCount(){var e,t,n,r,i;return null!=(i=null!=(t=null===(e=this.getPaginator())||void 0===e?void 0:e.length)?t:null===(r=null===(n=this.getDataSource())||void 0===n?void 0:n.data)||void 0===r?void 0:r.length)?i:0}goToPage(e){this.getPaginator().pageIndex=e,this.getPaginator()._changePageSize(this.getPaginator().pageSize)}getPageChangeObservable(){return this.getPaginator().page}getDataSource(){return this._cdkTable.dataSource}getPaginator(){return this.getDataSource().paginator}enablePaginator(e){this.getPaginator()&&(this.getPaginator().disabled=!e,this.getPaginator()._changePageSize(this.getPaginator().pageSize))}};return e.\u0275fac=function(t){return new(t||e)(K.Xb(K.O),K.Xb($r),K.Xb(pr),K.Xb(Je.j,11))},e.\u0275dir=K.Sb({type:e,selectors:[["","matTableExporter",""]],exportAs:["matTableExporter"],features:[K.Ib]}),e})(),Gr=(()=>{let e=class{};return e.\u0275mod=K.Vb({type:e}),e.\u0275inj=K.Ub({factory:function(t){return new(t||e)},imports:[[Je.l,fr]]}),e})();function Vr(e,t){if(1&e&&(K.dc(0,"mat-option",35),K.Oc(1),K.cc()),2&e){const e=t.$implicit;K.vc("value",e),K.Lb(1),K.Qc(" ",e," ")}}function jr(e,t){if(1&e){const e=K.ec();K.dc(0,"mat-form-field",32),K.dc(1,"mat-label"),K.Oc(2," Y-axis "),K.cc(),K.dc(3,"mat-select",33),K.lc("selectionChange",(function(t){return K.Ec(e),K.pc().selectLabel(t.value)})),K.Mc(4,Vr,2,2,"mat-option",34),K.cc(),K.cc()}if(2&e){const e=K.pc();K.Lb(3),K.vc("formControl",e.selectedLabel),K.Lb(1),K.vc("ngForOf",e.jobMultipleResulsLabels)}}function Zr(e,t){if(1&e&&(K.dc(0,"mat-option",35),K.Oc(1),K.cc()),2&e){const e=t.$implicit;K.vc("value",e),K.Lb(1),K.Qc(" ",e," ")}}function zr(e,t){if(1&e){const e=K.ec();K.dc(0,"mat-form-field",32),K.dc(1,"mat-label"),K.Oc(2," Select columns "),K.cc(),K.dc(3,"mat-select",36),K.lc("selectionChange",(function(t){return K.Ec(e),K.pc().selectHyperparams(t)}))("ngModelChange",(function(t){return K.Ec(e),K.pc().selectedCols=t})),K.Mc(4,Zr,2,2,"mat-option",34),K.cc(),K.cc()}if(2&e){const e=K.pc();K.Lb(3),K.vc("ngModel",e.selectedCols),K.Lb(1),K.vc("ngForOf",e.allColumns)}}function Kr(e,t){if(1&e&&(K.dc(0,"th",40),K.Oc(1),K.qc(2,"flu"),K.cc()),2&e){const e=K.pc().$implicit;K.Lb(1),K.Pc(K.rc(2,1,e))}}function Xr(e,t){if(1&e&&(K.dc(0,"span"),K.Oc(1),K.qc(2,"number"),K.cc()),2&e){const e=K.pc(2).$implicit,t=K.pc().$implicit;K.Lb(1),K.Pc(K.sc(2,1,e[t],"1.0-6"))}}function Ur(e,t){if(1&e&&(K.dc(0,"span"),K.Oc(1),K.cc()),2&e){const e=K.pc(2).$implicit,t=K.pc().$implicit;K.Lb(1),K.Pc(e[t])}}function Yr(e,t){if(1&e&&(K.dc(0,"span"),K.Mc(1,Xr,3,4,"span",42),K.Mc(2,Ur,2,1,"span",42),K.cc()),2&e){const e=K.pc().$implicit,t=K.pc().$implicit,n=K.pc();K.Lb(1),K.vc("ngIf",n.utilsService.isNumber(e[t])),K.Lb(1),K.vc("ngIf",!n.utilsService.isNumber(e[t]))}}function Jr(e,t){if(1&e&&(K.dc(0,"span"),K.Oc(1),K.qc(2,"date"),K.cc()),2&e){const e=K.pc(2).$implicit,t=K.pc().$implicit;K.Lb(1),K.Pc(K.sc(2,1,1e3*e[t],"medium"))}}function Qr(e,t){if(1&e&&(K.dc(0,"span"),K.Mc(1,Jr,3,4,"span",42),K.cc()),2&e){const e=K.pc().$implicit,t=K.pc().$implicit;K.Lb(1),K.vc("ngIf",e[t])}}function qr(e,t){if(1&e&&(K.dc(0,"td",41),K.Mc(1,Yr,3,2,"span",42),K.Mc(2,Qr,2,1,"span",42),K.cc()),2&e){const e=t.$implicit,n=K.pc().$implicit;K.vc("ngClass","status"==n?"status-"+e.status:""),K.Lb(1),K.vc("ngIf","start time"!==n&&"end time"!==n),K.Lb(1),K.vc("ngIf","start time"===n||"end time"===n)}}function ei(e,t){1&e&&(K.bc(0,37),K.Mc(1,Kr,3,3,"th",38),K.Mc(2,qr,3,3,"td",39),K.ac()),2&e&&K.vc("matColumnDef",t.$implicit)}function ti(e,t){1&e&&K.Yb(0,"tr",43)}function ni(e,t){1&e&&K.Yb(0,"tr",44)}const ri=function(){return[5,10,25,100]};let ii=(()=>{class e{constructor(e,t,n,r,i,s,o){this.store=e,this.route=t,this.router=n,this.cdRef=r,this.snackbarService=i,this.plotlyService=s,this.utilsService=o,this.pageSize=10,this.displayedColumns=[],this.displayedHyperparamsColumns=[],this.allColumns=[],this.columnsToDisplay=[],this.dataSource=new Je.k,this.tableFullData=[],this.tableHyperparamsData=[],this.optimizationCurve=!0,this.chartWidth=600,this.showInteractionGuide=!1,this.selectedLabel=new de.e,this.yAxisLabel="score",this.linePlot={data:[],layout:{height:540,showlegend:!0,title:{text:"Score",font:{family:"Courier New, monospace",size:24},xref:"paper",x:.05},xaxis:{title:{text:"x Axis",font:{family:"Courier New, monospace",size:18,color:"#7f7f7f"}}},yaxis:{title:{text:"Y Axis",font:{family:"Courier New, monospace",size:18,color:"#7f7f7f"}}}},config:{displayModeBar:!1,responsive:!0,displaylogo:!1,scrollZoom:!0}}}ngOnInit(){var e,t;this.subscriptions=new a.a,this.subscriptions.add(null===(t=null===(e=this.route)||void 0===e?void 0:e.parent)||void 0===t?void 0:t.params.subscribe(e=>{this.experimentId=e.id,this.selectedCols=[],this.allColumns=[]})),this.subscriptions.add(this.jobs$.subscribe(e=>{e&&e.length&&(this.jobs=e,this.displayedColumns=Object.keys(this.jobs[0].tableData),this.selectedCols&&this.selectedCols.length||(this.selectedCols=this.displayedColumns.slice()),this.displayedHyperparamsColumns=Object.keys(this.jobs[0].tableHyperParams),this.allColumns=this.displayedColumns.concat(this.displayedHyperparamsColumns),this.columnsToDisplay=this.selectedCols.slice(),this.jobs.map(e=>{this.tableHyperparamsData.push(e.tableHyperParams),this.tableFullData.push(e.tableFullData)}),this.dataSource.data=this.tableFullData.slice(),this.dataSource.paginator=this.paginator,this.dataSource.sort=this.sort,this.cdRef.markForCheck())})),this.subscriptions.add(this.jobsGraphData$.subscribe(e=>{e&&(this.jobsGraphData=e,this.linePlot.data=[],this.linePlot.data=[this.jobsGraphData],this.cdRef.markForCheck())})),this.subscriptions.add(this.jobsOptimizationGraphData$.subscribe(e=>{e&&(this.jobsOptimizationGraphData=e,this.linePlot.data.push(e),this.cdRef.markForCheck())})),this.subscriptions.add(this.theme$.subscribe(e=>{e&&(this.theme=e,this.toggleChartTheme())})),this.subscriptions.add(this.refreshInterval$.subscribe(e=>{e&&(this.refreshIntervalSubscription&&e!==this.refreshInterval&&this.refreshIntervalSubscription.unsubscribe(),this.refreshInterval=e,this.refreshIntervalSubscription=this.refreshIntervalData().subscribe())})),this.subscriptions.add(this.refreshingInterval$.subscribe(e=>{e&&(this.refreshData(),this.snackbarService.success("Data refreshed!"),this.store.dispatch(new w(!1)))})),this.subscriptions.add(this.jobMultipleResulsLabels$.subscribe(e=>{this.jobMultipleResulsLabels=e,this.cdRef.markForCheck()})),this.subscriptions.add(this.jobMultipleResulsSelectedLabel$.subscribe(e=>{this.jobMultipleResulsSelectedLabel=e,this.selectedLabel.patchValue(e),this.cdRef.markForCheck()}))}toggleChartTheme(){this.theme&&(this.linePlot.layout="dark"===this.theme.name?this.changeDarkModeChart():this.changeLightModeChart(),this.cdRef.markForCheck())}get exportedName(){return"jobStatus-"+(new Date).getTime()/1e3}selectHyperparams(e){e&&(this.selectedCols=e.value,this.dataSource.data=this.tableFullData.slice(),this.selectedCols=this.pushColumnToEnd("start time",this.selectedCols),this.selectedCols=this.pushColumnToEnd("end time",this.selectedCols),this.columnsToDisplay=this.selectedCols?this.selectedCols.slice():[],this.cdRef.markForCheck())}pushColumnToEnd(e,t){if(!e||!t||!t.length)return;const n=t.findIndex(t=>t===e);return-1!==n&&(t.splice(n,1),t.push(e)),t}downloadGraph(){const e=this.plotlyService.getInstanceByDivId("jobStatus");this.plotlyService.getPlotly().downloadImage(e,{format:"png",width:"1000",height:"450",filename:"jobStatus"})}toggleExperiment(e){e&&(this.experimentId=e,this.cleanData(),this.yAxisLabel="score",this.toggleChartTheme(),this.store.dispatch(new O(this.yAxisLabel)).subscribe(()=>{this.store.dispatch(new m({eid:this.experimentId})),this.optimizationCurve&&this.store.dispatch(new C({eid:this.experimentId})),this.store.dispatch(new M.a([`experiment/${e}/job-status`])),this.cdRef.markForCheck()}))}cleanData(){this.optimizationCurve=!0,this.columnsToDisplay=[],this.displayedHyperparamsColumns=[],this.tableHyperparamsData=[],this.tableFullData=[]}refreshIntervalData(){if(this.refreshInterval)return d(1e3*this.refreshInterval).pipe(Object(ee.a)(0),Object(F.a)(()=>{console.log("Fetching job status data..."),this.refreshData()}))}refreshData(){this.toggleChartTheme(),this.experimentId&&(this.tableHyperparamsData=[],this.tableFullData=[],this.store.dispatch(new m({eid:this.experimentId})).subscribe(()=>{this.optimizationCurve&&this.store.dispatch(new C({eid:this.experimentId}))}))}changeLightModeChart(){const e=new W.a;return{colorway:ar,height:540,showlegend:!0,xaxis:{zeroline:!1,title:{text:"Number of jobs",font:{size:18,color:"#7f7f7f"}}},yaxis:{zeroline:!1,title:{text:e.transform(this.yAxisLabel),font:{size:18,color:"#7f7f7f"}}}}}changeDarkModeChart(){const e=new W.a;return{colorway:ar,plot_bgcolor:"#424242",paper_bgcolor:"#424242",height:540,showlegend:!0,legend:{font:{color:"#ffffff"}},xaxis:{title:{text:"Number of jobs",font:{size:18,color:"#ffffff"}},gridcolor:"#c0c0c0",tickfont:{color:"#ffffff"},showline:!0,showgrid:!0,zeroline:!1,showticklabels:!0},yaxis:{title:{text:e.transform(this.yAxisLabel),font:{size:18,color:"#ffffff"}},gridcolor:"#c0c0c0",tickfont:{color:"#ffffff"},showline:!0,showgrid:!0,zeroline:!1,showticklabels:!0},line:{color:"#ffffff"}}}onToggleChartLine(e){if(e){switch(e.checked){case!0:this.store.dispatch(new C({eid:this.experimentId}));break;case!1:this.tableHyperparamsData=[],this.tableFullData=[],this.store.dispatch(new m({eid:this.experimentId}))}this.cdRef.markForCheck()}}ngOnDestroy(){this.subscriptions.unsubscribe(),this.refreshIntervalSubscription.unsubscribe()}onResize(e){e&&this.cdRef.markForCheck()}selectLabel(e){e&&(this.yAxisLabel=e,this.cdRef.markForCheck(),this.toggleChartTheme(),this.store.dispatch(new O(e)),this.optimizationCurve&&this.store.dispatch(new C({eid:this.experimentId})))}}return e.\u0275fac=function(t){return new(t||e)(K.Xb(o.i),K.Xb(i.a),K.Xb(i.g),K.Xb(K.j),K.Xb(Q.b),K.Xb(cr.c),K.Xb(Q.c))},e.\u0275cmp=K.Rb({type:e,selectors:[["app-job-status"]],viewQuery:function(e,t){if(1&e&&(K.Ic(at.a,!0),K.Ic(Ye.a,!0)),2&e){let e;K.Ac(e=K.mc())&&(t.paginator=e.first),K.Ac(e=K.mc())&&(t.sort=e.first)}},decls:72,vars:41,consts:[[1,"mat-headline"],["fxLayout","row","fxLayoutAlign","space-between center",1,"mt-5","w-full"],[1,"mr-3","w-full",3,"experimentId","toggleExperiment"],["appearance","outline","class","w-full mr-3",4,"ngIf"],[1,"mr-5",3,"ngModel","change","ngModelChange"],["fxLayoutAlign","space-between center",1,"w-full","mb-4","mr-3"],["matTooltip","Current view will be downloaded","mat-raised-button","",3,"click"],["fxFlexAlign","end","mat-raised-button","",3,"matMenuTriggerFor"],[1,"mr-3"],["fxLayout","row",1,"w-full"],["divId","jobStatus",1,"w-full","mr-3","chart",3,"data","layout","config","resize"],[1,"w-full","mt-3","mb-12"],["fxLayout","row","fxLayoutAlign","start center"],["matTooltip","Current table will be exported","mat-raised-button","",1,"mb-2","mr-1","mw-150",3,"matMenuTriggerFor"],["mat-table","","matTableExporter","","matSort","",1,"mat-elevation-z8","w-full","job-status-table",3,"dataSource"],["exporter","matTableExporter"],[3,"matColumnDef",4,"ngFor","ngForOf"],["mat-header-row","",4,"matHeaderRowDef"],["mat-row","",4,"matRowDef","matRowDefColumns"],[1,"mat-elevation-z8",3,"pageSize","pageSizeOptions"],["exportTableMenu","matMenu"],["mat-menu-item","",3,"click"],["xPosition","before"],["helpMenu","matMenu"],[1,"help-menu"],["fxLayout","row","fxLayoutAlign","end center",1,"w-full"],["mat-icon-button",""],["fxLayout","column","fxLayoutAlign","space-between center","fxLayoutGap","30px",1,"help-content"],["fxLayout","column","fxLayoutAlign","start center","fxLayoutGap","30px",1,"p-5"],[1,"w-full"],[1,"my-3","ml-5","help-list"],[1,"mt-3","ml-5","help-list"],["appearance","outline",1,"w-full","mr-3"],[1,"w-full",3,"formControl","selectionChange"],[3,"value",4,"ngFor","ngForOf"],[3,"value"],["multiple","",1,"w-full",3,"ngModel","selectionChange","ngModelChange"],[3,"matColumnDef"],["mat-header-cell","","mat-sort-header","",4,"matHeaderCellDef"],["mat-cell","",3,"ngClass",4,"matCellDef"],["mat-header-cell","","mat-sort-header",""],["mat-cell","",3,"ngClass"],[4,"ngIf"],["mat-header-row",""],["mat-row",""]],template:function(e,t){if(1&e){const e=K.ec();K.dc(0,"h1",0),K.Oc(1,"Status of all the jobs for the current experiment"),K.cc(),K.dc(2,"div",1),K.dc(3,"app-experiment-dropdown",2),K.lc("toggleExperiment",(function(e){return t.toggleExperiment(e)})),K.cc(),K.Mc(4,jr,5,2,"mat-form-field",3),K.dc(5,"mat-slide-toggle",4),K.lc("change",(function(e){return t.onToggleChartLine(e)}))("ngModelChange",(function(e){return t.optimizationCurve=e})),K.Oc(6," Best Results "),K.cc(),K.cc(),K.dc(7,"div",5),K.dc(8,"button",6),K.lc("click",(function(){return t.downloadGraph()})),K.Oc(9),K.qc(10,"uppercase"),K.cc(),K.dc(11,"button",7),K.dc(12,"mat-icon",8),K.Oc(13,"help_outline"),K.cc(),K.Oc(14),K.qc(15,"uppercase"),K.cc(),K.cc(),K.dc(16,"div",9),K.dc(17,"plotly-plot",10),K.lc("resize",(function(e){return t.onResize(e)}),!1,K.Dc),K.cc(),K.cc(),K.dc(18,"div",11),K.dc(19,"div",12),K.Mc(20,zr,5,2,"mat-form-field",3),K.dc(21,"button",13),K.Oc(22),K.qc(23,"uppercase"),K.cc(),K.cc(),K.dc(24,"table",14,15),K.Mc(26,ei,3,1,"ng-container",16),K.Mc(27,ti,1,0,"tr",17),K.Mc(28,ni,1,0,"tr",18),K.cc(),K.Yb(29,"mat-paginator",19),K.cc(),K.dc(30,"mat-menu",null,20),K.dc(32,"button",21),K.lc("click",(function(){return K.Ec(e),K.Bc(25).exportTable("xls",{fileName:t.exportedName})})),K.Oc(33),K.qc(34,"uppercase"),K.cc(),K.dc(35,"button",21),K.lc("click",(function(){return K.Ec(e),K.Bc(25).exportTable("xlsx",{fileName:t.exportedName})})),K.Oc(36),K.qc(37,"uppercase"),K.cc(),K.dc(38,"button",21),K.lc("click",(function(){return K.Ec(e),K.Bc(25).exportTable("csv",{fileName:t.exportedName})})),K.Oc(39),K.qc(40,"uppercase"),K.cc(),K.dc(41,"button",21),K.lc("click",(function(){return K.Ec(e),K.Bc(25).exportTable("txt",{fileName:t.exportedName})})),K.Oc(42),K.qc(43,"uppercase"),K.cc(),K.cc(),K.dc(44,"mat-menu",22,23),K.dc(46,"mat-toolbar",24),K.dc(47,"div",25),K.dc(48,"button",26),K.dc(49,"mat-icon"),K.Oc(50,"close"),K.cc(),K.cc(),K.cc(),K.cc(),K.dc(51,"mat-drawer-container",27),K.dc(52,"div",28),K.dc(53,"div",29),K.Oc(54," On the graph: "),K.dc(55,"ul",30),K.dc(56,"li"),K.Oc(57,"zoom out/in: place mouse on the graph and scroll up/down"),K.cc(),K.dc(58,"li"),K.Oc(59,"zoom in a specific area: click mouse anywhere on the graph and drag it to draw a square"),K.cc(),K.dc(60,"li"),K.Oc(61,"change the axis range: drag the label on x-axis or y-axis"),K.cc(),K.dc(62,"li"),K.Oc(63,"show/hide data: click on the corresponding legend"),K.cc(),K.dc(64,"li"),K.Oc(65,"show details of each job: hover over the data point"),K.cc(),K.dc(66,"li"),K.Oc(67,"go back to default view: double-click on anywhere on the plot"),K.cc(),K.cc(),K.Oc(68," On the table: "),K.dc(69,"ul",31),K.dc(70,"li"),K.Oc(71,"Sort: place mouse on the column header to show the sorting arrow"),K.cc(),K.cc(),K.cc(),K.cc(),K.cc(),K.cc()}if(2&e){const e=K.Bc(31),n=K.Bc(45);K.Lb(3),K.vc("experimentId",t.experimentId),K.Lb(1),K.vc("ngIf",t.jobMultipleResulsLabels&&t.jobMultipleResulsLabels.length>0),K.Lb(1),K.vc("ngModel",t.optimizationCurve),K.Lb(4),K.Qc(" ",K.rc(10,26,"Download graph png")," "),K.Lb(2),K.vc("matMenuTriggerFor",n),K.Lb(3),K.Qc(" ",K.rc(15,28,"interaction guide")," "),K.Lb(3),K.vc("data",t.linePlot.data)("layout",t.linePlot.layout)("config",t.linePlot.config),K.Lb(3),K.vc("ngIf",t.allColumns&&t.allColumns.length>0&&null!==t.allColumns),K.Lb(1),K.vc("matMenuTriggerFor",e),K.Lb(1),K.Qc(" ",K.rc(23,30,"Export as")," "),K.Lb(2),K.Jc("display",t.columnsToDisplay&&t.columnsToDisplay.length?"":"none"),K.vc("dataSource",t.dataSource),K.Lb(2),K.vc("ngForOf",t.columnsToDisplay),K.Lb(1),K.vc("matHeaderRowDef",t.columnsToDisplay),K.Lb(1),K.vc("matRowDefColumns",t.columnsToDisplay),K.Lb(1),K.Jc("display",t.columnsToDisplay&&t.columnsToDisplay.length&&t.dataSource.data.length>0?"block":"none"),K.vc("pageSize",t.pageSize)("pageSizeOptions",K.xc(40,ri)),K.Lb(4),K.Qc(" ",K.rc(34,32,"xls")," "),K.Lb(3),K.Qc(" ",K.rc(37,34,"xlsx")," "),K.Lb(3),K.Qc(" ",K.rc(40,36,"csv")," "),K.Lb(3),K.Qc(" ",K.rc(43,38,"txt")," ")}},directives:[oe.d,oe.c,kn,r.o,lr.a,de.n,de.q,ae.b,Ce.a,oe.a,le.d,ce.a,cr.a,Je.j,Hr,Ye.a,r.n,Je.g,Je.i,at.a,le.a,le.b,se.a,ue.b,oe.e,fe.c,fe.g,ge.a,de.f,me.m,Je.c,Je.e,Je.b,Je.d,Ye.b,Je.a,r.m,xe.a,Je.f,Je.h],pipes:[r.w,W.a,r.f,r.e],styles:[".headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%], .headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-title[_ngcontent-%COMP%]{flex-basis:0}.headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%]{justify-content:space-between;align-items:center}.headers-align[_ngcontent-%COMP%] .mat-form-field[_ngcontent-%COMP%] + .mat-form-field[_ngcontent-%COMP%]{margin-left:8px}mat-select[_ngcontent-%COMP%]{font-size:18px;padding-top:10px}.chart[_ngcontent-%COMP%]{height:540px}td.mat-cell[_ngcontent-%COMP%], th.mat-header-cell[_ngcontent-%COMP%]{text-align:center!important}th.mat-header-cell[_ngcontent-%COMP%] > div[_ngcontent-%COMP%]{justify-content:center!important}th[_ngcontent-%COMP%]{max-width:600px!important}.mat-sort-header-container[_ngcontent-%COMP%]{justify-content:center!important}.mw-150[_ngcontent-%COMP%]{min-width:150px}"],changeDetection:0}),Object(s.b)([Object(o.e)(ne.a.theme)],e.prototype,"theme$",void 0),Object(s.b)([Object(o.e)(q.jobs)],e.prototype,"jobs$",void 0),Object(s.b)([Object(o.e)(q.jobsGraphData)],e.prototype,"jobsGraphData$",void 0),Object(s.b)([Object(o.e)(q.jobsOptimizationGraphData)],e.prototype,"jobsOptimizationGraphData$",void 0),Object(s.b)([Object(o.e)(q.refreshInterval)],e.prototype,"refreshInterval$",void 0),Object(s.b)([Object(o.e)(q.refreshingInterval)],e.prototype,"refreshingInterval$",void 0),Object(s.b)([Object(o.e)(q.jobMultipleResulsLabels)],e.prototype,"jobMultipleResulsLabels$",void 0),Object(s.b)([Object(o.e)(q.jobMultipleResulsSelectedLabel)],e.prototype,"jobMultipleResulsSelectedLabel$",void 0),e})();function si(e,t){if(1&e&&(K.dc(0,"mat-option",22),K.Oc(1),K.cc()),2&e){const e=t.$implicit;K.vc("value",e),K.Lb(1),K.Qc(" ",e.label," ")}}function oi(e,t){if(1&e){const e=K.ec();K.dc(0,"mat-form-field",19),K.dc(1,"mat-label"),K.Oc(2,"Select axes"),K.cc(),K.dc(3,"mat-select",20),K.lc("selectionChange",(function(t){return K.Ec(e),K.pc().selectHyperparams(t)}))("ngModelChange",(function(t){return K.Ec(e),K.pc().selected=t})),K.Mc(4,si,2,2,"mat-option",21),K.cc(),K.cc()}if(2&e){const e=K.pc();K.Lb(3),K.vc("ngModel",e.selected),K.Lb(1),K.vc("ngForOf",e.hyperParamTraces)}}function ai(e,t){if(1&e){const e=K.ec();K.dc(0,"div",23),K.dc(1,"button",24),K.lc("click",(function(){return K.Ec(e),K.pc().downloadGraph()})),K.Oc(2),K.qc(3,"uppercase"),K.cc(),K.dc(4,"button",25),K.dc(5,"mat-icon",26),K.Oc(6,"help_outline"),K.cc(),K.Oc(7),K.qc(8,"uppercase"),K.cc(),K.cc()}if(2&e){K.pc();const e=K.Bc(10);K.Lb(2),K.Qc(" ",K.rc(3,3,"Download graph png")," "),K.Lb(2),K.vc("matMenuTriggerFor",e),K.Lb(3),K.Qc(" ",K.rc(8,5,"interaction guide")," ")}}function ci(e,t){if(1&e&&K.Yb(0,"plotly-plot",27),2&e){const e=K.pc();K.vc("data",e.graph.data)("layout",e.graph.layout)("config",e.graph.config)}}function li(e,t){if(1&e&&(K.dc(0,"h2"),K.Oc(1),K.cc()),2&e){const e=K.pc();K.Lb(1),K.Qc(" ",null==e.selectedExperiment||null==e.selectedExperiment.experiment?null:e.selectedExperiment.experiment.experimentName," does not contain any results\n")}}let hi=(()=>{class e{constructor(e,t,n,r,i){this.store=e,this.route=t,this.cdRef=n,this.plotlyService=r,this.router=i,this.initialAllowedParams=3,this.graph={data:[],layout:{height:540,title:"Hyperparameter Interaction Graph",showlegend:!0},config:{responsive:!0,displayModeBar:!1,scrollZoom:!0}},this.width=window.innerWidth,this.height=window.innerHeight,this.showInteractionGuide=!1}ngOnInit(){var e,t;this.subscriptions=new a.a,this.subscriptions.add(null===(t=null===(e=this.route)||void 0===e?void 0:e.parent)||void 0===t?void 0:t.params.subscribe(e=>{this.experimentId=e.id,this.experimentId&&(this.store.dispatch(new g(this.experimentId)),this.store.dispatch(new b(this.experimentId)))})),this.subscriptions.add(this.selectedExperiment$.subscribe(e=>{e&&(this.selectedExperiment=e,this.cdRef.markForCheck())})),this.subscriptions.add(this.sidenavOpen$.subscribe(e=>{null!=e&&this.theme&&(window.dispatchEvent(new Event("resize")),this.cdRef.detectChanges(),this.cdRef.markForCheck())})),this.subscriptions.add(this.parallelCoordinatesTrace$.subscribe(e=>{if(e){const t=new G.a;this.hyperParamTraces=e.dimensions.slice(),this.hyperParamTraces.map(e=>{e.label=t.transform(e.label,[30,"..."])}),this.initialTraces=[],this.selected=[];const n=this.hyperParamTraces.findIndex(e=>"score"===e.label.toLowerCase());if(-1!==n){this.initialTraces.push(this.hyperParamTraces[n]),this.hyperParamTraces.splice(n,1);for(let e=0;e{e&&(this.theme=e,this.graph.layout="dark"===e.name?this.changeDarkModeChart():this.changeLightModeChart(),this.cdRef.markForCheck())}))}ngOnDestroy(){this.subscriptions.unsubscribe()}downloadGraph(){const e=this.plotlyService.getInstanceByDivId("hig");this.plotlyService.getPlotly().downloadImage(e,{format:"png",width:"1000",height:"450",filename:"hig"})}selectHyperparams(e){e&&(this.selected=e.value,this.graph.data[0].dimensions=[...this.selected,...this.initialTraces],this.cdRef.markForCheck())}cleanData(){this.graph.data=[],this.initialTraces=[],this.hyperParamTraces=[],this.cdRef.markForCheck()}toggleExperiment(e){return Object(s.a)(this,void 0,void 0,(function*(){e&&(this.experimentId=e,this.cleanData(),this.store.dispatch(new g(this.experimentId)),this.store.dispatch(new b(this.experimentId)),yield this.router.navigate([`experiment/${e}/hig`]))}))}changeLightModeChart(){return{height:540,title:"Hyperparameter Interaction Graph",showlegend:!0,hovermode:"closest"}}changeDarkModeChart(){return{hovermode:"closest",plot_bgcolor:"#424242",paper_bgcolor:"#424242",height:540,title:{text:"Hyperparameter Interaction Graph",font:{color:"#ffffff"}},showlegend:!0}}}return e.\u0275fac=function(t){return new(t||e)(K.Xb(o.i),K.Xb(i.a),K.Xb(K.j),K.Xb(cr.c),K.Xb(i.g))},e.\u0275cmp=K.Rb({type:e,selectors:[["app-pcg"]],decls:23,vars:5,consts:[[1,"mat-headline"],["fxLayout","row","fxLayoutAlign","space-between center",1,"mt-5"],["fxFlex.gt-md","50","fxFlex","100",1,"mr-3",3,"experimentId","toggleExperiment"],["appearance","outline","class","w-full",4,"ngIf"],["class","w-full mb-4 mr-3","fxLayoutAlign","space-between center",4,"ngIf"],[1,"w-full"],["class","w-full chart","id","hig","divId","hig",3,"data","layout","config",4,"ngIf"],[4,"ngIf"],["xPosition","before"],["helpMenu","matMenu"],[1,"help-menu"],["fxLayout","row","fxLayoutAlign","end center",1,"w-full"],["mat-icon-button",""],["fxLayout","column","fxLayoutAlign","space-between center","fxLayoutGap","30px",1,"help-content"],["fxLayout","column","fxLayoutAlign","start center","fxLayoutGap","30px",1,"p-5"],["fxLayout","column",1,"w-full"],["fxFlex","100","fxLayout","row","fxLayoutAlign","start center"],["fxLayout","row","fxLayoutAlign","center center","fxLayoutGap","30px"],["src","assets/images/js_parcoords_ex1.gif"],["appearance","outline",1,"w-full"],["multiple","",3,"ngModel","selectionChange","ngModelChange"],[3,"value",4,"ngFor","ngForOf"],[3,"value"],["fxLayoutAlign","space-between center",1,"w-full","mb-4","mr-3"],["matTooltip","Current view will be downloaded","mat-raised-button","",3,"click"],["fxFlexAlign","end","mat-raised-button","",3,"matMenuTriggerFor"],[1,"mr-3"],["id","hig","divId","hig",1,"w-full","chart",3,"data","layout","config"]],template:function(e,t){1&e&&(K.dc(0,"h1",0),K.Oc(1," The Hyperparameter Interaction Graph (HIG) helps you understand the interplay between different hyperparameters\n"),K.cc(),K.dc(2,"div",1),K.dc(3,"app-experiment-dropdown",2),K.lc("toggleExperiment",(function(e){return t.toggleExperiment(e)})),K.cc(),K.Mc(4,oi,5,2,"mat-form-field",3),K.cc(),K.Mc(5,ai,9,7,"div",4),K.dc(6,"div",5),K.Mc(7,ci,1,3,"plotly-plot",6),K.cc(),K.Mc(8,li,2,1,"h2",7),K.dc(9,"mat-menu",8,9),K.dc(11,"mat-toolbar",10),K.dc(12,"div",11),K.dc(13,"button",12),K.dc(14,"mat-icon"),K.Oc(15,"close"),K.cc(),K.cc(),K.cc(),K.cc(),K.dc(16,"mat-drawer-container",13),K.dc(17,"div",14),K.dc(18,"div",15),K.dc(19,"p",16),K.Oc(20," Hyperparameter Interaction Graph is richly interactive by default. Drag the lines along the axes to filter regions and drag the axis names across the plot to rearrange variables. Double click on the selected axis to go back to default view. "),K.cc(),K.dc(21,"div",17),K.Yb(22,"img",18),K.cc(),K.cc(),K.cc(),K.cc(),K.cc()),2&e&&(K.Lb(3),K.vc("experimentId",t.experimentId),K.Lb(1),K.vc("ngIf",t.hyperParamTraces&&t.hyperParamTraces.length>0&&null!==t.hyperParamTraces),K.Lb(1),K.vc("ngIf",null==t.hyperParamTraces?null:t.hyperParamTraces.length),K.Lb(2),K.vc("ngIf",null==t.hyperParamTraces?null:t.hyperParamTraces.length),K.Lb(1),K.vc("ngIf",!(null!=t.hyperParamTraces&&t.hyperParamTraces.length)&&t.selectedExperiment))},directives:[oe.d,oe.c,kn,oe.b,r.o,le.a,se.a,ae.b,ce.a,ue.b,oe.e,fe.c,fe.g,ge.a,de.n,de.q,r.n,me.m,Ce.a,oe.a,le.d,cr.a],pipes:[r.w],styles:[".headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%], .headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-title[_ngcontent-%COMP%]{flex-basis:0}.headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%]{justify-content:space-between;align-items:center}.headers-align[_ngcontent-%COMP%] .mat-form-field[_ngcontent-%COMP%] + .mat-form-field[_ngcontent-%COMP%]{margin-left:8px}mat-select[_ngcontent-%COMP%]{font-size:18px!important;padding-top:10px!important}.chart[_ngcontent-%COMP%]{height:540px}"],changeDetection:0}),Object(s.b)([Object(o.e)(ne.a.theme)],e.prototype,"theme$",void 0),Object(s.b)([Object(o.e)(q.metricsVsHparams)],e.prototype,"metricsVsHparams$",void 0),Object(s.b)([Object(o.e)(q.selectedExperiment)],e.prototype,"selectedExperiment$",void 0),Object(s.b)([Object(o.e)(q.parallelCoordinatesTrace)],e.prototype,"parallelCoordinatesTrace$",void 0),Object(s.b)([Object(o.e)(q.sidenavOpen)],e.prototype,"sidenavOpen$",void 0),e})();function di(e,t){if(1&e&&(K.dc(0,"mat-option",27),K.Oc(1),K.cc()),2&e){const e=t.$implicit;K.vc("value",e),K.Lb(1),K.Qc(" ",e.experimentName||e.scriptName," ")}}function ui(e,t){if(1&e){const e=K.ec();K.dc(0,"mat-form-field",25),K.dc(1,"mat-label"),K.Oc(2," Select Experiments "),K.cc(),K.dc(3,"mat-select",26),K.lc("ngModelChange",(function(t){return K.Ec(e),K.pc(2).selectedExperiments=t}))("selectionChange",(function(t){return K.Ec(e),K.pc(2).selectExperiment(t)})),K.Mc(4,di,2,2,"mat-option",8),K.cc(),K.cc()}if(2&e){const e=K.pc(2);K.Lb(3),K.vc("ngModel",e.selectedExperiments),K.Lb(1),K.vc("ngForOf",e.experiments)}}function fi(e,t){if(1&e&&(K.dc(0,"div",5),K.Mc(1,ui,5,2,"mat-form-field",24),K.cc()),2&e){const e=K.pc();K.Lb(1),K.vc("ngIf",e.experiments&&e.experiments.length>0)}}function gi(e,t){if(1&e&&(K.dc(0,"mat-option",27),K.Oc(1),K.cc()),2&e){const e=t.$implicit;K.vc("value",e),K.Lb(1),K.Qc(" ",e," ")}}function pi(e,t){if(1&e){const e=K.ec();K.dc(0,"mat-form-field",6),K.dc(1,"mat-label"),K.Oc(2," Y-axis "),K.cc(),K.dc(3,"mat-select",28),K.lc("selectionChange",(function(t){return K.Ec(e),K.pc().selectLabel(t.value)})),K.Mc(4,gi,2,2,"mat-option",8),K.cc(),K.cc()}if(2&e){const e=K.pc();K.Lb(3),K.vc("formControl",e.selectedLabel),K.Lb(1),K.vc("ngForOf",e.commonLabels)}}function mi(e,t){if(1&e&&(K.dc(0,"mat-option",27),K.Oc(1),K.cc()),2&e){const e=t.$implicit;K.vc("value",e),K.Lb(1),K.Qc(" ",e," ")}}var Ci=function(e){return e.JOBS="jobs",e.SCORE="score",e.TIME="time (seconds)",e}({}),bi=function(e){return e.JID="jid",e.END_TIME="end_time",e}({});let vi=(()=>{class e{constructor(e,t,n,r,i,s){this.store=e,this.router=t,this.route=n,this.cdRef=r,this.experimentService=i,this.plotlyService=s,this.experimentMultiplier=1,this.axisValues=[Ci.JOBS,Ci.TIME],this.yAxisValue=Ci.SCORE,this.selectedExperiments=[],this.checkedExperiments=[],this.SORT_BY=bi,this.selectedSortBy=bi.JID,this.stepSizes=[1,2],this.commonLabels=[],this.selectedLabel=new de.e,this.yAxisLabel="score",this.graph={data:[],layout:{height:540,xaxis:{title:{text:""}},yaxis:{title:{text:""}}},config:{responsive:!0,displayModeBar:!1,scrollZoom:!0}},this.showInteractionGuide=!1}ngOnInit(){var e,t;this.subscriptions=new a.a,this.xAxisType=new de.e(this.axisValues[0]),this.yAxisType=new de.e(this.yAxisValue),this.subscriptions.add(null===(t=null===(e=this.route)||void 0===e?void 0:e.parent)||void 0===t?void 0:t.params.subscribe(e=>{this.experimentId=e.id})),this.subscriptions.add(this.experiments$.subscribe(e=>{e&&e.length>0&&(this.getExperimentsBestHistory(),this.selectedExperiments.length||(this.experiments=e.slice()),this.computePlots(),this.cdRef.markForCheck())})),this.subscriptions.add(this.theme$.subscribe(e=>{e&&(this.theme=e,this.provideLayoutBasedOnTheme())})),this.subscriptions.add(this.experimentsMultiplier$.subscribe(e=>{}))}provideLayoutBasedOnTheme(){this.graph.layout=this.changeLightModeChart(),this.theme&&(this.graph.layout="dark"===this.theme.name?this.changeDarkModeChart():this.changeLightModeChart(),this.cdRef.markForCheck())}downloadGraph(){const e=this.plotlyService.getInstanceByDivId("multiExperiment");this.plotlyService.getPlotly().downloadImage(e,{format:"png",width:"1000",height:"450",filename:"multiExperiment"})}getExperimentsBestHistory(){this.experimentMultiplier&&this.experimentService.getAllExperimentHistoryBest(this.experimentMultiplier,this.selectedSortBy,this.selectedLabel.value).subscribe(e=>{if(e&&e.experiment_history_best){const t=e.experiment_history_best;this.experiments.map(e=>{e.history=[],t[e.eid]&&t[e.eid].map(t=>{const n={jid:t.jid,jobConfig:JSON.parse(t.job_config),score:t.score};e.history.push(n)})}),this.selectedExperiments.map(e=>{e.history=[],t[e.eid].map(t=>{const n={jid:t.jid,jobConfig:JSON.parse(t.job_config),score:t.score};e.history.push(n)})}),this.computePlots(),this.cdRef.markForCheck()}})}get xAxisValue(){return this.xAxisType.value}changeLightModeChart(){const e=new W.a;return{colorway:ar,hovermode:"closest",height:450,showlegend:!0,xaxis:{zeroline:!1,title:{text:e.transform(this.xAxisValue),font:{size:18,color:"#7f7f7f"}}},yaxis:{zeroline:!1,title:{text:e.transform(this.yAxisValue),font:{size:18,color:"#7f7f7f"}}}}}changeDarkModeChart(){const e=new W.a;return{colorway:ar,hovermode:"closest",plot_bgcolor:"#424242",paper_bgcolor:"#424242",height:450,showlegend:!0,legend:{font:{color:"#ffffff"}},xaxis:{title:{text:e.transform(this.xAxisValue),font:{size:18,color:"#ffffff"}},gridcolor:"#c0c0c0",tickfont:{color:"#ffffff"},showline:!0,showgrid:!0,zeroline:!1,showticklabels:!0},yaxis:{title:{text:e.transform(this.yAxisValue),font:{size:18,color:"#ffffff"}},gridcolor:"#c0c0c0",tickfont:{color:"#ffffff"},showline:!0,showgrid:!0,zeroline:!1,showticklabels:!0},line:{color:"#ffffff"}}}selectExperiment(e){const t="score";if(!e.value)return;this.selectedExperiments=e.value;let n=[];this.commonLabels=[];for(let r=0;r1?n.shift().filter((function(e){return n.every((function(t){return-1!==t.indexOf(e)}))})):n[0].slice(),this.commonLabels.length>0&&this.commonLabels.unshift(t)):this.commonLabels=[],this.selectedLabel.patchValue(t),this.selectLabel(t),this.computePlots()}selectLabel(e){e&&(this.yAxisValue=e,this.cdRef.markForCheck(),this.getExperimentsBestHistory(),this.computePlots())}changeXAxisValue(){switch(this.xAxisType.value){case Ci.JOBS:this.selectedSortBy=bi.JID;break;case Ci.TIME:this.selectedSortBy=bi.END_TIME;break;default:this.selectedSortBy=null}this.getExperimentsBestHistory(),this.computePlots()}computePlots(){if(this.provideLayoutBasedOnTheme(),this.selectedExperiments.length){const e=this.selectedExperiments.map(e=>({x:[...this.computeXAxis(e)],y:[...this.computeYAxis(e)],mode:"lines+markers",type:"scatter",name:e.experimentName?e.experimentName:e.scriptName}));this.graph.data=e,this.cdRef.markForCheck()}else this.graph.data=[]}computeXAxis(e){if(this.xAxisType.value&&e){switch(this.xAxisType.value){case Ci.JOBS:return e.history.map((t,n)=>{const r=this.experimentMultiplier*(n+1);return r-Math.floor(r/e.jobs.length)*(r%e.jobs.length)});case Ci.TIME:return e.jobs.map(t=>t.end_time?t.end_time-e.startTime:null)}this.cdRef.markForCheck()}}computeYAxis(e){if(this.yAxisType.value&&e)return e.history.map(e=>e.score);this.cdRef.markForCheck()}}return e.\u0275fac=function(t){return new(t||e)(K.Xb(o.i),K.Xb(i.g),K.Xb(i.a),K.Xb(K.j),K.Xb(Y),K.Xb(cr.c))},e.\u0275cmp=K.Rb({type:e,selectors:[["app-multi-exp-comp"]],decls:48,vars:14,consts:[["fxLayout","column"],[1,"mat-headline"],["fxLayout","row","fxLayout.lt-md","column","fxLayoutAlign","space-between center","fxLayoutGap","20px"],["class","w-full",4,"ngIf"],["appearance","outline","class","w-full mr-3",4,"ngIf"],[1,"w-full"],["appearance","outline",1,"w-full","mr-3"],[3,"formControl","selectionChange"],[3,"value",4,"ngFor","ngForOf"],["fxLayoutAlign","space-between center",1,"w-full","mb-4","mr-3"],["matTooltip","Current view will be downloaded","mat-raised-button","",3,"click"],["fxFlexAlign","end","mat-raised-button","",3,"matMenuTriggerFor"],[1,"mr-3"],["divId","multiExperiment",1,"w-full","chart",3,"data","layout","config"],["xPosition","before"],["helpMenu","matMenu"],[1,"help-menu"],["fxLayout","row","fxLayoutAlign","end center",1,"w-full"],["mat-icon-button",""],["fxLayout","column","fxLayoutAlign","space-between center","fxLayoutGap","30px",1,"help-content"],["fxLayout","column","fxLayoutAlign","start","fxLayoutGap","30px",1,"p-5","w-full"],["fxFlex","100","fxLayout","row","fxLayoutAlign","start",1,"my-0"],["fxLayout","row","fxLayoutAlign","space-between center","fxLayoutGap","30px"],[1,"mt-3","ml-5","help-list"],["appearance","outline","class","w-full",4,"ngIf"],["appearance","outline",1,"w-full"],["multiple","",3,"ngModel","ngModelChange","selectionChange"],[3,"value"],[1,"w-full",3,"formControl","selectionChange"]],template:function(e,t){if(1&e&&(K.dc(0,"div",0),K.dc(1,"h1",1),K.Oc(2,"Compare the best scores of multiple experiments against each other"),K.cc(),K.dc(3,"div",2),K.Mc(4,fi,2,1,"div",3),K.Mc(5,pi,5,2,"mat-form-field",4),K.dc(6,"div",5),K.dc(7,"mat-form-field",6),K.dc(8,"mat-label"),K.Oc(9," X-axis "),K.cc(),K.dc(10,"mat-select",7),K.lc("selectionChange",(function(){return t.changeXAxisValue()})),K.Mc(11,mi,2,2,"mat-option",8),K.cc(),K.cc(),K.cc(),K.cc(),K.dc(12,"div",9),K.dc(13,"button",10),K.lc("click",(function(){return t.downloadGraph()})),K.Oc(14),K.qc(15,"uppercase"),K.cc(),K.dc(16,"button",11),K.dc(17,"mat-icon",12),K.Oc(18,"help_outline"),K.cc(),K.Oc(19),K.qc(20,"uppercase"),K.cc(),K.cc(),K.dc(21,"div",5),K.Yb(22,"plotly-plot",13),K.cc(),K.cc(),K.dc(23,"mat-menu",14,15),K.dc(25,"mat-toolbar",16),K.dc(26,"div",17),K.dc(27,"button",18),K.dc(28,"mat-icon"),K.Oc(29,"close"),K.cc(),K.cc(),K.cc(),K.cc(),K.dc(30,"mat-drawer-container",19),K.dc(31,"div",20),K.dc(32,"p",21),K.Oc(33," On the graph: "),K.cc(),K.dc(34,"div",22),K.dc(35,"ul",23),K.dc(36,"li"),K.Oc(37,"zoom out/in: place mouse on the graph and scroll up/down"),K.cc(),K.dc(38,"li"),K.Oc(39,"zoom in a specific area: click mouse anywhere on the graph and drag it to draw a square"),K.cc(),K.dc(40,"li"),K.Oc(41,"change the axis range: drag the label on x-axis or y-axis"),K.cc(),K.dc(42,"li"),K.Oc(43,"show/hide data: click on the corresponding legend"),K.cc(),K.dc(44,"li"),K.Oc(45,"show detailed value: hover over the data point"),K.cc(),K.dc(46,"li"),K.Oc(47,"go back to default view: double-click on anywhere on the plot"),K.cc(),K.cc(),K.cc(),K.cc(),K.cc(),K.cc()),2&e){const e=K.Bc(24);K.Lb(4),K.vc("ngIf",t.experiments&&t.experiments.length),K.Lb(1),K.vc("ngIf",t.commonLabels&&t.commonLabels.length>0),K.Lb(5),K.vc("formControl",t.xAxisType),K.Lb(1),K.vc("ngForOf",t.axisValues),K.Lb(3),K.Qc(" ",K.rc(15,10,"Download graph png")," "),K.Lb(2),K.vc("matMenuTriggerFor",e),K.Lb(3),K.Qc(" ",K.rc(20,12,"interaction guide")," "),K.Lb(3),K.vc("data",t.graph.data)("layout",t.graph.layout)("config",t.graph.config)}},directives:[oe.d,oe.c,oe.e,r.o,fe.c,fe.g,ge.a,de.n,de.f,r.n,ae.b,Ce.a,oe.a,le.d,ce.a,cr.a,le.a,se.a,ue.b,oe.b,de.q,me.m],pipes:[r.w],styles:[".multiplier[_ngcontent-%COMP%]{width:110px;margin-top:1px}.multiplier[_ngcontent-%COMP%] > mat-form-field[_ngcontent-%COMP%]{text-align:center}.headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%], .headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-title[_ngcontent-%COMP%]{flex-basis:0}.headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%]{justify-content:space-between;align-items:center}.headers-align[_ngcontent-%COMP%] .mat-form-field[_ngcontent-%COMP%] + .mat-form-field[_ngcontent-%COMP%]{margin-left:8px}input[_ngcontent-%COMP%], mat-select[_ngcontent-%COMP%]{font-size:18px;padding-top:10px}.chart[_ngcontent-%COMP%]{height:540px}"],changeDetection:0}),Object(s.b)([Object(o.e)(q.experiments)],e.prototype,"experiments$",void 0),Object(s.b)([Object(o.e)(q.experimentsMultiplier)],e.prototype,"experimentsMultiplier$",void 0),Object(s.b)([Object(o.e)(ne.a.theme)],e.prototype,"theme$",void 0),e})();var Ii=n("2Vo4"),Ai=n("B/XX"),yi=n("xHqg"),wi=n("bSwM");function Si(e,t){1&e&&(K.dc(0,"a",32),K.Yb(1,"img",33),K.cc()),2&e&&(K.Lb(1),K.vc("src","assets/images/Auptimizer-dark.png",K.Fc))}function xi(e,t){if(1&e){const e=K.ec();K.dc(0,"button",34),K.lc("click",(function(){return K.Ec(e),K.pc().setTheme()})),K.dc(1,"mat-icon",14),K.Oc(2),K.cc(),K.Oc(3),K.qc(4,"flu"),K.cc()}if(2&e){const e=K.pc();K.Lb(2),K.Pc(e.currentTheme.icon),K.Lb(1),K.Qc("",K.rc(4,2,e.currentTheme.name)," mode ")}}function Ei(e,t){if(1&e&&(K.dc(0,"span"),K.Oc(1),K.cc()),2&e){const e=K.pc();K.Lb(1),K.Qc("v",e.version,"")}}const ki=function(){return["/"]};function _i(e,t){if(1&e){const e=K.ec();K.dc(0,"div",11),K.dc(1,"h1",39),K.Oc(2,"Auptimizer environment is already set up"),K.cc(),K.dc(3,"div",40),K.dc(4,"a",41),K.Oc(5,"BACK"),K.cc(),K.dc(6,"button",42),K.lc("click",(function(){return K.Ec(e),K.pc(2).createDatabaseStep()})),K.Oc(7,"RECREATE"),K.cc(),K.cc(),K.cc()}2&e&&(K.Lb(4),K.vc("routerLink",K.xc(1,ki)))}function Ti(e,t){if(1&e){const e=K.ec();K.dc(0,"div",43),K.dc(1,"h1",44),K.Oc(2,"There is no environment created"),K.cc(),K.dc(3,"p",45),K.Oc(4,"Let's set up an Auptimizer environment to run new experiments!"),K.cc(),K.dc(5,"div",46),K.dc(6,"button",47),K.lc("click",(function(){return K.Ec(e),K.pc(2).createDatabaseStep()})),K.Oc(7,"CREATE"),K.cc(),K.cc(),K.cc()}}function Ri(e,t){if(1&e&&(K.dc(0,"mat-card",35),K.Yb(1,"img",36),K.Mc(2,_i,8,2,"div",37),K.qc(3,"async"),K.Mc(4,Ti,8,0,"div",38),K.qc(5,"async"),K.cc()),2&e){const e=K.pc();K.Lb(1),K.vc("src","assets/images/Auptimizer-"+(null==e.currentTheme?null:e.currentTheme.name)+".png",K.Fc),K.Lb(1),K.vc("ngIf",K.rc(3,3,e.dbUrl$)),K.Lb(2),K.vc("ngIf",null===K.rc(5,5,e.dbUrl$))}}function Bi(e,t){1&e&&K.Oc(0,"Work dir")}function Oi(e,t){1&e&&K.Oc(0,"Ini path")}function Li(e,t){1&e&&K.Oc(0,"Resource")}function Pi(e,t){if(1&e){const e=K.ec();K.dc(0,"button",73),K.lc("click",(function(){K.Ec(e);const n=t.$implicit;return K.pc(2).setType(n)})),K.Oc(1),K.cc()}if(2&e){const e=t.$implicit,n=K.pc(2);K.vc("ngClass",e===n.type?"btn-primary":""),K.Lb(1),K.Qc(" ",e," ")}}function Fi(e,t){1&e&&(K.dc(0,"mat-form-field",74),K.dc(1,"mat-label"),K.Oc(2,"CPU"),K.cc(),K.Yb(3,"input",75),K.dc(4,"mat-hint"),K.Oc(5,"Number of CPUs"),K.cc(),K.dc(6,"mat-error"),K.Oc(7,"CPU must be a number"),K.cc(),K.cc())}function Ni(e,t){1&e&&(K.dc(0,"mat-form-field",74),K.dc(1,"mat-label"),K.Oc(2,"AWS File"),K.cc(),K.Yb(3,"input",76),K.cc())}function Mi(e,t){1&e&&(K.dc(0,"mat-form-field",74),K.dc(1,"mat-label"),K.Oc(2,"GPU File"),K.cc(),K.Yb(3,"input",77),K.cc())}function Di(e,t){1&e&&(K.dc(0,"mat-form-field",74),K.dc(1,"mat-label"),K.Oc(2,"Node File"),K.cc(),K.Yb(3,"input",78),K.cc())}function $i(e,t){if(1&e){const e=K.ec();K.dc(0,"mat-card",35),K.dc(1,"div",48),K.dc(2,"h1",49),K.Oc(3,"Set up an Auptimizer environment"),K.cc(),K.dc(4,"mat-horizontal-stepper",50,51),K.dc(6,"mat-step",52),K.Mc(7,Bi,1,0,"ng-template",53),K.dc(8,"form",54),K.dc(9,"p",55),K.Oc(10," Working directory that contains your training script and saves the experiment results. "),K.cc(),K.dc(11,"mat-form-field",56),K.dc(12,"mat-label"),K.Oc(13,"Working directory"),K.cc(),K.Yb(14,"input",57),K.dc(15,"mat-error"),K.Oc(16,"Missing field"),K.cc(),K.cc(),K.dc(17,"div",40),K.dc(18,"button",58),K.lc("click",(function(){return K.Ec(e),K.pc().databaseInitStep()})),K.Oc(19,"BACK"),K.cc(),K.dc(20,"button",59),K.Oc(21,"NEXT"),K.cc(),K.cc(),K.cc(),K.cc(),K.dc(22,"mat-step",52),K.Mc(23,Oi,1,0,"ng-template",53),K.dc(24,"form",54),K.dc(25,"p",55),K.Oc(26,"Path to the Auptimizer environment file."),K.cc(),K.dc(27,"mat-form-field",56),K.dc(28,"mat-label"),K.Oc(29,"Ini path"),K.cc(),K.Yb(30,"input",60),K.dc(31,"mat-error"),K.Oc(32,"Missing field"),K.cc(),K.cc(),K.dc(33,"div",40),K.dc(34,"button",61),K.Oc(35,"BACK"),K.cc(),K.dc(36,"button",59),K.Oc(37,"NEXT"),K.cc(),K.cc(),K.cc(),K.cc(),K.dc(38,"mat-step",52),K.Mc(39,Li,1,0,"ng-template",53),K.dc(40,"div",48),K.dc(41,"p",55),K.Oc(42,"Select your target resource to run experiments."),K.cc(),K.cc(),K.dc(43,"div",62),K.Mc(44,Pi,2,2,"button",63),K.cc(),K.dc(45,"form",54),K.Mc(46,Fi,8,0,"mat-form-field",64),K.Mc(47,Ni,4,0,"mat-form-field",64),K.Mc(48,Mi,4,0,"mat-form-field",64),K.Mc(49,Di,4,0,"mat-form-field",64),K.cc(),K.dc(50,"form",65),K.dc(51,"mat-checkbox",66),K.Oc(52,"Overwrite"),K.cc(),K.cc(),K.dc(53,"div",67),K.dc(54,"mat-icon",68),K.Oc(55,"info"),K.cc(),K.dc(56,"p",69),K.Oc(57,"Overwrite any existing Auptimizer environment set up in the same working directory"),K.cc(),K.cc(),K.dc(58,"div",70),K.dc(59,"button",61),K.Oc(60,"BACK"),K.cc(),K.dc(61,"div",71),K.dc(62,"button",72),K.lc("click",(function(){return K.Ec(e),K.pc().onSubmit()})),K.Oc(63,"SET UP"),K.cc(),K.cc(),K.cc(),K.cc(),K.cc(),K.cc(),K.cc()}if(2&e){const e=K.pc();K.Lb(6),K.vc("stepControl",e.firstFormGroup),K.Lb(2),K.vc("formGroup",e.firstFormGroup),K.Lb(14),K.vc("stepControl",e.secondFormGroup),K.Lb(2),K.vc("formGroup",e.secondFormGroup),K.Lb(14),K.vc("stepControl",e.thirdFormGroup),K.Lb(6),K.vc("ngForOf",e.types),K.Lb(1),K.vc("formGroup",e.thirdFormGroup),K.Lb(1),K.vc("ngIf","cpu"===e.type),K.Lb(1),K.vc("ngIf","aws"===e.type),K.Lb(1),K.vc("ngIf","gpu"===e.type),K.Lb(1),K.vc("ngIf","node"===e.type),K.Lb(1),K.vc("formGroup",e.overwriteFormGroup),K.Lb(11),K.vc("matTooltip",e.overwriteFormGroup.valid?"":"Please check `Overwrite`"),K.Lb(1),K.vc("disabled",!e.overwriteFormGroup.valid||!e.thirdFormGroup.valid)}}var Wi=function(e){return e[e.INIT=0]="INIT",e[e.WIZARD=1]="WIZARD",e}({});let Hi=(()=>{class e{constructor(e,t,n){this.helperService=e,this.store=t,this.cdRef=n,this.types=["cpu","aws","gpu","node"],this.type="cpu",this.step=Wi,this.currentStep$=new Ii.a(Wi.INIT),this.currentTheme={name:null,icon:null},this.showInteractionGuide=!1}ngOnInit(){this.subscriptions=new a.a,this.firstFormGroup=new de.h({work_dir:new de.e("",[de.t.required])}),this.secondFormGroup=new de.h({ini_path:new de.e("",[de.t.required])}),this.thirdFormGroup=new de.h({cpu:new de.e("",[de.t.pattern(/^[0-9]*$/)]),aws_file:new de.e(""),gpu_file:new de.e(""),node_file:new de.e("")},(1,e=>{let t=0;return Object.keys(e.controls).forEach(n=>{e.controls[n].value&&t++}),t<1?{minimumValues:!0}:null})),this.overwriteFormGroup=new de.h({overwrite:new de.e(!1,[de.t.required])}),this.version=te.a.version,this.subscriptions.add(this.themes$.subscribe(e=>{this.themes=e,this.cdRef.markForCheck()})),this.subscriptions.add(this.theme$.subscribe(e=>{this.currentTheme=e,this.cdRef.markForCheck()})),this.subscriptions.add(this.dbUrl$.subscribe(e=>{e&&(this.overwriteFormGroup.controls.overwrite.setValidators(de.t.requiredTrue),this.cdRef.markForCheck())})),this.subscriptions.add(this.notifications$.subscribe(e=>{e&&(this.notifications=e,this.cdRef.markForCheck())}))}ngOnDestroy(){this.subscriptions.unsubscribe()}setTheme(){this.themes&&this.themes.length&&(this.currentTheme=this.currentTheme===this.themes[0]?this.themes[1]:this.themes[0],this.store.dispatch(new V.b(this.currentTheme)))}createDatabaseStep(){this.currentStep$.next(Wi.WIZARD)}databaseInitStep(){this.currentStep$.next(Wi.INIT)}setType(e){e&&(this.type=e,this.thirdFormGroup.reset(),this.cdRef.markForCheck())}onSubmit(){if(!(this.firstFormGroup.valid&&this.secondFormGroup.valid&&this.thirdFormGroup.valid&&this.overwriteFormGroup.valid))return;const e={work_dir:this.firstFormGroup.value.work_dir,ini_path:this.secondFormGroup.value.ini_path,overwrite:this.overwriteFormGroup.value.overwrite};switch(this.type){case"cpu":e.cpu=this.thirdFormGroup.value.cpu;break;case"aws":e.aws_file=this.thirdFormGroup.value.aws_file;break;case"gpu":e.gpu_file=this.thirdFormGroup.value.gpu_file;break;case"node":e.node_file=this.thirdFormGroup.value.node_file}this.store.dispatch(new E(e))}}return e.\u0275fac=function(t){return new(t||e)(K.Xb(Q.a),K.Xb(o.i),K.Xb(K.j))},e.\u0275cmp=K.Rb({type:e,selectors:[["app-initialize"]],features:[K.Kb([{provide:Ai.h,useValue:{showError:!0}}])],decls:67,vars:17,consts:[["color","primary","fxLayout","row","fxLayoutAlign","space-between center",1,"header-font"],["fxLayout","row","fxLayoutAlign","center center"],["routerLink","/","class","logo-container",4,"ngIf"],["fxLayout","row","fxLayoutGap","20px","fxLayoutAlign","center center"],["mat-icon-button","",3,"disabled","matMenuTriggerFor"],["matBadgeColor","warn",3,"matBadgeHidden","matBadge"],["mat-button","","class","header-font",3,"click",4,"ngIf"],["fxLayoutAlign","center center"],[4,"ngIf"],["fxLayout","column","fxLayoutAlign","center center",1,"initialize-container","text-primary"],["fxLayout","column","fxLayoutAlign","center center"],["fxLayout","column","fxLayoutAlign","center center",1,"h-full"],["fxLayoutAlign","end",1,"w-full","mb-4"],["fxFlexAlign","end","mat-raised-button","",3,"matMenuTriggerFor"],[1,"mr-3"],["fxLayout","column","fxLayoutAlign","center center","class","db-card",4,"ngIf"],["xPosition","before"],["notificationMenu","matMenu"],[1,"notification-menu"],["fxLayout","row","fxLayoutAlign","end center",1,"w-full"],["mat-icon-button",""],[3,"click"],["helpMenu","matMenu"],[1,"help-menu"],["mat-icon-button","","aria-label","Example icon-button with share icon",1,"example-icon"],["fxLayout","column","fxLayoutAlign","space-between center","fxLayoutGap","30px",1,"help-content"],["fxLayout","column","fxLayoutAlign","start center","fxLayoutGap","30px",1,"p-5"],[1,"w-full"],[1,"ml-5","help-list"],[1,"font-medium"],["href","https://lge-arc-advancedai.github.io/auptimizer/environment.html#environment-template-file-ini","target","_blank"],["href","https://lge-arc-advancedai.github.io/auptimizer/environment.html#configuration-options","target","_blank"],["routerLink","/",1,"logo-container"],[3,"src"],["mat-button","",1,"header-font",3,"click"],["fxLayout","column","fxLayoutAlign","center center",1,"db-card"],[1,"db-logo",3,"src"],["class","h-full","fxLayout","column","fxLayoutAlign","center center",4,"ngIf"],["fxLayout","column",4,"ngIf"],[1,"db-title","max-480","pb-12"],["fxLayout","row","fxLayoutAlign","center start","fxLayoutGap","60px",1,"w-full"],["mat-raised-button","","color","primary",1,"db-btn",3,"routerLink"],["mat-button","",1,"db-btn",3,"click"],["fxLayout","column"],[1,"db-title","max-480"],[1,"db-subtitle"],["fxLayoutAlign","center start",1,"w-full"],["mat-raised-button","","color","primary",1,"db-btn",3,"click"],["fxLayout","column","fxLayoutAlign","center center",1,"w-full"],[1,"db-title","m-0"],["linear",""],["stepper",""],[3,"stepControl"],["matStepLabel",""],["fxLayout","column","fxLayoutAlign","center center",3,"formGroup"],[1,"db-text"],["appearance","outline",1,"w-full","my-4"],["type","text","matInput","","placeholder","Work dir","formControlName","work_dir","required",""],["mat-button","",3,"click"],["mat-raised-button","","color","primary","matStepperNext","",1,"db-btn"],["type","text","matInput","","placeholder","Ini path","formControlName","ini_path","required",""],["mat-button","","matStepperPrevious",""],["fxLayout","row","fxLayoutAlign","center",1,"w-full"],["class","path-type-btn",3,"ngClass","click",4,"ngFor","ngForOf"],["appearance","outline","class","w-full mb-4",4,"ngIf"],["fxLayout","column","fxLayoutAlign","start",3,"formGroup"],["formControlName","overwrite"],["fxLayout","row","fxLayoutAlign","start center","fxLayoutGap","15px",1,"db-info"],["color","warn"],[1,"mb-0"],["fxLayout","row","fxLayoutAlign","center start","fxLayoutGap","60px",1,"w-full","mt-5"],[3,"matTooltip"],["mat-raised-button","","color","primary",1,"db-btn",3,"disabled","click"],[1,"path-type-btn",3,"ngClass","click"],["appearance","outline",1,"w-full","mb-4"],["type","text","matInput","","placeholder","CPU","formControlName","cpu"],["type","text","matInput","","placeholder","AWS File","formControlName","aws_file"],["type","text","matInput","","placeholder","GPU","formControlName","gpu_file"],["type","text","matInput","","placeholder","Node file","formControlName","node_file"]],template:function(e,t){if(1&e&&(K.dc(0,"mat-toolbar",0),K.dc(1,"div",1),K.Mc(2,Si,2,1,"a",2),K.cc(),K.dc(3,"div",3),K.dc(4,"button",4),K.dc(5,"mat-icon",5),K.Oc(6," notifications"),K.cc(),K.cc(),K.Mc(7,xi,5,4,"button",6),K.dc(8,"div",7),K.dc(9,"span"),K.Oc(10,"Version:\xa0"),K.cc(),K.Mc(11,Ei,2,1,"span",8),K.cc(),K.cc(),K.cc(),K.dc(12,"div",9),K.dc(13,"mat-drawer-container",10),K.dc(14,"mat-drawer-content"),K.dc(15,"div",11),K.dc(16,"div",12),K.dc(17,"button",13),K.dc(18,"mat-icon",14),K.Oc(19,"help_outline"),K.cc(),K.Oc(20),K.qc(21,"uppercase"),K.cc(),K.cc(),K.Mc(22,Ri,6,7,"mat-card",15),K.qc(23,"async"),K.Mc(24,$i,64,14,"mat-card",15),K.qc(25,"async"),K.cc(),K.cc(),K.cc(),K.cc(),K.dc(26,"mat-menu",16,17),K.dc(28,"mat-toolbar",18),K.dc(29,"div",19),K.dc(30,"button",20),K.dc(31,"mat-icon"),K.Oc(32,"close"),K.cc(),K.cc(),K.cc(),K.cc(),K.dc(33,"app-notification",21),K.lc("click",(function(e){return e.stopPropagation()})),K.cc(),K.cc(),K.dc(34,"mat-menu",16,22),K.dc(36,"mat-toolbar",23),K.dc(37,"div",19),K.dc(38,"button",24),K.dc(39,"mat-icon"),K.Oc(40,"close"),K.cc(),K.cc(),K.cc(),K.cc(),K.dc(41,"mat-drawer-container",25),K.dc(42,"div",26),K.dc(43,"div",27),K.dc(44,"ul",28),K.dc(45,"li"),K.dc(46,"span",29),K.Oc(47,"Working directory:"),K.cc(),K.Oc(48," a directory that contains your training script "),K.cc(),K.dc(49,"li"),K.dc(50,"span",29),K.Oc(51,"Ini path:"),K.cc(),K.Oc(52," the path to the Auptimizer environment (.ini) file. Follow the "),K.dc(53,"a",30),K.Oc(54,"link"),K.cc(),K.Oc(55," to prepare this file "),K.cc(),K.dc(56,"li"),K.dc(57,"span",29),K.Oc(58,"cpu/aws/gpu/node:"),K.cc(),K.Oc(59," your target resource to run experiments. Follow the "),K.dc(60,"a",31),K.Oc(61,"link"),K.cc(),K.Oc(62," to fill out specifications for each resource. "),K.cc(),K.dc(63,"li"),K.dc(64,"span",29),K.Oc(65,"Overwrite:"),K.cc(),K.Oc(66," overwrite any existing Auptimizer environment set up in the same working directory "),K.cc(),K.cc(),K.cc(),K.cc(),K.cc(),K.cc()),2&e){const e=K.Bc(27),n=K.Bc(35);K.Lb(2),K.vc("ngIf",null==t.currentTheme?null:t.currentTheme.name),K.Lb(2),K.vc("disabled",!(null!=t.notifications&&t.notifications.length))("matMenuTriggerFor",e),K.Lb(1),K.vc("matBadgeHidden",!(null!=t.notifications&&t.notifications.length))("matBadge",null==t.notifications?null:t.notifications.length),K.Lb(2),K.vc("ngIf",t.currentTheme),K.Lb(4),K.vc("ngIf",t.version),K.Lb(6),K.vc("matMenuTriggerFor",n),K.Lb(3),K.Qc(" ",K.rc(21,11,"interaction guide")," "),K.Lb(2),K.vc("ngIf",K.rc(23,13,t.currentStep$)===t.step.INIT),K.Lb(2),K.vc("ngIf",K.rc(25,15,t.currentStep$)===t.step.WIZARD)}},directives:[se.a,oe.d,oe.c,r.o,oe.e,ae.b,le.d,ce.a,he.a,ue.b,ue.c,oe.a,le.a,Se,i.i,ot.a,ae.a,yi.a,yi.b,yi.c,de.u,de.o,de.i,fe.c,fe.g,rt.b,de.c,de.n,de.g,de.s,fe.b,yi.e,yi.f,r.n,wi.a,Ce.a,r.m,xe.a,fe.f],pipes:[r.w,r.b,W.a],styles:["[_nghost-%COMP%] .header-font[_ngcontent-%COMP%]{font-size:15px}[_nghost-%COMP%] .db-card[_ngcontent-%COMP%]{padding:60px}[_nghost-%COMP%] .db-logo[_ngcontent-%COMP%]{width:442px;height:61px}[_nghost-%COMP%] .max-480[_ngcontent-%COMP%]{max-width:480px}[_nghost-%COMP%] .db-title[_ngcontent-%COMP%]{margin:60px 30px 10px;font-size:30px;font-weight:500;font-stretch:normal;font-style:normal;line-height:1.2;letter-spacing:normal;text-align:center}[_nghost-%COMP%] .db-subtitle[_ngcontent-%COMP%]{height:49px;margin:9px 50px 44px 45px}[_nghost-%COMP%] .db-subtitle[_ngcontent-%COMP%], [_nghost-%COMP%] .db-text[_ngcontent-%COMP%]{width:347px;font-size:16px;font-weight:400;font-stretch:normal;font-style:normal;line-height:1.19;letter-spacing:normal;text-align:center}[_nghost-%COMP%] .db-btn[_ngcontent-%COMP%]{width:127px;height:36px;border-radius:6px}[_nghost-%COMP%] .db-info[_ngcontent-%COMP%]{width:350px;font-size:12px;font-weight:400;font-stretch:normal;font-style:normal;line-height:1.25;letter-spacing:normal;text-align:left;margin-top:5px}[_nghost-%COMP%] .path-type-btn[_ngcontent-%COMP%]{width:94px;height:74px;margin:15px;padding:25px 30px 25px 31px;border-radius:6px;border:1px solid #cbcbcb}[_nghost-%COMP%] .initialize-container[_ngcontent-%COMP%] > mat-drawer-container[_ngcontent-%COMP%]{padding:50px}[_nghost-%COMP%] .initialize-container[_ngcontent-%COMP%] > mat-drawer-container[_ngcontent-%COMP%], [_nghost-%COMP%] .initialize-container[_ngcontent-%COMP%] > mat-drawer-container[_ngcontent-%COMP%] > mat-drawer-container[_ngcontent-%COMP%]{height:100%;min-height:calc(100vh - 64px);width:100%}[_nghost-%COMP%] .logo-container[_ngcontent-%COMP%] > img[_ngcontent-%COMP%]{height:35px}[_nghost-%COMP%] .headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%], [_nghost-%COMP%] .headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-title[_ngcontent-%COMP%]{flex-basis:0}[_nghost-%COMP%] .headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%]{justify-content:space-between;align-items:center}[_nghost-%COMP%] ul[_ngcontent-%COMP%] > li[_ngcontent-%COMP%]{list-style-type:circle;padding-bottom:15px} mat-toolbar{font-size:14px} .mat-menu-panel{max-width:none!important} .mat-menu-content:not(:empty){padding:0!important}.notification-menu[_ngcontent-%COMP%]{height:40px}"],changeDetection:0}),Object(s.b)([Object(o.e)(ne.a.dbUrl)],e.prototype,"dbUrl$",void 0),Object(s.b)([Object(o.e)(ne.a.themes)],e.prototype,"themes$",void 0),Object(s.b)([Object(o.e)(ne.a.theme)],e.prototype,"theme$",void 0),Object(s.b)([Object(o.e)(ne.a.notifications)],e.prototype,"notifications$",void 0),e})();var Gi=n("YfBG");const Vi={name:"new_exp",proposer:"sequence",n_samples:10,random_seed:1,script:"rosenbrock_hpo.py",parameter_config:[{name:"x",range:[-5,5],type:"float"},{name:"y",range:[-5,5],type:"float"}],resource:"cpu",n_parallel:2,target:"min"};let ji=(()=>{class e{constructor(e,t,n,r,i,s,o,a){this.store=e,this.dialog=t,this.fb=n,this.route=r,this.router=i,this.helperService=s,this.cdRef=o,this.snackbarService=a,this.showInteractionGuide=!1,this.jsonHasError=!1,this.autoConvert=!0,this.errMessage=null,this.validateJSON=e=>{"Tree"===e?this.jsonEditorTree.set(this.jsonCode):"Code"===e&&this.jsonEditorCode.set(this.jsonCode)}}ngOnInit(){this.subscriptions=new a.a;const e=this.route.snapshot.params.id;this.initForm(),this.clear(),e?(this.experimentId=e,this.store.dispatch(new g(this.experimentId)),this.subscriptions.add(this.selectedExperiment$.subscribe(e=>{if(e&&this.experimentId){this.clear();const t=e.experiment.expConfig.workingdir,n=e.experiment.expConfig.resource,r=e.experiment.expConfig.cwd;this.createExperimentForm.patchValue({cwd:r||t}),t&&"node"!==n&&delete e.experiment.expConfig.workingdir,r&&delete e.experiment.expConfig.cwd,this.experimentConfig=e.experiment.expConfig,this.initEditor(),this.cdRef.markForCheck()}}))):(this.experimentConfig=Vi,this.initEditor(),this.cdRef.markForCheck())}initForm(){this.createExperimentForm=this.fb.group({cwd:["",[de.t.required]]})}initEditor(){this.options={code:{mode:"code",onChange:()=>{let e;try{e=this.jsonEditorCode.get()}catch(t){this.jsonHasError=!0,this.errMessage=t,this.cdRef.markForCheck()}e&&(this.jsonHasError=!1,this.jsonCode=e,this.autoConvert&&this.validateJSON("Tree"),this.cdRef.markForCheck())}},tree:{mode:"tree",onChange:()=>{let e;try{e=this.jsonEditorTree.get()}catch(t){this.jsonHasError=!0,this.snackbarService.error(t),this.cdRef.markForCheck()}e&&(this.jsonHasError=!1,this.jsonCode=e,this.validateJSON("Code"),this.cdRef.markForCheck())}}},this.jsonEditorCode=new Gi(document.getElementById("jsonEditorCode"),this.options.code),this.jsonEditorTree=new Gi(document.getElementById("jsonEditorTree"),this.options.tree),this.jsonCode=this.experimentConfig,this.validateJSON("Code"),this.autoConvert&&this.validateJSON("Tree")}ngOnDestroy(){this.subscriptions.unsubscribe(),this.clear()}clear(){this.experimentConfig=null,this.jsonCode=null,this.jsonEditorCode=null,this.jsonEditorTree=null,this.options=null}onCancel(){this.confirmDialogRef=this.dialog.open(qe.a,{width:"450px",data:{title:"Cancel and go back?",content:"Are you sure you want to cancel creating this experiment? Everything you added will be lost and you will return to the experiment list page.",confirmButtonText:"Confirm"},panelClass:"header-modal"}),this.confirmDialogRef.afterClosed().subscribe(e=>{e&&this.helperService.redirectTo("/list")})}onCreateExperiment(){this.createExperimentForm.valid&&this.jsonCode&&!this.jsonHasError&&(this.jsonHasError?this.snackbarService.error(this.errMessage):this.store.dispatch(new k({cwd:this.createExperimentForm.value.cwd,json_config_body:this.jsonCode})))}}return e.\u0275fac=function(t){return new(t||e)(K.Xb(o.i),K.Xb(nt.b),K.Xb(de.d),K.Xb(i.a),K.Xb(i.g),K.Xb(Q.a),K.Xb(K.j),K.Xb(Q.b))},e.\u0275cmp=K.Rb({type:e,selectors:[["app-create-experiment"]],decls:64,vars:12,consts:[[1,"mt-3",3,"formGroup"],["fxLayout","row","fxLayoutAlign","center center","fxLayout.lt-lg","column","fxLayoutGap","15px",1,"w-full","px-3"],["appearance","outline",1,"w-full"],["type","text","matInput","","placeholder","Working directory","formControlName","cwd"],["fxLayout","row","fxLayoutAlign","start center","fxLayoutGap","15px",1,"mb-3"],["mat-raised-button","","color","accent","type","submit",1,"minw-150",3,"disabled","click"],["mat-raised-button","","type","button",1,"minw-100",3,"click"],["fxLayoutAlign","end center",1,"w-full","px-3"],["type","button","fxFlexAlign","end","mat-raised-button","",3,"matMenuTriggerFor"],[1,"mr-3"],["fxLayout","row","fxLayoutAlign","start center",1,"px-4"],[1,"mat-title","m-0","w-full"],[1,"wrapper"],["id","jsonEditorCode",1,"left"],["id","jsonEditorTree",1,"right"],["xPosition","before"],["helpMenu","matMenu"],[1,"help-menu"],["fxLayout","row","fxLayoutAlign","end center",1,"w-full"],["mat-icon-button",""],["fxLayout","column","fxLayoutAlign","space-between center","fxLayoutGap","30px",1,"help-content"],["fxLayout","column","fxLayoutAlign","start center","fxLayoutGap","30px",1,"p-5"],[1,"w-full"],[1,"mt-3","ml-5","help-list"],["href","https://lge-arc-advancedai.github.io/auptimizer/algorithm.html","target","_blank"],["href","https://lge-arc-advancedai.github.io/auptimizer/experiment.html#manual-modification-of-training-code","target","_blank"]],template:function(e,t){if(1&e&&(K.dc(0,"form",0),K.dc(1,"div",1),K.dc(2,"mat-form-field",2),K.dc(3,"mat-label"),K.Oc(4,"Current working directory"),K.cc(),K.Yb(5,"input",3),K.dc(6,"mat-error"),K.Oc(7,"Missing field"),K.cc(),K.cc(),K.dc(8,"div",4),K.dc(9,"button",5),K.lc("click",(function(){return t.onCreateExperiment()})),K.Oc(10),K.qc(11,"uppercase"),K.cc(),K.dc(12,"button",6),K.lc("click",(function(){return t.onCancel()})),K.Oc(13),K.qc(14,"uppercase"),K.cc(),K.cc(),K.cc(),K.dc(15,"div",7),K.dc(16,"button",8),K.dc(17,"mat-icon",9),K.Oc(18,"help_outline"),K.cc(),K.Oc(19),K.qc(20,"uppercase"),K.cc(),K.cc(),K.dc(21,"div",10),K.dc(22,"h2",11),K.Oc(23,"JSON FILE"),K.cc(),K.dc(24,"h2",11),K.Oc(25,"VALIDATED JSON"),K.cc(),K.cc(),K.dc(26,"div",12),K.Yb(27,"div",13),K.Yb(28,"div",14),K.cc(),K.cc(),K.dc(29,"mat-menu",15,16),K.dc(31,"mat-toolbar",17),K.dc(32,"div",18),K.dc(33,"button",19),K.dc(34,"mat-icon"),K.Oc(35,"close"),K.cc(),K.cc(),K.cc(),K.cc(),K.dc(36,"mat-drawer-container",20),K.dc(37,"div",21),K.dc(38,"div",22),K.Oc(39," There are two panels on this page: "),K.dc(40,"ul",23),K.dc(41,"li"),K.Oc(42,"Left panel: Here you can input your JSON file and modify it."),K.cc(),K.dc(43,"li"),K.Oc(44,"Right panel: Here displays the parsed JSON file. You can use it to validate if everything looks correct! "),K.cc(),K.cc(),K.cc(),K.dc(45,"div",22),K.Oc(46," How to: "),K.dc(47,"ul",23),K.dc(48,"li"),K.Oc(49,"Enter your working directory that saves your experiment json files and training script."),K.cc(),K.dc(50,"li"),K.Oc(51,"Input experiment configuration json in the left panel. Please follow the "),K.dc(52,"a",24),K.Oc(53,"link"),K.cc(),K.Oc(54," to prepare the json file."),K.cc(),K.dc(55,"li"),K.Oc(56,"Make sure you have adapted your training script following the "),K.dc(57,"a",25),K.Oc(58,"link"),K.cc(),K.Oc(59,"."),K.cc(),K.dc(60,"li"),K.Oc(61,"Click on Create experiment to add this experiment to the database."),K.cc(),K.dc(62,"li"),K.Oc(63,"Click on Cancel to go back."),K.cc(),K.cc(),K.cc(),K.cc(),K.cc(),K.cc()),2&e){const e=K.Bc(30);K.vc("formGroup",t.createExperimentForm),K.Lb(9),K.vc("disabled",!t.createExperimentForm.valid||t.jsonHasError),K.Lb(1),K.Qc(" ",K.rc(11,6,"Create experiment")," "),K.Lb(3),K.Qc(" ",K.rc(14,8,"Cancel")," "),K.Lb(3),K.vc("matMenuTriggerFor",e),K.Lb(3),K.Qc(" ",K.rc(20,10,"interaction guide")," ")}},directives:[de.u,de.o,de.i,oe.d,oe.c,oe.e,fe.c,fe.g,rt.b,de.c,de.n,de.g,fe.b,ae.b,oe.a,le.d,ce.a,le.a,se.a,ue.b],pipes:[r.w],styles:["[_nghost-%COMP%] .mat-checkbox-label{color:#fff!important}.wrapper[_ngcontent-%COMP%]{width:calc(100vw - 16px);height:calc(100vh - 64px - 80px - 80px - 50px);display:flex;padding:8px}mat-form-field[_ngcontent-%COMP%]{font-size:18px}.minw-150[_ngcontent-%COMP%]{min-width:150px}.minw-100[_ngcontent-%COMP%]{min-width:100px}.headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%], .headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-title[_ngcontent-%COMP%]{flex-basis:0}.headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%]{justify-content:space-between;align-items:center}.headers-align[_ngcontent-%COMP%] .mat-form-field[_ngcontent-%COMP%] + .mat-form-field[_ngcontent-%COMP%]{margin-left:8px}"],changeDetection:0}),Object(s.b)([Object(o.e)(q.selectedExperiment)],e.prototype,"selectedExperiment$",void 0),e})();var Zi=n("l7GE"),zi=n("ZUHj");class Ki{constructor(e,t){this.observables=e,this.project=t}call(e,t){return t.subscribe(new Xi(e,this.observables,this.project))}}class Xi extends Zi.a{constructor(e,t,n){super(e),this.observables=t,this.project=n,this.toRespond=[];const r=t.length;this.values=new Array(r);for(let i=0;i0){const e=r.indexOf(n);-1!==e&&r.splice(e,1)}}notifyComplete(){}_next(e){if(0===this.toRespond.length){const t=[e,...this.values];this.project?this._tryProject(t):this.destination.next(t)}}_tryProject(e){let t;try{t=this.project.apply(this,e)}catch(n){return void this.destination.error(n)}this.destination.next(t)}}function Ui(e,t){1&e&&(K.dc(0,"div",15),K.Yb(1,"mat-spinner",16),K.cc()),2&e&&(K.Lb(1),K.vc("diameter",100))}function Yi(e,t){if(1&e&&(K.dc(0,"mat-option",29),K.Oc(1),K.cc()),2&e){const e=t.$implicit;K.vc("value",e),K.Lb(1),K.Qc(" ",e.name||e.scriptName," ")}}function Ji(e,t){if(1&e){const e=K.ec();K.dc(0,"mat-form-field",26),K.dc(1,"mat-label"),K.Oc(2,"Select Experiments"),K.cc(),K.dc(3,"mat-select",27),K.lc("ngModelChange",(function(t){return K.Ec(e),K.pc(3).selectedExperiment=t}))("selectionChange",(function(t){return K.Ec(e),K.pc(3).onSelectExperiment(t)})),K.Mc(4,Yi,2,2,"mat-option",28),K.cc(),K.cc()}if(2&e){const e=K.pc(3);K.Lb(3),K.vc("ngModel",e.selectedExperiment),K.Lb(1),K.vc("ngForOf",e.intermResults)}}function Qi(e,t){if(1&e&&(K.dc(0,"div",23),K.Mc(1,Ji,5,2,"mat-form-field",25),K.cc()),2&e){const e=K.pc(2);K.Lb(1),K.vc("ngIf",e.intermResults&&e.intermResults.length>0)}}function qi(e,t){if(1&e&&(K.dc(0,"mat-option",29),K.Oc(1),K.cc()),2&e){const e=t.$implicit;K.vc("value",e),K.Lb(1),K.Qc(" ",e," ")}}function es(e,t){if(1&e){const e=K.ec();K.dc(0,"div",23),K.dc(1,"mat-form-field",30),K.dc(2,"mat-label"),K.Oc(3," Y-axis "),K.cc(),K.dc(4,"mat-select",31),K.lc("selectionChange",(function(t){return K.Ec(e),K.pc(2).selectLabel(t.value)})),K.Mc(5,qi,2,2,"mat-option",28),K.cc(),K.cc(),K.cc()}if(2&e){const e=K.pc(2);K.Lb(4),K.vc("formControl",e.selectedLabel),K.Lb(1),K.vc("ngForOf",e.interimExperimentMultResLabels)}}function ts(e,t){if(1&e&&(K.dc(0,"mat-option",29),K.Oc(1),K.cc()),2&e){const e=t.$implicit;K.vc("value",e),K.Lb(1),K.Qc(" ",e.jid," ")}}function ns(e,t){if(1&e){const e=K.ec();K.dc(0,"mat-form-field",26),K.dc(1,"mat-label"),K.Oc(2,"Select Job"),K.cc(),K.dc(3,"mat-select",32),K.lc("ngModelChange",(function(t){return K.Ec(e),K.pc(3).selectedJobs=t}))("openedChange",(function(){return K.Ec(e),K.pc(3).onOpenSelectJob()}))("selectionChange",(function(t){return K.Ec(e),K.pc(3).onSelectJob(t)})),K.Mc(4,ts,2,2,"mat-option",28),K.cc(),K.cc()}if(2&e){const e=K.pc(3);K.Lb(3),K.vc("ngModel",e.selectedJobs),K.Lb(1),K.vc("ngForOf",e.jobs)}}function rs(e,t){if(1&e&&(K.dc(0,"div",23),K.Mc(1,ns,5,2,"mat-form-field",25),K.cc()),2&e){const e=K.pc(2);K.Lb(1),K.vc("ngIf",e.jobs&&e.jobs.length>0)}}function is(e,t){if(1&e){const e=K.ec();K.bc(0),K.dc(1,"div",17),K.Mc(2,Qi,2,1,"div",18),K.Mc(3,es,6,2,"div",18),K.Mc(4,rs,2,1,"div",18),K.cc(),K.dc(5,"div",19),K.dc(6,"button",20),K.lc("click",(function(){return K.Ec(e),K.pc().downloadGraph()})),K.Oc(7),K.qc(8,"uppercase"),K.cc(),K.dc(9,"button",21),K.dc(10,"mat-icon",22),K.Oc(11,"help_outline"),K.cc(),K.Oc(12),K.qc(13,"uppercase"),K.cc(),K.cc(),K.dc(14,"div",23),K.Yb(15,"plotly-plot",24),K.cc(),K.ac()}if(2&e){const e=K.pc(),t=K.Bc(8);K.Lb(2),K.vc("ngIf",e.intermResults&&e.intermResults.length),K.Lb(1),K.vc("ngIf",e.interimExperimentMultResLabels&&e.interimExperimentMultResLabels.length>0),K.Lb(1),K.vc("ngIf",e.jobs&&e.jobs.length),K.Lb(3),K.Qc(" ",K.rc(8,9,"Download graph png")," "),K.Lb(2),K.vc("matMenuTriggerFor",t),K.Lb(3),K.Qc(" ",K.rc(13,11,"interaction guide")," "),K.Lb(3),K.vc("data",e.graph.data)("layout",e.graph.layout)("config",e.graph.config)}}let ss=(()=>{class e{constructor(e,t,n,r,i,s,o){this.locale=e,this.store=t,this.cdRef=n,this.route=r,this.utilsService=i,this.plotlyService=s,this.router=o,this.selectedJobs=[],this.selectedLabel=new de.e,this.yAxisLabel="score",this.graph={data:[],layout:{height:540,xaxis:{title:{text:""}},yaxis:{title:{text:""}}},config:{responsive:!0,displayModeBar:!1,scrollZoom:!0}},this.showInteractionGuide=!1,this.flag=!0}ngOnInit(){var e,t,n,r;this.subscriptions=new a.a,(null===(t=null===(e=this.route)||void 0===e?void 0:e.parent)||void 0===t?void 0:t.params)&&this.subscriptions.add(this.intermResults$.pipe(function(...e){return t=>{let n;return"function"==typeof e[e.length-1]&&(n=e.pop()),t.lift(new Ki(e,n))}}(null===(r=null===(n=this.route)||void 0===n?void 0:n.parent)||void 0===r?void 0:r.params)).subscribe(([e,t])=>{t&&t.id&&(this.experimentId=t.id),e&&e.length&&(this.intermResults=e,this.intermResults.map(e=>{console.log(e),e.eid===+this.experimentId&&(this.selectedExperiment=e,console.log(this.selectedExperiment),this.store.dispatch(new I(this.selectedExperiment)),this.store.dispatch(new A({eid:this.selectedExperiment.eid})),this.cdRef.markForCheck())}),this.cdRef.markForCheck())})),this.subscriptions.add(this.theme$.subscribe(e=>{e&&(this.theme=e,this.toggleChartTheme())})),this.subscriptions.add(this.interimExperimentMultResLabels$.subscribe(e=>{this.interimExperimentMultResLabels=e})),this.subscriptions.add(this.interimExperimentSelectedLabel$.subscribe(e=>{e&&(console.log("interimExperimentSelectedLabel: ",e),this.selectedLabel.patchValue(e),this.cdRef.markForCheck())})),this.subscriptions.add(this.interimExperimentJobs$.subscribe(e=>{if(e&&e.length){const t=this.selectedJobs;this.jobs=e.slice().sort((e,t)=>this.utilsService.compare(e.jid,t.jid,!0)),!this.selectedJobs.length&&this.flag?(this.jobs[0]&&this.selectedJobs.push(this.jobs[0]),this.jobs[1]&&this.selectedJobs.push(this.jobs[1]),this.jobs[2]&&this.selectedJobs.push(this.jobs[2]),this.computePlots()):this.selectedJobs=this.jobs.filter(e=>t.some(t=>t.jid===e.jid)),this.selectedJobs=this.jobs.filter(e=>t.some(t=>t.jid===e.jid)),this.cdRef.markForCheck()}}))}ngOnDestroy(){this.subscriptions.unsubscribe()}onSelectExperiment(e){e&&(this.flag=!0,this.selectedJobs=[],this.graph.data=[],this.selectedExperiment=e.value,this.jobs=null,this.store.dispatch(new I(this.selectedExperiment)),this.store.dispatch(new A({eid:this.selectedExperiment.eid})),this.router.navigate([`experiment/${this.selectedExperiment.eid}/interm`]),this.cdRef.markForCheck())}onOpenSelectJob(){this.flag=!1,this.store.dispatch(new A({eid:this.selectedExperiment.eid}))}onSelectJob(e){e&&(this.selectedJobs=e.value,this.computePlots())}downloadGraph(){const e=this.plotlyService.getInstanceByDivId("interim");this.plotlyService.getPlotly().downloadImage(e,{format:"png",width:"1000",height:"450",filename:"intermediateResults"})}computePlots(){if(this.toggleChartTheme(),this.selectedJobs.length){const e=this.selectedJobs.map(e=>({x:[...e.interimResults.map(t=>(t.receiveTime-e.interimResults[0].receiveTime)/1e3)],y:[...e.interimResults.map(e=>e.score)],mode:"lines+markers",type:"scatter",name:e.jid}));this.graph.data=e}else this.graph.data=[];this.cdRef.markForCheck()}toggleChartTheme(){this.theme&&(this.graph.layout="dark"===this.theme.name?this.changeDarkModeChart():this.changeLightModeChart(),this.cdRef.markForCheck())}changeLightModeChart(){const e=new W.a;return{colorway:ar,hovermode:"closest",height:450,showlegend:!0,xaxis:{zeroline:!1,title:{text:e.transform("time (seconds)"),font:{size:18,color:"#7f7f7f"}}},yaxis:{zeroline:!1,title:{text:e.transform(this.yAxisLabel),font:{size:18,color:"#7f7f7f"}}}}}changeDarkModeChart(){const e=new W.a;return{colorway:ar,hovermode:"closest",plot_bgcolor:"#424242",paper_bgcolor:"#424242",height:450,showlegend:!0,legend:{font:{color:"#ffffff"}},xaxis:{title:{text:e.transform("time (seconds)"),font:{size:18,color:"#ffffff"}},gridcolor:"#c0c0c0",tickfont:{color:"#ffffff"},showline:!0,showgrid:!0,zeroline:!1,showticklabels:!0},yaxis:{title:{text:e.transform(this.yAxisLabel),font:{size:18,color:"#ffffff"}},gridcolor:"#c0c0c0",tickfont:{color:"#ffffff"},showline:!0,showgrid:!0,zeroline:!1,showticklabels:!0},line:{color:"#ffffff"}}}selectLabel(e){e&&(this.yAxisLabel=e,this.cdRef.markForCheck(),this.flag=!0,this.selectedJobs=[],this.graph.data=[],this.jobs=null,this.toggleChartTheme(),this.store.dispatch(new A({eid:this.selectedExperiment.eid,label:e})))}}return e.\u0275fac=function(t){return new(t||e)(K.Xb(K.B),K.Xb(o.i),K.Xb(K.j),K.Xb(i.a),K.Xb(Q.c),K.Xb(cr.c),K.Xb(i.g))},e.\u0275cmp=K.Rb({type:e,selectors:[["app-interm-results"]],decls:34,vars:6,consts:[["fxLayout","column"],[1,"mat-headline"],["fxLayout","row","fxLayoutAlign","center center","class","p-10",4,"ngIf"],[4,"ngIf"],["xPosition","before"],["helpMenu","matMenu"],[1,"help-menu"],["fxLayout","row","fxLayoutAlign","end center",1,"w-full"],["mat-icon-button",""],["fxLayout","column","fxLayoutAlign","space-between center","fxLayoutGap","30px",1,"help-content"],["fxLayout","column","fxLayoutAlign","start","fxLayoutGap","30px",1,"p-5","w-full"],["fxFlex","100","fxLayout","row","fxLayoutAlign","start",1,"mt-0","mb-3"],["fxFlex","100","fxLayout","row","fxLayoutAlign","start",1,"my-0"],["fxLayout","row","fxLayoutAlign","space-between center","fxLayoutGap","30px"],[1,"mt-3","ml-5","help-list"],["fxLayout","row","fxLayoutAlign","center center",1,"p-10"],["color","accent",1,"mt-10",3,"diameter"],["fxLayout","row","fxLayoutAlign","space-between center","fxLayoutGap","20px"],["class","w-full",4,"ngIf"],["fxLayoutAlign","space-between center",1,"w-full","mb-4","mr-3"],["matTooltip","Current view will be downloaded","mat-raised-button","",3,"click"],["fxFlexAlign","end","mat-raised-button","",3,"matMenuTriggerFor"],[1,"mr-3"],[1,"w-full"],["divId","interim",1,"w-full","chart",3,"data","layout","config"],["appearance","outline","class","w-full",4,"ngIf"],["appearance","outline",1,"w-full"],[3,"ngModel","ngModelChange","selectionChange"],[3,"value",4,"ngFor","ngForOf"],[3,"value"],["appearance","outline",1,"w-full","mr-3"],[1,"w-full",3,"formControl","selectionChange"],["multiple","",3,"ngModel","ngModelChange","openedChange","selectionChange"]],template:function(e,t){1&e&&(K.dc(0,"div",0),K.dc(1,"h1",1),K.Oc(2,"Compare the intermediate results of an experiment`s jobs against each other"),K.cc(),K.Mc(3,Ui,2,1,"div",2),K.qc(4,"async"),K.Mc(5,is,16,13,"ng-container",3),K.qc(6,"async"),K.cc(),K.dc(7,"mat-menu",4,5),K.dc(9,"mat-toolbar",6),K.dc(10,"div",7),K.dc(11,"button",8),K.dc(12,"mat-icon"),K.Oc(13,"close"),K.cc(),K.cc(),K.cc(),K.cc(),K.dc(14,"mat-drawer-container",9),K.dc(15,"div",10),K.dc(16,"p",11),K.Oc(17," To make best use of this graph, please check the job status to identify jobs you would like to visualize. "),K.cc(),K.dc(18,"p",12),K.Oc(19,"On the graph:"),K.cc(),K.dc(20,"div",13),K.dc(21,"ul",14),K.dc(22,"li"),K.Oc(23,"zoom out/in: place mouse on the graph and scroll up/down"),K.cc(),K.dc(24,"li"),K.Oc(25,"zoom in a specific area: click mouse anywhere on the graph and drag it to draw a square"),K.cc(),K.dc(26,"li"),K.Oc(27,"change the axis range: drag the label on x-axis or y-axis"),K.cc(),K.dc(28,"li"),K.Oc(29,"show/hide data: click on the corresponding legend"),K.cc(),K.dc(30,"li"),K.Oc(31,"show detailed value: hover over the data point"),K.cc(),K.dc(32,"li"),K.Oc(33,"go back to default view: double-click on anywhere on the plot"),K.cc(),K.cc(),K.cc(),K.cc(),K.cc(),K.cc()),2&e&&(K.Lb(3),K.vc("ngIf",K.rc(4,2,t.loadingIntermResults$)),K.Lb(2),K.vc("ngIf",!1===K.rc(6,4,t.loadingIntermResults$)))},directives:[oe.d,r.o,le.a,se.a,oe.c,ae.b,ce.a,ue.b,oe.e,oe.b,st.b,Ce.a,oe.a,le.d,cr.a,fe.c,fe.g,ge.a,de.n,de.q,r.n,me.m,de.f],pipes:[r.b,r.w],styles:[".multiplier[_ngcontent-%COMP%]{width:110px;margin-top:1px}.multiplier[_ngcontent-%COMP%] > mat-form-field[_ngcontent-%COMP%]{text-align:center}.headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%], .headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-title[_ngcontent-%COMP%]{flex-basis:0}.headers-align[_ngcontent-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%]{justify-content:space-between;align-items:center}.headers-align[_ngcontent-%COMP%] .mat-form-field[_ngcontent-%COMP%] + .mat-form-field[_ngcontent-%COMP%]{margin-left:8px}input[_ngcontent-%COMP%], mat-select[_ngcontent-%COMP%]{font-size:18px;padding-top:10px}.chart[_ngcontent-%COMP%]{height:540px}"],changeDetection:0}),Object(s.b)([Object(o.e)(ne.a.theme)],e.prototype,"theme$",void 0),Object(s.b)([Object(o.e)(q.intermResults)],e.prototype,"intermResults$",void 0),Object(s.b)([Object(o.e)(q.loadingIntermResults)],e.prototype,"loadingIntermResults$",void 0),Object(s.b)([Object(o.e)(q.interimExperimentJobs)],e.prototype,"interimExperimentJobs$",void 0),Object(s.b)([Object(o.e)(q.interimExperimentMultResLabels)],e.prototype,"interimExperimentMultResLabels$",void 0),Object(s.b)([Object(o.e)(q.interimExperimentSelectedLabel)],e.prototype,"interimExperimentSelectedLabel$",void 0),e})();const os=[{path:"",canActivate:[n("Ne9M").a],component:Oe,children:[{path:"",redirectTo:"list",pathMatch:"full"},{path:"list",component:wn},{path:"notification",component:Se},{path:"create",component:ji},{path:"create/:id",component:ji},{path:"experiment/:id",component:Ue,children:[{path:"",redirectTo:"overview",pathMatch:"full"},{path:"overview",component:or},{path:"job-status",component:ii},{path:"hig",component:hi},{path:"multi",component:vi},{path:"interm",component:ss}]}]},{path:"initialize",component:Hi}];let as=(()=>{class e{}return e.\u0275mod=K.Vb({type:e}),e.\u0275inj=K.Ub({factory:function(t){return new(t||e)},imports:[[i.j.forChild(os)],i.j]}),e})();var cs=n("PCNd");let ls=(()=>{class e{}return e.\u0275mod=K.Vb({type:e}),e.\u0275inj=K.Ub({factory:function(t){return new(t||e)},providers:[Y],imports:[[r.c,as,cs.a,o.d.forFeature([q]),Gr]]}),e})()},3:function(e,t){},4:function(e,t){},5:function(e,t){},EUZL:function(e,t,n){!function(t){t.version="0.14.5";var r=1200,i=1252;"undefined"==typeof cptable&&("undefined"!=typeof global?global.cptable=n("IkRI"):"undefined"!=typeof window&&(window.cptable=n("IkRI")));for(var s=[874,932,936,949,950],o=0;o<=8;++o)s.push(1250+o);var a={0:1252,1:65001,2:65001,77:1e4,128:932,129:949,130:1361,134:936,136:950,161:1253,162:1254,163:1258,177:1255,178:1256,186:1257,204:1251,222:874,238:1250,255:1252,69:6969},c=function(e){-1!=s.indexOf(e)&&(i=a[0]=e)},l=function(e){r=e,c(e)};function h(){l(1200),c(1252)}function d(e){for(var t=[],n=0,r=e.length;n>1;++n)t[n]=String.fromCharCode(e.charCodeAt(2*n)+(e.charCodeAt(2*n+1)<<8));return t.join("")}(e.slice(2)):254==t&&255==n?function(e){for(var t=[],n=0;n>1;++n)t[n]=String.fromCharCode(e.charCodeAt(2*n+1)+(e.charCodeAt(2*n)<<8));return t.join("")}(e.slice(2)):65279==t?e.slice(1):e},f=function(e){return String.fromCharCode(e)},g=function(e){return String.fromCharCode(e)};"undefined"!=typeof cptable&&(l=function(e){r=e,c(e)},u=function(e){return 255===e.charCodeAt(0)&&254===e.charCodeAt(1)?cptable.utils.decode(1200,d(e.slice(2))):e},f=function(e){return 1200===r?String.fromCharCode(e):cptable.utils.decode(r,[255&e,e>>8])[0]},g=function(e){return cptable.utils.decode(i,[e])[0]});var p,m=(p="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",{encode:function(e){for(var t="",n=0,r=0,i=0,s=0,o=0,a=0,c=0,l=0;l>2,o=(3&n)<<4|(r=e.charCodeAt(l++))>>4,a=(15&r)<<2|(i=e.charCodeAt(l++))>>6,c=63&i,isNaN(r)?a=c=64:isNaN(i)&&(c=64),t+=p.charAt(s)+p.charAt(o)+p.charAt(a)+p.charAt(c);return t},decode:function(e){var t="",n=0,r=0,i=0,s=0;e=e.replace(/[^\w\+\/\=]/g,"");for(var o=0;o>4),64!==(i=p.indexOf(e.charAt(o++)))&&(t+=String.fromCharCode((15&r)<<4|i>>2)),64!==(s=p.indexOf(e.charAt(o++)))&&(t+=String.fromCharCode((3&i)<<6|s));return t}}),C="undefined"!=typeof Buffer&&"undefined"!=typeof process&&void 0!==process.versions&&!!process.versions.node,b=function(){};if("undefined"!=typeof Buffer){var v=!Buffer.from;if(!v)try{Buffer.from("foo","utf8")}catch(El){v=!0}b=v?function(e,t){return t?new Buffer(e,t):new Buffer(e)}:Buffer.from.bind(Buffer),Buffer.alloc||(Buffer.alloc=function(e){return new Buffer(e)}),Buffer.allocUnsafe||(Buffer.allocUnsafe=function(e){return new Buffer(e)})}function I(e){return C?Buffer.alloc(e):new Array(e)}function A(e){return C?Buffer.allocUnsafe(e):new Array(e)}var y=function(e){return C?b(e,"binary"):e.split("").map((function(e){return 255&e.charCodeAt(0)}))};function w(e){if("undefined"==typeof ArrayBuffer)return y(e);for(var t=new ArrayBuffer(e.length),n=new Uint8Array(t),r=0;r!=e.length;++r)n[r]=255&e.charCodeAt(r);return t}function S(e){if(Array.isArray(e))return e.map(tc).join("");for(var t=[],n=0;n=0;)t+=e.charAt(n--);return t}function n(e,t){for(var n="";n.length=t?r:n("0",t-r.length)+r}function i(e,t){var r=""+e;return r.length>=t?r:n(" ",t-r.length)+r}function s(e,t){var r=""+e;return r.length>=t?r:r+n(" ",t-r.length)}e.version="0.10.2";var o=Math.pow(2,32);function a(e,t){return e>o||e<-o?function(e,t){var r=""+Math.round(e);return r.length>=t?r:n("0",t-r.length)+r}(e,t):function(e,t){var r=""+e;return r.length>=t?r:n("0",t-r.length)+r}(Math.round(e),t)}function c(e,t){return e.length>=7+(t=t||0)&&103==(32|e.charCodeAt(t))&&101==(32|e.charCodeAt(t+1))&&110==(32|e.charCodeAt(t+2))&&101==(32|e.charCodeAt(t+3))&&114==(32|e.charCodeAt(t+4))&&97==(32|e.charCodeAt(t+5))&&108==(32|e.charCodeAt(t+6))}var l=[["Sun","Sunday"],["Mon","Monday"],["Tue","Tuesday"],["Wed","Wednesday"],["Thu","Thursday"],["Fri","Friday"],["Sat","Saturday"]],h=[["J","Jan","January"],["F","Feb","February"],["M","Mar","March"],["A","Apr","April"],["M","May","May"],["J","Jun","June"],["J","Jul","July"],["A","Aug","August"],["S","Sep","September"],["O","Oct","October"],["N","Nov","November"],["D","Dec","December"]];function d(e){e[0]="General",e[1]="0",e[2]="0.00",e[3]="#,##0",e[4]="#,##0.00",e[9]="0%",e[10]="0.00%",e[11]="0.00E+00",e[12]="# ?/?",e[13]="# ??/??",e[14]="m/d/yy",e[15]="d-mmm-yy",e[16]="d-mmm",e[17]="mmm-yy",e[18]="h:mm AM/PM",e[19]="h:mm:ss AM/PM",e[20]="h:mm",e[21]="h:mm:ss",e[22]="m/d/yy h:mm",e[37]="#,##0 ;(#,##0)",e[38]="#,##0 ;[Red](#,##0)",e[39]="#,##0.00;(#,##0.00)",e[40]="#,##0.00;[Red](#,##0.00)",e[45]="mm:ss",e[46]="[h]:mm:ss",e[47]="mmss.0",e[48]="##0.0E+0",e[49]="@",e[56]='"\u4e0a\u5348/\u4e0b\u5348 "hh"\u6642"mm"\u5206"ss"\u79d2 "',e[65535]="General"}var u={};function f(e,t,n){for(var r=e<0?-1:1,i=e*r,s=0,o=1,a=0,c=1,l=0,h=0,d=Math.floor(i);lt&&(l>t?(h=c,a=s):(h=l,a=o)),!n)return[0,r*a,h];var u=Math.floor(r*a/h);return[u,r*a-u*h,h]}function g(e,t,n){if(e>2958465||e<0)return null;var r=0|e,i=Math.floor(86400*(e-r)),s=0,o=[],a={D:r,T:i,u:86400*(e-r)-i,y:0,m:0,d:0,H:0,M:0,S:0,q:0};if(Math.abs(a.u)<1e-6&&(a.u=0),t&&t.date1904&&(r+=1462),a.u>.9999&&(a.u=0,86400==++i&&(a.T=i=0,++r,++a.D)),60===r)o=n?[1317,10,29]:[1900,2,29],s=3;else if(0===r)o=n?[1317,8,29]:[1900,1,0],s=6;else{r>60&&--r;var c=new Date(1900,0,1);c.setDate(c.getDate()+r-1),o=[c.getFullYear(),c.getMonth()+1,c.getDate()],s=c.getDay(),r<60&&(s=(s+6)%7),n&&(s=0)}return a.y=o[0],a.m=o[1],a.d=o[2],a.S=i%60,i=Math.floor(i/60),a.M=i%60,i=Math.floor(i/60),a.H=i,a.q=s,a}d(u),e.parse_date_code=g;var p=new Date(1899,11,31,0,0,0),m=p.getTime(),C=new Date(1900,2,1,0,0,0);function b(e,t){var n=e.getTime();return t?n-=1262304e5:e>=C&&(n+=864e5),(n-(m+6e4*(e.getTimezoneOffset()-p.getTimezoneOffset())))/864e5}function v(e){return e.toString(10)}e._general_int=v;var I=function(){var e=/\.(\d*[1-9])0+$/,t=/\.0*$/,n=/\.(\d*[1-9])0+/,r=/\.0*[Ee]/,i=/(E[+-])(\d)$/;function s(n){return n.indexOf(".")>-1?n.replace(t,"").replace(e,".$1"):n}return function(t){var o,a=Math.floor(Math.log(Math.abs(t))*Math.LOG10E);return o=a>=-4&&a<=-1?t.toPrecision(10+a):Math.abs(a)<=9?function(e){var t=e<0?12:11,n=s(e.toFixed(12));return n.length<=t||(n=e.toPrecision(10)).length<=t?n:e.toExponential(5)}(t):10===a?t.toFixed(10).substr(0,12):function(t){var n=t.toFixed(11).replace(e,".$1");return n.length>(t<0?12:11)&&(n=t.toPrecision(6)),n}(t),s(function(e){for(var t=0;t!=e.length;++t)if(101==(32|e.charCodeAt(t)))return e.replace(n,".$1").replace(r,"E").replace("e","E").replace(i,"$10$2");return e}(o))}}();function A(e,t){switch(typeof e){case"string":return e;case"boolean":return e?"TRUE":"FALSE";case"number":return(0|e)===e?v(e):I(e);case"undefined":return"";case"object":if(null==e)return"";if(e instanceof Date)return O(14,b(e,t&&t.date1904),t)}throw new Error("unsupported value in General format: "+e)}function y(e,t,n,i){var s,o="",a=0,c=0,d=n.y,u=0;switch(e){case 98:d=n.y+543;case 121:switch(t.length){case 1:case 2:s=d%100,u=2;break;default:s=d%1e4,u=4}break;case 109:switch(t.length){case 1:case 2:s=n.m,u=t.length;break;case 3:return h[n.m-1][1];case 5:return h[n.m-1][0];default:return h[n.m-1][2]}break;case 100:switch(t.length){case 1:case 2:s=n.d,u=t.length;break;case 3:return l[n.q][0];default:return l[n.q][1]}break;case 104:switch(t.length){case 1:case 2:s=1+(n.H+11)%12,u=t.length;break;default:throw"bad hour format: "+t}break;case 72:switch(t.length){case 1:case 2:s=n.H,u=t.length;break;default:throw"bad hour format: "+t}break;case 77:switch(t.length){case 1:case 2:s=n.M,u=t.length;break;default:throw"bad minute format: "+t}break;case 115:if("s"!=t&&"ss"!=t&&".0"!=t&&".00"!=t&&".000"!=t)throw"bad second format: "+t;return 0!==n.u||"s"!=t&&"ss"!=t?(c=i>=2?3===i?1e3:100:1===i?10:1,(a=Math.round(c*(n.S+n.u)))>=60*c&&(a=0),"s"===t?0===a?"0":""+a/c:(o=r(a,2+i),"ss"===t?o.substr(0,2):"."+o.substr(2,t.length-1))):r(n.S,t.length);case 90:switch(t){case"[h]":case"[hh]":s=24*n.D+n.H;break;case"[m]":case"[mm]":s=60*(24*n.D+n.H)+n.M;break;case"[s]":case"[ss]":s=60*(60*(24*n.D+n.H)+n.M)+Math.round(n.S+n.u);break;default:throw"bad abstime format: "+t}u=3===t.length?1:2;break;case 101:s=d,u=1}return u>0?r(s,u):""}function w(e){if(e.length<=3)return e;for(var t=e.length%3,n=e.substr(0,t);t!=e.length;t+=3)n+=(n.length>0?",":"")+e.substr(t,3);return n}e._general_num=I,e._general=A;var S=function(){var e=/%/g,o=/# (\?+)( ?)\/( ?)(\d+)/,c=/^#*0*\.([0#]+)/,l=/\).*[0#]/,h=/\(###\) ###\\?-####/;function d(e){for(var t,n="",r=0;r!=e.length;++r)switch(t=e.charCodeAt(r)){case 35:break;case 63:n+=" ";break;case 48:n+="0";break;default:n+=String.fromCharCode(t)}return n}function u(e,t){var n=Math.pow(10,t);return""+Math.round(e*n)/n}function g(e,t){return t<(""+Math.round((e-Math.floor(e))*Math.pow(10,t))).length?0:Math.round((e-Math.floor(e))*Math.pow(10,t))}return function(p,m,C){return(0|C)===C?function a(u,g,p){if(40===u.charCodeAt(0)&&!g.match(l)){var m=g.replace(/\( */,"").replace(/ \)/,"").replace(/\)/,"");return p>=0?a("n",m,p):"("+a("n",m,-p)+")"}if(44===g.charCodeAt(g.length-1))return function(e,t,n){for(var r=t.length-1;44===t.charCodeAt(r-1);)--r;return S(e,t.substr(0,r),n/Math.pow(10,3*(t.length-r)))}(u,g,p);if(-1!==g.indexOf("%"))return function(t,r,i){var s=r.replace(e,""),o=r.length-s.length;return S(t,s,i*Math.pow(10,2*o))+n("%",o)}(u,g,p);if(-1!==g.indexOf("E"))return function e(t,n){var r,i=t.indexOf("E")-t.indexOf(".")-1;if(t.match(/^#+0.0E\+0$/)){if(0==n)return"0.0E+0";if(n<0)return"-"+e(t,-n);var s=t.indexOf(".");-1===s&&(s=t.indexOf("E"));var o=Math.floor(Math.log(n)*Math.LOG10E)%s;if(o<0&&(o+=s),!(r=(n/Math.pow(10,o)).toPrecision(i+1+(s+o)%s)).match(/[Ee]/)){var a=Math.floor(Math.log(n)*Math.LOG10E);-1===r.indexOf(".")?r=r.charAt(0)+"."+r.substr(1)+"E+"+(a-r.length+o):r+="E+"+(a-o),r=r.replace(/\+-/,"-")}r=r.replace(/^([+-]?)(\d*)\.(\d*)[Ee]/,(function(e,t,n,r){return t+n+r.substr(0,(s+o)%s)+"."+r.substr(o)+"E"}))}else r=n.toExponential(i);return t.match(/E\+00$/)&&r.match(/e[+-]\d$/)&&(r=r.substr(0,r.length-1)+"0"+r.charAt(r.length-1)),t.match(/E\-/)&&r.match(/e\+/)&&(r=r.replace(/e\+/,"e")),r.replace("e","E")}(g,p);if(36===g.charCodeAt(0))return"$"+a(u,g.substr(" "==g.charAt(1)?2:1),p);var C,b,v,I,A=Math.abs(p),y=p<0?"-":"";if(g.match(/^00+$/))return y+r(A,g.length);if(g.match(/^[#?]+$/))return C=""+p,0===p&&(C=""),C.length>g.length?C:d(g.substr(0,g.length-C.length))+C;if(b=g.match(o))return function(e,t,r){return r+(0===t?"":""+t)+n(" ",e[1].length+2+e[4].length)}(b,A,y);if(g.match(/^#+0+$/))return y+r(A,g.length-g.indexOf("0"));if(b=g.match(c))return C=(C=(""+p).replace(/^([^\.]+)$/,"$1."+d(b[1])).replace(/\.$/,"."+d(b[1]))).replace(/\.(\d*)$/,(function(e,t){return"."+t+n("0",d(b[1]).length-t.length)})),-1!==g.indexOf("0.")?C:C.replace(/^0\./,".");if(g=g.replace(/^#+([0.])/,"$1"),b=g.match(/^(0*)\.(#*)$/))return y+(""+A).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,b[1].length?"0.":".");if(b=g.match(/^#{1,3},##0(\.?)$/))return y+w(""+A);if(b=g.match(/^#,##0\.([#0]*0)$/))return p<0?"-"+a(u,g,-p):w(""+p)+"."+n("0",b[1].length);if(b=g.match(/^#,#*,#0/))return a(u,g.replace(/^#,#*,/,""),p);if(b=g.match(/^([0#]+)(\\?-([0#]+))+$/))return C=t(a(u,g.replace(/[\\-]/g,""),p)),v=0,t(t(g.replace(/\\/g,"")).replace(/[0#]/g,(function(e){return v=0?p("n",v,b):"("+p("n",v,-b)+")"}if(44===C.charCodeAt(C.length-1))return function(e,t,n){for(var r=t.length-1;44===t.charCodeAt(r-1);)--r;return S(e,t.substr(0,r),n/Math.pow(10,3*(t.length-r)))}(m,C,b);if(-1!==C.indexOf("%"))return function(t,r,i){var s=r.replace(e,""),o=r.length-s.length;return S(t,s,i*Math.pow(10,2*o))+n("%",o)}(m,C,b);if(-1!==C.indexOf("E"))return function e(t,n){var r,i=t.indexOf("E")-t.indexOf(".")-1;if(t.match(/^#+0.0E\+0$/)){if(0==n)return"0.0E+0";if(n<0)return"-"+e(t,-n);var s=t.indexOf(".");-1===s&&(s=t.indexOf("E"));var o=Math.floor(Math.log(n)*Math.LOG10E)%s;if(o<0&&(o+=s),-1===(r=(n/Math.pow(10,o)).toPrecision(i+1+(s+o)%s)).indexOf("e")){var a=Math.floor(Math.log(n)*Math.LOG10E);for(-1===r.indexOf(".")?r=r.charAt(0)+"."+r.substr(1)+"E+"+(a-r.length+o):r+="E+"+(a-o);"0."===r.substr(0,2);)r=(r=r.charAt(0)+r.substr(2,s)+"."+r.substr(2+s)).replace(/^0+([1-9])/,"$1").replace(/^0+\./,"0.");r=r.replace(/\+-/,"-")}r=r.replace(/^([+-]?)(\d*)\.(\d*)[Ee]/,(function(e,t,n,r){return t+n+r.substr(0,(s+o)%s)+"."+r.substr(o)+"E"}))}else r=n.toExponential(i);return t.match(/E\+00$/)&&r.match(/e[+-]\d$/)&&(r=r.substr(0,r.length-1)+"0"+r.charAt(r.length-1)),t.match(/E\-/)&&r.match(/e\+/)&&(r=r.replace(/e\+/,"e")),r.replace("e","E")}(C,b);if(36===C.charCodeAt(0))return"$"+p(m,C.substr(" "==C.charAt(1)?2:1),b);var I,A,y,x,E=Math.abs(b),k=b<0?"-":"";if(C.match(/^00+$/))return k+a(E,C.length);if(C.match(/^[#?]+$/))return"0"===(I=a(b,0))&&(I=""),I.length>C.length?I:d(C.substr(0,C.length-I.length))+I;if(A=C.match(o))return function(e,t,s){var o=parseInt(e[4],10),a=Math.round(t*o),c=Math.floor(a/o),l=a-c*o,h=o;return s+(0===c?"":""+c)+" "+(0===l?n(" ",e[1].length+1+e[4].length):i(l,e[1].length)+e[2]+"/"+e[3]+r(h,e[4].length))}(A,E,k);if(C.match(/^#+0+$/))return k+a(E,C.length-C.indexOf("0"));if(A=C.match(c))return I=u(b,A[1].length).replace(/^([^\.]+)$/,"$1."+d(A[1])).replace(/\.$/,"."+d(A[1])).replace(/\.(\d*)$/,(function(e,t){return"."+t+n("0",d(A[1]).length-t.length)})),-1!==C.indexOf("0.")?I:I.replace(/^0\./,".");if(C=C.replace(/^#+([0.])/,"$1"),A=C.match(/^(0*)\.(#*)$/))return k+u(E,A[2].length).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,A[1].length?"0.":".");if(A=C.match(/^#{1,3},##0(\.?)$/))return k+w(a(E,0));if(A=C.match(/^#,##0\.([#0]*0)$/))return b<0?"-"+p(m,C,-b):w(""+(Math.floor(b)+function(e,t){return t<(""+Math.round((e-Math.floor(e))*Math.pow(10,t))).length?1:0}(b,A[1].length)))+"."+r(g(b,A[1].length),A[1].length);if(A=C.match(/^#,#*,#0/))return p(m,C.replace(/^#,#*,/,""),b);if(A=C.match(/^([0#]+)(\\?-([0#]+))+$/))return I=t(p(m,C.replace(/[\\-]/g,""),b)),y=0,t(t(C.replace(/\\/g,"")).replace(/[0#]/g,(function(e){return y-2147483648?""+(e>=0?0|e:e-1|0):""+Math.floor(e)}(b)).replace(/^\d,\d{3}$/,"0$&").replace(/^\d*$/,(function(e){return"00,"+(e.length<3?r(0,3-e.length):"")+e}))+"."+r(y,A[1].length);switch(C){case"###,##0.00":return p(m,"#,##0.00",b);case"###,###":case"##,###":case"#,###":var B=w(a(E,0));return"0"!==B?k+B:"";case"###,###.00":return p(m,"###,##0.00",b).replace(/^0\./,".");case"#,###.00":return p(m,"#,##0.00",b).replace(/^0\./,".")}throw new Error("unsupported format |"+C+"|")}(p,m,C)}}();function x(e){for(var t=[],n=!1,r=0,i=0;r-1||"\\"==n&&"-"==e.charAt(t+1)&&"0#".indexOf(e.charAt(t+2))>-1););break;case"?":for(;e.charAt(++t)===n;);break;case"*":++t," "!=e.charAt(t)&&"*"!=e.charAt(t)||++t;break;case"(":case")":++t;break;case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":for(;t-1;);break;case" ":default:++t}return!1}function _(e,t,n,r){for(var i,s,o,a=[],l="",h=0,d="",u="t",f="H";h=12?"P":"A"),m.t="T",f="h",h+=3):"AM/PM"===e.substr(h,5).toUpperCase()?(null!=i&&(m.v=i.H>=12?"PM":"AM"),m.t="T",h+=5,f="h"):(m.t="t",++h),null==i&&"T"===m.t)return"";a[a.length]=m,u=d;break;case"[":for(l=d;"]"!==e.charAt(h++)&&h-1&&(l=(l.match(/\$([^-\[\]]*)/)||[])[1]||"$",k(e)||(a[a.length]={t:"t",v:l}));break;case".":if(null!=i){for(l=d;++h-1||"\\"==d&&"-"==e.charAt(h+1)&&h-1;)l+=d;a[a.length]={t:"n",v:l};break;case"?":for(l=d;e.charAt(++h)===d;)l+=d;a[a.length]={t:d,v:l},u=d;break;case"*":++h," "!=e.charAt(h)&&"*"!=e.charAt(h)||++h;break;case"(":case")":a[a.length]={t:1===r?"t":d,v:d},++h;break;case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":for(l=d;h-1;)l+=e.charAt(h);a[a.length]={t:"D",v:l};break;case" ":a[a.length]={t:d,v:d},++h;break;default:if(-1===",$-+/():!^&'~{}<>=\u20acacfijklopqrtuvwxzP".indexOf(d))throw new Error("unrecognized character "+d+" in "+e);a[a.length]={t:"t",v:d},++h}var C,b=0,v=0;for(h=a.length-1,u="t";h>=0;--h)switch(a[h].t){case"h":case"H":a[h].t=f,u="h",b<1&&(b=1);break;case"s":(C=a[h].v.match(/\.0+$/))&&(v=Math.max(v,C[0].length-1)),b<3&&(b=3);case"d":case"y":case"M":case"e":u=a[h].t;break;case"m":"s"===u&&(a[h].t="M",b<2&&(b=2));break;case"X":break;case"Z":b<1&&a[h].v.match(/[Hh]/)&&(b=1),b<2&&a[h].v.match(/[Mm]/)&&(b=2),b<3&&a[h].v.match(/[Ss]/)&&(b=3)}switch(b){case 0:break;case 1:i.u>=.5&&(i.u=0,++i.S),i.S>=60&&(i.S=0,++i.M),i.M>=60&&(i.M=0,++i.H);break;case 2:i.u>=.5&&(i.u=0,++i.S),i.S>=60&&(i.S=0,++i.M)}var I,w="";for(h=0;h0){40==w.charCodeAt(0)?(x=t<0&&45===w.charCodeAt(0)?-t:t,_=S("(",w,x)):(_=S("n",w,x=t<0&&r>1?-t:t),x<0&&a[0]&&"t"==a[0].t&&(_=_.substr(1),a[0].v="-"+a[0].v)),I=_.length-1;var R=a.length;for(h=0;h-1){R=h;break}var B=a.length;if(R===a.length&&-1===_.indexOf("E")){for(h=a.length-1;h>=0;--h)null!=a[h]&&-1!=="n?(".indexOf(a[h].t)&&(I>=a[h].v.length-1?a[h].v=_.substr(1+(I-=a[h].v.length),a[h].v.length):I<0?a[h].v="":(a[h].v=_.substr(0,I+1),I=-1),a[h].t="t",B=h);I>=0&&B=0;--h)if(null!=a[h]&&-1!=="n?(".indexOf(a[h].t)){for(s=a[h].v.indexOf(".")>-1&&h===R?a[h].v.indexOf(".")-1:a[h].v.length-1,T=a[h].v.substr(s+1);s>=0;--s)I>=0&&("0"===a[h].v.charAt(s)||"#"===a[h].v.charAt(s))&&(T=_.charAt(I--)+T);a[h].v=T,a[h].t="t",B=h}for(I>=0&&B-1&&h===R?a[h].v.indexOf(".")+1:0,T=a[h].v.substr(0,s);s-1&&(a[h].v=S(a[h].t,a[h].v,x=r>1&&t<0&&h>0&&"-"===a[h-1].v?-t:t),a[h].t="t");var O="";for(h=0;h!==a.length;++h)null!=a[h]&&(O+=a[h].v);return O}e.is_date=k,e._eval=_;var T=/\[[=<>]/,R=/\[(=|>[=]?|<[>=]?)(-?\d+(?:\.\d*)?)\]/;function B(e,t){if(null==t)return!1;var n=parseFloat(t[2]);switch(t[1]){case"=":if(e==n)return!0;break;case">":if(e>n)return!0;break;case"<":if(e":if(e!=n)return!0;break;case">=":if(e>=n)return!0;break;case"<=":if(e<=n)return!0}return!1}function O(e,t,n){null==n&&(n={});var r="";switch(typeof e){case"string":r="m/d/yy"==e&&n.dateNF?n.dateNF:e;break;case"number":r=14==e&&n.dateNF?n.dateNF:(null!=n.table?n.table:u)[e]}if(c(r,0))return A(t,n);t instanceof Date&&(t=b(t,n.date1904));var i=function(e,t){var n=x(e),r=n.length,i=n[r-1].indexOf("@");if(r<4&&i>-1&&--r,n.length>4)throw new Error("cannot find right format for |"+n.join("|")+"|");if("number"!=typeof t)return[4,4===n.length||i>-1?n[n.length-1]:"@"];switch(n.length){case 1:n=i>-1?["General","General","General",n[0]]:[n[0],n[0],n[0],"@"];break;case 2:n=i>-1?[n[0],n[0],n[0],n[1]]:[n[0],n[1],n[0],"@"];break;case 3:n=i>-1?[n[0],n[1],n[0],n[2]]:[n[0],n[1],n[2],"@"]}var s=t>0?n[0]:t<0?n[1]:n[2];if(-1===n[0].indexOf("[")&&-1===n[1].indexOf("["))return[r,s];if(null!=n[0].match(T)||null!=n[1].match(T)){var o=n[0].match(R),a=n[1].match(R);return B(t,o)?[r,n[0]]:B(t,a)?[r,n[1]]:[r,n[null!=o&&null!=a?2:1]]}return[r,s]}(r,t);if(c(i[1]))return A(t,n);if(!0===t)t="TRUE";else if(!1===t)t="FALSE";else if(""===t||null==t)return"";return _(i[1],t,n,i[0])}function L(e,t){if("number"!=typeof t){t=+t||-1;for(var n=0;n<392;++n)if(null!=u[n]){if(u[n]==e){t=n;break}}else t<0&&(t=n);t<0&&(t=391)}return u[t]=e,t}e.load=L,e._table=u,e.get_table=function(){return u},e.load_table=function(e){for(var t=0;392!=t;++t)void 0!==e[t]&&L(e[t],t)},e.init_table=d,e.format=O};R(T);var B,O={"General Number":"General","General Date":T._table[22],"Long Date":"dddd, mmmm dd, yyyy","Medium Date":T._table[15],"Short Date":T._table[14],"Long Time":T._table[19],"Medium Time":T._table[18],"Short Time":T._table[20],Currency:'"$"#,##0.00_);[Red]\\("$"#,##0.00\\)',Fixed:T._table[2],Standard:T._table[4],Percent:T._table[10],Scientific:T._table[11],"Yes/No":'"Yes";"Yes";"No";@',"True/False":'"True";"True";"False";@',"On/Off":'"Yes";"Yes";"No";@'},L={5:'"$"#,##0_);\\("$"#,##0\\)',6:'"$"#,##0_);[Red]\\("$"#,##0\\)',7:'"$"#,##0.00_);\\("$"#,##0.00\\)',8:'"$"#,##0.00_);[Red]\\("$"#,##0.00\\)',23:"General",24:"General",25:"General",26:"General",27:"m/d/yy",28:"m/d/yy",29:"m/d/yy",30:"m/d/yy",31:"m/d/yy",32:"h:mm:ss",33:"h:mm:ss",34:"h:mm:ss",35:"h:mm:ss",36:"m/d/yy",41:'_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)',42:'_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)',43:'_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)',44:'_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)',50:"m/d/yy",51:"m/d/yy",52:"m/d/yy",53:"m/d/yy",54:"m/d/yy",55:"m/d/yy",56:"m/d/yy",57:"m/d/yy",58:"m/d/yy",59:"0",60:"0.00",61:"#,##0",62:"#,##0.00",63:'"$"#,##0_);\\("$"#,##0\\)',64:'"$"#,##0_);[Red]\\("$"#,##0\\)',65:'"$"#,##0.00_);\\("$"#,##0.00\\)',66:'"$"#,##0.00_);[Red]\\("$"#,##0.00\\)',67:"0%",68:"0.00%",69:"# ?/?",70:"# ??/??",71:"m/d/yy",72:"m/d/yy",73:"d-mmm-yy",74:"d-mmm",75:"mmm-yy",76:"h:mm",77:"h:mm:ss",78:"m/d/yy h:mm",79:"mm:ss",80:"[h]:mm:ss",81:"mmss.0"},P=/[dD]+|[mM]+|[yYeE]+|[Hh]+|[Ss]+/g;!function(e){e.version="1.2.0";var t=function(){for(var e=0,t=new Array(256),n=0;256!=n;++n)t[n]=e=1&(e=1&(e=1&(e=1&(e=1&(e=1&(e=1&(e=1&(e=n)?-306674912^e>>>1:e>>>1)?-306674912^e>>>1:e>>>1)?-306674912^e>>>1:e>>>1)?-306674912^e>>>1:e>>>1)?-306674912^e>>>1:e>>>1)?-306674912^e>>>1:e>>>1)?-306674912^e>>>1:e>>>1)?-306674912^e>>>1:e>>>1;return"undefined"!=typeof Int32Array?new Int32Array(t):t}();e.table=t,e.bstr=function(e,n){for(var r=-1^n,i=e.length-1,s=0;s>>8^t[255&(r^e.charCodeAt(s++))])>>>8^t[255&(r^e.charCodeAt(s++))];return s===i&&(r=r>>>8^t[255&(r^e.charCodeAt(s))]),-1^r},e.buf=function(e,n){if(e.length>1e4)return function(e,n){for(var r=-1^n,i=e.length-7,s=0;s>>8^t[255&(r^e[s++])])>>>8^t[255&(r^e[s++])])>>>8^t[255&(r^e[s++])])>>>8^t[255&(r^e[s++])])>>>8^t[255&(r^e[s++])])>>>8^t[255&(r^e[s++])])>>>8^t[255&(r^e[s++])])>>>8^t[255&(r^e[s++])];for(;s>>8^t[255&(r^e[s++])];return-1^r}(e,n);for(var r=-1^n,i=e.length-3,s=0;s>>8^t[255&(r^e[s++])])>>>8^t[255&(r^e[s++])])>>>8^t[255&(r^e[s++])])>>>8^t[255&(r^e[s++])];for(;s>>8^t[255&(r^e[s++])];return-1^r},e.str=function(e,n){for(var r,i,s=-1^n,o=0,a=e.length;o>>8^t[255&(s^r)]:r<2048?s=(s=s>>>8^t[255&(s^(192|r>>6&31))])>>>8^t[255&(s^(128|63&r))]:r>=55296&&r<57344?(r=64+(1023&r),i=1023&e.charCodeAt(o++),s=(s=(s=(s=s>>>8^t[255&(s^(240|r>>8&7))])>>>8^t[255&(s^(128|r>>2&63))])>>>8^t[255&(s^(128|i>>6&15|(3&r)<<4))])>>>8^t[255&(s^(128|63&i))]):s=(s=(s=s>>>8^t[255&(s^(224|r>>12&15))])>>>8^t[255&(s^(128|r>>6&63))])>>>8^t[255&(s^(128|63&r))];return-1^s}}(B={});var F,N=function(){var e,t={};function r(e){if("/"==e.charAt(e.length-1))return-1===e.slice(0,-1).indexOf("/")?e:r(e.slice(0,-1));var t=e.lastIndexOf("/");return-1===t?e:e.slice(0,t+1)}function i(e){if("/"==e.charAt(e.length-1))return i(e.slice(0,-1));var t=e.lastIndexOf("/");return-1===t?e:e.slice(t+1)}function s(e,t){"string"==typeof t&&(t=new Date(t));var n=t.getHours();n=(n=n<<6|t.getMinutes())<<5|t.getSeconds()>>>1,e.write_shift(2,n);var r=t.getFullYear()-1980;r=(r=r<<4|t.getMonth()+1)<<5|t.getDate(),e.write_shift(2,r)}function o(e){Bt(e,0);for(var t={},n=0;e.l<=e.length-4;){var r=e.read_shift(2),i=e.read_shift(2),s=e.l+i,o={};switch(r){case 21589:1&(n=e.read_shift(1))&&(o.mtime=e.read_shift(4)),i>5&&(2&n&&(o.atime=e.read_shift(4)),4&n&&(o.ctime=e.read_shift(4))),o.mtime&&(o.mt=new Date(1e3*o.mtime))}e.l=s,t[r]=o}return t}function a(){return e||(e=n(3))}function c(e,t){if(80==e[0]&&75==e[1])return he(e,t);if(e.length<512)throw new Error("CFB file size "+e.length+" < 512");var n,r,i,s,o,a,c=512,u=[],f=e.slice(0,512);Bt(f,0);var g=function(e){if(80==e[e.l]&&75==e[e.l+1])return[0,0];e.chk(w,"Header Signature: "),e.l+=16;var t=e.read_shift(2,"u");return[e.read_shift(2,"u"),t]}(f);switch(n=g[0]){case 3:c=512;break;case 4:c=4096;break;case 0:if(0==g[1])return he(e,t);default:throw new Error("Major Version: Expected 3 or 4 saw "+n)}512!==c&&Bt(f=e.slice(0,c),28);var p=e.slice(0,c);!function(e,t){var n;switch(e.l+=2,n=e.read_shift(2)){case 9:if(3!=t)throw new Error("Sector Shift: Expected 9 saw "+n);break;case 12:if(4!=t)throw new Error("Sector Shift: Expected 12 saw "+n);break;default:throw new Error("Sector Shift: Expected 9 or 12 saw "+n)}e.chk("0600","Mini Sector Shift: "),e.chk("000000000000","Reserved: ")}(f,n);var m=f.read_shift(4,"i");if(3===n&&0!==m)throw new Error("# Directory Sectors: Expected 0 saw "+m);f.l+=4,s=f.read_shift(4,"i"),f.l+=4,f.chk("00100000","Mini Stream Cutoff Size: "),o=f.read_shift(4,"i"),r=f.read_shift(4,"i"),a=f.read_shift(4,"i"),i=f.read_shift(4,"i");for(var C=-1,b=0;b<109&&!((C=f.read_shift(4,"i"))<0);++b)u[b]=C;var I=function(e,t){for(var n=Math.ceil(e.length/t)-1,r=[],i=1;i>>2)-1;if(!a)return;for(var l=0;l=i&&(u-=i),!o[u]){for(c=[],d=u;d>=0;){o[d]=!0,a[a.length]=d,c.push(e[d]);var g=n[Math.floor(4*d/r)];if(r<4+(f=4*d&l))throw new Error("FAT boundary crossed: "+d+" 4 "+r);if(!e[g])break;d=xt(e[g],f)}s[u]={nodes:a,data:nt([c])}}return s}(I,s,u,c);A[s].name="!Directory",r>0&&o!==v&&(A[o].name="!MiniFAT"),A[u[0]].name="!FAT",A.fat_addrs=u,A.ssz=c;var y=[],S=[],x=[];!function(e,t,n,r,i,s,o,a){for(var c,u=0,f=r.length?2:0,g=t[e].data,p=0,m=0;p0&&u!==v&&(t[u].name="!StreamData")):b.size>=4096?(b.storage="fat",void 0===t[b.start]&&(t[b.start]=h(n,b.start,t.fat_addrs,t.ssz)),t[b.start].name=b.name,b.content=t[b.start].data.slice(0,b.size)):(b.storage="minifat",b.size<0?b.size=0:u!==v&&b.start!==v&&t[u]&&(b.content=l(b,t[u].data,(t[a]||{}).data))),b.content&&Bt(b.content,0),s[c]=b,o.push(b)}}(s,A,I,y,r,{},S,o),function(e,t,n){for(var r=0,i=0,s=0,o=0,a=0,c=n.length,l=[],h=[];r0&&s>=0;)i.push(t.slice(s*b,s*b+b)),r-=b,s=xt(n,4*s);return 0===i.length?Lt(0):E(i).slice(0,e.size)}function h(e,t,n,r,i){var s=[],o=[];i||(i=[]);var a=r-1,c=0,l=0;for(c=t;c>=0;){i[c]=!0,s[s.length]=c,o.push(e[c]);var h=n[Math.floor(4*c/r)];if(r<4+(l=4*c&a))throw new Error("FAT boundary crossed: "+c+" 4 "+r);if(!e[h])break;c=xt(e[h],l)}return{nodes:s,data:nt([o])}}function d(e,t){return new Date(1e3*(St(e,t+4)/1e7*Math.pow(2,32)+St(e,t)/1e7-11644473600))}function u(e,t){var n=t||{},r=n.root||"Root Entry";if(e.FullPaths||(e.FullPaths=[]),e.FileIndex||(e.FileIndex=[]),e.FullPaths.length!==e.FileIndex.length)throw new Error("inconsistent CFB structure");0===e.FullPaths.length&&(e.FullPaths[0]=r+"/",e.FileIndex[0]={name:r,type:5}),n.CLSID&&(e.FileIndex[0].clsid=n.CLSID),function(e){var t="\x01Sh33tJ5";if(!N.find(e,"/"+t)){var n=Lt(4);n[0]=55,n[1]=n[3]=50,n[2]=54,e.FileIndex.push({name:t,type:2,content:n,size:4,L:69,R:69,C:69}),e.FullPaths.push(e.FullPaths[0]+t),f(e)}}(e)}function f(e,t){u(e);for(var n=!1,s=!1,o=e.FullPaths.length-1;o>=0;--o){var a=e.FileIndex[o];switch(a.type){case 0:s?n=!0:(e.FileIndex.pop(),e.FullPaths.pop());break;case 1:case 2:case 5:s=!0,isNaN(a.R*a.L*a.C)&&(n=!0),a.R>-1&&a.L>-1&&a.R==a.L&&(n=!0);break;default:n=!0}}if(n||t){var c=new Date(1987,1,19),l=0,h=[];for(o=0;o1?1:-1,f.size=0,f.type=5;else if("/"==g.slice(-1)){for(l=o+1;l=h.length?-1:l,l=o+1;l=h.length?-1:l,f.type=1}else r(e.FullPaths[o+1]||"")==r(g)&&(f.R=o+1),f.type=2}}}function g(e,t){var n=t||{};if(f(e),"zip"==n.fileType)return function(e,t){var n=t||{},r=[],i=[],o=Lt(1),a=n.compression?8:0,c=0,l=0,h=0,d=0,u=e.FullPaths[0],f=u,g=e.FileIndex[0],p=[],m=0;for(c=1;c0&&(s<4096?t+=s+63>>6:n+=s+511>>9)}}for(var o=e.FullPaths.length+3>>2,a=t+127>>7,c=(t+7>>3)+n+o+a,l=c+127>>7,h=l<=109?0:Math.ceil((l-109)/127);c+l+h+127>>7>l;)h=++l<=109?0:Math.ceil((l-109)/127);var d=[1,h,l,a,o,n,t,0];return e.FileIndex[0].size=t<<6,d[7]=(e.FileIndex[0].start=d[0]+d[1]+d[2]+d[3]+d[4]+d[5])+(d[6]+7>>3),d}(e),i=Lt(r[7]<<9),o=0,a=0;for(o=0;o<8;++o)i.write_shift(1,S[o]);for(o=0;o<8;++o)i.write_shift(2,0);for(i.write_shift(2,62),i.write_shift(2,3),i.write_shift(2,65534),i.write_shift(2,9),i.write_shift(2,6),o=0;o<3;++o)i.write_shift(2,0);for(i.write_shift(4,0),i.write_shift(4,r[2]),i.write_shift(4,r[0]+r[1]+r[2]+r[3]-1),i.write_shift(4,0),i.write_shift(4,4096),i.write_shift(4,r[3]?r[0]+r[1]+r[2]-1:v),i.write_shift(4,r[3]),i.write_shift(-4,r[1]?r[0]-1:v),i.write_shift(4,r[1]),o=0;o<109;++o)i.write_shift(-4,o>9)));for(c(r[6]+7>>3);511&i.l;)i.write_shift(-4,T.ENDOFCHAIN);for(a=o=0,l=0;l=4096||(d.start=a,c(h+63>>6)));for(;511&i.l;)i.write_shift(-4,T.ENDOFCHAIN);for(o=0;o=4096){for(i.l=d.start+1<<9,l=0;l0&&d.size<4096){for(l=0;l>16|P>>8|P);function G(e,t){var n=W[255&e];return t<=8?n>>>8-t:(n=n<<8|W[e>>8&255],t<=16?n>>>16-t:(n=n<<8|W[e>>16&255])>>>24-t)}function V(e,t){var n=7&t,r=t>>>3;return(e[r]|(n<=6?0:e[r+1]<<8))>>>n&3}function j(e,t){var n=7&t,r=t>>>3;return(e[r]|(n<=5?0:e[r+1]<<8))>>>n&7}function Z(e,t){var n=7&t,r=t>>>3;return(e[r]|(n<=3?0:e[r+1]<<8))>>>n&31}function z(e,t){var n=7&t,r=t>>>3;return(e[r]|(n<=1?0:e[r+1]<<8))>>>n&127}function K(e,t,n){var r=7&t,i=t>>>3,s=(1<>>r;return n<8-r?o&s:(o|=e[i+1]<<8-r,n<16-r?o&s:(o|=e[i+2]<<16-r,n<24-r?o&s:(o|=e[i+3]<<24-r)&s))}function X(e,t){var n=e.length,r=2*n>t?2*n:t+5,i=0;if(n>=t)return e;if(C){var s=A(r);if(e.copy)e.copy(s);else for(;i0;)t[t.l++]=e[n++]}return t.l},function(e){var t=Lt(50+Math.floor(1.1*e.length)),n=Y(e,t);return t.slice(0,n)});function Q(e,t,n){var r=1,i=0,s=0,o=0,a=0,c=e.length,l=$?new Uint16Array(32):U(32);for(s=0;s<32;++s)l[s]=0;for(s=c;s>r-d,o=(1<=0;--o)t[a|o<>>3;return(e[r]|(n<=4?0:e[r+1]<<8))>>>n&15}(e,t+=5)+4;t+=4;for(var s=0,o=$?new Uint8Array(19):U(19),a=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],c=1,l=$?new Uint8Array(8):U(8),h=$?new Uint8Array(8):U(8),d=o.length,u=0;u>8-g;for(var p=(1<<7-g)-1;p>=0;--p)ie[f|p<>>=3){case 16:for(s=3+V(e,t),t+=2,f=m[m.length-1];s-- >0;)m.push(f);break;case 17:for(s=3+j(e,t),t+=3;s-- >0;)m.push(0);break;case 18:for(s=11+z(e,t),t+=7;s-- >0;)m.push(0);break;default:m.push(f),c>>0,a=0,c=0;0==(1&r);)if(r=j(e,n),n+=3,r>>>1!=0)for(r>>>1==1?(a=9,c=5):(n=ae(e,n),a=se,c=oe),!t&&o>>1==1?q[l]:ne[l];if(n+=15&h,0==((h>>>=4)>>>8&255))i[s++]=h;else{if(256==h)break;var d=(h-=257)<8?0:h-4>>2;d>5&&(d=0);var u=s+M[h];d>0&&(u+=K(e,n,d),n+=d),l=K(e,n,c),n+=15&(h=r>>>1==1?ee[l]:re[l]);var f=(h>>>=4)<4?0:h-2>>1,g=D[h];for(f>0&&(g+=K(e,n,f),n+=f),!t&&o>>3]|e[1+(n>>>3)]<<8;if(n+=32,!t&&o>>3,(n>>>3)+p),s+=p,n+=8*p;else for(;p-- >0;)i[s++]=e[n>>>3],n+=8}return[t?i:i.slice(0,s),n+7>>>3]}(e.slice(e.l||0),t);return e.l+=n[1],n[0]}function le(e,t){if(!e)throw new Error(t);"undefined"!=typeof console&&console.error(t)}function he(e,t){var n=e;Bt(n,0);var r={FileIndex:[],FullPaths:[]};u(r,{root:t.root});for(var i=n.length-4;(80!=n[i]||75!=n[i+1]||5!=n[i+2]||6!=n[i+3])&&i>=0;)--i;n.l=i+4,n.l+=4;var s=n.read_shift(2);n.l+=6;var a=n.read_shift(4);for(n.l=a,i=0;i>>=5);n>>>=4,r.setMilliseconds(0),r.setFullYear(n+1980),r.setMonth(s-1),r.setDate(i);var o=31&t,a=63&(t>>>=5);return r.setHours(t>>>=6),r.setMinutes(a),r.setSeconds(o<<1),r}(e);if(8257&s)throw new Error("Unsupported ZIP encryption");for(var l=e.read_shift(4),h=e.read_shift(4),d=e.read_shift(4),u=e.read_shift(2),f=e.read_shift(2),g="",m=0;m3&&(r=!0),i[s].slice(i[s].length-1)){case"Y":throw new Error("Unsupported ISO Duration Field: "+i[s].slice(i[s].length-1));case"D":n*=24;case"H":n*=60;case"M":if(!r)throw new Error("Unsupported ISO Duration Field: M");n*=60}t+=n*parseInt(i[s],10)}return t}var U=new Date("2017-02-19T19:06:09.000Z");isNaN(U.getFullYear())&&(U=new Date("2/19/17"));var Y=2017==U.getFullYear();function J(e,t){var n=new Date(e);if(Y)return t>0?n.setTime(n.getTime()+60*n.getTimezoneOffset()*1e3):t<0&&n.setTime(n.getTime()-60*n.getTimezoneOffset()*1e3),n;if(e instanceof Date)return e;if(1917==U.getFullYear()&&!isNaN(n.getFullYear())){var r=n.getFullYear();return e.indexOf(""+r)>-1||n.setFullYear(n.getFullYear()+100),n}var i=e.match(/\d+/g)||["2017","2","19","0","0","0"],s=new Date(+i[0],+i[1]-1,+i[2],+i[3]||0,+i[4]||0,+i[5]||0);return e.indexOf("Z")>-1&&(s=new Date(s.getTime()-60*s.getTimezoneOffset()*1e3)),s}function Q(e){for(var t="",n=0;n!=e.length;++n)t+=String.fromCharCode(e[n]);return t}function q(e){if("undefined"!=typeof JSON&&!Array.isArray(e))return JSON.parse(JSON.stringify(e));if("object"!=typeof e||null==e)return e;if(e instanceof Date)return new Date(e.getTime());var t={};for(var n in e)e.hasOwnProperty(n)&&(t[n]=q(e[n]));return t}function ee(e,t){for(var n="";n.length8099?n:(i>0||s>1)&&101!=r||e.toLowerCase().match(/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/)?t:e.match(/[^-0-9:,\/\\]/)?n:t}var re,ie=5=="abacaba".split(/(:?b)/i).length;function se(e){return e?e.data?u(e.data):e.asNodeBuffer&&C?u(e.asNodeBuffer().toString("binary")):e.asBinary?u(e.asBinary()):e._data&&e._data.getContent?u(Q(Array.prototype.slice.call(e._data.getContent(),0))):null:null}function oe(e,t){for(var n=W(e.files),r=t.toLowerCase(),i=r.replace(/\//g,"\\"),s=0;s\/]+)\s*=\s*((?:")([^"]*)(?:")|(?:')([^']*)(?:')|([^'">\s]+))/g,fe=/<[\/\?]?[a-zA-Z0-9:]+(?:\s+[^"\s?>\/]+\s*=\s*(?:"[^"]*"|'[^']*'|[^'">\s=]+))*\s?[\/\?]?>/g;de.match(fe)||(fe=/<[^>]*>/g);var ge=/<\w*:/,pe=/<(\/?)\w+:/;function me(e,t){for(var n={},r=0,i=0;r!==e.length&&32!==(i=e.charCodeAt(r))&&10!==i&&13!==i;++r);if(t||(n[0]=e.slice(0,r)),r===e.length)return n;var s=e.match(ue),o=0,a="",c=0,l="",h="",d=1;if(s)for(c=0;c!=s.length;++c){for(h=s[c],i=0;i!=h.length&&61!==h.charCodeAt(i);++i);for(l=h.slice(0,i).trim();32==h.charCodeAt(i+1);)++i;for(d=34==(r=h.charCodeAt(i+1))||39==r?1:0,a=h.slice(i+1+d,h.length-d),o=0;o!=l.length&&58!==l.charCodeAt(o);++o);if(o===l.length)l.indexOf("_")>0&&(l=l.slice(0,l.indexOf("_"))),n[l]=a,n[l.toLowerCase()]=a;else{var u=(5===o&&"xmlns"===l.slice(0,5)?"xmlns":"")+l.slice(o+1);if(n[u]&&"ext"==l.slice(o-3,o))continue;n[u]=a,n[u.toLowerCase()]=a}}return n}function Ce(e){return e.replace(pe,"<$1")}var be,ve,Ie={""":'"',"'":"'",">":">","<":"<","&":"&"},Ae=G(Ie),ye=(be=/&(?:quot|apos|gt|lt|amp|#x?([\da-fA-F]+));/g,ve=/_x([\da-fA-F]{4})_/g,function e(t){var n=t+"",r=n.indexOf("-1?16:10))||e})).replace(ve,(function(e,t){return String.fromCharCode(parseInt(t,16))}));var i=n.indexOf("]]>");return e(n.slice(0,r))+n.slice(r+9,i)+e(n.slice(i+3))}),we=/[&<>'"]/g,Se=/[\u0000-\u0008\u000b-\u001f]/g;function xe(e){return(e+"").replace(we,(function(e){return Ae[e]})).replace(Se,(function(e){return"_x"+("000"+e.charCodeAt(0).toString(16)).slice(-4)+"_"}))}function Ee(e){return xe(e).replace(/ /g,"_x0020_")}var ke=/[\u0000-\u001f]/g;function _e(e){return(e+"").replace(we,(function(e){return Ae[e]})).replace(/\n/g," ").replace(ke,(function(e){return""+("000"+e.charCodeAt(0).toString(16)).slice(-4)+";"}))}var Te=function(){var e=/(\d+);/g;function t(e,t){return String.fromCharCode(parseInt(t,10))}return function(n){return n.replace(e,t)}}();function Re(e){switch(e){case 1:case!0:case"1":case"true":case"TRUE":return!0;default:return!1}}var Be=function(e){for(var t="",n=0,r=0,i=0,s=0,o=0,a=0;n191&&r<224?(o=(31&r)<<6,o|=63&i,t+=String.fromCharCode(o)):(s=e.charCodeAt(n++),r<240?t+=String.fromCharCode((15&r)<<12|(63&i)<<6|63&s):(a=((7&r)<<18|(63&i)<<12|(63&s)<<6|63&(o=e.charCodeAt(n++)))-65536,t+=String.fromCharCode(55296+(a>>>10&1023)),t+=String.fromCharCode(56320+(1023&a)))));return t},Oe=function(e){for(var t=[],n=0,r=0,i=0;n>6))),t.push(String.fromCharCode(128+(63&r)));break;case r>=55296&&r<57344:r-=55296,i=e.charCodeAt(n++)-56320+(r<<10),t.push(String.fromCharCode(240+(i>>18&7))),t.push(String.fromCharCode(144+(i>>12&63))),t.push(String.fromCharCode(128+(i>>6&63))),t.push(String.fromCharCode(128+(63&i)));break;default:t.push(String.fromCharCode(224+(r>>12))),t.push(String.fromCharCode(128+(r>>6&63))),t.push(String.fromCharCode(128+(63&r)))}return t.join("")};if(C){var Le=function(e){var t,n,r,i=Buffer.alloc(2*e.length),s=1,o=0,a=0;for(n=0;n>>10&1023),t=56320+(1023&t)),0!==a&&(i[o++]=255&a,i[o++]=a>>>8,a=0),i[o++]=t%256,i[o++]=t>>>8;return i.slice(0,o).toString("ucs2")},Pe="foo bar baz\xe2\x98\x83\xf0\x9f\x8d\xa3";Be(Pe)==Le(Pe)&&(Be=Le);var Fe=function(e){return b(e,"binary").toString("utf8")};Be(Pe)==Fe(Pe)&&(Be=Fe),Oe=function(e){return b(e,"utf8").toString("binary")}}var Ne,Me,De,$e=(Ne={},function(e,t){var n=e+"|"+(t||"");return Ne[n]?Ne[n]:Ne[n]=new RegExp("<(?:\\w+:)?"+e+'(?: xml:space="preserve")?(?:[^>]*)>([\\s\\S]*?)(?:\\w+:)?'+e+">",t||"")}),We=(Me=[["nbsp"," "],["middot","\xb7"],["quot",'"'],["apos","'"],["gt",">"],["lt","<"],["amp","&"]].map((function(e){return[new RegExp("&"+e[0]+";","g"),e[1]]})),function(e){for(var t=e.replace(/^[\t\n\r ]+/,"").replace(/[\t\n\r ]+$/,"").replace(/[\t\n\r ]+/g," ").replace(/<\s*[bB][rR]\s*\/?>/g,"\n").replace(/<[^>]*>/g,""),n=0;n([\\s\\S]*?)(?:vt:)?"+e+">","g")}),Ge=/<\/?(?:vt:)?variant>/g,Ve=/<(?:vt:)([^>]*)>([\s\S]*);function je(e,t){var n=me(e),r=e.match(He(n.baseType))||[],i=[];if(r.length!=n.size){if(t.WTF)throw new Error("unexpected vector length "+r.length+" != "+n.size);return i}return r.forEach((function(e){var t=e.replace(Ge,"").match(Ve);t&&i.push({v:Be(t[2]),t:t[1]})})),i}var Ze=/(^\s|\s$|\n)/;function ze(e,t){return"<"+e+(t.match(Ze)?' xml:space="preserve"':"")+">"+t+""+e+">"}function Ke(e){return W(e).map((function(t){return" "+t+'="'+e[t]+'"'})).join("")}function Xe(e,t,n){return"<"+e+(null!=n?Ke(n):"")+(null!=t?(t.match(Ze)?' xml:space="preserve"':"")+">"+t+""+e:"/")+">"}function Ue(e,t){try{return e.toISOString().replace(/\.\d*/,"")}catch(El){if(t)throw El}return""}var Ye,Je,Qe={dc:"http://purl.org/dc/elements/1.1/",dcterms:"http://purl.org/dc/terms/",dcmitype:"http://purl.org/dc/dcmitype/",mx:"http://schemas.microsoft.com/office/mac/excel/2008/main",r:"http://schemas.openxmlformats.org/officeDocument/2006/relationships",sjs:"http://schemas.openxmlformats.org/package/2006/sheetjs/core-properties",vt:"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes",xsi:"http://www.w3.org/2001/XMLSchema-instance",xsd:"http://www.w3.org/2001/XMLSchema",main:["http://schemas.openxmlformats.org/spreadsheetml/2006/main","http://purl.oclc.org/ooxml/spreadsheetml/main","http://schemas.microsoft.com/office/excel/2006/main","http://schemas.microsoft.com/office/excel/2006/2"]},qe="urn:schemas-microsoft-com:office:office",et="urn:schemas-microsoft-com:office:excel",tt="urn:schemas-microsoft-com:office:spreadsheet",nt=function(e){for(var t=[],n=0;n0?ct(e,t+4,t+4+n-1):""},dt=ht,ut=function(e,t){var n=St(e,t);return n>0?ct(e,t+4,t+4+n-1):""},ft=ut,gt=function(e,t){var n=2*St(e,t);return n>0?ct(e,t+4,t+4+n-1):""},pt=gt;Ye=Je=function(e,t){var n=St(e,t);return n>0?it(e,t+4,t+4+n):""};var mt,Ct,bt=function(e,t){var n=St(e,t);return n>0?ct(e,t+4,t+4+n):""},vt=bt;mt=Ct=function(e,t){return function(e,t){for(var n=1-2*(e[t+7]>>>7),r=((127&e[t+7])<<4)+(e[t+6]>>>4&15),i=15&e[t+6],s=5;s>=0;--s)i=256*i+e[t+s];return 2047==r?0==i?n*(1/0):NaN:(0==r?r=-1022:(r-=1023,i+=Math.pow(2,52)),n*Math.pow(2,r-52)*i)}(e,t)};var It=function(e){return Array.isArray(e)};C&&(it=function(e,t,n){return Buffer.isBuffer(e)?e.toString("utf16le",t,n).replace(k,""):st(e,t,n)},ot=function(e,t,n){return Buffer.isBuffer(e)?e.toString("hex",t,t+n):at(e,t,n)},ht=function(e,t){if(!Buffer.isBuffer(e))return dt(e,t);var n=e.readUInt32LE(t);return n>0?e.toString("utf8",t+4,t+4+n-1):""},ut=function(e,t){if(!Buffer.isBuffer(e))return ft(e,t);var n=e.readUInt32LE(t);return n>0?e.toString("utf8",t+4,t+4+n-1):""},gt=function(e,t){if(!Buffer.isBuffer(e))return pt(e,t);var n=2*e.readUInt32LE(t);return e.toString("utf16le",t+4,t+4+n-1)},Ye=function(e,t){if(!Buffer.isBuffer(e))return Je(e,t);var n=e.readUInt32LE(t);return e.toString("utf16le",t+4,t+4+n)},bt=function(e,t){if(!Buffer.isBuffer(e))return vt(e,t);var n=e.readUInt32LE(t);return e.toString("utf8",t+4,t+4+n)},ct=function(e,t,n){return Buffer.isBuffer(e)?e.toString("utf8",t,n):lt(e,t,n)},nt=function(e){return e[0].length>0&&Buffer.isBuffer(e[0][0])?Buffer.concat(e[0]):rt(e)},E=function(e){return Buffer.isBuffer(e[0])?Buffer.concat(e):[].concat.apply([],e)},mt=function(e,t){return Buffer.isBuffer(e)?e.readDoubleLE(t):Ct(e,t)},It=function(e){return Buffer.isBuffer(e)||Array.isArray(e)}),"undefined"!=typeof cptable&&(it=function(e,t,n){return cptable.utils.decode(1200,e.slice(t,n)).replace(k,"")},ct=function(e,t,n){return cptable.utils.decode(65001,e.slice(t,n))},ht=function(e,t){var n=St(e,t);return n>0?cptable.utils.decode(i,e.slice(t+4,t+4+n-1)):""},ut=function(e,t){var n=St(e,t);return n>0?cptable.utils.decode(r,e.slice(t+4,t+4+n-1)):""},gt=function(e,t){var n=2*St(e,t);return n>0?cptable.utils.decode(1200,e.slice(t+4,t+4+n-1)):""},Ye=function(e,t){var n=St(e,t);return n>0?cptable.utils.decode(1200,e.slice(t+4,t+4+n)):""},bt=function(e,t){var n=St(e,t);return n>0?cptable.utils.decode(65001,e.slice(t+4,t+4+n)):""});var At=function(e,t){return e[t]},yt=function(e,t){return 256*e[t+1]+e[t]},wt=function(e,t){var n=256*e[t+1]+e[t];return n<32768?n:-1*(65535-n+1)},St=function(e,t){return e[t+3]*(1<<24)+(e[t+2]<<16)+(e[t+1]<<8)+e[t]},xt=function(e,t){return e[t+3]<<24|e[t+2]<<16|e[t+1]<<8|e[t]},Et=function(e,t){return e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3]};function kt(e,t){var n,i,s,o,a,c,l="",h=[];switch(t){case"dbcs":if(c=this.l,C&&Buffer.isBuffer(this))l=this.slice(this.l,this.l+2*e).toString("utf16le");else for(a=0;a0?xt:Et)(this,this.l),this.l+=4,n):(i=St(this,this.l),this.l+=4,i);case 8:case-8:if("f"===t)return i=8==e?mt(this,this.l):mt([this[this.l+7],this[this.l+6],this[this.l+5],this[this.l+4],this[this.l+3],this[this.l+2],this[this.l+1],this[this.l+0]],0),this.l+=8,i;e=8;case 16:l=ot(this,this.l,e)}}return this.l+=e,l}var _t=function(e,t,n){e[n]=255&t,e[n+1]=t>>>8&255};function Tt(e,t,n){var r=0,i=0;if("dbcs"===n){for(i=0;i!=t.length;++i)_t(this,t.charCodeAt(i),this.l+2*i);r=2*t.length}else if("sbcs"===n){for(t=t.replace(/[^\x00-\x7F]/g,"_"),i=0;i!=t.length;++i)this[this.l+i]=255&t.charCodeAt(i);r=t.length}else{if("hex"===n){for(;i>8}for(;this.l>>=8);break;case 3:r=3,this[this.l]=255&t,this[this.l+1]=255&(t>>>=8),this[this.l+2]=255&(t>>>=8);break;case 4:r=4,function(e,t,n){e[n]=255&t,e[n+1]=t>>>8&255,e[n+2]=t>>>16&255,e[n+3]=t>>>24&255}(this,t,this.l);break;case 8:if(r=8,"f"===n){!function(e,t,n){var r=(t<0||1/t==-1/0?1:0)<<7,i=0,s=0,o=r?-t:t;isFinite(o)?0==o?i=s=0:(i=Math.floor(Math.log(o)/Math.LN2),s=o*Math.pow(2,52-i),i<=-1023&&(!isFinite(s)||s>4|r}(this,t,this.l);break}case 16:break;case-4:r=4,function(e,t,n){e[n]=255&t,e[n+1]=t>>8&255,e[n+2]=t>>16&255,e[n+3]=t>>24&255}(this,t,this.l)}}return this.l+=r,this}function Rt(e,t){var n=ot(this,this.l,e.length>>1);if(n!==e)throw new Error(t+"Expected "+e+" saw "+n);this.l+=e.length>>1}function Bt(e,t){e.l=t,e.read_shift=kt,e.chk=Rt,e.write_shift=Tt}function Ot(e,t){e.l+=t}function Lt(e){var t=I(e);return Bt(t,0),t}function Pt(e,t,n){if(e){var r,i,s;Bt(e,e.l||0);for(var o=e.length,a=0,c=0;e.lr.l&&((r=r.slice(0,r.l)).l=r.length),r.length>0&&e.push(r),r=null)},s=function(e){return r&&e=128?1:0)+1,r>=128&&++i,r>=16384&&++i,r>=2097152&&++i;var o=e.next(i);s<=127?o.write_shift(1,s):(o.write_shift(1,128+(127&s)),o.write_shift(1,s>>7));for(var a=0;4!=a;++a){if(!(r>=128)){o.write_shift(1,r);break}o.write_shift(1,128+(127&r)),r>>=7}r>0&&It(n)&&e.push(n)}}function Mt(e,t,n){var r=q(e);if(t.s?(r.cRel&&(r.c+=t.s.c),r.rRel&&(r.r+=t.s.r)):(r.cRel&&(r.c+=t.c),r.rRel&&(r.r+=t.r)),!n||n.biff<12){for(;r.c>=256;)r.c-=256;for(;r.r>=65536;)r.r-=65536}return r}function Dt(e,t,n){var r=q(e);return r.s=Mt(r.s,t.s,n),r.e=Mt(r.e,t.s,n),r}function $t(e,t){e.cRel&&e.c<0&&((e=q(e)).c+=t>8?16384:256),e.rRel&&e.r<0&&((e=q(e)).r+=t>8?1048576:t>5?65536:16384);var n=Kt(e);return 0===e.cRel&&(n=n.replace(/^([A-Z])/,"$$$1")),0===e.rRel&&(n=n.replace(/([A-Z]|^)(\d+)$/,"$1$$$2")),n}function Wt(e,t){return 0!=e.s.r||e.s.rRel||e.e.r!=(t.biff>=12?1048575:t.biff>=8?65536:16384)||e.e.rRel?0!=e.s.c||e.s.cRel||e.e.c!=(t.biff>=12?65535:255)||e.e.cRel?$t(e.s,t.biff)+":"+$t(e.e,t.biff):(e.s.rRel?"":"$")+Gt(e.s.r)+":"+(e.e.rRel?"":"$")+Gt(e.e.r):(e.s.cRel?"":"$")+jt(e.s.c)+":"+(e.e.cRel?"":"$")+jt(e.e.c)}function Ht(e){return parseInt(e.replace(/\$(\d+)$/,"$1"),10)-1}function Gt(e){return""+(e+1)}function Vt(e){for(var t=e.replace(/^\$([A-Z])/,"$1"),n=0,r=0;r!==t.length;++r)n=26*n+t.charCodeAt(r)-64;return n-1}function jt(e){var t="";for(++e;e;e=Math.floor((e-1)/26))t=String.fromCharCode((e-1)%26+65)+t;return t}function Zt(e){return e.replace(/(\$?[A-Z]*)(\$?\d*)/,"$1,$2").split(",")}function zt(e){var t=Zt(e);return{c:Vt(t[0]),r:Ht(t[1])}}function Kt(e){return jt(e.c)+Gt(e.r)}function Xt(e){var t=e.split(":").map(zt);return{s:t[0],e:t[t.length-1]}}function Ut(e,t){return void 0===t||"number"==typeof t?Ut(e.s,e.e):("string"!=typeof e&&(e=Kt(e)),"string"!=typeof t&&(t=Kt(t)),e==t?e:e+":"+t)}function Yt(e){var t={s:{c:0,r:0},e:{c:0,r:0}},n=0,r=0,i=0,s=e.length;for(n=0;r26);++r)n=26*n+i;for(t.s.c=--n,n=0;r9);++r)n=10*n+i;if(t.s.r=--n,r===s||58===e.charCodeAt(++r))return t.e.c=t.s.c,t.e.r=t.s.r,t;for(n=0;r!=s&&!((i=e.charCodeAt(r)-64)<1||i>26);++r)n=26*n+i;for(t.e.c=--n,n=0;r!=s&&!((i=e.charCodeAt(r)-48)<0||i>9);++r)n=10*n+i;return t.e.r=--n,t}function Jt(e,t,n){return null==e||null==e.t||"z"==e.t?"":void 0!==e.w?e.w:("d"==e.t&&!e.z&&n&&n.dateNF&&(e.z=n.dateNF),function(e,t){var n="d"==e.t&&t instanceof Date;if(null!=e.z)try{return e.w=T.format(e.z,n?z(t):t)}catch(El){}try{return e.w=T.format((e.XF||{}).numFmtId||(n?14:0),n?z(t):t)}catch(El){return""+t}}(e,null==t?e.v:t))}function Qt(e,t){var n=t&&t.sheet?t.sheet:"Sheet1",r={};return r[n]=e,{SheetNames:[n],Sheets:r}}function qt(e,t,n){var r=n||{},i=e?Array.isArray(e):r.dense,s=e||(i?[]:{}),o=0,a=0;if(s&&null!=r.origin)if("number"==typeof r.origin)o=r.origin;else{var c="string"==typeof r.origin?zt(r.origin):r.origin;o=c.r,a=c.c}var l={s:{c:1e7,r:1e7},e:{c:0,r:0}};if(s["!ref"]){var h=Yt(s["!ref"]);l.s.c=h.s.c,l.s.r=h.s.r,l.e.c=Math.max(l.e.c,h.e.c),l.e.r=Math.max(l.e.r,h.e.r),-1==o&&(l.e.r=o=h.e.r+1)}for(var d=0;d!=t.length;++d)if(t[d]){if(!Array.isArray(t[d]))throw new Error("aoa_to_sheet expects an array of arrays");for(var u=0;u!=t[d].length;++u)if(void 0!==t[d][u]){var f={v:t[d][u]},g=o+d,p=a+u;if(l.s.r>g&&(l.s.r=g),l.s.c>p&&(l.s.c=p),l.e.r0&&t.write_shift(0,e,"dbcs"),n?t.slice(0,t.l):t}function sn(e){return{ich:e.read_shift(2),ifnt:e.read_shift(2)}}function on(e,t){var n=e.l,r=e.read_shift(1),i=nn(e),s=[],o={t:i,h:i};if(0!=(1&r)){for(var a=e.read_shift(4),c=0;c!=a;++c)s.push(sn(e));o.r=s}else o.r=[{ich:0,ifnt:0}];return e.l=n+t,o}!function(e,t){var r;if(void 0!==t)r=t;else try{r=n(4)}catch(El){r=null}e.rc4=function(e,t){var n=new Array(256),r=0,i=0,s=0,o=0;for(i=0;256!=i;++i)n[i]=i;for(i=0;256!=i;++i)s=s+n[i]+e[i%e.length].charCodeAt(0)&255,o=n[i],n[i]=n[s],n[s]=o;i=s=0;var a=Buffer(t.length);for(r=0;r!=t.length;++r)o=n[i=i+1&255],n[i]=n[s=(s+n[i])%256],n[s]=o,a[r]=t[r]^n[n[i]+n[s]&255];return a},e.md5=function(e){if(!r)throw new Error("Unsupported crypto");return r.createHash("md5").update(e).digest("hex")}}({},"undefined"!=typeof crypto?crypto:void 0);var an=on;function cn(e){var t=e.read_shift(4),n=e.read_shift(2);return n+=e.read_shift(1)<<16,e.l++,{c:t,iStyleRef:n}}function ln(e,t){return null==t&&(t=Lt(8)),t.write_shift(-4,e.c),t.write_shift(3,e.iStyleRef||e.s),t.write_shift(1,0),t}var hn=nn,dn=rn;function un(e){var t=e.read_shift(4);return 0===t||4294967295===t?"":e.read_shift(t,"dbcs")}function fn(e,t){var n=!1;return null==t&&(n=!0,t=Lt(127)),t.write_shift(4,e.length>0?e.length:4294967295),e.length>0&&t.write_shift(0,e,"dbcs"),n?t.slice(0,t.l):t}var gn=nn,pn=un,mn=fn;function Cn(e){var t=e.slice(e.l,e.l+4),n=1&t[0],r=2&t[0];e.l+=4,t[0]&=252;var i=0===r?mt([0,0,0,0,t[0],t[1],t[2],t[3]],0):xt(t,0)>>2;return n?i/100:i}function bn(e){var t={s:{},e:{}};return t.s.r=e.read_shift(4),t.e.r=e.read_shift(4),t.s.c=e.read_shift(4),t.e.c=e.read_shift(4),t}var vn=bn,In=function(e,t){return t||(t=Lt(16)),t.write_shift(4,e.s.r),t.write_shift(4,e.e.r),t.write_shift(4,e.s.c),t.write_shift(4,e.e.c),t};function An(e){return e.read_shift(8,"f")}function yn(e,t){return(t||Lt(8)).write_shift(8,e,"f")}var wn={0:"#NULL!",7:"#DIV/0!",15:"#VALUE!",23:"#REF!",29:"#NAME?",36:"#NUM!",42:"#N/A",43:"#GETTING_DATA",255:"#WTF?"},Sn=V(wn);function xn(e,t){if(t||(t=Lt(8)),!e||e.auto)return t.write_shift(4,0),t.write_shift(4,0),t;e.index?(t.write_shift(1,2),t.write_shift(1,e.index)):e.theme?(t.write_shift(1,6),t.write_shift(1,e.theme)):(t.write_shift(1,5),t.write_shift(1,0));var n=e.tint||0;if(n>0?n*=32767:n<0&&(n*=32768),t.write_shift(2,n),e.rgb){var r=e.rgb||"FFFFFF";t.write_shift(1,parseInt(r.slice(0,2),16)),t.write_shift(1,parseInt(r.slice(2,4),16)),t.write_shift(1,parseInt(r.slice(4,6),16)),t.write_shift(1,255)}else t.write_shift(2,0),t.write_shift(1,0),t.write_shift(1,0);return t}function En(e,t){var n=e.read_shift(4);switch(n){case 0:return"";case 4294967295:case 4294967294:return{2:"BITMAP",3:"METAFILEPICT",8:"DIB",14:"ENHMETAFILE"}[e.read_shift(4)]||""}if(n>400)throw new Error("Unsupported Clipboard: "+n.toString(16));return e.l-=4,e.read_shift(0,1==t?"lpstr":"lpwstr")}var kn=80,_n=[kn,81],Tn={1:{n:"CodePage",t:2},2:{n:"Category",t:kn},3:{n:"PresentationFormat",t:kn},4:{n:"ByteCount",t:3},5:{n:"LineCount",t:3},6:{n:"ParagraphCount",t:3},7:{n:"SlideCount",t:3},8:{n:"NoteCount",t:3},9:{n:"HiddenCount",t:3},10:{n:"MultimediaClipCount",t:3},11:{n:"ScaleCrop",t:11},12:{n:"HeadingPairs",t:4108},13:{n:"TitlesOfParts",t:4126},14:{n:"Manager",t:kn},15:{n:"Company",t:kn},16:{n:"LinksUpToDate",t:11},17:{n:"CharacterCount",t:3},19:{n:"SharedDoc",t:11},22:{n:"HyperlinksChanged",t:11},23:{n:"AppVersion",t:3,p:"version"},24:{n:"DigSig",t:65},26:{n:"ContentType",t:kn},27:{n:"ContentStatus",t:kn},28:{n:"Language",t:kn},29:{n:"Version",t:kn},255:{}},Rn={1:{n:"CodePage",t:2},2:{n:"Title",t:kn},3:{n:"Subject",t:kn},4:{n:"Author",t:kn},5:{n:"Keywords",t:kn},6:{n:"Comments",t:kn},7:{n:"Template",t:kn},8:{n:"LastAuthor",t:kn},9:{n:"RevNumber",t:kn},10:{n:"EditTime",t:64},11:{n:"LastPrinted",t:64},12:{n:"CreatedDate",t:64},13:{n:"ModifiedDate",t:64},14:{n:"PageCount",t:3},15:{n:"WordCount",t:3},16:{n:"CharCount",t:3},17:{n:"Thumbnail",t:71},18:{n:"Application",t:kn},19:{n:"DocSecurity",t:3},255:{}},Bn={2147483648:{n:"Locale",t:19},2147483651:{n:"Behavior",t:19},1919054434:{}};!function(){for(var e in Bn)Bn.hasOwnProperty(e)&&(Tn[e]=Rn[e]=Bn[e])}();var On,Ln=H(Tn,"n"),Pn=H(Rn,"n"),Fn={1:"US",2:"CA",3:"",7:"RU",20:"EG",30:"GR",31:"NL",32:"BE",33:"FR",34:"ES",36:"HU",39:"IT",41:"CH",43:"AT",44:"GB",45:"DK",46:"SE",47:"NO",48:"PL",49:"DE",52:"MX",55:"BR",61:"AU",64:"NZ",66:"TH",81:"JP",82:"KR",84:"VN",86:"CN",90:"TR",105:"JS",213:"DZ",216:"MA",218:"LY",351:"PT",354:"IS",358:"FI",420:"CZ",886:"TW",961:"LB",962:"JO",963:"SY",964:"IQ",965:"KW",966:"SA",971:"AE",972:"IL",974:"QA",981:"IR",65535:"US"},Nn=[null,"solid","mediumGray","darkGray","lightGray","darkHorizontal","darkVertical","darkDown","darkUp","darkGrid","darkTrellis","lightHorizontal","lightVertical","lightDown","lightUp","lightGrid","lightTrellis","gray125","gray0625"],Mn=[0,16777215,16711680,65280,255,16776960,16711935,65535,0,16777215,16711680,65280,255,16776960,16711935,65535,8388608,32768,128,8421376,8388736,32896,12632256,8421504,10066431,10040166,16777164,13434879,6684774,16744576,26316,13421823,128,16711935,16776960,65535,8388736,8388608,32896,255,52479,13434879,13434828,16777113,10079487,16751052,13408767,16764057,3368703,3394764,10079232,16763904,16750848,16737792,6710937,9868950,13158,3381606,13056,3355392,10040064,10040166,3355545,3355443,16777215,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0].map((function(e){return[e>>16&255,e>>8&255,255&e]})),Dn={"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml":"workbooks","application/vnd.ms-excel.binIndexWs":"TODO","application/vnd.ms-excel.intlmacrosheet":"TODO","application/vnd.ms-excel.binIndexMs":"TODO","application/vnd.openxmlformats-package.core-properties+xml":"coreprops","application/vnd.openxmlformats-officedocument.custom-properties+xml":"custprops","application/vnd.openxmlformats-officedocument.extended-properties+xml":"extprops","application/vnd.openxmlformats-officedocument.customXmlProperties+xml":"TODO","application/vnd.openxmlformats-officedocument.spreadsheetml.customProperty":"TODO","application/vnd.ms-excel.pivotTable":"TODO","application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml":"TODO","application/vnd.ms-office.chartcolorstyle+xml":"TODO","application/vnd.ms-office.chartstyle+xml":"TODO","application/vnd.ms-excel.calcChain":"calcchains","application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml":"calcchains","application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings":"TODO","application/vnd.ms-office.activeX":"TODO","application/vnd.ms-office.activeX+xml":"TODO","application/vnd.ms-excel.attachedToolbars":"TODO","application/vnd.ms-excel.connections":"TODO","application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml":"TODO","application/vnd.ms-excel.externalLink":"links","application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml":"links","application/vnd.ms-excel.sheetMetadata":"TODO","application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml":"TODO","application/vnd.ms-excel.pivotCacheDefinition":"TODO","application/vnd.ms-excel.pivotCacheRecords":"TODO","application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml":"TODO","application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml":"TODO","application/vnd.ms-excel.queryTable":"TODO","application/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml":"TODO","application/vnd.ms-excel.userNames":"TODO","application/vnd.ms-excel.revisionHeaders":"TODO","application/vnd.ms-excel.revisionLog":"TODO","application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml":"TODO","application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml":"TODO","application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml":"TODO","application/vnd.ms-excel.tableSingleCells":"TODO","application/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml":"TODO","application/vnd.ms-excel.slicer":"TODO","application/vnd.ms-excel.slicerCache":"TODO","application/vnd.ms-excel.slicer+xml":"TODO","application/vnd.ms-excel.slicerCache+xml":"TODO","application/vnd.ms-excel.wsSortMap":"TODO","application/vnd.ms-excel.table":"TODO","application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml":"TODO","application/vnd.openxmlformats-officedocument.theme+xml":"themes","application/vnd.openxmlformats-officedocument.themeOverride+xml":"TODO","application/vnd.ms-excel.Timeline+xml":"TODO","application/vnd.ms-excel.TimelineCache+xml":"TODO","application/vnd.ms-office.vbaProject":"vba","application/vnd.ms-office.vbaProjectSignature":"vba","application/vnd.ms-office.volatileDependencies":"TODO","application/vnd.openxmlformats-officedocument.spreadsheetml.volatileDependencies+xml":"TODO","application/vnd.ms-excel.controlproperties+xml":"TODO","application/vnd.openxmlformats-officedocument.model+data":"TODO","application/vnd.ms-excel.Survey+xml":"TODO","application/vnd.openxmlformats-officedocument.drawing+xml":"drawings","application/vnd.openxmlformats-officedocument.drawingml.chart+xml":"TODO","application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml":"TODO","application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml":"TODO","application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml":"TODO","application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml":"TODO","application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml":"TODO","application/vnd.openxmlformats-officedocument.vmlDrawing":"TODO","application/vnd.openxmlformats-package.relationships+xml":"rels","application/vnd.openxmlformats-officedocument.oleObject":"TODO","image/png":"TODO",sheet:"js"},$n=(W(On={workbooks:{xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",xlsm:"application/vnd.ms-excel.sheet.macroEnabled.main+xml",xlsb:"application/vnd.ms-excel.sheet.binary.macroEnabled.main",xlam:"application/vnd.ms-excel.addin.macroEnabled.main+xml",xltx:"application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml"},strs:{xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",xlsb:"application/vnd.ms-excel.sharedStrings"},comments:{xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",xlsb:"application/vnd.ms-excel.comments"},sheets:{xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",xlsb:"application/vnd.ms-excel.worksheet"},charts:{xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml",xlsb:"application/vnd.ms-excel.chartsheet"},dialogs:{xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml",xlsb:"application/vnd.ms-excel.dialogsheet"},macros:{xlsx:"application/vnd.ms-excel.macrosheet+xml",xlsb:"application/vnd.ms-excel.macrosheet"},styles:{xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",xlsb:"application/vnd.ms-excel.styles"}}).forEach((function(e){["xlsm","xlam"].forEach((function(t){On[e][t]||(On[e][t]=On[e].xlsx)}))})),W(On).forEach((function(e){W(On[e]).forEach((function(t){Dn[On[e][t]]=e}))})),On),Wn=function(e){for(var t=[],n=W(e),r=0;r!==n.length;++r)null==t[e[n[r]]]&&(t[e[n[r]]]=[]),t[e[n[r]]].push(n[r]);return t}(Dn);Qe.CT="http://schemas.openxmlformats.org/package/2006/content-types";var Hn=Xe("Types",null,{xmlns:Qe.CT,"xmlns:xsd":Qe.xsd,"xmlns:xsi":Qe.xsi}),Gn=[["xml","application/xml"],["bin","application/vnd.ms-excel.sheet.binary.macroEnabled.main"],["vml","application/vnd.openxmlformats-officedocument.vmlDrawing"],["bmp","image/bmp"],["png","image/png"],["gif","image/gif"],["emf","image/x-emf"],["wmf","image/x-wmf"],["jpg","image/jpeg"],["jpeg","image/jpeg"],["tif","image/tiff"],["tiff","image/tiff"],["pdf","application/pdf"],["rels",Wn.rels[0]]].map((function(e){return Xe("Default",null,{Extension:e[0],ContentType:e[1]})})),Vn={WB:"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",SHEET:"http://sheetjs.openxmlformats.org/officeDocument/2006/relationships/officeDocument",HLINK:"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",VML:"http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing",VBA:"http://schemas.microsoft.com/office/2006/relationships/vbaProject"};function jn(e){var t=e.lastIndexOf("/");return e.slice(0,t+1)+"_rels/"+e.slice(t+1)+".rels"}function Zn(e,t){if(!e)return e;"/"!==t.charAt(0)&&(t="/"+t);var n={},r={};return(e.match(fe)||[]).forEach((function(e){var i=me(e);if("2&&(t[t.length]="",t[1]=t[1].replace("/>",">")),t.join("")}function Xn(e,t,n,r,i){if(i||(i={}),e["!id"]||(e["!id"]={}),t<0)for(t=1;e["!id"]["rId"+t];++t);if(i.Id="rId"+t,i.Type=r,i.Target=n,i.Type==Vn.HLINK&&(i.TargetMode="External"),e["!id"][i.Id])throw new Error("Cannot rewrite rId "+t);return e["!id"][i.Id]=i,e[("/"+i.Target).replace("//","/")]=i,t}function Un(e,t,n){return[' \n',' \n'," \n"].join("")}var Yn,Jn=(Yn='SheetJS '+t.version+" ",function(){return Yn}),Qn=[["cp:category","Category"],["cp:contentStatus","ContentStatus"],["cp:keywords","Keywords"],["cp:lastModifiedBy","LastAuthor"],["cp:lastPrinted","LastPrinted"],["cp:revision","RevNumber"],["cp:version","Version"],["dc:creator","Author"],["dc:description","Comments"],["dc:identifier","Identifier"],["dc:language","Language"],["dc:subject","Subject"],["dc:title","Title"],["dcterms:created","CreatedDate","date"],["dcterms:modified","ModifiedDate","date"]];Qe.CORE_PROPS="http://schemas.openxmlformats.org/package/2006/metadata/core-properties",Vn.CORE_PROPS="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties";var qn=function(){for(var e=new Array(Qn.length),t=0;t]*>([\\s\\S]*?)"+r+">")}return e}();function er(e){var t={};e=Be(e);for(var n=0;n0&&(t[r[1]]=i[1]),"date"===r[2]&&t[r[1]]&&(t[r[1]]=J(t[r[1]]))}return t}var tr=Xe("cp:coreProperties",null,{"xmlns:cp":Qe.CORE_PROPS,"xmlns:dc":Qe.dc,"xmlns:dcterms":Qe.dcterms,"xmlns:dcmitype":Qe.dcmitype,"xmlns:xsi":Qe.xsi});function nr(e,t,n,r,i){null==i[e]&&null!=t&&""!==t&&(i[e]=t,r[r.length]=n?Xe(e,t,n):ze(e,t))}var rr=[["Application","Application","string"],["AppVersion","AppVersion","string"],["Company","Company","string"],["DocSecurity","DocSecurity","string"],["Manager","Manager","string"],["HyperlinksChanged","HyperlinksChanged","bool"],["SharedDoc","SharedDoc","bool"],["LinksUpToDate","LinksUpToDate","bool"],["ScaleCrop","ScaleCrop","bool"],["HeadingPairs","HeadingPairs","raw"],["TitlesOfParts","TitlesOfParts","raw"]];function ir(e,t,n,r){var i=[];if("string"==typeof e)i=je(e,r);else for(var s=0;s0)for(var l=0;l!==i.length;l+=2){switch(c=+i[l+1].v,i[l].v){case"Worksheets":case"\u5de5\u4f5c\u8868":case"\u041b\u0438\u0441\u0442\u044b":case"\u0623\u0648\u0631\u0627\u0642 \u0627\u0644\u0639\u0645\u0644":case"\u30ef\u30fc\u30af\u30b7\u30fc\u30c8":case"\u05d2\u05dc\u05d9\u05d5\u05e0\u05d5\u05ea \u05e2\u05d1\u05d5\u05d3\u05d4":case"Arbeitsbl\xe4tter":case"\xc7al\u0131\u015fma Sayfalar\u0131":case"Feuilles de calcul":case"Fogli di lavoro":case"Folhas de c\xe1lculo":case"Planilhas":case"Regneark":case"Werkbladen":n.Worksheets=c,n.SheetNames=o.slice(a,a+c);break;case"Named Ranges":case"\u540d\u524d\u4ed8\u304d\u4e00\u89a7":case"Benannte Bereiche":case"Navngivne omr\xe5der":n.NamedRanges=c,n.DefinedNames=o.slice(a,a+c);break;case"Charts":case"Diagramme":n.Chartsheets=c,n.ChartNames=o.slice(a,a+c)}a+=c}}Qe.EXT_PROPS="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties",Vn.EXT_PROPS="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties";var sr=Xe("Properties",null,{xmlns:Qe.EXT_PROPS,"xmlns:vt":Qe.vt});Qe.CUST_PROPS="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties",Vn.CUST_PROPS="http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties";var or=/<[^>]+>[^<]*/g,ar=Xe("Properties",null,{xmlns:Qe.CUST_PROPS,"xmlns:vt":Qe.vt}),cr={Title:"Title",Subject:"Subject",Author:"Author",Keywords:"Keywords",Comments:"Description",LastAuthor:"LastAuthor",RevNumber:"Revision",Application:"AppName",LastPrinted:"LastPrinted",CreatedDate:"Created",ModifiedDate:"LastSaved",Category:"Category",Manager:"Manager",Company:"Company",AppVersion:"Version",ContentStatus:"ContentStatus",Identifier:"Identifier",Language:"Language"},lr=G(cr);function hr(e,t,n){e[t=lr[t]||t]=n}function dr(e){var t=e.read_shift(4),n=e.read_shift(4);return new Date(1e3*(n/1e7*Math.pow(2,32)+t/1e7-11644473600)).toISOString().replace(/\.000/,"")}function ur(e,t,n){var r=e.l,i=e.read_shift(0,"lpstr-cp");if(n)for(;e.l-r&3;)++e.l;return i}function fr(e,t,n){var r=e.read_shift(0,"lpwstr");return n&&(e.l+=4-(r.length+1&3)&3),r}function gr(e,t,n){return 31===t?fr(e):ur(e,0,n)}function pr(e,t,n){return gr(e,t,!1===n?0:4)}function mr(e){return[vr(e,81),vr(e,3)]}function Cr(e,t){for(var n=e.read_shift(4),r={},i=0;i!=n;++i){var s=e.read_shift(4),o=e.read_shift(4);r[s]=e.read_shift(o,1200===t?"utf16le":"utf8").replace(k,"").replace(_,"!"),1200===t&&o%2&&(e.l+=2)}return 3&e.l&&(e.l=e.l>>3<<2),r}function br(e){var t=e.read_shift(4),n=e.slice(e.l,e.l+t);return e.l+=t,(3&t)>0&&(e.l+=4-(3&t)&3),n}function vr(e,t,n){var r,i=e.read_shift(2),s=n||{};if(e.l+=2,12!==t&&i!==t&&-1===_n.indexOf(t))throw new Error("Expected type "+t+" saw "+i);switch(12===t?i:t){case 2:return r=e.read_shift(2,"i"),s.raw||(e.l+=2),r;case 3:return e.read_shift(4,"i");case 11:return 0!==e.read_shift(4);case 19:return e.read_shift(4);case 30:return ur(e,0,4).replace(k,"");case 31:return fr(e);case 64:return dr(e);case 65:return br(e);case 71:return function(e){var t={};return t.Size=e.read_shift(4),e.l+=t.Size+3-(t.Size-1)%4,t}(e);case 80:return pr(e,i,!s.raw).replace(k,"");case 81:return function(e,t){if(!t)throw new Error("VtUnalignedString must have positive length");return gr(e,t,0)}(e,i).replace(k,"");case 4108:return function(e){return function(e){for(var t=e.read_shift(4),n=[],r=0;r!=t/2;++r)n.push(mr(e));return n}(e)}(e);case 4126:return function(e){return function(e){for(var t=e.read_shift(4),n=[],r=0;r!=t;++r)n[r]=e.read_shift(0,"lpstr-cp").replace(k,"");return n}(e)}(e);default:throw new Error("TypedPropertyValue unrecognized type "+t+" "+i)}}function Ir(e,t){var n=Lt(4),r=Lt(4);switch(n.write_shift(4,80==e?31:e),e){case 3:r.write_shift(-4,t);break;case 5:(r=Lt(8)).write_shift(8,t,"f");break;case 11:r.write_shift(4,t?1:0);break;case 64:r=function(e){var t=("string"==typeof e?new Date(Date.parse(e)):e).getTime()/1e3+11644473600,n=t%Math.pow(2,32),r=(t-n)/Math.pow(2,32);r*=1e7;var i=(n*=1e7)/Math.pow(2,32)|0;i>0&&(n%=Math.pow(2,32),r+=i);var s=Lt(8);return s.write_shift(4,n),s.write_shift(4,r),s}(t);break;case 31:case 80:for((r=Lt(4+2*(t.length+1)+(t.length%2?0:2))).write_shift(4,t.length+1),r.write_shift(0,t,"dbcs");r.l!=r.length;)r.write_shift(1,0);break;default:throw new Error("TypedPropertyValue unrecognized type "+e+" "+t)}return E([n,r])}function Ar(e,t){var n=e.l,r=e.read_shift(4),i=e.read_shift(4),s=[],o=0,a=0,c=-1,h={};for(o=0;o!=i;++o){var d=e.read_shift(4),u=e.read_shift(4);s[o]=[d,u+n]}s.sort((function(e,t){return e[1]-t[1]}));var f={};for(o=0;o!=i;++o){if(e.l!==s[o][1]){var g=!0;if(o>0&&t)switch(t[s[o-1][0]].t){case 2:e.l+2===s[o][1]&&(e.l+=2,g=!1);break;case 80:case 4108:e.l<=s[o][1]&&(e.l=s[o][1],g=!1)}if((!t||0==o)&&e.l<=s[o][1]&&(g=!1,e.l=s[o][1]),g)throw new Error("Read Error: Expected address "+s[o][1]+" at "+e.l+" :"+o)}if(t){var p=t[s[o][0]];if(f[p.n]=vr(e,p.t,{raw:!0}),"version"===p.p&&(f[p.n]=String(f[p.n]>>16)+"."+("0000"+String(65535&f[p.n])).slice(-4)),"CodePage"==p.n)switch(f[p.n]){case 0:f[p.n]=1252;case 874:case 932:case 936:case 949:case 950:case 1250:case 1251:case 1253:case 1254:case 1255:case 1256:case 1257:case 1258:case 1e4:case 1200:case 1201:case 1252:case 65e3:case-536:case 65001:case-535:l(a=f[p.n]>>>0&65535);break;default:throw new Error("Unsupported CodePage: "+f[p.n])}}else if(1===s[o][0]){if(a=f.CodePage=vr(e,2),l(a),-1!==c){var m=e.l;e.l=s[c][1],h=Cr(e,a),e.l=m}}else if(0===s[o][0]){if(0===a){c=o,e.l=s[o+1][1];continue}h=Cr(e,a)}else{var C,b=h[s[o][0]];switch(e[e.l]){case 65:e.l+=4,C=br(e);break;case 30:case 31:e.l+=4,C=pr(e,e[e.l-4]).replace(/\u0000+$/,"");break;case 3:e.l+=4,C=e.read_shift(4,"i");break;case 19:e.l+=4,C=e.read_shift(4);break;case 5:e.l+=4,C=e.read_shift(8,"f");break;case 11:e.l+=4,C=_r(e,4);break;case 64:e.l+=4,C=J(dr(e));break;default:throw new Error("unparsed value: "+e[e.l])}f[b]=C}}return e.l=n+r,f}var yr=["CodePage","Thumbnail","_PID_LINKBASE","_PID_HLINKS","SystemIdentifier","FMTID"].concat(["Worksheets","SheetNames","NamedRanges","DefinedNames","Chartsheets","ChartNames"]);function wr(e){switch(typeof e){case"boolean":return 11;case"number":return(0|e)==e?3:5;case"string":return 31;case"object":if(e instanceof Date)return 64}return-1}function Sr(e,t,n){var r=Lt(8),i=[],s=[],o=8,a=0,c=Lt(8),l=Lt(8);if(c.write_shift(4,2),c.write_shift(4,1200),l.write_shift(4,1),s.push(c),i.push(l),o+=8+c.length,!t){(l=Lt(8)).write_shift(4,0),i.unshift(l);var h=[Lt(4)];for(h[0].write_shift(4,e.length),a=0;a-1)&&null!=e[a][1]){var u=e[a][1],f=0;if(t){var g=n[f=+t[e[a][0]]];if("version"==g.p&&"string"==typeof u){var p=u.split(".");u=(+p[0]<<16)+(+p[1]||0)}c=Ir(g.t,u)}else{var m=wr(u);-1==m&&(m=31,u=String(u)),c=Ir(m,u)}s.push(c),(l=Lt(8)).write_shift(4,t?f:2+a),i.push(l),o+=8+c.length}var C=8*(s.length+1);for(a=0;a=12?2:1),s="sbcs-cont",o=r;n&&n.biff>=8&&(r=1200),n&&8!=n.biff?12==n.biff&&(s="wstr"):e.read_shift(1)&&(s="dbcs-cont"),n.biff>=2&&n.biff<=5&&(s="cpstr");var a=i?e.read_shift(i,s):"";return r=o,a}function Pr(e){var t=r;r=1200;var n,i=e.read_shift(2),s=e.read_shift(1),o=4&s,a=8&s,c=1+(1&s),l=0,h={};a&&(l=e.read_shift(2)),o&&(n=e.read_shift(4));var d=0===i?"":e.read_shift(i,2==c?"dbcs-cont":"sbcs-cont");return a&&(e.l+=4*l),o&&(e.l+=n),h.t=d,a||(h.raw=""+h.t+" ",h.r=h.t),r=t,h}function Fr(e,t,n){if(n){if(n.biff>=2&&n.biff<=5)return e.read_shift(t,"cpstr");if(n.biff>=12)return e.read_shift(t,"dbcs-cont")}var r=e.read_shift(1);return e.read_shift(t,0===r?"sbcs-cont":"dbcs-cont")}function Nr(e,t,n){var r=e.read_shift(n&&2==n.biff?1:2);return 0===r?(e.l++,""):Fr(e,r,n)}function Mr(e,t,n){if(n.biff>5)return Nr(e,0,n);var r=e.read_shift(1);return 0===r?(e.l++,""):e.read_shift(r,n.biff<=4||!e.lens?"cpstr":"sbcs-cont")}function Dr(e,t,n){return n||(n=Lt(3+2*e.length)),n.write_shift(2,e.length),n.write_shift(1,1),n.write_shift(31,e,"utf16le"),n}function $r(e){var t=e.read_shift(4);return t>0?e.read_shift(t,"utf16le").replace(k,""):""}function Wr(e){var t=Lt(512),n=0,r=e.Target,i=r.indexOf("#")>-1?31:23;switch(r.charAt(0)){case"#":i=28;break;case".":i&=-3}t.write_shift(4,2),t.write_shift(4,i);var s=[8,6815827,6619237,4849780,83];for(n=0;n8?4:2;return[e.read_shift(r),e.read_shift(r,"i"),e.read_shift(r,"i")]}function zr(e){return[e.read_shift(2),Cn(e)]}function Kr(e){var t=e.read_shift(2),n=e.read_shift(2);return{s:{c:e.read_shift(2),r:t},e:{c:e.read_shift(2),r:n}}}function Xr(e,t){return t||(t=Lt(8)),t.write_shift(2,e.s.r),t.write_shift(2,e.e.r),t.write_shift(2,e.s.c),t.write_shift(2,e.e.c),t}function Ur(e){var t=e.read_shift(2),n=e.read_shift(2);return{s:{c:e.read_shift(1),r:t},e:{c:e.read_shift(1),r:n}}}var Yr=Ur;function Jr(e){e.l+=4;var t=e.read_shift(2),n=e.read_shift(2),r=e.read_shift(2);return e.l+=12,[n,t,r]}function Qr(e){e.l+=2,e.l+=e.read_shift(2)}var qr={0:Qr,4:Qr,5:Qr,6:Qr,7:function(e){return e.l+=4,e.cf=e.read_shift(2),{}},8:Qr,9:Qr,10:Qr,11:Qr,12:Qr,13:function(e){var t={};return e.l+=4,e.l+=16,t.fSharedNote=e.read_shift(2),e.l+=4,t},14:Qr,15:Qr,16:Qr,17:Qr,18:Qr,19:Qr,20:Qr,21:Jr};function ei(e,t){var n={BIFFVer:0,dt:0};switch(n.BIFFVer=e.read_shift(2),(t-=2)>=2&&(n.dt=e.read_shift(2),e.l-=2),n.BIFFVer){case 1536:case 1280:case 1024:case 768:case 512:case 2:case 7:break;default:if(t>6)throw new Error("Unexpected BIFF Ver "+n.BIFFVer)}return e.read_shift(t),n}function ti(e,t,n){var r=1536,i=16;switch(n.bookType){case"biff8":break;case"biff5":r=1280,i=8;break;case"biff4":r=4,i=6;break;case"biff3":r=3,i=6;break;case"biff2":r=2,i=4;break;case"xla":break;default:throw new Error("unsupported BIFF version")}var s=Lt(i);return s.write_shift(2,r),s.write_shift(2,t),i>4&&s.write_shift(2,29282),i>6&&s.write_shift(2,1997),i>8&&(s.write_shift(2,49161),s.write_shift(2,1),s.write_shift(2,1798),s.write_shift(2,0)),s}function ni(e,t){var n=!t||t.biff>=8?2:1,r=Lt(8+n*e.name.length);r.write_shift(4,e.pos),r.write_shift(1,e.hs||0),r.write_shift(1,e.dt),r.write_shift(1,e.name.length),t.biff>=8&&r.write_shift(1,1),r.write_shift(n*e.name.length,e.name,t.biff<8?"sbcs":"utf16le");var i=r.slice(0,r.l);return i.l=r.l,i}function ri(e,t,n){var r=0;n&&2==n.biff||(r=e.read_shift(2));var i=e.read_shift(2);return n&&2==n.biff&&(r=1-(i>>15),i&=32767),[{Unsynced:1&r,DyZero:(2&r)>>1,ExAsc:(4&r)>>2,ExDsc:(8&r)>>3},i]}function ii(e,t,n,r){var i=n&&5==n.biff;r||(r=Lt(i?3+t.length:5+2*t.length)),r.write_shift(2,e),r.write_shift(i?1:2,t.length),i||r.write_shift(1,1),r.write_shift((i?1:2)*t.length,t,i?"sbcs":"utf16le");var s=r.length>r.l?r.slice(0,r.l):r;return null==s.l&&(s.l=s.length),s}var si=Mr;function oi(e,t,n){var r=e.l+t,i=8!=n.biff&&n.biff?2:4,s=e.read_shift(i),o=e.read_shift(i),a=e.read_shift(2),c=e.read_shift(2);return e.l=r,{s:{r:s,c:a},e:{r:o,c:c}}}function ai(e,t,n,r){var i=n&&5==n.biff;return r||(r=Lt(i?16:20)),r.write_shift(2,0),e.style?(r.write_shift(2,e.numFmtId||0),r.write_shift(2,65524)):(r.write_shift(2,e.numFmtId||0),r.write_shift(2,t<<4)),r.write_shift(4,0),r.write_shift(4,0),i||r.write_shift(4,0),r.write_shift(2,0),r}function ci(e,t,n){var r=Vr(e);2==n.biff&&++e.l;var i=function(e){var t=e.read_shift(1);return 1===e.read_shift(1)?t:1===t}(e);return r.val=i,r.t=!0===i||!1===i?"b":"e",r}var li=function(e,t,n){return 0===t?"":Mr(e,0,n)};function hi(e,t,n){var r,i=e.read_shift(2),s={fBuiltIn:1&i,fWantAdvise:i>>>1&1,fWantPict:i>>>2&1,fOle:i>>>3&1,fOleLink:i>>>4&1,cf:i>>>5&1023,fIcon:i>>>15&1};return 14849===n.sbcch&&(r=function(e,t,n){e.l+=4;var r=e.l+(t-=4),i=Lr(e,0,n),s=e.read_shift(2);if(s!==(r-=e.l))throw new Error("Malformed AddinUdf: padding = "+r+" != "+s);return e.l+=s,i}(e,t-2,n)),s.body=r||e.read_shift(t-2),"string"==typeof r&&(s.Name=r),s}var di=["_xlnm.Consolidate_Area","_xlnm.Auto_Open","_xlnm.Auto_Close","_xlnm.Extract","_xlnm.Database","_xlnm.Criteria","_xlnm.Print_Area","_xlnm.Print_Titles","_xlnm.Recorder","_xlnm.Data_Form","_xlnm.Auto_Activate","_xlnm.Auto_Deactivate","_xlnm.Sheet_Title","_xlnm._FilterDatabase"];function ui(e,t,n){var r=e.l+t,i=e.read_shift(2),s=e.read_shift(1),o=e.read_shift(1),a=e.read_shift(n&&2==n.biff?1:2),c=0;(!n||n.biff>=5)&&(5!=n.biff&&(e.l+=2),c=e.read_shift(2),5==n.biff&&(e.l+=2),e.l+=4);var l=Fr(e,o,n);32&i&&(l=di[l.charCodeAt(0)]);var h=r-e.l;return n&&2==n.biff&&--h,{chKey:s,Name:l,itab:c,rgce:r==e.l||0===a?[]:function(e,t,n,r){var i,s=e.l+t,o=To(e,r,n);return s!==e.l&&(i=_o(e,s-e.l,o,n)),[o,i]}(e,h,n,a)}}function fi(e,t,n){if(n.biff<8)return function(e,t,n){3==e[e.l+1]&&e[e.l]++;var r=Lr(e,0,n);return 3==r.charCodeAt(0)?r.slice(1):r}(e,0,n);for(var r=[],i=e.l+t,s=e.read_shift(n.biff>8?4:2);0!=s--;)r.push(Zr(e,0,n));if(e.l!=i)throw new Error("Bad ExternSheet: "+e.l+" != "+i);return r}function gi(e,t,n){var r=Yr(e,6);switch(n.biff){case 2:e.l++,t-=7;break;case 3:case 4:e.l+=2,t-=8;break;default:e.l+=6,t-=12}return[r,Fo(e,t,n)]}var pi=[];function mi(e){var t=Lt(24),n=zt(e[0]);t.write_shift(2,n.r),t.write_shift(2,n.r),t.write_shift(2,n.c),t.write_shift(2,n.c);for(var r="d0 c9 ea 79 f9 ba ce 11 8c 82 00 aa 00 4b a9 0b".split(" "),i=0;i<16;++i)t.write_shift(1,parseInt(r[i],16));return E([t,Wr(e[1])])}function Ci(e){var t=e[1].Tooltip,n=Lt(10+2*(t.length+1));n.write_shift(2,2048);var r=zt(e[0]);n.write_shift(2,r.r),n.write_shift(2,r.r),n.write_shift(2,r.c),n.write_shift(2,r.c);for(var i=0;i=12?4:2,i=e.read_shift(r),s=e.read_shift(r),o=e.read_shift(r),a=e.read_shift(r),c=e.read_shift(2);return 2==r&&(e.l+=2),{s:i,e:s,w:o,ixfe:a,flags:c}}pi[8]=function(e,t){var n=e.l+t;e.l+=10;var r=e.read_shift(2);e.l+=4,e.l+=2,e.l+=2,e.l+=2,e.l+=4;var i=e.read_shift(1);return e.l+=i,e.l=n,{fmt:r}};var vi=Vr,Ii=Or,Ai=Nr,yi=function(){var e={1:437,2:850,3:1252,4:1e4,100:852,101:866,102:865,103:861,104:895,105:620,106:737,107:857,120:950,121:949,122:936,123:932,124:874,125:1255,126:1256,150:10007,151:10029,152:10006,200:1250,201:1251,202:1254,203:1253,0:20127,8:865,9:437,10:850,11:437,13:437,14:850,15:437,16:850,17:437,18:850,19:932,20:850,21:437,22:850,23:865,24:437,25:437,26:850,27:437,28:863,29:850,31:852,34:852,35:852,36:860,37:850,38:866,55:850,64:852,77:936,78:949,79:950,80:874,87:1252,88:1252,89:1252,255:16969},t=G({1:437,2:850,3:1252,4:1e4,100:852,101:866,102:865,103:861,104:895,105:620,106:737,107:857,120:950,121:949,122:936,123:932,124:874,125:1255,126:1256,150:10007,151:10029,152:10006,200:1250,201:1251,202:1254,203:1253,0:20127});function n(t,n){var r=n||{};return r.dateNF||(r.dateNF="yyyymmdd"),en(function(t,n){var r=[],i=I(1);switch(n.type){case"base64":i=y(m.decode(t));break;case"binary":i=y(t);break;case"buffer":case"array":i=t}Bt(i,0);var s=i.read_shift(1),o=!1,a=!1,c=!1;switch(s){case 2:case 3:break;case 48:a=!0,o=!0;break;case 49:a=!0;break;case 131:case 139:o=!0;break;case 140:o=!0,c=!0;break;case 245:o=!0;break;default:throw new Error("DBF Unsupported Version: "+s.toString(16))}var l=0,h=0;2==s&&(l=i.read_shift(2)),i.l+=3,2!=s&&(l=i.read_shift(4)),2!=s&&(h=i.read_shift(2));var d=i.read_shift(2),u=1252;2!=s&&(i.l+=16,i.read_shift(1),0!==i[i.l]&&(u=e[i[i.l]]),i.l+=1,i.l+=2),c&&(i.l+=36);for(var f=[],g={},p=h-10-(a?264:0),C=c?32:11;2==s?i.l